Digital ACT-191 ONEAPP-9311 story: control width can be set to auto (default) or value (pixel) , updating stepper view when size changes.
This commit is contained in:
parent
161f690488
commit
0210e0d128
@ -299,7 +299,7 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource {
|
|||||||
dropdownField.resignFirstResponder()
|
dropdownField.resignFirstResponder()
|
||||||
}
|
}
|
||||||
optionsPicker.isHidden = !optionsPicker.isHidden
|
optionsPicker.isHidden = !optionsPicker.isHidden
|
||||||
updateContainerView()
|
updateContainerView(flag: true)
|
||||||
updateErrorLabel()
|
updateErrorLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,17 +40,17 @@ open class InputStepper: EntryFieldBase {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Properties
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open var controlWidth: String? {
|
/// Accepts a string or number value to control the width of input stepper.
|
||||||
get { _controlWidth }
|
/// auto(default) - The control's width is determined by the combined width of the value, trailing text and padding
|
||||||
set {
|
/// Value - The control's width can be set to a fixed pixel or percentage value.
|
||||||
setControlWidth(newValue)
|
open var controlWidth: String? = "auto" { didSet { setNeedsUpdate() } }
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Default value of the input stepper, defaults to '0'.
|
/// Default value of the input stepper, defaults to '0'.
|
||||||
open var defaultValue:Int = 0 { didSet { setNeedsUpdate() } }
|
open var defaultValue:Int = 0 { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// Allows an id to be passed to input stepper.
|
||||||
|
open var id: Int? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// Maximum value of the input stepper, defaults to '99'.
|
/// Maximum value of the input stepper, defaults to '99'.
|
||||||
open var maxValue: Int? {
|
open var maxValue: Int? {
|
||||||
get { return _maxValue }
|
get { return _maxValue }
|
||||||
@ -63,7 +63,7 @@ open class InputStepper: EntryFieldBase {
|
|||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Minimum value of the input stepper, defaults to '0'.
|
/// Minimum value of the input stepper, defaults to '0'.
|
||||||
open var minValue: Int? {
|
open var minValue: Int? {
|
||||||
get { return _minValue }
|
get { return _minValue }
|
||||||
@ -78,11 +78,10 @@ open class InputStepper: EntryFieldBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The size of the input stepper. Defaults to 'large'.
|
/// The size of the input stepper. Defaults to 'large'.
|
||||||
open var size: Size {
|
open var size: Size = .large {
|
||||||
get { return _size }
|
didSet {
|
||||||
set {
|
updateStepperContainerViewSize()
|
||||||
_size = newValue
|
setNeedsUpdate()
|
||||||
updateSize()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,15 +91,23 @@ open class InputStepper: EntryFieldBase {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
internal var _controlWidth = "auto"
|
|
||||||
internal var _maxValue: Int = 99
|
internal var _maxValue: Int = 99
|
||||||
internal var _minValue: Int = 0
|
internal var _minValue: Int = 0
|
||||||
internal var _size: Size = .large
|
|
||||||
|
|
||||||
private var largeMinWidth = 121
|
/// This is the view that will be wrapped with the border for userInteraction.
|
||||||
private var smallMinWidth = 90
|
/// The only subview of this view is the stepperStackView.
|
||||||
|
internal var stepperContainerView = View().with {
|
||||||
|
$0.isAccessibilityElement = true
|
||||||
|
}
|
||||||
|
|
||||||
let decrementButton = ButtonIcon().with {
|
internal var stepperStackView = UIStackView().with {
|
||||||
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
$0.axis = .horizontal
|
||||||
|
$0.distribution = .fill
|
||||||
|
$0.alignment = .fill
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var decrementButton = ButtonIcon().with {
|
||||||
$0.kind = .ghost
|
$0.kind = .ghost
|
||||||
$0.iconName = Icon.Name(name: "minus")
|
$0.iconName = Icon.Name(name: "minus")
|
||||||
$0.iconOffset = .init(x: -2, y: 0)
|
$0.iconOffset = .init(x: -2, y: 0)
|
||||||
@ -109,7 +116,7 @@ open class InputStepper: EntryFieldBase {
|
|||||||
$0.backgroundColor = .clear
|
$0.backgroundColor = .clear
|
||||||
}
|
}
|
||||||
|
|
||||||
let incrementButton = ButtonIcon().with {
|
internal var incrementButton = ButtonIcon().with {
|
||||||
$0.kind = .ghost
|
$0.kind = .ghost
|
||||||
$0.iconName = Icon.Name(name: "plus")
|
$0.iconName = Icon.Name(name: "plus")
|
||||||
$0.iconOffset = .init(x: 2, y: 0)
|
$0.iconOffset = .init(x: 2, y: 0)
|
||||||
@ -118,17 +125,31 @@ open class InputStepper: EntryFieldBase {
|
|||||||
$0.backgroundColor = .clear
|
$0.backgroundColor = .clear
|
||||||
}
|
}
|
||||||
|
|
||||||
let textLabel = Label().with {
|
internal var textLabel = Label().with {
|
||||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
$0.textStyle = .boldBodyLarge
|
$0.textStyle = .boldBodyLarge
|
||||||
$0.backgroundColor = .clear
|
$0.numberOfLines = 1
|
||||||
|
$0.lineBreakMode = .byTruncatingTail
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Configuration
|
// MARK: - Constraints
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private var labelColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight , VDSColor.elementsPrimaryOndark)
|
internal var stepperWidthConstraint: NSLayoutConstraint?
|
||||||
private var labelDisabledColorConfiguration = SurfaceColorConfiguration(VDSColor.interactiveDisabledOnlight , VDSColor.interactiveDisabledOndark)
|
internal var stepperHeightConstraint: NSLayoutConstraint?
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Configuration Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
internal override var containerSize: CGSize { CGSize(width: size == .large ? largeMinWidth : smallMinWidth, height: size == .large ? largeMinHeight : smallMinHeight) }
|
||||||
|
|
||||||
|
internal var largeMinWidth = 121
|
||||||
|
internal var smallMinWidth = 90
|
||||||
|
internal var largeMinHeight = 44
|
||||||
|
internal var smallMinHeight = 32
|
||||||
|
|
||||||
|
internal let labelColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight , VDSColor.elementsPrimaryOndark)
|
||||||
|
internal let labelDisabledColorConfiguration = SurfaceColorConfiguration(VDSColor.interactiveDisabledOnlight , VDSColor.interactiveDisabledOndark)
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
@ -137,38 +158,68 @@ open class InputStepper: EntryFieldBase {
|
|||||||
super.initialSetup()
|
super.initialSetup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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()
|
||||||
|
|
||||||
|
// accessibility
|
||||||
isAccessibilityElement = false
|
isAccessibilityElement = false
|
||||||
accessibilityLabel = "Input Stepper"
|
accessibilityLabel = "Input Stepper"
|
||||||
|
|
||||||
|
// Set initial states
|
||||||
containerView.isEnabled = false
|
containerView.isEnabled = false
|
||||||
|
statusIcon.isHidden = true
|
||||||
|
|
||||||
|
// Add listeners
|
||||||
decrementButton.onClick = { _ in self.decrementButtonClick() }
|
decrementButton.onClick = { _ in self.decrementButtonClick() }
|
||||||
incrementButton.onClick = { _ in self.incrementButtonClick() }
|
incrementButton.onClick = { _ in self.incrementButtonClick() }
|
||||||
|
|
||||||
|
// setting color config
|
||||||
|
textLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func getFieldContainer() -> UIView {
|
open override func getFieldContainer() -> UIView {
|
||||||
// stackview for controls in EntryFieldBase.controlContainerView
|
stepperStackView.addArrangedSubview(decrementButton)
|
||||||
let controlStackView = UIStackView().with {
|
stepperStackView.addArrangedSubview(textLabel)
|
||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
stepperStackView.addArrangedSubview(incrementButton)
|
||||||
$0.axis = .horizontal
|
|
||||||
$0.spacing = VDSLayout.space3X
|
// Set space between decrement button, label, and increment button relative to input Stepper size.
|
||||||
$0.backgroundColor = .clear
|
let space = size == .large ? VDSLayout.space3X : VDSLayout.space2X
|
||||||
}
|
stepperStackView.setCustomSpacing(space, after: decrementButton)
|
||||||
controlStackView.addArrangedSubview(decrementButton)
|
stepperStackView.setCustomSpacing(space, after: textLabel)
|
||||||
controlStackView.addArrangedSubview(textLabel)
|
|
||||||
controlStackView.addArrangedSubview(incrementButton)
|
// Update Edge insets relative to input Stepper size.
|
||||||
return controlStackView
|
stepperStackView.pinToSuperView(.uniform(size == .large ? 6.0 : VDSLayout.space1X))
|
||||||
|
|
||||||
|
// stepperContainerView for controls in EntryFieldBase.controlContainerView
|
||||||
|
stepperContainerView.addSubview(stepperStackView)
|
||||||
|
|
||||||
|
stepperWidthConstraint = stepperContainerView.widthAnchor.constraint(equalToConstant: containerSize.width)
|
||||||
|
stepperWidthConstraint?.deactivate()
|
||||||
|
stepperHeightConstraint = stepperContainerView.heightAnchor.constraint(equalToConstant: containerSize.height)
|
||||||
|
stepperHeightConstraint?.deactivate()
|
||||||
|
|
||||||
|
return stepperContainerView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
updateContainerView(flag: false)
|
|
||||||
textLabel.text = String(defaultValue) + " " + (trailingText ?? "")
|
|
||||||
decrementButton.surface = surface
|
|
||||||
incrementButton.surface = surface
|
|
||||||
textLabel.surface = surface
|
|
||||||
statusIcon.isHidden = true
|
statusIcon.isHidden = true
|
||||||
|
updateConstraintsToFieldStackView(flag: false)
|
||||||
|
|
||||||
|
// Update label text, style, color, ande surface.
|
||||||
|
textLabel.text = String(defaultValue) + " " + (trailingText ?? "")
|
||||||
|
textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall
|
||||||
|
textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable()
|
||||||
|
textLabel.surface = surface
|
||||||
|
|
||||||
|
// Update increment and decrement button.
|
||||||
updateButtonStates()
|
updateButtonStates()
|
||||||
|
|
||||||
|
// Update stepper container border and corner radius.
|
||||||
|
setControlWidth(controlWidth)
|
||||||
|
updateContainerView(flag: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
@ -204,7 +255,11 @@ open class InputStepper: EntryFieldBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal func updateButtonStates() {
|
internal func updateButtonStates() {
|
||||||
textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable()
|
decrementButton.customContainerSize = size == .large ? 32 : 24
|
||||||
|
incrementButton.customContainerSize = size == .large ? 32 : 24
|
||||||
|
decrementButton.surface = surface
|
||||||
|
incrementButton.surface = surface
|
||||||
|
|
||||||
if isReadOnly || !isEnabled {
|
if isReadOnly || !isEnabled {
|
||||||
decrementButton.isEnabled = false
|
decrementButton.isEnabled = false
|
||||||
incrementButton.isEnabled = false
|
incrementButton.isEnabled = false
|
||||||
@ -214,27 +269,42 @@ open class InputStepper: EntryFieldBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func updateSize() {
|
// Update edge insets and height when size changes.
|
||||||
let value = size == .large ? 6.0 : VDSLayout.space1X
|
internal func updateStepperContainerViewSize() {
|
||||||
updateConstraintsToFieldStackView(value: value)
|
updateButtonStates()
|
||||||
|
|
||||||
// textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall
|
// Update Edge insets if size changes applied.
|
||||||
// textLabel.heightAnchor.constraint(equalToConstant: size == .large ? 44 : 32).activate()
|
stepperStackView.removeFromSuperview()
|
||||||
|
stepperContainerView.addSubview(stepperStackView)
|
||||||
// decrementButton.customContainerSize = size == .large ? 32 : 24
|
stepperStackView.pinToSuperView(.uniform(size == .large ? 6.0 : VDSLayout.space1X))
|
||||||
// incrementButton.customContainerSize = size == .large ? 32 : 24
|
|
||||||
|
|
||||||
|
// Update height if size changes applied.
|
||||||
|
stepperHeightConstraint?.deactivate()
|
||||||
|
stepperHeightConstraint = stepperContainerView.heightAnchor.constraint(equalToConstant: containerSize.height)
|
||||||
|
stepperHeightConstraint?.activate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set control width to input stepper.
|
||||||
internal func setControlWidth(_ text: String?) {
|
internal func setControlWidth(_ text: String?) {
|
||||||
if let text, text == "auto" {
|
if let text, text == "auto" {
|
||||||
// Set fixed width relative to default value, trailing text label
|
stepperWidthConstraint?.deactivate()
|
||||||
} else if let controlWidth = Int(text ?? "") {
|
} else if let controlWidth = Int(text ?? "") {
|
||||||
// Use provided new width either pixel or percentage
|
// Set controlWidth provided which is either pixel or percentage
|
||||||
width = CGFloat(controlWidth)
|
let width = width ?? CGFloat(containerView.frame.size.width)
|
||||||
} else {
|
updateStepperContainerWidth(controlWidth: CGFloat(controlWidth), width: width)
|
||||||
// Use EntryFieldBase width
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handling the controlwidth without going beyond the width of the parent container.
|
||||||
|
internal func updateStepperContainerWidth(controlWidth: CGFloat, width: CGFloat) {
|
||||||
|
if controlWidth >= containerSize.width && controlWidth <= width {
|
||||||
|
stepperWidthConstraint?.deactivate()
|
||||||
|
stepperWidthConstraint?.constant = controlWidth
|
||||||
|
stepperWidthConstraint?.activate()
|
||||||
|
} else if controlWidth >= width {
|
||||||
|
stepperWidthConstraint?.deactivate()
|
||||||
|
stepperWidthConstraint?.constant = width
|
||||||
|
stepperWidthConstraint?.activate()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -227,7 +227,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
get { fatalError("must be read from subclass")}
|
get { fatalError("must be read from subclass")}
|
||||||
}
|
}
|
||||||
|
|
||||||
open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } }
|
// open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
open var isRequired: Bool = false { didSet { setNeedsUpdate() } }
|
open var isRequired: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
@ -346,7 +346,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
/// Updates the UI
|
/// Updates the UI
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
updateContainerView()
|
updateContainerView(flag: true)
|
||||||
updateContainerWidth()
|
updateContainerWidth()
|
||||||
updateTitleLabel()
|
updateTitleLabel()
|
||||||
updateErrorLabel()
|
updateErrorLabel()
|
||||||
@ -372,7 +372,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
transparentBackground = false
|
transparentBackground = false
|
||||||
width = nil
|
width = nil
|
||||||
inputId = nil
|
inputId = nil
|
||||||
defaultValue = nil
|
// defaultValue = nil
|
||||||
isRequired = false
|
isRequired = false
|
||||||
isReadOnly = false
|
isReadOnly = false
|
||||||
onChange = nil
|
onChange = nil
|
||||||
@ -401,7 +401,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
open func getFieldContainer() -> UIView {
|
open func getFieldContainer() -> UIView {
|
||||||
fatalError("Subclass must return the view that contains the field/view the user will interact with.")
|
fatalError("Subclass must return the view that contains the field/view the user will interact with.")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Container for the area in which helper or error text presents.
|
/// Container for the area in which helper or error text presents.
|
||||||
open func getBottomContainer() -> UIView {
|
open func getBottomContainer() -> UIView {
|
||||||
return bottomContainerStackView
|
return bottomContainerStackView
|
||||||
@ -520,13 +520,35 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func updateContainerView() {
|
internal func updateContainerView(flag: Bool) {
|
||||||
containerView.backgroundColor = containerBackgroundColor
|
if flag {
|
||||||
containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor
|
containerView.backgroundColor = containerBackgroundColor
|
||||||
containerView.layer.borderWidth = VDSFormControls.borderWidth
|
containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor
|
||||||
containerView.layer.cornerRadius = VDSFormControls.borderRadius
|
containerView.layer.borderWidth = VDSFormControls.borderWidth
|
||||||
|
containerView.layer.cornerRadius = VDSFormControls.borderRadius
|
||||||
|
} else {
|
||||||
|
containerView.backgroundColor = .clear
|
||||||
|
containerView.layer.borderColor = nil
|
||||||
|
containerView.layer.borderWidth = 0
|
||||||
|
containerView.layer.cornerRadius = 0
|
||||||
|
fieldStackView.backgroundColor = containerBackgroundColor
|
||||||
|
fieldStackView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor
|
||||||
|
fieldStackView.layer.borderWidth = VDSFormControls.borderWidth
|
||||||
|
fieldStackView.layer.cornerRadius = containerView.frame.size.height / 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update constraints to containerStackView which has horizontal stack in which user interacts.
|
||||||
|
internal func updateConstraintsToFieldStackView(flag: Bool) {
|
||||||
|
fieldStackView.removeFromSuperview()
|
||||||
|
containerView.addSubview(fieldStackView)
|
||||||
|
if flag {
|
||||||
|
fieldStackView.pinToSuperView(.uniform(VDSLayout.space3X))
|
||||||
|
} else {
|
||||||
|
fieldStackView.pinTop().pinLeading().pinBottom().pinTrailingLessThanOrEqualTo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal func updateContainerWidth() {
|
internal func updateContainerWidth() {
|
||||||
widthConstraint?.deactivate()
|
widthConstraint?.deactivate()
|
||||||
trailingLessThanEqualsConstraint?.deactivate()
|
trailingLessThanEqualsConstraint?.deactivate()
|
||||||
|
|||||||
@ -287,7 +287,7 @@ open class InputField: EntryFieldBase {
|
|||||||
extension InputField: UITextFieldDelegate {
|
extension InputField: UITextFieldDelegate {
|
||||||
public func textFieldDidBeginEditing(_ textField: UITextField) {
|
public func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||||
fieldType.handler().textFieldDidBeginEditing(self, textField: textField)
|
fieldType.handler().textFieldDidBeginEditing(self, textField: textField)
|
||||||
updateContainerView()
|
updateContainerView(flag: true)
|
||||||
updateErrorLabel()
|
updateErrorLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user