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 VDSCoreTokens
import VDS
@objcMembers open class RadioButton: Control, MFButtonProtocol {
//--------------------------------------------------
@objcMembers open class RadioButton: VDS.RadioButton, RadioButtonSelectionHelperProtocol, VDSMoleculeViewProtocol, MFButtonProtocol, MVMCoreUIViewConstrainingProtocol {
//------------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var diameter: CGFloat = 20 {
didSet { widthConstraint?.constant = diameter }
//------------------------------------------------------
open var viewModel: RadioButtonModel!
open var delegateObject: MVMCoreUIDelegateObject?
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 {
radioModel?.state = isSelected
updateAccessibilityLabel()
viewModel.state = isSelected
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? {
model as? RadioButtonModel
}
lazy public var radioGroupName: String? = { radioModel?.fieldKey }()
lazy public var radioGroupName: String? = { viewModel.fieldKey }()
lazy public var radioButtonSelectionHelper: RadioButtonSelectionHelper? = {
@ -48,132 +46,120 @@ import VDSCoreTokens
return radioButtonModel
}()
public override var isEnabled: Bool {
didSet {
isUserInteractionEnabled = isEnabled
setNeedsDisplay()
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
override public init(frame: CGRect) {
super.init(frame: frame)
}
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
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()
}
/// There is currently no intention on using xib files.
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("xib file is not implemented for Checkbox.")
}
public convenience required init() {
self.init(frame:.zero)
}
//--------------------------------------------------
// MARK: - Validation
//--------------------------------------------------
/// The action performed when tapped.
func tapAction() {
if !isEnabled {
return
public func isValidField() -> Bool { isSelected }
public func formFieldName() -> String? {
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
if let radioButtonModel = radioButtonSelectionHelper {
radioButtonModel.selected(self)
if let radioButtonSelectionHelper {
radioButtonSelectionHelper.selected(self)
} else {
isSelected = !isSelected
}
if let radioModel = radioModel, let actionModel = radioModel.action, isSelected, !wasPreviouslySelected {
if let actionModel = viewModel.action, isSelected, !wasPreviouslySelected {
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)
setNeedsDisplay()
}
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
setNeedsUpdate()
}
//--------------------------------------------------
// MARK: - Methods
// MARK: - Actions
//--------------------------------------------------
/// Adjust accessibility label based on state of RadioButton.
func updateAccessibilityLabel() {
if let message = MVMCoreUIUtility.hardcodedString(withKey: "radio_button"),
let selectedState = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "radio_selected_state" : "radio_not_selected_state") {
accessibilityLabel = message + selectedState
/// 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
})
}
}
//--------------------------------------------------
// 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")
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
isSelected = viewModel.state
isEnabled = viewModel.enabled && !viewModel.readOnly
RadioButtonSelectionHelper.setupForRadioButtonGroup(viewModel, self, delegateObject: delegateObject)
}
}

View File

@ -7,47 +7,31 @@
//
import MVMCore
import VDS
open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
open class RadioButtonModel: FormFieldModel {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "radioButton"
public var id: String = UUID().uuidString
public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public static override var identifier: String { "radioButton" }
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.
public var fieldValue: String?
public var baseValue: AnyHashable?
public var groupName: String = FormValidator.defaultGroupName
public var fieldKey: String?
public var action: ActionModelProtocol?
public var inverted: Bool = false
public var surface: Surface { inverted ? .dark : .light }
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case id
case moleculeName
case backgroundColor
case accessibilityIdentifier
case state
case enabled
case fieldValue
case fieldKey
case groupName
case action
case readOnly
case inverted
}
@ -56,49 +40,41 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
//--------------------------------------------------
public init(_ state: Bool) {
super.init()
self.state = state
baseValue = state
self.baseValue = state
}
//--------------------------------------------------
// MARK: - Validation
//--------------------------------------------------
public func formFieldValue() -> AnyHashable? {
public override func formFieldValue() -> AnyHashable? {
guard enabled else { return nil }
return fieldValue
}
//--------------------------------------------------
// MARK: - Server Value
//--------------------------------------------------
open func formFieldServerValue() -> AnyHashable? {
return formFieldValue()
open override func setValidity(_ valid: Bool, errorMessage: String?) {
if let ruleErrorMessage = errorMessage, fieldKey != nil {
self.errorMessage = ruleErrorMessage
}
isValid = valid
updateUI?()
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
try super.init(from: decoder)
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) {
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
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)
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
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)
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(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.encodeModelIfPresent(action, forKey: .action)
try container.encodeIfPresent(inverted, forKey: .inverted)