From f7ded238578468d1b0ea87ae28a5da43bd2fb276 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 6 Jun 2024 16:18:32 -0500 Subject: [PATCH] CXTDT-565105 - InputField - Date - Typeover text not working. Signed-off-by: Matt Bruce --- .../InputField/FieldTypes/Date.swift | 125 +++++++++++++++++- .../TextFields/InputField/InputField.swift | 11 +- 2 files changed, 125 insertions(+), 11 deletions(-) diff --git a/VDS/Components/TextFields/InputField/FieldTypes/Date.swift b/VDS/Components/TextFields/InputField/FieldTypes/Date.swift index 19656745..17decc96 100644 --- a/VDS/Components/TextFields/InputField/FieldTypes/Date.swift +++ b/VDS/Components/TextFields/InputField/FieldTypes/Date.swift @@ -10,6 +10,18 @@ import UIKit extension InputField { + public class DateRule: Rule, Withable { + public var dateFormat: DateFormat? + public var errorMessage: String = "Enter a valid date" + private let dateFormatter = DateFormatter() + + public func isValid(value: String?) -> Bool { + guard let value, let dateFormat, !value.isEmpty else { return true } + dateFormatter.dateFormat = dateFormat.formatString + return dateFormatter.date(from: value) != nil + } + } + public enum DateFormat: String, CaseIterable { case mmyy case mmddyy @@ -46,6 +58,102 @@ extension InputField { case .mmddyyyy: [2,4] } } + + public func isValid(_ date: String) -> Bool { + let allowedCharacters = CharacterSet(charactersIn: "0123456789/") + + // Check if the input contains only allowed characters + if date.rangeOfCharacter(from: allowedCharacters.inverted) != nil || date.isEmpty { + return false + } + + let components = date.split(separator: "/") + + + func isMonth(_ month: String) -> Bool { + switch month.count { + case 1: + guard let month = Int(month), (0...1).contains(month) else { return false } + return true + case 2: + guard let month = Int(month), (1...12).contains(month) else { return false } + return true + default: + return false + } + } + + func isDay(_ day: String) -> Bool { + switch day.count { + case 1: + guard let day = Int(day),(1...3).contains(day) else { return false } + return true + case 2: + guard let day = Int(day), (1...31).contains(day) else { return false } + return true + default: + return false + } + } + + func isYear(_ year: String, max: Int) -> Bool { + guard year.count <= max else { + return false + } + return true + } + + switch self { + case .mmyy: + if components.count > 2 { + return false + } + + // Validate month part + if components.count > 0, let monthPart = components.first { + if !isMonth(String(monthPart)) { + return false + } + } + + // Validate year part + if components.count > 1, let yearPart = components.last { + if !isYear(String(yearPart), max: 2) { + return false + } + } + + case .mmddyy, .mmddyyyy: + if components.count > 3 { + return false + } + + // Validate month part + if components.count > 0, let monthPart = components.first { + if !isMonth(String(monthPart)) { + return false + } + } + + // Validate day part + if components.count > 1 { + let dayPart = components[1] + if !isDay(String(dayPart)) { + return false + } + } + + // Validate year part + if components.count > 2, let yearPart = components.last { + if !isYear(String(yearPart), max: self == .mmddyy ? 2 : 4) { + return false + } + } + } + + return true + } + } class DateHandler: FieldTypeHandler { @@ -58,16 +166,15 @@ extension InputField { override func updateView(_ inputField: InputField) { minWidth = 114.0 - placeholderText = inputField.dateFormat.placeholderText - + //placeholderText = inputField.dateFormat.placeholderText + inputField.textField.formatText = inputField.dateFormat.placeholderText super.updateView(inputField) } override func appendRules(_ inputField: InputField) { if let text = inputField.textField.text, text.count > 0 { - let rule = CharacterCountRule().copyWith { - $0.maxLength = inputField.dateFormat.maxLength - $0.compareType = .equals + let rule = DateRule().copyWith { + $0.dateFormat = inputField.dateFormat $0.errorMessage = "Enter a valid date." } inputField.rules.append(.init(rule)) @@ -86,9 +193,13 @@ extension InputField { if newText.count > inputField.dateFormat.maxLength { return false } - + if newText.count <= inputField.dateFormat.maxLength { - textField.text = String.format(newText, indices: inputField.dateFormat.separatorIndices, with: "/") + let rawNumber = newText.filter { $0.isNumber } + let formatted = String.format(rawNumber, indices: inputField.dateFormat.separatorIndices, with: "/") + if inputField.dateFormat.isValid(formatted) || formatted.isEmpty { + textField.text = formatted + } return false } else { return true diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index af358dbf..a160420a 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -190,9 +190,11 @@ open class InputField: EntryFieldBase { successLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: .success) - - borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success) + backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: [.success, .focused]) + borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success) + + textField.textColorConfiguration = textFieldTextColorConfiguration } open override func getFieldContainer() -> UIView { @@ -221,9 +223,9 @@ open class InputField: EntryFieldBase { super.updateView() + textField.surface = surface textField.isEnabled = isEnabled textField.isUserInteractionEnabled = isEnabled && !isReadOnly - textField.textColor = textFieldTextColorConfiguration.getColor(self) } open override func updateAccessibility() { @@ -248,7 +250,7 @@ open class InputField: EntryFieldBase { statusIcon.name = .checkmarkAlt statusIcon.color = iconColorConfiguration.getColor(self) statusIcon.surface = surface - statusIcon.isHidden = !isEnabled + statusIcon.isHidden = !isEnabled || state.contains(.focused) } else { successLabel.isHidden = true } @@ -303,6 +305,7 @@ extension InputField: UITextFieldDelegate { public func textFieldDidBeginEditing(_ textField: UITextField) { fieldType.handler().textFieldDidBeginEditing(self, textField: textField) updateContainerView() + updateErrorLabel() } public func textFieldDidEndEditing(_ textField: UITextField) {