From 05638422f61bf18fe7a2365d8029c573db7ecc24 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 28 Feb 2024 13:04:11 -0600 Subject: [PATCH 01/33] updated labels to ensure string is valid Signed-off-by: Matt Bruce --- VDS/Components/Label/Attributes/AnyLabelAttribute.swift | 1 + VDS/Components/Label/Attributes/ColorLabelAttribute.swift | 2 ++ VDS/Components/Label/Attributes/LabelAttributeModel.swift | 4 ++++ .../Label/Attributes/StrikeThroughLabelAttribute.swift | 1 + VDS/Components/Label/Attributes/TextStyleLabelAttribute.swift | 1 + VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift | 3 ++- 6 files changed, 11 insertions(+), 1 deletion(-) diff --git a/VDS/Components/Label/Attributes/AnyLabelAttribute.swift b/VDS/Components/Label/Attributes/AnyLabelAttribute.swift index b914c912..50e76ba7 100644 --- a/VDS/Components/Label/Attributes/AnyLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/AnyLabelAttribute.swift @@ -30,6 +30,7 @@ public struct AnyAttribute: LabelAttributeModel { } public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } attributedString.removeAttribute(key, range: range) attributedString.addAttribute(key, value: value, range: range) } diff --git a/VDS/Components/Label/Attributes/ColorLabelAttribute.swift b/VDS/Components/Label/Attributes/ColorLabelAttribute.swift index 354fbc93..50229ab0 100644 --- a/VDS/Components/Label/Attributes/ColorLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/ColorLabelAttribute.swift @@ -31,6 +31,8 @@ public struct ColorLabelAttribute: LabelAttributeModel { } public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } + var colorRange = range if length == 0 && location == 0 { colorRange = .init(location: location, length: attributedString.length) diff --git a/VDS/Components/Label/Attributes/LabelAttributeModel.swift b/VDS/Components/Label/Attributes/LabelAttributeModel.swift index 62e1bbd8..87708ffb 100644 --- a/VDS/Components/Label/Attributes/LabelAttributeModel.swift +++ b/VDS/Components/Label/Attributes/LabelAttributeModel.swift @@ -29,6 +29,10 @@ extension LabelAttributeModel { public static func == (lhs: any LabelAttributeModel, rhs: any LabelAttributeModel) -> Bool { lhs.isEqual(rhs) } + + public func isValidRange(on attributedString: NSMutableAttributedString) -> Bool { + true//range.location + range.length <= attributedString.string.count + } } public extension NSAttributedString { diff --git a/VDS/Components/Label/Attributes/StrikeThroughLabelAttribute.swift b/VDS/Components/Label/Attributes/StrikeThroughLabelAttribute.swift index 93650f2b..ba4c97ac 100644 --- a/VDS/Components/Label/Attributes/StrikeThroughLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/StrikeThroughLabelAttribute.swift @@ -24,6 +24,7 @@ public struct StrikeThroughLabelAttribute: LabelAttributeModel { } public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) attributedString.addAttribute(.baselineOffset, value: 0, range: range) } diff --git a/VDS/Components/Label/Attributes/TextStyleLabelAttribute.swift b/VDS/Components/Label/Attributes/TextStyleLabelAttribute.swift index ae3ea1e5..a161d580 100644 --- a/VDS/Components/Label/Attributes/TextStyleLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/TextStyleLabelAttribute.swift @@ -44,6 +44,7 @@ public struct TextStyleLabelAttribute: LabelAttributeModel { } public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } attributedString.removeAttribute(.font, range: range) attributedString.addAttribute(.font, value: textStyle.font, range: range) if let textColor { diff --git a/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift b/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift index 0ca4e2f9..a2ad403e 100644 --- a/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift @@ -52,7 +52,8 @@ public struct UnderlineLabelAttribute: LabelAttributeModel { //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- - public func setAttribute(on attributedString: NSMutableAttributedString) { + public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } attributedString.addAttribute(.underlineStyle, value: underlineValue.rawValue, range: range) if let color = color { attributedString.addAttribute(.underlineColor, value: color, range: range) From 82a8a32ccbd1f79c6144b83d7a6bc2a328917dc9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 10:14:42 -0600 Subject: [PATCH 02/33] refactored into base class for dealing with TileContainer Padding --- .../TileContainer/TileContainer.swift | 75 +++++++++++-------- VDS/Components/Tilelet/Tilelet.swift | 52 ++++++++----- 2 files changed, 75 insertions(+), 52 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 98090ae4..0484e815 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -10,8 +10,45 @@ import VDSColorTokens import VDSFormControlsTokens import UIKit +public protocol Valuing { + static var defaultValue: Self { get } + var value: CGFloat { get } +} + @objc(VDSTileContainer) -open class TileContainer: Control { +open class TileContainer: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: Valuing { + case padding2X + case padding4X + case padding6X + case padding8X + case padding12X + case custom(CGFloat) + + public static var defaultValue: Self { .padding4X } + + public var value: CGFloat { + switch self { + case .padding2X: + return VDSLayout.Spacing.space2X.value + case .padding4X: + return VDSLayout.Spacing.space4X.value + case .padding6X: + return VDSLayout.Spacing.space6X.value + case .padding8X: + return VDSLayout.Spacing.space8X.value + case .padding12X: + return VDSLayout.Spacing.space12X.value + case .custom(let padding): + return padding + } + } + } +} + +open class TileContainerBase: Control { //-------------------------------------------------- // MARK: - Initializers @@ -53,33 +90,6 @@ open class TileContainer: Control { case gradient(String, String) case none } - - /// Enum used to describe the padding choices used for this component. - public enum Padding { - case padding2X - case padding4X - case padding6X - case padding8X - case padding12X - case custom(CGFloat) - - public var value: CGFloat { - switch self { - case .padding2X: - return VDSLayout.Spacing.space2X.value - case .padding4X: - return VDSLayout.Spacing.space4X.value - case .padding6X: - return VDSLayout.Spacing.space6X.value - case .padding8X: - return VDSLayout.Spacing.space8X.value - case .padding12X: - return VDSLayout.Spacing.space12X.value - case .custom(let padding): - return padding - } - } - } /// Enum used to describe the aspect ratios used for this component. public enum AspectRatio: String, CaseIterable { @@ -130,7 +140,7 @@ open class TileContainer: Control { open var backgroundEffect: BackgroundEffect = .none { didSet { setNeedsUpdate() } } /// Sets the inside padding for the component - open var padding: Padding = .padding4X { didSet { setNeedsUpdate() } } + open var padding: PaddingType = PaddingType.defaultValue { didSet { setNeedsUpdate() } } /// Applies a background color if backgroundImage prop fails or has trouble loading. open var imageFallbackColor: Surface = .light { didSet { setNeedsUpdate() } } @@ -253,7 +263,6 @@ open class TileContainer: Control { super.reset() shouldUpdateView = false color = .white - padding = .padding4X aspectRatio = .ratio1x1 imageFallbackColor = .light width = nil @@ -395,7 +404,7 @@ open class TileContainer: Control { } -extension TileContainer { +extension TileContainerBase { struct DropshadowConfiguration: Dropshadowable { var shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().with { @@ -408,7 +417,7 @@ extension TileContainer { final class BackgroundColorConfiguration: ObjectColorable { - typealias ObjectType = TileContainer + typealias ObjectType = TileContainerBase let primaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark) let secondaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundSecondaryLight, VDSColor.backgroundSecondaryDark) @@ -418,7 +427,7 @@ extension TileContainer { required init() { } - func getColor(_ object: TileContainer) -> UIColor { + func getColor(_ object: ObjectType) -> UIColor { switch object.color { case .primary: primaryColorConfig.getColor(object.surface) diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 657a735b..3d5fe495 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -16,7 +16,38 @@ import Combine /// while it can include an arrow CTA, it does not require one in order to /// function. @objc(VDSTilelet) -open class Tilelet: TileContainer { +open class Tilelet: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: String, Valuing, CaseIterable { + case small + case large + + public static var defaultValue: Self { .large } + + public var value: CGFloat { + switch self { + case .small: + return UIDevice.isIPad ? VDSLayout.Spacing.space3X.value : VDSLayout.Spacing.space4X.value + case .large: + return UIDevice.isIPad ? VDSLayout.Spacing.space4X.value : VDSLayout.Spacing.space6X.value + } + } + + fileprivate var titleLockupBottomSpacing: CGFloat { + switch self.value { + case VDSLayout.Spacing.space3X.value: + return VDSLayout.Spacing.space4X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space6X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space8X.value + default: + return VDSLayout.Spacing.space4X.value + } + } + } + //-------------------------------------------------- // MARK: - Initializers @@ -392,7 +423,7 @@ open class Tilelet: TileContainer { view = titleLockupContainerView } if let view { - stackView.setCustomSpacing(padding.tiletSpacing, after: view) + stackView.setCustomSpacing(padding.titleLockupBottomSpacing, after: view) } if iconContainerView.superview == nil { stackView.addArrangedSubview(iconContainerView) @@ -403,20 +434,3 @@ open class Tilelet: TileContainer { } } } - -extension TileContainer.Padding { - fileprivate var tiletSpacing: CGFloat { - switch self { - case .padding2X: - return 16 - case .padding4X: - return 24 - case .padding6X: - return 32 - case .padding8X: - return 48 - default: - return 16 - } - } -} From 69a23745040dfb10fe755144124a6905faa455ea Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 10:34:10 -0600 Subject: [PATCH 03/33] First cut at form fiedable change Signed-off-by: Matt Bruce --- .../Attributes/LabelAttributeModel.swift | 2 +- .../TextFields/EntryFieldBase.swift | 4 +- .../TextFields/InputField/InputField.swift | 4 +- .../TextFields/TextArea/TextArea.swift | 43 +++++++++++--- VDS/Protocols/FormFieldable.swift | 58 ++++++++++++++++++- 5 files changed, 97 insertions(+), 14 deletions(-) diff --git a/VDS/Components/Label/Attributes/LabelAttributeModel.swift b/VDS/Components/Label/Attributes/LabelAttributeModel.swift index 87708ffb..ac43472f 100644 --- a/VDS/Components/Label/Attributes/LabelAttributeModel.swift +++ b/VDS/Components/Label/Attributes/LabelAttributeModel.swift @@ -31,7 +31,7 @@ extension LabelAttributeModel { } public func isValidRange(on attributedString: NSMutableAttributedString) -> Bool { - true//range.location + range.length <= attributedString.string.count + range.location + range.length <= attributedString.string.count } } diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index e66d31a5..3b738549 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -13,7 +13,7 @@ import Combine /// Base Class used to build out a Input controls. @objc(VDSEntryField) -open class EntryFieldBase: Control, Changeable { +open class EntryFieldBase: Control, Changeable, FormFieldable { //-------------------------------------------------- // MARK: - Initializers @@ -196,7 +196,7 @@ open class EntryFieldBase: Control, Changeable { open var inputId: String? { didSet { setNeedsUpdate() } } - open var value: AnyHashable? { didSet { 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 589ba28f..7a950a80 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -79,13 +79,13 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { open var fieldType: FieldType = .text { didSet { setNeedsUpdate() } } /// The text of this textField. - open override var text: String? { + open override var value: String? { get { textField.text } set { textField.text = newValue } } - + var _showError: Bool = false /// Whether not to show the error. open override var showError: Bool { diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 92f5525a..4b99eeb3 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -107,14 +107,15 @@ open class TextArea: EntryFieldBase { } } - /// The text of this textField. - open override var text: String? { + open override var value: String? { get { textView.text } set { textView.text = newValue + setNeedsUpdate() + } } - + /// UITextView shown in the TextArea. open var textView = TextView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -184,7 +185,7 @@ open class TextArea: EntryFieldBase { /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() - + countRule.maxLength = maxLength textView.isEditable = isEnabled textView.isEnabled = isEnabled textView.surface = surface @@ -208,6 +209,9 @@ open class TextArea: EntryFieldBase { } else { characterCounterLabel.text = "" } + + showError = !validator.validate() + errorText = validator.errorMessage icon.size = .medium icon.color = iconColorConfiguration.getColor(self) @@ -246,12 +250,8 @@ open class TextArea: EntryFieldBase { let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)" if ((maxLength ?? 0) > 0) { if (count > (maxLength ?? 0)) { - showError = true - errorText = "You have exceeded the character limit." return countStr } else { - showError = false - errorText = "" return ("\(countStr)" + "/" + "\(maxLength ?? 0)") } } else { @@ -274,6 +274,33 @@ open class TextArea: EntryFieldBase { textView.textAttributes = textAttributes } + + //-------------------------------------------------- + // MARK: - Validation + //-------------------------------------------------- + var countRule = CharacterCountRule() + lazy var validator: FieldValidator