diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 7351cf9e..c89651a4 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -63,6 +63,7 @@ EAB5FEF12927F4AA00998C17 /* SelfSizingCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */; }; EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF4292D371F00998C17 /* ButtonBase.swift */; }; EAB5FEF829393A7200998C17 /* ButtonGroupConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */; }; + EAB5FF0129424ACB00998C17 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FF0029424ACB00998C17 /* UIControl.swift */; }; EAC9257D29119B5400091998 /* TextLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9257C29119B5400091998 /* TextLink.swift */; }; EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925822911B35300091998 /* TextLinkCaret.swift */; }; EAC925842911C63100091998 /* Colorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEDF28F49DB3003B3210 /* Colorable.swift */; }; @@ -157,6 +158,7 @@ EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfSizingCollectionView.swift; sourceTree = ""; }; EAB5FEF4292D371F00998C17 /* ButtonBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonBase.swift; sourceTree = ""; }; EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupConstants.swift; sourceTree = ""; }; + EAB5FF0029424ACB00998C17 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.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 = ""; }; EAC925872911C9DE00091998 /* TextEntryField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = ""; }; @@ -331,6 +333,7 @@ EAF7F0B4289C126F00B287F5 /* UILabel.swift */, EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */, EAB5FED329267EB300998C17 /* UIView.swift */, + EAB5FF0029424ACB00998C17 /* UIControl.swift */, ); path = Extensions; sourceTree = ""; @@ -694,6 +697,7 @@ EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */, EA336171288B19200071C351 /* VDS.docc in Sources */, EAA5EEB528ECBFB4003B3210 /* ImageLabelAttribute.swift in Sources */, + EAB5FF0129424ACB00998C17 /* UIControl.swift in Sources */, EAB1D2E628AE842000DAE764 /* Publisher+Bind.swift in Sources */, EA3361AA288B25E40071C351 /* Disabling.swift in Sources */, EA3361B6288B2A410071C351 /* Control.swift in Sources */, @@ -864,7 +868,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../SharedFrameworks"; + FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../SharedFrameworks"; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -897,7 +901,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../SharedFrameworks"; + FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../SharedFrameworks"; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/VDS/Classes/ColorConfiguration.swift b/VDS/Classes/ColorConfiguration.swift index cafac5f9..c14e6f1c 100644 --- a/VDS/Classes/ColorConfiguration.swift +++ b/VDS/Classes/ColorConfiguration.swift @@ -10,26 +10,106 @@ import UIKit public typealias ObjectColorable = Colorable & Initable & ObjectWithable +public class ControlColorConfiguration: StateColorConfiguration, ObjectColorable { + public typealias ObjectType = UIControl & Surfaceable + + required public override init() {} + + public override func getColor(_ surface: Surface, forState state: UIControl.State) -> UIColor { + + // find the exact match + if let stateColor = stateColors.first(where: {$0.state == state }) { + return stateColor.surfaceConfig.getColor(surface) + + } else if state.contains(.disabled), let stateColor = stateColors.first(where: {$0.state == .disabled }) { + return stateColor.surfaceConfig.getColor(surface) + + } else if state.contains(.highlighted), let stateColor = stateColors.first(where: {$0.state == .highlighted }) { + return stateColor.surfaceConfig.getColor(surface) + + } else { + return .clear + + } + } + public func getColor(_ object: any ObjectType) -> UIColor { + return getColor(object.surface, forState: object.state) + } +} + +public class ViewColorConfiguration: StateColorConfiguration, ObjectColorable { + public typealias ObjectType = Disabling & Surfaceable + + required public override init() {} + + public func setSurfaceColors(_ lightColor: UIColor, _ darkColor: UIColor, forDisabled disabled: Bool) { + setSurfaceColors(lightColor, darkColor, forState: disabled) + } + + public func getColor(_ object: any ObjectType) -> UIColor { + return getColor(object.surface, forState: object.disabled) + } +} + +public class StateColorConfiguration { + + struct StateColor { + var state: StateType + var surfaceConfig: SurfaceColorConfiguration + } + + internal var stateColors: [StateColor] = [] + + public init() { } + + public func getColor(_ surface: Surface, forState state: StateType) -> UIColor { + if let stateColor = stateColors.first(where: {$0.state == state }) { + return stateColor.surfaceConfig.getColor(surface) + } else { + return .clear //default + } + } + + public func setSurfaceColors(_ lightColor: UIColor, _ darkColor: UIColor, forState state: StateType) { + stateColors.append(.init(state: state, surfaceConfig: .init(lightColor, darkColor))) + } + + public func reset() { + stateColors.removeAll() + } +} + + /// Meant to be used in a Object that implements the following interfaces for 2 possible color combinations /// - Surfaceable (var surface: Surface) /// /// let model = TestModel() /// model.surface = .light /// -/// let config = SurfaceColorConfiguration() -/// +/// let config = SurfaceColorConfiguration() /// config.lightColor = .black /// config.darkColor = .white /// /// let textColor = config.getColor(model) //returns .black -final public class SurfaceColorConfiguration: ObjectColorable { + +open class SurfaceColorConfiguration: ObjectColorable { public typealias ObjectType = Surfaceable public var lightColor: UIColor = .clear public var darkColor: UIColor = .clear required public init(){} + + public init(_ lightColor: UIColor, _ darkColor: UIColor) { + self.lightColor = lightColor + self.darkColor = darkColor + } + + public func getColor(_ surface: Surface) -> UIColor { + return surface == .light ? lightColor : darkColor + } + public func getColor(_ object: any ObjectType) -> UIColor { - return object.surface == .light ? lightColor : darkColor + return getColor(object.surface) } } @@ -189,3 +269,52 @@ final public class BinaryDisabledSurfaceColorConfiguration: BinaryDisabledSurfac getBinaryColor(object) } } + +public class ControlStateColorConfiguration: ObjectColorable { + public typealias ObjectType = UIControl & Surfaceable + public var disabled = SurfaceColorConfiguration() + public var normal = SurfaceColorConfiguration() + public var highlighted: SurfaceColorConfiguration? + public var selected: SurfaceColorConfiguration? + public var error: SurfaceColorConfiguration? + + required public init(){} + + public func setColorable(_ config: SurfaceColorConfiguration, for state: UIControl.State) { + switch state { + case .disabled: + disabled = config + + case .highlighted: + highlighted = config + + case .selected: + selected = config + + case .error: + error = config + + default: + normal = config + } + } + + public func getColor(_ object: any ObjectType) -> UIColor { + + if object.state == .disabled { + return disabled.getColor(object) + + } else if let highlighted, object.state == .highlighted { + return highlighted.getColor(object) + + } else if let selected, object.state == .selected { + return selected.getColor(object) + + } else if let error, object.state == .error { + return error.getColor(object) + + } else { + return normal.getColor(object) + } + } +} diff --git a/VDS/Classes/Control.swift b/VDS/Classes/Control.swift index 90dae4e5..d5af7f46 100644 --- a/VDS/Classes/Control.swift +++ b/VDS/Classes/Control.swift @@ -29,8 +29,24 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable { open override var isSelected: Bool { didSet { didChange() } } - open override var isHighlighted: Bool { didSet { updateView() } } - + var isHighlightAnimating = false + open override var isHighlighted: Bool { + didSet { + if isHighlightAnimating == false { + isHighlightAnimating = true + UIView.animate(withDuration: 0.1, animations: { [weak self] in + self?.updateView() + }) { [weak self] _ in + //you update the view since this is typically a quick change + UIView.animate(withDuration: 0.1, animations: { [weak self] in + self?.updateView() + self?.isHighlightAnimating = false + }) + } + } + } + } + open override var isEnabled: Bool { get { !disabled } set { diff --git a/VDS/Components/Badge/Badge.swift b/VDS/Components/Badge/Badge.swift index 9541a483..b3ffb2bd 100644 --- a/VDS/Components/Badge/Badge.swift +++ b/VDS/Components/Badge/Badge.swift @@ -102,74 +102,36 @@ public class Badge: View, Accessable { //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- - public func backgroundColor() -> UIColor { - var config: SurfaceColorConfiguration - switch fillColor { - case .red: - config = SurfaceColorConfiguration().with { - $0.lightColor = VDSColor.backgroundBrandhighlight - $0.darkColor = VDSColor.backgroundBrandhighlight - } - case .yellow: - config = SurfaceColorConfiguration().with { - $0.lightColor = VDSColor.paletteYellow62 - $0.darkColor = VDSColor.paletteYellow62 - } - case .green: - config = SurfaceColorConfiguration().with { - $0.lightColor = VDSColor.paletteGreen26 - $0.darkColor = VDSColor.paletteGreen34 - } - case .orange: - config = SurfaceColorConfiguration().with { - $0.lightColor = VDSColor.paletteOrange39 - $0.darkColor = VDSColor.paletteOrange46 - } - case .blue: - config = SurfaceColorConfiguration().with { - $0.lightColor = VDSColor.paletteBlue35 - $0.darkColor = VDSColor.paletteBlue45 - } - case .black: - config = SurfaceColorConfiguration().with { - $0.lightColor = VDSColor.paletteBlack - $0.darkColor = VDSColor.paletteBlack - } - case .white: - config = SurfaceColorConfiguration().with { - $0.lightColor = VDSColor.paletteWhite - $0.darkColor = VDSColor.paletteWhite - } - } - - return config.getColor(self) - } - - public func textColorConfiguration() -> AnyColorable { + private var backgroundColorConfig: StateColorConfiguration = { + let config = StateColorConfiguration() + config.setSurfaceColors(VDSColor.backgroundBrandhighlight, VDSColor.backgroundBrandhighlight, forState: .red) + config.setSurfaceColors(VDSColor.paletteYellow62, VDSColor.paletteYellow62, forState: .yellow) + config.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen34, forState: .green) + config.setSurfaceColors(VDSColor.paletteOrange39, VDSColor.paletteOrange46, forState: .orange) + config.setSurfaceColors(VDSColor.paletteBlue35, VDSColor.paletteBlue45, forState: .blue) + config.setSurfaceColors(VDSColor.paletteBlack, VDSColor.paletteBlack, forState: .black) + config.setSurfaceColors(VDSColor.paletteWhite, VDSColor.paletteWhite, forState: .white) + return config + }() + + private var textColorConfig = ViewColorConfiguration() + + public func updateTextColorConfig() { + textColorConfig.reset() switch fillColor { case .red, .black: - return DisabledSurfaceColorConfiguration().with { - $0.disabled.lightColor = VDSColor.elementsPrimaryOndark - $0.disabled.darkColor = VDSColor.elementsPrimaryOndark - $0.enabled.lightColor = VDSColor.elementsPrimaryOndark - $0.enabled.darkColor = VDSColor.elementsPrimaryOndark - }.eraseToAnyColorable() + textColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: false) + textColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: true) + case .yellow, .white: - return DisabledSurfaceColorConfiguration().with { - $0.disabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.disabled.darkColor = VDSColor.elementsPrimaryOnlight - $0.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.enabled.darkColor = VDSColor.elementsPrimaryOnlight - }.eraseToAnyColorable() + textColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forDisabled: false) + textColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forDisabled: true) + case .orange, .green, .blue: - return DisabledSurfaceColorConfiguration().with { - $0.disabled.lightColor = VDSColor.elementsPrimaryOndark - $0.disabled.darkColor = VDSColor.elementsPrimaryOnlight - $0.enabled.lightColor = VDSColor.elementsPrimaryOndark - $0.enabled.darkColor = VDSColor.elementsPrimaryOnlight - }.eraseToAnyColorable() + textColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forDisabled: false) + textColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forDisabled: true) } } @@ -177,9 +139,11 @@ public class Badge: View, Accessable { // MARK: - State //-------------------------------------------------- open override func updateView() { - backgroundColor = backgroundColor() + updateTextColorConfig() - label.textColorConfiguration = textColorConfiguration() + backgroundColor = backgroundColorConfig.getColor(surface, forState: fillColor) + + label.textColorConfiguration = textColorConfig.eraseToAnyColorable() label.numberOfLines = numberOfLines label.text = text label.surface = surface diff --git a/VDS/Components/Buttons/Button/Button.swift b/VDS/Components/Buttons/Button/Button.swift index d23106af..ed615958 100644 --- a/VDS/Components/Buttons/Button/Button.swift +++ b/VDS/Components/Buttons/Button/Button.swift @@ -39,7 +39,7 @@ open class Button: ButtonBase, Useable { open var width: CGFloat? { didSet { didChange() }} open override var textColor: UIColor { - buttonTitleColorConfiguration.getColor(self) + textColorConfiguration.getColor(self) } open override var typograpicalStyle: TypographicalStyle { @@ -49,58 +49,52 @@ open class Button: ButtonBase, Useable { //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- - private var buttonBackgroundColorConfiguration = 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 + + // Background Color Config + private var primaryBackgroundColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryLight, forState: .normal) + $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) + } - $0.primaryHighlighted.lightColor = VDSColor.interactiveActiveOnlight - $0.primaryHighlighted.darkColor = VDSColor.interactiveActiveOndark - - $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 secondaryBackgroundColorConfiguration = ControlColorConfiguration() - $0.secondaryHighlighted.lightColor = UIColor.clear - $0.secondaryHighlighted.darkColor = UIColor.clear + private var backgroundColorConfiguration: ControlColorConfiguration{ + use == .primary ? primaryBackgroundColorConfiguration : secondaryBackgroundColorConfiguration } - private var buttonBorderColorConfiguration = 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.primaryHighlighted.lightColor = VDSColor.elementsPrimaryOndark - $0.primaryHighlighted.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 - - $0.secondaryHighlighted.lightColor = VDSColor.interactiveActiveOnlight - $0.secondaryHighlighted.darkColor = VDSColor.interactiveActiveOndark + // Border Color Config + private var primaryBorderColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .normal) + $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .highlighted) + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) } - private var buttonTitleColorConfiguration = 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 + private var secondaryBorderColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) + $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) + } + + private var borderColorConfiguration: ControlColorConfiguration { + use == .primary ? primaryBorderColorConfiguration : secondaryBorderColorConfiguration + } - $0.primaryHighlighted.lightColor = VDSColor.elementsPrimaryOndark - $0.primaryHighlighted.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 - - $0.secondaryHighlighted.lightColor = VDSColor.interactiveActiveOnlight - $0.secondaryHighlighted.darkColor = VDSColor.interactiveActiveOndark + // Text Color Config + private var primaryTextColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .normal) + $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .highlighted) + $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .disabled) + } + + private var secondaryTextColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) + $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) + } + + private var textColorConfiguration: ControlColorConfiguration { + use == .primary ? primaryTextColorConfiguration : secondaryTextColorConfiguration } //-------------------------------------------------- @@ -150,8 +144,8 @@ open class Button: ButtonBase, Useable { open override func updateView() { super.updateView() - let bgColor = buttonBackgroundColorConfiguration.getColor(self) - let borderColor = buttonBorderColorConfiguration.getColor(self) + let bgColor = backgroundColorConfiguration.getColor(self) + let borderColor = borderColorConfiguration.getColor(self) let borderWidth = use == .secondary ? 1.0 : 0.0 let buttonHeight = size.height let cornerRadius = size.cornerRadius @@ -183,27 +177,20 @@ open class Button: ButtonBase, Useable { private class UseableColorConfiguration: ObjectColorable { typealias ObjectType = Buttonable & Useable - public var primary = DisabledSurfaceColorConfiguration() - public var secondary = DisabledSurfaceColorConfiguration() - - public var primaryHighlighted = SurfaceColorConfiguration() - public var secondaryHighlighted = SurfaceColorConfiguration() + public var primary = ControlStateColorConfiguration() + public var secondary = ControlStateColorConfiguration() required public init(){} public func getColor(_ object: ObjectType) -> UIColor { - if object.isHighlighted { - return object.use == .primary ? primaryHighlighted.getColor(object) : secondaryHighlighted.getColor(object) - } else { - return object.use == .primary ? primary.getColor(object) : secondary.getColor(object) - } + return object.use == .primary ? primary.getColor(object) : secondary.getColor(object) } } } -extension ButtonSize { +fileprivate extension ButtonSize { - public var height: CGFloat { + var height: CGFloat { switch self { case .large: return 44 @@ -212,11 +199,11 @@ extension ButtonSize { } } - public var cornerRadius: CGFloat { + var cornerRadius: CGFloat { height / 2 } - public var minimumWidth: CGFloat { + var minimumWidth: CGFloat { switch self { case .large: return 76 @@ -225,7 +212,7 @@ extension ButtonSize { } } - public var edgeInsets: UIEdgeInsets { + var edgeInsets: UIEdgeInsets { var verticalPadding = 0.0 var horizontalPadding = 0.0 switch self { diff --git a/VDS/Components/Buttons/Button/ButtonBase.swift b/VDS/Components/Buttons/Button/ButtonBase.swift index 887fff2c..005c542a 100644 --- a/VDS/Components/Buttons/Button/ButtonBase.swift +++ b/VDS/Components/Buttons/Button/ButtonBase.swift @@ -43,9 +43,25 @@ open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettab open var surface: Surface = .light { didSet { didChange() }} open var disabled: Bool = false { didSet { isEnabled = !disabled } } - - open override var isHighlighted: Bool { didSet { if isHighlighted != oldValue { updateView() } } } - + + var isHighlightAnimating = false + open override var isHighlighted: Bool { + didSet { + if isHighlightAnimating == false { + isHighlightAnimating = true + UIView.animate(withDuration: 0.1, animations: { [weak self] in + self?.updateView() + }) { [weak self] _ in + //you update the view since this is typically a quick change + UIView.animate(withDuration: 0.1, animations: { [weak self] in + self?.updateView() + self?.isHighlightAnimating = false + }) + } + } + } + } + open var typograpicalStyle: TypographicalStyle { .defaultStyle } open var textColor: UIColor { .black } @@ -129,7 +145,6 @@ open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettab // MARK: - PRIVATE //-------------------------------------------------- private func updateLabel() { - let font = typograpicalStyle.font //clear the arrays holding actions diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift index 165ac959..916609e4 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift @@ -152,7 +152,6 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega button.pinLeading() button.pinTrailing() button.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true - //cell.debugBorder() return cell } diff --git a/VDS/Components/Buttons/TextLink/TextLink.swift b/VDS/Components/Buttons/TextLink/TextLink.swift index 7dd4839f..4f543ac5 100644 --- a/VDS/Components/Buttons/TextLink/TextLink.swift +++ b/VDS/Components/Buttons/TextLink/TextLink.swift @@ -34,18 +34,13 @@ open class TextLink: ButtonBase { open override var textColor: UIColor { textColorConfiguration.getColor(self) } - - private var textColorConfiguration = HighlightDisabledSurfaceColorConfiguration().with { - $0.disabled.lightColor = VDSColor.elementsSecondaryOnlight - $0.disabled.darkColor = VDSColor.elementsSecondaryOndark - $0.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.enabled.darkColor = VDSColor.elementsPrimaryOndark - $0.highlighted.lightColor = VDSColor.interactiveActiveOnlight - $0.highlighted.darkColor = VDSColor.interactiveActiveOndark - + private var textColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) + $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forState: .disabled) + $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) } - + private var height: CGFloat { switch size { case .large: @@ -79,7 +74,7 @@ open class TextLink: ButtonBase { //-------------------------------------------------- open override func setup() { super.setup() - + if let titleLabel { addSubview(line) line.pinLeading(titleLabel.leadingAnchor) @@ -112,7 +107,6 @@ open class TextLink: ButtonBase { //need to set the properties so the super class //can render out the label correctly heightConstraint?.constant = height - lineHeightConstraint?.constant = isHighlighted ? 2.0 : 1.0 line.backgroundColor = textColor //always call last so the label is rendered @@ -120,20 +114,3 @@ open class TextLink: ButtonBase { } } - -class HighlightDisabledSurfaceColorConfiguration: ObjectColorable { - typealias ObjectType = Buttonable - public var highlighted = SurfaceColorConfiguration() - public var disabled = SurfaceColorConfiguration() - public var enabled = SurfaceColorConfiguration() - - required public init(){} - - public func getColor(_ object: any ObjectType) -> UIColor { - if object.isHighlighted { - return highlighted.getColor(object) - } else { - return object.disabled ? disabled.getColor(object) : enabled.getColor(object) - } - } -} diff --git a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift index ee702981..0013c77a 100644 --- a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift +++ b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift @@ -65,14 +65,10 @@ open class TextLinkCaret: ButtonBase { textColorConfiguration.getColor(self) } - private var textColorConfiguration = HighlightDisabledSurfaceColorConfiguration().with { - $0.disabled.lightColor = VDSColor.elementsSecondaryOnlight - $0.disabled.darkColor = VDSColor.elementsSecondaryOndark - $0.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.enabled.darkColor = VDSColor.elementsPrimaryOndark - - $0.highlighted.lightColor = VDSColor.interactiveActiveOnlight - $0.highlighted.darkColor = VDSColor.interactiveActiveOndark + private var textColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) + $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forState: .disabled) + $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) } //-------------------------------------------------- diff --git a/VDS/Components/Checkbox/Checkbox.swift b/VDS/Components/Checkbox/Checkbox.swift index 21e31c9b..97bd6361 100644 --- a/VDS/Components/Checkbox/Checkbox.swift +++ b/VDS/Components/Checkbox/Checkbox.swift @@ -124,9 +124,28 @@ open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Er didChange() } } + + var _showError: Bool = false + open var showError: Bool { + get { _showError } + set { + if !isSelected && _showError != newValue { + _showError = newValue + didChange() + } + } + } - open var showError: Bool = false { didSet { didChange() }} - + open override var state: UIControl.State { + get { + var state = super.state + if showError { + state.insert(.error) + } + return state + } + } + open var errorText: String? { didSet { didChange() }} open var inputId: String? { didSet { didChange() }} @@ -162,18 +181,13 @@ open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Er //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - open override func setup() { super.setup() - - //add tapGesture to self - publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in - self?.sendActions(for: .touchUpInside) - }.store(in: &subscribers) - + isAccessibilityElement = true accessibilityTraits = .button addSubview(mainStackView) + mainStackView.isUserInteractionEnabled = false mainStackView.addArrangedSubview(selectorStackView) mainStackView.addArrangedSubview(errorLabel) @@ -313,45 +327,27 @@ open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Er //-------------------------------------------------- public let checkboxSize = CGSize(width: 20, height: 20) - private var checkboxBackgroundColorConfiguration: ErrorBinaryDisabledSurfaceColorConfiguration = { - return ErrorBinaryDisabledSurfaceColorConfiguration().with { - $0.forTrue.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.forTrue.enabled.darkColor = VDSColor.elementsPrimaryOndark - $0.forTrue.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forTrue.disabled.darkColor = VDSColor.interactiveDisabledOndark - //error doesn't care enabled/disable - $0.error.forTrue.lightColor = VDSColor.elementsPrimaryOnlight - $0.error.forTrue.darkColor = VDSColor.elementsPrimaryOndark - $0.error.forFalse.lightColor = VDSColor.feedbackErrorBackgroundOnlight - $0.error.forFalse.darkColor = VDSColor.feedbackErrorBackgroundOndark - } - }() - - private var checkboxBorderColorConfiguration: ErrorBinaryDisabledSurfaceColorConfiguration = { - return ErrorBinaryDisabledSurfaceColorConfiguration().with { - $0.forTrue.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.forTrue.enabled.darkColor = VDSColor.elementsPrimaryOndark - $0.forFalse.enabled.lightColor = VDSFormControlsColor.borderOnlight - $0.forFalse.enabled.darkColor = VDSFormControlsColor.borderOndark - $0.forTrue.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forTrue.disabled.darkColor = VDSColor.interactiveDisabledOndark - $0.forFalse.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forFalse.disabled.darkColor = VDSColor.interactiveDisabledOndark - //error doesn't care enabled/disable - $0.error.forTrue.lightColor = VDSColor.elementsPrimaryOnlight - $0.error.forTrue.darkColor = VDSColor.elementsPrimaryOndark - $0.error.forFalse.lightColor = VDSColor.feedbackErrorOnlight - $0.error.forFalse.darkColor = VDSColor.feedbackErrorOndark - } - }() + 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]) + } - private var checkboxCheckColorConfiguration: BinarySurfaceColorConfiguration = { - return BinarySurfaceColorConfiguration().with { - $0.forTrue.lightColor = VDSColor.elementsPrimaryOndark - $0.forTrue.darkColor = VDSColor.elementsPrimaryOnlight - } - }() - //-------------------------------------------------- // MARK: - Checkbox View //-------------------------------------------------- @@ -364,9 +360,9 @@ open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Er open func updateSelector() { //get the colors - let backgroundColor = checkboxBackgroundColorConfiguration.getColor(self) - let borderColor = checkboxBorderColorConfiguration.getColor(self) - let checkColor = checkboxCheckColorConfiguration.getColor(self) + let backgroundColor = backgroundColorConfig.getColor(self) + let borderColor = borderColorConfig.getColor(self) + let checkColor = checkColorConfig.getColor(self) if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { shapeLayer.removeFromSuperlayer() @@ -413,27 +409,3 @@ open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Er } } } - -//-------------------------------------------------- -// MARK: - Color Class Configurations -//-------------------------------------------------- -internal class ErrorBinaryDisabledSurfaceColorConfiguration: BinaryDisabledSurfaceColorable { - typealias ObjectType = Errorable & Disabling & Surfaceable & BinaryColorable - var error = BinarySurfaceColorConfiguration() - var forTrue = DisabledSurfaceColorConfiguration() - var forFalse = DisabledSurfaceColorConfiguration() - - required init() {} - - func getColor(_ object: ObjectType) -> UIColor { - //only show error is enabled and showError == true - let showErrorColor = !object.disabled && object.showError - - if showErrorColor { - return error.getColor(object) - } else { - return getBinaryColor(object) - } - } -} - diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index b1e9256f..25b5c18a 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -57,11 +57,9 @@ public class Label: UILabel, Handlerable, ViewProtocol, Resettable { //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- - public var textColorConfiguration: AnyColorable = DisabledSurfaceColorConfiguration().with { - $0.disabled.lightColor = VDSColor.elementsSecondaryOnlight - $0.disabled.darkColor = VDSColor.elementsSecondaryOndark - $0.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.enabled.darkColor = VDSColor.elementsPrimaryOndark + public var textColorConfiguration: AnyColorable = ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: true) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) }.eraseToAnyColorable() //-------------------------------------------------- diff --git a/VDS/Components/RadioBox/RadioBox.swift b/VDS/Components/RadioBox/RadioBox.swift index ee53b936..ad3846db 100644 --- a/VDS/Components/RadioBox/RadioBox.swift +++ b/VDS/Components/RadioBox/RadioBox.swift @@ -160,14 +160,10 @@ open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{ open override func setup() { super.setup() - //add tapGesture to self - publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in - self?.sendActions(for: .touchUpInside) - }.store(in: &subscribers) - isAccessibilityElement = true accessibilityTraits = .button addSubview(selectorView) + selectorView.isUserInteractionEnabled = false selectorView.addSubview(mainStackView) @@ -289,29 +285,19 @@ open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{ private var selectorCornerRadius: CGFloat = 4.0 private var selectorBorderWidthSelected: CGFloat = 2.0 private var selectorBorderWidth: CGFloat = 1.0 - - private var radioBoxBackgroundColorConfiguration = BinaryDisabledSurfaceColorConfiguration().with { - $0.forFalse.enabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.forFalse.enabled.darkColor = VDSFormControlsColor.backgroundOndark - $0.forFalse.disabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.forFalse.disabled.darkColor = VDSFormControlsColor.backgroundOndark - - $0.forTrue.enabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.forTrue.enabled.darkColor = VDSFormControlsColor.backgroundOndark - $0.forTrue.disabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.forTrue.disabled.darkColor = VDSFormControlsColor.backgroundOndark + + private var backgroundColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .normal) + $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .disabled) + $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .selected) + $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .highlighted) } - - private var radioBoxBorderColorConfiguration = BinaryDisabledSurfaceColorConfiguration().with { - $0.forTrue.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.forTrue.enabled.darkColor = VDSColor.elementsPrimaryOndark - $0.forFalse.enabled.lightColor = VDSFormControlsColor.borderOnlight - $0.forFalse.enabled.darkColor = VDSFormControlsColor.borderOndark - - $0.forTrue.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forTrue.disabled.darkColor = VDSColor.interactiveDisabledOndark - $0.forFalse.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forFalse.disabled.darkColor = VDSColor.interactiveDisabledOndark + + private var borderColorConfiguration = 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: .selected) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .highlighted) } //-------------------------------------------------- @@ -323,9 +309,9 @@ open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{ open func updateSelector() { //get the colors - let backgroundColor = radioBoxBackgroundColorConfiguration.getColor(self) - let borderColor = radioBoxBorderColorConfiguration.getColor(self) - let borderWidth = isSelected ? selectorBorderWidthSelected : selectorBorderWidth + let backgroundColor = backgroundColorConfiguration.getColor(self) + let borderColor = borderColorConfiguration.getColor(self) + let borderWidth = isSelected || isHighlighted ? selectorBorderWidthSelected : selectorBorderWidth selectorView.backgroundColor = backgroundColor selectorView.layer.borderColor = borderColor.cgColor @@ -343,7 +329,7 @@ open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{ open override func draw(_ layer: CALayer, in ctx: CGContext) { - let borderColor = radioBoxBorderColorConfiguration.getColor(self) + let borderColor = borderColorConfiguration.getColor(self) shapeLayer?.removeFromSuperlayer() shapeLayer = nil diff --git a/VDS/Components/RadioButton/RadioButton.swift b/VDS/Components/RadioButton/RadioButton.swift index 6a1c28c7..fd36bded 100644 --- a/VDS/Components/RadioButton/RadioButton.swift +++ b/VDS/Components/RadioButton/RadioButton.swift @@ -131,6 +131,16 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable, open var showError: Bool = false { didSet { didChange() }} + open override var state: UIControl.State { + get { + var state = super.state + if showError { + state.insert(.error) + } + return state + } + } + open var errorText: String? { didSet { didChange() }} open var inputId: String? { didSet { didChange() }} @@ -170,15 +180,11 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable, open override func setup() { super.setup() - //add tapGesture to self - publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in - self?.sendActions(for: .touchUpInside) - }.store(in: &subscribers) - isAccessibilityElement = true accessibilityTraits = .button addSubview(mainStackView) - + mainStackView.isUserInteractionEnabled = false + mainStackView.addArrangedSubview(selectorStackView) mainStackView.addArrangedSubview(errorLabel) selectorStackView.addArrangedSubview(selectorView) @@ -321,38 +327,24 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable, //-------------------------------------------------- public let radioButtonSize = CGSize(width: 20, height: 20) public let radioButtonSelectedSize = CGSize(width: 10, height: 10) - - private var radioButtonBackgroundColorConfiguration = ErrorBinaryDisabledSurfaceColorConfiguration().with { - //error doesn't care enabled/disable - $0.error.forTrue.lightColor = VDSColor.elementsPrimaryOnlight - $0.error.forTrue.darkColor = VDSColor.elementsPrimaryOndark - $0.error.forFalse.lightColor = VDSColor.feedbackErrorBackgroundOnlight - $0.error.forFalse.darkColor = VDSColor.feedbackErrorBackgroundOndark + + 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 radioButtonBorderColorConfiguration = ErrorBinaryDisabledSurfaceColorConfiguration().with { - $0.forTrue.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.forTrue.enabled.darkColor = VDSColor.elementsPrimaryOndark - $0.forFalse.enabled.lightColor = VDSFormControlsColor.borderOnlight - $0.forFalse.enabled.darkColor = VDSFormControlsColor.borderOndark - $0.forTrue.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forTrue.disabled.darkColor = VDSColor.interactiveDisabledOndark - $0.forFalse.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forFalse.disabled.darkColor = VDSColor.interactiveDisabledOndark - //error doesn't care enabled/disable - $0.error.forTrue.lightColor = VDSColor.elementsPrimaryOnlight - $0.error.forTrue.darkColor = VDSColor.elementsPrimaryOndark - $0.error.forFalse.lightColor = VDSColor.feedbackErrorOnlight - $0.error.forFalse.darkColor = VDSColor.feedbackErrorOndark + private var checkColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled]) } - - private var radioButtonCheckColorConfiguration = BinaryDisabledSurfaceColorConfiguration().with { - $0.forTrue.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.forTrue.enabled.darkColor = VDSColor.elementsPrimaryOndark - $0.forTrue.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forTrue.disabled.darkColor = VDSColor.interactiveDisabledOndark - } - + //-------------------------------------------------- // MARK: - RadioButton View //-------------------------------------------------- @@ -374,9 +366,9 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable, guard length > 0.0, shapeLayer == nil else { return } //get the colors - let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(self) - let borderColor = radioButtonBorderColorConfiguration.getColor(self) - let radioSelectedColor = radioButtonCheckColorConfiguration.getColor(self) + let backgroundColor = backgroundColorConfiguration.getColor(self) + let borderColor = borderColorConfiguration.getColor(self) + let radioSelectedColor = checkColorConfiguration.getColor(self) selectorView.backgroundColor = backgroundColor selectorView.layer.borderColor = borderColor.cgColor diff --git a/VDS/Components/RadioSwatch/RadioSwatch.swift b/VDS/Components/RadioSwatch/RadioSwatch.swift index 3e987d5a..0ee397a7 100644 --- a/VDS/Components/RadioSwatch/RadioSwatch.swift +++ b/VDS/Components/RadioSwatch/RadioSwatch.swift @@ -13,6 +13,14 @@ import Combine @objc(VDSRadioSwatch) public class RadioSwatch: RadioSwatchBase{ + //for groups allows "toggle" + open override func toggle() { + isSelected.toggle() + } +} + +@objc(VDSSoloRadioSwatch) +public class SolorRadioSwatch: RadioSwatchBase{ public override func initialSetup() { super.initialSetup() publisher(for: .touchUpInside) @@ -92,15 +100,10 @@ open class RadioSwatchBase: Control, Accessable, DataTrackable, BinaryColorable open override func setup() { super.setup() - //add tapGesture to self - publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in - self?.sendActions(for: .touchUpInside) - }.store(in: &subscribers) - isAccessibilityElement = true accessibilityTraits = .button addSubview(selectorView) - + selectorView.isUserInteractionEnabled = false selectorView.addSubview(fillView) selectorView.pinToSuperView() @@ -165,33 +168,11 @@ open class RadioSwatchBase: Control, Accessable, DataTrackable, BinaryColorable public let fillSize = CGSize(width: 36, height: 36) public let disabledAlpha = 0.5 - private var radioSwatchBackgroundColorConfiguration = BinaryDisabledSurfaceColorConfiguration().with { - $0.forFalse.enabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.forFalse.enabled.darkColor = VDSFormControlsColor.backgroundOndark - $0.forFalse.disabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.forFalse.disabled.darkColor = VDSFormControlsColor.backgroundOndark - $0.forTrue.enabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.forTrue.enabled.darkColor = VDSFormControlsColor.backgroundOndark - $0.forTrue.disabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.forTrue.disabled.darkColor = VDSFormControlsColor.backgroundOndark - } - - private var radioSwatchBorderColorConfiguration = BinaryDisabledSurfaceColorConfiguration().with { - $0.forTrue.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.forTrue.enabled.darkColor = VDSColor.elementsPrimaryOndark - $0.forFalse.enabled.lightColor = VDSFormControlsColor.borderOnlight - $0.forFalse.enabled.darkColor = VDSFormControlsColor.borderOndark - $0.forTrue.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forTrue.disabled.darkColor = VDSColor.interactiveDisabledOndark - $0.forFalse.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forFalse.disabled.darkColor = VDSColor.interactiveDisabledOndark - } - - private var radioSwatchFillBorderColorConfiguration = DisabledSurfaceColorConfiguration().with { - $0.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.enabled.darkColor = VDSColor.elementsPrimaryOndark - $0.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.disabled.darkColor = VDSColor.interactiveDisabledOndark + private var borderColorConfiguration = 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) } //-------------------------------------------------- @@ -213,14 +194,14 @@ open class RadioSwatchBase: Control, Accessable, DataTrackable, BinaryColorable } open override func draw(_ layer: CALayer, in ctx: CGContext) { - - let backgroundColor = radioSwatchBackgroundColorConfiguration.getColor(self) - let borderColor = isSelected ? radioSwatchBorderColorConfiguration.getColor(self) : .clear - let fillBorderColor = radioSwatchFillBorderColorConfiguration.getColor(self) + let drawOuterBorder = isSelected || isHighlighted + let backgroundColor = UIColor.clear + let borderColor = isSelected ? borderColorConfiguration.getColor(self) : .clear + let fillBorderColor = borderColorConfiguration.getColor(self) selectorView.backgroundColor = backgroundColor selectorView.layer.borderColor = borderColor.cgColor selectorView.layer.cornerRadius = selectorView.bounds.width * 0.5 - selectorView.layer.borderWidth = isSelected ? selectorBorderWidth : 0 + selectorView.layer.borderWidth = drawOuterBorder ? selectorBorderWidth : 0 selectorView.layer.masksToBounds = true gradientLayer?.removeFromSuperlayer() @@ -258,7 +239,7 @@ open class RadioSwatchBase: Control, Accessable, DataTrackable, BinaryColorable shapeLayer = nil if strikethrough { - let strikeThroughBorderColor = radioSwatchBorderColorConfiguration.getColor(self) + let strikeThroughBorderColor = borderColorConfiguration.getColor(self) let bounds = selectorView.bounds let length = max(bounds.size.height, bounds.size.width) guard length > 0.0, shapeLayer == nil else { return } diff --git a/VDS/Components/RadioSwatch/RadioSwatchGroup.swift b/VDS/Components/RadioSwatch/RadioSwatchGroup.swift index 526df5d5..9929da63 100644 --- a/VDS/Components/RadioSwatch/RadioSwatchGroup.swift +++ b/VDS/Components/RadioSwatch/RadioSwatchGroup.swift @@ -16,6 +16,7 @@ public class RadioSwatchGroup: RadioSwatchGroupBase { selectedHandler?.toggle() selector.toggle() label.text = selector.text + didChange() valueChanged() } @@ -157,7 +158,11 @@ public class RadioSwatchGroupBase: SelectorGroupSe public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) let handler = selectorViews[indexPath.row] - handler.isUserInteractionEnabled = false + handler.subscribers.forEach{ $0.cancel() } + handler.subscribers.removeAll() + handler.publisher(for: .touchUpInside).sink { [weak self] handler in + self?.didSelect(selector: handler) + }.store(in: &handler.subscribers) cell.subviews.forEach { $0.removeFromSuperview() } cell.addSubview(handler) handler.pinToSuperView() diff --git a/VDS/Components/TextFields/EntryField/EntryField.swift b/VDS/Components/TextFields/EntryField/EntryField.swift index 150ad842..59dda2b1 100644 --- a/VDS/Components/TextFields/EntryField/EntryField.swift +++ b/VDS/Components/TextFields/EntryField/EntryField.swift @@ -76,28 +76,28 @@ open class EntryField: Control, Accessable { // Sizes are from InVision design specs. internal let containerSize = CGSize(width: 45, height: 45) - internal let primaryColorConfig = DisabledSurfaceColorConfiguration().with { - $0.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.disabled.darkColor = VDSColor.interactiveDisabledOndark - $0.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.enabled.darkColor = VDSColor.elementsPrimaryOndark + internal let primaryColorConfig = ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) } - internal let secondaryColorConfig = DisabledSurfaceColorConfiguration().with { - $0.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.disabled.darkColor = VDSColor.interactiveDisabledOndark - $0.enabled.lightColor = VDSColor.elementsSecondaryOnlight - $0.enabled.darkColor = VDSColor.elementsSecondaryOndark + internal let secondaryColorConfig = ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) + $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false) + } + + internal var backgroundColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .normal) + $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .disabled) + $0.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error) + } + + internal var borderColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOnlight, forState: .normal) + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) + $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) } - internal lazy var backgroundColorConfiguration: AnyColorable = { - return getBackgroundConfig() - }() - - internal lazy var borderColorConfiguration: AnyColorable = { - return getBorderConfig() - }() - //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @@ -107,6 +107,16 @@ open class EntryField: Control, Accessable { open var showError: Bool = false { didSet { didChange() }} + open override var state: UIControl.State { + get { + var state = super.state + if showError { + state.insert(.error) + } + return state + } + } + open var errorText: String? { didSet { didChange() }} open var tooltipTitle: String? { didSet { didChange() }} @@ -152,15 +162,11 @@ open class EntryField: Control, Accessable { //-------------------------------------------------- open override func setup() { super.setup() - - //add tapGesture to self - publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in - self?.sendActions(for: .touchUpInside) - }.store(in: &subscribers) - + isAccessibilityElement = true accessibilityTraits = .button addSubview(stackView) + stackView.isUserInteractionEnabled = false //create the wrapping view heightConstraint = containerView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height) @@ -189,32 +195,6 @@ open class EntryField: Control, Accessable { return containerView } - open func getBackgroundConfig() -> AnyColorable { - return ErrorDisabledSurfaceColorConfiguration().with { - $0.enabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.enabled.darkColor = VDSFormControlsColor.backgroundOndark - $0.disabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.disabled.darkColor = VDSFormControlsColor.backgroundOndark - - //error doesn't care enabled/disable - $0.error.lightColor = VDSColor.feedbackErrorBackgroundOnlight - $0.error.darkColor = VDSColor.feedbackErrorBackgroundOndark - }.eraseToAnyColorable() - } - - open func getBorderConfig() -> AnyColorable { - return ErrorDisabledSurfaceColorConfiguration().with { - $0.enabled.lightColor = VDSFormControlsColor.borderOnlight - $0.enabled.darkColor = VDSFormControlsColor.borderOnlight - $0.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.disabled.darkColor = VDSColor.interactiveDisabledOndark - - //error doesn't care enabled/disable - $0.error.lightColor = VDSColor.feedbackErrorOnlight - $0.error.darkColor = VDSColor.feedbackErrorOndark - }.eraseToAnyColorable() - } - open func getToolTipView() -> UIView? { guard let tooltipTitle, let tooltipContent else { return nil diff --git a/VDS/Components/TextFields/TextEntryField/TextEntryField.swift b/VDS/Components/TextFields/TextEntryField/TextEntryField.swift index c0eee646..92cf1cc6 100644 --- a/VDS/Components/TextFields/TextEntryField/TextEntryField.swift +++ b/VDS/Components/TextFields/TextEntryField/TextEntryField.swift @@ -52,7 +52,37 @@ open class TextEntryFieldBase: EntryField { open var type: TextEntryFieldType = .text { didSet { didChange() }} - open var showSuccess: Bool = false { didSet { didChange() }} + var _showError: Bool = false + open override var showError: Bool { + get { _showError } + set { + if !showSuccess && _showError != newValue { + _showError = newValue + didChange() + } + } + } + + var _showSuccess: Bool = false + open var showSuccess: Bool { + get { _showSuccess } + set { + if !showError && _showSuccess != newValue { + _showSuccess = newValue + didChange() + } + } + } + + open override var state: UIControl.State { + get { + var state = super.state + if showSuccess { + state.insert(.success) + } + return state + } + } open var successText: String? { didSet { didChange() }} @@ -80,6 +110,9 @@ open class TextEntryFieldBase: EntryField { successLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() + backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: .success) + borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success) + } public override func reset() { @@ -99,36 +132,6 @@ open class TextEntryFieldBase: EntryField { return containerStackView } - open override func getBackgroundConfig() -> AnyColorable { - return TextEntryFieldColorConfiguration().with { - $0.enabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.enabled.darkColor = VDSFormControlsColor.backgroundOndark - $0.disabled.lightColor = VDSFormControlsColor.backgroundOnlight - $0.disabled.darkColor = VDSFormControlsColor.backgroundOndark - - //error/success doesn't care enabled/disable - $0.error.lightColor = VDSColor.feedbackErrorBackgroundOnlight - $0.error.darkColor = VDSColor.feedbackErrorBackgroundOndark - $0.success.lightColor = VDSColor.feedbackSuccessBackgroundOnlight - $0.success.darkColor = VDSColor.feedbackSuccessBackgroundOndark - }.eraseToAnyColorable() - } - - open override func getBorderConfig() -> AnyColorable { - return TextEntryFieldColorConfiguration().with { - $0.enabled.lightColor = VDSFormControlsColor.borderOnlight - $0.enabled.darkColor = VDSFormControlsColor.borderOnlight - $0.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.disabled.darkColor = VDSColor.interactiveDisabledOndark - - //error/success doesn't care enabled/disable - $0.error.lightColor = VDSColor.feedbackErrorOnlight - $0.error.darkColor = VDSColor.feedbackErrorOndark - $0.success.lightColor = VDSColor.feedbackSuccessOnlight - $0.success.darkColor = VDSColor.feedbackSuccessOndark - }.eraseToAnyColorable() - } - //-------------------------------------------------- // MARK: - State //-------------------------------------------------- diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index ab7428f1..775768d3 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -88,27 +88,17 @@ open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable { public let toggleSize = CGSize(width: 52, height: 24) public let toggleContainerSize = CGSize(width: 52, height: 44) public let knobSize = CGSize(width: 20, height: 20) - - private var toggleColorConfiguration = BinaryDisabledSurfaceColorConfiguration().with { - $0.forTrue.enabled.lightColor = VDSColor.paletteGreen26 - $0.forTrue.enabled.darkColor = VDSColor.paletteGreen34 - $0.forTrue.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forTrue.disabled.darkColor = VDSColor.interactiveDisabledOndark - $0.forFalse.enabled.lightColor = VDSColor.elementsSecondaryOnlight - $0.forFalse.enabled.darkColor = VDSColor.paletteGray44 - $0.forFalse.disabled.lightColor = VDSColor.interactiveDisabledOnlight - $0.forFalse.disabled.darkColor = VDSColor.interactiveDisabledOndark + + private var toggleColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.paletteGray44, forState: .normal) + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) + $0.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen34, forState: .selected) } - private var knobColorConfiguration = BinaryDisabledSurfaceColorConfiguration().with { - $0.forTrue.enabled.lightColor = VDSColor.elementsPrimaryOndark - $0.forTrue.enabled.darkColor = VDSColor.elementsPrimaryOndark - $0.forTrue.disabled.lightColor = VDSColor.paletteGray95 - $0.forTrue.disabled.darkColor = VDSColor.paletteGray44 - $0.forFalse.enabled.lightColor = VDSColor.elementsPrimaryOndark - $0.forFalse.enabled.darkColor = VDSColor.elementsPrimaryOndark - $0.forFalse.disabled.lightColor = VDSColor.paletteGray95 - $0.forFalse.disabled.darkColor = VDSColor.paletteGray44 + private var knobColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forState: .normal) + $0.setSurfaceColors(VDSColor.paletteGray95, VDSColor.paletteGray44, forState: .disabled) + $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forState: .selected) } private var typograpicalStyle: TypographicalStyle { @@ -130,7 +120,15 @@ open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - open var isOn: Bool = false { didSet { didChange() }} + open var isOn: Bool { + get { isSelected } + set { + if isSelected != newValue { + isSelected = newValue + } + didChange() + } + } open var isAnimated: Bool = true { didSet { didChange() }} @@ -292,7 +290,6 @@ open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable { public override func reset() { super.reset() label.reset() - isSelected = false isOn = false isAnimated = true @@ -313,7 +310,6 @@ open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable { /// This will toggle the state of the Toggle and execute the actionBlock if provided. open func toggle() { isOn.toggle() - isSelected = isOn sendActions(for: .valueChanged) } diff --git a/VDS/Extensions/UIControl.swift b/VDS/Extensions/UIControl.swift new file mode 100644 index 00000000..ce32dcad --- /dev/null +++ b/VDS/Extensions/UIControl.swift @@ -0,0 +1,14 @@ +// +// UIControl.swift +// VDS +// +// Created by Matt Bruce on 12/8/22. +// + +import Foundation +import UIKit + +extension UIControl.State { + public static var error = UIControl.State(rawValue: 1 << 16) + public static var success = UIControl.State(rawValue: 1 << 17) +}