diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index cd08241d..a9804b9b 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -140,6 +140,7 @@ EAB5FEF829393A7200998C17 /* ButtonGroupConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */; }; EAB5FF0129424ACB00998C17 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FF0029424ACB00998C17 /* UIControl.swift */; }; EABFEB642A26473700C4C106 /* NSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFEB632A26473700C4C106 /* NSAttributedString.swift */; }; + EAC58BFD2BE935C300BA39FA /* TitleLockupTextColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58BFC2BE935C300BA39FA /* TitleLockupTextColor.swift */; }; EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC71A1C2A2E155A00E47A9F /* Checkbox.swift */; }; EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC71A1E2A2E173D00E47A9F /* RadioButton.swift */; }; EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */; }; @@ -335,6 +336,7 @@ EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupConstants.swift; sourceTree = ""; }; EAB5FF0029424ACB00998C17 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; EABFEB632A26473700C4C106 /* NSAttributedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSAttributedString.swift; sourceTree = ""; }; + EAC58BFC2BE935C300BA39FA /* TitleLockupTextColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleLockupTextColor.swift; sourceTree = ""; }; EAC71A1C2A2E155A00E47A9F /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; EAC71A1E2A2E173D00E47A9F /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; }; EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupCollectionViewCell.swift; sourceTree = ""; }; @@ -769,12 +771,13 @@ isa = PBXGroup; children = ( EA5E30522950DDA60082B959 /* TitleLockup.swift */, + EAEEECAC2B1FC1A600531FC2 /* TitleLockupChangeLog.txt */, EA985BEF2968A93600F2FF2E /* TitleLockupEyebrowModel.swift */, EA985BED2968A92400F2FF2E /* TitleLockupSubTitleModel.swift */, EA985BEB2968A91200F2FF2E /* TitleLockupTitleModel.swift */, + EAC58BFC2BE935C300BA39FA /* TitleLockupTextColor.swift */, EA985BF12968B5BB00F2FF2E /* TitleLockupTextStyle.swift */, EA513A942A4E1F82002A4DFF /* TitleLockupStyleConfiguration.swift */, - EAEEECAC2B1FC1A600531FC2 /* TitleLockupChangeLog.txt */, ); path = TitleLockup; sourceTree = ""; @@ -1145,6 +1148,7 @@ EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */, 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */, + EAC58BFD2BE935C300BA39FA /* TitleLockupTextColor.swift in Sources */, EAACB89A2B927108006A3869 /* Valuing.swift in Sources */, EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index eb67c255..00e7404f 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -165,11 +165,12 @@ open class InputField: EntryFieldBase { controlStackView.addArrangedSubview(textField) textField.heightAnchor.constraint(equalToConstant: 20).isActive = true + textField.delegate = self textField .textPublisher .sink { [weak self] newText in print("textPublisher newText: \(newText)") - self?.text = newText + self?.process(text: newText) self?.validate() self?.sendActions(for: .valueChanged) }.store(in: &subscribers) @@ -280,6 +281,7 @@ open class InputField: EntryFieldBase { var toolTipModel: Tooltip.TooltipModel? = tooltipModel var isSecureTextEntry = false var rules = [AnyRule]() + var placeholderText: String? if self.isRequired { let rule = RequiredRule() @@ -326,6 +328,7 @@ open class InputField: EntryFieldBase { case .date: minWidth = 114.0 + placeholderText = dateFormat.placeholderText case .securityCode: minWidth = 88.0 @@ -364,6 +367,9 @@ open class InputField: EntryFieldBase { minWidthConstraint?.isActive = true } + //placeholder + textField.placeholder = placeholderText + //tooltip tooltipModel = toolTipModel } @@ -421,6 +427,47 @@ open class InputField: EntryFieldBase { open var hidePasswordButtonText: String = "Hide" { didSet { setNeedsUpdate() } } open var showPasswordButtonText: String = "Show" { didSet { setNeedsUpdate() } } + + //-------------------------------------------------- + // MARK: - Date + //-------------------------------------------------- + open var dateFormat: DateFormat = .mmddyy { didSet { setNeedsUpdate() } } + +} + +extension InputField: UITextFieldDelegate { + public func process(text changedText: String) { + var newText: String = changedText + switch fieldType { + case .date: + guard newText.count <= dateFormat.maxLength else { return } + let numericText = newText.compactMap { $0.isNumber ? $0 : nil } + var formattedText = "" + + for (index, char) in numericText.enumerated() { + if (index == 2 || (index == 4 && (dateFormat != .mmyy))) && index < dateFormat.maxLength { + formattedText += "/" + } + formattedText.append(char) + } + newText = formattedText + + default: break + } + } + + public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + switch fieldType { + case .date: + // Allow only numbers and limit the length of text. + let allowedCharacters = CharacterSet.decimalDigits + let characterSet = CharacterSet(charactersIn: string) + return allowedCharacters.isSuperset(of: characterSet) && ((textField.text?.count ?? 0) + string.count - range.length) <= dateFormat.maxLength + + default: + return true + } + } } extension InputField.FieldType { @@ -442,3 +489,36 @@ extension InputField.FieldType { } } } + +extension InputField { + public enum DateFormat: String, CaseIterable { + case mmyy + case mmddyy + case mmddyyyy + + public var placeholderText: String { + switch self { + case .mmyy: "MM/YY" + case .mmddyy: "MM/DD/YY" + case .mmddyyyy: "MM/DD/YYYY" + } + } + + public var formatString: String { + switch self { + case .mmyy: "MM/yy" + case .mmddyy: "MM/dd/yy" + case .mmddyyyy: "MM/dd/yyyy" + } + } + + public var maxLength: Int { + switch self { + case .mmyy: 5 + case .mmddyy: 8 + case .mmddyyyy: 10 + } + } + + } +} diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index b4b62ab1..08b87a7f 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -69,7 +69,7 @@ open class TileContainerBase: Control where Padding case secondary case white case black - case custom(String) + case custom(UIColor) private var reflectedValue: String { String(reflecting: self) } @@ -81,7 +81,7 @@ open class TileContainerBase: Control where Padding /// Enum used to describe the background effect choices used for this component. public enum BackgroundEffect { case transparency - case gradient(String, String) + case gradient(UIColor, UIColor) case none } @@ -128,7 +128,7 @@ open class TileContainerBase: Control where Padding open var aspectRatio: AspectRatio = .ratio1x1 { didSet { setNeedsUpdate() } } /// Sets the background color for the component. - open var color: BackgroundColor = .secondary { didSet { setNeedsUpdate() } } + open var color: BackgroundColor? { didSet { setNeedsUpdate() } } /// Sets the background effect for the component. open var backgroundEffect: BackgroundEffect = .none { didSet { setNeedsUpdate() } } @@ -188,14 +188,14 @@ open class TileContainerBase: Control where Padding // MARK: - Configuration //-------------------------------------------------- private let cornerRadius = VDSFormControls.borderRadius * 2 - private var backgroundColorConfiguration = BackgroundColorConfiguration() + internal var backgroundColorConfiguration = BackgroundColorConfiguration() private let dropShadowConfiguration = DropShadowConfiguration().with { $0.shadowColorConfiguration = SurfaceColorConfiguration().with { $0.lightColor = VDSColor.elementsPrimaryOnlight }.eraseToAnyColorable() $0.shadowOffsetConfiguration = .init(.init(width: 0, height: 6), .zero) - $0.shadowRadiusConfiguration = .init(3.0, 0.0) - $0.shadowOpacityConfiguration = .init(0.01, 0.0) + $0.shadowRadiusConfiguration = .init(8.0, 0.0) + $0.shadowOpacityConfiguration = .init(0.1, 0.0) } private var borderColorConfiguration = SurfaceColorConfiguration().with { @@ -329,7 +329,7 @@ open class TileContainerBase: Control where Padding } applyBackgroundEffects() - + if showDropShadow, surface == .light { addDropShadow(dropShadowConfiguration) } else { @@ -395,7 +395,7 @@ open class TileContainerBase: Control where Padding removeGradientLayer() case .gradient(let firstColor, let secondColor): alphaConfiguration = 1.0 - addGradientLayer(with: UIColor(hexString: firstColor), secondColor: UIColor(hexString: secondColor)) + addGradientLayer(with: firstColor, secondColor: secondColor) backgroundImageView.isHidden = true backgroundImageView.alpha = 1.0 case .none: @@ -461,17 +461,22 @@ extension TileContainerBase { required init() { } func getColor(_ object: ObjectType) -> UIColor { - switch object.color { + guard let color = object.color else { + let config = object.surface == .light ? blackColorConfig : whiteColorConfig + return config.getColor(object.surface) + } + + switch color { case .primary: - primaryColorConfig.getColor(object.surface) + return primaryColorConfig.getColor(object.surface) case .secondary: - secondaryColorConfig.getColor(object.surface) + return secondaryColorConfig.getColor(object.surface) case .white: - whiteColorConfig.getColor(object.surface) + return whiteColorConfig.getColor(object.surface) case .black: - blackColorConfig.getColor(object.surface) - case .custom(let hexCode): - UIColor(hexString: hexCode) + return blackColorConfig.getColor(object.surface) + case .custom(let color): + return color } } } diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index fe9fe7f1..f0814e11 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -102,6 +102,10 @@ open class Tilelet: TileContainerBase { $0.backgroundColor = .clear } + private var backgroundColorSurface: Surface { + backgroundColorConfiguration.getColor(self).surface + } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @@ -444,7 +448,7 @@ open class Tilelet: TileContainerBase { badge.text = badgeModel.text badge.fillColor = badgeModel.fillColor badge.numberOfLines = badgeModel.numberOfLines - badge.surface = badgeModel.surface + badge.surface = backgroundColorSurface badge.maxWidth = badgeModel.maxWidth badgeLabelHeightGreaterThanConstraint?.constant = badge.label.minimumLineHeight if badgeContainerView.superview == nil { @@ -474,7 +478,7 @@ open class Tilelet: TileContainerBase { if showTitleLockup { //flip the surface for the titleLockup - titleLockup.surface = color == .black ? Surface.dark : Surface.light + titleLockup.surface = backgroundColorSurface //titleLockup if let textWidth { @@ -529,14 +533,14 @@ open class Tilelet: TileContainerBase { if let descriptiveIconModel { descriptiveIcon.name = descriptiveIconModel.name descriptiveIcon.size = descriptiveIconModel.size - descriptiveIcon.surface = descriptiveIconModel.surface + descriptiveIcon.surface = backgroundColorSurface descriptiveIcon.accessibilityLabel = descriptiveIconModel.accessibleText showIconContainerView = true } if let directionalIconModel { directionalIcon.size = directionalIconModel.size - directionalIcon.surface = directionalIconModel.surface + directionalIcon.surface = backgroundColorSurface directionalIcon.accessibilityLabel = "Right arrow" showIconContainerView = true } diff --git a/VDS/Components/Tilelet/TileletSubTitleModel.swift b/VDS/Components/Tilelet/TileletSubTitleModel.swift index fb68ff53..4c48cc3a 100644 --- a/VDS/Components/Tilelet/TileletSubTitleModel.swift +++ b/VDS/Components/Tilelet/TileletSubTitleModel.swift @@ -36,8 +36,8 @@ extension Tilelet { public var textAttributes: [any LabelAttributeModel]? /// Text color that will be used for the subTitle label. - public var textColor: Use = .primary - + public var textColor: TitleLockup.TextColor + /// LineBreakMode used in Badge label. public var lineBreakMode: NSLineBreakMode @@ -46,7 +46,7 @@ extension Tilelet { //-------------------------------------------------- public init(text: String, otherStandardStyle: OtherStandardStyle = .bodySmall, - textColor: Use = .primary, + textColor: TitleLockup.TextColor = .primary, textAttributes: [any LabelAttributeModel]? = nil, lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text @@ -64,7 +64,8 @@ extension Tilelet { TitleLockup.SubTitleModel(text: text, otherStandardStyle: otherStandardStyle.value, textColor: textColor, - textAttributes: textAttributes, lineBreakMode: lineBreakMode) + textAttributes: textAttributes, + lineBreakMode: lineBreakMode) } } } diff --git a/VDS/Components/Tilelet/TileletTitleModel.swift b/VDS/Components/Tilelet/TileletTitleModel.swift index fa8c603d..b9bc7bbe 100644 --- a/VDS/Components/Tilelet/TileletTitleModel.swift +++ b/VDS/Components/Tilelet/TileletTitleModel.swift @@ -32,6 +32,9 @@ extension Tilelet { /// Text that will be used for the title label. public var text: String = "" + /// TextColor that will be used for the title label. + public var textColor: TitleLockup.TitleTextColor + /// Used in combination with standardStyle to set the textStyle that will be used for the title label. public var isBold: Bool = false /// Text attributes that will be used for the title label. @@ -47,11 +50,13 @@ extension Tilelet { // MARK: - Initializers //-------------------------------------------------- public init(text: String, + textColor: TitleLockup.TitleTextColor = .primary, textAttributes: [any LabelAttributeModel]? = nil, isBold: Bool = true, standardStyle: StandardStyle = .titleSmall, lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text + self.textColor = textColor self.textAttributes = textAttributes self.standardStyle = standardStyle self.isBold = isBold @@ -64,8 +69,11 @@ extension Tilelet { /// Converts this type of model to a TitleLockup.TitleModel. public func toTitleLockupTitleModel() -> TitleLockup.TitleModel { TitleLockup.TitleModel(text: text, + textColor: textColor, textAttributes: textAttributes, - isBold: isBold, standardStyle: standardStyle.value, lineBreakMode: lineBreakMode) + isBold: isBold, + standardStyle: standardStyle.value, + lineBreakMode: lineBreakMode) } } } diff --git a/VDS/Components/Tilelet/TiletEyebrowModel.swift b/VDS/Components/Tilelet/TiletEyebrowModel.swift index e7c008b4..3d22ab74 100644 --- a/VDS/Components/Tilelet/TiletEyebrowModel.swift +++ b/VDS/Components/Tilelet/TiletEyebrowModel.swift @@ -18,6 +18,9 @@ extension Tilelet { /// Text that will be used for the eyebrow label. public var text: String = "" + /// Text color that will be used for the eyebrow label + public var textColor: TitleLockup.TextColor + /// Used in combination with standardStyle to set the textStyle that will be used for the eyebrow label. public var isBold: Bool = false /// Text attributes that will be used for the eyebrow label. @@ -33,11 +36,13 @@ extension Tilelet { // MARK: - Initializers //-------------------------------------------------- public init(text: String, + textColor: TitleLockup.TextColor = .primary, textAttributes: [any LabelAttributeModel]? = nil, isBold: Bool = true, standardStyle: Tilelet.SubTitleModel.OtherStandardStyle = .bodySmall, lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text + self.textColor = textColor self.textAttributes = textAttributes self.standardStyle = standardStyle self.isBold = isBold @@ -49,7 +54,11 @@ extension Tilelet { //-------------------------------------------------- /// Converts this type of model to a TitleLockup.TitleModel. public func toTitleLockupEyebrowModel() -> TitleLockup.EyebrowModel { - TitleLockup.EyebrowModel(text: text, isBold: isBold, standardStyle: standardStyle.value, textAttributes: textAttributes) + TitleLockup.EyebrowModel(text: text, + textColor: textColor, + isBold: isBold, + standardStyle: standardStyle.value, + textAttributes: textAttributes) } } } diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index c0c3b0b5..a838c3b9 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -258,22 +258,10 @@ open class TitleLockup: View { bottomSpacing: VDSLayout.space6X) ]), ]) - - private var textColorSecondaryConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight , VDSColor.elementsSecondaryOnlight).eraseToAnyColorable() - - private var textColorPrimaryConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark).eraseToAnyColorable() - + //-------------------------------------------------- // 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() - - titleLabel.textColorConfiguration = textColorPrimaryConfiguration - - } - open override func updateAccessibility() { super.updateAccessibility() var elements = [Any]() @@ -330,6 +318,7 @@ open class TitleLockup: View { if let eyebrowModel, !eyebrowModel.text.isEmpty { eyebrowLabel.textAlignment = allLabelsTextAlignment eyebrowLabel.text = eyebrowModel.text + eyebrowLabel.textColorConfiguration = eyebrowModel.textColor.configuration eyebrowLabel.attributes = eyebrowModel.textAttributes eyebrowLabel.numberOfLines = eyebrowModel.numberOfLines eyebrowLabel.surface = surface @@ -339,15 +328,12 @@ open class TitleLockup: View { //When uniform size is true and the title is bold, //the eyebrow is always regular weight and the secondary color. eyebrowLabel.textStyle = otherStandardStyle.value.regular - eyebrowLabel.textColorConfiguration = textColorSecondaryConfiguration } else { //When uniform size is true and the title is regular weight //the eyebrow is always bold and uses the primary color. eyebrowLabel.textStyle = otherStandardStyle.value.bold - eyebrowLabel.textColorConfiguration = textColorPrimaryConfiguration } } else { - eyebrowLabel.textColorConfiguration = textColorPrimaryConfiguration eyebrowLabel.textStyle = eyebrowModel.isBold ? otherStandardStyle.value.bold : otherStandardStyle.value.regular } addSubview(eyebrowLabel) @@ -363,6 +349,7 @@ open class TitleLockup: View { if let titleModel, !titleModel.text.isEmpty { titleLabel.textAlignment = allLabelsTextAlignment titleLabel.textStyle = titleModel.textStyle + titleLabel.textColorConfiguration = titleModel.textColor.configuration titleLabel.text = titleModel.text titleLabel.attributes = titleModel.textAttributes titleLabel.numberOfLines = titleModel.numberOfLines @@ -381,7 +368,7 @@ open class TitleLockup: View { if let subTitleModel, !subTitleModel.text.isEmpty { subTitleLabel.textAlignment = allLabelsTextAlignment subTitleLabel.textStyle = otherStandardStyle.value.regular - subTitleLabel.textColorConfiguration = subTitleModel.textColor == .secondary ? textColorSecondaryConfiguration : textColorPrimaryConfiguration + subTitleLabel.textColorConfiguration = subTitleModel.textColor.configuration subTitleLabel.text = subTitleModel.text subTitleLabel.attributes = subTitleModel.textAttributes subTitleLabel.numberOfLines = subTitleModel.numberOfLines diff --git a/VDS/Components/TitleLockup/TitleLockupEyebrowModel.swift b/VDS/Components/TitleLockup/TitleLockupEyebrowModel.swift index 28305bfd..1139a6ee 100644 --- a/VDS/Components/TitleLockup/TitleLockupEyebrowModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupEyebrowModel.swift @@ -13,6 +13,9 @@ extension TitleLockup { /// Text that will be used for the eyebrow label. public var text: String + /// Text color that will be used for the eyebrow label + public var textColor: TextColor + /// Used in combination with standardStyle to set the textStyle that will be used for the eyebrow label. public var isBold: Bool @@ -26,11 +29,13 @@ extension TitleLockup { public var numberOfLines: Int public init(text: String, + textColor: TextColor = .primary, isBold: Bool = true, standardStyle: OtherStandardStyle = .bodyLarge, textAttributes: [any LabelAttributeModel]? = nil, numberOfLines: Int = 0) { self.text = text + self.textColor = textColor self.isBold = isBold self.standardStyle = standardStyle self.textAttributes = textAttributes @@ -41,3 +46,4 @@ extension TitleLockup { public var textStyle: TextStyle { isBold ? standardStyle.value.bold : standardStyle.value.regular } } } + diff --git a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift index f558ffb4..7dcbfb85 100644 --- a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift @@ -18,7 +18,7 @@ extension TitleLockup { public var otherStandardStyle: OtherStandardStyle /// Text color used in the subtitle label. - public var textColor: Use + public var textColor: TextColor /// Array of LabelAttributeModel objects used in rendering the text in the subtitle label. public var textAttributes: [any LabelAttributeModel]? @@ -31,7 +31,7 @@ extension TitleLockup { public init(text: String, otherStandardStyle: OtherStandardStyle = .bodyLarge, - textColor: Use = .primary, + textColor: TextColor = .primary, textAttributes: [any LabelAttributeModel]? = nil, numberOfLines: Int = 0, lineBreakMode: NSLineBreakMode = .byWordWrapping) { diff --git a/VDS/Components/TitleLockup/TitleLockupTextColor.swift b/VDS/Components/TitleLockup/TitleLockupTextColor.swift new file mode 100644 index 00000000..c0760b6f --- /dev/null +++ b/VDS/Components/TitleLockup/TitleLockupTextColor.swift @@ -0,0 +1,59 @@ +// +// TitleLockupTextColor.swift +// VDS +// +// Created by Matt Bruce on 5/6/24. +// + +import Foundation +import VDSTokens +import UIKit + +extension TitleLockup { + private static var textColorSecondaryConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight , VDSColor.elementsSecondaryOnlight).eraseToAnyColorable() + + private static var textColorPrimaryConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark).eraseToAnyColorable() + + public enum TextColor: Equatable { + case primary + case secondary + case custom(UIColor, UIColor) + + private var reflectedValue: String { String(reflecting: self) } + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.reflectedValue == rhs.reflectedValue + } + + public var configuration: AnyColorable { + switch self { + case .primary: + TitleLockup.textColorPrimaryConfiguration + case .secondary: + TitleLockup.textColorSecondaryConfiguration + case .custom(let lightColor, let darkColor): + SurfaceColorConfiguration(lightColor, darkColor).eraseToAnyColorable() + } + } + } + + public enum TitleTextColor: Equatable { + case primary + case custom(UIColor, UIColor) + + private var reflectedValue: String { String(reflecting: self) } + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.reflectedValue == rhs.reflectedValue + } + + public var configuration: AnyColorable { + switch self { + case .primary: + TitleLockup.textColorPrimaryConfiguration + case .custom(let lightColor, let darkColor): + SurfaceColorConfiguration(lightColor, darkColor).eraseToAnyColorable() + } + } + } +} diff --git a/VDS/Components/TitleLockup/TitleLockupTitleModel.swift b/VDS/Components/TitleLockup/TitleLockupTitleModel.swift index 7541151f..2d7f7405 100644 --- a/VDS/Components/TitleLockup/TitleLockupTitleModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupTitleModel.swift @@ -14,7 +14,10 @@ extension TitleLockup { /// Text that will be used for the title label. public var text: String - /// Array of LabelAttributeModel objects used in rendering the text. + /// Text color that will be used for the eyebrow label + public var textColor: TitleTextColor + + /// Array of LabelAttributeModel objects used in rendering the text. public var textAttributes: [any LabelAttributeModel]? /// Used in combination with standardStyle to set the textStyle that will be used for the title label. @@ -30,12 +33,14 @@ extension TitleLockup { public var lineBreakMode: NSLineBreakMode public init(text: String, + textColor: TitleTextColor = .primary, textAttributes: [any LabelAttributeModel]? = nil, isBold: Bool = true, standardStyle: TitleStandardStyle = .featureXSmall, numberOfLines: Int = 0, lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text + self.textColor = textColor self.isBold = isBold self.textAttributes = textAttributes self.standardStyle = standardStyle diff --git a/VDS/Extensions/UIColor+VDSColor.swift b/VDS/Extensions/UIColor+VDSColor.swift index c622db33..9b165f64 100644 --- a/VDS/Extensions/UIColor+VDSColor.swift +++ b/VDS/Extensions/UIColor+VDSColor.swift @@ -176,4 +176,10 @@ extension UIColor { guard let found else { return nil} return found } + + public var surface: Surface { + var greyScale: CGFloat = 0 + getWhite(&greyScale, alpha: nil) + return greyScale < 0.5 ? .dark : .light + } }