diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 8df5a950..80db9b43 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; }; 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; EA0FC2C62914222900DF80B4 /* ButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0FC2C52914222900DF80B4 /* ButtonGroup.swift */; }; + EA1DA1CB2A2E36DC001C51D2 /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1DA1CA2A2E36DC001C51D2 /* SelectorBase.swift */; }; EA1F266528B945070033E859 /* RadioSwatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F266128B945070033E859 /* RadioSwatch.swift */; }; EA1F266628B945070033E859 /* RadioSwatchGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F266228B945070033E859 /* RadioSwatchGroup.swift */; }; EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA297A5429FB07760031ED56 /* TooltipLabelAttribute.swift */; }; @@ -56,7 +57,7 @@ EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */; }; EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; }; EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200528B526D6006B9984 /* CheckboxGroup.swift */; }; - EA89201328B568D8006B9984 /* RadioBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201228B568D8006B9984 /* RadioBox.swift */; }; + EA89201328B568D8006B9984 /* RadioBoxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201228B568D8006B9984 /* RadioBoxItem.swift */; }; EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201428B56CF4006B9984 /* RadioBoxGroup.swift */; }; EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA978EC4291D6AFE00ACC883 /* AnyLabelAttribute.swift */; }; EA985BE629688F6A00F2FF2E /* TileletBadgeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985BE529688F6A00F2FF2E /* TileletBadgeModel.swift */; }; @@ -96,6 +97,9 @@ EAB5FEF829393A7200998C17 /* ButtonGroupConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */; }; EAB5FF0129424ACB00998C17 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FF0029424ACB00998C17 /* UIControl.swift */; }; EABFEB642A26473700C4C106 /* NSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFEB632A26473700C4C106 /* NSAttributedString.swift */; }; + EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC71A1C2A2E155A00E47A9F /* Checkbox.swift */; }; + EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC71A1E2A2E173D00E47A9F /* RadioButton.swift */; }; + EAC71A212A2E1DC000E47A9F /* SelectorItemBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC71A202A2E1DC000E47A9F /* SelectorItemBase.swift */; }; EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */; }; EAC9257D29119B5400091998 /* TextLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9257C29119B5400091998 /* TextLink.swift */; }; EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925822911B35300091998 /* TextLinkCaret.swift */; }; @@ -105,7 +109,7 @@ EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; }; EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9829D4850E00101452 /* Clickable.swift */; }; EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9A29DB1A6000101452 /* Changeable.swift */; }; - EAF7F0952899861000B287F5 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* Checkbox.swift */; }; + EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* CheckboxItem.swift */; }; EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0992899B17200B287F5 /* CATransaction.swift */; }; EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F09F289AB7EC00B287F5 /* View.swift */; }; EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0A1289AFB3900B287F5 /* Errorable.swift */; }; @@ -118,7 +122,7 @@ EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0B2289B1ADC00B287F5 /* ActionLabelAttribute.swift */; }; EAF7F0B7289C12A600B287F5 /* UITapGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */; }; EAF7F0B9289C139800B287F5 /* ColorConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */; }; - EAF7F11728A1475A00B287F5 /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F11528A1475A00B287F5 /* RadioButton.swift */; }; + EAF7F11728A1475A00B287F5 /* RadioButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F11528A1475A00B287F5 /* RadioButtonItem.swift */; }; EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F13228A2A16500B287F5 /* AttachmentLabelAttributeModel.swift */; }; /* End PBXBuildFile section */ @@ -139,6 +143,7 @@ 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = ""; }; 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; EA0FC2C52914222900DF80B4 /* ButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroup.swift; sourceTree = ""; }; + EA1DA1CA2A2E36DC001C51D2 /* SelectorBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = ""; }; EA1F266128B945070033E859 /* RadioSwatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSwatch.swift; sourceTree = ""; }; EA1F266228B945070033E859 /* RadioSwatchGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSwatchGroup.swift; sourceTree = ""; }; EA297A5429FB07760031ED56 /* TooltipLabelAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipLabelAttribute.swift; sourceTree = ""; }; @@ -183,7 +188,7 @@ EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+VDSColor.swift"; sourceTree = ""; }; EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = ""; }; EA89200528B526D6006B9984 /* CheckboxGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxGroup.swift; sourceTree = ""; }; - EA89201228B568D8006B9984 /* RadioBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBox.swift; sourceTree = ""; }; + EA89201228B568D8006B9984 /* RadioBoxItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBoxItem.swift; sourceTree = ""; }; EA89201428B56CF4006B9984 /* RadioBoxGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBoxGroup.swift; sourceTree = ""; }; EA978EC4291D6AFE00ACC883 /* AnyLabelAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyLabelAttribute.swift; sourceTree = ""; }; EA985BE529688F6A00F2FF2E /* TileletBadgeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TileletBadgeModel.swift; sourceTree = ""; }; @@ -224,6 +229,9 @@ EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupConstants.swift; sourceTree = ""; }; EAB5FF0029424ACB00998C17 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; EABFEB632A26473700C4C106 /* NSAttributedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSAttributedString.swift; sourceTree = ""; }; + EAC71A1C2A2E155A00E47A9F /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; + EAC71A1E2A2E173D00E47A9F /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; }; + EAC71A202A2E1DC000E47A9F /* SelectorItemBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorItemBase.swift; sourceTree = ""; }; EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupCollectionViewCell.swift; sourceTree = ""; }; EAC9257C29119B5400091998 /* TextLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLink.swift; sourceTree = ""; }; EAC925822911B35300091998 /* TextLinkCaret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLinkCaret.swift; sourceTree = ""; }; @@ -232,7 +240,7 @@ EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = ""; }; EAF1FE9829D4850E00101452 /* Clickable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clickable.swift; sourceTree = ""; }; EAF1FE9A29DB1A6000101452 /* Changeable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Changeable.swift; sourceTree = ""; }; - EAF7F0932899861000B287F5 /* Checkbox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; + EAF7F0932899861000B287F5 /* CheckboxItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxItem.swift; sourceTree = ""; }; EAF7F0992899B17200B287F5 /* CATransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CATransaction.swift; sourceTree = ""; }; EAF7F09F289AB7EC00B287F5 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; EAF7F0A1289AFB3900B287F5 /* Errorable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errorable.swift; sourceTree = ""; }; @@ -245,7 +253,7 @@ EAF7F0B2289B1ADC00B287F5 /* ActionLabelAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionLabelAttribute.swift; sourceTree = ""; }; EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITapGestureRecognizer.swift; sourceTree = ""; }; EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorConfiguration.swift; sourceTree = ""; }; - EAF7F11528A1475A00B287F5 /* RadioButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; }; + EAF7F11528A1475A00B287F5 /* RadioButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonItem.swift; sourceTree = ""; }; EAF7F13228A2A16500B287F5 /* AttachmentLabelAttributeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentLabelAttributeModel.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -464,8 +472,10 @@ EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */, EA3361B5288B2A410071C351 /* Control.swift */, EAF7F09F289AB7EC00B287F5 /* View.swift */, - EA4DB18428CA967F00103EE3 /* SelectorGroupHandlerBase.swift */, EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */, + EA4DB18428CA967F00103EE3 /* SelectorGroupHandlerBase.swift */, + EAC71A202A2E1DC000E47A9F /* SelectorItemBase.swift */, + EA1DA1CA2A2E36DC001C51D2 /* SelectorBase.swift */, ); path = Classes; sourceTree = ""; @@ -580,7 +590,7 @@ EA89200B28B530F0006B9984 /* RadioBox */ = { isa = PBXGroup; children = ( - EA89201228B568D8006B9984 /* RadioBox.swift */, + EA89201228B568D8006B9984 /* RadioBoxItem.swift */, EA89201428B56CF4006B9984 /* RadioBoxGroup.swift */, ); path = RadioBox; @@ -679,7 +689,8 @@ EAF7F092289985E200B287F5 /* Checkbox */ = { isa = PBXGroup; children = ( - EAF7F0932899861000B287F5 /* Checkbox.swift */, + EAC71A1C2A2E155A00E47A9F /* Checkbox.swift */, + EAF7F0932899861000B287F5 /* CheckboxItem.swift */, EA89200528B526D6006B9984 /* CheckboxGroup.swift */, ); path = Checkbox; @@ -705,7 +716,8 @@ EAF7F11428A1470D00B287F5 /* RadioButton */ = { isa = PBXGroup; children = ( - EAF7F11528A1475A00B287F5 /* RadioButton.swift */, + EAC71A1E2A2E173D00E47A9F /* RadioButton.swift */, + EAF7F11528A1475A00B287F5 /* RadioButtonItem.swift */, EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */, ); path = RadioButton; @@ -836,7 +848,7 @@ EA297A5729FB0A360031ED56 /* AppleGuidlinesTouchable.swift in Sources */, EA3361C328902D960071C351 /* Toggle.swift in Sources */, EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */, - EA89201328B568D8006B9984 /* RadioBox.swift in Sources */, + EA89201328B568D8006B9984 /* RadioBoxItem.swift in Sources */, EAC9258C2911C9DE00091998 /* InputField.swift in Sources */, EA3362402892EF6C0071C351 /* Label.swift in Sources */, EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */, @@ -854,14 +866,15 @@ EAC925842911C63100091998 /* Colorable.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, + EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */, - EAF7F11728A1475A00B287F5 /* RadioButton.swift in Sources */, + EAF7F11728A1475A00B287F5 /* RadioButtonItem.swift in Sources */, EA985BEE2968A92400F2FF2E /* TitleLockupSubTitleModel.swift in Sources */, EA985BF22968B5BB00F2FF2E /* TitleLockupTextStyle.swift in Sources */, EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */, EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */, - EAF7F0952899861000B287F5 /* Checkbox.swift in Sources */, + EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */, EA985BE82968951C00F2FF2E /* TileletTitleModel.swift in Sources */, EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */, EA985BEA29689B6D00F2FF2E /* TileletSubTitleModel.swift in Sources */, @@ -894,6 +907,7 @@ EA5E3058295105A40082B959 /* Tilelet.swift in Sources */, EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */, EA985C1D296CD13600F2FF2E /* BundleManager.swift in Sources */, + EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */, EA1F266528B945070033E859 /* RadioSwatch.swift in Sources */, EA4DB18528CA967F00103EE3 /* SelectorGroupHandlerBase.swift in Sources */, EAF7F0AB289B13FD00B287F5 /* TextStyleLabelAttribute.swift in Sources */, @@ -922,9 +936,11 @@ EA3361B8288B2AAA0071C351 /* ViewProtocol.swift in Sources */, EA3361BF288B2EA60071C351 /* Handlerable.swift in Sources */, EA3361A8288B23300071C351 /* UIColor.swift in Sources */, + EA1DA1CB2A2E36DC001C51D2 /* SelectorBase.swift in Sources */, EAC9257D29119B5400091998 /* TextLink.swift in Sources */, EA1F266628B945070033E859 /* RadioSwatchGroup.swift in Sources */, EA596ABF2A16B4F500300C4B /* Tabs.swift in Sources */, + EAC71A212A2E1DC000E47A9F /* SelectorItemBase.swift in Sources */, EA985BEC2968A91200F2FF2E /* TitleLockupTitleModel.swift in Sources */, 5FC35BE328D51405004EBEAC /* Button.swift in Sources */, ); diff --git a/VDS/Classes/ColorConfiguration.swift b/VDS/Classes/ColorConfiguration.swift index 9d902a1d..3a4b468e 100644 --- a/VDS/Classes/ColorConfiguration.swift +++ b/VDS/Classes/ColorConfiguration.swift @@ -111,7 +111,6 @@ public class ControlColorConfiguration: KeyColorConfigurable { public typealias KeyType = UIControl.State public typealias ObjectType = Surfaceable & UIControl public var keyColors: [KeyColorConfiguration] = [] - private var lastKeyColor: KeyColorConfiguration? public required init() { } @@ -131,25 +130,12 @@ public class ControlColorConfiguration: KeyColorConfigurable { let state = object.state let surface = object.surface - // find the exact match if let keyColor = keyColors.first(where: {$0.key == state }) { - lastKeyColor = keyColor return keyColor.surfaceConfig.getColor(surface) - - } else if state.contains(.disabled), let keyColor = keyColors.first(where: {$0.key == .disabled }) { - lastKeyColor = keyColor + } else if let keyColor = keyColors.first(where: {$0.key.isSubset(of: state) }) { return keyColor.surfaceConfig.getColor(surface) - - } else if state.contains(.highlighted), let keyColor = keyColors.first(where: {$0.key == .highlighted }) { - lastKeyColor = keyColor - return keyColor.surfaceConfig.getColor(surface) - } else { - if let lastKeyColor { - return lastKeyColor.surfaceConfig.getColor(surface) - } else { - return .clear - } + return .clear } } } diff --git a/VDS/Classes/SelectorBase.swift b/VDS/Classes/SelectorBase.swift new file mode 100644 index 00000000..9ba1066c --- /dev/null +++ b/VDS/Classes/SelectorBase.swift @@ -0,0 +1,108 @@ +// +// SelectorBase.swift +// VDS +// +// Created by Matt Bruce on 6/5/23. +// + +import Foundation +import Combine +import VDSColorTokens +import VDSFormControlsTokens + +public protocol SelectorControlable: Control, Changeable { + var showError: Bool { get set } + var size: CGSize { get set } + var backgroundColorConfiguration: ControlColorConfiguration { get set } + var borderColorConfiguration: ControlColorConfiguration { get set } + var selectorColorConfiguration: ControlColorConfiguration { get set } +} + +open class SelectorBase: Control, SelectorControlable { + public var onChangeSubscriber: AnyCancellable? { + willSet { + if let onChangeSubscriber { + onChangeSubscriber.cancel() + } + } + } + + open var size = CGSize(width: 20, height: 20) { didSet { setNeedsUpdate() }} + + var _showError: Bool = false + open var showError: Bool { + get { _showError } + set { + if !isSelected && _showError != newValue { + _showError = newValue + setNeedsUpdate() + } + } + } + + open override var state: UIControl.State { + get { + var state = super.state + if showError { + state.insert(.error) + } + return state + } + } + + open var backgroundColorConfiguration = ControlColorConfiguration() { didSet { setNeedsUpdate() }} + + open var borderColorConfiguration = ControlColorConfiguration() { didSet { setNeedsUpdate() }} + + open var selectorColorConfiguration = ControlColorConfiguration() { didSet { setNeedsUpdate() }} + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + private var selectorHeightConstraint: NSLayoutConstraint? + private var selectorWidthConstraint: NSLayoutConstraint? + + internal var shapeLayer: CAShapeLayer? + + open override func initialSetup() { + super.initialSetup() + onClick = { control in + control.toggle() + } + } + + open override func setup() { + super.setup() + let layoutGuide = UILayoutGuide() + addLayoutGuide(layoutGuide) + + selectorHeightConstraint = layoutGuide.heightAnchor.constraint(equalToConstant: size.height) + selectorHeightConstraint?.isActive = true + + selectorWidthConstraint = layoutGuide.widthAnchor.constraint(equalToConstant: size.width) + selectorWidthConstraint?.isActive = true + + NSLayoutConstraint.activate([ + layoutGuide.topAnchor.constraint(equalTo: topAnchor), + layoutGuide.bottomAnchor.constraint(equalTo: bottomAnchor), + layoutGuide.leadingAnchor.constraint(equalTo: leadingAnchor), + layoutGuide.trailingAnchor.constraint(equalTo: trailingAnchor)]) + + layer.cornerRadius = 2.0 + layer.borderWidth = VDSFormControls.widthBorder + + } + + open func toggle() { } + + open override func updateView() { + super.updateView() + + selectorHeightConstraint?.constant = size.height + selectorWidthConstraint?.constant = size.width + + setNeedsLayout() + layoutIfNeeded() + } + +} diff --git a/VDS/Classes/SelectorItemBase.swift b/VDS/Classes/SelectorItemBase.swift new file mode 100644 index 00000000..b9291e7c --- /dev/null +++ b/VDS/Classes/SelectorItemBase.swift @@ -0,0 +1,276 @@ +// +// SelectorItemBase.swift +// VDS +// +// Created by Matt Bruce on 6/5/23. +// + +import Foundation +import UIKit +import Combine +import VDSColorTokens +import VDSFormControlsTokens + +/// Checkboxes are a multi-select component through which a customer indicates a choice. If a binary choice, the component is a checkbox. If the choice has multiple options, the component is a ``CheckboxGroup``. +open class SelectorItemBase: Control, Errorable, Changeable { + + //-------------------------------------------------- + // 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: - Private Properties + //-------------------------------------------------- + private var shouldShowError: Bool { + guard showError && !disabled && errorText?.isEmpty == false else { return false } + return true + } + + private var shouldShowLabels: Bool { + guard labelText?.isEmpty == false || childText?.isEmpty == false || labelAttributedText?.string.isEmpty == false || childAttributedText?.string.isEmpty == false else { return false } + return true + } + + private var mainStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.alignment = .top + $0.axis = .vertical + } + + private var selectorStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.alignment = .top + $0.axis = .horizontal + } + + private var selectorLabelStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .vertical + } + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + open var onChangeSubscriber: AnyCancellable? { + willSet { + if let onChangeSubscriber { + onChangeSubscriber.cancel() + } + } + } + + open var label = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.textPosition = .left + $0.textStyle = .boldBodyLarge + } + + open var childLabel = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.textPosition = .left + $0.textStyle = .bodyLarge + } + + open var errorLabel = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.textPosition = .left + $0.textStyle = .bodyMedium + } + + open var selectorView = Selector() + + open override var isSelected: Bool { didSet { setNeedsUpdate() }} + + open var labelText: String? { didSet { setNeedsUpdate() }} + + open var labelTextAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() }} + + open var labelAttributedText: NSAttributedString? { + didSet { + label.useAttributedText = !(labelAttributedText?.string.isEmpty ?? true) + label.attributedText = labelAttributedText + setNeedsUpdate() + } + } + + open var childText: String? { didSet { setNeedsUpdate() }} + + open var childTextAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() }} + + open var childAttributedText: NSAttributedString? { + didSet { + childLabel.useAttributedText = !(childAttributedText?.string.isEmpty ?? true) + childLabel.attributedText = childAttributedText + setNeedsUpdate() + } + } + + var _showError: Bool = false + open var showError: Bool { + get { _showError } + set { + if !isSelected && _showError != newValue { + _showError = newValue + setNeedsUpdate() + } + } + } + + open override var state: UIControl.State { + get { + var state = super.state + if showError { + state.insert(.error) + } + return state + } + } + + open var errorText: String? { didSet { setNeedsUpdate() }} + + open var inputId: String? { didSet { setNeedsUpdate() }} + + open var value: AnyHashable? { didSet { setNeedsUpdate() }} + + //functions + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func initialSetup() { + super.initialSetup() + onClick = { control in + control.toggle() + } + } + + open override func setup() { + super.setup() + + isAccessibilityElement = true + accessibilityTraits = .button + addSubview(mainStackView) + mainStackView.isUserInteractionEnabled = false + + mainStackView.addArrangedSubview(selectorStackView) + mainStackView.addArrangedSubview(errorLabel) + selectorStackView.addArrangedSubview(selectorView) + selectorStackView.addArrangedSubview(selectorLabelStackView) + selectorLabelStackView.addArrangedSubview(label) + selectorLabelStackView.addArrangedSubview(childLabel) + mainStackView.pinToSuperView() + } + + func updateLabels() { + + //deal with labels + if shouldShowLabels { + //add the stackview to hold the 2 labels + //top label + if let labelText { + label.surface = surface + label.disabled = disabled + label.attributes = labelTextAttributes + label.text = labelText + label.isHidden = false + } else if labelAttributedText != nil { + label.isHidden = false + + } else { + label.isHidden = true + } + + //bottom label + if let childText { + childLabel.text = childText + childLabel.surface = surface + childLabel.disabled = disabled + childLabel.attributes = childTextAttributes + childLabel.isHidden = false + + } else if childAttributedText != nil { + childLabel.isHidden = false + + } else { + childLabel.isHidden = true + } + selectorStackView.spacing = 12 + selectorLabelStackView.spacing = 4 + selectorLabelStackView.isHidden = false + + } else { + selectorStackView.spacing = 0 + selectorLabelStackView.spacing = 0 + selectorLabelStackView.isHidden = true + } + + //either add/remove the error from the main stack + if let errorText, shouldShowError { + errorLabel.text = errorText + errorLabel.surface = surface + errorLabel.disabled = disabled + mainStackView.spacing = 8 + errorLabel.isHidden = false + } else { + mainStackView.spacing = 0 + errorLabel.isHidden = true + } + } + + open override func reset() { + super.reset() + shouldUpdateView = false + label.reset() + childLabel.reset() + errorLabel.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 + value = nil + isSelected = false + + shouldUpdateView = true + setNeedsUpdate() + } + + /// This will checkbox the state of the Selector and execute the actionBlock if provided. + open func toggle() {} + + //-------------------------------------------------- + // MARK: - State + //-------------------------------------------------- + open override func updateView() { + updateLabels() + selectorView.showError = showError + selectorView.isSelected = isSelected + selectorView.isHighlighted = isHighlighted + selectorView.disabled = disabled + selectorView.surface = surface + updateAccessibilityLabel() + } + + open override func updateAccessibilityLabel() { + setAccessibilityLabel(for: [label, childLabel, errorLabel]) + } +} diff --git a/VDS/Components/Badge/Badge.swift b/VDS/Components/Badge/Badge.swift index 58562fd5..0d5fb623 100644 --- a/VDS/Components/Badge/Badge.swift +++ b/VDS/Components/Badge/Badge.swift @@ -86,7 +86,7 @@ open class Badge: View { //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- - private var backgroundColorConfig: AnyColorable = { + private var backgroundColorConfiguration: AnyColorable = { let config = KeyedColorConfiguration(keyPath: \.fillColor) config.setSurfaceColors(VDSColor.backgroundBrandhighlight, VDSColor.backgroundBrandhighlight, forKey: .red) config.setSurfaceColors(VDSColor.paletteYellow53, VDSColor.paletteYellow53, forKey: .yellow) @@ -125,7 +125,7 @@ open class Badge: View { open override func updateView() { updateTextColorConfig() - backgroundColor = backgroundColorConfig.getColor(self) + backgroundColor = backgroundColorConfiguration.getColor(self) label.textColorConfiguration = textColorConfig.eraseToAnyColorable() label.numberOfLines = numberOfLines diff --git a/VDS/Components/Checkbox/Checkbox.swift b/VDS/Components/Checkbox/Checkbox.swift index 630b97ce..7ea46a3f 100644 --- a/VDS/Components/Checkbox/Checkbox.swift +++ b/VDS/Components/Checkbox/Checkbox.swift @@ -2,7 +2,7 @@ // Checkbox.swift // VDS // -// Created by Matt Bruce on 7/22/22. +// Created by Matt Bruce on 6/5/23. // import Foundation @@ -11,274 +11,33 @@ import Combine import VDSColorTokens import VDSFormControlsTokens -/// Checkboxes are a multi-select component through which a customer indicates a choice. If a binary choice, the component is a checkbox. If the choice has multiple options, the component is a ``CheckboxGroup``. -@objc(VDSCheckboxBase) -open class Checkbox: Control, Errorable, Changeable { - - //-------------------------------------------------- - // 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) - } +@objc(VDSCheckbox) +open class Checkbox: SelectorBase { - //-------------------------------------------------- - // MARK: - Private Properties - //-------------------------------------------------- - private var shouldShowError: Bool { - guard showError && !disabled && errorText?.isEmpty == false else { return false } - return true - } - - private var shouldShowLabels: Bool { - guard labelText?.isEmpty == false || childText?.isEmpty == false || labelAttributedText?.string.isEmpty == false || childAttributedText?.string.isEmpty == false else { return false } - return true - } - - private var mainStackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.alignment = .top - $0.axis = .vertical - } - - private var selectorStackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.alignment = .top - $0.axis = .horizontal - } - - private var selectorLabelStackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.axis = .vertical - } - - //-------------------------------------------------- - // MARK: - Public Properties - //-------------------------------------------------- - public var onChangeSubscriber: AnyCancellable? { - willSet { - if let onChangeSubscriber { - onChangeSubscriber.cancel() - } - } - } - - open var label = Label().with { - $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.textPosition = .left - $0.textStyle = .boldBodyLarge - } - - open var childLabel = Label().with { - $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.textPosition = .left - $0.textStyle = .bodyLarge - } - - open var errorLabel = Label().with { - $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.textPosition = .left - $0.textStyle = .bodyMedium - } - - open var selectorView = UIView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - } - - open var isAnimated: Bool = true { didSet { setNeedsUpdate() }} - - open override var isSelected: Bool { didSet { setNeedsUpdate() }} - - open var labelText: String? { didSet { setNeedsUpdate() }} - - open var labelTextAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() }} - - open var labelAttributedText: NSAttributedString? { - didSet { - label.useAttributedText = !(labelAttributedText?.string.isEmpty ?? true) - label.attributedText = labelAttributedText - setNeedsUpdate() - } - } - - open var childText: String? { didSet { setNeedsUpdate() }} - - open var childTextAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() }} - - open var childAttributedText: NSAttributedString? { - didSet { - childLabel.useAttributedText = !(childAttributedText?.string.isEmpty ?? true) - childLabel.attributedText = childAttributedText - setNeedsUpdate() - } - } - - var _showError: Bool = false - open var showError: Bool { - get { _showError } - set { - if !isSelected && _showError != newValue { - _showError = newValue - setNeedsUpdate() - } - } - } - - open override var state: UIControl.State { - get { - var state = super.state - if showError { - state.insert(.error) - } - return state - } - } - - open var errorText: String? { didSet { setNeedsUpdate() }} - - open var inputId: String? { didSet { setNeedsUpdate() }} - - open var value: AnyHashable? { didSet { setNeedsUpdate() }} - - //-------------------------------------------------- - // MARK: - Constraints - //-------------------------------------------------- - - private var selectorHeightConstraint: NSLayoutConstraint? - private var selectorWidthConstraint: NSLayoutConstraint? - - //functions - //-------------------------------------------------- - // MARK: - Lifecycle - //-------------------------------------------------- - open override func initialSetup() { - super.initialSetup() - onClick = { control in - control.toggle() - } - } + open var isAnimated: Bool = false { didSet { setNeedsUpdate() }} open override func setup() { super.setup() - - isAccessibilityElement = true - accessibilityTraits = .button - addSubview(mainStackView) - mainStackView.isUserInteractionEnabled = false - mainStackView.addArrangedSubview(selectorStackView) - mainStackView.addArrangedSubview(errorLabel) - selectorStackView.addArrangedSubview(selectorView) - selectorStackView.addArrangedSubview(selectorLabelStackView) - selectorLabelStackView.addArrangedSubview(label) - selectorLabelStackView.addArrangedSubview(childLabel) + backgroundColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) + backgroundColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: [.selected, .highlighted]) + backgroundColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled]) + backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error) + backgroundColorConfiguration.setSurfaceColors(.clear, .clear, forState: [.error, .disabled]) - let selectorSize = getSelectorSize() - selectorHeightConstraint = selectorView.heightAnchor.constraint(equalToConstant: selectorSize.height) - selectorHeightConstraint?.isActive = true + borderColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) + borderColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .highlighted) + borderColorConfiguration.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOndark, forState: .normal) + borderColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) + borderColorConfiguration.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) + + borderColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled]) + borderColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.error, .disabled]) - selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width) - selectorWidthConstraint?.isActive = true - - updateSelector() - - mainStackView.pinToSuperView() - + selectorColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .selected) } - func updateLabels() { - - //deal with labels - if shouldShowLabels { - //add the stackview to hold the 2 labels - //top label - if let labelText { - label.surface = surface - label.disabled = disabled - label.attributes = labelTextAttributes - label.text = labelText - label.isHidden = false - } else if labelAttributedText != nil { - label.isHidden = false - - } else { - label.isHidden = true - } - - //bottom label - if let childText { - childLabel.text = childText - childLabel.surface = surface - childLabel.disabled = disabled - childLabel.attributes = childTextAttributes - childLabel.isHidden = false - - } else if childAttributedText != nil { - childLabel.isHidden = false - - } else { - childLabel.isHidden = true - } - selectorStackView.spacing = 12 - selectorLabelStackView.spacing = 4 - selectorLabelStackView.isHidden = false - - } else { - selectorStackView.spacing = 0 - selectorLabelStackView.spacing = 0 - selectorLabelStackView.isHidden = true - } - - //either add/remove the error from the main stack - if let errorText, shouldShowError { - errorLabel.text = errorText - errorLabel.surface = surface - errorLabel.disabled = disabled - mainStackView.spacing = 8 - errorLabel.isHidden = false - } else { - mainStackView.spacing = 0 - errorLabel.isHidden = true - } - } - - open override func reset() { - super.reset() - shouldUpdateView = false - label.reset() - childLabel.reset() - errorLabel.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 - value = nil - isSelected = false - - shouldUpdateView = true - setNeedsUpdate() - } - - /// This will checkbox the state of the Selector and execute the actionBlock if provided. - open func toggle() { + open override func toggle() { //removed error if showError && isSelected == false { showError.toggle() @@ -286,72 +45,25 @@ open class Checkbox: Control, Errorable, Changeable { isSelected.toggle() sendActions(for: .valueChanged) } + + open override func layoutSubviews() { + super.layoutSubviews() - //-------------------------------------------------- - // MARK: - State - //-------------------------------------------------- - open override func updateView() { - updateLabels() - updateSelector() - updateAccessibilityLabel() - } - - open override func updateAccessibilityLabel() { - setAccessibilityLabel(for: [label, childLabel, errorLabel]) - } - - //-------------------------------------------------- - // MARK: - Configuration Properties - //-------------------------------------------------- - public let checkboxSize = CGSize(width: 20, height: 20) - - private var backgroundColorConfig = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: [.selected,.highlighted]) - $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled]) - $0.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error) - } - - private var borderColorConfig = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOndark, forState: .normal) - $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .highlighted) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) - $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) - } - - private var checkColorConfig = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .selected) - $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: [.selected, .disabled]) - $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: [.selected, .highlighted]) - } - - //-------------------------------------------------- - // MARK: - Checkbox View - //-------------------------------------------------- - /// Manages the appearance of the checkbox. - private var shapeLayer: CAShapeLayer? - - open func getSelectorSize() -> CGSize { - return checkboxSize - } - - open func updateSelector() { //get the colors - let backgroundColor = backgroundColorConfig.getColor(self) - let borderColor = borderColorConfig.getColor(self) - let checkColor = checkColorConfig.getColor(self) + let backgroundColor = backgroundColorConfiguration.getColor(self) + let borderColor = borderColorConfiguration.getColor(self) + let selectorColor = selectorColorConfiguration.getColor(self) if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { shapeLayer.removeFromSuperlayer() self.shapeLayer = nil } - selectorView.layer.cornerRadius = VDSFormControls.borderradius - selectorView.layer.borderWidth = VDSFormControls.widthBorder + layer.cornerRadius = 2.0 + layer.borderWidth = VDSFormControls.widthBorder if shapeLayer == nil { - let bounds = selectorView.bounds + let bounds = bounds let length = max(bounds.size.height, bounds.size.width) guard length > 0.0, shapeLayer == nil else { return } @@ -374,7 +86,7 @@ open class Checkbox: Control, Errorable, Changeable { self.shapeLayer = shapeLayer shapeLayer.frame = bounds layer.addSublayer(shapeLayer) - shapeLayer.strokeColor = checkColor.cgColor + shapeLayer.strokeColor = selectorColor.cgColor shapeLayer.fillColor = UIColor.clear.cgColor shapeLayer.path = bezierPath.cgPath shapeLayer.lineJoin = .miter @@ -386,7 +98,7 @@ open class Checkbox: Control, Errorable, Changeable { shapeLayer?.removeAllAnimations() - if isAnimated && !disabled { + if isAnimated && !disabled && !isHighlighted { let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd") animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) animateStrokeEnd.duration = 0.3 @@ -397,16 +109,16 @@ open class Checkbox: Control, Errorable, Changeable { self.shapeLayer?.add(animateStrokeEnd, forKey: "strokeEnd") UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { - self.selectorView.backgroundColor = backgroundColor - self.selectorView.layer.borderColor = borderColor.cgColor + self.backgroundColor = backgroundColor + self.layer.borderColor = borderColor.cgColor }) } else { CATransaction.withDisabledAnimations { self.shapeLayer?.strokeEnd = isSelected ? 1 : 0 } - selectorView.backgroundColor = backgroundColor - selectorView.layer.borderColor = borderColor.cgColor + self.backgroundColor = backgroundColor + layer.borderColor = borderColor.cgColor } } } diff --git a/VDS/Components/Checkbox/CheckboxGroup.swift b/VDS/Components/Checkbox/CheckboxGroup.swift index b994a9cf..7c4817fc 100644 --- a/VDS/Components/Checkbox/CheckboxGroup.swift +++ b/VDS/Components/Checkbox/CheckboxGroup.swift @@ -9,18 +9,18 @@ import Foundation import UIKit @objc(VDSCheckboxGroup) -open class CheckboxGroup: SelectorGroupHandlerBase { +open class CheckboxGroup: SelectorGroupHandlerBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - public var selectedHandlers: [Checkbox]? { + public var selectedHandlers: [CheckboxItem]? { let selected = selectorViews.filter{ $0.isSelected == true } guard selected.count > 0 else { return nil } return selected } - public override var selectorViews: [Checkbox] { + public override var selectorViews: [CheckboxItem] { willSet { mainStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } } @@ -39,7 +39,7 @@ open class CheckboxGroup: SelectorGroupHandlerBase { didSet { if let selectorModels { selectorViews = selectorModels.map { model in - return Checkbox().with { + return CheckboxItem().with { $0.disabled = model.disabled $0.surface = model.surface $0.inputId = model.inputId @@ -98,7 +98,7 @@ open class CheckboxGroup: SelectorGroupHandlerBase { mainStackView.pinToSuperView() } - public override func didSelect(_ selectedControl: Checkbox) { + public override func didSelect(_ selectedControl: CheckboxItem) { selectedControl.toggle() if selectedControl.isSelected, showError{ showError.toggle() diff --git a/VDS/Components/Checkbox/CheckboxItem.swift b/VDS/Components/Checkbox/CheckboxItem.swift new file mode 100644 index 00000000..675aa7cc --- /dev/null +++ b/VDS/Components/Checkbox/CheckboxItem.swift @@ -0,0 +1,51 @@ +// +// Checkbox.swift +// VDS +// +// Created by Matt Bruce on 7/22/22. +// + +import Foundation +import UIKit + +/// Checkboxes are a multi-select component through which a customer indicates a choice. If a binary choice, the component is a checkbox. If the choice has multiple options, the component is a ``CheckboxGroup``. +@objc(VDSCheckboxItem) +open class CheckboxItem: SelectorItemBase { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + open var isAnimated: Bool = false { didSet { setNeedsUpdate() }} + + //-------------------------------------------------- + // 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: - Overrides + //-------------------------------------------------- + /// This will checkbox the state of the Selector and execute the actionBlock if provided. + open override func toggle() { + //removed error + if showError && isSelected == false { + showError.toggle() + } + isSelected.toggle() + sendActions(for: .valueChanged) + } + + open override func updateView() { + selectorView.isAnimated = isAnimated + super.updateView() + } +} diff --git a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift index a7de9401..57e5c7f6 100644 --- a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift +++ b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift @@ -75,14 +75,14 @@ open class ButtonIcon: Control { //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- - private var iconColorConfig: AnyColorable { + private var iconColorConfiguration: AnyColorable { if selectedIconName != nil { - return selectedIconColorConfig + return selectedIconColorConfiguration } else { if kind == .highContrast { - return highContrastIconColorConfig + return highContrastIconColorConfiguration } else { - return standardIconColorConfig + return standardIconColorConfiguration } } } @@ -104,7 +104,7 @@ open class ButtonIcon: Control { } } - private var standardIconColorConfig: AnyColorable = { + private var standardIconColorConfiguration: AnyColorable = { return ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) @@ -112,11 +112,11 @@ open class ButtonIcon: Control { }.eraseToAnyColorable() }() - private var highContrastIconColorConfig: AnyColorable = { + private var highContrastIconColorConfiguration: AnyColorable = { return SurfaceColorConfiguration(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight).eraseToAnyColorable() }() - private var selectedIconColorConfig: AnyColorable = { + private var selectedIconColorConfiguration: AnyColorable = { return ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsBrandhighlight, VDSColor.elementsPrimaryOndark, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) @@ -128,7 +128,7 @@ open class ButtonIcon: Control { var kind: Kind = .ghost var surfaceType: SurfaceType = .colorFill var floating: Bool = false - var backgroundColorConfig: AnyColorable = { + var backgroundColorConfiguration: AnyColorable = { SurfaceColorConfiguration(.clear, .clear).eraseToAnyColorable() }() } @@ -137,7 +137,7 @@ open class ButtonIcon: Control { var kind: Kind = .lowContrast var surfaceType: SurfaceType = .colorFill var floating: Bool = false - var backgroundColorConfig: AnyColorable = { + var backgroundColorConfiguration: AnyColorable = { SurfaceColorConfiguration(VDSColor.paletteGray44.withAlphaComponent(0.06), VDSColor.paletteGray44.withAlphaComponent(0.26)).eraseToAnyColorable() }() } @@ -146,7 +146,7 @@ open class ButtonIcon: Control { var kind: Kind = .lowContrast var surfaceType: SurfaceType = .colorFill var floating: Bool = true - var backgroundColorConfig: AnyColorable = { + var backgroundColorConfiguration: AnyColorable = { SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, .clear).eraseToAnyColorable() }() } @@ -155,11 +155,11 @@ open class ButtonIcon: Control { var kind: Kind = .lowContrast var surfaceType: SurfaceType = .media var floating: Bool = false - var backgroundColorConfig: AnyColorable = { + var backgroundColorConfiguration: AnyColorable = { SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, .clear).eraseToAnyColorable() }() var borderWidth: CGFloat = 1.0 - var borderColorConfig: AnyColorable = { + var borderColorConfiguration: AnyColorable = { SurfaceColorConfiguration(VDSColor.elementsLowcontrastOnlight, .clear).eraseToAnyColorable() }() } @@ -168,10 +168,10 @@ open class ButtonIcon: Control { var kind: Kind = .lowContrast var surfaceType: SurfaceType = .media var floating: Bool = true - var backgroundColorConfig: AnyColorable = { + var backgroundColorConfiguration: AnyColorable = { SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, .clear).eraseToAnyColorable() }() - var shadowColorConfig: AnyColorable = { + var shadowColorConfiguration: AnyColorable = { SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, .clear).eraseToAnyColorable() }() var shadowOpacity: CGFloat = 0.5 @@ -183,7 +183,7 @@ open class ButtonIcon: Control { var kind: Kind = .highContrast var surfaceType: SurfaceType = .colorFill var floating: Bool = false - var backgroundColorConfig: AnyColorable = { + var backgroundColorConfiguration: AnyColorable = { return ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryLight, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) @@ -197,7 +197,7 @@ open class ButtonIcon: Control { var kind: Kind = .highContrast var surfaceType: SurfaceType = .colorFill var floating: Bool = true - var backgroundColorConfig: AnyColorable = { + var backgroundColorConfiguration: AnyColorable = { return ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryLight, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) @@ -273,7 +273,7 @@ open class ButtonIcon: Control { //ensure there is an icon to set if let currentIconName { icon.name = currentIconName - let color = iconColorConfig.getColor(self) + let color = iconColorConfiguration.getColor(self) icon.color = color icon.size = size.value icon.customSize = customSize @@ -289,7 +289,7 @@ open class ButtonIcon: Control { let currentConfig = currentConfiguration - backgroundColor = currentConfig.backgroundColorConfig.getColor(self) + backgroundColor = currentConfig.backgroundColorConfiguration.getColor(self) // calculate center point for child view with offset let childCenter = CGPoint(x: center.x + iconOffset.x, y: center.y + iconOffset.y) @@ -314,7 +314,7 @@ open class ButtonIcon: Control { //border if let borderable = currentConfig as? Borderable { - layer.borderColor = borderable.borderColorConfig.getColor(self).cgColor + layer.borderColor = borderable.borderColorConfiguration.getColor(self).cgColor layer.borderWidth = borderable.borderWidth icon.layer.borderWidth = borderable.borderWidth } else { @@ -325,7 +325,7 @@ open class ButtonIcon: Control { if let dropshadowable = currentConfig as? Dropshadowable { layer.masksToBounds = false - layer.shadowColor = dropshadowable.shadowColorConfig.getColor(self).cgColor + layer.shadowColor = dropshadowable.shadowColorConfiguration.getColor(self).cgColor layer.shadowOpacity = Float(dropshadowable.shadowOpacity) layer.shadowOffset = dropshadowable.shadowOffset layer.shadowRadius = dropshadowable.shadowRadius @@ -362,11 +362,11 @@ extension ButtonIcon: AppleGuidlinesTouchable { private protocol Borderable { var borderWidth: CGFloat { get set } - var borderColorConfig: AnyColorable { get set } + var borderColorConfiguration: AnyColorable { get set } } private protocol Dropshadowable { - var shadowColorConfig: AnyColorable { get set } + var shadowColorConfiguration: AnyColorable { get set } var shadowOpacity: CGFloat { get set } var shadowOffset: CGSize { get set } var shadowRadius: CGFloat { get set } @@ -376,5 +376,5 @@ private protocol Configuration { var kind: ButtonIcon.Kind { get set } var surfaceType: ButtonIcon.SurfaceType { get set } var floating: Bool { get set } - var backgroundColorConfig: AnyColorable { get set } + var backgroundColorConfiguration: AnyColorable { get set } } diff --git a/VDS/Components/Line/Line.swift b/VDS/Components/Line/Line.swift index 2e74f9c4..4e038f7e 100644 --- a/VDS/Components/Line/Line.swift +++ b/VDS/Components/Line/Line.swift @@ -51,7 +51,7 @@ open class Line: View { //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- - public var lineViewColorConfig: AnyColorable = { + public var lineViewColorConfiguration: AnyColorable = { let config = KeyedColorConfiguration(keyPath: \.style) config.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forKey: .primary) config.setSurfaceColors(VDSColor.elementsLowcontrastOnlight, VDSColor.elementsLowcontrastOndark, forKey: .secondary) @@ -62,6 +62,6 @@ open class Line: View { // MARK: - State //-------------------------------------------------- open override func updateView() { - lineView.backgroundColor = lineViewColorConfig.getColor(self) + lineView.backgroundColor = lineViewColorConfiguration.getColor(self) } } diff --git a/VDS/Components/RadioBox/RadioBoxGroup.swift b/VDS/Components/RadioBox/RadioBoxGroup.swift index 9c39bf19..3196de93 100644 --- a/VDS/Components/RadioBox/RadioBoxGroup.swift +++ b/VDS/Components/RadioBox/RadioBoxGroup.swift @@ -9,12 +9,12 @@ import Foundation import UIKit @objc(VDSRadioBoxGroup) -open class RadioBoxGroup: SelectorGroupSelectedHandlerBase { +open class RadioBoxGroup: SelectorGroupSelectedHandlerBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - public override var selectorViews: [RadioBox] { + public override var selectorViews: [RadioBoxItem] { willSet { mainStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } } @@ -33,7 +33,7 @@ open class RadioBoxGroup: SelectorGroupSelectedHandlerBase { didSet { if let selectorModels { selectorViews = selectorModels.map { model in - return RadioBox().with { + return RadioBoxItem().with { $0.accessibilityLabel = model.accessibileText $0.text = model.text $0.textAttributes = model.textAttributes @@ -96,7 +96,7 @@ open class RadioBoxGroup: SelectorGroupSelectedHandlerBase { }.store(in: &subscribers) } - open override func didSelect(_ selectedControl: RadioBox) { + open override func didSelect(_ selectedControl: RadioBoxItem) { let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first oldSelectedControl?.toggle() selectedControl.toggle() diff --git a/VDS/Components/RadioBox/RadioBox.swift b/VDS/Components/RadioBox/RadioBoxItem.swift similarity index 94% rename from VDS/Components/RadioBox/RadioBox.swift rename to VDS/Components/RadioBox/RadioBoxItem.swift index f27a8809..66d40542 100644 --- a/VDS/Components/RadioBox/RadioBox.swift +++ b/VDS/Components/RadioBox/RadioBoxItem.swift @@ -11,8 +11,8 @@ import Combine import VDSColorTokens import VDSFormControlsTokens -@objc(VDSRadioBox) -open class RadioBox: Control, Changeable { +@objc(VDSRadioBoxItem) +open class RadioBoxItem: Control, Changeable { //-------------------------------------------------- // MARK: - Initializers @@ -155,8 +155,6 @@ open class RadioBox: Control, Changeable { selectorStackView.addArrangedSubview(subTextRightLabel) selectorLeftLabelStackView.addArrangedSubview(textLabel) selectorLeftLabelStackView.addArrangedSubview(subTextLabel) - - updateSelector() selectorView.pinToSuperView() mainStackView.pinToSuperView(.init(top: 16, left: 16, bottom: 16, right: 16)) @@ -241,12 +239,12 @@ open class RadioBox: Control, Changeable { //-------------------------------------------------- // MARK: - State - //-------------------------------------------------- + //-------------------------------------------------- open override func updateView() { updateLabels() - updateSelector() updateAccessibilityLabel() - setNeedsDisplay() + setNeedsLayout() + layoutIfNeeded() } open override func updateAccessibilityLabel() { @@ -281,31 +279,19 @@ open class RadioBox: Control, Changeable { /// Manages the appearance of the radioBox. private var shapeLayer: CAShapeLayer? - - open func updateSelector() { + + open override func layoutSubviews() { + super.layoutSubviews() + //get the colors let backgroundColor = backgroundColorConfiguration.getColor(self) let borderColor = borderColorConfiguration.getColor(self) - let borderWidth = isSelected ? selectorBorderWidthSelected : selectorBorderWidth + let borderWidth = isSelected || isHighlighted ? selectorBorderWidthSelected : selectorBorderWidth selectorView.backgroundColor = backgroundColor selectorView.layer.borderColor = borderColor.cgColor selectorView.layer.cornerRadius = selectorCornerRadius selectorView.layer.borderWidth = borderWidth - - layer.setNeedsDisplay() - } - - open override func layoutSubviews() { - super.layoutSubviews() - // Accounts for any size changes - layer.setNeedsDisplay() - } - - open override func draw(_ layer: CALayer, in ctx: CGContext) { - - let borderColor = borderColorConfiguration.getColor(self) - shapeLayer?.removeFromSuperlayer() shapeLayer = nil diff --git a/VDS/Components/RadioButton/RadioButton.swift b/VDS/Components/RadioButton/RadioButton.swift index 705af90e..00e7134c 100644 --- a/VDS/Components/RadioButton/RadioButton.swift +++ b/VDS/Components/RadioButton/RadioButton.swift @@ -2,7 +2,7 @@ // RadioButton.swift // VDS // -// Created by Matt Bruce on 7/22/22. +// Created by Matt Bruce on 6/5/23. // import Foundation @@ -12,352 +12,62 @@ import VDSColorTokens import VDSFormControlsTokens @objc(VDSRadioButton) -open class RadioButton: Control, Errorable, Changeable { +open class RadioButton: SelectorBase { - //-------------------------------------------------- - // MARK: - Initializers - //-------------------------------------------------- - required public init() { - super.init(frame: .zero) - } + open var selectedSize = CGSize(width: 10, height: 10) { didSet { setNeedsUpdate() }} - public override init(frame: CGRect) { - super.init(frame: .zero) - } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - } - - //-------------------------------------------------- - // MARK: - Private Properties - //-------------------------------------------------- - private var shouldShowError: Bool { - guard showError && !disabled && errorText?.isEmpty == false else { return false } - return true - } - - private var shouldShowLabels: Bool { - guard labelText?.isEmpty == false || childText?.isEmpty == false || labelAttributedText?.string.isEmpty == false || childAttributedText?.string.isEmpty == false else { return false } - return true - } - - private var mainStackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.alignment = .top - $0.axis = .vertical - } - - private var selectorStackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.alignment = .top - $0.axis = .horizontal - } - - private var selectorLabelStackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.axis = .vertical - } - - //-------------------------------------------------- - // MARK: - Public Properties - //-------------------------------------------------- - public var onChangeSubscriber: AnyCancellable? { - willSet { - if let onChangeSubscriber { - onChangeSubscriber.cancel() - } - } - } - - open var label = Label().with { - $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.textPosition = .left - $0.textStyle = .boldBodyLarge - } - - open var childLabel = Label().with { - $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.textPosition = .left - $0.textStyle = .bodyLarge - } - - open var errorLabel = Label().with { - $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.textPosition = .left - $0.textStyle = .bodyMedium - } - - public var selectorView = UIView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - } - - open var labelText: String? { didSet { setNeedsUpdate() }} - - open var labelTextAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() }} - - open var labelAttributedText: NSAttributedString? { - didSet { - label.useAttributedText = !(labelAttributedText?.string.isEmpty ?? true) - label.attributedText = labelAttributedText - setNeedsUpdate() - } - } - - open var childText: String? { didSet { setNeedsUpdate() }} - - open var childTextAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() }} - - open var childAttributedText: NSAttributedString? { - didSet { - childLabel.useAttributedText = !(childAttributedText?.string.isEmpty ?? true) - childLabel.attributedText = childAttributedText - setNeedsUpdate() - } - } - - open var showError: Bool = false { didSet { setNeedsUpdate() }} - - open override var state: UIControl.State { - get { - var state = super.state - if showError { - state.insert(.error) - } - return state - } - } - - open var errorText: String? { didSet { setNeedsUpdate() }} - - open var inputId: String? { didSet { setNeedsUpdate() }} - - open var value: AnyHashable? { didSet { setNeedsUpdate() }} - - //-------------------------------------------------- - // MARK: - Constraints - //-------------------------------------------------- - - private var selectorHeightConstraint: NSLayoutConstraint? - private var selectorWidthConstraint: NSLayoutConstraint? - - //functions - //-------------------------------------------------- - // MARK: - Lifecycle - //-------------------------------------------------- - open override func initialSetup() { - super.initialSetup() - onClick = { control in - control.toggle() - } - } - open override func setup() { super.setup() - isAccessibilityElement = true - accessibilityTraits = .button - addSubview(mainStackView) - mainStackView.isUserInteractionEnabled = false + backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error) + backgroundColorConfiguration.setSurfaceColors(.clear, .clear, forState: [.error, .disabled]) - mainStackView.addArrangedSubview(selectorStackView) - mainStackView.addArrangedSubview(errorLabel) - selectorStackView.addArrangedSubview(selectorView) - selectorStackView.addArrangedSubview(selectorLabelStackView) - selectorLabelStackView.addArrangedSubview(label) - selectorLabelStackView.addArrangedSubview(childLabel) + borderColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) + borderColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .highlighted) + borderColorConfiguration.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOndark, forState: .normal) + borderColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) + borderColorConfiguration.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) - let selectorSize = getSelectorSize() - selectorHeightConstraint = selectorView.heightAnchor.constraint(equalToConstant: selectorSize.height) - selectorHeightConstraint?.isActive = true - - selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width) - selectorWidthConstraint?.isActive = true - - updateSelector() - - mainStackView.pinToSuperView() - - } - - func updateLabels() { - - //deal with labels - if shouldShowLabels { - //add the stackview to hold the 2 labels - //top label - if let labelText { - label.text = labelText - label.surface = surface - label.disabled = disabled - label.attributes = labelTextAttributes - label.isHidden = false - - } else if labelAttributedText != nil { - label.isHidden = false - - } else { - label.isHidden = true - } - - //bottom label - if let childText { - childLabel.text = childText - childLabel.surface = surface - childLabel.disabled = disabled - childLabel.attributes = childTextAttributes - childLabel.isHidden = false - - } else if childAttributedText != nil { - childLabel.isHidden = false - - } else { - childLabel.isHidden = true - } - selectorStackView.spacing = 12 - selectorLabelStackView.spacing = 4 - selectorLabelStackView.isHidden = false - - } else { - selectorStackView.spacing = 0 - selectorLabelStackView.spacing = 0 - selectorLabelStackView.isHidden = true - } - - //either add/remove the error from the main stack - if let errorText, shouldShowError { - errorLabel.text = errorText - errorLabel.surface = surface - errorLabel.disabled = disabled - mainStackView.spacing = 8 - errorLabel.isHidden = false - } else { - mainStackView.spacing = 0 - errorLabel.isHidden = true - } - - } - - open override func reset() { - super.reset() - shouldUpdateView = false - label.reset() - childLabel.reset() - errorLabel.reset() - - label.textStyle = .boldBodyLarge - childLabel.textStyle = .bodyLarge - errorLabel.textStyle = .bodyMedium + borderColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled]) + borderColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.error, .disabled]) - labelText = nil - labelTextAttributes = nil - labelAttributedText = nil - childText = nil - childTextAttributes = nil - childAttributedText = nil - showError = false - errorText = nil - inputId = nil - value = nil - - isSelected = false - - shouldUpdateView = true - setNeedsUpdate() + selectorColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) + selectorColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled]) } - /// This will checkbox the state of the Selector and execute the actionBlock if provided. - open func toggle() { - guard !isSelected else { return } + open override func layoutSubviews() { + super.layoutSubviews() - //removed error - if showError && isSelected == false { - showError.toggle() - } - isSelected.toggle() - sendActions(for: .valueChanged) - } - - //-------------------------------------------------- - // MARK: - State - //-------------------------------------------------- - open override func updateView() { - updateLabels() - updateSelector() - updateAccessibilityLabel() - } - - open override func updateAccessibilityLabel() { - setAccessibilityLabel(for: [label, childLabel]) - } - - //-------------------------------------------------- - // MARK: - Configuration Properties - //-------------------------------------------------- - public let radioButtonSize = CGSize(width: 20, height: 20) - public let radioButtonSelectedSize = CGSize(width: 10, height: 10) + //get the colors + let backgroundColor = backgroundColorConfiguration.getColor(self) + let borderColor = borderColorConfiguration.getColor(self) + let selectorColor = selectorColorConfiguration.getColor(self) - private var backgroundColorConfiguration = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error) - } - - private var borderColorConfiguration = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .highlighted) - $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOndark, forState: .normal) - $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.disabled]) - $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) - } - - private var checkColorConfiguration = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) - $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled]) - } - - //-------------------------------------------------- - // MARK: - RadioButton View - //-------------------------------------------------- - /// Manages the appearance of the radioButton. - private var shapeLayer: CAShapeLayer? - open func getSelectorSize() -> CGSize { - radioButtonSize - } - - open func updateSelector() { - if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { shapeLayer.removeFromSuperlayer() self.shapeLayer = nil } - let bounds = radioButtonSize - let length = max(bounds.height, bounds.width) - guard length > 0.0, shapeLayer == nil else { return } + let bounds = bounds - //get the colors - let backgroundColor = backgroundColorConfiguration.getColor(self) - let borderColor = borderColorConfiguration.getColor(self) - let radioSelectedColor = checkColorConfiguration.getColor(self) - - selectorView.backgroundColor = backgroundColor - selectorView.layer.borderColor = borderColor.cgColor - selectorView.layer.cornerRadius = bounds.width * 0.5 - selectorView.layer.borderWidth = VDSFormControls.widthBorder + self.backgroundColor = backgroundColor + layer.borderColor = borderColor.cgColor + layer.cornerRadius = bounds.width * 0.5 + layer.borderWidth = VDSFormControls.widthBorder if shapeLayer == nil { - let selectedBounds = radioButtonSelectedSize + let selectedBounds = selectedSize let bezierPath = UIBezierPath(ovalIn: CGRect(x: (bounds.width - selectedBounds.width) / 2, y: (bounds.height - selectedBounds.height) / 2, - width: radioButtonSelectedSize.width, - height: radioButtonSelectedSize.height)) + width: selectedSize.width, + height: selectedSize.height)) let shapeLayer = CAShapeLayer() self.shapeLayer = shapeLayer - shapeLayer.frame = selectorView.bounds + shapeLayer.frame = bounds layer.addSublayer(shapeLayer) - shapeLayer.fillColor = radioSelectedColor.cgColor + shapeLayer.fillColor = selectorColor.cgColor shapeLayer.path = bezierPath.cgPath } } } - diff --git a/VDS/Components/RadioButton/RadioButtonGroup.swift b/VDS/Components/RadioButton/RadioButtonGroup.swift index ffdfdde3..992d76a5 100644 --- a/VDS/Components/RadioButton/RadioButtonGroup.swift +++ b/VDS/Components/RadioButton/RadioButtonGroup.swift @@ -9,12 +9,12 @@ import Foundation import UIKit @objc(VDSRadioButtonGroup) -open class RadioButtonGroup: SelectorGroupSelectedHandlerBase { +open class RadioButtonGroup: SelectorGroupSelectedHandlerBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - public override var selectorViews: [RadioButton] { + public override var selectorViews: [RadioButtonItem] { willSet { mainStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } } @@ -33,7 +33,7 @@ open class RadioButtonGroup: SelectorGroupSelectedHandlerBase { didSet { if let selectorModels { selectorViews = selectorModels.map { model in - return RadioButton().with { + return RadioButtonItem().with { $0.disabled = model.disabled $0.surface = model.surface $0.inputId = model.inputId @@ -93,7 +93,7 @@ open class RadioButtonGroup: SelectorGroupSelectedHandlerBase { mainStackView.pinToSuperView() } - public override func didSelect(_ selectedControl: RadioButton) { + public override func didSelect(_ selectedControl: RadioButtonItem) { if let selectedHandler { updateToggle(selectedHandler) } @@ -104,7 +104,7 @@ open class RadioButtonGroup: SelectorGroupSelectedHandlerBase { valueChanged() } - private func updateToggle(_ radioButton: RadioButton) { + private func updateToggle(_ radioButton: RadioButtonItem) { if radioButton.showError && radioButton.isSelected == false { radioButton.showError.toggle() } diff --git a/VDS/Components/RadioButton/RadioButtonItem.swift b/VDS/Components/RadioButton/RadioButtonItem.swift new file mode 100644 index 00000000..14ed50fb --- /dev/null +++ b/VDS/Components/RadioButton/RadioButtonItem.swift @@ -0,0 +1,44 @@ +// +// RadioButton.swift +// VDS +// +// Created by Matt Bruce on 7/22/22. +// + +import Foundation +import UIKit + +@objc(VDSRadioButtonItem) +open class RadioButtonItem: SelectorItemBase { + + //-------------------------------------------------- + // 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: - Overrides + //-------------------------------------------------- + /// This will checkbox the state of the Selector and execute the actionBlock if provided. + open override func toggle() { + guard !isSelected else { return } + + //removed error + if showError && isSelected == false { + showError.toggle() + } + isSelected.toggle() + sendActions(for: .valueChanged) + } +} + diff --git a/VDS/Components/Tooltip/Tooltip.swift b/VDS/Components/Tooltip/Tooltip.swift index 2ebb5247..6386daba 100644 --- a/VDS/Components/Tooltip/Tooltip.swift +++ b/VDS/Components/Tooltip/Tooltip.swift @@ -57,7 +57,7 @@ open class Tooltip: Control, TooltipLaunchable { //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- - private var iconColorConfig: AnyColorable { + private var iconColorConfiguration: AnyColorable { switch fillColor { case .primary: @@ -158,7 +158,7 @@ open class Tooltip: Control, TooltipLaunchable { widthConstraint?.constant = dimensions.width //get the color for the image - let imageColor = iconColorConfig.getColor(self) + let imageColor = iconColorConfiguration.getColor(self) imageView.image = infoImage.withTintColor(imageColor) accessibilityLabel = "Tooltip: \(title)" diff --git a/VDS/Components/Tooltip/TooltipAlertViewController.swift b/VDS/Components/Tooltip/TooltipAlertViewController.swift index 5de31cd0..e8e30a94 100644 --- a/VDS/Components/Tooltip/TooltipAlertViewController.swift +++ b/VDS/Components/Tooltip/TooltipAlertViewController.swift @@ -38,7 +38,7 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { } private var line = Line().with { instance in - instance.lineViewColorConfig = SurfaceColorConfiguration(VDSColor.elementsLowcontrastOnlight, VDSColor.elementsLowcontrastOndark).eraseToAnyColorable() + instance.lineViewColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsLowcontrastOnlight, VDSColor.elementsLowcontrastOndark).eraseToAnyColorable() } //--------------------------------------------------