From fcef842397c93caa8f4c81c0fae6c241098fccfa Mon Sep 17 00:00:00 2001 From: Jarrod Courtney Date: Fri, 23 Sep 2022 11:05:37 -0500 Subject: [PATCH 1/3] start of button Signed-off-by: Jarrod Courtney --- VDS.xcodeproj/project.pbxproj | 20 +++ VDS/Components/Button/Button.swift | 219 ++++++++++++++++++++++++ VDS/Components/Button/ButtonModel.swift | 42 +++++ VDS/Protocols/Useable.swift | 21 +++ 4 files changed, 302 insertions(+) create mode 100644 VDS/Components/Button/Button.swift create mode 100644 VDS/Components/Button/ButtonModel.swift create mode 100644 VDS/Protocols/Useable.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index dd3d8e50..f48fbe28 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; }; + 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; + 5FC35BE528D51414004EBEAC /* ButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE428D51413004EBEAC /* ButtonModel.swift */; }; EA1F265D28B944F00033E859 /* CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F265B28B944F00033E859 /* CollectionView.swift */; }; EA1F265E28B944F00033E859 /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F265C28B944F00033E859 /* CollectionViewCell.swift */; }; EA1F266428B945070033E859 /* RadioSwatchGroupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F266028B945070033E859 /* RadioSwatchGroupModel.swift */; }; @@ -100,6 +103,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = ""; }; + 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; + 5FC35BE428D51413004EBEAC /* ButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModel.swift; sourceTree = ""; }; EA1F265B28B944F00033E859 /* CollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionView.swift; sourceTree = ""; }; EA1F265C28B944F00033E859 /* CollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = ""; }; EA1F266028B945070033E859 /* RadioSwatchGroupModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSwatchGroupModel.swift; sourceTree = ""; }; @@ -209,6 +215,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 5FC35BE128D513EB004EBEAC /* Button */ = { + isa = PBXGroup; + children = ( + 5FC35BE228D51405004EBEAC /* Button.swift */, + 5FC35BE428D51413004EBEAC /* ButtonModel.swift */, + ); + path = Button; + sourceTree = ""; + }; EA1F265F28B945070033E859 /* RadioSwatch */ = { isa = PBXGroup; children = ( @@ -283,6 +298,7 @@ EA33619D288B1E330071C351 /* Components */ = { isa = PBXGroup; children = ( + 5FC35BE128D513EB004EBEAC /* Button */, EAF7F092289985E200B287F5 /* Checkbox */, EA3362412892EF700071C351 /* Label */, EA89200B28B530F0006B9984 /* RadioBox */, @@ -334,6 +350,7 @@ EA3361C8289054C50071C351 /* Surfaceable.swift */, EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */, EAB1D2CC28ABE76000DAE764 /* Withable.swift */, + 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */, ); path = Protocols; sourceTree = ""; @@ -630,6 +647,7 @@ EAB1D29E28A5619500DAE764 /* RadioButtonGroupModel.swift in Sources */, EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */, EA3C3B4C2894823E000CA526 /* AnyProxy.swift in Sources */, + 5FC35BE528D51414004EBEAC /* ButtonModel.swift in Sources */, EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */, EAB1D29A28A5611D00DAE764 /* SelectorGroupModelable.swift in Sources */, EAF7F0BB289D80ED00B287F5 /* Modelable.swift in Sources */, @@ -647,6 +665,7 @@ EA89200828B526E0006B9984 /* CheckboxGroupModel.swift in Sources */, EA3361B6288B2A410071C351 /* Control.swift in Sources */, EAB1D2A328A5994800DAE764 /* Debuggable.swift in Sources */, + 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */, EAF7F0B7289C12A600B287F5 /* UITapGestureRecognizer.swift in Sources */, EA3362452892F9130071C351 /* Labelable.swift in Sources */, EA3361AD288B26190071C351 /* DataTrackable.swift in Sources */, @@ -659,6 +678,7 @@ EA3361A8288B23300071C351 /* UIColor.swift in Sources */, EA1F266428B945070033E859 /* RadioSwatchGroupModel.swift in Sources */, EA1F266628B945070033E859 /* RadioSwatchGroup.swift in Sources */, + 5FC35BE328D51405004EBEAC /* Button.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/VDS/Components/Button/Button.swift b/VDS/Components/Button/Button.swift new file mode 100644 index 00000000..622c5b2b --- /dev/null +++ b/VDS/Components/Button/Button.swift @@ -0,0 +1,219 @@ +// +// Button.swift +// VDS +// +// Created by Jarrod Courtney on 9/16/22. +// + +import Foundation +import UIKit +import VDSColorTokens +import VDSFormControlsTokens +import Combine + +public class Button:ButtonBase{} + +open class ButtonBase: UIButton, ModelHandlerable, ViewProtocol, Resettable { + + //-------------------------------------------------- + // MARK: - Combine Properties + //-------------------------------------------------- + @Published public var model: ModelType = ModelType() + public var modelPublisher: Published.Publisher { $model } + public var subscribers = Set() + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + private var mainStackView: UIStackView = { + return UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.alignment = .top + $0.axis = .vertical + $0.spacing = 0 + } + }() + + private var buttonWidthConstraint: NSLayoutConstraint? + private var buttonHeightConstraint: NSLayoutConstraint? + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + @Proxy(\.model.surface) + open var surface: Surface + + @Proxy(\.model.use) + open var use: Use + + @Proxy(\.model.disabled) + open var disabled: Bool { + didSet { + self.isEnabled = !disabled + } + } + + open var buttonWidth: CGFloat = 200 { + didSet { + self.updateView(viewModel: model) + } + } + + open override var isEnabled: Bool { + get { !model.disabled } + set { + //create local vars for clear coding + let disabled = !newValue + if model.disabled != disabled { + model.disabled = disabled + } + isUserInteractionEnabled = isEnabled + } + } + + @Proxy(\.model.attributes) + open var attributes: [any LabelAttributeModel]? + + //-------------------------------------------------- + // MARK: - Configuration Properties + //-------------------------------------------------- + private var buttonBackgroundColorConfiguration: UseableColorConfiguration = { + return UseableColorConfiguration().with { + $0.primary.enabled.lightColor = VDSColor.backgroundPrimaryDark + $0.primary.enabled.darkColor = VDSColor.backgroundPrimaryLight + $0.primary.disabled.lightColor = VDSColor.interactiveDisabledOnlight + $0.primary.disabled.darkColor = VDSColor.interactiveDisabledOndark + + $0.secondary.enabled.lightColor = UIColor.clear + $0.secondary.enabled.darkColor = UIColor.clear + $0.secondary.disabled.lightColor = UIColor.clear + $0.secondary.disabled.darkColor = UIColor.clear + } + }() + + private var buttonBorderColorConfiguration: UseableColorConfiguration = { + return UseableColorConfiguration().with { + $0.primary.enabled.lightColor = VDSColor.elementsPrimaryOndark + $0.primary.enabled.darkColor = VDSColor.elementsPrimaryOnlight + $0.primary.disabled.lightColor = VDSColor.interactiveDisabledOnlight + $0.primary.disabled.darkColor = VDSColor.interactiveDisabledOndark + + $0.secondary.enabled.lightColor = VDSColor.elementsPrimaryOnlight + $0.secondary.enabled.darkColor = VDSColor.elementsPrimaryOndark + $0.secondary.disabled.lightColor = VDSColor.interactiveDisabledOnlight + $0.secondary.disabled.darkColor = VDSColor.interactiveDisabledOndark + } + }() + + private var buttonTitleColorConfiguration: UseableColorConfiguration = { + return UseableColorConfiguration().with { + $0.primary.enabled.lightColor = VDSColor.elementsPrimaryOndark + $0.primary.enabled.darkColor = VDSColor.elementsPrimaryOnlight + $0.primary.disabled.lightColor = VDSColor.elementsPrimaryOndark + $0.primary.disabled.darkColor = VDSColor.elementsPrimaryOnlight + + $0.secondary.enabled.lightColor = VDSColor.elementsPrimaryOnlight + $0.secondary.enabled.darkColor = VDSColor.elementsPrimaryOndark + $0.secondary.disabled.lightColor = VDSColor.interactiveDisabledOnlight + $0.secondary.disabled.darkColor = VDSColor.interactiveDisabledOndark + } + }() + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + initialSetup() + } + + public required init(with model: ModelType) { + super.init(frame: .zero) + initialSetup() + set(with: model) + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + initialSetup() + set(with: model) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + //-------------------------------------------------- + // MARK: - Public Functions + //-------------------------------------------------- + open func initialSetup() { + backgroundColor = .clear + translatesAutoresizingMaskIntoConstraints = false + accessibilityCustomActions = [] + accessibilityTraits = .staticText + setupUpdateView() + setup() + } + + open func setup() { + self.translatesAutoresizingMaskIntoConstraints = false + mainStackView.addArrangedSubview(self) + mainStackView.spacing = 12 + } + + open func reset() { +// text = nil + accessibilityCustomActions = [] + accessibilityTraits = .staticText + } + + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + open func updateView(viewModel: ModelType) { + if let text = viewModel.text { + self.setTitle(text, for: .normal) + } else { + self.setTitle("No ViewModel Text", for: .normal) + } + let backgroundColor = buttonBackgroundColorConfiguration.getColor(viewModel) + let borderColor = buttonBorderColorConfiguration.getColor(viewModel) + let borderWidth = viewModel.use == .secondary ? 1.0 : 0.0 + let titleColor = buttonTitleColorConfiguration.getColor(viewModel) + + self.titleLabel?.font = TypographicalStyle.BoldBodyLarge.font + self.backgroundColor = backgroundColor + self.setTitleColor(titleColor, for: .normal) + self.layer.borderColor = borderColor.cgColor + self.layer.cornerRadius = self.frame.size.height / 2 + self.layer.borderWidth = borderWidth + let buttonHeight : CGFloat = 44.0 + + if buttonWidthConstraint == nil { + buttonWidthConstraint = self.widthAnchor.constraint(equalToConstant: buttonWidth) + } else { + buttonWidthConstraint?.constant = buttonWidth + } + buttonWidthConstraint?.isActive = true + + if buttonHeightConstraint == nil { + buttonHeightConstraint = self.heightAnchor.constraint(equalToConstant: buttonHeight) + } else { + buttonHeightConstraint?.constant = buttonHeight + } + buttonHeightConstraint?.isActive = true + + } + + private class UseableColorConfiguration : Colorable { + public var primary = DisabledSurfaceColorConfiguration() + public var secondary = DisabledSurfaceColorConfiguration() + + required public init(){} + + public func getColor(_ viewModel: ModelType) -> UIColor { + return viewModel.use == .primary ? primary.getColor(viewModel) : secondary.getColor(viewModel) + } + } +} diff --git a/VDS/Components/Button/ButtonModel.swift b/VDS/Components/Button/ButtonModel.swift new file mode 100644 index 00000000..3a766043 --- /dev/null +++ b/VDS/Components/Button/ButtonModel.swift @@ -0,0 +1,42 @@ +// +// ButtonModel.swift +// VDS +// +// Created by Jarrod Courtney on 9/16/22. +// + +import Foundation +import UIKit + +public protocol ButtonModel: Modelable, Useable, Equatable, AnyEquatable { + var text: String? { get set } + var attributes: [any LabelAttributeModel]? { get set } +} + +public struct DefaultButtonModel: ButtonModel { + + public static func == (lhs: DefaultButtonModel, rhs: DefaultButtonModel) -> Bool { + lhs.isEqual(rhs) + } + + public func isEqual(_ equatable: DefaultButtonModel) -> Bool { + return id == equatable.id + && attributes == equatable.attributes + && text == equatable.text + && surface == equatable.surface + && use == equatable.use + && typograpicalStyle == equatable.typograpicalStyle + && disabled == equatable.disabled + && buttonWidth == equatable.buttonWidth + } + + public var id = UUID() + public var text: String? + public var attributes: [any LabelAttributeModel]? + public var typograpicalStyle: TypographicalStyle = .BoldBodyLarge + public var surface: Surface = .light + public var use: Use = .primary + public var disabled: Bool = false + public var buttonWidth: CGFloat? + public init(){} +} diff --git a/VDS/Protocols/Useable.swift b/VDS/Protocols/Useable.swift new file mode 100644 index 00000000..077ceb02 --- /dev/null +++ b/VDS/Protocols/Useable.swift @@ -0,0 +1,21 @@ +// +// Useable.swift +// VDS +// +// Created by Jarrod Courtney on 9/22/22. +// + +import Foundation +import UIKit +import VDSColorTokens + +public enum Use: String, Codable, Equatable { + case primary, secondary + public var color: UIColor { + return self == .primary ? VDSColor.backgroundPrimaryDark : .clear + } +} + +public protocol Useable { + var use: Use { get set } +} From a22bbbc2b3efdd44adec7175d8c3f8f5b5195c17 Mon Sep 17 00:00:00 2001 From: Jarrod Courtney Date: Fri, 23 Sep 2022 13:42:42 -0500 Subject: [PATCH 2/3] added buttonSize selector Signed-off-by: Jarrod Courtney --- VDS/Components/Button/Button.swift | 51 ++++++++++++++++++++++--- VDS/Components/Button/ButtonModel.swift | 5 +++ VDS/Protocols/Useable.swift | 22 +++++++++++ 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/VDS/Components/Button/Button.swift b/VDS/Components/Button/Button.swift index 622c5b2b..85b7a7d5 100644 --- a/VDS/Components/Button/Button.swift +++ b/VDS/Components/Button/Button.swift @@ -39,13 +39,29 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - + private let PaddingOneHalf: CGFloat = 2 + private let PaddingOne: CGFloat = 4 + private let PaddingTwo: CGFloat = 8 + private let PaddingThree: CGFloat = 12 + private let PaddingFour: CGFloat = 16 + private let PaddingFive: CGFloat = 24 + private let PaddingEight: CGFloat = 32 + private let PaddingTen: CGFloat = 40 + private let PaddingTwelve: CGFloat = 48 + private let PaddingEighteen: CGFloat = 72 + @Proxy(\.model.surface) open var surface: Surface @Proxy(\.model.use) open var use: Use + open var buttonSize: Use.Size = .large { + didSet { + model.buttonSize = buttonSize + } + } + @Proxy(\.model.disabled) open var disabled: Bool { didSet { @@ -181,19 +197,20 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP let borderColor = buttonBorderColorConfiguration.getColor(viewModel) let borderWidth = viewModel.use == .secondary ? 1.0 : 0.0 let titleColor = buttonTitleColorConfiguration.getColor(viewModel) + let buttonHeight = viewModel.buttonSize.getHeight() + let minWidth = buttonWidth >= viewModel.buttonSize.minimumWidth() ? buttonWidth : viewModel.buttonSize.minimumWidth() - self.titleLabel?.font = TypographicalStyle.BoldBodyLarge.font + self.titleLabel?.font = viewModel.buttonSize == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font self.backgroundColor = backgroundColor self.setTitleColor(titleColor, for: .normal) self.layer.borderColor = borderColor.cgColor - self.layer.cornerRadius = self.frame.size.height / 2 + self.layer.cornerRadius = buttonHeight / 2 self.layer.borderWidth = borderWidth - let buttonHeight : CGFloat = 44.0 if buttonWidthConstraint == nil { - buttonWidthConstraint = self.widthAnchor.constraint(equalToConstant: buttonWidth) + buttonWidthConstraint = self.widthAnchor.constraint(equalToConstant: minWidth) } else { - buttonWidthConstraint?.constant = buttonWidth + buttonWidthConstraint?.constant = minWidth } buttonWidthConstraint?.isActive = true @@ -204,8 +221,13 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP } buttonHeightConstraint?.isActive = true + contentEdgeInsets = getContentEdgeInsets(viewModel: viewModel) } + //-------------------------------------------------- + // MARK: - PRIVATE + //-------------------------------------------------- + private class UseableColorConfiguration : Colorable { public var primary = DisabledSurfaceColorConfiguration() public var secondary = DisabledSurfaceColorConfiguration() @@ -216,4 +238,21 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP return viewModel.use == .primary ? primary.getColor(viewModel) : secondary.getColor(viewModel) } } + + private func getContentEdgeInsets(viewModel: ModelType) -> UIEdgeInsets { + var verticalPadding = 0.0 + var horizontalPadding = 0.0 + switch viewModel.buttonSize { + case .large: + verticalPadding = PaddingThree + horizontalPadding = PaddingFive + break + case .small: + verticalPadding = PaddingTwo + horizontalPadding = PaddingFour + break + } + return UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding) + } + } diff --git a/VDS/Components/Button/ButtonModel.swift b/VDS/Components/Button/ButtonModel.swift index 3a766043..c7eee689 100644 --- a/VDS/Components/Button/ButtonModel.swift +++ b/VDS/Components/Button/ButtonModel.swift @@ -11,6 +11,9 @@ import UIKit public protocol ButtonModel: Modelable, Useable, Equatable, AnyEquatable { var text: String? { get set } var attributes: [any LabelAttributeModel]? { get set } + var buttonWidth: CGFloat? { get set } + var buttonSize: Use.Size { get set } + var use: Use { get set } } public struct DefaultButtonModel: ButtonModel { @@ -28,6 +31,7 @@ public struct DefaultButtonModel: ButtonModel { && typograpicalStyle == equatable.typograpicalStyle && disabled == equatable.disabled && buttonWidth == equatable.buttonWidth + && buttonSize == equatable.buttonSize } public var id = UUID() @@ -38,5 +42,6 @@ public struct DefaultButtonModel: ButtonModel { public var use: Use = .primary public var disabled: Bool = false public var buttonWidth: CGFloat? + public var buttonSize: Use.Size = .large public init(){} } diff --git a/VDS/Protocols/Useable.swift b/VDS/Protocols/Useable.swift index 077ceb02..62215ca8 100644 --- a/VDS/Protocols/Useable.swift +++ b/VDS/Protocols/Useable.swift @@ -14,6 +14,28 @@ public enum Use: String, Codable, Equatable { public var color: UIColor { return self == .primary ? VDSColor.backgroundPrimaryDark : .clear } + public enum Size: String, Codable { + case large + case small + + func getHeight() -> CGFloat { + switch self { + case .large: + return 44 + case .small: + return 32 + } + } + + func minimumWidth() -> CGFloat { + switch self { + case .large: + return 76 + case .small: + return 60 + } + } + } } public protocol Useable { From 7687c42fe2d80ecabf9f128cecda9c2993931d1c Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 23 Sep 2022 16:38:52 -0500 Subject: [PATCH 3/3] minor fixes Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 2 +- VDS/Components/Button/Button.swift | 173 +++++++++++++----------- VDS/Components/Button/ButtonModel.swift | 35 ++--- VDS/Protocols/Useable.swift | 25 ---- 4 files changed, 106 insertions(+), 129 deletions(-) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 416c3bdb..a34f8806 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -302,8 +302,8 @@ EA33619D288B1E330071C351 /* Components */ = { isa = PBXGroup; children = ( - 5FC35BE128D513EB004EBEAC /* Button */, EA4DB2FE28DCBC1900103EE3 /* Badge */, + 5FC35BE128D513EB004EBEAC /* Button */, EAF7F092289985E200B287F5 /* Checkbox */, EA3362412892EF700071C351 /* Label */, EA89200B28B530F0006B9984 /* RadioBox */, diff --git a/VDS/Components/Button/Button.swift b/VDS/Components/Button/Button.swift index 85b7a7d5..bdddb0a9 100644 --- a/VDS/Components/Button/Button.swift +++ b/VDS/Components/Button/Button.swift @@ -25,55 +25,34 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - private var mainStackView: UIStackView = { - return UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.alignment = .top - $0.axis = .vertical - $0.spacing = 0 - } - }() - - private var buttonWidthConstraint: NSLayoutConstraint? - private var buttonHeightConstraint: NSLayoutConstraint? + private var minWidthConstraint: NSLayoutConstraint? + private var widthConstraint: NSLayoutConstraint? + private var heightConstraint: NSLayoutConstraint? + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - private let PaddingOneHalf: CGFloat = 2 - private let PaddingOne: CGFloat = 4 - private let PaddingTwo: CGFloat = 8 - private let PaddingThree: CGFloat = 12 - private let PaddingFour: CGFloat = 16 - private let PaddingFive: CGFloat = 24 - private let PaddingEight: CGFloat = 32 - private let PaddingTen: CGFloat = 40 - private let PaddingTwelve: CGFloat = 48 - private let PaddingEighteen: CGFloat = 72 - @Proxy(\.model.surface) open var surface: Surface - @Proxy(\.model.use) - open var use: Use - - open var buttonSize: Use.Size = .large { - didSet { - model.buttonSize = buttonSize - } - } - @Proxy(\.model.disabled) open var disabled: Bool { didSet { - self.isEnabled = !disabled + isEnabled = !disabled } } + + @Proxy(\.model.text) + open var text: String? - open var buttonWidth: CGFloat = 200 { - didSet { - self.updateView(viewModel: model) - } - } + @Proxy(\.model.use) + open var use: Use + + @Proxy(\.model.size) + open var size: ButtonSize + + @Proxy(\.model.width) + open var width: CGFloat? open override var isEnabled: Bool { get { !model.disabled } @@ -86,10 +65,7 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP isUserInteractionEnabled = isEnabled } } - - @Proxy(\.model.attributes) - open var attributes: [any LabelAttributeModel]? - + //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- @@ -173,13 +149,21 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP } open func setup() { - self.translatesAutoresizingMaskIntoConstraints = false - mainStackView.addArrangedSubview(self) - mainStackView.spacing = 12 + translatesAutoresizingMaskIntoConstraints = false + titleLabel?.adjustsFontSizeToFitWidth = false + titleLabel?.lineBreakMode = .byTruncatingTail + + //only 1 of the 2 widths can be on at the same time + widthConstraint = widthAnchor.constraint(equalToConstant: 0) + minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: model.size.minimumWidth) + + //height + heightConstraint = heightAnchor.constraint(equalToConstant: model.size.height) + heightConstraint?.isActive = true } open func reset() { -// text = nil + model = ModelType() accessibilityCustomActions = [] accessibilityTraits = .staticText } @@ -188,40 +172,41 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP // MARK: - Overrides //-------------------------------------------------- open func updateView(viewModel: ModelType) { - if let text = viewModel.text { - self.setTitle(text, for: .normal) - } else { - self.setTitle("No ViewModel Text", for: .normal) - } - let backgroundColor = buttonBackgroundColorConfiguration.getColor(viewModel) + + let bgColor = buttonBackgroundColorConfiguration.getColor(viewModel) let borderColor = buttonBorderColorConfiguration.getColor(viewModel) - let borderWidth = viewModel.use == .secondary ? 1.0 : 0.0 let titleColor = buttonTitleColorConfiguration.getColor(viewModel) - let buttonHeight = viewModel.buttonSize.getHeight() - let minWidth = buttonWidth >= viewModel.buttonSize.minimumWidth() ? buttonWidth : viewModel.buttonSize.minimumWidth() - - self.titleLabel?.font = viewModel.buttonSize == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font - self.backgroundColor = backgroundColor - self.setTitleColor(titleColor, for: .normal) - self.layer.borderColor = borderColor.cgColor - self.layer.cornerRadius = buttonHeight / 2 - self.layer.borderWidth = borderWidth + let borderWidth = viewModel.use == .secondary ? 1.0 : 0.0 + let buttonHeight = viewModel.size.height + let cornerRadius = buttonHeight / 2 + let minWidth = viewModel.size.minimumWidth + let font = viewModel.size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font + let edgeInsets = viewModel.size.edgeInsets - if buttonWidthConstraint == nil { - buttonWidthConstraint = self.widthAnchor.constraint(equalToConstant: minWidth) + if let text = viewModel.text { + setTitle(text, for: .normal) } else { - buttonWidthConstraint?.constant = minWidth + setTitle("No ViewModel Text", for: .normal) } - buttonWidthConstraint?.isActive = true - - if buttonHeightConstraint == nil { - buttonHeightConstraint = self.heightAnchor.constraint(equalToConstant: buttonHeight) - } else { - buttonHeightConstraint?.constant = buttonHeight - } - buttonHeightConstraint?.isActive = true + titleLabel?.font = font + backgroundColor = bgColor + setTitleColor(titleColor, for: .normal) + layer.borderColor = borderColor.cgColor + layer.cornerRadius = cornerRadius + layer.borderWidth = borderWidth + contentEdgeInsets = edgeInsets - contentEdgeInsets = getContentEdgeInsets(viewModel: viewModel) + minWidthConstraint?.constant = minWidth + heightConstraint?.constant = buttonHeight + + if let width = viewModel.width, width > minWidth { + widthConstraint?.constant = width + widthConstraint?.isActive = true + minWidthConstraint?.isActive = false + } else { + widthConstraint?.isActive = false + minWidthConstraint?.isActive = true + } } //-------------------------------------------------- @@ -237,22 +222,52 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP public func getColor(_ viewModel: ModelType) -> UIColor { return viewModel.use == .primary ? primary.getColor(viewModel) : secondary.getColor(viewModel) } + } + +} + +extension ButtonSize { + + public var height: CGFloat { + switch self { + case .large: + return 44 + case .small: + return 32 + } } - private func getContentEdgeInsets(viewModel: ModelType) -> UIEdgeInsets { + public var minimumWidth: CGFloat { + switch self { + case .large: + return 76 + case .small: + return 60 + } + } + + public var edgeInsets: UIEdgeInsets { var verticalPadding = 0.0 var horizontalPadding = 0.0 - switch viewModel.buttonSize { + switch self { case .large: - verticalPadding = PaddingThree - horizontalPadding = PaddingFive + verticalPadding = 12 + horizontalPadding = 24 break case .small: - verticalPadding = PaddingTwo - horizontalPadding = PaddingFour + verticalPadding = 8 + horizontalPadding = 16 break } return UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding) } } + +extension Use { + + public var color: UIColor { + return self == .primary ? VDSColor.backgroundPrimaryDark : .clear + } + +} diff --git a/VDS/Components/Button/ButtonModel.swift b/VDS/Components/Button/ButtonModel.swift index c7eee689..d5b7f0d9 100644 --- a/VDS/Components/Button/ButtonModel.swift +++ b/VDS/Components/Button/ButtonModel.swift @@ -8,40 +8,27 @@ import Foundation import UIKit -public protocol ButtonModel: Modelable, Useable, Equatable, AnyEquatable { +public enum ButtonSize: String, Codable, CaseIterable { + case large + case small +} + +public protocol ButtonModel: Modelable, Useable { var text: String? { get set } - var attributes: [any LabelAttributeModel]? { get set } - var buttonWidth: CGFloat? { get set } - var buttonSize: Use.Size { get set } + var width: CGFloat? { get set } + var size: ButtonSize { get set } var use: Use { get set } } public struct DefaultButtonModel: ButtonModel { - - public static func == (lhs: DefaultButtonModel, rhs: DefaultButtonModel) -> Bool { - lhs.isEqual(rhs) - } - - public func isEqual(_ equatable: DefaultButtonModel) -> Bool { - return id == equatable.id - && attributes == equatable.attributes - && text == equatable.text - && surface == equatable.surface - && use == equatable.use - && typograpicalStyle == equatable.typograpicalStyle - && disabled == equatable.disabled - && buttonWidth == equatable.buttonWidth - && buttonSize == equatable.buttonSize - } - + public var id = UUID() public var text: String? - public var attributes: [any LabelAttributeModel]? public var typograpicalStyle: TypographicalStyle = .BoldBodyLarge public var surface: Surface = .light public var use: Use = .primary public var disabled: Bool = false - public var buttonWidth: CGFloat? - public var buttonSize: Use.Size = .large + public var width: CGFloat? + public var size: ButtonSize = .large public init(){} } diff --git a/VDS/Protocols/Useable.swift b/VDS/Protocols/Useable.swift index 62215ca8..3f60ae18 100644 --- a/VDS/Protocols/Useable.swift +++ b/VDS/Protocols/Useable.swift @@ -11,31 +11,6 @@ import VDSColorTokens public enum Use: String, Codable, Equatable { case primary, secondary - public var color: UIColor { - return self == .primary ? VDSColor.backgroundPrimaryDark : .clear - } - public enum Size: String, Codable { - case large - case small - - func getHeight() -> CGFloat { - switch self { - case .large: - return 44 - case .small: - return 32 - } - } - - func minimumWidth() -> CGFloat { - switch self { - case .large: - return 76 - case .small: - return 60 - } - } - } } public protocol Useable {