diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift index ee697a3e..38da2bdb 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryField.swift @@ -27,6 +27,11 @@ import UIKit return caret }() + public var baseDropdownEntryFieldModel: BaseDropdownEntryFieldModel? { + return model as? BaseDropdownEntryFieldModel + } + var additionalData: [AnyHashable: Any]? + //-------------------------------------------------- // MARK: - Property Observers //-------------------------------------------------- @@ -75,9 +80,22 @@ import UIKit public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - + self.additionalData = additionalData guard let model = model as? BaseDropdownEntryFieldModel else { return } dropDownCaretView.setOptional(with: model.caretView, delegateObject, additionalData) } + + public override func dismissFieldInput(_ sender: Any?) { + performDropdownAction() + super.dismissFieldInput(sender) + } + + func performDropdownAction() { + if let actionModel = baseDropdownEntryFieldModel?.action, let actionMap = actionModel.toJSON() { + var additionalData = self.additionalData ?? [:] + additionalData[KeySourceModel] = baseDropdownEntryFieldModel + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift index e34183de..0bb17284 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/BaseDropdownEntryFieldModel.swift @@ -12,6 +12,7 @@ //-------------------------------------------------- public var caretView: CaretViewModel? + public var action: ActionModelProtocol? public override class var identifier: String { return "" @@ -24,6 +25,7 @@ private enum CodingKeys: String, CodingKey { case moleculeName case caretView + case action } //-------------------------------------------------- @@ -34,6 +36,7 @@ try super.init(from: decoder) let typeContainer = try decoder.container(keyedBy: CodingKeys.self) caretView = try typeContainer.decodeIfPresent(CaretViewModel.self, forKey: .caretView) + action = try typeContainer.decodeModelIfPresent(codingKey: .action) } public override func encode(to encoder: Encoder) throws { @@ -41,5 +44,6 @@ var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(caretView, forKey: .caretView) + try container.encodeModelIfPresent(action, forKey: .action) } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift index cc8a4c71..ddc459c3 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift @@ -109,8 +109,8 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { pickerData = model.options setPickerDelegates(delegate: self) - if let pickerView = pickerView { - self.pickerView(pickerView, didSelectRow: model.selectedIndex, inComponent: 0) + if let pickerView = pickerView, let index = model.selectedIndex { + self.pickerView(pickerView, didSelectRow: index, inComponent: 0) } } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift index d89f5e16..0bc1ee0a 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift @@ -16,11 +16,14 @@ } public var options: [String] = [] - public var selectedIndex: Int = 0 + public var selectedIndex: Int? public override func formFieldValue() -> AnyHashable? { - guard !options.isEmpty else { return nil } - return options[selectedIndex] + guard !options.isEmpty, + let index = selectedIndex + else { return nil } + + return options[index] } //-------------------------------------------------- @@ -45,13 +48,16 @@ if let selectedIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .selectedIndex) { self.selectedIndex = selectedIndex } - baseValue = options.indices.contains(selectedIndex) ? options[selectedIndex] : nil + + if let index = selectedIndex { + baseValue = options.indices.contains(index) ? options[index] : nil + } } public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(options, forKey: .options) - try container.encode(options, forKey: .selectedIndex) + try container.encodeIfPresent(options, forKey: .selectedIndex) } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index f23fd337..edc37628 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -24,6 +24,10 @@ import MVMCore var groupName: String? var delegateObject: MVMCoreUIDelegateObject? + public var checkboxModel: CheckboxModel? { + return model as? CheckboxModel + } + public static let defaultHeightWidth: CGFloat = 18.0 /// If true the border of this checkbox will be circular. @@ -381,7 +385,7 @@ import MVMCore checkWidth = 2 checkAndBypassAnimations(selected: false) } - + public override func updateView(_ size: CGFloat) { super.updateView(size) @@ -391,6 +395,14 @@ import MVMCore } } + private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + if let actionMap = actionModel.toJSON() { + var additionalDatatoUpdate = additionalData ?? [:] + additionalDatatoUpdate[KeySourceModel] = checkboxModel + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) + } + } + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject @@ -422,10 +434,15 @@ import MVMCore isEnabled = model.enabled - if let action = model.action { - actionBlock = { - if let actionMap = action.toJSON() { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + if (model.action != nil || model.offAction != nil) { + actionBlock = { [weak self] in + guard let self = self else { return } + + if let offAction = model.offAction, !self.isSelected { + self.performCheckboxAction(with: offAction, delegateObject: delegateObject, additionalData: additionalData) + + } else if let action = model.action { + self.performCheckboxAction(with: action, delegateObject: delegateObject, additionalData: additionalData) } } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index 43a08aa6..bb21da1b 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -32,6 +32,7 @@ import Foundation public var invertedColor: Color = Color(uiColor: .mvmWhite) public var invertedBackgroundColor: Color = Color(uiColor: .mvmBlack) public var action: ActionModelProtocol? + public var offAction: ActionModelProtocol? public var fieldKey: String? public var groupName: String = FormValidator.defaultGroupName @@ -61,6 +62,7 @@ import Foundation case action case fieldKey case groupName + case offAction } //-------------------------------------------------- @@ -155,6 +157,7 @@ import Foundation if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { self.groupName = groupName } + offAction = try typeContainer.decodeModelIfPresent(codingKey: .offAction) } public func encode(to encoder: Encoder) throws { @@ -179,5 +182,6 @@ import Foundation try container.encodeIfPresent(enabled, forKey: .enabled) try container.encodeModelIfPresent(action, forKey: .action) try container.encodeIfPresent(groupName, forKey: .groupName) + try container.encodeModelIfPresent(offAction, forKey: .offAction) } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift index 70c1a6ef..46b4cef5 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift @@ -8,7 +8,7 @@ import Foundation -open class RadioBox: Control { +open class RadioBox: Control, MFButtonProtocol { public let label = Label(fontStyle: .RegularBodySmall) public let subTextLabel = Label(fontStyle: .RegularMicro) public var isOutOfStock = false @@ -22,6 +22,9 @@ open class RadioBox: Control { public var subTextLabelHeightConstraint: NSLayoutConstraint? + private var delegateObject: MVMCoreUIDelegateObject? + var additionalData: [AnyHashable: Any]? + public var radioBoxModel: RadioBoxModel? { return model as? RadioBoxModel } @@ -77,6 +80,8 @@ open class RadioBox: Control { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? RadioBoxModel else { return } + self.delegateObject = delegateObject + self.additionalData = additionalData label.text = model.text subTextLabel.text = model.subText isOutOfStock = model.strikethrough @@ -135,6 +140,11 @@ open class RadioBox: Control { guard isEnabled else { return } isSelected = true radioBoxModel?.selected = isSelected + if let actionModel = radioBoxModel?.action { + var additionalData = self.additionalData ?? [:] + additionalData[KeySourceModel] = radioBoxModel + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData) + } layer.setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift index 986eefac..923a65e0 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift @@ -17,6 +17,7 @@ import Foundation public var enabled: Bool = true public var strikethrough: Bool = false public var fieldValue: String? + public var action: ActionModelProtocol? private enum CodingKeys: String, CodingKey { case moleculeName @@ -28,6 +29,7 @@ import Foundation case enabled case strikethrough case fieldValue + case action } required public init(from decoder: Decoder) throws { @@ -47,6 +49,7 @@ import Foundation } fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) + action = try typeContainer.decodeModelIfPresent(codingKey: .action) } public func encode(to encoder: Encoder) throws { @@ -60,5 +63,6 @@ import Foundation try container.encode(enabled, forKey: .enabled) try container.encode(strikethrough, forKey: .strikethrough) try container.encodeIfPresent(fieldValue, forKey: .fieldValue) + try container.encodeModelIfPresent(action, forKey: .action) } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index e1f3ab93..9e18f936 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -30,6 +30,7 @@ import UIKit public var enabledColor: UIColor = .mvmBlack public var disabledColor: UIColor = .mvmCoolGray3 public var delegateObject: MVMCoreUIDelegateObject? + var additionalData: [AnyHashable: Any]? public var radioModel: RadioButtonModel? { return model as? RadioButtonModel @@ -100,7 +101,9 @@ import UIKit isSelected = !isSelected } if let actionModel = radioModel?.action, isSelected { - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: nil) + var additionalData = self.additionalData ?? [:] + additionalData[KeySourceModel] = radioModel + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData) } _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) setNeedsDisplay() @@ -158,6 +161,7 @@ import UIKit public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject + self.additionalData = additionalData guard let model = model as? RadioButtonModel else { return } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift index 36dcde69..e1502515 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift @@ -9,7 +9,7 @@ import UIKit -open class RadioSwatch: Control { +open class RadioSwatch: Control, MFButtonProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -20,6 +20,9 @@ open class RadioSwatch: Control { private var strikeLayer: CALayer? private var maskLayer: CALayer? + private var delegateObject: MVMCoreUIDelegateObject? + var additionalData: [AnyHashable: Any]? + public var radioSwatchModel: RadioSwatchModel? { return model as? RadioSwatchModel } @@ -57,6 +60,8 @@ open class RadioSwatch: Control { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? RadioSwatchModel else { return } + self.delegateObject = delegateObject + self.additionalData = additionalData bottomText.text = model.text isSelected = model.selected isEnabled = model.enabled @@ -117,6 +122,11 @@ open class RadioSwatch: Control { guard isEnabled else { return } isSelected = true radioSwatchModel?.selected = isSelected + if let actionModel = radioSwatchModel?.action { + var additionalData = self.additionalData ?? [:] + additionalData[KeySourceModel] = radioSwatchModel + Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData) + } layer.setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift index 3aadd467..7acb3999 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift @@ -17,6 +17,7 @@ import Foundation public var enabled: Bool = true public var strikethrough: Bool = false public var fieldValue: String? + public var action: ActionModelProtocol? private enum CodingKeys: String, CodingKey { case moleculeName @@ -27,6 +28,7 @@ import Foundation case enabled case strikethrough case fieldValue + case action } required public init(from decoder: Decoder) throws { @@ -46,6 +48,7 @@ import Foundation self.strikethrough = strikethrough } fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) + action = try typeContainer.decodeModelIfPresent(codingKey: .action) } public func encode(to encoder: Encoder) throws { @@ -58,6 +61,7 @@ import Foundation try container.encode(enabled, forKey: .enabled) try container.encode(strikethrough, forKey: .strikethrough) try container.encodeIfPresent(fieldValue, forKey: .fieldValue) + try container.encodeModelIfPresent(action, forKey: .action) } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift index dfffcded..dd7dc9f0 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift @@ -395,17 +395,19 @@ public typealias ActionBlockConfirmation = () -> (Bool) let actionMap = model.action?.toJSON() let alternateActionMap = model.alternateAction?.toJSON() if actionMap != nil || alternateActionMap != nil { + var additionalDatatoUpdate = additionalData ?? [:] + additionalDatatoUpdate[KeySourceModel] = model didToggleAction = { [weak self] in guard let self = self else { return } if self.isOn { if actionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) } } else { if alternateActionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalData, delegateObject: delegateObject) + MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) } else if actionMap != nil { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject) } } } diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.h b/MVMCoreUI/Utility/MVMCoreUIConstants.h index 4c0a3553..684d6044 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.h +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.h @@ -42,6 +42,7 @@ extern NSString * const KeyIsOpaque; extern NSString * const KeyFieldKey; extern NSString * const KeyRequired; +extern NSString * const KeySourceModel; #pragma mark - Values diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.m b/MVMCoreUI/Utility/MVMCoreUIConstants.m index 5f5a9a45..e4954b01 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.m +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.m @@ -40,6 +40,7 @@ NSString * const KeyTextColor = @"textColor"; NSString * const KeyIsHidden = @"isHidden"; NSString * const KeyIsOpaque = @"isOpaque"; +NSString * const KeySourceModel = @"sourceModel"; #pragma mark - Values