From aa30561b298ccfc36bb792702a7bc3e2581c2a2f Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 16 Feb 2024 09:33:39 -0600 Subject: [PATCH 01/34] refactored naming conventions Signed-off-by: Matt Bruce --- VDS/Components/Tilelet/TileletSubTitleModel.swift | 12 ++++++------ VDS/Components/TitleLockup/TitleLockup.swift | 2 +- .../TitleLockup/TitleLockupSubTitleModel.swift | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/VDS/Components/Tilelet/TileletSubTitleModel.swift b/VDS/Components/Tilelet/TileletSubTitleModel.swift index dab4c81c..ae28d958 100644 --- a/VDS/Components/Tilelet/TileletSubTitleModel.swift +++ b/VDS/Components/Tilelet/TileletSubTitleModel.swift @@ -14,7 +14,7 @@ extension Tilelet { // MARK: - Enums //-------------------------------------------------- /// Enum used to describe the textStyle of the subTitle label. - public enum StandardStyle: String, EnumSubset { + public enum OtherStandardStyle: String, EnumSubset { case bodyLarge case bodyMedium case bodySmall @@ -28,7 +28,7 @@ extension Tilelet { public var text: String = "" /// Text style that will be used for the subTitle label. - public var standardStyle: StandardStyle = .bodySmall + public var otherStandardStyle: OtherStandardStyle = .bodySmall /// Text attributes that will be used for the subTitle label. public var textAttributes: [any LabelAttributeModel]? @@ -40,13 +40,13 @@ extension Tilelet { // MARK: - Initializers //-------------------------------------------------- public init(text: String, + otherStandardStyle: OtherStandardStyle = .bodySmall, textColor: Use = .primary, - textAttributes: [any LabelAttributeModel]? = nil, - standardStyle: StandardStyle = .bodySmall) { + textAttributes: [any LabelAttributeModel]? = nil) { self.text = text + self.otherStandardStyle = otherStandardStyle self.textAttributes = textAttributes self.textColor = textColor - self.standardStyle = standardStyle } //-------------------------------------------------- @@ -55,7 +55,7 @@ extension Tilelet { /// Converts this type of model to a TitleLockup.SubTitleModel. public func toTitleLockupSubTitleModel() -> TitleLockup.SubTitleModel { TitleLockup.SubTitleModel(text: text, - standardStyle: standardStyle.value, + otherStandardStyle: otherStandardStyle.value, textColor: textColor, textAttributes: textAttributes) } diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index dbfd8637..e11b674d 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -45,7 +45,7 @@ open class TitleLockup: View { //-------------------------------------------------- private var otherStandardStyle: OtherStandardStyle { if let subTitleModel, !subTitleModel.text.isEmpty { - return subTitleModel.standardStyle + return subTitleModel.otherStandardStyle } else if let eyebrowModel, !eyebrowModel.text.isEmpty { return eyebrowModel.standardStyle } else { diff --git a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift index 3ba6c7ff..4ac0922c 100644 --- a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift @@ -14,7 +14,7 @@ extension TitleLockup { public var text: String /// Standard style that will be used for the subTitle label. - public var standardStyle: OtherStandardStyle + public var otherStandardStyle: OtherStandardStyle /// Text color used in the subtitle label. public var textColor: Use @@ -26,19 +26,19 @@ extension TitleLockup { public var numberOfLines: Int public init(text: String, - standardStyle: OtherStandardStyle = .bodyLarge, + otherStandardStyle: OtherStandardStyle = .bodyLarge, textColor: Use = .primary, textAttributes: [any LabelAttributeModel]? = nil, numberOfLines: Int = 0) { self.text = text - self.standardStyle = standardStyle + self.otherStandardStyle = otherStandardStyle self.textColor = textColor self.textAttributes = textAttributes self.numberOfLines = numberOfLines } /// TextStyle used to render the text. - public var textStyle: TextStyle { standardStyle.value.regular } + public var textStyle: TextStyle { otherStandardStyle.value.regular } } From febbd282cefd3528241c27281ad3b72a0bc9c5dc Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Thu, 22 Feb 2024 22:53:19 +0530 Subject: [PATCH 02/34] Updated DropShadowable to multiple configs --- VDS.xcodeproj/project.pbxproj | 8 +- .../Icon/ButtonIcon/ButtonIcon.swift | 75 +++++++----- .../TileContainer/TileContainer.swift | 21 ++-- VDS/Protocols/DropShadowable.swift | 113 ++++++++++++++++++ VDS/Protocols/Dropshadowable.swift | 64 ---------- 5 files changed, 174 insertions(+), 107 deletions(-) create mode 100644 VDS/Protocols/DropShadowable.swift delete mode 100644 VDS/Protocols/Dropshadowable.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index b13f7df5..c7a20041 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -15,7 +15,7 @@ 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; }; 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; 7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; }; - 71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */; }; + 71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */; }; 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; }; EA0B18022A9E236900F2D0CD /* SelectorGroupBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */; }; EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */; }; @@ -181,7 +181,7 @@ 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = ""; }; 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = ""; }; - 71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dropshadowable.swift; sourceTree = ""; }; + 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowable.swift; sourceTree = ""; }; 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = ""; }; EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorGroupBase.swift; sourceTree = ""; }; EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = ""; }; @@ -560,7 +560,7 @@ EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */, EAB1D2CC28ABE76000DAE764 /* Withable.swift */, 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */, - 71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */, + 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */, ); path = Protocols; sourceTree = ""; @@ -999,7 +999,7 @@ EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */, EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */, EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */, - 71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */, + 71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */, EA0D1C452A6AD73000E5C127 /* RawRepresentable.swift in Sources */, EA985C23296E033A00F2FF2E /* TextArea.swift in Sources */, EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */, diff --git a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift index 16f0416f..97bbbb77 100644 --- a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift +++ b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift @@ -232,19 +232,26 @@ open class ButtonIcon: Control, Changeable, FormFieldable { }() } - private struct LowContrastColorFillFloatingConfiguration: Configuration, Dropshadowable { + private struct LowContrastColorFillFloatingConfiguration: Configuration, DropShadowableConfiguration { var kind: Kind = .lowContrast var surfaceType: SurfaceType = .colorFill var floating: Bool = true var backgroundColorConfiguration: AnyColorable = { SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteGray20).eraseToAnyColorable() }() - var shadowColorConfiguration: AnyColorable = { - SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() - }() - var shadowOpacity: CGFloat = 0.16 - var shadowOffset: CGSize = .init(width: 0, height: 2) - var shadowRadius: CGFloat = 4 + private let dropshadow1Configuration = DropShadowConfiguration().with { + $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() + $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.12), CGFloat(0.22)) + $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1)) + $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(10), CGFloat(12)) + } + private let dropshadow2Configuration = DropShadowConfiguration().with { + $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() + $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.15), CGFloat(0.22)) + $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2)) + $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(4), CGFloat(6)) + } + var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] } } private struct LowContrastMediaConfiguration: Configuration, Borderable { @@ -260,19 +267,26 @@ open class ButtonIcon: Control, Changeable, FormFieldable { }() } - private struct LowContrastMediaFloatingConfiguration: Configuration, Dropshadowable { + private struct LowContrastMediaFloatingConfiguration: Configuration, DropShadowableConfiguration { var kind: Kind = .lowContrast var surfaceType: SurfaceType = .media var floating: Bool = true var backgroundColorConfiguration: AnyColorable = { SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteGray20).eraseToAnyColorable() }() - var shadowColorConfiguration: AnyColorable = { - SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() - }() - var shadowOpacity: CGFloat = 0.16 - var shadowOffset: CGSize = .init(width: 0, height: 2) - var shadowRadius: CGFloat = 4 + private let dropshadow1Configuration = DropShadowConfiguration().with { + $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() + $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.12), CGFloat(0.22)) + $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1)) + $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(10), CGFloat(12)) + } + private let dropshadow2Configuration = DropShadowConfiguration().with { + $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() + $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.05), CGFloat(0.15)) + $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2)) + $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(4), CGFloat(6)) + } + var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] } } private struct HighContrastConfiguration: Configuration { @@ -291,7 +305,7 @@ open class ButtonIcon: Control, Changeable, FormFieldable { }() } - private struct HighContrastFloatingConfiguration: Configuration, Dropshadowable { + private struct HighContrastFloatingConfiguration: Configuration, DropShadowableConfiguration { var kind: Kind = .highContrast var surfaceType: SurfaceType = .colorFill var floating: Bool = true @@ -305,12 +319,19 @@ open class ButtonIcon: Control, Changeable, FormFieldable { $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled]) }.eraseToAnyColorable() }() - var shadowColorConfiguration: AnyColorable = { - SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() - }() - var shadowOpacity: CGFloat = 0.16 - var shadowOffset: CGSize = .init(width: 0, height: 2) - var shadowRadius: CGFloat = 6 + private let dropshadow1Configuration = DropShadowConfiguration().with { + $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() + $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.22), CGFloat(0.12)) + $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1)) + $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(12), CGFloat(10)) + } + private let dropshadow2Configuration = DropShadowConfiguration().with { + $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() + $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.15), CGFloat(0.05)) + $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2)) + $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(6), CGFloat(4)) + } + var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] } } private var badgeIndicatorDefaultSize: CGSize = .zero @@ -452,12 +473,6 @@ open class ButtonIcon: Control, Changeable, FormFieldable { layer.borderColor = nil layer.borderWidth = 0 } - - if let dropshadowable = currentConfig as? Dropshadowable { - addDropShadow(dropshadowable) - } else { - removeDropShadows() - } badgeIndicatorCenterXConstraint?.constant = badgeIndicatorOffset.x + badgeIndicatorDefaultSize.width/2 badgeIndicatorCenterYConstraint?.constant = badgeIndicatorOffset.y + badgeIndicatorDefaultSize.height/2 @@ -467,6 +482,12 @@ open class ButtonIcon: Control, Changeable, FormFieldable { if showBadgeIndicator { updateExpandDirectionalConstraints() } + + if let configurations = (currentConfig as? DropShadowableConfiguration)?.configurations { + addDropShadows(configurations) + } else { + removeDropShadows() + } } //-------------------------------------------------- diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 98090ae4..8dc2d9dd 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -184,9 +184,15 @@ open class TileContainer: Control { // MARK: - Configuration //-------------------------------------------------- private let cornerRadius = VDSFormControls.borderradius * 2 - private var backgroundColorConfiguration = BackgroundColorConfiguration() - private var dropshadowConfiguration = DropshadowConfiguration() + 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) + } private var borderColorConfiguration = SurfaceColorConfiguration().with { $0.lightColor = VDSColor.elementsLowcontrastOnlight @@ -311,7 +317,7 @@ open class TileContainer: Control { heightConstraint?.isActive = false } if showDropShadows, surface == .light { - addDropShadow(dropshadowConfiguration) + addDropShadow(dropShadowConfiguration) } else { removeDropShadows() } @@ -397,15 +403,6 @@ open class TileContainer: Control { extension TileContainer { - struct DropshadowConfiguration: Dropshadowable { - var shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().with { - $0.lightColor = VDSColor.elementsPrimaryOnlight - }.eraseToAnyColorable() - var shadowOpacity: CGFloat = 0.01 - var shadowOffset: CGSize = .init(width: 0, height: 6) - var shadowRadius: CGFloat = 3 - } - final class BackgroundColorConfiguration: ObjectColorable { typealias ObjectType = TileContainer diff --git a/VDS/Protocols/DropShadowable.swift b/VDS/Protocols/DropShadowable.swift new file mode 100644 index 00000000..57c44036 --- /dev/null +++ b/VDS/Protocols/DropShadowable.swift @@ -0,0 +1,113 @@ +// +// DropShadowable.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 16/02/24. +// + +import Foundation +import UIKit + +protocol DropShadowable { + + var shadowColorConfiguration: AnyColorable { get set } + var shadowOpacityConfiguration: AnyConfigurationValue { get set } + var shadowOffsetConfiguration: AnyConfigurationValue { get set } + var shadowRadiusConfiguration: AnyConfigurationValue { get set } +} + +protocol DropShadowableConfiguration { + + var configurations: [DropShadowable] { get } +} + +final class DropShadowConfiguration: DropShadowable, ObjectWithable { + + typealias CGFloatConfigurationValue = AnyConfigurationValue + typealias CGSizeConfigurationValue = AnyConfigurationValue + + var shadowColorConfiguration: AnyColorable + var shadowOpacityConfiguration: CGFloatConfigurationValue + var shadowOffsetConfiguration: CGSizeConfigurationValue + var shadowRadiusConfiguration: CGFloatConfigurationValue + + init(shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().eraseToAnyColorable(), shadowOpacity: CGFloatConfigurationValue = CGFloatConfigurationValue(1.0, 1.0), shadowOffset: CGSizeConfigurationValue = CGSizeConfigurationValue(.zero, .zero), shadowRadius: CGFloatConfigurationValue = CGFloatConfigurationValue(1.0, 1.0)) { + self.shadowColorConfiguration = shadowColorConfiguration + self.shadowOpacityConfiguration = shadowOpacity + self.shadowOffsetConfiguration = shadowOffset + self.shadowRadiusConfiguration = shadowRadius + } +} + +extension ViewProtocol where Self: UIView { + + func addDropShadow(_ config: DropShadowable) { + addDropShadows([config]) + } + + func addDropShadows(_ configs: [DropShadowable]) { + removeDropShadows() + layer.backgroundColor = backgroundColor?.cgColor + layer.masksToBounds = false + for config in configs { + let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.cornerRadius) + let shadowLayer = CALayer() + shadowLayer.shadowPath = shadowPath.cgPath + shadowLayer.frame = bounds + shadowLayer.position = .init(x: bounds.midX, y: bounds.midY) + shadowLayer.backgroundColor = backgroundColor?.cgColor + shadowLayer.cornerRadius = layer.cornerRadius + shadowLayer.shadowColor = config.shadowColorConfiguration.getColor(self).cgColor + shadowLayer.shadowOpacity = Float(config.shadowOpacityConfiguration.getValue(self)) + shadowLayer.shadowOffset = config.shadowOffsetConfiguration.getValue(self) + shadowLayer.shadowRadius = config.shadowRadiusConfiguration.getValue(self) + shadowLayer.name = "dropShadowLayer" + shadowLayer.shouldRasterize = true + shadowLayer.rasterizationScale = UIScreen.main.scale + layer.insertSublayer(shadowLayer, at: 0) + } + } + + func removeDropShadows() { + layer.sublayers?.removeAll { $0.name == "dropShadowLayer" } + } + + func addGradientLayer(with firstColor: UIColor, secondColor: UIColor) { + removeGradientLayer() + let gradientLayer = CAGradientLayer() + gradientLayer.frame = bounds + gradientLayer.startPoint = CGPoint(x: 0, y: 1) + gradientLayer.endPoint = CGPoint(x: 1, y: 0) + gradientLayer.position = center + gradientLayer.shouldRasterize = true + gradientLayer.backgroundColor = UIColor.clear.cgColor + gradientLayer.rasterizationScale = UIScreen.main.scale + gradientLayer.cornerRadius = layer.cornerRadius + gradientLayer.colors = [firstColor.cgColor, secondColor.cgColor] + gradientLayer.name = "gradientLayer" + layer.insertSublayer(gradientLayer, at: 0) + } + + func removeGradientLayer() { + layer.sublayers?.removeAll { $0.name == "gradientLayer" } + } +} + +final class AnyConfigurationValue { + + var lightValue: ValueType + var darkValue: ValueType + + public init(_ lightValue: ValueType = ValueType.self, _ darkValue: ValueType = ValueType.self) { + self.lightValue = lightValue + self.darkValue = darkValue + } + + public func getValue(_ object: Any) -> ValueType { + guard let surfaceable = object as? Surfaceable else { + assertionFailure("Self doesn't confirms to Surfaceable") + return lightValue + } + return surfaceable.surface == .light ? lightValue : darkValue + } +} diff --git a/VDS/Protocols/Dropshadowable.swift b/VDS/Protocols/Dropshadowable.swift deleted file mode 100644 index 77f1a475..00000000 --- a/VDS/Protocols/Dropshadowable.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// Dropshadowable.swift -// VDS -// -// Created by Bandaru, Krishna Kishore on 16/02/24. -// - -import Foundation -import UIKit - -protocol Dropshadowable { - - var shadowColorConfiguration: AnyColorable { get set } - var shadowOpacity: CGFloat { get set } - var shadowOffset: CGSize { get set } - var shadowRadius: CGFloat { get set } -} - -extension ViewProtocol where Self: UIView { - - func addDropShadow(_ config: Dropshadowable) { - removeDropShadows() - layer.backgroundColor = backgroundColor?.cgColor - layer.masksToBounds = false - let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.cornerRadius) - let shadowLayer = CALayer() - shadowLayer.shadowPath = shadowPath.cgPath - shadowLayer.frame = bounds - shadowLayer.position = center - shadowLayer.backgroundColor = UIColor.clear.cgColor - shadowLayer.cornerRadius = layer.cornerRadius - shadowLayer.shadowColor = config.shadowColorConfiguration.getColor(self).cgColor - shadowLayer.shadowOpacity = Float(config.shadowOpacity) - shadowLayer.shadowOffset = .init(width: config.shadowOffset.width, height: config.shadowOffset.height) - shadowLayer.shadowRadius = config.shadowRadius - shadowLayer.name = "dropShadowLayer" - shadowLayer.shouldRasterize = true - shadowLayer.rasterizationScale = UIScreen.main.scale - layer.insertSublayer(shadowLayer, at: 0) - } - - func removeDropShadows() { - layer.sublayers?.removeAll { $0.name == "dropShadowLayer" } - } - - func addGradientLayer(with firstColor: UIColor, secondColor: UIColor) { - removeGradientLayer() - let gradientLayer = CAGradientLayer() - gradientLayer.frame = bounds - gradientLayer.startPoint = CGPoint(x: 0, y: 1) - gradientLayer.endPoint = CGPoint(x: 1, y: 0) - gradientLayer.position = center - gradientLayer.shouldRasterize = true - gradientLayer.rasterizationScale = UIScreen.main.scale - gradientLayer.cornerRadius = layer.cornerRadius - gradientLayer.colors = [firstColor.cgColor, secondColor.cgColor] - gradientLayer.name = "gradientLayer" - layer.insertSublayer(gradientLayer, at: 0) - } - - func removeGradientLayer() { - layer.sublayers?.removeAll { $0.name == "gradientLayer" } - } -} From 9883bd6214df931069fbe5a090bd4fbff461de71 Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 22 Feb 2024 23:55:46 +0530 Subject: [PATCH 03/34] Digital ACT191 story ONEAPP-6682 Flex Height as per Layout and spacing --- .../TextFields/TextArea/TextArea.swift | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 7d70cb44..4494f1b7 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -5,7 +5,6 @@ // Created by Matt Bruce on 1/10/23. // -import Foundation import Foundation import UIKit import VDSColorTokens @@ -49,7 +48,7 @@ open class TextArea: EntryFieldBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - override var containerSize: CGSize { CGSize(width: 45, height: 88) } + override var containerSize: CGSize { CGSize(width: 182, height: 88) } /// UITextView shown in the TextArea. open var textView = UITextView().with { @@ -72,7 +71,7 @@ open class TextArea: EntryFieldBase { open override func setup() { super.setup() - minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 0) + minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: containerSize.width) minWidthConstraint?.isActive = true controlContainerView.addSubview(textView) @@ -81,8 +80,8 @@ open class TextArea: EntryFieldBase { .pinLeading() .pinTrailingLessThanOrEqualTo(nil, 0, .defaultHigh) .pinBottom(0, .defaultHigh) - - textViewHeightConstraint = textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 64) + textView.isScrollEnabled = true + textViewHeightConstraint = textView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height) textViewHeightConstraint?.isActive = true backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: .success) borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success) @@ -132,17 +131,15 @@ extension TextArea: UITextViewDelegate { //if you want it to work "as-is" delete this code //since it will autogrow with the current settings if let textViewHeightConstraint, textView.isEditable { - let height = textView.frame.size.height - let constraintHeight = textViewHeightConstraint.constant - if height > constraintHeight { - if height > 64 && height < 152 { - textViewHeightConstraint.constant = 152 - } else if height > 152 { - textViewHeightConstraint.constant = 328 + let height = textView.contentSize.height + if height > 88 && height < 176 { + textViewHeightConstraint.constant = 176 + } else if height > 176 { + textViewHeightConstraint.constant = 352 } else { - textViewHeightConstraint.constant = 64 + textViewHeightConstraint.constant = 88 } - } + textViewHeightConstraint.isActive = true } //setting the value and firing control event From 783143717c71d219d725deeef868ea40312a03fd Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 23 Feb 2024 12:47:47 +0530 Subject: [PATCH 04/34] Digital ACT191 story ONEAPP-6682 adding character count limit to container --- .../TextFields/EntryFieldBase.swift | 35 ++++++++++++++++-- .../TextFields/TextArea/TextArea.swift | 37 ++++++++++++++++++- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index aaf03256..31529f61 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -70,6 +70,20 @@ open class EntryFieldBase: Control, Changeable { } }() + internal var bottomContainerView: UIView = { + return UIView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + } + }() + + internal var bottomContainerStackView: UIStackView = { + return UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .vertical + $0.distribution = .fill + } + }() + //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- @@ -204,14 +218,24 @@ open class EntryFieldBase: Control, Changeable { containerStackView.addArrangedSubview(controlContainerView) containerStackView.addArrangedSubview(icon) + //get the container this is what show helper text, error text + //can include other for character count, max length + let bottomContainer = getBottomContainer() + + //add bottomContainerStackView + //this is the vertical stack that contains error text, helper text + bottomContainer.addSubview(bottomContainerStackView) + bottomContainerStackView.pinToSuperView() + bottomContainerStackView.addArrangedSubview(errorLabel) + bottomContainerStackView.addArrangedSubview(helperLabel) + stackView.addArrangedSubview(titleLabel) stackView.addArrangedSubview(container) - stackView.addArrangedSubview(errorLabel) - stackView.addArrangedSubview(helperLabel) + stackView.addArrangedSubview(bottomContainer) stackView.setCustomSpacing(4, after: titleLabel) stackView.setCustomSpacing(8, after: container) - stackView.setCustomSpacing(8, after: errorLabel) + stackView.setCustomSpacing(8, after: bottomContainer) stackView .pinTop() @@ -273,6 +297,11 @@ open class EntryFieldBase: Control, Changeable { open func getContainer() -> UIView { return containerView } + + /// Container for the area in which helper or error text presents. + open func getBottomContainer() -> UIView { + return bottomContainerView + } open func updateTitleLabel() { diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 4494f1b7..7cca53da 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -45,6 +45,30 @@ open class TextArea: EntryFieldBase { } }() + internal var bottomView: UIView = { + return UIView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + } + }() + + internal var bottomStackView: UIStackView = { + return UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .horizontal + $0.distribution = .fill + $0.alignment = .top + } + }() + + open var characterCountLabel = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.textStyle = .bodySmall + } + open var maxLengthLabel = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.textStyle = .bodySmall + } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @@ -70,9 +94,10 @@ open class TextArea: EntryFieldBase { /// 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() - minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: containerSize.width) minWidthConstraint?.isActive = true + characterCountLabel.text = "0" + maxLengthLabel.text = "/200" controlContainerView.addSubview(textView) textView @@ -119,6 +144,16 @@ open class TextArea: EntryFieldBase { minWidthConstraint?.isActive = true } } + + /// Container for the area which shows helper text, error text, character count, max length value. + open override func getBottomContainer() -> UIView { + bottomView.addSubview(bottomStackView) + bottomStackView.pinToSuperView() + bottomStackView.addArrangedSubview(bottomContainerView) + bottomStackView.addArrangedSubview(characterCountLabel) + bottomStackView.addArrangedSubview(maxLengthLabel) + return bottomView + } } extension TextArea: UITextViewDelegate { From e319153ac659f79f581ab4a939848b90f895a8d1 Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 23 Feb 2024 16:11:14 +0530 Subject: [PATCH 05/34] Digital ACT191 story ONEAPP-6682 Checking character count overflow --- .../TextFields/TextArea/TextArea.swift | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 7cca53da..105286b5 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -35,7 +35,8 @@ open class TextArea: EntryFieldBase { //-------------------------------------------------- internal var minWidthConstraint: NSLayoutConstraint? internal var textViewHeightConstraint: NSLayoutConstraint? - + internal var allowCharCount: Int = 0 + internal var inputFieldStackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -64,10 +65,6 @@ open class TextArea: EntryFieldBase { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textStyle = .bodySmall } - open var maxLengthLabel = Label().with { - $0.setContentCompressionResistancePriority(.required, for: .vertical) - $0.textStyle = .bodySmall - } //-------------------------------------------------- // MARK: - Public Properties @@ -96,9 +93,6 @@ open class TextArea: EntryFieldBase { super.setup() minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: containerSize.width) minWidthConstraint?.isActive = true - characterCountLabel.text = "0" - maxLengthLabel.text = "/200" - controlContainerView.addSubview(textView) textView .pinTop() @@ -118,6 +112,8 @@ open class TextArea: EntryFieldBase { open override func reset() { super.reset() textView.text = "" + characterCountLabel.reset() + characterCountLabel.textStyle = .bodySmall } /// Container for the area in which the user interacts. @@ -132,7 +128,6 @@ open class TextArea: EntryFieldBase { textView.isEditable = isEnabled textView.textColor = textViewTextColorConfiguration.getColor(self) - //set the width constraints if let width { widthConstraint?.constant = width @@ -143,6 +138,12 @@ open class TextArea: EntryFieldBase { widthConstraint?.isActive = false minWidthConstraint?.isActive = true } + + // allow - 20% of character limit + let overflowLimit = Double(maxLength ?? 0) * 0.20 + allowCharCount = Int(overflowLimit) + (maxLength ?? 0) + characterCountLabel.text = getLimitText() + } /// Container for the area which shows helper text, error text, character count, max length value. @@ -151,9 +152,18 @@ open class TextArea: EntryFieldBase { bottomStackView.pinToSuperView() bottomStackView.addArrangedSubview(bottomContainerView) bottomStackView.addArrangedSubview(characterCountLabel) - bottomStackView.addArrangedSubview(maxLengthLabel) return bottomView } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + private func getLimitText() -> String { + let count = textView.text.count + let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)" + let text = "\(countStr)" + "/" + "\(maxLength ?? 0)" + return text + } } extension TextArea: UITextViewDelegate { @@ -177,9 +187,12 @@ extension TextArea: UITextViewDelegate { textViewHeightConstraint.isActive = true } - //setting the value and firing control event - value = textView.text - sendActions(for: .valueChanged) - + if textView.text.count <= allowCharCount { + //setting the value and firing control event + value = textView.text + sendActions(for: .valueChanged) + } else { + textView.text.removeLast() + } } } From 967eed952266cfe5e8cb7b966bb2ffc5879e4f13 Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 23 Feb 2024 19:12:17 +0530 Subject: [PATCH 06/34] Digital ACT191 story ONEAPP-6682 changes for error icon --- VDS/Components/TextFields/EntryFieldBase.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 31529f61..0d6a93e5 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -59,7 +59,7 @@ open class EntryFieldBase: Control, Changeable { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .horizontal - $0.distribution = .fillProportionally + $0.distribution = .fill $0.alignment = .top } }() @@ -198,7 +198,7 @@ open class EntryFieldBase: Control, Changeable { //create the wrapping view heightConstraint = containerView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height) - widthConstraint?.priority = .defaultHigh + heightConstraint?.priority = .defaultHigh heightConstraint?.isActive = true widthConstraint = containerView.widthAnchor.constraint(equalToConstant: 0) @@ -212,11 +212,12 @@ open class EntryFieldBase: Control, Changeable { //this is the horizontal stack that contains //the left, InputContainer, Icons, Buttons container.addSubview(containerStackView) - containerStackView.pinToSuperView(.uniform(12)) + containerStackView.pinToSuperView(.uniform(VDSFormControls.spaceInset)) //add the view to add input fields containerStackView.addArrangedSubview(controlContainerView) containerStackView.addArrangedSubview(icon) + containerStackView.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: controlContainerView) //get the container this is what show helper text, error text //can include other for character count, max length From c2662aaf307023e21e73e50ca7440de5d15a3fa6 Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 23 Feb 2024 19:26:41 +0530 Subject: [PATCH 07/34] Digital ACT191 story ONEAPP-6682 added change log --- VDS.xcodeproj/project.pbxproj | 4 ++ .../TextFields/TextArea/TextAreaChangeLog.txt | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 VDS/Components/TextFields/TextArea/TextAreaChangeLog.txt diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index b13f7df5..125d88a8 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */; }; 18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; }; 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; @@ -173,6 +174,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = ""; }; 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.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 = ""; }; @@ -715,6 +717,7 @@ isa = PBXGroup; children = ( EA985C22296E033A00F2FF2E /* TextArea.swift */, + 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */, ); path = TextArea; sourceTree = ""; @@ -946,6 +949,7 @@ EA3362042891E14D0071C351 /* VerizonNHGeTX-Bold.otf in Resources */, 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */, EAEEECA72B1F952000531FC2 /* TabsChangeLog.txt in Resources */, + 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */, EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */, EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */, EAEEEC982B1F8DD100531FC2 /* LineChangeLog.txt in Resources */, diff --git a/VDS/Components/TextFields/TextArea/TextAreaChangeLog.txt b/VDS/Components/TextFields/TextArea/TextAreaChangeLog.txt new file mode 100644 index 00000000..555ad730 --- /dev/null +++ b/VDS/Components/TextFields/TextArea/TextAreaChangeLog.txt @@ -0,0 +1,38 @@ +MM/DD/YYYY +---------------- +- Initial Brand 3.0 handoff + +12/27/2021 +---------------- +- Removed Max idth Updated the SPECS with FormControl tokens + +02/25/2022 +---------------- +- Replaced Info and Error Non-Scaling icons with VDS Icon. +- Removed “weight” and “vector effect” from Anatomy and States. + +07/27/2022 +---------------- +- Added Configurations section with transparentBackground principles. + +08/10/2022 +---------------- +- Updated default and inverted prop to light and dark surface. + +11/30/2022 +---------------- +- Added "(web only)" to any instance of "keyboard focus" + +12/13/2022 +---------------- +- Replaced form border and focus border pixel values and style & spacing with tokens. + +01/18/2023 +---------------- +- Updated Anatomy items: +- Added “Highlight” to item #10 +- Changed item #7 to “Tooltip” from “Tooltip Component” + +04/12/2023 +---------------- +- Updated hex colors for updated feedback tokens in error states. From f7134b9b8cd0e763b0a79952974e390b0a7fe2f8 Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 26 Feb 2024 11:52:06 +0530 Subject: [PATCH 08/34] Digital ACT191 story ONEAPP-6682 Character limit, error text, and readonly changes. - show error text when exceeds character limit. - No restriction if character counter does not display. - color changes for character counter label, error text, and readonly. - Showing Character count overflow. --- .../TextFields/EntryFieldBase.swift | 42 +++++++++++---- .../TextFields/TextArea/TextArea.swift | 54 +++++++++++++------ 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 0d6a93e5..dcb16fbf 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -107,11 +107,15 @@ open class EntryFieldBase: Control, Changeable { } internal var borderColorConfiguration = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOnlight, forState: .normal) + $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOndark, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) } + internal var readOnlyBorderColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSFormControlsColor.borderReadonlyOnlight, VDSFormControlsColor.borderReadonlyOndark, forState: .normal) + } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @@ -160,8 +164,22 @@ open class EntryFieldBase: Control, Changeable { } } - open var errorText: String? { didSet { setNeedsUpdate() } } - + private var _errorText: String? + + open var errorText: String? { + get { return _errorText } + set { + if let newValue { + _errorText = newValue + } else { + _errorText = nil + } + updateContainerView() + updateErrorLabel() + setNeedsUpdate() + } + } + open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } } open var transparentBackground: Bool = false { didSet { setNeedsUpdate() } } @@ -212,7 +230,7 @@ open class EntryFieldBase: Control, Changeable { //this is the horizontal stack that contains //the left, InputContainer, Icons, Buttons container.addSubview(containerStackView) - containerStackView.pinToSuperView(.uniform(VDSFormControls.spaceInset)) + containerStackView.pinToSuperView(.uniform(12)) //add the view to add input fields containerStackView.addArrangedSubview(controlContainerView) @@ -279,11 +297,7 @@ open class EntryFieldBase: Control, Changeable { open override func updateView() { super.updateView() - containerView.backgroundColor = backgroundColorConfiguration.getColor(self) - containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor - containerView.layer.borderWidth = VDSFormControls.widthBorder - containerView.layer.cornerRadius = VDSFormControls.borderradius - + updateContainerView() updateTitleLabel() updateErrorLabel() updateHelperLabel() @@ -291,6 +305,16 @@ open class EntryFieldBase: Control, Changeable { backgroundColor = surface.color } + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + private func updateContainerView() { + containerView.backgroundColor = backgroundColorConfiguration.getColor(self) + containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor + containerView.layer.borderWidth = VDSFormControls.widthBorder + containerView.layer.cornerRadius = VDSFormControls.borderradius + } + //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 105286b5..1a91a099 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -61,9 +61,11 @@ open class TextArea: EntryFieldBase { } }() - open var characterCountLabel = Label().with { + open var characterCounterLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textStyle = .bodySmall + $0.textAlignment = .right + $0.numberOfLines = 1 } //-------------------------------------------------- @@ -112,8 +114,8 @@ open class TextArea: EntryFieldBase { open override func reset() { super.reset() textView.text = "" - characterCountLabel.reset() - characterCountLabel.textStyle = .bodySmall + characterCounterLabel.reset() + characterCounterLabel.textStyle = .bodySmall } /// Container for the area in which the user interacts. @@ -139,11 +141,20 @@ open class TextArea: EntryFieldBase { minWidthConstraint?.isActive = true } - // allow - 20% of character limit - let overflowLimit = Double(maxLength ?? 0) * 0.20 - allowCharCount = Int(overflowLimit) + (maxLength ?? 0) - characterCountLabel.text = getLimitText() - + if ((maxLength ?? 0) > 0) { + // allow - 20% of character limit + let overflowLimit = Double(maxLength ?? 0) * 0.20 + allowCharCount = Int(overflowLimit) + (maxLength ?? 0) + characterCounterLabel.text = getCharacterCounterText() + } else { + characterCounterLabel.text = "" + } + + icon.size = .medium + containerView.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : borderColorConfiguration.getColor(self).cgColor + textView.isEditable = readOnly ? false : true + textView.backgroundColor = backgroundColorConfiguration.getColor(self) + characterCounterLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() } /// Container for the area which shows helper text, error text, character count, max length value. @@ -151,18 +162,25 @@ open class TextArea: EntryFieldBase { bottomView.addSubview(bottomStackView) bottomStackView.pinToSuperView() bottomStackView.addArrangedSubview(bottomContainerView) - bottomStackView.addArrangedSubview(characterCountLabel) + bottomStackView.addArrangedSubview(characterCounterLabel) return bottomView } //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- - private func getLimitText() -> String { + private func getCharacterCounterText() -> String { let count = textView.text.count let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)" - let text = "\(countStr)" + "/" + "\(maxLength ?? 0)" - return text + if count > maxLength ?? 0 { + showError = true + errorText = "You have exceeded the character limit." + return countStr + } else { + showError = false + errorText = "" + return ("\(countStr)" + "/" + "\(maxLength ?? 0)") + } } } @@ -187,12 +205,18 @@ extension TextArea: UITextViewDelegate { textViewHeightConstraint.isActive = true } - if textView.text.count <= allowCharCount { + if ((maxLength ?? 0) > 0) { + if textView.text.count <= allowCharCount { + //setting the value and firing control event + value = textView.text + sendActions(for: .valueChanged) + } else { + textView.text.removeLast() + } + } else { //setting the value and firing control event value = textView.text sendActions(for: .valueChanged) - } else { - textView.text.removeLast() } } } From 5b010275546b6428872ce76e894f9a4a74ca71a4 Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 26 Feb 2024 14:54:15 +0530 Subject: [PATCH 09/34] Digital ACT191 story ONEAPP-6682 highlight text color, background color for character counter --- .../TextFields/TextArea/TextArea.swift | 51 +++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 1a91a099..1ac1900d 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -87,6 +87,21 @@ open class TextArea: EntryFieldBase { $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) }.eraseToAnyColorable() { didSet { setNeedsUpdate() } } + /// Color configuration for error icon. + internal var iconColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) + } + + /// Color configuration for character counter's highlight background color + internal var highlightBackgroundColor = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryLight, forState: .normal) + } + + /// Color configuration for character counter's highlight text color + internal var highlightTextColor = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .normal) + } + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -106,7 +121,6 @@ open class TextArea: EntryFieldBase { textViewHeightConstraint?.isActive = true backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: .success) borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success) - textView.delegate = self } @@ -151,13 +165,15 @@ open class TextArea: EntryFieldBase { } icon.size = .medium + icon.color = iconColorConfiguration.getColor(self) containerView.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : borderColorConfiguration.getColor(self).cgColor textView.isEditable = readOnly ? false : true textView.backgroundColor = backgroundColorConfiguration.getColor(self) characterCounterLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() + textView.tintColor = iconColorConfiguration.getColor(self) } - /// Container for the area which shows helper text, error text, character count, max length value. + /// Container for the area showing helper text, error text, character count, maximum length value. open override func getBottomContainer() -> UIView { bottomView.addSubview(bottomStackView) bottomStackView.pinToSuperView() @@ -182,6 +198,15 @@ open class TextArea: EntryFieldBase { return ("\(countStr)" + "/" + "\(maxLength ?? 0)") } } + + open func highlightCharacterOverflow() { + let count = textView.text.count + print("count: \(count), maxLength: \(maxLength ?? 0)") + guard let text = textView.attributedText?.mutableCopy() as? NSMutableAttributedString else { return } + text.addAttribute(NSAttributedString.Key.backgroundColor, value: highlightBackgroundColor.getColor(self), range: NSRange(location:(maxLength ?? 0 ), length: (count - (maxLength ?? 0)))) + text.addAttribute(NSAttributedString.Key.foregroundColor, value: highlightTextColor.getColor(self), range: NSRange(location:(maxLength ?? 0 ), length: (count - (maxLength ?? 0)))) + textView.attributedText = text.copy() as? NSAttributedString + } } extension TextArea: UITextViewDelegate { @@ -189,29 +214,35 @@ extension TextArea: UITextViewDelegate { // MARK: - UITextViewDelegate //-------------------------------------------------- public func textViewDidChange(_ textView: UITextView) { - + //dynamic textView Height sizing based on Figma //if you want it to work "as-is" delete this code //since it will autogrow with the current settings if let textViewHeightConstraint, textView.isEditable { let height = textView.contentSize.height - if height > 88 && height < 176 { - textViewHeightConstraint.constant = 176 - } else if height > 176 { - textViewHeightConstraint.constant = 352 - } else { - textViewHeightConstraint.constant = 88 - } + if height > 88 && height < 176 { + textViewHeightConstraint.constant = 176 + } else if height > 176 { + textViewHeightConstraint.constant = 352 + } else { + textViewHeightConstraint.constant = 88 + } textViewHeightConstraint.isActive = true } + //The exceeding characters will be highlighted to help users correct their entry. if ((maxLength ?? 0) > 0) { if textView.text.count <= allowCharCount { //setting the value and firing control event value = textView.text sendActions(for: .valueChanged) + + if (textView.text.count > (maxLength ?? 0)) { + highlightCharacterOverflow() + } } else { textView.text.removeLast() + highlightCharacterOverflow() } } else { //setting the value and firing control event From 72ef366eedddeeae35e4fdae1fb005777957b033 Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 26 Feb 2024 17:02:36 +0530 Subject: [PATCH 10/34] Digital ACT191 story ONEAPP-6682 added accessibility, focused border color to container --- .../TextFields/TextArea/TextArea.swift | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 1ac1900d..01604134 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -108,6 +108,10 @@ open class TextArea: EntryFieldBase { /// 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() + accessibilityLabel = "TextArea" + isAccessibilityElement = true + + containerStackView.pinToSuperView(.uniform(VDSFormControls.spaceInset)) minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: containerSize.width) minWidthConstraint?.isActive = true controlContainerView.addSubview(textView) @@ -117,10 +121,12 @@ open class TextArea: EntryFieldBase { .pinTrailingLessThanOrEqualTo(nil, 0, .defaultHigh) .pinBottom(0, .defaultHigh) textView.isScrollEnabled = true + textView.autocorrectionType = .no textViewHeightConstraint = textView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height) textViewHeightConstraint?.isActive = true backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: .success) borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success) + borderColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .focused) textView.delegate = self } @@ -182,26 +188,39 @@ open class TextArea: EntryFieldBase { return bottomView } + /// Used to update any Accessibility properties. + open override func updateAccessibility() { + super.updateAccessibility() + if showError { + setAccessibilityLabel(for: [titleLabel, textView, errorLabel, helperLabel]) + } else { + setAccessibilityLabel(for: [titleLabel, textView, helperLabel]) + } + } + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- private func getCharacterCounterText() -> String { let count = textView.text.count let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)" - if count > maxLength ?? 0 { - showError = true - errorText = "You have exceeded the character limit." - return countStr + 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 { - showError = false - errorText = "" - return ("\(countStr)" + "/" + "\(maxLength ?? 0)") + return "" } } open func highlightCharacterOverflow() { let count = textView.text.count - print("count: \(count), maxLength: \(maxLength ?? 0)") guard let text = textView.attributedText?.mutableCopy() as? NSMutableAttributedString else { return } text.addAttribute(NSAttributedString.Key.backgroundColor, value: highlightBackgroundColor.getColor(self), range: NSRange(location:(maxLength ?? 0 ), length: (count - (maxLength ?? 0)))) text.addAttribute(NSAttributedString.Key.foregroundColor, value: highlightTextColor.getColor(self), range: NSRange(location:(maxLength ?? 0 ), length: (count - (maxLength ?? 0)))) From f527ec5483ea03bf8f7318f73c54913175098b01 Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 26 Feb 2024 18:02:37 +0530 Subject: [PATCH 11/34] Digital ACT191 story ONEAPP-6682 minor changes --- .../TextFields/TextArea/TextArea.swift | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 01604134..135a2421 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -42,7 +42,7 @@ open class TextArea: EntryFieldBase { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .horizontal $0.distribution = .fill - $0.spacing = 12 + $0.spacing = VDSLayout.Spacing.space3X.value } }() @@ -58,6 +58,7 @@ open class TextArea: EntryFieldBase { $0.axis = .horizontal $0.distribution = .fill $0.alignment = .top + $0.spacing = VDSLayout.Spacing.space2X.value } }() @@ -128,6 +129,8 @@ open class TextArea: EntryFieldBase { borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success) borderColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .focused) textView.delegate = self + characterCounterLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() + bottomContainerStackView.spacing = VDSLayout.Spacing.space2X.value } /// Resets to default settings. @@ -161,10 +164,10 @@ open class TextArea: EntryFieldBase { minWidthConstraint?.isActive = true } - if ((maxLength ?? 0) > 0) { + if let maxLength, maxLength > 0 { // allow - 20% of character limit - let overflowLimit = Double(maxLength ?? 0) * 0.20 - allowCharCount = Int(overflowLimit) + (maxLength ?? 0) + let overflowLimit = Double(maxLength) * 0.20 + allowCharCount = Int(overflowLimit) + maxLength characterCounterLabel.text = getCharacterCounterText() } else { characterCounterLabel.text = "" @@ -175,8 +178,9 @@ open class TextArea: EntryFieldBase { containerView.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : borderColorConfiguration.getColor(self).cgColor textView.isEditable = readOnly ? false : true textView.backgroundColor = backgroundColorConfiguration.getColor(self) - characterCounterLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() textView.tintColor = iconColorConfiguration.getColor(self) + characterCounterLabel.surface = surface + highlightCharacterOverflow() } /// Container for the area showing helper text, error text, character count, maximum length value. @@ -221,9 +225,11 @@ open class TextArea: EntryFieldBase { open func highlightCharacterOverflow() { let count = textView.text.count - guard let text = textView.attributedText?.mutableCopy() as? NSMutableAttributedString else { return } - text.addAttribute(NSAttributedString.Key.backgroundColor, value: highlightBackgroundColor.getColor(self), range: NSRange(location:(maxLength ?? 0 ), length: (count - (maxLength ?? 0)))) - text.addAttribute(NSAttributedString.Key.foregroundColor, value: highlightTextColor.getColor(self), range: NSRange(location:(maxLength ?? 0 ), length: (count - (maxLength ?? 0)))) + guard let maxLength, + count > maxLength, + let text = textView.attributedText?.mutableCopy() as? NSMutableAttributedString else { return } + text.addAttribute(NSAttributedString.Key.backgroundColor, value: highlightBackgroundColor.getColor(self), range: NSRange(location:maxLength, length: (count - maxLength))) + text.addAttribute(NSAttributedString.Key.foregroundColor, value: highlightTextColor.getColor(self), range: NSRange(location:maxLength, length: (count - maxLength))) textView.attributedText = text.copy() as? NSAttributedString } } From 4f41d9cc93b07d6cf3481bbe8ae141b2827184ff Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 26 Feb 2024 22:46:33 +0530 Subject: [PATCH 12/34] Digital ACT191 story ONEAPP-6682 added preset default height property. --- .../TextFields/TextArea/TextArea.swift | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 135a2421..a9c1cbf4 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -69,11 +69,44 @@ open class TextArea: EntryFieldBase { $0.numberOfLines = 1 } + private var _minHeight: Height = .twoX + + open var minHeight: Height? { + get { return _minHeight } + set { + if let newValue { + _minHeight = newValue + } else { + _minHeight = .twoX + } + textViewHeightConstraint?.constant = _minHeight.value + setNeedsUpdate() + } + } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- override var containerSize: CGSize { CGSize(width: 182, height: 88) } - + + /// Enum used to describe the the height of TextArea. + public enum Height: String, CaseIterable { + case twoX = "2X" + case fourX = "4X" + case eightX = "8X" + + var value: CGFloat { + switch self { + case .twoX: + 88 + case .fourX: + 176 + case .eightX: + 352 + } + } + } + /// UITextView shown in the TextArea. open var textView = UITextView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -139,6 +172,8 @@ open class TextArea: EntryFieldBase { textView.text = "" characterCounterLabel.reset() characterCounterLabel.textStyle = .bodySmall + minHeight = .twoX + setNeedsUpdate() } /// Container for the area in which the user interacts. @@ -230,7 +265,7 @@ open class TextArea: EntryFieldBase { let text = textView.attributedText?.mutableCopy() as? NSMutableAttributedString else { return } text.addAttribute(NSAttributedString.Key.backgroundColor, value: highlightBackgroundColor.getColor(self), range: NSRange(location:maxLength, length: (count - maxLength))) text.addAttribute(NSAttributedString.Key.foregroundColor, value: highlightTextColor.getColor(self), range: NSRange(location:maxLength, length: (count - maxLength))) - textView.attributedText = text.copy() as? NSAttributedString + textView.attributedText = text } } @@ -244,15 +279,15 @@ extension TextArea: UITextViewDelegate { //if you want it to work "as-is" delete this code //since it will autogrow with the current settings if let textViewHeightConstraint, textView.isEditable { - let height = textView.contentSize.height - if height > 88 && height < 176 { - textViewHeightConstraint.constant = 176 - } else if height > 176 { - textViewHeightConstraint.constant = 352 + var height = textView.contentSize.height + height = max(height, _minHeight.value) + if height > Height.twoX.value && height < Height.fourX.value { + textViewHeightConstraint.constant = Height.fourX.value + } else if height > Height.fourX.value { + textViewHeightConstraint.constant = Height.eightX.value } else { - textViewHeightConstraint.constant = 88 + textViewHeightConstraint.constant = Height.twoX.value } - textViewHeightConstraint.isActive = true } //The exceeding characters will be highlighted to help users correct their entry. @@ -261,7 +296,6 @@ extension TextArea: UITextViewDelegate { //setting the value and firing control event value = textView.text sendActions(for: .valueChanged) - if (textView.text.count > (maxLength ?? 0)) { highlightCharacterOverflow() } From 9e91f38c52475851f072a4291dc891968904b3e3 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 26 Feb 2024 17:50:47 -0600 Subject: [PATCH 13/34] added "text" as a property Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 6 ++++++ VDS/Components/TextFields/InputField/InputField.swift | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index dcb16fbf..e66d31a5 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -180,6 +180,12 @@ open class EntryFieldBase: Control, Changeable { } } + /// 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() } } diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index 94ef5231..589ba28f 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -78,6 +78,14 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { /// Representing the type of input. open var fieldType: FieldType = .text { didSet { setNeedsUpdate() } } + /// The text of this textField. + open override var text: String? { + get { textField.text } + set { + textField.text = newValue + } + } + var _showError: Bool = false /// Whether not to show the error. open override var showError: Bool { From 6d38474675932ac6cf855e88aa59d82e5c5090cb Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 26 Feb 2024 17:51:16 -0600 Subject: [PATCH 14/34] added text as a property added subclass of UITextField to deal with text rendering and attributes. Signed-off-by: Matt Bruce --- .../TextFields/TextArea/TextArea.swift | 179 ++++++++++++++++-- 1 file changed, 164 insertions(+), 15 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index a9c1cbf4..e9da9b11 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -107,20 +107,21 @@ open class TextArea: EntryFieldBase { } } + /// The text of this textField. + open override var text: String? { + get { textView.text } + set { + textView.text = newValue + } + } + /// UITextView shown in the TextArea. - open var textView = UITextView().with { + open var textView = TextView().with { $0.translatesAutoresizingMaskIntoConstraints = false - $0.font = TextStyle.bodyLarge.font $0.sizeToFit() $0.isScrollEnabled = false } - /// Color configuration for the textView. - open var textViewTextColorConfiguration: AnyColorable = ViewColorConfiguration().with { - $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) - }.eraseToAnyColorable() { didSet { setNeedsUpdate() } } - /// Color configuration for error icon. internal var iconColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) @@ -187,7 +188,9 @@ open class TextArea: EntryFieldBase { super.updateView() textView.isEditable = isEnabled - textView.textColor = textViewTextColorConfiguration.getColor(self) + textView.isEnabled = isEnabled + textView.surface = surface + //set the width constraints if let width { widthConstraint?.constant = width @@ -260,12 +263,18 @@ open class TextArea: EntryFieldBase { open func highlightCharacterOverflow() { let count = textView.text.count - guard let maxLength, - count > maxLength, - let text = textView.attributedText?.mutableCopy() as? NSMutableAttributedString else { return } - text.addAttribute(NSAttributedString.Key.backgroundColor, value: highlightBackgroundColor.getColor(self), range: NSRange(location:maxLength, length: (count - maxLength))) - text.addAttribute(NSAttributedString.Key.foregroundColor, value: highlightTextColor.getColor(self), range: NSRange(location:maxLength, length: (count - maxLength))) - textView.attributedText = text + guard let maxLength, count > maxLength else { + textView.textAttributes = nil + return + } + + var textAttributes = [any LabelAttributeModel]() + let location = maxLength + let length = count - maxLength + textAttributes.append(ColorLabelAttribute(location: location, length: length, color: highlightBackgroundColor.getColor(self), isForegroundColor: false)) + textAttributes.append(ColorLabelAttribute(location: location, length: length, color: highlightTextColor.getColor(self), isForegroundColor: true)) + + textView.textAttributes = textAttributes } } @@ -310,3 +319,143 @@ extension TextArea: UITextViewDelegate { } } } + +/// Will move this into a new file, need to talk with Scott/Kyle +open class TextView: UITextView, ViewProtocol { + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero, textContainer: nil) + initialSetup() + } + + public override init(frame: CGRect, textContainer: NSTextContainer?) { + super.init(frame: frame, textContainer: textContainer) + initialSetup() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + //-------------------------------------------------- + // MARK: - Combine Properties + //-------------------------------------------------- + /// Set of Subscribers for any Publishers for this Control. + open var subscribers = Set() + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + private var initialSetupPerformed = false + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + /// Key of whether or not updateView() is called in setNeedsUpdate() + open var shouldUpdateView: Bool = true + + open var surface: Surface = .light { didSet { setNeedsUpdate() } } + + /// Array of LabelAttributeModel objects used in rendering the text. + open var textAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() } } + + /// TextStyle used on the titleLabel. + open var textStyle: TextStyle { .defaultStyle } + + /// Will determine if a scaled font should be used for the titleLabel font. + open var useScaledFont: Bool = false { didSet { setNeedsUpdate() } } + + open var isEnabled: Bool = true { didSet { setNeedsUpdate() } } + + open var textColorConfiguration: AnyColorable = ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) + }.eraseToAnyColorable(){ didSet { setNeedsUpdate() }} + + open override var textColor: UIColor? { + get { textColorConfiguration.getColor(self) } + set { } + } + + override public var text: String! { + get { super.text } + set { + super.text = newValue + updateLabel() + } + } + + override public var textAlignment: NSTextAlignment { + didSet { + if textAlignment != oldValue { + // Text alignment can be part of our paragraph style, so we may need to + // re-style when changed + updateLabel() + } + } + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open func initialSetup() { + if !initialSetupPerformed { + backgroundColor = .clear + translatesAutoresizingMaskIntoConstraints = false + accessibilityCustomActions = [] + setup() + setNeedsUpdate() + } + } + + + open func setup() { + translatesAutoresizingMaskIntoConstraints = false + } + + open func updateView() { + updateLabel() + } + + open func updateAccessibility() {} + + open func reset() { + shouldUpdateView = false + surface = .light + text = nil + accessibilityCustomActions = [] + shouldUpdateView = true + setNeedsUpdate() + } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + private func updateLabel() { + + //clear the arrays holding actions + accessibilityCustomActions = [] + if let text, !text.isEmpty { + //create the primary string + let mutableText = NSMutableAttributedString.mutableText(for: text, + textStyle: textStyle, + useScaledFont: useScaledFont, + textColor: textColor!, + alignment: textAlignment, + lineBreakMode: .byWordWrapping) + //apply any attributes + if let attributes = textAttributes { + mutableText.apply(attributes: attributes) + } + attributedText = mutableText + } else { + attributedText = nil + } + } + + +} From a2026a4319069a7b56b67a7953511e22d334b75c Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 27 Feb 2024 17:24:16 +0530 Subject: [PATCH 15/34] Digital ACT191 story ONEAPP-6682 removed unnecessary code --- VDS/Components/TextFields/TextArea/TextArea.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index a9c1cbf4..3cf465dc 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -143,7 +143,6 @@ open class TextArea: EntryFieldBase { open override func setup() { super.setup() accessibilityLabel = "TextArea" - isAccessibilityElement = true containerStackView.pinToSuperView(.uniform(VDSFormControls.spaceInset)) minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: containerSize.width) @@ -172,7 +171,6 @@ open class TextArea: EntryFieldBase { textView.text = "" characterCounterLabel.reset() characterCounterLabel.textStyle = .bodySmall - minHeight = .twoX setNeedsUpdate() } From af2c5cd368d09b083f1f9712d770edc20ab2d39e Mon Sep 17 00:00:00 2001 From: vasavk Date: Wed, 28 Feb 2024 16:39:44 +0530 Subject: [PATCH 16/34] Digital ACT-191 ONEAPP-6682 story: Fixed minor issue for TextArea height --- VDS/Components/TextFields/TextArea/TextArea.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index aee4ab9a..92f5525a 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -288,7 +288,7 @@ extension TextArea: UITextViewDelegate { if let textViewHeightConstraint, textView.isEditable { var height = textView.contentSize.height height = max(height, _minHeight.value) - if height > Height.twoX.value && height < Height.fourX.value { + if height > Height.twoX.value && height <= Height.fourX.value { textViewHeightConstraint.constant = Height.fourX.value } else if height > Height.fourX.value { textViewHeightConstraint.constant = Height.eightX.value From 301ead74474f52670eac2d3945e88dbf2608f092 Mon Sep 17 00:00:00 2001 From: vasavk Date: Wed, 28 Feb 2024 22:03:17 +0530 Subject: [PATCH 17/34] Digital ACT-191 ONEAPP-6682 story: Fix for the observed crash on removing last char when having char counter and exceeded limit. --- VDS/Components/TextFields/TextArea/TextArea.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 92f5525a..432d24bd 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -300,12 +300,11 @@ extension TextArea: UITextViewDelegate { //The exceeding characters will be highlighted to help users correct their entry. if ((maxLength ?? 0) > 0) { if textView.text.count <= allowCharCount { + highlightCharacterOverflow() + //setting the value and firing control event value = textView.text sendActions(for: .valueChanged) - if (textView.text.count > (maxLength ?? 0)) { - highlightCharacterOverflow() - } } else { textView.text.removeLast() highlightCharacterOverflow() From a4582f11a597b50af9bd082500819f256c61e504 Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 29 Feb 2024 00:45:54 +0530 Subject: [PATCH 18/34] Digital ACT-191 ONEAPP-6682 story: fixed the bug dealing with the character counter --- VDS/Components/TextFields/TextArea/TextArea.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 432d24bd..8e5f8e58 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -251,7 +251,7 @@ open class TextArea: EntryFieldBase { return countStr } else { showError = false - errorText = "" + errorText = nil return ("\(countStr)" + "/" + "\(maxLength ?? 0)") } } else { @@ -261,7 +261,7 @@ open class TextArea: EntryFieldBase { open func highlightCharacterOverflow() { let count = textView.text.count - guard let maxLength, count > maxLength else { + guard let maxLength, maxLength > 0, count > maxLength else { textView.textAttributes = nil return } @@ -315,6 +315,7 @@ extension TextArea: UITextViewDelegate { sendActions(for: .valueChanged) } } + } /// Will move this into a new file, need to talk with Scott/Kyle From 4df689b27558182c8a1676a1e7ae59b789d0a227 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 12:14:12 -0600 Subject: [PATCH 19/34] added internalShowError/internalErrorText so that you won't overwrite external validators Signed-off-by: Matt Bruce --- .../TextFields/EntryFieldBase.swift | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index e66d31a5..8eed853c 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -153,27 +153,30 @@ open class EntryFieldBase: Control, Changeable { /// Whether not to show the error. open var showError: Bool = false { didSet { setNeedsUpdate() } } + /// Whether or not to show the internal error + internal var showInternalError: Bool = false { didSet { setNeedsUpdate() } } + /// Override UIControl state to add the .error state if showError is true. open override var state: UIControl.State { get { var state = super.state - if showError { + if showError || showInternalError { state.insert(.error) } return state } } - - private var _errorText: String? - + open var errorText: String? { - get { return _errorText } - set { - if let newValue { - _errorText = newValue - } else { - _errorText = nil - } + didSet { + updateContainerView() + updateErrorLabel() + setNeedsUpdate() + } + } + + internal var internalErrorText: String? { + didSet { updateContainerView() updateErrorLabel() setNeedsUpdate() @@ -365,7 +368,16 @@ open class EntryFieldBase: Control, Changeable { } open func updateErrorLabel(){ - if showError, let errorText { + if showError, showInternalError, let errorText, let internalErrorText { + errorLabel.text = [internalErrorText, errorText].joined(separator: "\n") + errorLabel.surface = surface + errorLabel.isEnabled = isEnabled + errorLabel.isHidden = false + icon.name = .error + icon.color = VDSColor.paletteBlack + icon.surface = surface + icon.isHidden = !isEnabled + } else if showError, let errorText { errorLabel.text = errorText errorLabel.surface = surface errorLabel.isEnabled = isEnabled @@ -374,6 +386,15 @@ open class EntryFieldBase: Control, Changeable { icon.color = VDSColor.paletteBlack icon.surface = surface icon.isHidden = !isEnabled + } else if showInternalError, let internalErrorText { + errorLabel.text = internalErrorText + errorLabel.surface = surface + errorLabel.isEnabled = isEnabled + errorLabel.isHidden = false + icon.name = .error + icon.color = VDSColor.paletteBlack + icon.surface = surface + icon.isHidden = !isEnabled } else { icon.isHidden = true errorLabel.isHidden = true From b907e0e001099d480fabcb714bb5cc5afc2c432b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 12:14:27 -0600 Subject: [PATCH 20/34] implemented new internal error handling Signed-off-by: Matt Bruce --- .../TextFields/TextArea/TextArea.swift | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 8e5f8e58..d4f11a9e 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -112,6 +112,7 @@ open class TextArea: EntryFieldBase { get { textView.text } set { textView.text = newValue + value = newValue } } @@ -227,7 +228,7 @@ open class TextArea: EntryFieldBase { bottomStackView.addArrangedSubview(characterCounterLabel) return bottomView } - + /// Used to update any Accessibility properties. open override func updateAccessibility() { super.updateAccessibility() @@ -246,12 +247,13 @@ 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." + showInternalError = true + internalErrorText = "You have exceeded the character limit." return countStr } else { - showError = false - errorText = nil + + showInternalError = false + internalErrorText = nil return ("\(countStr)" + "/" + "\(maxLength ?? 0)") } } else { @@ -303,7 +305,7 @@ extension TextArea: UITextViewDelegate { highlightCharacterOverflow() //setting the value and firing control event - value = textView.text + text = textView.text sendActions(for: .valueChanged) } else { textView.text.removeLast() @@ -311,7 +313,7 @@ extension TextArea: UITextViewDelegate { } } else { //setting the value and firing control event - value = textView.text + text = textView.text sendActions(for: .valueChanged) } } From 3ada7fa0375fbeb7347f6b05d2e8afcc3b1a10ba Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 12:14:35 -0600 Subject: [PATCH 21/34] updated property Signed-off-by: Matt Bruce --- VDS/Components/TextFields/InputField/InputField.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index 589ba28f..83768a6b 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -83,6 +83,7 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { get { textField.text } set { textField.text = newValue + value = newValue } } From de973fd7b87b84ea80ea0e00a95ded23a0946f7b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 14:06:13 -0600 Subject: [PATCH 22/34] added formfieldable Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 8eed853c..121b1510 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 From 98b30237b285557e00b392b80c5960691d1c41f2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 28 Feb 2024 13:04:11 -0600 Subject: [PATCH 23/34] 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 e7a1698602a44946f158c8bafd9b6d8e7def2244 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 14:08:33 -0600 Subject: [PATCH 24/34] removed test Signed-off-by: Matt Bruce --- VDS/Components/Label/Attributes/LabelAttributeModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 } } From c75064e84e6f81b35d5bf7275c97fae37b68d64d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 14:19:51 -0600 Subject: [PATCH 25/34] added more logic to the text/value properties Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 14 +++++++++++++- .../TextFields/InputField/InputField.swift | 7 +++++-- VDS/Components/TextFields/TextArea/TextArea.swift | 9 ++++++--- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 121b1510..6964ae0a 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -198,8 +198,20 @@ open class EntryFieldBase: Control, Changeable, FormFieldable { open var maxLength: Int? { didSet { setNeedsUpdate() } } open var inputId: String? { didSet { setNeedsUpdate() } } + + /// The text of this textField. + private var _value: AnyHashable? + open var value: AnyHashable? { + get { _value } + set { + if let newValue, newValue != _value { + _value = newValue + text = newValue as? String + } + setNeedsUpdate() + } + } - open var value: AnyHashable? { 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 83768a6b..ac350bc1 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -82,8 +82,11 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { open override var text: String? { get { textField.text } set { - textField.text = newValue - value = newValue + if let newValue, newValue != text { + textField.text = newValue + value = newValue + } + setNeedsUpdate() } } diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index d4f11a9e..5fe4e68b 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -107,12 +107,15 @@ open class TextArea: EntryFieldBase { } } - /// The text of this textField. + /// The text of this textView open override var text: String? { get { textView.text } set { - textView.text = newValue - value = newValue + if let newValue, newValue != text { + textView.text = newValue + value = newValue + } + setNeedsUpdate() } } From 6cd8b31985091607221d499303247cb45702b710 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 14:38:17 -0600 Subject: [PATCH 26/34] refactored out TextView Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 4 + .../TextFields/TextArea/TextArea.swift | 169 ++---------------- .../TextFields/TextArea/TextView.swift | 150 ++++++++++++++++ 3 files changed, 169 insertions(+), 154 deletions(-) create mode 100644 VDS/Components/TextFields/TextArea/TextView.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 125d88a8..10b469ed 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -68,6 +68,7 @@ EA5F86C82A1BD99100BC83E4 /* TabModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86C72A1BD99100BC83E4 /* TabModel.swift */; }; EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */; }; EA5F86D02A1F936100BC83E4 /* TabsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */; }; + EA6F330E2B911E9000BACAB9 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6F330D2B911E9000BACAB9 /* TextView.swift */; }; EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */; }; EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */; }; EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; }; @@ -236,6 +237,7 @@ EA5F86C72A1BD99100BC83E4 /* TabModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabModel.swift; sourceTree = ""; }; EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = ""; }; EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsContainer.swift; sourceTree = ""; }; + EA6F330D2B911E9000BACAB9 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = ""; }; EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIcon.swift; sourceTree = ""; }; EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+VDSColor.swift"; sourceTree = ""; }; EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = ""; }; @@ -717,6 +719,7 @@ isa = PBXGroup; children = ( EA985C22296E033A00F2FF2E /* TextArea.swift */, + EA6F330D2B911E9000BACAB9 /* TextView.swift */, 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */, ); path = TextArea; @@ -1059,6 +1062,7 @@ EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */, EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */, EA8E40912A7D3F6300934ED3 /* UIView+Accessibility.swift in Sources */, + EA6F330E2B911E9000BACAB9 /* TextView.swift in Sources */, EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */, EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */, EAD0688E2A55F819002E3A2D /* Loader.swift in Sources */, diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 5fe4e68b..317748e4 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -35,7 +35,6 @@ open class TextArea: EntryFieldBase { //-------------------------------------------------- internal var minWidthConstraint: NSLayoutConstraint? internal var textViewHeightConstraint: NSLayoutConstraint? - internal var allowCharCount: Int = 0 internal var inputFieldStackView: UIStackView = { return UIStackView().with { @@ -204,11 +203,9 @@ open class TextArea: EntryFieldBase { minWidthConstraint?.isActive = true } + let characterError = getCharacterCounterText() if let maxLength, maxLength > 0 { - // allow - 20% of character limit - let overflowLimit = Double(maxLength) * 0.20 - allowCharCount = Int(overflowLimit) + maxLength - characterCounterLabel.text = getCharacterCounterText() + characterCounterLabel.text = characterError } else { characterCounterLabel.text = "" } @@ -245,22 +242,23 @@ open class TextArea: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- - private func getCharacterCounterText() -> String { + private func getCharacterCounterText() -> String? { let count = textView.text.count let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)" - if ((maxLength ?? 0) > 0) { - if (count > (maxLength ?? 0)) { + if let maxLength, maxLength > 0 { + if count > maxLength { showInternalError = true internalErrorText = "You have exceeded the character limit." return countStr } else { - showInternalError = false internalErrorText = nil - return ("\(countStr)" + "/" + "\(maxLength ?? 0)") + return ("\(countStr)" + "/" + "\(maxLength)") } } else { - return "" + showInternalError = false + internalErrorText = nil + return nil } } @@ -303,7 +301,11 @@ extension TextArea: UITextViewDelegate { } //The exceeding characters will be highlighted to help users correct their entry. - if ((maxLength ?? 0) > 0) { + if let maxLength, maxLength > 0 { + // allow - 20% of character limit + let overflowLimit = Double(maxLength) * 0.20 + let allowCharCount = Int(overflowLimit) + maxLength + if textView.text.count <= allowCharCount { highlightCharacterOverflow() @@ -319,146 +321,5 @@ extension TextArea: UITextViewDelegate { text = textView.text sendActions(for: .valueChanged) } - } - -} - -/// Will move this into a new file, need to talk with Scott/Kyle -open class TextView: UITextView, ViewProtocol { - - //-------------------------------------------------- - // MARK: - Initializers - //-------------------------------------------------- - required public init() { - super.init(frame: .zero, textContainer: nil) - initialSetup() - } - - public override init(frame: CGRect, textContainer: NSTextContainer?) { - super.init(frame: frame, textContainer: textContainer) - initialSetup() - } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - initialSetup() - } - - //-------------------------------------------------- - // MARK: - Combine Properties - //-------------------------------------------------- - /// Set of Subscribers for any Publishers for this Control. - open var subscribers = Set() - - //-------------------------------------------------- - // MARK: - Private Properties - //-------------------------------------------------- - private var initialSetupPerformed = false - - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - /// Key of whether or not updateView() is called in setNeedsUpdate() - open var shouldUpdateView: Bool = true - - open var surface: Surface = .light { didSet { setNeedsUpdate() } } - - /// Array of LabelAttributeModel objects used in rendering the text. - open var textAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() } } - - /// TextStyle used on the titleLabel. - open var textStyle: TextStyle { .defaultStyle } - - /// Will determine if a scaled font should be used for the titleLabel font. - open var useScaledFont: Bool = false { didSet { setNeedsUpdate() } } - - open var isEnabled: Bool = true { didSet { setNeedsUpdate() } } - - open var textColorConfiguration: AnyColorable = ViewColorConfiguration().with { - $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) - }.eraseToAnyColorable(){ didSet { setNeedsUpdate() }} - - open override var textColor: UIColor? { - get { textColorConfiguration.getColor(self) } - set { } - } - - override public var text: String! { - get { super.text } - set { - super.text = newValue - updateLabel() - } - } - - override public var textAlignment: NSTextAlignment { - didSet { - if textAlignment != oldValue { - // Text alignment can be part of our paragraph style, so we may need to - // re-style when changed - updateLabel() - } - } - } - - //-------------------------------------------------- - // MARK: - Lifecycle - //-------------------------------------------------- - open func initialSetup() { - if !initialSetupPerformed { - backgroundColor = .clear - translatesAutoresizingMaskIntoConstraints = false - accessibilityCustomActions = [] - setup() - setNeedsUpdate() - } - } - - - open func setup() { - translatesAutoresizingMaskIntoConstraints = false - } - - open func updateView() { - updateLabel() - } - - open func updateAccessibility() {} - - open func reset() { - shouldUpdateView = false - surface = .light - text = nil - accessibilityCustomActions = [] - shouldUpdateView = true - setNeedsUpdate() - } - - //-------------------------------------------------- - // MARK: - Private Methods - //-------------------------------------------------- - private func updateLabel() { - - //clear the arrays holding actions - accessibilityCustomActions = [] - if let text, !text.isEmpty { - //create the primary string - let mutableText = NSMutableAttributedString.mutableText(for: text, - textStyle: textStyle, - useScaledFont: useScaledFont, - textColor: textColor!, - alignment: textAlignment, - lineBreakMode: .byWordWrapping) - //apply any attributes - if let attributes = textAttributes { - mutableText.apply(attributes: attributes) - } - attributedText = mutableText - } else { - attributedText = nil - } - } - - + } } diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift new file mode 100644 index 00000000..cc1567bf --- /dev/null +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -0,0 +1,150 @@ +// +// TextView.swift +// VDS +// +// Created by Matt Bruce on 2/29/24. +// + +import Foundation +import UIKit +import Combine +import VDSColorTokens + +/// Will move this into a new file, need to talk with Scott/Kyle +open class TextView: UITextView, ViewProtocol { + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero, textContainer: nil) + initialSetup() + } + + public override init(frame: CGRect, textContainer: NSTextContainer?) { + super.init(frame: frame, textContainer: textContainer) + initialSetup() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + //-------------------------------------------------- + // MARK: - Combine Properties + //-------------------------------------------------- + /// Set of Subscribers for any Publishers for this Control. + open var subscribers = Set() + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + private var initialSetupPerformed = false + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + /// Key of whether or not updateView() is called in setNeedsUpdate() + open var shouldUpdateView: Bool = true + + open var surface: Surface = .light { didSet { setNeedsUpdate() } } + + /// Array of LabelAttributeModel objects used in rendering the text. + open var textAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() } } + + /// TextStyle used on the titleLabel. + open var textStyle: TextStyle { .defaultStyle } + + /// Will determine if a scaled font should be used for the titleLabel font. + open var useScaledFont: Bool = false { didSet { setNeedsUpdate() } } + + open var isEnabled: Bool = true { didSet { setNeedsUpdate() } } + + open var textColorConfiguration: AnyColorable = ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) + }.eraseToAnyColorable(){ didSet { setNeedsUpdate() }} + + open override var textColor: UIColor? { + get { textColorConfiguration.getColor(self) } + set { } + } + + override public var text: String! { + get { super.text } + set { + super.text = newValue + updateLabel() + } + } + + override public var textAlignment: NSTextAlignment { + didSet { + if textAlignment != oldValue { + // Text alignment can be part of our paragraph style, so we may need to + // re-style when changed + updateLabel() + } + } + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open func initialSetup() { + if !initialSetupPerformed { + backgroundColor = .clear + translatesAutoresizingMaskIntoConstraints = false + accessibilityCustomActions = [] + setup() + setNeedsUpdate() + } + } + + + open func setup() { + translatesAutoresizingMaskIntoConstraints = false + } + + open func updateView() { + updateLabel() + } + + open func updateAccessibility() {} + + open func reset() { + shouldUpdateView = false + surface = .light + text = nil + accessibilityCustomActions = [] + shouldUpdateView = true + setNeedsUpdate() + } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + private func updateLabel() { + + //clear the arrays holding actions + accessibilityCustomActions = [] + if let text, !text.isEmpty { + //create the primary string + let mutableText = NSMutableAttributedString.mutableText(for: text, + textStyle: textStyle, + useScaledFont: useScaledFont, + textColor: textColor!, + alignment: textAlignment, + lineBreakMode: .byWordWrapping) + //apply any attributes + if let attributes = textAttributes { + mutableText.apply(attributes: attributes) + } + attributedText = mutableText + } else { + attributedText = nil + } + } +} + From cedba7ba8c6090bd2f8f0f28ad81ad60e37ef3b4 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 16:34:55 -0600 Subject: [PATCH 27/34] added namespace for control Signed-off-by: Matt Bruce --- VDS/Components/TextFields/TextArea/TextView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift index cc1567bf..ea96ed6e 100644 --- a/VDS/Components/TextFields/TextArea/TextView.swift +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -10,7 +10,7 @@ import UIKit import Combine import VDSColorTokens -/// Will move this into a new file, need to talk with Scott/Kyle +@objc(VDSTextView) open class TextView: UITextView, ViewProtocol { //-------------------------------------------------- From 25058dd92de108c46d141e135b866d2dd4716834 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Fri, 1 Mar 2024 12:41:13 +0530 Subject: [PATCH 28/34] self.isVisibleOnScreen for isActive --- VDS/Components/Loader/Loader.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Loader/Loader.swift b/VDS/Components/Loader/Loader.swift index baf6ecdb..3b4c8ebd 100644 --- a/VDS/Components/Loader/Loader.swift +++ b/VDS/Components/Loader/Loader.swift @@ -80,7 +80,7 @@ open class Loader: View { super.updateView() icon.color = iconColorConfiguration.getColor(self) icon.customSize = size - if isActive { + if isActive, self.isVisibleOnScreen { startAnimating() } else { stopAnimating() From 6e8fe4ea91625d847489cadfe477eb00e1fabcbf Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 1 Mar 2024 09:14:13 -0600 Subject: [PATCH 29/34] use && instead of comma (,) if you don't use if let Signed-off-by: Matt Bruce --- VDS/Components/Loader/Loader.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Loader/Loader.swift b/VDS/Components/Loader/Loader.swift index 3b4c8ebd..1cfb02c4 100644 --- a/VDS/Components/Loader/Loader.swift +++ b/VDS/Components/Loader/Loader.swift @@ -80,7 +80,7 @@ open class Loader: View { super.updateView() icon.color = iconColorConfiguration.getColor(self) icon.customSize = size - if isActive, self.isVisibleOnScreen { + if isActive && isVisibleOnScreen { startAnimating() } else { stopAnimating() From 1cce53922283de8dec505b47b6d14941028d88d1 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 1 Mar 2024 10:14:05 -0600 Subject: [PATCH 30/34] added layoutsubviews to call setNeedsUpdate Signed-off-by: Matt Bruce --- VDS/BaseClasses/Control.swift | 4 ++++ VDS/BaseClasses/View.swift | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/VDS/BaseClasses/Control.swift b/VDS/BaseClasses/Control.swift index e4ba8cde..cc2246aa 100644 --- a/VDS/BaseClasses/Control.swift +++ b/VDS/BaseClasses/Control.swift @@ -132,4 +132,8 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { return true } + open override func layoutSubviews() { + super.layoutSubviews() + setNeedsUpdate() + } } diff --git a/VDS/BaseClasses/View.swift b/VDS/BaseClasses/View.swift index 89e5ce4b..a807c25c 100644 --- a/VDS/BaseClasses/View.swift +++ b/VDS/BaseClasses/View.swift @@ -86,4 +86,9 @@ open class View: UIView, ViewProtocol, UserInfoable { isEnabled = true } + open override func layoutSubviews() { + super.layoutSubviews() + setNeedsUpdate() + } + } From ab33a0030bae04cdb5c003686fdda19aa9365b6b Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 4 Mar 2024 13:25:16 +0530 Subject: [PATCH 31/34] updated dropshadow values as per spec --- VDS/Components/Icon/ButtonIcon/ButtonIcon.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift index 97bbbb77..a11bf67e 100644 --- a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift +++ b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift @@ -247,7 +247,7 @@ open class ButtonIcon: Control, Changeable, FormFieldable { } private let dropshadow2Configuration = DropShadowConfiguration().with { $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() - $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.15), CGFloat(0.22)) + $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.05), CGFloat(0.15)) $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2)) $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(4), CGFloat(6)) } From e7a85d16bc17f880a657fa406527ec606c636735 Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 4 Mar 2024 16:25:16 +0530 Subject: [PATCH 32/34] Digital ACT-191 ONEAPP-6318 story: updated role as button for the button icon. --- VDS/Components/Icon/ButtonIcon/ButtonIcon.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift index a11bf67e..f998c28c 100644 --- a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift +++ b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift @@ -343,7 +343,7 @@ open class ButtonIcon: Control, Changeable, FormFieldable { open override func setup() { super.setup() isAccessibilityElement = true - accessibilityTraits = .image + accessibilityTraits = .button accessibilityElements = [badgeIndicator] //create a layoutGuide for the icon to key off of From 1c0b8bbf459e41272020e97e7e705e9e7fab78a6 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Tue, 5 Mar 2024 17:49:25 +0530 Subject: [PATCH 33/34] Addressed review comments --- VDS.xcodeproj/project.pbxproj | 8 +++ .../Icon/ButtonIcon/ButtonIcon.swift | 36 +++++------ VDS/Protocols/DropShadowable.swift | 64 ++++++------------- VDS/Utilities/DropShadowConfiguration.swift | 33 ++++++++++ VDS/Utilities/SurfaceConfigurationValue.swift | 31 +++++++++ 5 files changed, 110 insertions(+), 62 deletions(-) create mode 100644 VDS/Utilities/DropShadowConfiguration.swift create mode 100644 VDS/Utilities/SurfaceConfigurationValue.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 2b87c3c7..b6a3d192 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -18,6 +18,8 @@ 7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; }; 71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */; }; 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; }; + 71FC86DE2B9738B900700965 /* SurfaceConfigurationValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */; }; + 71FC86E02B973AE500700965 /* DropShadowConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DF2B973AE500700965 /* DropShadowConfiguration.swift */; }; EA0B18022A9E236900F2D0CD /* SelectorGroupBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */; }; EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */; }; EA0B18062A9E2D2D00F2D0CD /* SelectorItemBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */; }; @@ -186,6 +188,8 @@ 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = ""; }; 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowable.swift; sourceTree = ""; }; 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = ""; }; + 71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceConfigurationValue.swift; sourceTree = ""; }; + 71FC86DF2B973AE500700965 /* DropShadowConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowConfiguration.swift; sourceTree = ""; }; EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorGroupBase.swift; sourceTree = ""; }; EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = ""; }; EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorItemBase.swift; sourceTree = ""; }; @@ -583,6 +587,8 @@ isa = PBXGroup; children = ( EA3361BC288B2C760071C351 /* TypeAlias.swift */, + 71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */, + 71FC86DF2B973AE500700965 /* DropShadowConfiguration.swift */, ); path = Utilities; sourceTree = ""; @@ -1031,6 +1037,7 @@ EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */, EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */, EA985BE82968951C00F2FF2E /* TileletTitleModel.swift in Sources */, + 71FC86DE2B9738B900700965 /* SurfaceConfigurationValue.swift in Sources */, EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */, EA985BEA29689B6D00F2FF2E /* TileletSubTitleModel.swift in Sources */, EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */, @@ -1101,6 +1108,7 @@ EAB2376829E9992800AABE9A /* TooltipAlertViewController.swift in Sources */, EA33623E2892EE950071C351 /* UIDevice.swift in Sources */, EA985C692971B90B00F2FF2E /* IconSize.swift in Sources */, + 71FC86E02B973AE500700965 /* DropShadowConfiguration.swift in Sources */, EA985C672970C21600F2FF2E /* VDSLayout.swift in Sources */, EA3362302891EB4A0071C351 /* Font.swift in Sources */, EAF7F0AD289B142900B287F5 /* StrikeThroughLabelAttribute.swift in Sources */, diff --git a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift index f998c28c..65968214 100644 --- a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift +++ b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift @@ -241,15 +241,15 @@ open class ButtonIcon: Control, Changeable, FormFieldable { }() private let dropshadow1Configuration = DropShadowConfiguration().with { $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() - $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.12), CGFloat(0.22)) - $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1)) - $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(10), CGFloat(12)) + $0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.12), CGFloat(0.22)) + $0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1)) + $0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(10), CGFloat(12)) } private let dropshadow2Configuration = DropShadowConfiguration().with { $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() - $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.05), CGFloat(0.15)) - $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2)) - $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(4), CGFloat(6)) + $0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.05), CGFloat(0.15)) + $0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2)) + $0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(4), CGFloat(6)) } var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] } } @@ -276,15 +276,15 @@ open class ButtonIcon: Control, Changeable, FormFieldable { }() private let dropshadow1Configuration = DropShadowConfiguration().with { $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() - $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.12), CGFloat(0.22)) - $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1)) - $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(10), CGFloat(12)) + $0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.12), CGFloat(0.22)) + $0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1)) + $0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(10), CGFloat(12)) } private let dropshadow2Configuration = DropShadowConfiguration().with { $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() - $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.05), CGFloat(0.15)) - $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2)) - $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(4), CGFloat(6)) + $0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.05), CGFloat(0.15)) + $0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2)) + $0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(4), CGFloat(6)) } var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] } } @@ -321,15 +321,15 @@ open class ButtonIcon: Control, Changeable, FormFieldable { }() private let dropshadow1Configuration = DropShadowConfiguration().with { $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() - $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.22), CGFloat(0.12)) - $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1)) - $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(12), CGFloat(10)) + $0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.22), CGFloat(0.12)) + $0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1)) + $0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(12), CGFloat(10)) } private let dropshadow2Configuration = DropShadowConfiguration().with { $0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable() - $0.shadowOpacityConfiguration = AnyConfigurationValue(CGFloat(0.15), CGFloat(0.05)) - $0.shadowOffsetConfiguration = AnyConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2)) - $0.shadowRadiusConfiguration = AnyConfigurationValue(CGFloat(6), CGFloat(4)) + $0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.15), CGFloat(0.05)) + $0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2)) + $0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(6), CGFloat(4)) } var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] } } diff --git a/VDS/Protocols/DropShadowable.swift b/VDS/Protocols/DropShadowable.swift index 57c44036..b569ecab 100644 --- a/VDS/Protocols/DropShadowable.swift +++ b/VDS/Protocols/DropShadowable.swift @@ -8,37 +8,32 @@ import Foundation import UIKit +/** + DropShadowable protocol helps with the configuration values for adding drop shadows for light & dark surfaces. +*/ protocol DropShadowable { - + ///Shadow Color configuration for light and dark surfaces var shadowColorConfiguration: AnyColorable { get set } - var shadowOpacityConfiguration: AnyConfigurationValue { get set } - var shadowOffsetConfiguration: AnyConfigurationValue { get set } - var shadowRadiusConfiguration: AnyConfigurationValue { get set } + ///Shadow Opacity configuration for light and dark surfaces + var shadowOpacityConfiguration: SurfaceConfigurationValue { get set } + ///Shadow Offset configuration for light and dark surfaces + var shadowOffsetConfiguration: SurfaceConfigurationValue { get set } + ///Shadow Radius configuration for light and dark surfaces + var shadowRadiusConfiguration: SurfaceConfigurationValue { get set } } +/** + DropShadowableConfiguration protocol helps with multiple drop shadows configurations can be added to a view. + */ protocol DropShadowableConfiguration { + ///Configurations are the DropShadowable list, these are applied on the view var configurations: [DropShadowable] { get } } -final class DropShadowConfiguration: DropShadowable, ObjectWithable { - - typealias CGFloatConfigurationValue = AnyConfigurationValue - typealias CGSizeConfigurationValue = AnyConfigurationValue - - var shadowColorConfiguration: AnyColorable - var shadowOpacityConfiguration: CGFloatConfigurationValue - var shadowOffsetConfiguration: CGSizeConfigurationValue - var shadowRadiusConfiguration: CGFloatConfigurationValue - - init(shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().eraseToAnyColorable(), shadowOpacity: CGFloatConfigurationValue = CGFloatConfigurationValue(1.0, 1.0), shadowOffset: CGSizeConfigurationValue = CGSizeConfigurationValue(.zero, .zero), shadowRadius: CGFloatConfigurationValue = CGFloatConfigurationValue(1.0, 1.0)) { - self.shadowColorConfiguration = shadowColorConfiguration - self.shadowOpacityConfiguration = shadowOpacity - self.shadowOffsetConfiguration = shadowOffset - self.shadowRadiusConfiguration = shadowRadius - } -} - +/** + Extension on ViewProtocol for adding drop shadows & gradient layer on view. + */ extension ViewProtocol where Self: UIView { func addDropShadow(_ config: DropShadowable) { @@ -58,9 +53,9 @@ extension ViewProtocol where Self: UIView { shadowLayer.backgroundColor = backgroundColor?.cgColor shadowLayer.cornerRadius = layer.cornerRadius shadowLayer.shadowColor = config.shadowColorConfiguration.getColor(self).cgColor - shadowLayer.shadowOpacity = Float(config.shadowOpacityConfiguration.getValue(self)) - shadowLayer.shadowOffset = config.shadowOffsetConfiguration.getValue(self) - shadowLayer.shadowRadius = config.shadowRadiusConfiguration.getValue(self) + shadowLayer.shadowOpacity = Float(config.shadowOpacityConfiguration.value(for: self)) + shadowLayer.shadowOffset = config.shadowOffsetConfiguration.value(for: self) + shadowLayer.shadowRadius = config.shadowRadiusConfiguration.value(for: self) shadowLayer.name = "dropShadowLayer" shadowLayer.shouldRasterize = true shadowLayer.rasterizationScale = UIScreen.main.scale @@ -92,22 +87,3 @@ extension ViewProtocol where Self: UIView { layer.sublayers?.removeAll { $0.name == "gradientLayer" } } } - -final class AnyConfigurationValue { - - var lightValue: ValueType - var darkValue: ValueType - - public init(_ lightValue: ValueType = ValueType.self, _ darkValue: ValueType = ValueType.self) { - self.lightValue = lightValue - self.darkValue = darkValue - } - - public func getValue(_ object: Any) -> ValueType { - guard let surfaceable = object as? Surfaceable else { - assertionFailure("Self doesn't confirms to Surfaceable") - return lightValue - } - return surfaceable.surface == .light ? lightValue : darkValue - } -} diff --git a/VDS/Utilities/DropShadowConfiguration.swift b/VDS/Utilities/DropShadowConfiguration.swift new file mode 100644 index 00000000..e0cc3dd8 --- /dev/null +++ b/VDS/Utilities/DropShadowConfiguration.swift @@ -0,0 +1,33 @@ +// +// DropShadowConfiguration.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 05/03/24. +// + +import Foundation + +/** + DropShadowConfiguration confirms to DropShadowable where it has configurable properties required for drop shadow +*/ +final class DropShadowConfiguration: DropShadowable, ObjectWithable { + + typealias CGFloatConfigurationValue = SurfaceConfigurationValue + typealias CGSizeConfigurationValue = SurfaceConfigurationValue + + ///Shadow Color configuration for light and dark surfaces + var shadowColorConfiguration: AnyColorable + ///Shadow Opacity configuration for light and dark surfaces + var shadowOpacityConfiguration: CGFloatConfigurationValue + ///Shadow Offset configuration for light and dark surfaces + var shadowOffsetConfiguration: CGSizeConfigurationValue + ///Shadow Radius configuration for light and dark surfaces + var shadowRadiusConfiguration: CGFloatConfigurationValue + + init(shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().eraseToAnyColorable(), shadowOpacity: CGFloatConfigurationValue = CGFloatConfigurationValue(1.0, 1.0), shadowOffset: CGSizeConfigurationValue = CGSizeConfigurationValue(.zero, .zero), shadowRadius: CGFloatConfigurationValue = CGFloatConfigurationValue(1.0, 1.0)) { + self.shadowColorConfiguration = shadowColorConfiguration + self.shadowOpacityConfiguration = shadowOpacity + self.shadowOffsetConfiguration = shadowOffset + self.shadowRadiusConfiguration = shadowRadius + } +} diff --git a/VDS/Utilities/SurfaceConfigurationValue.swift b/VDS/Utilities/SurfaceConfigurationValue.swift new file mode 100644 index 00000000..1b304d2a --- /dev/null +++ b/VDS/Utilities/SurfaceConfigurationValue.swift @@ -0,0 +1,31 @@ +// +// SurfaceConfigurationValue.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 05/03/24. +// + +import Foundation + +/** +SurfaceConfiguration is a type that holds the generic datatype for light surface & dark surface and returns the value based on the surface. +*/ +struct SurfaceConfigurationValue { + + var lightValue: ValueType + var darkValue: ValueType + + public init(_ lightValue: ValueType, _ darkValue: ValueType) { + self.lightValue = lightValue + self.darkValue = darkValue + } + + public init(value: ValueType) { + self.lightValue = value + self.darkValue = value + } + + public func value(for object: Surfaceable) -> ValueType { + object.surface == .light ? lightValue : darkValue + } +} From d34b477274be4f78c27a647097f5bf36ffb292b4 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 5 Mar 2024 13:17:37 -0600 Subject: [PATCH 34/34] refactored var to track internal error Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 8 ++++---- VDS/Components/TextFields/TextArea/TextArea.swift | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 6964ae0a..ae91cc7a 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -154,13 +154,13 @@ open class EntryFieldBase: Control, Changeable, FormFieldable { open var showError: Bool = false { didSet { setNeedsUpdate() } } /// Whether or not to show the internal error - internal var showInternalError: Bool = false { didSet { setNeedsUpdate() } } + open internal(set) var hasInternalError: Bool = false { didSet { setNeedsUpdate() } } /// Override UIControl state to add the .error state if showError is true. open override var state: UIControl.State { get { var state = super.state - if showError || showInternalError { + if showError || hasInternalError { state.insert(.error) } return state @@ -380,7 +380,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldable { } open func updateErrorLabel(){ - if showError, showInternalError, let errorText, let internalErrorText { + if showError, hasInternalError, let errorText, let internalErrorText { errorLabel.text = [internalErrorText, errorText].joined(separator: "\n") errorLabel.surface = surface errorLabel.isEnabled = isEnabled @@ -398,7 +398,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldable { icon.color = VDSColor.paletteBlack icon.surface = surface icon.isHidden = !isEnabled - } else if showInternalError, let internalErrorText { + } else if hasInternalError, let internalErrorText { errorLabel.text = internalErrorText errorLabel.surface = surface errorLabel.isEnabled = isEnabled diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 317748e4..d30c187f 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -247,16 +247,16 @@ open class TextArea: EntryFieldBase { let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)" if let maxLength, maxLength > 0 { if count > maxLength { - showInternalError = true + hasInternalError = true internalErrorText = "You have exceeded the character limit." return countStr } else { - showInternalError = false + hasInternalError = false internalErrorText = nil return ("\(countStr)" + "/" + "\(maxLength)") } } else { - showInternalError = false + hasInternalError = false internalErrorText = nil return nil }