diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index f7f5870b..e8c458bd 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -594,6 +594,10 @@ EA6642932BCDA97D00D81DC4 /* TileContainerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6642922BCDA97D00D81DC4 /* TileContainerModel.swift */; }; EA6E8B952B504A43000139B4 /* ButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6E8B942B504A43000139B4 /* ButtonGroup.swift */; }; EA6E8B972B504A4D000139B4 /* ButtonGroupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6E8B962B504A4D000139B4 /* ButtonGroupModel.swift */; }; + EA7AE5472C73C01A00107C74 /* CheckboxesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7AE5462C73C01A00107C74 /* CheckboxesModel.swift */; }; + EA7AE5492C7403DC00107C74 /* Checkboxes.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7AE5482C7403DC00107C74 /* Checkboxes.swift */; }; + EA7AE54B2C74CACA00107C74 /* RadioButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7AE54A2C74CACA00107C74 /* RadioButtons.swift */; }; + EA7AE54D2C74CAD700107C74 /* RadioButtonsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7AE54C2C74CAD700107C74 /* RadioButtonsModel.swift */; }; EA7D81602B2B6E6800D29F9E /* Icon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7D815F2B2B6E6800D29F9E /* Icon.swift */; }; EA7D81622B2B6E7F00D29F9E /* IconModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7D81612B2B6E7F00D29F9E /* IconModel.swift */; }; EA7D81642B2BABCB00D29F9E /* TooltipModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7D81632B2BABCB00D29F9E /* TooltipModel.swift */; }; @@ -1223,6 +1227,10 @@ EA6642922BCDA97D00D81DC4 /* TileContainerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TileContainerModel.swift; sourceTree = ""; }; EA6E8B942B504A43000139B4 /* ButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroup.swift; sourceTree = ""; }; EA6E8B962B504A4D000139B4 /* ButtonGroupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupModel.swift; sourceTree = ""; }; + EA7AE5462C73C01A00107C74 /* CheckboxesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxesModel.swift; sourceTree = ""; }; + EA7AE5482C7403DC00107C74 /* Checkboxes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkboxes.swift; sourceTree = ""; }; + EA7AE54A2C74CACA00107C74 /* RadioButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtons.swift; sourceTree = ""; }; + EA7AE54C2C74CAD700107C74 /* RadioButtonsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonsModel.swift; sourceTree = ""; }; EA7D815F2B2B6E6800D29F9E /* Icon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Icon.swift; sourceTree = ""; }; EA7D81612B2B6E7F00D29F9E /* IconModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconModel.swift; sourceTree = ""; }; EA7D81632B2BABCB00D29F9E /* TooltipModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipModel.swift; sourceTree = ""; }; @@ -2031,8 +2039,12 @@ BBAA4F00243D8E3B005AAD5F /* RadioBoxModel.swift */, D264FAA6243FE13B00D98315 /* RadioBox.swift */, 0116A4E4228B19640094F3ED /* RadioButtonSelectionHelper.swift */, + EA7AE54C2C74CAD700107C74 /* RadioButtonsModel.swift */, + EA7AE54A2C74CACA00107C74 /* RadioButtons.swift */, 011D95AE2407266E000E3791 /* RadioButtonModel.swift */, 01004F2F22721C3800991ECC /* RadioButton.swift */, + EA7AE5462C73C01A00107C74 /* CheckboxesModel.swift */, + EA7AE5482C7403DC00107C74 /* Checkboxes.swift */, 31BE15CA23D8924C00452370 /* CheckboxModel.swift */, 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */, AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */, @@ -2848,6 +2860,7 @@ AA7F32AD246C0F8C00C965BA /* ListLeftVariableRadioButtonAllTextAndLinks.swift in Sources */, EAA482CE2B45F2F300978105 /* MFLoadingSpinner+VDS.swift in Sources */, D272F5F92473163100BD1A8F /* BarButtonItem.swift in Sources */, + EA7AE54D2C74CAD700107C74 /* RadioButtonsModel.swift in Sources */, D2D2FCF3252B72CF0033EAAA /* MoleculeSectionFooter.swift in Sources */, 0A9D09202433796500D2E6C0 /* BarsIndicatorView.swift in Sources */, D2E2A99423D8CCBC000B42E6 /* HeadlineBodyLinkModel.swift in Sources */, @@ -3108,6 +3121,7 @@ AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */, AFA4935729EE3DCC001A9663 /* AlertDelegateProtocol.swift in Sources */, 58A9DD7D2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift in Sources */, + EA7AE54B2C74CACA00107C74 /* RadioButtons.swift in Sources */, D264FAA3243E632F00D98315 /* ProgrammaticCollectionViewController.swift in Sources */, D29DF27A21E7A533003B2FB9 /* MVMCoreUISession.m in Sources */, 27F9736A246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift in Sources */, @@ -3265,6 +3279,7 @@ 01F2C20327C81F9700DC3D36 /* SubNavManagerNavigationController.swift in Sources */, EABFC1412763BB8D00E78B40 /* FormLabel.swift in Sources */, AA997252247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift in Sources */, + EA7AE5492C7403DC00107C74 /* Checkboxes.swift in Sources */, D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */, D23EA7FB2475F09800D60C34 /* CarouselItemProtocol.swift in Sources */, D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */, @@ -3315,6 +3330,7 @@ D2D3957D252FDBCD00047B11 /* ModalSectionListTemplateModel.swift in Sources */, D2B9D0E4265EEE9D0084735C /* MoleculeListProtocol.swift in Sources */, D29C559625C099630082E7D6 /* VideoDataManager.swift in Sources */, + EA7AE5472C73C01A00107C74 /* CheckboxesModel.swift in Sources */, 8D4687E2242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift in Sources */, D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */, EA6642912BCDA97300D81DC4 /* TileContainer.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkboxes.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkboxes.swift new file mode 100644 index 00000000..3cd6cd1d --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkboxes.swift @@ -0,0 +1,55 @@ +// +// Checkboxes.swift +// MVMCoreUI +// +// Created by Matt Bruce on 8/19/24. +// Copyright © 2024 Verizon Wireless. All rights reserved. +// + +import Foundation +import VDS + +open class Checkboxes: VDS.CheckboxGroup, VDSMoleculeViewProtocol { + + //------------------------------------------------------ + // MARK: - Properties + //------------------------------------------------------ + open var viewModel: CheckboxesModel! + open var delegateObject: MVMCoreUIDelegateObject? + open var additionalData: [AnyHashable : Any]? + + // Form Validation + var fieldKey: String? + var fieldValue: JSONValue? + var groupName: String? + + /// The models for the molecules. + public var checkboxes: [CheckboxLabelModel]? + + // MARK: - MoleculeViewProtocol + public func viewModelDidUpdate() { + surface = viewModel.surface + showError = viewModel.showError + isEnabled = viewModel.enabled && !viewModel.readOnly + checkboxes = viewModel.checkboxes + checkboxes?.forEach { + FormValidator.setupValidation(for: $0.checkbox, delegate: delegateObject?.formHolderDelegate) + } + + selectorModels = viewModel.checkboxes.toVDSCheckboxItemModel(surface: surface, + delegateObject: delegateObject, + additionalData: additionalData) + } + + open func updateView(_ size: CGFloat) {} + + open override func didSelect(_ selectedControl: CheckboxItem) { + super.didSelect(selectedControl) + + // since the boxes has the state being tracked, we need to update the values here. + if let index = items.firstIndex(where: {$0 === selectedControl}), let selectedBox = checkboxes?[index] { + selectedBox.checkbox.selected = true + } + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxesModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxesModel.swift new file mode 100644 index 00000000..9ffbc20a --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxesModel.swift @@ -0,0 +1,85 @@ +// +// CheckboxesModel.swift +// MVMCoreUI +// +// Created by Matt Bruce on 8/19/24. +// Copyright © 2024 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore +import VDS + +@objcMembers public class CheckboxesModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String { "checkboxes" } + public var id: String = UUID().uuidString + + public var backgroundColor: Color? + public var accessibilityIdentifier: String? + + public var enabled: Bool = true + public var required: Bool = true + public var readOnly: Bool = false + public var showError: Bool = false + public var inverted: Bool = false + public var surface: Surface { inverted ? .dark : .light } + + public var checkboxes: [CheckboxLabelModel] + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case id + case moleculeName + case accessibilityIdentifier + case inverted + case enabled + case readOnly + case showError + case checkboxes + } + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public init(with checkboxes: [CheckboxLabelModel]){ + self.checkboxes = checkboxes + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) + showError = try typeContainer.decodeIfPresent(Bool.self, forKey: .showError) ?? false + enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true + readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false + + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { + self.inverted = inverted + } + checkboxes = try typeContainer.decode([CheckboxLabelModel].self, forKey: .checkboxes) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(id, forKey: .id) + try container.encodeIfPresent(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) + try container.encode(readOnly, forKey: .readOnly) + try container.encode(enabled, forKey: .enabled) + try container.encode(inverted, forKey: .inverted) + try container.encode(checkboxes, forKey: .checkboxes) + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtons.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtons.swift new file mode 100644 index 00000000..24d1dee7 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtons.swift @@ -0,0 +1,54 @@ +// +// RadioButtons.swift +// MVMCoreUI +// +// Created by Matt Bruce on 8/20/24. +// Copyright © 2024 Verizon Wireless. All rights reserved. +// + +import Foundation +import VDS + +open class RadioButtons: VDS.RadioButtonGroup, VDSMoleculeViewProtocol { + + //------------------------------------------------------ + // MARK: - Properties + //------------------------------------------------------ + open var viewModel: RadioButtonsModel! + open var delegateObject: MVMCoreUIDelegateObject? + open var additionalData: [AnyHashable : Any]? + + // Form Validation + var fieldKey: String? + var fieldValue: JSONValue? + var groupName: String? + + /// The models for the molecules. + public var radioButtons: [RadioButtonLabelModel]? + + // MARK: - MoleculeViewProtocol + public func viewModelDidUpdate() { + showError = viewModel.showError + isEnabled = viewModel.enabled && !viewModel.readOnly + surface = viewModel.surface + + radioButtons = viewModel.radioButtons + selectorModels = viewModel.radioButtons.toVDSRadioButtonItemModel(surface: surface, + delegateObject: delegateObject, + additionalData: additionalData) + FormValidator.setupValidation(for: viewModel, delegate: delegateObject?.formHolderDelegate) + } + + open func updateView(_ size: CGFloat) {} + + open override func didSelect(_ selectedControl: RadioButtonItem) { + super.didSelect(selectedControl) + + // since the boxes has the state being tracked, we need to update the values here. + if let index = items.firstIndex(where: {$0 === selectedControl}), let selectedBox = radioButtons?[index] { + radioButtons?.forEach { $0.radioButton.state = false } + selectedBox.radioButton.state = true + } + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonsModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonsModel.swift new file mode 100644 index 00000000..b1277d08 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonsModel.swift @@ -0,0 +1,72 @@ +// +// RadioButtonsModel.swift +// MVMCoreUI +// +// Created by Matt Bruce on 8/20/24. +// Copyright © 2024 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore +import VDS + +@objcMembers public class RadioButtonsModel: FormFieldModel { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public override static var identifier: String { "radioButtons" } + + public var radioButtons: [RadioButtonLabelModel] + + //-------------------------------------------------- + // MARK: - Form Validation + //-------------------------------------------------- + + /// Returns the fieldValue of the selected box, otherwise the text of the selected box. + public override func formFieldValue() -> AnyHashable? { + guard enabled else { return nil } + let selectedBox = radioButtons.first { $0.radioButton.state } + return selectedBox?.radioButton.formFieldValue() + } + + //-------------------------------------------------- + // MARK: - Server Value + //-------------------------------------------------- + open override func formFieldServerValue() -> AnyHashable? { + return formFieldValue() + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case radioButtons + } + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public init(with radioButtons: [RadioButtonLabelModel]){ + self.radioButtons = radioButtons + super.init() + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + radioButtons = try typeContainer.decode([RadioButtonLabelModel].self, forKey: .radioButtons) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(radioButtons, forKey: .radioButtons) + } +}