diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index b583c220..42826897 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -13,9 +13,12 @@ 18450CF12BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */; }; 1855EC662BAABF2A002ACAC2 /* BreadcrumbItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */; }; 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */; }; + 186D13CB2BBA8B1500986B53 /* DropdownSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */; }; + 186D13CF2BBC36EF00986B53 /* DropdownSelectChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */; }; 18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; }; 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A012B96E848006602CC /* Breadcrumbs.swift */; }; 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A032B96F050006602CC /* BreadcrumbItem.swift */; }; + 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; 44604AD429CE186A00E62B51 /* NotificationButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */; }; @@ -202,9 +205,12 @@ 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = BreadcrumbsChangeLog.txt; sourceTree = ""; }; 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItemModel.swift; sourceTree = ""; }; 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = ""; }; + 186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownSelect.swift; sourceTree = ""; }; + 186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = DropdownSelectChangeLog.txt; sourceTree = ""; }; 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = ""; }; 18A65A012B96E848006602CC /* Breadcrumbs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Breadcrumbs.swift; sourceTree = ""; }; 18A65A032B96F050006602CC /* BreadcrumbItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItem.swift; sourceTree = ""; }; + 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = ""; }; 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = ""; }; 445BA07729C07B3D0036A7C5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationButtonModel.swift; sourceTree = ""; }; @@ -407,6 +413,16 @@ path = CarouselScrollbar; sourceTree = ""; }; + 186D13C92BBA8A3500986B53 /* DropdownSelect */ = { + isa = PBXGroup; + children = ( + 186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */, + 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */, + 186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */, + ); + path = DropdownSelect; + sourceTree = ""; + }; 18A65A002B96E7E1006602CC /* Breadcrumbs */ = { isa = PBXGroup; children = ( @@ -569,6 +585,7 @@ EA0FC2BE2912D18200DF80B4 /* Buttons */, 1808BEBA2BA41B1D00129230 /* CarouselScrollbar */, EAF7F092289985E200B287F5 /* Checkbox */, + 186D13C92BBA8A3500986B53 /* DropdownSelect */, EA985BF3296C609E00F2FF2E /* Icon */, EA3362412892EF700071C351 /* Label */, 44604AD529CE195300E62B51 /* Line */, @@ -1058,6 +1075,7 @@ EAEEECA02B1F908200531FC2 /* BadgeIndicatorChangeLog.txt in Resources */, EAA5EEB928ECD24B003B3210 /* Icons.xcassets in Resources */, EAEEECA92B1F969700531FC2 /* TooltipChangeLog.txt in Resources */, + 186D13CF2BBC36EF00986B53 /* DropdownSelectChangeLog.txt in Resources */, EAEEEC9C2B1F8F0700531FC2 /* TextLinkCaretChangeLog.txt in Resources */, EAA5EEE428F5B855003B3210 /* VerizonNHGDS-Light.otf in Resources */, 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */, @@ -1117,6 +1135,7 @@ EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */, EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */, + 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */, EAACB89A2B927108006A3869 /* Valuing.swift in Sources */, EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, @@ -1180,6 +1199,7 @@ 1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */, EAF978212A99035B00C2FEA9 /* Enabling.swift in Sources */, EA5E3058295105A40082B959 /* Tilelet.swift in Sources */, + 186D13CB2BBA8B1500986B53 /* DropdownSelect.swift in Sources */, EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */, EA0B180A2AA78F9000F2D0CD /* UIEdgeInsets.swift in Sources */, EA985C1D296CD13600F2FF2E /* BundleManager.swift in Sources */, diff --git a/VDS/Components/DropdownSelect/DropdownOptionModel.swift b/VDS/Components/DropdownSelect/DropdownOptionModel.swift new file mode 100644 index 00000000..c82519eb --- /dev/null +++ b/VDS/Components/DropdownSelect/DropdownOptionModel.swift @@ -0,0 +1,22 @@ +// +// DropdownOptionModel.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 03/04/24. +// + +import Foundation + +extension DropdownSelect { + public struct DropdownOptionModel { + + /// Text that goes as option to DropdownSelect + public var text: String + public var value: String + + public init(text: String, value: String? = nil) { + self.text = text + self.value = value ?? text + } + } +} diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift new file mode 100644 index 00000000..a56eb783 --- /dev/null +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -0,0 +1,290 @@ +// +// DropdownSelect.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 28/03/24. +// + +import Foundation +import UIKit +import VDSColorTokens +import VDSFormControlsTokens +import Combine + +/// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection. +@objc(VDSDropdownSelect) +open class DropdownSelect: EntryFieldBase { + //-------------------------------------------------- + // 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: - Public Properties + //-------------------------------------------------- + /// If true, the label will be displayed inside the dropdown containerView. Otherwise, the label will be above the dropdown containerView like a normal text input. + open var showInlineLabel: Bool = false { didSet { setNeedsUpdate() }} + + /// Allows unique ID to be passed to the element. + open var selectId: Int? { didSet { setNeedsUpdate() }} + + /// Current SelectedItem + open var selectedItem: DropdownOptionModel? { + guard let selectId else { return nil } + return options[selectId] + } + + /// Array of options to show + open var options: [DropdownOptionModel] = [] { didSet { setNeedsUpdate() }} + + /// A callback when the selected option changes. Passes parameters (option). + open var onItemSelected: ((Int, DropdownOptionModel) -> Void)? + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + internal var minWidthDefault = 66.0 + internal var minWidthInlineLabel = 102.0 + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + open var inlineDisplayLabel = Label().with { + $0.textAlignment = .left + $0.textStyle = .boldBodyLarge + $0.lineBreakMode = .byCharWrapping + $0.sizeToFit() + } + + open var selectedOptionLabel = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.textAlignment = .left + $0.textStyle = .bodyLarge + $0.lineBreakMode = .byCharWrapping + } + + open var dropdownField = UITextField().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.tintColor = UIColor.clear + $0.font = TextStyle.bodyLarge.font + } + + open var optionsPicker = UIPickerView() + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + internal var inlineWidthConstraint: NSLayoutConstraint? + + //-------------------------------------------------- + // MARK: - Configuration Properties + //-------------------------------------------------- + internal override var containerSize: CGSize { CGSize(width: showInlineLabel ? minWidthInlineLabel : width ?? minWidthDefault, height: 44) } + + 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) + } + + //-------------------------------------------------- + // 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() + + accessibilityLabel = "Dropdown Select" + + // stackview for controls in EntryFieldBase.controlContainerView + let controlStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .horizontal + $0.spacing = VDSFormControls.spaceInset + } + controlContainerView.addSubview(controlStackView) + controlStackView.pinToSuperView() + + controlStackView.addArrangedSubview(dropdownField) + controlStackView.addArrangedSubview(inlineDisplayLabel) + controlStackView.addArrangedSubview(selectedOptionLabel) + + controlStackView.setCustomSpacing(0, after: dropdownField) + controlStackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: inlineDisplayLabel) + controlStackView.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: selectedOptionLabel) + dropdownField.width(0) + inlineWidthConstraint = inlineDisplayLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 0) + inlineWidthConstraint?.isActive = true + + // setting color config + inlineDisplayLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() + selectedOptionLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() + + // Options PickerView + optionsPicker.delegate = self + optionsPicker.dataSource = self + optionsPicker.isHidden = true + dropdownField.inputView = optionsPicker + dropdownField.inputAccessoryView = { + let inputToolbar = UIToolbar().with { + $0.barStyle = .default + $0.isTranslucent = true + $0.items=[ + UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: self, action: nil), + UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.done, target: self, action: #selector(pickerDoneClicked)) + ] + } + inputToolbar.sizeToFit() + return inputToolbar + }() + + // tap gesture + containerStackView + .publisher(for: UITapGestureRecognizer()) + .sink { [weak self] _ in + self?.launchPicker() + } + .store(in: &subscribers) + } + + /// Used to make changes to the View based off a change events or from local properties. + open override func updateView() { + super.updateView() + + updateInlineLabel() + + dropdownField.isUserInteractionEnabled = readOnly ? false : true + selectedOptionLabel.surface = surface + selectedOptionLabel.isEnabled = isEnabled + } + + /// Resets to default settings. + open override func reset() { + super.reset() + + inlineDisplayLabel.textStyle = .boldBodyLarge + selectedOptionLabel.textStyle = .bodyLarge + showInlineLabel = false + options = [] + selectId = nil + } + + //-------------------------------------------------- + // MARK: - Public Methods + //-------------------------------------------------- + + open override func updateTitleLabel() { + + //update the local vars for the label since we no long have a model + var attributes: [any LabelAttributeModel] = [] + var updatedLabelText = labelText + + updatedLabelText = showInlineLabel ? "" : updatedLabelText + + if let oldText = updatedLabelText, !required, !oldText.hasSuffix("Optional") { + let optionColorAttr = ColorLabelAttribute(location: oldText.count + 2, + length: 8, + color: secondaryColorConfiguration.getColor(self)) + + updatedLabelText = showInlineLabel ? "Optional" : "\(oldText) Optional" + attributes.append(optionColorAttr) + } + + if let tooltipModel { + attributes.append(TooltipLabelAttribute(surface: surface, model: tooltipModel, presenter: self)) + } + + titleLabel.text = updatedLabelText + titleLabel.attributes = attributes + titleLabel.surface = surface + titleLabel.isEnabled = isEnabled + } + + open func updateInlineLabel() { + + inlineWidthConstraint?.isActive = false + + /// inline label text and selected option text separated by ':' + if let labelText, !labelText.isEmpty { + inlineDisplayLabel.text = showInlineLabel ? (labelText + ":") : "" + } else { + inlineDisplayLabel.text = showInlineLabel ? labelText : "" + } + inlineDisplayLabel.surface = surface + inlineDisplayLabel.isEnabled = isEnabled + + /// Update width as per updated text size + inlineWidthConstraint = inlineDisplayLabel.widthAnchor.constraint(equalToConstant: inlineDisplayLabel.intrinsicContentSize.width) + inlineWidthConstraint?.isActive = true + + if let selectId, selectId < options.count { + updateSelectedOptionLabel(option: options[selectId]) + } + } + + open func updateSelectedOptionLabel(option: DropdownOptionModel? = nil) { + selectedOptionLabel.text = option?.text ?? "" + value = option?.value + } + + open override func updateErrorLabel() { + super.updateErrorLabel() + if !showError && !hasInternalError { + icon.name = .downCaret + } + icon.surface = surface + icon.isHidden = readOnly ? true : false + icon.color = iconColorConfiguration.getColor(self) + } + + @objc open func pickerDoneClicked() { + optionsPicker.isHidden = true + dropdownField.resignFirstResponder() + } +} + +//-------------------------------------------------- +// MARK: - UIPickerView Delegate & Datasource +//-------------------------------------------------- +extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { + + internal func launchPicker() { + if optionsPicker.isHidden { + dropdownField.becomeFirstResponder() + } else { + dropdownField.resignFirstResponder() + } + optionsPicker.isHidden = !optionsPicker.isHidden + } + + public func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 1 + } + + public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + return options.count + } + + public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + guard options.count > row else { return nil } + return options[row].text + } + + public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + guard options.count > row else { return } + selectId = row + updateSelectedOptionLabel(option: options[row]) + self.onItemSelected?(row, options[row]) + } +} diff --git a/VDS/Components/DropdownSelect/DropdownSelectChangeLog.txt b/VDS/Components/DropdownSelect/DropdownSelectChangeLog.txt new file mode 100644 index 00000000..537b6532 --- /dev/null +++ b/VDS/Components/DropdownSelect/DropdownSelectChangeLog.txt @@ -0,0 +1,45 @@ +MM/DD/YYYY +---------------- +Initial Brand 3.0 handoff + +12/27/2021 +---------------- +- Removed Max Width, Increased Min width. +- Updated the tokens with FormControl tokens + +02/24/2022 +---------------- +- Replaced Error and Caret Down Non-Scaling icons with VDS Icon. + +05/02/2022 +---------------- +- Anatomy: Noted that Label and Inline Label are optional within the code + +07/27/2022 +---------------- +- Updated Dropdown Select Configurations to include Background Transparent Boolean. + +08/10/2022 +---------------- +- Updated surface prop from inverted to light and dark + +11/30/2022 +---------------- +- Added "(web only)" to any instance of "keyboard focus" + +12/13/2022 +---------------- +- Replaced form border and focus border pixel values and styles & spacing with tokens. + +01/19/2023 +---------------- +- Added Tooltip and Optional Indicator to Anatomy Updated Specs to use new SPEC Templates and SPEC DOC Components. +- Added “Option List” and artwork to Anatomy. + +04/12/2023 +---------------- +- Updated hex colors for updated feedback tokens in error states. + +12/27/23 +---------------- +- Removed Web/App Parity Discrepancies section diff --git a/VDS/Components/Icon/IconName.swift b/VDS/Components/Icon/IconName.swift index 1234df63..cfdf1cf2 100644 --- a/VDS/Components/Icon/IconName.swift +++ b/VDS/Components/Icon/IconName.swift @@ -51,6 +51,8 @@ extension Icon { public static let checkmark = Name(name: "checkmark") public static let checkmarkAlt = Name(name: "checkmark-alt") public static let close = Name(name: "close") + public static let downCaret = Name(name: "down-caret") + public static let downCaretBold = Name(name: "down-caret-bold") public static let error = Name(name: "error") public static let info = Name(name: "info") public static let multipleDocuments = Name(name: "multiple-documents") diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 5d8957fe..d2f3a6c7 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -110,6 +110,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOndark, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.disabled,.error]) } internal var readOnlyBorderColorConfiguration = ControlColorConfiguration().with { @@ -185,33 +186,24 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { setNeedsUpdate() } } - - /// Override this to conveniently get/set the textfield(s). - open var text: String? { - get { nil } - set { fatalError("You MUST override EntryField's 'text' variable in your subclass.") } - } - + open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } } open var transparentBackground: Bool = false { didSet { setNeedsUpdate() } } open var width: CGFloat? { didSet { setNeedsUpdate() } } - - open var maxLength: Int? { didSet { setNeedsUpdate() } } - + open var inputId: String? { didSet { setNeedsUpdate() } } /// The text of this textField. - private var _value: String? + internal var _value: String? open var value: String? { get { _value } set { if let newValue, newValue != _value { _value = newValue - text = newValue + sendActions(for: .valueChanged) } - setNeedsUpdate() } } @@ -308,7 +300,6 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { tooltipModel = nil transparentBackground = false width = nil - maxLength = nil inputId = nil value = nil defaultValue = nil @@ -335,7 +326,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //-------------------------------------------------- private func updateContainerView() { containerView.backgroundColor = backgroundColorConfiguration.getColor(self) - containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor + containerView.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : borderColorConfiguration.getColor(self).cgColor containerView.layer.borderWidth = VDSFormControls.widthBorder containerView.layer.cornerRadius = VDSFormControls.borderradius } diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index ebd76525..b45500a0 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -78,17 +78,28 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { /// Representing the type of input. open var fieldType: FieldType = .text { didSet { setNeedsUpdate() } } - /// The text of this textField. - open override var text: String? { - get { textField.text } + /// The text of this TextField. + private var _text: String? + open var text: String? { + get { _text } set { - if let newValue, newValue != text { + if let newValue, newValue != _text { + _text = newValue textField.text = newValue value = newValue } setNeedsUpdate() } } + + /// The value of this textField. + open override var value: String? { + didSet { + if text != value { + text = value + } + } + } var _showError: Bool = false /// Whether not to show the error. diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index b5fada0c..73a1e67c 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -105,11 +105,11 @@ open class TextArea: EntryFieldBase { } } } - - /// The text of this textView + + /// The text of this TextArea. private var _text: String? - open override var text: String? { - get { textView.text } + open var text: String? { + get { _text } set { if let newValue, newValue != _text { _text = newValue @@ -119,6 +119,15 @@ open class TextArea: EntryFieldBase { setNeedsUpdate() } } + + /// The value of this textField. + open override var value: String? { + didSet { + if text != value { + text = value + } + } + } /// UITextView shown in the TextArea. open var textView = TextView().with { @@ -127,7 +136,15 @@ open class TextArea: EntryFieldBase { $0.isScrollEnabled = false } - open override var maxLength: Int? { willSet { countRule.maxLength = newValue }} + open var maxLength: Int? { + willSet { + countRule.maxLength = newValue + } + + didSet { + setNeedsUpdate() + } + } /// Color configuration for error icon. internal var iconColorConfiguration = ControlColorConfiguration().with { diff --git a/VDS/VDS.docc/VDS.md b/VDS/VDS.docc/VDS.md index 63956f83..607e09f5 100755 --- a/VDS/VDS.docc/VDS.md +++ b/VDS/VDS.docc/VDS.md @@ -29,6 +29,7 @@ Using the system allows designers and developers to collaborate more easily and - ``Checkbox`` - ``CheckboxItem`` - ``CheckboxGroup`` +- ``DropdownSelect`` - ``Icon`` - ``InputField`` - ``Label``