Digital ACT-191 ONEAPP-7135 story: using tooltip model

This commit is contained in:
vasavk 2024-04-02 18:20:02 +05:30
parent 530a6786d8
commit a6f03e3e1e

View File

@ -34,69 +34,48 @@ open class DropdownSelect: Control {
// MARK: - Public Properties // 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() }} open var error: Bool = false { didSet { setNeedsUpdate() }}
/// Message displayed when there is an error. /// Message displayed when there is an error.
open var errorText: String? { didSet { setNeedsUpdate() }} open var errorText: String? { didSet { setNeedsUpdate() }}
/// If provided, will be used as text for the helper label. /// If provided, will be used as text for the helper label.
open var helperText: String? { didSet { setNeedsUpdate() }} 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() }} 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. /// 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() }} open var inlineLabel: Bool = false { didSet { setNeedsUpdate() }}
/// If provided, will be used as context for the label on the input field. /// If provided, will be used as context for the label on the input field.
open var label: String? { didSet { setNeedsUpdate() }} open var label: String? { didSet { setNeedsUpdate() }}
/// Not allowed the user interaction to select/change input if it is true. /// Not allowed the user interaction to select/change input if it is true.
open var readOnly: Bool = false { didSet { setNeedsUpdate() }} open var readOnly: Bool = false { didSet { setNeedsUpdate() }}
/// Used to show optional indicator for the label. /// Used to show optional indicator for the label.
open var required: Bool = false { didSet { setNeedsUpdate() }} open var required: Bool = false { didSet { setNeedsUpdate() }}
/// Allows unique ID to be passed to the element. /// Allows unique ID to be passed to the element.
open var selectId: String? { didSet { setNeedsUpdate() }} open var selectId: String? { didSet { setNeedsUpdate() }}
// TO DO: either have model or individual title and content.
/// Config object for tooltip option, is optional. /// Config object for tooltip option, is optional.
open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } } 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. /// If provided, will render with trnasparent background.
open var transparentBackground: Bool = false { didSet { setNeedsUpdate() }} open var transparentBackground: Bool = false { didSet { setNeedsUpdate() }}
/// Used to set width for the Dropdown Select. /// Used to set width for the Dropdown Select.
open var width: Int? { didSet { setNeedsUpdate() }} open var width: Int? { didSet { setNeedsUpdate() }}
// TO DO: create model for options /// TO DO: create model for options
open var options: [String]? { didSet { setNeedsUpdate() }} 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 var showError: Bool = false { didSet { setNeedsUpdate() }}
open override var state: UIControl.State { open override var state: UIControl.State {
get { get {
var state = super.state var state = super.state
@ -110,18 +89,14 @@ open class DropdownSelect: Control {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
//-------------------------------------------------- //--------------------------------------------------
internal var _tooltipTitle: String = ""
internal var _tooltipContent: String = ""
internal var minWidthDefault = 66.0 internal var minWidthDefault = 66.0
internal var minWidthInlineLabel = 102.0 internal var minWidthInlineLabel = 102.0
var stackView: UIStackView = UIStackView().with { var stackView: UIStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false $0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical $0.axis = .vertical
} }
private var eyebrowLabel = TrailingTooltipLabel().with { private var eyebrowLabel = TrailingTooltipLabel().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.labelTextAlignment = .left $0.labelTextAlignment = .left
@ -131,7 +106,7 @@ open class DropdownSelect: Control {
var container: UIView = UIView().with { var container: UIView = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false $0.translatesAutoresizingMaskIntoConstraints = false
} }
var containerStack: UIStackView = UIStackView().with { var containerStack: UIStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false $0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .horizontal $0.axis = .horizontal
@ -143,7 +118,7 @@ open class DropdownSelect: Control {
$0.tintColor = UIColor.clear $0.tintColor = UIColor.clear
$0.font = TextStyle.bodyLarge.font $0.font = TextStyle.bodyLarge.font
} }
private var inlineDisplayLabel = Label().with { private var inlineDisplayLabel = Label().with {
$0.textAlignment = .left $0.textAlignment = .left
$0.textStyle = .boldBodyLarge $0.textStyle = .boldBodyLarge
@ -155,12 +130,12 @@ open class DropdownSelect: Control {
$0.textAlignment = .left $0.textAlignment = .left
$0.textStyle = .bodyLarge $0.textStyle = .bodyLarge
} }
private var icon: Icon = Icon().with { private var icon: Icon = Icon().with {
$0.size = .medium $0.size = .medium
$0.name = Icon.Name(name: "down-caret") $0.name = Icon.Name(name: "down-caret")
} }
private var errorLabel = Label().with { private var errorLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textAlignment = .left $0.textAlignment = .left
@ -172,7 +147,7 @@ open class DropdownSelect: Control {
$0.textAlignment = .left $0.textAlignment = .left
$0.textStyle = .bodySmall $0.textStyle = .bodySmall
} }
private var optionsPicker = UIPickerView() private var optionsPicker = UIPickerView()
//-------------------------------------------------- //--------------------------------------------------
@ -185,7 +160,7 @@ open class DropdownSelect: Control {
// MARK: - Configuration Properties // MARK: - Configuration Properties
//-------------------------------------------------- //--------------------------------------------------
internal var containerSize: CGSize { CGSize(width: 45, height: 44) } internal var containerSize: CGSize { CGSize(width: 45, height: 44) }
/// Color configuration for error icon. /// Color configuration for error icon.
internal let primaryColorConfig = ViewColorConfiguration().with { internal let primaryColorConfig = ViewColorConfiguration().with {
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) $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.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true)
$0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false) $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false)
} }
internal var backgroundColorConfiguration = ControlColorConfiguration().with { internal var backgroundColorConfiguration = ControlColorConfiguration().with {
$0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .normal) $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .normal)
$0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .disabled) $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .disabled)
$0.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error) $0.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error)
} }
internal var containerBorderColorConfiguration = ControlColorConfiguration().with { internal var containerBorderColorConfiguration = ControlColorConfiguration().with {
$0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOnlight, forState: .normal) $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOnlight, forState: .normal)
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) $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. /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
open override func setup() { open override func setup() {
super.setup() super.setup()
isAccessibilityElement = true isAccessibilityElement = true
accessibilityLabel = "Dropdown Select" accessibilityLabel = "Dropdown Select"
addSubview(stackView) addSubview(stackView)
container.addSubview(containerStack) container.addSubview(containerStack)
@ -227,6 +201,7 @@ open class DropdownSelect: Control {
dropdownField.width(0) dropdownField.width(0)
// container stack
let spacing = VDSFormControls.spaceInset let spacing = VDSFormControls.spaceInset
containerStack.pinToSuperView(.init(top: spacing, left: spacing, bottom: spacing, right: spacing)) containerStack.pinToSuperView(.init(top: spacing, left: spacing, bottom: spacing, right: spacing))
containerStack.addArrangedSubview(dropdownField) containerStack.addArrangedSubview(dropdownField)
@ -237,12 +212,13 @@ open class DropdownSelect: Control {
containerStack.setCustomSpacing(0, after: dropdownField) containerStack.setCustomSpacing(0, after: dropdownField)
containerStack.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: inlineDisplayLabel) containerStack.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: inlineDisplayLabel)
containerStack.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: selectedOptionLabel) containerStack.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: selectedOptionLabel)
// component stack
stackView.addArrangedSubview(eyebrowLabel) stackView.addArrangedSubview(eyebrowLabel)
stackView.addArrangedSubview(container) stackView.addArrangedSubview(container)
stackView.addArrangedSubview(errorLabel) stackView.addArrangedSubview(errorLabel)
stackView.addArrangedSubview(helperLabel) stackView.addArrangedSubview(helperLabel)
stackView.setCustomSpacing(4, after: eyebrowLabel) stackView.setCustomSpacing(4, after: eyebrowLabel)
stackView.setCustomSpacing(8, after: container) stackView.setCustomSpacing(8, after: container)
stackView.setCustomSpacing(8, after: errorLabel) stackView.setCustomSpacing(8, after: errorLabel)
@ -250,10 +226,10 @@ open class DropdownSelect: Control {
stackView.pinToSuperView() stackView.pinToSuperView()
inlineWidthConstraint = inlineDisplayLabel.widthAnchor.constraint(equalToConstant: 0) inlineWidthConstraint = inlineDisplayLabel.widthAnchor.constraint(equalToConstant: 0)
inlineWidthConstraint?.isActive = true inlineWidthConstraint?.isActive = true
widthConstraint = stackView.widthAnchor.constraint(greaterThanOrEqualToConstant: minWidthDefault) widthConstraint = stackView.widthAnchor.constraint(greaterThanOrEqualToConstant: minWidthDefault)
widthConstraint?.isActive = true widthConstraint?.isActive = true
eyebrowLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() eyebrowLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable()
errorLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable() errorLabel.textColorConfiguration = primaryColorConfig.eraseToAnyColorable()
helperLabel.textColorConfiguration = secondaryColorConfig.eraseToAnyColorable() helperLabel.textColorConfiguration = secondaryColorConfig.eraseToAnyColorable()
@ -261,7 +237,7 @@ open class DropdownSelect: Control {
optionsPicker.delegate = self optionsPicker.delegate = self
optionsPicker.dataSource = self optionsPicker.dataSource = self
optionsPicker.isHidden = true optionsPicker.isHidden = true
dropdownField.inputView = optionsPicker dropdownField.inputView = optionsPicker
@ -272,40 +248,7 @@ open class DropdownSelect: Control {
}.store(in: &subscribers) }.store(in: &subscribers)
} }
open override func reset() { /// Used to make changes to the View based off a change events or from local properties.
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() { open override func updateView() {
container.backgroundColor = backgroundColorConfiguration.getColor(self) container.backgroundColor = backgroundColorConfiguration.getColor(self)
container.layer.borderColor = containerBorderColorConfiguration.getColor(self).cgColor 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() { open func updateTitleLabel() {
//update the local vars for the label since we no long have a model
var attributes: [any LabelAttributeModel] = [] var attributes: [any LabelAttributeModel] = []
var updatedLabelText = label var updatedLabelText = label
updatedLabelText = inlineLabel ? "" : updatedLabelText updatedLabelText = inlineLabel ? "" : updatedLabelText
if let oldText = updatedLabelText, !required, !oldText.hasSuffix("Optional") { if let oldText = updatedLabelText, !required, !oldText.hasSuffix("Optional") {
let optionColorAttr = ColorLabelAttribute(location: oldText.count + 2, let optionColorAttr = ColorLabelAttribute(location: oldText.count + 2,
length: 8, length: 8,
@ -347,30 +323,24 @@ open class DropdownSelect: Control {
attributes.append(optionColorAttr) attributes.append(optionColorAttr)
} }
// updateTooltip()
if let tooltipModel { if let tooltipModel {
attributes.append(TooltipLabelAttribute(surface: surface, model: tooltipModel, presenter: self)) attributes.append(TooltipLabelAttribute(surface: surface, model: tooltipModel, presenter: self))
} }
eyebrowLabel.labelText = updatedLabelText eyebrowLabel.labelText = updatedLabelText
eyebrowLabel.labelAttributes = attributes eyebrowLabel.labelAttributes = attributes
eyebrowLabel.tooltipModel = tooltipModel eyebrowLabel.tooltipModel = tooltipModel
eyebrowLabel.surface = surface eyebrowLabel.surface = surface
eyebrowLabel.isEnabled = isEnabled eyebrowLabel.isEnabled = isEnabled
} }
//--------------------------------------------------
// MARK: - Public Methods
//--------------------------------------------------
open func updateInlineLabel() { open func updateInlineLabel() {
///Minimum width with inline text as per design ///Minimum width with inline text as per design
widthConstraint?.constant = inlineLabel ? minWidthInlineLabel : minWidthDefault widthConstraint?.constant = inlineLabel ? minWidthInlineLabel : minWidthDefault
widthConstraint?.isActive = true widthConstraint?.isActive = true
// inlineDisplayLabel.text = inlineLabel ? (label!.isEmpty ? ((label ?? "") + ":") : label) : "" // inlineDisplayLabel.text = inlineLabel ? (label!.isEmpty ? ((label ?? "") + ":") : label) : ""
inlineDisplayLabel.text = inlineLabel ? label : "" inlineDisplayLabel.text = inlineLabel ? label : ""
inlineDisplayLabel.surface = surface inlineDisplayLabel.surface = surface
inlineWidthConstraint?.constant = inlineDisplayLabel.intrinsicContentSize.width inlineWidthConstraint?.constant = inlineDisplayLabel.intrinsicContentSize.width
@ -381,7 +351,7 @@ open class DropdownSelect: Control {
selectedOptionLabel.text = text ?? "" selectedOptionLabel.text = text ?? ""
} }
open func updateErrorLabel(){ open func updateErrorLabel() {
if showError, let errorText { if showError, let errorText {
errorLabel.text = errorText errorLabel.text = errorText
errorLabel.surface = surface errorLabel.surface = surface
@ -398,8 +368,7 @@ open class DropdownSelect: Control {
} }
} }
open func updateHelperLabel(){ open func updateHelperLabel() {
if let helperText { if let helperText {
helperLabel.text = helperText helperLabel.text = helperText
helperLabel.surface = surface helperLabel.surface = surface
@ -426,18 +395,12 @@ open class DropdownSelect: Control {
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(pickerDoneClicked)) let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(pickerDoneClicked))
let flexibleSpaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) let flexibleSpaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
inputToolbar.setItems([flexibleSpaceButton, doneButton], animated: false) inputToolbar.setItems([flexibleSpaceButton, doneButton], animated: false)
inputToolbar.isUserInteractionEnabled = true inputToolbar.isUserInteractionEnabled = true
inputToolbar.sizeToFit() inputToolbar.sizeToFit()
return inputToolbar return inputToolbar
} }
func updateTooltip() {
self.tooltipModel = .init(title: tooltipTitle, content: tooltipContent)
}
} }
extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate { extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate {