From d943202e8389b2b666499e753694fa515b46138f Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 10 Jul 2024 16:33:54 -0500 Subject: [PATCH 1/7] updated placeholder logic Signed-off-by: Matt Bruce --- VDS/Components/TextFields/TextArea/TextView.swift | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift index 3f40ae38..3c5dc5ee 100644 --- a/VDS/Components/TextFields/TextArea/TextView.swift +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -45,11 +45,7 @@ open class TextView: UITextView, ViewProtocol, Errorable { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - open var placeholder: String? { - didSet { - placeholderLabel.text = placeholder - } - } + open var placeholder: String? { didSet { setNeedsUpdate() } } open var placeholderLabel = Label().with { $0.textColorConfiguration = ViewColorConfiguration().with { @@ -319,8 +315,9 @@ open class TextView: UITextView, ViewProtocol, Errorable { attributedText = nil } placeholderLabel.textStyle = textStyle - placeholderLabel.isHidden = !text.isEmpty placeholderLabel.surface = surface + placeholderLabel.text = placeholder + placeholderLabel.isHidden = !text.isEmpty } } From 46d4098cf2a1c762e81c686670f543260a98aabb Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 11 Jul 2024 10:53:41 -0500 Subject: [PATCH 2/7] fixed issue with showing errorIcon without error message Signed-off-by: Matt Bruce --- .../TextFields/EntryFieldBase.swift | 93 ++++++++++--------- 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 5bd50351..89186a3b 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -28,7 +28,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { public required init?(coder: NSCoder) { super.init(coder: coder) } - + //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- @@ -91,7 +91,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { $0.spacing = VDSLayout.space2X } }() - + /// This is the view that will be wrapped with the border for userInteraction. /// The only subview of this view is the fieldStackView internal var containerView = View().with { @@ -115,7 +115,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { internal var widthConstraint: NSLayoutConstraint? internal var trailingEqualsConstraint: NSLayoutConstraint? internal var trailingLessThanEqualsConstraint: NSLayoutConstraint? - + //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- @@ -133,14 +133,14 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false) } - + internal var backgroundColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .normal) $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .disabled) $0.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error) $0.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: [.error, .focused]) } - + internal var borderColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOndark, forState: .normal) $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forState: .focused) @@ -155,7 +155,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .error) } - + internal var readOnlyBorderColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSFormControlsColor.borderReadonlyOnlight, VDSFormControlsColor.borderReadonlyOndark, forState: .normal) } @@ -164,7 +164,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { // MARK: - Public Properties //-------------------------------------------------- open var onChangeSubscriber: AnyCancellable? - + open var titleLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textStyle = .bodySmall @@ -185,7 +185,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { $0.size = .medium $0.isAccessibilityElement = true } - + open var labelText: String? { didSet { setNeedsUpdate() } } open var helperText: String? { didSet { setNeedsUpdate() } } @@ -195,7 +195,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { /// FormFieldValidator open var validator: (any FormFieldValidatorable)? - + /// Override UIControl state to add the .error state if showError is true. open override var state: UIControl.State { get { @@ -214,17 +214,17 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { return state } } - + open var errorText: String? { didSet { setNeedsUpdate() } } - + open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } } - + open var transparentBackground: Bool = false { didSet { setNeedsUpdate() } } open var width: CGFloat? { didSet { setNeedsUpdate() } } - + open var inputId: String? { didSet { setNeedsUpdate() } } - + /// The text of this textField. open var value: String? { get { fatalError("must be read from subclass")} @@ -235,21 +235,21 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open var isRequired: Bool = false { didSet { setNeedsUpdate() } } open var isReadOnly: Bool = false { didSet { setNeedsUpdate() } } - + open var helperTextPlacement: HelperTextPlacement = .bottom { didSet { updateHelperTextPosition() } } - + open var rules = [AnyRule]() - + open var accessibilityHintText: String = "Double tap to open" //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() @@ -371,7 +371,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { titleLabel.textStyle = .bodySmall errorLabel.textStyle = .bodySmall helperLabel.textStyle = .bodySmall - + labelText = nil helperText = nil showError = false @@ -389,19 +389,19 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open override var canBecomeFirstResponder: Bool { responder?.canBecomeFirstResponder ?? super.canBecomeFirstResponder } - + open override func becomeFirstResponder() -> Bool { responder?.becomeFirstResponder() ?? super.becomeFirstResponder() } - + open override var canResignFirstResponder: Bool { responder?.canResignFirstResponder ?? super.canResignFirstResponder } - + open override func resignFirstResponder() -> Bool { responder?.resignFirstResponder() ?? super.resignFirstResponder() } - + //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- @@ -409,21 +409,21 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open func getFieldContainer() -> UIView { fatalError("Subclass must return the view that contains the field/view the user will interact with.") } - + /// Container for the area in which helper or error text presents. open func getBottomContainer() -> UIView { return bottomContainerStackView } - + open func validate(){ updateRules() validator = FormFieldValidator(field: self, rules: rules) validator?.validate() setNeedsUpdate() } - + open func updateTitleLabel() { - + //update the local vars for the label since we no //long have a model var attributes: [any LabelAttributeModel] = [] @@ -444,36 +444,43 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { if let tooltipModel { attributes.append(TooltipLabelAttribute(surface: surface, model: tooltipModel, presenter: self)) } - + //set the titleLabel titleLabel.text = updatedLabelText titleLabel.attributes = attributes titleLabel.surface = surface titleLabel.isEnabled = isEnabled } - + open func updateErrorLabel(){ - if showError, let errorText { - errorLabel.text = errorText - errorLabel.surface = surface - errorLabel.isEnabled = isEnabled - errorLabel.isHidden = false - statusIcon.name = .error - statusIcon.surface = surface - statusIcon.isHidden = !isEnabled || state.contains(.focused) - } else if hasInternalError, let internalErrorText { - errorLabel.text = internalErrorText - errorLabel.surface = surface - errorLabel.isEnabled = isEnabled - errorLabel.isHidden = false + + /// always show the errorIcon if there is an error + if showError || hasInternalError { statusIcon.name = .error statusIcon.surface = surface statusIcon.isHidden = !isEnabled || state.contains(.focused) } else { statusIcon.isHidden = true - errorLabel.isHidden = true } statusIcon.color = iconColorConfiguration.getColor(self) + + // only show errorLabel if there is a message + var message: String? + if showError, let errorText { + message = errorText + } else if hasInternalError, let internalErrorText { + message = internalErrorText + } + + if let message { + errorLabel.text = message + errorLabel.surface = surface + errorLabel.isEnabled = isEnabled + errorLabel.isHidden = false + } else { + errorLabel.isHidden = true + } + } open func updateHelperLabel(){ From c2e7465c4e6d4089e2ddcb23e8c74df330f20ce0 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 11 Jul 2024 12:39:34 -0500 Subject: [PATCH 3/7] fixed bug in textarea maxLength Signed-off-by: Matt Bruce --- VDS/Components/TextFields/TextArea/TextArea.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index c668a851..f8a9a6b3 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -111,7 +111,9 @@ open class TextArea: EntryFieldBase { } didSet { - validate() + if textView.isFirstResponder { + validate() + } } } From fae53c4f5be00bb71430aaf6eb7945c768840f14 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 11 Jul 2024 12:39:46 -0500 Subject: [PATCH 4/7] added variable to shut off internal required Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 89186a3b..bc2c31ba 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -186,6 +186,8 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { $0.isAccessibilityElement = true } + open var useRequiredRule: Bool = true { didSet { setNeedsUpdate() } } + open var labelText: String? { didSet { setNeedsUpdate() } } open var helperText: String? { didSet { setNeedsUpdate() } } @@ -522,7 +524,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //-------------------------------------------------- internal func updateRules() { rules.removeAll() - if self.isRequired { + if isRequired && useRequiredRule { let rule = RequiredRule() if let errorText, !errorText.isEmpty { rule.errorMessage = errorText From 405a84b3598d3e2d552f8b58d372602426f3a416 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 11 Jul 2024 14:03:48 -0500 Subject: [PATCH 5/7] containerview public Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index bc2c31ba..cf2dffa2 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -91,13 +91,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { $0.spacing = VDSLayout.space2X } }() - - /// This is the view that will be wrapped with the border for userInteraction. - /// The only subview of this view is the fieldStackView - internal var containerView = View().with { - $0.isAccessibilityElement = true - } - + /// This is set by a local method. internal var bottomContainerView: UIView! @@ -163,6 +157,12 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + /// This is the view that will be wrapped with the border for userInteraction. + /// The only subview of this view is the fieldStackView + open var containerView = View().with { + $0.isAccessibilityElement = true + } + open var onChangeSubscriber: AnyCancellable? open var titleLabel = Label().with { From acd688405d011757dbce22031ad158c361156979 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 11 Jul 2024 14:03:56 -0500 Subject: [PATCH 6/7] updates public Signed-off-by: Matt Bruce --- VDS/Components/TextFields/InputField/InputField.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index a7e0bbb7..71ad6b04 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -107,6 +107,9 @@ open class InputField: EntryFieldBase { $0.isAccessibilityElement = false $0.autocorrectionType = .no $0.spellCheckingType = .no + $0.smartQuotesType = .no + $0.smartDashesType = .no + $0.smartInsertDeleteType = .no } /// Color configuration for the textField. From d573002be536075c275d961247c30f3d3505b5e3 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 11 Jul 2024 14:04:07 -0500 Subject: [PATCH 7/7] refactored textfield Signed-off-by: Matt Bruce --- .../TextFields/InputField/TextField.swift | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/VDS/Components/TextFields/InputField/TextField.swift b/VDS/Components/TextFields/InputField/TextField.swift index 05dc30f2..b56f3423 100644 --- a/VDS/Components/TextFields/InputField/TextField.swift +++ b/VDS/Components/TextFields/InputField/TextField.swift @@ -47,6 +47,11 @@ open class TextField: UITextField, ViewProtocol, Errorable { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- + /// Set to true to hide the blinking textField cursor. + open var hideBlinkingCaret = false + open var enableClipboardActions: Bool = true + open var onDidDeleteBackwards: (() -> Void)? + /// Key of whether or not updateView() is called in setNeedsUpdate() open var shouldUpdateView: Bool = true @@ -209,6 +214,23 @@ open class TextField: UITextField, ViewProtocol, Errorable { return success } + open override func caretRect(for position: UITextPosition) -> CGRect { + + if hideBlinkingCaret { + return .zero + } + + let caretRect = super.caretRect(for: position) + return CGRect(origin: caretRect.origin, size: CGSize(width: 1, height: caretRect.height)) + } + + open override func deleteBackward() { + super.deleteBackward() + onDidDeleteBackwards?() + } + + open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { enableClipboardActions } + //-------------------------------------------------- // MARK: - Private Methods //--------------------------------------------------