diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index c6251dea..2f2a3eb7 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ EAF7F11828A1475A00B287F5 /* RadioButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F11628A1475A00B287F5 /* RadioButtonModel.swift */; }; EAF7F12C28A1617600B287F5 /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F12B28A1617600B287F5 /* SelectorBase.swift */; }; EAF7F12F28A1619600B287F5 /* SelectorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F12E28A1619600B287F5 /* SelectorModel.swift */; }; + EAF7F13128A17FAB00B287F5 /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F13028A17FAB00B287F5 /* RadioButtonGroup.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -139,6 +140,7 @@ EAF7F11628A1475A00B287F5 /* RadioButtonModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonModel.swift; sourceTree = ""; }; EAF7F12B28A1617600B287F5 /* SelectorBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = ""; }; EAF7F12E28A1619600B287F5 /* SelectorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorModel.swift; sourceTree = ""; }; + EAF7F13028A17FAB00B287F5 /* RadioButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -360,6 +362,7 @@ EAF7F11428A1470D00B287F5 /* RadioButton */ = { isa = PBXGroup; children = ( + EAF7F13028A17FAB00B287F5 /* RadioButtonGroup.swift */, EAF7F11528A1475A00B287F5 /* RadioButton.swift */, EAF7F11628A1475A00B287F5 /* RadioButtonModel.swift */, ); @@ -497,6 +500,7 @@ EAF7F11828A1475A00B287F5 /* RadioButtonModel.swift in Sources */, EAF7F12F28A1619600B287F5 /* SelectorModel.swift in Sources */, EA3362402892EF6C0071C351 /* Label.swift in Sources */, + EAF7F13128A17FAB00B287F5 /* RadioButtonGroup.swift in Sources */, EAF7F0B3289B1ADC00B287F5 /* LabelAttributeAction.swift in Sources */, EA33622E2891EA3C0071C351 /* DispatchQueue+Once.swift in Sources */, EAF7F0AF289B144C00B287F5 /* LabelAttributeUnderline.swift in Sources */, diff --git a/VDS/Components/RadioButton/RadioButton.swift b/VDS/Components/RadioButton/RadioButton.swift index 40ec6c4f..db94ec6e 100644 --- a/VDS/Components/RadioButton/RadioButton.swift +++ b/VDS/Components/RadioButton/RadioButton.swift @@ -10,7 +10,19 @@ import UIKit import VDSColorTokens import VDSFormControlsTokens import Combine -public class RadioButton: RadioButtonBase{} + +public class RadioButton: RadioButtonBase{ + public var selectorController: RadioButtonGroup? + + //this is where the code would go for the controller + public override func toggleAndAction() { + if let selectorController = selectorController { + selectorController.didSelect(selector: self) + } else { + super.toggleAndAction() + } + } +} open class RadioButtonBase: SelectorBase { @@ -131,6 +143,6 @@ open class RadioButtonBase: SelectorBase return super.getColor(viewModel) } } - } -} + } +} diff --git a/VDS/Components/RadioButton/RadioButtonGroup.swift b/VDS/Components/RadioButton/RadioButtonGroup.swift new file mode 100644 index 00000000..50d622a8 --- /dev/null +++ b/VDS/Components/RadioButton/RadioButtonGroup.swift @@ -0,0 +1,128 @@ +// +// RadioButtonGroup.swift +// VDS +// +// Created by Matt Bruce on 8/8/22. +// + +import Foundation +import UIKit + +public protocol SelectorGroup: Modelable { + associatedtype SelectorType: SelectorModel + var models: [SelectorType] { get set } +} + +public struct RadioButtonGroupModel: SelectorGroup, FormFieldable{ + public var inputId: String? + public var value: AnyHashable? + public var surface: Surface = .light + public var disabled: Bool = false + public var models: [DefaultRadioButtonModel] + public init() { models = [] } + public init(models: [DefaultRadioButtonModel]){ + self.models = models + } +} + +public class RadioButtonGroup: View, Changable { + + public var radioButtons: [RadioButton] = [] + public var selectedView: RadioButton? + public var onChange: Blocks.ActionBlock? + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + private var mainStackView: UIStackView = { + let stackView = UIStackView() + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.alignment = .top + stackView.axis = .vertical + stackView.spacing = 10 + return stackView + }() + + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + override public var disabled: Bool { + didSet { + let models = model.models.compactMap { existing in + return updated(existing){ + $0.disabled = disabled + } + } + model.models = models + } + } + + override public var surface: Surface { + didSet { + let models = model.models.compactMap { existing in + return updated(existing){ + $0.surface = surface + } + } + model.models = models + } + } + + open override func setup() { + super.setup() + + isAccessibilityElement = true + accessibilityTraits = .button + addSubview(mainStackView) + + mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true + mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + } + + public override func onStateChange(viewModel: RadioButtonGroupModel) { + super.onStateChange(viewModel: viewModel) + + for radioButtonModel in viewModel.models { + let radioButton = radioButtons.first(where: { existingRadioButton in + return existingRadioButton.model.inputId == radioButtonModel.inputId && radioButtonModel.inputId != nil + }) + + //found view + if let radioButton = radioButton { + radioButton.set(with: radioButtonModel) + } else { + //create view + let newRadioButton = RadioButton(with: radioButtonModel) + newRadioButton.selectorController = self + self.radioButtons.append(newRadioButton) + mainStackView.addArrangedSubview(newRadioButton) + } + } + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public convenience init() { + self.init(with: ModelType()) + } + + required public init(with model: ModelType) { + super.init(with: model) + } + + required public init?(coder: NSCoder) { + super.init(with: ModelType()) + } + + //-------------------------------------------------- + // MARK: - Delegate + //-------------------------------------------------- + public func didSelect(selector: RadioButton) { + selectedView?.isSelected = false + selector.isSelected = true + selectedView = selector + } +}