Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios into vasavk/priceLockup
This commit is contained in:
commit
1bd58256f7
@ -7,6 +7,8 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
180636C72C29B0A400C92D86 /* InputStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180636C62C29B0A400C92D86 /* InputStepper.swift */; };
|
||||||
|
180636C92C29B0DF00C92D86 /* InputStepperLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 180636C82C29B0DF00C92D86 /* InputStepperLog.txt */; };
|
||||||
1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; };
|
1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; };
|
||||||
1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; };
|
1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; };
|
||||||
184023452C61E7AD00A412C8 /* PriceLockup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184023442C61E7AD00A412C8 /* PriceLockup.swift */; };
|
184023452C61E7AD00A412C8 /* PriceLockup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184023442C61E7AD00A412C8 /* PriceLockup.swift */; };
|
||||||
@ -209,6 +211,8 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
180636C62C29B0A400C92D86 /* InputStepper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputStepper.swift; sourceTree = "<group>"; };
|
||||||
|
180636C82C29B0DF00C92D86 /* InputStepperLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = InputStepperLog.txt; sourceTree = "<group>"; };
|
||||||
1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = "<group>"; };
|
1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = "<group>"; };
|
||||||
1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = "<group>"; };
|
1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = "<group>"; };
|
||||||
1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = "<group>"; };
|
1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = "<group>"; };
|
||||||
@ -453,6 +457,15 @@
|
|||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
180636C52C29B06200C92D86 /* InputStepper */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
180636C62C29B0A400C92D86 /* InputStepper.swift */,
|
||||||
|
180636C82C29B0DF00C92D86 /* InputStepperLog.txt */,
|
||||||
|
);
|
||||||
|
path = InputStepper;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
1808BEBA2BA41B1D00129230 /* CarouselScrollbar */ = {
|
1808BEBA2BA41B1D00129230 /* CarouselScrollbar */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -694,6 +707,7 @@
|
|||||||
EAC58C1F2BF127F000BA39FA /* DatePicker */,
|
EAC58C1F2BF127F000BA39FA /* DatePicker */,
|
||||||
186D13C92BBA8A3500986B53 /* DropdownSelect */,
|
186D13C92BBA8A3500986B53 /* DropdownSelect */,
|
||||||
EA985BF3296C609E00F2FF2E /* Icon */,
|
EA985BF3296C609E00F2FF2E /* Icon */,
|
||||||
|
180636C52C29B06200C92D86 /* InputStepper */,
|
||||||
EA3362412892EF700071C351 /* Label */,
|
EA3362412892EF700071C351 /* Label */,
|
||||||
44604AD529CE195300E62B51 /* Line */,
|
44604AD529CE195300E62B51 /* Line */,
|
||||||
EAD0688C2A55F801002E3A2D /* Loader */,
|
EAD0688C2A55F801002E3A2D /* Loader */,
|
||||||
@ -1203,6 +1217,7 @@
|
|||||||
EA3362072891E14D0071C351 /* VerizonNHGeDS-Regular.otf in Resources */,
|
EA3362072891E14D0071C351 /* VerizonNHGeDS-Regular.otf in Resources */,
|
||||||
EA3362062891E14D0071C351 /* VerizonNHGeTX-Regular.otf in Resources */,
|
EA3362062891E14D0071C351 /* VerizonNHGeTX-Regular.otf in Resources */,
|
||||||
EA3362052891E14D0071C351 /* VerizonNHGeDS-Bold.otf in Resources */,
|
EA3362052891E14D0071C351 /* VerizonNHGeDS-Bold.otf in Resources */,
|
||||||
|
180636C92C29B0DF00C92D86 /* InputStepperLog.txt in Resources */,
|
||||||
EAA5EEB928ECD24B003B3210 /* Icons.xcassets in Resources */,
|
EAA5EEB928ECD24B003B3210 /* Icons.xcassets in Resources */,
|
||||||
EAA5EEE428F5B855003B3210 /* VerizonNHGDS-Light.otf in Resources */,
|
EAA5EEE428F5B855003B3210 /* VerizonNHGDS-Light.otf in Resources */,
|
||||||
);
|
);
|
||||||
@ -1246,6 +1261,7 @@
|
|||||||
files = (
|
files = (
|
||||||
445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */,
|
445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */,
|
||||||
EA5E304C294CBDD00082B959 /* TileContainer.swift in Sources */,
|
EA5E304C294CBDD00082B959 /* TileContainer.swift in Sources */,
|
||||||
|
180636C72C29B0A400C92D86 /* InputStepper.swift in Sources */,
|
||||||
EAF7F0A6289B0CE000B287F5 /* Resetable.swift in Sources */,
|
EAF7F0A6289B0CE000B287F5 /* Resetable.swift in Sources */,
|
||||||
EA985C2D296F03FE00F2FF2E /* TileletIconModels.swift in Sources */,
|
EA985C2D296F03FE00F2FF2E /* TileletIconModels.swift in Sources */,
|
||||||
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */,
|
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */,
|
||||||
|
|||||||
@ -78,20 +78,30 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open func initialSetup() {
|
private func initialSetup() {
|
||||||
if !initialSetupPerformed {
|
if !initialSetupPerformed {
|
||||||
initialSetupPerformed = true
|
initialSetupPerformed = true
|
||||||
|
shouldUpdateView = false
|
||||||
setup()
|
setup()
|
||||||
|
setDefaults()
|
||||||
|
shouldUpdateView = true
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open func setup() {
|
open func setup() {
|
||||||
backgroundColor = .clear
|
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
insetsLayoutMarginsFromSafeArea = false
|
insetsLayoutMarginsFromSafeArea = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open func setDefaults() {
|
||||||
|
backgroundColor = .clear
|
||||||
|
surface = .light
|
||||||
|
isEnabled = true
|
||||||
|
onClick = nil
|
||||||
|
userInfo.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
open func updateView() { }
|
open func updateView() { }
|
||||||
|
|
||||||
open func updateAccessibility() {
|
open func updateAccessibility() {
|
||||||
@ -108,13 +118,12 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open func reset() {
|
open func reset() {
|
||||||
backgroundColor = .clear
|
shouldUpdateView = false
|
||||||
surface = .light
|
setDefaults()
|
||||||
isEnabled = true
|
shouldUpdateView = true
|
||||||
onClick = nil
|
setNeedsUpdate()
|
||||||
userInfo.removeAll()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -100,13 +100,21 @@ open class SelectorBase: Control, SelectorControlable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Executed on initialization for this View.
|
|
||||||
open override func initialSetup() {
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||||
super.initialSetup()
|
open override func setup() {
|
||||||
|
super.setup()
|
||||||
|
isAccessibilityElement = true
|
||||||
|
accessibilityTraits = .button
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
|
||||||
onClick = { control in
|
onClick = { control in
|
||||||
control.toggle()
|
control.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
bridge_accessibilityLabelBlock = { [weak self] in
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
return "\(Self.self)\(showError ? ", error" : "")"
|
return "\(Self.self)\(showError ? ", error" : "")"
|
||||||
@ -118,14 +126,6 @@ open class SelectorBase: Control, SelectorControlable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
|
||||||
open override func setup() {
|
|
||||||
super.setup()
|
|
||||||
|
|
||||||
isAccessibilityElement = true
|
|
||||||
accessibilityTraits = .button
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
setNeedsLayout()
|
setNeedsLayout()
|
||||||
|
|||||||
@ -65,25 +65,30 @@ open class SelectorGroupBase<SelectorItemType: Groupable>: Control, SelectorGrou
|
|||||||
}
|
}
|
||||||
|
|
||||||
didSet {
|
didSet {
|
||||||
|
setItemsActions()
|
||||||
for selector in items {
|
for selector in items {
|
||||||
selector.onClick = { [weak self] handler in
|
|
||||||
self?.didSelect(handler)
|
|
||||||
self?.setNeedsUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
selector.accessibilityAction = { [weak self] handler in
|
|
||||||
guard let handler = handler as? SelectorItemType else { return }
|
|
||||||
self?.didSelect(handler)
|
|
||||||
self?.setNeedsUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
mainStackView.addArrangedSubview(selector)
|
mainStackView.addArrangedSubview(selector)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open var onChangeSubscriber: AnyCancellable?
|
open var onChangeSubscriber: AnyCancellable?
|
||||||
|
|
||||||
|
private func setItemsActions() {
|
||||||
|
for selector in items {
|
||||||
|
selector.onClick = { [weak self] handler in
|
||||||
|
self?.didSelect(handler)
|
||||||
|
self?.setNeedsUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
selector.accessibilityAction = { [weak self] handler in
|
||||||
|
guard let handler = handler as? SelectorItemType else { return }
|
||||||
|
self?.didSelect(handler)
|
||||||
|
self?.setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether the Control is enabled or not.
|
/// Whether the Control is enabled or not.
|
||||||
override open var isEnabled: Bool {
|
override open var isEnabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
@ -115,6 +120,11 @@ open class SelectorGroupBase<SelectorItemType: Groupable>: Control, SelectorGrou
|
|||||||
.pinBottom(0, .defaultHigh)
|
.pinBottom(0, .defaultHigh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
onChange = nil
|
||||||
|
}
|
||||||
|
|
||||||
/// Handler for the Group to override on a select event.
|
/// Handler for the Group to override on a select event.
|
||||||
/// - Parameter selectedControl: Selected Control the user interacted.
|
/// - Parameter selectedControl: Selected Control the user interacted.
|
||||||
open func didSelect(_ selectedControl: SelectorItemType) {
|
open func didSelect(_ selectedControl: SelectorItemType) {
|
||||||
@ -131,8 +141,8 @@ open class SelectorGroupBase<SelectorItemType: Groupable>: Control, SelectorGrou
|
|||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
open override func reset() {
|
open override func reset() {
|
||||||
super.reset()
|
super.reset()
|
||||||
onChange = nil
|
|
||||||
items.forEach{ $0.reset() }
|
items.forEach{ $0.reset() }
|
||||||
|
setItemsActions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -66,18 +66,21 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
|
|||||||
/// Label used to render labelText.
|
/// Label used to render labelText.
|
||||||
open var label = Label().with {
|
open var label = Label().with {
|
||||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
$0.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
$0.textStyle = .boldBodyLarge
|
$0.textStyle = .boldBodyLarge
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Label used to render childText.
|
/// Label used to render childText.
|
||||||
open var childLabel = Label().with {
|
open var childLabel = Label().with {
|
||||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
$0.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
$0.textStyle = .bodyLarge
|
$0.textStyle = .bodyLarge
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Label used to render errorText.
|
/// Label used to render errorText.
|
||||||
open var errorLabel = Label().with {
|
open var errorLabel = Label().with {
|
||||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
$0.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
$0.textStyle = .bodyMedium
|
$0.textStyle = .bodyMedium
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,9 +160,32 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Executed on initialization for this View.
|
|
||||||
open override func initialSetup() {
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||||
super.initialSetup()
|
open override func setup() {
|
||||||
|
super.setup()
|
||||||
|
|
||||||
|
selectorView.isAccessibilityElement = true
|
||||||
|
isAccessibilityElement = false
|
||||||
|
addSubview(mainStackView)
|
||||||
|
|
||||||
|
mainStackView.isUserInteractionEnabled = false
|
||||||
|
mainStackView.addArrangedSubview(selectorStackView)
|
||||||
|
mainStackView.addArrangedSubview(errorLabel)
|
||||||
|
selectorStackView.addArrangedSubview(selectorView)
|
||||||
|
selectorStackView.addArrangedSubview(selectorLabelStackView)
|
||||||
|
selectorLabelStackView.addArrangedSubview(label)
|
||||||
|
selectorLabelStackView.addArrangedSubview(childLabel)
|
||||||
|
mainStackView
|
||||||
|
.pinTop()
|
||||||
|
.pinLeading()
|
||||||
|
.pinTrailing()
|
||||||
|
.pinBottom(0, .defaultHigh)
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
|
||||||
onClick = { [weak self] control in
|
onClick = { [weak self] control in
|
||||||
guard let self, isEnabled else { return }
|
guard let self, isEnabled else { return }
|
||||||
toggle()
|
toggle()
|
||||||
@ -203,29 +229,23 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
|
|||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
return !isEnabled ? "" : "Double tap to activate."
|
return !isEnabled ? "" : "Double tap to activate."
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
label.textStyle = .boldBodyLarge
|
||||||
open override func setup() {
|
childLabel.textStyle = .bodyLarge
|
||||||
super.setup()
|
errorLabel.textStyle = .bodyMedium
|
||||||
|
|
||||||
selectorView.isAccessibilityElement = true
|
labelText = nil
|
||||||
isAccessibilityElement = false
|
labelTextAttributes = nil
|
||||||
addSubview(mainStackView)
|
labelAttributedText = nil
|
||||||
|
childText = nil
|
||||||
|
childTextAttributes = nil
|
||||||
|
childAttributedText = nil
|
||||||
|
showError = false
|
||||||
|
errorText = nil
|
||||||
|
inputId = nil
|
||||||
|
isSelected = false
|
||||||
|
|
||||||
mainStackView.isUserInteractionEnabled = false
|
onChange = nil
|
||||||
mainStackView.addArrangedSubview(selectorStackView)
|
|
||||||
mainStackView.addArrangedSubview(errorLabel)
|
|
||||||
selectorStackView.addArrangedSubview(selectorView)
|
|
||||||
selectorStackView.addArrangedSubview(selectorLabelStackView)
|
|
||||||
selectorLabelStackView.addArrangedSubview(label)
|
|
||||||
selectorLabelStackView.addArrangedSubview(childLabel)
|
|
||||||
mainStackView
|
|
||||||
.pinTop()
|
|
||||||
.pinLeading()
|
|
||||||
.pinTrailing()
|
|
||||||
.pinBottom(0, .defaultHigh)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
@ -281,30 +301,10 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
|
|||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
open override func reset() {
|
open override func reset() {
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
label.reset()
|
label.reset()
|
||||||
childLabel.reset()
|
childLabel.reset()
|
||||||
errorLabel.reset()
|
errorLabel.reset()
|
||||||
|
super.reset()
|
||||||
label.textStyle = .boldBodyLarge
|
|
||||||
childLabel.textStyle = .bodyLarge
|
|
||||||
errorLabel.textStyle = .bodyMedium
|
|
||||||
|
|
||||||
labelText = nil
|
|
||||||
labelTextAttributes = nil
|
|
||||||
labelAttributedText = nil
|
|
||||||
childText = nil
|
|
||||||
childTextAttributes = nil
|
|
||||||
childAttributedText = nil
|
|
||||||
showError = false
|
|
||||||
errorText = nil
|
|
||||||
inputId = nil
|
|
||||||
isSelected = false
|
|
||||||
|
|
||||||
onChange = nil
|
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import Combine
|
|||||||
/// Base Class used to build Views.
|
/// Base Class used to build Views.
|
||||||
@objcMembers
|
@objcMembers
|
||||||
@objc(VDSView)
|
@objc(VDSView)
|
||||||
open class View: UIView, ViewProtocol, UserInfoable {
|
open class View: UIView, ViewProtocol, UserInfoable, Clickable {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
@ -37,6 +37,7 @@ open class View: UIView, ViewProtocol, UserInfoable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open var subscribers = Set<AnyCancellable>()
|
open var subscribers = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
open var onClickSubscriber: AnyCancellable?
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -57,20 +58,30 @@ open class View: UIView, ViewProtocol, UserInfoable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open func initialSetup() {
|
private func initialSetup() {
|
||||||
if !initialSetupPerformed {
|
if !initialSetupPerformed {
|
||||||
initialSetupPerformed = true
|
initialSetupPerformed = true
|
||||||
|
shouldUpdateView = false
|
||||||
setup()
|
setup()
|
||||||
|
setDefaults()
|
||||||
|
shouldUpdateView = true
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open func setup() {
|
open func setup() {
|
||||||
backgroundColor = .clear
|
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
insetsLayoutMarginsFromSafeArea = false
|
insetsLayoutMarginsFromSafeArea = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open func setDefaults() {
|
||||||
|
backgroundColor = .clear
|
||||||
|
surface = .light
|
||||||
|
isEnabled = true
|
||||||
|
onClick = nil
|
||||||
|
userInfo.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
open func updateView() { }
|
open func updateView() { }
|
||||||
|
|
||||||
open func updateAccessibility() {
|
open func updateAccessibility() {
|
||||||
@ -82,9 +93,10 @@ open class View: UIView, ViewProtocol, UserInfoable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func reset() {
|
open func reset() {
|
||||||
backgroundColor = .clear
|
shouldUpdateView = false
|
||||||
surface = .light
|
setDefaults()
|
||||||
isEnabled = true
|
shouldUpdateView = true
|
||||||
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func layoutSubviews() {
|
open override func layoutSubviews() {
|
||||||
|
|||||||
@ -149,25 +149,28 @@ open class Badge: View {
|
|||||||
maxWidthConstraint = label.widthLessThanEqualTo(constant: 0).with { $0.isActive = false }
|
maxWidthConstraint = label.widthLessThanEqualTo(constant: 0).with { $0.isActive = false }
|
||||||
clipsToBounds = true
|
clipsToBounds = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
|
||||||
bridge_accessibilityLabelBlock = { [weak self] in
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
label.reset()
|
|
||||||
label.lineBreakMode = .byTruncatingTail
|
label.lineBreakMode = .byTruncatingTail
|
||||||
label.textStyle = .boldBodySmall
|
label.textStyle = .boldBodySmall
|
||||||
fillColor = .red
|
fillColor = .red
|
||||||
text = ""
|
text = ""
|
||||||
maxWidth = nil
|
maxWidth = nil
|
||||||
numberOfLines = 1
|
numberOfLines = 1
|
||||||
shouldUpdateView = true
|
}
|
||||||
setNeedsUpdate()
|
|
||||||
|
/// Resets to default settings.
|
||||||
|
open override func reset() {
|
||||||
|
label.reset()
|
||||||
|
super.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
|||||||
@ -305,17 +305,31 @@ open class BadgeIndicator: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
open override func setDefaults() {
|
||||||
open override func reset() {
|
super.setDefaults()
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
label.reset()
|
|
||||||
label.lineBreakMode = .byTruncatingTail
|
label.lineBreakMode = .byTruncatingTail
|
||||||
label.textAlignment = .center
|
label.textAlignment = .center
|
||||||
fillColor = .red
|
fillColor = .red
|
||||||
number = nil
|
number = nil
|
||||||
shouldUpdateView = true
|
kind = .simple
|
||||||
setNeedsUpdate()
|
leadingCharacter = nil
|
||||||
|
trailingText = nil
|
||||||
|
size = .xxlarge
|
||||||
|
dotSize = nil
|
||||||
|
verticalPadding = nil
|
||||||
|
horizontalPadding = nil
|
||||||
|
hideDot = false
|
||||||
|
hideBorder = false
|
||||||
|
width = nil
|
||||||
|
height = nil
|
||||||
|
accessibilityText = nil
|
||||||
|
maximumDigits = .two
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets to default settings.
|
||||||
|
open override func reset() {
|
||||||
|
label.reset()
|
||||||
|
super.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
|||||||
@ -72,17 +72,15 @@ open class BreadcrumbItem: ButtonBase {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
open override func setDefaults() {
|
||||||
open override func setup() {
|
super.setDefaults()
|
||||||
super.setup()
|
isAccessibilityElement = true
|
||||||
|
accessibilityTraits = .link
|
||||||
|
|
||||||
titleLabel?.numberOfLines = 0
|
titleLabel?.numberOfLines = 0
|
||||||
titleLabel?.lineBreakMode = .byWordWrapping
|
titleLabel?.lineBreakMode = .byWordWrapping
|
||||||
contentHorizontalAlignment = .left
|
contentHorizontalAlignment = .left
|
||||||
|
|
||||||
isAccessibilityElement = true
|
|
||||||
accessibilityTraits = .link
|
|
||||||
|
|
||||||
bridge_accessibilityHintBlock = { [weak self] in
|
bridge_accessibilityHintBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
return !isEnabled ? "" : "Double tap to open."
|
return !isEnabled ? "" : "Double tap to open."
|
||||||
@ -131,17 +129,4 @@ open class BreadcrumbItem: ButtonBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
text = nil
|
|
||||||
accessibilityCustomActions = []
|
|
||||||
isAccessibilityElement = true
|
|
||||||
accessibilityTraits = .button
|
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -109,21 +109,18 @@ open class Breadcrumbs: View {
|
|||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Executed on initialization for this View.
|
/// Executed on initialization for this View.
|
||||||
open override func initialSetup() {
|
open override func setup() {
|
||||||
super.initialSetup()
|
super.setup()
|
||||||
containerView.addSubview(collectionView)
|
containerView.addSubview(collectionView)
|
||||||
collectionView.pinToSuperView()
|
collectionView.pinToSuperView()
|
||||||
addSubview(containerView)
|
addSubview(containerView)
|
||||||
containerView.pinToSuperView()
|
containerView.pinToSuperView()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
open override func reset() {
|
open override func reset() {
|
||||||
super.reset()
|
super.reset()
|
||||||
shouldUpdateView = false
|
|
||||||
breadcrumbs.forEach { $0.reset() }
|
breadcrumbs.forEach { $0.reset() }
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
|||||||
@ -223,16 +223,12 @@ open class Button: ButtonBase, Useable {
|
|||||||
isAccessibilityElement = true
|
isAccessibilityElement = true
|
||||||
accessibilityTraits = .button
|
accessibilityTraits = .button
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
open override func setDefaults() {
|
||||||
open override func reset() {
|
super.setDefaults()
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
use = .primary
|
use = .primary
|
||||||
width = nil
|
width = nil
|
||||||
size = .large
|
size = .large
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
|||||||
@ -97,13 +97,13 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open func initialSetup() {
|
private func initialSetup() {
|
||||||
if !initialSetupPerformed {
|
if !initialSetupPerformed {
|
||||||
initialSetupPerformed = true
|
initialSetupPerformed = true
|
||||||
backgroundColor = .clear
|
shouldUpdateView = false
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
accessibilityCustomActions = []
|
|
||||||
setup()
|
setup()
|
||||||
|
setDefaults()
|
||||||
|
shouldUpdateView = true
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,10 +111,19 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
|
|||||||
|
|
||||||
open func setup() {
|
open func setup() {
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
}
|
||||||
|
|
||||||
|
open func setDefaults() {
|
||||||
|
backgroundColor = .clear
|
||||||
|
accessibilityCustomActions = []
|
||||||
titleLabel?.adjustsFontSizeToFitWidth = false
|
titleLabel?.adjustsFontSizeToFitWidth = false
|
||||||
titleLabel?.lineBreakMode = .byTruncatingTail
|
titleLabel?.lineBreakMode = .byTruncatingTail
|
||||||
titleLabel?.numberOfLines = 1
|
titleLabel?.numberOfLines = 1
|
||||||
|
surface = .light
|
||||||
|
isEnabled = true
|
||||||
|
text = nil
|
||||||
|
onClick = nil
|
||||||
|
userInfo.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
open func updateView() {
|
open func updateView() {
|
||||||
@ -131,12 +140,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
|
|||||||
|
|
||||||
open func reset() {
|
open func reset() {
|
||||||
shouldUpdateView = false
|
shouldUpdateView = false
|
||||||
surface = .light
|
setDefaults()
|
||||||
isEnabled = true
|
|
||||||
text = nil
|
|
||||||
accessibilityCustomActions = []
|
|
||||||
onClick = nil
|
|
||||||
userInfo.removeAll()
|
|
||||||
shouldUpdateView = true
|
shouldUpdateView = true
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -167,15 +167,17 @@ open class ButtonGroup: View {
|
|||||||
collectionView.reloadData()
|
collectionView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func reset() {
|
open override func setDefaults() {
|
||||||
super.reset()
|
super.setDefaults()
|
||||||
shouldUpdateView = false
|
|
||||||
rowQuantityPhone = 0
|
rowQuantityPhone = 0
|
||||||
rowQuantityTablet = 0
|
rowQuantityTablet = 0
|
||||||
alignment = .center
|
alignment = .center
|
||||||
|
childWidth = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func reset() {
|
||||||
buttons.forEach { $0.reset() }
|
buttons.forEach { $0.reset() }
|
||||||
shouldUpdateView = true
|
super.reset()
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func layoutSubviews() {
|
open override func layoutSubviews() {
|
||||||
|
|||||||
@ -91,12 +91,7 @@ open class TextLink: ButtonBase {
|
|||||||
open override func setup() {
|
open override func setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
isAccessibilityElement = true
|
isAccessibilityElement = true
|
||||||
accessibilityTraits = .link
|
|
||||||
|
|
||||||
//left align titleLabel in case this is pinned leading/trailing
|
|
||||||
//default is always set to center
|
|
||||||
contentHorizontalAlignment = .left
|
|
||||||
|
|
||||||
if let titleLabel {
|
if let titleLabel {
|
||||||
addSubview(line)
|
addSubview(line)
|
||||||
line.pinLeading(titleLabel.leadingAnchor)
|
line.pinLeading(titleLabel.leadingAnchor)
|
||||||
@ -106,12 +101,21 @@ open class TextLink: ButtonBase {
|
|||||||
lineHeightConstraint = line.height(constant: 1)
|
lineHeightConstraint = line.height(constant: 1)
|
||||||
lineHeightConstraint?.isActive = true
|
lineHeightConstraint?.isActive = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
size = .large
|
||||||
|
accessibilityTraits = .link
|
||||||
|
|
||||||
|
//left align titleLabel in case this is pinned leading/trailing
|
||||||
|
//default is always set to center
|
||||||
|
contentHorizontalAlignment = .left
|
||||||
|
|
||||||
bridge_accessibilityHintBlock = { [weak self] in
|
bridge_accessibilityHintBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
return !isEnabled ? "" : "Double tap to open."
|
return !isEnabled ? "" : "Double tap to open."
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
@ -123,18 +127,5 @@ open class TextLink: ButtonBase {
|
|||||||
//always call last so the label is rendered
|
//always call last so the label is rendered
|
||||||
super.updateView()
|
super.updateView()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
text = nil
|
|
||||||
size = .large
|
|
||||||
accessibilityCustomActions = []
|
|
||||||
isAccessibilityElement = true
|
|
||||||
accessibilityTraits = .link
|
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,10 +76,8 @@ open class TextLinkCaret: ButtonBase {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
open override func setDefaults() {
|
||||||
open override func setup() {
|
super.setDefaults()
|
||||||
super.setup()
|
|
||||||
|
|
||||||
//left align titleLabel in case this is pinned leading/trailing
|
//left align titleLabel in case this is pinned leading/trailing
|
||||||
//default is always set to center
|
//default is always set to center
|
||||||
contentHorizontalAlignment = .left
|
contentHorizontalAlignment = .left
|
||||||
@ -88,11 +86,12 @@ open class TextLinkCaret: ButtonBase {
|
|||||||
titleLabel?.numberOfLines = 0
|
titleLabel?.numberOfLines = 0
|
||||||
titleLabel?.lineBreakMode = .byWordWrapping
|
titleLabel?.lineBreakMode = .byWordWrapping
|
||||||
|
|
||||||
|
iconPosition = .right
|
||||||
|
|
||||||
bridge_accessibilityHintBlock = { [weak self] in
|
bridge_accessibilityHintBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
return !isEnabled ? "" : "Double tap to open."
|
return !isEnabled ? "" : "Double tap to open."
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
@ -100,14 +99,7 @@ open class TextLinkCaret: ButtonBase {
|
|||||||
imageAttribute = CaretLabelAttribute(tintColor: textColor, position: iconPosition)
|
imageAttribute = CaretLabelAttribute(tintColor: textColor, position: iconPosition)
|
||||||
super.updateView()
|
super.updateView()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
iconPosition = .right
|
|
||||||
text = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The natural size for the receiving view, considering only properties of the view itself.
|
/// The natural size for the receiving view, considering only properties of the view itself.
|
||||||
open override var intrinsicContentSize: CGSize {
|
open override var intrinsicContentSize: CGSize {
|
||||||
guard let titleLabel else { return super.intrinsicContentSize }
|
guard let titleLabel else { return super.intrinsicContentSize }
|
||||||
|
|||||||
@ -125,10 +125,6 @@ open class CalendarBase: Control, Changeable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open override func initialSetup() {
|
|
||||||
super.initialSetup()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||||
open override func setup() {
|
open override func setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
@ -154,6 +150,19 @@ open class CalendarBase: Control, Changeable {
|
|||||||
|
|
||||||
collectionView.pinCenterX(anchor: containerView.centerXAnchor)
|
collectionView.pinCenterX(anchor: containerView.centerXAnchor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
hideContainerBorder = false
|
||||||
|
hideCurrentDateIndicator = false
|
||||||
|
transparentBackground = false
|
||||||
|
activeDates = []
|
||||||
|
inactiveDates = []
|
||||||
|
indicators = []
|
||||||
|
minDate = Date()
|
||||||
|
maxDate = Date()
|
||||||
|
selectedDate = Date()
|
||||||
|
}
|
||||||
|
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
@ -174,17 +183,6 @@ open class CalendarBase: Control, Changeable {
|
|||||||
containerView.layer.borderWidth = VDSFormControls.borderWidth
|
containerView.layer.borderWidth = VDSFormControls.borderWidth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
hideContainerBorder = false
|
|
||||||
hideCurrentDateIndicator = false
|
|
||||||
transparentBackground = false
|
|
||||||
activeDates = []
|
|
||||||
inactiveDates = []
|
|
||||||
indicators = []
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
|
|||||||
@ -196,11 +196,6 @@ open class Carousel: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Executed on initialization for this View.
|
|
||||||
open override func initialSetup() {
|
|
||||||
super.initialSetup()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||||
open override func setup() {
|
open override func setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
@ -249,6 +244,18 @@ open class Carousel: View {
|
|||||||
updatePaginationInset()
|
updatePaginationInset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
gutter = UIDevice.isIPad ? .gutter6X : .gutter3X
|
||||||
|
layout = UIDevice.isIPad ? .threeUP : .oneUP
|
||||||
|
onChange = nil
|
||||||
|
pagination = .init(kind: .lowContrast, floating: true)
|
||||||
|
paginationDisplay = .none
|
||||||
|
paginationInset = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X
|
||||||
|
peek = .standard
|
||||||
|
groupIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
@ -279,19 +286,7 @@ open class Carousel: View {
|
|||||||
updatePaginationControls()
|
updatePaginationControls()
|
||||||
addCarouselSlots()
|
addCarouselSlots()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
layout = UIDevice.isIPad ? .threeUP : .oneUP
|
|
||||||
pagination = .init(kind: .lowContrast, floating: true)
|
|
||||||
paginationDisplay = .none
|
|
||||||
paginationInset = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X
|
|
||||||
gutter = UIDevice.isIPad ? .gutter6X : .gutter3X
|
|
||||||
peek = .standard
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -235,10 +235,6 @@ open class CarouselScrollbar: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open override func initialSetup() {
|
|
||||||
super.initialSetup()
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func setup() {
|
open override func setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
isAccessibilityElement = false
|
isAccessibilityElement = false
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import Combine
|
|||||||
/// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection.
|
/// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection.
|
||||||
@objcMembers
|
@objcMembers
|
||||||
@objc(VDSDatePicker)
|
@objc(VDSDatePicker)
|
||||||
open class DatePicker: EntryFieldBase {
|
open class DatePicker: EntryFieldBase<String> {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -154,15 +154,12 @@ open class DatePicker: EntryFieldBase {
|
|||||||
selectedDateLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
|
selectedDateLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
|
||||||
|
|
||||||
// tap gesture
|
// tap gesture
|
||||||
containerView
|
containerView.onClick = { [weak self] _ in
|
||||||
.publisher(for: UITapGestureRecognizer())
|
guard let self else { return }
|
||||||
.sink { [weak self] _ in
|
if isEnabled && !isReadOnly {
|
||||||
guard let self else { return }
|
showPopover()
|
||||||
if isEnabled && !isReadOnly {
|
|
||||||
showPopover()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.store(in: &subscribers)
|
}
|
||||||
|
|
||||||
NotificationCenter.default
|
NotificationCenter.default
|
||||||
.publisher(for: UIDevice.orientationDidChangeNotification).sink { [weak self] _ in
|
.publisher(for: UIDevice.orientationDidChangeNotification).sink { [weak self] _ in
|
||||||
@ -174,6 +171,14 @@ open class DatePicker: EntryFieldBase {
|
|||||||
popoverOverlayView.isHidden = true
|
popoverOverlayView.isHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
selectedDate = nil
|
||||||
|
calendarModel = .init()
|
||||||
|
dateFormat = .shortNumeric
|
||||||
|
selectedDateLabel.textStyle = .bodyLarge
|
||||||
|
}
|
||||||
|
|
||||||
open override func getFieldContainer() -> UIView {
|
open override func getFieldContainer() -> UIView {
|
||||||
// stackview for controls in EntryFieldBase.controlContainerView
|
// stackview for controls in EntryFieldBase.controlContainerView
|
||||||
let controlStackView = UIStackView().with {
|
let controlStackView = UIStackView().with {
|
||||||
@ -200,12 +205,6 @@ open class DatePicker: EntryFieldBase {
|
|||||||
calendarIcon.color = iconColorConfiguration.getColor(self)
|
calendarIcon.color = iconColorConfiguration.getColor(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
selectedDateLabel.textStyle = .bodyLarge
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func formatDate(_ date: Date) {
|
internal func formatDate(_ date: Date) {
|
||||||
let formatter = DateFormatter()
|
let formatter = DateFormatter()
|
||||||
formatter.dateFormat = dateFormat.format
|
formatter.dateFormat = dateFormat.format
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import Combine
|
|||||||
/// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection.
|
/// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection.
|
||||||
@objcMembers
|
@objcMembers
|
||||||
@objc(VDSDropdownSelect)
|
@objc(VDSDropdownSelect)
|
||||||
open class DropdownSelect: EntryFieldBase {
|
open class DropdownSelect: EntryFieldBase<String> {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -153,15 +153,23 @@ open class DropdownSelect: EntryFieldBase {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// tap gesture
|
// tap gesture
|
||||||
containerView
|
containerView.onClick = { [weak self] _ in
|
||||||
.publisher(for: UITapGestureRecognizer())
|
self?.launchPicker()
|
||||||
.sink { [weak self] _ in
|
}
|
||||||
self?.launchPicker()
|
|
||||||
}
|
|
||||||
.store(in: &subscribers)
|
|
||||||
containerView.height(44)
|
containerView.height(44)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
showInlineLabel = false
|
||||||
|
selectId = nil
|
||||||
|
inlineDisplayLabel.textStyle = .boldBodyLarge
|
||||||
|
selectedOptionLabel.textStyle = .bodyLarge
|
||||||
|
showInlineLabel = false
|
||||||
|
options = []
|
||||||
|
selectId = nil
|
||||||
|
}
|
||||||
|
|
||||||
open override func getFieldContainer() -> UIView {
|
open override func getFieldContainer() -> UIView {
|
||||||
let controlStackView = UIStackView().with {
|
let controlStackView = UIStackView().with {
|
||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
@ -188,17 +196,6 @@ open class DropdownSelect: EntryFieldBase {
|
|||||||
selectedOptionLabel.surface = surface
|
selectedOptionLabel.surface = surface
|
||||||
selectedOptionLabel.isEnabled = isEnabled
|
selectedOptionLabel.isEnabled = isEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
|
|
||||||
inlineDisplayLabel.textStyle = .boldBodyLarge
|
|
||||||
selectedOptionLabel.textStyle = .bodyLarge
|
|
||||||
showInlineLabel = false
|
|
||||||
options = []
|
|
||||||
selectId = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Methods
|
// MARK: - Public Methods
|
||||||
|
|||||||
@ -402,17 +402,36 @@ open class ButtonIcon: Control, Changeable {
|
|||||||
centerXConstraint?.activate()
|
centerXConstraint?.activate()
|
||||||
centerYConstraint = icon.centerYAnchor.constraint(equalTo: iconLayoutGuide.centerYAnchor, constant: 0)
|
centerYConstraint = icon.centerYAnchor.constraint(equalTo: iconLayoutGuide.centerYAnchor, constant: 0)
|
||||||
centerYConstraint?.activate()
|
centerYConstraint?.activate()
|
||||||
|
|
||||||
|
publisher(for: .touchUpInside)
|
||||||
|
.sink(receiveValue: { [weak self] _ in
|
||||||
|
guard let self, isEnabled,
|
||||||
|
selectedIconName != nil,
|
||||||
|
selectable else { return }
|
||||||
|
toggle()
|
||||||
|
})
|
||||||
|
.store(in: &subscribers)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executed on initialization for this View.
|
open override func setDefaults() {
|
||||||
open override func initialSetup() {
|
super.setDefaults()
|
||||||
super.initialSetup()
|
badgeIndicatorModel = nil
|
||||||
onClick = { control in
|
kind = .ghost
|
||||||
guard control.isEnabled else { return }
|
surfaceType = .colorFill
|
||||||
if control.selectedIconName != nil && control.selectable {
|
iconName = nil
|
||||||
control.toggle()
|
selectedIconName = nil
|
||||||
}
|
selectedIconColorConfiguration = nil
|
||||||
}
|
size = .large
|
||||||
|
floating = false
|
||||||
|
fitToIcon = false
|
||||||
|
hideBorder = true
|
||||||
|
showBadgeIndicator = false
|
||||||
|
selectable = false
|
||||||
|
iconOffset = .init(x: 0, y: 0)
|
||||||
|
customContainerSize = nil
|
||||||
|
customIconSize = nil
|
||||||
|
customBadgeIndicatorOffset = nil
|
||||||
|
onChange = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This will change the state of the Selector and execute the actionBlock if provided.
|
/// This will change the state of the Selector and execute the actionBlock if provided.
|
||||||
@ -422,26 +441,6 @@ open class ButtonIcon: Control, Changeable {
|
|||||||
sendActions(for: .valueChanged)
|
sendActions(for: .valueChanged)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
kind = .ghost
|
|
||||||
surfaceType = .colorFill
|
|
||||||
size = .large
|
|
||||||
floating = false
|
|
||||||
hideBorder = true
|
|
||||||
iconOffset = .init(x: 0, y: 0)
|
|
||||||
iconName = nil
|
|
||||||
selectedIconName = nil
|
|
||||||
showBadgeIndicator = false
|
|
||||||
selectable = false
|
|
||||||
badgeIndicatorModel = nil
|
|
||||||
onChange = nil
|
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
|
|||||||
@ -90,22 +90,29 @@ open class Icon: View {
|
|||||||
|
|
||||||
addSubview(imageView)
|
addSubview(imageView)
|
||||||
imageView.pinToSuperView()
|
imageView.pinToSuperView()
|
||||||
|
|
||||||
backgroundColor = .clear
|
|
||||||
|
|
||||||
isAccessibilityElement = true
|
isAccessibilityElement = true
|
||||||
accessibilityTraits = .none
|
accessibilityTraits = .none
|
||||||
accessibilityHint = "image"
|
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
backgroundColor = .clear
|
||||||
|
color = VDSColor.paletteBlack
|
||||||
|
size = .medium
|
||||||
|
name = nil
|
||||||
|
customSize = nil
|
||||||
|
imageView.image = nil
|
||||||
|
|
||||||
|
accessibilityHint = "image"
|
||||||
bridge_accessibilityLabelBlock = { [weak self] in
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
return name?.rawValue ?? "icon"
|
return name?.rawValue ?? "icon"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
@ -123,12 +130,6 @@ open class Icon: View {
|
|||||||
invalidateIntrinsicContentSize()
|
invalidateIntrinsicContentSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
color = VDSColor.paletteBlack
|
|
||||||
imageView.image = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UIImage {
|
extension UIImage {
|
||||||
|
|||||||
422
VDS/Components/InputStepper/InputStepper.swift
Normal file
422
VDS/Components/InputStepper/InputStepper.swift
Normal file
@ -0,0 +1,422 @@
|
|||||||
|
//
|
||||||
|
// InputStepper.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Kanamarlapudi, Vasavi on 24/06/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import VDSCoreTokens
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
/// A stepper is a two-segment control that people use to increase or decrease an incremental value.'
|
||||||
|
@objcMembers
|
||||||
|
@objc(VDSInputStepper)
|
||||||
|
open class InputStepper: EntryFieldBase<Int> {
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Initializers
|
||||||
|
//--------------------------------------------------
|
||||||
|
required public init() {
|
||||||
|
super.init(frame: .zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override init(frame: CGRect) {
|
||||||
|
super.init(frame: .zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Enums
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// Enum used to describe the size of Input Stepper.
|
||||||
|
public enum Size: String, CaseIterable {
|
||||||
|
case large, small
|
||||||
|
|
||||||
|
var minWidth: CGFloat {
|
||||||
|
self == .large ? 121 : 90
|
||||||
|
}
|
||||||
|
|
||||||
|
var minHeight: CGFloat {
|
||||||
|
self == .large ? 44 : 32
|
||||||
|
}
|
||||||
|
|
||||||
|
var space: CGFloat {
|
||||||
|
self == .large ? VDSLayout.space3X : VDSLayout.space2X
|
||||||
|
}
|
||||||
|
var padding: CGFloat {
|
||||||
|
self == .large ? 6.0 : VDSLayout.space1X
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttonContainerSize: Int {
|
||||||
|
self == .large ? 32 : 24
|
||||||
|
}
|
||||||
|
|
||||||
|
var textStyle: TextStyle {
|
||||||
|
self == .large ? .boldBodyLarge : .boldBodySmall
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum used to describe the width of a fixed value or percentage of the input stepper control.
|
||||||
|
public enum ControlWidth {
|
||||||
|
case percentage(CGFloat)
|
||||||
|
case value(CGFloat)
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Public Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// If there is a width that is larger than this size's minimumWidth, the input stepper will resize to this width.
|
||||||
|
open var controlWidth: ControlWidth? {
|
||||||
|
get { _controlWidth }
|
||||||
|
set {
|
||||||
|
if let newValue {
|
||||||
|
switch newValue {
|
||||||
|
case .percentage(let percentage):
|
||||||
|
if percentage <= 100.0 {
|
||||||
|
_controlWidth = newValue
|
||||||
|
}
|
||||||
|
case .value(let value):
|
||||||
|
if value > 0 && value > containerSize.width {
|
||||||
|
_controlWidth = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_controlWidth = nil
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accepts percentage value to width of parent container.
|
||||||
|
open var widthPercentage: CGFloat? {
|
||||||
|
didSet {
|
||||||
|
if let percentage = widthPercentage, percentage > 100 {
|
||||||
|
widthPercentage = 100
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _defaultValue: Int = 0
|
||||||
|
open override var defaultValue: Int? {
|
||||||
|
get { _defaultValue }
|
||||||
|
set {
|
||||||
|
if let newValue {
|
||||||
|
_defaultValue = newValue > maxValue ? maxValue : newValue < minValue ? minValue : newValue
|
||||||
|
} else {
|
||||||
|
_defaultValue = 0
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override var value: Int? { return defaultValue }
|
||||||
|
|
||||||
|
/// Maximum value of the input stepper, defaults to '99'.
|
||||||
|
lazy open var maxValue: Int = { _defaultMaxValue }() {
|
||||||
|
didSet {
|
||||||
|
if maxValue > _defaultMaxValue || maxValue < _defaultMinValue && maxValue > minValue {
|
||||||
|
maxValue = _defaultMaxValue
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Minimum value of the input stepper, defaults to '0'.
|
||||||
|
lazy open var minValue: Int = { _defaultMinValue }() {
|
||||||
|
didSet {
|
||||||
|
if minValue < _defaultMinValue && minValue >= _defaultMaxValue && minValue < maxValue {
|
||||||
|
minValue = _defaultMinValue
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The size of the input stepper. Defaults to 'large'.
|
||||||
|
open var size: Size = .large { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// Accepts any text or character to appear next to input stepper value.
|
||||||
|
open var trailingText: String? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
private var _controlWidth: ControlWidth? = nil
|
||||||
|
private var _defaultMinValue: Int = 0
|
||||||
|
private var _defaultMaxValue: Int = 99
|
||||||
|
|
||||||
|
/// This is the view that will be wrapped with the border for userInteraction.
|
||||||
|
/// The only subview of this view is the stepperStackView.
|
||||||
|
internal var stepperContainerView = View().with {
|
||||||
|
$0.isAccessibilityElement = true
|
||||||
|
$0.accessibilityLabel = "Input Stepper"
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var stepperStackView = UIStackView().with {
|
||||||
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
$0.axis = .horizontal
|
||||||
|
$0.distribution = .fill
|
||||||
|
$0.alignment = .fill
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var decrementButton = ButtonIcon().with {
|
||||||
|
$0.kind = .ghost
|
||||||
|
$0.iconName = Icon.Name(name: "minus")
|
||||||
|
$0.iconOffset = .init(x: -2, y: 0)
|
||||||
|
$0.customContainerSize = 32
|
||||||
|
$0.icon.customSize = 16
|
||||||
|
$0.backgroundColor = .clear
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var incrementButton = ButtonIcon().with {
|
||||||
|
$0.kind = .ghost
|
||||||
|
$0.iconName = Icon.Name(name: "plus")
|
||||||
|
$0.iconOffset = .init(x: 2, y: 0)
|
||||||
|
$0.customContainerSize = 32
|
||||||
|
$0.icon.customSize = 16
|
||||||
|
$0.backgroundColor = .clear
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var textLabel = Label().with {
|
||||||
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
$0.textStyle = .boldBodyLarge
|
||||||
|
$0.numberOfLines = 1
|
||||||
|
$0.lineBreakMode = .byTruncatingTail
|
||||||
|
$0.textAlignment = .center
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Constraints
|
||||||
|
//--------------------------------------------------
|
||||||
|
internal var stepperWidthConstraint: NSLayoutConstraint?
|
||||||
|
internal var stepperHeightConstraint: NSLayoutConstraint?
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Configuration Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
internal override var containerSize: CGSize { CGSize(width: size.minWidth, height: size.minHeight) }
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||||
|
open override func setup() {
|
||||||
|
super.setup()
|
||||||
|
// Set initial states
|
||||||
|
defaultValue = 0
|
||||||
|
containerView.isEnabled = false
|
||||||
|
statusIcon.isHidden = true
|
||||||
|
|
||||||
|
//override the default settings since the containerView
|
||||||
|
//fieldStackView relationShip needs to be updated
|
||||||
|
//we are not applying spacing either in the edges since this
|
||||||
|
//is the view that will take place of the containerView for the
|
||||||
|
//design of the original "containerView". This will get refactored at
|
||||||
|
//some point.
|
||||||
|
fieldStackView.applyAlignment(.leading)
|
||||||
|
|
||||||
|
// Add listeners
|
||||||
|
decrementButton.onClick = { _ in self.decrementButtonClick() }
|
||||||
|
incrementButton.onClick = { _ in self.incrementButtonClick() }
|
||||||
|
|
||||||
|
// setting color config
|
||||||
|
textLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func getFieldContainer() -> UIView {
|
||||||
|
stepperStackView.addArrangedSubview(decrementButton)
|
||||||
|
stepperStackView.addArrangedSubview(textLabel)
|
||||||
|
stepperStackView.addArrangedSubview(incrementButton)
|
||||||
|
|
||||||
|
// Set space between decrement button, label, and increment button relative to input Stepper size.
|
||||||
|
stepperStackView.setCustomSpacing(size.space, after: decrementButton)
|
||||||
|
stepperStackView.setCustomSpacing(size.space, after: textLabel)
|
||||||
|
|
||||||
|
// stepperContainerView for controls in EntryFieldBase.controlContainerView
|
||||||
|
stepperContainerView.addSubview(stepperStackView)
|
||||||
|
|
||||||
|
// Update Edge insets relative to input Stepper size.
|
||||||
|
stepperStackView.pinToSuperView(.uniform(size.padding))
|
||||||
|
|
||||||
|
stepperWidthConstraint = stepperContainerView.width(constant: containerSize.width, priority: .required)
|
||||||
|
stepperHeightConstraint = stepperContainerView.height(constant: containerSize.height, priority: .required)
|
||||||
|
|
||||||
|
return stepperContainerView
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
open override func updateView() {
|
||||||
|
super.updateView()
|
||||||
|
statusIcon.isHidden = true
|
||||||
|
|
||||||
|
// Update label
|
||||||
|
textLabel.isEnabled = isEnabled
|
||||||
|
textLabel.surface = surface
|
||||||
|
textLabel.text = "\(_defaultValue) " + (trailingText ?? "")
|
||||||
|
textLabel.textStyle = size.textStyle
|
||||||
|
|
||||||
|
updateButtonStates()
|
||||||
|
}
|
||||||
|
|
||||||
|
open override var accessibilityElements: [Any]? {
|
||||||
|
get {
|
||||||
|
var elements = [Any]()
|
||||||
|
|
||||||
|
if !isReadOnly || isEnabled {
|
||||||
|
elements.append(contentsOf: [titleLabel, containerView, decrementButton, textLabel, incrementButton])
|
||||||
|
} else {
|
||||||
|
elements.append(contentsOf: [titleLabel, containerView, textLabel])
|
||||||
|
}
|
||||||
|
|
||||||
|
if showError {
|
||||||
|
if let errorText, !errorText.isEmpty {
|
||||||
|
elements.append(errorLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let helperText, !helperText.isEmpty {
|
||||||
|
elements.append(helperLabel)
|
||||||
|
}
|
||||||
|
return elements
|
||||||
|
}
|
||||||
|
|
||||||
|
set { super.accessibilityElements = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets to default settings.
|
||||||
|
open override func reset() {
|
||||||
|
super.reset()
|
||||||
|
textLabel.reset()
|
||||||
|
controlWidth = nil
|
||||||
|
widthPercentage = nil
|
||||||
|
defaultValue = 0
|
||||||
|
minValue = _defaultMinValue
|
||||||
|
maxValue = _defaultMaxValue
|
||||||
|
trailingText = nil
|
||||||
|
size = .large
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Overrides
|
||||||
|
//--------------------------------------------------
|
||||||
|
override func updateContainerView() {
|
||||||
|
//we are not calling super since we
|
||||||
|
//are using the fieldStackView as the "containerView"
|
||||||
|
//which will get the look/feel of the containerView.
|
||||||
|
//this will get refactored in the future.
|
||||||
|
fieldStackView.backgroundColor = containerBackgroundColor
|
||||||
|
fieldStackView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor
|
||||||
|
fieldStackView.layer.borderWidth = VDSFormControls.borderWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override func updateContainerWidth() {
|
||||||
|
//we are not calling super here since
|
||||||
|
//we are changing how the widths are getting calculated
|
||||||
|
//now by including a percentage.
|
||||||
|
|
||||||
|
defer {
|
||||||
|
fieldStackView.layer.cornerRadius = containerSize.height / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
stepperWidthConstraint?.deactivate()
|
||||||
|
widthConstraint?.deactivate()
|
||||||
|
trailingLessThanEqualsConstraint?.deactivate()
|
||||||
|
trailingEqualsConstraint?.deactivate()
|
||||||
|
|
||||||
|
var widthConstraintConstant: CGFloat?
|
||||||
|
|
||||||
|
if let widthPercentage, let superWidth = horizontalPinnedWidth() {
|
||||||
|
// test value vs minimum width and take the greater value
|
||||||
|
widthConstraintConstant = max(superWidth * (widthPercentage / 100), minWidth)
|
||||||
|
} else if let width, width >= minWidth, width <= maxWidth {
|
||||||
|
widthConstraintConstant = width
|
||||||
|
} else if let parentWidth = width, parentWidth >= maxWidth {
|
||||||
|
widthConstraintConstant = maxWidth
|
||||||
|
} else if let parentWidth = width, parentWidth <= minWidth {
|
||||||
|
widthConstraintConstant = minWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
if let widthConstraintConstant {
|
||||||
|
widthConstraint?.constant = widthConstraintConstant
|
||||||
|
widthConstraint?.activate()
|
||||||
|
trailingLessThanEqualsConstraint?.activate()
|
||||||
|
} else {
|
||||||
|
trailingEqualsConstraint?.activate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Edge insets if size changes applied.
|
||||||
|
stepperStackView.applyAlignment(.fill, edges: .uniform(size.padding))
|
||||||
|
|
||||||
|
// Update height if size changes applied.
|
||||||
|
stepperHeightConstraint?.constant = containerSize.height
|
||||||
|
|
||||||
|
//update the stepper's widthConstraint if
|
||||||
|
//controlWidth was set
|
||||||
|
guard let controlWidth else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the inputStepper's controlWidth based on percentage received relative to its parentView's frame.
|
||||||
|
let containerWidth: CGFloat = widthConstraintConstant ?? containerView.frame.size.width
|
||||||
|
var stepperWidthConstant: CGFloat?
|
||||||
|
var stepperWidth: CGFloat
|
||||||
|
|
||||||
|
switch controlWidth {
|
||||||
|
case .percentage(let percentage):
|
||||||
|
stepperWidth = max(containerWidth * ((percentage) / 100), minWidth)
|
||||||
|
|
||||||
|
case .value(let value):
|
||||||
|
stepperWidth = value
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the value of the stepperWidthConstant
|
||||||
|
if stepperWidth >= containerSize.width && stepperWidth <= containerWidth {
|
||||||
|
stepperWidthConstant = stepperWidth
|
||||||
|
} else if stepperWidth >= containerWidth {
|
||||||
|
stepperWidthConstant = containerWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
if let stepperWidthConstant {
|
||||||
|
stepperWidthConstraint?.constant = stepperWidthConstant
|
||||||
|
stepperWidthConstraint?.activate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Methods
|
||||||
|
//--------------------------------------------------
|
||||||
|
internal func decrementButtonClick() {
|
||||||
|
if _defaultValue > minValue {
|
||||||
|
defaultValue = _defaultValue - 1
|
||||||
|
sendActions(for: .valueChanged)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func incrementButtonClick() {
|
||||||
|
if _defaultValue < maxValue {
|
||||||
|
defaultValue = _defaultValue + 1
|
||||||
|
sendActions(for: .valueChanged)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func updateButtonStates() {
|
||||||
|
decrementButton.customContainerSize = size.buttonContainerSize
|
||||||
|
incrementButton.customContainerSize = size.buttonContainerSize
|
||||||
|
decrementButton.surface = surface
|
||||||
|
incrementButton.surface = surface
|
||||||
|
|
||||||
|
if isReadOnly || !isEnabled {
|
||||||
|
decrementButton.isEnabled = false
|
||||||
|
incrementButton.isEnabled = false
|
||||||
|
} else {
|
||||||
|
decrementButton.isEnabled = (defaultValue ?? _defaultMaxValue ) > minValue ? true : false
|
||||||
|
incrementButton.isEnabled = (defaultValue ?? _defaultMinValue) < maxValue ? true : false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
44
VDS/Components/InputStepper/InputStepperLog.txt
Normal file
44
VDS/Components/InputStepper/InputStepperLog.txt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
MM/DD/YYYY
|
||||||
|
----------------
|
||||||
|
|
||||||
|
02/2024
|
||||||
|
----------------
|
||||||
|
- New component
|
||||||
|
|
||||||
|
02/15/2024
|
||||||
|
----------------
|
||||||
|
- Added Border align: Inside to Anatomy
|
||||||
|
- Removed leadingText property values from States.
|
||||||
|
- Added Read-only to States.
|
||||||
|
- Created a section for Minimum width in Layout and spacing and updated the minWidth to 145px.
|
||||||
|
- Added trailingText spacing to Layout and spacing.
|
||||||
|
- Reduced space between Button Icons and text to 12px in Layout and spacing.
|
||||||
|
- Added top/bottom padding values to Layout and spacing.
|
||||||
|
|
||||||
|
03/01/2024
|
||||||
|
----------------
|
||||||
|
- Removed Leading Text from “Content and other properties” section.
|
||||||
|
|
||||||
|
03/20/2024
|
||||||
|
----------------
|
||||||
|
- Updated Anatomy artwork and items
|
||||||
|
- Added width and controlWidth to Configurations
|
||||||
|
- Updated Width under Layout and spacing to show layout examples
|
||||||
|
|
||||||
|
04/12/2024
|
||||||
|
----------------
|
||||||
|
- Added a new configuration property (size) that includes large and small
|
||||||
|
- Reduced details from Anatomy page and added them to Configurations/Size
|
||||||
|
- Added Hit area, Small Input Stepper spacing properties to Layout and spacing
|
||||||
|
- Updated the Behavior page to display disabled button icon when value is at max and min
|
||||||
|
|
||||||
|
04/29/2024
|
||||||
|
----------------
|
||||||
|
- Updated the Behavior page to display disabled button icon when value is at max and min
|
||||||
|
|
||||||
|
05/10/2024
|
||||||
|
----------------
|
||||||
|
- Added helperTextPlacement property to Configurations
|
||||||
|
- Added Layout examples for right Helper Text placement in Layout and Spacing
|
||||||
|
- Added overflow examples in Overflow section of Layout and Spacing
|
||||||
@ -192,42 +192,46 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open func initialSetup() {
|
private func initialSetup() {
|
||||||
if !initialSetupPerformed {
|
if !initialSetupPerformed {
|
||||||
initialSetupPerformed = true
|
initialSetupPerformed = true
|
||||||
//register for ContentSizeChanges
|
shouldUpdateView = false
|
||||||
NotificationCenter
|
|
||||||
.Publisher(center: .default, name: UIContentSizeCategory.didChangeNotification)
|
|
||||||
.sink { [weak self] notification in
|
|
||||||
self?.setNeedsUpdate()
|
|
||||||
}.store(in: &subscribers)
|
|
||||||
backgroundColor = .clear
|
|
||||||
numberOfLines = 0
|
|
||||||
lineBreakMode = .byTruncatingTail
|
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
accessibilityCustomActions = []
|
|
||||||
isAccessibilityElement = true
|
|
||||||
accessibilityTraits = .staticText
|
|
||||||
textAlignment = .left
|
|
||||||
setup()
|
setup()
|
||||||
|
setDefaults()
|
||||||
|
shouldUpdateView = true
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open func setup() {
|
open func setup() {
|
||||||
|
//register for ContentSizeChanges
|
||||||
|
NotificationCenter
|
||||||
|
.Publisher(center: .default, name: UIContentSizeCategory.didChangeNotification)
|
||||||
|
.sink { [weak self] notification in
|
||||||
|
self?.setNeedsUpdate()
|
||||||
|
}.store(in: &subscribers)
|
||||||
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
isAccessibilityElement = true
|
||||||
}
|
}
|
||||||
|
|
||||||
open func reset() {
|
open func setDefaults() {
|
||||||
shouldUpdateView = false
|
backgroundColor = .clear
|
||||||
|
accessibilityTraits = .staticText
|
||||||
|
accessibilityCustomActions = []
|
||||||
surface = .light
|
surface = .light
|
||||||
isEnabled = true
|
isEnabled = true
|
||||||
attributes = nil
|
attributes = nil
|
||||||
textStyle = .defaultStyle
|
textStyle = .defaultStyle
|
||||||
|
lineBreakMode = .byTruncatingTail
|
||||||
textAlignment = .left
|
textAlignment = .left
|
||||||
text = nil
|
text = nil
|
||||||
attributedText = nil
|
attributedText = nil
|
||||||
numberOfLines = 0
|
numberOfLines = 0
|
||||||
backgroundColor = .clear
|
}
|
||||||
|
|
||||||
|
open func reset() {
|
||||||
|
shouldUpdateView = false
|
||||||
|
setDefaults()
|
||||||
shouldUpdateView = true
|
shouldUpdateView = true
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,11 +81,6 @@ open class Line: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
|
||||||
open override func setup() {
|
|
||||||
super.setup()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
@ -94,8 +89,8 @@ open class Line: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
open override func reset() {
|
open override func setDefaults() {
|
||||||
super.reset()
|
super.setDefaults()
|
||||||
style = .primary
|
style = .primary
|
||||||
orientation = .horizontal
|
orientation = .horizontal
|
||||||
}
|
}
|
||||||
|
|||||||
@ -274,20 +274,13 @@ open class Notification: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
open override func setDefaults() {
|
||||||
open override func reset() {
|
super.setDefaults()
|
||||||
super.reset()
|
|
||||||
|
|
||||||
shouldUpdateView = false
|
|
||||||
|
|
||||||
titleLabel.reset()
|
|
||||||
titleLabel.text = ""
|
titleLabel.text = ""
|
||||||
titleLabel.textStyle = UIDevice.isIPad ? .boldBodyLarge : .boldBodySmall
|
titleLabel.textStyle = UIDevice.isIPad ? .boldBodyLarge : .boldBodySmall
|
||||||
|
|
||||||
subTitleLabel.reset()
|
|
||||||
subTitleLabel.textStyle = UIDevice.isIPad ? .bodyLarge : .bodySmall
|
subTitleLabel.textStyle = UIDevice.isIPad ? .bodyLarge : .bodySmall
|
||||||
|
|
||||||
buttonGroup.reset()
|
|
||||||
buttonGroup.alignment = .left
|
buttonGroup.alignment = .left
|
||||||
|
|
||||||
primaryButtonModel = nil
|
primaryButtonModel = nil
|
||||||
@ -302,9 +295,15 @@ open class Notification: View {
|
|||||||
closeButton.name = .close
|
closeButton.name = .close
|
||||||
|
|
||||||
hideCloseButton = false
|
hideCloseButton = false
|
||||||
|
|
||||||
shouldUpdateView = true
|
}
|
||||||
setNeedsUpdate()
|
|
||||||
|
/// Resets to default settings.
|
||||||
|
open override func reset() {
|
||||||
|
titleLabel.reset()
|
||||||
|
subTitleLabel.reset()
|
||||||
|
buttonGroup.reset()
|
||||||
|
super.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
|||||||
@ -95,8 +95,8 @@ open class Pagination: View {
|
|||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Executed on initialization for this View.
|
/// Executed on initialization for this View.
|
||||||
open override func initialSetup() {
|
open override func setup() {
|
||||||
super.initialSetup()
|
super.setup()
|
||||||
|
|
||||||
collectionContainerView.addSubview(collectionView)
|
collectionContainerView.addSubview(collectionView)
|
||||||
containerView.addSubview(previousButton)
|
containerView.addSubview(previousButton)
|
||||||
|
|||||||
@ -60,8 +60,8 @@ open class PaginationButton: ButtonBase {
|
|||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Executed on initialization for this View.
|
/// Executed on initialization for this View.
|
||||||
open override func initialSetup() {
|
open override func setup() {
|
||||||
super.initialSetup()
|
super.setup()
|
||||||
if #available(iOS 15.0, *) {
|
if #available(iOS 15.0, *) {
|
||||||
configuration = buttonConfiguration
|
configuration = buttonConfiguration
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -67,17 +67,26 @@ open class RadioBoxGroup: SelectorGroupBase<RadioBoxItem>, SelectorGroupSingleSe
|
|||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private func ensureDevice() {
|
private func ensureDevice() {
|
||||||
|
var axis: NSLayoutConstraint.Axis = .vertical
|
||||||
|
var distribution: UIStackView.Distribution = .fill
|
||||||
|
|
||||||
|
defer {
|
||||||
|
mainStackView.axis = axis
|
||||||
|
mainStackView.distribution = distribution
|
||||||
|
}
|
||||||
|
|
||||||
if UIDevice.isIPad {
|
if UIDevice.isIPad {
|
||||||
mainStackView.axis = .horizontal
|
axis = .horizontal
|
||||||
mainStackView.distribution = .fillEqually
|
distribution = .fillEqually
|
||||||
} else {
|
} else {
|
||||||
if UIDevice.current.orientation.isPortrait || UIDevice.current.orientation == .unknown {
|
guard let supportedOrientations = UIApplication.shared.windows.first?.rootViewController?.supportedInterfaceOrientations else {
|
||||||
mainStackView.axis = .vertical
|
return
|
||||||
mainStackView.distribution = .fill
|
}
|
||||||
|
|
||||||
} else {
|
let orientation = UIDevice.current.orientation
|
||||||
mainStackView.axis = .horizontal
|
if supportedOrientations.contains(.landscape) && (orientation == .landscapeLeft || orientation == .landscapeRight) {
|
||||||
mainStackView.distribution = .fillEqually
|
axis = .horizontal
|
||||||
|
distribution = .fillEqually
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -165,11 +165,38 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Executed on initialization for this View.
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||||
open override func initialSetup() {
|
open override func setup() {
|
||||||
super.initialSetup()
|
super.setup()
|
||||||
onClick = { control in
|
|
||||||
control.toggle()
|
isAccessibilityElement = false
|
||||||
|
selectorView.isAccessibilityElement = true
|
||||||
|
selectorView.accessibilityTraits = .button
|
||||||
|
addSubview(selectorView)
|
||||||
|
selectorView.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
selectorView.addSubview(selectorStackView)
|
||||||
|
|
||||||
|
selectorStackView.addArrangedSubview(selectorLeftLabelStackView)
|
||||||
|
selectorStackView.addArrangedSubview(subTextRightLabel)
|
||||||
|
selectorLeftLabelStackView.addArrangedSubview(textLabel)
|
||||||
|
selectorLeftLabelStackView.addArrangedSubview(subTextLabel)
|
||||||
|
|
||||||
|
selectorView
|
||||||
|
.pinTop()
|
||||||
|
.pinLeading()
|
||||||
|
.pinTrailing(0, .defaultHigh)
|
||||||
|
.pinBottom(0, .defaultHigh)
|
||||||
|
|
||||||
|
selectorStackView.pinToSuperView(.uniform(VDSLayout.space3X))
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
|
||||||
|
onClick = { [weak self] _ in
|
||||||
|
guard let self, isEnabled else { return }
|
||||||
|
toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
selectorView.bridge_accessibilityLabelBlock = { [weak self] in
|
selectorView.bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
@ -204,43 +231,7 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
|||||||
|
|
||||||
return accessibilityLabels.joined(separator: ", ")
|
return accessibilityLabels.joined(separator: ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
|
||||||
open override func setup() {
|
|
||||||
super.setup()
|
|
||||||
|
|
||||||
isAccessibilityElement = false
|
|
||||||
selectorView.isAccessibilityElement = true
|
|
||||||
selectorView.accessibilityTraits = .button
|
|
||||||
addSubview(selectorView)
|
|
||||||
selectorView.isUserInteractionEnabled = false
|
|
||||||
|
|
||||||
selectorView.addSubview(selectorStackView)
|
|
||||||
|
|
||||||
selectorStackView.addArrangedSubview(selectorLeftLabelStackView)
|
|
||||||
selectorStackView.addArrangedSubview(subTextRightLabel)
|
|
||||||
selectorLeftLabelStackView.addArrangedSubview(textLabel)
|
|
||||||
selectorLeftLabelStackView.addArrangedSubview(subTextLabel)
|
|
||||||
|
|
||||||
selectorView
|
|
||||||
.pinTop()
|
|
||||||
.pinLeading()
|
|
||||||
.pinTrailing(0, .defaultHigh)
|
|
||||||
.pinBottom(0, .defaultHigh)
|
|
||||||
|
|
||||||
selectorStackView.pinToSuperView(.uniform(VDSLayout.space3X))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
textLabel.reset()
|
|
||||||
subTextLabel.reset()
|
|
||||||
subTextRightLabel.reset()
|
|
||||||
|
|
||||||
textLabel.textStyle = .boldBodyLarge
|
textLabel.textStyle = .boldBodyLarge
|
||||||
subTextLabel.textStyle = .bodyLarge
|
subTextLabel.textStyle = .bodyLarge
|
||||||
subTextRightLabel.textStyle = .bodyLarge
|
subTextRightLabel.textStyle = .bodyLarge
|
||||||
@ -260,9 +251,14 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
|||||||
|
|
||||||
isSelected = false
|
isSelected = false
|
||||||
onChange = nil
|
onChange = nil
|
||||||
|
}
|
||||||
|
|
||||||
shouldUpdateView = true
|
/// Resets to default settings.
|
||||||
setNeedsUpdate()
|
open override func reset() {
|
||||||
|
textLabel.reset()
|
||||||
|
subTextLabel.reset()
|
||||||
|
subTextRightLabel.reset()
|
||||||
|
super.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This will change the state of the Selector and execute the actionBlock if provided.
|
/// This will change the state of the Selector and execute the actionBlock if provided.
|
||||||
|
|||||||
@ -77,12 +77,13 @@ open class RadioButtonGroup: SelectorGroupBase<RadioButtonItem>, SelectorGroupSi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func reset() {
|
open override func setDefaults() {
|
||||||
super.reset()
|
super.setDefaults()
|
||||||
|
inputId = nil
|
||||||
showError = false
|
showError = false
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func didSelect(_ selectedControl: RadioButtonItem) {
|
open override func didSelect(_ selectedControl: RadioButtonItem) {
|
||||||
if let selectedItem {
|
if let selectedItem {
|
||||||
updateToggle(selectedItem)
|
updateToggle(selectedItem)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,8 +92,8 @@ open class Table: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
///Called upon initializing the table view
|
///Called upon initializing the table view
|
||||||
open override func initialSetup() {
|
open override func setup() {
|
||||||
super.initialSetup()
|
super.setup()
|
||||||
addSubview(matrixView)
|
addSubview(matrixView)
|
||||||
matrixView.pinToSuperView()
|
matrixView.pinToSuperView()
|
||||||
}
|
}
|
||||||
@ -109,18 +109,16 @@ open class Table: View {
|
|||||||
matrixView.collectionViewLayout.invalidateLayout()
|
matrixView.collectionViewLayout.invalidateLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
open override func setDefaults() {
|
||||||
open override func reset() {
|
super.setDefaults()
|
||||||
super.reset()
|
|
||||||
striped = false
|
striped = false
|
||||||
padding = .standard
|
padding = .standard
|
||||||
tableHeader = []
|
tableHeader = []
|
||||||
tableRows = []
|
tableRows = []
|
||||||
fillContainer = true
|
fillContainer = true
|
||||||
columnWidths = nil
|
columnWidths = nil
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateColumnWidths() -> [CGFloat] {
|
func calculateColumnWidths() -> [CGFloat] {
|
||||||
guard let noOfColumns = tableData.first?.columnsCount else { return [] }
|
guard let noOfColumns = tableData.first?.columnsCount else { return [] }
|
||||||
let itemWidth = floor(matrixView.safeAreaLayoutGuide.layoutFrame.width / CGFloat(noOfColumns))
|
let itemWidth = floor(matrixView.safeAreaLayoutGuide.layoutFrame.width / CGFloat(noOfColumns))
|
||||||
|
|||||||
@ -221,10 +221,11 @@ open class Tabs: View {
|
|||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
updateContentView()
|
updateContentView()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func reset() {
|
open override func setDefaults() {
|
||||||
super.reset()
|
super.setDefaults()
|
||||||
shouldUpdateView = false
|
onTabDidSelect = nil
|
||||||
|
onTabShouldSelect = nil
|
||||||
orientation = .horizontal
|
orientation = .horizontal
|
||||||
borderLine = true
|
borderLine = true
|
||||||
fillContainer = false
|
fillContainer = false
|
||||||
@ -235,11 +236,9 @@ open class Tabs: View {
|
|||||||
selectedIndex = 0
|
selectedIndex = 0
|
||||||
size = .medium
|
size = .medium
|
||||||
sticky = false
|
sticky = false
|
||||||
tabViews.forEach{ $0.reset() }
|
tabModels = []
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -11,10 +11,7 @@ import VDSCoreTokens
|
|||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
/// Base Class used to build out a Input controls.
|
/// Base Class used to build out a Input controls.
|
||||||
@objcMembers
|
open class EntryFieldBase<ValueType>: Control, Changeable, FormFieldInternalValidatable {
|
||||||
@objc(VDSEntryField)
|
|
||||||
open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -229,11 +226,11 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
open var inputId: String? { didSet { setNeedsUpdate() } }
|
open var inputId: String? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// The text of this textField.
|
/// The text of this textField.
|
||||||
open var value: String? {
|
open var value: ValueType? {
|
||||||
get { fatalError("must be read from subclass")}
|
get { fatalError("must be read from subclass")}
|
||||||
}
|
}
|
||||||
|
|
||||||
open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } }
|
open var defaultValue: ValueType? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
open var isRequired: Bool = false { didSet { setNeedsUpdate() } }
|
open var isRequired: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
@ -245,7 +242,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open var rules = [AnyRule<String>]()
|
open var rules = [AnyRule<ValueType>]()
|
||||||
|
|
||||||
open var accessibilityHintText: String = "Double tap to open"
|
open var accessibilityHintText: String = "Double tap to open"
|
||||||
|
|
||||||
@ -316,6 +313,41 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
errorLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
|
errorLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
|
||||||
helperLabel.textColorConfiguration = secondaryColorConfiguration.eraseToAnyColorable()
|
helperLabel.textColorConfiguration = secondaryColorConfiguration.eraseToAnyColorable()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the UI
|
||||||
|
open override func updateView() {
|
||||||
|
super.updateView()
|
||||||
|
updateRules()
|
||||||
|
updateContainerView()
|
||||||
|
updateContainerWidth()
|
||||||
|
updateTitleLabel()
|
||||||
|
updateErrorLabel()
|
||||||
|
updateHelperLabel()
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
|
||||||
|
titleLabel.textStyle = .bodySmall
|
||||||
|
errorLabel.textStyle = .bodySmall
|
||||||
|
helperLabel.textStyle = .bodySmall
|
||||||
|
|
||||||
|
labelText = nil
|
||||||
|
helperText = nil
|
||||||
|
showError = false
|
||||||
|
errorText = nil
|
||||||
|
tooltipModel = nil
|
||||||
|
transparentBackground = false
|
||||||
|
width = nil
|
||||||
|
inputId = nil
|
||||||
|
defaultValue = nil
|
||||||
|
isRequired = false
|
||||||
|
isReadOnly = false
|
||||||
|
helperTextPlacement = .bottom
|
||||||
|
rules = []
|
||||||
|
onChange = nil
|
||||||
|
|
||||||
containerView.bridge_accessibilityLabelBlock = { [weak self] in
|
containerView.bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
var accessibilityLabels = [String]()
|
var accessibilityLabels = [String]()
|
||||||
@ -342,50 +374,23 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
containerView.bridge_accessibilityValueBlock = { [weak self] in
|
containerView.bridge_accessibilityValueBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self, let value else { return "" }
|
||||||
return value
|
return "\(value)"
|
||||||
}
|
}
|
||||||
|
|
||||||
statusIcon.bridge_accessibilityLabelBlock = { [weak self] in
|
statusIcon.bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
return showError || hasInternalError ? "error" : nil
|
return showError || hasInternalError ? "error" : nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the UI
|
|
||||||
open override func updateView() {
|
|
||||||
super.updateView()
|
|
||||||
updateRules()
|
|
||||||
updateContainerView()
|
|
||||||
updateContainerWidth()
|
|
||||||
updateTitleLabel()
|
|
||||||
updateErrorLabel()
|
|
||||||
updateHelperLabel()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
open override func reset() {
|
open override func reset() {
|
||||||
super.reset()
|
|
||||||
titleLabel.reset()
|
titleLabel.reset()
|
||||||
errorLabel.reset()
|
errorLabel.reset()
|
||||||
helperLabel.reset()
|
helperLabel.reset()
|
||||||
|
super.reset()
|
||||||
titleLabel.textStyle = .bodySmall
|
|
||||||
errorLabel.textStyle = .bodySmall
|
|
||||||
helperLabel.textStyle = .bodySmall
|
|
||||||
|
|
||||||
labelText = nil
|
|
||||||
helperText = nil
|
|
||||||
showError = false
|
|
||||||
errorText = nil
|
|
||||||
tooltipModel = nil
|
|
||||||
transparentBackground = false
|
|
||||||
width = nil
|
|
||||||
inputId = nil
|
|
||||||
defaultValue = nil
|
|
||||||
isRequired = false
|
|
||||||
isReadOnly = false
|
|
||||||
onChange = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open override var canBecomeFirstResponder: Bool {
|
open override var canBecomeFirstResponder: Bool {
|
||||||
@ -523,8 +528,8 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
internal func updateRules() {
|
internal func updateRules() {
|
||||||
rules.removeAll()
|
rules.removeAll()
|
||||||
if isRequired && useRequiredRule {
|
if isRequired && useRequiredRule && ValueType.self == String.self {
|
||||||
let rule = RequiredRule()
|
let rule = RequiredRule<ValueType>()
|
||||||
if let errorText, !errorText.isEmpty {
|
if let errorText, !errorText.isEmpty {
|
||||||
rule.errorMessage = errorText
|
rule.errorMessage = errorText
|
||||||
} else if let labelText{
|
} else if let labelText{
|
||||||
@ -542,7 +547,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
containerView.layer.borderWidth = VDSFormControls.borderWidth
|
containerView.layer.borderWidth = VDSFormControls.borderWidth
|
||||||
containerView.layer.cornerRadius = VDSFormControls.borderRadius
|
containerView.layer.cornerRadius = VDSFormControls.borderRadius
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func updateContainerWidth() {
|
internal func updateContainerWidth() {
|
||||||
widthConstraint?.deactivate()
|
widthConstraint?.deactivate()
|
||||||
trailingLessThanEqualsConstraint?.deactivate()
|
trailingLessThanEqualsConstraint?.deactivate()
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import Combine
|
|||||||
/// dates and security codes in their correct formats.
|
/// dates and security codes in their correct formats.
|
||||||
@objcMembers
|
@objcMembers
|
||||||
@objc(VDSInputField)
|
@objc(VDSInputField)
|
||||||
open class InputField: EntryFieldBase {
|
open class InputField: EntryFieldBase<String> {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
@ -205,6 +205,22 @@ open class InputField: EntryFieldBase {
|
|||||||
|
|
||||||
textField.textColorConfiguration = textFieldTextColorConfiguration
|
textField.textColorConfiguration = textFieldTextColorConfiguration
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func getFieldContainer() -> UIView {
|
||||||
|
return textField
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
textField.text = ""
|
||||||
|
|
||||||
|
successLabel.textStyle = .bodySmall
|
||||||
|
|
||||||
|
fieldType = .text
|
||||||
|
showSuccess = false
|
||||||
|
successText = nil
|
||||||
|
|
||||||
containerView.bridge_accessibilityLabelBlock = { [weak self] in
|
containerView.bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
var accessibilityLabels = [String]()
|
var accessibilityLabels = [String]()
|
||||||
@ -259,22 +275,10 @@ open class InputField: EntryFieldBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func getFieldContainer() -> UIView {
|
|
||||||
return textField
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
open override func reset() {
|
open override func reset() {
|
||||||
super.reset()
|
|
||||||
textField.text = ""
|
|
||||||
|
|
||||||
successLabel.reset()
|
successLabel.reset()
|
||||||
successLabel.textStyle = .bodySmall
|
super.reset()
|
||||||
|
|
||||||
fieldType = .text
|
|
||||||
showSuccess = false
|
|
||||||
successText = nil
|
|
||||||
helperTextPlacement = .bottom
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
|||||||
@ -98,19 +98,22 @@ open class TextField: UITextField, ViewProtocol, Errorable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open func initialSetup() {
|
private func initialSetup() {
|
||||||
if !initialSetupPerformed {
|
if !initialSetupPerformed {
|
||||||
initialSetupPerformed = true
|
initialSetupPerformed = true
|
||||||
backgroundColor = .clear
|
shouldUpdateView = false
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
||||||
clipsToBounds = true
|
|
||||||
setup()
|
setup()
|
||||||
|
setDefaults()
|
||||||
|
shouldUpdateView = true
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open func setup() {
|
open func setup() {
|
||||||
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||||
|
clipsToBounds = true
|
||||||
|
|
||||||
let accessView = UIView(frame: .init(origin: .zero, size: .init(width: UIScreen.main.bounds.width, height: 44)))
|
let accessView = UIView(frame: .init(origin: .zero, size: .init(width: UIScreen.main.bounds.width, height: 44)))
|
||||||
accessView.backgroundColor = .white
|
accessView.backgroundColor = .white
|
||||||
accessView.addBorder(side: .top, width: 1, color: .lightGray)
|
accessView.addBorder(side: .top, width: 1, color: .lightGray)
|
||||||
@ -124,6 +127,17 @@ open class TextField: UITextField, ViewProtocol, Errorable {
|
|||||||
inputAccessoryView = accessView
|
inputAccessoryView = accessView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open func setDefaults() {
|
||||||
|
backgroundColor = .clear
|
||||||
|
surface = .light
|
||||||
|
text = nil
|
||||||
|
formatText = nil
|
||||||
|
useScaledFont = false
|
||||||
|
showError = false
|
||||||
|
errorText = nil
|
||||||
|
textStyle = .defaultStyle
|
||||||
|
}
|
||||||
|
|
||||||
@objc func doneButtonAction() {
|
@objc func doneButtonAction() {
|
||||||
// Resigns the first responder status when 'Done' is tapped
|
// Resigns the first responder status when 'Done' is tapped
|
||||||
let _ = resignFirstResponder()
|
let _ = resignFirstResponder()
|
||||||
@ -174,8 +188,7 @@ open class TextField: UITextField, ViewProtocol, Errorable {
|
|||||||
|
|
||||||
open func reset() {
|
open func reset() {
|
||||||
shouldUpdateView = false
|
shouldUpdateView = false
|
||||||
surface = .light
|
setDefaults()
|
||||||
text = nil
|
|
||||||
shouldUpdateView = true
|
shouldUpdateView = true
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,12 +7,14 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class RequiredRule: Rule {
|
class RequiredRule<ValueType>: Rule {
|
||||||
var maxLength: Int?
|
var maxLength: Int?
|
||||||
var errorMessage: String = "This field is required."
|
var errorMessage: String = "This field is required."
|
||||||
|
|
||||||
func isValid(value: String?) -> Bool {
|
func isValid(value: ValueType?) -> Bool {
|
||||||
guard let value, !value.isEmpty, value.count > 0 else { return false }
|
guard let value,
|
||||||
|
!"\(value)".isEmpty,
|
||||||
|
"\(value)".count > 0 else { return false }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import Combine
|
|||||||
/// Use a text area when you want customers to enter text that’s longer than a single line.
|
/// Use a text area when you want customers to enter text that’s longer than a single line.
|
||||||
@objcMembers
|
@objcMembers
|
||||||
@objc(VDSTextArea)
|
@objc(VDSTextArea)
|
||||||
open class TextArea: EntryFieldBase {
|
open class TextArea: EntryFieldBase<String> {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -166,14 +166,19 @@ open class TextArea: EntryFieldBase {
|
|||||||
bottomContainerStackView.spacing = VDSLayout.space2X
|
bottomContainerStackView.spacing = VDSLayout.space2X
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
minHeight = .twoX
|
||||||
|
maxLength = nil
|
||||||
|
textView.text = ""
|
||||||
|
characterCounterLabel.textStyle = .bodySmall
|
||||||
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
open override func reset() {
|
open override func reset() {
|
||||||
super.reset()
|
|
||||||
textView.text = ""
|
|
||||||
characterCounterLabel.reset()
|
characterCounterLabel.reset()
|
||||||
characterCounterLabel.textStyle = .bodySmall
|
super.reset()
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
|||||||
@ -107,17 +107,20 @@ open class TextView: UITextView, ViewProtocol, Errorable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open func initialSetup() {
|
private func initialSetup() {
|
||||||
if !initialSetupPerformed {
|
if !initialSetupPerformed {
|
||||||
initialSetupPerformed = true
|
initialSetupPerformed = true
|
||||||
backgroundColor = .clear
|
shouldUpdateView = false
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
setup()
|
setup()
|
||||||
|
setDefaults()
|
||||||
|
shouldUpdateView = true
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open func setup() {
|
open func setup() {
|
||||||
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
let accessView = UIView(frame: .init(origin: .zero, size: .init(width: UIScreen.main.bounds.width, height: 44)))
|
let accessView = UIView(frame: .init(origin: .zero, size: .init(width: UIScreen.main.bounds.width, height: 44)))
|
||||||
accessView.backgroundColor = .white
|
accessView.backgroundColor = .white
|
||||||
accessView.addBorder(side: .top, width: 1, color: .lightGray)
|
accessView.addBorder(side: .top, width: 1, color: .lightGray)
|
||||||
@ -134,6 +137,15 @@ open class TextView: UITextView, ViewProtocol, Errorable {
|
|||||||
placeholderLabel.pinToSuperView()
|
placeholderLabel.pinToSuperView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open func setDefaults() {
|
||||||
|
backgroundColor = .clear
|
||||||
|
surface = .light
|
||||||
|
text = nil
|
||||||
|
placeholder = nil
|
||||||
|
errorText = nil
|
||||||
|
showError = false
|
||||||
|
}
|
||||||
|
|
||||||
@objc func doneButtonAction() {
|
@objc func doneButtonAction() {
|
||||||
// Resigns the first responder status when 'Done' is tapped
|
// Resigns the first responder status when 'Done' is tapped
|
||||||
resignFirstResponder()
|
resignFirstResponder()
|
||||||
@ -153,8 +165,7 @@ open class TextView: UITextView, ViewProtocol, Errorable {
|
|||||||
|
|
||||||
open func reset() {
|
open func reset() {
|
||||||
shouldUpdateView = false
|
shouldUpdateView = false
|
||||||
surface = .light
|
setDefaults()
|
||||||
text = nil
|
|
||||||
shouldUpdateView = true
|
shouldUpdateView = true
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ open class TileContainer: TileContainerBase<TileContainer.Padding> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class TileContainerBase<PaddingType: DefaultValuing>: Control where PaddingType.ValueType == CGFloat {
|
open class TileContainerBase<PaddingType: DefaultValuing>: View where PaddingType.ValueType == CGFloat {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
@ -118,6 +118,8 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
|
|||||||
$0.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
|
$0.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var isHighlighted: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Properties
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -279,19 +281,18 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
|
|||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
open override func setDefaults() {
|
||||||
open override func reset() {
|
super.setDefaults()
|
||||||
super.reset()
|
backgroundImage = nil
|
||||||
shouldUpdateView = false
|
|
||||||
color = .white
|
color = .white
|
||||||
aspectRatio = .none
|
backgroundEffect = .none
|
||||||
|
padding = .defaultValue
|
||||||
|
aspectRatio = .ratio1x1
|
||||||
imageFallbackColor = .light
|
imageFallbackColor = .light
|
||||||
width = nil
|
width = nil
|
||||||
height = nil
|
height = nil
|
||||||
showBorder = false
|
showBorder = false
|
||||||
showDropShadow = false
|
showDropShadow = false
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
@ -337,6 +338,27 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
|
|||||||
set {}
|
set {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||||
|
super.touchesBegan(touches, with: event)
|
||||||
|
if let onClickSubscriber {
|
||||||
|
isHighlighted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||||
|
super.touchesEnded(touches, with: event)
|
||||||
|
if let onClickSubscriber {
|
||||||
|
isHighlighted = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||||
|
super.touchesCancelled(touches, with: event)
|
||||||
|
if let onClickSubscriber {
|
||||||
|
isHighlighted = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Methods
|
// MARK: - Public Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -390,20 +390,19 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
open override func setDefaults() {
|
||||||
open override func reset() {
|
super.setDefaults()
|
||||||
shouldUpdateView = false
|
|
||||||
super.reset()
|
|
||||||
aspectRatio = .none
|
aspectRatio = .none
|
||||||
color = .black
|
color = .black
|
||||||
|
textWidth = nil
|
||||||
|
textPostion = .top
|
||||||
|
|
||||||
//models
|
//models
|
||||||
badgeModel = nil
|
badgeModel = nil
|
||||||
titleModel = nil
|
titleModel = nil
|
||||||
subTitleModel = nil
|
subTitleModel = nil
|
||||||
descriptiveIconModel = nil
|
descriptiveIconModel = nil
|
||||||
directionalIconModel = nil
|
directionalIconModel = nil
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
|||||||
@ -280,15 +280,12 @@ open class TitleLockup: View {
|
|||||||
set {}
|
set {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
open override func setDefaults() {
|
||||||
open override func reset() {
|
super.setDefaults()
|
||||||
super.reset()
|
textAlignment = .left
|
||||||
shouldUpdateView = false
|
|
||||||
eyebrowModel = nil
|
eyebrowModel = nil
|
||||||
titleModel = nil
|
titleModel = nil
|
||||||
subTitleModel = nil
|
subTitleModel = nil
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var labelViews = [UIView]()
|
var labelViews = [UIView]()
|
||||||
|
|||||||
@ -55,6 +55,7 @@ open class Toggle: Control, Changeable, FormFieldable {
|
|||||||
private var leftConstraints: [NSLayoutConstraint] = []
|
private var leftConstraints: [NSLayoutConstraint] = []
|
||||||
private var rightConstraints: [NSLayoutConstraint] = []
|
private var rightConstraints: [NSLayoutConstraint] = []
|
||||||
private var labelConstraints: [NSLayoutConstraint] = []
|
private var labelConstraints: [NSLayoutConstraint] = []
|
||||||
|
private var toggleConstraints: [NSLayoutConstraint] = []
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Configuration
|
// MARK: - Configuration
|
||||||
@ -95,7 +96,7 @@ open class Toggle: Control, Changeable, FormFieldable {
|
|||||||
open var toggleView = ToggleView().with {
|
open var toggleView = ToggleView().with {
|
||||||
$0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
$0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||||
$0.isUserInteractionEnabled = false
|
$0.isUserInteractionEnabled = false
|
||||||
$0.isAccessibilityElement = false
|
$0.isAccessibilityElement = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used in showing the on/off text.
|
/// Used in showing the on/off text.
|
||||||
@ -148,35 +149,15 @@ open class Toggle: Control, Changeable, FormFieldable {
|
|||||||
|
|
||||||
open var value: AnyHashable? { isOn }
|
open var value: AnyHashable? { isOn }
|
||||||
|
|
||||||
/// The natural size for the receiving view, considering only properties of the view itself.
|
|
||||||
open override var intrinsicContentSize: CGSize {
|
|
||||||
if showLabel {
|
|
||||||
label.sizeToFit()
|
|
||||||
let size = CGSize(width: label.frame.width + spacingBetween + toggleContainerSize.width,
|
|
||||||
height: max(toggleContainerSize.height, label.frame.height))
|
|
||||||
return size
|
|
||||||
} else {
|
|
||||||
return toggleContainerSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open override var shouldHighlight: Bool { false }
|
open override var shouldHighlight: Bool { false }
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Executed on initialization for this View.
|
|
||||||
open override func initialSetup() {
|
|
||||||
super.initialSetup()
|
|
||||||
onClick = { control in
|
|
||||||
control.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||||
open override func setup() {
|
open override func setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
|
|
||||||
isAccessibilityElement = true
|
isAccessibilityElement = true
|
||||||
if #available(iOS 17.0, *) {
|
if #available(iOS 17.0, *) {
|
||||||
accessibilityTraits = .toggleButton
|
accessibilityTraits = .toggleButton
|
||||||
@ -208,6 +189,59 @@ open class Toggle: Control, Changeable, FormFieldable {
|
|||||||
label.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor)
|
label.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Set content hugging priority
|
||||||
|
setContentHuggingPriority(.required, for: .horizontal)
|
||||||
|
|
||||||
|
isAccessibilityElement = true
|
||||||
|
if #available(iOS 17.0, *) {
|
||||||
|
accessibilityTraits = .toggleButton
|
||||||
|
} else {
|
||||||
|
accessibilityTraits = .button
|
||||||
|
}
|
||||||
|
addSubview(label)
|
||||||
|
addSubview(toggleView)
|
||||||
|
|
||||||
|
// Set up initial constraints for label and switch
|
||||||
|
toggleView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
||||||
|
|
||||||
|
//toggle
|
||||||
|
toggleConstraints = [
|
||||||
|
toggleView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor),
|
||||||
|
toggleView.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor)
|
||||||
|
]
|
||||||
|
|
||||||
|
//toggle and label variants
|
||||||
|
labelConstraints = [
|
||||||
|
height(constant: toggleContainerSize.height, priority: .defaultLow),
|
||||||
|
heightGreaterThanEqualTo(constant: toggleContainerSize.height, priority: .defaultHigh),
|
||||||
|
label.topAnchor.constraint(equalTo: topAnchor),
|
||||||
|
label.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||||
|
]
|
||||||
|
|
||||||
|
//label-toggle
|
||||||
|
leftConstraints = [
|
||||||
|
label.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
toggleView.leadingAnchor.constraint(equalTo: label.trailingAnchor, constant: spacingBetween),
|
||||||
|
toggleView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
||||||
|
]
|
||||||
|
|
||||||
|
//toggle-label
|
||||||
|
rightConstraints = [
|
||||||
|
toggleView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
label.leadingAnchor.constraint(equalTo: toggleView.trailingAnchor, constant: spacingBetween),
|
||||||
|
label.trailingAnchor.constraint(equalTo: trailingAnchor)
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func setDefaults() {
|
||||||
|
super.setDefaults()
|
||||||
|
|
||||||
|
onClick = { [weak self] _ in
|
||||||
|
guard let self else { return }
|
||||||
|
toggle()
|
||||||
|
}
|
||||||
|
|
||||||
bridge_accessibilityValueBlock = { [weak self] in
|
bridge_accessibilityValueBlock = { [weak self] in
|
||||||
guard let self else { return "" }
|
guard let self else { return "" }
|
||||||
if showText {
|
if showText {
|
||||||
@ -216,13 +250,7 @@ open class Toggle: Control, Changeable, FormFieldable {
|
|||||||
return isSelected ? "On" : "Off"
|
return isSelected ? "On" : "Off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets to default settings.
|
|
||||||
open override func reset() {
|
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
label.reset()
|
|
||||||
isEnabled = true
|
isEnabled = true
|
||||||
isOn = false
|
isOn = false
|
||||||
isAnimated = true
|
isAnimated = true
|
||||||
@ -234,8 +262,12 @@ open class Toggle: Control, Changeable, FormFieldable {
|
|||||||
textPosition = .left
|
textPosition = .left
|
||||||
inputId = nil
|
inputId = nil
|
||||||
onChange = nil
|
onChange = nil
|
||||||
shouldUpdateView = true
|
}
|
||||||
setNeedsUpdate()
|
|
||||||
|
/// Resets to default settings.
|
||||||
|
open override func reset() {
|
||||||
|
label.reset()
|
||||||
|
super.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
@ -261,6 +293,8 @@ open class Toggle: Control, Changeable, FormFieldable {
|
|||||||
label.isHidden = !showLabel
|
label.isHidden = !showLabel
|
||||||
|
|
||||||
if showLabel {
|
if showLabel {
|
||||||
|
NSLayoutConstraint.deactivate(toggleConstraints)
|
||||||
|
|
||||||
label.textAlignment = textPosition == .left ? .right : .left
|
label.textAlignment = textPosition == .left ? .right : .left
|
||||||
label.textStyle = textStyle
|
label.textStyle = textStyle
|
||||||
label.text = statusText
|
label.text = statusText
|
||||||
@ -279,6 +313,7 @@ open class Toggle: Control, Changeable, FormFieldable {
|
|||||||
NSLayoutConstraint.deactivate(leftConstraints)
|
NSLayoutConstraint.deactivate(leftConstraints)
|
||||||
NSLayoutConstraint.deactivate(rightConstraints)
|
NSLayoutConstraint.deactivate(rightConstraints)
|
||||||
NSLayoutConstraint.deactivate(labelConstraints)
|
NSLayoutConstraint.deactivate(labelConstraints)
|
||||||
|
NSLayoutConstraint.activate(toggleConstraints)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,18 +105,10 @@ open class ToggleView: Control, Changeable, FormFieldable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Executed on initialization for this View.
|
|
||||||
open override func initialSetup() {
|
|
||||||
super.initialSetup()
|
|
||||||
onClick = { control in
|
|
||||||
control.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||||
open override func setup() {
|
open override func setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
|
|
||||||
isAccessibilityElement = true
|
isAccessibilityElement = true
|
||||||
if #available(iOS 17.0, *) {
|
if #available(iOS 17.0, *) {
|
||||||
accessibilityTraits = .toggleButton
|
accessibilityTraits = .toggleButton
|
||||||
@ -157,20 +149,21 @@ open class ToggleView: Control, Changeable, FormFieldable {
|
|||||||
accessibilityLabel = "Toggle"
|
accessibilityLabel = "Toggle"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
open override func setDefaults() {
|
||||||
open override func reset() {
|
super.setDefaults()
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
isOn = false
|
isOn = false
|
||||||
isAnimated = true
|
isAnimated = true
|
||||||
inputId = nil
|
inputId = nil
|
||||||
toggleView.backgroundColor = toggleColorConfiguration.getColor(self)
|
toggleView.backgroundColor = toggleColorConfiguration.getColor(self)
|
||||||
knobView.backgroundColor = knobColorConfiguration.getColor(self)
|
knobView.backgroundColor = knobColorConfiguration.getColor(self)
|
||||||
onChange = nil
|
onChange = nil
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
onClick = { [weak self] _ in
|
||||||
|
guard let self else { return }
|
||||||
|
toggle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
|
|||||||
@ -159,18 +159,16 @@ open class Tooltip: Control, TooltipLaunchable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
open override func setDefaults() {
|
||||||
open override func reset() {
|
super.setDefaults()
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
size = .medium
|
|
||||||
title = ""
|
|
||||||
content = ""
|
|
||||||
fillColor = .primary
|
|
||||||
closeButtonText = "Close"
|
closeButtonText = "Close"
|
||||||
shouldUpdateView = true
|
fillColor = .primary
|
||||||
setNeedsUpdate()
|
size = .medium
|
||||||
|
title = nil
|
||||||
|
content = nil
|
||||||
|
contentView = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
|
|||||||
@ -84,17 +84,13 @@ open class TrailingTooltipLabel: View, TooltipLaunchable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
open override func setDefaults() {
|
||||||
open override func reset() {
|
super.setDefaults()
|
||||||
super.reset()
|
|
||||||
shouldUpdateView = false
|
|
||||||
labelText = nil
|
labelText = nil
|
||||||
labelAttributes = nil
|
labelAttributes = nil
|
||||||
labelTextStyle = .defaultStyle
|
labelTextStyle = .defaultStyle
|
||||||
labelTextAlignment = .left
|
labelTextAlignment = .left
|
||||||
tooltipModel = nil
|
tooltipModel = nil
|
||||||
shouldUpdateView = true
|
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,12 +9,12 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
public protocol Clickable: ViewProtocol where Self: UIControl {
|
public protocol Clickable: ViewProtocol {
|
||||||
/// Sets the primary Subscriber used for the UIControl event .touchUpInside.
|
/// Sets the primary Subscriber used for the UIControl event .touchUpInside.
|
||||||
var onClickSubscriber: AnyCancellable? { get set }
|
var onClickSubscriber: AnyCancellable? { get set }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Clickable {
|
extension Clickable where Self: UIControl {
|
||||||
/// Allows the setting of a completion block against the onClickSubscriber cancellable. This will
|
/// Allows the setting of a completion block against the onClickSubscriber cancellable. This will
|
||||||
/// completion block will get executed against the UIControl publisher for the 'touchUpInside' action.
|
/// completion block will get executed against the UIControl publisher for the 'touchUpInside' action.
|
||||||
public var onClick: ((Self) -> ())? {
|
public var onClick: ((Self) -> ())? {
|
||||||
@ -23,7 +23,7 @@ extension Clickable {
|
|||||||
onClickSubscriber?.cancel()
|
onClickSubscriber?.cancel()
|
||||||
if let newValue {
|
if let newValue {
|
||||||
onClickSubscriber = publisher(for: .touchUpInside)
|
onClickSubscriber = publisher(for: .touchUpInside)
|
||||||
.sink { [weak self] c in
|
.sink { [weak self] c in
|
||||||
guard let self, self.isEnabled else { return }
|
guard let self, self.isEnabled else { return }
|
||||||
newValue(c)
|
newValue(c)
|
||||||
}
|
}
|
||||||
@ -34,3 +34,24 @@ extension Clickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Clickable where Self: UIView {
|
||||||
|
/// Allows the setting of a completion block against the onClickSubscriber cancellable. This will
|
||||||
|
/// completion block will get executed against the UIControl publisher for the 'touchUpInside' action.
|
||||||
|
public var onClick: ((Self) -> ())? {
|
||||||
|
get { return nil }
|
||||||
|
set {
|
||||||
|
onClickSubscriber?.cancel()
|
||||||
|
if let newValue {
|
||||||
|
onClickSubscriber = publisher(for: UITapGestureRecognizer())
|
||||||
|
.sink { [weak self] _ in
|
||||||
|
guard let self, self.isEnabled else { return }
|
||||||
|
newValue(self)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onClickSubscriber = nil
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Protocol used for a FormField object.
|
/// Protocol used for a FormField object.
|
||||||
public protocol FormFieldable {
|
public protocol FormFieldable<ValueType> {
|
||||||
associatedtype ValueType = AnyHashable
|
associatedtype ValueType = AnyHashable
|
||||||
|
|
||||||
/// Unique Id for the Form Field object within a Form.
|
/// Unique Id for the Form Field object within a Form.
|
||||||
@ -19,7 +19,7 @@ public protocol FormFieldable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Protocol for FormFieldable that require internal validation.
|
/// Protocol for FormFieldable that require internal validation.
|
||||||
public protocol FormFieldInternalValidatable: FormFieldable, Errorable {
|
public protocol FormFieldInternalValidatable<ValueType>: FormFieldable, Errorable {
|
||||||
/// Rules that drive the validator
|
/// Rules that drive the validator
|
||||||
var rules: [AnyRule<ValueType>] { get set }
|
var rules: [AnyRule<ValueType>] { get set }
|
||||||
|
|
||||||
|
|||||||
@ -19,12 +19,12 @@ public protocol ViewProtocol: AnyObject, Initable, Resettable, Enabling, Surface
|
|||||||
/// Used for setting an implementation for the default Accessible Action
|
/// Used for setting an implementation for the default Accessible Action
|
||||||
var accessibilityAction: ((Self) -> Void)? { get set }
|
var accessibilityAction: ((Self) -> Void)? { get set }
|
||||||
|
|
||||||
/// Executed on initialization for this View.
|
|
||||||
func initialSetup()
|
|
||||||
|
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||||
func setup()
|
func setup()
|
||||||
|
|
||||||
|
/// Default configurations for values and properties. This is called in the setup() and reset().
|
||||||
|
func setDefaults()
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
func updateView()
|
func updateView()
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,7 @@ Using the system allows designers and developers to collaborate more easily and
|
|||||||
- ``CheckboxGroup``
|
- ``CheckboxGroup``
|
||||||
- ``DropdownSelect``
|
- ``DropdownSelect``
|
||||||
- ``Icon``
|
- ``Icon``
|
||||||
|
- ``InputStepper``
|
||||||
- ``InputField``
|
- ``InputField``
|
||||||
- ``Label``
|
- ``Label``
|
||||||
- ``Line``
|
- ``Line``
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user