From 7be09ebb8a14db3c5ff24fd45b50cef0572e6cc2 Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 2 Apr 2024 10:40:18 +0530 Subject: [PATCH 01/25] Digital ACT-191 ONEAPP-7135 story: added new page --- VDS.xcodeproj/project.pbxproj | 38 ++++++++++++------- .../DropdownSelect/DropdownSelect.swift | 32 ++++++++++++++++ 2 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 VDS/Components/DropdownSelect/DropdownSelect.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 580022a0..7bf3c054 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -7,12 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + 1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; }; + 1808BEC02BA456B700129230 /* CarouselScrollbarChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */; }; 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; 18450CF12BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */; }; 1855EC662BAABF2A002ACAC2 /* BreadcrumbItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */; }; - 1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; }; - 1808BEC02BA456B700129230 /* CarouselScrollbarChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */; }; 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */; }; + 186D13CB2BBA8B1500986B53 /* DropdownSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */; }; 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 */; }; @@ -196,12 +197,13 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = ""; }; + 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = ""; }; 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = BreadcrumbsChangeLog.txt; sourceTree = ""; }; 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItemModel.swift; sourceTree = ""; }; - 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = ""; }; - 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = ""; }; 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = ""; }; + 186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownSelect.swift; 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 = ""; }; @@ -398,6 +400,23 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 1808BEBA2BA41B1D00129230 /* CarouselScrollbar */ = { + isa = PBXGroup; + children = ( + 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */, + 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */, + ); + path = CarouselScrollbar; + sourceTree = ""; + }; + 186D13C92BBA8A3500986B53 /* DropdownSelect */ = { + isa = PBXGroup; + children = ( + 186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */, + ); + path = DropdownSelect; + sourceTree = ""; + }; 18A65A002B96E7E1006602CC /* Breadcrumbs */ = { isa = PBXGroup; children = ( @@ -410,15 +429,6 @@ path = Breadcrumbs; sourceTree = ""; }; - 1808BEBA2BA41B1D00129230 /* CarouselScrollbar */ = { - isa = PBXGroup; - children = ( - 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */, - 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */, - ); - path = CarouselScrollbar; - sourceTree = ""; - }; 445BA07629C07ABA0036A7C5 /* Notification */ = { isa = PBXGroup; children = ( @@ -569,6 +579,7 @@ EA0FC2BE2912D18200DF80B4 /* Buttons */, 1808BEBA2BA41B1D00129230 /* CarouselScrollbar */, EAF7F092289985E200B287F5 /* Checkbox */, + 186D13C92BBA8A3500986B53 /* DropdownSelect */, EA985BF3296C609E00F2FF2E /* Icon */, EA3362412892EF700071C351 /* Label */, 44604AD529CE195300E62B51 /* Line */, @@ -1180,6 +1191,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/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift new file mode 100644 index 00000000..35c48a4f --- /dev/null +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -0,0 +1,32 @@ +// +// DropdownSelect.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 28/03/24. +// + +import Foundation +import UIKit +import VDSColorTokens +import VDSFormControlsTokens +import Combine + +@objc(VDSDropdownSelect) +open class DropdownSelect: Control { + + //-------------------------------------------------- + // 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) + } + +} From 847f74f6597409b19c7a44f8458581c20e0d3da5 Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 2 Apr 2024 10:44:20 +0530 Subject: [PATCH 02/25] Digital ACT-191 ONEAPP-7135 story: added DropdownSelect to VDS.md --- VDS/VDS.docc/VDS.md | 1 + 1 file changed, 1 insertion(+) 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`` From 530a6786d8fd842da8363373687f4abcec53af3e Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 2 Apr 2024 14:37:02 +0530 Subject: [PATCH 03/25] Digital ACT-191 ONEAPP-7135 story: design changes --- .../DropdownSelect/DropdownSelect.swift | 439 ++++++++++++++++++ 1 file changed, 439 insertions(+) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 35c48a4f..d7613f94 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -11,6 +11,7 @@ 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: Control { @@ -29,4 +30,442 @@ open class DropdownSelect: Control { super.init(coder: coder) } + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + + ///Boolean value that determines if component should show the error state/error message. + open var error: Bool = false { didSet { setNeedsUpdate() }} + + /// Message displayed when there is an error. + open var errorText: String? { didSet { setNeedsUpdate() }} + + /// If provided, will be used as text for the helper label. + open var helperText: String? { didSet { setNeedsUpdate() }} + + /// used if the component is enabled or not. + open override var isEnabled: Bool { didSet { setNeedsUpdate() }} + + /// If true, the label will be displayed inside the dropdown container. Otherwise, the label will be above the dropdown container like a normal text input. + open var inlineLabel: Bool = false { didSet { setNeedsUpdate() }} + + /// If provided, will be used as context for the label on the input field. + open var label: String? { didSet { setNeedsUpdate() }} + + /// Not allowed the user interaction to select/change input if it is true. + open var readOnly: Bool = false { didSet { setNeedsUpdate() }} + + /// Used to show optional indicator for the label. + open var required: Bool = false { didSet { setNeedsUpdate() }} + + /// Allows unique ID to be passed to the element. + open var selectId: String? { didSet { setNeedsUpdate() }} + + // TO DO: either have model or individual title and content. + /// Config object for tooltip option, is optional. + open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } } + + /// Used to set tooltip title. + open var tooltipTitle: String { + get { return _tooltipTitle } + set { + _tooltipTitle = newValue + updateTooltip() + setNeedsUpdate() + } + } + + /// Used to set tooltip content. + open var tooltipContent: String { + get { return _tooltipContent } + set { + _tooltipContent = newValue + updateTooltip() + setNeedsUpdate() + } + } + + /// If provided, will render with trnasparent background. + open var transparentBackground: Bool = false { didSet { setNeedsUpdate() }} + + /// Used to set width for the Dropdown Select. + open var width: Int? { didSet { setNeedsUpdate() }} + + // TO DO: create model for options + open var options: [String]? { didSet { setNeedsUpdate() }} + + ///Boolean or a Function that returns a boolean value that determines if component should show the error state/error message.Functon receives the 'event' object on input change. + open var showError: Bool = false { didSet { setNeedsUpdate() }} + + open override var state: UIControl.State { + get { + var state = super.state + if showError { + state.insert(.error) + } + return state + } + } + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + internal var _tooltipTitle: String = "" + + internal var _tooltipContent: String = "" + + internal var minWidthDefault = 66.0 + internal var minWidthInlineLabel = 102.0 + + var stackView: UIStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .vertical + } + + private var eyebrowLabel = TrailingTooltipLabel().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.labelTextAlignment = .left + $0.labelTextStyle = .bodySmall + } + + var container: UIView = UIView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + } + + var containerStack: UIStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .horizontal + $0.spacing = VDSFormControls.spaceInset + } + + private var dropdownField = UITextField().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.tintColor = UIColor.clear + $0.font = TextStyle.bodyLarge.font + } + + private var inlineDisplayLabel = Label().with { + $0.textAlignment = .left + $0.textStyle = .boldBodyLarge + $0.sizeToFit() + } + + private var selectedOptionLabel = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.textAlignment = .left + $0.textStyle = .bodyLarge + } + + private var icon: Icon = Icon().with { + $0.size = .medium + $0.name = Icon.Name(name: "down-caret") + } + + private var errorLabel = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.textAlignment = .left + $0.textStyle = .bodySmall + } + + private var helperLabel = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.textAlignment = .left + $0.textStyle = .bodySmall + } + + private var optionsPicker = UIPickerView() + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + internal var widthConstraint: NSLayoutConstraint? + internal var inlineWidthConstraint: NSLayoutConstraint? + + //-------------------------------------------------- + // MARK: - Configuration Properties + //-------------------------------------------------- + internal var containerSize: CGSize { CGSize(width: 45, height: 44) } + + /// Color configuration for error icon. + internal let primaryColorConfig = ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) + } + + internal let secondaryColorConfig = ViewColorConfiguration().with { + $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) + } + + internal var containerBorderColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOnlight, forState: .normal) + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) + $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + /// 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() + + isAccessibilityElement = true + accessibilityLabel = "Dropdown Select" + + addSubview(stackView) + + container.addSubview(containerStack) + stackView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height).isActive = true + + dropdownField.width(0) + + let spacing = VDSFormControls.spaceInset + containerStack.pinToSuperView(.init(top: spacing, left: spacing, bottom: spacing, right: spacing)) + containerStack.addArrangedSubview(dropdownField) + containerStack.addArrangedSubview(inlineDisplayLabel) + containerStack.addArrangedSubview(selectedOptionLabel) + containerStack.addArrangedSubview(icon) + + containerStack.setCustomSpacing(0, after: dropdownField) + containerStack.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: inlineDisplayLabel) + containerStack.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: selectedOptionLabel) + + stackView.addArrangedSubview(eyebrowLabel) + stackView.addArrangedSubview(container) + stackView.addArrangedSubview(errorLabel) + stackView.addArrangedSubview(helperLabel) + + stackView.setCustomSpacing(4, after: eyebrowLabel) + stackView.setCustomSpacing(8, after: container) + stackView.setCustomSpacing(8, after: errorLabel) + + stackView.pinToSuperView() + inlineWidthConstraint = inlineDisplayLabel.widthAnchor.constraint(equalToConstant: 0) + inlineWidthConstraint?.isActive = true + + widthConstraint = stackView.widthAnchor.constraint(greaterThanOrEqualToConstant: minWidthDefault) + widthConstraint?.isActive = true + + eyebrowLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() + errorLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() + helperLabel.textColorConfiguration = secondaryColorConfig.eraseToAnyColorable() + inlineDisplayLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() + + optionsPicker.delegate = self + optionsPicker.dataSource = self + + optionsPicker.isHidden = true + + dropdownField.inputView = optionsPicker + dropdownField.inputAccessoryView = toolBarForPicker() + + containerStack.publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in + self?.launchPicker() + }.store(in: &subscribers) + } + + open override func reset() { + super.reset() + + eyebrowLabel.reset() + inlineDisplayLabel.reset() + selectedOptionLabel.reset() + errorLabel.reset() + helperLabel.reset() + + eyebrowLabel.labelTextAlignment = .left + eyebrowLabel.labelTextStyle = .bodySmall + inlineDisplayLabel.textStyle = .boldBodyLarge + inlineDisplayLabel.textAlignment = .left + selectedOptionLabel.textStyle = .bodyLarge + selectedOptionLabel.textAlignment = .left + errorLabel.textAlignment = .left + errorLabel.textStyle = .bodySmall + helperLabel.textAlignment = .left + helperLabel.textStyle = .bodySmall + tooltipModel = nil + tooltipTitle = "" + tooltipContent = "" + label = nil + errorText = nil + error = false + isEnabled = false + readOnly = false + inlineLabel = false + helperText = nil + transparentBackground = false + required = false + options = [] + } + + open override func updateView() { + container.backgroundColor = backgroundColorConfiguration.getColor(self) + container.layer.borderColor = containerBorderColorConfiguration.getColor(self).cgColor + container.layer.borderWidth = VDSFormControls.widthBorder + container.layer.cornerRadius = VDSFormControls.borderradius + + updateTitleLabel() + updateInlineLabel() + updateErrorLabel() + updateHelperLabel() + + selectedOptionLabel.surface = surface + backgroundColor = surface.color + } + + /// Used to update any Accessibility properties. + open override func updateAccessibility() { + super.updateAccessibility() + if showError { + setAccessibilityLabel(for: [eyebrowLabel, selectedOptionLabel, errorLabel, helperLabel]) + } else { + setAccessibilityLabel(for: [eyebrowLabel, selectedOptionLabel, helperLabel]) + } + } + + open func updateTitleLabel() { + + var attributes: [any LabelAttributeModel] = [] + var updatedLabelText = label + + updatedLabelText = inlineLabel ? "" : updatedLabelText + + if let oldText = updatedLabelText, !required, !oldText.hasSuffix("Optional") { + let optionColorAttr = ColorLabelAttribute(location: oldText.count + 2, + length: 8, + color: secondaryColorConfig.getColor(self)) + + updatedLabelText = "\(oldText) Optional" + attributes.append(optionColorAttr) + } + +// updateTooltip() + if let tooltipModel { + attributes.append(TooltipLabelAttribute(surface: surface, model: tooltipModel, presenter: self)) + } + + eyebrowLabel.labelText = updatedLabelText + eyebrowLabel.labelAttributes = attributes + eyebrowLabel.tooltipModel = tooltipModel + eyebrowLabel.surface = surface + eyebrowLabel.isEnabled = isEnabled + + } + + //-------------------------------------------------- + // MARK: - Public Methods + //-------------------------------------------------- + + open func updateInlineLabel() { + + ///Minimum width with inline text as per design + widthConstraint?.constant = inlineLabel ? minWidthInlineLabel : minWidthDefault + widthConstraint?.isActive = true + +// inlineDisplayLabel.text = inlineLabel ? (label!.isEmpty ? ((label ?? "") + ":") : label) : "" + inlineDisplayLabel.text = inlineLabel ? label : "" + inlineDisplayLabel.surface = surface + inlineWidthConstraint?.constant = inlineDisplayLabel.intrinsicContentSize.width + inlineWidthConstraint?.isActive = !inlineLabel + } + + open func updateSelectedOptionLabel(text: String? = nil) { + selectedOptionLabel.text = text ?? "" + } + + open func updateErrorLabel(){ + if showError, let errorText { + errorLabel.text = errorText + errorLabel.surface = surface + errorLabel.isEnabled = isEnabled + errorLabel.isHidden = false + icon.name = .error + icon.size = .medium + icon.color = .black + icon.surface = surface + } else { + icon.name = Icon.Name(name: "down-caret") + icon.surface = surface + errorLabel.isHidden = true + } + } + + open func updateHelperLabel(){ + + if let helperText { + helperLabel.text = helperText + helperLabel.surface = surface + helperLabel.isEnabled = isEnabled + helperLabel.isHidden = false + } else { + helperLabel.isHidden = true + } + } + + @objc open func pickerDoneClicked() { + dropdownField.resignFirstResponder() + } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + + private func toolBarForPicker() -> UIToolbar { + + let inputToolbar: UIToolbar = UIToolbar() + inputToolbar.barStyle = .default + inputToolbar.isTranslucent = true + + let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(pickerDoneClicked)) + let flexibleSpaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + + inputToolbar.setItems([flexibleSpaceButton, doneButton], animated: false) + inputToolbar.isUserInteractionEnabled = true + inputToolbar.sizeToFit() + return inputToolbar + } + + func updateTooltip() { + self.tooltipModel = .init(title: tooltipTitle, content: tooltipContent) + } + + +} + +extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate { + + 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 ?? 0 + } + + public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + guard let options, options.count > row else { return nil } + return options[row] + } + + public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + guard let options, options.count > row else { return } + updateSelectedOptionLabel(text: options[row]) + } } From a6f03e3e1e449b5d2867cdc445983adc162ec722 Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 2 Apr 2024 18:20:02 +0530 Subject: [PATCH 04/25] Digital ACT-191 ONEAPP-7135 story: using tooltip model --- .../DropdownSelect/DropdownSelect.swift | 191 +++++++----------- 1 file changed, 77 insertions(+), 114 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index d7613f94..826f768e 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -34,69 +34,48 @@ open class DropdownSelect: Control { // MARK: - Public Properties //-------------------------------------------------- - ///Boolean value that determines if component should show the error state/error message. + /// Boolean value that determines if component should show the error state/error message. open var error: Bool = false { didSet { setNeedsUpdate() }} - + /// Message displayed when there is an error. open var errorText: String? { didSet { setNeedsUpdate() }} - + /// If provided, will be used as text for the helper label. open var helperText: String? { didSet { setNeedsUpdate() }} - - /// used if the component is enabled or not. + + /// Used if the component is enabled or not. open override var isEnabled: Bool { didSet { setNeedsUpdate() }} - + /// If true, the label will be displayed inside the dropdown container. Otherwise, the label will be above the dropdown container like a normal text input. open var inlineLabel: Bool = false { didSet { setNeedsUpdate() }} - + /// If provided, will be used as context for the label on the input field. open var label: String? { didSet { setNeedsUpdate() }} /// Not allowed the user interaction to select/change input if it is true. open var readOnly: Bool = false { didSet { setNeedsUpdate() }} - + /// Used to show optional indicator for the label. open var required: Bool = false { didSet { setNeedsUpdate() }} - + /// Allows unique ID to be passed to the element. open var selectId: String? { didSet { setNeedsUpdate() }} - - // TO DO: either have model or individual title and content. + /// Config object for tooltip option, is optional. open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } } - - /// Used to set tooltip title. - open var tooltipTitle: String { - get { return _tooltipTitle } - set { - _tooltipTitle = newValue - updateTooltip() - setNeedsUpdate() - } - } - /// Used to set tooltip content. - open var tooltipContent: String { - get { return _tooltipContent } - set { - _tooltipContent = newValue - updateTooltip() - setNeedsUpdate() - } - } - /// If provided, will render with trnasparent background. open var transparentBackground: Bool = false { didSet { setNeedsUpdate() }} /// Used to set width for the Dropdown Select. open var width: Int? { didSet { setNeedsUpdate() }} - - // TO DO: create model for options + + /// TO DO: create model for options open var options: [String]? { didSet { setNeedsUpdate() }} - ///Boolean or a Function that returns a boolean value that determines if component should show the error state/error message.Functon receives the 'event' object on input change. + /// Boolean value that determines if component should show the error state/error message. Functon receives the 'event' object on input change. open var showError: Bool = false { didSet { setNeedsUpdate() }} - + open override var state: UIControl.State { get { var state = super.state @@ -110,18 +89,14 @@ open class DropdownSelect: Control { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var _tooltipTitle: String = "" - - internal var _tooltipContent: String = "" - internal var minWidthDefault = 66.0 internal var minWidthInlineLabel = 102.0 - + var stackView: UIStackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .vertical } - + private var eyebrowLabel = TrailingTooltipLabel().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.labelTextAlignment = .left @@ -131,7 +106,7 @@ open class DropdownSelect: Control { var container: UIView = UIView().with { $0.translatesAutoresizingMaskIntoConstraints = false } - + var containerStack: UIStackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .horizontal @@ -143,7 +118,7 @@ open class DropdownSelect: Control { $0.tintColor = UIColor.clear $0.font = TextStyle.bodyLarge.font } - + private var inlineDisplayLabel = Label().with { $0.textAlignment = .left $0.textStyle = .boldBodyLarge @@ -155,12 +130,12 @@ open class DropdownSelect: Control { $0.textAlignment = .left $0.textStyle = .bodyLarge } - + private var icon: Icon = Icon().with { $0.size = .medium $0.name = Icon.Name(name: "down-caret") } - + private var errorLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textAlignment = .left @@ -172,7 +147,7 @@ open class DropdownSelect: Control { $0.textAlignment = .left $0.textStyle = .bodySmall } - + private var optionsPicker = UIPickerView() //-------------------------------------------------- @@ -185,7 +160,7 @@ open class DropdownSelect: Control { // MARK: - Configuration Properties //-------------------------------------------------- internal var containerSize: CGSize { CGSize(width: 45, height: 44) } - + /// Color configuration for error icon. internal let primaryColorConfig = ViewColorConfiguration().with { $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) @@ -196,13 +171,13 @@ open class DropdownSelect: Control { $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) } - + internal var containerBorderColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOnlight, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) @@ -210,16 +185,15 @@ open class DropdownSelect: Control { } //-------------------------------------------------- - // MARK: - Lifecycle + // 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() - + isAccessibilityElement = true accessibilityLabel = "Dropdown Select" - + addSubview(stackView) container.addSubview(containerStack) @@ -227,6 +201,7 @@ open class DropdownSelect: Control { dropdownField.width(0) + // container stack let spacing = VDSFormControls.spaceInset containerStack.pinToSuperView(.init(top: spacing, left: spacing, bottom: spacing, right: spacing)) containerStack.addArrangedSubview(dropdownField) @@ -237,12 +212,13 @@ open class DropdownSelect: Control { containerStack.setCustomSpacing(0, after: dropdownField) containerStack.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: inlineDisplayLabel) containerStack.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: selectedOptionLabel) - + + // component stack stackView.addArrangedSubview(eyebrowLabel) stackView.addArrangedSubview(container) stackView.addArrangedSubview(errorLabel) stackView.addArrangedSubview(helperLabel) - + stackView.setCustomSpacing(4, after: eyebrowLabel) stackView.setCustomSpacing(8, after: container) stackView.setCustomSpacing(8, after: errorLabel) @@ -250,10 +226,10 @@ open class DropdownSelect: Control { stackView.pinToSuperView() inlineWidthConstraint = inlineDisplayLabel.widthAnchor.constraint(equalToConstant: 0) inlineWidthConstraint?.isActive = true - + widthConstraint = stackView.widthAnchor.constraint(greaterThanOrEqualToConstant: minWidthDefault) widthConstraint?.isActive = true - + eyebrowLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() errorLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() helperLabel.textColorConfiguration = secondaryColorConfig.eraseToAnyColorable() @@ -261,7 +237,7 @@ open class DropdownSelect: Control { optionsPicker.delegate = self optionsPicker.dataSource = self - + optionsPicker.isHidden = true dropdownField.inputView = optionsPicker @@ -272,40 +248,7 @@ open class DropdownSelect: Control { }.store(in: &subscribers) } - open override func reset() { - super.reset() - - eyebrowLabel.reset() - inlineDisplayLabel.reset() - selectedOptionLabel.reset() - errorLabel.reset() - helperLabel.reset() - - eyebrowLabel.labelTextAlignment = .left - eyebrowLabel.labelTextStyle = .bodySmall - inlineDisplayLabel.textStyle = .boldBodyLarge - inlineDisplayLabel.textAlignment = .left - selectedOptionLabel.textStyle = .bodyLarge - selectedOptionLabel.textAlignment = .left - errorLabel.textAlignment = .left - errorLabel.textStyle = .bodySmall - helperLabel.textAlignment = .left - helperLabel.textStyle = .bodySmall - tooltipModel = nil - tooltipTitle = "" - tooltipContent = "" - label = nil - errorText = nil - error = false - isEnabled = false - readOnly = false - inlineLabel = false - helperText = nil - transparentBackground = false - required = false - options = [] - } - + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { container.backgroundColor = backgroundColorConfiguration.getColor(self) container.layer.borderColor = containerBorderColorConfiguration.getColor(self).cgColor @@ -331,13 +274,46 @@ open class DropdownSelect: Control { } } + /// Resets to default settings. + open override func reset() { + super.reset() + + eyebrowLabel.reset() + inlineDisplayLabel.reset() + selectedOptionLabel.reset() + errorLabel.reset() + helperLabel.reset() + + eyebrowLabel.labelTextStyle = .bodySmall + inlineDisplayLabel.textStyle = .boldBodyLarge + selectedOptionLabel.textStyle = .bodyLarge + errorLabel.textStyle = .bodySmall + helperLabel.textStyle = .bodySmall + tooltipModel = nil + label = nil + errorText = nil + error = false + isEnabled = false + readOnly = false + inlineLabel = false + helperText = nil + transparentBackground = false + required = false + options = [] + } + + //-------------------------------------------------- + // MARK: - Public Methods + //-------------------------------------------------- + open func updateTitleLabel() { - + + //update the local vars for the label since we no long have a model var attributes: [any LabelAttributeModel] = [] var updatedLabelText = label updatedLabelText = inlineLabel ? "" : updatedLabelText - + if let oldText = updatedLabelText, !required, !oldText.hasSuffix("Optional") { let optionColorAttr = ColorLabelAttribute(location: oldText.count + 2, length: 8, @@ -347,30 +323,24 @@ open class DropdownSelect: Control { attributes.append(optionColorAttr) } -// updateTooltip() if let tooltipModel { attributes.append(TooltipLabelAttribute(surface: surface, model: tooltipModel, presenter: self)) } - + eyebrowLabel.labelText = updatedLabelText eyebrowLabel.labelAttributes = attributes eyebrowLabel.tooltipModel = tooltipModel eyebrowLabel.surface = surface eyebrowLabel.isEnabled = isEnabled - } - //-------------------------------------------------- - // MARK: - Public Methods - //-------------------------------------------------- - open func updateInlineLabel() { - + ///Minimum width with inline text as per design widthConstraint?.constant = inlineLabel ? minWidthInlineLabel : minWidthDefault widthConstraint?.isActive = true - -// inlineDisplayLabel.text = inlineLabel ? (label!.isEmpty ? ((label ?? "") + ":") : label) : "" + + // inlineDisplayLabel.text = inlineLabel ? (label!.isEmpty ? ((label ?? "") + ":") : label) : "" inlineDisplayLabel.text = inlineLabel ? label : "" inlineDisplayLabel.surface = surface inlineWidthConstraint?.constant = inlineDisplayLabel.intrinsicContentSize.width @@ -381,7 +351,7 @@ open class DropdownSelect: Control { selectedOptionLabel.text = text ?? "" } - open func updateErrorLabel(){ + open func updateErrorLabel() { if showError, let errorText { errorLabel.text = errorText errorLabel.surface = surface @@ -398,8 +368,7 @@ open class DropdownSelect: Control { } } - open func updateHelperLabel(){ - + open func updateHelperLabel() { if let helperText { helperLabel.text = helperText helperLabel.surface = surface @@ -426,18 +395,12 @@ open class DropdownSelect: Control { let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(pickerDoneClicked)) let flexibleSpaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) - + inputToolbar.setItems([flexibleSpaceButton, doneButton], animated: false) inputToolbar.isUserInteractionEnabled = true inputToolbar.sizeToFit() return inputToolbar } - - func updateTooltip() { - self.tooltipModel = .init(title: tooltipTitle, content: tooltipContent) - } - - } extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate { From ae86948502f8c67328924ae48acfa6413be6957a Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 2 Apr 2024 18:27:15 +0530 Subject: [PATCH 05/25] Digital ACT-191 ONEAPP-7135 story: added change log for DropdownSelect --- VDS.xcodeproj/project.pbxproj | 4 ++ .../DropdownSelectChangeLog.txt | 45 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 VDS/Components/DropdownSelect/DropdownSelectChangeLog.txt diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 7bf3c054..b071240b 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 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 */; }; @@ -204,6 +205,7 @@ 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 = ""; }; @@ -413,6 +415,7 @@ isa = PBXGroup; children = ( 186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */, + 186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */, ); path = DropdownSelect; sourceTree = ""; @@ -1069,6 +1072,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 */, 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 From e3b6b832e29b57bf5704bf811e9e4c121d784d17 Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 4 Apr 2024 11:31:38 +0530 Subject: [PATCH 06/25] Digital ACT-191 ONEAPP-7135 story: added model class for options, added callback method to pass selected option text, updating inlineDisplayLabel text if inlineLabel true --- VDS.xcodeproj/project.pbxproj | 4 ++ .../DropdownSelect/DropdownOptionModel.swift | 20 ++++++ .../DropdownSelect/DropdownSelect.swift | 72 +++++++++++-------- 3 files changed, 65 insertions(+), 31 deletions(-) create mode 100644 VDS/Components/DropdownSelect/DropdownOptionModel.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index b071240b..d63b3cad 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 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 */; }; @@ -209,6 +210,7 @@ 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 = ""; }; @@ -415,6 +417,7 @@ isa = PBXGroup; children = ( 186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */, + 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */, 186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */, ); path = DropdownSelect; @@ -1132,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 */, diff --git a/VDS/Components/DropdownSelect/DropdownOptionModel.swift b/VDS/Components/DropdownSelect/DropdownOptionModel.swift new file mode 100644 index 00000000..ba7caa7f --- /dev/null +++ b/VDS/Components/DropdownSelect/DropdownOptionModel.swift @@ -0,0 +1,20 @@ +// +// 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 init(text: String) { + self.text = text + } + } +} diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 826f768e..f5ce15ef 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -68,10 +68,13 @@ open class DropdownSelect: Control { open var transparentBackground: Bool = false { didSet { setNeedsUpdate() }} /// Used to set width for the Dropdown Select. - open var width: Int? { didSet { setNeedsUpdate() }} + open var width: CGFloat? { didSet { setNeedsUpdate() } } + + /// Array of options to show + open var options: [DropdownOptionModel] = [] { didSet { setNeedsUpdate() }} - /// TO DO: create model for options - open var options: [String]? { didSet { setNeedsUpdate() }} + /// A callback when the selected option changes. Passes parameters (option). + open var onDropdownItemSelect: ((DropdownOptionModel) -> Void)? /// Boolean value that determines if component should show the error state/error message. Functon receives the 'event' object on input change. open var showError: Bool = false { didSet { setNeedsUpdate() }} @@ -159,7 +162,7 @@ open class DropdownSelect: Control { //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- - internal var containerSize: CGSize { CGSize(width: 45, height: 44) } + internal var containerSize: CGSize { CGSize(width: minWidthDefault, height: 44) } /// Color configuration for error icon. internal let primaryColorConfig = ViewColorConfiguration().with { @@ -187,6 +190,7 @@ open class DropdownSelect: Control { //-------------------------------------------------- // 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() @@ -194,16 +198,15 @@ open class DropdownSelect: Control { isAccessibilityElement = true accessibilityLabel = "Dropdown Select" + // stackview addSubview(stackView) - - container.addSubview(containerStack) stackView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height).isActive = true - - dropdownField.width(0) - + // container stack + container.addSubview(containerStack) let spacing = VDSFormControls.spaceInset containerStack.pinToSuperView(.init(top: spacing, left: spacing, bottom: spacing, right: spacing)) + containerStack.addArrangedSubview(dropdownField) containerStack.addArrangedSubview(inlineDisplayLabel) containerStack.addArrangedSubview(selectedOptionLabel) @@ -212,8 +215,9 @@ open class DropdownSelect: Control { containerStack.setCustomSpacing(0, after: dropdownField) containerStack.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: inlineDisplayLabel) containerStack.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: selectedOptionLabel) - - // component stack + dropdownField.width(0) + + // component stackview subviews stackView.addArrangedSubview(eyebrowLabel) stackView.addArrangedSubview(container) stackView.addArrangedSubview(errorLabel) @@ -237,12 +241,9 @@ open class DropdownSelect: Control { optionsPicker.delegate = self optionsPicker.dataSource = self - optionsPicker.isHidden = true - dropdownField.inputView = optionsPicker dropdownField.inputAccessoryView = toolBarForPicker() - containerStack.publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in self?.launchPicker() }.store(in: &subscribers) @@ -336,15 +337,20 @@ open class DropdownSelect: Control { open func updateInlineLabel() { - ///Minimum width with inline text as per design - widthConstraint?.constant = inlineLabel ? minWidthInlineLabel : minWidthDefault - widthConstraint?.isActive = true - - // inlineDisplayLabel.text = inlineLabel ? (label!.isEmpty ? ((label ?? "") + ":") : label) : "" - inlineDisplayLabel.text = inlineLabel ? label : "" + /// inline label text and selected option text separated by ':' + if let label, !label.isEmpty { + inlineDisplayLabel.text = inlineLabel ? (label + ":") : "" + } else { + inlineDisplayLabel.text = inlineLabel ? label : "" + } inlineDisplayLabel.surface = surface + + /// Minimum width with inline text as per design inlineWidthConstraint?.constant = inlineDisplayLabel.intrinsicContentSize.width inlineWidthConstraint?.isActive = !inlineLabel + widthConstraint?.constant = inlineLabel ? minWidthInlineLabel : minWidthDefault + widthConstraint?.isActive = true + } open func updateSelectedOptionLabel(text: String? = nil) { @@ -392,12 +398,12 @@ open class DropdownSelect: Control { let inputToolbar: UIToolbar = UIToolbar() inputToolbar.barStyle = .default inputToolbar.isTranslucent = true - - let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(pickerDoneClicked)) - let flexibleSpaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) - - inputToolbar.setItems([flexibleSpaceButton, doneButton], animated: false) - inputToolbar.isUserInteractionEnabled = true + + // add a done button to the toolbar + inputToolbar.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 } @@ -414,21 +420,25 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource, UITextFi optionsPicker.isHidden = !optionsPicker.isHidden } + //-------------------------------------------------- + // MARK: - UIPickerView Delegate & Datasource + //-------------------------------------------------- public func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { - return options?.count ?? 0 + return options.count } public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { - guard let options, options.count > row else { return nil } - return options[row] + guard options.count > row else { return nil } + return options[row].text } public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { - guard let options, options.count > row else { return } - updateSelectedOptionLabel(text: options[row]) + guard options.count > row else { return } + updateSelectedOptionLabel(text: options[row].text) + self.onDropdownItemSelect?(options[row]) } } From e5d8b12bd69e72d8665f2a1e50d8caf56d0b0e55 Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 4 Apr 2024 11:40:07 +0530 Subject: [PATCH 07/25] Digital ACT-191 ONEAPP-7135 story: color configurations as per states --- .../DropdownSelect/DropdownSelect.swift | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index f5ce15ef..c70b531d 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -175,6 +175,12 @@ open class DropdownSelect: Control { $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false) } + internal let iconColorConfig = 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 backgroundColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .normal) $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .disabled) @@ -185,6 +191,16 @@ open class DropdownSelect: Control { $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOnlight, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) + $0.setSurfaceColors(VDSFormControlsColor.borderHoverOnlight, VDSFormControlsColor.borderHoverOndark, forState: .focused) + } + + internal var errorBorderColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .focused) + } + + internal var readOnlyBorderColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSFormControlsColor.borderReadonlyOnlight, VDSFormControlsColor.borderReadonlyOndark, forState: .normal) } //-------------------------------------------------- @@ -238,6 +254,8 @@ open class DropdownSelect: Control { errorLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() helperLabel.textColorConfiguration = secondaryColorConfig.eraseToAnyColorable() inlineDisplayLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() + selectedOptionLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() + icon.color = iconColorConfig.getColor(self) optionsPicker.delegate = self optionsPicker.dataSource = self @@ -251,16 +269,20 @@ open class DropdownSelect: Control { /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { + container.backgroundColor = backgroundColorConfiguration.getColor(self) - container.layer.borderColor = containerBorderColorConfiguration.getColor(self).cgColor container.layer.borderWidth = VDSFormControls.widthBorder container.layer.cornerRadius = VDSFormControls.borderradius + container.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : (showError ? errorBorderColorConfiguration.getColor(self).cgColor : containerBorderColorConfiguration.getColor(self).cgColor) + dropdownField.isUserInteractionEnabled = readOnly ? false : true updateTitleLabel() updateInlineLabel() updateErrorLabel() updateHelperLabel() - + if readOnly { + icon.name = nil + } selectedOptionLabel.surface = surface backgroundColor = surface.color } From 149fda901dade76e8d8fba89a2d37ece5a1ad93f Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 4 Apr 2024 14:17:36 +0530 Subject: [PATCH 08/25] Digital ACT-191 ONEAPP-7135 story: Text Overflow handling, did set transparent background if is true --- VDS/Components/DropdownSelect/DropdownSelect.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index c70b531d..3e8aac32 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -132,6 +132,7 @@ open class DropdownSelect: Control { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textAlignment = .left $0.textStyle = .bodyLarge + $0.lineBreakMode = .byCharWrapping } private var icon: Icon = Icon().with { @@ -275,7 +276,8 @@ open class DropdownSelect: Control { container.layer.cornerRadius = VDSFormControls.borderradius container.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : (showError ? errorBorderColorConfiguration.getColor(self).cgColor : containerBorderColorConfiguration.getColor(self).cgColor) dropdownField.isUserInteractionEnabled = readOnly ? false : true - + stackView.backgroundColor = transparentBackground ? .clear : surface.color + updateTitleLabel() updateInlineLabel() updateErrorLabel() From 791bfc8870e1eead1608a3dfd9e93f75674f0cad Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 5 Apr 2024 12:44:11 +0530 Subject: [PATCH 09/25] Digital ACT-191 ONEAPP-7135 story: Inline Text label alignment changes --- .../DropdownSelect/DropdownSelect.swift | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 3e8aac32..e9b29a76 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -125,6 +125,7 @@ open class DropdownSelect: Control { private var inlineDisplayLabel = Label().with { $0.textAlignment = .left $0.textStyle = .boldBodyLarge + $0.lineBreakMode = .byCharWrapping $0.sizeToFit() } @@ -157,15 +158,13 @@ open class DropdownSelect: Control { //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- - internal var widthConstraint: NSLayoutConstraint? internal var inlineWidthConstraint: NSLayoutConstraint? - + //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- - internal var containerSize: CGSize { CGSize(width: minWidthDefault, height: 44) } + internal var containerSize: CGSize { CGSize(width: inlineLabel ? minWidthDefault : minWidthInlineLabel, height: 44) } - /// Color configuration for error icon. internal let primaryColorConfig = ViewColorConfiguration().with { $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) @@ -217,6 +216,7 @@ open class DropdownSelect: Control { // stackview addSubview(stackView) + stackView.pinToSuperView() stackView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height).isActive = true // container stack @@ -233,6 +233,8 @@ open class DropdownSelect: Control { containerStack.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: inlineDisplayLabel) containerStack.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: selectedOptionLabel) dropdownField.width(0) + inlineWidthConstraint = inlineDisplayLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 0) + inlineWidthConstraint?.isActive = true // component stackview subviews stackView.addArrangedSubview(eyebrowLabel) @@ -244,13 +246,7 @@ open class DropdownSelect: Control { stackView.setCustomSpacing(8, after: container) stackView.setCustomSpacing(8, after: errorLabel) - stackView.pinToSuperView() - inlineWidthConstraint = inlineDisplayLabel.widthAnchor.constraint(equalToConstant: 0) - inlineWidthConstraint?.isActive = true - - widthConstraint = stackView.widthAnchor.constraint(greaterThanOrEqualToConstant: minWidthDefault) - widthConstraint?.isActive = true - + // setting color config eyebrowLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() errorLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() helperLabel.textColorConfiguration = secondaryColorConfig.eraseToAnyColorable() @@ -258,6 +254,7 @@ open class DropdownSelect: Control { selectedOptionLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() icon.color = iconColorConfig.getColor(self) + // Options PickerView optionsPicker.delegate = self optionsPicker.dataSource = self optionsPicker.isHidden = true @@ -361,6 +358,8 @@ open class DropdownSelect: Control { open func updateInlineLabel() { + inlineWidthConstraint?.isActive = false + /// inline label text and selected option text separated by ':' if let label, !label.isEmpty { inlineDisplayLabel.text = inlineLabel ? (label + ":") : "" @@ -369,12 +368,9 @@ open class DropdownSelect: Control { } inlineDisplayLabel.surface = surface - /// Minimum width with inline text as per design - inlineWidthConstraint?.constant = inlineDisplayLabel.intrinsicContentSize.width - inlineWidthConstraint?.isActive = !inlineLabel - widthConstraint?.constant = inlineLabel ? minWidthInlineLabel : minWidthDefault - widthConstraint?.isActive = true - + /// Update width as per updated text size + inlineWidthConstraint = inlineDisplayLabel.widthAnchor.constraint(equalToConstant: inlineDisplayLabel.intrinsicContentSize.width) + inlineWidthConstraint?.isActive = true } open func updateSelectedOptionLabel(text: String? = nil) { From 51d1ded4c8f6c658718536d0f39c04e3d1f3a882 Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 5 Apr 2024 21:08:52 +0530 Subject: [PATCH 10/25] Digital ACT-191 ONEAPP-7135 story: updated variable name for error boolean --- .../DropdownSelect/DropdownSelect.swift | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index e9b29a76..76518c93 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -75,14 +75,11 @@ open class DropdownSelect: Control { /// A callback when the selected option changes. Passes parameters (option). open var onDropdownItemSelect: ((DropdownOptionModel) -> Void)? - - /// Boolean value that determines if component should show the error state/error message. Functon receives the 'event' object on input change. - open var showError: Bool = false { didSet { setNeedsUpdate() }} - + open override var state: UIControl.State { get { var state = super.state - if showError { + if error { state.insert(.error) } return state @@ -209,8 +206,7 @@ open class DropdownSelect: Control { /// 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() - + super.setup() isAccessibilityElement = true accessibilityLabel = "Dropdown Select" @@ -271,7 +267,7 @@ open class DropdownSelect: Control { container.backgroundColor = backgroundColorConfiguration.getColor(self) container.layer.borderWidth = VDSFormControls.widthBorder container.layer.cornerRadius = VDSFormControls.borderradius - container.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : (showError ? errorBorderColorConfiguration.getColor(self).cgColor : containerBorderColorConfiguration.getColor(self).cgColor) + container.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : (error ? errorBorderColorConfiguration.getColor(self).cgColor : containerBorderColorConfiguration.getColor(self).cgColor) dropdownField.isUserInteractionEnabled = readOnly ? false : true stackView.backgroundColor = transparentBackground ? .clear : surface.color @@ -289,7 +285,7 @@ open class DropdownSelect: Control { /// Used to update any Accessibility properties. open override func updateAccessibility() { super.updateAccessibility() - if showError { + if error { setAccessibilityLabel(for: [eyebrowLabel, selectedOptionLabel, errorLabel, helperLabel]) } else { setAccessibilityLabel(for: [eyebrowLabel, selectedOptionLabel, helperLabel]) @@ -378,7 +374,7 @@ open class DropdownSelect: Control { } open func updateErrorLabel() { - if showError, let errorText { + if error, let errorText { errorLabel.text = errorText errorLabel.surface = surface errorLabel.isEnabled = isEnabled From 99d6878ed92cb8b88199a5b3b0415cd8dd8d4903 Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 5 Apr 2024 21:14:23 +0530 Subject: [PATCH 11/25] Digital ACT-191 ONEAPP-7135 story: removed unnecessary code --- VDS/Components/DropdownSelect/DropdownSelect.swift | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 76518c93..ec179700 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -281,17 +281,7 @@ open class DropdownSelect: Control { selectedOptionLabel.surface = surface backgroundColor = surface.color } - - /// Used to update any Accessibility properties. - open override func updateAccessibility() { - super.updateAccessibility() - if error { - setAccessibilityLabel(for: [eyebrowLabel, selectedOptionLabel, errorLabel, helperLabel]) - } else { - setAccessibilityLabel(for: [eyebrowLabel, selectedOptionLabel, helperLabel]) - } - } - + /// Resets to default settings. open override func reset() { super.reset() From d0f5f26e823077b0f6a4d86ab7216f73406118d4 Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 5 Apr 2024 21:36:29 +0530 Subject: [PATCH 12/25] Digital ACT-191 ONEAPP-7135 story: allowed selectedID to update selected label text --- VDS/Components/DropdownSelect/DropdownSelect.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index ec179700..39f182ca 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -59,7 +59,7 @@ open class DropdownSelect: Control { open var required: Bool = false { didSet { setNeedsUpdate() }} /// Allows unique ID to be passed to the element. - open var selectId: String? { didSet { setNeedsUpdate() }} + open var selectId: Int? { didSet { setNeedsUpdate() }} /// Config object for tooltip option, is optional. open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } } @@ -357,6 +357,10 @@ open class DropdownSelect: Control { /// 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(text: options[selectId].text) + } } open func updateSelectedOptionLabel(text: String? = nil) { @@ -392,6 +396,7 @@ open class DropdownSelect: Control { } @objc open func pickerDoneClicked() { + optionsPicker.isHidden = true dropdownField.resignFirstResponder() } From f584ccd6bac63f48155fa241fc0a861c13522ef5 Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 8 Apr 2024 16:22:01 +0530 Subject: [PATCH 13/25] Digital ACT-191 ONEAPP-7135 story: minor changes on noticed issues --- VDS/Components/DropdownSelect/DropdownSelect.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 39f182ca..3b1549df 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -308,6 +308,7 @@ open class DropdownSelect: Control { transparentBackground = false required = false options = [] + selectId = 0 } //-------------------------------------------------- @@ -449,6 +450,7 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource, UITextFi public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { guard options.count > row else { return } + selectId = row updateSelectedOptionLabel(text: options[row].text) self.onDropdownItemSelect?(options[row]) } From 67c96ca74cfab7f840c06ca85a4a9ceaaaa6f603 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 8 Apr 2024 10:15:11 -0500 Subject: [PATCH 14/25] moved maxLength out of base Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 5 +---- VDS/Components/TextFields/TextArea/TextArea.swift | 10 +++++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 5d8957fe..ce76ea16 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -197,9 +197,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { 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. @@ -308,7 +306,6 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { tooltipModel = nil transparentBackground = false width = nil - maxLength = nil inputId = nil value = nil defaultValue = nil diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index b5fada0c..5ce47b1f 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -127,7 +127,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 { From 52eaed7be6b9d4df975695bd175f6a6bb3a83d3c Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 8 Apr 2024 10:50:24 -0500 Subject: [PATCH 15/25] added new iconNames Signed-off-by: Matt Bruce --- VDS/Components/Icon/IconName.swift | 2 ++ 1 file changed, 2 insertions(+) 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") From fc6c398b3f6b13fd5cc70e3b4bdda77f1aa45182 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 8 Apr 2024 10:50:57 -0500 Subject: [PATCH 16/25] refactored to similar naming conventions for existing components Signed-off-by: Matt Bruce --- .../DropdownSelect/DropdownSelect.swift | 82 +++++++++---------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 3b1549df..69f6d92a 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -35,22 +35,19 @@ open class DropdownSelect: Control { //-------------------------------------------------- /// Boolean value that determines if component should show the error state/error message. - open var error: Bool = false { didSet { setNeedsUpdate() }} + open var showError: Bool = false { didSet { setNeedsUpdate() }} /// Message displayed when there is an error. open var errorText: String? { didSet { setNeedsUpdate() }} /// If provided, will be used as text for the helper label. open var helperText: String? { didSet { setNeedsUpdate() }} - - /// Used if the component is enabled or not. - open override var isEnabled: Bool { didSet { setNeedsUpdate() }} - - /// If true, the label will be displayed inside the dropdown container. Otherwise, the label will be above the dropdown container like a normal text input. - open var inlineLabel: Bool = false { didSet { setNeedsUpdate() }} + + /// 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() }} /// If provided, will be used as context for the label on the input field. - open var label: String? { didSet { setNeedsUpdate() }} + open var labelText: String? { didSet { setNeedsUpdate() }} /// Not allowed the user interaction to select/change input if it is true. open var readOnly: Bool = false { didSet { setNeedsUpdate() }} @@ -79,7 +76,7 @@ open class DropdownSelect: Control { open override var state: UIControl.State { get { var state = super.state - if error { + if showError { state.insert(.error) } return state @@ -97,13 +94,13 @@ open class DropdownSelect: Control { $0.axis = .vertical } - private var eyebrowLabel = TrailingTooltipLabel().with { + private var titleLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.labelTextAlignment = .left - $0.labelTextStyle = .bodySmall + $0.textAlignment = .left + $0.textStyle = .bodySmall } - var container: UIView = UIView().with { + var containerView: UIView = UIView().with { $0.translatesAutoresizingMaskIntoConstraints = false } @@ -160,7 +157,7 @@ open class DropdownSelect: Control { //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- - internal var containerSize: CGSize { CGSize(width: inlineLabel ? minWidthDefault : minWidthInlineLabel, height: 44) } + internal var containerSize: CGSize { CGSize(width: showInlineLabel ? minWidthInlineLabel : width ?? minWidthDefault, height: 44) } internal let primaryColorConfig = ViewColorConfiguration().with { $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) @@ -215,8 +212,8 @@ open class DropdownSelect: Control { stackView.pinToSuperView() stackView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height).isActive = true - // container stack - container.addSubview(containerStack) + // containerView stack + containerView.addSubview(containerStack) let spacing = VDSFormControls.spaceInset containerStack.pinToSuperView(.init(top: spacing, left: spacing, bottom: spacing, right: spacing)) @@ -233,17 +230,17 @@ open class DropdownSelect: Control { inlineWidthConstraint?.isActive = true // component stackview subviews - stackView.addArrangedSubview(eyebrowLabel) - stackView.addArrangedSubview(container) + stackView.addArrangedSubview(titleLabel) + stackView.addArrangedSubview(containerView) stackView.addArrangedSubview(errorLabel) stackView.addArrangedSubview(helperLabel) - stackView.setCustomSpacing(4, after: eyebrowLabel) - stackView.setCustomSpacing(8, after: container) + stackView.setCustomSpacing(4, after: titleLabel) + stackView.setCustomSpacing(8, after: containerView) stackView.setCustomSpacing(8, after: errorLabel) // setting color config - eyebrowLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() + titleLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() errorLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() helperLabel.textColorConfiguration = secondaryColorConfig.eraseToAnyColorable() inlineDisplayLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() @@ -264,10 +261,10 @@ open class DropdownSelect: Control { /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { - container.backgroundColor = backgroundColorConfiguration.getColor(self) - container.layer.borderWidth = VDSFormControls.widthBorder - container.layer.cornerRadius = VDSFormControls.borderradius - container.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : (error ? errorBorderColorConfiguration.getColor(self).cgColor : containerBorderColorConfiguration.getColor(self).cgColor) + containerView.backgroundColor = backgroundColorConfiguration.getColor(self) + containerView.layer.borderWidth = VDSFormControls.widthBorder + containerView.layer.cornerRadius = VDSFormControls.borderradius + containerView.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : (showError ? errorBorderColorConfiguration.getColor(self).cgColor : containerBorderColorConfiguration.getColor(self).cgColor) dropdownField.isUserInteractionEnabled = readOnly ? false : true stackView.backgroundColor = transparentBackground ? .clear : surface.color @@ -286,24 +283,24 @@ open class DropdownSelect: Control { open override func reset() { super.reset() - eyebrowLabel.reset() + titleLabel.reset() inlineDisplayLabel.reset() selectedOptionLabel.reset() errorLabel.reset() helperLabel.reset() - eyebrowLabel.labelTextStyle = .bodySmall + titleLabel.textStyle = .bodySmall inlineDisplayLabel.textStyle = .boldBodyLarge selectedOptionLabel.textStyle = .bodyLarge errorLabel.textStyle = .bodySmall helperLabel.textStyle = .bodySmall tooltipModel = nil - label = nil + labelText = nil errorText = nil - error = false + showError = false isEnabled = false readOnly = false - inlineLabel = false + showInlineLabel = false helperText = nil transparentBackground = false required = false @@ -319,16 +316,16 @@ open class DropdownSelect: Control { //update the local vars for the label since we no long have a model var attributes: [any LabelAttributeModel] = [] - var updatedLabelText = label + var updatedLabelText = labelText - updatedLabelText = inlineLabel ? "" : updatedLabelText + updatedLabelText = showInlineLabel ? "" : updatedLabelText if let oldText = updatedLabelText, !required, !oldText.hasSuffix("Optional") { let optionColorAttr = ColorLabelAttribute(location: oldText.count + 2, length: 8, color: secondaryColorConfig.getColor(self)) - updatedLabelText = "\(oldText) Optional" + updatedLabelText = showInlineLabel ? "Optional" : "\(oldText) Optional" attributes.append(optionColorAttr) } @@ -336,11 +333,10 @@ open class DropdownSelect: Control { attributes.append(TooltipLabelAttribute(surface: surface, model: tooltipModel, presenter: self)) } - eyebrowLabel.labelText = updatedLabelText - eyebrowLabel.labelAttributes = attributes - eyebrowLabel.tooltipModel = tooltipModel - eyebrowLabel.surface = surface - eyebrowLabel.isEnabled = isEnabled + titleLabel.text = updatedLabelText + titleLabel.attributes = attributes + titleLabel.surface = surface + titleLabel.isEnabled = isEnabled } open func updateInlineLabel() { @@ -348,10 +344,10 @@ open class DropdownSelect: Control { inlineWidthConstraint?.isActive = false /// inline label text and selected option text separated by ':' - if let label, !label.isEmpty { - inlineDisplayLabel.text = inlineLabel ? (label + ":") : "" + if let labelText, !labelText.isEmpty { + inlineDisplayLabel.text = showInlineLabel ? (labelText + ":") : "" } else { - inlineDisplayLabel.text = inlineLabel ? label : "" + inlineDisplayLabel.text = showInlineLabel ? labelText : "" } inlineDisplayLabel.surface = surface @@ -369,7 +365,7 @@ open class DropdownSelect: Control { } open func updateErrorLabel() { - if error, let errorText { + if showError, let errorText { errorLabel.text = errorText errorLabel.surface = surface errorLabel.isEnabled = isEnabled @@ -379,7 +375,7 @@ open class DropdownSelect: Control { icon.color = .black icon.surface = surface } else { - icon.name = Icon.Name(name: "down-caret") + icon.name = .downCaret icon.surface = surface errorLabel.isHidden = true } From c0c666c2cb0aeeb4de7d8421c72aab20367e22ac Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 8 Apr 2024 11:11:12 -0500 Subject: [PATCH 17/25] fixed error/disabled border Signed-off-by: Matt Bruce --- .../DropdownSelect/DropdownSelect.swift | 16 +++++++--------- VDS/Components/TextFields/EntryFieldBase.swift | 3 ++- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 69f6d92a..68e4fba4 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -181,18 +181,14 @@ open class DropdownSelect: Control { $0.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error) } - internal var containerBorderColorConfiguration = ControlColorConfiguration().with { + internal var borderColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOnlight, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.disabled,.error]) $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) $0.setSurfaceColors(VDSFormControlsColor.borderHoverOnlight, VDSFormControlsColor.borderHoverOndark, forState: .focused) } - - internal var errorBorderColorConfiguration = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .focused) - } - + internal var readOnlyBorderColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSFormControlsColor.borderReadonlyOnlight, VDSFormControlsColor.borderReadonlyOndark, forState: .normal) } @@ -264,7 +260,7 @@ open class DropdownSelect: Control { containerView.backgroundColor = backgroundColorConfiguration.getColor(self) containerView.layer.borderWidth = VDSFormControls.widthBorder containerView.layer.cornerRadius = VDSFormControls.borderradius - containerView.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : (showError ? errorBorderColorConfiguration.getColor(self).cgColor : containerBorderColorConfiguration.getColor(self).cgColor) + containerView.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : borderColorConfiguration.getColor(self).cgColor dropdownField.isUserInteractionEnabled = readOnly ? false : true stackView.backgroundColor = transparentBackground ? .clear : surface.color @@ -276,6 +272,7 @@ open class DropdownSelect: Control { icon.name = nil } selectedOptionLabel.surface = surface + selectedOptionLabel.isEnabled = isEnabled backgroundColor = surface.color } @@ -350,6 +347,7 @@ open class DropdownSelect: Control { 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) @@ -372,13 +370,13 @@ open class DropdownSelect: Control { errorLabel.isHidden = false icon.name = .error icon.size = .medium - icon.color = .black icon.surface = surface } else { icon.name = .downCaret icon.surface = surface errorLabel.isHidden = true } + icon.color = titleLabel.textColorConfiguration.getColor(self) } open func updateHelperLabel() { diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index ce76ea16..f6bba297 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 { @@ -332,7 +333,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 } From e7220e67a393bac0937ee3ffbb7a70d6cba0a697 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 8 Apr 2024 11:26:09 -0500 Subject: [PATCH 18/25] put to public since all subcomponents are required to be accessible to devs. Signed-off-by: Matt Bruce --- .../DropdownSelect/DropdownSelect.swift | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 68e4fba4..072a9846 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -93,13 +93,7 @@ open class DropdownSelect: Control { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .vertical } - - private var titleLabel = Label().with { - $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.textAlignment = .left - $0.textStyle = .bodySmall - } - + var containerView: UIView = UIView().with { $0.translatesAutoresizingMaskIntoConstraints = false } @@ -110,44 +104,53 @@ open class DropdownSelect: Control { $0.spacing = VDSFormControls.spaceInset } - private var dropdownField = UITextField().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.tintColor = UIColor.clear - $0.font = TextStyle.bodyLarge.font + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + open var titleLabel = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.textAlignment = .left + $0.textStyle = .bodySmall } - private var inlineDisplayLabel = Label().with { + open var inlineDisplayLabel = Label().with { $0.textAlignment = .left $0.textStyle = .boldBodyLarge $0.lineBreakMode = .byCharWrapping $0.sizeToFit() } - private var selectedOptionLabel = Label().with { + open var selectedOptionLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textAlignment = .left $0.textStyle = .bodyLarge $0.lineBreakMode = .byCharWrapping } - private var icon: Icon = Icon().with { + open var icon: Icon = Icon().with { $0.size = .medium $0.name = Icon.Name(name: "down-caret") } - private var errorLabel = Label().with { + open var errorLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textAlignment = .left $0.textStyle = .bodySmall } - private var helperLabel = Label().with { + open var helperLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textAlignment = .left $0.textStyle = .bodySmall } - private var optionsPicker = UIPickerView() + open var dropdownField = UITextField().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.tintColor = UIColor.clear + $0.font = TextStyle.bodyLarge.font + } + + open var optionsPicker = UIPickerView() //-------------------------------------------------- // MARK: - Constraints @@ -376,7 +379,7 @@ open class DropdownSelect: Control { icon.surface = surface errorLabel.isHidden = true } - icon.color = titleLabel.textColorConfiguration.getColor(self) + icon.color = iconColorConfig.getColor(self) } open func updateHelperLabel() { From 7b722e4c7e63b8387651a48422610b84e7bcf482 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Apr 2024 14:33:10 -0500 Subject: [PATCH 19/25] renamed Signed-off-by: Matt Bruce --- .../DropdownSelect/DropdownSelect.swift | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 072a9846..55510dd4 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -98,7 +98,7 @@ open class DropdownSelect: Control { $0.translatesAutoresizingMaskIntoConstraints = false } - var containerStack: UIStackView = UIStackView().with { + var containerStackView: UIStackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .horizontal $0.spacing = VDSFormControls.spaceInset @@ -212,18 +212,18 @@ open class DropdownSelect: Control { stackView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height).isActive = true // containerView stack - containerView.addSubview(containerStack) + containerView.addSubview(containerStackView) let spacing = VDSFormControls.spaceInset - containerStack.pinToSuperView(.init(top: spacing, left: spacing, bottom: spacing, right: spacing)) + containerStackView.pinToSuperView(.init(top: spacing, left: spacing, bottom: spacing, right: spacing)) - containerStack.addArrangedSubview(dropdownField) - containerStack.addArrangedSubview(inlineDisplayLabel) - containerStack.addArrangedSubview(selectedOptionLabel) - containerStack.addArrangedSubview(icon) + containerStackView.addArrangedSubview(dropdownField) + containerStackView.addArrangedSubview(inlineDisplayLabel) + containerStackView.addArrangedSubview(selectedOptionLabel) + containerStackView.addArrangedSubview(icon) - containerStack.setCustomSpacing(0, after: dropdownField) - containerStack.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: inlineDisplayLabel) - containerStack.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: selectedOptionLabel) + containerStackView.setCustomSpacing(0, after: dropdownField) + containerStackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: inlineDisplayLabel) + containerStackView.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: selectedOptionLabel) dropdownField.width(0) inlineWidthConstraint = inlineDisplayLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 0) inlineWidthConstraint?.isActive = true @@ -252,7 +252,7 @@ open class DropdownSelect: Control { optionsPicker.isHidden = true dropdownField.inputView = optionsPicker dropdownField.inputAccessoryView = toolBarForPicker() - containerStack.publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in + containerStackView.publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in self?.launchPicker() }.store(in: &subscribers) } From 7b9aa51d0a5b720a762415f6fe410b729485b8ce Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Apr 2024 15:37:58 -0500 Subject: [PATCH 20/25] refactored out text of base class Signed-off-by: Matt Bruce --- .../TextFields/EntryFieldBase.swift | 21 ++----------------- .../TextFields/InputField/InputField.swift | 15 ++++++++++++- .../TextFields/TextArea/TextArea.swift | 15 ++++++++++++- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index f6bba297..c57a3432 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -186,13 +186,7 @@ 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() } } @@ -201,18 +195,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open var inputId: String? { didSet { setNeedsUpdate() } } - /// The text of this textField. - private var _value: String? - open var value: String? { - get { _value } - set { - if let newValue, newValue != _value { - _value = newValue - text = newValue - } - setNeedsUpdate() - } - } + open var value: String? { didSet { setNeedsUpdate() } } open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index ebd76525..b739b577 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -79,7 +79,7 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { open var fieldType: FieldType = .text { didSet { setNeedsUpdate() } } /// The text of this textField. - open override var text: String? { + open var text: String? { get { textField.text } set { if let newValue, newValue != text { @@ -89,6 +89,19 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { setNeedsUpdate() } } + + /// The value of this textField. + private var _value: String? + open override var value: String? { + get { _value } + set { + if let newValue, newValue != _value { + _value = newValue + text = newValue + } + setNeedsUpdate() + } + } 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 5ce47b1f..ee09ecb3 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -108,7 +108,7 @@ open class TextArea: EntryFieldBase { /// The text of this textView private var _text: String? - open override var text: String? { + open var text: String? { get { textView.text } set { if let newValue, newValue != _text { @@ -119,6 +119,19 @@ open class TextArea: EntryFieldBase { setNeedsUpdate() } } + + /// The text of this textField. + private var _value: String? + open override var value: String? { + get { _value } + set { + if let newValue, newValue != _value { + _value = newValue + text = newValue + } + setNeedsUpdate() + } + } /// UITextView shown in the TextArea. open var textView = TextView().with { From 8311de540979e5991ba37ce989eea7de833b26a9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Apr 2024 15:38:10 -0500 Subject: [PATCH 21/25] added value in case there is a difference Signed-off-by: Matt Bruce --- VDS/Components/DropdownSelect/DropdownOptionModel.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/VDS/Components/DropdownSelect/DropdownOptionModel.swift b/VDS/Components/DropdownSelect/DropdownOptionModel.swift index ba7caa7f..c82519eb 100644 --- a/VDS/Components/DropdownSelect/DropdownOptionModel.swift +++ b/VDS/Components/DropdownSelect/DropdownOptionModel.swift @@ -12,9 +12,11 @@ extension DropdownSelect { /// Text that goes as option to DropdownSelect public var text: String + public var value: String - public init(text: String) { + public init(text: String, value: String? = nil) { self.text = text + self.value = value ?? text } } } From 4f0f611fb65ea3fa40b81ae012e0c6d1f3eb835a Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Apr 2024 15:38:39 -0500 Subject: [PATCH 22/25] now subclassing EntryFieldBase Signed-off-by: Matt Bruce --- .../DropdownSelect/DropdownSelect.swift | 304 ++++-------------- 1 file changed, 67 insertions(+), 237 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 55510dd4..94f7112d 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -13,8 +13,7 @@ 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: Control { - +open class DropdownSelect: EntryFieldBase { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -33,86 +32,27 @@ open class DropdownSelect: Control { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - - /// Boolean value that determines if component should show the error state/error message. - open var showError: Bool = false { didSet { setNeedsUpdate() }} - - /// Message displayed when there is an error. - open var errorText: String? { didSet { setNeedsUpdate() }} - - /// If provided, will be used as text for the helper label. - open var helperText: String? { didSet { setNeedsUpdate() }} - /// 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() }} - - /// If provided, will be used as context for the label on the input field. - open var labelText: String? { didSet { setNeedsUpdate() }} - - /// Not allowed the user interaction to select/change input if it is true. - open var readOnly: Bool = false { didSet { setNeedsUpdate() }} - - /// Used to show optional indicator for the label. - open var required: Bool = false { didSet { setNeedsUpdate() }} - + /// Allows unique ID to be passed to the element. open var selectId: Int? { didSet { setNeedsUpdate() }} - /// Config object for tooltip option, is optional. - open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } } - - /// If provided, will render with trnasparent background. - open var transparentBackground: Bool = false { didSet { setNeedsUpdate() }} - - /// Used to set width for the Dropdown Select. - open var width: CGFloat? { didSet { setNeedsUpdate() } } - /// Array of options to show open var options: [DropdownOptionModel] = [] { didSet { setNeedsUpdate() }} /// A callback when the selected option changes. Passes parameters (option). open var onDropdownItemSelect: ((DropdownOptionModel) -> Void)? - open override var state: UIControl.State { - get { - var state = super.state - if showError { - state.insert(.error) - } - return state - } - } - //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- internal var minWidthDefault = 66.0 internal var minWidthInlineLabel = 102.0 - - var stackView: UIStackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.axis = .vertical - } - - var containerView: UIView = UIView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - } - - var containerStackView: UIStackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.axis = .horizontal - $0.spacing = VDSFormControls.spaceInset - } - + //-------------------------------------------------- // MARK: - Public Properties - //-------------------------------------------------- - open var titleLabel = Label().with { - $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.textAlignment = .left - $0.textStyle = .bodySmall - } - + //-------------------------------------------------- open var inlineDisplayLabel = Label().with { $0.textAlignment = .left $0.textStyle = .boldBodyLarge @@ -126,24 +66,7 @@ open class DropdownSelect: Control { $0.textStyle = .bodyLarge $0.lineBreakMode = .byCharWrapping } - - open var icon: Icon = Icon().with { - $0.size = .medium - $0.name = Icon.Name(name: "down-caret") - } - - open var errorLabel = Label().with { - $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.textAlignment = .left - $0.textStyle = .bodySmall - } - - open var helperLabel = Label().with { - $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.textAlignment = .left - $0.textStyle = .bodySmall - } - + open var dropdownField = UITextField().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.tintColor = UIColor.clear @@ -160,42 +83,14 @@ open class DropdownSelect: Control { //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- - internal var containerSize: CGSize { CGSize(width: showInlineLabel ? minWidthInlineLabel : width ?? minWidthDefault, height: 44) } - - internal let primaryColorConfig = ViewColorConfiguration().with { - $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) - } - - internal let secondaryColorConfig = ViewColorConfiguration().with { - $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) - $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false) - } - - internal let iconColorConfig = ControlColorConfiguration().with { + 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) } - - 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) - } - - internal var borderColorConfiguration = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOnlight, forState: .normal) - $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) - $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.disabled,.error]) - $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) - $0.setSurfaceColors(VDSFormControlsColor.borderHoverOnlight, VDSFormControlsColor.borderHoverOndark, forState: .focused) - } - - internal var readOnlyBorderColorConfiguration = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSFormControlsColor.borderReadonlyOnlight, VDSFormControlsColor.borderReadonlyOndark, forState: .normal) - } - + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -203,107 +98,78 @@ open class DropdownSelect: Control { /// 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() - isAccessibilityElement = true + accessibilityLabel = "Dropdown Select" - // stackview - addSubview(stackView) - stackView.pinToSuperView() - stackView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height).isActive = true - - // containerView stack - containerView.addSubview(containerStackView) - let spacing = VDSFormControls.spaceInset - containerStackView.pinToSuperView(.init(top: spacing, left: spacing, bottom: spacing, right: spacing)) + // 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() - containerStackView.addArrangedSubview(dropdownField) - containerStackView.addArrangedSubview(inlineDisplayLabel) - containerStackView.addArrangedSubview(selectedOptionLabel) - containerStackView.addArrangedSubview(icon) + controlStackView.addArrangedSubview(dropdownField) + controlStackView.addArrangedSubview(inlineDisplayLabel) + controlStackView.addArrangedSubview(selectedOptionLabel) - containerStackView.setCustomSpacing(0, after: dropdownField) - containerStackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: inlineDisplayLabel) - containerStackView.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: 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 - - // component stackview subviews - stackView.addArrangedSubview(titleLabel) - stackView.addArrangedSubview(containerView) - stackView.addArrangedSubview(errorLabel) - stackView.addArrangedSubview(helperLabel) - - stackView.setCustomSpacing(4, after: titleLabel) - stackView.setCustomSpacing(8, after: containerView) - stackView.setCustomSpacing(8, after: errorLabel) // setting color config - titleLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() - errorLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() - helperLabel.textColorConfiguration = secondaryColorConfig.eraseToAnyColorable() - inlineDisplayLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() - selectedOptionLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() - icon.color = iconColorConfig.getColor(self) + inlineDisplayLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() + selectedOptionLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() // Options PickerView optionsPicker.delegate = self optionsPicker.dataSource = self optionsPicker.isHidden = true dropdownField.inputView = optionsPicker - dropdownField.inputAccessoryView = toolBarForPicker() - containerStackView.publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in - self?.launchPicker() - }.store(in: &subscribers) + 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() - containerView.backgroundColor = backgroundColorConfiguration.getColor(self) - containerView.layer.borderWidth = VDSFormControls.widthBorder - containerView.layer.cornerRadius = VDSFormControls.borderradius - containerView.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : borderColorConfiguration.getColor(self).cgColor - dropdownField.isUserInteractionEnabled = readOnly ? false : true - stackView.backgroundColor = transparentBackground ? .clear : surface.color - - updateTitleLabel() updateInlineLabel() - updateErrorLabel() - updateHelperLabel() - if readOnly { - icon.name = nil - } + + dropdownField.isUserInteractionEnabled = readOnly ? false : true selectedOptionLabel.surface = surface selectedOptionLabel.isEnabled = isEnabled - backgroundColor = surface.color } /// Resets to default settings. open override func reset() { super.reset() - titleLabel.reset() - inlineDisplayLabel.reset() - selectedOptionLabel.reset() - errorLabel.reset() - helperLabel.reset() - - titleLabel.textStyle = .bodySmall inlineDisplayLabel.textStyle = .boldBodyLarge selectedOptionLabel.textStyle = .bodyLarge - errorLabel.textStyle = .bodySmall - helperLabel.textStyle = .bodySmall - tooltipModel = nil - labelText = nil - errorText = nil - showError = false - isEnabled = false - readOnly = false showInlineLabel = false - helperText = nil - transparentBackground = false - required = false options = [] selectId = 0 } @@ -312,7 +178,7 @@ open class DropdownSelect: Control { // MARK: - Public Methods //-------------------------------------------------- - open func updateTitleLabel() { + open override func updateTitleLabel() { //update the local vars for the label since we no long have a model var attributes: [any LabelAttributeModel] = [] @@ -323,7 +189,7 @@ open class DropdownSelect: Control { if let oldText = updatedLabelText, !required, !oldText.hasSuffix("Optional") { let optionColorAttr = ColorLabelAttribute(location: oldText.count + 2, length: 8, - color: secondaryColorConfig.getColor(self)) + color: secondaryColorConfiguration.getColor(self)) updatedLabelText = showInlineLabel ? "Optional" : "\(oldText) Optional" attributes.append(optionColorAttr) @@ -357,70 +223,37 @@ open class DropdownSelect: Control { inlineWidthConstraint?.isActive = true if let selectId, selectId < options.count { - updateSelectedOptionLabel(text: options[selectId].text) + updateSelectedOptionLabel(option: options[selectId]) } } - open func updateSelectedOptionLabel(text: String? = nil) { - selectedOptionLabel.text = text ?? "" + open func updateSelectedOptionLabel(option: DropdownOptionModel? = nil) { + selectedOptionLabel.text = option?.text ?? "" + value = option?.value } - open func updateErrorLabel() { - if showError, let errorText { - errorLabel.text = errorText - errorLabel.surface = surface - errorLabel.isEnabled = isEnabled - errorLabel.isHidden = false - icon.name = .error - icon.size = .medium - icon.surface = surface - } else { + open override func updateErrorLabel() { + super.updateErrorLabel() + if !showError && !hasInternalError { icon.name = .downCaret - icon.surface = surface - errorLabel.isHidden = true - } - icon.color = iconColorConfig.getColor(self) - } - - open func updateHelperLabel() { - if let helperText { - helperLabel.text = helperText - helperLabel.surface = surface - helperLabel.isEnabled = isEnabled - helperLabel.isHidden = false - } else { - helperLabel.isHidden = true } + icon.surface = surface + icon.isHidden = readOnly ? true : false + icon.color = iconColorConfiguration.getColor(self) } @objc open func pickerDoneClicked() { optionsPicker.isHidden = true dropdownField.resignFirstResponder() } - - //-------------------------------------------------- - // MARK: - Private Methods - //-------------------------------------------------- - - private func toolBarForPicker() -> UIToolbar { - - let inputToolbar: UIToolbar = UIToolbar() - inputToolbar.barStyle = .default - inputToolbar.isTranslucent = true - - // add a done button to the toolbar - inputToolbar.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 - } } -extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate { +//-------------------------------------------------- +// MARK: - UIPickerView Delegate & Datasource +//-------------------------------------------------- +extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { - func launchPicker() { + internal func launchPicker() { if optionsPicker.isHidden { dropdownField.becomeFirstResponder() } else { @@ -429,9 +262,6 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource, UITextFi optionsPicker.isHidden = !optionsPicker.isHidden } - //-------------------------------------------------- - // MARK: - UIPickerView Delegate & Datasource - //-------------------------------------------------- public func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } @@ -448,7 +278,7 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource, UITextFi public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { guard options.count > row else { return } selectId = row - updateSelectedOptionLabel(text: options[row].text) + updateSelectedOptionLabel(option: options[row]) self.onDropdownItemSelect?(options[row]) } } From a2f79fcee87d8d4381ff77be9c5a32fe34b7ec9d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Apr 2024 16:11:52 -0500 Subject: [PATCH 23/25] refactored to put value and the valuechanged for the control Signed-off-by: Matt Bruce --- .../TextFields/EntryFieldBase.swift | 12 ++++++++++- .../TextFields/InputField/InputField.swift | 10 +++------- .../TextFields/TextArea/TextArea.swift | 20 +++++++------------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index c57a3432..d2f3a6c7 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -195,7 +195,17 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open var inputId: String? { didSet { setNeedsUpdate() } } - open var value: String? { didSet { setNeedsUpdate() } } + /// The text of this textField. + internal var _value: String? + open var value: String? { + get { _value } + set { + if let newValue, newValue != _value { + _value = newValue + sendActions(for: .valueChanged) + } + } + } open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index b739b577..448dbbf1 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -91,15 +91,11 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { } /// The value of this textField. - private var _value: String? open override var value: String? { - get { _value } - set { - if let newValue, newValue != _value { - _value = newValue - text = newValue + didSet { + if text != value { + text = value } - setNeedsUpdate() } } diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index ee09ecb3..3c1bbf56 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -105,14 +105,12 @@ open class TextArea: EntryFieldBase { } } } - - /// The text of this textView - private var _text: String? + + /// The text of this TextArea. open var text: String? { get { textView.text } set { - if let newValue, newValue != _text { - _text = newValue + if let newValue, newValue != text { textView.text = newValue value = newValue } @@ -120,16 +118,12 @@ open class TextArea: EntryFieldBase { } } - /// The text of this textField. - private var _value: String? + /// The value of this textField. open override var value: String? { - get { _value } - set { - if let newValue, newValue != _value { - _value = newValue - text = newValue + didSet { + if text != value { + text = value } - setNeedsUpdate() } } From b07c0108883e10cb347dc4074fe32411b9e461cc Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Apr 2024 16:12:24 -0500 Subject: [PATCH 24/25] refactored naming Signed-off-by: Matt Bruce --- VDS/Components/DropdownSelect/DropdownSelect.swift | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 94f7112d..a56eb783 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -38,11 +38,17 @@ open class DropdownSelect: EntryFieldBase { /// 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 onDropdownItemSelect: ((DropdownOptionModel) -> Void)? + open var onItemSelected: ((Int, DropdownOptionModel) -> Void)? //-------------------------------------------------- // MARK: - Private Properties @@ -52,7 +58,7 @@ open class DropdownSelect: EntryFieldBase { //-------------------------------------------------- // MARK: - Public Properties - //-------------------------------------------------- + //-------------------------------------------------- open var inlineDisplayLabel = Label().with { $0.textAlignment = .left $0.textStyle = .boldBodyLarge @@ -171,7 +177,7 @@ open class DropdownSelect: EntryFieldBase { selectedOptionLabel.textStyle = .bodyLarge showInlineLabel = false options = [] - selectId = 0 + selectId = nil } //-------------------------------------------------- @@ -279,6 +285,6 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { guard options.count > row else { return } selectId = row updateSelectedOptionLabel(option: options[row]) - self.onDropdownItemSelect?(options[row]) + self.onItemSelected?(row, options[row]) } } From 994feff20ee067b281b918e15808c9b16db4020d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Apr 2024 16:19:11 -0500 Subject: [PATCH 25/25] fixed bug in InputField/TextArea after refactor Signed-off-by: Matt Bruce --- VDS/Components/TextFields/InputField/InputField.swift | 8 +++++--- VDS/Components/TextFields/TextArea/TextArea.swift | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index 448dbbf1..b45500a0 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -78,11 +78,13 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { /// Representing the type of input. open var fieldType: FieldType = .text { didSet { setNeedsUpdate() } } - /// The text of this textField. + /// The text of this TextField. + private var _text: String? open var text: String? { - get { textField.text } + get { _text } set { - if let newValue, newValue != text { + if let newValue, newValue != _text { + _text = newValue textField.text = newValue value = newValue } diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 3c1bbf56..73a1e67c 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -107,10 +107,12 @@ open class TextArea: EntryFieldBase { } /// The text of this TextArea. + private var _text: String? open var text: String? { - get { textView.text } + get { _text } set { - if let newValue, newValue != text { + if let newValue, newValue != _text { + _text = newValue textView.text = newValue value = newValue }