From 42e6f9025c60eec270313914336c555b0c1b6332 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 5 Jun 2023 10:42:42 -0500 Subject: [PATCH] refactored checkbox for new subclasses Signed-off-by: Matt Bruce --- VDS/Components/Checkbox/Checkbox.swift | 106 ++------- VDS/Components/Checkbox/CheckboxItem.swift | 260 +-------------------- 2 files changed, 22 insertions(+), 344 deletions(-) diff --git a/VDS/Components/Checkbox/Checkbox.swift b/VDS/Components/Checkbox/Checkbox.swift index fefe1787..bb47479d 100644 --- a/VDS/Components/Checkbox/Checkbox.swift +++ b/VDS/Components/Checkbox/Checkbox.swift @@ -11,101 +11,29 @@ import Combine import VDSColorTokens import VDSFormControlsTokens -public protocol SelectorControlable: Control, Changeable { - var showError: Bool { get set } - var size: CGSize { get set } - var backgroundColorConfig: ControlColorConfiguration { get set } - var borderColorConfig: ControlColorConfiguration { get set } - var selectorColorConfig: ControlColorConfiguration { get set } -} - @objc(VDSCheckbox) -open class Checkbox: Control, SelectorControlable { - public var onChangeSubscriber: AnyCancellable? +open class Checkbox: SelectorBase { open var isAnimated: Bool = true { didSet { setNeedsUpdate() }} - 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 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) - }{ didSet { setNeedsUpdate() }} - - open 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) - }{ didSet { setNeedsUpdate() }} - - open var selectorColorConfig = 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]) - }{ didSet { setNeedsUpdate() }} - - //-------------------------------------------------- - // MARK: - Constraints - //-------------------------------------------------- - private var selectorHeightConstraint: NSLayoutConstraint? - private var selectorWidthConstraint: NSLayoutConstraint? - - private var shapeLayer: CAShapeLayer? - 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 override func updateView() { - super.updateView() - - selectorHeightConstraint?.constant = size.height - selectorWidthConstraint?.constant = size.width + backgroundColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) + backgroundColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: [.selected,.highlighted]) + backgroundColorConfig.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled]) + backgroundColorConfig.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error) - setNeedsLayout() - layoutIfNeeded() + borderColorConfig.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOndark, forState: .normal) + borderColorConfig.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) + borderColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .highlighted) + borderColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) + borderColorConfig.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) + + selectorColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .selected) + selectorColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: [.selected, .disabled]) + selectorColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: [.selected, .highlighted]) + } open override func layoutSubviews() { @@ -114,7 +42,7 @@ open class Checkbox: Control, SelectorControlable { //get the colors let backgroundColor = backgroundColorConfig.getColor(self) let borderColor = borderColorConfig.getColor(self) - let checkColor = selectorColorConfig.getColor(self) + let selectorColor = selectorColorConfig.getColor(self) if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { shapeLayer.removeFromSuperlayer() @@ -148,7 +76,7 @@ open class Checkbox: Control, SelectorControlable { 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 diff --git a/VDS/Components/Checkbox/CheckboxItem.swift b/VDS/Components/Checkbox/CheckboxItem.swift index 368ccee6..d580d87a 100644 --- a/VDS/Components/Checkbox/CheckboxItem.swift +++ b/VDS/Components/Checkbox/CheckboxItem.swift @@ -7,13 +7,10 @@ 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``. -@objc(VDSCheckboxBase) -open class CheckboxItem: Control, Errorable, Changeable { +@objc(VDSCheckboxItem) +open class CheckboxItem: SelectorItemBase { //-------------------------------------------------- // MARK: - Initializers @@ -31,247 +28,10 @@ open class CheckboxItem: Control, Errorable, Changeable { } //-------------------------------------------------- - // MARK: - Private Properties + // MARK: - Overrides //-------------------------------------------------- - 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 = Checkbox() - - 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 override var isHighlighted: Bool { - didSet { - selectorView.isHighlighted = isHighlighted - } - } - - 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 - - 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() { + open override func toggle() { //removed error if showError && isSelected == false { showError.toggle() @@ -280,18 +40,8 @@ open class CheckboxItem: Control, Errorable, Changeable { sendActions(for: .valueChanged) } - //-------------------------------------------------- - // MARK: - State - //-------------------------------------------------- open override func updateView() { - updateLabels() selectorView.isAnimated = isAnimated - selectorView.showError = showError - selectorView.isSelected = isSelected - updateAccessibilityLabel() - } - - open override func updateAccessibilityLabel() { - setAccessibilityLabel(for: [label, childLabel, errorLabel]) + super.updateView() } }