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!
public var diameter: CGFloat = 20 { open var delegateObject: MVMCoreUIDelegateObject?
didSet { widthConstraint?.constant = diameter } open var additionalData: [AnyHashable : Any]?
open var radioButtonModel: RadioButtonModel {
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 lazy public var radioGroupName: String? = { viewModel.fieldKey }()
}
public var disabledColor: UIColor {
return radioModel?.inverted ?? false ? VDSColor.interactiveDisabledOndark : VDSColor.interactiveDisabledOnlight
}
public var delegateObject: MVMCoreUIDelegateObject?
var additionalData: [AnyHashable: Any]?
public var radioModel: RadioButtonModel? {
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 var heightConstraint: NSLayoutConstraint?
//--------------------------------------------------
// 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()
}
} }
public convenience required init() {
self.init(frame:.zero)
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Validation // MARK: - Validation
//-------------------------------------------------- //--------------------------------------------------
/// The action performed when tapped. public func isValidField() -> Bool { isSelected }
func tapAction() {
if !isEnabled { public func formFieldName() -> String? {
return viewModel.fieldKey
}
public func formFieldGroupName() -> String? {
viewModel.fieldKey
}
public func formFieldValue() -> AnyHashable? {
guard let radioModel = viewModel, radioModel.enabled else { return nil }
return radioModel.fieldValue
}
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
open override func setup() {
super.setup()
bridge_accessibilityLabelBlock = { [weak self] in
guard let self else { return nil }
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
}
} }
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint")
}
open override func toggle() {
guard !isSelected, isEnabled else { return }
//removed error
if showError && isSelected == false {
showError.toggle()
}
let wasPreviouslySelected = isSelected let wasPreviouslySelected = isSelected
if let radioButtonModel = radioButtonSelectionHelper { if let radioButtonSelectionHelper {
radioButtonModel.selected(self) radioButtonSelectionHelper.selected(self)
} else { } else {
isSelected = !isSelected isSelected = !isSelected
} }
if let radioModel = radioModel, let actionModel = radioModel.action, isSelected, !wasPreviouslySelected { if let actionModel = viewModel.action, isSelected, !wasPreviouslySelected {
Task(priority: .userInitiated) { Task(priority: .userInitiated) {
try await Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioModel) try await Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: viewModel)
} }
} }
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
setNeedsDisplay() setNeedsUpdate()
}
public func isValidField() -> Bool { isSelected }
public func formFieldName() -> String? {
radioModel?.fieldKey
}
public func formFieldGroupName() -> String? {
radioModel?.fieldKey
}
public func formFieldValue() -> AnyHashable? {
guard let radioModel = radioModel, radioModel.enabled else { return nil }
return radioModel.fieldValue
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Methods // MARK: - Actions
//-------------------------------------------------- //--------------------------------------------------
/// Adjust accessibility label based on state of RadioButton. /// This will toggle the state of the Checkbox and execute the actionBlock if provided.
func updateAccessibilityLabel() { public func tapAction() {
if let message = MVMCoreUIUtility.hardcodedString(withKey: "radio_button"), toggle()
let selectedState = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "radio_selected_state" : "radio_not_selected_state") { }
accessibilityLabel = message + selectedState
//--------------------------------------------------
// 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
// MARK: - MVMViewProtocol RadioButtonSelectionHelper.setupForRadioButtonGroup(viewModel, self, delegateObject: delegateObject)
//--------------------------------------------------
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")
accessibilityTraits = .button
updateAccessibilityLabel()
}
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 }
isSelected = model.state
isEnabled = model.enabled && !model.readOnly
RadioButtonSelectionHelper.setupForRadioButtonGroup(model, self, delegateObject: delegateObject)
}
public override func reset() {
super.reset()
backgroundColor = .clear
} }
} }

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,49 +40,41 @@ 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?()
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Codec // MARK: - Codec
//-------------------------------------------------- //--------------------------------------------------
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)