From 82a8a32ccbd1f79c6144b83d7a6bc2a328917dc9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 10:14:42 -0600 Subject: [PATCH 01/76] refactored into base class for dealing with TileContainer Padding --- .../TileContainer/TileContainer.swift | 75 +++++++++++-------- VDS/Components/Tilelet/Tilelet.swift | 52 ++++++++----- 2 files changed, 75 insertions(+), 52 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 98090ae4..0484e815 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -10,8 +10,45 @@ import VDSColorTokens import VDSFormControlsTokens import UIKit +public protocol Valuing { + static var defaultValue: Self { get } + var value: CGFloat { get } +} + @objc(VDSTileContainer) -open class TileContainer: Control { +open class TileContainer: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: Valuing { + case padding2X + case padding4X + case padding6X + case padding8X + case padding12X + case custom(CGFloat) + + public static var defaultValue: Self { .padding4X } + + public var value: CGFloat { + switch self { + case .padding2X: + return VDSLayout.Spacing.space2X.value + case .padding4X: + return VDSLayout.Spacing.space4X.value + case .padding6X: + return VDSLayout.Spacing.space6X.value + case .padding8X: + return VDSLayout.Spacing.space8X.value + case .padding12X: + return VDSLayout.Spacing.space12X.value + case .custom(let padding): + return padding + } + } + } +} + +open class TileContainerBase: Control { //-------------------------------------------------- // MARK: - Initializers @@ -53,33 +90,6 @@ open class TileContainer: Control { case gradient(String, String) case none } - - /// Enum used to describe the padding choices used for this component. - public enum Padding { - case padding2X - case padding4X - case padding6X - case padding8X - case padding12X - case custom(CGFloat) - - public var value: CGFloat { - switch self { - case .padding2X: - return VDSLayout.Spacing.space2X.value - case .padding4X: - return VDSLayout.Spacing.space4X.value - case .padding6X: - return VDSLayout.Spacing.space6X.value - case .padding8X: - return VDSLayout.Spacing.space8X.value - case .padding12X: - return VDSLayout.Spacing.space12X.value - case .custom(let padding): - return padding - } - } - } /// Enum used to describe the aspect ratios used for this component. public enum AspectRatio: String, CaseIterable { @@ -130,7 +140,7 @@ open class TileContainer: Control { open var backgroundEffect: BackgroundEffect = .none { didSet { setNeedsUpdate() } } /// Sets the inside padding for the component - open var padding: Padding = .padding4X { didSet { setNeedsUpdate() } } + open var padding: PaddingType = PaddingType.defaultValue { didSet { setNeedsUpdate() } } /// Applies a background color if backgroundImage prop fails or has trouble loading. open var imageFallbackColor: Surface = .light { didSet { setNeedsUpdate() } } @@ -253,7 +263,6 @@ open class TileContainer: Control { super.reset() shouldUpdateView = false color = .white - padding = .padding4X aspectRatio = .ratio1x1 imageFallbackColor = .light width = nil @@ -395,7 +404,7 @@ open class TileContainer: Control { } -extension TileContainer { +extension TileContainerBase { struct DropshadowConfiguration: Dropshadowable { var shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().with { @@ -408,7 +417,7 @@ extension TileContainer { final class BackgroundColorConfiguration: ObjectColorable { - typealias ObjectType = TileContainer + typealias ObjectType = TileContainerBase let primaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark) let secondaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundSecondaryLight, VDSColor.backgroundSecondaryDark) @@ -418,7 +427,7 @@ extension TileContainer { required init() { } - func getColor(_ object: TileContainer) -> UIColor { + func getColor(_ object: ObjectType) -> UIColor { switch object.color { case .primary: primaryColorConfig.getColor(object.surface) diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 657a735b..3d5fe495 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -16,7 +16,38 @@ import Combine /// while it can include an arrow CTA, it does not require one in order to /// function. @objc(VDSTilelet) -open class Tilelet: TileContainer { +open class Tilelet: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: String, Valuing, CaseIterable { + case small + case large + + public static var defaultValue: Self { .large } + + public var value: CGFloat { + switch self { + case .small: + return UIDevice.isIPad ? VDSLayout.Spacing.space3X.value : VDSLayout.Spacing.space4X.value + case .large: + return UIDevice.isIPad ? VDSLayout.Spacing.space4X.value : VDSLayout.Spacing.space6X.value + } + } + + fileprivate var titleLockupBottomSpacing: CGFloat { + switch self.value { + case VDSLayout.Spacing.space3X.value: + return VDSLayout.Spacing.space4X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space6X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space8X.value + default: + return VDSLayout.Spacing.space4X.value + } + } + } + //-------------------------------------------------- // MARK: - Initializers @@ -392,7 +423,7 @@ open class Tilelet: TileContainer { view = titleLockupContainerView } if let view { - stackView.setCustomSpacing(padding.tiletSpacing, after: view) + stackView.setCustomSpacing(padding.titleLockupBottomSpacing, after: view) } if iconContainerView.superview == nil { stackView.addArrangedSubview(iconContainerView) @@ -403,20 +434,3 @@ open class Tilelet: TileContainer { } } } - -extension TileContainer.Padding { - fileprivate var tiletSpacing: CGFloat { - switch self { - case .padding2X: - return 16 - case .padding4X: - return 24 - case .padding6X: - return 32 - case .padding8X: - return 48 - default: - return 16 - } - } -} From add8e974c02762950aba14ce2a48645f83160c70 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Fri, 1 Mar 2024 17:17:04 +0530 Subject: [PATCH 02/76] added tilelet linebreak mode config & text position --- VDS.xcodeproj/project.pbxproj | 4 + VDS/Components/Badge/Badge.swift | 1 + VDS/Components/Tilelet/Tilelet.swift | 121 ++++++++++++++++-- .../Tilelet/TileletBadgeModel.swift | 7 +- .../Tilelet/TileletSubTitleModel.swift | 13 +- .../Tilelet/TileletTitleModel.swift | 19 ++- VDS/Components/Tilelet/TitleletChangeLog.txt | 110 ++++++++++++++++ VDS/Components/TitleLockup/TitleLockup.swift | 4 +- .../TitleLockupSubTitleModel.swift | 8 +- .../TitleLockup/TitleLockupTitleModel.swift | 8 +- 10 files changed, 275 insertions(+), 20 deletions(-) create mode 100644 VDS/Components/Tilelet/TitleletChangeLog.txt diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index ef6acb80..5d78557f 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 44604AD729CE196600E62B51 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD629CE196600E62B51 /* Line.swift */; }; 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; }; 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; + 710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 710607942B91A99500F2863F /* TitleletChangeLog.txt */; }; 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 */; }; @@ -180,6 +181,7 @@ 44604AD629CE196600E62B51 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = ""; }; 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; + 710607942B91A99500F2863F /* TitleletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TitleletChangeLog.txt; sourceTree = ""; }; 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 = ""; }; @@ -676,6 +678,7 @@ EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */, EA985BE72968951C00F2FF2E /* TileletTitleModel.swift */, EA985C2C296F03FE00F2FF2E /* TileletIconModels.swift */, + 710607942B91A99500F2863F /* TitleletChangeLog.txt */, ); path = Tilelet; sourceTree = ""; @@ -947,6 +950,7 @@ 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */, EAEEECA72B1F952000531FC2 /* TabsChangeLog.txt in Resources */, EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */, + 710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */, EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */, EAEEEC982B1F8DD100531FC2 /* LineChangeLog.txt in Resources */, EAEEECA22B1F92AD00531FC2 /* LabelChangeLog.txt in Resources */, diff --git a/VDS/Components/Badge/Badge.swift b/VDS/Components/Badge/Badge.swift index f6bb36d5..bbe1233d 100644 --- a/VDS/Components/Badge/Badge.swift +++ b/VDS/Components/Badge/Badge.swift @@ -147,6 +147,7 @@ open class Badge: View { label.widthGreaterThanEqualTo(constant: minWidth) maxWidthConstraint = label.widthLessThanEqualTo(constant: 0).with { $0.isActive = false } + clipsToBounds = true } /// Resets to default settings. diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 657a735b..e562ff0a 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -39,6 +39,7 @@ open class Tilelet: TileContainer { /// Enum to represent the Vertical Layout of the Text. public enum TextPosition: String, CaseIterable { case top + case middle case bottom } @@ -72,7 +73,7 @@ open class Tilelet: TileContainer { //-------------------------------------------------- // MARK: - Public Properties - //-------------------------------------------------- + //-------------------------------------------------- public override var onClickSubscriber: AnyCancellable? { didSet { isAccessibilityElement = onClickSubscriber != nil @@ -82,6 +83,27 @@ open class Tilelet: TileContainer { /// Title lockup positioned in the contentView. open var titleLockup = TitleLockup().with { $0.standardStyleConfiguration = .init(styleConfigurations: [ + .init(deviceType: .iPhone, + titleStandardStyles: [.bodySmall], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodySmall], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), + .init(deviceType: .iPhone, + titleStandardStyles: [.bodyMedium], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyMedium], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), + .init(deviceType: .iPhone, + titleStandardStyles: [.bodyLarge], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyLarge], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), .init(deviceType: .iPhone, titleStandardStyles: [.titleSmall], spacingConfigurations: [ @@ -103,6 +125,27 @@ open class Tilelet: TileContainer { topSpacing: VDSLayout.Spacing.space3X.value, bottomSpacing: VDSLayout.Spacing.space3X.value) ]), + .init(deviceType: .iPad, + titleStandardStyles: [.bodySmall], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodySmall], + topSpacing: VDSLayout.Spacing.space2X.value, + bottomSpacing: VDSLayout.Spacing.space2X.value) + ]), + .init(deviceType: .iPad, + titleStandardStyles: [.bodyMedium], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyMedium], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), + .init(deviceType: .iPad, + titleStandardStyles: [.bodyLarge], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyLarge], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), .init(deviceType: .iPad, titleStandardStyles: [.titleSmall, .titleMedium], spacingConfigurations: [ @@ -206,6 +249,12 @@ open class Tilelet: TileContainer { //-------------------------------------------------- internal var titleLockupWidthConstraint: NSLayoutConstraint? internal var titleLockupTrailingConstraint: NSLayoutConstraint? + internal var titleLockupTopConstraint: NSLayoutConstraint? + internal var titleLockupBottomConstraint: NSLayoutConstraint? + internal var titleLockupTopGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupBottomGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupCenterYConstraint: NSLayoutConstraint? + internal var titleLockupTitleLabelBottomConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Overrides @@ -230,11 +279,16 @@ open class Tilelet: TileContainer { titleLockupContainerView.addSubview(titleLockup) titleLockup - .pinTop() .pinLeading() - .pinBottom() + titleLockupTopConstraint = titleLockup.topAnchor.constraint(equalTo: titleLockupContainerView.topAnchor) + titleLockupTopConstraint?.activate() + titleLockupBottomConstraint = titleLockupContainerView.bottomAnchor.constraint(equalTo: titleLockup.bottomAnchor) + titleLockupBottomConstraint?.activate() titleLockupTrailingConstraint = titleLockup.trailingAnchor.constraint(equalTo: titleLockupContainerView.trailingAnchor) titleLockupTrailingConstraint?.isActive = true + titleLockupBottomGreaterThanConstraint = titleLockupContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLockup.bottomAnchor) + titleLockupTopGreaterThanConstraint = titleLockup.topAnchor.constraint(greaterThanOrEqualTo: titleLockupContainerView.topAnchor) + titleLockupCenterYConstraint = titleLockup.centerYAnchor.constraint(equalTo: titleLockupContainerView.centerYAnchor) iconContainerView.addSubview(descriptiveIcon) iconContainerView.addSubview(directionalIcon) @@ -249,6 +303,12 @@ open class Tilelet: TileContainer { .pinTop() .pinBottom() + padding = .custom( UIDevice.isIPad ? VDSLayout.Spacing.space6X.value : VDSLayout.Spacing.space4X.value) + //If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). + var labelPriority = UILayoutPriority.defaultHigh.rawValue + titleLockup.titleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority), for: .vertical) + badge.label.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-1), for: .vertical) + titleLockup.subTitleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-2), for: .vertical) } /// Resets to default settings. @@ -261,7 +321,7 @@ open class Tilelet: TileContainer { titleModel = nil subTitleModel = nil descriptiveIconModel = nil - directionalIconModel = nil + directionalIconModel = nil shouldUpdateView = true setNeedsUpdate() } @@ -273,7 +333,11 @@ open class Tilelet: TileContainer { updateBadge() updateTitleLockup() updateIcons() - + ///Content-driven height Tilelets - Minimum height is configurable. + ///if width != nil && (aspectRatio != .none || height != nil) then tilelet is not self growing, so we can apply text position alignments. + if width != nil && (aspectRatio != .none || height != nil) { + updateTextPositionAlignment() + } layoutIfNeeded() } @@ -401,20 +465,59 @@ open class Tilelet: TileContainer { } else { removeFromSuperview(iconContainerView) } + if let lastElement = titleLockup.lastElement { + titleLockupTitleLabelBottomConstraint?.deactivate() + let anchorConstraint = showIconContainerView ? iconContainerView.topAnchor : containerView.bottomAnchor + titleLockupTitleLabelBottomConstraint = anchorConstraint.constraint(greaterThanOrEqualTo: lastElement.bottomAnchor) + titleLockupTitleLabelBottomConstraint?.activate() + } + } + + private func updateTextPositionAlignment() { + switch textPostion { + case .top: + titleLockupTopConstraint?.activate() + titleLockupTopGreaterThanConstraint?.deactivate() + titleLockupBottomConstraint?.deactivate() + titleLockupBottomGreaterThanConstraint?.activate() + titleLockupCenterYConstraint?.deactivate() + case .middle: + titleLockupTopConstraint?.deactivate() + titleLockupTopGreaterThanConstraint?.activate() + titleLockupBottomConstraint?.deactivate() + titleLockupBottomGreaterThanConstraint?.activate() + titleLockupCenterYConstraint?.activate() + case .bottom: + titleLockupTopConstraint?.deactivate() + titleLockupTopGreaterThanConstraint?.activate() + titleLockupBottomConstraint?.activate() + titleLockupBottomGreaterThanConstraint?.deactivate() + titleLockupCenterYConstraint?.deactivate() + } + } +} + +extension TitleLockup { + + fileprivate var lastElement: UIView? { + guard subTitleModel == nil else { return subTitleLabel } + guard titleModel == nil else { return titleLabel } + return nil } } extension TileContainer.Padding { + fileprivate var tiletSpacing: CGFloat { switch self { - case .padding2X: - return 16 case .padding4X: return 24 case .padding6X: return 32 - case .padding8X: - return 48 + case .custom(let padding): + if padding == VDSLayout.Spacing.space3X.value { + return 16 + } else { fallthrough } default: return 16 } diff --git a/VDS/Components/Tilelet/TileletBadgeModel.swift b/VDS/Components/Tilelet/TileletBadgeModel.swift index f58c134c..9dbd03fe 100644 --- a/VDS/Components/Tilelet/TileletBadgeModel.swift +++ b/VDS/Components/Tilelet/TileletBadgeModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension Tilelet { @@ -26,12 +27,16 @@ extension Tilelet { /// Max width that will be used for the badge. public var maxWidth: CGFloat? - public init(text: String, fillColor: Badge.FillColor = .red, surface: Surface = .light, numberOfLines: Int = 0, maxWidth: CGFloat? = nil) { + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + + public init(text: String, fillColor: Badge.FillColor = .red, surface: Surface = .light, numberOfLines: Int = 0, maxWidth: CGFloat? = nil, lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.fillColor = fillColor self.surface = surface self.numberOfLines = numberOfLines self.maxWidth = maxWidth + self.lineBreakMode = lineBreakMode } } } diff --git a/VDS/Components/Tilelet/TileletSubTitleModel.swift b/VDS/Components/Tilelet/TileletSubTitleModel.swift index dab4c81c..63820316 100644 --- a/VDS/Components/Tilelet/TileletSubTitleModel.swift +++ b/VDS/Components/Tilelet/TileletSubTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension Tilelet { /// Model that represents the options available for the sub title label. @@ -18,7 +19,8 @@ extension Tilelet { case bodyLarge case bodyMedium case bodySmall - + case titleSmall + case titleMedium public var defaultValue: TitleLockup.OtherStandardStyle { .bodySmall } } //-------------------------------------------------- @@ -36,17 +38,22 @@ extension Tilelet { /// Text color that will be used for the subTitle label. public var textColor: Use = .primary + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- public init(text: String, textColor: Use = .primary, textAttributes: [any LabelAttributeModel]? = nil, - standardStyle: StandardStyle = .bodySmall) { + standardStyle: StandardStyle = .bodySmall, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.textAttributes = textAttributes self.textColor = textColor self.standardStyle = standardStyle + self.lineBreakMode = lineBreakMode } //-------------------------------------------------- @@ -57,7 +64,7 @@ extension Tilelet { TitleLockup.SubTitleModel(text: text, standardStyle: standardStyle.value, textColor: textColor, - textAttributes: textAttributes) + textAttributes: textAttributes, lineBreakMode: lineBreakMode) } } } diff --git a/VDS/Components/Tilelet/TileletTitleModel.swift b/VDS/Components/Tilelet/TileletTitleModel.swift index 728de07b..fa8c603d 100644 --- a/VDS/Components/Tilelet/TileletTitleModel.swift +++ b/VDS/Components/Tilelet/TileletTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension Tilelet { /// Model that represents the options available for the title label. @@ -19,7 +20,10 @@ extension Tilelet { case titleLarge case titleMedium case titleSmall - + case bodyLarge + case bodyMedium + case bodySmall + public var defaultValue: TitleLockup.TitleStandardStyle { .titleSmall } } //-------------------------------------------------- @@ -28,21 +32,30 @@ extension Tilelet { /// Text that will be used for the title label. public var text: String = "" + /// Used in combination with standardStyle to set the textStyle that will be used for the title label. + public var isBold: Bool = false /// Text attributes that will be used for the title label. public var textAttributes: [any LabelAttributeModel]? /// Text style that will be used for the title label. public var standardStyle: StandardStyle = .titleSmall + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- public init(text: String, textAttributes: [any LabelAttributeModel]? = nil, - standardStyle: StandardStyle = .titleSmall) { + isBold: Bool = true, + standardStyle: StandardStyle = .titleSmall, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.textAttributes = textAttributes self.standardStyle = standardStyle + self.isBold = isBold + self.lineBreakMode = lineBreakMode } //-------------------------------------------------- @@ -52,7 +65,7 @@ extension Tilelet { public func toTitleLockupTitleModel() -> TitleLockup.TitleModel { TitleLockup.TitleModel(text: text, textAttributes: textAttributes, - standardStyle: standardStyle.value) + isBold: isBold, standardStyle: standardStyle.value, lineBreakMode: lineBreakMode) } } } diff --git a/VDS/Components/Tilelet/TitleletChangeLog.txt b/VDS/Components/Tilelet/TitleletChangeLog.txt new file mode 100644 index 00000000..bbbc8edf --- /dev/null +++ b/VDS/Components/Tilelet/TitleletChangeLog.txt @@ -0,0 +1,110 @@ +MM/DD/YYYY +---------------- + +08/31/2022 +---------------- +- Updates to Typography and default Title Lockup configurations: +- Default +- Regular title updated to bold weight +- Subtitle updated to primary color +- Inverted +- Regular title updated to bold weight +- Subtitle updated to primary color + +07/15/2022 +---------------- +- Updates to Typography and default Title Lockup configurations: +- Default +- Bold Title updated to Regular weight +- Subtitle updated to secondary color +- Inverted +- Bold Title updated to Regular weight +- Subtitle updated to secondary color + +04/27/2022 +---------------- +- Updates to Title Lockup configurations. + +04/14/2022 +---------------- +- Updates to Tilelet Group spec based on 4/14 handoff feedback. + +04/04/2022 +---------------- +- Visuals added to Tilelet Group spec. + +04/01/2022 +---------------- +- Updates based on 3/31 handoff feedback. + +03/30/2022 +---------------- +- Tilelet added to Test App. + +03/29/2022 +---------------- +- Interactive states updated. + +03/22/2022 +---------------- +- Layout and spacing page added. + +03/21/2022 +---------------- +- Updated anatomy, configuration, and element pages. + +02/21/2022 +---------------- +- Title Lockup’s width properties added. + +09/14/2022 +---------------- +- States section updated to reflect states inherited from Tile Container +- Dev note added to Layouts. + +11/30/2022 +---------------- +- Added "(web only)" to any instance of "keyboard focus" + +12/13/2022 +---------------- +- Replaced focus border pixel and style & spacing values with tokens. + +06/19/2023 +---------------- +- Updated anatomy element names to be local to Tilelet and indicated the core component that powers them under the hood. + +09/21/2023 +---------------- +- Updated Anatomy to include Title Lockup with Eyebrow and Tooltip +- Updated Configurations to include Title Lockup section +- Updated Layout and spacing to include Alignment (Left, Center) and textPosition (Middle)
 +- Updated Elements to include Eyebrow and uniform 1:1 pairings + +10/09/2023 +---------------- +- Updated Configurations VDS Title Lockup and VDS Tile Container sections. + +10/13/2023 +---------------- +- SPEC format changes to Anatomy to include nested component configurations +- Removed Configurations page + +11/20/2023 +---------------- +- Updated visuals to reflect new corner radius value - 12px +- Updated focus border corner radius to 14px +- View changes + +12/14/2023 +---------------- +- Added secondary, primary backgroundColor options +- Relabeled configurations section headings to use property names +- Relabeled surface sections to use “surface:” labeling convention +- Deprecated gray backgroundColor +- View changes + +02/08/2024 +---------------- +- Added external-icon option to Descriptive Icon in Configurations +- View changes diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index dbfd8637..6dc42e70 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -350,7 +350,7 @@ open class TitleLockup: View { titleLabel.attributes = titleModel.textAttributes titleLabel.numberOfLines = titleModel.numberOfLines titleLabel.surface = surface - + titleLabel.lineBreakMode = titleModel.lineBreakMode addSubview(titleLabel) titleLabel .pinTop(previousView?.bottomAnchor ?? self.topAnchor, eyebrowLabel == previousView ? topSpacing : 0) @@ -369,7 +369,7 @@ open class TitleLockup: View { subTitleLabel.attributes = subTitleModel.textAttributes subTitleLabel.numberOfLines = subTitleModel.numberOfLines subTitleLabel.surface = surface - + subTitleLabel.lineBreakMode = subTitleModel.lineBreakMode addSubview(subTitleLabel) subTitleLabel .pinTop(previousView?.bottomAnchor ?? self.topAnchor, (eyebrowLabel == previousView || titleLabel == previousView) ? bottomSpacing : 0) diff --git a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift index 3ba6c7ff..5ad105ef 100644 --- a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension TitleLockup { /// Model that represents the options available for the sub title label. @@ -25,16 +26,21 @@ extension TitleLockup { /// Number of lines used in the subtitle label. public var numberOfLines: Int + /// LineBreakMode used in subtitle label. + public var lineBreakMode: NSLineBreakMode + public init(text: String, standardStyle: OtherStandardStyle = .bodyLarge, textColor: Use = .primary, textAttributes: [any LabelAttributeModel]? = nil, - numberOfLines: Int = 0) { + numberOfLines: Int = 0, + lineBreakMode: NSLineBreakMode = .byWordWrapping) { self.text = text self.standardStyle = standardStyle self.textColor = textColor self.textAttributes = textAttributes self.numberOfLines = numberOfLines + self.lineBreakMode = lineBreakMode } /// TextStyle used to render the text. diff --git a/VDS/Components/TitleLockup/TitleLockupTitleModel.swift b/VDS/Components/TitleLockup/TitleLockupTitleModel.swift index 2b957276..7541151f 100644 --- a/VDS/Components/TitleLockup/TitleLockupTitleModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension TitleLockup { /// Model that represents the options available for the sub title label. @@ -25,16 +26,21 @@ extension TitleLockup { /// Number of lines that will be used for the title label. public var numberOfLines: Int + /// LineBreakMode used in subtitle label. + public var lineBreakMode: NSLineBreakMode + public init(text: String, textAttributes: [any LabelAttributeModel]? = nil, isBold: Bool = true, standardStyle: TitleStandardStyle = .featureXSmall, - numberOfLines: Int = 0) { + numberOfLines: Int = 0, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.isBold = isBold self.textAttributes = textAttributes self.standardStyle = standardStyle self.numberOfLines = numberOfLines + self.lineBreakMode = lineBreakMode } /// TextStyle used to render the text. From 963e31acd3e98759c0b815b6e5507ecb6bff0fe5 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 1 Mar 2024 14:28:17 -0600 Subject: [PATCH 03/76] Adding the padding fix between TileContainer/Tilelet Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 8 +++ .../TileContainer/TileContainer.swift | 70 ++++++++++--------- VDS/Components/Tilelet/Tilelet.swift | 54 ++++++++------ VDS/Extensions/VDSLayout.swift | 4 +- VDS/Protocols/EnumSubset.swift | 2 +- VDS/Protocols/EnumValuing.swift | 12 ++++ VDS/Protocols/Valuing.swift | 13 ++++ 7 files changed, 107 insertions(+), 56 deletions(-) create mode 100644 VDS/Protocols/EnumValuing.swift create mode 100644 VDS/Protocols/Valuing.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 5d78557f..4b863360 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -99,6 +99,8 @@ EAA5EEF128F5C909003B3210 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */; }; EAA5EEF328F5C909003B3210 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */; }; EAA7456C2AB23E2000C1841F /* TooltipModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA7456B2AB23E2000C1841F /* TooltipModel.swift */; }; + EAACB8982B92706F006A3869 /* EnumValuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8972B92706F006A3869 /* EnumValuing.swift */; }; + EAACB89A2B927108006A3869 /* Valuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8992B927108006A3869 /* Valuing.swift */; }; EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */; }; EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CC28ABE76000DAE764 /* Withable.swift */; }; EAB1D2CF28ABEF2B00DAE764 /* Typography+Base.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CE28ABEF2B00DAE764 /* Typography+Base.swift */; }; @@ -268,6 +270,8 @@ EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; EAA7456B2AB23E2000C1841F /* TooltipModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipModel.swift; sourceTree = ""; }; + EAACB8972B92706F006A3869 /* EnumValuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumValuing.swift; sourceTree = ""; }; + EAACB8992B927108006A3869 /* Valuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Valuing.swift; sourceTree = ""; }; EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = ""; }; EAB1D2CC28ABE76000DAE764 /* Withable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = ""; }; EAB1D2CE28ABEF2B00DAE764 /* Typography+Base.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Typography+Base.swift"; sourceTree = ""; }; @@ -552,6 +556,7 @@ EA3361A9288B25E40071C351 /* Disabling.swift */, EAF978202A99035B00C2FEA9 /* Enabling.swift */, EA5E305929510F8B0082B959 /* EnumSubset.swift */, + EAACB8972B92706F006A3869 /* EnumValuing.swift */, EAF7F0A1289AFB3900B287F5 /* Errorable.swift */, EA3361AE288B26310071C351 /* FormFieldable.swift */, EA33624628931B050071C351 /* Initable.swift */, @@ -562,6 +567,7 @@ EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */, EAB1D2CC28ABE76000DAE764 /* Withable.swift */, 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */, + EAACB8992B927108006A3869 /* Valuing.swift */, 71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */, ); path = Protocols; @@ -1001,6 +1007,7 @@ EAC9258C2911C9DE00091998 /* InputField.swift in Sources */, EA3362402892EF6C0071C351 /* Label.swift in Sources */, EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */, + EAACB8982B92706F006A3869 /* EnumValuing.swift in Sources */, EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */, EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */, 71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */, @@ -1016,6 +1023,7 @@ EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */, EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */, + EAACB89A2B927108006A3869 /* Valuing.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 98090ae4..27cfa1f4 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -11,7 +11,39 @@ import VDSFormControlsTokens import UIKit @objc(VDSTileContainer) -open class TileContainer: Control { +open class TileContainer: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: EnumValuing { + case padding2X + case padding4X + case padding6X + case padding8X + case padding12X + case custom(CGFloat) + + public static var defaultValue: Self { .padding4X } + + public var value: CGFloat { + switch self { + case .padding2X: + return VDSLayout.Spacing.space2X.value + case .padding4X: + return VDSLayout.Spacing.space4X.value + case .padding6X: + return VDSLayout.Spacing.space6X.value + case .padding8X: + return VDSLayout.Spacing.space8X.value + case .padding12X: + return VDSLayout.Spacing.space12X.value + case .custom(let padding): + return padding + } + } + } +} + +open class TileContainerBase: Control where PaddingType.ValueType == CGFloat { //-------------------------------------------------- // MARK: - Initializers @@ -53,33 +85,6 @@ open class TileContainer: Control { case gradient(String, String) case none } - - /// Enum used to describe the padding choices used for this component. - public enum Padding { - case padding2X - case padding4X - case padding6X - case padding8X - case padding12X - case custom(CGFloat) - - public var value: CGFloat { - switch self { - case .padding2X: - return VDSLayout.Spacing.space2X.value - case .padding4X: - return VDSLayout.Spacing.space4X.value - case .padding6X: - return VDSLayout.Spacing.space6X.value - case .padding8X: - return VDSLayout.Spacing.space8X.value - case .padding12X: - return VDSLayout.Spacing.space12X.value - case .custom(let padding): - return padding - } - } - } /// Enum used to describe the aspect ratios used for this component. public enum AspectRatio: String, CaseIterable { @@ -130,7 +135,7 @@ open class TileContainer: Control { open var backgroundEffect: BackgroundEffect = .none { didSet { setNeedsUpdate() } } /// Sets the inside padding for the component - open var padding: Padding = .padding4X { didSet { setNeedsUpdate() } } + open var padding: PaddingType = PaddingType.defaultValue { didSet { setNeedsUpdate() } } /// Applies a background color if backgroundImage prop fails or has trouble loading. open var imageFallbackColor: Surface = .light { didSet { setNeedsUpdate() } } @@ -253,7 +258,6 @@ open class TileContainer: Control { super.reset() shouldUpdateView = false color = .white - padding = .padding4X aspectRatio = .ratio1x1 imageFallbackColor = .light width = nil @@ -395,7 +399,7 @@ open class TileContainer: Control { } -extension TileContainer { +extension TileContainerBase { struct DropshadowConfiguration: Dropshadowable { var shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().with { @@ -408,7 +412,7 @@ extension TileContainer { final class BackgroundColorConfiguration: ObjectColorable { - typealias ObjectType = TileContainer + typealias ObjectType = TileContainerBase let primaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark) let secondaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundSecondaryLight, VDSColor.backgroundSecondaryDark) @@ -418,7 +422,7 @@ extension TileContainer { required init() { } - func getColor(_ object: TileContainer) -> UIColor { + func getColor(_ object: ObjectType) -> UIColor { switch object.color { case .primary: primaryColorConfig.getColor(object.surface) diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index e562ff0a..77d6c70e 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -16,7 +16,38 @@ import Combine /// while it can include an arrow CTA, it does not require one in order to /// function. @objc(VDSTilelet) -open class Tilelet: TileContainer { +open class Tilelet: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: String, EnumValuing, CaseIterable { + case small + case large + + public static var defaultValue: Self { .large } + + public var value: CGFloat { + switch self { + case .small: + return UIDevice.isIPad ? VDSLayout.Spacing.space3X.value : VDSLayout.Spacing.space4X.value + case .large: + return UIDevice.isIPad ? VDSLayout.Spacing.space4X.value : VDSLayout.Spacing.space6X.value + } + } + + fileprivate var titleLockupBottomSpacing: CGFloat { + switch self.value { + case VDSLayout.Spacing.space3X.value: + return VDSLayout.Spacing.space4X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space6X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space8X.value + default: + return VDSLayout.Spacing.space4X.value + } + } + } + //-------------------------------------------------- // MARK: - Initializers @@ -303,7 +334,6 @@ open class Tilelet: TileContainer { .pinTop() .pinBottom() - padding = .custom( UIDevice.isIPad ? VDSLayout.Spacing.space6X.value : VDSLayout.Spacing.space4X.value) //If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). var labelPriority = UILayoutPriority.defaultHigh.rawValue titleLockup.titleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority), for: .vertical) @@ -456,7 +486,7 @@ open class Tilelet: TileContainer { view = titleLockupContainerView } if let view { - stackView.setCustomSpacing(padding.tiletSpacing, after: view) + stackView.setCustomSpacing(padding.titleLockupBottomSpacing, after: view) } if iconContainerView.superview == nil { stackView.addArrangedSubview(iconContainerView) @@ -505,21 +535,3 @@ extension TitleLockup { return nil } } - -extension TileContainer.Padding { - - fileprivate var tiletSpacing: CGFloat { - switch self { - case .padding4X: - return 24 - case .padding6X: - return 32 - case .custom(let padding): - if padding == VDSLayout.Spacing.space3X.value { - return 16 - } else { fallthrough } - default: - return 16 - } - } -} diff --git a/VDS/Extensions/VDSLayout.swift b/VDS/Extensions/VDSLayout.swift index f30a0635..30df201b 100644 --- a/VDS/Extensions/VDSLayout.swift +++ b/VDS/Extensions/VDSLayout.swift @@ -10,7 +10,9 @@ import Foundation /// Represents constants used that deal with layout. public struct VDSLayout { /// Enum used to describe the spacing constants. - public enum Spacing: String, CaseIterable { + public enum Spacing: String, CaseIterable, EnumValuing { + public static var defaultValue: VDSLayout.Spacing = .space2X + case space1X case space2X case space3X diff --git a/VDS/Protocols/EnumSubset.swift b/VDS/Protocols/EnumSubset.swift index a690fca3..831ce1fb 100644 --- a/VDS/Protocols/EnumSubset.swift +++ b/VDS/Protocols/EnumSubset.swift @@ -7,7 +7,7 @@ import Foundation -public protocol EnumSubset: RawRepresentable, CaseIterable { +public protocol EnumSubset: RawRepresentable, CaseIterable, Valuing { associatedtype T:RawRepresentable var defaultValue: T { get } } diff --git a/VDS/Protocols/EnumValuing.swift b/VDS/Protocols/EnumValuing.swift new file mode 100644 index 00000000..b8f27824 --- /dev/null +++ b/VDS/Protocols/EnumValuing.swift @@ -0,0 +1,12 @@ +// +// EnumValuing.swift +// VDS +// +// Created by Matt Bruce on 3/1/24. +// + +import Foundation + +public protocol EnumValuing: Valuing { + static var defaultValue: Self { get } +} diff --git a/VDS/Protocols/Valuing.swift b/VDS/Protocols/Valuing.swift new file mode 100644 index 00000000..61cea9e8 --- /dev/null +++ b/VDS/Protocols/Valuing.swift @@ -0,0 +1,13 @@ +// +// Valuing.swift +// VDS +// +// Created by Matt Bruce on 3/1/24. +// + +import Foundation + +public protocol Valuing { + associatedtype ValueType + var value: ValueType { get } +} From 42ad973396001ba55c7cfe9111b5f5a8a6b02545 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 1 Mar 2024 14:43:32 -0600 Subject: [PATCH 04/76] refactored enum Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 8 ++++---- VDS/Protocols/{EnumValuing.swift => DefaultValuing.swift} | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) rename VDS/Protocols/{EnumValuing.swift => DefaultValuing.swift} (52%) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 4b863360..d6c5f3f5 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -99,7 +99,7 @@ EAA5EEF128F5C909003B3210 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */; }; EAA5EEF328F5C909003B3210 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */; }; EAA7456C2AB23E2000C1841F /* TooltipModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA7456B2AB23E2000C1841F /* TooltipModel.swift */; }; - EAACB8982B92706F006A3869 /* EnumValuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8972B92706F006A3869 /* EnumValuing.swift */; }; + EAACB8982B92706F006A3869 /* DefaultValuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8972B92706F006A3869 /* DefaultValuing.swift */; }; EAACB89A2B927108006A3869 /* Valuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8992B927108006A3869 /* Valuing.swift */; }; EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */; }; EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CC28ABE76000DAE764 /* Withable.swift */; }; @@ -270,7 +270,7 @@ EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; EAA7456B2AB23E2000C1841F /* TooltipModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipModel.swift; sourceTree = ""; }; - EAACB8972B92706F006A3869 /* EnumValuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumValuing.swift; sourceTree = ""; }; + EAACB8972B92706F006A3869 /* DefaultValuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultValuing.swift; sourceTree = ""; }; EAACB8992B927108006A3869 /* Valuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Valuing.swift; sourceTree = ""; }; EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = ""; }; EAB1D2CC28ABE76000DAE764 /* Withable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = ""; }; @@ -553,10 +553,10 @@ EAF1FE9A29DB1A6000101452 /* Changeable.swift */, EAF1FE9829D4850E00101452 /* Clickable.swift */, EAA5EEDF28F49DB3003B3210 /* Colorable.swift */, + EAACB8972B92706F006A3869 /* DefaultValuing.swift */, EA3361A9288B25E40071C351 /* Disabling.swift */, EAF978202A99035B00C2FEA9 /* Enabling.swift */, EA5E305929510F8B0082B959 /* EnumSubset.swift */, - EAACB8972B92706F006A3869 /* EnumValuing.swift */, EAF7F0A1289AFB3900B287F5 /* Errorable.swift */, EA3361AE288B26310071C351 /* FormFieldable.swift */, EA33624628931B050071C351 /* Initable.swift */, @@ -1007,7 +1007,7 @@ EAC9258C2911C9DE00091998 /* InputField.swift in Sources */, EA3362402892EF6C0071C351 /* Label.swift in Sources */, EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */, - EAACB8982B92706F006A3869 /* EnumValuing.swift in Sources */, + EAACB8982B92706F006A3869 /* DefaultValuing.swift in Sources */, EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */, EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */, 71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */, diff --git a/VDS/Protocols/EnumValuing.swift b/VDS/Protocols/DefaultValuing.swift similarity index 52% rename from VDS/Protocols/EnumValuing.swift rename to VDS/Protocols/DefaultValuing.swift index b8f27824..5f8bcb72 100644 --- a/VDS/Protocols/EnumValuing.swift +++ b/VDS/Protocols/DefaultValuing.swift @@ -7,6 +7,8 @@ import Foundation -public protocol EnumValuing: Valuing { +public protocol DefaultValuing: Valuing { static var defaultValue: Self { get } } + +public protocol CGFloatDefaultValuing: DefaultValuing where ValueType == CGFloat {} From 275a57d510b270119951ceb7ae0ba5c1d9bf771f Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 1 Mar 2024 14:44:24 -0600 Subject: [PATCH 05/76] updated spacing Signed-off-by: Matt Bruce --- VDS/Extensions/VDSLayout.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/VDS/Extensions/VDSLayout.swift b/VDS/Extensions/VDSLayout.swift index 30df201b..26ada9c4 100644 --- a/VDS/Extensions/VDSLayout.swift +++ b/VDS/Extensions/VDSLayout.swift @@ -10,8 +10,7 @@ import Foundation /// Represents constants used that deal with layout. public struct VDSLayout { /// Enum used to describe the spacing constants. - public enum Spacing: String, CaseIterable, EnumValuing { - public static var defaultValue: VDSLayout.Spacing = .space2X + public enum Spacing: String, CaseIterable, Valuing { case space1X case space2X From 317173917f23d3738d29f0932c03826903ad00eb Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 1 Mar 2024 14:46:41 -0600 Subject: [PATCH 06/76] updated enums Signed-off-by: Matt Bruce --- VDS/Components/TileContainer/TileContainer.swift | 4 ++-- VDS/Components/Tilelet/Tilelet.swift | 2 +- VDS/Protocols/DefaultValuing.swift | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 27cfa1f4..6cf7d24f 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -14,7 +14,7 @@ import UIKit open class TileContainer: TileContainerBase { /// Enum used to describe the padding choices used for this component. - public enum Padding: EnumValuing { + public enum Padding: DefaultValuing { case padding2X case padding4X case padding6X @@ -43,7 +43,7 @@ open class TileContainer: TileContainerBase { } } -open class TileContainerBase: Control where PaddingType.ValueType == CGFloat { +open class TileContainerBase: Control where PaddingType.ValueType == CGFloat { //-------------------------------------------------- // MARK: - Initializers diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 77d6c70e..f8a3b4db 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -19,7 +19,7 @@ import Combine open class Tilelet: TileContainerBase { /// Enum used to describe the padding choices used for this component. - public enum Padding: String, EnumValuing, CaseIterable { + public enum Padding: String, DefaultValuing, CaseIterable { case small case large diff --git a/VDS/Protocols/DefaultValuing.swift b/VDS/Protocols/DefaultValuing.swift index 5f8bcb72..ffeaf0eb 100644 --- a/VDS/Protocols/DefaultValuing.swift +++ b/VDS/Protocols/DefaultValuing.swift @@ -10,5 +10,3 @@ import Foundation public protocol DefaultValuing: Valuing { static var defaultValue: Self { get } } - -public protocol CGFloatDefaultValuing: DefaultValuing where ValueType == CGFloat {} From 40883825b5b72eba5c7ed0c30db2982e850512de Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Sat, 2 Mar 2024 18:26:00 +0530 Subject: [PATCH 07/76] added new Pagination component --- VDS.xcodeproj/project.pbxproj | 12 + VDS/Components/Pagination/Pagination.swift | 228 ++++++++++++++++++ .../Contents.json | 12 + .../pagination-arrow-left.svg | 10 + .../Contents.json | 12 + .../pagination-arrow-right.svg | 3 + 6 files changed, 277 insertions(+) create mode 100644 VDS/Components/Pagination/Pagination.swift create mode 100644 VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/Contents.json create mode 100644 VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/pagination-arrow-left.svg create mode 100644 VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/Contents.json create mode 100644 VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/pagination-arrow-right.svg diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 366fca71..79deb46b 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -16,6 +16,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 */; }; + 71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71B23C2C2B91FA690027F7D9 /* Pagination.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 */; }; @@ -184,6 +185,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 = ""; }; + 71B23C2C2B91FA690027F7D9 /* Pagination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pagination.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 = ""; }; @@ -384,6 +386,14 @@ path = Button; sourceTree = ""; }; + 71B23C2B2B91FA510027F7D9 /* Pagination */ = { + isa = PBXGroup; + children = ( + 71B23C2C2B91FA690027F7D9 /* Pagination.swift */, + ); + path = Pagination; + sourceTree = ""; + }; EA0B17FF2A9E21CA00F2D0CD /* Selector */ = { isa = PBXGroup; children = ( @@ -487,6 +497,7 @@ EA33619D288B1E330071C351 /* Components */ = { isa = PBXGroup; children = ( + 71B23C2B2B91FA510027F7D9 /* Pagination */, EA4DB2FE28DCBC1900103EE3 /* Badge */, EAD062AE2A3B87210015965D /* BadgeIndicator */, EA0FC2BE2912D18200DF80B4 /* Buttons */, @@ -1051,6 +1062,7 @@ EA8E40932A82889500934ED3 /* TooltipDialog.swift in Sources */, 44604AD429CE186A00E62B51 /* NotificationButtonModel.swift in Sources */, EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */, + 71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */, EA0D1C372A681CCE00E5C127 /* ToggleView.swift in Sources */, EAF7F0B9289C139800B287F5 /* ColorConfiguration.swift in Sources */, EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */, diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift new file mode 100644 index 00000000..4df0a034 --- /dev/null +++ b/VDS/Components/Pagination/Pagination.swift @@ -0,0 +1,228 @@ +// +// Pagination.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 01/03/24. +// + +import Foundation +import VDSColorTokens +import Combine + +@objc(VDSPagination) +open class Pagination: View { + + @Published var onPreviousTapped: PassthroughSubject = PassthroughSubject() + @Published var onNextTapped: PassthroughSubject = PassthroughSubject() + @Published var onPageWillChange: PassthroughSubject = PassthroughSubject() + @Published var onPageChanged: PassthroughSubject = PassthroughSubject() + + public var total: Int = 0 { + didSet { + setNeedsUpdate() + } + } + public var selectedPage: Int = 0 { didSet { setNeedsUpdate() } } + private var numberOfRows: Int = 0 { + didSet { + collectionView.collectionViewLayout.invalidateLayout() + setNeedsUpdate() + } + } + private let pageItemCellSize: CGSize = .init(width: 20, height: 16) + private let spacingBetweenCell: CGFloat = VDSLayout.Spacing.space1X.value + private let buttonTintColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) + private let buttonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) + + private lazy var collectionView: UICollectionView = { + let layout = UICollectionViewFlowLayout() + layout.itemSize = pageItemCellSize + layout.scrollDirection = .horizontal + layout.minimumInteritemSpacing = spacingBetweenCell + layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize + layout.minimumLineSpacing = spacingBetweenCell + layout.sectionInset = .zero + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.isScrollEnabled = false + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsHorizontalScrollIndicator = false + collectionView.showsVerticalScrollIndicator = false + collectionView.register(PaginationCellItem.self, forCellWithReuseIdentifier: PaginationCellItem.identifier) + collectionView.backgroundColor = .clear + return collectionView + }() + + //TODO: Need to check with textStyle with Matt as its getter only in ButtonBase + private lazy var previousButton: ButtonBase = { + let previousButton: ButtonBase + if #available(iOS 15.0, *) { + var configuration = ButtonBase.Configuration.plain() + configuration.imagePadding = VDSLayout.Spacing.space2X.value + configuration.attributedTitle = AttributedString("Previous", attributes: AttributeContainer([NSAttributedString.Key.font: TextStyle.boldBodySmall.font])) + configuration.titleAlignment = .leading + configuration.imagePlacement = .leading + configuration.contentInsets = .zero + previousButton = ButtonBase(configuration: configuration) + } else { + previousButton = ButtonBase() + previousButton.imageEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: VDSLayout.Spacing.space2X.value) + previousButton.setTitle("Previous", for: .normal) + previousButton.titleLabel?.font = TextStyle.boldBodySmall.font + } + previousButton.contentHorizontalAlignment = .leading + previousButton.translatesAutoresizingMaskIntoConstraints = false + previousButton.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + previousButton.setImage(BundleManager.shared.image(for: "pagination-arrow-left")?.withRenderingMode(.alwaysTemplate), for: .normal) + return previousButton + }() + + private let nextButton: ButtonBase = { + let nextButton: ButtonBase + if #available(iOS 15.0, *) { + var configuration = ButtonBase.Configuration.plain() + configuration.imagePadding = VDSLayout.Spacing.space2X.value + configuration.attributedTitle = AttributedString("Next", attributes: AttributeContainer([NSAttributedString.Key.font: TextStyle.boldBodySmall.font])) + configuration.imagePlacement = .trailing + configuration.titleAlignment = .trailing + configuration.contentInsets = .zero + nextButton = ButtonBase(configuration: configuration) + } else { + nextButton = ButtonBase() + nextButton.imageEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: VDSLayout.Spacing.space2X.value) + nextButton.semanticContentAttribute = .forceRightToLeft + nextButton.titleLabel?.font = TextStyle.boldBodySmall.font + nextButton.setTitle("Next", for: .normal) + } + //nextButton.textStyle = .boldBodySmall + nextButton.translatesAutoresizingMaskIntoConstraints = false + nextButton.contentHorizontalAlignment = .trailing + nextButton.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + nextButton.setImage(BundleManager.shared.image(for: "pagination-arrow-right")?.withRenderingMode(.alwaysTemplate), for: .normal) + return nextButton + }() + + private let containerView: View = View().with { + $0.translatesAutoresizingMaskIntoConstraints = false + } + + open override func initialSetup() { + super.initialSetup() + + addSubview(containerView) + containerView.pinToSuperView() + containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 288).activate() + containerView.heightAnchor.constraint(equalToConstant: 44).activate() + containerView.addSubview(previousButton) + containerView.addSubview(collectionView) + containerView.addSubview(nextButton) + + previousButton + .pinTop() + .pinBottom() + .pinLeading() + previousButton.trailingAnchor.constraint(greaterThanOrEqualTo: collectionView.leadingAnchor).activate() + collectionView.heightAnchor.constraint(equalToConstant: VDSLayout.Spacing.space4X.value).activate() + collectionView.centerYAnchor.constraint(equalTo: centerYAnchor).activate() + collectionView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + collectionView.trailingAnchor.constraint(greaterThanOrEqualTo: nextButton.leadingAnchor).activate() + collectionView.widthAnchor.constraint(equalToConstant: 92).activate() + nextButton + .pinTop() + .pinBottom() + .pinTrailing() + nextButton.onClick = onbuttonTapped + previousButton.onClick = onbuttonTapped + previousButton.isHidden = true + } + + open override func updateView() { + super.updateView() + + previousButton.tintColor = buttonTintColorConfiguration.getColor(surface) + nextButton.tintColor = buttonTintColorConfiguration.getColor(surface) + previousButton.setTitleColor(buttonTextColorConfiguration.getColor(surface), for: .normal) + nextButton.setTitleColor(buttonTextColorConfiguration.getColor(surface), for: .normal) + collectionView.reloadData() + } + + private func onbuttonTapped(_ sender: UIButton) { + let isNextAction = sender == nextButton + if isNextAction { + selectedPage += 1 + } else { + selectedPage -= 1 + } + updateSelection() + } + + private func updateSelection() { + collectionView.scrollToItem(at: IndexPath(row: max(selectedPage-1, 0), section: 0), at: .left, animated: false) + previousButton.isHidden = selectedPage == 0 + nextButton.isHidden = selectedPage == total - 1 + } +} + +extension Pagination: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { + + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { total } + + public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PaginationCellItem.identifier, for: indexPath) as? PaginationCellItem else { return UICollectionViewCell() } + cell.update(selectedPage, currentIndex: indexPath.row, surface: surface) + return cell + } + + public func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool { + onPageWillChange.send(selectedPage) + return true + } + + public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + selectedPage = indexPath.row + updateSelection() + onPageChanged.send(indexPath.row) + } +} + +internal final class PaginationCellItem: UICollectionViewCell { + + static let identifier: String = String(describing: PaginationCellItem.self) + private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + + private var indexLabel: Label = Label().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.textAlignment = .center + $0.numberOfLines = 1 + } + + override init(frame: CGRect) { + super.init(frame: frame) + setUp() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setUp() + } + + private func setUp() { + let containerView = View() + containerView.translatesAutoresizingMaskIntoConstraints = false + containerView.addSubview(indexLabel) + contentView.addSubview(containerView) + containerView.pinToSuperView() + indexLabel.pinToSuperView() + indexLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: VDSLayout.Spacing.space5X.value).activate() + contentView.backgroundColor = .clear + containerView.backgroundColor = .clear + indexLabel.backgroundColor = .clear + } + + internal func update(_ selectedIndex: Int, currentIndex: Int, surface: Surface) { + indexLabel.textStyle = selectedIndex == currentIndex ? .boldBodySmall : .bodySmall + indexLabel.text = "\(currentIndex)" + indexLabel.textColor = textColorConfiguration.getColor(surface) + } +} diff --git a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/Contents.json b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/Contents.json new file mode 100644 index 00000000..6c37b40d --- /dev/null +++ b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "pagination-arrow-left.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/pagination-arrow-left.svg b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/pagination-arrow-left.svg new file mode 100644 index 00000000..a8c85c97 --- /dev/null +++ b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/pagination-arrow-left.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/Contents.json b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/Contents.json new file mode 100644 index 00000000..d294d555 --- /dev/null +++ b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "pagination-arrow-right.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/pagination-arrow-right.svg b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/pagination-arrow-right.svg new file mode 100644 index 00000000..be02bedf --- /dev/null +++ b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/pagination-arrow-right.svg @@ -0,0 +1,3 @@ + + + From 8ed924c8056f582430ff8b1b3f5b08ce911e34cb Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Tue, 5 Mar 2024 16:48:26 +0530 Subject: [PATCH 08/76] Created PaginationButton and changelog --- VDS.xcodeproj/project.pbxproj | 12 ++ VDS/Components/Pagination/Pagination.swift | 165 +++++------------- .../Pagination/PaginationButton.swift | 87 +++++++++ .../Pagination/PaginationCellItem.swift | 51 ++++++ .../Pagination/PaginationChangeLog.txt | 34 ++++ 5 files changed, 227 insertions(+), 122 deletions(-) create mode 100644 VDS/Components/Pagination/PaginationButton.swift create mode 100644 VDS/Components/Pagination/PaginationCellItem.swift create mode 100644 VDS/Components/Pagination/PaginationChangeLog.txt diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 79deb46b..d37e3f8c 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -17,8 +17,11 @@ 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; 7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; }; 71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71B23C2C2B91FA690027F7D9 /* Pagination.swift */; }; + 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */; }; 71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */; }; 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; }; + 71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86D92B96F44C00700965 /* PaginationButton.swift */; }; + 71FC86DC2B96F4C800700965 /* PaginationCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DB2B96F4C800700965 /* PaginationCellItem.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,8 +189,11 @@ 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = ""; }; 71B23C2C2B91FA690027F7D9 /* Pagination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pagination.swift; sourceTree = ""; }; + 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = PaginationChangeLog.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 = ""; }; + 71FC86D92B96F44C00700965 /* PaginationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationButton.swift; sourceTree = ""; }; + 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationCellItem.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 = ""; }; @@ -390,6 +396,9 @@ isa = PBXGroup; children = ( 71B23C2C2B91FA690027F7D9 /* Pagination.swift */, + 71FC86D92B96F44C00700965 /* PaginationButton.swift */, + 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */, + 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */, ); path = Pagination; sourceTree = ""; @@ -981,6 +990,7 @@ EAEEECA92B1F969700531FC2 /* TooltipChangeLog.txt in Resources */, EAEEEC9C2B1F8F0700531FC2 /* TextLinkCaretChangeLog.txt in Resources */, EAA5EEE428F5B855003B3210 /* VerizonNHGDS-Light.otf in Resources */, + 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */, EAEEECAD2B1FC1A600531FC2 /* TitleLockupChangeLog.txt in Resources */, EAEEECAB2B1FBF2A00531FC2 /* ToggleChangeLog.txt in Resources */, ); @@ -1039,6 +1049,7 @@ EA985BEE2968A92400F2FF2E /* TitleLockupSubTitleModel.swift in Sources */, EA985BF22968B5BB00F2FF2E /* TitleLockupTextStyle.swift in Sources */, EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */, + 71FC86DC2B96F4C800700965 /* PaginationCellItem.swift in Sources */, EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */, EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */, EA985BE82968951C00F2FF2E /* TileletTitleModel.swift in Sources */, @@ -1055,6 +1066,7 @@ EAC9258F2911C9DE00091998 /* EntryFieldBase.swift in Sources */, EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */, EAD068922A560B65002E3A2D /* LoaderViewController.swift in Sources */, + 71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */, EABFEB642A26473700C4C106 /* NSAttributedString.swift in Sources */, EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */, EA0FC2C62914222900DF80B4 /* ButtonGroup.swift in Sources */, diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index 4df0a034..170e1935 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -12,37 +12,57 @@ import Combine @objc(VDSPagination) open class Pagination: View { - @Published var onPreviousTapped: PassthroughSubject = PassthroughSubject() - @Published var onNextTapped: PassthroughSubject = PassthroughSubject() - @Published var onPageWillChange: PassthroughSubject = PassthroughSubject() - @Published var onPageChanged: PassthroughSubject = PassthroughSubject() + open var onPageDidSelect: ((Int) -> Void)? + + public let previousButton: PaginationButton = .init(type: .previous) + public let nextButton: PaginationButton = .init(type: .next) public var total: Int = 0 { didSet { + previousButton.isHidden = true + nextButton.isHidden = total <= 1 + _selectedPage = 0 setNeedsUpdate() } } - public var selectedPage: Int = 0 { didSet { setNeedsUpdate() } } - private var numberOfRows: Int = 0 { - didSet { - collectionView.collectionViewLayout.invalidateLayout() + + public var selectedPage: Int { + set { + if newValue >= total { + _selectedPage = total - 1 + } else if newValue < 0 { + _selectedPage = 0 + } else { + _selectedPage = max(newValue - 1, 0) + } setNeedsUpdate() + updateSelection() + } + get { + _selectedPage } } + + private var _selectedPage: Int = 0 private let pageItemCellSize: CGSize = .init(width: 20, height: 16) private let spacingBetweenCell: CGFloat = VDSLayout.Spacing.space1X.value - private let buttonTintColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) - private let buttonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) - private lazy var collectionView: UICollectionView = { + private let containerView: View = View().with { + $0.translatesAutoresizingMaskIntoConstraints = false + } + + private lazy var flowLayout: UICollectionViewFlowLayout = { let layout = UICollectionViewFlowLayout() - layout.itemSize = pageItemCellSize layout.scrollDirection = .horizontal layout.minimumInteritemSpacing = spacingBetweenCell - layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize layout.minimumLineSpacing = spacingBetweenCell layout.sectionInset = .zero - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + layout.estimatedItemSize = pageItemCellSize + return layout + }() + + private lazy var collectionView: UICollectionView = { + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) collectionView.isScrollEnabled = false collectionView.translatesAutoresizingMaskIntoConstraints = false collectionView.delegate = self @@ -54,59 +74,6 @@ open class Pagination: View { return collectionView }() - //TODO: Need to check with textStyle with Matt as its getter only in ButtonBase - private lazy var previousButton: ButtonBase = { - let previousButton: ButtonBase - if #available(iOS 15.0, *) { - var configuration = ButtonBase.Configuration.plain() - configuration.imagePadding = VDSLayout.Spacing.space2X.value - configuration.attributedTitle = AttributedString("Previous", attributes: AttributeContainer([NSAttributedString.Key.font: TextStyle.boldBodySmall.font])) - configuration.titleAlignment = .leading - configuration.imagePlacement = .leading - configuration.contentInsets = .zero - previousButton = ButtonBase(configuration: configuration) - } else { - previousButton = ButtonBase() - previousButton.imageEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: VDSLayout.Spacing.space2X.value) - previousButton.setTitle("Previous", for: .normal) - previousButton.titleLabel?.font = TextStyle.boldBodySmall.font - } - previousButton.contentHorizontalAlignment = .leading - previousButton.translatesAutoresizingMaskIntoConstraints = false - previousButton.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - previousButton.setImage(BundleManager.shared.image(for: "pagination-arrow-left")?.withRenderingMode(.alwaysTemplate), for: .normal) - return previousButton - }() - - private let nextButton: ButtonBase = { - let nextButton: ButtonBase - if #available(iOS 15.0, *) { - var configuration = ButtonBase.Configuration.plain() - configuration.imagePadding = VDSLayout.Spacing.space2X.value - configuration.attributedTitle = AttributedString("Next", attributes: AttributeContainer([NSAttributedString.Key.font: TextStyle.boldBodySmall.font])) - configuration.imagePlacement = .trailing - configuration.titleAlignment = .trailing - configuration.contentInsets = .zero - nextButton = ButtonBase(configuration: configuration) - } else { - nextButton = ButtonBase() - nextButton.imageEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: VDSLayout.Spacing.space2X.value) - nextButton.semanticContentAttribute = .forceRightToLeft - nextButton.titleLabel?.font = TextStyle.boldBodySmall.font - nextButton.setTitle("Next", for: .normal) - } - //nextButton.textStyle = .boldBodySmall - nextButton.translatesAutoresizingMaskIntoConstraints = false - nextButton.contentHorizontalAlignment = .trailing - nextButton.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - nextButton.setImage(BundleManager.shared.image(for: "pagination-arrow-right")?.withRenderingMode(.alwaysTemplate), for: .normal) - return nextButton - }() - - private let containerView: View = View().with { - $0.translatesAutoresizingMaskIntoConstraints = false - } - open override func initialSetup() { super.initialSetup() @@ -140,27 +107,27 @@ open class Pagination: View { open override func updateView() { super.updateView() - previousButton.tintColor = buttonTintColorConfiguration.getColor(surface) - nextButton.tintColor = buttonTintColorConfiguration.getColor(surface) - previousButton.setTitleColor(buttonTextColorConfiguration.getColor(surface), for: .normal) - nextButton.setTitleColor(buttonTextColorConfiguration.getColor(surface), for: .normal) + nextButton.surface = surface + previousButton.surface = surface collectionView.reloadData() } private func onbuttonTapped(_ sender: UIButton) { let isNextAction = sender == nextButton if isNextAction { - selectedPage += 1 + _selectedPage += 1 } else { - selectedPage -= 1 + _selectedPage -= 1 } updateSelection() } private func updateSelection() { - collectionView.scrollToItem(at: IndexPath(row: max(selectedPage-1, 0), section: 0), at: .left, animated: false) + let indexPath = IndexPath(row: selectedPage, section: 0) + collectionView.scrollToItem(at: indexPath, at: .left, animated: false) previousButton.isHidden = selectedPage == 0 nextButton.isHidden = selectedPage == total - 1 + collectionView.reloadData() } } @@ -174,55 +141,9 @@ extension Pagination: UICollectionViewDelegate, UICollectionViewDataSource, UICo return cell } - public func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool { - onPageWillChange.send(selectedPage) - return true - } - public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - selectedPage = indexPath.row + _selectedPage = indexPath.row updateSelection() - onPageChanged.send(indexPath.row) - } -} - -internal final class PaginationCellItem: UICollectionViewCell { - - static let identifier: String = String(describing: PaginationCellItem.self) - private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) - - private var indexLabel: Label = Label().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.textAlignment = .center - $0.numberOfLines = 1 - } - - override init(frame: CGRect) { - super.init(frame: frame) - setUp() - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - setUp() - } - - private func setUp() { - let containerView = View() - containerView.translatesAutoresizingMaskIntoConstraints = false - containerView.addSubview(indexLabel) - contentView.addSubview(containerView) - containerView.pinToSuperView() - indexLabel.pinToSuperView() - indexLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: VDSLayout.Spacing.space5X.value).activate() - contentView.backgroundColor = .clear - containerView.backgroundColor = .clear - indexLabel.backgroundColor = .clear - } - - internal func update(_ selectedIndex: Int, currentIndex: Int, surface: Surface) { - indexLabel.textStyle = selectedIndex == currentIndex ? .boldBodySmall : .bodySmall - indexLabel.text = "\(currentIndex)" - indexLabel.textColor = textColorConfiguration.getColor(surface) + onPageDidSelect?(indexPath.row) } } diff --git a/VDS/Components/Pagination/PaginationButton.swift b/VDS/Components/Pagination/PaginationButton.swift new file mode 100644 index 00000000..6a1cd3fa --- /dev/null +++ b/VDS/Components/Pagination/PaginationButton.swift @@ -0,0 +1,87 @@ +// +// PaginationButton.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 05/03/24. +// + +import UIKit +import VDSColorTokens + +open class PaginationButton: ButtonBase { + + private let buttonTintColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) + private let buttonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) + + @available(iOS 15.0, *) + var buttonConfiguration: Button.Configuration { + var configuration = ButtonBase.Configuration.plain() + configuration.imagePadding = VDSLayout.Spacing.space2X.value + configuration.imagePlacement = type == .next ? .trailing : .leading + configuration.titleAlignment = type == .next ? .trailing : .leading + configuration.contentInsets = .zero + return configuration + } + + open override var textStyle: TextStyle { TextStyle.boldBodySmall } + + open override var textColor: UIColor { buttonTextColorConfiguration.getColor(surface) } + + private var type: Type = .next + + init(type: Type) { + self.type = type + super.init() + } + + required public init() { + super.init() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + + open override func initialSetup() { + super.initialSetup() + if #available(iOS 15.0, *) { + configuration = buttonConfiguration + } else { + semanticContentAttribute = type == .next ? .forceRightToLeft : .forceLeftToRight + imageEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: VDSLayout.Spacing.space2X.value) + } + contentHorizontalAlignment = type == .next ? .trailing : .leading + } + + open override func updateView() { + text = type.title + setImage(type.image, for: .normal) + tintColor = buttonTintColorConfiguration.getColor(surface) + super.updateView() + } +} + +extension PaginationButton { + + enum `Type` { + case previous, next + + var title: String { + switch self { + case .next: + "Next" + case .previous: + "Previous" + } + } + + var image: UIImage? { + switch self { + case .previous: + BundleManager.shared.image(for: "pagination-arrow-left")?.withRenderingMode(.alwaysTemplate) + case .next: + BundleManager.shared.image(for: "pagination-arrow-right")?.withRenderingMode(.alwaysTemplate) + } + } + } +} diff --git a/VDS/Components/Pagination/PaginationCellItem.swift b/VDS/Components/Pagination/PaginationCellItem.swift new file mode 100644 index 00000000..1a546551 --- /dev/null +++ b/VDS/Components/Pagination/PaginationCellItem.swift @@ -0,0 +1,51 @@ +// +// PaginationCellItem.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 05/03/24. +// + +import UIKit +import VDSColorTokens + +final class PaginationCellItem: UICollectionViewCell { + + static let identifier: String = String(describing: PaginationCellItem.self) + + private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + + private var indexLabel: Label = Label().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.textAlignment = .center + $0.numberOfLines = 1 + } + + override init(frame: CGRect) { + super.init(frame: frame) + setUp() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setUp() + } + + private func setUp() { + let containerView = View() + containerView.translatesAutoresizingMaskIntoConstraints = false + containerView.addSubview(indexLabel) + contentView.addSubview(containerView) + containerView.pinToSuperView() + indexLabel.pinToSuperView() + indexLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: VDSLayout.Spacing.space5X.value).activate() + contentView.backgroundColor = .clear + containerView.backgroundColor = .clear + indexLabel.backgroundColor = .clear + } + + func update(_ selectedIndex: Int, currentIndex: Int, surface: Surface) { + indexLabel.textStyle = selectedIndex == currentIndex ? .boldBodySmall : .bodySmall + indexLabel.text = "\(currentIndex + 1)" + indexLabel.textColor = textColorConfiguration.getColor(surface) + } +} diff --git a/VDS/Components/Pagination/PaginationChangeLog.txt b/VDS/Components/Pagination/PaginationChangeLog.txt new file mode 100644 index 00000000..66f0ac14 --- /dev/null +++ b/VDS/Components/Pagination/PaginationChangeLog.txt @@ -0,0 +1,34 @@ +MM/DD/YYYY +---------------- + +Initial Brand 3.0 handoff + +12/17/2021 +---------------- +- Replaced focusring colors (previously interactive/onlight/ondark) with accessibility/onlight/ondark colors +- Updated focus border name (previously interactive.focusring.onlight) with focusring.onlight/ondark + +02/28/2022 +---------------- +- Change Page Item Active to Page Item Selected. All Active references changed to Selected. + +03/01/2022 +---------------- +- Replaced Left and Right Arrow Non-Scaling icons with VDS Icon. +- Removed “weight” and “vector effect” from Anatomy frame. + +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 focus border pixel and style & spacing values with tokens. + +01/12/2023 +---------------- +- Removed “Page Item Selected” from Anatomy. From cd85748a11732e632f0cb4e53e9641f42aac9f64 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Wed, 6 Mar 2024 21:25:18 +0530 Subject: [PATCH 09/76] Fixed bugs, added comments and created new flow layout --- VDS.xcodeproj/project.pbxproj | 8 + VDS/Components/Pagination/Pagination.swift | 174 ++++++++++++------ .../Pagination/PaginationButton.swift | 35 +++- .../Pagination/PaginationCellItem.swift | 15 +- .../Pagination/PaginationFlowLayout.swift | 91 +++++++++ VDS/Utilities/Clamping.swift | 24 +++ 6 files changed, 276 insertions(+), 71 deletions(-) create mode 100644 VDS/Components/Pagination/PaginationFlowLayout.swift create mode 100644 VDS/Utilities/Clamping.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index d37e3f8c..c70712cf 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; }; 71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86D92B96F44C00700965 /* PaginationButton.swift */; }; 71FC86DC2B96F4C800700965 /* PaginationCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */; }; + 71FC86E22B97483000700965 /* Clamping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86E12B97483000700965 /* Clamping.swift */; }; + 71FC86E42B9841AC00700965 /* PaginationFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86E32B9841AC00700965 /* PaginationFlowLayout.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 */; }; @@ -194,6 +196,8 @@ 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = ""; }; 71FC86D92B96F44C00700965 /* PaginationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationButton.swift; sourceTree = ""; }; 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationCellItem.swift; sourceTree = ""; }; + 71FC86E12B97483000700965 /* Clamping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clamping.swift; sourceTree = ""; }; + 71FC86E32B9841AC00700965 /* PaginationFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationFlowLayout.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 = ""; }; @@ -398,6 +402,7 @@ 71B23C2C2B91FA690027F7D9 /* Pagination.swift */, 71FC86D92B96F44C00700965 /* PaginationButton.swift */, 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */, + 71FC86E32B9841AC00700965 /* PaginationFlowLayout.swift */, 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */, ); path = Pagination; @@ -603,6 +608,7 @@ isa = PBXGroup; children = ( EA3361BC288B2C760071C351 /* TypeAlias.swift */, + 71FC86E12B97483000700965 /* Clamping.swift */, ); path = Utilities; sourceTree = ""; @@ -1022,6 +1028,7 @@ EA3361C328902D960071C351 /* Toggle.swift in Sources */, EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */, EA89201328B568D8006B9984 /* RadioBoxItem.swift in Sources */, + 71FC86E42B9841AC00700965 /* PaginationFlowLayout.swift in Sources */, EAC9258C2911C9DE00091998 /* InputField.swift in Sources */, EA3362402892EF6C0071C351 /* Label.swift in Sources */, EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */, @@ -1030,6 +1037,7 @@ 71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */, EA0D1C452A6AD73000E5C127 /* RawRepresentable.swift in Sources */, EA985C23296E033A00F2FF2E /* TextArea.swift in Sources */, + 71FC86E22B97483000700965 /* Clamping.swift in Sources */, EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */, EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */, EA33622E2891EA3C0071C351 /* DispatchQueue+Once.swift in Sources */, diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index 170e1935..f4bf8a60 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -9,58 +9,24 @@ import Foundation import VDSColorTokens import Combine +///Pagination is a control that enables customers to navigate multiple pages of content by selecting either a specific page or the next or previous set of four pages. @objc(VDSPagination) open class Pagination: View { - open var onPageDidSelect: ((Int) -> Void)? - - public let previousButton: PaginationButton = .init(type: .previous) - public let nextButton: PaginationButton = .init(type: .next) - - public var total: Int = 0 { - didSet { - previousButton.isHidden = true - nextButton.isHidden = total <= 1 - _selectedPage = 0 - setNeedsUpdate() - } - } - - public var selectedPage: Int { - set { - if newValue >= total { - _selectedPage = total - 1 - } else if newValue < 0 { - _selectedPage = 0 - } else { - _selectedPage = max(newValue - 1, 0) - } - setNeedsUpdate() - updateSelection() - } - get { - _selectedPage - } - } - - private var _selectedPage: Int = 0 - private let pageItemCellSize: CGSize = .init(width: 20, height: 16) - private let spacingBetweenCell: CGFloat = VDSLayout.Spacing.space1X.value - + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + ///Collectionview width anchor + private var collectionViewWidthAnchor: NSLayoutConstraint? + ///Selected page index + private var _selectedPageIndex: Int = 0 + ///Custom flow layout defined for the Pagination + private let flowLayout = PaginationFlowLayout() + ///A root view for the pagination private let containerView: View = View().with { $0.translatesAutoresizingMaskIntoConstraints = false } - - private lazy var flowLayout: UICollectionViewFlowLayout = { - let layout = UICollectionViewFlowLayout() - layout.scrollDirection = .horizontal - layout.minimumInteritemSpacing = spacingBetweenCell - layout.minimumLineSpacing = spacingBetweenCell - layout.sectionInset = .zero - layout.estimatedItemSize = pageItemCellSize - return layout - }() - + ///Collectionview to render pagination indexes private lazy var collectionView: UICollectionView = { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) collectionView.isScrollEnabled = false @@ -74,6 +40,48 @@ open class Pagination: View { return collectionView }() + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + ///Previous button to select previous page + public let previousButton: PaginationButton = .init(type: .previous) + ///Next button to select next page + public let nextButton: PaginationButton = .init(type: .next) + /// A callback when the page changes. Passes parameters (selectedPage). + public var onPageDidSelect: ((Int) -> Void)? + /// Total number of pages, allows limit ranging from 0 to 9999. + @Clamping(range: 0...9999) + public var total: Int { + didSet { + previousButton.isHidden = true + nextButton.isHidden = total <= 1 + _selectedPageIndex = 0 + setNeedsUpdate() + updateSelection() + } + } + ///Selected active page number and clips to total pages if selected index is greater than the total pages. + public var selectedPage: Int { + set { + if newValue >= total { + _selectedPageIndex = total - 1 + } else if newValue < 0 { + _selectedPageIndex = 0 + } else { + _selectedPageIndex = max(newValue - 1, 0) + } + setNeedsUpdate() + updateSelection() + } + get { + _selectedPageIndex + 1 //Returns selected page value not index + } + } + + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + /// Executed on initialization for this View. open override func initialSetup() { super.initialSetup() @@ -94,56 +102,100 @@ open class Pagination: View { collectionView.centerYAnchor.constraint(equalTo: centerYAnchor).activate() collectionView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() collectionView.trailingAnchor.constraint(greaterThanOrEqualTo: nextButton.leadingAnchor).activate() - collectionView.widthAnchor.constraint(equalToConstant: 92).activate() + collectionViewWidthAnchor = collectionView.widthAnchor.constraint(equalToConstant: 92) + collectionViewWidthAnchor?.activate() + nextButton .pinTop() .pinBottom() .pinTrailing() + nextButton.onClick = onbuttonTapped previousButton.onClick = onbuttonTapped previousButton.isHidden = true + + flowLayout.$collectionViewWidth + .receive(on: RunLoop.main) + .sink { [weak self] value in + self?.collectionViewWidthAnchor?.constant = value + }.store(in: &subscribers) } + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() - nextButton.surface = surface previousButton.surface = surface collectionView.reloadData() } + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + ///When previous/next button is tapped private func onbuttonTapped(_ sender: UIButton) { let isNextAction = sender == nextButton - if isNextAction { - _selectedPage += 1 - } else { - _selectedPage -= 1 - } + _selectedPageIndex = if isNextAction { _selectedPageIndex + 1 } else { _selectedPageIndex - 1 } updateSelection() } + ///Refreshing the UI based on the selected page private func updateSelection() { - let indexPath = IndexPath(row: selectedPage, section: 0) - collectionView.scrollToItem(at: indexPath, at: .left, animated: false) - previousButton.isHidden = selectedPage == 0 - nextButton.isHidden = selectedPage == total - 1 + guard _selectedPageIndex < total else { return } + collectionView.scrollToItem(at: IndexPath(row: _selectedPageIndex, section: 0), at: .left, animated: false) + previousButton.isHidden = _selectedPageIndex == 0 + nextButton.isHidden = _selectedPageIndex == total - 1 collectionView.reloadData() + verifyIfMaxDigitChanged() + } + + ///Identifying if there is any change in the digits of upcoming page + func verifyIfMaxDigitChanged() { + let upperLimitPage = _selectedPageIndex + flowLayout.maxNumberOfColumns + let upperLimitDigits = upperLimitPage.digitCount //future value digits + switch (flowLayout.numberOfColumns, upperLimitDigits) { + case (_, 3), (_, 4): + flowLayout.numberOfColumns = 3 + default: + flowLayout.numberOfColumns = 4 + } + if upperLimitDigits != flowLayout.upperLimitDigits { + flowLayout.upperLimitDigits = upperLimitDigits + flowLayout.invalidateLayout() + collectionView.reloadData() + collectionView.scrollToItem(at: IndexPath(row: self._selectedPageIndex, section: 0), at: .left, animated: false) + } } } extension Pagination: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { - + //-------------------------------------------------- + // MARK: - UICollectionView Delegate & Datasource + //-------------------------------------------------- public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { total } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PaginationCellItem.identifier, for: indexPath) as? PaginationCellItem else { return UICollectionViewCell() } - cell.update(selectedPage, currentIndex: indexPath.row, surface: surface) + cell.update(_selectedPageIndex, currentIndex: indexPath.row, surface: surface) return cell } public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - _selectedPage = indexPath.row + _selectedPageIndex = indexPath.row updateSelection() - onPageDidSelect?(indexPath.row) + onPageDidSelect?(selectedPage) + } +} + +fileprivate extension Int { + //-------------------------------------------------- + // MARK: - Extension on Int to identify number of digits in given number. + //-------------------------------------------------- + var digitCount: Int { + numberOfDigits(in: self) + } + + private func numberOfDigits(in number: Int) -> Int { + number < 10 && number >= 0 ? 1 : 1 + numberOfDigits(in: number/10) } } diff --git a/VDS/Components/Pagination/PaginationButton.swift b/VDS/Components/Pagination/PaginationButton.swift index 6a1cd3fa..3805051d 100644 --- a/VDS/Components/Pagination/PaginationButton.swift +++ b/VDS/Components/Pagination/PaginationButton.swift @@ -8,13 +8,20 @@ import UIKit import VDSColorTokens +///This is customised button for Pagination view open class PaginationButton: ButtonBase { - + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + /// Type of the PaginationButton + private var type: Type = .next + /// Button tint color configuration private let buttonTintColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) + /// Button title color configuration private let buttonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) - + /// Button configuration for iOS 15+ @available(iOS 15.0, *) - var buttonConfiguration: Button.Configuration { + private var buttonConfiguration: Button.Configuration { var configuration = ButtonBase.Configuration.plain() configuration.imagePadding = VDSLayout.Spacing.space2X.value configuration.imagePlacement = type == .next ? .trailing : .leading @@ -23,12 +30,17 @@ open class PaginationButton: ButtonBase { return configuration } + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + /// TextStyle used on the titleLabel. open override var textStyle: TextStyle { TextStyle.boldBodySmall } - + /// UIColor used on the titleLabel text. open override var textColor: UIColor { buttonTextColorConfiguration.getColor(surface) } - private var type: Type = .next - + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- init(type: Type) { self.type = type super.init() @@ -42,6 +54,10 @@ open class PaginationButton: ButtonBase { super.init(coder: coder) } + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + /// Executed on initialization for this View. open override func initialSetup() { super.initialSetup() if #available(iOS 15.0, *) { @@ -53,6 +69,7 @@ open class PaginationButton: ButtonBase { contentHorizontalAlignment = type == .next ? .trailing : .leading } + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { text = type.title setImage(type.image, for: .normal) @@ -62,7 +79,9 @@ open class PaginationButton: ButtonBase { } extension PaginationButton { - + //-------------------------------------------------- + // MARK: - Enum to configure PaginationButton + //-------------------------------------------------- enum `Type` { case previous, next @@ -74,7 +93,7 @@ extension PaginationButton { "Previous" } } - + ///Image for the configuration type var image: UIImage? { switch self { case .previous: diff --git a/VDS/Components/Pagination/PaginationCellItem.swift b/VDS/Components/Pagination/PaginationCellItem.swift index 1a546551..68fa7271 100644 --- a/VDS/Components/Pagination/PaginationCellItem.swift +++ b/VDS/Components/Pagination/PaginationCellItem.swift @@ -8,18 +8,27 @@ import UIKit import VDSColorTokens +///This is customised view for Pagination cell item final class PaginationCellItem: UICollectionViewCell { + ///Identifier for the PaginationCellItem static let identifier: String = String(describing: PaginationCellItem.self) - + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + ///Text color configuration for the element private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) - + ///Pagination index label private var indexLabel: Label = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .center $0.numberOfLines = 1 } + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- override init(frame: CGRect) { super.init(frame: frame) setUp() @@ -30,6 +39,7 @@ final class PaginationCellItem: UICollectionViewCell { setUp() } + ///Configuring the cell with default setup private func setUp() { let containerView = View() containerView.translatesAutoresizingMaskIntoConstraints = false @@ -43,6 +53,7 @@ final class PaginationCellItem: UICollectionViewCell { indexLabel.backgroundColor = .clear } + ///Updating UI based on selected index, current index along with surface func update(_ selectedIndex: Int, currentIndex: Int, surface: Surface) { indexLabel.textStyle = selectedIndex == currentIndex ? .boldBodySmall : .bodySmall indexLabel.text = "\(currentIndex + 1)" diff --git a/VDS/Components/Pagination/PaginationFlowLayout.swift b/VDS/Components/Pagination/PaginationFlowLayout.swift new file mode 100644 index 00000000..89debfe2 --- /dev/null +++ b/VDS/Components/Pagination/PaginationFlowLayout.swift @@ -0,0 +1,91 @@ +// +// PaginationFlowLayout.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 06/03/24. +// + +import Foundation +import UIKit + +///Customised flow layout for Pagination view +final class PaginationFlowLayout : UICollectionViewLayout { + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + ///Spacing between the pagination cells + private let spacingBetweenCell: CGFloat = VDSLayout.Spacing.space1X.value + ///Pre-defined sizes of the pagination cell based on number of digits. + private var upperLimitSize: CGSize { + switch upperLimitDigits { + case 3: .init(width: 28, height: 16) + case 4: .init(width: 34, height: 16) + default: .init(width: 20, height: 16) + } + } + ///Property to store the defined layout attributes. + private var itemCache : [UICollectionViewLayoutAttributes] = [] + + //-------------------------------------------------- + // MARK: - Internal Properties + //-------------------------------------------------- + ///Maximum number of page indexes shown on UI + let maxNumberOfColumns: Int = 4 + ///Number of digits of the maximum page index. + var upperLimitDigits: Int = 0 + ///Number of page indexes shown on UI. + var numberOfColumns: Int = 4 + ///A property that publishes when there is change in collection view width. + @Published var collectionViewWidth: CGFloat = 0 + + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + ///Preparing the layout collection attributes for pagination and updating the collectionview width. + override func prepare() { + + guard let collectionView else { return } + + itemCache.removeAll() + var xPos : CGFloat = 0 + for item in 0.. [UICollectionViewLayoutAttributes]? { + var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = [] + for attributes in itemCache { + if attributes.frame.intersects(rect) { + visibleLayoutAttributes.append(attributes) + } + } + return visibleLayoutAttributes + } + + ///This will return the layout attributes at particular indexPath + override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { + return itemCache[indexPath.row] + } + + ///Returns the collectionview content size + override var collectionViewContentSize: CGSize { + guard let lastAttribute = itemCache.last else { return super.collectionViewContentSize } + return .init(width: lastAttribute.frame.width + lastAttribute.frame.origin.x, height: 16) + } +} diff --git a/VDS/Utilities/Clamping.swift b/VDS/Utilities/Clamping.swift new file mode 100644 index 00000000..c8213828 --- /dev/null +++ b/VDS/Utilities/Clamping.swift @@ -0,0 +1,24 @@ +// +// Clamping.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 05/03/24. +// + +import Foundation + +@propertyWrapper public struct Clamping { + + private var value: Value + private let range: ClosedRange + + public init(range: ClosedRange) { + self.value = range.lowerBound + self.range = range + } + + public var wrappedValue: Value { + get { value } + set { value = min(max(range.lowerBound, newValue), range.upperBound) } + } +} From ee41a6555e6bf41ddc58fc6d2eb186f6471e1b36 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Fri, 8 Mar 2024 11:33:07 +0530 Subject: [PATCH 10/76] removed vertical layout & max width constraint --- VDS/Components/Notification/Notification.swift | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/VDS/Components/Notification/Notification.swift b/VDS/Components/Notification/Notification.swift index 126f83f0..9523e6c8 100644 --- a/VDS/Components/Notification/Notification.swift +++ b/VDS/Components/Notification/Notification.swift @@ -54,7 +54,8 @@ open class Notification: View { } /// Enum used to describe the orientation of Notification. - public enum Layout: String, CaseIterable { + /// Only horizontal orientation is supported for iPhone & iPad devices only. + private enum Layout: String, CaseIterable { case vertical, horizontal } @@ -176,7 +177,8 @@ open class Notification: View { private var _layout: Layout = .vertical /// Determines the orientation of buttons and text in the Notification. - open var layout: Layout { + /// Only horizontal orientation is supported for iPhone & iPad devices only. + private var layout: Layout { set { if !UIDevice.isIPad, newValue == .horizontal { return } _layout = newValue @@ -398,18 +400,18 @@ open class Notification: View { } private func setConstraints() { - maxWidthConstraint?.deactivate() labelViewAndButtonViewConstraint?.deactivate() labelViewBottomConstraint?.deactivate() buttonGroupCenterYConstraint?.deactivate() buttonGroupBottomConstraint?.deactivate() - maxWidthConstraint?.constant = maxViewWidth - maxWidthConstraint?.isActive = UIDevice.isIPad labelViewAndButtonViewConstraint?.isActive = layout == .vertical && !buttonGroup.buttons.isEmpty labelViewBottomConstraint?.isActive = layout == .horizontal || buttonGroup.buttons.isEmpty buttonGroupCenterYConstraint?.isActive = layout == .horizontal buttonGroupBottomConstraint?.isActive = layout == .vertical typeIconWidthConstraint?.constant = typeIcon.size.dimensions.width closeIconWidthConstraint?.constant = closeButton.size.dimensions.width + //iPad 12.9 inches is more than the configured maxViewWidth(1232), verified with designer on the same. Suggested to remove max width constraint + //maxWidthConstraint?.constant = maxViewWidth + //maxWidthConstraint?.isActive = UIDevice.isIPad } } From 441971d2f917ff5ebdd97437fa4878e18dba9893 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 09:20:17 -0600 Subject: [PATCH 11/76] updated to fix issue for attributedText Signed-off-by: Matt Bruce --- .../Selector/SelectorItemBase.swift | 2 - VDS/Components/Label/Label.swift | 118 ++++++++++++------ VDS/Components/RadioBox/RadioBoxItem.swift | 3 - 3 files changed, 82 insertions(+), 41 deletions(-) diff --git a/VDS/BaseClasses/Selector/SelectorItemBase.swift b/VDS/BaseClasses/Selector/SelectorItemBase.swift index 9ca86d8c..869c40e0 100644 --- a/VDS/BaseClasses/Selector/SelectorItemBase.swift +++ b/VDS/BaseClasses/Selector/SelectorItemBase.swift @@ -102,7 +102,6 @@ open class SelectorItemBase: Control, Errorable, /// Instead of use labelText and labelTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes. open var labelAttributedText: NSAttributedString? { didSet { - label.useAttributedText = !(labelAttributedText?.string.isEmpty ?? true) label.attributedText = labelAttributedText setNeedsUpdate() } @@ -117,7 +116,6 @@ open class SelectorItemBase: Control, Errorable, /// Instead of use childText and childTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes. open var childAttributedText: NSAttributedString? { didSet { - childLabel.useAttributedText = !(childAttributedText?.string.isEmpty ?? true) childLabel.attributedText = childAttributedText setNeedsUpdate() } diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 67e53abe..c998c70b 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -42,6 +42,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + private enum TextSetMode { + case text + case attributedText + } + + private var textSetMode: TextSetMode = .text + private var initialSetupPerformed = false private var edgeInsets: UIEdgeInsets { textStyle.edgeInsets } @@ -102,10 +109,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- /// Key of whether or not updateView() is called in setNeedsUpdate() open var shouldUpdateView: Bool = true - - /// Determines if the label should use its own attributedText property instead of rendering the attributedText propert - /// based of other local properties, such as textStyle, textColor, surface, etc... The default value is false. - open var useAttributedText: Bool = false /// Will determine if a scaled font should be used for the font. open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }} @@ -132,15 +135,25 @@ open class Label: UILabel, ViewProtocol, UserInfoable { private var _text: String? /// Text that will be used in the label. - override open var text: String? { - get { _text } + override public var text: String! { + get { super.text } set { - if _text != newValue || newValue != attributedText?.string { - _text = newValue - useAttributedText = false - attributes?.removeAll() - setNeedsUpdate() - } + // When text is set, we may need to re-style it as attributedText + // with the correct paragraph style to achieve the desired line height. + textSetMode = .text + attributes?.removeAll() + styleText(newValue) + } + } + + ///AttributedText that will be used in the label. + override public var attributedText: NSAttributedString? { + get { super.attributedText } + set { + // When text is set, we may need to re-style it as attributedText + // with the correct paragraph style to achieve the desired line height. + textSetMode = .attributedText + styleAttributedText(newValue) } } @@ -200,30 +213,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable { } open func updateView() { - if !useAttributedText { - if let text { - accessibilityCustomActions = [] - - //create the primary string - let mutableText = NSMutableAttributedString.mutableText(for: text, - textStyle: textStyle, - useScaledFont: useScaledFont, - textColor: textColorConfiguration.getColor(self), - alignment: textAlignment, - lineBreakMode: lineBreakMode) - - applyAttributes(mutableText) - - //set the attributed text - attributedText = mutableText - - //force a drawText - setNeedsDisplay() - - setNeedsLayout() - layoutIfNeeded() - } - } + restyleText() + + //force a drawText + setNeedsDisplay() + + setNeedsLayout() + layoutIfNeeded() } open func updateAccessibility() { @@ -269,6 +265,56 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- + private func restyleText() { + if textSetMode == .text { + styleText(text) + } else { + styleAttributedText(attributedText) + } + } + + private func styleText(_ newValue: String!) { + defer { invalidateIntrinsicContentSize() } + guard let newValue else { + // We don't need to use attributed text + super.attributedText = nil + super.text = newValue + return + } + + accessibilityCustomActions = [] + + //create the primary string + let mutableText = NSMutableAttributedString.mutableText(for: newValue, + textStyle: textStyle, + useScaledFont: useScaledFont, + textColor: textColorConfiguration.getColor(self), + alignment: textAlignment, + lineBreakMode: lineBreakMode) + + applyAttributes(mutableText) + + // Set attributed text to match typography + super.attributedText = mutableText + + } + + private func styleAttributedText(_ newValue: NSAttributedString?) { + defer { invalidateIntrinsicContentSize() } + guard let newValue = newValue else { + // We don't need any additional styling + super.attributedText = newValue + return + } + + var mutableText = NSMutableAttributedString(attributedString: newValue) + + applyAttributes(mutableText) + + // Modify attributed text to match typography + super.attributedText = newValue + } + private func applyAttributes(_ mutableAttributedString: NSMutableAttributedString) { actions = [] diff --git a/VDS/Components/RadioBox/RadioBoxItem.swift b/VDS/Components/RadioBox/RadioBoxItem.swift index 81f3f709..cfeb54e6 100644 --- a/VDS/Components/RadioBox/RadioBoxItem.swift +++ b/VDS/Components/RadioBox/RadioBoxItem.swift @@ -100,7 +100,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable { /// If provided, the RadioBox textAttributedText will be rendered. open var textAttributedText: NSAttributedString? { didSet { - textLabel.useAttributedText = !(textAttributedText?.string.isEmpty ?? true) textLabel.attributedText = textAttributedText setNeedsUpdate() } @@ -115,7 +114,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable { /// If provided, the RadioBox subTextAttributedText will be rendered. open var subTextAttributedText: NSAttributedString? { didSet { - subTextLabel.useAttributedText = !(subTextAttributedText?.string.isEmpty ?? true) subTextLabel.attributedText = subTextAttributedText setNeedsUpdate() } @@ -130,7 +128,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable { /// If provided, the RadioBox subTextRightAttributedText will be rendered. open var subTextRightAttributedText: NSAttributedString? { didSet { - subTextRightLabel.useAttributedText = !(subTextRightAttributedText?.string.isEmpty ?? true) subTextRightLabel.attributedText = subTextRightAttributedText setNeedsUpdate() } From 0fffd3811396b7120575501620e7af1a71fe8cb2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 10:08:12 -0600 Subject: [PATCH 12/76] updated initialSetup Signed-off-by: Matt Bruce --- VDS/Components/Buttons/ButtonBase.swift | 1 + VDS/Components/TextFields/TextArea/TextView.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index 255c6f64..cc096d41 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -102,6 +102,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { //-------------------------------------------------- open func initialSetup() { if !initialSetupPerformed { + initialSetupPerformed = true backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false accessibilityCustomActions = [] diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift index ea96ed6e..ffb355d1 100644 --- a/VDS/Components/TextFields/TextArea/TextView.swift +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -94,6 +94,7 @@ open class TextView: UITextView, ViewProtocol { //-------------------------------------------------- open func initialSetup() { if !initialSetupPerformed { + initialSetupPerformed = true backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false accessibilityCustomActions = [] From 21ab644c16358f85f46a0e6996aff75a29b360c2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 10:08:32 -0600 Subject: [PATCH 13/76] refactored for custom fonts Signed-off-by: Matt Bruce --- VDS/Fonts/Font.swift | 11 +++++++++-- VDS/Fonts/FontProtocol.swift | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/VDS/Fonts/Font.swift b/VDS/Fonts/Font.swift index c5caaa32..6b875ad3 100644 --- a/VDS/Fonts/Font.swift +++ b/VDS/Fonts/Font.swift @@ -8,13 +8,14 @@ import Foundation /// Enum that is matched up for the Verizon fonts. -public enum Font: String, FontProtocol { +public enum Font: FontProtocol { case edsBold case edsRegular case dsLight case etxBold case etxRegular - + case custom(String) + public var fontName: String { switch self { case .edsBold: @@ -27,9 +28,15 @@ public enum Font: String, FontProtocol { return "VerizonNHGeTX-Bold" case .etxRegular: return "VerizonNHGeTX-Regular" + case .custom(let fontName): + return fontName } } + public static var allCases: [Font] { + [.edsBold, .edsRegular, .dsLight, .etxBold, .etxRegular] + } + /// File Extension for each of the Font enums. public var fontFileExtension: String { return "otf" diff --git a/VDS/Fonts/FontProtocol.swift b/VDS/Fonts/FontProtocol.swift index 9ee21939..fbca693b 100644 --- a/VDS/Fonts/FontProtocol.swift +++ b/VDS/Fonts/FontProtocol.swift @@ -9,9 +9,10 @@ import Foundation import UIKit /// Used in Classes that require Fonts -public protocol FontProtocol: CaseIterable, RawRepresentable, Hashable { +public protocol FontProtocol: Hashable { var fontFileExtension: String { get } var fontName: String { get } + static var allCases: [Self] { get } } extension FontProtocol { From 23f3f8b6bfe471b29ed8110785911f4e5ae4ae57 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 10:08:50 -0600 Subject: [PATCH 14/76] added a conversion of a UIFont to a TextStyle Signed-off-by: Matt Bruce --- VDS/Typography/Typogprahy+Styles.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/VDS/Typography/Typogprahy+Styles.swift b/VDS/Typography/Typogprahy+Styles.swift index ec144617..0cd6e614 100644 --- a/VDS/Typography/Typogprahy+Styles.swift +++ b/VDS/Typography/Typogprahy+Styles.swift @@ -209,6 +209,24 @@ extension TextStyle { boldMicro ] } + + public static func convert(font: UIFont) -> TextStyle { + var found: TextStyle? + + for textStyle in allCases { + if font.fontName == textStyle.fontFace.fontName && + font.pointSize == textStyle.pointSize { + found = textStyle + break + } + } + + guard let found else { + return TextStyle(rawValue: "Custom", fontFace: .custom(font.fontName), pointSize: font.pointSize) + } + + return found + } } extension TextStyle { From 89a4c5d397e7df17355d5869c14ac03445e12d69 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 10:09:59 -0600 Subject: [PATCH 15/76] updated label with overrides for textColor and font Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 35 +++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index c998c70b..0c89ae3f 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -131,32 +131,43 @@ open class Label: UILabel, ViewProtocol, UserInfoable { /// Line break mode for the label, default is set to word wrapping. open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }} - - private var _text: String? - + /// Text that will be used in the label. - override public var text: String! { + override open var text: String! { get { super.text } set { - // When text is set, we may need to re-style it as attributedText - // with the correct paragraph style to achieve the desired line height. textSetMode = .text - attributes?.removeAll() styleText(newValue) } } ///AttributedText that will be used in the label. - override public var attributedText: NSAttributedString? { + override open var attributedText: NSAttributedString? { get { super.attributedText } set { - // When text is set, we may need to re-style it as attributedText - // with the correct paragraph style to achieve the desired line height. textSetMode = .attributedText styleAttributedText(newValue) } } + override open var font: UIFont! { + didSet { + if let font, initialSetupPerformed { + textStyle = TextStyle.convert(font: font) + } + setNeedsUpdate() + } + } + + override open var textColor: UIColor! { + didSet { + if let textColor, initialSetupPerformed { + textColorConfiguration = SurfaceColorConfiguration(textColor, textColor).eraseToAnyColorable() + } + setNeedsUpdate() + } + } + /// Whether the View is enabled or not. open override var isEnabled: Bool { didSet { setNeedsUpdate() } } @@ -175,13 +186,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- open func initialSetup() { if !initialSetupPerformed { + initialSetupPerformed = true //register for ContentSizeChanges NotificationCenter .Publisher(center: .default, name: UIContentSizeCategory.didChangeNotification) .sink { [weak self] notification in self?.setNeedsUpdate() }.store(in: &subscribers) - backgroundColor = .clear numberOfLines = 0 lineBreakMode = .byWordWrapping @@ -307,7 +318,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { return } - var mutableText = NSMutableAttributedString(attributedString: newValue) + let mutableText = NSMutableAttributedString(attributedString: newValue) applyAttributes(mutableText) From 473ab531e86e354b420811f2d8be77216aeb9f3e Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 12:34:07 -0600 Subject: [PATCH 16/76] fixed bug Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 0c89ae3f..60e88740 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -323,7 +323,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { applyAttributes(mutableText) // Modify attributed text to match typography - super.attributedText = newValue + super.attributedText = mutableText } private func applyAttributes(_ mutableAttributedString: NSMutableAttributedString) { From fbdcdfc2774428db1efa330e18645280d1da84ab Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 12:34:23 -0600 Subject: [PATCH 17/76] refactored method Signed-off-by: Matt Bruce --- VDS/Typography/Typogprahy+Styles.swift | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/VDS/Typography/Typogprahy+Styles.swift b/VDS/Typography/Typogprahy+Styles.swift index 0cd6e614..0136d3c4 100644 --- a/VDS/Typography/Typogprahy+Styles.swift +++ b/VDS/Typography/Typogprahy+Styles.swift @@ -211,20 +211,9 @@ extension TextStyle { } public static func convert(font: UIFont) -> TextStyle { - var found: TextStyle? - - for textStyle in allCases { - if font.fontName == textStyle.fontFace.fontName && - font.pointSize == textStyle.pointSize { - found = textStyle - break - } + guard let found = allCases.first(where: { font.fontName == $0.fontFace.fontName && font.pointSize == $0.pointSize} ) else { + return TextStyle(rawValue: "Custom\(font.fontName)", fontFace: .custom(font), pointSize: font.pointSize) } - - guard let found else { - return TextStyle(rawValue: "Custom", fontFace: .custom(font.fontName), pointSize: font.pointSize) - } - return found } } From fd641a85cf5d2814f759322f9f33ec64c2f3ad37 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 12:34:31 -0600 Subject: [PATCH 18/76] updated to handle custom fonts Signed-off-by: Matt Bruce --- VDS/Fonts/Font.swift | 22 +++++++++++++++++++--- VDS/Fonts/FontProtocol.swift | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/VDS/Fonts/Font.swift b/VDS/Fonts/Font.swift index 6b875ad3..f4ba798d 100644 --- a/VDS/Fonts/Font.swift +++ b/VDS/Fonts/Font.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit /// Enum that is matched up for the Verizon fonts. public enum Font: FontProtocol { @@ -14,7 +15,7 @@ public enum Font: FontProtocol { case dsLight case etxBold case etxRegular - case custom(String) + case custom(UIFont) public var fontName: String { switch self { @@ -28,8 +29,8 @@ public enum Font: FontProtocol { return "VerizonNHGeTX-Bold" case .etxRegular: return "VerizonNHGeTX-Regular" - case .custom(let fontName): - return fontName + case .custom(let font): + return font.fontName } } @@ -41,4 +42,19 @@ public enum Font: FontProtocol { public var fontFileExtension: String { return "otf" } + + /// Returns a UIFont for the fontName and size given. + /// - Parameters: + /// - size: Size of the font + /// - Returns: UIFont for the fontName and Size. + public func font(ofSize size: CGFloat) -> UIFont{ + DispatchQueue.once(block: { self.register() }) + switch self { + case .custom(let font): + return font + default: + guard let found = UIFont(name: self.fontName, size: size) else { return .systemFont(ofSize: size) } + return found + } + } } diff --git a/VDS/Fonts/FontProtocol.swift b/VDS/Fonts/FontProtocol.swift index fbca693b..df6ac6a3 100644 --- a/VDS/Fonts/FontProtocol.swift +++ b/VDS/Fonts/FontProtocol.swift @@ -9,7 +9,7 @@ import Foundation import UIKit /// Used in Classes that require Fonts -public protocol FontProtocol: Hashable { +public protocol FontProtocol { var fontFileExtension: String { get } var fontName: String { get } static var allCases: [Self] { get } From ae12db4a2497738a2f2dc6c07fb3438fa6ad1382 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Mon, 11 Mar 2024 16:25:32 +0530 Subject: [PATCH 19/76] reordering the Pagination folder & made selected index as second element --- VDS.xcodeproj/project.pbxproj | 18 +++++++----------- VDS/Components/Pagination/Pagination.swift | 7 +++++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 82643d0f..0b263712 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -18,16 +18,14 @@ 7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; }; 71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71B23C2C2B91FA690027F7D9 /* Pagination.swift */; }; 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.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 */; }; 71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86D92B96F44C00700965 /* PaginationButton.swift */; }; 71FC86DC2B96F4C800700965 /* PaginationCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */; }; - 71FC86E22B97483000700965 /* Clamping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86E12B97483000700965 /* Clamping.swift */; }; - 71FC86E42B9841AC00700965 /* PaginationFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86E32B9841AC00700965 /* PaginationFlowLayout.swift */; }; - 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 */; }; + 71FC86E22B97483000700965 /* Clamping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86E12B97483000700965 /* Clamping.swift */; }; + 71FC86E42B9841AC00700965 /* PaginationFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86E32B9841AC00700965 /* PaginationFlowLayout.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 */; }; @@ -196,16 +194,14 @@ 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = ""; }; 71B23C2C2B91FA690027F7D9 /* Pagination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pagination.swift; sourceTree = ""; }; 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = PaginationChangeLog.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 = ""; }; 71FC86D92B96F44C00700965 /* PaginationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationButton.swift; sourceTree = ""; }; 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationCellItem.swift; sourceTree = ""; }; - 71FC86E12B97483000700965 /* Clamping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clamping.swift; sourceTree = ""; }; - 71FC86E32B9841AC00700965 /* PaginationFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationFlowLayout.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 = ""; }; 71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceConfigurationValue.swift; sourceTree = ""; }; 71FC86DF2B973AE500700965 /* DropShadowConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowConfiguration.swift; sourceTree = ""; }; + 71FC86E12B97483000700965 /* Clamping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clamping.swift; sourceTree = ""; }; + 71FC86E32B9841AC00700965 /* PaginationFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationFlowLayout.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 = ""; }; @@ -519,7 +515,6 @@ EA33619D288B1E330071C351 /* Components */ = { isa = PBXGroup; children = ( - 71B23C2B2B91FA510027F7D9 /* Pagination */, EA4DB2FE28DCBC1900103EE3 /* Badge */, EAD062AE2A3B87210015965D /* BadgeIndicator */, EA0FC2BE2912D18200DF80B4 /* Buttons */, @@ -529,6 +524,7 @@ 44604AD529CE195300E62B51 /* Line */, EAD0688C2A55F801002E3A2D /* Loader */, 445BA07629C07ABA0036A7C5 /* Notification */, + 71B23C2B2B91FA510027F7D9 /* Pagination */, EA89200B28B530F0006B9984 /* RadioBox */, EAF7F11428A1470D00B287F5 /* RadioButton */, EA596ABB2A16B4D500300C4B /* Tabs */, diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index f4bf8a60..1f00b949 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -142,7 +142,8 @@ open class Pagination: View { ///Refreshing the UI based on the selected page private func updateSelection() { guard _selectedPageIndex < total else { return } - collectionView.scrollToItem(at: IndexPath(row: _selectedPageIndex, section: 0), at: .left, animated: false) + //Need to make selected page as second element so scrolling previous index of the selected page to left + collectionView.scrollToItem(at: IndexPath(row: max(_selectedPageIndex - 1, 0), section: 0), at: .left, animated: false) previousButton.isHidden = _selectedPageIndex == 0 nextButton.isHidden = _selectedPageIndex == total - 1 collectionView.reloadData() @@ -163,7 +164,8 @@ open class Pagination: View { flowLayout.upperLimitDigits = upperLimitDigits flowLayout.invalidateLayout() collectionView.reloadData() - collectionView.scrollToItem(at: IndexPath(row: self._selectedPageIndex, section: 0), at: .left, animated: false) + //Need to make selected page as second element so scrolling previous index of the selected page to left + collectionView.scrollToItem(at: IndexPath(row: max(_selectedPageIndex - 1, 0), section: 0), at: .left, animated: false) } } } @@ -181,6 +183,7 @@ extension Pagination: UICollectionViewDelegate, UICollectionViewDataSource, UICo } public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + guard _selectedPageIndex != indexPath.row else { return } _selectedPageIndex = indexPath.row updateSelection() onPageDidSelect?(selectedPage) From 3a064c914ce016a43c6e723ac8fbe18931c2f137 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Mon, 11 Mar 2024 18:08:33 +0530 Subject: [PATCH 20/76] added Pagination in VDS.md --- VDS/VDS.docc/VDS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/VDS/VDS.docc/VDS.md b/VDS/VDS.docc/VDS.md index 8beb9255..dae2c72a 100755 --- a/VDS/VDS.docc/VDS.md +++ b/VDS/VDS.docc/VDS.md @@ -33,6 +33,7 @@ Using the system allows designers and developers to collaborate more easily and - ``Line`` - ``Loader`` - ``Notification`` +- ``Pagination`` - ``RadioBoxItem`` - ``RadioBoxGroup`` - ``RadioButton`` From 466dbb7c6a2d697020128a0b527931d62e079075 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 12:47:16 -0500 Subject: [PATCH 21/76] update for TitleLockup for label compression/hugging Signed-off-by: Matt Bruce --- VDS/Components/TitleLockup/TitleLockup.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index e11b674d..3802b529 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -68,6 +68,7 @@ open class TitleLockup: View { /// Label used to render the eyebrow model. open var eyebrowLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.setContentHuggingPriority(.required, for: .vertical) } /// Model used in rendering the eyebrow label. @@ -77,6 +78,7 @@ open class TitleLockup: View { /// Label used to render the title model. open var titleLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.setContentHuggingPriority(.required, for: .vertical) $0.accessibilityTraits.insert([.header]) } @@ -87,6 +89,7 @@ open class TitleLockup: View { /// Label used to render the subtitle model. open var subTitleLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.setContentHuggingPriority(.required, for: .vertical) } /// Model used in rendering the subtitle label. @@ -380,7 +383,7 @@ open class TitleLockup: View { } //pin the last view to the bottom of this view - previousView?.pinBottom(0, .defaultHigh) + previousView?.pinBottom(0) //debugging for borders eyebrowLabel.debugBorder(show: hasDebugBorder, color: .green) From 4733a3a00c000300e6056d87fb0ec16d2cce3bba Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 12:47:38 -0500 Subject: [PATCH 22/76] refactored to not use KVO, but a publisher KVO Signed-off-by: Matt Bruce --- VDS/Classes/SelfSizingCollectionView.swift | 24 ++++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/VDS/Classes/SelfSizingCollectionView.swift b/VDS/Classes/SelfSizingCollectionView.swift index c50082c4..6f37308c 100644 --- a/VDS/Classes/SelfSizingCollectionView.swift +++ b/VDS/Classes/SelfSizingCollectionView.swift @@ -34,7 +34,6 @@ public final class SelfSizingCollectionView: UICollectionView { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - private var contentSizeObservation: NSKeyValueObservation? private var collectionViewHeight: NSLayoutConstraint? private var anyCancellable: AnyCancellable? @@ -45,7 +44,6 @@ public final class SelfSizingCollectionView: UICollectionView { /// The natural size for the receiving view, considering only properties of the view itself. public override var intrinsicContentSize: CGSize { let contentSize = self.contentSize - //print(#function, contentSize) return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height) } @@ -68,17 +66,21 @@ public final class SelfSizingCollectionView: UICollectionView { setContentHuggingPriority(.required, for: .vertical) setContentCompressionResistancePriority(.required, for: .vertical) collectionViewHeight = heightAnchor.constraint(equalToConstant: 0).activate() - - // Observing the value of contentSize seems to be the only reliable way to get the contentSize after the collection view lays out its subviews. - self.contentSizeObservation = self.observe(\.contentSize, options: [.old, .new]) { [weak self] _, change in - // If we don't specify `options: [.old, .new]`, the change.oldValue and .newValue will always be `nil`. - if change.newValue != change.oldValue { - self?.invalidateIntrinsicContentSize() - if let height = change.newValue?.height { - self?.collectionViewHeight?.constant = height + + anyCancellable = self.publisher(for: \.contentSize, options: [.old, .new]) + .scan((old: CGSize.zero, new: CGSize.zero)) { accumulator, newValue in + // accumulator.old contains the old contentSize value + // accumulator.new contains the new contentSize value before the current update + // newValue is the current update to contentSize + return (old: accumulator.new, new: newValue) + } + .sink { [weak self] compare in + if compare.old != compare.new { + print("Old contentSize: \(compare.old), New contentSize: \(compare.new)") + self?.invalidateIntrinsicContentSize() + self?.collectionViewHeight?.constant = compare.new.height } } - } } } From 73c7e23b99bb099835323b8ea463a76b0acc71c7 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 12:56:14 -0500 Subject: [PATCH 23/76] removed print Signed-off-by: Matt Bruce --- VDS/Classes/SelfSizingCollectionView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/VDS/Classes/SelfSizingCollectionView.swift b/VDS/Classes/SelfSizingCollectionView.swift index 6f37308c..d8656490 100644 --- a/VDS/Classes/SelfSizingCollectionView.swift +++ b/VDS/Classes/SelfSizingCollectionView.swift @@ -76,7 +76,6 @@ public final class SelfSizingCollectionView: UICollectionView { } .sink { [weak self] compare in if compare.old != compare.new { - print("Old contentSize: \(compare.old), New contentSize: \(compare.new)") self?.invalidateIntrinsicContentSize() self?.collectionViewHeight?.constant = compare.new.height } From 6aa09da464b7693edf207920193f378b2287a355 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 13:51:19 -0500 Subject: [PATCH 24/76] refactored to have a public contentSize publisher Signed-off-by: Matt Bruce --- VDS/Classes/SelfSizingCollectionView.swift | 26 +++++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/VDS/Classes/SelfSizingCollectionView.swift b/VDS/Classes/SelfSizingCollectionView.swift index d8656490..6f4de7d0 100644 --- a/VDS/Classes/SelfSizingCollectionView.swift +++ b/VDS/Classes/SelfSizingCollectionView.swift @@ -36,7 +36,15 @@ public final class SelfSizingCollectionView: UICollectionView { //-------------------------------------------------- private var collectionViewHeight: NSLayoutConstraint? private var anyCancellable: AnyCancellable? - + private var contentSizeSubject = CurrentValueSubject(.zero) + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + public var contentSizePublisher: AnyPublisher { + contentSizeSubject.eraseToAnyPublisher() + } + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -67,17 +75,13 @@ public final class SelfSizingCollectionView: UICollectionView { setContentCompressionResistancePriority(.required, for: .vertical) collectionViewHeight = heightAnchor.constraint(equalToConstant: 0).activate() - anyCancellable = self.publisher(for: \.contentSize, options: [.old, .new]) - .scan((old: CGSize.zero, new: CGSize.zero)) { accumulator, newValue in - // accumulator.old contains the old contentSize value - // accumulator.new contains the new contentSize value before the current update - // newValue is the current update to contentSize - return (old: accumulator.new, new: newValue) - } + anyCancellable = self.publisher(for: \.contentSize, options: [.new]) .sink { [weak self] compare in - if compare.old != compare.new { - self?.invalidateIntrinsicContentSize() - self?.collectionViewHeight?.constant = compare.new.height + guard let self else { return } + if compare.height != self.collectionViewHeight?.constant { + self.invalidateIntrinsicContentSize() + self.collectionViewHeight?.constant = compare.height + self.contentSizeSubject.send(compare) } } } From 59b72835c08b05b2c2012b9f14adc4168ec94a9a Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 13:51:32 -0500 Subject: [PATCH 25/76] include new contentSizePublisher Signed-off-by: Matt Bruce --- VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift index 7d506961..a51371c2 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift @@ -9,6 +9,7 @@ import Foundation import UIKit import VDSColorTokens import VDSFormControlsTokens +import Combine /// A button group contains combinations of related CTAs including ``Button``, ``TextLink``, and ``TextLinkCaret``. This group component controls a combination's orientation, spacing, size and allowable size pairings. @objc(VDSButtonGroup) @@ -98,6 +99,8 @@ open class ButtonGroup: View { buttons.forEach { $0.surface = surface } } } + + open var contentSizePublisher: AnyPublisher { collectionView.contentSizePublisher } //-------------------------------------------------- // MARK: - Private Properties @@ -108,6 +111,7 @@ open class ButtonGroup: View { $0.delegate = self } + /// CollectionView that renders the array of buttonBase obects. fileprivate lazy var collectionView: SelfSizingCollectionView = { return SelfSizingCollectionView(frame: .zero, collectionViewLayout: positionLayout).with { From 1fa8f485f8a92e5dc9ba8bf8e8eb5fb52ce092d2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 15:43:55 -0500 Subject: [PATCH 26/76] updated constraint to defaultHigh Signed-off-by: Matt Bruce --- VDS/Classes/SelfSizingCollectionView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Classes/SelfSizingCollectionView.swift b/VDS/Classes/SelfSizingCollectionView.swift index 6f4de7d0..ad5d5661 100644 --- a/VDS/Classes/SelfSizingCollectionView.swift +++ b/VDS/Classes/SelfSizingCollectionView.swift @@ -73,7 +73,7 @@ public final class SelfSizingCollectionView: UICollectionView { //ensure autoLayout uses intrinsic height setContentHuggingPriority(.required, for: .vertical) setContentCompressionResistancePriority(.required, for: .vertical) - collectionViewHeight = heightAnchor.constraint(equalToConstant: 0).activate() + collectionViewHeight = height(constant: 0, priority: .defaultHigh) anyCancellable = self.publisher(for: \.contentSize, options: [.new]) .sink { [weak self] compare in From 024194e7e56a9c8a6bff0ebb75a9d6d9003febbb Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Tue, 12 Mar 2024 18:24:48 +0530 Subject: [PATCH 27/76] Updated accessibility, add addressed review comments --- VDS.xcodeproj/project.pbxproj | 4 + VDS/Components/Pagination/Pagination.swift | 82 ++++++++++++------- .../Pagination/PaginationCellItem.swift | 1 + .../Pagination/PaginationCollectionView.swift | 66 +++++++++++++++ 4 files changed, 122 insertions(+), 31 deletions(-) create mode 100644 VDS/Components/Pagination/PaginationCollectionView.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 0b263712..38a79f1e 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -16,6 +16,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 */; }; + 71ACE89C2BA0451200FB6ADC /* PaginationCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89B2BA0451200FB6ADC /* PaginationCollectionView.swift */; }; 71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71B23C2C2B91FA690027F7D9 /* Pagination.swift */; }; 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */; }; 71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */; }; @@ -192,6 +193,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 = ""; }; + 71ACE89B2BA0451200FB6ADC /* PaginationCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationCollectionView.swift; sourceTree = ""; }; 71B23C2C2B91FA690027F7D9 /* Pagination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pagination.swift; sourceTree = ""; }; 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = PaginationChangeLog.txt; sourceTree = ""; }; 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowable.swift; sourceTree = ""; }; @@ -404,6 +406,7 @@ isa = PBXGroup; children = ( 71B23C2C2B91FA690027F7D9 /* Pagination.swift */, + 71ACE89B2BA0451200FB6ADC /* PaginationCollectionView.swift */, 71FC86D92B96F44C00700965 /* PaginationButton.swift */, 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */, 71FC86E32B9841AC00700965 /* PaginationFlowLayout.swift */, @@ -1056,6 +1059,7 @@ EAC925842911C63100091998 /* Colorable.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, + 71ACE89C2BA0451200FB6ADC /* PaginationCollectionView.swift in Sources */, EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */, diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index 1f00b949..754935b7 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -21,24 +21,16 @@ open class Pagination: View { ///Selected page index private var _selectedPageIndex: Int = 0 ///Custom flow layout defined for the Pagination - private let flowLayout = PaginationFlowLayout() + private var flowLayout: PaginationFlowLayout { + guard let flowLayout = collectionContainerView.collectionView.collectionViewLayout as? PaginationFlowLayout else { fatalError("Flow layout should be PaginationFlowLayout class") } + return flowLayout + } ///A root view for the pagination private let containerView: View = View().with { $0.translatesAutoresizingMaskIntoConstraints = false } - ///Collectionview to render pagination indexes - private lazy var collectionView: UICollectionView = { - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) - collectionView.isScrollEnabled = false - collectionView.translatesAutoresizingMaskIntoConstraints = false - collectionView.delegate = self - collectionView.dataSource = self - collectionView.showsHorizontalScrollIndicator = false - collectionView.showsVerticalScrollIndicator = false - collectionView.register(PaginationCellItem.self, forCellWithReuseIdentifier: PaginationCellItem.identifier) - collectionView.backgroundColor = .clear - return collectionView - }() + ///Container view to hold collectionview to render pagination indexes + private let collectionContainerView = PaginationCollectionView() //-------------------------------------------------- // MARK: - Public Properties @@ -86,24 +78,32 @@ open class Pagination: View { super.initialSetup() addSubview(containerView) - containerView.pinToSuperView() - containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 288).activate() + containerView + .pinTop() + .pinBottom() + containerView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor).activate() + trailingAnchor.constraint(greaterThanOrEqualTo: containerView.trailingAnchor).activate() + containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + containerView.widthAnchor.constraint(equalToConstant: 288).activate() containerView.heightAnchor.constraint(equalToConstant: 44).activate() containerView.addSubview(previousButton) - containerView.addSubview(collectionView) + containerView.addSubview(collectionContainerView) containerView.addSubview(nextButton) previousButton .pinTop() .pinBottom() .pinLeading() - previousButton.trailingAnchor.constraint(greaterThanOrEqualTo: collectionView.leadingAnchor).activate() - collectionView.heightAnchor.constraint(equalToConstant: VDSLayout.Spacing.space4X.value).activate() - collectionView.centerYAnchor.constraint(equalTo: centerYAnchor).activate() - collectionView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - collectionView.trailingAnchor.constraint(greaterThanOrEqualTo: nextButton.leadingAnchor).activate() - collectionViewWidthAnchor = collectionView.widthAnchor.constraint(equalToConstant: 92) + + previousButton.trailingAnchor.constraint(greaterThanOrEqualTo: collectionContainerView.leadingAnchor).activate() + collectionContainerView.heightAnchor.constraint(equalToConstant: VDSLayout.Spacing.space4X.value).activate() + collectionContainerView.centerYAnchor.constraint(equalTo: centerYAnchor).activate() + collectionContainerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + collectionContainerView.trailingAnchor.constraint(greaterThanOrEqualTo: nextButton.leadingAnchor).activate() + collectionViewWidthAnchor = collectionContainerView.widthAnchor.constraint(equalToConstant: 92) collectionViewWidthAnchor?.activate() + collectionContainerView.collectionView.delegate = self + collectionContainerView.collectionView.dataSource = self nextButton .pinTop() @@ -117,8 +117,24 @@ open class Pagination: View { flowLayout.$collectionViewWidth .receive(on: RunLoop.main) .sink { [weak self] value in - self?.collectionViewWidthAnchor?.constant = value - }.store(in: &subscribers) + self?.collectionViewWidthAnchor?.constant = value //As cell width is dynamic i.e cell may contain 2 or 3 or 4 charcters. Make sure that all the visible cells are displayed. + }.store(in: &subscribers) + collectionContainerView.onAccessibilityIncrement = { [weak self] in + guard let self else { return } + self.selectedPage = max(0, self.selectedPage + 1) + } + collectionContainerView.onAccessibilityDecrement = { [weak self] in + guard let self else { return } + self.selectedPage = max(0, self.selectedPage - 1) + } + } + + ///Updating the accessiblity values i.e elements, label, value other items for the component. + open override func updateAccessibility() { + super.updateAccessibility() + accessibilityElements = [previousButton, collectionContainerView, nextButton] + collectionContainerView.accessibilityLabel = "Pagination containing \(total) pages" + collectionContainerView.accessibilityValue = "Page \(selectedPage) of \(total) selected" } /// Used to make changes to the View based off a change events or from local properties. @@ -126,7 +142,7 @@ open class Pagination: View { super.updateView() nextButton.surface = surface previousButton.surface = surface - collectionView.reloadData() + collectionContainerView.collectionView.reloadData() } //-------------------------------------------------- @@ -137,21 +153,25 @@ open class Pagination: View { let isNextAction = sender == nextButton _selectedPageIndex = if isNextAction { _selectedPageIndex + 1 } else { _selectedPageIndex - 1 } updateSelection() + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) { [weak self] in + guard let self else { return } + UIAccessibility.post(notification: .announcement, argument: "Page \(self.selectedPage) of \(self.total) selected") + } } ///Refreshing the UI based on the selected page private func updateSelection() { guard _selectedPageIndex < total else { return } //Need to make selected page as second element so scrolling previous index of the selected page to left - collectionView.scrollToItem(at: IndexPath(row: max(_selectedPageIndex - 1, 0), section: 0), at: .left, animated: false) + collectionContainerView.collectionView.scrollToItem(at: IndexPath(row: max(_selectedPageIndex - 1, 0), section: 0), at: .left, animated: false) previousButton.isHidden = _selectedPageIndex == 0 nextButton.isHidden = _selectedPageIndex == total - 1 - collectionView.reloadData() + collectionContainerView.collectionView.reloadData() verifyIfMaxDigitChanged() } ///Identifying if there is any change in the digits of upcoming page - func verifyIfMaxDigitChanged() { + private func verifyIfMaxDigitChanged() { let upperLimitPage = _selectedPageIndex + flowLayout.maxNumberOfColumns let upperLimitDigits = upperLimitPage.digitCount //future value digits switch (flowLayout.numberOfColumns, upperLimitDigits) { @@ -163,9 +183,9 @@ open class Pagination: View { if upperLimitDigits != flowLayout.upperLimitDigits { flowLayout.upperLimitDigits = upperLimitDigits flowLayout.invalidateLayout() - collectionView.reloadData() + collectionContainerView.collectionView.reloadData() //Need to make selected page as second element so scrolling previous index of the selected page to left - collectionView.scrollToItem(at: IndexPath(row: max(_selectedPageIndex - 1, 0), section: 0), at: .left, animated: false) + collectionContainerView.collectionView.scrollToItem(at: IndexPath(row: max(_selectedPageIndex - 1, 0), section: 0), at: .left, animated: false) } } } diff --git a/VDS/Components/Pagination/PaginationCellItem.swift b/VDS/Components/Pagination/PaginationCellItem.swift index 68fa7271..68f51142 100644 --- a/VDS/Components/Pagination/PaginationCellItem.swift +++ b/VDS/Components/Pagination/PaginationCellItem.swift @@ -23,6 +23,7 @@ final class PaginationCellItem: UICollectionViewCell { private var indexLabel: Label = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .center + $0.isAccessibilityElement = false $0.numberOfLines = 1 } diff --git a/VDS/Components/Pagination/PaginationCollectionView.swift b/VDS/Components/Pagination/PaginationCollectionView.swift new file mode 100644 index 00000000..a41c65ef --- /dev/null +++ b/VDS/Components/Pagination/PaginationCollectionView.swift @@ -0,0 +1,66 @@ +// +// PaginationCollectionView.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 12/03/24. +// + +import UIKit + +///PaginationCollectionView is a container view that holds collectionview for displaying page indexes +final class PaginationCollectionView: View { + + //-------------------------------------------------- + // MARK: - Internal Properties + //-------------------------------------------------- + ///Notifies when accessibility increment is happend when user swipes up + var onAccessibilityIncrement: (() -> Void)? + ///Notifies when accessibility decrement is happend when user swipes down + var onAccessibilityDecrement: (() -> Void)? + ///Collectionview to render pagination indexes + lazy var collectionView: UICollectionView = { + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) + collectionView.isScrollEnabled = false + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.showsHorizontalScrollIndicator = false + collectionView.showsVerticalScrollIndicator = false + collectionView.isAccessibilityElement = true + collectionView.register(PaginationCellItem.self, forCellWithReuseIdentifier: PaginationCellItem.identifier) + collectionView.backgroundColor = .clear + return collectionView + }() + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + ///Custom flow layout defined for the Pagination + private let flowLayout = PaginationFlowLayout() + + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + ///Accessibilty traits for the Pagination view + override var accessibilityTraits: UIAccessibilityTraits { + get { [.adjustable] } + set { } + } + + ///Accessibilty increment + override func accessibilityIncrement() { + onAccessibilityIncrement?() + } + + ///Accessibilty decrement + override func accessibilityDecrement() { + onAccessibilityDecrement?() + } + + /// Executed on initialization for this View. + override func setup() { + super.setup() + addSubview(collectionView) + collectionView.pinToSuperView() + isAccessibilityElement = true + accessibilityElements = [collectionView] + } +} From 42dccb47805276eb88f37431f139143ca9ee0e5d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 12 Mar 2024 09:55:02 -0500 Subject: [PATCH 28/76] add image helper Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 4 ++++ VDS/Extensions/UIImage+Helper.swift | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 VDS/Extensions/UIImage+Helper.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index b6a3d192..63549b92 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -132,6 +132,7 @@ EAD068922A560B65002E3A2D /* LoaderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD068912A560B65002E3A2D /* LoaderViewController.swift */; }; EAD068942A560C13002E3A2D /* LoaderLaunchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD068932A560C13002E3A2D /* LoaderLaunchable.swift */; }; EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; }; + EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE785302BA0A438009428EA /* UIImage+Helper.swift */; }; EAEEEC922B1F807300531FC2 /* BadgeChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC912B1F807300531FC2 /* BadgeChangeLog.txt */; }; EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC952B1F893B00531FC2 /* ButtonChangeLog.txt */; }; EAEEEC982B1F8DD100531FC2 /* LineChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC972B1F8DD100531FC2 /* LineChangeLog.txt */; }; @@ -303,6 +304,7 @@ EAD068912A560B65002E3A2D /* LoaderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoaderViewController.swift; sourceTree = ""; }; EAD068932A560C13002E3A2D /* LoaderLaunchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoaderLaunchable.swift; sourceTree = ""; }; EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = ""; }; + EAE785302BA0A438009428EA /* UIImage+Helper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Helper.swift"; sourceTree = ""; }; EAEEEC912B1F807300531FC2 /* BadgeChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = BadgeChangeLog.txt; sourceTree = ""; }; EAEEEC952B1F893B00531FC2 /* ButtonChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonChangeLog.txt; sourceTree = ""; }; EAEEEC972B1F8DD100531FC2 /* LineChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LineChangeLog.txt; sourceTree = ""; }; @@ -536,6 +538,7 @@ EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */, EA33623D2892EE950071C351 /* UIDevice.swift */, EA0B18092AA78F9000F2D0CD /* UIEdgeInsets.swift */, + EAE785302BA0A438009428EA /* UIImage+Helper.swift */, EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */, EA8E40902A7D3F6300934ED3 /* UIView+Accessibility.swift */, EAB5FED329267EB300998C17 /* UIView+NSLayoutConstraint.swift */, @@ -1025,6 +1028,7 @@ EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */, EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */, + EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, diff --git a/VDS/Extensions/UIImage+Helper.swift b/VDS/Extensions/UIImage+Helper.swift new file mode 100644 index 00000000..c8f103dc --- /dev/null +++ b/VDS/Extensions/UIImage+Helper.swift @@ -0,0 +1,26 @@ +// +// UIImage+Icon.swift +// VDS +// +// Created by Matt Bruce on 3/11/24. +// + +import Foundation +import UIKit + +extension UIImage { + + /// UIImage helper for finding images based on the Icon.Name which uses the internal BundleManager. + /// - Parameters: + /// - name: RawRepresentable. + /// - color: Color to Tint the image with + /// - renderingMode: UIImage Rendering mode. + /// - Returns: UIImage for this proecess + public static func image(representing representable: T, color: UIColor? = nil, renderingMode: UIImage.RenderingMode = .alwaysOriginal) -> UIImage? where T.RawValue == String { + guard let image = BundleManager.shared.image(for: representable.rawValue) else { return nil } + + guard let color else { return image } + + return image.withTintColor(color, renderingMode: renderingMode) + } +} From 8369403cbe9737f6b679c089752d21ddddb80101 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 12 Mar 2024 09:57:42 -0500 Subject: [PATCH 29/76] refactored for the iconName image helper Signed-off-by: Matt Bruce --- VDS/Components/Icon/Icon.swift | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/VDS/Components/Icon/Icon.swift b/VDS/Components/Icon/Icon.swift index 5ac143f6..f52279f1 100644 --- a/VDS/Components/Icon/Icon.swift +++ b/VDS/Components/Icon/Icon.swift @@ -44,7 +44,7 @@ open class Icon: View { //-------------------------------------------------- /// UIImageView used to render the icon. open var imageView = UIImageView().with { - $0.isAccessibilityElement = false + $0.isAccessibilityElement = false $0.translatesAutoresizingMaskIntoConstraints = false $0.contentMode = .scaleAspectFill $0.clipsToBounds = true @@ -109,8 +109,8 @@ open class Icon: View { //get the image name //set the image - if let name, let image = getImage(for: name.rawValue) { - setImage(image: image, imageColor: imageColor) + if let name, let image = UIImage.image(for: name, color: imageColor) { + imageView.image = image } else { imageView.image = nil } @@ -129,17 +129,17 @@ open class Icon: View { super.updateAccessibility() accessibilityLabel = name?.rawValue ?? "icon" } - - //-------------------------------------------------- - // MARK: - Private Methods - //-------------------------------------------------- - private func getImage(for imageName: String) -> UIImage? { - - return BundleManager.shared.image(for: imageName) - } - - private func setImage(image: UIImage, imageColor: UIColor) { - imageView.image = image.withTintColor(imageColor) - } } +extension UIImage { + + /// UIImage helper for finding images based on the Icon.Name which uses the internal BundleManager. + /// - Parameters: + /// - name: Icon.Name rawRepresentable. + /// - color: Color to Tint the image with + /// - renderingMode: UIImage Rendering mode. + /// - Returns: UIImage for this proecess + public static func image(for iconName: Icon.Name, color: UIColor? = nil, renderingMode: UIImage.RenderingMode = .alwaysOriginal) -> UIImage? { + image(representing: iconName, color: color, renderingMode: renderingMode) + } +} From 7b6334f28d7a1fd5a381ff66f73c60729bdfc3b7 Mon Sep 17 00:00:00 2001 From: vasavk Date: Wed, 13 Mar 2024 07:24:27 +0530 Subject: [PATCH 30/76] Digital ACT-191 ONEAPP-6827 story: added new breadcrumbItem component --- VDS.xcodeproj/project.pbxproj | 20 +++ .../Breadcrumbs/BreadcrumbCellItem.swift | 17 +++ .../Breadcrumbs/BreadcrumbItem.swift | 125 ++++++++++++++++++ VDS/Components/Breadcrumbs/Breadcrumbs.swift | 18 +++ 4 files changed, 180 insertions(+) create mode 100644 VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift create mode 100644 VDS/Components/Breadcrumbs/BreadcrumbItem.swift create mode 100644 VDS/Components/Breadcrumbs/Breadcrumbs.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 366fca71..66ac0ba3 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -7,8 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */; }; 18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; }; + 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A012B96E848006602CC /* Breadcrumbs.swift */; }; + 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A032B96F050006602CC /* BreadcrumbItem.swift */; }; 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; 44604AD429CE186A00E62B51 /* NotificationButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */; }; @@ -175,8 +178,11 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = ""; }; 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = ""; }; + 18A65A012B96E848006602CC /* Breadcrumbs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Breadcrumbs.swift; sourceTree = ""; }; + 18A65A032B96F050006602CC /* BreadcrumbItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItem.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 = ""; }; 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationButtonModel.swift; sourceTree = ""; }; @@ -356,6 +362,16 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 18A65A002B96E7E1006602CC /* Breadcrumbs */ = { + isa = PBXGroup; + children = ( + 18A65A012B96E848006602CC /* Breadcrumbs.swift */, + 18A65A032B96F050006602CC /* BreadcrumbItem.swift */, + 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */, + ); + path = Breadcrumbs; + sourceTree = ""; + }; 445BA07629C07ABA0036A7C5 /* Notification */ = { isa = PBXGroup; children = ( @@ -489,6 +505,7 @@ children = ( EA4DB2FE28DCBC1900103EE3 /* Badge */, EAD062AE2A3B87210015965D /* BadgeIndicator */, + 18A65A002B96E7E1006602CC /* Breadcrumbs */, EA0FC2BE2912D18200DF80B4 /* Buttons */, EAF7F092289985E200B287F5 /* Checkbox */, EA985BF3296C609E00F2FF2E /* Icon */, @@ -994,10 +1011,12 @@ EAF7F0A6289B0CE000B287F5 /* Resetable.swift in Sources */, EA985C2D296F03FE00F2FF2E /* TileletIconModels.swift in Sources */, EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */, + 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */, EA0D1C3F2A6AD5E200E5C127 /* Typography+ContentSizeCategory.swift in Sources */, EA5F86C82A1BD99100BC83E4 /* TabModel.swift in Sources */, EA0D1C432A6AD70900E5C127 /* VDSTypography.swift in Sources */, EA297A5729FB0A360031ED56 /* AppleGuidelinesTouchable.swift in Sources */, + 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */, EA3361C328902D960071C351 /* Toggle.swift in Sources */, EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */, EA89201328B568D8006B9984 /* RadioBoxItem.swift in Sources */, @@ -1005,6 +1024,7 @@ EA3362402892EF6C0071C351 /* Label.swift in Sources */, EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */, EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */, + 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */, EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */, 71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */, EA0D1C452A6AD73000E5C127 /* RawRepresentable.swift in Sources */, diff --git a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift new file mode 100644 index 00000000..5bb6b03a --- /dev/null +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -0,0 +1,17 @@ +// +// BreadcrumbCellItem.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 11/03/24. +// + +import UIKit +import VDSColorTokens + +///This is customised view for Breadcrumb cell item +final class BreadcrumbCellItem: UICollectionViewCell { + + ///Identifier for the PaginationCellItem + static let identifier: String = String(describing: BreadcrumbCellItem.self) + +} diff --git a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift new file mode 100644 index 00000000..ac8951cb --- /dev/null +++ b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift @@ -0,0 +1,125 @@ +// +// BreadcrumbItem.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 05/03/24. +// + +import Foundation +import VDSColorTokens +import VDSFormControlsTokens +import Combine + +/// A Breadcrumb Item contains href(link) and selected flag. +/// Breadcrumb links to its respective page if it is not disabled. +/// Breadcrumb contains text with a separator by default, highlights text in bold without a separator if selected. +@objc (VDSBreadcrumbItem) +open class BreadcrumbItem: ButtonBase { + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + /// The Breadcrumb link to links to its respective page. + open var link: String? { didSet { setNeedsUpdate() } } + + /// TextStyle used on the titleLabel. + open override var textStyle: TextStyle { isSelected ? TextStyle.boldBodySmall : TextStyle.bodySmall } + + /// If true, it will be rendered as selected. + open var selectable: Bool = false { + didSet { + //update selected state + if selectable{ + isSelected = true + } else { + isSelected = false + } + setNeedsUpdate() + } + } + + /// UIColor used on the titleLabel text. + open override var textColor: UIColor { + textColorConfiguration.getColor(self) + } + + /// The natural size for the receiving view, considering only properties of the view itself. + open override var intrinsicContentSize: CGSize { + return titleLabel?.intrinsicContentSize ?? super.intrinsicContentSize + } + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + var separator = " /" + + private var textColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) + $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) + } + + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. + open override func setup() { + super.setup() + isAccessibilityElement = true + accessibilityTraits = .button + accessibilityLabel = "Breadcrumb" + } + + /// Used to make changes to the View based off a change events or from local properties. + open override func updateView() { + //always call last so the label is rendered + if (text != nil) { + var newText: String = text ?? "" + if isSelected { + if newText.contains(separator) { + let result = newText.dropLast(2) + newText = String(result) + } + } else { + if !newText.contains(separator) { + newText = (text ?? "") + separator + } + } + print("newText:\(newText), isSelected: \(isSelected)") + text = newText + if let titleLabel { + titleLabel.text = text + } + } + super.updateView() + + } + + /// Resets to default settings. + open override func reset() { + super.reset() + shouldUpdateView = false + text = nil + link = nil + accessibilityCustomActions = [] + isAccessibilityElement = true + accessibilityTraits = .button + shouldUpdateView = true + setNeedsUpdate() + } +} diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift new file mode 100644 index 00000000..a8269ac9 --- /dev/null +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -0,0 +1,18 @@ +// +// Breadcrumbs.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 11/03/24. +// + +import Foundation +import VDSColorTokens +import Combine + +/// A Breadcrumbs contains BreadcrumbItems. +/// It contains Breadcrumb Item Default, Breadcrumb Item Selected, Separator. +/// Breadcrumbs are secondary navigation that use a hierarchy of internal links to tell customers where they are in an experience. Each breadcrumb links to its respective page, except for that of current page. +@objc(VDSBreadcrumbs) +open class Breadcrumbs: View { + +} From 28e9f79f40e9cd10c75b45423a3513b05b0473b0 Mon Sep 17 00:00:00 2001 From: vasavk Date: Wed, 13 Mar 2024 07:33:36 +0530 Subject: [PATCH 31/76] Digital ACT-191 ONEAPP-6827 story: added Breadcrumbs in VDS.md --- VDS/VDS.docc/VDS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/VDS/VDS.docc/VDS.md b/VDS/VDS.docc/VDS.md index 8beb9255..6b6dbeb6 100755 --- a/VDS/VDS.docc/VDS.md +++ b/VDS/VDS.docc/VDS.md @@ -21,6 +21,7 @@ Using the system allows designers and developers to collaborate more easily and ### Components - ``Badge`` - ``BadgeIndicator`` +- ``Breadcrumbs`` - ``Button`` - ``ButtonIcon`` - ``ButtonGroup`` From 4cc1d5287d70815089dfdbf238946d2ceb0a63ac Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Wed, 13 Mar 2024 12:59:05 +0530 Subject: [PATCH 32/76] updated cell size for 3 & 4 digits --- VDS/Components/Pagination/PaginationFlowLayout.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/VDS/Components/Pagination/PaginationFlowLayout.swift b/VDS/Components/Pagination/PaginationFlowLayout.swift index 89debfe2..542e4eff 100644 --- a/VDS/Components/Pagination/PaginationFlowLayout.swift +++ b/VDS/Components/Pagination/PaginationFlowLayout.swift @@ -18,8 +18,7 @@ final class PaginationFlowLayout : UICollectionViewLayout { ///Pre-defined sizes of the pagination cell based on number of digits. private var upperLimitSize: CGSize { switch upperLimitDigits { - case 3: .init(width: 28, height: 16) - case 4: .init(width: 34, height: 16) + case 3, 4: .init(width: 34, height: 16) default: .init(width: 20, height: 16) } } From 26a0ca220fe6ec82d09ed36389cde34143149af6 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Wed, 13 Mar 2024 13:51:52 +0530 Subject: [PATCH 33/76] removed unused private layout properties --- .../Notification/Notification.swift | 62 ++----------------- 1 file changed, 5 insertions(+), 57 deletions(-) diff --git a/VDS/Components/Notification/Notification.swift b/VDS/Components/Notification/Notification.swift index 9523e6c8..b0f49239 100644 --- a/VDS/Components/Notification/Notification.swift +++ b/VDS/Components/Notification/Notification.swift @@ -53,12 +53,6 @@ open class Notification: View { } } - /// Enum used to describe the orientation of Notification. - /// Only horizontal orientation is supported for iPhone & iPad devices only. - private enum Layout: String, CaseIterable { - case vertical, horizontal - } - //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -81,19 +75,9 @@ open class Notification: View { $0.translatesAutoresizingMaskIntoConstraints = false } - private var labelButtonViewSpacing: CGFloat { - let spacing: CGFloat = UIDevice.isIPad ? 20 : 16 - return switch layout { - case .vertical: - 0 - case .horizontal: - spacing - } - } + private var labelButtonViewSpacing: CGFloat { UIDevice.isIPad ? 20 : 16 } internal var onCloseSubscriber: AnyCancellable? - - private var maxWidthConstraint: NSLayoutConstraint? private var leadingConstraint: NSLayoutConstraint? @@ -173,20 +157,6 @@ open class Notification: View { /// Add this attribute determine your type of Notification. open var style: Style = .info { didSet { setNeedsUpdate()}} - - private var _layout: Layout = .vertical - - /// Determines the orientation of buttons and text in the Notification. - /// Only horizontal orientation is supported for iPhone & iPad devices only. - private var layout: Layout { - set { - if !UIDevice.isIPad, newValue == .horizontal { return } - _layout = newValue - buttonGroup.alignment = _layout == .horizontal ? .center : .left - setNeedsUpdate() - } - get { _layout } - } //-------------------------------------------------- // MARK: - Configuration @@ -221,18 +191,11 @@ open class Notification: View { return 288 } - private var maxViewWidth: CGFloat { - return 1232 - } - - private var labelViewWidthConstraint: NSLayoutConstraint? private var labelViewBottomConstraint: NSLayoutConstraint? private var labelViewAndButtonViewConstraint: NSLayoutConstraint? private var buttonViewTopConstraint: NSLayoutConstraint? private var typeIconWidthConstraint: NSLayoutConstraint? private var closeIconWidthConstraint: NSLayoutConstraint? - private var buttonGroupCenterYConstraint: NSLayoutConstraint? - private var buttonGroupBottomConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Overrides @@ -257,21 +220,18 @@ open class Notification: View { mainStackView.heightAnchor.constraint(greaterThanOrEqualToConstant: minContentHeight), layoutGuide.widthAnchor.constraint(greaterThanOrEqualToConstant: minViewWidth) ]) - maxWidthConstraint = layoutGuide.widthAnchor.constraint(lessThanOrEqualToConstant: maxViewWidth) labelButtonView.addSubview(labelsView) labelsView .pinTop() .pinLeading() - labelViewWidthConstraint = labelsView.widthAnchor.constraint(equalTo: labelButtonView.widthAnchor, multiplier: 1.0) - labelViewWidthConstraint?.activate() + labelsView.widthAnchor.constraint(equalTo: labelButtonView.widthAnchor, multiplier: 1.0).activate() labelViewBottomConstraint = labelButtonView.bottomAnchor.constraint(equalTo: labelsView.bottomAnchor) labelButtonView.addSubview(buttonGroup) buttonGroup .pinTrailing() - buttonGroupBottomConstraint = labelButtonView.bottomAnchor.constraint(equalTo: buttonGroup.bottomAnchor) - buttonGroupCenterYConstraint = buttonGroup.centerYAnchor.constraint(equalTo: labelButtonView.centerYAnchor) + labelButtonView.bottomAnchor.constraint(equalTo: buttonGroup.bottomAnchor).activate() labelViewAndButtonViewConstraint = buttonGroup.topAnchor.constraint(equalTo: labelsView.bottomAnchor, constant: VDSLayout.Spacing.space3X.value) buttonGroup.widthAnchor.constraint(equalTo: labelsView.widthAnchor).activate() @@ -316,7 +276,6 @@ open class Notification: View { closeButton.size = UIDevice.isIPad ? .medium : .small closeButton.name = .close - layout = .vertical hideCloseButton = false shouldUpdateView = true @@ -385,33 +344,22 @@ open class Notification: View { secondaryButton.onClick = secondaryButtonModel.onClick buttons.append(secondaryButton) } - labelViewWidthConstraint?.deactivate() if buttons.isEmpty { buttonGroup.isHidden = true - labelViewWidthConstraint = labelsView.widthAnchor.constraint(equalTo: labelButtonView.widthAnchor) buttonGroup.buttons.removeAll() } else { labelsView.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: subTitleLabel) buttonGroup.buttons = buttons buttonGroup.isHidden = false - labelViewWidthConstraint = labelsView.widthAnchor.constraint(equalTo: labelButtonView.widthAnchor, multiplier: layout == .vertical ? 1.0 : 0.5, constant: layout == .vertical ? 0 : -labelButtonViewSpacing) } - labelViewWidthConstraint?.activate() } private func setConstraints() { labelViewAndButtonViewConstraint?.deactivate() labelViewBottomConstraint?.deactivate() - buttonGroupCenterYConstraint?.deactivate() - buttonGroupBottomConstraint?.deactivate() - labelViewAndButtonViewConstraint?.isActive = layout == .vertical && !buttonGroup.buttons.isEmpty - labelViewBottomConstraint?.isActive = layout == .horizontal || buttonGroup.buttons.isEmpty - buttonGroupCenterYConstraint?.isActive = layout == .horizontal - buttonGroupBottomConstraint?.isActive = layout == .vertical + labelViewAndButtonViewConstraint?.isActive = !buttonGroup.buttons.isEmpty + labelViewBottomConstraint?.isActive = buttonGroup.buttons.isEmpty typeIconWidthConstraint?.constant = typeIcon.size.dimensions.width closeIconWidthConstraint?.constant = closeButton.size.dimensions.width - //iPad 12.9 inches is more than the configured maxViewWidth(1232), verified with designer on the same. Suggested to remove max width constraint - //maxWidthConstraint?.constant = maxViewWidth - //maxWidthConstraint?.isActive = UIDevice.isIPad } } From 663b1b1fede334e29432a5c02e882a6f2115ecc6 Mon Sep 17 00:00:00 2001 From: vasavk Date: Wed, 13 Mar 2024 16:41:21 +0530 Subject: [PATCH 34/76] Digital ACT-191 ONEAPP-6827 story: added Breadcrumbs change log, modified BreadcrumbCellItem --- VDS.xcodeproj/project.pbxproj | 4 + .../Breadcrumbs/BreadcrumbCellItem.swift | 68 +++++++++++++- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 94 +++++++++++++++++++ .../Breadcrumbs/BreadcrumbsChangeLog.txt | 36 +++++++ 4 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 VDS/Components/Breadcrumbs/BreadcrumbsChangeLog.txt diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 66ac0ba3..c86f3116 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; + 18450CF12BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */; }; 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */; }; 18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; }; 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A012B96E848006602CC /* Breadcrumbs.swift */; }; @@ -179,6 +180,7 @@ /* Begin PBXFileReference section */ 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; + 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = BreadcrumbsChangeLog.txt; sourceTree = ""; }; 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = ""; }; 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = ""; }; 18A65A012B96E848006602CC /* Breadcrumbs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Breadcrumbs.swift; sourceTree = ""; }; @@ -368,6 +370,7 @@ 18A65A012B96E848006602CC /* Breadcrumbs.swift */, 18A65A032B96F050006602CC /* BreadcrumbItem.swift */, 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */, + 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */, ); path = Breadcrumbs; sourceTree = ""; @@ -982,6 +985,7 @@ 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */, EA3362062891E14D0071C351 /* VerizonNHGeTX-Regular.otf in Resources */, EA3362052891E14D0071C351 /* VerizonNHGeDS-Bold.otf in Resources */, + 18450CF12BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt in Resources */, EAEEECA02B1F908200531FC2 /* BadgeIndicatorChangeLog.txt in Resources */, EAA5EEB928ECD24B003B3210 /* Icons.xcassets in Resources */, EAEEECA92B1F969700531FC2 /* TooltipChangeLog.txt in Resources */, diff --git a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift index 5bb6b03a..91cd9945 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -11,7 +11,73 @@ import VDSColorTokens ///This is customised view for Breadcrumb cell item final class BreadcrumbCellItem: UICollectionViewCell { - ///Identifier for the PaginationCellItem + ///Identifier for the BreadcrumbCellItem static let identifier: String = String(describing: BreadcrumbCellItem.self) + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + + internal var stackView: UIStackView = { + return UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .horizontal + $0.distribution = .fill + $0.alignment = .top + } + }() + + internal var breadcrumb = BreadcrumbItem().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.titleLabel?.numberOfLines = 0 + } + + ///separator label + private var separator: Label = Label().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.textAlignment = .left + $0.numberOfLines = 0 + $0.text = "/" + } + + private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + override init(frame: CGRect) { + super.init(frame: frame) + setUp() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setUp() + } + + ///Configuring the cell with default setup + private func setUp() { + //add stackview + //this is the horizontal stack that contains breadcrumb and separator + stackView.addArrangedSubview(breadcrumb) + stackView.addArrangedSubview(separator) + stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: breadcrumb) + stackView + .pinTop() + .pinLeading() + .pinTrailing(0, .defaultHigh) + .pinBottom(0, .defaultHigh) + contentView.addSubview(stackView) + separator.backgroundColor = .clear + + stackView.backgroundColor = .red + breadcrumb.backgroundColor = .green + separator.backgroundColor = .yellow + } + + ///Updating UI based on selected index, current index along with surface + func update(_ selectedIndex: Int, currentIndex: Int, surface: Surface, showSlash: Bool) { + separator.textColor = textColorConfiguration.getColor(surface) + separator.isHidden = showSlash + } } diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index a8269ac9..4777d3c6 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -15,4 +15,98 @@ import Combine @objc(VDSBreadcrumbs) open class Breadcrumbs: View { + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + ///Collectionview width anchor + private var collectionViewWidthAnchor: NSLayoutConstraint? + ///Selected page index + private var _selectedPageIndex: Int = 0 + ///A root view for the Breadcrumbs + private let containerView: View = View().with { + $0.translatesAutoresizingMaskIntoConstraints = false + } + + let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout() + + ///Collectionview to render Breadcrumbs indexes + private lazy var collectionView: UICollectionView = { + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + layout.itemSize = CGSize(width: 50, height: 25) + layout.minimumInteritemSpacing = VDSLayout.Spacing.space1X.value + collectionView.isScrollEnabled = false + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsHorizontalScrollIndicator = false + collectionView.showsVerticalScrollIndicator = false + collectionView.register(BreadcrumbCellItem.self, forCellWithReuseIdentifier: BreadcrumbCellItem.identifier) + collectionView.backgroundColor = .clear + return collectionView + }() + + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- +// /// Array of Breadcrumb Items Titles that are shown as Breadcrumbs. +// open var breadcrumbs: [String?] = [] { didSet { setNeedsUpdate() } } + + /// Array of Breadcrumb Items that are shown in the group. + open var breadcrumbItems: [ButtonBase] = [] { didSet { setNeedsUpdate() } } + + /// Whether this object is enabled or not + open var selected: Bool = false { didSet { setNeedsUpdate() } } + + /// Current Surface and this is used to pass down to child objects that implement Surfacable + override open var surface: Surface { + didSet { + breadcrumbItems.forEach { $0.surface = surface } + } + } + + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. + open override func setup() { + super.setup() + } + + /// Executed on initialization for this View. + open override func initialSetup() { + super.initialSetup() + } + + /// Resets to default settings. + open override func reset() { + super.reset() + setNeedsUpdate() + } + + /// Used to make changes to the View based off a change events or from local properties. + open override func updateView() { + super.updateView() + } +} + +extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { + //-------------------------------------------------- + // MARK: - UICollectionView Delegate & Datasource + //-------------------------------------------------- + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { breadcrumbItems.count } + + public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BreadcrumbCellItem.identifier, for: indexPath) as? BreadcrumbCellItem else { return UICollectionViewCell() } + + var showSlash = (indexPath.row == (breadcrumbItems.count - 1)) + cell.update(_selectedPageIndex, currentIndex: indexPath.row, surface: surface, showSlash: showSlash) + return cell + } + + public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { +// _selectedPageIndex = indexPath.row +// updateSelection() +// onPageDidSelect?(selectedPage) + } } diff --git a/VDS/Components/Breadcrumbs/BreadcrumbsChangeLog.txt b/VDS/Components/Breadcrumbs/BreadcrumbsChangeLog.txt new file mode 100644 index 00000000..3fae7754 --- /dev/null +++ b/VDS/Components/Breadcrumbs/BreadcrumbsChangeLog.txt @@ -0,0 +1,36 @@ +MM/DD/YYYY +---------------- +- Initial Brand 3.0 handoff + +12/17/2021 +---------------- +- Replaced focusring colors (previously interactive/onlight/ondark) with accessibility/onlight/ondark colors +- Updated focus border name (previously interactive.focusring.onlight) with focusring.onlight/ondark + +2/28/2022 +---------------- +- Changed Last Breadcrumb Item to Selected Item + +03/08/2022 +---------------- +- Added dev note for Active and Hover states. + +08/04/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 focus border pixel and style & spacing values with tokens. + +01/03/2022 +---------------- +- Updated Specs to use new SPEC Templates and SPEC DOC Components. + +01/06/2023 +---------------- +- Tweaked anatomy element naming to align with design doc and dev doc From f25e9af766b216724ae882c8fb27a4e9c3252d6f Mon Sep 17 00:00:00 2001 From: vasavk Date: Wed, 13 Mar 2024 17:50:19 +0530 Subject: [PATCH 35/76] Digital ACT-191 ONEAPP-6827 story: minor changes to see form fields --- VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift | 7 +++---- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 9 ++++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift index 91cd9945..74e35961 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -17,7 +17,6 @@ final class BreadcrumbCellItem: UICollectionViewCell { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var stackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -70,9 +69,9 @@ final class BreadcrumbCellItem: UICollectionViewCell { contentView.addSubview(stackView) separator.backgroundColor = .clear - stackView.backgroundColor = .red - breadcrumb.backgroundColor = .green - separator.backgroundColor = .yellow +// stackView.backgroundColor = .red +// breadcrumb.backgroundColor = .green +// separator.backgroundColor = .yellow } ///Updating UI based on selected index, current index along with surface diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index 4777d3c6..cfef9936 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -76,6 +76,13 @@ open class Breadcrumbs: View { /// Executed on initialization for this View. open override func initialSetup() { super.initialSetup() + + addSubview(containerView) + containerView.pinToSuperView() + //for TEST + containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 288).activate() + containerView.heightAnchor.constraint(equalToConstant: 150).activate() + containerView.addSubview(collectionView) } /// Resets to default settings. @@ -99,7 +106,7 @@ extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource, UIC public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BreadcrumbCellItem.identifier, for: indexPath) as? BreadcrumbCellItem else { return UICollectionViewCell() } - var showSlash = (indexPath.row == (breadcrumbItems.count - 1)) + let showSlash = (indexPath.row == (breadcrumbItems.count - 1)) cell.update(_selectedPageIndex, currentIndex: indexPath.row, surface: surface, showSlash: showSlash) return cell } From b4765bf0b4da433734205edc0b457550359598d2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 13 Mar 2024 10:41:06 -0500 Subject: [PATCH 36/76] fixed bug for the Label Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 60e88740..db9804d0 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -133,10 +133,12 @@ open class Label: UILabel, ViewProtocol, UserInfoable { open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }} /// Text that will be used in the label. + private var _text: String! override open var text: String! { - get { super.text } + get { _text } set { textSetMode = .text + _text = newValue styleText(newValue) } } @@ -278,7 +280,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- private func restyleText() { if textSetMode == .text { - styleText(text) + styleText(_text) } else { styleAttributedText(attributedText) } @@ -304,10 +306,9 @@ open class Label: UILabel, ViewProtocol, UserInfoable { lineBreakMode: lineBreakMode) applyAttributes(mutableText) - + // Set attributed text to match typography super.attributedText = mutableText - } private func styleAttributedText(_ newValue: NSAttributedString?) { From d7435068a3b9117388c22b89769ea4ccad73d739 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 13 Mar 2024 10:41:06 -0500 Subject: [PATCH 37/76] fixed bug for the Label Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 60e88740..db9804d0 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -133,10 +133,12 @@ open class Label: UILabel, ViewProtocol, UserInfoable { open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }} /// Text that will be used in the label. + private var _text: String! override open var text: String! { - get { super.text } + get { _text } set { textSetMode = .text + _text = newValue styleText(newValue) } } @@ -278,7 +280,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- private func restyleText() { if textSetMode == .text { - styleText(text) + styleText(_text) } else { styleAttributedText(attributedText) } @@ -304,10 +306,9 @@ open class Label: UILabel, ViewProtocol, UserInfoable { lineBreakMode: lineBreakMode) applyAttributes(mutableText) - + // Set attributed text to match typography super.attributedText = mutableText - } private func styleAttributedText(_ newValue: NSAttributedString?) { From 9c8437fe6c1dc482fad0419716f611a47d952fb2 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Wed, 13 Mar 2024 22:28:52 +0530 Subject: [PATCH 38/76] Fixed layout issues --- VDS.xcodeproj/project.pbxproj | 8 +-- VDS/Components/Pagination/Pagination.swift | 62 ++++++++++++------- ...ew.swift => PaginationContainerView.swift} | 25 +------- .../Pagination/PaginationFlowLayout.swift | 5 +- 4 files changed, 48 insertions(+), 52 deletions(-) rename VDS/Components/Pagination/{PaginationCollectionView.swift => PaginationContainerView.swift} (52%) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 38a79f1e..f57edb0f 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -16,7 +16,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 */; }; - 71ACE89C2BA0451200FB6ADC /* PaginationCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89B2BA0451200FB6ADC /* PaginationCollectionView.swift */; }; + 71ACE89C2BA0451200FB6ADC /* PaginationContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89B2BA0451200FB6ADC /* PaginationContainerView.swift */; }; 71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71B23C2C2B91FA690027F7D9 /* Pagination.swift */; }; 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */; }; 71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */; }; @@ -193,7 +193,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 = ""; }; - 71ACE89B2BA0451200FB6ADC /* PaginationCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationCollectionView.swift; sourceTree = ""; }; + 71ACE89B2BA0451200FB6ADC /* PaginationContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationContainerView.swift; sourceTree = ""; }; 71B23C2C2B91FA690027F7D9 /* Pagination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pagination.swift; sourceTree = ""; }; 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = PaginationChangeLog.txt; sourceTree = ""; }; 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowable.swift; sourceTree = ""; }; @@ -406,7 +406,7 @@ isa = PBXGroup; children = ( 71B23C2C2B91FA690027F7D9 /* Pagination.swift */, - 71ACE89B2BA0451200FB6ADC /* PaginationCollectionView.swift */, + 71ACE89B2BA0451200FB6ADC /* PaginationContainerView.swift */, 71FC86D92B96F44C00700965 /* PaginationButton.swift */, 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */, 71FC86E32B9841AC00700965 /* PaginationFlowLayout.swift */, @@ -1059,7 +1059,7 @@ EAC925842911C63100091998 /* Colorable.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, - 71ACE89C2BA0451200FB6ADC /* PaginationCollectionView.swift in Sources */, + 71ACE89C2BA0451200FB6ADC /* PaginationContainerView.swift in Sources */, EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */, diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index 754935b7..7728d6dc 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -16,21 +16,36 @@ open class Pagination: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + ///Maximum component width + private let maxWidth: CGFloat = 288.0 ///Collectionview width anchor private var collectionViewWidthAnchor: NSLayoutConstraint? + ///Collectionview container Center X constraint + private var collectionContainerViewCenterXConstraint: NSLayoutConstraint? ///Selected page index private var _selectedPageIndex: Int = 0 ///Custom flow layout defined for the Pagination - private var flowLayout: PaginationFlowLayout { - guard let flowLayout = collectionContainerView.collectionView.collectionViewLayout as? PaginationFlowLayout else { fatalError("Flow layout should be PaginationFlowLayout class") } - return flowLayout - } + private let flowLayout = PaginationFlowLayout() ///A root view for the pagination - private let containerView: View = View().with { + public let containerView: View = View().with { $0.translatesAutoresizingMaskIntoConstraints = false } - ///Container view to hold collectionview to render pagination indexes - private let collectionContainerView = PaginationCollectionView() + ///Collectionview to render pagination indexes + private lazy var collectionView: UICollectionView = { + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) + collectionView.isScrollEnabled = false + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.showsHorizontalScrollIndicator = false + collectionView.showsVerticalScrollIndicator = false + collectionView.isAccessibilityElement = true + collectionView.register(PaginationCellItem.self, forCellWithReuseIdentifier: PaginationCellItem.identifier) + collectionView.backgroundColor = .clear + collectionView.delegate = self + collectionView.dataSource = self + return collectionView + }() + ///Container view to hold collectionview to render pagination indexes and to handler accessibility. + private let collectionContainerView = PaginationContainerView() //-------------------------------------------------- // MARK: - Public Properties @@ -84,26 +99,27 @@ open class Pagination: View { containerView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor).activate() trailingAnchor.constraint(greaterThanOrEqualTo: containerView.trailingAnchor).activate() containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - containerView.widthAnchor.constraint(equalToConstant: 288).activate() + containerView.widthAnchor.constraint(equalToConstant: maxWidth).activate() containerView.heightAnchor.constraint(equalToConstant: 44).activate() containerView.addSubview(previousButton) containerView.addSubview(collectionContainerView) containerView.addSubview(nextButton) - + collectionContainerView.addSubview(collectionView) previousButton .pinTop() .pinBottom() .pinLeading() previousButton.trailingAnchor.constraint(greaterThanOrEqualTo: collectionContainerView.leadingAnchor).activate() - collectionContainerView.heightAnchor.constraint(equalToConstant: VDSLayout.Spacing.space4X.value).activate() - collectionContainerView.centerYAnchor.constraint(equalTo: centerYAnchor).activate() - collectionContainerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() collectionContainerView.trailingAnchor.constraint(greaterThanOrEqualTo: nextButton.leadingAnchor).activate() - collectionViewWidthAnchor = collectionContainerView.widthAnchor.constraint(equalToConstant: 92) + collectionContainerView + .pinTop() + .pinBottom() + collectionView.heightAnchor.constraint(equalToConstant: VDSLayout.Spacing.space4X.value).activate() + collectionView.centerYAnchor.constraint(equalTo: centerYAnchor).activate() + collectionView.centerXAnchor.constraint(equalTo: collectionContainerView.centerXAnchor).activate() + collectionViewWidthAnchor = collectionView.widthAnchor.constraint(equalToConstant: 92) collectionViewWidthAnchor?.activate() - collectionContainerView.collectionView.delegate = self - collectionContainerView.collectionView.dataSource = self nextButton .pinTop() @@ -142,7 +158,7 @@ open class Pagination: View { super.updateView() nextButton.surface = surface previousButton.surface = surface - collectionContainerView.collectionView.reloadData() + collectionView.reloadData() } //-------------------------------------------------- @@ -163,10 +179,10 @@ open class Pagination: View { private func updateSelection() { guard _selectedPageIndex < total else { return } //Need to make selected page as second element so scrolling previous index of the selected page to left - collectionContainerView.collectionView.scrollToItem(at: IndexPath(row: max(_selectedPageIndex - 1, 0), section: 0), at: .left, animated: false) + collectionView.scrollToItem(at: IndexPath(row: max(_selectedPageIndex - 1, 0), section: 0), at: .left, animated: false) previousButton.isHidden = _selectedPageIndex == 0 nextButton.isHidden = _selectedPageIndex == total - 1 - collectionContainerView.collectionView.reloadData() + collectionView.reloadData() verifyIfMaxDigitChanged() } @@ -175,17 +191,17 @@ open class Pagination: View { let upperLimitPage = _selectedPageIndex + flowLayout.maxNumberOfColumns let upperLimitDigits = upperLimitPage.digitCount //future value digits switch (flowLayout.numberOfColumns, upperLimitDigits) { - case (_, 3), (_, 4): - flowLayout.numberOfColumns = 3 - default: + case (_, 1), (_, 2): flowLayout.numberOfColumns = 4 + default: + flowLayout.numberOfColumns = 3 } if upperLimitDigits != flowLayout.upperLimitDigits { flowLayout.upperLimitDigits = upperLimitDigits flowLayout.invalidateLayout() - collectionContainerView.collectionView.reloadData() + collectionView.reloadData() //Need to make selected page as second element so scrolling previous index of the selected page to left - collectionContainerView.collectionView.scrollToItem(at: IndexPath(row: max(_selectedPageIndex - 1, 0), section: 0), at: .left, animated: false) + collectionView.scrollToItem(at: IndexPath(row: max(_selectedPageIndex - 1, 0), section: 0), at: .left, animated: false) } } } diff --git a/VDS/Components/Pagination/PaginationCollectionView.swift b/VDS/Components/Pagination/PaginationContainerView.swift similarity index 52% rename from VDS/Components/Pagination/PaginationCollectionView.swift rename to VDS/Components/Pagination/PaginationContainerView.swift index a41c65ef..b79a2fe2 100644 --- a/VDS/Components/Pagination/PaginationCollectionView.swift +++ b/VDS/Components/Pagination/PaginationContainerView.swift @@ -1,5 +1,5 @@ // -// PaginationCollectionView.swift +// PaginationContainerView.swift // VDS // // Created by Bandaru, Krishna Kishore on 12/03/24. @@ -8,7 +8,7 @@ import UIKit ///PaginationCollectionView is a container view that holds collectionview for displaying page indexes -final class PaginationCollectionView: View { +final class PaginationContainerView: View { //-------------------------------------------------- // MARK: - Internal Properties @@ -17,24 +17,6 @@ final class PaginationCollectionView: View { var onAccessibilityIncrement: (() -> Void)? ///Notifies when accessibility decrement is happend when user swipes down var onAccessibilityDecrement: (() -> Void)? - ///Collectionview to render pagination indexes - lazy var collectionView: UICollectionView = { - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) - collectionView.isScrollEnabled = false - collectionView.translatesAutoresizingMaskIntoConstraints = false - collectionView.showsHorizontalScrollIndicator = false - collectionView.showsVerticalScrollIndicator = false - collectionView.isAccessibilityElement = true - collectionView.register(PaginationCellItem.self, forCellWithReuseIdentifier: PaginationCellItem.identifier) - collectionView.backgroundColor = .clear - return collectionView - }() - - //-------------------------------------------------- - // MARK: - Private Properties - //-------------------------------------------------- - ///Custom flow layout defined for the Pagination - private let flowLayout = PaginationFlowLayout() //-------------------------------------------------- // MARK: - Overrides @@ -58,9 +40,6 @@ final class PaginationCollectionView: View { /// Executed on initialization for this View. override func setup() { super.setup() - addSubview(collectionView) - collectionView.pinToSuperView() isAccessibilityElement = true - accessibilityElements = [collectionView] } } diff --git a/VDS/Components/Pagination/PaginationFlowLayout.swift b/VDS/Components/Pagination/PaginationFlowLayout.swift index 542e4eff..c6bd02c3 100644 --- a/VDS/Components/Pagination/PaginationFlowLayout.swift +++ b/VDS/Components/Pagination/PaginationFlowLayout.swift @@ -18,8 +18,9 @@ final class PaginationFlowLayout : UICollectionViewLayout { ///Pre-defined sizes of the pagination cell based on number of digits. private var upperLimitSize: CGSize { switch upperLimitDigits { - case 3, 4: .init(width: 34, height: 16) - default: .init(width: 20, height: 16) + case 1, 2: .init(width: 20, height: 16) + case 3: .init(width: 28, height: 16) + default: .init(width: 34, height: 16) } } ///Property to store the defined layout attributes. From 812fac5a597be52e41f13cbb71b797f317ece61b Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 14 Mar 2024 12:15:36 +0530 Subject: [PATCH 39/76] Digital ACT-191 ONEAPP-6827 story: updating cell data --- .../Breadcrumbs/BreadcrumbCellItem.swift | 41 ++++++++++------ .../Breadcrumbs/BreadcrumbItem.swift | 36 +++++++------- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 48 ++++++++----------- 3 files changed, 64 insertions(+), 61 deletions(-) diff --git a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift index 74e35961..c5175001 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -13,16 +13,28 @@ final class BreadcrumbCellItem: UICollectionViewCell { ///Identifier for the BreadcrumbCellItem static let identifier: String = String(describing: BreadcrumbCellItem.self) - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + internal var crumbWidthConstraint: NSLayoutConstraint? + + var crumb : BreadcrumbItem? { + didSet { + guard let crumb = crumb else { return } + breadcrumb = crumb + crumbWidthConstraint?.constant = crumb.intrinsicContentSize.width + crumbWidthConstraint?.isActive = true + } + } + internal var stackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .horizontal $0.distribution = .fill $0.alignment = .top + $0.spacing = VDSLayout.Spacing.space1X.value } }() @@ -41,6 +53,12 @@ final class BreadcrumbCellItem: UICollectionViewCell { private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + private var crumbTextColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) + $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) + } + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -56,27 +74,22 @@ final class BreadcrumbCellItem: UICollectionViewCell { ///Configuring the cell with default setup private func setUp() { - //add stackview - //this is the horizontal stack that contains breadcrumb and separator + //add stackview - this is the horizontal stack that contains breadcrumb and separator stackView.addArrangedSubview(breadcrumb) + crumbWidthConstraint = breadcrumb.widthAnchor.constraint(greaterThanOrEqualToConstant: 0).activate() stackView.addArrangedSubview(separator) stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: breadcrumb) - stackView - .pinTop() - .pinLeading() - .pinTrailing(0, .defaultHigh) - .pinBottom(0, .defaultHigh) + stackView.pinToSuperView() contentView.addSubview(stackView) separator.backgroundColor = .clear - -// stackView.backgroundColor = .red -// breadcrumb.backgroundColor = .green -// separator.backgroundColor = .yellow } ///Updating UI based on selected index, current index along with surface - func update(_ selectedIndex: Int, currentIndex: Int, surface: Surface, showSlash: Bool) { + func update(surface: Surface, hideSlash: Bool, breadCrumbItem: BreadcrumbItem) { + breadcrumb = breadCrumbItem + breadcrumb.text = breadCrumbItem.text separator.textColor = textColorConfiguration.getColor(surface) - separator.isHidden = showSlash + separator.isHidden = hideSlash + print("selected: \(breadcrumb.isSelected), hideSlash: \(hideSlash), text: \(String(describing: breadCrumbItem.text)))") } } diff --git a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift index ac8951cb..a5972496 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift @@ -88,24 +88,24 @@ open class BreadcrumbItem: ButtonBase { /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { //always call last so the label is rendered - if (text != nil) { - var newText: String = text ?? "" - if isSelected { - if newText.contains(separator) { - let result = newText.dropLast(2) - newText = String(result) - } - } else { - if !newText.contains(separator) { - newText = (text ?? "") + separator - } - } - print("newText:\(newText), isSelected: \(isSelected)") - text = newText - if let titleLabel { - titleLabel.text = text - } - } +// if (text != nil) { +// var newText: String = text ?? "" +// if isSelected { +// if newText.contains(separator) { +// let result = newText.dropLast(2) +// newText = String(result) +// } +// } else { +// if !newText.contains(separator) { +// newText = (text ?? "") + separator +// } +// } +// print("newText:\(newText), isSelected: \(isSelected)") +// text = newText +// if let titleLabel { +// titleLabel.text = text +// } +// } super.updateView() } diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index cfef9936..d6325629 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -18,21 +18,11 @@ open class Breadcrumbs: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - ///Collectionview width anchor - private var collectionViewWidthAnchor: NSLayoutConstraint? - ///Selected page index - private var _selectedPageIndex: Int = 0 - ///A root view for the Breadcrumbs - private let containerView: View = View().with { - $0.translatesAutoresizingMaskIntoConstraints = false - } - let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout() ///Collectionview to render Breadcrumbs indexes private lazy var collectionView: UICollectionView = { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) - layout.itemSize = CGSize(width: 50, height: 25) layout.minimumInteritemSpacing = VDSLayout.Spacing.space1X.value collectionView.isScrollEnabled = false collectionView.translatesAutoresizingMaskIntoConstraints = false @@ -49,15 +39,15 @@ open class Breadcrumbs: View { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- -// /// Array of Breadcrumb Items Titles that are shown as Breadcrumbs. -// open var breadcrumbs: [String?] = [] { didSet { setNeedsUpdate() } } - /// Array of Breadcrumb Items that are shown in the group. open var breadcrumbItems: [ButtonBase] = [] { didSet { setNeedsUpdate() } } /// Whether this object is enabled or not - open var selected: Bool = false { didSet { setNeedsUpdate() } } - + override open var isEnabled: Bool { + didSet { + breadcrumbItems.forEach { $0.isEnabled = isEnabled } + } + } /// Current Surface and this is used to pass down to child objects that implement Surfacable override open var surface: Surface { didSet { @@ -76,13 +66,9 @@ open class Breadcrumbs: View { /// Executed on initialization for this View. open override func initialSetup() { super.initialSetup() - - addSubview(containerView) - containerView.pinToSuperView() - //for TEST - containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 288).activate() - containerView.heightAnchor.constraint(equalToConstant: 150).activate() - containerView.addSubview(collectionView) + addSubview(collectionView) + collectionView.pinToSuperView() + collectionView.heightAnchor.constraint(equalToConstant: 100).activate() } /// Resets to default settings. @@ -94,6 +80,7 @@ open class Breadcrumbs: View { /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() + collectionView.reloadData() } } @@ -101,19 +88,22 @@ extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource, UIC //-------------------------------------------------- // MARK: - UICollectionView Delegate & Datasource //-------------------------------------------------- - public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { breadcrumbItems.count } + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { breadcrumbItems.count + } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BreadcrumbCellItem.identifier, for: indexPath) as? BreadcrumbCellItem else { return UICollectionViewCell() } - - let showSlash = (indexPath.row == (breadcrumbItems.count - 1)) - cell.update(_selectedPageIndex, currentIndex: indexPath.row, surface: surface, showSlash: showSlash) + let breadcrumb : BreadcrumbItem = breadcrumbItems[indexPath.row] as! BreadcrumbItem + let hideSlash = (indexPath.row == (breadcrumbItems.count - 1)) + cell.crumb = breadcrumb + cell.update(surface: surface, hideSlash: hideSlash, breadCrumbItem: breadcrumb) return cell } + public func collectionView(_ collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize { + breadcrumbItems[indexPath.row].intrinsicContentSize + } + public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { -// _selectedPageIndex = indexPath.row -// updateSelection() -// onPageDidSelect?(selectedPage) } } From a39e08871156b9bd853e00ad18f693884493d2c5 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Thu, 14 Mar 2024 13:02:32 +0530 Subject: [PATCH 40/76] Digital ACT-191 ONEAPP-6827 story: fixed layout issues --- .../Breadcrumbs/BreadcrumbCellItem.swift | 41 ++++++++++++------- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 18 ++++---- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift index c5175001..c9d66327 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -22,7 +22,7 @@ final class BreadcrumbCellItem: UICollectionViewCell { var crumb : BreadcrumbItem? { didSet { guard let crumb = crumb else { return } - breadcrumb = crumb + breadCrumbItem = crumb crumbWidthConstraint?.constant = crumb.intrinsicContentSize.width crumbWidthConstraint?.isActive = true } @@ -33,15 +33,12 @@ final class BreadcrumbCellItem: UICollectionViewCell { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .horizontal $0.distribution = .fill - $0.alignment = .top + $0.alignment = .fill $0.spacing = VDSLayout.Spacing.space1X.value } }() - internal var breadcrumb = BreadcrumbItem().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.titleLabel?.numberOfLines = 0 - } + internal var breadCrumbItem: BreadcrumbItem? ///separator label private var separator: Label = Label().with { @@ -74,22 +71,36 @@ final class BreadcrumbCellItem: UICollectionViewCell { ///Configuring the cell with default setup private func setUp() { - //add stackview - this is the horizontal stack that contains breadcrumb and separator - stackView.addArrangedSubview(breadcrumb) - crumbWidthConstraint = breadcrumb.widthAnchor.constraint(greaterThanOrEqualToConstant: 0).activate() - stackView.addArrangedSubview(separator) - stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: breadcrumb) - stackView.pinToSuperView() contentView.addSubview(stackView) + stackView.pinToSuperView() separator.backgroundColor = .clear } ///Updating UI based on selected index, current index along with surface func update(surface: Surface, hideSlash: Bool, breadCrumbItem: BreadcrumbItem) { - breadcrumb = breadCrumbItem - breadcrumb.text = breadCrumbItem.text + stackView.removeAllArrangedSubviews() + stackView.addArrangedSubview(separator) + stackView.addArrangedSubview(breadCrumbItem) + stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: breadCrumbItem) separator.textColor = textColorConfiguration.getColor(surface) separator.isHidden = hideSlash - print("selected: \(breadcrumb.isSelected), hideSlash: \(hideSlash), text: \(String(describing: breadCrumbItem.text)))") + print("selected: \(breadCrumbItem.isSelected), hideSlash: \(hideSlash), text: \(String(describing: breadCrumbItem.text)))") + self.breadCrumbItem = breadCrumbItem + layoutIfNeeded() + } +} + +extension UIStackView { + + @discardableResult + func removeAllArrangedSubviews() -> [UIView] { + return arrangedSubviews.reduce([UIView]()) { $0 + [removeArrangedSubViewProperly($1)] } + } + + func removeArrangedSubViewProperly(_ view: UIView) -> UIView { + removeArrangedSubview(view) + NSLayoutConstraint.deactivate(view.constraints) + view.removeFromSuperview() + return view } } diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index d6325629..cd48c012 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -18,12 +18,17 @@ open class Breadcrumbs: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout() + let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout().with { + $0.estimatedItemSize = UICollectionViewFlowLayout.automaticSize + $0.minimumInteritemSpacing = VDSLayout.Spacing.space1X.value + $0.minimumLineSpacing = VDSLayout.Spacing.space1X.value + $0.sectionInset = .zero + $0.scrollDirection = .vertical + } ///Collectionview to render Breadcrumbs indexes private lazy var collectionView: UICollectionView = { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) - layout.minimumInteritemSpacing = VDSLayout.Spacing.space1X.value collectionView.isScrollEnabled = false collectionView.translatesAutoresizingMaskIntoConstraints = false collectionView.delegate = self @@ -84,7 +89,7 @@ open class Breadcrumbs: View { } } -extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { +extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource { //-------------------------------------------------- // MARK: - UICollectionView Delegate & Datasource //-------------------------------------------------- @@ -94,16 +99,11 @@ extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource, UIC public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BreadcrumbCellItem.identifier, for: indexPath) as? BreadcrumbCellItem else { return UICollectionViewCell() } let breadcrumb : BreadcrumbItem = breadcrumbItems[indexPath.row] as! BreadcrumbItem - let hideSlash = (indexPath.row == (breadcrumbItems.count - 1)) - cell.crumb = breadcrumb + let hideSlash = (indexPath.row == 0) cell.update(surface: surface, hideSlash: hideSlash, breadCrumbItem: breadcrumb) return cell } - public func collectionView(_ collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize { - breadcrumbItems[indexPath.row].intrinsicContentSize - } - public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { } } From 2703112148b2631611e4eb2d7a9eecfb36a79847 Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 14 Mar 2024 15:05:01 +0530 Subject: [PATCH 41/76] Digital ACT-191 ONEAPP-6827 story: removed unused and commented code --- .../Breadcrumbs/BreadcrumbCellItem.swift | 22 +--------- .../Breadcrumbs/BreadcrumbItem.swift | 20 --------- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 44 +++++++++---------- 3 files changed, 23 insertions(+), 63 deletions(-) diff --git a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift index c9d66327..84111e11 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -17,17 +17,6 @@ final class BreadcrumbCellItem: UICollectionViewCell { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var crumbWidthConstraint: NSLayoutConstraint? - - var crumb : BreadcrumbItem? { - didSet { - guard let crumb = crumb else { return } - breadCrumbItem = crumb - crumbWidthConstraint?.constant = crumb.intrinsicContentSize.width - crumbWidthConstraint?.isActive = true - } - } - internal var stackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -50,12 +39,6 @@ final class BreadcrumbCellItem: UICollectionViewCell { private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) - private var crumbTextColorConfiguration = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) - $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) - } - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -76,15 +59,14 @@ final class BreadcrumbCellItem: UICollectionViewCell { separator.backgroundColor = .clear } - ///Updating UI based on selected index, current index along with surface + ///Updating the breadCrumbItem and UI based on the selected flag along with the surface func update(surface: Surface, hideSlash: Bool, breadCrumbItem: BreadcrumbItem) { stackView.removeAllArrangedSubviews() stackView.addArrangedSubview(separator) stackView.addArrangedSubview(breadCrumbItem) - stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: breadCrumbItem) + stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: separator) separator.textColor = textColorConfiguration.getColor(surface) separator.isHidden = hideSlash - print("selected: \(breadCrumbItem.isSelected), hideSlash: \(hideSlash), text: \(String(describing: breadCrumbItem.text)))") self.breadCrumbItem = breadCrumbItem layoutIfNeeded() } diff --git a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift index a5972496..856099b8 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift @@ -66,8 +66,6 @@ open class BreadcrumbItem: ButtonBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - var separator = " /" - private var textColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) @@ -88,24 +86,6 @@ open class BreadcrumbItem: ButtonBase { /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { //always call last so the label is rendered -// if (text != nil) { -// var newText: String = text ?? "" -// if isSelected { -// if newText.contains(separator) { -// let result = newText.dropLast(2) -// newText = String(result) -// } -// } else { -// if !newText.contains(separator) { -// newText = (text ?? "") + separator -// } -// } -// print("newText:\(newText), isSelected: \(isSelected)") -// text = newText -// if let titleLabel { -// titleLabel.text = text -// } -// } super.updateView() } diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index cd48c012..5a7e05d0 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -15,6 +15,25 @@ import Combine @objc(VDSBreadcrumbs) open class Breadcrumbs: View { + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + /// Array of Breadcrumb Items that are shown in the group. + open var breadcrumbItems: [BreadcrumbItem] = [] { didSet { setNeedsUpdate() } } + + /// Whether this object is enabled or not + override open var isEnabled: Bool { + didSet { + breadcrumbItems.forEach { $0.isEnabled = isEnabled } + } + } + /// Current Surface and this is used to pass down to child objects that implement Surfacable + override open var surface: Surface { + didSet { + breadcrumbItems.forEach { $0.surface = surface } + } + } + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -40,26 +59,6 @@ open class Breadcrumbs: View { return collectionView }() - - //-------------------------------------------------- - // MARK: - Public Properties - //-------------------------------------------------- - /// Array of Breadcrumb Items that are shown in the group. - open var breadcrumbItems: [ButtonBase] = [] { didSet { setNeedsUpdate() } } - - /// Whether this object is enabled or not - override open var isEnabled: Bool { - didSet { - breadcrumbItems.forEach { $0.isEnabled = isEnabled } - } - } - /// Current Surface and this is used to pass down to child objects that implement Surfacable - override open var surface: Surface { - didSet { - breadcrumbItems.forEach { $0.surface = surface } - } - } - //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -73,7 +72,7 @@ open class Breadcrumbs: View { super.initialSetup() addSubview(collectionView) collectionView.pinToSuperView() - collectionView.heightAnchor.constraint(equalToConstant: 100).activate() + collectionView.heightAnchor.constraint(equalToConstant: 80).activate() } /// Resets to default settings. @@ -98,9 +97,8 @@ extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource { public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BreadcrumbCellItem.identifier, for: indexPath) as? BreadcrumbCellItem else { return UICollectionViewCell() } - let breadcrumb : BreadcrumbItem = breadcrumbItems[indexPath.row] as! BreadcrumbItem let hideSlash = (indexPath.row == 0) - cell.update(surface: surface, hideSlash: hideSlash, breadCrumbItem: breadcrumb) + cell.update(surface: surface, hideSlash: hideSlash, breadCrumbItem: breadcrumbItems[indexPath.row]) return cell } From 547b03ac1cd5e47f400e34b22f00f9e1d8c03cfd Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 14 Mar 2024 18:42:33 -0500 Subject: [PATCH 42/76] fixed issue with attributedText setting not working correctly Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index db9804d0..fd77dc7e 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -108,14 +108,14 @@ open class Label: UILabel, ViewProtocol, UserInfoable { // MARK: - Public Properties //-------------------------------------------------- /// Key of whether or not updateView() is called in setNeedsUpdate() - open var shouldUpdateView: Bool = true + open var shouldUpdateView: Bool = true /// Will determine if a scaled font should be used for the font. open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }} open var surface: Surface = .light { didSet { setNeedsUpdate() }} - /// Array of LabelAttributeModel objects used in rendering the text. + /// Array of LabelAttributeModel objects used in rendering the text. open var attributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() }} /// TextStyle used on the this label. @@ -135,20 +135,18 @@ open class Label: UILabel, ViewProtocol, UserInfoable { /// Text that will be used in the label. private var _text: String! override open var text: String! { - get { _text } - set { + didSet { + _text = text textSetMode = .text - _text = newValue - styleText(newValue) + setNeedsUpdate() } } ///AttributedText that will be used in the label. override open var attributedText: NSAttributedString? { - get { super.attributedText } - set { + didSet { textSetMode = .attributedText - styleAttributedText(newValue) + setNeedsUpdate() } } @@ -306,7 +304,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { lineBreakMode: lineBreakMode) applyAttributes(mutableText) - + // Set attributed text to match typography super.attributedText = mutableText } @@ -322,7 +320,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { let mutableText = NSMutableAttributedString(attributedString: newValue) applyAttributes(mutableText) - + // Modify attributed text to match typography super.attributedText = mutableText } @@ -424,3 +422,5 @@ open class Label: UILabel, ViewProtocol, UserInfoable { } } } + + From 60f71a7709733f9304de0082c1fd3d859b95150d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 14 Mar 2024 18:49:21 -0500 Subject: [PATCH 43/76] added string empty checks. Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index fd77dc7e..955d9604 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -286,7 +286,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { private func styleText(_ newValue: String!) { defer { invalidateIntrinsicContentSize() } - guard let newValue else { + guard let newValue, !newValue.isEmpty else { // We don't need to use attributed text super.attributedText = nil super.text = newValue @@ -311,7 +311,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { private func styleAttributedText(_ newValue: NSAttributedString?) { defer { invalidateIntrinsicContentSize() } - guard let newValue = newValue else { + guard let newValue, !newValue.string.isEmpty else { // We don't need any additional styling super.attributedText = newValue return From fdd0d35f2b683f6c05dff5c3e50cecc202480a0f Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Fri, 15 Mar 2024 17:05:15 +0530 Subject: [PATCH 44/76] Added EyebrowModel for tilelet --- VDS.xcodeproj/project.pbxproj | 6 +- VDS/Components/Tilelet/Tilelet.swift | 20 ++++--- .../Tilelet/TileletSubTitleModel.swift | 3 - .../Tilelet/TiletEyebrowModel.swift | 55 +++++++++++++++++++ 4 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 VDS/Components/Tilelet/TiletEyebrowModel.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 1d773b71..a22c51b8 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; 710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 710607942B91A99500F2863F /* TitleletChangeLog.txt */; }; 7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; }; + 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */; }; 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 */; }; @@ -190,6 +191,7 @@ 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; 710607942B91A99500F2863F /* TitleletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TitleletChangeLog.txt; sourceTree = ""; }; 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = ""; }; + 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TiletEyebrowModel.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 = ""; }; 71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceConfigurationValue.swift; sourceTree = ""; }; @@ -691,8 +693,9 @@ children = ( EA5E3057295105A40082B959 /* Tilelet.swift */, EA985BE529688F6A00F2FF2E /* TileletBadgeModel.swift */, - EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */, + 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */, EA985BE72968951C00F2FF2E /* TileletTitleModel.swift */, + EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */, EA985C2C296F03FE00F2FF2E /* TileletIconModels.swift */, 710607942B91A99500F2863F /* TitleletChangeLog.txt */, ); @@ -1042,6 +1045,7 @@ EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */, + 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */, EAF7F11728A1475A00B287F5 /* RadioButtonItem.swift in Sources */, EA985BEE2968A92400F2FF2E /* TitleLockupSubTitleModel.swift in Sources */, EA985BF22968B5BB00F2FF2E /* TitleLockupTextStyle.swift in Sources */, diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index f8a3b4db..0f595343 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -250,6 +250,9 @@ open class Tilelet: TileContainerBase { /// If set, this is used to render the subTitleLabel of the TitleLockup. open var subTitleModel: SubTitleModel? { didSet { setNeedsUpdate() } } + /// If set, this is used to render the eyebrowLabel of the TitleLockup. + open var eyebrowModel: EyebrowModel? { didSet { setNeedsUpdate() } } + //only 1 Icon can be active private var _descriptiveIconModel: DescriptiveIcon? @@ -320,7 +323,7 @@ open class Tilelet: TileContainerBase { titleLockupBottomGreaterThanConstraint = titleLockupContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLockup.bottomAnchor) titleLockupTopGreaterThanConstraint = titleLockup.topAnchor.constraint(greaterThanOrEqualTo: titleLockupContainerView.topAnchor) titleLockupCenterYConstraint = titleLockup.centerYAnchor.constraint(equalTo: titleLockupContainerView.centerYAnchor) - + iconContainerView.addSubview(descriptiveIcon) iconContainerView.addSubview(directionalIcon) @@ -335,7 +338,7 @@ open class Tilelet: TileContainerBase { .pinBottom() //If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). - var labelPriority = UILayoutPriority.defaultHigh.rawValue + let labelPriority = UILayoutPriority.defaultHigh.rawValue titleLockup.titleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority), for: .vertical) badge.label.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-1), for: .vertical) titleLockup.subTitleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-2), for: .vertical) @@ -400,7 +403,11 @@ open class Tilelet: TileContainerBase { private func updateTitleLockup() { var showTitleLockup = false - + + if let eyebrowModel, !eyebrowModel.text.isEmpty { + showTitleLockup = true + } + if let titleModel, !titleModel.text.isEmpty { showTitleLockup = true } @@ -444,6 +451,7 @@ open class Tilelet: TileContainerBase { } //set models + titleLockup.eyebrowModel = eyebrowModel?.toTitleLockupEyebrowModel() titleLockup.titleModel = titleModel?.toTitleLockupTitleModel() titleLockup.subTitleModel = subTitleModel?.toTitleLockupSubTitleModel() @@ -495,12 +503,6 @@ open class Tilelet: TileContainerBase { } else { removeFromSuperview(iconContainerView) } - if let lastElement = titleLockup.lastElement { - titleLockupTitleLabelBottomConstraint?.deactivate() - let anchorConstraint = showIconContainerView ? iconContainerView.topAnchor : containerView.bottomAnchor - titleLockupTitleLabelBottomConstraint = anchorConstraint.constraint(greaterThanOrEqualTo: lastElement.bottomAnchor) - titleLockupTitleLabelBottomConstraint?.activate() - } } private func updateTextPositionAlignment() { diff --git a/VDS/Components/Tilelet/TileletSubTitleModel.swift b/VDS/Components/Tilelet/TileletSubTitleModel.swift index 62adda7e..fb68ff53 100644 --- a/VDS/Components/Tilelet/TileletSubTitleModel.swift +++ b/VDS/Components/Tilelet/TileletSubTitleModel.swift @@ -48,14 +48,11 @@ extension Tilelet { otherStandardStyle: OtherStandardStyle = .bodySmall, textColor: Use = .primary, textAttributes: [any LabelAttributeModel]? = nil, - standardStyle: StandardStyle = .bodySmall, lineBreakMode: NSLineBreakMode = .byTruncatingTail) { - textAttributes: [any LabelAttributeModel]? = nil) { self.text = text self.otherStandardStyle = otherStandardStyle self.textAttributes = textAttributes self.textColor = textColor - self.standardStyle = standardStyle self.lineBreakMode = lineBreakMode } diff --git a/VDS/Components/Tilelet/TiletEyebrowModel.swift b/VDS/Components/Tilelet/TiletEyebrowModel.swift new file mode 100644 index 00000000..e7c008b4 --- /dev/null +++ b/VDS/Components/Tilelet/TiletEyebrowModel.swift @@ -0,0 +1,55 @@ +// +// TiletEyebrowModel.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 13/03/24. +// + +import Foundation +import UIKit + +extension Tilelet { + + /// Model that represents the options available for the eyebrow label. + public struct EyebrowModel { + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + /// Text that will be used for the eyebrow label. + public var text: String = "" + + /// Used in combination with standardStyle to set the textStyle that will be used for the eyebrow label. + public var isBold: Bool = false + /// Text attributes that will be used for the eyebrow label. + public var textAttributes: [any LabelAttributeModel]? + + /// Text style that will be used for the eyebrow label. If subtitle standard style + public var standardStyle: Tilelet.SubTitleModel.OtherStandardStyle = .titleSmall + + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public init(text: String, + textAttributes: [any LabelAttributeModel]? = nil, + isBold: Bool = true, + standardStyle: Tilelet.SubTitleModel.OtherStandardStyle = .bodySmall, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { + self.text = text + self.textAttributes = textAttributes + self.standardStyle = standardStyle + self.isBold = isBold + self.lineBreakMode = lineBreakMode + } + + //-------------------------------------------------- + // MARK: - Public Methods + //-------------------------------------------------- + /// Converts this type of model to a TitleLockup.TitleModel. + public func toTitleLockupEyebrowModel() -> TitleLockup.EyebrowModel { + TitleLockup.EyebrowModel(text: text, isBold: isBold, standardStyle: standardStyle.value, textAttributes: textAttributes) + } + } +} From 6290a4300404c9d7726dde2af2d4d4b8445931d3 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 18 Mar 2024 10:53:13 -0500 Subject: [PATCH 45/76] removed intrinsic size Signed-off-by: Matt Bruce --- VDS/Components/Buttons/ButtonBase.swift | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index cc096d41..e8a379d9 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -141,17 +141,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { shouldUpdateView = true setNeedsUpdate() } - - //-------------------------------------------------- - // MARK: - Overrides - //-------------------------------------------------- - override open var intrinsicContentSize: CGSize { - let intrinsicContentSize = super.intrinsicContentSize - let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right - let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom - return CGSize(width: adjustedWidth, height: adjustedHeight) - } - + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- From 2e0d0be8b68786b9be98add64b9576be1771a139 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 18 Mar 2024 10:53:42 -0500 Subject: [PATCH 46/76] left aligned content for scenarios to where the container is stretched. Signed-off-by: Matt Bruce --- VDS/Components/Buttons/TextLink/TextLink.swift | 4 ++++ VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/VDS/Components/Buttons/TextLink/TextLink.swift b/VDS/Components/Buttons/TextLink/TextLink.swift index dcb4a46d..1b1f79e8 100644 --- a/VDS/Components/Buttons/TextLink/TextLink.swift +++ b/VDS/Components/Buttons/TextLink/TextLink.swift @@ -87,6 +87,10 @@ open class TextLink: ButtonBase { isAccessibilityElement = true accessibilityTraits = .link + //left align titleLabel in case this is pinned leading/trailing + //default is always set to center + contentHorizontalAlignment = .left + if let titleLabel { addSubview(line) line.pinLeading(titleLabel.leadingAnchor) diff --git a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift index aaa9f3cb..eb3776ea 100644 --- a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift +++ b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift @@ -79,6 +79,11 @@ open class TextLinkCaret: ButtonBase { /// 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() + + //left align titleLabel in case this is pinned leading/trailing + //default is always set to center + contentHorizontalAlignment = .left + accessibilityTraits = .link titleLabel?.numberOfLines = 0 titleLabel?.lineBreakMode = .byWordWrapping From 51562aa58900977d168ece15e20d5c9a3627cec6 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 18 Mar 2024 11:13:23 -0500 Subject: [PATCH 47/76] fixed issues for intrinsicSize Signed-off-by: Matt Bruce --- VDS/Components/Buttons/TextLink/TextLink.swift | 8 +++++++- .../Buttons/TextLinkCaret/TextLinkCaret.swift | 11 ++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/VDS/Components/Buttons/TextLink/TextLink.swift b/VDS/Components/Buttons/TextLink/TextLink.swift index 1b1f79e8..51221b34 100644 --- a/VDS/Components/Buttons/TextLink/TextLink.swift +++ b/VDS/Components/Buttons/TextLink/TextLink.swift @@ -75,7 +75,13 @@ open class TextLink: ButtonBase { /// The natural size for the receiving view, considering only properties of the view itself. open override var intrinsicContentSize: CGSize { - return titleLabel?.intrinsicContentSize ?? super.intrinsicContentSize + guard let titleLabel else { return super.intrinsicContentSize } + // Calculate the titleLabel's intrinsic content size + let labelSize = titleLabel.sizeThatFits(CGSize(width: self.frame.width, height: CGFloat.greatestFiniteMagnitude)) + // Adjust the size if needed (add any additional padding if your design requires) + let adjustedSize = CGSize(width: labelSize.width + contentEdgeInsets.left + contentEdgeInsets.right, + height: labelSize.height + contentEdgeInsets.top + contentEdgeInsets.bottom) + return adjustedSize } //-------------------------------------------------- diff --git a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift index eb3776ea..f02d2b10 100644 --- a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift +++ b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift @@ -103,9 +103,14 @@ open class TextLinkCaret: ButtonBase { } /// The natural size for the receiving view, considering only properties of the view itself. - override open var intrinsicContentSize: CGSize { - //get the labels size, if not the button - return titleLabel?.intrinsicContentSize ?? super.intrinsicContentSize + open override var intrinsicContentSize: CGSize { + guard let titleLabel else { return super.intrinsicContentSize } + // Calculate the titleLabel's intrinsic content size + let labelSize = titleLabel.sizeThatFits(CGSize(width: self.frame.width, height: CGFloat.greatestFiniteMagnitude)) + // Adjust the size if needed (add any additional padding if your design requires) + let adjustedSize = CGSize(width: labelSize.width + contentEdgeInsets.left + contentEdgeInsets.right, + height: labelSize.height + contentEdgeInsets.top + contentEdgeInsets.bottom) + return adjustedSize } } From d76da5d216fc547fc5a6274f5c0389640c6844fd Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 18 Mar 2024 10:53:13 -0500 Subject: [PATCH 48/76] removed intrinsic size Signed-off-by: Matt Bruce --- VDS/Components/Buttons/ButtonBase.swift | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index cc096d41..e8a379d9 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -141,17 +141,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { shouldUpdateView = true setNeedsUpdate() } - - //-------------------------------------------------- - // MARK: - Overrides - //-------------------------------------------------- - override open var intrinsicContentSize: CGSize { - let intrinsicContentSize = super.intrinsicContentSize - let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right - let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom - return CGSize(width: adjustedWidth, height: adjustedHeight) - } - + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- From 3c8176062c300804c6d7f8228241f67a0803e9be Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 18 Mar 2024 10:53:42 -0500 Subject: [PATCH 49/76] left aligned content for scenarios to where the container is stretched. Signed-off-by: Matt Bruce --- VDS/Components/Buttons/TextLink/TextLink.swift | 4 ++++ VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/VDS/Components/Buttons/TextLink/TextLink.swift b/VDS/Components/Buttons/TextLink/TextLink.swift index dcb4a46d..1b1f79e8 100644 --- a/VDS/Components/Buttons/TextLink/TextLink.swift +++ b/VDS/Components/Buttons/TextLink/TextLink.swift @@ -87,6 +87,10 @@ open class TextLink: ButtonBase { isAccessibilityElement = true accessibilityTraits = .link + //left align titleLabel in case this is pinned leading/trailing + //default is always set to center + contentHorizontalAlignment = .left + if let titleLabel { addSubview(line) line.pinLeading(titleLabel.leadingAnchor) diff --git a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift index aaa9f3cb..eb3776ea 100644 --- a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift +++ b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift @@ -79,6 +79,11 @@ open class TextLinkCaret: ButtonBase { /// 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() + + //left align titleLabel in case this is pinned leading/trailing + //default is always set to center + contentHorizontalAlignment = .left + accessibilityTraits = .link titleLabel?.numberOfLines = 0 titleLabel?.lineBreakMode = .byWordWrapping From 7d892b3190217fce828cf5dd9b33bd9d0e116c59 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 18 Mar 2024 11:13:23 -0500 Subject: [PATCH 50/76] fixed issues for intrinsicSize Signed-off-by: Matt Bruce --- VDS/Components/Buttons/TextLink/TextLink.swift | 8 +++++++- .../Buttons/TextLinkCaret/TextLinkCaret.swift | 11 ++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/VDS/Components/Buttons/TextLink/TextLink.swift b/VDS/Components/Buttons/TextLink/TextLink.swift index 1b1f79e8..51221b34 100644 --- a/VDS/Components/Buttons/TextLink/TextLink.swift +++ b/VDS/Components/Buttons/TextLink/TextLink.swift @@ -75,7 +75,13 @@ open class TextLink: ButtonBase { /// The natural size for the receiving view, considering only properties of the view itself. open override var intrinsicContentSize: CGSize { - return titleLabel?.intrinsicContentSize ?? super.intrinsicContentSize + guard let titleLabel else { return super.intrinsicContentSize } + // Calculate the titleLabel's intrinsic content size + let labelSize = titleLabel.sizeThatFits(CGSize(width: self.frame.width, height: CGFloat.greatestFiniteMagnitude)) + // Adjust the size if needed (add any additional padding if your design requires) + let adjustedSize = CGSize(width: labelSize.width + contentEdgeInsets.left + contentEdgeInsets.right, + height: labelSize.height + contentEdgeInsets.top + contentEdgeInsets.bottom) + return adjustedSize } //-------------------------------------------------- diff --git a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift index eb3776ea..f02d2b10 100644 --- a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift +++ b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift @@ -103,9 +103,14 @@ open class TextLinkCaret: ButtonBase { } /// The natural size for the receiving view, considering only properties of the view itself. - override open var intrinsicContentSize: CGSize { - //get the labels size, if not the button - return titleLabel?.intrinsicContentSize ?? super.intrinsicContentSize + open override var intrinsicContentSize: CGSize { + guard let titleLabel else { return super.intrinsicContentSize } + // Calculate the titleLabel's intrinsic content size + let labelSize = titleLabel.sizeThatFits(CGSize(width: self.frame.width, height: CGFloat.greatestFiniteMagnitude)) + // Adjust the size if needed (add any additional padding if your design requires) + let adjustedSize = CGSize(width: labelSize.width + contentEdgeInsets.left + contentEdgeInsets.right, + height: labelSize.height + contentEdgeInsets.top + contentEdgeInsets.bottom) + return adjustedSize } } From 597753e3c01cd9b836611b66cdca5dbbf0dfc15d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 18 Mar 2024 16:30:29 -0500 Subject: [PATCH 51/76] added invalidateCOntentSize after label updates Signed-off-by: Matt Bruce --- VDS/Components/Buttons/ButtonBase.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index e8a379d9..46f72c55 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -146,7 +146,8 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { // MARK: - Private Methods //-------------------------------------------------- private func updateLabel() { - + defer { invalidateIntrinsicContentSize() } + //clear the arrays holding actions accessibilityCustomActions = [] if let text, !text.isEmpty { From 0258fba13b49290767d3a9c1071ca9a3fd3ed0dc Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 18 Mar 2024 16:51:02 -0500 Subject: [PATCH 52/76] fixed bug in multiline label within UIbutton Signed-off-by: Matt Bruce --- .../Buttons/TextLinkCaret/TextLinkCaret.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift index f02d2b10..df276354 100644 --- a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift +++ b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift @@ -106,12 +106,20 @@ open class TextLinkCaret: ButtonBase { open override var intrinsicContentSize: CGSize { guard let titleLabel else { return super.intrinsicContentSize } // Calculate the titleLabel's intrinsic content size - let labelSize = titleLabel.sizeThatFits(CGSize(width: self.frame.width, height: CGFloat.greatestFiniteMagnitude)) + let labelSize = titleLabel.sizeThatFits(CGSize(width: self.frame.width - (contentEdgeInsets.left + contentEdgeInsets.right), height: CGFloat.greatestFiniteMagnitude)) // Adjust the size if needed (add any additional padding if your design requires) let adjustedSize = CGSize(width: labelSize.width + contentEdgeInsets.left + contentEdgeInsets.right, height: labelSize.height + contentEdgeInsets.top + contentEdgeInsets.bottom) return adjustedSize } + + open override func layoutSubviews() { + super.layoutSubviews() + // This ensures the titleLabel is correctly positioned within the button + titleLabel?.preferredMaxLayoutWidth = self.frame.width - (contentEdgeInsets.left + contentEdgeInsets.right) + super.layoutSubviews() // Calling super again to ensure layout is updated with preferredMaxLayoutWidth + } + } extension TextLinkCaret { From 4c9c844b1d770b43aaac52bdf3167a3ec6d83f10 Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 19 Mar 2024 14:31:16 +0530 Subject: [PATCH 53/76] Digital ACT-191 ONEAPP-6827 story: alignment issue fix on surface change --- .../Breadcrumbs/BreadcrumbCellItem.swift | 37 ++++++++++++------- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 20 ++++++++-- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift index 84111e11..f8899577 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -33,7 +33,9 @@ final class BreadcrumbCellItem: UICollectionViewCell { private var separator: Label = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .left - $0.numberOfLines = 0 + $0.numberOfLines = 1 + $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) $0.text = "/" } @@ -54,6 +56,7 @@ final class BreadcrumbCellItem: UICollectionViewCell { ///Configuring the cell with default setup private func setUp() { + separator.textColorConfiguration = textColorConfiguration.eraseToAnyColorable() contentView.addSubview(stackView) stackView.pinToSuperView() separator.backgroundColor = .clear @@ -61,7 +64,8 @@ final class BreadcrumbCellItem: UICollectionViewCell { ///Updating the breadCrumbItem and UI based on the selected flag along with the surface func update(surface: Surface, hideSlash: Bool, breadCrumbItem: BreadcrumbItem) { - stackView.removeAllArrangedSubviews() + separator.surface = surface + breadCrumbItem.surface = surface stackView.addArrangedSubview(separator) stackView.addArrangedSubview(breadCrumbItem) stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: separator) @@ -72,17 +76,24 @@ final class BreadcrumbCellItem: UICollectionViewCell { } } -extension UIStackView { - - @discardableResult - func removeAllArrangedSubviews() -> [UIView] { - return arrangedSubviews.reduce([UIView]()) { $0 + [removeArrangedSubViewProperly($1)] } - } +class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout { - func removeArrangedSubViewProperly(_ view: UIView) -> UIView { - removeArrangedSubview(view) - NSLayoutConstraint.deactivate(view.constraints) - view.removeFromSuperview() - return view + override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { + let attributes = super.layoutAttributesForElements(in: rect) + + var leftMargin = sectionInset.left + var maxY: CGFloat = -1.0 + attributes?.forEach { layoutAttribute in + if layoutAttribute.frame.origin.y >= maxY { + leftMargin = sectionInset.left + } + + layoutAttribute.frame.origin.x = leftMargin + + leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing + maxY = max(layoutAttribute.frame.maxY , maxY) + } + + return attributes } } diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index 5a7e05d0..b69e5ead 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -27,6 +27,7 @@ open class Breadcrumbs: View { breadcrumbItems.forEach { $0.isEnabled = isEnabled } } } + /// Current Surface and this is used to pass down to child objects that implement Surfacable override open var surface: Surface { didSet { @@ -37,7 +38,7 @@ open class Breadcrumbs: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout().with { + let layout: UICollectionViewFlowLayout = LeftAlignedCollectionViewFlowLayout().with { $0.estimatedItemSize = UICollectionViewFlowLayout.automaticSize $0.minimumInteritemSpacing = VDSLayout.Spacing.space1X.value $0.minimumLineSpacing = VDSLayout.Spacing.space1X.value @@ -45,7 +46,7 @@ open class Breadcrumbs: View { $0.scrollDirection = .vertical } - ///Collectionview to render Breadcrumbs indexes + ///Collectionview to render Breadcrumb Items private lazy var collectionView: UICollectionView = { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.isScrollEnabled = false @@ -78,6 +79,9 @@ open class Breadcrumbs: View { /// Resets to default settings. open override func reset() { super.reset() + shouldUpdateView = false + breadcrumbItems.forEach { $0.reset() } + shouldUpdateView = true setNeedsUpdate() } @@ -86,6 +90,15 @@ open class Breadcrumbs: View { super.updateView() collectionView.reloadData() } + + open override func layoutSubviews() { + super.layoutSubviews() + // Accounts for any collection size changes + DispatchQueue.main.async { [weak self] in + guard let self else { return } + self.collectionView.collectionViewLayout.invalidateLayout() + } + } } extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource { @@ -102,6 +115,7 @@ extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource { return cell } - public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + public func collectionView(_ collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize { + breadcrumbItems[indexPath.row].intrinsicContentSize } } From 868c728f63a40b3a66eb38185b934154cefe9b88 Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 19 Mar 2024 17:33:19 +0530 Subject: [PATCH 54/76] Digital ACT-191 ONEAPP-6827 story: container size changes --- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index b69e5ead..94d6e6fe 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -38,6 +38,11 @@ open class Breadcrumbs: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + // Sizes are from InVision design specs. + internal var containerSize: CGSize { CGSize(width: 45, height: 44) } + + internal var heightConstraint: NSLayoutConstraint? + let layout: UICollectionViewFlowLayout = LeftAlignedCollectionViewFlowLayout().with { $0.estimatedItemSize = UICollectionViewFlowLayout.automaticSize $0.minimumInteritemSpacing = VDSLayout.Spacing.space1X.value @@ -60,12 +65,16 @@ open class Breadcrumbs: View { return collectionView }() - //-------------------------------------------------- + //------------------------------------------s-------- // MARK: - Overrides //-------------------------------------------------- /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() + //create the wrapping view + heightConstraint = self.heightAnchor.constraint(equalToConstant: containerSize.height) + heightConstraint?.priority = .defaultHigh + heightConstraint?.isActive = true } /// Executed on initialization for this View. @@ -73,7 +82,6 @@ open class Breadcrumbs: View { super.initialSetup() addSubview(collectionView) collectionView.pinToSuperView() - collectionView.heightAnchor.constraint(equalToConstant: 80).activate() } /// Resets to default settings. @@ -89,6 +97,8 @@ open class Breadcrumbs: View { open override func updateView() { super.updateView() collectionView.reloadData() + heightConstraint?.constant = collectionView.collectionViewLayout.collectionViewContentSize.height + heightConstraint?.isActive = true } open override func layoutSubviews() { From baf934ba8da4cb8ff25c1290b927991cf0d94bca Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Tue, 19 Mar 2024 18:44:25 +0530 Subject: [PATCH 55/76] added Truncation logic in tilelet --- VDS/Components/Tilelet/Tilelet.swift | 58 ++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 0f595343..b1367736 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -289,6 +289,11 @@ open class Tilelet: TileContainerBase { internal var titleLockupBottomGreaterThanConstraint: NSLayoutConstraint? internal var titleLockupCenterYConstraint: NSLayoutConstraint? internal var titleLockupTitleLabelBottomConstraint: NSLayoutConstraint? + //Truncation constraints + internal var badgeLabelHeightGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupEyebrowLabelHeightGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupSubTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Overrides @@ -319,7 +324,7 @@ open class Tilelet: TileContainerBase { titleLockupBottomConstraint = titleLockupContainerView.bottomAnchor.constraint(equalTo: titleLockup.bottomAnchor) titleLockupBottomConstraint?.activate() titleLockupTrailingConstraint = titleLockup.trailingAnchor.constraint(equalTo: titleLockupContainerView.trailingAnchor) - titleLockupTrailingConstraint?.isActive = true + titleLockupTrailingConstraint?.activate() titleLockupBottomGreaterThanConstraint = titleLockupContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLockup.bottomAnchor) titleLockupTopGreaterThanConstraint = titleLockup.topAnchor.constraint(greaterThanOrEqualTo: titleLockupContainerView.topAnchor) titleLockupCenterYConstraint = titleLockup.centerYAnchor.constraint(equalTo: titleLockupContainerView.centerYAnchor) @@ -337,11 +342,46 @@ open class Tilelet: TileContainerBase { .pinTop() .pinBottom() - //If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). + badge.bottomAnchor.constraint(equalTo: badge.label.bottomAnchor, constant: 2).activate() + + /** + Truncation: + If a Tilelet has only a Title or a Subtitle, then the Title or Subtitle is truncated and appended with an ellipsis when there is not enough space to display the full text. + If a Tilelet has both Title and Subtitle, then only Subtitle will be truncated. + If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). + Minimum bottom space below Badge is 4px; less than 4px results in truncation. + */ let labelPriority = UILayoutPriority.defaultHigh.rawValue titleLockup.titleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority), for: .vertical) badge.label.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-1), for: .vertical) titleLockup.subTitleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-2), for: .vertical) + titleLockup.eyebrowLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-3), for: .vertical) + + titleLockup.titleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority), for: .vertical) + badge.label.setContentHuggingPriority(UILayoutPriority(labelPriority-1), for: .vertical) + titleLockup.subTitleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-2), for: .vertical) + titleLockup.eyebrowLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-3), for: .vertical) + + /** + Added these constraints for: + At fixed width & height if all the labels(Badge, Eyebrow, Title, Subtitle) are having more number of lines then we should display atleast one line of content per label instead of pushing labels out of bounds. + So adding minimum single line height constraint + */ + badgeLabelHeightGreaterThanConstraint = badge.label.heightGreaterThanEqualTo(constant: badge.label.minimumLineHeight) + badgeLabelHeightGreaterThanConstraint?.priority = .defaultHigh + badgeLabelHeightGreaterThanConstraint?.activate() + + titleLockupEyebrowLabelHeightGreaterThanConstraint = titleLockup.eyebrowLabel.heightGreaterThanEqualTo(constant: titleLockup.eyebrowLabel.minimumLineHeight) + titleLockupEyebrowLabelHeightGreaterThanConstraint?.priority = .defaultHigh + titleLockupEyebrowLabelHeightGreaterThanConstraint?.activate() + + titleLockupTitleLabelHeightGreaterThanConstraint = titleLockup.titleLabel.heightGreaterThanEqualTo(constant: titleLockup.titleLabel.minimumLineHeight) + titleLockupTitleLabelHeightGreaterThanConstraint?.priority = .defaultHigh + titleLockupTitleLabelHeightGreaterThanConstraint?.activate() + + titleLockupSubTitleLabelHeightGreaterThanConstraint = titleLockup.subTitleLabel.heightGreaterThanEqualTo(constant: titleLockup.subTitleLabel.minimumLineHeight) + titleLockupSubTitleLabelHeightGreaterThanConstraint?.priority = .defaultHigh + titleLockupSubTitleLabelHeightGreaterThanConstraint?.activate() } /// Resets to default settings. @@ -391,6 +431,7 @@ open class Tilelet: TileContainerBase { badge.numberOfLines = badgeModel.numberOfLines badge.surface = badgeModel.surface badge.maxWidth = badgeModel.maxWidth + badgeLabelHeightGreaterThanConstraint?.constant = badge.label.minimumLineHeight if badgeContainerView.superview == nil { stackView.insertArrangedSubview(badgeContainerView, at: 0) setNeedsLayout() @@ -462,6 +503,9 @@ open class Tilelet: TileContainerBase { } else { removeFromSuperview(titleLockupContainerView) } + titleLockupEyebrowLabelHeightGreaterThanConstraint?.constant = titleLockup.eyebrowLabel.minimumLineHeight + titleLockupTitleLabelHeightGreaterThanConstraint?.constant = titleLockup.titleLabel.minimumLineHeight + titleLockupSubTitleLabelHeightGreaterThanConstraint?.constant = titleLockup.subTitleLabel.minimumLineHeight } private func updateIcons() { @@ -529,11 +573,11 @@ open class Tilelet: TileContainerBase { } } -extension TitleLockup { +extension Label { - fileprivate var lastElement: UIView? { - guard subTitleModel == nil else { return subTitleLabel } - guard titleModel == nil else { return titleLabel } - return nil + ///To calculate label single line height + fileprivate var minimumLineHeight: CGFloat { + guard let text, !text.isEmpty, let attributes = attributedText?.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: attributedText?.string.count ?? 0))[.paragraphStyle] as? NSParagraphStyle else { return font.lineHeight } + return attributes.minimumLineHeight } } From 86e91c3ac7398ff4ed92abecc6b04f7c9c18c865 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Wed, 20 Mar 2024 16:50:47 +0530 Subject: [PATCH 56/76] Added Center X & Y constraints and modified code based on constraints --- VDS/Components/Pagination/Pagination.swift | 38 +++-- .../Pagination/PaginationCellItem.swift | 2 +- VDS/Protocols/LayoutConstraintable.swift | 152 ++++++++++++++++++ 3 files changed, 174 insertions(+), 18 deletions(-) diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index 7728d6dc..72cb3224 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -92,34 +92,38 @@ open class Pagination: View { open override func initialSetup() { super.initialSetup() - addSubview(containerView) - containerView - .pinTop() - .pinBottom() - containerView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor).activate() - trailingAnchor.constraint(greaterThanOrEqualTo: containerView.trailingAnchor).activate() - containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - containerView.widthAnchor.constraint(equalToConstant: maxWidth).activate() - containerView.heightAnchor.constraint(equalToConstant: 44).activate() + collectionContainerView.addSubview(collectionView) containerView.addSubview(previousButton) containerView.addSubview(collectionContainerView) containerView.addSubview(nextButton) - collectionContainerView.addSubview(collectionView) + addSubview(containerView) + + containerView + .pinTop() + .pinBottom() + .pinLeadingGreaterThanOrEqualTo() + .pinTrailingLessThanOrEqualTo() + .pinCenterX() + .width(maxWidth) + .height(44) + previousButton .pinTop() .pinBottom() .pinLeading() + .pinTrailingGreaterThanOrEqualTo(collectionContainerView.leadingAnchor) - previousButton.trailingAnchor.constraint(greaterThanOrEqualTo: collectionContainerView.leadingAnchor).activate() - collectionContainerView.trailingAnchor.constraint(greaterThanOrEqualTo: nextButton.leadingAnchor).activate() collectionContainerView + .pinTrailingGreaterThanOrEqualTo(nextButton.leadingAnchor) .pinTop() .pinBottom() - collectionView.heightAnchor.constraint(equalToConstant: VDSLayout.Spacing.space4X.value).activate() - collectionView.centerYAnchor.constraint(equalTo: centerYAnchor).activate() - collectionView.centerXAnchor.constraint(equalTo: collectionContainerView.centerXAnchor).activate() - collectionViewWidthAnchor = collectionView.widthAnchor.constraint(equalToConstant: 92) - collectionViewWidthAnchor?.activate() + + collectionView + .height(VDSLayout.Spacing.space4X.value) + .pinCenterY() + .pinCenterX() + + collectionViewWidthAnchor = collectionView.width(constant: 92) nextButton .pinTop() diff --git a/VDS/Components/Pagination/PaginationCellItem.swift b/VDS/Components/Pagination/PaginationCellItem.swift index 68f51142..24edc3a4 100644 --- a/VDS/Components/Pagination/PaginationCellItem.swift +++ b/VDS/Components/Pagination/PaginationCellItem.swift @@ -48,7 +48,7 @@ final class PaginationCellItem: UICollectionViewCell { contentView.addSubview(containerView) containerView.pinToSuperView() indexLabel.pinToSuperView() - indexLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: VDSLayout.Spacing.space5X.value).activate() + indexLabel.widthGreaterThanEqualTo(VDSLayout.Spacing.space5X.value) contentView.backgroundColor = .clear containerView.backgroundColor = .clear indexLabel.backgroundColor = .clear diff --git a/VDS/Protocols/LayoutConstraintable.swift b/VDS/Protocols/LayoutConstraintable.swift index 3c05bbc7..457b5fa5 100644 --- a/VDS/Protocols/LayoutConstraintable.swift +++ b/VDS/Protocols/LayoutConstraintable.swift @@ -478,7 +478,159 @@ extension LayoutConstraintable { } } +//-------------------------------------------------- +// MARK: - Center X Constraints +//-------------------------------------------------- +extension LayoutConstraintable { + + @discardableResult + /// Adds a centerXAnchor. + /// - Parameter constant: Constant size. + /// - Returns: Yourself. + public func pinCenterX(_ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self { + pinCenterX(nil, constant) + } + @discardableResult + /// Adds a centerXAnchor to a specific XAxisAnchor. + /// - Parameter anchor:The anchor in which to attach the centerXAnchor. + /// - constant: Constant size. + /// - Returns: Yourself. + public func pinCenterX(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self { + pinCenterX(anchor: anchor, constant: constant) + return self + } + + @discardableResult + /// Adds a centerXAnchor to a specific XAxisAnchor passed in using a lessThanOrEqualTo Constraint + /// - Parameter anchor:The anchor in which to attach the centerXAnchor + /// - constant: Constant size. + /// - Returns: Yourself. + public func pinCenterXLessThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self { + pinCenterXLessThanOrEqualTo(anchor: anchor, constant: constant) + return self + } + + @discardableResult + /// Adds a centerXAnchor to a specific XAxisAnchor passed in using a greaterThanOrEqualTo Constraint + /// - Parameter anchor:The anchor in which to attach the centerXAnchor + /// - constant: Constant size. + /// - Returns: Yourself. + public func pinCenterXGreaterThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self { + pinCenterXGreaterThanOrEqualTo(anchor: anchor, constant: constant) + return self + } + + @discardableResult + /// Adds a centerXAnchor for the constant passed into the method. + /// - Parameter anchor:The anchor in which to attach the centerXAnchor + /// - constant: Constant size. + /// - Returns: The Constraint that was created. + public func pinCenterX(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? { + let found: NSLayoutXAxisAnchor? = anchor ?? superview?.centerXAnchor + guard let found else { return nil } + return centerXAnchor.constraint(equalTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true } + } + + @discardableResult + /// Adds a centerXAnchor with the constant passed in using a lessThanOrEqualTo Constraint. + /// - Parameter anchor:The anchor in which to attach the centerXAnchor + /// - constant: Constant size. + /// - Returns: The Constraint that was created. + public func pinCenterXLessThanOrEqualTo(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? { + let found: NSLayoutXAxisAnchor? = anchor ?? superview?.centerXAnchor + guard let found else { return nil } + return centerXAnchor.constraint(lessThanOrEqualTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true } + } + + @discardableResult + /// Adds a centerXAnchor with the constant passed in using a greaterThanOrEqualTo Constraint. + /// - Parameter anchor:The anchor in which to attach the centerXAnchor + /// - constant: Constant size. + /// - Returns: The Constraint that was created. + public func pinCenterXGreaterThanOrEqualTo(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? { + let found: NSLayoutXAxisAnchor? = anchor ?? superview?.centerXAnchor + guard let found else { return nil } + return centerXAnchor.constraint(greaterThanOrEqualTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true } + } +} + +//-------------------------------------------------- +// MARK: - Center Y Constraints +//-------------------------------------------------- +extension LayoutConstraintable { + + @discardableResult + /// Adds a centerYAnchor. + /// - Parameter constant: Constant size. + /// - Returns: Yourself. + public func pinCenterY(_ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self { + pinCenterY(nil, constant) + } + + @discardableResult + /// Adds a centerYAnchor to a specific YAxisAnchor. + /// - Parameter anchor:The anchor in which to attach the centerYAnchor. + /// - constant: Constant size. + /// - Returns: Yourself. + public func pinCenterY(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self { + pinCenterY(anchor: anchor, constant: constant) + return self + } + + @discardableResult + /// Adds a centerYAnchor to a specific YAxisAnchor passed in using a lessThanOrEqualTo Constraint + /// - Parameter anchor:The anchor in which to attach the centerYAnchor + /// - constant: Constant size. + /// - Returns: Yourself. + public func pinCenterYLessThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self { + pinCenterYLessThanOrEqualTo(anchor: anchor, constant: constant) + return self + } + + @discardableResult + /// Adds a centerYAnchor to a specific YAxisAnchor passed in using a greaterThanOrEqualTo Constraint + /// - Parameter anchor:The anchor in which to attach the centerYAnchor + /// - constant: Constant size. + /// - Returns: Yourself. + public func pinCenterYGreaterThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self { + pinCenterXGreaterThanOrEqualTo(anchor: anchor, constant: constant) + return self + } + + @discardableResult + /// Adds a centerYAnchor for the constant passed into the method. + /// - Parameter anchor:The anchor in which to attach the centerYAnchor + /// - constant: Constant size. + /// - Returns: The Constraint that was created. + public func pinCenterY(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? { + let found: NSLayoutYAxisAnchor? = anchor ?? superview?.centerYAnchor + guard let found else { return nil } + return centerYAnchor.constraint(equalTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true } + } + + @discardableResult + /// Adds a centerYAnchor with the constant passed in using a lessThanOrEqualTo Constraint. + /// - Parameter anchor:The anchor in which to attach the centerYAnchor + /// - constant: Constant size. + /// - Returns: The Constraint that was created. + public func pinCenterYLessThanOrEqualTo(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? { + let found: NSLayoutYAxisAnchor? = anchor ?? superview?.centerYAnchor + guard let found else { return nil } + return centerYAnchor.constraint(lessThanOrEqualTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true } + } + + @discardableResult + /// Adds a centerYAnchor with the constant passed in using a greaterThanOrEqualTo Constraint. + /// - Parameter anchor:The anchor in which to attach the centerYAnchor + /// - constant: Constant size. + /// - Returns: The Constraint that was created. + public func pinCenterYGreaterThanOrEqualTo(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? { + let found: NSLayoutYAxisAnchor? = anchor ?? superview?.centerYAnchor + guard let found else { return nil } + return centerYAnchor.constraint(greaterThanOrEqualTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true } + } +} //-------------------------------------------------- // MARK: - Implementations //-------------------------------------------------- From e0d1fd844d19d2ce66d651a88776fabf150755c5 Mon Sep 17 00:00:00 2001 From: vasavk Date: Wed, 20 Mar 2024 17:20:58 +0530 Subject: [PATCH 57/76] Digital ACT-191 ONEAPP-6827 story: Updated code as per review feedback --- VDS.xcodeproj/project.pbxproj | 6 +- .../Breadcrumbs/BreadcrumbCellItem.swift | 4 +- .../Breadcrumbs/BreadcrumbItem.swift | 14 +---- .../Breadcrumbs/BreadcrumbItemModel.swift | 32 +++++++++++ VDS/Components/Breadcrumbs/Breadcrumbs.swift | 56 ++++++++++++++++--- 5 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 39250df1..5557e118 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; 18450CF12BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */; }; + 1855EC662BAABF2A002ACAC2 /* BreadcrumbItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */; }; 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */; }; 18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; }; 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A012B96E848006602CC /* Breadcrumbs.swift */; }; @@ -184,6 +185,7 @@ /* Begin PBXFileReference section */ 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = BreadcrumbsChangeLog.txt; sourceTree = ""; }; + 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItemModel.swift; sourceTree = ""; }; 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = ""; }; 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = ""; }; 18A65A012B96E848006602CC /* Breadcrumbs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Breadcrumbs.swift; sourceTree = ""; }; @@ -373,8 +375,9 @@ 18A65A002B96E7E1006602CC /* Breadcrumbs */ = { isa = PBXGroup; children = ( - 18A65A012B96E848006602CC /* Breadcrumbs.swift */, 18A65A032B96F050006602CC /* BreadcrumbItem.swift */, + 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */, + 18A65A012B96E848006602CC /* Breadcrumbs.swift */, 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */, 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */, ); @@ -1043,6 +1046,7 @@ EA0D1C452A6AD73000E5C127 /* RawRepresentable.swift in Sources */, EA985C23296E033A00F2FF2E /* TextArea.swift in Sources */, EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */, + 1855EC662BAABF2A002ACAC2 /* BreadcrumbItemModel.swift in Sources */, EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */, EA33622E2891EA3C0071C351 /* DispatchQueue+Once.swift in Sources */, EA4DB2FD28D3D0CA00103EE3 /* AnyEquatable.swift in Sources */, diff --git a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift index f8899577..888ade37 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -66,9 +66,9 @@ final class BreadcrumbCellItem: UICollectionViewCell { func update(surface: Surface, hideSlash: Bool, breadCrumbItem: BreadcrumbItem) { separator.surface = surface breadCrumbItem.surface = surface - stackView.addArrangedSubview(separator) stackView.addArrangedSubview(breadCrumbItem) - stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: separator) + stackView.addArrangedSubview(separator) + stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: breadCrumbItem) separator.textColor = textColorConfiguration.getColor(surface) separator.isHidden = hideSlash self.breadCrumbItem = breadCrumbItem diff --git a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift index 856099b8..7d936d65 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift @@ -40,18 +40,8 @@ open class BreadcrumbItem: ButtonBase { /// TextStyle used on the titleLabel. open override var textStyle: TextStyle { isSelected ? TextStyle.boldBodySmall : TextStyle.bodySmall } - /// If true, it will be rendered as selected. - open var selectable: Bool = false { - didSet { - //update selected state - if selectable{ - isSelected = true - } else { - isSelected = false - } - setNeedsUpdate() - } - } + /// Whether the Control is selected or not. + open override var isSelected: Bool { didSet { setNeedsUpdate() } } /// UIColor used on the titleLabel text. open override var textColor: UIColor { diff --git a/VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift b/VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift new file mode 100644 index 00000000..f792df3f --- /dev/null +++ b/VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift @@ -0,0 +1,32 @@ +// +// BreadcrumbItemModel.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 20/03/24. +// + +import Foundation + +extension Breadcrumbs { + public struct BreadcrumbItemModel { + + ///Text that goes in the breadcrumb item + public var text: String + + /// The Breadcrumb link to links to its respective page. + public var link: String + + /// The Breadcrumb link to links to its respective page. + public var isSelected: Bool? + + ///Click event when you click on a breadcrumb item + public var onClick: ((BreadcrumbItem) -> Void)? + + public init(text: String, link: String, isSelected: Bool? = false, onClick: ((BreadcrumbItem) -> Void)? = nil) { + self.text = text + self.isSelected = isSelected + self.onClick = onClick + self.link = link + } + } +} diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index 94d6e6fe..ca6afaa1 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -18,23 +18,32 @@ open class Breadcrumbs: View { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - /// Array of Breadcrumb Items that are shown in the group. - open var breadcrumbItems: [BreadcrumbItem] = [] { didSet { setNeedsUpdate() } } + /// Array of ``BreadcrumbItem`` views for the Breadcrumbs. + open var breadcrumbs: [BreadcrumbItem] = [] + + /// Array of ``BreadcurmbItemModel`` you are wanting to show. + open var breadcrumbModels: [BreadcrumbItemModel] = [] { didSet { updateBreadcrumbItems() } } /// Whether this object is enabled or not override open var isEnabled: Bool { didSet { - breadcrumbItems.forEach { $0.isEnabled = isEnabled } + breadcrumbs.forEach { $0.isEnabled = isEnabled } } } /// Current Surface and this is used to pass down to child objects that implement Surfacable override open var surface: Surface { didSet { - breadcrumbItems.forEach { $0.surface = surface } + breadcrumbs.forEach { $0.surface = surface } } } + /// A callback when the selected item changes. Passes parameters (crumb). + open var onBreadcrumbDidSelect: ((BreadcrumbItem) -> Void)? + + /// A callback when the Tab determine if a item should be selected. + open var onBreadcrumbShouldSelect:((BreadcrumbItem) -> Bool)? + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -65,6 +74,35 @@ open class Breadcrumbs: View { return collectionView }() + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + /// Removes all of the Breadcrumbs and creates new ones from the Breadcrumb Models property. + private func updateBreadcrumbItems() { + // Clear existing breadcrumbs + for breadcrumbItem in breadcrumbs { + breadcrumbItem.removeFromSuperview() + } + + breadcrumbs.removeAll() + // Create new breadcrumb items from the models + for model in breadcrumbModels { + let breadcrumbItem = BreadcrumbItem() + breadcrumbItem.text = model.text + breadcrumbItem.link = model.link + breadcrumbItem.isSelected = model.isSelected ?? false + breadcrumbs.append(breadcrumbItem) + breadcrumbItem.onClick = { [weak self] breadcrumb in + guard let self else { return } + if self.onBreadcrumbShouldSelect?(breadcrumb) ?? true { + model.onClick?(breadcrumb) + self.onBreadcrumbDidSelect?(breadcrumb) + } + } + } + setNeedsUpdate() + } + //------------------------------------------s-------- // MARK: - Overrides //-------------------------------------------------- @@ -88,7 +126,7 @@ open class Breadcrumbs: View { open override func reset() { super.reset() shouldUpdateView = false - breadcrumbItems.forEach { $0.reset() } + breadcrumbs.forEach { $0.reset() } shouldUpdateView = true setNeedsUpdate() } @@ -115,17 +153,17 @@ extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource { //-------------------------------------------------- // MARK: - UICollectionView Delegate & Datasource //-------------------------------------------------- - public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { breadcrumbItems.count + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { breadcrumbs.count } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BreadcrumbCellItem.identifier, for: indexPath) as? BreadcrumbCellItem else { return UICollectionViewCell() } - let hideSlash = (indexPath.row == 0) - cell.update(surface: surface, hideSlash: hideSlash, breadCrumbItem: breadcrumbItems[indexPath.row]) + let hideSlash = (indexPath.row == breadcrumbs.count - 1 || breadcrumbs.count == 1) + cell.update(surface: surface, hideSlash: hideSlash, breadCrumbItem: breadcrumbs[indexPath.row]) return cell } public func collectionView(_ collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize { - breadcrumbItems[indexPath.row].intrinsicContentSize + breadcrumbs[indexPath.row].intrinsicContentSize } } From f528b3cd62303f3f9c3a35be7d6a8b7e74763be1 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Wed, 20 Mar 2024 20:54:19 +0530 Subject: [PATCH 58/76] changed constraints for backgroundImageView and updated frames for CAlayers --- .../TileContainer/TileContainer.swift | 17 +++++++++++++++-- VDS/Components/Tilelet/Tilelet.swift | 6 ++---- VDS/Protocols/DropShadowable.swift | 5 ++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 14d38d05..fc0ffa6e 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -240,7 +240,12 @@ open class TileContainerBase: Control where Padding heightConstraint = layoutGuide.heightAnchor.constraint(equalToConstant: 0) - backgroundImageView.pin(layoutGuide) + backgroundImageView + .pinTop(layoutGuide.topAnchor) + .pinLeading(layoutGuide.leadingAnchor) + .pinTrailing(layoutGuide.trailingAnchor) + .pinBottom(layoutGuide.bottomAnchor, 0, .dragThatCanResizeScene) + backgroundImageView.isUserInteractionEnabled = false backgroundImageView.isHidden = true @@ -257,6 +262,7 @@ open class TileContainerBase: Control where Padding layer.cornerRadius = cornerRadius backgroundImageView.layer.cornerRadius = cornerRadius highlightView.layer.cornerRadius = cornerRadius + clipsToBounds = true } /// Resets to default settings. @@ -327,7 +333,14 @@ open class TileContainerBase: Control where Padding } applyBackgroundEffects() } - + + /// Used to update frames for the added CAlayers to our view + open override func layoutSubviews() { + super.layoutSubviews() + dropShadowLayers?.forEach { $0.frame = bounds } + gradientLayers?.forEach { $0.frame = bounds } + } + //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index b1367736..fca3b4c2 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -349,6 +349,7 @@ open class Tilelet: TileContainerBase { If a Tilelet has only a Title or a Subtitle, then the Title or Subtitle is truncated and appended with an ellipsis when there is not enough space to display the full text. If a Tilelet has both Title and Subtitle, then only Subtitle will be truncated. If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). + Atleast one line text based on priority Minimum bottom space below Badge is 4px; less than 4px results in truncation. */ let labelPriority = UILayoutPriority.defaultHigh.rawValue @@ -576,8 +577,5 @@ open class Tilelet: TileContainerBase { extension Label { ///To calculate label single line height - fileprivate var minimumLineHeight: CGFloat { - guard let text, !text.isEmpty, let attributes = attributedText?.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: attributedText?.string.count ?? 0))[.paragraphStyle] as? NSParagraphStyle else { return font.lineHeight } - return attributes.minimumLineHeight - } + fileprivate var minimumLineHeight: CGFloat { textStyle.lineHeight } } diff --git a/VDS/Protocols/DropShadowable.swift b/VDS/Protocols/DropShadowable.swift index b569ecab..120abb98 100644 --- a/VDS/Protocols/DropShadowable.swift +++ b/VDS/Protocols/DropShadowable.swift @@ -50,7 +50,6 @@ extension ViewProtocol where Self: UIView { 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.value(for: self)) @@ -86,4 +85,8 @@ extension ViewProtocol where Self: UIView { func removeGradientLayer() { layer.sublayers?.removeAll { $0.name == "gradientLayer" } } + + //Helper variables to get gradient & shadow layers. These are exposed to update frame of layers when view's frame is updated. + var dropShadowLayers: [CALayer]? { layer.sublayers?.filter { $0.name == "dropShadowLayer" } } + var gradientLayers: [CAGradientLayer]? { layer.sublayers?.filter { $0.name == "gradientLayer" } as? [CAGradientLayer] } } From 9203b6cab59a72c17b8324b373c105b0b4da4948 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 20 Mar 2024 15:24:35 -0500 Subject: [PATCH 59/76] added/updated isValid(range: Signed-off-by: Matt Bruce --- .../Label/Attributes/LabelAttributeModel.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/VDS/Components/Label/Attributes/LabelAttributeModel.swift b/VDS/Components/Label/Attributes/LabelAttributeModel.swift index ac43472f..01dfec08 100644 --- a/VDS/Components/Label/Attributes/LabelAttributeModel.swift +++ b/VDS/Components/Label/Attributes/LabelAttributeModel.swift @@ -31,11 +31,21 @@ extension LabelAttributeModel { } public func isValidRange(on attributedString: NSMutableAttributedString) -> Bool { - range.location + range.length <= attributedString.string.count + attributedString.isValid(range: range) + } +} + +public extension String { + func isValid(range: NSRange) -> Bool { + range.location >= 0 && range.length > 0 && range.location + range.length <= count } } public extension NSAttributedString { + func isValid(range: NSRange) -> Bool { + range.location >= 0 && range.length > 0 && range.location + range.length <= length + } + func createAttributeModels() -> [(any LabelAttributeModel)] { var attributes: [any VDS.LabelAttributeModel] = [] enumerateAttributes(in: NSMakeRange(0, length)) { attributeMap, range, stop in @@ -61,6 +71,7 @@ public extension NSAttributedString { extension NSMutableAttributedString { public func apply(attribute: any LabelAttributeModel) { + guard isValid(range: attribute.range) else { return } attribute.setAttribute(on: self) } From 1d9667c1736cd404c85e7af7f09db393a39cf729 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 20 Mar 2024 15:24:59 -0500 Subject: [PATCH 60/76] added validators for invalid range Signed-off-by: Matt Bruce --- VDS/Components/Label/Attributes/ActionLabelAttribute.swift | 2 ++ .../Label/Attributes/AttachmentLabelAttributeModel.swift | 2 ++ VDS/Components/Label/Attributes/TooltipLabelAttribute.swift | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/VDS/Components/Label/Attributes/ActionLabelAttribute.swift b/VDS/Components/Label/Attributes/ActionLabelAttribute.swift index acd28f91..6d7a1034 100644 --- a/VDS/Components/Label/Attributes/ActionLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/ActionLabelAttribute.swift @@ -56,6 +56,8 @@ public struct ActionLabelAttribute: ActionLabelAttributeModel { } public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } + if(shouldUnderline){ UnderlineLabelAttribute(location: location, length: length).setAttribute(on: attributedString) } diff --git a/VDS/Components/Label/Attributes/AttachmentLabelAttributeModel.swift b/VDS/Components/Label/Attributes/AttachmentLabelAttributeModel.swift index 5de6f4b2..c4f9bd79 100644 --- a/VDS/Components/Label/Attributes/AttachmentLabelAttributeModel.swift +++ b/VDS/Components/Label/Attributes/AttachmentLabelAttributeModel.swift @@ -14,6 +14,8 @@ public protocol AttachmentLabelAttributeModel: LabelAttributeModel { extension AttachmentLabelAttributeModel { public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } + do { let mutableString = NSMutableAttributedString() let attachment = try getAttachment() diff --git a/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift b/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift index 914a8783..bd02cf87 100644 --- a/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift @@ -24,7 +24,9 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable public var presenter: UIView? public func setAttribute(on attributedString: NSMutableAttributedString) { - //update the location + guard isValidRange(on: attributedString) else { return } + + //update the location location = attributedString.string.count - 1 //set the default color off surface for text From 0fc70e9f7542cc5baa68b3397f66eb0fe60e8e76 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 20 Mar 2024 15:29:24 -0500 Subject: [PATCH 61/76] ensure range is valid. Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 955d9604..c0edef0b 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -344,7 +344,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { for attribute in attributes { //see if the attribute is Actionable - if let actionable = attribute as? any ActionLabelAttributeModel{ + if let actionable = attribute as? any ActionLabelAttributeModel, mutableAttributedString.isValid(range: actionable.range) { //create a accessibleAction let customAccessibilityAction = customAccessibilityAction(text: mutableAttributedString.string, range: actionable.range, accessibleText: actionable.accessibleText) @@ -379,7 +379,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { guard let text = text, let attributedText else { return nil } - let actionText = accessibleText ?? NSString(string:text).substring(with: range) + let actionText = accessibleText ?? (text.isValid(range: range) ? NSString(string:text).substring(with: range) : text) // Calculate the frame of the substring let layoutManager = NSLayoutManager() From f8fec46805fc8b695700d4be7caa35a9a388cf0c Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Thu, 21 Mar 2024 16:25:51 +0530 Subject: [PATCH 62/76] iPad bug fix for imageview --- VDS/Components/TileContainer/TileContainer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index fc0ffa6e..d94d942f 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -244,7 +244,7 @@ open class TileContainerBase: Control where Padding .pinTop(layoutGuide.topAnchor) .pinLeading(layoutGuide.leadingAnchor) .pinTrailing(layoutGuide.trailingAnchor) - .pinBottom(layoutGuide.bottomAnchor, 0, .dragThatCanResizeScene) + .pinBottom(layoutGuide.bottomAnchor, 0, .defaultLow) backgroundImageView.isUserInteractionEnabled = false backgroundImageView.isHidden = true From d11ef82a4a48c0b1fdab9b58e6e216daf0162911 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 11:16:17 -0500 Subject: [PATCH 63/76] refactored out flowlayout into a class and renamed Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 6 +++- .../Breadcrumbs/BreadcrumbsFlowLayout.swift | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 VDS/Components/Breadcrumbs/BreadcrumbsFlowLayout.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 5557e118..1454090c 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -154,6 +154,7 @@ EAEEECAF2B1FC2BA00531FC2 /* ToggleViewChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEECAE2B1FC2BA00531FC2 /* ToggleViewChangeLog.txt */; }; EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9829D4850E00101452 /* Clickable.swift */; }; EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9A29DB1A6000101452 /* Changeable.swift */; }; + EAF4A6A12BAC8B94006BCC2C /* BreadcrumbsFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF4A6A02BAC8B94006BCC2C /* BreadcrumbsFlowLayout.swift */; }; EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* CheckboxItem.swift */; }; EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0992899B17200B287F5 /* CATransaction.swift */; }; EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F09F289AB7EC00B287F5 /* View.swift */; }; @@ -332,6 +333,7 @@ EAEEECAE2B1FC2BA00531FC2 /* ToggleViewChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ToggleViewChangeLog.txt; sourceTree = ""; }; EAF1FE9829D4850E00101452 /* Clickable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clickable.swift; sourceTree = ""; }; EAF1FE9A29DB1A6000101452 /* Changeable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Changeable.swift; sourceTree = ""; }; + EAF4A6A02BAC8B94006BCC2C /* BreadcrumbsFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbsFlowLayout.swift; sourceTree = ""; }; EAF7F0932899861000B287F5 /* CheckboxItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxItem.swift; sourceTree = ""; }; EAF7F0992899B17200B287F5 /* CATransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CATransaction.swift; sourceTree = ""; }; EAF7F09F289AB7EC00B287F5 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; @@ -375,9 +377,10 @@ 18A65A002B96E7E1006602CC /* Breadcrumbs */ = { isa = PBXGroup; children = ( + 18A65A012B96E848006602CC /* Breadcrumbs.swift */, + EAF4A6A02BAC8B94006BCC2C /* BreadcrumbsFlowLayout.swift */, 18A65A032B96F050006602CC /* BreadcrumbItem.swift */, 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */, - 18A65A012B96E848006602CC /* Breadcrumbs.swift */, 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */, 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */, ); @@ -1085,6 +1088,7 @@ EAD068922A560B65002E3A2D /* LoaderViewController.swift in Sources */, EABFEB642A26473700C4C106 /* NSAttributedString.swift in Sources */, EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */, + EAF4A6A12BAC8B94006BCC2C /* BreadcrumbsFlowLayout.swift in Sources */, EA0FC2C62914222900DF80B4 /* ButtonGroup.swift in Sources */, EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */, EA8E40932A82889500934ED3 /* TooltipDialog.swift in Sources */, diff --git a/VDS/Components/Breadcrumbs/BreadcrumbsFlowLayout.swift b/VDS/Components/Breadcrumbs/BreadcrumbsFlowLayout.swift new file mode 100644 index 00000000..d6daf151 --- /dev/null +++ b/VDS/Components/Breadcrumbs/BreadcrumbsFlowLayout.swift @@ -0,0 +1,31 @@ +// +// BreadcrumsFlowLayout.swift +// VDS +// +// Created by Matt Bruce on 3/21/24. +// + +import Foundation +import UIKit + +class BreadcrumbsFlowLayout: UICollectionViewFlowLayout { + + override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { + let attributes = super.layoutAttributesForElements(in: rect) + + var leftMargin = sectionInset.left + var maxY: CGFloat = -1.0 + attributes?.forEach { layoutAttribute in + if layoutAttribute.frame.origin.y >= maxY { + leftMargin = sectionInset.left + } + + layoutAttribute.frame.origin.x = leftMargin + + leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing + maxY = max(layoutAttribute.frame.maxY , maxY) + } + + return attributes + } +} From 5ce775a8856f221e76b907ceb25a6393047399ca Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 11:16:31 -0500 Subject: [PATCH 64/76] added isSelected to buttonBase Signed-off-by: Matt Bruce --- VDS/Components/Buttons/ButtonBase.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index 46f72c55..a1cb39dc 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -97,6 +97,9 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { /// Whether the Control is enabled or not. open override var isEnabled: Bool { didSet { setNeedsUpdate() } } + /// Whether the Control is selected or not. + open override var isSelected: Bool { didSet { setNeedsUpdate() } } + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- From c23e7f700b29eca2d2b24fae75d48e62dd45ac17 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 11:16:43 -0500 Subject: [PATCH 65/76] refactored model to correct properties Signed-off-by: Matt Bruce --- .../Breadcrumbs/BreadcrumbItemModel.swift | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift b/VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift index f792df3f..507212de 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift @@ -12,21 +12,20 @@ extension Breadcrumbs { ///Text that goes in the breadcrumb item public var text: String - - /// The Breadcrumb link to links to its respective page. - public var link: String + public var enabled: Bool + /// The Breadcrumb link to links to its respective page. - public var isSelected: Bool? + public var selected: Bool ///Click event when you click on a breadcrumb item public var onClick: ((BreadcrumbItem) -> Void)? - public init(text: String, link: String, isSelected: Bool? = false, onClick: ((BreadcrumbItem) -> Void)? = nil) { + public init(text: String, enabeled: Bool = true, selected: Bool = false, onClick: ((BreadcrumbItem) -> Void)? = nil) { self.text = text - self.isSelected = isSelected + self.enabled = enabeled + self.selected = selected self.onClick = onClick - self.link = link } } } From b9f17f2af4a1fe0706ee19da5f74f459a9a3b45b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 11:17:18 -0500 Subject: [PATCH 66/76] added disabled state removed link Signed-off-by: Matt Bruce --- VDS/Components/Breadcrumbs/BreadcrumbItem.swift | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift index 7d936d65..3b279551 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift @@ -34,15 +34,9 @@ open class BreadcrumbItem: ButtonBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - /// The Breadcrumb link to links to its respective page. - open var link: String? { didSet { setNeedsUpdate() } } - /// TextStyle used on the titleLabel. open override var textStyle: TextStyle { isSelected ? TextStyle.boldBodySmall : TextStyle.bodySmall } - - /// Whether the Control is selected or not. - open override var isSelected: Bool { didSet { setNeedsUpdate() } } - + /// UIColor used on the titleLabel text. open override var textColor: UIColor { textColorConfiguration.getColor(self) @@ -58,6 +52,7 @@ open class BreadcrumbItem: ButtonBase { //-------------------------------------------------- private var textColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) + $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .disabled) $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) } @@ -85,7 +80,6 @@ open class BreadcrumbItem: ButtonBase { super.reset() shouldUpdateView = false text = nil - link = nil accessibilityCustomActions = [] isAccessibilityElement = true accessibilityTraits = .button From e9bd1614ec60d77c2b02e9859fe765c122aade95 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 11:17:26 -0500 Subject: [PATCH 67/76] refactored cell Signed-off-by: Matt Bruce --- .../Breadcrumbs/BreadcrumbCellItem.swift | 26 ++----------------- 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift index 888ade37..7026c885 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -66,34 +66,12 @@ final class BreadcrumbCellItem: UICollectionViewCell { func update(surface: Surface, hideSlash: Bool, breadCrumbItem: BreadcrumbItem) { separator.surface = surface breadCrumbItem.surface = surface - stackView.addArrangedSubview(breadCrumbItem) stackView.addArrangedSubview(separator) - stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: breadCrumbItem) + stackView.addArrangedSubview(breadCrumbItem) + stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: separator) separator.textColor = textColorConfiguration.getColor(surface) separator.isHidden = hideSlash self.breadCrumbItem = breadCrumbItem layoutIfNeeded() } } - -class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout { - - override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { - let attributes = super.layoutAttributesForElements(in: rect) - - var leftMargin = sectionInset.left - var maxY: CGFloat = -1.0 - attributes?.forEach { layoutAttribute in - if layoutAttribute.frame.origin.y >= maxY { - leftMargin = sectionInset.left - } - - layoutAttribute.frame.origin.x = leftMargin - - leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing - maxY = max(layoutAttribute.frame.maxY , maxY) - } - - return attributes - } -} From 7a533944b81fce491d264a5766e21730d2fbd814 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 11:17:44 -0500 Subject: [PATCH 68/76] refactored breadcrumbs to use selfsizing cv Signed-off-by: Matt Bruce --- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 54 +++++++------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index ca6afaa1..ad2f20de 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -19,7 +19,7 @@ open class Breadcrumbs: View { // MARK: - Public Properties //-------------------------------------------------- /// Array of ``BreadcrumbItem`` views for the Breadcrumbs. - open var breadcrumbs: [BreadcrumbItem] = [] + open var breadcrumbs: [BreadcrumbItem] = [] { didSet { setNeedsUpdate() } } /// Array of ``BreadcurmbItemModel`` you are wanting to show. open var breadcrumbModels: [BreadcrumbItemModel] = [] { didSet { updateBreadcrumbItems() } } @@ -47,12 +47,7 @@ open class Breadcrumbs: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - // Sizes are from InVision design specs. - internal var containerSize: CGSize { CGSize(width: 45, height: 44) } - - internal var heightConstraint: NSLayoutConstraint? - - let layout: UICollectionViewFlowLayout = LeftAlignedCollectionViewFlowLayout().with { + let layout: UICollectionViewFlowLayout = BreadcrumbsFlowLayout().with { $0.estimatedItemSize = UICollectionViewFlowLayout.automaticSize $0.minimumInteritemSpacing = VDSLayout.Spacing.space1X.value $0.minimumLineSpacing = VDSLayout.Spacing.space1X.value @@ -61,8 +56,8 @@ open class Breadcrumbs: View { } ///Collectionview to render Breadcrumb Items - private lazy var collectionView: UICollectionView = { - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + private lazy var collectionView: SelfSizingCollectionView = { + let collectionView = SelfSizingCollectionView(frame: .zero, collectionViewLayout: layout) collectionView.isScrollEnabled = false collectionView.translatesAutoresizingMaskIntoConstraints = false collectionView.delegate = self @@ -84,37 +79,26 @@ open class Breadcrumbs: View { breadcrumbItem.removeFromSuperview() } - breadcrumbs.removeAll() // Create new breadcrumb items from the models - for model in breadcrumbModels { + breadcrumbs = breadcrumbModels.compactMap({ model in let breadcrumbItem = BreadcrumbItem() breadcrumbItem.text = model.text - breadcrumbItem.link = model.link - breadcrumbItem.isSelected = model.isSelected ?? false - breadcrumbs.append(breadcrumbItem) + breadcrumbItem.isEnabled = model.enabled + breadcrumbItem.isSelected = model.selected breadcrumbItem.onClick = { [weak self] breadcrumb in - guard let self else { return } + guard let self, breadcrumb.isEnabled else { return } if self.onBreadcrumbShouldSelect?(breadcrumb) ?? true { model.onClick?(breadcrumb) self.onBreadcrumbDidSelect?(breadcrumb) } } - } - setNeedsUpdate() + return breadcrumbItem + }) } //------------------------------------------s-------- // MARK: - Overrides //-------------------------------------------------- - /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. - open override func setup() { - super.setup() - //create the wrapping view - heightConstraint = self.heightAnchor.constraint(equalToConstant: containerSize.height) - heightConstraint?.priority = .defaultHigh - heightConstraint?.isActive = true - } - /// Executed on initialization for this View. open override func initialSetup() { super.initialSetup() @@ -135,17 +119,17 @@ open class Breadcrumbs: View { open override func updateView() { super.updateView() collectionView.reloadData() - heightConstraint?.constant = collectionView.collectionViewLayout.collectionViewContentSize.height - heightConstraint?.isActive = true } open override func layoutSubviews() { - super.layoutSubviews() + //Don't call super since we don't want an infinite loop + //super.layoutSubviews() + // Accounts for any collection size changes DispatchQueue.main.async { [weak self] in guard let self else { return } self.collectionView.collectionViewLayout.invalidateLayout() - } + } } } @@ -153,17 +137,15 @@ extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource { //-------------------------------------------------- // MARK: - UICollectionView Delegate & Datasource //-------------------------------------------------- - public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { breadcrumbs.count + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + breadcrumbs.count } - + public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BreadcrumbCellItem.identifier, for: indexPath) as? BreadcrumbCellItem else { return UICollectionViewCell() } - let hideSlash = (indexPath.row == breadcrumbs.count - 1 || breadcrumbs.count == 1) + let hideSlash = indexPath.row == 0 cell.update(surface: surface, hideSlash: hideSlash, breadCrumbItem: breadcrumbs[indexPath.row]) return cell } - public func collectionView(_ collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize { - breadcrumbs[indexPath.row].intrinsicContentSize - } } From f862c8bd1ce326aefdecf20fa7c54b6749331dff Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 11:20:11 -0500 Subject: [PATCH 69/76] fix bug, must find other solution later Signed-off-by: Matt Bruce --- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index ad2f20de..0ed7585d 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -122,14 +122,17 @@ open class Breadcrumbs: View { } open override func layoutSubviews() { - //Don't call super since we don't want an infinite loop - //super.layoutSubviews() - + //Turn off the ability to execute updateView() in the super + //since we don't want an infinite loop + shouldUpdateView = false + super.layoutSubviews() + shouldUpdateView = true + // Accounts for any collection size changes DispatchQueue.main.async { [weak self] in guard let self else { return } self.collectionView.collectionViewLayout.invalidateLayout() - } + } } } From e139a0a0b8ff32e51e3bad99f4a6ff3dd0ef9618 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 14:15:41 -0500 Subject: [PATCH 70/76] refactor cell some more Signed-off-by: Matt Bruce --- .../Breadcrumbs/BreadcrumbCellItem.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift index 7026c885..9f0ea68b 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -64,14 +64,31 @@ final class BreadcrumbCellItem: UICollectionViewCell { ///Updating the breadCrumbItem and UI based on the selected flag along with the surface func update(surface: Surface, hideSlash: Bool, breadCrumbItem: BreadcrumbItem) { + //remove views from stack + separator.removeFromSuperview() + self.breadCrumbItem?.removeFromSuperview() + + //update surface separator.surface = surface breadCrumbItem.surface = surface + + //add to stack stackView.addArrangedSubview(separator) stackView.addArrangedSubview(breadCrumbItem) stackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: separator) + + //update separator separator.textColor = textColorConfiguration.getColor(surface) separator.isHidden = hideSlash + self.breadCrumbItem = breadCrumbItem layoutIfNeeded() } + + /// Remove views from StackView. + override func prepareForReuse() { + super.prepareForReuse() + separator.removeFromSuperview() + breadCrumbItem?.removeFromSuperview() + } } From 37d43fbb62b5e90581b11b4e149951c60caf8fce Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 14:15:48 -0500 Subject: [PATCH 71/76] added comments Signed-off-by: Matt Bruce --- VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift b/VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift index 507212de..75c97c86 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbItemModel.swift @@ -13,6 +13,7 @@ extension Breadcrumbs { ///Text that goes in the breadcrumb item public var text: String + /// Whether the Item can be clicked. public var enabled: Bool /// The Breadcrumb link to links to its respective page. From bbcdbebbb0b9290abb0addfef11bfbe0ac4f8051 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 14:31:57 -0500 Subject: [PATCH 72/76] refactored some naming to keep in line with others Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 8 ++++---- VDS/Components/Pagination/Pagination.swift | 2 +- VDS/Components/Pagination/PaginationButton.swift | 1 + ...ationContainerView.swift => PaginationContainer.swift} | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) rename VDS/Components/Pagination/{PaginationContainerView.swift => PaginationContainer.swift} (96%) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 0dca7215..419b82cd 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -16,7 +16,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 */; }; - 71ACE89C2BA0451200FB6ADC /* PaginationContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89B2BA0451200FB6ADC /* PaginationContainerView.swift */; }; + 71ACE89C2BA0451200FB6ADC /* PaginationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89B2BA0451200FB6ADC /* PaginationContainer.swift */; }; 71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71B23C2C2B91FA690027F7D9 /* Pagination.swift */; }; 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */; }; 71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */; }; @@ -194,7 +194,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 = ""; }; - 71ACE89B2BA0451200FB6ADC /* PaginationContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationContainerView.swift; sourceTree = ""; }; + 71ACE89B2BA0451200FB6ADC /* PaginationContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationContainer.swift; sourceTree = ""; }; 71B23C2C2B91FA690027F7D9 /* Pagination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pagination.swift; sourceTree = ""; }; 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = PaginationChangeLog.txt; sourceTree = ""; }; 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowable.swift; sourceTree = ""; }; @@ -408,7 +408,7 @@ isa = PBXGroup; children = ( 71B23C2C2B91FA690027F7D9 /* Pagination.swift */, - 71ACE89B2BA0451200FB6ADC /* PaginationContainerView.swift */, + 71ACE89B2BA0451200FB6ADC /* PaginationContainer.swift */, 71FC86D92B96F44C00700965 /* PaginationButton.swift */, 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */, 71FC86E32B9841AC00700965 /* PaginationFlowLayout.swift */, @@ -1063,7 +1063,7 @@ EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, - 71ACE89C2BA0451200FB6ADC /* PaginationContainerView.swift in Sources */, + 71ACE89C2BA0451200FB6ADC /* PaginationContainer.swift in Sources */, EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */, diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index 72cb3224..b7adcab3 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -45,7 +45,7 @@ open class Pagination: View { return collectionView }() ///Container view to hold collectionview to render pagination indexes and to handler accessibility. - private let collectionContainerView = PaginationContainerView() + private let collectionContainerView = PaginationContainer() //-------------------------------------------------- // MARK: - Public Properties diff --git a/VDS/Components/Pagination/PaginationButton.swift b/VDS/Components/Pagination/PaginationButton.swift index 3805051d..4ed851b5 100644 --- a/VDS/Components/Pagination/PaginationButton.swift +++ b/VDS/Components/Pagination/PaginationButton.swift @@ -9,6 +9,7 @@ import UIKit import VDSColorTokens ///This is customised button for Pagination view +@objc(PaginationButton) open class PaginationButton: ButtonBase { //-------------------------------------------------- // MARK: - Private Properties diff --git a/VDS/Components/Pagination/PaginationContainerView.swift b/VDS/Components/Pagination/PaginationContainer.swift similarity index 96% rename from VDS/Components/Pagination/PaginationContainerView.swift rename to VDS/Components/Pagination/PaginationContainer.swift index b79a2fe2..2172e32a 100644 --- a/VDS/Components/Pagination/PaginationContainerView.swift +++ b/VDS/Components/Pagination/PaginationContainer.swift @@ -6,9 +6,9 @@ // import UIKit - + ///PaginationCollectionView is a container view that holds collectionview for displaying page indexes -final class PaginationContainerView: View { +final class PaginationContainer: View { //-------------------------------------------------- // MARK: - Internal Properties @@ -17,7 +17,7 @@ final class PaginationContainerView: View { var onAccessibilityIncrement: (() -> Void)? ///Notifies when accessibility decrement is happend when user swipes down var onAccessibilityDecrement: (() -> Void)? - + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- From 67e42b9730711884df56871d23e6579c1cdb48d5 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 14:32:04 -0500 Subject: [PATCH 73/76] removed dumb code Signed-off-by: Matt Bruce --- VDS/Components/Tabs/Tabs.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/VDS/Components/Tabs/Tabs.swift b/VDS/Components/Tabs/Tabs.swift index 0aa81bdf..647a74a2 100644 --- a/VDS/Components/Tabs/Tabs.swift +++ b/VDS/Components/Tabs/Tabs.swift @@ -264,7 +264,6 @@ open class Tabs: View { model.onClick?(tab.index) self.selectedIndex = tab.index self.onTabDidSelect?(tab.index) - let t = tabViews[tab.index] } } } From d1006f12664735048026fa331d3977e497617d65 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 21 Mar 2024 15:11:38 -0500 Subject: [PATCH 74/76] removed images that already existed added resizer and refactored code Signed-off-by: Matt Bruce --- VDS/Components/Icon/IconName.swift | 1 - VDS/Components/Pagination/PaginationButton.swift | 14 +++++++++----- VDS/Extensions/UIImage+Helper.swift | 13 +++++++++++++ .../pagination-arrow-left.imageset/Contents.json | 12 ------------ .../pagination-arrow-left.svg | 10 ---------- .../pagination-arrow-right.imageset/Contents.json | 12 ------------ .../pagination-arrow-right.svg | 3 --- 7 files changed, 22 insertions(+), 43 deletions(-) delete mode 100644 VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/Contents.json delete mode 100644 VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/pagination-arrow-left.svg delete mode 100644 VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/Contents.json delete mode 100644 VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/pagination-arrow-right.svg diff --git a/VDS/Components/Icon/IconName.swift b/VDS/Components/Icon/IconName.swift index c2d5076b..1234df63 100644 --- a/VDS/Components/Icon/IconName.swift +++ b/VDS/Components/Icon/IconName.swift @@ -48,7 +48,6 @@ extension Icon { internal static let paginationRightCaret = Name(name: "pagination-right-caret") internal static let verizonUp = Name(name: "verizon-up") internal static let warningBold = Name(name: "warning-bold") - public static let checkmark = Name(name: "checkmark") public static let checkmarkAlt = Name(name: "checkmark-alt") public static let close = Name(name: "close") diff --git a/VDS/Components/Pagination/PaginationButton.swift b/VDS/Components/Pagination/PaginationButton.swift index 4ed851b5..290bbef5 100644 --- a/VDS/Components/Pagination/PaginationButton.swift +++ b/VDS/Components/Pagination/PaginationButton.swift @@ -73,8 +73,9 @@ open class PaginationButton: ButtonBase { /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { text = type.title - setImage(type.image, for: .normal) - tintColor = buttonTintColorConfiguration.getColor(surface) + let color = buttonTintColorConfiguration.getColor(surface) + setImage(type.image(color), for: .normal) + tintColor = color super.updateView() } } @@ -94,13 +95,16 @@ extension PaginationButton { "Previous" } } + + private var imageSize: CGSize { Icon.Size.xsmall.dimensions } + ///Image for the configuration type - var image: UIImage? { + func image(_ color: UIColor) -> UIImage? { switch self { case .previous: - BundleManager.shared.image(for: "pagination-arrow-left")?.withRenderingMode(.alwaysTemplate) + UIImage.image(for: .paginationLeftArrow, color: color, renderingMode: .alwaysTemplate)?.resized(to: imageSize) case .next: - BundleManager.shared.image(for: "pagination-arrow-right")?.withRenderingMode(.alwaysTemplate) + UIImage.image(for: .paginationRightArrow, color: color, renderingMode: .alwaysTemplate)?.resized(to: imageSize) } } } diff --git a/VDS/Extensions/UIImage+Helper.swift b/VDS/Extensions/UIImage+Helper.swift index c8f103dc..41f25a42 100644 --- a/VDS/Extensions/UIImage+Helper.swift +++ b/VDS/Extensions/UIImage+Helper.swift @@ -23,4 +23,17 @@ extension UIImage { return image.withTintColor(color, renderingMode: renderingMode) } + + /// Resizes image to a specific Size + /// - Parameter size: Size to resize + /// - Returns: Image that is resized + public func resized(to size: CGSize) -> UIImage? { + UIGraphicsBeginImageContextWithOptions(size, false, 0.0) + + defer { UIGraphicsEndImageContext() } + draw(in: .init(origin: .zero, size: size)) + + return UIGraphicsGetImageFromCurrentImageContext() + } + } diff --git a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/Contents.json b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/Contents.json deleted file mode 100644 index 6c37b40d..00000000 --- a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "pagination-arrow-left.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/pagination-arrow-left.svg b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/pagination-arrow-left.svg deleted file mode 100644 index a8c85c97..00000000 --- a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-left.imageset/pagination-arrow-left.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/Contents.json b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/Contents.json deleted file mode 100644 index d294d555..00000000 --- a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "pagination-arrow-right.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/pagination-arrow-right.svg b/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/pagination-arrow-right.svg deleted file mode 100644 index be02bedf..00000000 --- a/VDS/SupportingFiles/Icons.xcassets/Restricted/pagination-arrow-right.imageset/pagination-arrow-right.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - From 303554462ce1982be08e9964344757d2c6565bc0 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Mon, 25 Mar 2024 19:12:47 +0530 Subject: [PATCH 75/76] accessibility fixes for Notification --- VDS/Components/Notification/Notification.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/VDS/Components/Notification/Notification.swift b/VDS/Components/Notification/Notification.swift index 126f83f0..ce2775d0 100644 --- a/VDS/Components/Notification/Notification.swift +++ b/VDS/Components/Notification/Notification.swift @@ -338,6 +338,16 @@ open class Notification: View { layer.cornerRadius = UIScreen.main.bounds.width == bounds.width ? 0 : 4.0 } + ///Updating the accessiblity values i.e elements, label, value other items for the component. + open override func updateAccessibility() { + super.updateAccessibility() + accessibilityElements = [closeButton, typeIcon, titleLabel, subTitleLabel, buttonGroup] + typeIcon.accessibilityLabel = style.rawValue + typeIcon.imageView.image?.isAccessibilityElement = false + closeButton.accessibilityTraits = [.button] + closeButton.accessibilityLabel = "Close Notification" + } + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- From 25713cd533abba89f248a3aa0e6fa2d19d0e45fe Mon Sep 17 00:00:00 2001 From: "Bandaru, Krishna Kishore" Date: Tue, 26 Mar 2024 16:54:50 +0000 Subject: [PATCH 76/76] Tilelet Enhancements --- VDS.xcodeproj/project.pbxproj | 18 +- VDS/Components/Badge/Badge.swift | 1 + .../TileContainer/TileContainer.swift | 87 +++++--- VDS/Components/Tilelet/Tilelet.swift | 209 +++++++++++++++--- .../Tilelet/TileletBadgeModel.swift | 7 +- .../Tilelet/TileletSubTitleModel.swift | 13 +- .../Tilelet/TileletTitleModel.swift | 19 +- .../Tilelet/TiletEyebrowModel.swift | 55 +++++ VDS/Components/Tilelet/TitleletChangeLog.txt | 110 +++++++++ VDS/Components/TitleLockup/TitleLockup.swift | 4 +- .../TitleLockupSubTitleModel.swift | 8 +- .../TitleLockup/TitleLockupTitleModel.swift | 8 +- VDS/Extensions/VDSLayout.swift | 3 +- VDS/Protocols/DefaultValuing.swift | 12 + VDS/Protocols/DropShadowable.swift | 5 +- VDS/Protocols/EnumSubset.swift | 2 +- VDS/Protocols/Valuing.swift | 13 ++ 17 files changed, 499 insertions(+), 75 deletions(-) create mode 100644 VDS/Components/Tilelet/TiletEyebrowModel.swift create mode 100644 VDS/Components/Tilelet/TitleletChangeLog.txt create mode 100644 VDS/Protocols/DefaultValuing.swift create mode 100644 VDS/Protocols/Valuing.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index b09a8626..7c65a8b0 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -20,10 +20,12 @@ 44604AD729CE196600E62B51 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD629CE196600E62B51 /* Line.swift */; }; 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; }; 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; + 710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 710607942B91A99500F2863F /* TitleletChangeLog.txt */; }; 7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; }; 71ACE89C2BA0451200FB6ADC /* PaginationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89B2BA0451200FB6ADC /* PaginationContainer.swift */; }; 71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71B23C2C2B91FA690027F7D9 /* Pagination.swift */; }; 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */; }; + 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */; }; 71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */; }; 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; }; 71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86D92B96F44C00700965 /* PaginationButton.swift */; }; @@ -114,6 +116,8 @@ EAA5EEF128F5C909003B3210 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */; }; EAA5EEF328F5C909003B3210 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */; }; EAA7456C2AB23E2000C1841F /* TooltipModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA7456B2AB23E2000C1841F /* TooltipModel.swift */; }; + EAACB8982B92706F006A3869 /* DefaultValuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8972B92706F006A3869 /* DefaultValuing.swift */; }; + EAACB89A2B927108006A3869 /* Valuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8992B927108006A3869 /* Valuing.swift */; }; EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */; }; EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CC28ABE76000DAE764 /* Withable.swift */; }; EAB1D2CF28ABEF2B00DAE764 /* Typography+Base.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CE28ABEF2B00DAE764 /* Typography+Base.swift */; }; @@ -204,10 +208,12 @@ 44604AD629CE196600E62B51 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = ""; }; 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; + 710607942B91A99500F2863F /* TitleletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TitleletChangeLog.txt; sourceTree = ""; }; 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = ""; }; 71ACE89B2BA0451200FB6ADC /* PaginationContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationContainer.swift; sourceTree = ""; }; 71B23C2C2B91FA690027F7D9 /* Pagination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pagination.swift; sourceTree = ""; }; 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = PaginationChangeLog.txt; sourceTree = ""; }; + 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TiletEyebrowModel.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 = ""; }; 71FC86D92B96F44C00700965 /* PaginationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationButton.swift; sourceTree = ""; }; @@ -300,6 +306,8 @@ EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; EAA7456B2AB23E2000C1841F /* TooltipModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipModel.swift; sourceTree = ""; }; + EAACB8972B92706F006A3869 /* DefaultValuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultValuing.swift; sourceTree = ""; }; + EAACB8992B927108006A3869 /* Valuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Valuing.swift; sourceTree = ""; }; EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = ""; }; EAB1D2CC28ABE76000DAE764 /* Withable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = ""; }; EAB1D2CE28ABEF2B00DAE764 /* Typography+Base.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Typography+Base.swift"; sourceTree = ""; }; @@ -612,6 +620,7 @@ EAF1FE9A29DB1A6000101452 /* Changeable.swift */, EAF1FE9829D4850E00101452 /* Clickable.swift */, EAA5EEDF28F49DB3003B3210 /* Colorable.swift */, + EAACB8972B92706F006A3869 /* DefaultValuing.swift */, EA3361A9288B25E40071C351 /* Disabling.swift */, EAF978202A99035B00C2FEA9 /* Enabling.swift */, EA5E305929510F8B0082B959 /* EnumSubset.swift */, @@ -625,6 +634,7 @@ EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */, EAB1D2CC28ABE76000DAE764 /* Withable.swift */, 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */, + EAACB8992B927108006A3869 /* Valuing.swift */, 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */, ); path = Protocols; @@ -741,9 +751,11 @@ children = ( EA5E3057295105A40082B959 /* Tilelet.swift */, EA985BE529688F6A00F2FF2E /* TileletBadgeModel.swift */, - EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */, + 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */, EA985BE72968951C00F2FF2E /* TileletTitleModel.swift */, + EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */, EA985C2C296F03FE00F2FF2E /* TileletIconModels.swift */, + 710607942B91A99500F2863F /* TitleletChangeLog.txt */, ); path = Tilelet; sourceTree = ""; @@ -1018,6 +1030,7 @@ EAEEECA72B1F952000531FC2 /* TabsChangeLog.txt in Resources */, 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */, EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */, + 710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */, EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */, EAEEEC982B1F8DD100531FC2 /* LineChangeLog.txt in Resources */, EAEEECA22B1F92AD00531FC2 /* LabelChangeLog.txt in Resources */, @@ -1073,6 +1086,7 @@ EAC9258C2911C9DE00091998 /* InputField.swift in Sources */, EA3362402892EF6C0071C351 /* Label.swift in Sources */, EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */, + EAACB8982B92706F006A3869 /* DefaultValuing.swift in Sources */, EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */, 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */, EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */, @@ -1091,6 +1105,7 @@ EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */, EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */, + EAACB89A2B927108006A3869 /* Valuing.swift in Sources */, EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, @@ -1098,6 +1113,7 @@ EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */, + 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */, EAF7F11728A1475A00B287F5 /* RadioButtonItem.swift in Sources */, EA985BEE2968A92400F2FF2E /* TitleLockupSubTitleModel.swift in Sources */, EA985BF22968B5BB00F2FF2E /* TitleLockupTextStyle.swift in Sources */, diff --git a/VDS/Components/Badge/Badge.swift b/VDS/Components/Badge/Badge.swift index f6bb36d5..bbe1233d 100644 --- a/VDS/Components/Badge/Badge.swift +++ b/VDS/Components/Badge/Badge.swift @@ -147,6 +147,7 @@ open class Badge: View { label.widthGreaterThanEqualTo(constant: minWidth) maxWidthConstraint = label.widthLessThanEqualTo(constant: 0).with { $0.isActive = false } + clipsToBounds = true } /// Resets to default settings. diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 8dc2d9dd..d94d942f 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -11,7 +11,39 @@ import VDSFormControlsTokens import UIKit @objc(VDSTileContainer) -open class TileContainer: Control { +open class TileContainer: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: DefaultValuing { + case padding2X + case padding4X + case padding6X + case padding8X + case padding12X + case custom(CGFloat) + + public static var defaultValue: Self { .padding4X } + + public var value: CGFloat { + switch self { + case .padding2X: + return VDSLayout.Spacing.space2X.value + case .padding4X: + return VDSLayout.Spacing.space4X.value + case .padding6X: + return VDSLayout.Spacing.space6X.value + case .padding8X: + return VDSLayout.Spacing.space8X.value + case .padding12X: + return VDSLayout.Spacing.space12X.value + case .custom(let padding): + return padding + } + } + } +} + +open class TileContainerBase: Control where PaddingType.ValueType == CGFloat { //-------------------------------------------------- // MARK: - Initializers @@ -53,33 +85,6 @@ open class TileContainer: Control { case gradient(String, String) case none } - - /// Enum used to describe the padding choices used for this component. - public enum Padding { - case padding2X - case padding4X - case padding6X - case padding8X - case padding12X - case custom(CGFloat) - - public var value: CGFloat { - switch self { - case .padding2X: - return VDSLayout.Spacing.space2X.value - case .padding4X: - return VDSLayout.Spacing.space4X.value - case .padding6X: - return VDSLayout.Spacing.space6X.value - case .padding8X: - return VDSLayout.Spacing.space8X.value - case .padding12X: - return VDSLayout.Spacing.space12X.value - case .custom(let padding): - return padding - } - } - } /// Enum used to describe the aspect ratios used for this component. public enum AspectRatio: String, CaseIterable { @@ -130,7 +135,7 @@ open class TileContainer: Control { open var backgroundEffect: BackgroundEffect = .none { didSet { setNeedsUpdate() } } /// Sets the inside padding for the component - open var padding: Padding = .padding4X { didSet { setNeedsUpdate() } } + open var padding: PaddingType = PaddingType.defaultValue { didSet { setNeedsUpdate() } } /// Applies a background color if backgroundImage prop fails or has trouble loading. open var imageFallbackColor: Surface = .light { didSet { setNeedsUpdate() } } @@ -235,7 +240,12 @@ open class TileContainer: Control { heightConstraint = layoutGuide.heightAnchor.constraint(equalToConstant: 0) - backgroundImageView.pin(layoutGuide) + backgroundImageView + .pinTop(layoutGuide.topAnchor) + .pinLeading(layoutGuide.leadingAnchor) + .pinTrailing(layoutGuide.trailingAnchor) + .pinBottom(layoutGuide.bottomAnchor, 0, .defaultLow) + backgroundImageView.isUserInteractionEnabled = false backgroundImageView.isHidden = true @@ -252,6 +262,7 @@ open class TileContainer: Control { layer.cornerRadius = cornerRadius backgroundImageView.layer.cornerRadius = cornerRadius highlightView.layer.cornerRadius = cornerRadius + clipsToBounds = true } /// Resets to default settings. @@ -259,7 +270,6 @@ open class TileContainer: Control { super.reset() shouldUpdateView = false color = .white - padding = .padding4X aspectRatio = .ratio1x1 imageFallbackColor = .light width = nil @@ -323,7 +333,14 @@ open class TileContainer: Control { } applyBackgroundEffects() } - + + /// Used to update frames for the added CAlayers to our view + open override func layoutSubviews() { + super.layoutSubviews() + dropShadowLayers?.forEach { $0.frame = bounds } + gradientLayers?.forEach { $0.frame = bounds } + } + //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- @@ -401,11 +418,11 @@ open class TileContainer: Control { } -extension TileContainer { +extension TileContainerBase { final class BackgroundColorConfiguration: ObjectColorable { - typealias ObjectType = TileContainer + typealias ObjectType = TileContainerBase let primaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark) let secondaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundSecondaryLight, VDSColor.backgroundSecondaryDark) @@ -415,7 +432,7 @@ extension TileContainer { required init() { } - func getColor(_ object: TileContainer) -> UIColor { + func getColor(_ object: ObjectType) -> UIColor { switch object.color { case .primary: primaryColorConfig.getColor(object.surface) diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 657a735b..fca3b4c2 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -16,7 +16,38 @@ import Combine /// while it can include an arrow CTA, it does not require one in order to /// function. @objc(VDSTilelet) -open class Tilelet: TileContainer { +open class Tilelet: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: String, DefaultValuing, CaseIterable { + case small + case large + + public static var defaultValue: Self { .large } + + public var value: CGFloat { + switch self { + case .small: + return UIDevice.isIPad ? VDSLayout.Spacing.space3X.value : VDSLayout.Spacing.space4X.value + case .large: + return UIDevice.isIPad ? VDSLayout.Spacing.space4X.value : VDSLayout.Spacing.space6X.value + } + } + + fileprivate var titleLockupBottomSpacing: CGFloat { + switch self.value { + case VDSLayout.Spacing.space3X.value: + return VDSLayout.Spacing.space4X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space6X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space8X.value + default: + return VDSLayout.Spacing.space4X.value + } + } + } + //-------------------------------------------------- // MARK: - Initializers @@ -39,6 +70,7 @@ open class Tilelet: TileContainer { /// Enum to represent the Vertical Layout of the Text. public enum TextPosition: String, CaseIterable { case top + case middle case bottom } @@ -72,7 +104,7 @@ open class Tilelet: TileContainer { //-------------------------------------------------- // MARK: - Public Properties - //-------------------------------------------------- + //-------------------------------------------------- public override var onClickSubscriber: AnyCancellable? { didSet { isAccessibilityElement = onClickSubscriber != nil @@ -82,6 +114,27 @@ open class Tilelet: TileContainer { /// Title lockup positioned in the contentView. open var titleLockup = TitleLockup().with { $0.standardStyleConfiguration = .init(styleConfigurations: [ + .init(deviceType: .iPhone, + titleStandardStyles: [.bodySmall], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodySmall], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), + .init(deviceType: .iPhone, + titleStandardStyles: [.bodyMedium], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyMedium], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), + .init(deviceType: .iPhone, + titleStandardStyles: [.bodyLarge], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyLarge], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), .init(deviceType: .iPhone, titleStandardStyles: [.titleSmall], spacingConfigurations: [ @@ -103,6 +156,27 @@ open class Tilelet: TileContainer { topSpacing: VDSLayout.Spacing.space3X.value, bottomSpacing: VDSLayout.Spacing.space3X.value) ]), + .init(deviceType: .iPad, + titleStandardStyles: [.bodySmall], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodySmall], + topSpacing: VDSLayout.Spacing.space2X.value, + bottomSpacing: VDSLayout.Spacing.space2X.value) + ]), + .init(deviceType: .iPad, + titleStandardStyles: [.bodyMedium], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyMedium], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), + .init(deviceType: .iPad, + titleStandardStyles: [.bodyLarge], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyLarge], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), .init(deviceType: .iPad, titleStandardStyles: [.titleSmall, .titleMedium], spacingConfigurations: [ @@ -176,6 +250,9 @@ open class Tilelet: TileContainer { /// If set, this is used to render the subTitleLabel of the TitleLockup. open var subTitleModel: SubTitleModel? { didSet { setNeedsUpdate() } } + /// If set, this is used to render the eyebrowLabel of the TitleLockup. + open var eyebrowModel: EyebrowModel? { didSet { setNeedsUpdate() } } + //only 1 Icon can be active private var _descriptiveIconModel: DescriptiveIcon? @@ -206,6 +283,17 @@ open class Tilelet: TileContainer { //-------------------------------------------------- internal var titleLockupWidthConstraint: NSLayoutConstraint? internal var titleLockupTrailingConstraint: NSLayoutConstraint? + internal var titleLockupTopConstraint: NSLayoutConstraint? + internal var titleLockupBottomConstraint: NSLayoutConstraint? + internal var titleLockupTopGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupBottomGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupCenterYConstraint: NSLayoutConstraint? + internal var titleLockupTitleLabelBottomConstraint: NSLayoutConstraint? + //Truncation constraints + internal var badgeLabelHeightGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupEyebrowLabelHeightGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupSubTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Overrides @@ -230,12 +318,17 @@ open class Tilelet: TileContainer { titleLockupContainerView.addSubview(titleLockup) titleLockup - .pinTop() .pinLeading() - .pinBottom() + titleLockupTopConstraint = titleLockup.topAnchor.constraint(equalTo: titleLockupContainerView.topAnchor) + titleLockupTopConstraint?.activate() + titleLockupBottomConstraint = titleLockupContainerView.bottomAnchor.constraint(equalTo: titleLockup.bottomAnchor) + titleLockupBottomConstraint?.activate() titleLockupTrailingConstraint = titleLockup.trailingAnchor.constraint(equalTo: titleLockupContainerView.trailingAnchor) - titleLockupTrailingConstraint?.isActive = true - + titleLockupTrailingConstraint?.activate() + titleLockupBottomGreaterThanConstraint = titleLockupContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLockup.bottomAnchor) + titleLockupTopGreaterThanConstraint = titleLockup.topAnchor.constraint(greaterThanOrEqualTo: titleLockupContainerView.topAnchor) + titleLockupCenterYConstraint = titleLockup.centerYAnchor.constraint(equalTo: titleLockupContainerView.centerYAnchor) + iconContainerView.addSubview(descriptiveIcon) iconContainerView.addSubview(directionalIcon) @@ -249,6 +342,47 @@ open class Tilelet: TileContainer { .pinTop() .pinBottom() + badge.bottomAnchor.constraint(equalTo: badge.label.bottomAnchor, constant: 2).activate() + + /** + Truncation: + If a Tilelet has only a Title or a Subtitle, then the Title or Subtitle is truncated and appended with an ellipsis when there is not enough space to display the full text. + If a Tilelet has both Title and Subtitle, then only Subtitle will be truncated. + If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). + Atleast one line text based on priority + Minimum bottom space below Badge is 4px; less than 4px results in truncation. + */ + let labelPriority = UILayoutPriority.defaultHigh.rawValue + titleLockup.titleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority), for: .vertical) + badge.label.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-1), for: .vertical) + titleLockup.subTitleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-2), for: .vertical) + titleLockup.eyebrowLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-3), for: .vertical) + + titleLockup.titleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority), for: .vertical) + badge.label.setContentHuggingPriority(UILayoutPriority(labelPriority-1), for: .vertical) + titleLockup.subTitleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-2), for: .vertical) + titleLockup.eyebrowLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-3), for: .vertical) + + /** + Added these constraints for: + At fixed width & height if all the labels(Badge, Eyebrow, Title, Subtitle) are having more number of lines then we should display atleast one line of content per label instead of pushing labels out of bounds. + So adding minimum single line height constraint + */ + badgeLabelHeightGreaterThanConstraint = badge.label.heightGreaterThanEqualTo(constant: badge.label.minimumLineHeight) + badgeLabelHeightGreaterThanConstraint?.priority = .defaultHigh + badgeLabelHeightGreaterThanConstraint?.activate() + + titleLockupEyebrowLabelHeightGreaterThanConstraint = titleLockup.eyebrowLabel.heightGreaterThanEqualTo(constant: titleLockup.eyebrowLabel.minimumLineHeight) + titleLockupEyebrowLabelHeightGreaterThanConstraint?.priority = .defaultHigh + titleLockupEyebrowLabelHeightGreaterThanConstraint?.activate() + + titleLockupTitleLabelHeightGreaterThanConstraint = titleLockup.titleLabel.heightGreaterThanEqualTo(constant: titleLockup.titleLabel.minimumLineHeight) + titleLockupTitleLabelHeightGreaterThanConstraint?.priority = .defaultHigh + titleLockupTitleLabelHeightGreaterThanConstraint?.activate() + + titleLockupSubTitleLabelHeightGreaterThanConstraint = titleLockup.subTitleLabel.heightGreaterThanEqualTo(constant: titleLockup.subTitleLabel.minimumLineHeight) + titleLockupSubTitleLabelHeightGreaterThanConstraint?.priority = .defaultHigh + titleLockupSubTitleLabelHeightGreaterThanConstraint?.activate() } /// Resets to default settings. @@ -261,7 +395,7 @@ open class Tilelet: TileContainer { titleModel = nil subTitleModel = nil descriptiveIconModel = nil - directionalIconModel = nil + directionalIconModel = nil shouldUpdateView = true setNeedsUpdate() } @@ -273,7 +407,11 @@ open class Tilelet: TileContainer { updateBadge() updateTitleLockup() updateIcons() - + ///Content-driven height Tilelets - Minimum height is configurable. + ///if width != nil && (aspectRatio != .none || height != nil) then tilelet is not self growing, so we can apply text position alignments. + if width != nil && (aspectRatio != .none || height != nil) { + updateTextPositionAlignment() + } layoutIfNeeded() } @@ -294,6 +432,7 @@ open class Tilelet: TileContainer { badge.numberOfLines = badgeModel.numberOfLines badge.surface = badgeModel.surface badge.maxWidth = badgeModel.maxWidth + badgeLabelHeightGreaterThanConstraint?.constant = badge.label.minimumLineHeight if badgeContainerView.superview == nil { stackView.insertArrangedSubview(badgeContainerView, at: 0) setNeedsLayout() @@ -306,7 +445,11 @@ open class Tilelet: TileContainer { private func updateTitleLockup() { var showTitleLockup = false - + + if let eyebrowModel, !eyebrowModel.text.isEmpty { + showTitleLockup = true + } + if let titleModel, !titleModel.text.isEmpty { showTitleLockup = true } @@ -350,6 +493,7 @@ open class Tilelet: TileContainer { } //set models + titleLockup.eyebrowModel = eyebrowModel?.toTitleLockupEyebrowModel() titleLockup.titleModel = titleModel?.toTitleLockupTitleModel() titleLockup.subTitleModel = subTitleModel?.toTitleLockupSubTitleModel() @@ -360,6 +504,9 @@ open class Tilelet: TileContainer { } else { removeFromSuperview(titleLockupContainerView) } + titleLockupEyebrowLabelHeightGreaterThanConstraint?.constant = titleLockup.eyebrowLabel.minimumLineHeight + titleLockupTitleLabelHeightGreaterThanConstraint?.constant = titleLockup.titleLabel.minimumLineHeight + titleLockupSubTitleLabelHeightGreaterThanConstraint?.constant = titleLockup.subTitleLabel.minimumLineHeight } private func updateIcons() { @@ -392,7 +539,7 @@ open class Tilelet: TileContainer { view = titleLockupContainerView } if let view { - stackView.setCustomSpacing(padding.tiletSpacing, after: view) + stackView.setCustomSpacing(padding.titleLockupBottomSpacing, after: view) } if iconContainerView.superview == nil { stackView.addArrangedSubview(iconContainerView) @@ -402,21 +549,33 @@ open class Tilelet: TileContainer { removeFromSuperview(iconContainerView) } } -} - -extension TileContainer.Padding { - fileprivate var tiletSpacing: CGFloat { - switch self { - case .padding2X: - return 16 - case .padding4X: - return 24 - case .padding6X: - return 32 - case .padding8X: - return 48 - default: - return 16 + + private func updateTextPositionAlignment() { + switch textPostion { + case .top: + titleLockupTopConstraint?.activate() + titleLockupTopGreaterThanConstraint?.deactivate() + titleLockupBottomConstraint?.deactivate() + titleLockupBottomGreaterThanConstraint?.activate() + titleLockupCenterYConstraint?.deactivate() + case .middle: + titleLockupTopConstraint?.deactivate() + titleLockupTopGreaterThanConstraint?.activate() + titleLockupBottomConstraint?.deactivate() + titleLockupBottomGreaterThanConstraint?.activate() + titleLockupCenterYConstraint?.activate() + case .bottom: + titleLockupTopConstraint?.deactivate() + titleLockupTopGreaterThanConstraint?.activate() + titleLockupBottomConstraint?.activate() + titleLockupBottomGreaterThanConstraint?.deactivate() + titleLockupCenterYConstraint?.deactivate() } } } + +extension Label { + + ///To calculate label single line height + fileprivate var minimumLineHeight: CGFloat { textStyle.lineHeight } +} diff --git a/VDS/Components/Tilelet/TileletBadgeModel.swift b/VDS/Components/Tilelet/TileletBadgeModel.swift index f58c134c..9dbd03fe 100644 --- a/VDS/Components/Tilelet/TileletBadgeModel.swift +++ b/VDS/Components/Tilelet/TileletBadgeModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension Tilelet { @@ -26,12 +27,16 @@ extension Tilelet { /// Max width that will be used for the badge. public var maxWidth: CGFloat? - public init(text: String, fillColor: Badge.FillColor = .red, surface: Surface = .light, numberOfLines: Int = 0, maxWidth: CGFloat? = nil) { + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + + public init(text: String, fillColor: Badge.FillColor = .red, surface: Surface = .light, numberOfLines: Int = 0, maxWidth: CGFloat? = nil, lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.fillColor = fillColor self.surface = surface self.numberOfLines = numberOfLines self.maxWidth = maxWidth + self.lineBreakMode = lineBreakMode } } } diff --git a/VDS/Components/Tilelet/TileletSubTitleModel.swift b/VDS/Components/Tilelet/TileletSubTitleModel.swift index ae28d958..fb68ff53 100644 --- a/VDS/Components/Tilelet/TileletSubTitleModel.swift +++ b/VDS/Components/Tilelet/TileletSubTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension Tilelet { /// Model that represents the options available for the sub title label. @@ -18,7 +19,8 @@ extension Tilelet { case bodyLarge case bodyMedium case bodySmall - + case titleSmall + case titleMedium public var defaultValue: TitleLockup.OtherStandardStyle { .bodySmall } } //-------------------------------------------------- @@ -36,17 +38,22 @@ extension Tilelet { /// Text color that will be used for the subTitle label. public var textColor: Use = .primary + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- public init(text: String, otherStandardStyle: OtherStandardStyle = .bodySmall, textColor: Use = .primary, - textAttributes: [any LabelAttributeModel]? = nil) { + textAttributes: [any LabelAttributeModel]? = nil, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.otherStandardStyle = otherStandardStyle self.textAttributes = textAttributes self.textColor = textColor + self.lineBreakMode = lineBreakMode } //-------------------------------------------------- @@ -57,7 +64,7 @@ extension Tilelet { TitleLockup.SubTitleModel(text: text, otherStandardStyle: otherStandardStyle.value, textColor: textColor, - textAttributes: textAttributes) + textAttributes: textAttributes, lineBreakMode: lineBreakMode) } } } diff --git a/VDS/Components/Tilelet/TileletTitleModel.swift b/VDS/Components/Tilelet/TileletTitleModel.swift index 728de07b..fa8c603d 100644 --- a/VDS/Components/Tilelet/TileletTitleModel.swift +++ b/VDS/Components/Tilelet/TileletTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension Tilelet { /// Model that represents the options available for the title label. @@ -19,7 +20,10 @@ extension Tilelet { case titleLarge case titleMedium case titleSmall - + case bodyLarge + case bodyMedium + case bodySmall + public var defaultValue: TitleLockup.TitleStandardStyle { .titleSmall } } //-------------------------------------------------- @@ -28,21 +32,30 @@ extension Tilelet { /// Text that will be used for the title label. public var text: String = "" + /// Used in combination with standardStyle to set the textStyle that will be used for the title label. + public var isBold: Bool = false /// Text attributes that will be used for the title label. public var textAttributes: [any LabelAttributeModel]? /// Text style that will be used for the title label. public var standardStyle: StandardStyle = .titleSmall + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- public init(text: String, textAttributes: [any LabelAttributeModel]? = nil, - standardStyle: StandardStyle = .titleSmall) { + isBold: Bool = true, + standardStyle: StandardStyle = .titleSmall, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.textAttributes = textAttributes self.standardStyle = standardStyle + self.isBold = isBold + self.lineBreakMode = lineBreakMode } //-------------------------------------------------- @@ -52,7 +65,7 @@ extension Tilelet { public func toTitleLockupTitleModel() -> TitleLockup.TitleModel { TitleLockup.TitleModel(text: text, textAttributes: textAttributes, - standardStyle: standardStyle.value) + isBold: isBold, standardStyle: standardStyle.value, lineBreakMode: lineBreakMode) } } } diff --git a/VDS/Components/Tilelet/TiletEyebrowModel.swift b/VDS/Components/Tilelet/TiletEyebrowModel.swift new file mode 100644 index 00000000..e7c008b4 --- /dev/null +++ b/VDS/Components/Tilelet/TiletEyebrowModel.swift @@ -0,0 +1,55 @@ +// +// TiletEyebrowModel.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 13/03/24. +// + +import Foundation +import UIKit + +extension Tilelet { + + /// Model that represents the options available for the eyebrow label. + public struct EyebrowModel { + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + /// Text that will be used for the eyebrow label. + public var text: String = "" + + /// Used in combination with standardStyle to set the textStyle that will be used for the eyebrow label. + public var isBold: Bool = false + /// Text attributes that will be used for the eyebrow label. + public var textAttributes: [any LabelAttributeModel]? + + /// Text style that will be used for the eyebrow label. If subtitle standard style + public var standardStyle: Tilelet.SubTitleModel.OtherStandardStyle = .titleSmall + + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public init(text: String, + textAttributes: [any LabelAttributeModel]? = nil, + isBold: Bool = true, + standardStyle: Tilelet.SubTitleModel.OtherStandardStyle = .bodySmall, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { + self.text = text + self.textAttributes = textAttributes + self.standardStyle = standardStyle + self.isBold = isBold + self.lineBreakMode = lineBreakMode + } + + //-------------------------------------------------- + // MARK: - Public Methods + //-------------------------------------------------- + /// Converts this type of model to a TitleLockup.TitleModel. + public func toTitleLockupEyebrowModel() -> TitleLockup.EyebrowModel { + TitleLockup.EyebrowModel(text: text, isBold: isBold, standardStyle: standardStyle.value, textAttributes: textAttributes) + } + } +} diff --git a/VDS/Components/Tilelet/TitleletChangeLog.txt b/VDS/Components/Tilelet/TitleletChangeLog.txt new file mode 100644 index 00000000..bbbc8edf --- /dev/null +++ b/VDS/Components/Tilelet/TitleletChangeLog.txt @@ -0,0 +1,110 @@ +MM/DD/YYYY +---------------- + +08/31/2022 +---------------- +- Updates to Typography and default Title Lockup configurations: +- Default +- Regular title updated to bold weight +- Subtitle updated to primary color +- Inverted +- Regular title updated to bold weight +- Subtitle updated to primary color + +07/15/2022 +---------------- +- Updates to Typography and default Title Lockup configurations: +- Default +- Bold Title updated to Regular weight +- Subtitle updated to secondary color +- Inverted +- Bold Title updated to Regular weight +- Subtitle updated to secondary color + +04/27/2022 +---------------- +- Updates to Title Lockup configurations. + +04/14/2022 +---------------- +- Updates to Tilelet Group spec based on 4/14 handoff feedback. + +04/04/2022 +---------------- +- Visuals added to Tilelet Group spec. + +04/01/2022 +---------------- +- Updates based on 3/31 handoff feedback. + +03/30/2022 +---------------- +- Tilelet added to Test App. + +03/29/2022 +---------------- +- Interactive states updated. + +03/22/2022 +---------------- +- Layout and spacing page added. + +03/21/2022 +---------------- +- Updated anatomy, configuration, and element pages. + +02/21/2022 +---------------- +- Title Lockup’s width properties added. + +09/14/2022 +---------------- +- States section updated to reflect states inherited from Tile Container +- Dev note added to Layouts. + +11/30/2022 +---------------- +- Added "(web only)" to any instance of "keyboard focus" + +12/13/2022 +---------------- +- Replaced focus border pixel and style & spacing values with tokens. + +06/19/2023 +---------------- +- Updated anatomy element names to be local to Tilelet and indicated the core component that powers them under the hood. + +09/21/2023 +---------------- +- Updated Anatomy to include Title Lockup with Eyebrow and Tooltip +- Updated Configurations to include Title Lockup section +- Updated Layout and spacing to include Alignment (Left, Center) and textPosition (Middle)
 +- Updated Elements to include Eyebrow and uniform 1:1 pairings + +10/09/2023 +---------------- +- Updated Configurations VDS Title Lockup and VDS Tile Container sections. + +10/13/2023 +---------------- +- SPEC format changes to Anatomy to include nested component configurations +- Removed Configurations page + +11/20/2023 +---------------- +- Updated visuals to reflect new corner radius value - 12px +- Updated focus border corner radius to 14px +- View changes + +12/14/2023 +---------------- +- Added secondary, primary backgroundColor options +- Relabeled configurations section headings to use property names +- Relabeled surface sections to use “surface:” labeling convention +- Deprecated gray backgroundColor +- View changes + +02/08/2024 +---------------- +- Added external-icon option to Descriptive Icon in Configurations +- View changes diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index 3802b529..761ac9c5 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -353,7 +353,7 @@ open class TitleLockup: View { titleLabel.attributes = titleModel.textAttributes titleLabel.numberOfLines = titleModel.numberOfLines titleLabel.surface = surface - + titleLabel.lineBreakMode = titleModel.lineBreakMode addSubview(titleLabel) titleLabel .pinTop(previousView?.bottomAnchor ?? self.topAnchor, eyebrowLabel == previousView ? topSpacing : 0) @@ -372,7 +372,7 @@ open class TitleLockup: View { subTitleLabel.attributes = subTitleModel.textAttributes subTitleLabel.numberOfLines = subTitleModel.numberOfLines subTitleLabel.surface = surface - + subTitleLabel.lineBreakMode = subTitleModel.lineBreakMode addSubview(subTitleLabel) subTitleLabel .pinTop(previousView?.bottomAnchor ?? self.topAnchor, (eyebrowLabel == previousView || titleLabel == previousView) ? bottomSpacing : 0) diff --git a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift index 4ac0922c..f558ffb4 100644 --- a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension TitleLockup { /// Model that represents the options available for the sub title label. @@ -25,16 +26,21 @@ extension TitleLockup { /// Number of lines used in the subtitle label. public var numberOfLines: Int + /// LineBreakMode used in subtitle label. + public var lineBreakMode: NSLineBreakMode + public init(text: String, otherStandardStyle: OtherStandardStyle = .bodyLarge, textColor: Use = .primary, textAttributes: [any LabelAttributeModel]? = nil, - numberOfLines: Int = 0) { + numberOfLines: Int = 0, + lineBreakMode: NSLineBreakMode = .byWordWrapping) { self.text = text self.otherStandardStyle = otherStandardStyle self.textColor = textColor self.textAttributes = textAttributes self.numberOfLines = numberOfLines + self.lineBreakMode = lineBreakMode } /// TextStyle used to render the text. diff --git a/VDS/Components/TitleLockup/TitleLockupTitleModel.swift b/VDS/Components/TitleLockup/TitleLockupTitleModel.swift index 2b957276..7541151f 100644 --- a/VDS/Components/TitleLockup/TitleLockupTitleModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension TitleLockup { /// Model that represents the options available for the sub title label. @@ -25,16 +26,21 @@ extension TitleLockup { /// Number of lines that will be used for the title label. public var numberOfLines: Int + /// LineBreakMode used in subtitle label. + public var lineBreakMode: NSLineBreakMode + public init(text: String, textAttributes: [any LabelAttributeModel]? = nil, isBold: Bool = true, standardStyle: TitleStandardStyle = .featureXSmall, - numberOfLines: Int = 0) { + numberOfLines: Int = 0, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.isBold = isBold self.textAttributes = textAttributes self.standardStyle = standardStyle self.numberOfLines = numberOfLines + self.lineBreakMode = lineBreakMode } /// TextStyle used to render the text. diff --git a/VDS/Extensions/VDSLayout.swift b/VDS/Extensions/VDSLayout.swift index f30a0635..26ada9c4 100644 --- a/VDS/Extensions/VDSLayout.swift +++ b/VDS/Extensions/VDSLayout.swift @@ -10,7 +10,8 @@ import Foundation /// Represents constants used that deal with layout. public struct VDSLayout { /// Enum used to describe the spacing constants. - public enum Spacing: String, CaseIterable { + public enum Spacing: String, CaseIterable, Valuing { + case space1X case space2X case space3X diff --git a/VDS/Protocols/DefaultValuing.swift b/VDS/Protocols/DefaultValuing.swift new file mode 100644 index 00000000..ffeaf0eb --- /dev/null +++ b/VDS/Protocols/DefaultValuing.swift @@ -0,0 +1,12 @@ +// +// EnumValuing.swift +// VDS +// +// Created by Matt Bruce on 3/1/24. +// + +import Foundation + +public protocol DefaultValuing: Valuing { + static var defaultValue: Self { get } +} diff --git a/VDS/Protocols/DropShadowable.swift b/VDS/Protocols/DropShadowable.swift index b569ecab..120abb98 100644 --- a/VDS/Protocols/DropShadowable.swift +++ b/VDS/Protocols/DropShadowable.swift @@ -50,7 +50,6 @@ extension ViewProtocol where Self: UIView { 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.value(for: self)) @@ -86,4 +85,8 @@ extension ViewProtocol where Self: UIView { func removeGradientLayer() { layer.sublayers?.removeAll { $0.name == "gradientLayer" } } + + //Helper variables to get gradient & shadow layers. These are exposed to update frame of layers when view's frame is updated. + var dropShadowLayers: [CALayer]? { layer.sublayers?.filter { $0.name == "dropShadowLayer" } } + var gradientLayers: [CAGradientLayer]? { layer.sublayers?.filter { $0.name == "gradientLayer" } as? [CAGradientLayer] } } diff --git a/VDS/Protocols/EnumSubset.swift b/VDS/Protocols/EnumSubset.swift index a690fca3..831ce1fb 100644 --- a/VDS/Protocols/EnumSubset.swift +++ b/VDS/Protocols/EnumSubset.swift @@ -7,7 +7,7 @@ import Foundation -public protocol EnumSubset: RawRepresentable, CaseIterable { +public protocol EnumSubset: RawRepresentable, CaseIterable, Valuing { associatedtype T:RawRepresentable var defaultValue: T { get } } diff --git a/VDS/Protocols/Valuing.swift b/VDS/Protocols/Valuing.swift new file mode 100644 index 00000000..61cea9e8 --- /dev/null +++ b/VDS/Protocols/Valuing.swift @@ -0,0 +1,13 @@ +// +// Valuing.swift +// VDS +// +// Created by Matt Bruce on 3/1/24. +// + +import Foundation + +public protocol Valuing { + associatedtype ValueType + var value: ValueType { get } +}