initial generic entryfield

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2024-07-30 12:24:14 -05:00
parent 06b0c80a58
commit c326213302
8 changed files with 32 additions and 39 deletions

View File

@ -6,7 +6,7 @@ import Combine
/// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection. /// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection.
@objcMembers @objcMembers
@objc(VDSDatePicker) @objc(VDSDatePicker)
open class DatePicker: EntryFieldBase { open class DatePicker: EntryFieldBase<String> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------

View File

@ -13,7 +13,7 @@ import Combine
/// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection. /// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection.
@objcMembers @objcMembers
@objc(VDSDropdownSelect) @objc(VDSDropdownSelect)
open class DropdownSelect: EntryFieldBase { open class DropdownSelect: EntryFieldBase<String> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------

View File

@ -10,9 +10,10 @@ import UIKit
import VDSCoreTokens import VDSCoreTokens
import Combine import Combine
/// A stepper is a two-segment control that people use to increase or decrease an incremental value. /// A stepper is a two-segment control that people use to increase or decrease an incremental value.'
@objcMembers
@objc(VDSInputStepper) @objc(VDSInputStepper)
open class InputStepper: EntryFieldBase { open class InputStepper: EntryFieldBase<Int> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
@ -70,6 +71,8 @@ open class InputStepper: EntryFieldBase {
/// Allows an id to be passed to input stepper. /// Allows an id to be passed to input stepper.
open var id: Int? { didSet { setNeedsUpdate() } } open var id: Int? { didSet { setNeedsUpdate() } }
open override var value: Int? { return defaultValue }
/// Maximum value of the input stepper, defaults to '99'. /// Maximum value of the input stepper, defaults to '99'.
open var maxValue: Int? = 99 { open var maxValue: Int? = 99 {
didSet { didSet {
@ -101,20 +104,9 @@ open class InputStepper: EntryFieldBase {
/// Accepts any text or character to appear next to input stepper value. /// Accepts any text or character to appear next to input stepper value.
open var trailingText: String? { didSet { setNeedsUpdate() } } open var trailingText: String? { didSet { setNeedsUpdate() } }
/// Value for the textField
open override var value: String? {
return nil
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
//-------------------------------------------------- //--------------------------------------------------
/// Default Int value of the input stepper, defaults to '0'.
internal var defaultIntValue: Int {
guard let intValue = defaultValue as? Int else { return 0 }
return intValue
}
/// This is the view that will be wrapped with the border for userInteraction. /// This is the view that will be wrapped with the border for userInteraction.
/// The only subview of this view is the stepperStackView. /// The only subview of this view is the stepperStackView.
internal var stepperContainerView = View().with { internal var stepperContainerView = View().with {
@ -186,6 +178,7 @@ open class InputStepper: EntryFieldBase {
super.setup() super.setup()
// Set initial states // Set initial states
defaultValue = 0
containerView.isEnabled = false containerView.isEnabled = false
statusIcon.isHidden = true statusIcon.isHidden = true
@ -227,7 +220,7 @@ open class InputStepper: EntryFieldBase {
statusIcon.isHidden = true statusIcon.isHidden = true
// Update label text, style, color, ande surface. // Update label text, style, color, ande surface.
textLabel.text = String(defaultIntValue) + " " + (trailingText ?? "") textLabel.text = "\(value ?? 0) " + (trailingText ?? "")
textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall
textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable() textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable()
textLabel.surface = surface textLabel.surface = surface
@ -275,6 +268,7 @@ open class InputStepper: EntryFieldBase {
controlWidthPercentage = nil controlWidthPercentage = nil
widthPercentage = nil widthPercentage = nil
id = nil id = nil
defaultValue = 0
minValue = 0 minValue = 0
maxValue = 99 maxValue = 99
trailingText = nil trailingText = nil
@ -314,18 +308,18 @@ open class InputStepper: EntryFieldBase {
//-------------------------------------------------- //--------------------------------------------------
internal func checkDefaultValue() { internal func checkDefaultValue() {
if let minValue, let maxValue { if let minValue, let maxValue {
defaultValue = defaultIntValue > maxValue ? maxValue : defaultIntValue < minValue ? minValue : defaultIntValue defaultValue = defaultValue! > maxValue ? maxValue : defaultValue! < minValue ? minValue : defaultValue!
} }
} }
internal func decrementButtonClick() { internal func decrementButtonClick() {
defaultValue = defaultIntValue - 1 defaultValue = defaultValue! - 1
checkDefaultValue() checkDefaultValue()
updateButtonStates() updateButtonStates()
} }
internal func incrementButtonClick() { internal func incrementButtonClick() {
defaultValue = defaultIntValue + 1 defaultValue = defaultValue! + 1
checkDefaultValue() checkDefaultValue()
updateButtonStates() updateButtonStates()
} }
@ -340,8 +334,8 @@ open class InputStepper: EntryFieldBase {
decrementButton.isEnabled = false decrementButton.isEnabled = false
incrementButton.isEnabled = false incrementButton.isEnabled = false
} else { } else {
decrementButton.isEnabled = defaultIntValue > minValue ?? 0 ? true : false decrementButton.isEnabled = defaultValue! > minValue ?? 0 ? true : false
incrementButton.isEnabled = defaultIntValue < maxValue ?? 99 ? true : false incrementButton.isEnabled = defaultValue! < maxValue ?? 99 ? true : false
} }
} }

View File

@ -11,10 +11,7 @@ import VDSCoreTokens
import Combine import Combine
/// Base Class used to build out a Input controls. /// Base Class used to build out a Input controls.
@objcMembers open class EntryFieldBase<ValueType>: Control, Changeable, FormFieldInternalValidatable {
@objc(VDSEntryField)
open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@ -229,11 +226,11 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
open var inputId: String? { didSet { setNeedsUpdate() } } open var inputId: String? { didSet { setNeedsUpdate() } }
/// The text of this textField. /// The text of this textField.
open var value: String? { open var value: ValueType? {
get { fatalError("must be read from subclass")} get { fatalError("must be read from subclass")}
} }
open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } open var defaultValue: ValueType? { didSet { setNeedsUpdate() } }
open var isRequired: Bool = false { didSet { setNeedsUpdate() } } open var isRequired: Bool = false { didSet { setNeedsUpdate() } }
@ -245,7 +242,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
} }
} }
open var rules = [AnyRule<String>]() open var rules = [AnyRule<ValueType>]()
open var accessibilityHintText: String = "Double tap to open" open var accessibilityHintText: String = "Double tap to open"
@ -342,8 +339,8 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
} }
containerView.bridge_accessibilityValueBlock = { [weak self] in containerView.bridge_accessibilityValueBlock = { [weak self] in
guard let self else { return "" } guard let self, let value else { return "" }
return value return "\(value)"
} }
statusIcon.bridge_accessibilityLabelBlock = { [weak self] in statusIcon.bridge_accessibilityLabelBlock = { [weak self] in
@ -523,8 +520,8 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
//-------------------------------------------------- //--------------------------------------------------
internal func updateRules() { internal func updateRules() {
rules.removeAll() rules.removeAll()
if isRequired && useRequiredRule { if isRequired && useRequiredRule && ValueType.self == String.self {
let rule = RequiredRule() let rule = RequiredRule<ValueType>()
if let errorText, !errorText.isEmpty { if let errorText, !errorText.isEmpty {
rule.errorMessage = errorText rule.errorMessage = errorText
} else if let labelText{ } else if let labelText{

View File

@ -15,7 +15,7 @@ import Combine
/// dates and security codes in their correct formats. /// dates and security codes in their correct formats.
@objcMembers @objcMembers
@objc(VDSInputField) @objc(VDSInputField)
open class InputField: EntryFieldBase { open class InputField: EntryFieldBase<String> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers

View File

@ -7,12 +7,14 @@
import Foundation import Foundation
class RequiredRule: Rule { class RequiredRule<ValueType>: Rule {
var maxLength: Int? var maxLength: Int?
var errorMessage: String = "This field is required." var errorMessage: String = "This field is required."
func isValid(value: String?) -> Bool { func isValid(value: ValueType?) -> Bool {
guard let value, !value.isEmpty, value.count > 0 else { return false } guard let value,
!"\(value)".isEmpty,
"\(value)".count > 0 else { return false }
return true return true
} }
} }

View File

@ -14,7 +14,7 @@ import Combine
/// Use a text area when you want customers to enter text thats longer than a single line. /// Use a text area when you want customers to enter text thats longer than a single line.
@objcMembers @objcMembers
@objc(VDSTextArea) @objc(VDSTextArea)
open class TextArea: EntryFieldBase { open class TextArea: EntryFieldBase<String> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
/// Protocol used for a FormField object. /// Protocol used for a FormField object.
public protocol FormFieldable { public protocol FormFieldable<ValueType> {
associatedtype ValueType = AnyHashable associatedtype ValueType = AnyHashable
/// Unique Id for the Form Field object within a Form. /// Unique Id for the Form Field object within a Form.
@ -19,7 +19,7 @@ public protocol FormFieldable {
} }
/// Protocol for FormFieldable that require internal validation. /// Protocol for FormFieldable that require internal validation.
public protocol FormFieldInternalValidatable: FormFieldable, Errorable { public protocol FormFieldInternalValidatable<ValueType>: FormFieldable, Errorable {
/// Rules that drive the validator /// Rules that drive the validator
var rules: [AnyRule<ValueType>] { get set } var rules: [AnyRule<ValueType>] { get set }