converted to VDS

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2024-07-03 15:58:15 -05:00
parent fa52fa8c12
commit 10c55b12be
2 changed files with 131 additions and 176 deletions

View File

@ -7,37 +7,35 @@
// //
import UIKit import UIKit
import VDSCoreTokens import VDS
@objcMembers open class RadioButton: Control, MFButtonProtocol { @objcMembers open class RadioButton: VDS.RadioButton, RadioButtonSelectionHelperProtocol, VDSMoleculeViewProtocol, MFButtonProtocol, MVMCoreUIViewConstrainingProtocol {
//-------------------------------------------------- //------------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //------------------------------------------------------
open var viewModel: RadioButtonModel!
open var delegateObject: MVMCoreUIDelegateObject?
open var additionalData: [AnyHashable : Any]?
public var diameter: CGFloat = 20 { open var radioButtonModel: RadioButtonModel {
didSet { widthConstraint?.constant = diameter } viewModel
} }
@objc public override var isSelected: Bool { // Form Validation
var fieldKey: String?
var fieldValue: JSONValue?
var groupName: String?
open override var isSelected: Bool {
didSet { didSet {
radioModel?.state = isSelected viewModel.state = isSelected
updateAccessibilityLabel() if oldValue != isSelected {
sendActions(for: .valueChanged)
}
} }
} }
public var enabledColor: UIColor {
return radioModel?.inverted ?? false ? VDSColor.elementsPrimaryOndark : VDSColor.elementsPrimaryOnlight
}
public var disabledColor: UIColor {
return radioModel?.inverted ?? false ? VDSColor.interactiveDisabledOndark : VDSColor.interactiveDisabledOnlight
}
public var delegateObject: MVMCoreUIDelegateObject?
var additionalData: [AnyHashable: Any]?
public var radioModel: RadioButtonModel? { lazy public var radioGroupName: String? = { viewModel.fieldKey }()
model as? RadioButtonModel
}
lazy public var radioGroupName: String? = { radioModel?.fieldKey }()
lazy public var radioButtonSelectionHelper: RadioButtonSelectionHelper? = { lazy public var radioButtonSelectionHelper: RadioButtonSelectionHelper? = {
@ -48,132 +46,120 @@ import VDSCoreTokens
return radioButtonModel return radioButtonModel
}() }()
public override var isEnabled: Bool { //--------------------------------------------------
didSet { // MARK: - Initializers
isUserInteractionEnabled = isEnabled //--------------------------------------------------
setNeedsDisplay()
} override public init(frame: CGRect) {
super.init(frame: frame)
} }
//-------------------------------------------------- /// There is currently no intention on using xib files.
// MARK: - Constraints required public init?(coder aDecoder: NSCoder) {
//-------------------------------------------------- super.init(coder: aDecoder)
fatalError("xib file is not implemented for Checkbox.")
}
public var widthConstraint: NSLayoutConstraint? public convenience required init() {
public var heightConstraint: NSLayoutConstraint? self.init(frame:.zero)
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
open override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
let color = isEnabled == false ? disabledColor.cgColor : enabledColor.cgColor
layer.cornerRadius = bounds.width * 0.5
layer.borderColor = color
layer.borderWidth = bounds.width * 0.0333
if isSelected {
// Space around inner circle is 1/5 the size
context.addEllipse(in: CGRect(x: bounds.width * 0.2,
y: bounds.height * 0.2,
width: bounds.width * 0.6,
height: bounds.height * 0.6))
context.setFillColor(color)
context.fillPath()
}
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Validation // MARK: - Validation
//-------------------------------------------------- //--------------------------------------------------
/// The action performed when tapped.
func tapAction() {
if !isEnabled {
return
}
let wasPreviouslySelected = isSelected
if let radioButtonModel = radioButtonSelectionHelper {
radioButtonModel.selected(self)
} else {
isSelected = !isSelected
}
if let radioModel = radioModel, let actionModel = radioModel.action, isSelected, !wasPreviouslySelected {
Task(priority: .userInitiated) {
try await Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioModel)
}
}
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
setNeedsDisplay()
}
public func isValidField() -> Bool { isSelected } public func isValidField() -> Bool { isSelected }
public func formFieldName() -> String? { public func formFieldName() -> String? {
radioModel?.fieldKey viewModel.fieldKey
} }
public func formFieldGroupName() -> String? { public func formFieldGroupName() -> String? {
radioModel?.fieldKey viewModel.fieldKey
} }
public func formFieldValue() -> AnyHashable? { public func formFieldValue() -> AnyHashable? {
guard let radioModel = radioModel, radioModel.enabled else { return nil } guard let radioModel = viewModel, radioModel.enabled else { return nil }
return radioModel.fieldValue return radioModel.fieldValue
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Methods // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
/// Adjust accessibility label based on state of RadioButton. open override func setup() {
func updateAccessibilityLabel() { super.setup()
if let message = MVMCoreUIUtility.hardcodedString(withKey: "radio_button"), bridge_accessibilityLabelBlock = { [weak self] in
let selectedState = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "radio_selected_state" : "radio_not_selected_state") { guard let self else { return nil }
accessibilityLabel = message + selectedState if let message = MVMCoreUIUtility.hardcodedString(withKey: "radio_button"),
let selectedState = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "radio_selected_state" : "radio_not_selected_state") {
return message + selectedState
} else {
return nil
}
} }
}
//--------------------------------------------------
// MARK: - MVMViewProtocol
//--------------------------------------------------
open override func setupView() {
super.setupView()
backgroundColor = .clear
clipsToBounds = true
widthConstraint = widthAnchor.constraint(equalToConstant: 20)
widthConstraint?.isActive = true
heightConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1)
heightConstraint?.isActive = true
addTarget(self, action: #selector(tapAction), for: .touchUpInside)
isAccessibilityElement = true
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint")
accessibilityTraits = .button
updateAccessibilityLabel()
} }
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { open override func toggle() {
super.set(with: model, delegateObject, additionalData) guard !isSelected, isEnabled else { return }
self.delegateObject = delegateObject
self.additionalData = additionalData
guard let model = model as? RadioButtonModel else { return } //removed error
if showError && isSelected == false {
showError.toggle()
}
isSelected = model.state let wasPreviouslySelected = isSelected
isEnabled = model.enabled && !model.readOnly if let radioButtonSelectionHelper {
RadioButtonSelectionHelper.setupForRadioButtonGroup(model, self, delegateObject: delegateObject) radioButtonSelectionHelper.selected(self)
} else {
isSelected = !isSelected
}
if let actionModel = viewModel.action, isSelected, !wasPreviouslySelected {
Task(priority: .userInitiated) {
try await Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: viewModel)
}
}
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
setNeedsUpdate()
} }
public override func reset() { //--------------------------------------------------
super.reset() // MARK: - Actions
backgroundColor = .clear //--------------------------------------------------
/// This will toggle the state of the Checkbox and execute the actionBlock if provided.
public func tapAction() {
toggle()
}
//--------------------------------------------------
// MARK: - Molecular
//--------------------------------------------------
open func needsToBeConstrained() -> Bool { true }
public func horizontalAlignment() -> UIStackView.Alignment { .leading }
public func updateView(_ size: CGFloat) {}
public func viewModelDidUpdate() {
//events
viewModel.updateUI = {
MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in
guard let self = self else { return }
let isValid = viewModel.isValid ?? true
showError = !isValid
isEnabled = viewModel.enabled
})
}
isSelected = viewModel.state
isEnabled = viewModel.enabled && !viewModel.readOnly
RadioButtonSelectionHelper.setupForRadioButtonGroup(viewModel, self, delegateObject: delegateObject)
} }
} }

View File

@ -7,47 +7,31 @@
// //
import MVMCore import MVMCore
import VDS
open class RadioButtonModel: FormFieldModel {
open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public static var identifier: String = "radioButton" public static override var identifier: String { "radioButton" }
public var id: String = UUID().uuidString
public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var state: Bool = false public var state: Bool = false
public var enabled: Bool = true
public var readOnly: Bool = false
/// The specific value to send to server. TODO: update this to be more generic. /// The specific value to send to server. TODO: update this to be more generic.
public var fieldValue: String? public var fieldValue: String?
public var baseValue: AnyHashable?
public var groupName: String = FormValidator.defaultGroupName
public var fieldKey: String?
public var action: ActionModelProtocol? public var action: ActionModelProtocol?
public var inverted: Bool = false public var inverted: Bool = false
public var surface: Surface { inverted ? .dark : .light }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
//-------------------------------------------------- //--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case id
case moleculeName
case backgroundColor
case accessibilityIdentifier
case state case state
case enabled
case fieldValue case fieldValue
case fieldKey
case groupName
case action case action
case readOnly
case inverted case inverted
} }
@ -56,24 +40,26 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
//-------------------------------------------------- //--------------------------------------------------
public init(_ state: Bool) { public init(_ state: Bool) {
super.init()
self.state = state self.state = state
baseValue = state self.baseValue = state
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Validation // MARK: - Validation
//-------------------------------------------------- //--------------------------------------------------
public func formFieldValue() -> AnyHashable? { public override func formFieldValue() -> AnyHashable? {
guard enabled else { return nil } guard enabled else { return nil }
return fieldValue return fieldValue
} }
//-------------------------------------------------- open override func setValidity(_ valid: Bool, errorMessage: String?) {
// MARK: - Server Value if let ruleErrorMessage = errorMessage, fieldKey != nil {
//-------------------------------------------------- self.errorMessage = ruleErrorMessage
open func formFieldServerValue() -> AnyHashable? { }
return formFieldValue() isValid = valid
updateUI?()
} }
//-------------------------------------------------- //--------------------------------------------------
@ -81,24 +67,14 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
//-------------------------------------------------- //--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
try super.init(from: decoder)
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) {
self.state = state self.state = state
} }
enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true
readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
baseValue = state baseValue = state
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
}
fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue)
action = try typeContainer.decodeModelIfPresent(codingKey: .action) action = try typeContainer.decodeModelIfPresent(codingKey: .action)
if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) {
@ -106,17 +82,10 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
} }
} }
public func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encode(id, forKey: .id)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(state, forKey: .state) try container.encode(state, forKey: .state)
try container.encode(enabled, forKey: .enabled)
try container.encode(readOnly, forKey: .readOnly)
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(groupName, forKey: .groupName)
try container.encodeIfPresent(fieldValue, forKey: .fieldValue) try container.encodeIfPresent(fieldValue, forKey: .fieldValue)
try container.encodeModelIfPresent(action, forKey: .action) try container.encodeModelIfPresent(action, forKey: .action)
try container.encodeIfPresent(inverted, forKey: .inverted) try container.encodeIfPresent(inverted, forKey: .inverted)