From 52449157d6db30c826a4d25d118b7304adc54649 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 28 Jun 2024 13:31:03 -0500 Subject: [PATCH] initial work Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 4 + VDS/BaseClasses/EntryFieldContainer.swift | 293 ++++++++++++++++++ VDS/Components/DatePicker/DatePicker.swift | 9 +- .../DropdownSelect/DropdownSelect.swift | 11 +- .../TextFields/EntryFieldBase.swift | 117 ++----- .../InputField/FieldTypes/FieldType.swift | 4 +- .../TextFields/InputField/InputField.swift | 33 +- .../TextFields/TextArea/TextArea.swift | 2 +- 8 files changed, 348 insertions(+), 125 deletions(-) create mode 100644 VDS/BaseClasses/EntryFieldContainer.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index a5403c49..290c1d2d 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -86,6 +86,7 @@ EA513A952A4E1F82002A4DFF /* TitleLockupStyleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA513A942A4E1F82002A4DFF /* TitleLockupStyleConfiguration.swift */; }; EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA596ABC2A16B4EC00300C4B /* Tab.swift */; }; EA596ABF2A16B4F500300C4B /* Tabs.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA596ABE2A16B4F500300C4B /* Tabs.swift */; }; + EA5DBD1F2C2EFDBD00290DF8 /* EntryFieldContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5DBD1E2C2EFDBD00290DF8 /* EntryFieldContainer.swift */; }; EA5E304C294CBDD00082B959 /* TileContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5E304B294CBDD00082B959 /* TileContainer.swift */; }; EA5E30532950DDA60082B959 /* TitleLockup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5E30522950DDA60082B959 /* TitleLockup.swift */; }; EA5E3058295105A40082B959 /* Tilelet.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5E3057295105A40082B959 /* Tilelet.swift */; }; @@ -298,6 +299,7 @@ EA513A942A4E1F82002A4DFF /* TitleLockupStyleConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleLockupStyleConfiguration.swift; sourceTree = ""; }; EA596ABC2A16B4EC00300C4B /* Tab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tab.swift; sourceTree = ""; }; EA596ABE2A16B4F500300C4B /* Tabs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tabs.swift; sourceTree = ""; }; + EA5DBD1E2C2EFDBD00290DF8 /* EntryFieldContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryFieldContainer.swift; sourceTree = ""; }; EA5E304B294CBDD00082B959 /* TileContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TileContainer.swift; sourceTree = ""; }; EA5E30522950DDA60082B959 /* TitleLockup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleLockup.swift; sourceTree = ""; }; EA5E3057295105A40082B959 /* Tilelet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tilelet.swift; sourceTree = ""; }; @@ -557,6 +559,7 @@ EA0B17FF2A9E21CA00F2D0CD /* Selector */, EA3361B5288B2A410071C351 /* Control.swift */, EAF7F09F289AB7EC00B287F5 /* View.swift */, + EA5DBD1E2C2EFDBD00290DF8 /* EntryFieldContainer.swift */, ); path = BaseClasses; sourceTree = ""; @@ -1251,6 +1254,7 @@ EA985BF7296C665E00F2FF2E /* IconName.swift in Sources */, EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */, EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */, + EA5DBD1F2C2EFDBD00290DF8 /* EntryFieldContainer.swift in Sources */, EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */, 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */, diff --git a/VDS/BaseClasses/EntryFieldContainer.swift b/VDS/BaseClasses/EntryFieldContainer.swift new file mode 100644 index 00000000..a830c1c4 --- /dev/null +++ b/VDS/BaseClasses/EntryFieldContainer.swift @@ -0,0 +1,293 @@ +// +// EntryFieldContainer.swift +// VDS +// +// Created by Matt Bruce on 6/28/24. +// + +import Foundation +import UIKit +import VDSCoreTokens +import Combine + +/// Base Class used to build out a Input controls. +@objc(VDSEntryFieldContainer) +open class EntryFieldContainer: Control, Changeable, FormFieldInternalValidatable { + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + internal var responder: UIResponder? + + internal var fieldStackView: UIStackView = { + return UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .horizontal + $0.distribution = .fill + $0.alignment = .top + } + }() + + internal var containerBackgroundColor: UIColor { + if showError || hasInternalError { + return backgroundColorConfiguration.getColor(self) + } else { + return transparentBackground ? .clear : backgroundColorConfiguration.getColor(self) + } + } + + //-------------------------------------------------- + // MARK: - Configuration Properties + //-------------------------------------------------- + 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) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forState: [.focused, .error]) + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) + $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) + $0.setSurfaceColors(VDSFormControlsColor.borderReadonlyOnlight, VDSFormControlsColor.borderReadonlyOndark, forState: .readonly) + } + + internal let iconColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) + $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) + } + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + open var onChangeSubscriber: AnyCancellable? + + open var fieldView: UIView? { + didSet { + if let fieldView { + fieldStackView.removeArrangedSubviews() + fieldView.translatesAutoresizingMaskIntoConstraints = false + + //add the view to add input fields + fieldStackView.addArrangedSubview(fieldView) + fieldStackView.addArrangedSubview(statusIcon) + fieldStackView.setCustomSpacing(VDSLayout.space3X, after: fieldView) + } + } + } + + open var statusIcon: Icon = Icon().with { + $0.size = .medium + $0.isAccessibilityElement = true + } + + /// Whether not to show the error. + open var showError: Bool = false { didSet { setNeedsUpdate() } } + + /// 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 { + var state = super.state + if isEnabled { + if !isReadOnly && (showError || hasInternalError){ + state.insert(.error) + } + if isReadOnly { + state.insert(.readonly) + } + if let responder, responder.isFirstResponder { + state.insert(.focused) + } + } + return state + } + } + open var titleText: String? { didSet { setNeedsUpdate() } } + + open var errorText: String? { 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")} + } + + open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } + + open var isRequired: Bool = false { didSet { setNeedsUpdate() } } + + open var isReadOnly: Bool = false { didSet { setNeedsUpdate() } } + + open var rules = [AnyRule]() + + //-------------------------------------------------- + // 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() + + //add ContainerStackView + //this is the horizontal stack that contains + //InputContainer, Icons, Buttons + addSubview(fieldStackView) + fieldStackView.pinToSuperView(.uniform(VDSLayout.space3X)) + + bridge_accessibilityLabelBlock = { [weak self] in + guard let self else { return "" } + var accessibilityLabels = [String]() + + if let text = titleText?.trimmingCharacters(in: .whitespaces) { + accessibilityLabels.append(text) + } + if isReadOnly { + accessibilityLabels.append("read only") + } + if !isEnabled { + accessibilityLabels.append("dimmed") + } + if let errorText, showError { + accessibilityLabels.append("error, \(errorText)") + } + + accessibilityLabels.append("\(Self.self)") + + return accessibilityLabels.joined(separator: ", ") + } + + bridge_accessibilityHintBlock = { [weak self] in + guard let self else { return "" } + return isReadOnly || !isEnabled ? "" : "Double tap to open" + } + + bridge_accessibilityValueBlock = { [weak self] in + guard let self else { return "" } + return value + } + + statusIcon.bridge_accessibilityLabelBlock = { [weak self] in + guard let self else { return "" } + return showError || hasInternalError ? "error" : nil + } + } + + /// Updates the UI + open override func updateView() { + super.updateView() + statusIcon.surface = surface + updateContainer() + updateError() + } + + open func updateContainer() { + //container of self + backgroundColor = containerBackgroundColor + layer.borderColor = borderColorConfiguration.getColor(self).cgColor + layer.borderWidth = VDSFormControls.borderWidth + layer.cornerRadius = VDSFormControls.borderRadius + } + + open func updateError() { + //dealing with error + if showError { + statusIcon.name = .error + statusIcon.isHidden = !isEnabled || state.contains(.focused) + } else if hasInternalError { + statusIcon.name = .error + statusIcon.isHidden = !isEnabled || state.contains(.focused) + } else { + statusIcon.isHidden = true + } + statusIcon.isAccessibilityElement = showError + statusIcon.color = iconColorConfiguration.getColor(self) + } + + /// Resets to default settings. + open override func reset() { + super.reset() + showError = false + errorText = nil + transparentBackground = false + width = nil + inputId = nil + defaultValue = nil + isRequired = false + isReadOnly = false + onChange = nil + } + + 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 + //-------------------------------------------------- + + open func validate(){ + updateRules() + validator = FormFieldValidator(field: self, rules: rules) + validator?.validate() + setNeedsUpdate() + } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + internal func updateRules() { + rules.removeAll() + if self.isRequired { + let rule = RequiredRule() + if let errorText, !errorText.isEmpty { + rule.errorMessage = errorText + } else { + rule.errorMessage = "You must enter a value" + } + rules.append(.init(rule)) + } + } +} diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index 17ededf4..c46bc8b0 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -130,11 +130,10 @@ open class DatePicker: EntryFieldBase { //-------------------------------------------------- // 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() - + // setting color config selectedDateLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() @@ -171,7 +170,7 @@ open class DatePicker: EntryFieldBase { controlStackView.addArrangedSubview(hiddenView) return controlStackView } - + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() @@ -182,7 +181,7 @@ open class DatePicker: EntryFieldBase { selectedDateLabel.surface = surface selectedDateLabel.isEnabled = isEnabled - calendarIcon.color = iconColorConfiguration.getColor(self) + calendarIcon.color = containerView.iconColorConfiguration.getColor(self) } /// Resets to default settings. @@ -271,7 +270,7 @@ extension DatePicker { // update containerview _ = responder?.becomeFirstResponder() - updateContainerView() + containerView.setNeedsUpdate() // animate the calendar to show UIView.animate(withDuration: 0.5, diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 832349d2..439ccf02 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -258,12 +258,11 @@ open class DropdownSelect: EntryFieldBase { open override func updateErrorLabel() { super.updateErrorLabel() - if !showError && !hasInternalError || !optionsPicker.isHidden { - statusIcon.name = .downCaret + if !optionsPicker.isHidden { + containerView.statusIcon.name = .downCaret } - statusIcon.surface = surface - statusIcon.isHidden = isReadOnly ? true : false - statusIcon.color = iconColorConfiguration.getColor(self) + containerView.statusIcon.isHidden = isReadOnly ? true : false + containerView.statusIcon.color = containerView.iconColorConfiguration.getColor(self) } @objc open func pickerDoneClicked() { @@ -287,7 +286,7 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { dropdownField.resignFirstResponder() } optionsPicker.isHidden = !optionsPicker.isHidden - updateContainerView() + containerView.setNeedsUpdate() updateErrorLabel() } diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 5bd50351..0e380ae9 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -11,7 +11,7 @@ import VDSCoreTokens import Combine /// Base Class used to build out a Input controls. -@objc(VDSEntryField) +@objc(VDSEntryFieldBase) open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //-------------------------------------------------- @@ -73,15 +73,6 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { $0.distribution = .fillEqually } - internal var fieldStackView: UIStackView = { - return UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.axis = .horizontal - $0.distribution = .fill - $0.alignment = .top - } - }() - /// This is a vertical stack used for the errorLabel and helperLabel. internal var bottomContainerStackView: UIStackView = { return UIStackView().with { @@ -94,21 +85,13 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { /// 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 { + internal var containerView = EntryFieldContainer().with { $0.isAccessibilityElement = true } /// This is set by a local method. internal var bottomContainerView: UIView! - - internal var containerBackgroundColor: UIColor { - if showError || hasInternalError { - return backgroundColorConfiguration.getColor(self) - } else { - return transparentBackground ? .clear : backgroundColorConfiguration.getColor(self) - } - } - + //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- @@ -133,32 +116,6 @@ 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) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forState: [.focused, .error]) - $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) - $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) - $0.setSurfaceColors(VDSFormControlsColor.borderReadonlyOnlight, VDSFormControlsColor.borderReadonlyOndark, forState: .readonly) - } - - internal let iconColorConfiguration = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) - $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) - } //-------------------------------------------------- // MARK: - Public Properties @@ -181,11 +138,6 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { $0.textStyle = .bodySmall } - open var statusIcon: Icon = Icon().with { - $0.size = .medium - $0.isAccessibilityElement = true - } - open var labelText: String? { didSet { setNeedsUpdate() } } open var helperText: String? { didSet { setNeedsUpdate() } } @@ -254,6 +206,9 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open override func setup() { super.setup() + containerView.fieldView = getFieldContainer() + containerView.responder = responder + let layoutGuide = UILayoutGuide() addLayoutGuide(layoutGuide) layoutGuide @@ -274,21 +229,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { mainStackView.pinLeading(anchor: layoutGuide.leadingAnchor) mainStackView.pinBottom(anchor: layoutGuide.bottomAnchor) mainStackView.pinTrailing(anchor: layoutGuide.trailingAnchor) - - //add ContainerStackView - //this is the horizontal stack that contains - //InputContainer, Icons, Buttons - containerView.addSubview(fieldStackView) - fieldStackView.pinToSuperView(.uniform(VDSLayout.space3X)) - - let fieldContainerView = getFieldContainer() - fieldContainerView.translatesAutoresizingMaskIntoConstraints = false - - //add the view to add input fields - fieldStackView.addArrangedSubview(fieldContainerView) - fieldStackView.addArrangedSubview(statusIcon) - fieldStackView.setCustomSpacing(VDSLayout.space3X, after: fieldContainerView) - + //get the container this is what show helper text, error text //can include other for character count, max length bottomContainerView = getBottomContainer() @@ -313,6 +254,16 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { errorLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() helperLabel.textColorConfiguration = secondaryColorConfiguration.eraseToAnyColorable() + containerView.onChange = { [weak self] sender in + guard let self, let onChange else { return } + onChange(self) + } + + containerView.onClick = { [weak self] sender in + guard let self, let onClick else { return } + onClick(self) + } + containerView.bridge_accessibilityLabelBlock = { [weak self] in guard let self else { return "" } var accessibilityLabels = [String]() @@ -344,18 +295,12 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { guard let self else { return "" } return value } - - statusIcon.bridge_accessibilityLabelBlock = { [weak self] in - guard let self else { return "" } - return showError || hasInternalError ? "error" : nil - } } /// Updates the UI open override func updateView() { super.updateView() - updateContainerView() - updateContainerWidth() + updateContainer() updateTitleLabel() updateErrorLabel() updateHelperLabel() @@ -458,22 +403,17 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { 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 - statusIcon.name = .error - statusIcon.surface = surface - statusIcon.isHidden = !isEnabled || state.contains(.focused) } else { - statusIcon.isHidden = true errorLabel.isHidden = true } - statusIcon.color = iconColorConfiguration.getColor(self) + + containerView.errorText = errorLabel.text + containerView.showError = !errorLabel.isHidden } open func updateHelperLabel(){ @@ -494,7 +434,6 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { elements.append(contentsOf: [titleLabel, containerView]) if showError { - elements.append(statusIcon) if let errorText, !errorText.isEmpty { elements.append(errorLabel) } @@ -528,14 +467,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { } } - internal func updateContainerView() { - containerView.backgroundColor = containerBackgroundColor - containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor - containerView.layer.borderWidth = VDSFormControls.borderWidth - containerView.layer.cornerRadius = VDSFormControls.borderRadius - } - - internal func updateContainerWidth() { + internal func updateContainer() { widthConstraint?.deactivate() trailingLessThanEqualsConstraint?.deactivate() trailingEqualsConstraint?.deactivate() @@ -547,6 +479,11 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { } else { trailingEqualsConstraint?.activate() } + containerView.surface = surface + containerView.defaultValue = defaultValue + containerView.isEnabled = isEnabled + containerView.isRequired = isRequired + containerView.isReadOnly = isReadOnly } internal func updateHelperTextPosition() { diff --git a/VDS/Components/TextFields/InputField/FieldTypes/FieldType.swift b/VDS/Components/TextFields/InputField/FieldTypes/FieldType.swift index b3c2a92b..5f3461ef 100644 --- a/VDS/Components/TextFields/InputField/FieldTypes/FieldType.swift +++ b/VDS/Components/TextFields/InputField/FieldTypes/FieldType.swift @@ -68,10 +68,10 @@ extension InputField { actionModel.onClick(inputField) } inputField.actionTextLink.isHidden = false - inputField.fieldStackView.setCustomSpacing(VDSLayout.space2X, after: inputField.statusIcon) + inputField.containerView.fieldStackView.setCustomSpacing(VDSLayout.space2X, after: inputField.containerView.statusIcon) } else { inputField.actionTextLink.isHidden = true - inputField.fieldStackView.setCustomSpacing(0, after: inputField.statusIcon) + inputField.containerView.fieldStackView.setCustomSpacing(0, after: inputField.containerView.statusIcon) } //placeholder diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index f7f54896..8a09e96b 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -35,15 +35,7 @@ open class InputField: EntryFieldBase { // MARK: - Private Properties //-------------------------------------------------- internal override var responder: UIResponder? { textField } - - internal override var containerBackgroundColor: UIColor { - if showSuccess { - return backgroundColorConfiguration.getColor(self) - } else { - return super.containerBackgroundColor - } - } - + internal override var minWidth: CGFloat { fieldType.handler().minWidth } internal override var maxWidth: CGFloat { let frameWidth = frame.size.width @@ -186,14 +178,14 @@ open class InputField: EntryFieldBase { textField.delegate = self bottomContainerStackView.insertArrangedSubview(successLabel, at: 0) - fieldStackView.addArrangedSubview(actionTextLink) + containerView.fieldStackView.addArrangedSubview(actionTextLink) successLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() - backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: .success) - backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: [.success, .focused]) + containerView.backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: .success) + containerView.backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: [.success, .focused]) - borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success) + containerView.borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success) textField.textColorConfiguration = textFieldTextColorConfiguration @@ -234,7 +226,7 @@ open class InputField: EntryFieldBase { return accessibilityLabels.joined(separator: ", ") } - statusIcon.bridge_accessibilityLabelBlock = { [weak self] in + containerView.statusIcon.bridge_accessibilityLabelBlock = { [weak self] in guard let self else { return "" } if showError { return "error" @@ -290,10 +282,9 @@ open class InputField: EntryFieldBase { successLabel.isEnabled = isEnabled successLabel.isHidden = false errorLabel.isHidden = true - statusIcon.name = .checkmarkAlt - statusIcon.color = iconColorConfiguration.getColor(self) - statusIcon.surface = surface - statusIcon.isHidden = !isEnabled || state.contains(.focused) + containerView.statusIcon.name = .checkmarkAlt + containerView.statusIcon.color = containerView.iconColorConfiguration.getColor(self) + containerView.statusIcon.isHidden = !isEnabled || state.contains(.focused) } else { successLabel.isHidden = true } @@ -312,8 +303,8 @@ open class InputField: EntryFieldBase { elements.append(leftView) } - if !statusIcon.isHidden{ - elements.append(statusIcon) + if !containerView.statusIcon.isHidden{ + elements.append(containerView.statusIcon) } if !actionTextLink.isHidden { @@ -340,7 +331,7 @@ open class InputField: EntryFieldBase { extension InputField: UITextFieldDelegate { public func textFieldDidBeginEditing(_ textField: UITextField) { fieldType.handler().textFieldDidBeginEditing(self, textField: textField) - updateContainerView() + updateContainer() updateErrorLabel() } diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index d0396d7a..1cb42aba 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -180,7 +180,7 @@ open class TextArea: EntryFieldBase { characterCounterLabel.text = getCharacterCounterText() textView.isEditable = !isEnabled || isReadOnly ? false : true - textView.tintColor = iconColorConfiguration.getColor(self) + textView.tintColor = containerView.iconColorConfiguration.getColor(self) characterCounterLabel.surface = surface highlightCharacterOverflow() }