From 0673a876d1e9f714e0ed4b899f2ff7de986dce86 Mon Sep 17 00:00:00 2001 From: vasavk Date: Wed, 29 May 2024 16:06:51 +0530 Subject: [PATCH 001/113] Digital ACT-191 ONEAPP-7013 story: added new page --- VDS.xcodeproj/project.pbxproj | 16 +++++++++ VDS/Components/Carousel/Carousel.swift | 33 +++++++++++++++++++ VDS/Components/Carousel/CarouselChangeLog.txt | 15 +++++++++ 3 files changed, 64 insertions(+) create mode 100644 VDS/Components/Carousel/Carousel.swift create mode 100644 VDS/Components/Carousel/CarouselChangeLog.txt diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 8f6878a0..f808e999 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 18A3F12A2BD9298900498E4A /* Calendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A3F1292BD9298900498E4A /* Calendar.swift */; }; 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A012B96E848006602CC /* Breadcrumbs.swift */; }; 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A032B96F050006602CC /* BreadcrumbItem.swift */; }; + 18AE87502C06FDA60075F181 /* Carousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18AE874F2C06FDA60075F181 /* Carousel.swift */; }; + 18AE87542C06FE610075F181 /* CarouselChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */; }; 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */; }; @@ -237,6 +239,8 @@ 18A3F1292BD9298900498E4A /* Calendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Calendar.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 = ""; }; + 18AE874F2C06FDA60075F181 /* Carousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Carousel.swift; sourceTree = ""; }; + 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselChangeLog.txt; sourceTree = ""; }; 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = ""; }; 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = ""; }; 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarIndicatorModel.swift; sourceTree = ""; }; @@ -493,6 +497,15 @@ path = Breadcrumbs; sourceTree = ""; }; + 18AE874E2C06FD610075F181 /* Carousel */ = { + isa = PBXGroup; + children = ( + 18AE874F2C06FDA60075F181 /* Carousel.swift */, + 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */, + ); + path = Carousel; + sourceTree = ""; + }; 445BA07629C07ABA0036A7C5 /* Notification */ = { isa = PBXGroup; children = ( @@ -649,6 +662,7 @@ 18A65A002B96E7E1006602CC /* Breadcrumbs */, EA0FC2BE2912D18200DF80B4 /* Buttons */, 18A3F1202BD8F5DE00498E4A /* Calendar */, + 18AE874E2C06FD610075F181 /* Carousel */, 1808BEBA2BA41B1D00129230 /* CarouselScrollbar */, EAF7F092289985E200B287F5 /* Checkbox */, EAC58C1F2BF127F000BA39FA /* DatePicker */, @@ -1164,6 +1178,7 @@ EA3362072891E14D0071C351 /* VerizonNHGeDS-Regular.otf in Resources */, EAEEEC9A2B1F8E4400531FC2 /* TextLinkChangeLog.txt in Resources */, 1808BEC02BA456B700129230 /* CarouselScrollbarChangeLog.txt in Resources */, + 18AE87542C06FE610075F181 /* CarouselChangeLog.txt in Resources */, EAEEECAF2B1FC2BA00531FC2 /* ToggleViewChangeLog.txt in Resources */, EAEEEC922B1F807300531FC2 /* BadgeChangeLog.txt in Resources */, EAEEEC9E2B1F8F7700531FC2 /* ButtonGroupChangeLog.txt in Resources */, @@ -1321,6 +1336,7 @@ EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */, EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */, EAF7F0AB289B13FD00B287F5 /* TextStyleLabelAttribute.swift in Sources */, + 18AE87502C06FDA60075F181 /* Carousel.swift in Sources */, EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */, EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */, EA985BE629688F6A00F2FF2E /* TileletBadgeModel.swift in Sources */, diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift new file mode 100644 index 00000000..84bfd340 --- /dev/null +++ b/VDS/Components/Carousel/Carousel.swift @@ -0,0 +1,33 @@ +// +// Carousel.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 29/05/24. +// + +import Foundation +import UIKit +import VDSTokens +import Combine + +/// A carousel is a collection of related content in a row that a customer can navigate through horizontally. +/// Use this component to show content that is supplementary, not essential for task completion. +@objc(VDSCarousel) +open class Carousel: View { + + //-------------------------------------------------- + // 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) + } + +} diff --git a/VDS/Components/Carousel/CarouselChangeLog.txt b/VDS/Components/Carousel/CarouselChangeLog.txt new file mode 100644 index 00000000..a1fd0f4f --- /dev/null +++ b/VDS/Components/Carousel/CarouselChangeLog.txt @@ -0,0 +1,15 @@ +MM/DD/YYYY +---------------- + +06/22/2023 +---------------- +- Initial Beta Release + +10/02/2023 +---------------- +- Removed (Beta) from header. Removed deprecated sections and “New” badge from Kind section. + +11/20/2023 +---------------- +- Updated visuals to reflect new corner radius value - 12px +- Updated focus border corner radius to 14px From 57a0486b3fc81f35c185a15c181df15941122fe7 Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 4 Jun 2024 09:30:41 +0530 Subject: [PATCH 002/113] Digital ACT-191 ONEAPP-7016 story: added properties, added Carousel to VDS.md --- VDS.xcodeproj/project.pbxproj | 4 + VDS/Components/Carousel/Carousel.swift | 278 ++++++++++++++++++ .../Carousel/CarouselSlotAlignmentModel.swift | 25 ++ VDS/VDS.docc/VDS.md | 1 + 4 files changed, 308 insertions(+) create mode 100644 VDS/Components/Carousel/CarouselSlotAlignmentModel.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index f808e999..27693abe 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A032B96F050006602CC /* BreadcrumbItem.swift */; }; 18AE87502C06FDA60075F181 /* Carousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18AE874F2C06FDA60075F181 /* Carousel.swift */; }; 18AE87542C06FE610075F181 /* CarouselChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */; }; + 18B42AC62C09D197008D6262 /* CarouselSlotAlignmentModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */; }; 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */; }; @@ -241,6 +242,7 @@ 18A65A032B96F050006602CC /* BreadcrumbItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItem.swift; sourceTree = ""; }; 18AE874F2C06FDA60075F181 /* Carousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Carousel.swift; sourceTree = ""; }; 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselChangeLog.txt; sourceTree = ""; }; + 18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselSlotAlignmentModel.swift; sourceTree = ""; }; 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = ""; }; 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = ""; }; 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarIndicatorModel.swift; sourceTree = ""; }; @@ -501,6 +503,7 @@ isa = PBXGroup; children = ( 18AE874F2C06FDA60075F181 /* Carousel.swift */, + 18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */, 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */, ); path = Carousel; @@ -1300,6 +1303,7 @@ EA8E40932A82889500934ED3 /* TooltipDialog.swift in Sources */, 44604AD429CE186A00E62B51 /* NotificationButtonModel.swift in Sources */, EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */, + 18B42AC62C09D197008D6262 /* CarouselSlotAlignmentModel.swift in Sources */, 71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */, EA0D1C372A681CCE00E5C127 /* ToggleView.swift in Sources */, EAF7F0B9289C139800B287F5 /* ColorConfiguration.swift in Sources */, diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 84bfd340..a90e4e4b 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -30,4 +30,282 @@ open class Carousel: View { super.init(coder: coder) } + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + /// Aspect-ratio options for tilelet in the carousel. If 'none' is passed, the tilelet will take the height of the tallest item in the carousel. + open var aspectRatio: AspectRatio = .ratio1x1 { didSet { setNeedsUpdate() } } + + /// Data used to render tilelets in the carousel. + open var data: Array? = [] { didSet { setNeedsUpdate() } } + + /// Space between each tile. The default value will be 24px in tablet and 12px in mobile. + open var gutter: Gutter? { + get { return _gutter } + set { + if let newValue { + _gutter = newValue + } else { + _gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX + } + setNeedsUpdate() + } + } + + /// The amount of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. + open var layout: Layout? { + get { return _layout } + set { + if let newValue { + _layout = newValue + } else { + _layout = UIDevice.isIPad ? .threeUP : .oneUP + } + setNeedsUpdate() + } + } + + /// A callback when moving the carousel. Returns event object and selectedGroupIndex. + open var onChange: ((Int) -> Void)? { // TO DO: return object and index + get { nil } + set { + onChangeCancellable?.cancel() + if let newValue { + onChangeCancellable = onChangePublisher + .sink { c in + newValue(c) + } + } + } + } + + // TO DO: pagination + /// Config object for pagination. + /// Custom data type for pagination prop in this component. + + /// If provided, will determine the conditions to render the pagination arrows. + open var paginationDisplay: PaginationDisplay? { + get { return _paginationDisplay } + set { + if let newValue { + _paginationDisplay = newValue + } else { + _paginationDisplay = .none + } + setNeedsUpdate() + } + } + + /// If provided, will apply margin to pagination arrows. Can be set to either positive or negative values. + /// The default value will be 12px in tablet and 8px in mobile. + open var paginationInset: CGFloat? { + get { return _paginationInset } + set { + if let newValue { + _paginationInset = newValue + } else { + _paginationInset = UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space8X + } + setNeedsUpdate() + } + } + + /// Options for user to configure the partially-visible tile in group. Setting peek to 'none' will display arrow navigation icons on mobile devices. + open var peek: Peek? { + get { return _peek } + set { + if let newValue { + _peek = newValue + } else { + _peek = .none + } + setNeedsUpdate() + } + } + + // TO DO: renderItem + /// Render item function. This will pass the data array and expects a react component in return. +// open var renderItem: ([] -> Any)? { // TO DO: return object and index +// get { nil } +// set { +// onScrollCancellable?.cancel() +// if let newValue { +// onScrollCancellable = onScrollPublisher +// .sink { c in +// newValue(c) +// } +// } +// } +// } +// open var renderItem: ([] -> Void)? {} + + /// The initial visible slide's index in the carousel. + open var selectedIndex: Int? { didSet { setNeedsUpdate() } } + + /// If provided, will set the alignment for slot content when the slots has different heights. + open var slotAlignment: [CarouselSlotAlignmentModel] = [] { didSet { setNeedsUpdate() } } + + /// + open var hidePaginationBorder: Bool = false { didSet { setNeedsUpdate() } } + + /// viewport + /// viewportOverride + /// viewportOverrideObjectType + /// Custom data type for viewportOverride prop in this component. + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + // Sizes are from InVision design specs. + internal var containerSize: CGSize { CGSize(width: 320, height: 44) } + internal var containerView = View().with { + $0.clipsToBounds = true + } + + /// Previous button to show previous slide. + private let previousButton: PaginationButton = .init(type: .previous) + + /// Next button to show next slide. + private let nextButton: PaginationButton = .init(type: .next) + + /// A publisher for when the scrubber position changes. Passes parameters (position). + open var onChangePublisher = PassthroughSubject() + private var onChangeCancellable: AnyCancellable? + + /// A publisher for when the carousel moves. Passes parameters (data). +// open var onScrollPublisher = PassthroughSubject, Never>() +// private var onScrollCancellable: AnyCancellable? + + internal var _layout: Layout = UIDevice.isIPad ? .threeUP : .oneUP + internal var _paginationDisplay: PaginationDisplay = .none + internal var _paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space8X + internal var _gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX + internal var _peek: Peek = .none + + private var selectedGroupIndex: Int? { didSet { setNeedsUpdate() } } +// private var minPaginationInset: CGFloat { +// return UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space8X +// } + + //-------------------------------------------------- + // MARK: - Enums + //-------------------------------------------------- + /// Enum used to describe the aspect ratios used for this component. + public enum AspectRatio: String, CaseIterable { + case ratio1x1 = "1:1" + case ratio3x4 = "3:4" + case ratio4x3 = "4:3" + case ratio2x3 = "2:3" + case ratio3x2 = "3:2" + case ratio9x16 = "9:16" + case ratio16x9 = "16:9" + case ratio1x2 = "1:2" + case ratio2x1 = "2:1" + case none + } + + /// Enum used to describe the number of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. + public enum Layout: String, CaseIterable { + case oneUP = "1UP" + case twoUP = "2UP" + case threeUP = "3UP" + case fourUP = "4UP" + case fiveUP = "5UP" + case sixUP = "6UP" + + var value: Int { + switch self { + case .oneUP: + 1 + case .twoUP: + 2 + case .threeUP: + 3 + case .fourUP: + 4 + case .fiveUP: + 5 + case .sixUP: + 6 + } + } + } + + /// Enum used to describe the number of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. + public enum Gutter: String, CaseIterable { + case twelvePX = "12px" + case twentyFourPX = "24px" + + var value: CGFloat { + switch self { + case .twelvePX: + VDSLayout.space12X + case .twentyFourPX: + VDSLayout.space24X + } + } + } + + /// Enum used to describe the pagination display for this component. + public enum PaginationDisplay: String, CaseIterable { + case onHover, persistent, none + } + + /// Enum used to describe the peek for this component. Options for user to configure the partially-visible tile in group. Setting peek to 'none' will display arrow navigation icons on mobile devices. + public enum Peek: String, CaseIterable { + case standard, minimum, none + } + + /// Enum used to describe the pagination kind for pagination button icons. + public enum PaginationKind: String, CaseIterable { + case ghost, lowContrast, highContrast + } // TO DO: it should be used as pagination properties, validate desc stmt. and API - passing data is different. + + // TO DO: move to model class + /// Enum used to describe the vertical of slotAlignment. + public enum Vertical: String, CaseIterable { + case top, middle, bottom + } + + /// Enum used to describe the horizontal of slotAlignment. + public enum Horizontal: String, CaseIterable { + case left, center, right + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func initialSetup() { + super.initialSetup() + } + + open override func setup() { + super.setup() + isAccessibilityElement = false + + addSubview(containerView) + containerView + .pinTop() + .pinBottom() + .pinLeadingGreaterThanOrEqualTo() + .pinTrailingLessThanOrEqualTo() + .height(containerSize.height) +// .width(containerSize.width) + + containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + + } + + open override func updateView() { + super.updateView() + } + + open override func reset() { +// for subview in subviews { +// for recognizer in subview.gestureRecognizers ?? [] { +// subview.removeGestureRecognizer(recognizer) +// } +// } + super.reset() + } } diff --git a/VDS/Components/Carousel/CarouselSlotAlignmentModel.swift b/VDS/Components/Carousel/CarouselSlotAlignmentModel.swift new file mode 100644 index 00000000..5a8ec661 --- /dev/null +++ b/VDS/Components/Carousel/CarouselSlotAlignmentModel.swift @@ -0,0 +1,25 @@ +// +// CarouselSlotAlignmentModel.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 31/05/24. +// + +import Foundation + +/// Custom data type for slotAlignment prop for 'Carousel' component. +extension Carousel { + public struct CarouselSlotAlignmentModel { + + /// Text that shown to an indicator for legend + public var vertical: String + + /// Date to an indicator + public var horizontal: String + + public init(vertical: String, horizontal: String) { + self.vertical = vertical + self.horizontal = horizontal + } + } +} diff --git a/VDS/VDS.docc/VDS.md b/VDS/VDS.docc/VDS.md index d145ea0a..6582ce4a 100755 --- a/VDS/VDS.docc/VDS.md +++ b/VDS/VDS.docc/VDS.md @@ -26,6 +26,7 @@ Using the system allows designers and developers to collaborate more easily and - ``ButtonIcon`` - ``ButtonGroup`` - ``CalendarBase`` +- ``Carousel`` - ``CarouselScrollbar`` - ``Checkbox`` - ``CheckboxItem`` From bc35186bf65dcbb4ed9a102a3239528e13b8a987 Mon Sep 17 00:00:00 2001 From: vasavk Date: Sun, 9 Jun 2024 11:02:18 +0530 Subject: [PATCH 003/113] Digital ACT-191 ONEAPP-7013 story: added pagination model --- VDS.xcodeproj/project.pbxproj | 4 + VDS/Components/Carousel/Carousel.swift | 221 +++++++++--------- .../Carousel/CarouselPaginationModel.swift | 40 ++++ 3 files changed, 160 insertions(+), 105 deletions(-) create mode 100644 VDS/Components/Carousel/CarouselPaginationModel.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 27693abe..80abac94 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 18AE87542C06FE610075F181 /* CarouselChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */; }; 18B42AC62C09D197008D6262 /* CarouselSlotAlignmentModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */; }; 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; + 18B9763F2C11BA4A009271DF /* CarouselPaginationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */; }; 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */; }; 18FEA1B52BE0E63600A56439 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */; }; @@ -244,6 +245,7 @@ 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselChangeLog.txt; sourceTree = ""; }; 18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselSlotAlignmentModel.swift; sourceTree = ""; }; 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = ""; }; + 18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselPaginationModel.swift; sourceTree = ""; }; 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = ""; }; 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarIndicatorModel.swift; sourceTree = ""; }; 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = ""; }; @@ -503,6 +505,7 @@ isa = PBXGroup; children = ( 18AE874F2C06FDA60075F181 /* Carousel.swift */, + 18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */, 18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */, 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */, ); @@ -1293,6 +1296,7 @@ EA5F86D02A1F936100BC83E4 /* TabsContainer.swift in Sources */, EAF7F0B1289B177F00B287F5 /* ColorLabelAttribute.swift in Sources */, EAC9258F2911C9DE00091998 /* EntryFieldBase.swift in Sources */, + 18B9763F2C11BA4A009271DF /* CarouselPaginationModel.swift in Sources */, EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */, EAD068922A560B65002E3A2D /* LoaderViewController.swift in Sources */, 71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */, diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index a90e4e4b..b7f045df 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -37,30 +37,22 @@ open class Carousel: View { open var aspectRatio: AspectRatio = .ratio1x1 { didSet { setNeedsUpdate() } } /// Data used to render tilelets in the carousel. - open var data: Array? = [] { didSet { setNeedsUpdate() } } + open var data: [Any] = [] { didSet { setNeedsUpdate() } } /// Space between each tile. The default value will be 24px in tablet and 12px in mobile. - open var gutter: Gutter? { + open var gutter: Gutter { get { return _gutter } set { - if let newValue { - _gutter = newValue - } else { - _gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX - } + _gutter = newValue setNeedsUpdate() } } /// The amount of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. - open var layout: Layout? { + open var layout: Layout { get { return _layout } set { - if let newValue { - _layout = newValue - } else { - _layout = UIDevice.isIPad ? .threeUP : .oneUP - } + _layout = newValue setNeedsUpdate() } } @@ -78,114 +70,49 @@ open class Carousel: View { } } } - - // TO DO: pagination + /// Config object for pagination. - /// Custom data type for pagination prop in this component. + open var pagination: CarouselPaginationModel { + get { return _pagination } + set { + _pagination = newValue + setNeedsUpdate() + } + } /// If provided, will determine the conditions to render the pagination arrows. - open var paginationDisplay: PaginationDisplay? { + open var paginationDisplay: PaginationDisplay { get { return _paginationDisplay } set { - if let newValue { - _paginationDisplay = newValue - } else { - _paginationDisplay = .none - } + _paginationDisplay = newValue setNeedsUpdate() } } /// If provided, will apply margin to pagination arrows. Can be set to either positive or negative values. /// The default value will be 12px in tablet and 8px in mobile. - open var paginationInset: CGFloat? { + open var paginationInset: CGFloat { get { return _paginationInset } set { - if let newValue { - _paginationInset = newValue - } else { - _paginationInset = UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space8X - } + _paginationInset = newValue setNeedsUpdate() } } /// Options for user to configure the partially-visible tile in group. Setting peek to 'none' will display arrow navigation icons on mobile devices. - open var peek: Peek? { + open var peek: Peek { get { return _peek } set { - if let newValue { - _peek = newValue - } else { - _peek = .none - } + _peek = newValue setNeedsUpdate() } } - - // TO DO: renderItem - /// Render item function. This will pass the data array and expects a react component in return. -// open var renderItem: ([] -> Any)? { // TO DO: return object and index -// get { nil } -// set { -// onScrollCancellable?.cancel() -// if let newValue { -// onScrollCancellable = onScrollPublisher -// .sink { c in -// newValue(c) -// } -// } -// } -// } -// open var renderItem: ([] -> Void)? {} /// The initial visible slide's index in the carousel. open var selectedIndex: Int? { didSet { setNeedsUpdate() } } /// If provided, will set the alignment for slot content when the slots has different heights. - open var slotAlignment: [CarouselSlotAlignmentModel] = [] { didSet { setNeedsUpdate() } } - - /// - open var hidePaginationBorder: Bool = false { didSet { setNeedsUpdate() } } - - /// viewport - /// viewportOverride - /// viewportOverrideObjectType - /// Custom data type for viewportOverride prop in this component. - - //-------------------------------------------------- - // MARK: - Private Properties - //-------------------------------------------------- - // Sizes are from InVision design specs. - internal var containerSize: CGSize { CGSize(width: 320, height: 44) } - internal var containerView = View().with { - $0.clipsToBounds = true - } - - /// Previous button to show previous slide. - private let previousButton: PaginationButton = .init(type: .previous) - - /// Next button to show next slide. - private let nextButton: PaginationButton = .init(type: .next) - - /// A publisher for when the scrubber position changes. Passes parameters (position). - open var onChangePublisher = PassthroughSubject() - private var onChangeCancellable: AnyCancellable? - - /// A publisher for when the carousel moves. Passes parameters (data). -// open var onScrollPublisher = PassthroughSubject, Never>() -// private var onScrollCancellable: AnyCancellable? - - internal var _layout: Layout = UIDevice.isIPad ? .threeUP : .oneUP - internal var _paginationDisplay: PaginationDisplay = .none - internal var _paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space8X - internal var _gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX - internal var _peek: Peek = .none - - private var selectedGroupIndex: Int? { didSet { setNeedsUpdate() } } -// private var minPaginationInset: CGFloat { -// return UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space8X -// } + open var slotAlignment: [CarouselSlotAlignmentModel] = [] { didSet { setNeedsUpdate() } } //-------------------------------------------------- // MARK: - Enums @@ -255,12 +182,7 @@ open class Carousel: View { public enum Peek: String, CaseIterable { case standard, minimum, none } - - /// Enum used to describe the pagination kind for pagination button icons. - public enum PaginationKind: String, CaseIterable { - case ghost, lowContrast, highContrast - } // TO DO: it should be used as pagination properties, validate desc stmt. and API - passing data is different. - + // TO DO: move to model class /// Enum used to describe the vertical of slotAlignment. public enum Vertical: String, CaseIterable { @@ -271,6 +193,61 @@ open class Carousel: View { public enum Horizontal: String, CaseIterable { case left, center, right } + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + // Sizes are from InVision design specs. + internal var containerSize: CGSize { CGSize(width: 320, height: 44) } + + private let contentStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .vertical + $0.distribution = .fill + $0.spacing = UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space6X + $0.backgroundColor = .clear + } + + internal var carouselScrollBar = CarouselScrollbar().with { + $0.layout = UIDevice.isIPad ? .threeUP : .oneUP + $0.position = 0 + $0.backgroundColor = .clear + } + + internal var containerView = View().with { + $0.clipsToBounds = true + $0.backgroundColor = .clear + } + + internal var scrollContainerView = View().with { + $0.clipsToBounds = true + $0.backgroundColor = .clear + } + + private var scrollView = UIScrollView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundColor = .clear + } + + /// A publisher for when the scrubber position changes. Passes parameters (position). + open var onChangePublisher = PassthroughSubject() + private var onChangeCancellable: AnyCancellable? + + internal var _layout: Layout = UIDevice.isIPad ? .threeUP : .oneUP + internal var _pagination: CarouselPaginationModel = .init(kind: .lowContrast, floating: true) + internal var _paginationDisplay: PaginationDisplay = .none + internal var _paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space8X + internal var _gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX + internal var _peek: Peek = .none + internal var _numberOfSlides: Int = 1 + + private var selectedGroupIndex: Int? { didSet { setNeedsUpdate() } } + + private var containerStackHeightConstraint: NSLayoutConstraint? + private var containerViewHeightConstraint: NSLayoutConstraint? + + // The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile. + let space = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X //-------------------------------------------------- // MARK: - Lifecycle @@ -282,22 +259,56 @@ open class Carousel: View { open override func setup() { super.setup() isAccessibilityElement = false - + + // add containerView addSubview(containerView) containerView .pinTop() .pinBottom() - .pinLeadingGreaterThanOrEqualTo() - .pinTrailingLessThanOrEqualTo() - .height(containerSize.height) -// .width(containerSize.width) - + .pinLeading() + .pinTrailing() + .heightGreaterThanEqualTo(containerSize.height) containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + + // add content stackview + containerView.addSubview(contentStackView) + + // add scrollview + scrollContainerView.addSubview(scrollView) + + // add pagination button icons + scrollContainerView.addSubview(pagination.previousButton) + pagination.previousButton + .pinLeading(paginationInset) + .pinCenterY() + + scrollContainerView.addSubview(pagination.nextButton) + pagination.nextButton + .pinTrailing(paginationInset) + .pinCenterY() + // add scroll container view & carousel scrollbar + contentStackView.addArrangedSubview(scrollContainerView) + contentStackView.addArrangedSubview(carouselScrollBar) + contentStackView.setCustomSpacing(space, after: scrollContainerView) + contentStackView + .pinTop() + .pinBottom() + .pinLeading() + .pinTrailing() + .heightGreaterThanEqualTo(space+containerSize.height) + contentStackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } open override func updateView() { + containerViewHeightConstraint?.isActive = false super.updateView() + containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: 200) + containerViewHeightConstraint?.isActive = true + + pagination.previousButton.isHidden = (paginationDisplay == .none) + pagination.nextButton.isHidden = (paginationDisplay == .none) + layoutIfNeeded() } open override func reset() { diff --git a/VDS/Components/Carousel/CarouselPaginationModel.swift b/VDS/Components/Carousel/CarouselPaginationModel.swift new file mode 100644 index 00000000..0abeeded --- /dev/null +++ b/VDS/Components/Carousel/CarouselPaginationModel.swift @@ -0,0 +1,40 @@ +// +// CarouselPaginationModel.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 06/06/24. +// + +import Foundation +import UIKit + +/// Custom data type for pagination prop for 'Carousel' component. +extension Carousel { + public struct CarouselPaginationModel { + + /// Previous button to show previous slide. + public var previousButton = ButtonIcon().with { + $0.kind = .lowContrast + $0.iconName = .leftCaret + $0.iconOffset = .init(x: -2, y: 0) + $0.icon.size = UIDevice.isIPad ? .small : .xsmall + $0.size = UIDevice.isIPad ? .large : .small + } + + /// Next button to show next slide. + public var nextButton = ButtonIcon().with { + $0.kind = .lowContrast + $0.iconName = .rightCaret + $0.iconOffset = .init(x: 2, y: 0) + $0.icon.size = UIDevice.isIPad ? .small : .xsmall + $0.size = UIDevice.isIPad ? .large : .small + } + + public init(kind: ButtonIcon.Kind, floating: Bool) { + previousButton.kind = kind + nextButton.kind = kind + previousButton.floating = floating + nextButton.floating = floating + } + } +} From 0fc8c5c530e8c466b094cce26fe994f9342e36dd Mon Sep 17 00:00:00 2001 From: vasavk Date: Sun, 9 Jun 2024 11:25:39 +0530 Subject: [PATCH 004/113] Digital ACT-191 ONEAPP-7013 story: updating pagination buttons while changing pagination properties --- VDS/Components/Carousel/Carousel.swift | 36 +++++++++++++++---- .../Carousel/CarouselPaginationModel.swift | 28 ++++----------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index b7f045df..4007f5fe 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -228,7 +228,25 @@ open class Carousel: View { $0.translatesAutoresizingMaskIntoConstraints = false $0.backgroundColor = .clear } - + + /// Previous button to show previous slide. + private var previousButton = ButtonIcon().with { + $0.kind = .lowContrast + $0.iconName = .leftCaret + $0.iconOffset = .init(x: -2, y: 0) + $0.icon.size = UIDevice.isIPad ? .small : .xsmall + $0.size = UIDevice.isIPad ? .large : .small + } + + /// Next button to show next slide. + private var nextButton = ButtonIcon().with { + $0.kind = .lowContrast + $0.iconName = .rightCaret + $0.iconOffset = .init(x: 2, y: 0) + $0.icon.size = UIDevice.isIPad ? .small : .xsmall + $0.size = UIDevice.isIPad ? .large : .small + } + /// A publisher for when the scrubber position changes. Passes parameters (position). open var onChangePublisher = PassthroughSubject() private var onChangeCancellable: AnyCancellable? @@ -277,13 +295,13 @@ open class Carousel: View { scrollContainerView.addSubview(scrollView) // add pagination button icons - scrollContainerView.addSubview(pagination.previousButton) - pagination.previousButton + scrollContainerView.addSubview(previousButton) + previousButton .pinLeading(paginationInset) .pinCenterY() - scrollContainerView.addSubview(pagination.nextButton) - pagination.nextButton + scrollContainerView.addSubview(nextButton) + nextButton .pinTrailing(paginationInset) .pinCenterY() @@ -306,8 +324,12 @@ open class Carousel: View { containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: 200) containerViewHeightConstraint?.isActive = true - pagination.previousButton.isHidden = (paginationDisplay == .none) - pagination.nextButton.isHidden = (paginationDisplay == .none) + previousButton.isHidden = (paginationDisplay == .none) + nextButton.isHidden = (paginationDisplay == .none) + previousButton.kind = pagination.kind + previousButton.floating = pagination.floating + nextButton.kind = pagination.kind + nextButton.floating = pagination.floating layoutIfNeeded() } diff --git a/VDS/Components/Carousel/CarouselPaginationModel.swift b/VDS/Components/Carousel/CarouselPaginationModel.swift index 0abeeded..69766223 100644 --- a/VDS/Components/Carousel/CarouselPaginationModel.swift +++ b/VDS/Components/Carousel/CarouselPaginationModel.swift @@ -11,30 +11,16 @@ import UIKit /// Custom data type for pagination prop for 'Carousel' component. extension Carousel { public struct CarouselPaginationModel { + + /// Button icon property 'kind' is supported + public var kind: ButtonIcon.Kind - /// Previous button to show previous slide. - public var previousButton = ButtonIcon().with { - $0.kind = .lowContrast - $0.iconName = .leftCaret - $0.iconOffset = .init(x: -2, y: 0) - $0.icon.size = UIDevice.isIPad ? .small : .xsmall - $0.size = UIDevice.isIPad ? .large : .small - } - - /// Next button to show next slide. - public var nextButton = ButtonIcon().with { - $0.kind = .lowContrast - $0.iconName = .rightCaret - $0.iconOffset = .init(x: 2, y: 0) - $0.icon.size = UIDevice.isIPad ? .small : .xsmall - $0.size = UIDevice.isIPad ? .large : .small - } + /// Button icon property 'floating' is supported + public var floating: Bool public init(kind: ButtonIcon.Kind, floating: Bool) { - previousButton.kind = kind - nextButton.kind = kind - previousButton.floating = floating - nextButton.floating = floating + self.kind = kind + self.floating = floating } } } From 35e4cf0301133871a4dad6db21d1a72709ad2046 Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 11 Jun 2024 15:16:52 +0530 Subject: [PATCH 005/113] Digital ACT-191 ONEAPP-7013 story: show slides on Scrollview --- VDS/Components/Carousel/Carousel.swift | 276 +++++++++++------- .../Carousel/CarouselPaginationModel.swift | 4 +- .../CarouselScrollbar/CarouselScrollbar.swift | 14 +- 3 files changed, 186 insertions(+), 108 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 4007f5fe..8c234351 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -30,6 +30,92 @@ open class Carousel: View { super.init(coder: coder) } + //-------------------------------------------------- + // MARK: - Enums + //-------------------------------------------------- + /// Enum used to describe the aspect ratios used for this component. + public enum AspectRatio: String, CaseIterable { + case ratio1x1 = "1:1" + case ratio3x4 = "3:4" + case ratio4x3 = "4:3" + case ratio2x3 = "2:3" + case ratio3x2 = "3:2" + case ratio9x16 = "9:16" + case ratio16x9 = "16:9" + case ratio1x2 = "1:2" + case ratio2x1 = "2:1" + case none + } + + /// Enum used to describe the number of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. + public enum Layout: String, CaseIterable { + case oneUP = "1UP" + case twoUP = "2UP" + case threeUP = "3UP" + case fourUP = "4UP" + case fiveUP = "5UP" + case sixUP = "6UP" + + var value: Int { + switch self { + case .oneUP: + 1 + case .twoUP: + 2 + case .threeUP: + 3 + case .fourUP: + 4 + case .fiveUP: + 5 + case .sixUP: + 6 + } + } + } + + /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. + public enum Gutter: String, CaseIterable { + case twelvePX = "12px" + case twentyFourPX = "24px" + + var value: CGFloat { + switch self { + case .twelvePX: + VDSLayout.space3X + case .twentyFourPX: + VDSLayout.space6X + } + } + } + + /// Enum used to describe the pagination display for this component. + public enum PaginationDisplay: String, CaseIterable { + case onHover, persistent, none + } + + /// Enum used to describe the peek for this component. Options for user to configure the partially-visible tile in group. Setting peek to 'none' will display arrow navigation icons on mobile devices. + public enum Peek: String, CaseIterable { + case standard, minimum, none + } + + // TO DO: move to model class + /// Enum used to describe the vertical of slotAlignment. + public enum Vertical: String, CaseIterable { + case top, middle, bottom + } + + /// Enum used to describe the horizontal of slotAlignment. + public enum Horizontal: String, CaseIterable { + case left, center, right + } + + /// Enum used to describe the width of a fixed value or percentage of parent's width. + public enum Width { + case percentage(CGFloat) + case value(CGFloat) + } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @@ -38,8 +124,30 @@ open class Carousel: View { /// Data used to render tilelets in the carousel. open var data: [Any] = [] { didSet { setNeedsUpdate() } } + + /// If provided, width of slots will be rendered based on this value. If omitted, default widths are rendered. + open var width : Width? { + get { _width } + set { + if let newValue { + switch newValue { + case .percentage(let percentage): + if percentage >= 10 && percentage <= 100.0 { + _width = newValue + } + case .value(let value): + if value > minimumSlotWidth { /*(size.minimumSlotWidth)*/ + _width = newValue + } + } + } else { + _width = nil + } + setNeedsUpdate() + } + } - /// Space between each tile. The default value will be 24px in tablet and 12px in mobile. + /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. open var gutter: Gutter { get { return _gutter } set { @@ -113,87 +221,7 @@ open class Carousel: View { /// If provided, will set the alignment for slot content when the slots has different heights. open var slotAlignment: [CarouselSlotAlignmentModel] = [] { didSet { setNeedsUpdate() } } - - //-------------------------------------------------- - // MARK: - Enums - //-------------------------------------------------- - /// Enum used to describe the aspect ratios used for this component. - public enum AspectRatio: String, CaseIterable { - case ratio1x1 = "1:1" - case ratio3x4 = "3:4" - case ratio4x3 = "4:3" - case ratio2x3 = "2:3" - case ratio3x2 = "3:2" - case ratio9x16 = "9:16" - case ratio16x9 = "16:9" - case ratio1x2 = "1:2" - case ratio2x1 = "2:1" - case none - } - - /// Enum used to describe the number of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. - public enum Layout: String, CaseIterable { - case oneUP = "1UP" - case twoUP = "2UP" - case threeUP = "3UP" - case fourUP = "4UP" - case fiveUP = "5UP" - case sixUP = "6UP" - - var value: Int { - switch self { - case .oneUP: - 1 - case .twoUP: - 2 - case .threeUP: - 3 - case .fourUP: - 4 - case .fiveUP: - 5 - case .sixUP: - 6 - } - } - } - - /// Enum used to describe the number of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. - public enum Gutter: String, CaseIterable { - case twelvePX = "12px" - case twentyFourPX = "24px" - var value: CGFloat { - switch self { - case .twelvePX: - VDSLayout.space12X - case .twentyFourPX: - VDSLayout.space24X - } - } - } - - /// Enum used to describe the pagination display for this component. - public enum PaginationDisplay: String, CaseIterable { - case onHover, persistent, none - } - - /// Enum used to describe the peek for this component. Options for user to configure the partially-visible tile in group. Setting peek to 'none' will display arrow navigation icons on mobile devices. - public enum Peek: String, CaseIterable { - case standard, minimum, none - } - - // TO DO: move to model class - /// Enum used to describe the vertical of slotAlignment. - public enum Vertical: String, CaseIterable { - case top, middle, bottom - } - - /// Enum used to describe the horizontal of slotAlignment. - public enum Horizontal: String, CaseIterable { - case left, center, right - } - //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -204,7 +232,7 @@ open class Carousel: View { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .vertical $0.distribution = .fill - $0.spacing = UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space6X + $0.spacing = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X $0.backgroundColor = .clear } @@ -254,19 +282,21 @@ open class Carousel: View { internal var _layout: Layout = UIDevice.isIPad ? .threeUP : .oneUP internal var _pagination: CarouselPaginationModel = .init(kind: .lowContrast, floating: true) internal var _paginationDisplay: PaginationDisplay = .none - internal var _paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space8X + internal var _paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X internal var _gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX internal var _peek: Peek = .none internal var _numberOfSlides: Int = 1 + private var _width: Width? = nil private var selectedGroupIndex: Int? { didSet { setNeedsUpdate() } } - private var containerStackHeightConstraint: NSLayoutConstraint? private var containerViewHeightConstraint: NSLayoutConstraint? // The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile. - let space = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X - + let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X + var slotHeight = 100.0 + var peekMinimum = 24.0 + var minimumSlotWidth = 0.0 //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -293,6 +323,7 @@ open class Carousel: View { // add scrollview scrollContainerView.addSubview(scrollView) + scrollView.pinToSuperView() // add pagination button icons scrollContainerView.addSubview(previousButton) @@ -308,37 +339,84 @@ open class Carousel: View { // add scroll container view & carousel scrollbar contentStackView.addArrangedSubview(scrollContainerView) contentStackView.addArrangedSubview(carouselScrollBar) - contentStackView.setCustomSpacing(space, after: scrollContainerView) + contentStackView.setCustomSpacing(scrollbarTopSpace, after: scrollContainerView) contentStackView .pinTop() .pinBottom() .pinLeading() .pinTrailing() - .heightGreaterThanEqualTo(space+containerSize.height) + .heightGreaterThanEqualTo(scrollbarTopSpace + containerSize.height) contentStackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } open override func updateView() { - containerViewHeightConstraint?.isActive = false super.updateView() - containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: 200) - containerViewHeightConstraint?.isActive = true + containerViewHeightConstraint?.isActive = false + updatePaginationControls() + getSlotWidth() + // perform a loop to iterate each subView + scrollView.subviews.forEach { subView in + // removing subView from its parent view + subView.removeFromSuperview() + } + + // add carousel items + if data.count > 0 { + var xPos = gutter.value + for _ in 0...data.count - 1 { + let carouselSlot = View().with { + $0.clipsToBounds = true + $0.backgroundColor = .lightGray + } + scrollView.addSubview(carouselSlot) + carouselSlot + .pinTop() + .pinBottom() + .pinLeading(xPos) + .width(minimumSlotWidth) + .height(slotHeight) + xPos = xPos + minimumSlotWidth + gutter.value + } + scrollView.heightAnchor.constraint(equalToConstant: slotHeight).isActive = true + scrollView.contentSize = CGSize(width: xPos-minimumSlotWidth, height: slotHeight) + } + let containerHeight = slotHeight + scrollbarTopSpace + containerSize.height + containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: containerHeight) + containerViewHeightConstraint?.isActive = true + layoutIfNeeded() + } + + open override func reset() { + super.reset() + } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + func updatePaginationControls() { + containerView.surface = surface previousButton.isHidden = (paginationDisplay == .none) nextButton.isHidden = (paginationDisplay == .none) previousButton.kind = pagination.kind previousButton.floating = pagination.floating nextButton.kind = pagination.kind nextButton.floating = pagination.floating - layoutIfNeeded() + previousButton.surface = surface + nextButton.surface = surface } - open override func reset() { -// for subview in subviews { -// for recognizer in subview.gestureRecognizers ?? [] { -// subview.removeGestureRecognizer(recognizer) -// } -// } - super.reset() + func getSlotWidth() { + let actualWidth = containerView.frame.size.width + minimumSlotWidth = actualWidth - (CGFloat(layout.value) * gutter.value) + switch peek { + case .standard: + minimumSlotWidth = minimumSlotWidth - (3*peekMinimum) + case .minimum: + minimumSlotWidth = minimumSlotWidth - peekMinimum + case .none: + break + } + minimumSlotWidth = minimumSlotWidth / CGFloat(layout.value) } } diff --git a/VDS/Components/Carousel/CarouselPaginationModel.swift b/VDS/Components/Carousel/CarouselPaginationModel.swift index 69766223..ab85d6a4 100644 --- a/VDS/Components/Carousel/CarouselPaginationModel.swift +++ b/VDS/Components/Carousel/CarouselPaginationModel.swift @@ -12,10 +12,10 @@ import UIKit extension Carousel { public struct CarouselPaginationModel { - /// Button icon property 'kind' is supported + /// Pagination supports Button icon property 'kind'. public var kind: ButtonIcon.Kind - /// Button icon property 'floating' is supported + /// Pagination supports Button icon property 'floating'. public var floating: Bool public init(kind: ButtonIcon.Kind, floating: Bool) { diff --git a/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift b/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift index 00e810d3..abba5017 100644 --- a/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift +++ b/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift @@ -45,13 +45,13 @@ open class CarouselScrollbar: View { } /// The number of slides that can appear at once in a set in a carousel container. - open var selectedLayout: Layout? { - get { return _selectedLayout } + open var layout: Layout? { + get { return _layout } set { if let newValue { - _selectedLayout = newValue + _layout = newValue } else { - _selectedLayout = .oneUP + _layout = .oneUP } setThumbWidth() scrollThumbToPosition(position) @@ -198,7 +198,7 @@ open class CarouselScrollbar: View { //-------------------------------------------------- // Sizes are from InVision design specs. internal var containerSize: CGSize { CGSize(width: 45, height: 44) } - internal var _selectedLayout: Layout = .oneUP + internal var _layout: Layout = .oneUP internal var _numberOfSlides: Int = 1 internal var totalPositions: Int = 1 internal var _position: Int = 1 @@ -329,7 +329,7 @@ open class CarouselScrollbar: View { // Compute track width and should maintain minimum thumb width if needed private func setThumbWidth() { - let width = (Float(trackViewWidth) / Float(numberOfSlides)) * Float(_selectedLayout.value) + let width = (Float(trackViewWidth) / Float(numberOfSlides)) * Float(_layout.value) computedWidth = (width > Float(trackViewWidth)) ? Float(trackViewWidth) : width thumbWidth = (width <= Float(trackViewWidth) && width > minThumbWidth) ? width : ((width > Float(trackViewWidth)) ? Float(trackViewWidth) : minThumbWidth) thumbView.frame.size.width = CGFloat(thumbWidth) @@ -362,7 +362,7 @@ open class CarouselScrollbar: View { } private func checkPositions() { - totalPositions = Int (ceil (Double(numberOfSlides) / Double(_selectedLayout.value))) + totalPositions = Int (ceil (Double(numberOfSlides) / Double(_layout.value))) } private func scrollThumbToPosition(_ position: Int) { From ba1ffdf39082184ac7ef395c15c943f40d1046e3 Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 13 Jun 2024 13:51:32 +0530 Subject: [PATCH 006/113] Digital ACT-191 ONEAPP-7013 story: added listeners to perform actions --- VDS/Components/Carousel/Carousel.swift | 126 ++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 12 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 8c234351..077fc546 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -91,10 +91,11 @@ open class Carousel: View { /// Enum used to describe the pagination display for this component. public enum PaginationDisplay: String, CaseIterable { - case onHover, persistent, none + case persistent, none } - /// Enum used to describe the peek for this component. Options for user to configure the partially-visible tile in group. Setting peek to 'none' will display arrow navigation icons on mobile devices. + /// Enum used to describe the peek for this component. + /// This is how much a tile is partially visible. It is measured by the distance between the edge of the tile and the edge of the viewport or carousel container. A peek can appear on the left and/or right edge of the carousel container or viewport, depending on the carousel’s scroll position. public enum Peek: String, CaseIterable { case standard, minimum, none } @@ -157,7 +158,7 @@ open class Carousel: View { } /// The amount of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. - open var layout: Layout { + open var layout: CarouselScrollbar.Layout { get { return _layout } set { _layout = newValue @@ -197,7 +198,7 @@ open class Carousel: View { } } - /// If provided, will apply margin to pagination arrows. Can be set to either positive or negative values. + /// If provided, will apply margin to pagination arrows. Can be set to either positive or negative values. /// The default value will be 12px in tablet and 8px in mobile. open var paginationInset: CGFloat { get { return _paginationInset } @@ -279,7 +280,7 @@ open class Carousel: View { open var onChangePublisher = PassthroughSubject() private var onChangeCancellable: AnyCancellable? - internal var _layout: Layout = UIDevice.isIPad ? .threeUP : .oneUP + internal var _layout: CarouselScrollbar.Layout = UIDevice.isIPad ? .threeUP : .oneUP internal var _pagination: CarouselPaginationModel = .init(kind: .lowContrast, floating: true) internal var _paginationDisplay: PaginationDisplay = .none internal var _paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X @@ -294,6 +295,7 @@ open class Carousel: View { // The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile. let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X + var slotHeight = 100.0 var peekMinimum = 24.0 var minimumSlotWidth = 0.0 @@ -351,7 +353,19 @@ open class Carousel: View { open override func updateView() { super.updateView() + + carouselScrollBar.numberOfSlides = data.count + carouselScrollBar.position = (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) ? 1 : carouselScrollBar.position + carouselScrollBar.layout = _layout + carouselScrollBar.isHidden = (Int((Float(carouselScrollBar.numberOfSlides)) * Float(carouselScrollBar._layout.value)) <= 1) ? true : false + + // When peek is set to ‘none,’ pagination controls are automatically set to persistent. + if peek == .none { + paginationDisplay = .persistent + } + containerViewHeightConstraint?.isActive = false + containerStackHeightConstraint?.isActive = false updatePaginationControls() getSlotWidth() @@ -363,11 +377,11 @@ open class Carousel: View { // add carousel items if data.count > 0 { - var xPos = gutter.value - for _ in 0...data.count - 1 { + var xPos = 0.0 + for x in 0...data.count - 1 { let carouselSlot = View().with { $0.clipsToBounds = true - $0.backgroundColor = .lightGray + $0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1) } scrollView.addSubview(carouselSlot) carouselSlot @@ -379,25 +393,74 @@ open class Carousel: View { xPos = xPos + minimumSlotWidth + gutter.value } scrollView.heightAnchor.constraint(equalToConstant: slotHeight).isActive = true - scrollView.contentSize = CGSize(width: xPos-minimumSlotWidth, height: slotHeight) + scrollView.contentSize = CGSize(width: xPos - gutter.value, height: slotHeight) } + let containerHeight = slotHeight + scrollbarTopSpace + containerSize.height - containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: containerHeight) + if carouselScrollBar.isHidden { + containerStackHeightConstraint = contentStackView.heightAnchor.constraint(equalToConstant: slotHeight) + containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: slotHeight) + } else { + containerStackHeightConstraint = contentStackView.heightAnchor.constraint(equalToConstant: containerHeight) + containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: containerHeight) + } containerViewHeightConstraint?.isActive = true + containerStackHeightConstraint?.isActive = true + + addlisteners() layoutIfNeeded() } open override func reset() { + for subview in subviews { + for recognizer in subview.gestureRecognizers ?? [] { + subview.removeGestureRecognizer(recognizer) + } + } super.reset() } //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- + func addlisteners() { + nextButton.onClick = { _ in self.nextButtonClick() } + previousButton.onClick = { _ in self.previousButtonClick() } + + //setup test page to show scrubber id was changed +// carouselScrollBar.onScrubberDrag = { [weak self] scrubberId in +// guard let self else { return } +// updateScrollPosition(position: scrubberId, callbackText:"onScrubberDrag") +// } + + /// will be called when the thumb move forward. + carouselScrollBar.onMoveForward = { [weak self] scrubberId in + guard let self else { return } + updateScrollPosition(position: scrubberId, callbackText:"onMoveForward") + } + + /// will be called when the thumb move backward. + carouselScrollBar.onMoveBackward = { [weak self] scrubberId in + guard let self else { return } + updateScrollPosition(position: scrubberId, callbackText:"onMoveBackward") + } + + /// will be called when the thumb touch start. + carouselScrollBar.onThumbTouchStart = { [weak self] scrubberId in + guard let self else { return } + updateScrollPosition(position: scrubberId, callbackText:"onThumbTouchStart") + } + + /// will be called when the thumb touch end. + carouselScrollBar.onThumbTouchEnd = { [weak self] scrubberId in + guard let self else { return } + updateScrollPosition(position: scrubberId, callbackText:"onThumbTouchEnd") + } + } + func updatePaginationControls() { containerView.surface = surface - previousButton.isHidden = (paginationDisplay == .none) - nextButton.isHidden = (paginationDisplay == .none) + showPaginationControls() previousButton.kind = pagination.kind previousButton.floating = pagination.floating nextButton.kind = pagination.kind @@ -419,4 +482,43 @@ open class Carousel: View { } minimumSlotWidth = minimumSlotWidth / CGFloat(layout.value) } + + func nextButtonClick() { + carouselScrollBar.position = carouselScrollBar.position+1 + showPaginationControls() + updateScrollPosition(position: carouselScrollBar.position, callbackText:"pageControlClicks") + } + + func previousButtonClick() { + carouselScrollBar.position = carouselScrollBar.position-1 + showPaginationControls() + updateScrollPosition(position: carouselScrollBar.position, callbackText:"pageControlClicks") + } + + func updateScrollPosition(position: Int, callbackText: String) { + if carouselScrollBar.numberOfSlides > 0 { + let contentOffsetWidth = scrollView.contentSize.width + let totalPositions = totalPositions() + let multiplier: Float = (position == 1) ? 0 : Float((position)-1) / Float(totalPositions) + let xPos = (position == totalPositions) ? (contentOffsetWidth - containerView.frame.size.width) : CGFloat(Float(contentOffsetWidth) * multiplier) + carouselScrollBar.scrubberId = position + let yPos = scrollView.contentOffset.y + scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true) + showPaginationControls() + } + } + + private func totalPositions() -> Int { + return Int (ceil (Double(carouselScrollBar.numberOfSlides) / Double(_layout.value))) + } + + func showPaginationControls() { + if carouselScrollBar.numberOfSlides == _layout.value { + previousButton.isHidden = true + nextButton.isHidden = true + } else { + previousButton.isHidden = (carouselScrollBar.position == 1) || (paginationDisplay == .none) + nextButton.isHidden = (carouselScrollBar.position == totalPositions()) || (paginationDisplay == .none) + } + } } From de8a2ebdeeed4ca1ce8cbb3b4841505cb993e7f5 Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 13 Jun 2024 14:13:27 +0530 Subject: [PATCH 007/113] Digital ACT-191 ONEAPP-7013 story: Using custom container size and custom icon sizes for positioning controls --- VDS/Components/Carousel/Carousel.swift | 41 +++++--------------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 077fc546..95abb990 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -47,33 +47,6 @@ open class Carousel: View { case none } - /// Enum used to describe the number of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. - public enum Layout: String, CaseIterable { - case oneUP = "1UP" - case twoUP = "2UP" - case threeUP = "3UP" - case fourUP = "4UP" - case fiveUP = "5UP" - case sixUP = "6UP" - - var value: Int { - switch self { - case .oneUP: - 1 - case .twoUP: - 2 - case .threeUP: - 3 - case .fourUP: - 4 - case .fiveUP: - 5 - case .sixUP: - 6 - } - } - } - /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. public enum Gutter: String, CaseIterable { case twelvePX = "12px" @@ -263,8 +236,8 @@ open class Carousel: View { $0.kind = .lowContrast $0.iconName = .leftCaret $0.iconOffset = .init(x: -2, y: 0) - $0.icon.size = UIDevice.isIPad ? .small : .xsmall - $0.size = UIDevice.isIPad ? .large : .small + $0.customContainerSize = UIDevice.isIPad ? 40 : 28 + $0.icon.customSize = UIDevice.isIPad ? 16 : 12 } /// Next button to show next slide. @@ -272,8 +245,8 @@ open class Carousel: View { $0.kind = .lowContrast $0.iconName = .rightCaret $0.iconOffset = .init(x: 2, y: 0) - $0.icon.size = UIDevice.isIPad ? .small : .xsmall - $0.size = UIDevice.isIPad ? .large : .small + $0.customContainerSize = UIDevice.isIPad ? 40 : 28 + $0.icon.customSize = UIDevice.isIPad ? 16 : 12 } /// A publisher for when the scrubber position changes. Passes parameters (position). @@ -355,10 +328,10 @@ open class Carousel: View { super.updateView() carouselScrollBar.numberOfSlides = data.count - carouselScrollBar.position = (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) ? 1 : carouselScrollBar.position carouselScrollBar.layout = _layout - carouselScrollBar.isHidden = (Int((Float(carouselScrollBar.numberOfSlides)) * Float(carouselScrollBar._layout.value)) <= 1) ? true : false - + carouselScrollBar.position = (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) ? 1 : carouselScrollBar.position + carouselScrollBar.isHidden = (totalPositions() <= 1) ? true : false + // When peek is set to ‘none,’ pagination controls are automatically set to persistent. if peek == .none { paginationDisplay = .persistent From a829df86e58ae23c67826aa00ce83be6ebd077b4 Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 14 Jun 2024 13:17:29 +0530 Subject: [PATCH 008/113] Digital ACT-191 ONEAPP-7013 story: Fixed Constraints issue and refactored Code --- VDS/Components/Carousel/Carousel.swift | 113 +++++++++++-------------- 1 file changed, 51 insertions(+), 62 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 95abb990..0486af1d 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -33,25 +33,11 @@ open class Carousel: View { //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- - /// Enum used to describe the aspect ratios used for this component. - public enum AspectRatio: String, CaseIterable { - case ratio1x1 = "1:1" - case ratio3x4 = "3:4" - case ratio4x3 = "4:3" - case ratio2x3 = "2:3" - case ratio3x2 = "3:2" - case ratio9x16 = "9:16" - case ratio16x9 = "16:9" - case ratio1x2 = "1:2" - case ratio2x1 = "2:1" - case none - } - /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. public enum Gutter: String, CaseIterable { case twelvePX = "12px" case twentyFourPX = "24px" - + var value: CGFloat { switch self { case .twelvePX: @@ -61,7 +47,7 @@ open class Carousel: View { } } } - + /// Enum used to describe the pagination display for this component. public enum PaginationDisplay: String, CaseIterable { case persistent, none @@ -72,8 +58,7 @@ open class Carousel: View { public enum Peek: String, CaseIterable { case standard, minimum, none } - - // TO DO: move to model class + /// Enum used to describe the vertical of slotAlignment. public enum Vertical: String, CaseIterable { case top, middle, bottom @@ -94,11 +79,11 @@ open class Carousel: View { // MARK: - Public Properties //-------------------------------------------------- /// Aspect-ratio options for tilelet in the carousel. If 'none' is passed, the tilelet will take the height of the tallest item in the carousel. - open var aspectRatio: AspectRatio = .ratio1x1 { didSet { setNeedsUpdate() } } - + open var aspectRatio: Tilelet.AspectRatio = .ratio1x1 { didSet { setNeedsUpdate() } } + /// Data used to render tilelets in the carousel. open var data: [Any] = [] { didSet { setNeedsUpdate() } } - + /// If provided, width of slots will be rendered based on this value. If omitted, default widths are rendered. open var width : Width? { get { _width } @@ -129,7 +114,7 @@ open class Carousel: View { setNeedsUpdate() } } - + /// The amount of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. open var layout: CarouselScrollbar.Layout { get { return _layout } @@ -138,9 +123,9 @@ open class Carousel: View { setNeedsUpdate() } } - + /// A callback when moving the carousel. Returns event object and selectedGroupIndex. - open var onChange: ((Int) -> Void)? { // TO DO: return object and index + open var onChange: ((Int) -> Void)? { get { nil } set { onChangeCancellable?.cancel() @@ -161,7 +146,7 @@ open class Carousel: View { setNeedsUpdate() } } - + /// If provided, will determine the conditions to render the pagination arrows. open var paginationDisplay: PaginationDisplay { get { return _paginationDisplay } @@ -170,7 +155,7 @@ open class Carousel: View { setNeedsUpdate() } } - + /// If provided, will apply margin to pagination arrows. Can be set to either positive or negative values. /// The default value will be 12px in tablet and 8px in mobile. open var paginationInset: CGFloat { @@ -189,19 +174,19 @@ open class Carousel: View { setNeedsUpdate() } } - + /// The initial visible slide's index in the carousel. open var selectedIndex: Int? { didSet { setNeedsUpdate() } } /// If provided, will set the alignment for slot content when the slots has different heights. open var slotAlignment: [CarouselSlotAlignmentModel] = [] { didSet { setNeedsUpdate() } } - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- // Sizes are from InVision design specs. internal var containerSize: CGSize { CGSize(width: 320, height: 44) } - + private let contentStackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .vertical @@ -231,28 +216,32 @@ open class Carousel: View { $0.backgroundColor = .clear } - /// Previous button to show previous slide. - private var previousButton = ButtonIcon().with { - $0.kind = .lowContrast - $0.iconName = .leftCaret - $0.iconOffset = .init(x: -2, y: 0) - $0.customContainerSize = UIDevice.isIPad ? 40 : 28 - $0.icon.customSize = UIDevice.isIPad ? 16 : 12 - } - - /// Next button to show next slide. - private var nextButton = ButtonIcon().with { - $0.kind = .lowContrast - $0.iconName = .rightCaret - $0.iconOffset = .init(x: 2, y: 0) - $0.customContainerSize = UIDevice.isIPad ? 40 : 28 - $0.icon.customSize = UIDevice.isIPad ? 16 : 12 - } + /// Previous button to show previous slide. + private var previousButton = ButtonIcon().with { + $0.kind = .lowContrast + $0.iconName = .leftCaret + $0.iconOffset = .init(x: -2, y: 0) + $0.customContainerSize = UIDevice.isIPad ? 40 : 28 + $0.icon.customSize = UIDevice.isIPad ? 16 : 12 + } + + /// Next button to show next slide. + private var nextButton = ButtonIcon().with { + $0.kind = .lowContrast + $0.iconName = .rightCaret + $0.iconOffset = .init(x: 2, y: 0) + $0.customContainerSize = UIDevice.isIPad ? 40 : 28 + $0.icon.customSize = UIDevice.isIPad ? 16 : 12 + } /// A publisher for when the scrubber position changes. Passes parameters (position). open var onChangePublisher = PassthroughSubject() private var onChangeCancellable: AnyCancellable? - + + /// A publisher for when the carousel moves. Passes parameters (data). + open var onScrollPublisher = PassthroughSubject, Never>() + private var onScrollCancellable: AnyCancellable? + internal var _layout: CarouselScrollbar.Layout = UIDevice.isIPad ? .threeUP : .oneUP internal var _pagination: CarouselPaginationModel = .init(kind: .lowContrast, floating: true) internal var _paginationDisplay: PaginationDisplay = .none @@ -260,12 +249,12 @@ open class Carousel: View { internal var _gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX internal var _peek: Peek = .none internal var _numberOfSlides: Int = 1 - + private var _width: Width? = nil private var selectedGroupIndex: Int? { didSet { setNeedsUpdate() } } private var containerStackHeightConstraint: NSLayoutConstraint? private var containerViewHeightConstraint: NSLayoutConstraint? - + // The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile. let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X @@ -282,7 +271,7 @@ open class Carousel: View { open override func setup() { super.setup() isAccessibilityElement = false - + // add containerView addSubview(containerView) containerView @@ -305,12 +294,12 @@ open class Carousel: View { previousButton .pinLeading(paginationInset) .pinCenterY() - + scrollContainerView.addSubview(nextButton) nextButton .pinTrailing(paginationInset) .pinCenterY() - + // add scroll container view & carousel scrollbar contentStackView.addArrangedSubview(scrollContainerView) contentStackView.addArrangedSubview(carouselScrollBar) @@ -320,13 +309,15 @@ open class Carousel: View { .pinBottom() .pinLeading() .pinTrailing() - .heightGreaterThanEqualTo(scrollbarTopSpace + containerSize.height) + .heightGreaterThanEqualTo(containerSize.height) contentStackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + + addlisteners() } open override func updateView() { super.updateView() - + carouselScrollBar.numberOfSlides = data.count carouselScrollBar.layout = _layout carouselScrollBar.position = (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) ? 1 : carouselScrollBar.position @@ -354,7 +345,7 @@ open class Carousel: View { for x in 0...data.count - 1 { let carouselSlot = View().with { $0.clipsToBounds = true - $0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1) + $0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1) } scrollView.addSubview(carouselSlot) carouselSlot @@ -365,7 +356,6 @@ open class Carousel: View { .height(slotHeight) xPos = xPos + minimumSlotWidth + gutter.value } - scrollView.heightAnchor.constraint(equalToConstant: slotHeight).isActive = true scrollView.contentSize = CGSize(width: xPos - gutter.value, height: slotHeight) } @@ -380,7 +370,6 @@ open class Carousel: View { containerViewHeightConstraint?.isActive = true containerStackHeightConstraint?.isActive = true - addlisteners() layoutIfNeeded() } @@ -401,10 +390,10 @@ open class Carousel: View { previousButton.onClick = { _ in self.previousButtonClick() } //setup test page to show scrubber id was changed -// carouselScrollBar.onScrubberDrag = { [weak self] scrubberId in -// guard let self else { return } -// updateScrollPosition(position: scrubberId, callbackText:"onScrubberDrag") -// } + // carouselScrollBar.onScrubberDrag = { [weak self] scrubberId in + // guard let self else { return } + // updateScrollPosition(position: scrubberId, callbackText:"onScrubberDrag") + // } /// will be called when the thumb move forward. carouselScrollBar.onMoveForward = { [weak self] scrubberId in @@ -467,7 +456,7 @@ open class Carousel: View { showPaginationControls() updateScrollPosition(position: carouselScrollBar.position, callbackText:"pageControlClicks") } - + func updateScrollPosition(position: Int, callbackText: String) { if carouselScrollBar.numberOfSlides > 0 { let contentOffsetWidth = scrollView.contentSize.width From 0a27c27189927b3731c52a85434889e983c67016 Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 14 Jun 2024 18:02:57 +0530 Subject: [PATCH 009/113] Digital ACT-191 ONEAPP-7013 story: add corner radius to slot, supporting to dynamic pagination inset --- VDS/Components/Carousel/Carousel.swift | 38 +++++++++++++++----------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 0486af1d..cf52506b 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -161,8 +161,9 @@ open class Carousel: View { open var paginationInset: CGFloat { get { return _paginationInset } set { - _paginationInset = newValue - setNeedsUpdate() + let minValue = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X + _paginationInset = (newValue < minValue) ? minValue : newValue + updatePaginationInset() } } @@ -180,7 +181,7 @@ open class Carousel: View { /// If provided, will set the alignment for slot content when the slots has different heights. open var slotAlignment: [CarouselSlotAlignmentModel] = [] { didSet { setNeedsUpdate() } } - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -254,7 +255,9 @@ open class Carousel: View { private var selectedGroupIndex: Int? { didSet { setNeedsUpdate() } } private var containerStackHeightConstraint: NSLayoutConstraint? private var containerViewHeightConstraint: NSLayoutConstraint? - + private var prevButtonLeadingConstraint: NSLayoutConstraint? + private var nextButtonTrailingConstraint: NSLayoutConstraint? + // The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile. let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X @@ -292,12 +295,12 @@ open class Carousel: View { // add pagination button icons scrollContainerView.addSubview(previousButton) previousButton - .pinLeading(paginationInset) + .pinLeadingGreaterThanOrEqualTo() .pinCenterY() scrollContainerView.addSubview(nextButton) nextButton - .pinTrailing(paginationInset) + .pinTrailingLessThanOrEqualTo() .pinCenterY() // add scroll container view & carousel scrollbar @@ -314,10 +317,9 @@ open class Carousel: View { addlisteners() } - + open override func updateView() { super.updateView() - carouselScrollBar.numberOfSlides = data.count carouselScrollBar.layout = _layout carouselScrollBar.position = (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) ? 1 : carouselScrollBar.position @@ -327,7 +329,7 @@ open class Carousel: View { if peek == .none { paginationDisplay = .persistent } - + containerViewHeightConstraint?.isActive = false containerStackHeightConstraint?.isActive = false updatePaginationControls() @@ -354,6 +356,7 @@ open class Carousel: View { .pinLeading(xPos) .width(minimumSlotWidth) .height(slotHeight) + carouselSlot.layer.cornerRadius = 12.0 xPos = xPos + minimumSlotWidth + gutter.value } scrollView.contentSize = CGSize(width: xPos - gutter.value, height: slotHeight) @@ -388,13 +391,7 @@ open class Carousel: View { func addlisteners() { nextButton.onClick = { _ in self.nextButtonClick() } previousButton.onClick = { _ in self.previousButtonClick() } - - //setup test page to show scrubber id was changed - // carouselScrollBar.onScrubberDrag = { [weak self] scrubberId in - // guard let self else { return } - // updateScrollPosition(position: scrubberId, callbackText:"onScrubberDrag") - // } - + /// will be called when the thumb move forward. carouselScrollBar.onMoveForward = { [weak self] scrubberId in guard let self else { return } @@ -431,6 +428,15 @@ open class Carousel: View { nextButton.surface = surface } + func updatePaginationInset() { + prevButtonLeadingConstraint?.isActive = false + nextButtonTrailingConstraint?.isActive = false + prevButtonLeadingConstraint = previousButton.leadingAnchor.constraint(equalTo: scrollContainerView.leadingAnchor, constant: paginationInset) + nextButtonTrailingConstraint = nextButton.trailingAnchor.constraint(equalTo: scrollContainerView.trailingAnchor, constant: -paginationInset) + prevButtonLeadingConstraint?.isActive = true + nextButtonTrailingConstraint?.isActive = true + } + func getSlotWidth() { let actualWidth = containerView.frame.size.width minimumSlotWidth = actualWidth - (CGFloat(layout.value) * gutter.value) From 993afcc367ff8da31643eac5c2b6215295101c4e Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 14 Jun 2024 20:32:22 +0530 Subject: [PATCH 010/113] updated to VDSCoreTokens --- VDS/Components/Carousel/Carousel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index cf52506b..4ad8ef8c 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -7,7 +7,7 @@ import Foundation import UIKit -import VDSTokens +import VDSCoreTokens import Combine /// A carousel is a collection of related content in a row that a customer can navigate through horizontally. From c00ddc68fd2d37c92641294241879a9df7746faf Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 24 Jun 2024 16:31:00 +0530 Subject: [PATCH 011/113] Digital ACT-191 ONEAPP-7013 story: changes to pagination inset --- VDS/Components/Carousel/Carousel.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 4ad8ef8c..6c091ce2 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -157,12 +157,11 @@ open class Carousel: View { } /// If provided, will apply margin to pagination arrows. Can be set to either positive or negative values. - /// The default value will be 12px in tablet and 8px in mobile. + /// The default value will be 12px in tablet and 8px in mobile. These values are the default in order to avoid overlapping content within the carousel. open var paginationInset: CGFloat { get { return _paginationInset } set { - let minValue = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X - _paginationInset = (newValue < minValue) ? minValue : newValue + _paginationInset = newValue updatePaginationInset() } } From 0b47d70352912b61b433ae44fe93841f63a8c210 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 24 Jun 2024 17:04:36 +0530 Subject: [PATCH 012/113] Digital ACT-191 ONEAPP-7013 story: changes to the default view of the carousel with one peek visible --- VDS/Components/Carousel/Carousel.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 6c091ce2..378289c7 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -441,7 +441,13 @@ open class Carousel: View { minimumSlotWidth = actualWidth - (CGFloat(layout.value) * gutter.value) switch peek { case .standard: - minimumSlotWidth = minimumSlotWidth - (3*peekMinimum) + // Supported for all Tablet viewports and layouts. + // Supported only for 1up layouts on Mobile viewports. + if UIDevice.isIPad { + minimumSlotWidth = minimumSlotWidth - (minimumSlotWidth/(CGFloat(layout.value) + 2)) + } else if layout == .oneUP { + minimumSlotWidth = minimumSlotWidth - (minimumSlotWidth/4) + } case .minimum: minimumSlotWidth = minimumSlotWidth - peekMinimum case .none: From 0840ed5ed31819f27012c79fdd0ef9be7c210aba Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 24 Jun 2024 18:42:07 +0530 Subject: [PATCH 013/113] Digital ACT-191 ONEAPP-7013 story: Adjust slot width if scrollbar suppressed and/or based on peek value --- VDS/Components/Carousel/Carousel.swift | 42 +++++++++++++++++--------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 378289c7..80c55b16 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -247,7 +247,7 @@ open class Carousel: View { internal var _paginationDisplay: PaginationDisplay = .none internal var _paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X internal var _gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX - internal var _peek: Peek = .none + internal var _peek: Peek = .standard internal var _numberOfSlides: Int = 1 private var _width: Width? = nil @@ -324,11 +324,17 @@ open class Carousel: View { carouselScrollBar.position = (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) ? 1 : carouselScrollBar.position carouselScrollBar.isHidden = (totalPositions() <= 1) ? true : false - // When peek is set to ‘none,’ pagination controls are automatically set to persistent. + // Mobile/Tablet layouts without peek - must show pagination controls. + // If peek is ‘none’, pagination controls should show. So set to persistent. if peek == .none { paginationDisplay = .persistent } + // Minimum (Mobile only) Supported only on Mobile viewports. If a user passes Minimum for tablet carousel, the peek reverts to Standard. + if UIDevice.isIPad && peek == .minimum { + peek = .standard + } + containerViewHeightConstraint?.isActive = false containerStackHeightConstraint?.isActive = false updatePaginationControls() @@ -438,20 +444,26 @@ open class Carousel: View { func getSlotWidth() { let actualWidth = containerView.frame.size.width - minimumSlotWidth = actualWidth - (CGFloat(layout.value) * gutter.value) - switch peek { - case .standard: - // Supported for all Tablet viewports and layouts. - // Supported only for 1up layouts on Mobile viewports. - if UIDevice.isIPad { - minimumSlotWidth = minimumSlotWidth - (minimumSlotWidth/(CGFloat(layout.value) + 2)) - } else if layout == .oneUP { - minimumSlotWidth = minimumSlotWidth - (minimumSlotWidth/4) + let isScrollbarSuppressed = data.count > 0 && layout.value == data.count + let isPeekMinimumOnTablet = UIDevice.isIPad && peek == .minimum + let isPeekNone: Bool = peek == .none + minimumSlotWidth = isScrollbarSuppressed || isPeekMinimumOnTablet || isPeekNone ? actualWidth - ((CGFloat(layout.value)-1) * gutter.value): actualWidth - (CGFloat(layout.value) * gutter.value) + if !isScrollbarSuppressed { + switch peek { + case .standard: + // Standard(Default) Peek - Supported for all Tablet viewports and layouts. Supported only for 1up layouts on Mobile viewports. + if UIDevice.isIPad { + minimumSlotWidth = minimumSlotWidth - (minimumSlotWidth/(CGFloat(layout.value) + 3)) + } else if layout == .oneUP { + minimumSlotWidth = minimumSlotWidth - (minimumSlotWidth/4) + } + case .minimum: + // Peek Mimumum Width: 24px from edge of container (at the default view of the carousel with one peek visible) + // Minimum (Mobile only) Supported only on Mobile viewports. If a user passes Minimum for tablet carousel, the peek reverts to Standard. + minimumSlotWidth = isPeekMinimumOnTablet ? minimumSlotWidth : minimumSlotWidth - peekMinimum + case .none: + break } - case .minimum: - minimumSlotWidth = minimumSlotWidth - peekMinimum - case .none: - break } minimumSlotWidth = minimumSlotWidth / CGFloat(layout.value) } From 9efd50a0fab9b2f1b2727534061537c5a80283ed Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 24 Jun 2024 19:57:15 +0530 Subject: [PATCH 014/113] Digital ACT-191 ONEAPP-9311 story: added new page for Input Stepper --- VDS.xcodeproj/project.pbxproj | 16 +++++++ .../InputStepper/InputStepper.swift | 31 +++++++++++++ .../InputStepper/InputStepperLog.txt | 44 +++++++++++++++++++ VDS/VDS.docc/VDS.md | 1 + 4 files changed, 92 insertions(+) create mode 100644 VDS/Components/InputStepper/InputStepper.swift create mode 100644 VDS/Components/InputStepper/InputStepperLog.txt diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index ff8a6735..f1912a04 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 180636C72C29B0A400C92D86 /* InputStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180636C62C29B0A400C92D86 /* InputStepper.swift */; }; + 180636C92C29B0DF00C92D86 /* InputStepperLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 180636C82C29B0DF00C92D86 /* InputStepperLog.txt */; }; 1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; }; 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; 1842B1DF2BECE28B0021AFCA /* CalendarDateViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1842B1DE2BECE28B0021AFCA /* CalendarDateViewCell.swift */; }; @@ -203,6 +205,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 180636C62C29B0A400C92D86 /* InputStepper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputStepper.swift; sourceTree = ""; }; + 180636C82C29B0DF00C92D86 /* InputStepperLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = InputStepperLog.txt; sourceTree = ""; }; 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = ""; }; 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = ""; }; 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; @@ -440,6 +444,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 180636C52C29B06200C92D86 /* InputStepper */ = { + isa = PBXGroup; + children = ( + 180636C62C29B0A400C92D86 /* InputStepper.swift */, + 180636C82C29B0DF00C92D86 /* InputStepperLog.txt */, + ); + path = InputStepper; + sourceTree = ""; + }; 1808BEBA2BA41B1D00129230 /* CarouselScrollbar */ = { isa = PBXGroup; children = ( @@ -660,6 +673,7 @@ EAC58C1F2BF127F000BA39FA /* DatePicker */, 186D13C92BBA8A3500986B53 /* DropdownSelect */, EA985BF3296C609E00F2FF2E /* Icon */, + 180636C52C29B06200C92D86 /* InputStepper */, EA3362412892EF700071C351 /* Label */, 44604AD529CE195300E62B51 /* Line */, EAD0688C2A55F801002E3A2D /* Loader */, @@ -1166,6 +1180,7 @@ EA3362072891E14D0071C351 /* VerizonNHGeDS-Regular.otf in Resources */, EA3362062891E14D0071C351 /* VerizonNHGeTX-Regular.otf in Resources */, EA3362052891E14D0071C351 /* VerizonNHGeDS-Bold.otf in Resources */, + 180636C92C29B0DF00C92D86 /* InputStepperLog.txt in Resources */, EAA5EEB928ECD24B003B3210 /* Icons.xcassets in Resources */, EAA5EEE428F5B855003B3210 /* VerizonNHGDS-Light.otf in Resources */, ); @@ -1209,6 +1224,7 @@ files = ( 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */, EA5E304C294CBDD00082B959 /* TileContainer.swift in Sources */, + 180636C72C29B0A400C92D86 /* InputStepper.swift in Sources */, EAF7F0A6289B0CE000B287F5 /* Resetable.swift in Sources */, EA985C2D296F03FE00F2FF2E /* TileletIconModels.swift in Sources */, EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */, diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift new file mode 100644 index 00000000..84651c85 --- /dev/null +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -0,0 +1,31 @@ +// +// InputStepper.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 24/06/24. +// + +import Foundation +import UIKit +import VDSCoreTokens +import Combine + +@objc(VDSInputStepper) +open class InputStepper: EntryFieldBase { + + //-------------------------------------------------- + // 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) + } + +} diff --git a/VDS/Components/InputStepper/InputStepperLog.txt b/VDS/Components/InputStepper/InputStepperLog.txt new file mode 100644 index 00000000..e8cd816b --- /dev/null +++ b/VDS/Components/InputStepper/InputStepperLog.txt @@ -0,0 +1,44 @@ + +MM/DD/YYYY +---------------- + +02/2024 +---------------- +- New component + +02/15/2024 +---------------- +- Added Border align: Inside to Anatomy +- Removed leadingText property values from States. +- Added Read-only to States. +- Created a section for Minimum width in Layout and spacing and updated the minWidth to 145px. +- Added trailingText spacing to Layout and spacing. +- Reduced space between Button Icons and text to 12px in Layout and spacing. +- Added top/bottom padding values to Layout and spacing. + +03/01/2024 +---------------- +- Removed Leading Text from “Content and other properties” section. + +03/20/2024 +---------------- +- Updated Anatomy artwork and items +- Added width and controlWidth to Configurations +- Updated Width under Layout and spacing to show layout examples + +04/12/2024 +---------------- +- Added a new configuration property (size) that includes large and small +- Reduced details from Anatomy page and added them to Configurations/Size +- Added Hit area, Small Input Stepper spacing properties to Layout and spacing +- Updated the Behavior page to display disabled button icon when value is at max and min + +04/29/2024 +---------------- +- Updated the Behavior page to display disabled button icon when value is at max and min + +05/10/2024 +---------------- +- Added helperTextPlacement property to Configurations +- Added Layout examples for right Helper Text placement in Layout and Spacing +- Added overflow examples in Overflow section of Layout and Spacing diff --git a/VDS/VDS.docc/VDS.md b/VDS/VDS.docc/VDS.md index d145ea0a..3cfe568d 100755 --- a/VDS/VDS.docc/VDS.md +++ b/VDS/VDS.docc/VDS.md @@ -32,6 +32,7 @@ Using the system allows designers and developers to collaborate more easily and - ``CheckboxGroup`` - ``DropdownSelect`` - ``Icon`` +- ``InputStepper`` - ``InputField`` - ``Label`` - ``Line`` From c51093cfaf3788e567c4ee29c560e5b104a9125b Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 28 Jun 2024 17:04:00 +0530 Subject: [PATCH 015/113] Digital ACT-191 ONEAPP-7013 story: update view as per aspect ratio --- VDS/Components/Carousel/Carousel.swift | 46 +++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 80c55b16..c5d0ab0e 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -355,6 +355,8 @@ open class Carousel: View { $0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1) } scrollView.addSubview(carouselSlot) + let size = ratioSize(for: minimumSlotWidth) + slotHeight = size.height carouselSlot .pinTop() .pinBottom() @@ -382,17 +384,51 @@ open class Carousel: View { } open override func reset() { - for subview in subviews { - for recognizer in subview.gestureRecognizers ?? [] { - subview.removeGestureRecognizer(recognizer) - } - } super.reset() + shouldUpdateView = false + aspectRatio = .ratio1x1 + layout = UIDevice.isIPad ? .threeUP : .oneUP + pagination = .init(kind: .lowContrast, floating: true) + paginationDisplay = .none + paginationInset = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X + gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX + peek = .standard + width = nil } //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- + private func ratioSize(for width: CGFloat) -> CGSize { + var height: CGFloat = width + + switch aspectRatio { + case .ratio1x1: + break; + case .ratio3x4: + height = (4 / 3) * width + case .ratio4x3: + height = (3 / 4) * width + case .ratio2x3: + height = (3 / 2) * width + case .ratio3x2: + height = (2 / 3) * width + case .ratio9x16: + height = (16 / 9) * width + case .ratio16x9: + height = (9 / 16) * width + case .ratio1x2: + height = (2 / 1) * width + case .ratio2x1: + height = (1 / 2) * width + + default: + break + } + + return CGSize(width: width, height: height) + } + func addlisteners() { nextButton.onClick = { _ in self.nextButtonClick() } previousButton.onClick = { _ in self.previousButtonClick() } From fa5051d258f21cc82f4a0da0c2690280819a3a7f Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 1 Jul 2024 10:32:06 +0530 Subject: [PATCH 016/113] Digital ACT-191 ONEAPP-7013 story: flexible width support, Carousel can be set to any pixel or percentage --- VDS/Components/Carousel/Carousel.swift | 124 +++++++++++++++---------- 1 file changed, 74 insertions(+), 50 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index c5d0ab0e..5b5c99bf 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -79,7 +79,7 @@ open class Carousel: View { // MARK: - Public Properties //-------------------------------------------------- /// Aspect-ratio options for tilelet in the carousel. If 'none' is passed, the tilelet will take the height of the tallest item in the carousel. - open var aspectRatio: Tilelet.AspectRatio = .ratio1x1 { didSet { setNeedsUpdate() } } + open var aspectRatio: Tilelet.AspectRatio = .none { didSet { setNeedsUpdate() } } /// Data used to render tilelets in the carousel. open var data: [Any] = [] { didSet { setNeedsUpdate() } } @@ -92,10 +92,13 @@ open class Carousel: View { switch newValue { case .percentage(let percentage): if percentage >= 10 && percentage <= 100.0 { - _width = newValue + let expectedWidth = safeAreaLayoutGuide.layoutFrame.size.width * (percentage/100) + if expectedWidth > carouselScrollbarMinWidth { + _width = newValue + } } case .value(let value): - if value > minimumSlotWidth { /*(size.minimumSlotWidth)*/ + if value > carouselScrollbarMinWidth { _width = newValue } } @@ -256,6 +259,7 @@ open class Carousel: View { private var containerViewHeightConstraint: NSLayoutConstraint? private var prevButtonLeadingConstraint: NSLayoutConstraint? private var nextButtonTrailingConstraint: NSLayoutConstraint? + private var containerLeadingConstraint: NSLayoutConstraint? // The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile. let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X @@ -263,6 +267,7 @@ open class Carousel: View { var slotHeight = 100.0 var peekMinimum = 24.0 var minimumSlotWidth = 0.0 + var carouselScrollbarMinWidth = 96.0 //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -279,11 +284,12 @@ open class Carousel: View { containerView .pinTop() .pinBottom() - .pinLeading() + .pinLeadingGreaterThanOrEqualTo() .pinTrailing() .heightGreaterThanEqualTo(containerSize.height) - containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - + containerView.centerYAnchor.constraint(equalTo: centerYAnchor).activate() + containerLeadingConstraint = containerView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 0) + // add content stackview containerView.addSubview(contentStackView) @@ -312,13 +318,30 @@ open class Carousel: View { .pinLeading() .pinTrailing() .heightGreaterThanEqualTo(containerSize.height) - contentStackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() addlisteners() } open override func updateView() { super.updateView() + + if containerView.frame.size.width > 0 { + if let width { + containerLeadingConstraint?.deactivate() + switch width { + case .value(let value): + var expectedWidth = value + let fullWidth = safeAreaLayoutGuide.layoutFrame.size.width + expectedWidth = expectedWidth > fullWidth ? fullWidth : expectedWidth + containerLeadingConstraint?.constant = safeAreaLayoutGuide.layoutFrame.size.width - expectedWidth + case .percentage(let percentage): + let expectedWidth = safeAreaLayoutGuide.layoutFrame.size.width * (percentage/100) + containerLeadingConstraint?.constant = safeAreaLayoutGuide.layoutFrame.size.width - expectedWidth + } + containerLeadingConstraint?.activate() + } + } + carouselScrollBar.numberOfSlides = data.count carouselScrollBar.layout = _layout carouselScrollBar.position = (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) ? 1 : carouselScrollBar.position @@ -335,58 +358,59 @@ open class Carousel: View { peek = .standard } - containerViewHeightConstraint?.isActive = false - containerStackHeightConstraint?.isActive = false updatePaginationControls() getSlotWidth() - - // perform a loop to iterate each subView - scrollView.subviews.forEach { subView in - // removing subView from its parent view - subView.removeFromSuperview() - } - - // add carousel items - if data.count > 0 { - var xPos = 0.0 - for x in 0...data.count - 1 { - let carouselSlot = View().with { - $0.clipsToBounds = true - $0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1) - } - scrollView.addSubview(carouselSlot) - let size = ratioSize(for: minimumSlotWidth) - slotHeight = size.height - carouselSlot - .pinTop() - .pinBottom() - .pinLeading(xPos) - .width(minimumSlotWidth) - .height(slotHeight) - carouselSlot.layer.cornerRadius = 12.0 - xPos = xPos + minimumSlotWidth + gutter.value + + if containerView.frame.size.width > 0 { + containerViewHeightConstraint?.isActive = false + containerStackHeightConstraint?.isActive = false + + // perform a loop to iterate each subView + scrollView.subviews.forEach { subView in + // removing subView from its parent view + subView.removeFromSuperview() } - scrollView.contentSize = CGSize(width: xPos - gutter.value, height: slotHeight) + + // add carousel items + if data.count > 0 { + var xPos = 0.0 + for x in 0...data.count - 1 { + let carouselSlot = View().with { + $0.clipsToBounds = true + $0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1) + } + scrollView.addSubview(carouselSlot) + let size = ratioSize(for: minimumSlotWidth) + slotHeight = size.height + carouselSlot + .pinTop() + .pinBottom() + .pinLeading(xPos) + .width(minimumSlotWidth) + .height(slotHeight) + carouselSlot.layer.cornerRadius = 12.0 + xPos = xPos + minimumSlotWidth + gutter.value + } + scrollView.contentSize = CGSize(width: xPos - gutter.value, height: slotHeight) + } + + let containerHeight = slotHeight + scrollbarTopSpace + containerSize.height + if carouselScrollBar.isHidden { + containerStackHeightConstraint = contentStackView.heightAnchor.constraint(equalToConstant: slotHeight) + containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: slotHeight) + } else { + containerStackHeightConstraint = contentStackView.heightAnchor.constraint(equalToConstant: containerHeight) + containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: containerHeight) + } + containerViewHeightConstraint?.isActive = true + containerStackHeightConstraint?.isActive = true } - - let containerHeight = slotHeight + scrollbarTopSpace + containerSize.height - if carouselScrollBar.isHidden { - containerStackHeightConstraint = contentStackView.heightAnchor.constraint(equalToConstant: slotHeight) - containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: slotHeight) - } else { - containerStackHeightConstraint = contentStackView.heightAnchor.constraint(equalToConstant: containerHeight) - containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: containerHeight) - } - containerViewHeightConstraint?.isActive = true - containerStackHeightConstraint?.isActive = true - - layoutIfNeeded() } open override func reset() { super.reset() shouldUpdateView = false - aspectRatio = .ratio1x1 + aspectRatio = .none layout = UIDevice.isIPad ? .threeUP : .oneUP pagination = .init(kind: .lowContrast, floating: true) paginationDisplay = .none From 6f5e94887e6de1f480ff95e7abe442516e177ac0 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 1 Jul 2024 14:13:44 +0530 Subject: [PATCH 017/113] activating leading constratint --- VDS/Components/Carousel/Carousel.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 5b5c99bf..e461bc99 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -289,6 +289,7 @@ open class Carousel: View { .heightGreaterThanEqualTo(containerSize.height) containerView.centerYAnchor.constraint(equalTo: centerYAnchor).activate() containerLeadingConstraint = containerView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 0) + containerLeadingConstraint?.activate() // add content stackview containerView.addSubview(contentStackView) From f13451ea37fb80aea10823fc4b86eb1ce42b2303 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 1 Jul 2024 15:03:00 +0530 Subject: [PATCH 018/113] Digital ACT-191 ONEAPP-7013 story: Standard Peek supported for all Tablet viewports and layouts and for 1up layouts on Mobile viewports only. --- VDS/Components/Carousel/Carousel.swift | 46 +++++++++++++++----------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index e461bc99..0347efc0 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -358,10 +358,34 @@ open class Carousel: View { if UIDevice.isIPad && peek == .minimum { peek = .standard } + + // Standard(Default) Peek - Supported for all Tablet viewports and layouts. Supported only for 1up layouts on Mobile viewports. + if peek == .standard && !UIDevice.isIPad && layout != CarouselScrollbar.Layout.oneUP { + peek = .minimum + } updatePaginationControls() getSlotWidth() - + addCarouselSlots() + } + + open override func reset() { + super.reset() + shouldUpdateView = false + aspectRatio = .none + layout = UIDevice.isIPad ? .threeUP : .oneUP + pagination = .init(kind: .lowContrast, floating: true) + paginationDisplay = .none + paginationInset = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X + gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX + peek = .standard + width = nil + } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + private func addCarouselSlots() { if containerView.frame.size.width > 0 { containerViewHeightConstraint?.isActive = false containerStackHeightConstraint?.isActive = false @@ -408,22 +432,6 @@ open class Carousel: View { } } - open override func reset() { - super.reset() - shouldUpdateView = false - aspectRatio = .none - layout = UIDevice.isIPad ? .threeUP : .oneUP - pagination = .init(kind: .lowContrast, floating: true) - paginationDisplay = .none - paginationInset = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X - gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX - peek = .standard - width = nil - } - - //-------------------------------------------------- - // MARK: - Private Methods - //-------------------------------------------------- private func ratioSize(for width: CGFloat) -> CGSize { var height: CGFloat = width @@ -543,10 +551,10 @@ open class Carousel: View { func updateScrollPosition(position: Int, callbackText: String) { if carouselScrollBar.numberOfSlides > 0 { - let contentOffsetWidth = scrollView.contentSize.width + let scrollContentSizeWidth = scrollView.contentSize.width let totalPositions = totalPositions() let multiplier: Float = (position == 1) ? 0 : Float((position)-1) / Float(totalPositions) - let xPos = (position == totalPositions) ? (contentOffsetWidth - containerView.frame.size.width) : CGFloat(Float(contentOffsetWidth) * multiplier) + let xPos = (position == totalPositions) ? (scrollContentSizeWidth - containerView.frame.size.width) : CGFloat(Float(scrollContentSizeWidth) * multiplier) carouselScrollBar.scrubberId = position let yPos = scrollView.contentOffset.y scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true) From 668b20f77b8942f76a29f3a0ca06b02fc4fe00f7 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Tue, 2 Jul 2024 13:22:39 +0530 Subject: [PATCH 019/113] Digital ACT-191 ONEAPP-7013 story: Updating scrollbar position on scrolling carousel content --- VDS/Components/Carousel/Carousel.swift | 57 ++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 0347efc0..498e1dc5 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -365,7 +365,6 @@ open class Carousel: View { } updatePaginationControls() - getSlotWidth() addCarouselSlots() } @@ -386,6 +385,7 @@ open class Carousel: View { // MARK: - Private Methods //-------------------------------------------------- private func addCarouselSlots() { + getSlotWidth() if containerView.frame.size.width > 0 { containerViewHeightConstraint?.isActive = false containerStackHeightConstraint?.isActive = false @@ -405,6 +405,7 @@ open class Carousel: View { $0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1) } scrollView.addSubview(carouselSlot) + scrollView.delegate = self let size = ratioSize(for: minimumSlotWidth) slotHeight = size.height carouselSlot @@ -553,15 +554,49 @@ open class Carousel: View { if carouselScrollBar.numberOfSlides > 0 { let scrollContentSizeWidth = scrollView.contentSize.width let totalPositions = totalPositions() - let multiplier: Float = (position == 1) ? 0 : Float((position)-1) / Float(totalPositions) - let xPos = (position == totalPositions) ? (scrollContentSizeWidth - containerView.frame.size.width) : CGFloat(Float(scrollContentSizeWidth) * multiplier) - carouselScrollBar.scrubberId = position + var xPos = 0.0 + if position == 1 { + xPos = 0.0 + } else if position == totalPositions { + xPos = scrollContentSizeWidth - containerView.frame.size.width + } else { + let isScrollbarSuppressed = data.count > 0 && layout.value == data.count + let isPeekMinimumOnTablet = UIDevice.isIPad && peek == .minimum + if !isScrollbarSuppressed { + let subpart = minimumSlotWidth + gutter.value + let xPosition = CGFloat( Float(position-1) * Float(layout.value) * Float(subpart)) + switch peek { + case .standard: + if UIDevice.isIPad { + xPos = xPosition - (minimumSlotWidth/(CGFloat(layout.value) + 3))/2 + } else if layout == .oneUP { + xPos = xPosition - gutter.value - (minimumSlotWidth/4)/2 + } + case .minimum: + xPos = isPeekMinimumOnTablet ? xPosition : xPosition - peekMinimum/2 + case .none: + xPos = xPosition + } + } + } + carouselScrollBar.scrubberId = position+1 let yPos = scrollView.contentOffset.y scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true) showPaginationControls() } } + func updateScrollbarPosition(targetContentOffsetXPos:CGFloat) { + let scrollContentSizeWidth = scrollView.contentSize.width + let totalPositions = totalPositions() + let layoutSpace = Int (floor( Double(scrollContentSizeWidth / Double(totalPositions)))) + let remindSpace = Int(targetContentOffsetXPos) % layoutSpace + var contentPos = (Int(targetContentOffsetXPos) / layoutSpace) + 1 + contentPos = remindSpace > layoutSpace/2 ? contentPos+1 : contentPos + carouselScrollBar.position = contentPos + updateScrollPosition(position: contentPos, callbackText: "ScrollViewMoved") + } + private func totalPositions() -> Int { return Int (ceil (Double(carouselScrollBar.numberOfSlides) / Double(_layout.value))) } @@ -576,3 +611,17 @@ open class Carousel: View { } } } + +extension Carousel: UIScrollViewDelegate { + //-------------------------------------------------- + // MARK: - UIScrollView Delegate + //-------------------------------------------------- + public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + updateScrollbarPosition(targetContentOffsetXPos: targetContentOffset.pointee.x) + } + public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + var visibleRect = CGRect() + visibleRect.origin = scrollView.contentOffset + updateScrollbarPosition(targetContentOffsetXPos: visibleRect.origin.x) + } +} From 8619c641096645cf2b80bf8a288cecb93030074b Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Tue, 2 Jul 2024 14:47:20 +0530 Subject: [PATCH 020/113] Digital ACT-191 ONEAPP-7013 story: refactored code --- VDS/Components/Carousel/Carousel.swift | 247 +++++++++++++------------ 1 file changed, 126 insertions(+), 121 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 498e1dc5..dbc10d98 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -183,13 +183,11 @@ open class Carousel: View { /// If provided, will set the alignment for slot content when the slots has different heights. open var slotAlignment: [CarouselSlotAlignmentModel] = [] { didSet { setNeedsUpdate() } } - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - // Sizes are from InVision design specs. - internal var containerSize: CGSize { CGSize(width: 320, height: 44) } - + internal var containerSize: CGSize { CGSize(width: frame.size.width, height: 44) } private let contentStackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .vertical @@ -260,7 +258,7 @@ open class Carousel: View { private var prevButtonLeadingConstraint: NSLayoutConstraint? private var nextButtonTrailingConstraint: NSLayoutConstraint? private var containerLeadingConstraint: NSLayoutConstraint? - + // The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile. let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X @@ -268,18 +266,21 @@ open class Carousel: View { var peekMinimum = 24.0 var minimumSlotWidth = 0.0 var carouselScrollbarMinWidth = 96.0 + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- + /// Executed on initialization for this View. open override func initialSetup() { super.initialSetup() } + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() isAccessibilityElement = false - // add containerView + // Add containerView addSubview(containerView) containerView .pinTop() @@ -287,18 +288,19 @@ open class Carousel: View { .pinLeadingGreaterThanOrEqualTo() .pinTrailing() .heightGreaterThanEqualTo(containerSize.height) + containerView.centerYAnchor.constraint(equalTo: centerYAnchor).activate() containerLeadingConstraint = containerView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 0) containerLeadingConstraint?.activate() - - // add content stackview + + // Add content stackview containerView.addSubview(contentStackView) - // add scrollview + // Add scrollview scrollContainerView.addSubview(scrollView) scrollView.pinToSuperView() - // add pagination button icons + // Add pagination button icons scrollContainerView.addSubview(previousButton) previousButton .pinLeadingGreaterThanOrEqualTo() @@ -309,7 +311,7 @@ open class Carousel: View { .pinTrailingLessThanOrEqualTo() .pinCenterY() - // add scroll container view & carousel scrollbar + // Add scroll container view & carousel scrollbar contentStackView.addArrangedSubview(scrollContainerView) contentStackView.addArrangedSubview(carouselScrollBar) contentStackView.setCustomSpacing(scrollbarTopSpace, after: scrollContainerView) @@ -322,7 +324,8 @@ open class Carousel: View { addlisteners() } - + + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() @@ -353,7 +356,7 @@ open class Carousel: View { if peek == .none { paginationDisplay = .persistent } - + // Minimum (Mobile only) Supported only on Mobile viewports. If a user passes Minimum for tablet carousel, the peek reverts to Standard. if UIDevice.isIPad && peek == .minimum { peek = .standard @@ -363,11 +366,12 @@ open class Carousel: View { if peek == .standard && !UIDevice.isIPad && layout != CarouselScrollbar.Layout.oneUP { peek = .minimum } - + updatePaginationControls() addCarouselSlots() } + /// Resets to default settings. open override func reset() { super.reset() shouldUpdateView = false @@ -384,19 +388,69 @@ open class Carousel: View { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- + private func addlisteners() { + nextButton.onClick = { _ in self.nextButtonClick() } + previousButton.onClick = { _ in self.previousButtonClick() } + + /// Will be called when the thumb move forward. + carouselScrollBar.onMoveForward = { [weak self] scrubberId in + guard let self else { return } + updateScrollPosition(position: scrubberId, callbackText:"onMoveForward") + } + + /// Will be called when the thumb move backward. + carouselScrollBar.onMoveBackward = { [weak self] scrubberId in + guard let self else { return } + updateScrollPosition(position: scrubberId, callbackText:"onMoveBackward") + } + + /// Will be called when the thumb touch start. + carouselScrollBar.onThumbTouchStart = { [weak self] scrubberId in + guard let self else { return } + updateScrollPosition(position: scrubberId, callbackText:"onThumbTouchStart") + } + + /// Will be called when the thumb touch end. + carouselScrollBar.onThumbTouchEnd = { [weak self] scrubberId in + guard let self else { return } + updateScrollPosition(position: scrubberId, callbackText:"onThumbTouchEnd") + } + } + + private func updatePaginationControls() { + containerView.surface = surface + showPaginationControls() + previousButton.kind = pagination.kind + previousButton.floating = pagination.floating + nextButton.kind = pagination.kind + nextButton.floating = pagination.floating + previousButton.surface = surface + nextButton.surface = surface + } + + private func showPaginationControls() { + if carouselScrollBar.numberOfSlides == _layout.value { + previousButton.isHidden = true + nextButton.isHidden = true + } else { + previousButton.isHidden = (carouselScrollBar.position == 1) || (paginationDisplay == .none) + nextButton.isHidden = (carouselScrollBar.position == totalPositions()) || (paginationDisplay == .none) + } + } + private func addCarouselSlots() { getSlotWidth() if containerView.frame.size.width > 0 { containerViewHeightConstraint?.isActive = false containerStackHeightConstraint?.isActive = false - // perform a loop to iterate each subView + // Perform a loop to iterate each subView scrollView.subviews.forEach { subView in - // removing subView from its parent view + // Removing subView from its parent view subView.removeFromSuperview() } - // add carousel items + // Add carousel items if data.count > 0 { var xPos = 0.0 for x in 0...data.count - 1 { @@ -433,86 +487,7 @@ open class Carousel: View { } } - private func ratioSize(for width: CGFloat) -> CGSize { - var height: CGFloat = width - - switch aspectRatio { - case .ratio1x1: - break; - case .ratio3x4: - height = (4 / 3) * width - case .ratio4x3: - height = (3 / 4) * width - case .ratio2x3: - height = (3 / 2) * width - case .ratio3x2: - height = (2 / 3) * width - case .ratio9x16: - height = (16 / 9) * width - case .ratio16x9: - height = (9 / 16) * width - case .ratio1x2: - height = (2 / 1) * width - case .ratio2x1: - height = (1 / 2) * width - - default: - break - } - - return CGSize(width: width, height: height) - } - - func addlisteners() { - nextButton.onClick = { _ in self.nextButtonClick() } - previousButton.onClick = { _ in self.previousButtonClick() } - - /// will be called when the thumb move forward. - carouselScrollBar.onMoveForward = { [weak self] scrubberId in - guard let self else { return } - updateScrollPosition(position: scrubberId, callbackText:"onMoveForward") - } - - /// will be called when the thumb move backward. - carouselScrollBar.onMoveBackward = { [weak self] scrubberId in - guard let self else { return } - updateScrollPosition(position: scrubberId, callbackText:"onMoveBackward") - } - - /// will be called when the thumb touch start. - carouselScrollBar.onThumbTouchStart = { [weak self] scrubberId in - guard let self else { return } - updateScrollPosition(position: scrubberId, callbackText:"onThumbTouchStart") - } - - /// will be called when the thumb touch end. - carouselScrollBar.onThumbTouchEnd = { [weak self] scrubberId in - guard let self else { return } - updateScrollPosition(position: scrubberId, callbackText:"onThumbTouchEnd") - } - } - - func updatePaginationControls() { - containerView.surface = surface - showPaginationControls() - previousButton.kind = pagination.kind - previousButton.floating = pagination.floating - nextButton.kind = pagination.kind - nextButton.floating = pagination.floating - previousButton.surface = surface - nextButton.surface = surface - } - - func updatePaginationInset() { - prevButtonLeadingConstraint?.isActive = false - nextButtonTrailingConstraint?.isActive = false - prevButtonLeadingConstraint = previousButton.leadingAnchor.constraint(equalTo: scrollContainerView.leadingAnchor, constant: paginationInset) - nextButtonTrailingConstraint = nextButton.trailingAnchor.constraint(equalTo: scrollContainerView.trailingAnchor, constant: -paginationInset) - prevButtonLeadingConstraint?.isActive = true - nextButtonTrailingConstraint?.isActive = true - } - - func getSlotWidth() { + private func getSlotWidth() { let actualWidth = containerView.frame.size.width let isScrollbarSuppressed = data.count > 0 && layout.value == data.count let isPeekMinimumOnTablet = UIDevice.isIPad && peek == .minimum @@ -538,19 +513,69 @@ open class Carousel: View { minimumSlotWidth = minimumSlotWidth / CGFloat(layout.value) } - func nextButtonClick() { + private func nextButtonClick() { carouselScrollBar.position = carouselScrollBar.position+1 showPaginationControls() updateScrollPosition(position: carouselScrollBar.position, callbackText:"pageControlClicks") } - func previousButtonClick() { + private func previousButtonClick() { carouselScrollBar.position = carouselScrollBar.position-1 showPaginationControls() updateScrollPosition(position: carouselScrollBar.position, callbackText:"pageControlClicks") } - func updateScrollPosition(position: Int, callbackText: String) { + private func ratioSize(for width: CGFloat) -> CGSize { + var height: CGFloat = width + + switch aspectRatio { + case .ratio1x1: + break; + case .ratio3x4: + height = (4 / 3) * width + case .ratio4x3: + height = (3 / 4) * width + case .ratio2x3: + height = (3 / 2) * width + case .ratio3x2: + height = (2 / 3) * width + case .ratio9x16: + height = (16 / 9) * width + case .ratio16x9: + height = (9 / 16) * width + case .ratio1x2: + height = (2 / 1) * width + case .ratio2x1: + height = (1 / 2) * width + + default: + break + } + + return CGSize(width: width, height: height) + } + + private func updatePaginationInset() { + prevButtonLeadingConstraint?.isActive = false + nextButtonTrailingConstraint?.isActive = false + prevButtonLeadingConstraint = previousButton.leadingAnchor.constraint(equalTo: scrollContainerView.leadingAnchor, constant: paginationInset) + nextButtonTrailingConstraint = nextButton.trailingAnchor.constraint(equalTo: scrollContainerView.trailingAnchor, constant: -paginationInset) + prevButtonLeadingConstraint?.isActive = true + nextButtonTrailingConstraint?.isActive = true + } + + private func updateScrollbarPosition(targetContentOffsetXPos:CGFloat) { + let scrollContentSizeWidth = scrollView.contentSize.width + let totalPositions = totalPositions() + let layoutSpace = Int (floor( Double(scrollContentSizeWidth / Double(totalPositions)))) + let remindSpace = Int(targetContentOffsetXPos) % layoutSpace + var contentPos = (Int(targetContentOffsetXPos) / layoutSpace) + 1 + contentPos = remindSpace > layoutSpace/2 ? contentPos+1 : contentPos + carouselScrollBar.position = contentPos + updateScrollPosition(position: contentPos, callbackText: "ScrollViewMoved") + } + + private func updateScrollPosition(position: Int, callbackText: String) { if carouselScrollBar.numberOfSlides > 0 { let scrollContentSizeWidth = scrollView.contentSize.width let totalPositions = totalPositions() @@ -586,30 +611,9 @@ open class Carousel: View { } } - func updateScrollbarPosition(targetContentOffsetXPos:CGFloat) { - let scrollContentSizeWidth = scrollView.contentSize.width - let totalPositions = totalPositions() - let layoutSpace = Int (floor( Double(scrollContentSizeWidth / Double(totalPositions)))) - let remindSpace = Int(targetContentOffsetXPos) % layoutSpace - var contentPos = (Int(targetContentOffsetXPos) / layoutSpace) + 1 - contentPos = remindSpace > layoutSpace/2 ? contentPos+1 : contentPos - carouselScrollBar.position = contentPos - updateScrollPosition(position: contentPos, callbackText: "ScrollViewMoved") - } - private func totalPositions() -> Int { return Int (ceil (Double(carouselScrollBar.numberOfSlides) / Double(_layout.value))) } - - func showPaginationControls() { - if carouselScrollBar.numberOfSlides == _layout.value { - previousButton.isHidden = true - nextButton.isHidden = true - } else { - previousButton.isHidden = (carouselScrollBar.position == 1) || (paginationDisplay == .none) - nextButton.isHidden = (carouselScrollBar.position == totalPositions()) || (paginationDisplay == .none) - } - } } extension Carousel: UIScrollViewDelegate { @@ -619,6 +623,7 @@ extension Carousel: UIScrollViewDelegate { public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { updateScrollbarPosition(targetContentOffsetXPos: targetContentOffset.pointee.x) } + public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { var visibleRect = CGRect() visibleRect.origin = scrollView.contentOffset From 77288e4c547cf8a2248912ec5bce506c62fbc604 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Thu, 4 Jul 2024 20:24:33 +0530 Subject: [PATCH 021/113] Digital ACT-191 ONEAPP-7013 story: slot alignment and rendering data --- VDS.xcodeproj/project.pbxproj | 10 +- VDS/Components/Carousel/Carousel.swift | 157 +++++++++++++++--- .../Carousel/CarouselRenderItemStyle.swift | 33 ++++ .../Carousel/CarouselSlotAlignmentModel.swift | 14 +- .../Carousel/CarouselSlotItemModel.swift | 31 ++++ 5 files changed, 212 insertions(+), 33 deletions(-) create mode 100644 VDS/Components/Carousel/CarouselRenderItemStyle.swift create mode 100644 VDS/Components/Carousel/CarouselSlotItemModel.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 4c72a920..64ec60bb 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 18013CED2C355BF900907F18 /* CarouselSlotItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18013CEC2C355BF900907F18 /* CarouselSlotItemModel.swift */; }; + 18013CEF2C355C5200907F18 /* CarouselRenderItemStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18013CEE2C355C5200907F18 /* CarouselRenderItemStyle.swift */; }; 1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; }; 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; 1842B1DF2BECE28B0021AFCA /* CalendarDateViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1842B1DE2BECE28B0021AFCA /* CalendarDateViewCell.swift */; }; @@ -19,11 +21,9 @@ 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A012B96E848006602CC /* Breadcrumbs.swift */; }; 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A032B96F050006602CC /* BreadcrumbItem.swift */; }; 18AE87502C06FDA60075F181 /* Carousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18AE874F2C06FDA60075F181 /* Carousel.swift */; }; - 18AE87542C06FE610075F181 /* CarouselChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */; }; 18B42AC62C09D197008D6262 /* CarouselSlotAlignmentModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */; }; 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; 18B9763F2C11BA4A009271DF /* CarouselPaginationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */; }; - 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */; }; 18FEA1B52BE0E63600A56439 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */; }; 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; @@ -209,6 +209,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 18013CEC2C355BF900907F18 /* CarouselSlotItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselSlotItemModel.swift; sourceTree = ""; }; + 18013CEE2C355C5200907F18 /* CarouselRenderItemStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselRenderItemStyle.swift; sourceTree = ""; }; 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = ""; }; 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = ""; }; 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; @@ -502,6 +504,8 @@ 18AE874F2C06FDA60075F181 /* Carousel.swift */, 18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */, 18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */, + 18013CEC2C355BF900907F18 /* CarouselSlotItemModel.swift */, + 18013CEE2C355C5200907F18 /* CarouselRenderItemStyle.swift */, 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */, ); path = Carousel; @@ -1363,6 +1367,7 @@ EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */, EAF7F0AB289B13FD00B287F5 /* TextStyleLabelAttribute.swift in Sources */, 18AE87502C06FDA60075F181 /* Carousel.swift in Sources */, + 18013CEF2C355C5200907F18 /* CarouselRenderItemStyle.swift in Sources */, EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */, EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */, EA985BE629688F6A00F2FF2E /* TileletBadgeModel.swift in Sources */, @@ -1405,6 +1410,7 @@ EA596ABF2A16B4F500300C4B /* Tabs.swift in Sources */, EAD062A72A3B67770015965D /* UIView+CALayer.swift in Sources */, EAD068942A560C13002E3A2D /* LoaderLaunchable.swift in Sources */, + 18013CED2C355BF900907F18 /* CarouselSlotItemModel.swift in Sources */, 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */, EA985BEC2968A91200F2FF2E /* TitleLockupTitleModel.swift in Sources */, 5FC35BE328D51405004EBEAC /* Button.swift in Sources */, diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index dbc10d98..a3cb1f1c 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -33,21 +33,6 @@ open class Carousel: View { //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- - /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. - public enum Gutter: String, CaseIterable { - case twelvePX = "12px" - case twentyFourPX = "24px" - - var value: CGFloat { - switch self { - case .twelvePX: - VDSLayout.space3X - case .twentyFourPX: - VDSLayout.space6X - } - } - } - /// Enum used to describe the pagination display for this component. public enum PaginationDisplay: String, CaseIterable { case persistent, none @@ -75,6 +60,21 @@ open class Carousel: View { case value(CGFloat) } + /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. + public enum Gutter: String, CaseIterable { + case twelvePX = "12px" + case twentyFourPX = "24px" + + var value: CGFloat { + switch self { + case .twelvePX: + VDSLayout.space3X + case .twentyFourPX: + VDSLayout.space6X + } + } + } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @@ -123,11 +123,12 @@ open class Carousel: View { get { return _layout } set { _layout = newValue + carouselScrollBar.position = 0 setNeedsUpdate() } } - /// A callback when moving the carousel. Returns event object and selectedGroupIndex. + /// A callback when moving the carousel. Returns initial visible slide's index in the carousel. open var onChange: ((Int) -> Void)? { get { nil } set { @@ -182,7 +183,42 @@ open class Carousel: View { open var selectedIndex: Int? { didSet { setNeedsUpdate() } } /// If provided, will set the alignment for slot content when the slots has different heights. - open var slotAlignment: [CarouselSlotAlignmentModel] = [] { didSet { setNeedsUpdate() } } + open var slotAlignment: CarouselSlotAlignmentModel? { + get { return _slotAlignment } + set { + if let newValue { + _slotAlignment = newValue + } else { + _slotAlignment = nil + } + setNeedsUpdate() + } + } + + /// Render item style. If provided, the slot gets the background, width, height, border-radius. + open var renderItemStyle: CarouselRenderItemStyle? { + get { return _renderItemStyle } + set { + if let newValue { + _renderItemStyle = newValue + } else { + _renderItemStyle = nil + } + setNeedsUpdate() + } + } + + /// Render item. It passes a data array object and expects the styled component to apply in return. + open var renderItem: CarouselSlotItemModel? { + get { _renderItem } + set { + if let newValue { + _renderItem = newValue + } else { + _renderItem = nil + } + } + } //-------------------------------------------------- // MARK: - Private Properties @@ -250,9 +286,12 @@ open class Carousel: View { internal var _gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX internal var _peek: Peek = .standard internal var _numberOfSlides: Int = 1 + internal var _slotAlignment: CarouselSlotAlignmentModel? = nil + internal var _renderItemStyle: CarouselRenderItemStyle? = nil + internal var _renderItem: CarouselSlotItemModel? = nil private var _width: Width? = nil - private var selectedGroupIndex: Int? { didSet { setNeedsUpdate() } } + private var selectedGroupIndex: Int? = nil private var containerStackHeightConstraint: NSLayoutConstraint? private var containerViewHeightConstraint: NSLayoutConstraint? private var prevButtonLeadingConstraint: NSLayoutConstraint? @@ -323,6 +362,7 @@ open class Carousel: View { .heightGreaterThanEqualTo(containerSize.height) addlisteners() + updatePaginationInset() } /// Used to make changes to the View based off a change events or from local properties. @@ -348,7 +388,9 @@ open class Carousel: View { carouselScrollBar.numberOfSlides = data.count carouselScrollBar.layout = _layout - carouselScrollBar.position = (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) ? 1 : carouselScrollBar.position + if (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) { + carouselScrollBar.position = 1 + } carouselScrollBar.isHidden = (totalPositions() <= 1) ? true : false // Mobile/Tablet layouts without peek - must show pagination controls. @@ -392,31 +434,32 @@ open class Carousel: View { nextButton.onClick = { _ in self.nextButtonClick() } previousButton.onClick = { _ in self.previousButtonClick() } - /// Will be called when the thumb move forward. + /// Will be called when the scrollbar thumb move forward. carouselScrollBar.onMoveForward = { [weak self] scrubberId in guard let self else { return } updateScrollPosition(position: scrubberId, callbackText:"onMoveForward") } - /// Will be called when the thumb move backward. + /// Will be called when the scrollbar thumb move backward. carouselScrollBar.onMoveBackward = { [weak self] scrubberId in guard let self else { return } updateScrollPosition(position: scrubberId, callbackText:"onMoveBackward") } - /// Will be called when the thumb touch start. + /// Will be called when the scrollbar thumb touch start. carouselScrollBar.onThumbTouchStart = { [weak self] scrubberId in guard let self else { return } updateScrollPosition(position: scrubberId, callbackText:"onThumbTouchStart") } - /// Will be called when the thumb touch end. + /// Will be called when the scrollbar thumb touch end. carouselScrollBar.onThumbTouchEnd = { [weak self] scrubberId in guard let self else { return } updateScrollPosition(position: scrubberId, callbackText:"onThumbTouchEnd") } } + // Update pagination buttons with selected surface, kind, floating values private func updatePaginationControls() { containerView.surface = surface showPaginationControls() @@ -428,6 +471,7 @@ open class Carousel: View { nextButton.surface = surface } + // Show/Hide pagination buttons of Carousel based on First or Middle or Last private func showPaginationControls() { if carouselScrollBar.numberOfSlides == _layout.value { previousButton.isHidden = true @@ -438,6 +482,7 @@ open class Carousel: View { } } + // Add carousel slots and load data if any private func addCarouselSlots() { getSlotWidth() if containerView.frame.size.width > 0 { @@ -454,6 +499,8 @@ open class Carousel: View { if data.count > 0 { var xPos = 0.0 for x in 0...data.count - 1 { + + // Add Carousel Slot let carouselSlot = View().with { $0.clipsToBounds = true $0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1) @@ -470,6 +517,29 @@ open class Carousel: View { .height(slotHeight) carouselSlot.layer.cornerRadius = 12.0 xPos = xPos + minimumSlotWidth + gutter.value + + // Add subview for content to Carousel Slot + let contentView = View().with { + $0.clipsToBounds = true + $0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1) + } + carouselSlot.addSubview(contentView) + + // Add received component + let item : CarouselSlotItemModel = .init(style: renderItemStyle, component: data[x] as? UIView) + if let component = item.component { + if slotAlignment != nil { + setSlotAlignment(contentView: contentView, parentView: carouselSlot) + } else { + contentView.pinToSuperView() + } + contentView.addSubview(component) + component.pinToSuperView() + contentView.layer.cornerRadius = component.layer.cornerRadius + if var surfacedView = component as? Surfaceable { + surfacedView.surface = surface + } + } } scrollView.contentSize = CGSize(width: xPos - gutter.value, height: slotHeight) } @@ -487,6 +557,37 @@ open class Carousel: View { } } + // Set slot alignment if provided. Used only when slot content have different heights or widths. + private func setSlotAlignment(contentView: View, parentView: View) { + parentView.backgroundColor = .clear + switch slotAlignment?.vertical { + case .top: + contentView.topAnchor.constraint(equalTo: parentView.topAnchor).activate() + break + case .middle: + contentView.centerYAnchor.constraint(equalTo: parentView.centerYAnchor).activate() + break + case .bottom: + contentView.bottomAnchor.constraint(equalTo: parentView.bottomAnchor).activate() + break + default: break + } + + switch slotAlignment?.horizontal { + case .left: + contentView.leadingAnchor.constraint(equalTo: parentView.leadingAnchor).activate() + break + case .center: + contentView.centerXAnchor.constraint(equalTo: parentView.centerXAnchor).activate() + break + case .right: + parentView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).activate() + break + default: break + } + } + + // Get the slot width relative to the peak private func getSlotWidth() { let actualWidth = containerView.frame.size.width let isScrollbarSuppressed = data.count > 0 && layout.value == data.count @@ -505,7 +606,7 @@ open class Carousel: View { case .minimum: // Peek Mimumum Width: 24px from edge of container (at the default view of the carousel with one peek visible) // Minimum (Mobile only) Supported only on Mobile viewports. If a user passes Minimum for tablet carousel, the peek reverts to Standard. - minimumSlotWidth = isPeekMinimumOnTablet ? minimumSlotWidth : minimumSlotWidth - peekMinimum + minimumSlotWidth = isPeekMinimumOnTablet ? minimumSlotWidth : minimumSlotWidth - peekMinimum - gutter.value case .none: break } @@ -525,6 +626,7 @@ open class Carousel: View { updateScrollPosition(position: carouselScrollBar.position, callbackText:"pageControlClicks") } + // The size of slot depends on the selected aspect ratio private func ratioSize(for width: CGFloat) -> CGSize { var height: CGFloat = width @@ -575,6 +677,7 @@ open class Carousel: View { updateScrollPosition(position: contentPos, callbackText: "ScrollViewMoved") } + // Update scrollview offset relative to scrollbar thumb position private func updateScrollPosition(position: Int, callbackText: String) { if carouselScrollBar.numberOfSlides > 0 { let scrollContentSizeWidth = scrollView.contentSize.width @@ -598,7 +701,7 @@ open class Carousel: View { xPos = xPosition - gutter.value - (minimumSlotWidth/4)/2 } case .minimum: - xPos = isPeekMinimumOnTablet ? xPosition : xPosition - peekMinimum/2 + xPos = isPeekMinimumOnTablet ? xPosition : xPosition - peekMinimum case .none: xPos = xPosition } @@ -608,9 +711,13 @@ open class Carousel: View { let yPos = scrollView.contentOffset.y scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true) showPaginationControls() + selectedIndex = ((position-1) * layout.value) + 1 + onChangePublisher.send(selectedIndex ?? 1) + selectedGroupIndex = position } } + // Get the overall positions of the carousel scrollbar relative to the slides and selected layout private func totalPositions() -> Int { return Int (ceil (Double(carouselScrollBar.numberOfSlides) / Double(_layout.value))) } diff --git a/VDS/Components/Carousel/CarouselRenderItemStyle.swift b/VDS/Components/Carousel/CarouselRenderItemStyle.swift new file mode 100644 index 00000000..c447dfa1 --- /dev/null +++ b/VDS/Components/Carousel/CarouselRenderItemStyle.swift @@ -0,0 +1,33 @@ +// +// CarouselRenderItemStyle.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 30/06/24. +// + +import Foundation +import UIKit +import VDSCoreTokens + +/// A custom data type that holds the style props if provided any. +public struct CarouselRenderItemStyle { + + /// BackgroundColor for slot + public let backgroundColor: String? + + /// Height for slot + public var height: CGFloat? + + /// BorderRadius for slot + public var borderRadius: CGFloat? + + /// Width for slot + public var width: CGFloat? + + public init(backgroundColor: String?, height: CGFloat?, width: CGFloat?, borderRadius: CGFloat?) { + self.backgroundColor = backgroundColor + self.height = height + self.borderRadius = borderRadius ?? 12.0 + self.width = width + } +} diff --git a/VDS/Components/Carousel/CarouselSlotAlignmentModel.swift b/VDS/Components/Carousel/CarouselSlotAlignmentModel.swift index 5a8ec661..ee1d491b 100644 --- a/VDS/Components/Carousel/CarouselSlotAlignmentModel.swift +++ b/VDS/Components/Carousel/CarouselSlotAlignmentModel.swift @@ -7,17 +7,19 @@ import Foundation -/// Custom data type for slotAlignment prop for 'Carousel' component. +/// Custom data type for the SlotAlignment prop for the 'carousel' component. extension Carousel { + + /// Used only when slot content have different heights or widths. public struct CarouselSlotAlignmentModel { - /// Text that shown to an indicator for legend - public var vertical: String + /// Used for vertical alignment of slot alignment. + public var vertical: Carousel.Vertical - /// Date to an indicator - public var horizontal: String + /// Used for horizontal alignment of slot alignment. + public var horizontal: Carousel.Horizontal - public init(vertical: String, horizontal: String) { + public init(vertical: Carousel.Vertical, horizontal: Carousel.Horizontal) { self.vertical = vertical self.horizontal = horizontal } diff --git a/VDS/Components/Carousel/CarouselSlotItemModel.swift b/VDS/Components/Carousel/CarouselSlotItemModel.swift new file mode 100644 index 00000000..3830b29f --- /dev/null +++ b/VDS/Components/Carousel/CarouselSlotItemModel.swift @@ -0,0 +1,31 @@ +// +// CarouselSlotItemModel.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 30/06/24. +// + +import Foundation +import UIKit +import VDSCoreTokens + +/// A custom data type that holds the style and component for a slot of the 'Carousel' component. +public struct CarouselSlotItemModel { + + /// Style props if provided any + public var style: CarouselRenderItemStyle? + + /// Component to be show on Carousel slot + public var component: UIView? + + public init(style: CarouselRenderItemStyle? = nil, component: UIView? = nil) { + self.style = style + self.component = component + if let color = style?.backgroundColor { + self.component?.backgroundColor = .init(hexString: color) + } + if let borderRadius = style?.borderRadius { + self.component?.layer.cornerRadius = borderRadius + } + } +} From 548984d499d943c439fb36b2c2665b1e6759c9b4 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 12 Jul 2024 13:43:26 +0530 Subject: [PATCH 022/113] Digital ACT-191 ONEAPP-9311 story: added properites --- .../InputStepper/InputStepper.swift | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 84651c85..d9b4b6b8 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -10,6 +10,7 @@ import UIKit import VDSCoreTokens import Combine +/// A stepper is a two-segment control that people use to increase or decrease an incremental value. @objc(VDSInputStepper) open class InputStepper: EntryFieldBase { @@ -28,4 +29,141 @@ open class InputStepper: EntryFieldBase { super.init(coder: coder) } + //-------------------------------------------------- + // MARK: - Enums + //-------------------------------------------------- + + /// Enum used to describe the control width of Input Stepper. + public enum controlWidth: String, CaseIterable { + case auto, value + } + + /// Enum used to describe the size of Input Stepper. + public enum Size: String, CaseIterable { + case large, small + } + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + /// Accepts a string or number value to control the width of input stepper to a fixed pixel or percentage value. + open var controlWidth: CGFloat? { didSet { setNeedsUpdate() } } + + /// Maximum value of the input stepper, defaults to '99'. + open var maxValue: Int? { + get { return _maxValue } + set { + if let newValue, newValue < 100 && newValue > 0 { + _maxValue = newValue + } else { + _maxValue = 99 + } + setNeedsUpdate() + } + } + + /// Minimum value of the input stepper, defaults to '0'. + open var minValue: Int? { + get { return _minValue } + set { + if let newValue, newValue >= 0 && newValue < 99 { + _minValue = newValue + } else { + _minValue = 0 + } + setNeedsUpdate() + } + } + + /// The size of the input stepper. Defaults to 'large'. + open var size: Size { + get { return _size } + set { + _size = newValue + setNeedsUpdate() + } + } + + /// Accepts any text or character to appear next to input stepper value. + open var trailingText: String? { didSet { setNeedsUpdate() } } + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + internal var _maxValue: Int = 99 + internal var _minValue: Int = 0 + internal var _size: Size = .large + + private var stepperWidthConstraint: NSLayoutConstraint? + private var stepperHeightConstraint: NSLayoutConstraint? + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func initialSetup() { + super.initialSetup() + } + + open override func setup() { + super.setup() + isAccessibilityElement = false + accessibilityLabel = "Input Stepper" + + containerView.isEnabled = false + } + + open override func getFieldContainer() -> UIView { + // stackview for controls in EntryFieldBase.controlContainerView + let controlStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .horizontal + $0.spacing = VDSLayout.space3X + $0.backgroundColor = .clear + } + let view = View().with { + $0.clipsToBounds = true + $0.backgroundColor = .clear + } + + let controlView = View().with { + $0.clipsToBounds = true + $0.backgroundColor = .clear + } + + let label = Label().with { + $0.text = "Label" + } + + let minusButton = ButtonIcon().with { + $0.kind = .ghost + $0.iconName = .leftCaret + $0.iconOffset = .init(x: -2, y: 0) + $0.customContainerSize = 40 + $0.icon.customSize = 16 + } + + let plusButton = ButtonIcon().with { + $0.kind = .ghost + $0.iconName = .rightCaret + $0.iconOffset = .init(x: 2, y: 0) + $0.customContainerSize = 40 + $0.icon.customSize = 16 + } + + controlStackView.addArrangedSubview(minusButton) + controlStackView.addArrangedSubview(label) + controlStackView.addArrangedSubview(plusButton) + controlView.addSubview(controlStackView) + controlStackView.pinToSuperView() + + view.addSubview(controlView) + controlView.pinToSuperView() + view.pinToSuperView() + return view + } + + open override func updateView() { + super.updateView() + updateContainerView(flag: false) + } } From f95317eb24b0006fcc545c9881c23da52046d506 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 12 Jul 2024 14:36:09 -0500 Subject: [PATCH 023/113] added var to protocol Signed-off-by: Matt Bruce --- VDS/Protocols/FormFieldable.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/VDS/Protocols/FormFieldable.swift b/VDS/Protocols/FormFieldable.swift index 8a620765..82666772 100644 --- a/VDS/Protocols/FormFieldable.swift +++ b/VDS/Protocols/FormFieldable.swift @@ -20,6 +20,9 @@ public protocol FormFieldable { /// Protocol for FormFieldable that require internal validation. public protocol FormFieldInternalValidatable: FormFieldable, Errorable { + /// Rules that drive the validator + var rules: [AnyRule] { get set } + /// Is there an internalError var hasInternalError: Bool { get } /// Internal Error Message that will show. From 95f4ad06a1a9772704dccf87c9710086d4564e7a Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 12 Jul 2024 14:36:27 -0500 Subject: [PATCH 024/113] refactored updating of rules Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index cf2dffa2..78c1077a 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -356,6 +356,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { /// Updates the UI open override func updateView() { super.updateView() + updateRules() updateContainerView() updateContainerWidth() updateTitleLabel() @@ -418,7 +419,6 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { } open func validate(){ - updateRules() validator = FormFieldValidator(field: self, rules: rules) validator?.validate() setNeedsUpdate() From 4ca74fa445f8cacb99523538b7c5b2646f65ed52 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 12 Jul 2024 14:36:37 -0500 Subject: [PATCH 025/113] dynamically add rules Signed-off-by: Matt Bruce --- VDS/Components/TextFields/TextArea/TextArea.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index f8a9a6b3..8768352e 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -111,6 +111,8 @@ open class TextArea: EntryFieldBase { } didSet { + setNeedsUpdate() + if textView.isFirstResponder { validate() } @@ -191,8 +193,9 @@ open class TextArea: EntryFieldBase { override func updateRules() { super.updateRules() - - rules.append(.init(countRule)) + if let maxLength, maxLength > 0 { + rules.append(.init(countRule)) + } } open override func getFieldContainer() -> UIView { From 17f9240c7e9c8f200c9b5953b877e84d8a74db8a Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 15 Jul 2024 15:05:57 +0530 Subject: [PATCH 026/113] Digital ACT-191 ONEAPP-9311 story: added size configuration property that includes large and small --- .../InputStepper/InputStepper.swift | 146 +++++++++++------- 1 file changed, 91 insertions(+), 55 deletions(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index d9b4b6b8..71a3c766 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -32,28 +32,30 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- - - /// Enum used to describe the control width of Input Stepper. - public enum controlWidth: String, CaseIterable { - case auto, value - } - /// Enum used to describe the size of Input Stepper. public enum Size: String, CaseIterable { case large, small } - + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - /// Accepts a string or number value to control the width of input stepper to a fixed pixel or percentage value. - open var controlWidth: CGFloat? { didSet { setNeedsUpdate() } } + open var controlWidth: String? { + get { _controlWidth } + set { + setControlWidth(newValue) + setNeedsUpdate() + } + } + + /// Default value of the input stepper, defaults to '0'. + open var defaultValue:Int = 0 { didSet { setNeedsUpdate() } } /// Maximum value of the input stepper, defaults to '99'. open var maxValue: Int? { get { return _maxValue } set { - if let newValue, newValue < 100 && newValue > 0 { + if let newValue, newValue <= 99 && newValue > 0 { _maxValue = newValue } else { _maxValue = 99 @@ -80,7 +82,7 @@ open class InputStepper: EntryFieldBase { get { return _size } set { _size = newValue - setNeedsUpdate() + updateSize() } } @@ -90,13 +92,38 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + internal var _controlWidth = "auto" internal var _maxValue: Int = 99 internal var _minValue: Int = 0 internal var _size: Size = .large - - private var stepperWidthConstraint: NSLayoutConstraint? - private var stepperHeightConstraint: NSLayoutConstraint? + private var largeMinWidth = 121 + private var smallMinWidth = 90 + + let decrementButton = ButtonIcon().with { + $0.kind = .ghost + $0.iconName = Icon.Name(name: "minus") + $0.iconOffset = .init(x: -2, y: 0) + $0.customContainerSize = 32 + $0.icon.customSize = 16 + $0.backgroundColor = .clear + } + + let incrementButton = ButtonIcon().with { + $0.kind = .ghost + $0.iconName = Icon.Name(name: "plus") + $0.iconOffset = .init(x: 2, y: 0) + $0.customContainerSize = 32 + $0.icon.customSize = 16 + $0.backgroundColor = .clear + } + + let textLabel = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.textStyle = .boldBodyLarge + $0.backgroundColor = .clear + } + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -108,8 +135,7 @@ open class InputStepper: EntryFieldBase { super.setup() isAccessibilityElement = false accessibilityLabel = "Input Stepper" - - containerView.isEnabled = false + containerView.isEnabled = false } open override func getFieldContainer() -> UIView { @@ -120,50 +146,60 @@ open class InputStepper: EntryFieldBase { $0.spacing = VDSLayout.space3X $0.backgroundColor = .clear } - let view = View().with { - $0.clipsToBounds = true - $0.backgroundColor = .clear - } - let controlView = View().with { - $0.clipsToBounds = true - $0.backgroundColor = .clear - } - - let label = Label().with { - $0.text = "Label" - } - - let minusButton = ButtonIcon().with { - $0.kind = .ghost - $0.iconName = .leftCaret - $0.iconOffset = .init(x: -2, y: 0) - $0.customContainerSize = 40 - $0.icon.customSize = 16 - } - - let plusButton = ButtonIcon().with { - $0.kind = .ghost - $0.iconName = .rightCaret - $0.iconOffset = .init(x: 2, y: 0) - $0.customContainerSize = 40 - $0.icon.customSize = 16 - } - - controlStackView.addArrangedSubview(minusButton) - controlStackView.addArrangedSubview(label) - controlStackView.addArrangedSubview(plusButton) - controlView.addSubview(controlStackView) - controlStackView.pinToSuperView() - - view.addSubview(controlView) - controlView.pinToSuperView() - view.pinToSuperView() - return view + controlStackView.addArrangedSubview(decrementButton) + controlStackView.addArrangedSubview(textLabel) + controlStackView.addArrangedSubview(incrementButton) + return controlStackView } open override func updateView() { super.updateView() updateContainerView(flag: false) + textLabel.text = String(defaultValue) + " " + (trailingText ?? "") + decrementButton.surface = surface + incrementButton.surface = surface + textLabel.surface = surface + statusIcon.isHidden = true } + + /// Resets to default settings. + open override func reset() { + super.reset() + textLabel.reset() + textLabel.textStyle = .boldBodyLarge + textLabel.text = "" + controlWidth = nil + minValue = nil + maxValue = nil + trailingText = nil + helperTextPlacement = .bottom + } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + internal func updateSize() { + let value = size == .large ? 6.0 : VDSLayout.space1X + updateConstraintsToFieldStackView(value: value) + +// textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall +// textLabel.heightAnchor.constraint(equalToConstant: size == .large ? 44 : 32).activate() + +// decrementButton.customContainerSize = size == .large ? 32 : 24 +// incrementButton.customContainerSize = size == .large ? 32 : 24 + + } + + internal func setControlWidth(_ text: String?) { + if let text, text == "auto" { + // Set fixed width relative to default value, trailing text label + } else if let controlWidth = Int(text ?? "") { + // Use provided new width either pixel or percentage + width = CGFloat(controlWidth) + } else { + // Use EntryFieldBase width + } + } + } From 6c26655cbb45d4c15db3ef389466137ff8f569d1 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 15 Jul 2024 15:06:57 +0530 Subject: [PATCH 027/113] Digital ACT-191 ONEAPP-9311 story: added actions to decrement and increment buttons. --- .../InputStepper/InputStepper.swift | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 71a3c766..8cfe49a4 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -135,7 +135,10 @@ open class InputStepper: EntryFieldBase { super.setup() isAccessibilityElement = false accessibilityLabel = "Input Stepper" - containerView.isEnabled = false + containerView.isEnabled = false + + decrementButton.onClick = { _ in self.decrementButtonClick() } + incrementButton.onClick = { _ in self.incrementButtonClick() } } open override func getFieldContainer() -> UIView { @@ -160,6 +163,7 @@ open class InputStepper: EntryFieldBase { decrementButton.surface = surface incrementButton.surface = surface textLabel.surface = surface + updateButtonStates() statusIcon.isHidden = true } @@ -179,6 +183,27 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- + + internal func decrementButtonClick() { + defaultValue = defaultValue - 1 + updateButtonStates() + } + + internal func incrementButtonClick() { + defaultValue = defaultValue + 1 + updateButtonStates() + } + + internal func updateButtonStates() { + if isReadOnly || !isEnabled { + decrementButton.isEnabled = false + incrementButton.isEnabled = false + } else { + decrementButton.isEnabled = defaultValue > _minValue ? true : false + incrementButton.isEnabled = defaultValue < _maxValue ? true : false + } + } + internal func updateSize() { let value = size == .large ? 6.0 : VDSLayout.space1X updateConstraintsToFieldStackView(value: value) From 29d00ec352e99b8f149109a1944aa68ee8330c3e Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 15 Jul 2024 15:08:32 +0530 Subject: [PATCH 028/113] Digital ACT-191 ONEAPP-9311 story: update color configurations to stepper text for default and disabled states --- VDS/Components/InputStepper/InputStepper.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 8cfe49a4..752937ba 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -123,7 +123,13 @@ open class InputStepper: EntryFieldBase { $0.textStyle = .boldBodyLarge $0.backgroundColor = .clear } - + + //-------------------------------------------------- + // MARK: - Configuration + //-------------------------------------------------- + private var labelColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight , VDSColor.elementsPrimaryOndark) + private var labelDisabledColorConfiguration = SurfaceColorConfiguration(VDSColor.interactiveDisabledOnlight , VDSColor.interactiveDisabledOndark) + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -195,6 +201,7 @@ open class InputStepper: EntryFieldBase { } internal func updateButtonStates() { + textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable() if isReadOnly || !isEnabled { decrementButton.isEnabled = false incrementButton.isEnabled = false From 161f690488085e8187e9e4782a141b21b83ffe27 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 15 Jul 2024 15:37:26 +0530 Subject: [PATCH 029/113] Digital ACT-191 ONEAPP-9311 story: Check the default value and update it to max or min on button icon presses. --- .../InputStepper/InputStepper.swift | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 752937ba..d630d1d1 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -47,7 +47,7 @@ open class InputStepper: EntryFieldBase { setNeedsUpdate() } } - + /// Default value of the input stepper, defaults to '0'. open var defaultValue:Int = 0 { didSet { setNeedsUpdate() } } @@ -76,7 +76,7 @@ open class InputStepper: EntryFieldBase { setNeedsUpdate() } } - + /// The size of the input stepper. Defaults to 'large'. open var size: Size { get { return _size } @@ -88,7 +88,7 @@ open class InputStepper: EntryFieldBase { /// Accepts any text or character to appear next to input stepper value. open var trailingText: String? { didSet { setNeedsUpdate() } } - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -142,7 +142,6 @@ open class InputStepper: EntryFieldBase { isAccessibilityElement = false accessibilityLabel = "Input Stepper" containerView.isEnabled = false - decrementButton.onClick = { _ in self.decrementButtonClick() } incrementButton.onClick = { _ in self.incrementButtonClick() } } @@ -155,7 +154,6 @@ open class InputStepper: EntryFieldBase { $0.spacing = VDSLayout.space3X $0.backgroundColor = .clear } - controlStackView.addArrangedSubview(decrementButton) controlStackView.addArrangedSubview(textLabel) controlStackView.addArrangedSubview(incrementButton) @@ -169,8 +167,8 @@ open class InputStepper: EntryFieldBase { decrementButton.surface = surface incrementButton.surface = surface textLabel.surface = surface - updateButtonStates() statusIcon.isHidden = true + updateButtonStates() } /// Resets to default settings. @@ -189,14 +187,19 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- + internal func checkDefaultValue() { + defaultValue = defaultValue > _maxValue ? _maxValue : defaultValue < _minValue ? _minValue : defaultValue + } internal func decrementButtonClick() { defaultValue = defaultValue - 1 + checkDefaultValue() updateButtonStates() } internal func incrementButtonClick() { defaultValue = defaultValue + 1 + checkDefaultValue() updateButtonStates() } @@ -215,12 +218,12 @@ open class InputStepper: EntryFieldBase { let value = size == .large ? 6.0 : VDSLayout.space1X updateConstraintsToFieldStackView(value: value) -// textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall -// textLabel.heightAnchor.constraint(equalToConstant: size == .large ? 44 : 32).activate() - -// decrementButton.customContainerSize = size == .large ? 32 : 24 -// incrementButton.customContainerSize = size == .large ? 32 : 24 - + // textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall + // textLabel.heightAnchor.constraint(equalToConstant: size == .large ? 44 : 32).activate() + + // decrementButton.customContainerSize = size == .large ? 32 : 24 + // incrementButton.customContainerSize = size == .large ? 32 : 24 + } internal func setControlWidth(_ text: String?) { @@ -233,5 +236,5 @@ open class InputStepper: EntryFieldBase { // Use EntryFieldBase width } } - + } From 699626a4b821cffed1f1f2328a4cc13e8e74d3ef Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 15 Jul 2024 08:43:19 -0500 Subject: [PATCH 030/113] updated in the didChange Signed-off-by: Matt Bruce --- VDS/Components/TextFields/InputField/InputField.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index e1609f96..fd36ecac 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -364,11 +364,11 @@ extension InputField: UITextFieldDelegate { open func textFieldDidChangeSelection(_ textField: UITextField) { fieldType.handler().textFieldDidChangeSelection(self, textField: textField) + text = textField.text + sendActions(for: .valueChanged) if fieldType.handler().validateOnChange { validate() } - sendActions(for: .valueChanged) - setNeedsUpdate() } open func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { From 10f4f1db1186e005e15a01732ae858cd9041a953 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 15 Jul 2024 08:43:27 -0500 Subject: [PATCH 031/113] added new validator Signed-off-by: Matt Bruce --- .../InputField/FieldTypes/Telephone.swift | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/VDS/Components/TextFields/InputField/FieldTypes/Telephone.swift b/VDS/Components/TextFields/InputField/FieldTypes/Telephone.swift index 89417c19..88a52723 100644 --- a/VDS/Components/TextFields/InputField/FieldTypes/Telephone.swift +++ b/VDS/Components/TextFields/InputField/FieldTypes/Telephone.swift @@ -10,6 +10,35 @@ import UIKit extension InputField { + public class TelephoneNumberValidator: Rule, Withable { + public var format: String + public var errorMessage: String = "Please enter a valid telephone number" + + public init(format: String) { + self.format = format + } + + public func isValid(value: String?) -> Bool { + guard let value, !value.isEmpty else { return true } + let regex = createRegex(from: format) + let predicate = NSPredicate(format: "SELF MATCHES %@", regex) + let valid = predicate.evaluate(with: value) + return valid + } + + private func createRegex(from format: String) -> String { + // Escape special regex characters in the format string + let escapedFormat = NSRegularExpression.escapedPattern(for: format) + + // Replace placeholder characters with regex patterns + let regex = escapedFormat + .replacingOccurrences(of: "X", with: "\\d") + + return "^" + regex + "$" + } + } + + class TelephoneHandler: FieldTypeHandler { static let shared = TelephoneHandler() @@ -25,14 +54,7 @@ extension InputField { } override func appendRules(_ inputField: InputField) { - if let text = inputField.textField.text, text.count > 0 { - let rule = CharacterCountRule().copyWith { - $0.maxLength = "XXX-XXX-XXXX".count - $0.compareType = .equals - $0.errorMessage = "Enter a valid telephone." - } - inputField.rules.append(.init(rule)) - } + inputField.rules.append(.init(TelephoneNumberValidator(format: "XXX-XXX-XXXX"))) } override func textField(_ inputField: InputField, textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { @@ -62,6 +84,8 @@ extension InputField { textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition) } + value = formattedNumber + // Prevent the default behavior return false @@ -71,6 +95,7 @@ extension InputField { if let text = inputField.text { let rawNumber = text.filter { $0.isNumber } textField.text = formatUSNumber(rawNumber) + value = textField.text } } From 6661e603333738fd661d96d0b3740df44ba82ccb Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 16 Jul 2024 16:17:27 -0500 Subject: [PATCH 032/113] abstract to String extension method Signed-off-by: Matt Bruce --- .../InputField/FieldTypes/Telephone.swift | 71 ++++++++++--------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/VDS/Components/TextFields/InputField/FieldTypes/Telephone.swift b/VDS/Components/TextFields/InputField/FieldTypes/Telephone.swift index 88a52723..10255be6 100644 --- a/VDS/Components/TextFields/InputField/FieldTypes/Telephone.swift +++ b/VDS/Components/TextFields/InputField/FieldTypes/Telephone.swift @@ -71,7 +71,7 @@ extension InputField { let rawNumber = newText.filter { $0.isNumber } // Format the number with dashes - let formattedNumber = formatUSNumber(rawNumber) + let formattedNumber = rawNumber.formatUSNumber() // Set the formatted text textField.text = formattedNumber @@ -93,44 +93,45 @@ extension InputField { override func textFieldDidEndEditing(_ inputField: InputField, textField: UITextField) { if let text = inputField.text { - let rawNumber = text.filter { $0.isNumber } - textField.text = formatUSNumber(rawNumber) + textField.text = text.formatUSNumber() value = textField.text } } - - func formatUSNumber(_ number: String) -> String { - // Format the number in the style XXX-XXX-XXXX - let areaCodeLength = 3 - let centralOfficeCodeLength = 3 - let lineNumberLength = 4 - - var formattedNumber = "" - - if number.count > 0 { - formattedNumber.append(contentsOf: number.prefix(areaCodeLength)) - } - - if number.count > areaCodeLength { - let startIndex = number.index(number.startIndex, offsetBy: areaCodeLength) - let endIndex = number.index(startIndex, offsetBy: min(centralOfficeCodeLength, number.count - areaCodeLength)) - let centralOfficeCode = number[startIndex.. areaCodeLength + centralOfficeCodeLength { - let startIndex = number.index(number.startIndex, offsetBy: areaCodeLength + centralOfficeCodeLength) - let endIndex = number.index(startIndex, offsetBy: min(lineNumberLength, number.count - areaCodeLength - centralOfficeCodeLength)) - let lineNumber = number[startIndex.. String { + // Format the number in the style XXX-XXX-XXXX + let areaCodeLength = 3 + let centralOfficeCodeLength = 3 + let lineNumberLength = 4 + + var formattedNumber = "" + let number = filter { $0.isNumber } + + if number.count > 0 { + formattedNumber.append(contentsOf: number.prefix(areaCodeLength)) + } + + if number.count > areaCodeLength { + let startIndex = number.index(number.startIndex, offsetBy: areaCodeLength) + let endIndex = number.index(startIndex, offsetBy: min(centralOfficeCodeLength, number.count - areaCodeLength)) + let centralOfficeCode = number[startIndex.. areaCodeLength + centralOfficeCodeLength { + let startIndex = number.index(number.startIndex, offsetBy: areaCodeLength + centralOfficeCodeLength) + let endIndex = number.index(startIndex, offsetBy: min(lineNumberLength, number.count - areaCodeLength - centralOfficeCodeLength)) + let lineNumber = number[startIndex.. Date: Wed, 17 Jul 2024 18:32:04 +0530 Subject: [PATCH 033/113] Digital ACT-191 ONEAPP-9311 story: changes about PR notes --- VDS/Components/Carousel/Carousel.swift | 193 +++++------------- .../Carousel/CarouselSlotItemModel.swift | 4 +- 2 files changed, 58 insertions(+), 139 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index a3cb1f1c..90b35588 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -78,14 +78,11 @@ open class Carousel: View { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - /// Aspect-ratio options for tilelet in the carousel. If 'none' is passed, the tilelet will take the height of the tallest item in the carousel. - open var aspectRatio: Tilelet.AspectRatio = .none { didSet { setNeedsUpdate() } } - - /// Data used to render tilelets in the carousel. - open var data: [Any] = [] { didSet { setNeedsUpdate() } } + /// views used to render view in the carousel slots. + open var views: [UIView] = [] { didSet { setNeedsUpdate() } } /// If provided, width of slots will be rendered based on this value. If omitted, default widths are rendered. - open var width : Width? { + open var width: Width? { get { _width } set { if let newValue { @@ -110,19 +107,11 @@ open class Carousel: View { } /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. - open var gutter: Gutter { - get { return _gutter } - set { - _gutter = newValue - setNeedsUpdate() - } - } + open var gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX { didSet { setNeedsUpdate() } } /// The amount of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. - open var layout: CarouselScrollbar.Layout { - get { return _layout } - set { - _layout = newValue + open var layout: CarouselScrollbar.Layout = UIDevice.isIPad ? .threeUP : .oneUP { + didSet { carouselScrollBar.position = 0 setNeedsUpdate() } @@ -143,82 +132,29 @@ open class Carousel: View { } /// Config object for pagination. - open var pagination: CarouselPaginationModel { - get { return _pagination } - set { - _pagination = newValue - setNeedsUpdate() - } - } + open var pagination: CarouselPaginationModel = .init(kind: .lowContrast, floating: true) { didSet {setNeedsUpdate() } } /// If provided, will determine the conditions to render the pagination arrows. - open var paginationDisplay: PaginationDisplay { - get { return _paginationDisplay } - set { - _paginationDisplay = newValue - setNeedsUpdate() - } - } + open var paginationDisplay: PaginationDisplay = .none { didSet {setNeedsUpdate() } } /// If provided, will apply margin to pagination arrows. Can be set to either positive or negative values. /// The default value will be 12px in tablet and 8px in mobile. These values are the default in order to avoid overlapping content within the carousel. - open var paginationInset: CGFloat { - get { return _paginationInset } - set { - _paginationInset = newValue - updatePaginationInset() - } - } + open var paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X { didSet { updatePaginationInset() } } /// Options for user to configure the partially-visible tile in group. Setting peek to 'none' will display arrow navigation icons on mobile devices. - open var peek: Peek { - get { return _peek } - set { - _peek = newValue - setNeedsUpdate() - } - } + open var peek: Peek = .standard { didSet { setNeedsUpdate() } } /// The initial visible slide's index in the carousel. open var selectedIndex: Int? { didSet { setNeedsUpdate() } } /// If provided, will set the alignment for slot content when the slots has different heights. - open var slotAlignment: CarouselSlotAlignmentModel? { - get { return _slotAlignment } - set { - if let newValue { - _slotAlignment = newValue - } else { - _slotAlignment = nil - } - setNeedsUpdate() - } - } + open var slotAlignment: CarouselSlotAlignmentModel? = nil { didSet { setNeedsUpdate() } } /// Render item style. If provided, the slot gets the background, width, height, border-radius. - open var renderItemStyle: CarouselRenderItemStyle? { - get { return _renderItemStyle } - set { - if let newValue { - _renderItemStyle = newValue - } else { - _renderItemStyle = nil - } - setNeedsUpdate() - } - } + open var renderItemStyle: CarouselRenderItemStyle? = nil { didSet { setNeedsUpdate() } } - /// Render item. It passes a data array object and expects the styled component to apply in return. - open var renderItem: CarouselSlotItemModel? { - get { _renderItem } - set { - if let newValue { - _renderItem = newValue - } else { - _renderItem = nil - } - } - } +// /// Render item. It passes a data array object and expects the styled component to apply in return. +// open var renderItem: CarouselSlotItemModel? = nil { didSet { setNeedsUpdate() } } //-------------------------------------------------- // MARK: - Private Properties @@ -278,18 +214,6 @@ open class Carousel: View { /// A publisher for when the carousel moves. Passes parameters (data). open var onScrollPublisher = PassthroughSubject, Never>() private var onScrollCancellable: AnyCancellable? - - internal var _layout: CarouselScrollbar.Layout = UIDevice.isIPad ? .threeUP : .oneUP - internal var _pagination: CarouselPaginationModel = .init(kind: .lowContrast, floating: true) - internal var _paginationDisplay: PaginationDisplay = .none - internal var _paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X - internal var _gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX - internal var _peek: Peek = .standard - internal var _numberOfSlides: Int = 1 - internal var _slotAlignment: CarouselSlotAlignmentModel? = nil - internal var _renderItemStyle: CarouselRenderItemStyle? = nil - internal var _renderItem: CarouselSlotItemModel? = nil - private var _width: Width? = nil private var selectedGroupIndex: Int? = nil private var containerStackHeightConstraint: NSLayoutConstraint? @@ -301,7 +225,7 @@ open class Carousel: View { // The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile. let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X - var slotHeight = 100.0 + var slotHeight = 50.0 var peekMinimum = 24.0 var minimumSlotWidth = 0.0 var carouselScrollbarMinWidth = 96.0 @@ -386,8 +310,8 @@ open class Carousel: View { } } - carouselScrollBar.numberOfSlides = data.count - carouselScrollBar.layout = _layout + carouselScrollBar.numberOfSlides = views.count + carouselScrollBar.layout = layout if (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) { carouselScrollBar.position = 1 } @@ -417,7 +341,6 @@ open class Carousel: View { open override func reset() { super.reset() shouldUpdateView = false - aspectRatio = .none layout = UIDevice.isIPad ? .threeUP : .oneUP pagination = .init(kind: .lowContrast, floating: true) paginationDisplay = .none @@ -473,7 +396,7 @@ open class Carousel: View { // Show/Hide pagination buttons of Carousel based on First or Middle or Last private func showPaginationControls() { - if carouselScrollBar.numberOfSlides == _layout.value { + if carouselScrollBar.numberOfSlides == layout.value { previousButton.isHidden = true nextButton.isHidden = true } else { @@ -482,6 +405,26 @@ open class Carousel: View { } } + private func estimateHeightFor(item: CarouselSlotItemModel, with width: CGFloat) -> CGFloat { + let itemWidth = width + let maxSize = CGSize(width: itemWidth, height: CGFloat.greatestFiniteMagnitude) + let estItemSize = item.component?.systemLayoutSizeFitting(maxSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) ?? CGSize(width: itemWidth, height: item.defaultHeight) + return estItemSize.height + } + + private func fetchCarouselHeight() { + if views.count > 0 { + var height = slotHeight + for x in 0...views.count - 1 { + // Add received component + let item : CarouselSlotItemModel = .init(style: renderItemStyle, component: views[x]) + slotHeight = estimateHeightFor(item: item, with: minimumSlotWidth) + height = slotHeight > height ? slotHeight : height + } + slotHeight = height + } + } + // Add carousel slots and load data if any private func addCarouselSlots() { getSlotWidth() @@ -494,11 +437,13 @@ open class Carousel: View { // Removing subView from its parent view subView.removeFromSuperview() } + + fetchCarouselHeight() // Add carousel items - if data.count > 0 { + if views.count > 0 { var xPos = 0.0 - for x in 0...data.count - 1 { + for x in 0...views.count - 1 { // Add Carousel Slot let carouselSlot = View().with { @@ -507,8 +452,7 @@ open class Carousel: View { } scrollView.addSubview(carouselSlot) scrollView.delegate = self - let size = ratioSize(for: minimumSlotWidth) - slotHeight = size.height + carouselSlot .pinTop() .pinBottom() @@ -518,24 +462,28 @@ open class Carousel: View { carouselSlot.layer.cornerRadius = 12.0 xPos = xPos + minimumSlotWidth + gutter.value + // Add received component + let item : CarouselSlotItemModel = .init(style: renderItemStyle, component: views[x]) + let contentViewHeight = estimateHeightFor(item: item, with: minimumSlotWidth) + // Add subview for content to Carousel Slot let contentView = View().with { $0.clipsToBounds = true - $0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1) } carouselSlot.addSubview(contentView) - - // Add received component - let item : CarouselSlotItemModel = .init(style: renderItemStyle, component: data[x] as? UIView) + if let component = item.component { if slotAlignment != nil { + // If slotAlignment exist, should use expected height + contentView.widthAnchor.constraint(equalToConstant: minimumSlotWidth).activate() + contentView.heightAnchor.constraint(equalToConstant: contentViewHeight).activate() setSlotAlignment(contentView: contentView, parentView: carouselSlot) } else { +// // If no slotAlignment, should use full slot contentView.pinToSuperView() } contentView.addSubview(component) component.pinToSuperView() - contentView.layer.cornerRadius = component.layer.cornerRadius if var surfacedView = component as? Surfaceable { surfacedView.surface = surface } @@ -590,7 +538,7 @@ open class Carousel: View { // Get the slot width relative to the peak private func getSlotWidth() { let actualWidth = containerView.frame.size.width - let isScrollbarSuppressed = data.count > 0 && layout.value == data.count + let isScrollbarSuppressed = views.count > 0 && layout.value == views.count let isPeekMinimumOnTablet = UIDevice.isIPad && peek == .minimum let isPeekNone: Bool = peek == .none minimumSlotWidth = isScrollbarSuppressed || isPeekMinimumOnTablet || isPeekNone ? actualWidth - ((CGFloat(layout.value)-1) * gutter.value): actualWidth - (CGFloat(layout.value) * gutter.value) @@ -626,37 +574,6 @@ open class Carousel: View { updateScrollPosition(position: carouselScrollBar.position, callbackText:"pageControlClicks") } - // The size of slot depends on the selected aspect ratio - private func ratioSize(for width: CGFloat) -> CGSize { - var height: CGFloat = width - - switch aspectRatio { - case .ratio1x1: - break; - case .ratio3x4: - height = (4 / 3) * width - case .ratio4x3: - height = (3 / 4) * width - case .ratio2x3: - height = (3 / 2) * width - case .ratio3x2: - height = (2 / 3) * width - case .ratio9x16: - height = (16 / 9) * width - case .ratio16x9: - height = (9 / 16) * width - case .ratio1x2: - height = (2 / 1) * width - case .ratio2x1: - height = (1 / 2) * width - - default: - break - } - - return CGSize(width: width, height: height) - } - private func updatePaginationInset() { prevButtonLeadingConstraint?.isActive = false nextButtonTrailingConstraint?.isActive = false @@ -688,7 +605,7 @@ open class Carousel: View { } else if position == totalPositions { xPos = scrollContentSizeWidth - containerView.frame.size.width } else { - let isScrollbarSuppressed = data.count > 0 && layout.value == data.count + let isScrollbarSuppressed = views.count > 0 && layout.value == views.count let isPeekMinimumOnTablet = UIDevice.isIPad && peek == .minimum if !isScrollbarSuppressed { let subpart = minimumSlotWidth + gutter.value @@ -719,7 +636,7 @@ open class Carousel: View { // Get the overall positions of the carousel scrollbar relative to the slides and selected layout private func totalPositions() -> Int { - return Int (ceil (Double(carouselScrollBar.numberOfSlides) / Double(_layout.value))) + return Int (ceil (Double(carouselScrollBar.numberOfSlides) / Double(layout.value))) } } diff --git a/VDS/Components/Carousel/CarouselSlotItemModel.swift b/VDS/Components/Carousel/CarouselSlotItemModel.swift index 3830b29f..a52192c4 100644 --- a/VDS/Components/Carousel/CarouselSlotItemModel.swift +++ b/VDS/Components/Carousel/CarouselSlotItemModel.swift @@ -14,7 +14,9 @@ public struct CarouselSlotItemModel { /// Style props if provided any public var style: CarouselRenderItemStyle? - + + public let defaultHeight: CGFloat = 50.0 + /// Component to be show on Carousel slot public var component: UIView? From 4ae07b640246a241291fdd7ded7dac02915c5dd2 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Thu, 18 Jul 2024 12:25:04 +0530 Subject: [PATCH 034/113] Digital ACT-191 ONEAPP-7013 story: removing width prop, and passing selectedGroupIndex through onChange --- VDS/Components/Carousel/Carousel.swift | 96 ++++--------------- .../Carousel/CarouselSlotItemModel.swift | 12 +-- 2 files changed, 20 insertions(+), 88 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 90b35588..5595ef68 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -81,31 +81,6 @@ open class Carousel: View { /// views used to render view in the carousel slots. open var views: [UIView] = [] { didSet { setNeedsUpdate() } } - /// If provided, width of slots will be rendered based on this value. If omitted, default widths are rendered. - open var width: Width? { - get { _width } - set { - if let newValue { - switch newValue { - case .percentage(let percentage): - if percentage >= 10 && percentage <= 100.0 { - let expectedWidth = safeAreaLayoutGuide.layoutFrame.size.width * (percentage/100) - if expectedWidth > carouselScrollbarMinWidth { - _width = newValue - } - } - case .value(let value): - if value > carouselScrollbarMinWidth { - _width = newValue - } - } - } else { - _width = nil - } - setNeedsUpdate() - } - } - /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. open var gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX { didSet { setNeedsUpdate() } } @@ -117,7 +92,7 @@ open class Carousel: View { } } - /// A callback when moving the carousel. Returns initial visible slide's index in the carousel. + /// A callback when moving the carousel. Returns selectedGroupIndex. open var onChange: ((Int) -> Void)? { get { nil } set { @@ -150,12 +125,6 @@ open class Carousel: View { /// If provided, will set the alignment for slot content when the slots has different heights. open var slotAlignment: CarouselSlotAlignmentModel? = nil { didSet { setNeedsUpdate() } } - /// Render item style. If provided, the slot gets the background, width, height, border-radius. - open var renderItemStyle: CarouselRenderItemStyle? = nil { didSet { setNeedsUpdate() } } - -// /// Render item. It passes a data array object and expects the styled component to apply in return. -// open var renderItem: CarouselSlotItemModel? = nil { didSet { setNeedsUpdate() } } - //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -207,15 +176,10 @@ open class Carousel: View { $0.icon.customSize = UIDevice.isIPad ? 16 : 12 } - /// A publisher for when the scrubber position changes. Passes parameters (position). + /// A publisher for when moving the carousel. Passes parameters selectedGroupIndex (position). open var onChangePublisher = PassthroughSubject() private var onChangeCancellable: AnyCancellable? - /// A publisher for when the carousel moves. Passes parameters (data). - open var onScrollPublisher = PassthroughSubject, Never>() - private var onScrollCancellable: AnyCancellable? - private var _width: Width? = nil - private var selectedGroupIndex: Int? = nil private var containerStackHeightConstraint: NSLayoutConstraint? private var containerViewHeightConstraint: NSLayoutConstraint? private var prevButtonLeadingConstraint: NSLayoutConstraint? @@ -225,7 +189,7 @@ open class Carousel: View { // The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile. let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X - var slotHeight = 50.0 + var slotDefaultHeight = 50.0 var peekMinimum = 24.0 var minimumSlotWidth = 0.0 var carouselScrollbarMinWidth = 96.0 @@ -248,13 +212,10 @@ open class Carousel: View { containerView .pinTop() .pinBottom() - .pinLeadingGreaterThanOrEqualTo() + .pinLeading() .pinTrailing() .heightGreaterThanEqualTo(containerSize.height) - containerView.centerYAnchor.constraint(equalTo: centerYAnchor).activate() - containerLeadingConstraint = containerView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 0) - containerLeadingConstraint?.activate() // Add content stackview containerView.addSubview(contentStackView) @@ -293,23 +254,6 @@ open class Carousel: View { open override func updateView() { super.updateView() - if containerView.frame.size.width > 0 { - if let width { - containerLeadingConstraint?.deactivate() - switch width { - case .value(let value): - var expectedWidth = value - let fullWidth = safeAreaLayoutGuide.layoutFrame.size.width - expectedWidth = expectedWidth > fullWidth ? fullWidth : expectedWidth - containerLeadingConstraint?.constant = safeAreaLayoutGuide.layoutFrame.size.width - expectedWidth - case .percentage(let percentage): - let expectedWidth = safeAreaLayoutGuide.layoutFrame.size.width * (percentage/100) - containerLeadingConstraint?.constant = safeAreaLayoutGuide.layoutFrame.size.width - expectedWidth - } - containerLeadingConstraint?.activate() - } - } - carouselScrollBar.numberOfSlides = views.count carouselScrollBar.layout = layout if (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) { @@ -347,7 +291,6 @@ open class Carousel: View { paginationInset = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX peek = .standard - width = nil } //-------------------------------------------------- @@ -412,17 +355,16 @@ open class Carousel: View { return estItemSize.height } - private func fetchCarouselHeight() { + private func fetchCarouselHeight() -> CGFloat { + var height = slotDefaultHeight if views.count > 0 { - var height = slotHeight for x in 0...views.count - 1 { - // Add received component - let item : CarouselSlotItemModel = .init(style: renderItemStyle, component: views[x]) - slotHeight = estimateHeightFor(item: item, with: minimumSlotWidth) - height = slotHeight > height ? slotHeight : height + let item : CarouselSlotItemModel = .init(component: views[x]) + let estHeight = estimateHeightFor(item: item, with: minimumSlotWidth) + height = estHeight > height ? estHeight : height } - slotHeight = height } + return height } // Add carousel slots and load data if any @@ -431,14 +373,13 @@ open class Carousel: View { if containerView.frame.size.width > 0 { containerViewHeightConstraint?.isActive = false containerStackHeightConstraint?.isActive = false + let slotHeight = fetchCarouselHeight() // Perform a loop to iterate each subView scrollView.subviews.forEach { subView in // Removing subView from its parent view subView.removeFromSuperview() } - - fetchCarouselHeight() // Add carousel items if views.count > 0 { @@ -452,7 +393,7 @@ open class Carousel: View { } scrollView.addSubview(carouselSlot) scrollView.delegate = self - + carouselSlot .pinTop() .pinBottom() @@ -463,7 +404,7 @@ open class Carousel: View { xPos = xPos + minimumSlotWidth + gutter.value // Add received component - let item : CarouselSlotItemModel = .init(style: renderItemStyle, component: views[x]) + let item : CarouselSlotItemModel = .init(component: views[x]) let contentViewHeight = estimateHeightFor(item: item, with: minimumSlotWidth) // Add subview for content to Carousel Slot @@ -471,7 +412,7 @@ open class Carousel: View { $0.clipsToBounds = true } carouselSlot.addSubview(contentView) - + if let component = item.component { if slotAlignment != nil { // If slotAlignment exist, should use expected height @@ -479,12 +420,15 @@ open class Carousel: View { contentView.heightAnchor.constraint(equalToConstant: contentViewHeight).activate() setSlotAlignment(contentView: contentView, parentView: carouselSlot) } else { -// // If no slotAlignment, should use full slot + // If no slotAlignment, should use full slot contentView.pinToSuperView() } + carouselSlot.backgroundColor = .clear + carouselSlot.layer.cornerRadius = 0 contentView.addSubview(component) component.pinToSuperView() if var surfacedView = component as? Surfaceable { + contentView.surface = surface surfacedView.surface = surface } } @@ -628,9 +572,7 @@ open class Carousel: View { let yPos = scrollView.contentOffset.y scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true) showPaginationControls() - selectedIndex = ((position-1) * layout.value) + 1 - onChangePublisher.send(selectedIndex ?? 1) - selectedGroupIndex = position + onChangePublisher.send(position-1) } } diff --git a/VDS/Components/Carousel/CarouselSlotItemModel.swift b/VDS/Components/Carousel/CarouselSlotItemModel.swift index a52192c4..3cb7240a 100644 --- a/VDS/Components/Carousel/CarouselSlotItemModel.swift +++ b/VDS/Components/Carousel/CarouselSlotItemModel.swift @@ -11,23 +11,13 @@ import VDSCoreTokens /// A custom data type that holds the style and component for a slot of the 'Carousel' component. public struct CarouselSlotItemModel { - - /// Style props if provided any - public var style: CarouselRenderItemStyle? public let defaultHeight: CGFloat = 50.0 /// Component to be show on Carousel slot public var component: UIView? - public init(style: CarouselRenderItemStyle? = nil, component: UIView? = nil) { - self.style = style + public init(component: UIView? = nil) { self.component = component - if let color = style?.backgroundColor { - self.component?.backgroundColor = .init(hexString: color) - } - if let borderRadius = style?.borderRadius { - self.component?.layer.cornerRadius = borderRadius - } } } From c2ec941a3cb4ef0d9be861774b8eb84ca1159dc4 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Thu, 18 Jul 2024 12:49:12 +0530 Subject: [PATCH 035/113] Digital ACT-191 ONEAPP-7013 story: updating carousel position if receives selectedIndex --- VDS/Components/Carousel/Carousel.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 5595ef68..35357778 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -279,6 +279,12 @@ open class Carousel: View { updatePaginationControls() addCarouselSlots() + + // If selectedIndex is received, the carousel should update its position. + if let selectedIndex { + let totalPos = totalPositions() + carouselScrollBar.position = selectedIndex >= totalPos ? totalPos : selectedIndex+1 + } } /// Resets to default settings. @@ -300,6 +306,12 @@ open class Carousel: View { nextButton.onClick = { _ in self.nextButtonClick() } previousButton.onClick = { _ in self.previousButtonClick() } + /// Will be called when the scrubber position changes. + carouselScrollBar.onScrubberDrag = { [weak self] scrubberId in + guard let self else { return } + updateScrollPosition(position: scrubberId, callbackText:"onThumbPositionChange") + } + /// Will be called when the scrollbar thumb move forward. carouselScrollBar.onMoveForward = { [weak self] scrubberId in guard let self else { return } From 559423f629110d992625fd294cbe3e9c95345933 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Thu, 18 Jul 2024 13:22:41 +0530 Subject: [PATCH 036/113] Digital ACT-191 ONEAPP-7013 story: removed unused file --- VDS.xcodeproj/project.pbxproj | 4 --- .../Carousel/CarouselRenderItemStyle.swift | 33 ------------------- 2 files changed, 37 deletions(-) delete mode 100644 VDS/Components/Carousel/CarouselRenderItemStyle.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 64ec60bb..e1bf1b05 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 18013CED2C355BF900907F18 /* CarouselSlotItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18013CEC2C355BF900907F18 /* CarouselSlotItemModel.swift */; }; - 18013CEF2C355C5200907F18 /* CarouselRenderItemStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18013CEE2C355C5200907F18 /* CarouselRenderItemStyle.swift */; }; 1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; }; 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; 1842B1DF2BECE28B0021AFCA /* CalendarDateViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1842B1DE2BECE28B0021AFCA /* CalendarDateViewCell.swift */; }; @@ -210,7 +209,6 @@ /* Begin PBXFileReference section */ 18013CEC2C355BF900907F18 /* CarouselSlotItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselSlotItemModel.swift; sourceTree = ""; }; - 18013CEE2C355C5200907F18 /* CarouselRenderItemStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselRenderItemStyle.swift; sourceTree = ""; }; 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = ""; }; 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = ""; }; 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; @@ -505,7 +503,6 @@ 18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */, 18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */, 18013CEC2C355BF900907F18 /* CarouselSlotItemModel.swift */, - 18013CEE2C355C5200907F18 /* CarouselRenderItemStyle.swift */, 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */, ); path = Carousel; @@ -1367,7 +1364,6 @@ EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */, EAF7F0AB289B13FD00B287F5 /* TextStyleLabelAttribute.swift in Sources */, 18AE87502C06FDA60075F181 /* Carousel.swift in Sources */, - 18013CEF2C355C5200907F18 /* CarouselRenderItemStyle.swift in Sources */, EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */, EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */, EA985BE629688F6A00F2FF2E /* TileletBadgeModel.swift in Sources */, diff --git a/VDS/Components/Carousel/CarouselRenderItemStyle.swift b/VDS/Components/Carousel/CarouselRenderItemStyle.swift deleted file mode 100644 index c447dfa1..00000000 --- a/VDS/Components/Carousel/CarouselRenderItemStyle.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// CarouselRenderItemStyle.swift -// VDS -// -// Created by Kanamarlapudi, Vasavi on 30/06/24. -// - -import Foundation -import UIKit -import VDSCoreTokens - -/// A custom data type that holds the style props if provided any. -public struct CarouselRenderItemStyle { - - /// BackgroundColor for slot - public let backgroundColor: String? - - /// Height for slot - public var height: CGFloat? - - /// BorderRadius for slot - public var borderRadius: CGFloat? - - /// Width for slot - public var width: CGFloat? - - public init(backgroundColor: String?, height: CGFloat?, width: CGFloat?, borderRadius: CGFloat?) { - self.backgroundColor = backgroundColor - self.height = height - self.borderRadius = borderRadius ?? 12.0 - self.width = width - } -} From 93004f6e1205114d60c6c1fee52d7b0064912f2e Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Thu, 18 Jul 2024 17:28:40 +0530 Subject: [PATCH 037/113] Digital ACT-191 ONEAPP-7013 story: Refactored code, removing redundant view and adding component to slot. --- VDS/Components/Carousel/Carousel.swift | 31 ++++++++++++-------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 35357778..9a9b3251 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -417,32 +417,29 @@ open class Carousel: View { // Add received component let item : CarouselSlotItemModel = .init(component: views[x]) - let contentViewHeight = estimateHeightFor(item: item, with: minimumSlotWidth) - - // Add subview for content to Carousel Slot - let contentView = View().with { - $0.clipsToBounds = true - } - carouselSlot.addSubview(contentView) - if let component = item.component { + carouselSlot.addSubview(component) if slotAlignment != nil { - // If slotAlignment exist, should use expected height - contentView.widthAnchor.constraint(equalToConstant: minimumSlotWidth).activate() - contentView.heightAnchor.constraint(equalToConstant: contentViewHeight).activate() - setSlotAlignment(contentView: contentView, parentView: carouselSlot) + // If slotAlignment exist, use component's own sizes + component.widthAnchor.constraint(lessThanOrEqualTo: carouselSlot.widthAnchor).activate() + setSlotAlignment(contentView: component, parentView: carouselSlot) } else { // If no slotAlignment, should use full slot - contentView.pinToSuperView() + component.pinToSuperView() } carouselSlot.backgroundColor = .clear carouselSlot.layer.cornerRadius = 0 - contentView.addSubview(component) - component.pinToSuperView() if var surfacedView = component as? Surfaceable { - contentView.surface = surface surfacedView.surface = surface } + + // TO BE REMOVED AFTER VQA - borderWidth and borderColor below did set only for Testing purpose to VQA + carouselSlot.layer.borderWidth = 1.0 + carouselSlot.layer.borderColor = SurfaceColorConfiguration().with { + $0.lightColor = VDSColor.elementsPrimaryOnlight + $0.darkColor = VDSColor.elementsPrimaryOndark + }.getColor(self).cgColor + } } scrollView.contentSize = CGSize(width: xPos - gutter.value, height: slotHeight) @@ -462,7 +459,7 @@ open class Carousel: View { } // Set slot alignment if provided. Used only when slot content have different heights or widths. - private func setSlotAlignment(contentView: View, parentView: View) { + private func setSlotAlignment(contentView: UIView, parentView: View) { parentView.backgroundColor = .clear switch slotAlignment?.vertical { case .top: From 076f4c082cb6aeb2b68be8137fc50c3471a4714c Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Thu, 18 Jul 2024 20:26:06 +0530 Subject: [PATCH 038/113] Digital ACT-191 ONEAPP-7013 story: removed border, background color to slot, removed unused file, updated slotAlignment method. --- VDS.xcodeproj/project.pbxproj | 4 - VDS/Components/Carousel/Carousel.swift | 80 +++++++------------ .../Carousel/CarouselSlotItemModel.swift | 23 ------ 3 files changed, 30 insertions(+), 77 deletions(-) delete mode 100644 VDS/Components/Carousel/CarouselSlotItemModel.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 93331c0e..59600512 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 18013CED2C355BF900907F18 /* CarouselSlotItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18013CEC2C355BF900907F18 /* CarouselSlotItemModel.swift */; }; 1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; }; 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; 1842B1DF2BECE28B0021AFCA /* CalendarDateViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1842B1DE2BECE28B0021AFCA /* CalendarDateViewCell.swift */; }; @@ -208,7 +207,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 18013CEC2C355BF900907F18 /* CarouselSlotItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselSlotItemModel.swift; sourceTree = ""; }; 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = ""; }; 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = ""; }; 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; @@ -502,7 +500,6 @@ 18AE874F2C06FDA60075F181 /* Carousel.swift */, 18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */, 18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */, - 18013CEC2C355BF900907F18 /* CarouselSlotItemModel.swift */, 18AE87532C06FE610075F181 /* CarouselChangeLog.txt */, ); path = Carousel; @@ -1406,7 +1403,6 @@ EA596ABF2A16B4F500300C4B /* Tabs.swift in Sources */, EAD062A72A3B67770015965D /* UIView+CALayer.swift in Sources */, EAD068942A560C13002E3A2D /* LoaderLaunchable.swift in Sources */, - 18013CED2C355BF900907F18 /* CarouselSlotItemModel.swift in Sources */, 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */, EA985BEC2968A91200F2FF2E /* TitleLockupTitleModel.swift in Sources */, 5FC35BE328D51405004EBEAC /* Button.swift in Sources */, diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 9a9b3251..0a6e71f9 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -123,7 +123,7 @@ open class Carousel: View { open var selectedIndex: Int? { didSet { setNeedsUpdate() } } /// If provided, will set the alignment for slot content when the slots has different heights. - open var slotAlignment: CarouselSlotAlignmentModel? = nil { didSet { setNeedsUpdate() } } + open var slotAlignment: CarouselSlotAlignmentModel? = .init(vertical: .top, horizontal: .left) { didSet { setNeedsUpdate() } } //-------------------------------------------------- // MARK: - Private Properties @@ -360,19 +360,17 @@ open class Carousel: View { } } - private func estimateHeightFor(item: CarouselSlotItemModel, with width: CGFloat) -> CGFloat { - let itemWidth = width + private func estimateHeightFor(component: UIView, with itemWidth: CGFloat) -> CGFloat { let maxSize = CGSize(width: itemWidth, height: CGFloat.greatestFiniteMagnitude) - let estItemSize = item.component?.systemLayoutSizeFitting(maxSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) ?? CGSize(width: itemWidth, height: item.defaultHeight) + let estItemSize = component.systemLayoutSizeFitting(maxSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) return estItemSize.height } private func fetchCarouselHeight() -> CGFloat { var height = slotDefaultHeight if views.count > 0 { - for x in 0...views.count - 1 { - let item : CarouselSlotItemModel = .init(component: views[x]) - let estHeight = estimateHeightFor(item: item, with: minimumSlotWidth) + for index in 0...views.count - 1 { + let estHeight = estimateHeightFor(component: views[index], with: minimumSlotWidth) height = estHeight > height ? estHeight : height } } @@ -396,12 +394,11 @@ open class Carousel: View { // Add carousel items if views.count > 0 { var xPos = 0.0 - for x in 0...views.count - 1 { + for index in 0...views.count - 1 { // Add Carousel Slot let carouselSlot = View().with { $0.clipsToBounds = true - $0.backgroundColor = UIColor(red: CGFloat(216) / 255.0, green: CGFloat(218) / 255.0, blue: CGFloat(218) / 255.0, alpha: 1) } scrollView.addSubview(carouselSlot) scrollView.delegate = self @@ -412,35 +409,11 @@ open class Carousel: View { .pinLeading(xPos) .width(minimumSlotWidth) .height(slotHeight) - carouselSlot.layer.cornerRadius = 12.0 xPos = xPos + minimumSlotWidth + gutter.value - // Add received component - let item : CarouselSlotItemModel = .init(component: views[x]) - if let component = item.component { - carouselSlot.addSubview(component) - if slotAlignment != nil { - // If slotAlignment exist, use component's own sizes - component.widthAnchor.constraint(lessThanOrEqualTo: carouselSlot.widthAnchor).activate() - setSlotAlignment(contentView: component, parentView: carouselSlot) - } else { - // If no slotAlignment, should use full slot - component.pinToSuperView() - } - carouselSlot.backgroundColor = .clear - carouselSlot.layer.cornerRadius = 0 - if var surfacedView = component as? Surfaceable { - surfacedView.surface = surface - } - - // TO BE REMOVED AFTER VQA - borderWidth and borderColor below did set only for Testing purpose to VQA - carouselSlot.layer.borderWidth = 1.0 - carouselSlot.layer.borderColor = SurfaceColorConfiguration().with { - $0.lightColor = VDSColor.elementsPrimaryOnlight - $0.darkColor = VDSColor.elementsPrimaryOndark - }.getColor(self).cgColor - - } + let component = views[index] + carouselSlot.addSubview(component) + setSlotAlignment(contentView: component) } scrollView.contentSize = CGSize(width: xPos - gutter.value, height: slotHeight) } @@ -459,31 +432,38 @@ open class Carousel: View { } // Set slot alignment if provided. Used only when slot content have different heights or widths. - private func setSlotAlignment(contentView: UIView, parentView: View) { - parentView.backgroundColor = .clear + private func setSlotAlignment(contentView: UIView) { switch slotAlignment?.vertical { case .top: - contentView.topAnchor.constraint(equalTo: parentView.topAnchor).activate() - break + contentView + .pinTop() + .pinBottomLessThanOrEqualTo() case .middle: - contentView.centerYAnchor.constraint(equalTo: parentView.centerYAnchor).activate() - break + contentView + .pinTopGreaterThanOrEqualTo() + .pinBottomLessThanOrEqualTo() + .pinCenterY() case .bottom: - contentView.bottomAnchor.constraint(equalTo: parentView.bottomAnchor).activate() - break + contentView + .pinTopGreaterThanOrEqualTo() + .pinBottom() default: break } switch slotAlignment?.horizontal { case .left: - contentView.leadingAnchor.constraint(equalTo: parentView.leadingAnchor).activate() - break + contentView + .pinLeading() + .pinTrailingLessThanOrEqualTo() case .center: - contentView.centerXAnchor.constraint(equalTo: parentView.centerXAnchor).activate() - break + contentView + .pinLeadingGreaterThanOrEqualTo() + .pinTrailingLessThanOrEqualTo() + .pinCenterX() case .right: - parentView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).activate() - break + contentView + .pinLeadingGreaterThanOrEqualTo() + .pinTrailing() default: break } } diff --git a/VDS/Components/Carousel/CarouselSlotItemModel.swift b/VDS/Components/Carousel/CarouselSlotItemModel.swift deleted file mode 100644 index 3cb7240a..00000000 --- a/VDS/Components/Carousel/CarouselSlotItemModel.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// CarouselSlotItemModel.swift -// VDS -// -// Created by Kanamarlapudi, Vasavi on 30/06/24. -// - -import Foundation -import UIKit -import VDSCoreTokens - -/// A custom data type that holds the style and component for a slot of the 'Carousel' component. -public struct CarouselSlotItemModel { - - public let defaultHeight: CGFloat = 50.0 - - /// Component to be show on Carousel slot - public var component: UIView? - - public init(component: UIView? = nil) { - self.component = component - } -} From bf40368dc45aa68fa2248eeb01d7a134cd981004 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 18 Jul 2024 12:26:12 -0500 Subject: [PATCH 039/113] refactored layoutConstrainable for methods Signed-off-by: Matt Bruce --- VDS/Protocols/LayoutConstraintable.swift | 88 ++++++++++++++++++++---- 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/VDS/Protocols/LayoutConstraintable.swift b/VDS/Protocols/LayoutConstraintable.swift index ba08e3c2..bee145ad 100644 --- a/VDS/Protocols/LayoutConstraintable.swift +++ b/VDS/Protocols/LayoutConstraintable.swift @@ -705,11 +705,11 @@ extension LayoutConstraintable { } // Method to check if the view is pinned to its superview - public func isPinnedToSuperview() -> Bool { - isPinnedVerticallyToSuperview() && isPinnedHorizontallyToSuperview() + public func isPinnedEqual() -> Bool { + isPinnedEqualVertically() && isPinnedEqualHorizontally() } - public func horizontalPinnedSize() -> CGSize? { + public func horizontalPinnedWidth() -> CGFloat? { guard let view = self as? UIView, let superview = view.superview else { return nil } let constraints = superview.constraints @@ -735,44 +735,106 @@ extension LayoutConstraintable { if let leadingView = leadingObject as? UIView, let trailingView = trailingObject as? UIView { let leadingPosition = leadingView.convert(leadingView.bounds.origin, to: superview).x let trailingPosition = trailingView.convert(trailingView.bounds.origin, to: superview).x + trailingView.bounds.width - return CGSize(width: trailingPosition - leadingPosition, height: view.bounds.size.height) + return trailingPosition - leadingPosition } else if let leadingGuide = leadingObject as? UILayoutGuide, let trailingGuide = trailingObject as? UILayoutGuide { let leadingPosition = leadingGuide.layoutFrame.minX let trailingPosition = trailingGuide.layoutFrame.maxX - return CGSize(width: trailingPosition - leadingPosition, height: view.bounds.size.height) + return trailingPosition - leadingPosition } else if let leadingView = leadingObject as? UIView, let trailingGuide = trailingObject as? UILayoutGuide { let leadingPosition = leadingView.convert(leadingView.bounds.origin, to: superview).x let trailingPosition = trailingGuide.layoutFrame.maxX - return CGSize(width: trailingPosition - leadingPosition, height: view.bounds.size.height) + return trailingPosition - leadingPosition } else if let leadingGuide = leadingObject as? UILayoutGuide, let trailingView = trailingObject as? UIView { let leadingPosition = leadingGuide.layoutFrame.minX let trailingPosition = trailingView.convert(trailingView.bounds.origin, to: superview).x + trailingView.bounds.width - return CGSize(width: trailingPosition - leadingPosition, height: view.bounds.size.height) + return trailingPosition - leadingPosition } } else if let pinnedObject = leadingPinnedObject { if let view = pinnedObject as? UIView { - return view.bounds.size + return view.bounds.size.width } else if let layoutGuide = pinnedObject as? UILayoutGuide { - return layoutGuide.layoutFrame.size + return layoutGuide.layoutFrame.size.width } } else if let pinnedObject = trailingPinnedObject { if let view = pinnedObject as? UIView { - return view.bounds.size + return view.bounds.size.width } else if let layoutGuide = pinnedObject as? UILayoutGuide { - return layoutGuide.layoutFrame.size + return layoutGuide.layoutFrame.size.width } } return nil } + + public func verticalPinnedHeight() -> CGFloat? { + guard let view = self as? UIView, let superview = view.superview else { return nil } + let constraints = superview.constraints + + var topPinnedObject: AnyObject? + var bottomPinnedObject: AnyObject? + + for constraint in constraints { + if (constraint.firstItem === view && (constraint.firstAttribute == .top || constraint.firstAttribute == .topMargin)) { + topPinnedObject = constraint.secondItem as AnyObject? + } else if (constraint.secondItem === view && (constraint.secondAttribute == .top || constraint.secondAttribute == .topMargin)) { + topPinnedObject = constraint.firstItem as AnyObject? + } else if (constraint.firstItem === view && (constraint.firstAttribute == .bottom || constraint.firstAttribute == .bottomMargin)) { + bottomPinnedObject = constraint.secondItem as AnyObject? + } else if (constraint.secondItem === view && (constraint.secondAttribute == .bottom || constraint.secondAttribute == .bottomMargin)) { + bottomPinnedObject = constraint.firstItem as AnyObject? + } + } + + // Ensure both top and bottom pinned objects are identified + if let topObject = topPinnedObject, let bottomObject = bottomPinnedObject { + + // Calculate the size based on the pinned objects + if let topView = topObject as? UIView, let bottomView = bottomObject as? UIView { + let topPosition = topView.convert(topView.bounds.origin, to: superview).y + let bottomPosition = bottomView.convert(bottomView.bounds.origin, to: superview).y + bottomView.bounds.height + return bottomPosition - topPosition + + } else if let topGuide = topObject as? UILayoutGuide, let bottomGuide = bottomObject as? UILayoutGuide { + let topPosition = topGuide.layoutFrame.minY + let bottomPosition = bottomGuide.layoutFrame.maxY + return bottomPosition - topPosition + + } else if let topView = topObject as? UIView, let bottomGuide = bottomObject as? UILayoutGuide { + let topPosition = topView.convert(topView.bounds.origin, to: superview).y + let bottomPosition = bottomGuide.layoutFrame.maxY + return bottomPosition - topPosition + + } else if let topGuide = topObject as? UILayoutGuide, let bottomView = bottomObject as? UIView { + let topPosition = topGuide.layoutFrame.minY + let bottomPosition = bottomView.convert(bottomView.bounds.origin, to: superview).y + bottomView.bounds.height + return bottomPosition - topPosition + } + + } else if let pinnedObject = topPinnedObject { + if let view = pinnedObject as? UIView { + return view.bounds.size.height + } else if let layoutGuide = pinnedObject as? UILayoutGuide { + return layoutGuide.layoutFrame.size.height + } + + } else if let pinnedObject = bottomPinnedObject { + if let view = pinnedObject as? UIView { + return view.bounds.size.height + } else if let layoutGuide = pinnedObject as? UILayoutGuide { + return layoutGuide.layoutFrame.size.height + } + } + + return nil + } - public func isPinnedHorizontallyToSuperview() -> Bool { + public func isPinnedEqualHorizontally() -> Bool { guard let view = self as? UIView, let superview = view.superview else { return false } let constraints = superview.constraints var leadingPinned = false @@ -796,7 +858,7 @@ extension LayoutConstraintable { return leadingPinned && trailingPinned } - public func isPinnedVerticallyToSuperview() -> Bool { + public func isPinnedEqualVertically() -> Bool { guard let view = self as? UIView, let superview = view.superview else { return false } let constraints = superview.constraints var topPinned = false From b91017068c65bc6865039490d7111fe3821475c6 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 18 Jul 2024 15:34:03 -0500 Subject: [PATCH 040/113] Missed a few "fill" types and refactored to use a multiplier Signed-off-by: Matt Bruce --- .../TileContainer/TileContainer.swift | 248 ++++++++++-------- 1 file changed, 141 insertions(+), 107 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 6eeaf316..131a62f5 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -74,7 +74,7 @@ open class TileContainerBase: Control where Padding case custom(UIColor) private var reflectedValue: String { String(reflecting: self) } - + public static func == (lhs: Self, rhs: Self) -> Bool { lhs.reflectedValue == rhs.reflectedValue } @@ -86,7 +86,7 @@ open class TileContainerBase: Control where Padding case gradient(UIColor, UIColor) case none } - + /// Enum used to describe the aspect ratios used for this component. public enum AspectRatio: String, CaseIterable { case ratio1x1 = "1:1" @@ -109,7 +109,7 @@ open class TileContainerBase: Control where Padding $0.contentMode = .scaleAspectFill $0.clipsToBounds = true } - + open var containerView = View().with { $0.setContentHuggingPriority(.defaultLow, for: .horizontal) $0.setContentHuggingPriority(.defaultLow, for: .vertical) @@ -125,27 +125,27 @@ open class TileContainerBase: Control where Padding /// This is the container in which views will be pinned. open var contentView = View() - + /// This is the view used to show the high light color for a onClick. open var highlightView = View().with { $0.isUserInteractionEnabled = false } - + /// This controls the aspect ratio for the component. open var aspectRatio: AspectRatio = .ratio1x1 { didSet { setNeedsUpdate() } } - + /// Sets the background color for the component. open var color: BackgroundColor? { didSet { setNeedsUpdate() } } /// Sets the background effect for the component. open var backgroundEffect: BackgroundEffect = .none { didSet { setNeedsUpdate() } } - + /// Sets the inside padding for the component 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() } } - + private var _width: CGFloat? /// Sets the width for the component. Accepts a pixel value. open var width: CGFloat? { @@ -159,7 +159,7 @@ open class TileContainerBase: Control where Padding setNeedsUpdate() } } - + private var _height: CGFloat? /// Sets the height for the component. Accepts a pixel value. open var height: CGFloat? { @@ -179,13 +179,14 @@ open class TileContainerBase: Control where Padding /// Determines if there is a drop shadow or not. open var showDropShadow: Bool = false { didSet { setNeedsUpdate() } } - + //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- internal var widthConstraint: NSLayoutConstraint? internal var heightConstraint: NSLayoutConstraint? - + internal var aspectRatioConstraint: NSLayoutConstraint? + //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- @@ -228,13 +229,13 @@ open class TileContainerBase: Control where Padding containerView.addSubview(backgroundImageView) backgroundImageView.pinToSuperView() - + containerView.addSubview(contentView) contentView.pinToSuperView() - + containerView.addSubview(highlightView) highlightView.pinToSuperView() - + widthConstraint = widthAnchor.constraint(equalToConstant: 0).deactivate() heightConstraint = heightAnchor.constraint(equalToConstant: 0).deactivate() @@ -266,7 +267,7 @@ open class TileContainerBase: Control where Padding setNeedsUpdate() } }.store(in: &subscribers) - + } /// Overriden to take the hit if there is an onClickSubscriber and the view is not a UIControl @@ -291,7 +292,7 @@ open class TileContainerBase: Control where Padding shouldUpdateView = true setNeedsUpdate() } - + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() @@ -301,13 +302,14 @@ open class TileContainerBase: Control where Padding containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor containerView.layer.borderWidth = showBorder ? VDSFormControls.borderWidth : 0 - + contentView.removeConstraints() contentView.pinToSuperView(.uniform(padding.value)) updateContainerView() + } - + open override var accessibilityElements: [Any]? { get { var items = [Any]() @@ -328,7 +330,7 @@ open class TileContainerBase: Control where Padding //append all children that are accessible items.append(contentsOf: elements) - + return items } set {} @@ -337,7 +339,7 @@ open class TileContainerBase: Control where Padding //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- - + /// This will place a view within the contentView of this component. public func addContentView(_ view: UIView, shouldPin: Bool = true) { view.removeFromSuperview() @@ -346,7 +348,7 @@ open class TileContainerBase: Control where Padding view.pinToSuperView() } } - + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- @@ -379,55 +381,10 @@ open class TileContainerBase: Control where Padding containerView.backgroundColor = color.withAlphaComponent(alphaConfiguration) } } - - private func ratioSize(for width: CGFloat) -> CGSize { - var height: CGFloat = width - switch aspectRatio { - case .ratio1x1: - break; - case .ratio3x4: - height = (4 / 3) * width - case .ratio4x3: - height = (3 / 4) * width - case .ratio2x3: - height = (3 / 2) * width - case .ratio3x2: - height = (2 / 3) * width - case .ratio9x16: - height = (16 / 9) * width - case .ratio16x9: - height = (9 / 16) * width - case .ratio1x2: - height = (2 / 1) * width - case .ratio2x1: - height = (1 / 2) * width - - default: - break - } - - return CGSize(width: width, height: height) - } - - private func sizeContainerView(width: CGFloat? = nil, height: CGFloat? = nil) { - if let width, width > 0 { - widthConstraint?.constant = width - widthConstraint?.activate() - } - - if let height, height > 0 { - heightConstraint?.constant = height - heightConstraint?.activate() - } - } - private func updateContainerView() { applyBackgroundEffects() - - widthConstraint?.deactivate() - heightConstraint?.deactivate() - + if showDropShadow, surface == .light { containerView.addDropShadow(dropShadowConfiguration) } else { @@ -436,50 +393,100 @@ open class TileContainerBase: Control where Padding containerView.dropShadowLayers?.forEach { $0.frame = containerView.bounds } containerView.gradientLayers?.forEach { $0.frame = containerView.bounds } + + //sizing the container with constraints + + //Set local vars + var containerViewWidth: CGFloat? = width + let containerViewHeight: CGFloat? = height + let multiplier = aspectRatio.multiplier - if width != nil || height != nil { - var containerViewWidth: CGFloat? - var containerViewHeight: CGFloat? - //run logic to determine which to activate - if let width, aspectRatio == .none && height == nil{ - containerViewWidth = width - - } else if let height, aspectRatio == .none && width == nil{ - containerViewHeight = height - - } else if let height, let width { - containerViewWidth = width - containerViewHeight = height - - } else if let width { - let size = ratioSize(for: width) - containerViewWidth = size.width - containerViewHeight = size.height + //turn off the constraints + aspectRatioConstraint?.deactivate() + widthConstraint?.deactivate() + heightConstraint?.deactivate() - } else if let height { - let size = ratioSize(for: height) - containerViewWidth = size.width - containerViewHeight = size.height - } - sizeContainerView(width: containerViewWidth, height: containerViewHeight) - } else { - if let parentSize = horizontalPinnedSize() { - - var containerViewWidth: CGFloat? - var containerViewHeight: CGFloat? - - let size = ratioSize(for: parentSize.width) - if aspectRatio == .none { - containerViewWidth = size.width - } else { - containerViewWidth = size.width - containerViewHeight = size.height - } - - sizeContainerView(width: containerViewWidth, height: containerViewHeight) - } + //------------------------------------------------------------------------- + //Overriding Nil Width Rules + //------------------------------------------------------------------------- + //Rule 1: + //In the scenario where we only have a height but the multiplie is nil, we + //want to set the width with the parent's width which will more or less "fill" + //the container horizontally + //- height is set + //- width is not set + //- aspectRatio is not set + if let superviewWidth, superviewWidth > 0, + containerViewHeight != nil, + containerViewWidth == nil, + multiplier == nil { + containerViewWidth = superviewWidth + } + + //Rule 2: + //In the scenario where no width and height is set, want to set the width with the + //parent's width which will more or less "fill" the container horizontally + //- height is not set + //- width is not set + else if let superviewWidth, superviewWidth > 0, + containerViewWidth == nil, + containerViewHeight == nil { + containerViewWidth = superviewWidth + } + //------------------------------------------------------------------------- + + + //------------------------------------------------------------------------- + //Width + AspectRatio Constraint - Will exit out if set + //------------------------------------------------------------------------- + if let containerViewWidth, + let multiplier, + containerViewWidth > 0, + containerViewHeight == nil { + widthConstraint?.constant = containerViewWidth + widthConstraint?.activate() + aspectRatioConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: multiplier) + aspectRatioConstraint?.activate() + return + } + //------------------------------------------------------------------------- + //Height + AspectRatio Constraint - Will exit out if set + //------------------------------------------------------------------------- + else if let containerViewHeight, + let multiplier, + containerViewHeight > 0, + containerViewWidth == nil { + heightConstraint?.constant = containerViewHeight + heightConstraint?.activate() + aspectRatioConstraint = widthAnchor.constraint(equalTo: heightAnchor, multiplier: multiplier) + aspectRatioConstraint?.activate() + return + } + + //------------------------------------------------------------------------- + //Width Constraint + //------------------------------------------------------------------------- + if let containerViewWidth, + containerViewWidth > 0 { + widthConstraint?.constant = containerViewWidth + widthConstraint?.activate() + } + + //------------------------------------------------------------------------- + //Height Constraint + //------------------------------------------------------------------------- + if let containerViewHeight, + containerViewHeight > 0 { + heightConstraint?.constant = containerViewHeight + heightConstraint?.activate() } } + + /// This is the size of the superview's allowed space for this container first by constrained size which would include padding/inset values an + private var superviewWidth: CGFloat? { + horizontalPinnedWidth() ?? superview?.frame.size.width + } + } extension TileContainerBase { @@ -519,3 +526,30 @@ extension TileContainerBase { } } } + +extension TileContainerBase.AspectRatio { + var multiplier: CGFloat? { + switch self { + case .ratio1x1: + return 1 + case .ratio3x4: + return 4 / 3 + case .ratio4x3: + return 3 / 4 + case .ratio2x3: + return 3 / 2 + case .ratio3x2: + return 2 / 3 + case .ratio9x16: + return 16 / 9 + case .ratio16x9: + return 9 / 16 + case .ratio1x2: + return 2 / 1 + case .ratio2x1: + return 1 / 2 + case .none: + return nil + } + } +} From f1b5fd18c962af74af62f63afd2e8e99894af552 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 19 Jul 2024 11:17:54 +0530 Subject: [PATCH 041/113] Digital ACT-191 ONEAPP-7013 story: removed unused code, updated Gutter pattern, renamed selectedIndex to groupIndex --- VDS/Components/Carousel/Carousel.swift | 48 ++++++++++++-------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 0a6e71f9..0cc6d738 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -39,7 +39,9 @@ open class Carousel: View { } /// Enum used to describe the peek for this component. - /// This is how much a tile is partially visible. It is measured by the distance between the edge of the tile and the edge of the viewport or carousel container. A peek can appear on the left and/or right edge of the carousel container or viewport, depending on the carousel’s scroll position. + /// This is how much a tile is partially visible. It is measured by the distance between the edge of + /// the tile and the edge of the viewport or carousel container. A peek can appear on the left and/or + /// right edge of the carousel container or viewport, depending on the carousel’s scroll position. public enum Peek: String, CaseIterable { case standard, minimum, none } @@ -53,23 +55,19 @@ open class Carousel: View { public enum Horizontal: String, CaseIterable { case left, center, right } - - /// Enum used to describe the width of a fixed value or percentage of parent's width. - public enum Width { - case percentage(CGFloat) - case value(CGFloat) - } - + /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. - public enum Gutter: String, CaseIterable { - case twelvePX = "12px" - case twentyFourPX = "24px" + public enum Gutter: String, CaseIterable , DefaultValuing { + case gutter3X = "3X" + case gutter6X = "6X" - var value: CGFloat { + public static var defaultValue: Self { UIDevice.isIPad ? .gutter6X : .gutter3X } + + public var value: CGFloat { switch self { - case .twelvePX: + case .gutter3X: VDSLayout.space3X - case .twentyFourPX: + case .gutter6X: VDSLayout.space6X } } @@ -82,9 +80,10 @@ open class Carousel: View { open var views: [UIView] = [] { didSet { setNeedsUpdate() } } /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. - open var gutter: Gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX { didSet { setNeedsUpdate() } } + open var gutter: Gutter = Gutter.defaultValue { didSet { setNeedsUpdate() } } - /// The amount of slides visible in the carousel container at one time. The default value will be 3UP in tablet and 1UP in mobile. + /// The amount of slides visible in the carousel container at one time. + /// The default value will be 3UP in tablet and 1UP in mobile. open var layout: CarouselScrollbar.Layout = UIDevice.isIPad ? .threeUP : .oneUP { didSet { carouselScrollBar.position = 0 @@ -116,11 +115,12 @@ open class Carousel: View { /// The default value will be 12px in tablet and 8px in mobile. These values are the default in order to avoid overlapping content within the carousel. open var paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X { didSet { updatePaginationInset() } } - /// Options for user to configure the partially-visible tile in group. Setting peek to 'none' will display arrow navigation icons on mobile devices. + /// Options for user to configure the partially-visible tile in group. + /// Setting peek to 'none' will display arrow navigation icons on mobile devices. open var peek: Peek = .standard { didSet { setNeedsUpdate() } } /// The initial visible slide's index in the carousel. - open var selectedIndex: Int? { didSet { setNeedsUpdate() } } + open var groupIndex: Int? { didSet { setNeedsUpdate() } } /// If provided, will set the alignment for slot content when the slots has different heights. open var slotAlignment: CarouselSlotAlignmentModel? = .init(vertical: .top, horizontal: .left) { didSet { setNeedsUpdate() } } @@ -184,15 +184,13 @@ open class Carousel: View { private var containerViewHeightConstraint: NSLayoutConstraint? private var prevButtonLeadingConstraint: NSLayoutConstraint? private var nextButtonTrailingConstraint: NSLayoutConstraint? - private var containerLeadingConstraint: NSLayoutConstraint? // The scrollbar has top 5X space. So the expected top space is adjusted for tablet and mobile. let scrollbarTopSpace = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space1X - + var slotDefaultHeight = 50.0 var peekMinimum = 24.0 var minimumSlotWidth = 0.0 - var carouselScrollbarMinWidth = 96.0 //-------------------------------------------------- // MARK: - Lifecycle @@ -280,10 +278,10 @@ open class Carousel: View { updatePaginationControls() addCarouselSlots() - // If selectedIndex is received, the carousel should update its position. - if let selectedIndex { + // If groupIndex is received, the carousel should update its position. + if let groupIndex { let totalPos = totalPositions() - carouselScrollBar.position = selectedIndex >= totalPos ? totalPos : selectedIndex+1 + carouselScrollBar.position = groupIndex >= totalPos ? totalPos : groupIndex+1 } } @@ -295,7 +293,7 @@ open class Carousel: View { pagination = .init(kind: .lowContrast, floating: true) paginationDisplay = .none paginationInset = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X - gutter = UIDevice.isIPad ? .twentyFourPX : .twelvePX + gutter = UIDevice.isIPad ? .gutter6X : .gutter3X peek = .standard } From 938364535b4a7dcde81cbd74a9343ec44102457a Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 19 Jul 2024 14:42:00 +0530 Subject: [PATCH 042/113] Digital ACT-191 ONEAPP-7013 story: removed redundant code which causes scrolling issue --- VDS/Components/Carousel/Carousel.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 0cc6d738..b7c0477a 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -304,12 +304,6 @@ open class Carousel: View { nextButton.onClick = { _ in self.nextButtonClick() } previousButton.onClick = { _ in self.previousButtonClick() } - /// Will be called when the scrubber position changes. - carouselScrollBar.onScrubberDrag = { [weak self] scrubberId in - guard let self else { return } - updateScrollPosition(position: scrubberId, callbackText:"onThumbPositionChange") - } - /// Will be called when the scrollbar thumb move forward. carouselScrollBar.onMoveForward = { [weak self] scrubberId in guard let self else { return } From 99fe7e22ac6462621e0bdaf4fd02f2689cca96f1 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 19 Jul 2024 16:41:40 +0530 Subject: [PATCH 043/113] Digital ACT-191 ONEAPP-7013 story: fixed observed issue on content scroll while changing peek, gutter and layout. --- VDS/Components/Carousel/Carousel.swift | 44 +++++++++----------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index b7c0477a..3a754ae8 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -120,7 +120,7 @@ open class Carousel: View { open var peek: Peek = .standard { didSet { setNeedsUpdate() } } /// The initial visible slide's index in the carousel. - open var groupIndex: Int? { didSet { setNeedsUpdate() } } + open var groupIndex: Int = 0 { didSet { setNeedsUpdate() } } /// If provided, will set the alignment for slot content when the slots has different heights. open var slotAlignment: CarouselSlotAlignmentModel? = .init(vertical: .top, horizontal: .left) { didSet { setNeedsUpdate() } } @@ -277,12 +277,6 @@ open class Carousel: View { updatePaginationControls() addCarouselSlots() - - // If groupIndex is received, the carousel should update its position. - if let groupIndex { - let totalPos = totalPositions() - carouselScrollBar.position = groupIndex >= totalPos ? totalPos : groupIndex+1 - } } /// Resets to default settings. @@ -304,6 +298,12 @@ open class Carousel: View { nextButton.onClick = { _ in self.nextButtonClick() } previousButton.onClick = { _ in self.previousButtonClick() } + /// Will be called when the scrubber position changes. + carouselScrollBar.onScrubberDrag = { [weak self] scrubberId in + guard let self else { return } + updateScrollPosition(position: scrubberId, callbackText:"onThumbPositionChange") + } + /// Will be called when the scrollbar thumb move forward. carouselScrollBar.onMoveForward = { [weak self] scrubberId in guard let self else { return } @@ -484,7 +484,7 @@ open class Carousel: View { break } } - minimumSlotWidth = minimumSlotWidth / CGFloat(layout.value) + minimumSlotWidth = ceil(minimumSlotWidth / CGFloat(layout.value)) } private func nextButtonClick() { @@ -533,27 +533,18 @@ open class Carousel: View { let isScrollbarSuppressed = views.count > 0 && layout.value == views.count let isPeekMinimumOnTablet = UIDevice.isIPad && peek == .minimum if !isScrollbarSuppressed { - let subpart = minimumSlotWidth + gutter.value - let xPosition = CGFloat( Float(position-1) * Float(layout.value) * Float(subpart)) - switch peek { - case .standard: - if UIDevice.isIPad { - xPos = xPosition - (minimumSlotWidth/(CGFloat(layout.value) + 3))/2 - } else if layout == .oneUP { - xPos = xPosition - gutter.value - (minimumSlotWidth/4)/2 - } - case .minimum: - xPos = isPeekMinimumOnTablet ? xPosition : xPosition - peekMinimum - case .none: - xPos = xPosition - } + let slotWidthWithGutter = minimumSlotWidth + gutter.value + let xPosition = CGFloat( Float(position-1) * Float(layout.value) * Float(slotWidthWithGutter)) + let peekWidth = (containerView.frame.size.width - gutter.value - (Double(layout.value) * (minimumSlotWidth + gutter.value)))/2 + xPos = (peek == .none || isPeekMinimumOnTablet) ? xPosition : xPosition - gutter.value - peekWidth } } carouselScrollBar.scrubberId = position+1 let yPos = scrollView.contentOffset.y - scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true) + scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: false) showPaginationControls() - onChangePublisher.send(position-1) + groupIndex = position-1 + onChangePublisher.send(groupIndex) } } @@ -571,9 +562,4 @@ extension Carousel: UIScrollViewDelegate { updateScrollbarPosition(targetContentOffsetXPos: targetContentOffset.pointee.x) } - public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { - var visibleRect = CGRect() - visibleRect.origin = scrollView.contentOffset - updateScrollbarPosition(targetContentOffsetXPos: visibleRect.origin.x) - } } From 7c431974ac02324bce273bb965b351a52e12497f Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 19 Jul 2024 18:04:50 +0530 Subject: [PATCH 044/113] minor changes --- VDS/Components/Carousel/Carousel.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 3a754ae8..e05b94ea 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -56,7 +56,7 @@ open class Carousel: View { case left, center, right } - /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. + /// Space between each tile. The default value will be 6X in tablet and 3X in mobile. public enum Gutter: String, CaseIterable , DefaultValuing { case gutter3X = "3X" case gutter6X = "6X" @@ -79,7 +79,7 @@ open class Carousel: View { /// views used to render view in the carousel slots. open var views: [UIView] = [] { didSet { setNeedsUpdate() } } - /// Space between each tile. The default value will be 24px (6X) in tablet and 12px (3X) in mobile. + /// Space between each tile. The default value will be 6X in tablet and 3X in mobile. open var gutter: Gutter = Gutter.defaultValue { didSet { setNeedsUpdate() } } /// The amount of slides visible in the carousel container at one time. @@ -112,7 +112,7 @@ open class Carousel: View { open var paginationDisplay: PaginationDisplay = .none { didSet {setNeedsUpdate() } } /// If provided, will apply margin to pagination arrows. Can be set to either positive or negative values. - /// The default value will be 12px in tablet and 8px in mobile. These values are the default in order to avoid overlapping content within the carousel. + /// The default value will be 3X in tablet and 2X in mobile. These values are the default in order to avoid overlapping content within the carousel. open var paginationInset: CGFloat = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X { didSet { updatePaginationInset() } } /// Options for user to configure the partially-visible tile in group. From afc4d71f38104afaf89985db11240830ded45f5a Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 19 Jul 2024 20:54:03 +0530 Subject: [PATCH 045/113] updated scroll animation to true --- VDS/Components/Carousel/Carousel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index e05b94ea..3e8aed2d 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -541,7 +541,7 @@ open class Carousel: View { } carouselScrollBar.scrubberId = position+1 let yPos = scrollView.contentOffset.y - scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: false) + scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true) showPaginationControls() groupIndex = position-1 onChangePublisher.send(groupIndex) From e02ea9712e2ddd8b855876a9d02010f15935279b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 19 Jul 2024 12:28:14 -0500 Subject: [PATCH 046/113] put last part in else Signed-off-by: Matt Bruce --- .../TileContainer/TileContainer.swift | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 131a62f5..0852bca6 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -447,7 +447,7 @@ open class TileContainerBase: Control where Padding widthConstraint?.activate() aspectRatioConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: multiplier) aspectRatioConstraint?.activate() - return + } //------------------------------------------------------------------------- //Height + AspectRatio Constraint - Will exit out if set @@ -460,25 +460,26 @@ open class TileContainerBase: Control where Padding heightConstraint?.activate() aspectRatioConstraint = widthAnchor.constraint(equalTo: heightAnchor, multiplier: multiplier) aspectRatioConstraint?.activate() - return - } - //------------------------------------------------------------------------- - //Width Constraint - //------------------------------------------------------------------------- - if let containerViewWidth, - containerViewWidth > 0 { - widthConstraint?.constant = containerViewWidth - widthConstraint?.activate() - } - - //------------------------------------------------------------------------- - //Height Constraint - //------------------------------------------------------------------------- - if let containerViewHeight, - containerViewHeight > 0 { - heightConstraint?.constant = containerViewHeight - heightConstraint?.activate() + } else { + + //------------------------------------------------------------------------- + //Width Constraint + //------------------------------------------------------------------------- + if let containerViewWidth, + containerViewWidth > 0 { + widthConstraint?.constant = containerViewWidth + widthConstraint?.activate() + } + + //------------------------------------------------------------------------- + //Height Constraint + //------------------------------------------------------------------------- + if let containerViewHeight, + containerViewHeight > 0 { + heightConstraint?.constant = containerViewHeight + heightConstraint?.activate() + } } } From 00f73a503c03ed40c3684410dd232b23ce15015c Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 19 Jul 2024 13:13:21 -0500 Subject: [PATCH 047/113] CXTDT-581800 - Date Picker - Selected Error state icon Signed-off-by: Matt Bruce --- VDS/Components/DatePicker/DatePicker.swift | 18 +++++++++++++++++- VDS/SupportingFiles/ReleaseNotes.txt | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index 83450403..3a41780e 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -27,6 +27,19 @@ open class DatePicker: EntryFieldBase { /// A callback when the selected option changes. Passes parameters (option). open var onDateSelected: ((Date, DatePicker) -> Void)? + /// Override UIControl state to add the .error state if showError is true. + open override var state: UIControl.State { + get { + var state = super.state + if isEnabled { + if isCalendarShowing { + state.insert(.focused) + } + } + return state + } + } + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -35,8 +48,9 @@ open class DatePicker: EntryFieldBase { true } } - + internal override var responder: UIResponder? { hiddenView } + internal var isCalendarShowing: Bool = false { didSet { setNeedsUpdate() } } internal var hiddenView = Responder().with { $0.width(0) } internal var minWidthDefault = 186.0 internal var bottomStackView: UIStackView = { @@ -315,6 +329,7 @@ extension DatePicker { } } + isCalendarShowing = true } private func hidePopoverView() { @@ -346,6 +361,7 @@ extension DatePicker { UIAccessibility.post(notification: .layoutChanged, argument: containerView) } } + isCalendarShowing = false } private func calculatePopoverPosition(relativeTo sourceView: UIView, in parentView: UIView, size: CGSize, with spacing: CGFloat) -> CGPoint? { diff --git a/VDS/SupportingFiles/ReleaseNotes.txt b/VDS/SupportingFiles/ReleaseNotes.txt index 973e8ed8..bc182364 100644 --- a/VDS/SupportingFiles/ReleaseNotes.txt +++ b/VDS/SupportingFiles/ReleaseNotes.txt @@ -1,7 +1,10 @@ 1.0.71 ---------------- +- CXTDT-581800 - DatePicker - Selected Error state icon - CXTDT-581803 - DatePicker - Calendar does not switch to Dark Mode - CXTDT-584278 – InputField - Accessibility +- CXTDT-586375 - Table - Issue With Stripe +- CXTDT-577463 - InputField - Accessibility - #7 1.0.70 ---------------- From 54a2a63ba41633e753c9bc7d9f93c1301b9edbd8 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 19 Jul 2024 13:17:49 -0500 Subject: [PATCH 048/113] CXTDT-581801 - DatePicker - border disappears for on dark focus state Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 4 ++-- VDS/SupportingFiles/ReleaseNotes.txt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 78c1077a..fbae1563 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -137,8 +137,8 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { internal var borderColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOndark, forState: .normal) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forState: .focused) - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forState: [.focused, .error]) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .focused) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: [.focused, .error]) $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) $0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) $0.setSurfaceColors(VDSFormControlsColor.borderReadonlyOnlight, VDSFormControlsColor.borderReadonlyOndark, forState: .readonly) diff --git a/VDS/SupportingFiles/ReleaseNotes.txt b/VDS/SupportingFiles/ReleaseNotes.txt index bc182364..6450add8 100644 --- a/VDS/SupportingFiles/ReleaseNotes.txt +++ b/VDS/SupportingFiles/ReleaseNotes.txt @@ -1,6 +1,7 @@ 1.0.71 ---------------- - CXTDT-581800 - DatePicker - Selected Error state icon +- CXTDT-581801 - DatePicker - border disappears for on dark focus state - CXTDT-581803 - DatePicker - Calendar does not switch to Dark Mode - CXTDT-584278 – InputField - Accessibility - CXTDT-586375 - Table - Issue With Stripe From a253e2efc86655ae2ef5e636b484f9e41e6c7a77 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 19 Jul 2024 13:28:08 -0500 Subject: [PATCH 049/113] =?UTF-8?q?CXTDT-565796=20-=20DropdownSelect=20?= =?UTF-8?q?=E2=80=93=20Removed=20the=20"Type"=20from=20the=20VoiceOver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 4 +--- VDS/SupportingFiles/ReleaseNotes.txt | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index fbae1563..571783bc 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -331,9 +331,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { if let errorText, showError { accessibilityLabels.append("error, \(errorText)") } - - accessibilityLabels.append("\(Self.self)") - + return accessibilityLabels.joined(separator: ", ") } diff --git a/VDS/SupportingFiles/ReleaseNotes.txt b/VDS/SupportingFiles/ReleaseNotes.txt index 6450add8..69b82096 100644 --- a/VDS/SupportingFiles/ReleaseNotes.txt +++ b/VDS/SupportingFiles/ReleaseNotes.txt @@ -6,6 +6,7 @@ - CXTDT-584278 – InputField - Accessibility - CXTDT-586375 - Table - Issue With Stripe - CXTDT-577463 - InputField - Accessibility - #7 +- CXTDT-565796 - DropdownSelect – Removed the "Type" from the VoiceOver 1.0.70 ---------------- From 7cabfdb78cb9cf8260a2796d23c339bcf0b2f8e3 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 19 Jul 2024 13:29:14 -0500 Subject: [PATCH 050/113] updated version to 71 Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 59600512..230ce227 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -1557,7 +1557,7 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 70; + CURRENT_PROJECT_VERSION = 71; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1595,7 +1595,7 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 70; + CURRENT_PROJECT_VERSION = 71; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; From 2a1b9380c82d192ef3278c79e69be0e4042c4f73 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 19 Jul 2024 13:42:57 -0500 Subject: [PATCH 051/113] updated comments Signed-off-by: Matt Bruce --- VDS/Components/TileContainer/TileContainer.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 0852bca6..cbe68e3a 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -437,7 +437,7 @@ open class TileContainerBase: Control where Padding //------------------------------------------------------------------------- - //Width + AspectRatio Constraint - Will exit out if set + //Width + AspectRatio Constraint //------------------------------------------------------------------------- if let containerViewWidth, let multiplier, @@ -450,7 +450,7 @@ open class TileContainerBase: Control where Padding } //------------------------------------------------------------------------- - //Height + AspectRatio Constraint - Will exit out if set + //Height + AspectRatio Constraint //------------------------------------------------------------------------- else if let containerViewHeight, let multiplier, From 0210e0d1280f35a2a31844bdf07881440b5653f6 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Wed, 24 Jul 2024 20:40:16 +0530 Subject: [PATCH 052/113] Digital ACT-191 ONEAPP-9311 story: control width can be set to auto (default) or value (pixel) , updating stepper view when size changes. --- .../DropdownSelect/DropdownSelect.swift | 2 +- .../InputStepper/InputStepper.swift | 184 ++++++++++++------ .../TextFields/EntryFieldBase.swift | 42 +++- .../TextFields/InputField/InputField.swift | 2 +- 4 files changed, 161 insertions(+), 69 deletions(-) diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 7fc52808..59b48ac9 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -299,7 +299,7 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { dropdownField.resignFirstResponder() } optionsPicker.isHidden = !optionsPicker.isHidden - updateContainerView() + updateContainerView(flag: true) updateErrorLabel() } diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index d630d1d1..57cdf756 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -40,17 +40,17 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - open var controlWidth: String? { - get { _controlWidth } - set { - setControlWidth(newValue) - setNeedsUpdate() - } - } - + /// Accepts a string or number value to control the width of input stepper. + /// auto(default) - The control's width is determined by the combined width of the value, trailing text and padding + /// Value - The control's width can be set to a fixed pixel or percentage value. + open var controlWidth: String? = "auto" { didSet { setNeedsUpdate() } } + /// Default value of the input stepper, defaults to '0'. open var defaultValue:Int = 0 { didSet { setNeedsUpdate() } } + /// Allows an id to be passed to input stepper. + open var id: Int? { didSet { setNeedsUpdate() } } + /// Maximum value of the input stepper, defaults to '99'. open var maxValue: Int? { get { return _maxValue } @@ -63,7 +63,7 @@ open class InputStepper: EntryFieldBase { setNeedsUpdate() } } - + /// Minimum value of the input stepper, defaults to '0'. open var minValue: Int? { get { return _minValue } @@ -78,11 +78,10 @@ open class InputStepper: EntryFieldBase { } /// The size of the input stepper. Defaults to 'large'. - open var size: Size { - get { return _size } - set { - _size = newValue - updateSize() + open var size: Size = .large { + didSet { + updateStepperContainerViewSize() + setNeedsUpdate() } } @@ -92,15 +91,23 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var _controlWidth = "auto" internal var _maxValue: Int = 99 internal var _minValue: Int = 0 - internal var _size: Size = .large - private var largeMinWidth = 121 - private var smallMinWidth = 90 + /// This is the view that will be wrapped with the border for userInteraction. + /// The only subview of this view is the stepperStackView. + internal var stepperContainerView = View().with { + $0.isAccessibilityElement = true + } - let decrementButton = ButtonIcon().with { + internal var stepperStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .horizontal + $0.distribution = .fill + $0.alignment = .fill + } + + internal var decrementButton = ButtonIcon().with { $0.kind = .ghost $0.iconName = Icon.Name(name: "minus") $0.iconOffset = .init(x: -2, y: 0) @@ -109,7 +116,7 @@ open class InputStepper: EntryFieldBase { $0.backgroundColor = .clear } - let incrementButton = ButtonIcon().with { + internal var incrementButton = ButtonIcon().with { $0.kind = .ghost $0.iconName = Icon.Name(name: "plus") $0.iconOffset = .init(x: 2, y: 0) @@ -118,17 +125,31 @@ open class InputStepper: EntryFieldBase { $0.backgroundColor = .clear } - let textLabel = Label().with { + internal var textLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textStyle = .boldBodyLarge - $0.backgroundColor = .clear + $0.numberOfLines = 1 + $0.lineBreakMode = .byTruncatingTail } - + //-------------------------------------------------- - // MARK: - Configuration + // MARK: - Constraints //-------------------------------------------------- - private var labelColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight , VDSColor.elementsPrimaryOndark) - private var labelDisabledColorConfiguration = SurfaceColorConfiguration(VDSColor.interactiveDisabledOnlight , VDSColor.interactiveDisabledOndark) + internal var stepperWidthConstraint: NSLayoutConstraint? + internal var stepperHeightConstraint: NSLayoutConstraint? + + //-------------------------------------------------- + // MARK: - Configuration Properties + //-------------------------------------------------- + internal override var containerSize: CGSize { CGSize(width: size == .large ? largeMinWidth : smallMinWidth, height: size == .large ? largeMinHeight : smallMinHeight) } + + internal var largeMinWidth = 121 + internal var smallMinWidth = 90 + internal var largeMinHeight = 44 + internal var smallMinHeight = 32 + + internal let labelColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight , VDSColor.elementsPrimaryOndark) + internal let labelDisabledColorConfiguration = SurfaceColorConfiguration(VDSColor.interactiveDisabledOnlight , VDSColor.interactiveDisabledOndark) //-------------------------------------------------- // MARK: - Lifecycle @@ -137,38 +158,68 @@ open class InputStepper: EntryFieldBase { super.initialSetup() } + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() + + // accessibility isAccessibilityElement = false accessibilityLabel = "Input Stepper" + + // Set initial states containerView.isEnabled = false + statusIcon.isHidden = true + + // Add listeners decrementButton.onClick = { _ in self.decrementButtonClick() } incrementButton.onClick = { _ in self.incrementButtonClick() } + + // setting color config + textLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() } open override func getFieldContainer() -> UIView { - // stackview for controls in EntryFieldBase.controlContainerView - let controlStackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.axis = .horizontal - $0.spacing = VDSLayout.space3X - $0.backgroundColor = .clear - } - controlStackView.addArrangedSubview(decrementButton) - controlStackView.addArrangedSubview(textLabel) - controlStackView.addArrangedSubview(incrementButton) - return controlStackView + stepperStackView.addArrangedSubview(decrementButton) + stepperStackView.addArrangedSubview(textLabel) + stepperStackView.addArrangedSubview(incrementButton) + + // Set space between decrement button, label, and increment button relative to input Stepper size. + let space = size == .large ? VDSLayout.space3X : VDSLayout.space2X + stepperStackView.setCustomSpacing(space, after: decrementButton) + stepperStackView.setCustomSpacing(space, after: textLabel) + + // Update Edge insets relative to input Stepper size. + stepperStackView.pinToSuperView(.uniform(size == .large ? 6.0 : VDSLayout.space1X)) + + // stepperContainerView for controls in EntryFieldBase.controlContainerView + stepperContainerView.addSubview(stepperStackView) + + stepperWidthConstraint = stepperContainerView.widthAnchor.constraint(equalToConstant: containerSize.width) + stepperWidthConstraint?.deactivate() + stepperHeightConstraint = stepperContainerView.heightAnchor.constraint(equalToConstant: containerSize.height) + stepperHeightConstraint?.deactivate() + + return stepperContainerView } + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() - updateContainerView(flag: false) - textLabel.text = String(defaultValue) + " " + (trailingText ?? "") - decrementButton.surface = surface - incrementButton.surface = surface - textLabel.surface = surface statusIcon.isHidden = true + updateConstraintsToFieldStackView(flag: false) + + // Update label text, style, color, ande surface. + textLabel.text = String(defaultValue) + " " + (trailingText ?? "") + textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall + textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable() + textLabel.surface = surface + + // Update increment and decrement button. updateButtonStates() + + // Update stepper container border and corner radius. + setControlWidth(controlWidth) + updateContainerView(flag: false) } /// Resets to default settings. @@ -204,7 +255,11 @@ open class InputStepper: EntryFieldBase { } internal func updateButtonStates() { - textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable() + decrementButton.customContainerSize = size == .large ? 32 : 24 + incrementButton.customContainerSize = size == .large ? 32 : 24 + decrementButton.surface = surface + incrementButton.surface = surface + if isReadOnly || !isEnabled { decrementButton.isEnabled = false incrementButton.isEnabled = false @@ -214,27 +269,42 @@ open class InputStepper: EntryFieldBase { } } - internal func updateSize() { - let value = size == .large ? 6.0 : VDSLayout.space1X - updateConstraintsToFieldStackView(value: value) + // Update edge insets and height when size changes. + internal func updateStepperContainerViewSize() { + updateButtonStates() - // textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall - // textLabel.heightAnchor.constraint(equalToConstant: size == .large ? 44 : 32).activate() - - // decrementButton.customContainerSize = size == .large ? 32 : 24 - // incrementButton.customContainerSize = size == .large ? 32 : 24 + // Update Edge insets if size changes applied. + stepperStackView.removeFromSuperview() + stepperContainerView.addSubview(stepperStackView) + stepperStackView.pinToSuperView(.uniform(size == .large ? 6.0 : VDSLayout.space1X)) + // Update height if size changes applied. + stepperHeightConstraint?.deactivate() + stepperHeightConstraint = stepperContainerView.heightAnchor.constraint(equalToConstant: containerSize.height) + stepperHeightConstraint?.activate() } + // Set control width to input stepper. internal func setControlWidth(_ text: String?) { if let text, text == "auto" { - // Set fixed width relative to default value, trailing text label + stepperWidthConstraint?.deactivate() } else if let controlWidth = Int(text ?? "") { - // Use provided new width either pixel or percentage - width = CGFloat(controlWidth) - } else { - // Use EntryFieldBase width - } + // Set controlWidth provided which is either pixel or percentage + let width = width ?? CGFloat(containerView.frame.size.width) + updateStepperContainerWidth(controlWidth: CGFloat(controlWidth), width: width) + } } + // Handling the controlwidth without going beyond the width of the parent container. + internal func updateStepperContainerWidth(controlWidth: CGFloat, width: CGFloat) { + if controlWidth >= containerSize.width && controlWidth <= width { + stepperWidthConstraint?.deactivate() + stepperWidthConstraint?.constant = controlWidth + stepperWidthConstraint?.activate() + } else if controlWidth >= width { + stepperWidthConstraint?.deactivate() + stepperWidthConstraint?.constant = width + stepperWidthConstraint?.activate() + } + } } diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 2ef89480..bf75d747 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -227,7 +227,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { get { fatalError("must be read from subclass")} } - open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } +// open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } open var isRequired: Bool = false { didSet { setNeedsUpdate() } } @@ -346,7 +346,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { /// Updates the UI open override func updateView() { super.updateView() - updateContainerView() + updateContainerView(flag: true) updateContainerWidth() updateTitleLabel() updateErrorLabel() @@ -372,7 +372,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { transparentBackground = false width = nil inputId = nil - defaultValue = nil +// defaultValue = nil isRequired = false isReadOnly = false onChange = nil @@ -401,7 +401,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open func getFieldContainer() -> UIView { fatalError("Subclass must return the view that contains the field/view the user will interact with.") } - + /// Container for the area in which helper or error text presents. open func getBottomContainer() -> UIView { return bottomContainerStackView @@ -520,13 +520,35 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { } } - internal func updateContainerView() { - containerView.backgroundColor = containerBackgroundColor - containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor - containerView.layer.borderWidth = VDSFormControls.borderWidth - containerView.layer.cornerRadius = VDSFormControls.borderRadius + internal func updateContainerView(flag: Bool) { + if flag { + containerView.backgroundColor = containerBackgroundColor + containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor + containerView.layer.borderWidth = VDSFormControls.borderWidth + containerView.layer.cornerRadius = VDSFormControls.borderRadius + } else { + containerView.backgroundColor = .clear + containerView.layer.borderColor = nil + containerView.layer.borderWidth = 0 + containerView.layer.cornerRadius = 0 + fieldStackView.backgroundColor = containerBackgroundColor + fieldStackView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor + fieldStackView.layer.borderWidth = VDSFormControls.borderWidth + fieldStackView.layer.cornerRadius = containerView.frame.size.height / 2 + } } - + + /// Update constraints to containerStackView which has horizontal stack in which user interacts. + internal func updateConstraintsToFieldStackView(flag: Bool) { + fieldStackView.removeFromSuperview() + containerView.addSubview(fieldStackView) + if flag { + fieldStackView.pinToSuperView(.uniform(VDSLayout.space3X)) + } else { + fieldStackView.pinTop().pinLeading().pinBottom().pinTrailingLessThanOrEqualTo() + } + } + internal func updateContainerWidth() { widthConstraint?.deactivate() trailingLessThanEqualsConstraint?.deactivate() diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index c2f779d4..39074cf1 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -287,7 +287,7 @@ open class InputField: EntryFieldBase { extension InputField: UITextFieldDelegate { public func textFieldDidBeginEditing(_ textField: UITextField) { fieldType.handler().textFieldDidBeginEditing(self, textField: textField) - updateContainerView() + updateContainerView(flag: true) updateErrorLabel() } From 781df9ff1a6d7da8ed09dde37771038e3842e8c3 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 25 Jul 2024 10:45:20 -0500 Subject: [PATCH 053/113] added objcMembers Signed-off-by: Matt Bruce --- VDS/BaseClasses/Control.swift | 1 + VDS/BaseClasses/Selector/SelectorBase.swift | 2 ++ VDS/BaseClasses/View.swift | 1 + VDS/Classes/AlertViewController.swift | 2 ++ VDS/Classes/ClearPopoverViewController.swift | 2 ++ 5 files changed, 8 insertions(+) diff --git a/VDS/BaseClasses/Control.swift b/VDS/BaseClasses/Control.swift index 7aa08717..472f0635 100644 --- a/VDS/BaseClasses/Control.swift +++ b/VDS/BaseClasses/Control.swift @@ -10,6 +10,7 @@ import UIKit import Combine /// Base Class use to build Controls. +@objcMembers @objc(VDSControl) open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { //-------------------------------------------------- diff --git a/VDS/BaseClasses/Selector/SelectorBase.swift b/VDS/BaseClasses/Selector/SelectorBase.swift index fb8d771e..1e2f5fba 100644 --- a/VDS/BaseClasses/Selector/SelectorBase.swift +++ b/VDS/BaseClasses/Selector/SelectorBase.swift @@ -28,6 +28,8 @@ public protocol SelectorControlable: Control, Changeable { } /// Base Class used to build out a Selector control. +@objcMembers +@objc(VDSSelectorBase) open class SelectorBase: Control, SelectorControlable { //-------------------------------------------------- // MARK: - Initializers diff --git a/VDS/BaseClasses/View.swift b/VDS/BaseClasses/View.swift index 7e88df8e..88996ba6 100644 --- a/VDS/BaseClasses/View.swift +++ b/VDS/BaseClasses/View.swift @@ -10,6 +10,7 @@ import UIKit import Combine /// Base Class used to build Views. +@objcMembers @objc(VDSView) open class View: UIView, ViewProtocol, UserInfoable { diff --git a/VDS/Classes/AlertViewController.swift b/VDS/Classes/AlertViewController.swift index 30a5d5c6..5472fab8 100644 --- a/VDS/Classes/AlertViewController.swift +++ b/VDS/Classes/AlertViewController.swift @@ -10,6 +10,8 @@ import UIKit import Combine import VDSCoreTokens +@objcMembers +@objc(VDSAlertViewController) open class AlertViewController: UIViewController, Surfaceable { /// Set of Subscribers for any Publishers for this Control. diff --git a/VDS/Classes/ClearPopoverViewController.swift b/VDS/Classes/ClearPopoverViewController.swift index 6f9bcb67..d2137b62 100644 --- a/VDS/Classes/ClearPopoverViewController.swift +++ b/VDS/Classes/ClearPopoverViewController.swift @@ -8,6 +8,8 @@ import Foundation import UIKit +@objcMembers +@objc(VDSClearPopoverViewController) open class ClearPopoverViewController: UIViewController, UIPopoverPresentationControllerDelegate { /// The view to be inserted inside the popover From 6bf3e19ebe23275f4207282833d12b4ee23de3d7 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 25 Jul 2024 10:53:26 -0500 Subject: [PATCH 054/113] added @objcMembers to all classes Signed-off-by: Matt Bruce --- VDS/Components/Badge/Badge.swift | 1 + .../BadgeIndicator/BadgeIndicator.swift | 1 + .../Breadcrumbs/BreadcrumbItem.swift | 1 + VDS/Components/Breadcrumbs/Breadcrumbs.swift | 1 + VDS/Components/Buttons/Button/Button.swift | 1 + VDS/Components/Buttons/ButtonBase.swift | 1 + .../Buttons/ButtonGroup/ButtonGroup.swift | 1 + .../Buttons/TextLink/TextLink.swift | 1 + .../Buttons/TextLinkCaret/TextLinkCaret.swift | 1 + VDS/Components/Calendar/Calendar.swift | 1 + VDS/Components/Carousel/Carousel.swift | 1 + .../CarouselScrollbar/CarouselScrollbar.swift | 1 + VDS/Components/Checkbox/Checkbox.swift | 1 + VDS/Components/Checkbox/CheckboxGroup.swift | 1 + VDS/Components/Checkbox/CheckboxItem.swift | 29 +++++++++++++++++++ VDS/Components/DatePicker/DatePicker.swift | 1 + .../DropdownSelect/DropdownSelect.swift | 1 + VDS/Components/Icon/Icon.swift | 1 + VDS/Components/Label/Label.swift | 1 + VDS/Components/Line/Line.swift | 1 + VDS/Components/Loader/Loader.swift | 1 + .../Loader/LoaderViewController.swift | 2 ++ .../Notification/Notification.swift | 1 + VDS/Components/Pagination/Pagination.swift | 1 + .../Pagination/PaginationButton.swift | 1 + VDS/Components/RadioBox/RadioBoxGroup.swift | 1 + VDS/Components/RadioBox/RadioBoxItem.swift | 1 + VDS/Components/RadioButton/RadioButton.swift | 1 + .../RadioButton/RadioButtonGroup.swift | 1 + .../RadioButton/RadioButtonItem.swift | 1 + VDS/Components/Table/Table.swift | 1 + VDS/Components/Tabs/Tab.swift | 2 +- VDS/Components/Tabs/Tabs.swift | 1 + VDS/Components/Tabs/TabsContainer.swift | 1 + .../TextFields/EntryFieldBase.swift | 1 + .../TextFields/InputField/InputField.swift | 19 ++++++++++++ .../TextFields/InputField/TextField.swift | 1 + .../TextFields/TextArea/TextArea.swift | 1 + .../TextFields/TextArea/TextView.swift | 1 + .../TileContainer/TileContainer.swift | 1 + VDS/Components/Tilelet/Tilelet.swift | 1 + VDS/Components/TitleLockup/TitleLockup.swift | 1 + VDS/Components/Toggle/Toggle.swift | 1 + VDS/Components/Toggle/ToggleView.swift | 1 + VDS/Components/Tooltip/Tooltip.swift | 1 + .../Tooltip/TooltipAlertViewController.swift | 2 ++ VDS/Components/Tooltip/TooltipDialog.swift | 2 ++ 47 files changed, 96 insertions(+), 1 deletion(-) diff --git a/VDS/Components/Badge/Badge.swift b/VDS/Components/Badge/Badge.swift index 5753428e..07477f5f 100644 --- a/VDS/Components/Badge/Badge.swift +++ b/VDS/Components/Badge/Badge.swift @@ -15,6 +15,7 @@ import Combine /// If you are using AutoLayoutConstraints you have a combination of Leading/Left and Trailing/Right NSLayoutConstraints, /// you need to ensure that one of these Horizontal Contraints is not constraint of "equatTo". If you are to pin the left/right edges /// to its parent this object will stretch to the parent's width. +@objcMembers @objc(VDSBadge) open class Badge: View { diff --git a/VDS/Components/BadgeIndicator/BadgeIndicator.swift b/VDS/Components/BadgeIndicator/BadgeIndicator.swift index 60025390..13a6cb21 100644 --- a/VDS/Components/BadgeIndicator/BadgeIndicator.swift +++ b/VDS/Components/BadgeIndicator/BadgeIndicator.swift @@ -11,6 +11,7 @@ import VDSCoreTokens import Combine /// A badge indicator is a visual label used to convey status or highlight supplemental information. +@objcMembers @objc(VDSBadgeIndicator) open class BadgeIndicator: View { diff --git a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift index bf6d4ed1..66f62989 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift @@ -13,6 +13,7 @@ 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. +@objcMembers @objc (VDSBreadcrumbItem) open class BreadcrumbItem: ButtonBase { diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index b5256497..5a2de7d0 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -13,6 +13,7 @@ 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. +@objcMembers @objc(VDSBreadcrumbs) open class Breadcrumbs: View { diff --git a/VDS/Components/Buttons/Button/Button.swift b/VDS/Components/Buttons/Button/Button.swift index f4147f54..0b9d13f1 100644 --- a/VDS/Components/Buttons/Button/Button.swift +++ b/VDS/Components/Buttons/Button/Button.swift @@ -15,6 +15,7 @@ import Combine /// If you are using AutoLayoutConstraints you have a combination of Leading/Left and Trailing/Right NSLayoutConstraints, /// you need to ensure that one of these Horizontal Contraints is not constraint of "equatTo". If you are to pin the left/right edges /// to its parent this object will stretch to the parent's width. +@objcMembers @objc(VDSButton) open class Button: ButtonBase, Useable { diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index eebf3a68..c764b89b 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -11,6 +11,7 @@ import VDSCoreTokens import Combine /// Base class used for UIButton type classes. +@objcMembers @objc(VDSButtonBase) open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift index 124a7fba..8b662070 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift @@ -11,6 +11,7 @@ import VDSCoreTokens 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. +@objcMembers @objc(VDSButtonGroup) open class ButtonGroup: View { diff --git a/VDS/Components/Buttons/TextLink/TextLink.swift b/VDS/Components/Buttons/TextLink/TextLink.swift index f77dfa79..70f2840b 100644 --- a/VDS/Components/Buttons/TextLink/TextLink.swift +++ b/VDS/Components/Buttons/TextLink/TextLink.swift @@ -16,6 +16,7 @@ import Combine /// If you are using AutoLayoutConstraints you have a combination of Leading/Left and Trailing/Right NSLayoutConstraints, /// you need to ensure that one of these Horizontal Contraints is not constraint of "equatTo". If you are to pin the left/right edges /// to its parent this object will stretch to the parent's width. +@objcMembers @objc(VDSTextLink) open class TextLink: ButtonBase { //-------------------------------------------------- diff --git a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift index d1522d02..f8c86a44 100644 --- a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift +++ b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift @@ -16,6 +16,7 @@ import Combine /// If you are using AutoLayoutConstraints you have a combination of Leading/Left and Trailing/Right NSLayoutConstraints, /// you need to ensure that one of these Horizontal Contraints is not constraint of "equatTo". If you are to pin the left/right edges /// to its parent this object will stretch to the parent's width. +@objcMembers @objc(VDSTextLinkCaret) open class TextLinkCaret: ButtonBase { //-------------------------------------------------- diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index cd1c783b..bed772d8 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -11,6 +11,7 @@ import VDSCoreTokens import Combine /// A calendar is a monthly view that lets customers select a single date. +@objcMembers @objc(VDSCalendar) open class CalendarBase: Control, Changeable { diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 3e8aed2d..d1624253 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -12,6 +12,7 @@ import Combine /// A carousel is a collection of related content in a row that a customer can navigate through horizontally. /// Use this component to show content that is supplementary, not essential for task completion. +@objcMembers @objc(VDSCarousel) open class Carousel: View { diff --git a/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift b/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift index 1e05d875..f171c395 100644 --- a/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift +++ b/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift @@ -12,6 +12,7 @@ import Combine /// A carousel scrollbar is a control that allows to navigate between items in a carousel. /// It's also a status indicator that conveys the relative amount of content in a carousel and a location within it. +@objcMembers @objc(VDSCarouselScrollbar) open class CarouselScrollbar: View { diff --git a/VDS/Components/Checkbox/Checkbox.swift b/VDS/Components/Checkbox/Checkbox.swift index 3369ebcb..5b2afdc5 100644 --- a/VDS/Components/Checkbox/Checkbox.swift +++ b/VDS/Components/Checkbox/Checkbox.swift @@ -12,6 +12,7 @@ import VDSCoreTokens /// Checkboxes are a multi-select component through which a customer indicates a choice. This is also used within /// ``CheckboxItem`` and ``CheckboxGroup`` +@objcMembers @objc(VDSCheckbox) open class Checkbox: SelectorBase { diff --git a/VDS/Components/Checkbox/CheckboxGroup.swift b/VDS/Components/Checkbox/CheckboxGroup.swift index b450d4d5..1431d9dc 100644 --- a/VDS/Components/Checkbox/CheckboxGroup.swift +++ b/VDS/Components/Checkbox/CheckboxGroup.swift @@ -12,6 +12,7 @@ import VDSCoreTokens /// When the choice has multiple options, use a checkbox group. For example, use a checkbox group when /// asking a customer which attributes they would like to filter their search by. This uses ``CheckboxItem`` /// to allow user selection. +@objcMembers @objc(VDSCheckboxGroup) open class CheckboxGroup: SelectorGroupBase, SelectorGroupMultiSelect { diff --git a/VDS/Components/Checkbox/CheckboxItem.swift b/VDS/Components/Checkbox/CheckboxItem.swift index 21943636..c115fe79 100644 --- a/VDS/Components/Checkbox/CheckboxItem.swift +++ b/VDS/Components/Checkbox/CheckboxItem.swift @@ -9,6 +9,7 @@ import Foundation import UIKit /// Checkboxes are a multi-select component through which a customer indicates a choice. If a binary choice, the component is a checkbox. If the choice has multiple options, the component is a ``CheckboxGroup``. +@objcMembers @objc(VDSCheckboxItem) open class CheckboxItem: SelectorItemBase { @@ -47,6 +48,13 @@ open class CheckboxItem: SelectorItemBase { isSelected.toggle() sendActions(for: .valueChanged) } + + open override func setup() { + super.setup() + let foo = ConcreteClass(customView: Checkbox()) + + print(foo.customView.isAnimated) + } /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { @@ -54,3 +62,24 @@ open class CheckboxItem: SelectorItemBase { super.updateView() } } + + +@objcMembers +open class GenericClass: NSObject { + public var customView: T + + public init(customView: T = T()) { + self.customView = customView + } +} + +@objcMembers +@objc(VDSConcreteClass) +open class ConcreteClass: GenericClass { + +} + +@objcMembers +@objc(VDSConcreteCheckboxClass) +open class ConcreteCheckboxClass: ConcreteClass {} + diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index 3a41780e..df7cc7c9 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -4,6 +4,7 @@ import VDSCoreTokens import Combine /// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection. +@objcMembers @objc(VDSDatePicker) open class DatePicker: EntryFieldBase { //-------------------------------------------------- diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 832349d2..947210f4 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -11,6 +11,7 @@ import VDSCoreTokens import Combine /// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection. +@objcMembers @objc(VDSDropdownSelect) open class DropdownSelect: EntryFieldBase { //-------------------------------------------------- diff --git a/VDS/Components/Icon/Icon.swift b/VDS/Components/Icon/Icon.swift index 140f7b87..fd5c5fb2 100644 --- a/VDS/Components/Icon/Icon.swift +++ b/VDS/Components/Icon/Icon.swift @@ -13,6 +13,7 @@ import Combine /// An icon is a graphical element that conveys information at a glance. It helps orient /// a customer, explain functionality and draw attention to interactive elements. Icons /// should have a functional purpose and should never be used for decoration. +@objcMembers @objc(VDSIcon) open class Icon: View { diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index e408953b..f420be7d 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -12,6 +12,7 @@ import Combine /// Label is a standard view used to draw text with applying Typography through ``TextStyle`` as well /// as other attributes using any implemetation of ``LabelAttributeModel``. +@objcMembers @objc(VDSLabel) open class Label: UILabel, ViewProtocol, UserInfoable { diff --git a/VDS/Components/Line/Line.swift b/VDS/Components/Line/Line.swift index e6edae70..627210f9 100644 --- a/VDS/Components/Line/Line.swift +++ b/VDS/Components/Line/Line.swift @@ -10,6 +10,7 @@ import UIKit import VDSCoreTokens /// A line visually separates content sections or elements in lists, tables and layouts to indicate content hierarchy. +@objcMembers @objc(VDSLine) open class Line: View { diff --git a/VDS/Components/Loader/Loader.swift b/VDS/Components/Loader/Loader.swift index 13ceeb43..5b7f1b59 100644 --- a/VDS/Components/Loader/Loader.swift +++ b/VDS/Components/Loader/Loader.swift @@ -11,6 +11,7 @@ import VDSCoreTokens /// A loader is an indicator that uses animation to show customers that there is an indefinite amount of wait time while a task is ongoing, e.g. a page is loading, a form is being submitted. The component disappears when the task is complete. +@objcMembers @objc(VDSLoader) open class Loader: View { diff --git a/VDS/Components/Loader/LoaderViewController.swift b/VDS/Components/Loader/LoaderViewController.swift index 65639c18..408422fb 100644 --- a/VDS/Components/Loader/LoaderViewController.swift +++ b/VDS/Components/Loader/LoaderViewController.swift @@ -10,6 +10,8 @@ import UIKit import VDSCoreTokens /// ViewController to show the Loader, this will be presented using the LoaderLaunchable Protocl. +@objcMembers +@objc(VDSLoaderViewController) open class LoaderViewController: UIViewController, Surfaceable { //-------------------------------------------------- // MARK: - Private Properties diff --git a/VDS/Components/Notification/Notification.swift b/VDS/Components/Notification/Notification.swift index 66be33c4..6df7da68 100644 --- a/VDS/Components/Notification/Notification.swift +++ b/VDS/Components/Notification/Notification.swift @@ -14,6 +14,7 @@ import Combine /// in context. There are four types: information, success, warning and error; each /// with different color and content. They may be screen-specific, flow-specific or /// experience-wide. +@objcMembers @objc(VDSNotification) open class Notification: View { diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index 8988aaa4..02c1e85e 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -11,6 +11,7 @@ import VDSCoreTokens 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. +@objcMembers @objc(VDSPagination) open class Pagination: View { diff --git a/VDS/Components/Pagination/PaginationButton.swift b/VDS/Components/Pagination/PaginationButton.swift index d7aaf8e2..d5de6e3f 100644 --- a/VDS/Components/Pagination/PaginationButton.swift +++ b/VDS/Components/Pagination/PaginationButton.swift @@ -9,6 +9,7 @@ import UIKit import VDSCoreTokens ///This is customised button for Pagination view +@objcMembers @objc(PaginationButton) open class PaginationButton: ButtonBase { //-------------------------------------------------- diff --git a/VDS/Components/RadioBox/RadioBoxGroup.swift b/VDS/Components/RadioBox/RadioBoxGroup.swift index c58802c0..b5531e33 100644 --- a/VDS/Components/RadioBox/RadioBoxGroup.swift +++ b/VDS/Components/RadioBox/RadioBoxGroup.swift @@ -11,6 +11,7 @@ import UIKit /// Radio boxes are single-select components through which a customer indicates a choice. /// They're stylized ``RadioButtons`` that must always be paired with one or more ``RadioBoxItem`` /// in a radio box group. Use radio boxes to display choices like device storage. +@objcMembers @objc(VDSRadioBoxGroup) open class RadioBoxGroup: SelectorGroupBase, SelectorGroupSingleSelect { diff --git a/VDS/Components/RadioBox/RadioBoxItem.swift b/VDS/Components/RadioBox/RadioBoxItem.swift index 872a4d05..1da80c0a 100644 --- a/VDS/Components/RadioBox/RadioBoxItem.swift +++ b/VDS/Components/RadioBox/RadioBoxItem.swift @@ -12,6 +12,7 @@ import VDSCoreTokens /// Radio boxes are single-select components through which a customer indicates a choice /// that are used within a ``RadioBoxGroup``. +@objcMembers @objc(VDSRadioBoxItem) open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable { diff --git a/VDS/Components/RadioButton/RadioButton.swift b/VDS/Components/RadioButton/RadioButton.swift index f19e7b2f..7241fddf 100644 --- a/VDS/Components/RadioButton/RadioButton.swift +++ b/VDS/Components/RadioButton/RadioButton.swift @@ -13,6 +13,7 @@ import VDSCoreTokens /// Radio buttons are single-select components through which a customer indicates a choice. /// They must always be paired with one or more ``RadioButtonItem`` within a ``RadioButtonGroup``. /// Use radio buttons to display choices like delivery method. +@objcMembers @objc(VDSRadioButton) open class RadioButton: SelectorBase { diff --git a/VDS/Components/RadioButton/RadioButtonGroup.swift b/VDS/Components/RadioButton/RadioButtonGroup.swift index f44f5587..13adf080 100644 --- a/VDS/Components/RadioButton/RadioButtonGroup.swift +++ b/VDS/Components/RadioButton/RadioButtonGroup.swift @@ -11,6 +11,7 @@ import UIKit /// Radio buttons items are single-select components through which a customer indicates a choice. /// They must always be paired with one or more other ``RadioButtonItem`` within a radio button group. /// Use radio buttons to display choices like delivery method. +@objcMembers @objc(VDSRadioButtonGroup) open class RadioButtonGroup: SelectorGroupBase, SelectorGroupSingleSelect { diff --git a/VDS/Components/RadioButton/RadioButtonItem.swift b/VDS/Components/RadioButton/RadioButtonItem.swift index bc15531d..af84ac89 100644 --- a/VDS/Components/RadioButton/RadioButtonItem.swift +++ b/VDS/Components/RadioButton/RadioButtonItem.swift @@ -11,6 +11,7 @@ import UIKit /// Radio buttons items are single-select components through which a customer indicates a choice. /// They must always be paired with one or more other radio button items within a ``RadioButtonGroup``. /// Use radio buttons to display choices like delivery method. +@objcMembers @objc(VDSRadioButtonItem) open class RadioButtonItem: SelectorItemBase { diff --git a/VDS/Components/Table/Table.swift b/VDS/Components/Table/Table.swift index 39cc6698..bb599545 100644 --- a/VDS/Components/Table/Table.swift +++ b/VDS/Components/Table/Table.swift @@ -10,6 +10,7 @@ import UIKit import VDSCoreTokens ///Table is view composed of rows and columns, which takes any view into each cell and resizes based on the highest cell height. +@objcMembers @objc(VDSTable) open class Table: View { diff --git a/VDS/Components/Tabs/Tab.swift b/VDS/Components/Tabs/Tab.swift index 4c0b55cd..edc8af55 100644 --- a/VDS/Components/Tabs/Tab.swift +++ b/VDS/Components/Tabs/Tab.swift @@ -11,7 +11,7 @@ import VDSCoreTokens import Combine extension Tabs { - + @objcMembers @objc(VDSTab) open class Tab: Control, Groupable { diff --git a/VDS/Components/Tabs/Tabs.swift b/VDS/Components/Tabs/Tabs.swift index 88ae02e7..e909d43b 100644 --- a/VDS/Components/Tabs/Tabs.swift +++ b/VDS/Components/Tabs/Tabs.swift @@ -10,6 +10,7 @@ import UIKit import VDSCoreTokens /// Tabs are organizational components that group content and allow customers to navigate its display. Use them to separate content when the content is related but doesn’t need to be compared. +@objcMembers @objc(VDSTabs) open class Tabs: View { diff --git a/VDS/Components/Tabs/TabsContainer.swift b/VDS/Components/Tabs/TabsContainer.swift index d44b877f..945ecbaf 100644 --- a/VDS/Components/Tabs/TabsContainer.swift +++ b/VDS/Components/Tabs/TabsContainer.swift @@ -8,6 +8,7 @@ import Foundation import UIKit +@objcMembers @objc(VDSTabsContainer) open class TabsContainer: View { diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 571783bc..481318b6 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -11,6 +11,7 @@ import VDSCoreTokens import Combine /// Base Class used to build out a Input controls. +@objcMembers @objc(VDSEntryField) open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index fd36ecac..a65df78e 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -13,6 +13,7 @@ import Combine /// An input field is an input wherein a customer enters information. They typically appear in forms. /// Specialized input fields capture credit card numbers, inline actions, passwords, phone numbers, /// dates and security codes in their correct formats. +@objcMembers @objc(VDSInputField) open class InputField: EntryFieldBase { @@ -311,6 +312,24 @@ open class InputField: EntryFieldBase { } } + open var widthPercentage: CGFloat? { didSet { setNeedsUpdate() } } + + internal override func updateContainerWidth() { + widthConstraint?.deactivate() + trailingLessThanEqualsConstraint?.deactivate() + trailingEqualsConstraint?.deactivate() + + //see if there is a widthPercentage and follow the same pattern as done for "width" + let currentWidth = (horizontalPinnedWidth() ?? 0) * (widthPercentage ?? 0) + if currentWidth >= minWidth, currentWidth <= maxWidth { + widthConstraint?.constant = currentWidth + widthConstraint?.activate() + trailingLessThanEqualsConstraint?.activate() + } else { + super.updateContainerWidth() + } + } + override func updateRules() { super.updateRules() fieldType.handler().appendRules(self) diff --git a/VDS/Components/TextFields/InputField/TextField.swift b/VDS/Components/TextFields/InputField/TextField.swift index b56f3423..3fdf308f 100644 --- a/VDS/Components/TextFields/InputField/TextField.swift +++ b/VDS/Components/TextFields/InputField/TextField.swift @@ -10,6 +10,7 @@ import UIKit import Combine import VDSCoreTokens +@objcMembers @objc(VDSTextField) open class TextField: UITextField, ViewProtocol, Errorable { diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 8768352e..07c79ca6 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -12,6 +12,7 @@ import Combine /// A text area is an input wherein a customer enters long-form information. /// Use a text area when you want customers to enter text that’s longer than a single line. +@objcMembers @objc(VDSTextArea) open class TextArea: EntryFieldBase { //-------------------------------------------------- diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift index 3c5dc5ee..63356635 100644 --- a/VDS/Components/TextFields/TextArea/TextView.swift +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -10,6 +10,7 @@ import UIKit import Combine import VDSCoreTokens +@objcMembers @objc(VDSTextView) open class TextView: UITextView, ViewProtocol, Errorable { diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index cbe68e3a..f35d2614 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -10,6 +10,7 @@ import VDSCoreTokens import UIKit import Combine +@objcMembers @objc(VDSTileContainer) open class TileContainer: TileContainerBase { diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index e52332ff..4391bfb3 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -15,6 +15,7 @@ import Combine /// support quick scanning and engagement. A Tilelet is fully clickable and /// while it can include an arrow CTA, it does not require one in order to /// function. +@objcMembers @objc(VDSTilelet) open class Tilelet: TileContainerBase { diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index bc5c4c3a..47ae5421 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -12,6 +12,7 @@ import Combine /// Title Lockup ensures the readability of words on the screen /// with approved built in text size configurations. +@objcMembers @objc(VDSTitleLockup) open class TitleLockup: View { diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index aaa411de..7cb4ef71 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -12,6 +12,7 @@ import Combine /// A toggle is a control that lets customers instantly turn on /// or turn off a single option, setting or function. +@objcMembers @objc(VDSToggle) open class Toggle: Control, Changeable, FormFieldable { diff --git a/VDS/Components/Toggle/ToggleView.swift b/VDS/Components/Toggle/ToggleView.swift index dc5e9570..9274be1a 100644 --- a/VDS/Components/Toggle/ToggleView.swift +++ b/VDS/Components/Toggle/ToggleView.swift @@ -12,6 +12,7 @@ import Combine /// A toggle is a control that lets customers instantly turn on /// or turn off a single option, setting or function. +@objcMembers @objc(VDSToggleView) open class ToggleView: Control, Changeable, FormFieldable { diff --git a/VDS/Components/Tooltip/Tooltip.swift b/VDS/Components/Tooltip/Tooltip.swift index f07fb1be..6eaf7ef4 100644 --- a/VDS/Components/Tooltip/Tooltip.swift +++ b/VDS/Components/Tooltip/Tooltip.swift @@ -13,6 +13,7 @@ import Combine /// A tooltip is an overlay that clarifies another component or content /// element. It is triggered when a customer hovers, clicks or taps /// the tooltip icon. +@objcMembers @objc(VDSTooltip) open class Tooltip: Control, TooltipLaunchable { diff --git a/VDS/Components/Tooltip/TooltipAlertViewController.swift b/VDS/Components/Tooltip/TooltipAlertViewController.swift index 043f07f4..0fb3d69c 100644 --- a/VDS/Components/Tooltip/TooltipAlertViewController.swift +++ b/VDS/Components/Tooltip/TooltipAlertViewController.swift @@ -10,6 +10,8 @@ import UIKit import Combine import VDSCoreTokens +@objcMembers +@objc(VDSTooltipAlertViewController) open class TooltipAlertViewController: UIViewController, Surfaceable { /// Set of Subscribers for any Publishers for this Control. diff --git a/VDS/Components/Tooltip/TooltipDialog.swift b/VDS/Components/Tooltip/TooltipDialog.swift index 0650a808..bb6b5653 100644 --- a/VDS/Components/Tooltip/TooltipDialog.swift +++ b/VDS/Components/Tooltip/TooltipDialog.swift @@ -9,6 +9,8 @@ import Foundation import UIKit import VDSCoreTokens +@objcMembers +@objc(VDSTooltipDialog) open class TooltipDialog: View, UIScrollViewDelegate { //-------------------------------------------------- From 4e88ee936a231b3e05d9fcd5c5bb39d07a13fcc6 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 26 Jul 2024 20:48:45 +0530 Subject: [PATCH 055/113] Digital ACT-191 ONEAPP-9311 story: parent container width can be set to value (percentage), controlWidth of input stepper can be set to value - percentage --- .../InputStepper/InputStepper.swift | 130 ++++++++++++++---- 1 file changed, 102 insertions(+), 28 deletions(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 57cdf756..cc55048c 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -42,9 +42,31 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- /// Accepts a string or number value to control the width of input stepper. /// auto(default) - The control's width is determined by the combined width of the value, trailing text and padding - /// Value - The control's width can be set to a fixed pixel or percentage value. + /// Value - The control's width can be set to a fixed pixel. open var controlWidth: String? = "auto" { didSet { setNeedsUpdate() } } + /// Accepts percentage value to controlWidth of input stepper. + open var controlWidthPercentage: CGFloat? { + didSet { + if let percentage = controlWidthPercentage, percentage > 100 { + controlWidthPercentage = 100 + } + updateControlWidthPercentage() + setNeedsUpdate() + } + } + + /// Accepts percentage value to width of parent container. + open var widthPercentage: CGFloat? { + didSet { + if let percentage = widthPercentage, percentage > 100 { + widthPercentage = 100 + } + updatePercentageWidth() + setNeedsUpdate() + } + } + /// Default value of the input stepper, defaults to '0'. open var defaultValue:Int = 0 { didSet { setNeedsUpdate() } } @@ -52,26 +74,20 @@ open class InputStepper: EntryFieldBase { open var id: Int? { didSet { setNeedsUpdate() } } /// Maximum value of the input stepper, defaults to '99'. - open var maxValue: Int? { - get { return _maxValue } - set { - if let newValue, newValue <= 99 && newValue > 0 { - _maxValue = newValue - } else { - _maxValue = 99 + open var maxValue: Int? = 99 { + didSet { + if let value = maxValue, value > 99 || value < 0 { + maxValue = 99 } setNeedsUpdate() } } /// Minimum value of the input stepper, defaults to '0'. - open var minValue: Int? { - get { return _minValue } - set { - if let newValue, newValue >= 0 && newValue < 99 { - _minValue = newValue - } else { - _minValue = 0 + open var minValue: Int? = 0 { + didSet { + if let value = minValue, value < 0 && value >= 99 { + minValue = 0 } setNeedsUpdate() } @@ -91,9 +107,6 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var _maxValue: Int = 99 - internal var _minValue: Int = 0 - /// This is the view that will be wrapped with the border for userInteraction. /// The only subview of this view is the stepperStackView. internal var stepperContainerView = View().with { @@ -148,8 +161,8 @@ open class InputStepper: EntryFieldBase { internal var largeMinHeight = 44 internal var smallMinHeight = 32 - internal let labelColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight , VDSColor.elementsPrimaryOndark) - internal let labelDisabledColorConfiguration = SurfaceColorConfiguration(VDSColor.interactiveDisabledOnlight , VDSColor.interactiveDisabledOndark) + internal let labelColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + internal let labelDisabledColorConfiguration = SurfaceColorConfiguration(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark) //-------------------------------------------------- // MARK: - Lifecycle @@ -207,6 +220,7 @@ open class InputStepper: EntryFieldBase { super.updateView() statusIcon.isHidden = true updateConstraintsToFieldStackView(flag: false) + updateContainerView(flag: false) // Update label text, style, color, ande surface. textLabel.text = String(defaultValue) + " " + (trailingText ?? "") @@ -219,7 +233,9 @@ open class InputStepper: EntryFieldBase { // Update stepper container border and corner radius. setControlWidth(controlWidth) - updateContainerView(flag: false) + updateControlWidthPercentage() + + setNeedsLayout() } /// Resets to default settings. @@ -227,19 +243,52 @@ open class InputStepper: EntryFieldBase { super.reset() textLabel.reset() textLabel.textStyle = .boldBodyLarge - textLabel.text = "" - controlWidth = nil - minValue = nil - maxValue = nil + controlWidth = "auto" + controlWidthPercentage = nil + widthPercentage = nil + defaultValue = 0 + id = nil + minValue = 0 + maxValue = 99 trailingText = nil + size = .large helperTextPlacement = .bottom } + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + internal override func updateContainerWidth() { + widthConstraint?.deactivate() + trailingLessThanEqualsConstraint?.deactivate() + trailingEqualsConstraint?.deactivate() + + if let width, width >= minWidth, width <= maxWidth { + widthConstraint?.constant = width + widthConstraint?.activate() + trailingLessThanEqualsConstraint?.activate() + } else if let parentWidth = width, parentWidth >= maxWidth { + width = maxWidth + widthConstraint?.constant = maxWidth + widthConstraint?.activate() + trailingLessThanEqualsConstraint?.activate() + } else if let parentWidth = width, parentWidth <= minWidth { + width = minWidth + widthConstraint?.constant = minWidth + widthConstraint?.activate() + trailingLessThanEqualsConstraint?.activate() + } else { + trailingEqualsConstraint?.activate() + } + } + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- internal func checkDefaultValue() { - defaultValue = defaultValue > _maxValue ? _maxValue : defaultValue < _minValue ? _minValue : defaultValue + if let _minValue = minValue, let _maxValue = maxValue { + defaultValue = defaultValue > _maxValue ? _maxValue : defaultValue < _minValue ? _minValue : defaultValue + } } internal func decrementButtonClick() { @@ -264,8 +313,8 @@ open class InputStepper: EntryFieldBase { decrementButton.isEnabled = false incrementButton.isEnabled = false } else { - decrementButton.isEnabled = defaultValue > _minValue ? true : false - incrementButton.isEnabled = defaultValue < _maxValue ? true : false + decrementButton.isEnabled = defaultValue > minValue ?? 0 ? true : false + incrementButton.isEnabled = defaultValue < maxValue ?? 99 ? true : false } } @@ -307,4 +356,29 @@ open class InputStepper: EntryFieldBase { stepperWidthConstraint?.activate() } } + + private func updateControlWidthPercentage() { + let superWidth = width ?? CGFloat(containerView.frame.size.width) + + // Set the inputStepper's controlWidth based on the controlWidth percentage received relative to its parentView's frame. + if let controlWidthPercentage { + controlWidth = String( Int( max(superWidth * ((controlWidthPercentage) / 100), minWidth))) + setControlWidth(controlWidth) + } + } + + private func updatePercentageWidth() { + guard let superWidth = superview?.frame.width else { + return + } + + // Set width of Parent container based on width perecentage received relative to its superview frame. + if let widthPercentage { + width = max(superWidth * ((widthPercentage) / 100), minWidth) + if controlWidthPercentage != nil { + updateControlWidthPercentage() + } + } + + } } From 90eb01cb2459917ce33af04780df196a0a9ee2b0 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 26 Jul 2024 21:24:15 +0530 Subject: [PATCH 056/113] Digital ACT-191 ONEAPP-9311 story: aligning text to center --- .../InputStepper/InputStepper.swift | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index cc55048c..5abf8724 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -44,7 +44,7 @@ open class InputStepper: EntryFieldBase { /// auto(default) - The control's width is determined by the combined width of the value, trailing text and padding /// Value - The control's width can be set to a fixed pixel. open var controlWidth: String? = "auto" { didSet { setNeedsUpdate() } } - + /// Accepts percentage value to controlWidth of input stepper. open var controlWidthPercentage: CGFloat? { didSet { @@ -72,7 +72,7 @@ open class InputStepper: EntryFieldBase { /// Allows an id to be passed to input stepper. open var id: Int? { didSet { setNeedsUpdate() } } - + /// Maximum value of the input stepper, defaults to '99'. open var maxValue: Int? = 99 { didSet { @@ -82,7 +82,7 @@ open class InputStepper: EntryFieldBase { setNeedsUpdate() } } - + /// Minimum value of the input stepper, defaults to '0'. open var minValue: Int? = 0 { didSet { @@ -94,7 +94,7 @@ open class InputStepper: EntryFieldBase { } /// The size of the input stepper. Defaults to 'large'. - open var size: Size = .large { + open var size: Size = .large { didSet { updateStepperContainerViewSize() setNeedsUpdate() @@ -143,24 +143,25 @@ open class InputStepper: EntryFieldBase { $0.textStyle = .boldBodyLarge $0.numberOfLines = 1 $0.lineBreakMode = .byTruncatingTail + $0.textAlignment = .center } - + //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- internal var stepperWidthConstraint: NSLayoutConstraint? internal var stepperHeightConstraint: NSLayoutConstraint? - + //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- internal override var containerSize: CGSize { CGSize(width: size == .large ? largeMinWidth : smallMinWidth, height: size == .large ? largeMinHeight : smallMinHeight) } - + internal var largeMinWidth = 121 internal var smallMinWidth = 90 internal var largeMinHeight = 44 internal var smallMinHeight = 32 - + internal let labelColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) internal let labelDisabledColorConfiguration = SurfaceColorConfiguration(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark) @@ -182,11 +183,11 @@ open class InputStepper: EntryFieldBase { // Set initial states containerView.isEnabled = false statusIcon.isHidden = true - + // Add listeners decrementButton.onClick = { _ in self.decrementButtonClick() } incrementButton.onClick = { _ in self.incrementButtonClick() } - + // setting color config textLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() } @@ -195,12 +196,12 @@ open class InputStepper: EntryFieldBase { stepperStackView.addArrangedSubview(decrementButton) stepperStackView.addArrangedSubview(textLabel) stepperStackView.addArrangedSubview(incrementButton) - + // Set space between decrement button, label, and increment button relative to input Stepper size. let space = size == .large ? VDSLayout.space3X : VDSLayout.space2X stepperStackView.setCustomSpacing(space, after: decrementButton) stepperStackView.setCustomSpacing(space, after: textLabel) - + // Update Edge insets relative to input Stepper size. stepperStackView.pinToSuperView(.uniform(size == .large ? 6.0 : VDSLayout.space1X)) @@ -221,20 +222,19 @@ open class InputStepper: EntryFieldBase { statusIcon.isHidden = true updateConstraintsToFieldStackView(flag: false) updateContainerView(flag: false) - + // Update label text, style, color, ande surface. textLabel.text = String(defaultValue) + " " + (trailingText ?? "") textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable() textLabel.surface = surface - + // Update increment and decrement button. updateButtonStates() // Update stepper container border and corner radius. setControlWidth(controlWidth) updateControlWidthPercentage() - setNeedsLayout() } @@ -262,7 +262,7 @@ open class InputStepper: EntryFieldBase { widthConstraint?.deactivate() trailingLessThanEqualsConstraint?.deactivate() trailingEqualsConstraint?.deactivate() - + if let width, width >= minWidth, width <= maxWidth { widthConstraint?.constant = width widthConstraint?.activate() @@ -286,7 +286,7 @@ open class InputStepper: EntryFieldBase { // MARK: - Private Methods //-------------------------------------------------- internal func checkDefaultValue() { - if let _minValue = minValue, let _maxValue = maxValue { + if let _minValue = minValue, let _maxValue = maxValue { defaultValue = defaultValue > _maxValue ? _maxValue : defaultValue < _minValue ? _minValue : defaultValue } } @@ -308,7 +308,7 @@ open class InputStepper: EntryFieldBase { incrementButton.customContainerSize = size == .large ? 32 : 24 decrementButton.surface = surface incrementButton.surface = surface - + if isReadOnly || !isEnabled { decrementButton.isEnabled = false incrementButton.isEnabled = false @@ -341,7 +341,7 @@ open class InputStepper: EntryFieldBase { // Set controlWidth provided which is either pixel or percentage let width = width ?? CGFloat(containerView.frame.size.width) updateStepperContainerWidth(controlWidth: CGFloat(controlWidth), width: width) - } + } } // Handling the controlwidth without going beyond the width of the parent container. @@ -359,7 +359,7 @@ open class InputStepper: EntryFieldBase { private func updateControlWidthPercentage() { let superWidth = width ?? CGFloat(containerView.frame.size.width) - + // Set the inputStepper's controlWidth based on the controlWidth percentage received relative to its parentView's frame. if let controlWidthPercentage { controlWidth = String( Int( max(superWidth * ((controlWidthPercentage) / 100), minWidth))) @@ -379,6 +379,5 @@ open class InputStepper: EntryFieldBase { updateControlWidthPercentage() } } - } } From 3ea4b74af2fa1ba35b098e9dce483339bbdc236e Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 29 Jul 2024 16:17:52 +0530 Subject: [PATCH 057/113] Digital ACT-191 ONEAPP-9311 story: added accessibility changes --- .../InputStepper/InputStepper.swift | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 5abf8724..b9139b5a 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -104,6 +104,11 @@ open class InputStepper: EntryFieldBase { /// Accepts any text or character to appear next to input stepper value. open var trailingText: String? { didSet { setNeedsUpdate() } } + /// Value for the textField + open override var value: String? { + return nil + } + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -111,6 +116,7 @@ open class InputStepper: EntryFieldBase { /// The only subview of this view is the stepperStackView. internal var stepperContainerView = View().with { $0.isAccessibilityElement = true + $0.accessibilityLabel = "Input Stepper" } internal var stepperStackView = UIStackView().with { @@ -145,7 +151,7 @@ open class InputStepper: EntryFieldBase { $0.lineBreakMode = .byTruncatingTail $0.textAlignment = .center } - + //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- @@ -175,11 +181,7 @@ open class InputStepper: EntryFieldBase { /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() - - // accessibility - isAccessibilityElement = false - accessibilityLabel = "Input Stepper" - + // Set initial states containerView.isEnabled = false statusIcon.isHidden = true @@ -238,6 +240,30 @@ open class InputStepper: EntryFieldBase { setNeedsLayout() } + open override var accessibilityElements: [Any]? { + get { + var elements = [Any]() + + if !isReadOnly || isEnabled { + elements.append(contentsOf: [titleLabel, containerView, decrementButton, textLabel, incrementButton]) + } else { + elements.append(contentsOf: [titleLabel, containerView, textLabel]) + } + + if showError { + if let errorText, !errorText.isEmpty { + elements.append(errorLabel) + } + } + if let helperText, !helperText.isEmpty { + elements.append(helperLabel) + } + return elements + } + + set { super.accessibilityElements = newValue } + } + /// Resets to default settings. open override func reset() { super.reset() From 06b0c80a58da380e6793e2529b0ccc0109374b6f Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Tue, 30 Jul 2024 14:54:42 +0530 Subject: [PATCH 058/113] Digital ACT-191 ONEAPP-9311 story: changes as per review notes --- VDS/Components/DatePicker/DatePicker.swift | 2 +- .../DropdownSelect/DropdownSelect.swift | 2 +- .../InputStepper/InputStepper.swift | 40 +++++++++++++------ .../TextFields/EntryFieldBase.swift | 40 +++++-------------- .../TextFields/InputField/InputField.swift | 2 +- 5 files changed, 39 insertions(+), 47 deletions(-) diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index db4807b4..df7cc7c9 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -286,7 +286,7 @@ extension DatePicker { // update containerview _ = responder?.becomeFirstResponder() - updateContainerView(flag: true) + updateContainerView() // animate the calendar to show UIView.animate(withDuration: 0.5, diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 8aa36272..947210f4 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -288,7 +288,7 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource { dropdownField.resignFirstResponder() } optionsPicker.isHidden = !optionsPicker.isHidden - updateContainerView(flag: true) + updateContainerView() updateErrorLabel() } diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 251df4b2..63288e96 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -67,9 +67,6 @@ open class InputStepper: EntryFieldBase { } } - /// Default value of the input stepper, defaults to '0'. - open var defaultValue:Int = 0 { didSet { setNeedsUpdate() } } - /// Allows an id to be passed to input stepper. open var id: Int? { didSet { setNeedsUpdate() } } @@ -112,6 +109,12 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + /// Default Int value of the input stepper, defaults to '0'. + internal var defaultIntValue: Int { + guard let intValue = defaultValue as? Int else { return 0 } + return intValue + } + /// This is the view that will be wrapped with the border for userInteraction. /// The only subview of this view is the stepperStackView. internal var stepperContainerView = View().with { @@ -222,11 +225,9 @@ open class InputStepper: EntryFieldBase { open override func updateView() { super.updateView() statusIcon.isHidden = true - updateConstraintsToFieldStackView(flag: false) - updateContainerView(flag: false) // Update label text, style, color, ande surface. - textLabel.text = String(defaultValue) + " " + (trailingText ?? "") + textLabel.text = String(defaultIntValue) + " " + (trailingText ?? "") textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable() textLabel.surface = surface @@ -237,6 +238,7 @@ open class InputStepper: EntryFieldBase { // Update stepper container border and corner radius. setControlWidth(controlWidth) updateControlWidthPercentage() + updateStepperView() setNeedsLayout() } @@ -272,7 +274,6 @@ open class InputStepper: EntryFieldBase { controlWidth = "auto" controlWidthPercentage = nil widthPercentage = nil - defaultValue = 0 id = nil minValue = 0 maxValue = 99 @@ -312,19 +313,19 @@ open class InputStepper: EntryFieldBase { // MARK: - Private Methods //-------------------------------------------------- internal func checkDefaultValue() { - if let _minValue = minValue, let _maxValue = maxValue { - defaultValue = defaultValue > _maxValue ? _maxValue : defaultValue < _minValue ? _minValue : defaultValue + if let minValue, let maxValue { + defaultValue = defaultIntValue > maxValue ? maxValue : defaultIntValue < minValue ? minValue : defaultIntValue } } internal func decrementButtonClick() { - defaultValue = defaultValue - 1 + defaultValue = defaultIntValue - 1 checkDefaultValue() updateButtonStates() } internal func incrementButtonClick() { - defaultValue = defaultValue + 1 + defaultValue = defaultIntValue + 1 checkDefaultValue() updateButtonStates() } @@ -339,8 +340,8 @@ open class InputStepper: EntryFieldBase { decrementButton.isEnabled = false incrementButton.isEnabled = false } else { - decrementButton.isEnabled = defaultValue > minValue ?? 0 ? true : false - incrementButton.isEnabled = defaultValue < maxValue ?? 99 ? true : false + decrementButton.isEnabled = defaultIntValue > minValue ?? 0 ? true : false + incrementButton.isEnabled = defaultIntValue < maxValue ?? 99 ? true : false } } @@ -406,4 +407,17 @@ open class InputStepper: EntryFieldBase { } } } + + private func updateStepperView() { + fieldStackView.removeConstraints() + fieldStackView.pinTop().pinLeading().pinBottom().pinTrailingLessThanOrEqualTo() + containerView.backgroundColor = .clear + containerView.layer.borderColor = nil + containerView.layer.borderWidth = 0 + containerView.layer.cornerRadius = 0 + fieldStackView.backgroundColor = containerBackgroundColor + fieldStackView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor + fieldStackView.layer.borderWidth = VDSFormControls.borderWidth + fieldStackView.layer.cornerRadius = containerView.frame.size.height / 2 + } } diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 755b7ffa..6b30c169 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -233,7 +233,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { get { fatalError("must be read from subclass")} } -// open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } + open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } open var isRequired: Bool = false { didSet { setNeedsUpdate() } } @@ -356,7 +356,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open override func updateView() { super.updateView() updateRules() - updateContainerView(flag: true) + updateContainerView() updateContainerWidth() updateTitleLabel() updateErrorLabel() @@ -382,7 +382,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { transparentBackground = false width = nil inputId = nil -// defaultValue = nil + defaultValue = nil isRequired = false isReadOnly = false onChange = nil @@ -536,35 +536,13 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { } } - internal func updateContainerView(flag: Bool) { - if flag { - containerView.backgroundColor = containerBackgroundColor - containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor - containerView.layer.borderWidth = VDSFormControls.borderWidth - containerView.layer.cornerRadius = VDSFormControls.borderRadius - } else { - containerView.backgroundColor = .clear - containerView.layer.borderColor = nil - containerView.layer.borderWidth = 0 - containerView.layer.cornerRadius = 0 - fieldStackView.backgroundColor = containerBackgroundColor - fieldStackView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor - fieldStackView.layer.borderWidth = VDSFormControls.borderWidth - fieldStackView.layer.cornerRadius = containerView.frame.size.height / 2 - } + internal func updateContainerView() { + containerView.backgroundColor = containerBackgroundColor + containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor + containerView.layer.borderWidth = VDSFormControls.borderWidth + containerView.layer.cornerRadius = VDSFormControls.borderRadius } - - /// Update constraints to containerStackView which has horizontal stack in which user interacts. - internal func updateConstraintsToFieldStackView(flag: Bool) { - fieldStackView.removeFromSuperview() - containerView.addSubview(fieldStackView) - if flag { - fieldStackView.pinToSuperView(.uniform(VDSLayout.space3X)) - } else { - fieldStackView.pinTop().pinLeading().pinBottom().pinTrailingLessThanOrEqualTo() - } - } - + internal func updateContainerWidth() { widthConstraint?.deactivate() trailingLessThanEqualsConstraint?.deactivate() diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index 6f09f12a..a65df78e 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -371,7 +371,7 @@ open class InputField: EntryFieldBase { extension InputField: UITextFieldDelegate { open func textFieldDidBeginEditing(_ textField: UITextField) { fieldType.handler().textFieldDidBeginEditing(self, textField: textField) - updateContainerView(flag: true) + updateContainerView() updateErrorLabel() } From d02ca3eebd0ba0f99a7d9ea89e02733c1cc9b86b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 30 Jul 2024 11:06:03 -0500 Subject: [PATCH 059/113] added ruleType so outside sources can know the type for this rule Signed-off-by: Matt Bruce --- VDS/Protocols/FormFieldable.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/VDS/Protocols/FormFieldable.swift b/VDS/Protocols/FormFieldable.swift index 82666772..e5e6894c 100644 --- a/VDS/Protocols/FormFieldable.swift +++ b/VDS/Protocols/FormFieldable.swift @@ -72,16 +72,23 @@ public protocol Rule { func isValid(value: ValueType?) -> Bool /// Error Message to be show if the value is invalid. var errorMessage: String { get } + /// type of rule + var ruleType: String { get } +} + +extension Rule { + public var ruleType: String { "\(Self.self)" } } /// Type Erased Rule for a specific ValueType. public struct AnyRule: Rule { private let _isValid: (ValueType?) -> Bool - + public var ruleType: String public let errorMessage: String public init(_ rule: R) where R.ValueType == ValueType { self._isValid = rule.isValid + self.ruleType = rule.ruleType self.errorMessage = rule.errorMessage } From c32621330276a6b50b9c21e23bb3a868377474b9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 30 Jul 2024 12:24:14 -0500 Subject: [PATCH 060/113] initial generic entryfield Signed-off-by: Matt Bruce --- VDS/Components/DatePicker/DatePicker.swift | 2 +- .../DropdownSelect/DropdownSelect.swift | 2 +- .../InputStepper/InputStepper.swift | 32 ++++++++----------- .../TextFields/EntryFieldBase.swift | 19 +++++------ .../TextFields/InputField/InputField.swift | 2 +- .../TextFields/Rules/RequiredRule.swift | 8 +++-- .../TextFields/TextArea/TextArea.swift | 2 +- VDS/Protocols/FormFieldable.swift | 4 +-- 8 files changed, 32 insertions(+), 39 deletions(-) diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index df7cc7c9..06381fab 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -6,7 +6,7 @@ import Combine /// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection. @objcMembers @objc(VDSDatePicker) -open class DatePicker: EntryFieldBase { +open class DatePicker: EntryFieldBase { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 947210f4..d0ef7275 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -13,7 +13,7 @@ import Combine /// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection. @objcMembers @objc(VDSDropdownSelect) -open class DropdownSelect: EntryFieldBase { +open class DropdownSelect: EntryFieldBase { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 63288e96..afb93dcb 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -10,9 +10,10 @@ import UIKit import VDSCoreTokens import Combine -/// A stepper is a two-segment control that people use to increase or decrease an incremental value. +/// A stepper is a two-segment control that people use to increase or decrease an incremental value.' +@objcMembers @objc(VDSInputStepper) -open class InputStepper: EntryFieldBase { +open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Initializers @@ -70,6 +71,8 @@ open class InputStepper: EntryFieldBase { /// Allows an id to be passed to input stepper. open var id: Int? { didSet { setNeedsUpdate() } } + open override var value: Int? { return defaultValue } + /// Maximum value of the input stepper, defaults to '99'. open var maxValue: Int? = 99 { didSet { @@ -101,20 +104,9 @@ open class InputStepper: EntryFieldBase { /// Accepts any text or character to appear next to input stepper value. open var trailingText: String? { didSet { setNeedsUpdate() } } - /// Value for the textField - open override var value: String? { - return nil - } - //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - /// Default Int value of the input stepper, defaults to '0'. - internal var defaultIntValue: Int { - guard let intValue = defaultValue as? Int else { return 0 } - return intValue - } - /// This is the view that will be wrapped with the border for userInteraction. /// The only subview of this view is the stepperStackView. internal var stepperContainerView = View().with { @@ -186,6 +178,7 @@ open class InputStepper: EntryFieldBase { super.setup() // Set initial states + defaultValue = 0 containerView.isEnabled = false statusIcon.isHidden = true @@ -227,7 +220,7 @@ open class InputStepper: EntryFieldBase { statusIcon.isHidden = true // Update label text, style, color, ande surface. - textLabel.text = String(defaultIntValue) + " " + (trailingText ?? "") + textLabel.text = "\(value ?? 0) " + (trailingText ?? "") textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable() textLabel.surface = surface @@ -275,6 +268,7 @@ open class InputStepper: EntryFieldBase { controlWidthPercentage = nil widthPercentage = nil id = nil + defaultValue = 0 minValue = 0 maxValue = 99 trailingText = nil @@ -314,18 +308,18 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- internal func checkDefaultValue() { if let minValue, let maxValue { - defaultValue = defaultIntValue > maxValue ? maxValue : defaultIntValue < minValue ? minValue : defaultIntValue + defaultValue = defaultValue! > maxValue ? maxValue : defaultValue! < minValue ? minValue : defaultValue! } } internal func decrementButtonClick() { - defaultValue = defaultIntValue - 1 + defaultValue = defaultValue! - 1 checkDefaultValue() updateButtonStates() } internal func incrementButtonClick() { - defaultValue = defaultIntValue + 1 + defaultValue = defaultValue! + 1 checkDefaultValue() updateButtonStates() } @@ -340,8 +334,8 @@ open class InputStepper: EntryFieldBase { decrementButton.isEnabled = false incrementButton.isEnabled = false } else { - decrementButton.isEnabled = defaultIntValue > minValue ?? 0 ? true : false - incrementButton.isEnabled = defaultIntValue < maxValue ?? 99 ? true : false + decrementButton.isEnabled = defaultValue! > minValue ?? 0 ? true : false + incrementButton.isEnabled = defaultValue! < maxValue ?? 99 ? true : false } } diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 6b30c169..e492d6fa 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -11,10 +11,7 @@ import VDSCoreTokens import Combine /// Base Class used to build out a Input controls. -@objcMembers -@objc(VDSEntryField) -open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { - +open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -229,11 +226,11 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { open var inputId: String? { didSet { setNeedsUpdate() } } /// The text of this textField. - open var value: String? { + open var value: ValueType? { get { fatalError("must be read from subclass")} } - open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } + open var defaultValue: ValueType? { didSet { setNeedsUpdate() } } open var isRequired: Bool = false { didSet { setNeedsUpdate() } } @@ -245,7 +242,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { } } - open var rules = [AnyRule]() + open var rules = [AnyRule]() open var accessibilityHintText: String = "Double tap to open" @@ -342,8 +339,8 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { } containerView.bridge_accessibilityValueBlock = { [weak self] in - guard let self else { return "" } - return value + guard let self, let value else { return "" } + return "\(value)" } statusIcon.bridge_accessibilityLabelBlock = { [weak self] in @@ -523,8 +520,8 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //-------------------------------------------------- internal func updateRules() { rules.removeAll() - if isRequired && useRequiredRule { - let rule = RequiredRule() + if isRequired && useRequiredRule && ValueType.self == String.self { + let rule = RequiredRule() if let errorText, !errorText.isEmpty { rule.errorMessage = errorText } else if let labelText{ diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index a65df78e..675fc6ac 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -15,7 +15,7 @@ import Combine /// dates and security codes in their correct formats. @objcMembers @objc(VDSInputField) -open class InputField: EntryFieldBase { +open class InputField: EntryFieldBase { //-------------------------------------------------- // MARK: - Initializers diff --git a/VDS/Components/TextFields/Rules/RequiredRule.swift b/VDS/Components/TextFields/Rules/RequiredRule.swift index df00bc59..d4296036 100644 --- a/VDS/Components/TextFields/Rules/RequiredRule.swift +++ b/VDS/Components/TextFields/Rules/RequiredRule.swift @@ -7,12 +7,14 @@ import Foundation -class RequiredRule: Rule { +class RequiredRule: Rule { var maxLength: Int? var errorMessage: String = "This field is required." - func isValid(value: String?) -> Bool { - guard let value, !value.isEmpty, value.count > 0 else { return false } + func isValid(value: ValueType?) -> Bool { + guard let value, + !"\(value)".isEmpty, + "\(value)".count > 0 else { return false } return true } } diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 07c79ca6..e9a714be 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -14,7 +14,7 @@ import Combine /// Use a text area when you want customers to enter text that’s longer than a single line. @objcMembers @objc(VDSTextArea) -open class TextArea: EntryFieldBase { +open class TextArea: EntryFieldBase { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- diff --git a/VDS/Protocols/FormFieldable.swift b/VDS/Protocols/FormFieldable.swift index 82666772..98aa6ae8 100644 --- a/VDS/Protocols/FormFieldable.swift +++ b/VDS/Protocols/FormFieldable.swift @@ -8,7 +8,7 @@ import Foundation /// Protocol used for a FormField object. -public protocol FormFieldable { +public protocol FormFieldable { associatedtype ValueType = AnyHashable /// Unique Id for the Form Field object within a Form. @@ -19,7 +19,7 @@ public protocol FormFieldable { } /// Protocol for FormFieldable that require internal validation. -public protocol FormFieldInternalValidatable: FormFieldable, Errorable { +public protocol FormFieldInternalValidatable: FormFieldable, Errorable { /// Rules that drive the validator var rules: [AnyRule] { get set } From 46b11c2585ac1abe4df7c17f35390f845c359c00 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Wed, 31 Jul 2024 14:52:02 +0530 Subject: [PATCH 061/113] Digital ACT-191 ONEAPP-9311 story: refactored code and resolved constraint issues --- .../InputStepper/InputStepper.swift | 90 +++++++++++-------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 63288e96..37c82a88 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -37,21 +37,33 @@ open class InputStepper: EntryFieldBase { case large, small } + /// Enum used to describe the width of a fixed value or percentage of the input stepper control. + public enum ControlWidth { + case percentage(CGFloat) + case value(CGFloat) + } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - /// Accepts a string or number value to control the width of input stepper. - /// auto(default) - The control's width is determined by the combined width of the value, trailing text and padding - /// Value - The control's width can be set to a fixed pixel. - open var controlWidth: String? = "auto" { didSet { setNeedsUpdate() } } - - /// Accepts percentage value to controlWidth of input stepper. - open var controlWidthPercentage: CGFloat? { - didSet { - if let percentage = controlWidthPercentage, percentage > 100 { - controlWidthPercentage = 100 + /// If there is a width that is larger than this size's minimumWidth, the input stepper will resize to this width. + open var controlWidth: ControlWidth? { + get { _controlWidth } + set { + if let newValue { + switch newValue { + case .percentage(let percentage): + if percentage <= 100.0 { + _controlWidth = newValue + } + case .value(let value): + if value > 0 && value > containerSize.width { + _controlWidth = newValue + } + } + } else { + _controlWidth = nil } - updateControlWidthPercentage() setNeedsUpdate() } } @@ -62,7 +74,6 @@ open class InputStepper: EntryFieldBase { if let percentage = widthPercentage, percentage > 100 { widthPercentage = 100 } - updatePercentageWidth() setNeedsUpdate() } } @@ -109,6 +120,8 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + private var _controlWidth: ControlWidth? = nil + /// Default Int value of the input stepper, defaults to '0'. internal var defaultIntValue: Int { guard let intValue = defaultValue as? Int else { return 0 } @@ -236,8 +249,8 @@ open class InputStepper: EntryFieldBase { updateButtonStates() // Update stepper container border and corner radius. - setControlWidth(controlWidth) - updateControlWidthPercentage() + updateContainerWidthWithPercentage() + updateInputStepperWidth() updateStepperView() setNeedsLayout() } @@ -271,8 +284,7 @@ open class InputStepper: EntryFieldBase { super.reset() textLabel.reset() textLabel.textStyle = .boldBodyLarge - controlWidth = "auto" - controlWidthPercentage = nil + controlWidth = nil widthPercentage = nil id = nil minValue = 0 @@ -286,6 +298,7 @@ open class InputStepper: EntryFieldBase { // MARK: - Overrides //-------------------------------------------------- internal override func updateContainerWidth() { + stepperWidthConstraint?.deactivate() widthConstraint?.deactivate() trailingLessThanEqualsConstraint?.deactivate() trailingEqualsConstraint?.deactivate() @@ -361,17 +374,25 @@ open class InputStepper: EntryFieldBase { } // Set control width to input stepper. - private func setControlWidth(_ text: String?) { - if let text, text == "auto" { - stepperWidthConstraint?.deactivate() - } else if let controlWidth = Int(text ?? "") { - // Set controlWidth provided which is either pixel or percentage - let width = width ?? CGFloat(containerView.frame.size.width) - updateStepperContainerWidth(controlWidth: CGFloat(controlWidth), width: width) + private func updateInputStepperWidth() { + guard let controlWidth else { + return + } + + switch controlWidth { + case .percentage(let percentage): + // Set the inputStepper's controlWidth based on percentage received relative to its parentView's frame. + let superWidth = width ?? CGFloat(containerView.frame.size.width) + let value = max(superWidth * ((percentage) / 100), minWidth) + updateStepperContainerWidth(controlWidth: value, width: superWidth) + + case .value(let value): + let superWidth = width ?? CGFloat(containerView.frame.size.width) + updateStepperContainerWidth(controlWidth: value, width: superWidth) } } - // Handling the controlwidth without going beyond the width of the parent container. + // Handling the controlwidth without exceeding the width of the parent container. private func updateStepperContainerWidth(controlWidth: CGFloat, width: CGFloat) { if controlWidth >= containerSize.width && controlWidth <= width { stepperWidthConstraint?.deactivate() @@ -384,30 +405,21 @@ open class InputStepper: EntryFieldBase { } } - private func updateControlWidthPercentage() { - let superWidth = width ?? CGFloat(containerView.frame.size.width) - - // Set the inputStepper's controlWidth based on the controlWidth percentage received relative to its parentView's frame. - if let controlWidthPercentage { - controlWidth = String( Int( max(superWidth * ((controlWidthPercentage) / 100), minWidth))) - setControlWidth(controlWidth) - } - } - - private func updatePercentageWidth() { + // Update the container view width based on the percentage received. + private func updateContainerWidthWithPercentage() { guard let superWidth = superview?.frame.width else { return } // Set width of Parent container based on width perecentage received relative to its superview frame. if let widthPercentage { - width = max(superWidth * ((widthPercentage) / 100), minWidth) - if controlWidthPercentage != nil { - updateControlWidthPercentage() - } + // test value vs minimum width and take the greater value + width = max(superWidth * (widthPercentage / 100), minWidth) + updateContainerWidth() } } + // Add border and update constratints to stepper view. private func updateStepperView() { fieldStackView.removeConstraints() fieldStackView.pinTop().pinLeading().pinBottom().pinTrailingLessThanOrEqualTo() From 3ec2c984258b09d1e2552b238fb0323d364f4d58 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 31 Jul 2024 12:26:39 -0500 Subject: [PATCH 062/113] setup contentHugging and added more contraints to toggle when not in label mode Signed-off-by: Matt Bruce --- VDS/Components/Toggle/Toggle.swift | 61 +++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index 7cb4ef71..c147739b 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -55,6 +55,7 @@ open class Toggle: Control, Changeable, FormFieldable { private var leftConstraints: [NSLayoutConstraint] = [] private var rightConstraints: [NSLayoutConstraint] = [] private var labelConstraints: [NSLayoutConstraint] = [] + private var toggleConstraints: [NSLayoutConstraint] = [] //-------------------------------------------------- // MARK: - Configuration @@ -95,7 +96,7 @@ open class Toggle: Control, Changeable, FormFieldable { open var toggleView = ToggleView().with { $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) $0.isUserInteractionEnabled = false - $0.isAccessibilityElement = false + $0.isAccessibilityElement = false } /// Used in showing the on/off text. @@ -148,18 +149,6 @@ open class Toggle: Control, Changeable, FormFieldable { open var value: AnyHashable? { isOn } - /// The natural size for the receiving view, considering only properties of the view itself. - open override var intrinsicContentSize: CGSize { - if showLabel { - label.sizeToFit() - let size = CGSize(width: label.frame.width + spacingBetween + toggleContainerSize.width, - height: max(toggleContainerSize.height, label.frame.height)) - return size - } else { - return toggleContainerSize - } - } - open override var shouldHighlight: Bool { false } //-------------------------------------------------- @@ -208,6 +197,49 @@ open class Toggle: Control, Changeable, FormFieldable { label.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor) ] + // Set content hugging priority + setContentHuggingPriority(.required, for: .horizontal) + + isAccessibilityElement = true + if #available(iOS 17.0, *) { + accessibilityTraits = .toggleButton + } else { + accessibilityTraits = .button + } + addSubview(label) + addSubview(toggleView) + + // Set up initial constraints for label and switch + toggleView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + + //toggle + toggleConstraints = [ + toggleView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor), + toggleView.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor) + ] + + //toggle and label variants + labelConstraints = [ + height(constant: toggleContainerSize.height, priority: .defaultLow), + heightGreaterThanEqualTo(constant: toggleContainerSize.height, priority: .defaultHigh), + label.topAnchor.constraint(equalTo: topAnchor), + label.bottomAnchor.constraint(equalTo: bottomAnchor), + ] + + //label-toggle + leftConstraints = [ + label.leadingAnchor.constraint(equalTo: leadingAnchor), + toggleView.leadingAnchor.constraint(equalTo: label.trailingAnchor, constant: spacingBetween), + toggleView.trailingAnchor.constraint(equalTo: trailingAnchor) + ] + + //toggle-label + rightConstraints = [ + toggleView.leadingAnchor.constraint(equalTo: leadingAnchor), + label.leadingAnchor.constraint(equalTo: toggleView.trailingAnchor, constant: spacingBetween), + label.trailingAnchor.constraint(equalTo: trailingAnchor) + ] + bridge_accessibilityValueBlock = { [weak self] in guard let self else { return "" } if showText { @@ -261,6 +293,8 @@ open class Toggle: Control, Changeable, FormFieldable { label.isHidden = !showLabel if showLabel { + NSLayoutConstraint.deactivate(toggleConstraints) + label.textAlignment = textPosition == .left ? .right : .left label.textStyle = textStyle label.text = statusText @@ -279,6 +313,7 @@ open class Toggle: Control, Changeable, FormFieldable { NSLayoutConstraint.deactivate(leftConstraints) NSLayoutConstraint.deactivate(rightConstraints) NSLayoutConstraint.deactivate(labelConstraints) + NSLayoutConstraint.activate(toggleConstraints) } } } From 0d38eb495b2134833cb16d6fb21a69348dc481f4 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 1 Aug 2024 08:00:13 -0500 Subject: [PATCH 063/113] ensure that the horizontal plane of a label that has characters doesn't get crushed. Signed-off-by: Matt Bruce --- VDS/BaseClasses/Selector/SelectorItemBase.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/VDS/BaseClasses/Selector/SelectorItemBase.swift b/VDS/BaseClasses/Selector/SelectorItemBase.swift index 44ed01b1..a15fbfbd 100644 --- a/VDS/BaseClasses/Selector/SelectorItemBase.swift +++ b/VDS/BaseClasses/Selector/SelectorItemBase.swift @@ -66,18 +66,21 @@ open class SelectorItemBase: Control, Errorable, Changea /// Label used to render labelText. open var label = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.setContentCompressionResistancePriority(.required, for: .horizontal) $0.textStyle = .boldBodyLarge } /// Label used to render childText. open var childLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.setContentCompressionResistancePriority(.required, for: .horizontal) $0.textStyle = .bodyLarge } /// Label used to render errorText. open var errorLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.setContentCompressionResistancePriority(.required, for: .horizontal) $0.textStyle = .bodyMedium } From 417cddb382b187083e37c3b69346819fa39fd097 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 2 Aug 2024 16:38:31 -0500 Subject: [PATCH 064/113] refactored code Signed-off-by: Matt Bruce --- .../InputStepper/InputStepper.swift | 134 ++++++++++-------- 1 file changed, 76 insertions(+), 58 deletions(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 37c82a88..e8980f0f 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -10,9 +10,10 @@ import UIKit import VDSCoreTokens import Combine -/// A stepper is a two-segment control that people use to increase or decrease an incremental value. +/// A stepper is a two-segment control that people use to increase or decrease an incremental value.' +@objcMembers @objc(VDSInputStepper) -open class InputStepper: EntryFieldBase { +open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Initializers @@ -35,6 +36,29 @@ open class InputStepper: EntryFieldBase { /// Enum used to describe the size of Input Stepper. public enum Size: String, CaseIterable { case large, small + + var minWidth: CGFloat { + self == .large ? 121 : 90 + } + + var minHeight: CGFloat { + self == .large ? 44 : 32 + } + + var space: CGFloat { + self == .large ? VDSLayout.space3X : VDSLayout.space2X + } + var padding: CGFloat { + self == .large ? 6.0 : VDSLayout.space1X + } + + var buttonContainerSize: Int { + self == .large ? 32 : 24 + } + + var textStyle: TextStyle { + self == .large ? .boldBodyLarge : .boldBodySmall + } } /// Enum used to describe the width of a fixed value or percentage of the input stepper control. @@ -81,21 +105,36 @@ open class InputStepper: EntryFieldBase { /// Allows an id to be passed to input stepper. open var id: Int? { didSet { setNeedsUpdate() } } + private var _defaultValue: Int = 0 + open override var defaultValue: Int? { + get { _defaultValue } + set { + if let newValue { + _defaultValue = newValue > maxValue ? maxValue : newValue < minValue ? minValue : newValue + } else { + _defaultValue = 0 + } + setNeedsUpdate() + } + } + + open override var value: Int? { return defaultValue } + /// Maximum value of the input stepper, defaults to '99'. - open var maxValue: Int? = 99 { + lazy open var maxValue: Int = { _defaultMaxValue }() { didSet { - if let value = maxValue, value > 99 || value < 0 { - maxValue = 99 + if maxValue > _defaultMaxValue || maxValue < _defaultMinValue && maxValue > minValue { + maxValue = _defaultMaxValue } setNeedsUpdate() } } /// Minimum value of the input stepper, defaults to '0'. - open var minValue: Int? = 0 { + lazy open var minValue: Int = { _defaultMinValue }() { didSet { - if let value = minValue, value < 0 && value >= 99 { - minValue = 0 + if minValue < _defaultMinValue && minValue >= _defaultMaxValue && minValue < maxValue { + minValue = _defaultMinValue } setNeedsUpdate() } @@ -112,21 +151,12 @@ open class InputStepper: EntryFieldBase { /// Accepts any text or character to appear next to input stepper value. open var trailingText: String? { didSet { setNeedsUpdate() } } - /// Value for the textField - open override var value: String? { - return nil - } - //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- private var _controlWidth: ControlWidth? = nil - - /// Default Int value of the input stepper, defaults to '0'. - internal var defaultIntValue: Int { - guard let intValue = defaultValue as? Int else { return 0 } - return intValue - } + private var _defaultMinValue: Int = 0 + private var _defaultMaxValue: Int = 99 /// This is the view that will be wrapped with the border for userInteraction. /// The only subview of this view is the stepperStackView. @@ -177,28 +207,21 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- - internal override var containerSize: CGSize { CGSize(width: size == .large ? largeMinWidth : smallMinWidth, height: size == .large ? largeMinHeight : smallMinHeight) } - - internal var largeMinWidth = 121 - internal var smallMinWidth = 90 - internal var largeMinHeight = 44 - internal var smallMinHeight = 32 - + internal override var containerSize: CGSize { CGSize(width: size.minWidth, height: size.minHeight) } + internal let labelColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) internal let labelDisabledColorConfiguration = SurfaceColorConfiguration(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark) //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - open override func initialSetup() { - super.initialSetup() - } - + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() - + // Set initial states + defaultValue = 0 containerView.isEnabled = false statusIcon.isHidden = true @@ -216,12 +239,11 @@ open class InputStepper: EntryFieldBase { stepperStackView.addArrangedSubview(incrementButton) // Set space between decrement button, label, and increment button relative to input Stepper size. - let space = size == .large ? VDSLayout.space3X : VDSLayout.space2X - stepperStackView.setCustomSpacing(space, after: decrementButton) - stepperStackView.setCustomSpacing(space, after: textLabel) + stepperStackView.setCustomSpacing(size.space, after: decrementButton) + stepperStackView.setCustomSpacing(size.space, after: textLabel) // Update Edge insets relative to input Stepper size. - stepperStackView.pinToSuperView(.uniform(size == .large ? 6.0 : VDSLayout.space1X)) + stepperStackView.pinToSuperView(.uniform(size.padding)) // stepperContainerView for controls in EntryFieldBase.controlContainerView stepperContainerView.addSubview(stepperStackView) @@ -240,8 +262,8 @@ open class InputStepper: EntryFieldBase { statusIcon.isHidden = true // Update label text, style, color, ande surface. - textLabel.text = String(defaultIntValue) + " " + (trailingText ?? "") - textLabel.textStyle = size == .large ? .boldBodyLarge : .boldBodySmall + textLabel.text = "\(_defaultValue) " + (trailingText ?? "") + textLabel.textStyle = size.textStyle textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable() textLabel.surface = surface @@ -287,8 +309,9 @@ open class InputStepper: EntryFieldBase { controlWidth = nil widthPercentage = nil id = nil - minValue = 0 - maxValue = 99 + defaultValue = 0 + minValue = _defaultMinValue + maxValue = _defaultMaxValue trailingText = nil size = .large helperTextPlacement = .bottom @@ -325,27 +348,23 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- - internal func checkDefaultValue() { - if let minValue, let maxValue { - defaultValue = defaultIntValue > maxValue ? maxValue : defaultIntValue < minValue ? minValue : defaultIntValue + internal func decrementButtonClick() { + if _defaultValue > minValue { + defaultValue = _defaultValue - 1 + sendActions(for: .valueChanged) } } - internal func decrementButtonClick() { - defaultValue = defaultIntValue - 1 - checkDefaultValue() - updateButtonStates() - } - internal func incrementButtonClick() { - defaultValue = defaultIntValue + 1 - checkDefaultValue() - updateButtonStates() + if _defaultValue < maxValue { + defaultValue = _defaultValue + 1 + sendActions(for: .valueChanged) + } } internal func updateButtonStates() { - decrementButton.customContainerSize = size == .large ? 32 : 24 - incrementButton.customContainerSize = size == .large ? 32 : 24 + decrementButton.customContainerSize = size.buttonContainerSize + incrementButton.customContainerSize = size.buttonContainerSize decrementButton.surface = surface incrementButton.surface = surface @@ -353,8 +372,8 @@ open class InputStepper: EntryFieldBase { decrementButton.isEnabled = false incrementButton.isEnabled = false } else { - decrementButton.isEnabled = defaultIntValue > minValue ?? 0 ? true : false - incrementButton.isEnabled = defaultIntValue < maxValue ?? 99 ? true : false + decrementButton.isEnabled = (defaultValue ?? _defaultMaxValue ) > minValue ? true : false + incrementButton.isEnabled = (defaultValue ?? _defaultMinValue) < maxValue ? true : false } } @@ -365,7 +384,7 @@ open class InputStepper: EntryFieldBase { // Update Edge insets if size changes applied. stepperStackView.removeFromSuperview() stepperContainerView.addSubview(stepperStackView) - stepperStackView.pinToSuperView(.uniform(size == .large ? 6.0 : VDSLayout.space1X)) + stepperStackView.pinToSuperView(.uniform(size.padding)) // Update height if size changes applied. stepperHeightConstraint?.deactivate() @@ -407,7 +426,7 @@ open class InputStepper: EntryFieldBase { // Update the container view width based on the percentage received. private func updateContainerWidthWithPercentage() { - guard let superWidth = superview?.frame.width else { + guard let superWidth = horizontalPinnedWidth() else { return } @@ -415,7 +434,6 @@ open class InputStepper: EntryFieldBase { if let widthPercentage { // test value vs minimum width and take the greater value width = max(superWidth * (widthPercentage / 100), minWidth) - updateContainerWidth() } } From 9fc100d3d6a3736f3a883d2c93992ed0d06eac76 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 2 Aug 2024 16:40:42 -0500 Subject: [PATCH 065/113] updated default Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index e492d6fa..284a32b3 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -371,6 +371,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalVali errorLabel.textStyle = .bodySmall helperLabel.textStyle = .bodySmall + helperTextPlacement = .bottom labelText = nil helperText = nil showError = false From a024e1d5b32b920840f4fbec2ea5bb855e225727 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 2 Aug 2024 16:40:53 -0500 Subject: [PATCH 066/113] removed property Signed-off-by: Matt Bruce --- VDS/Components/InputStepper/InputStepper.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index e8980f0f..3637723d 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -102,9 +102,6 @@ open class InputStepper: EntryFieldBase { } } - /// Allows an id to be passed to input stepper. - open var id: Int? { didSet { setNeedsUpdate() } } - private var _defaultValue: Int = 0 open override var defaultValue: Int? { get { _defaultValue } @@ -305,16 +302,13 @@ open class InputStepper: EntryFieldBase { open override func reset() { super.reset() textLabel.reset() - textLabel.textStyle = .boldBodyLarge controlWidth = nil widthPercentage = nil - id = nil defaultValue = 0 minValue = _defaultMinValue maxValue = _defaultMaxValue trailingText = nil size = .large - helperTextPlacement = .bottom } //-------------------------------------------------- From 0158d2f5db51d57f944c118c8160098c6d6d1745 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Tue, 6 Aug 2024 11:05:35 +0530 Subject: [PATCH 067/113] Digital ACT-191 ONEAPP-9314 story: added new page --- VDS.xcodeproj/project.pbxproj | 16 ++++ VDS/Components/PriceLockup/PriceLockup.swift | 76 +++++++++++++++++++ .../PriceLockup/PriceLockupChangeLog.txt | 28 +++++++ VDS/VDS.docc/VDS.md | 1 + 4 files changed, 121 insertions(+) create mode 100644 VDS/Components/PriceLockup/PriceLockup.swift create mode 100644 VDS/Components/PriceLockup/PriceLockupChangeLog.txt diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 230ce227..8a9cf9ed 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; }; 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; + 184023452C61E7AD00A412C8 /* PriceLockup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184023442C61E7AD00A412C8 /* PriceLockup.swift */; }; + 184023472C61E7EC00A412C8 /* PriceLockupChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 184023462C61E7EC00A412C8 /* PriceLockupChangeLog.txt */; }; 1842B1DF2BECE28B0021AFCA /* CalendarDateViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1842B1DE2BECE28B0021AFCA /* CalendarDateViewCell.swift */; }; 1842B1E12BECE7B70021AFCA /* CalendarHeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1842B1E02BECE7B70021AFCA /* CalendarHeaderReusableView.swift */; }; 1842B1E32BECF0A20021AFCA /* CalendarFooterReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1842B1E22BECF0A10021AFCA /* CalendarFooterReusableView.swift */; }; @@ -210,6 +212,8 @@ 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = ""; }; 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = ""; }; 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; + 184023442C61E7AD00A412C8 /* PriceLockup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceLockup.swift; sourceTree = ""; }; + 184023462C61E7EC00A412C8 /* PriceLockupChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = PriceLockupChangeLog.txt; sourceTree = ""; }; 1842B1DE2BECE28B0021AFCA /* CalendarDateViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarDateViewCell.swift; sourceTree = ""; }; 1842B1E02BECE7B70021AFCA /* CalendarHeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarHeaderReusableView.swift; sourceTree = ""; }; 1842B1E22BECF0A10021AFCA /* CalendarFooterReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarFooterReusableView.swift; sourceTree = ""; }; @@ -458,6 +462,15 @@ path = CarouselScrollbar; sourceTree = ""; }; + 184023432C61E78D00A412C8 /* PriceLockup */ = { + isa = PBXGroup; + children = ( + 184023442C61E7AD00A412C8 /* PriceLockup.swift */, + 184023462C61E7EC00A412C8 /* PriceLockupChangeLog.txt */, + ); + path = PriceLockup; + sourceTree = ""; + }; 186D13C92BBA8A3500986B53 /* DropdownSelect */ = { isa = PBXGroup; children = ( @@ -686,6 +699,7 @@ EAD0688C2A55F801002E3A2D /* Loader */, 445BA07629C07ABA0036A7C5 /* Notification */, 71B23C2B2B91FA510027F7D9 /* Pagination */, + 184023432C61E78D00A412C8 /* PriceLockup */, EA89200B28B530F0006B9984 /* RadioBox */, EAF7F11428A1470D00B287F5 /* RadioButton */, 440B84C82BD8E0CE004A732A /* Table */, @@ -1185,6 +1199,7 @@ buildActionMask = 2147483647; files = ( EA3362042891E14D0071C351 /* VerizonNHGeTX-Bold.otf in Resources */, + 184023472C61E7EC00A412C8 /* PriceLockupChangeLog.txt in Resources */, EA3362072891E14D0071C351 /* VerizonNHGeDS-Regular.otf in Resources */, EA3362062891E14D0071C351 /* VerizonNHGeTX-Regular.otf in Resources */, EA3362052891E14D0071C351 /* VerizonNHGeDS-Bold.otf in Resources */, @@ -1394,6 +1409,7 @@ EAC58C0E2BED021600BA39FA /* Password.swift in Sources */, EAF7F0AD289B142900B287F5 /* StrikeThroughLabelAttribute.swift in Sources */, EAB5FEF12927F4AA00998C17 /* SelfSizingCollectionView.swift in Sources */, + 184023452C61E7AD00A412C8 /* PriceLockup.swift in Sources */, EA3361B8288B2AAA0071C351 /* ViewProtocol.swift in Sources */, EA3361A8288B23300071C351 /* UIColor.swift in Sources */, EA2DC9B42BE2C6FE004F58C5 /* TextField.swift in Sources */, diff --git a/VDS/Components/PriceLockup/PriceLockup.swift b/VDS/Components/PriceLockup/PriceLockup.swift new file mode 100644 index 00000000..745dc3c0 --- /dev/null +++ b/VDS/Components/PriceLockup/PriceLockup.swift @@ -0,0 +1,76 @@ +// +// PriceLockup.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 06/08/24. +// + +import Foundation +import UIKit +import VDSCoreTokens + +@objcMembers +@objc(VDSPriceLockup) +open class PriceLockup: View { + + //-------------------------------------------------- + // 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: - Enums + //-------------------------------------------------- + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + + //-------------------------------------------------- + // MARK: - Configuration Properties + //-------------------------------------------------- + + //-------------------------------------------------- + // 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() + } + + /// Used to make changes to the View based off a change events or from local properties. + open override func updateView() { + super.updateView() + + } + + /// Resets to default settings. + open override func reset() { + super.reset() + shouldUpdateView = false + + shouldUpdateView = true + setNeedsUpdate() + } + + open override var accessibilityElements: [Any]? { + get { + return nil + } + set {} + } +} diff --git a/VDS/Components/PriceLockup/PriceLockupChangeLog.txt b/VDS/Components/PriceLockup/PriceLockupChangeLog.txt new file mode 100644 index 00000000..e06ee9f8 --- /dev/null +++ b/VDS/Components/PriceLockup/PriceLockupChangeLog.txt @@ -0,0 +1,28 @@ +MM/DD/YYYY +---------------- + +11/16/2023 +---------------- +- Added leadingText and trailingText to anatomy +- Added leadingText and trailingText props to configurations +- Added term prop to configurations +- Removed Suspended orange color and corresponding color tokens + +11/27/2023 +---------------- +- Removed “Delimiter” from Anatomy as “Term” includes both delimiter and term +- Added “Figma only” badge to leadingText and trailingText in Configurations +- Added superscript to “none” under term in Configurations +- Added Overflow section to Layout and spacing +- Updated Spacing to allow for leading and trailing text + +12/18/23 +---------------- +- Updated all pages with spec template updates from Doc Utility Expansion Pack +- Added Content props section to Config page + +1/15/24 +---------------- +- Clarified strikethrough does not apply to leading or trailing text +- Clarified and added to text overflow examples +- Correct Success to Savings in the configuration seciton diff --git a/VDS/VDS.docc/VDS.md b/VDS/VDS.docc/VDS.md index 6582ce4a..aa583ada 100755 --- a/VDS/VDS.docc/VDS.md +++ b/VDS/VDS.docc/VDS.md @@ -39,6 +39,7 @@ Using the system allows designers and developers to collaborate more easily and - ``Loader`` - ``Notification`` - ``Pagination`` +- ``PriceLockup`` - ``RadioBoxItem`` - ``RadioBoxGroup`` - ``RadioButton`` From 815024cc5ce56bc4db11bc8b531b04654916f9ff Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Tue, 6 Aug 2024 14:35:12 +0530 Subject: [PATCH 068/113] Digital ACT-191 ONEAPP-9314 story: added public properties --- VDS/Components/PriceLockup/PriceLockup.swift | 75 +++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/VDS/Components/PriceLockup/PriceLockup.swift b/VDS/Components/PriceLockup/PriceLockup.swift index 745dc3c0..ef11a2cb 100644 --- a/VDS/Components/PriceLockup/PriceLockup.swift +++ b/VDS/Components/PriceLockup/PriceLockup.swift @@ -31,25 +31,96 @@ open class PriceLockup: View { //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- + /// Enum used to describe the kind of PriceLockup. + public enum Kind: String, CaseIterable { + case primary, secondary, savings + } + + /// Enum used to describe the term of PriceLockup. + public enum Term: String, CaseIterable { + case month, year, biennial, none + } + + /// Enum type describing size of PriceLockup. + public enum Size: String, CaseIterable { + case xxxsmall + case xxsmall + case xsmall + case small + case medium + case large + case xlarge + case xxlarge + } //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + /// If true, the component will render as bold. + open var bold: Bool = false { didSet { setNeedsUpdate() } } + + /// Currency - If hideCurrency true, the component will render without currency. + open var hideCurrency: Bool = false { didSet { setNeedsUpdate() } } + + /// Leading text for the component. + open var leadingText: String? { didSet { setNeedsUpdate() } } + + /// Value rendered for the component. + open var price: CGFloat? { didSet { setNeedsUpdate() } } + + /// Color to the component. The default kind is primary. + open var kind: Kind = .primary { didSet { setNeedsUpdate() } } + + /// Size of the component. It varies by size and viewport(mobile/Tablet). + /// The default size is medium with viewport mobile. + open var size: Size = .medium { didSet { setNeedsUpdate() } } + + /// If true, the component with a strikethrough. It applies only when uniformSize is true. + /// Does not apply a strikethrough format to leading and trailing text. + open var strikethrough: Bool = false { didSet { setNeedsUpdate() } } + + /// Term text for the component. Superscript placement can vary when term and delimeter are "none". + /// The default term is 'month'. + open var term: Term = .month { didSet { setNeedsUpdate() } } + + /// Trailing text for the component. + open var trailingText: String? { didSet { setNeedsUpdate() } } + + /// Superscript text for the component. + open var superscript: String? { didSet { setNeedsUpdate() } } + + /// If true, currency and value have the same font text style as delimeter, term label and superscript. + /// This will render the pricing and term sections as a uniform size. + open var uniformSize: Bool = false { didSet { setNeedsUpdate() } } + + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - + internal var containerView = View().with { + $0.clipsToBounds = true + } //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- - + internal var containerSize: CGSize { CGSize(width: 45, height: 44) } + //-------------------------------------------------- // 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() + addSubview(containerView) + containerView + .pinTop() + .pinBottom() + .pinLeadingGreaterThanOrEqualTo() + .pinTrailingLessThanOrEqualTo() + .height(containerSize.height) + + containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } /// Used to make changes to the View based off a change events or from local properties. From 6938d918605b4e6e9c1aaba7f94f91bc7a6860df Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 7 Aug 2024 16:36:30 -0500 Subject: [PATCH 069/113] refactored a ton of code... Signed-off-by: Matt Bruce --- .../InputStepper/InputStepper.swift | 201 ++++++++---------- 1 file changed, 89 insertions(+), 112 deletions(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 3637723d..92730f20 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -138,12 +138,7 @@ open class InputStepper: EntryFieldBase { } /// The size of the input stepper. Defaults to 'large'. - open var size: Size = .large { - didSet { - updateStepperContainerViewSize() - setNeedsUpdate() - } - } + open var size: Size = .large { didSet { setNeedsUpdate() } } /// Accepts any text or character to appear next to input stepper value. open var trailingText: String? { didSet { setNeedsUpdate() } } @@ -205,10 +200,7 @@ open class InputStepper: EntryFieldBase { // MARK: - Configuration Properties //-------------------------------------------------- internal override var containerSize: CGSize { CGSize(width: size.minWidth, height: size.minHeight) } - - internal let labelColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) - internal let labelDisabledColorConfiguration = SurfaceColorConfiguration(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark) - + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -216,12 +208,19 @@ open class InputStepper: EntryFieldBase { /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() - // Set initial states defaultValue = 0 containerView.isEnabled = false statusIcon.isHidden = true + //override the default settings since the containerView + //fieldStackView relationShip needs to be updated + //we are not applying spacing either in the edges since this + //is the view that will take place of the containerView for the + //design of the original "containerView". This will get refactored at + //some point. + fieldStackView.applyAlignment(.leading) + // Add listeners decrementButton.onClick = { _ in self.decrementButtonClick() } incrementButton.onClick = { _ in self.incrementButtonClick() } @@ -238,17 +237,15 @@ open class InputStepper: EntryFieldBase { // Set space between decrement button, label, and increment button relative to input Stepper size. stepperStackView.setCustomSpacing(size.space, after: decrementButton) stepperStackView.setCustomSpacing(size.space, after: textLabel) - - // Update Edge insets relative to input Stepper size. - stepperStackView.pinToSuperView(.uniform(size.padding)) - + // stepperContainerView for controls in EntryFieldBase.controlContainerView stepperContainerView.addSubview(stepperStackView) - stepperWidthConstraint = stepperContainerView.widthAnchor.constraint(equalToConstant: containerSize.width) - stepperWidthConstraint?.deactivate() - stepperHeightConstraint = stepperContainerView.heightAnchor.constraint(equalToConstant: containerSize.height) - stepperHeightConstraint?.deactivate() + // Update Edge insets relative to input Stepper size. + stepperStackView.pinToSuperView(.uniform(size.padding)) + + stepperWidthConstraint = stepperContainerView.width(constant: containerSize.width, priority: .required) + stepperHeightConstraint = stepperContainerView.height(constant: containerSize.height, priority: .required) return stepperContainerView } @@ -259,19 +256,12 @@ open class InputStepper: EntryFieldBase { statusIcon.isHidden = true // Update label text, style, color, ande surface. + textLabel.isEnabled = isEnabled + textLabel.surface = surface textLabel.text = "\(_defaultValue) " + (trailingText ?? "") textLabel.textStyle = size.textStyle - textLabel.textColorConfiguration = !isEnabled ? labelDisabledColorConfiguration.eraseToAnyColorable() : labelColorConfiguration.eraseToAnyColorable() - textLabel.surface = surface - // Update increment and decrement button. updateButtonStates() - - // Update stepper container border and corner radius. - updateContainerWidthWithPercentage() - updateInputStepperWidth() - updateStepperView() - setNeedsLayout() } open override var accessibilityElements: [Any]? { @@ -314,29 +304,89 @@ open class InputStepper: EntryFieldBase { //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- + override func updateContainerView() { + //we are not calling super since we + //are using the fieldStackView as the "containerView" + //which will get the look/feel of the containerView. + //this will get refactored in the future. + fieldStackView.backgroundColor = containerBackgroundColor + fieldStackView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor + fieldStackView.layer.borderWidth = VDSFormControls.borderWidth + } + internal override func updateContainerWidth() { + + defer { + fieldStackView.layer.cornerRadius = containerSize.height / 2 + } + + //we are not calling super here since + //we are changing how the widths are getting calculated + //now by including a percentage. stepperWidthConstraint?.deactivate() widthConstraint?.deactivate() trailingLessThanEqualsConstraint?.deactivate() trailingEqualsConstraint?.deactivate() + + var widthConstraintConstant: CGFloat? - if let width, width >= minWidth, width <= maxWidth { - widthConstraint?.constant = width - widthConstraint?.activate() - trailingLessThanEqualsConstraint?.activate() + if let widthPercentage, let superWidth = horizontalPinnedWidth() { + // test value vs minimum width and take the greater value + widthConstraintConstant = max(superWidth * (widthPercentage / 100), minWidth) + } else if let width, width >= minWidth, width <= maxWidth { + widthConstraintConstant = width } else if let parentWidth = width, parentWidth >= maxWidth { - width = maxWidth - widthConstraint?.constant = maxWidth - widthConstraint?.activate() - trailingLessThanEqualsConstraint?.activate() + widthConstraintConstant = maxWidth } else if let parentWidth = width, parentWidth <= minWidth { - width = minWidth - widthConstraint?.constant = minWidth + widthConstraintConstant = minWidth + } + + if let widthConstraintConstant { + widthConstraint?.constant = widthConstraintConstant widthConstraint?.activate() trailingLessThanEqualsConstraint?.activate() } else { trailingEqualsConstraint?.activate() } + + // Update Edge insets if size changes applied. + stepperStackView.applyAlignment(.fill, edges: .uniform(size.padding)) + + // Update height if size changes applied. + stepperHeightConstraint?.constant = containerSize.height + + //update the stepper's widthConstraint if + //controlWidth was set + guard let controlWidth else { + return + } + + var stepperWidthConstant: CGFloat? + var stepperWidth: CGFloat + var containerWidth: CGFloat + + switch controlWidth { + case .percentage(let percentage): + // Set the inputStepper's controlWidth based on percentage received relative to its parentView's frame. + containerWidth = widthConstraintConstant ?? CGFloat(containerView.frame.size.width) + stepperWidth = max(containerWidth * ((percentage) / 100), minWidth) + + case .value(let value): + containerWidth = widthConstraintConstant ?? CGFloat(containerView.frame.size.width) + stepperWidth = value + } + + //get the value of the stepperWidthConstant + if stepperWidth >= containerSize.width && stepperWidth <= containerWidth { + stepperWidthConstant = stepperWidth + } else if stepperWidth >= containerWidth { + stepperWidthConstant = containerWidth + } + + if let stepperWidthConstant { + stepperWidthConstraint?.constant = stepperWidthConstant + stepperWidthConstraint?.activate() + } } //-------------------------------------------------- @@ -370,78 +420,5 @@ open class InputStepper: EntryFieldBase { incrementButton.isEnabled = (defaultValue ?? _defaultMinValue) < maxValue ? true : false } } - - // Update edge insets and height when size changes. - internal func updateStepperContainerViewSize() { - updateButtonStates() - - // Update Edge insets if size changes applied. - stepperStackView.removeFromSuperview() - stepperContainerView.addSubview(stepperStackView) - stepperStackView.pinToSuperView(.uniform(size.padding)) - - // Update height if size changes applied. - stepperHeightConstraint?.deactivate() - stepperHeightConstraint = stepperContainerView.heightAnchor.constraint(equalToConstant: containerSize.height) - stepperHeightConstraint?.activate() - } - - // Set control width to input stepper. - private func updateInputStepperWidth() { - guard let controlWidth else { - return - } - - switch controlWidth { - case .percentage(let percentage): - // Set the inputStepper's controlWidth based on percentage received relative to its parentView's frame. - let superWidth = width ?? CGFloat(containerView.frame.size.width) - let value = max(superWidth * ((percentage) / 100), minWidth) - updateStepperContainerWidth(controlWidth: value, width: superWidth) - - case .value(let value): - let superWidth = width ?? CGFloat(containerView.frame.size.width) - updateStepperContainerWidth(controlWidth: value, width: superWidth) - } - } - - // Handling the controlwidth without exceeding the width of the parent container. - private func updateStepperContainerWidth(controlWidth: CGFloat, width: CGFloat) { - if controlWidth >= containerSize.width && controlWidth <= width { - stepperWidthConstraint?.deactivate() - stepperWidthConstraint?.constant = controlWidth - stepperWidthConstraint?.activate() - } else if controlWidth >= width { - stepperWidthConstraint?.deactivate() - stepperWidthConstraint?.constant = width - stepperWidthConstraint?.activate() - } - } - - // Update the container view width based on the percentage received. - private func updateContainerWidthWithPercentage() { - guard let superWidth = horizontalPinnedWidth() else { - return - } - - // Set width of Parent container based on width perecentage received relative to its superview frame. - if let widthPercentage { - // test value vs minimum width and take the greater value - width = max(superWidth * (widthPercentage / 100), minWidth) - } - } - - // Add border and update constratints to stepper view. - private func updateStepperView() { - fieldStackView.removeConstraints() - fieldStackView.pinTop().pinLeading().pinBottom().pinTrailingLessThanOrEqualTo() - containerView.backgroundColor = .clear - containerView.layer.borderColor = nil - containerView.layer.borderWidth = 0 - containerView.layer.cornerRadius = 0 - fieldStackView.backgroundColor = containerBackgroundColor - fieldStackView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor - fieldStackView.layer.borderWidth = VDSFormControls.borderWidth - fieldStackView.layer.cornerRadius = containerView.frame.size.height / 2 - } + } From cbdd82ee31a10c6c7e1d8ba0662aa38a49a3518c Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 7 Aug 2024 16:37:38 -0500 Subject: [PATCH 070/113] updated comments Signed-off-by: Matt Bruce --- VDS/Components/InputStepper/InputStepper.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 92730f20..11fde045 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -255,7 +255,7 @@ open class InputStepper: EntryFieldBase { super.updateView() statusIcon.isHidden = true - // Update label text, style, color, ande surface. + // Update label textLabel.isEnabled = isEnabled textLabel.surface = surface textLabel.text = "\(_defaultValue) " + (trailingText ?? "") From 6c4fb1c5ba01ce1475053fdd6d4828ca1a992ff8 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 7 Aug 2024 16:42:19 -0500 Subject: [PATCH 071/113] moved code up to 1 point rather than duplicating Signed-off-by: Matt Bruce --- VDS/Components/InputStepper/InputStepper.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 11fde045..9302d0ec 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -361,18 +361,16 @@ open class InputStepper: EntryFieldBase { return } + // Set the inputStepper's controlWidth based on percentage received relative to its parentView's frame. + let containerWidth: CGFloat = widthConstraintConstant ?? containerView.frame.size.width var stepperWidthConstant: CGFloat? var stepperWidth: CGFloat - var containerWidth: CGFloat switch controlWidth { case .percentage(let percentage): - // Set the inputStepper's controlWidth based on percentage received relative to its parentView's frame. - containerWidth = widthConstraintConstant ?? CGFloat(containerView.frame.size.width) stepperWidth = max(containerWidth * ((percentage) / 100), minWidth) case .value(let value): - containerWidth = widthConstraintConstant ?? CGFloat(containerView.frame.size.width) stepperWidth = value } From d4e7d7476d114fd1ccd64bd5f5b3903e9fa954de Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 7 Aug 2024 16:43:14 -0500 Subject: [PATCH 072/113] rearranged comments Signed-off-by: Matt Bruce --- VDS/Components/InputStepper/InputStepper.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/VDS/Components/InputStepper/InputStepper.swift b/VDS/Components/InputStepper/InputStepper.swift index 9302d0ec..6c44b58f 100644 --- a/VDS/Components/InputStepper/InputStepper.swift +++ b/VDS/Components/InputStepper/InputStepper.swift @@ -315,14 +315,14 @@ open class InputStepper: EntryFieldBase { } internal override func updateContainerWidth() { - + //we are not calling super here since + //we are changing how the widths are getting calculated + //now by including a percentage. + defer { fieldStackView.layer.cornerRadius = containerSize.height / 2 } - //we are not calling super here since - //we are changing how the widths are getting calculated - //now by including a percentage. stepperWidthConstraint?.deactivate() widthConstraint?.deactivate() trailingLessThanEqualsConstraint?.deactivate() From 1e9aa876dbe77b303c6cd5fc8024da5b9a0d1de3 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 8 Aug 2024 10:59:30 -0500 Subject: [PATCH 073/113] refactor for use in both UIControl and UIView Signed-off-by: Matt Bruce --- VDS/Protocols/Clickable.swift | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/VDS/Protocols/Clickable.swift b/VDS/Protocols/Clickable.swift index c2fc9056..fe6ff10d 100644 --- a/VDS/Protocols/Clickable.swift +++ b/VDS/Protocols/Clickable.swift @@ -9,12 +9,12 @@ import Foundation import UIKit import Combine -public protocol Clickable: ViewProtocol where Self: UIControl { +public protocol Clickable: ViewProtocol { /// Sets the primary Subscriber used for the UIControl event .touchUpInside. var onClickSubscriber: AnyCancellable? { get set } } -extension Clickable { +extension Clickable where Self: UIControl { /// Allows the setting of a completion block against the onClickSubscriber cancellable. This will /// completion block will get executed against the UIControl publisher for the 'touchUpInside' action. public var onClick: ((Self) -> ())? { @@ -23,7 +23,7 @@ extension Clickable { onClickSubscriber?.cancel() if let newValue { onClickSubscriber = publisher(for: .touchUpInside) - .sink { [weak self] c in + .sink { [weak self] c in guard let self, self.isEnabled else { return } newValue(c) } @@ -34,3 +34,24 @@ extension Clickable { } } } + +extension Clickable where Self: UIView { + /// Allows the setting of a completion block against the onClickSubscriber cancellable. This will + /// completion block will get executed against the UIControl publisher for the 'touchUpInside' action. + public var onClick: ((Self) -> ())? { + get { return nil } + set { + onClickSubscriber?.cancel() + if let newValue { + onClickSubscriber = publisher(for: UITapGestureRecognizer()) + .sink { [weak self] _ in + guard let self, self.isEnabled else { return } + newValue(self) + } + } else { + onClickSubscriber = nil + } + setNeedsUpdate() + } + } +} From 7e36bc074a04235f3c67f6b5955b828a0b941e2e Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 8 Aug 2024 10:59:45 -0500 Subject: [PATCH 074/113] added clickable Signed-off-by: Matt Bruce --- VDS/BaseClasses/View.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/VDS/BaseClasses/View.swift b/VDS/BaseClasses/View.swift index 88996ba6..8012c5aa 100644 --- a/VDS/BaseClasses/View.swift +++ b/VDS/BaseClasses/View.swift @@ -12,7 +12,7 @@ import Combine /// Base Class used to build Views. @objcMembers @objc(VDSView) -open class View: UIView, ViewProtocol, UserInfoable { +open class View: UIView, ViewProtocol, UserInfoable, Clickable { //-------------------------------------------------- // MARK: - Initializers @@ -37,6 +37,7 @@ open class View: UIView, ViewProtocol, UserInfoable { //-------------------------------------------------- open var subscribers = Set() + open var onClickSubscriber: AnyCancellable? //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- From e0d769ccf3cb332c821fec3c9ab2deb87906d1f7 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 8 Aug 2024 11:00:02 -0500 Subject: [PATCH 075/113] refactored to now use the onClick Signed-off-by: Matt Bruce --- VDS/Components/DatePicker/DatePicker.swift | 13 +++++-------- VDS/Components/DropdownSelect/DropdownSelect.swift | 9 +++------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index df7cc7c9..d65465b7 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -154,15 +154,12 @@ open class DatePicker: EntryFieldBase { selectedDateLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() // tap gesture - containerView - .publisher(for: UITapGestureRecognizer()) - .sink { [weak self] _ in - guard let self else { return } - if isEnabled && !isReadOnly { - showPopover() - } + containerView.onClick = { [weak self] _ in + guard let self else { return } + if isEnabled && !isReadOnly { + showPopover() } - .store(in: &subscribers) + } NotificationCenter.default .publisher(for: UIDevice.orientationDidChangeNotification).sink { [weak self] _ in diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 947210f4..f0dc8084 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -153,12 +153,9 @@ open class DropdownSelect: EntryFieldBase { }() // tap gesture - containerView - .publisher(for: UITapGestureRecognizer()) - .sink { [weak self] _ in - self?.launchPicker() - } - .store(in: &subscribers) + containerView.onClick = { [weak self] _ in + self?.launchPicker() + } containerView.height(44) } From 0434b3f63ec207f22bfda1369243773acf6fc45a Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 8 Aug 2024 11:00:33 -0500 Subject: [PATCH 076/113] refactored to a View instead of Control implemented isHighlighted Signed-off-by: Matt Bruce --- .../TileContainer/TileContainer.swift | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index f35d2614..cdfe675a 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -44,7 +44,7 @@ open class TileContainer: TileContainerBase { } } -open class TileContainerBase: Control where PaddingType.ValueType == CGFloat { +open class TileContainerBase: View where PaddingType.ValueType == CGFloat { //-------------------------------------------------- // MARK: - Initializers @@ -118,6 +118,8 @@ open class TileContainerBase: Control where Padding $0.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) } + private var isHighlighted: Bool = false { didSet { setNeedsUpdate() } } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @@ -337,6 +339,27 @@ open class TileContainerBase: Control where Padding set {} } + open override func touchesBegan(_ touches: Set, with event: UIEvent?) { + super.touchesBegan(touches, with: event) + if let onClickSubscriber { + isHighlighted = true + } + } + + open override func touchesEnded(_ touches: Set, with event: UIEvent?) { + super.touchesEnded(touches, with: event) + if let onClickSubscriber { + isHighlighted = false + } + } + + open override func touchesCancelled(_ touches: Set, with event: UIEvent?) { + super.touchesCancelled(touches, with: event) + if let onClickSubscriber { + isHighlighted = false + } + } + //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- From 317cf89fb6b1fcb2ef7827b8c8052653162d0f09 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 11:21:51 -0500 Subject: [PATCH 077/113] ensure updateView() can't be called until setup() is run Signed-off-by: Matt Bruce --- VDS/BaseClasses/Control.swift | 2 ++ VDS/BaseClasses/View.swift | 2 ++ VDS/Components/Buttons/ButtonBase.swift | 2 ++ VDS/Components/Label/Label.swift | 2 ++ VDS/Components/TextFields/InputField/TextField.swift | 2 ++ VDS/Components/TextFields/TextArea/TextView.swift | 2 ++ 6 files changed, 12 insertions(+) diff --git a/VDS/BaseClasses/Control.swift b/VDS/BaseClasses/Control.swift index 472f0635..0dcaa3f1 100644 --- a/VDS/BaseClasses/Control.swift +++ b/VDS/BaseClasses/Control.swift @@ -81,7 +81,9 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { open func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true + shouldUpdateView = false setup() + shouldUpdateView = true setNeedsUpdate() } } diff --git a/VDS/BaseClasses/View.swift b/VDS/BaseClasses/View.swift index 88996ba6..b39afbf2 100644 --- a/VDS/BaseClasses/View.swift +++ b/VDS/BaseClasses/View.swift @@ -60,7 +60,9 @@ open class View: UIView, ViewProtocol, UserInfoable { open func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true + shouldUpdateView = false setup() + shouldUpdateView = true setNeedsUpdate() } } diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index c764b89b..b210560a 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -103,7 +103,9 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false accessibilityCustomActions = [] + shouldUpdateView = false setup() + shouldUpdateView = true setNeedsUpdate() } } diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index f420be7d..3d56470b 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -209,7 +209,9 @@ open class Label: UILabel, ViewProtocol, UserInfoable { isAccessibilityElement = true accessibilityTraits = .staticText textAlignment = .left + shouldUpdateView = false setup() + shouldUpdateView = true setNeedsUpdate() } } diff --git a/VDS/Components/TextFields/InputField/TextField.swift b/VDS/Components/TextFields/InputField/TextField.swift index 3fdf308f..7adf1aa0 100644 --- a/VDS/Components/TextFields/InputField/TextField.swift +++ b/VDS/Components/TextFields/InputField/TextField.swift @@ -105,7 +105,9 @@ open class TextField: UITextField, ViewProtocol, Errorable { translatesAutoresizingMaskIntoConstraints = false setContentCompressionResistancePriority(.defaultLow, for: .horizontal) clipsToBounds = true + shouldUpdateView = false setup() + shouldUpdateView = true setNeedsUpdate() } } diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift index 63356635..526911c4 100644 --- a/VDS/Components/TextFields/TextArea/TextView.swift +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -112,7 +112,9 @@ open class TextView: UITextView, ViewProtocol, Errorable { initialSetupPerformed = true backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false + shouldUpdateView = false setup() + shouldUpdateView = true setNeedsUpdate() } } From f713cc0659c1d6330ff67847ba1779bdcc443c77 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 11:22:26 -0500 Subject: [PATCH 078/113] CXTDT-599736 - Unable to turn off the Toggles for Privacy preferences Signed-off-by: Matt Bruce --- VDS/Components/Toggle/Toggle.swift | 19 +++++++++---------- VDS/Components/Toggle/ToggleView.swift | 15 +++++++-------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index c147739b..e4390683 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -153,19 +153,18 @@ open class Toggle: Control, Changeable, FormFieldable { //-------------------------------------------------- // MARK: - Overrides - //-------------------------------------------------- - /// Executed on initialization for this View. - open override func initialSetup() { - super.initialSetup() - onClick = { control in - control.toggle() - } - } - + //-------------------------------------------------- /// 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() - + + publisher(for: .touchUpInside) + .sink(receiveValue: { [weak self] _ in + guard let self else { return } + toggle() + }) + .store(in: &subscribers) + isAccessibilityElement = true if #available(iOS 17.0, *) { accessibilityTraits = .toggleButton diff --git a/VDS/Components/Toggle/ToggleView.swift b/VDS/Components/Toggle/ToggleView.swift index 9274be1a..a0dbcda4 100644 --- a/VDS/Components/Toggle/ToggleView.swift +++ b/VDS/Components/Toggle/ToggleView.swift @@ -105,17 +105,16 @@ open class ToggleView: Control, Changeable, FormFieldable { //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - /// Executed on initialization for this View. - open override func initialSetup() { - super.initialSetup() - onClick = { control in - control.toggle() - } - } - /// 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() + + publisher(for: .touchUpInside) + .sink(receiveValue: { [weak self] _ in + guard let self else { return } + toggle() + }) + .store(in: &subscribers) isAccessibilityElement = true if #available(iOS 17.0, *) { From 9f6a83f8ce6c978900417c11f56b2c90349013ae Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 11:23:53 -0500 Subject: [PATCH 079/113] ensure isEnabled Signed-off-by: Matt Bruce --- VDS/Components/Toggle/Toggle.swift | 2 +- VDS/Components/Toggle/ToggleView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index e4390683..29f7dd6d 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -160,7 +160,7 @@ open class Toggle: Control, Changeable, FormFieldable { publisher(for: .touchUpInside) .sink(receiveValue: { [weak self] _ in - guard let self else { return } + guard let self, isEnabled else { return } toggle() }) .store(in: &subscribers) diff --git a/VDS/Components/Toggle/ToggleView.swift b/VDS/Components/Toggle/ToggleView.swift index a0dbcda4..fb663e99 100644 --- a/VDS/Components/Toggle/ToggleView.swift +++ b/VDS/Components/Toggle/ToggleView.swift @@ -111,7 +111,7 @@ open class ToggleView: Control, Changeable, FormFieldable { publisher(for: .touchUpInside) .sink(receiveValue: { [weak self] _ in - guard let self else { return } + guard let self, isEnabled else { return } toggle() }) .store(in: &subscribers) From eb8edd2acd7b95a4787cb9273e130a0a847b4bca Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 11:51:40 -0500 Subject: [PATCH 080/113] removed initialSetup so as devs won't use this method anymore, this will now be private to the base classes Signed-off-by: Matt Bruce --- VDS/Protocols/ViewProtocol.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/VDS/Protocols/ViewProtocol.swift b/VDS/Protocols/ViewProtocol.swift index c7cba091..695386cb 100644 --- a/VDS/Protocols/ViewProtocol.swift +++ b/VDS/Protocols/ViewProtocol.swift @@ -19,9 +19,6 @@ public protocol ViewProtocol: AnyObject, Initable, Resettable, Enabling, Surface /// Used for setting an implementation for the default Accessible Action var accessibilityAction: ((Self) -> Void)? { get set } - /// Executed on initialization for this View. - func initialSetup() - /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. func setup() From 760433243eb04db671a6488fd81329abaeedec36 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 11:52:25 -0500 Subject: [PATCH 081/113] updated to private methods Signed-off-by: Matt Bruce --- VDS/BaseClasses/Control.swift | 2 +- VDS/BaseClasses/View.swift | 2 +- VDS/Components/Buttons/ButtonBase.swift | 8 ++--- VDS/Components/Label/Label.swift | 30 +++++++++---------- .../TextFields/InputField/TextField.swift | 11 +++---- .../TextFields/TextArea/TextView.swift | 7 +++-- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/VDS/BaseClasses/Control.swift b/VDS/BaseClasses/Control.swift index 0dcaa3f1..ad138db9 100644 --- a/VDS/BaseClasses/Control.swift +++ b/VDS/BaseClasses/Control.swift @@ -78,7 +78,7 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - open func initialSetup() { + private func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true shouldUpdateView = false diff --git a/VDS/BaseClasses/View.swift b/VDS/BaseClasses/View.swift index b39afbf2..b8acb096 100644 --- a/VDS/BaseClasses/View.swift +++ b/VDS/BaseClasses/View.swift @@ -57,7 +57,7 @@ open class View: UIView, ViewProtocol, UserInfoable { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - open func initialSetup() { + private func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true shouldUpdateView = false diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index b210560a..8af0ee68 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -97,12 +97,9 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - open func initialSetup() { + private func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true - backgroundColor = .clear - translatesAutoresizingMaskIntoConstraints = false - accessibilityCustomActions = [] shouldUpdateView = false setup() shouldUpdateView = true @@ -112,8 +109,9 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { open func setup() { + backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false - + accessibilityCustomActions = [] titleLabel?.adjustsFontSizeToFitWidth = false titleLabel?.lineBreakMode = .byTruncatingTail titleLabel?.numberOfLines = 1 diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 3d56470b..5930dea7 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -192,23 +192,9 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - open func initialSetup() { + private 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 = .byTruncatingTail - translatesAutoresizingMaskIntoConstraints = false - accessibilityCustomActions = [] - isAccessibilityElement = true - accessibilityTraits = .staticText - textAlignment = .left shouldUpdateView = false setup() shouldUpdateView = true @@ -217,6 +203,20 @@ open class Label: UILabel, ViewProtocol, UserInfoable { } open func setup() { + //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 = .byTruncatingTail + translatesAutoresizingMaskIntoConstraints = false + accessibilityCustomActions = [] + isAccessibilityElement = true + accessibilityTraits = .staticText + textAlignment = .left } open func reset() { diff --git a/VDS/Components/TextFields/InputField/TextField.swift b/VDS/Components/TextFields/InputField/TextField.swift index 7adf1aa0..2f6ddb1a 100644 --- a/VDS/Components/TextFields/InputField/TextField.swift +++ b/VDS/Components/TextFields/InputField/TextField.swift @@ -98,13 +98,9 @@ open class TextField: UITextField, ViewProtocol, Errorable { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - open func initialSetup() { + private func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true - backgroundColor = .clear - translatesAutoresizingMaskIntoConstraints = false - setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - clipsToBounds = true shouldUpdateView = false setup() shouldUpdateView = true @@ -113,6 +109,11 @@ open class TextField: UITextField, ViewProtocol, Errorable { } open func setup() { + backgroundColor = .clear + translatesAutoresizingMaskIntoConstraints = false + setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + clipsToBounds = true + let accessView = UIView(frame: .init(origin: .zero, size: .init(width: UIScreen.main.bounds.width, height: 44))) accessView.backgroundColor = .white accessView.addBorder(side: .top, width: 1, color: .lightGray) diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift index 526911c4..575c2da8 100644 --- a/VDS/Components/TextFields/TextArea/TextView.swift +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -107,11 +107,9 @@ open class TextView: UITextView, ViewProtocol, Errorable { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - open func initialSetup() { + private func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true - backgroundColor = .clear - translatesAutoresizingMaskIntoConstraints = false shouldUpdateView = false setup() shouldUpdateView = true @@ -120,6 +118,9 @@ open class TextView: UITextView, ViewProtocol, Errorable { } open func setup() { + backgroundColor = .clear + translatesAutoresizingMaskIntoConstraints = false + let accessView = UIView(frame: .init(origin: .zero, size: .init(width: UIScreen.main.bounds.width, height: 44))) accessView.backgroundColor = .white accessView.addBorder(side: .top, width: 1, color: .lightGray) From c2c88c7b90d2bc34780e33383348c3e7d0727887 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 11:57:28 -0500 Subject: [PATCH 082/113] moved to setup Signed-off-by: Matt Bruce --- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 4 ++-- VDS/Components/Calendar/Calendar.swift | 4 ---- VDS/Components/Carousel/Carousel.swift | 5 ----- VDS/Components/CarouselScrollbar/CarouselScrollbar.swift | 4 ---- VDS/Components/Pagination/Pagination.swift | 4 ++-- VDS/Components/Pagination/PaginationButton.swift | 4 ++-- VDS/Components/Table/Table.swift | 4 ++-- 7 files changed, 8 insertions(+), 21 deletions(-) diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index 5a2de7d0..6c45383d 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -109,8 +109,8 @@ open class Breadcrumbs: View { // MARK: - Overrides //-------------------------------------------------- /// Executed on initialization for this View. - open override func initialSetup() { - super.initialSetup() + open override func setup() { + super.setup() containerView.addSubview(collectionView) collectionView.pinToSuperView() addSubview(containerView) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index bed772d8..e7c2cb20 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -125,10 +125,6 @@ open class CalendarBase: Control, Changeable { //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - open override func initialSetup() { - super.initialSetup() - } - /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index d1624253..766a7a70 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -196,11 +196,6 @@ open class Carousel: View { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - /// Executed on initialization for this View. - open override func initialSetup() { - super.initialSetup() - } - /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() diff --git a/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift b/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift index f171c395..8a4d4a50 100644 --- a/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift +++ b/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift @@ -235,10 +235,6 @@ open class CarouselScrollbar: View { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - open override func initialSetup() { - super.initialSetup() - } - open override func setup() { super.setup() isAccessibilityElement = false diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index 02c1e85e..6234e155 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -95,8 +95,8 @@ open class Pagination: View { // MARK: - Overrides //-------------------------------------------------- /// Executed on initialization for this View. - open override func initialSetup() { - super.initialSetup() + open override func setup() { + super.setup() collectionContainerView.addSubview(collectionView) containerView.addSubview(previousButton) diff --git a/VDS/Components/Pagination/PaginationButton.swift b/VDS/Components/Pagination/PaginationButton.swift index d5de6e3f..14f399b9 100644 --- a/VDS/Components/Pagination/PaginationButton.swift +++ b/VDS/Components/Pagination/PaginationButton.swift @@ -60,8 +60,8 @@ open class PaginationButton: ButtonBase { // MARK: - Overrides //-------------------------------------------------- /// Executed on initialization for this View. - open override func initialSetup() { - super.initialSetup() + open override func setup() { + super.setup() if #available(iOS 15.0, *) { configuration = buttonConfiguration } else { diff --git a/VDS/Components/Table/Table.swift b/VDS/Components/Table/Table.swift index bb599545..95873d33 100644 --- a/VDS/Components/Table/Table.swift +++ b/VDS/Components/Table/Table.swift @@ -92,8 +92,8 @@ open class Table: View { //-------------------------------------------------- ///Called upon initializing the table view - open override func initialSetup() { - super.initialSetup() + open override func setup() { + super.setup() addSubview(matrixView) matrixView.pinToSuperView() } From 0decdb5a1685189f0fc9f5435ebec1015ba2f8b2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 13:19:33 -0500 Subject: [PATCH 083/113] add the touchUpInside since this is the only one using this Signed-off-by: Matt Bruce --- .../Icon/ButtonIcon/ButtonIcon.swift | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift index bf8a50f7..95406fb4 100644 --- a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift +++ b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift @@ -402,17 +402,15 @@ open class ButtonIcon: Control, Changeable { centerXConstraint?.activate() centerYConstraint = icon.centerYAnchor.constraint(equalTo: iconLayoutGuide.centerYAnchor, constant: 0) centerYConstraint?.activate() - } - - /// Executed on initialization for this View. - open override func initialSetup() { - super.initialSetup() - onClick = { control in - guard control.isEnabled else { return } - if control.selectedIconName != nil && control.selectable { - control.toggle() - } - } + + publisher(for: .touchUpInside) + .sink(receiveValue: { [weak self] _ in + guard let self, isEnabled, + selectedIconName != nil, + selectable else { return } + toggle() + }) + .store(in: &subscribers) } /// This will change the state of the Selector and execute the actionBlock if provided. From 09886ffe26efec41de21a7c76f9cda091820f055 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 14:35:51 -0500 Subject: [PATCH 084/113] added setDefaults() into protocol and implemented Signed-off-by: Matt Bruce --- VDS/BaseClasses/Control.swift | 23 ++++++++++++------- VDS/BaseClasses/View.swift | 16 +++++++++---- VDS/Components/Buttons/ButtonBase.swift | 18 +++++++++------ VDS/Components/Label/Label.swift | 20 ++++++++-------- .../TextFields/InputField/TextField.swift | 16 ++++++++++--- .../TextFields/TextArea/TextView.swift | 14 ++++++++--- VDS/Protocols/ViewProtocol.swift | 5 +++- 7 files changed, 77 insertions(+), 35 deletions(-) diff --git a/VDS/BaseClasses/Control.swift b/VDS/BaseClasses/Control.swift index ad138db9..7ebe3824 100644 --- a/VDS/BaseClasses/Control.swift +++ b/VDS/BaseClasses/Control.swift @@ -83,17 +83,25 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { initialSetupPerformed = true shouldUpdateView = false setup() + setDefaults() shouldUpdateView = true setNeedsUpdate() } } open func setup() { - backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false } - + + open func setDefaults() { + backgroundColor = .clear + surface = .light + isEnabled = true + onClick = nil + userInfo.removeAll() + } + open func updateView() { } open func updateAccessibility() { @@ -110,13 +118,12 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { } } - + open func reset() { - backgroundColor = .clear - surface = .light - isEnabled = true - onClick = nil - userInfo.removeAll() + shouldUpdateView = false + setDefaults() + shouldUpdateView = true + setNeedsUpdate() } //-------------------------------------------------- diff --git a/VDS/BaseClasses/View.swift b/VDS/BaseClasses/View.swift index b8acb096..911daa69 100644 --- a/VDS/BaseClasses/View.swift +++ b/VDS/BaseClasses/View.swift @@ -62,17 +62,24 @@ open class View: UIView, ViewProtocol, UserInfoable { initialSetupPerformed = true shouldUpdateView = false setup() + setDefaults() shouldUpdateView = true setNeedsUpdate() } } open func setup() { - backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false } + open func setDefaults() { + backgroundColor = .clear + surface = .light + isEnabled = true + userInfo.removeAll() + } + open func updateView() { } open func updateAccessibility() { @@ -84,9 +91,10 @@ open class View: UIView, ViewProtocol, UserInfoable { } open func reset() { - backgroundColor = .clear - surface = .light - isEnabled = true + shouldUpdateView = false + setDefaults() + shouldUpdateView = true + setNeedsUpdate() } open override func layoutSubviews() { diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index 8af0ee68..b555218d 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -102,6 +102,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { initialSetupPerformed = true shouldUpdateView = false setup() + setDefaults() shouldUpdateView = true setNeedsUpdate() } @@ -109,12 +110,20 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { open func setup() { - backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false + } + + open func setDefaults() { + backgroundColor = .clear accessibilityCustomActions = [] titleLabel?.adjustsFontSizeToFitWidth = false titleLabel?.lineBreakMode = .byTruncatingTail titleLabel?.numberOfLines = 1 + surface = .light + isEnabled = true + text = nil + onClick = nil + userInfo.removeAll() } open func updateView() { @@ -131,12 +140,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { open func reset() { shouldUpdateView = false - surface = .light - isEnabled = true - text = nil - accessibilityCustomActions = [] - onClick = nil - userInfo.removeAll() + setDefaults() shouldUpdateView = true setNeedsUpdate() } diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 5930dea7..245dcf81 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -197,6 +197,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { initialSetupPerformed = true shouldUpdateView = false setup() + setDefaults() shouldUpdateView = true setNeedsUpdate() } @@ -209,27 +210,28 @@ open class Label: UILabel, ViewProtocol, UserInfoable { .sink { [weak self] notification in self?.setNeedsUpdate() }.store(in: &subscribers) - backgroundColor = .clear - numberOfLines = 0 - lineBreakMode = .byTruncatingTail translatesAutoresizingMaskIntoConstraints = false - accessibilityCustomActions = [] isAccessibilityElement = true - accessibilityTraits = .staticText - textAlignment = .left } - open func reset() { - shouldUpdateView = false + open func setDefaults() { + backgroundColor = .clear + accessibilityTraits = .staticText + accessibilityCustomActions = [] surface = .light isEnabled = true attributes = nil textStyle = .defaultStyle + lineBreakMode = .byTruncatingTail textAlignment = .left text = nil attributedText = nil numberOfLines = 0 - backgroundColor = .clear + } + + open func reset() { + shouldUpdateView = false + setDefaults() shouldUpdateView = true setNeedsUpdate() } diff --git a/VDS/Components/TextFields/InputField/TextField.swift b/VDS/Components/TextFields/InputField/TextField.swift index 2f6ddb1a..9fb8d7f6 100644 --- a/VDS/Components/TextFields/InputField/TextField.swift +++ b/VDS/Components/TextFields/InputField/TextField.swift @@ -103,13 +103,13 @@ open class TextField: UITextField, ViewProtocol, Errorable { initialSetupPerformed = true shouldUpdateView = false setup() + setDefaults() shouldUpdateView = true setNeedsUpdate() } } open func setup() { - backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false setContentCompressionResistancePriority(.defaultLow, for: .horizontal) clipsToBounds = true @@ -127,6 +127,17 @@ open class TextField: UITextField, ViewProtocol, Errorable { inputAccessoryView = accessView } + open func setDefaults() { + backgroundColor = .clear + surface = .light + text = nil + formatText = nil + useScaledFont = false + showError = false + errorText = nil + textStyle = .defaultStyle + } + @objc func doneButtonAction() { // Resigns the first responder status when 'Done' is tapped let _ = resignFirstResponder() @@ -177,8 +188,7 @@ open class TextField: UITextField, ViewProtocol, Errorable { open func reset() { shouldUpdateView = false - surface = .light - text = nil + setDefaults() shouldUpdateView = true setNeedsUpdate() } diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift index 575c2da8..b8098a49 100644 --- a/VDS/Components/TextFields/TextArea/TextView.swift +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -112,13 +112,13 @@ open class TextView: UITextView, ViewProtocol, Errorable { initialSetupPerformed = true shouldUpdateView = false setup() + setDefaults() shouldUpdateView = true setNeedsUpdate() } } open func setup() { - backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false let accessView = UIView(frame: .init(origin: .zero, size: .init(width: UIScreen.main.bounds.width, height: 44))) @@ -137,6 +137,15 @@ open class TextView: UITextView, ViewProtocol, Errorable { placeholderLabel.pinToSuperView() } + open func setDefaults() { + backgroundColor = .clear + surface = .light + text = nil + placeholder = nil + errorText = nil + showError = false + } + @objc func doneButtonAction() { // Resigns the first responder status when 'Done' is tapped resignFirstResponder() @@ -156,8 +165,7 @@ open class TextView: UITextView, ViewProtocol, Errorable { open func reset() { shouldUpdateView = false - surface = .light - text = nil + setDefaults() shouldUpdateView = true setNeedsUpdate() } diff --git a/VDS/Protocols/ViewProtocol.swift b/VDS/Protocols/ViewProtocol.swift index 695386cb..9a509dc1 100644 --- a/VDS/Protocols/ViewProtocol.swift +++ b/VDS/Protocols/ViewProtocol.swift @@ -21,7 +21,10 @@ public protocol ViewProtocol: AnyObject, Initable, Resettable, Enabling, Surface /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. func setup() - + + /// Default configurations for values and properties. This is called in the setup() and reset(). + func setDefaults() + /// Used to make changes to the View based off a change events or from local properties. func updateView() From 973e25034e1b404e2d0cb72984c73740a6647714 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 14:37:08 -0500 Subject: [PATCH 085/113] reverted back to onClick() since I found the issue in reset() Signed-off-by: Matt Bruce --- VDS/Components/Toggle/Toggle.swift | 12 +++++------- VDS/Components/Toggle/ToggleView.swift | 10 ++++------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index 29f7dd6d..260fdc31 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -157,13 +157,11 @@ open class Toggle: Control, Changeable, FormFieldable { /// 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() - - publisher(for: .touchUpInside) - .sink(receiveValue: { [weak self] _ in - guard let self, isEnabled else { return } - toggle() - }) - .store(in: &subscribers) + + onClick = { [weak self] _ in + guard let self else { return } + toggle() + } isAccessibilityElement = true if #available(iOS 17.0, *) { diff --git a/VDS/Components/Toggle/ToggleView.swift b/VDS/Components/Toggle/ToggleView.swift index fb663e99..1ba5d605 100644 --- a/VDS/Components/Toggle/ToggleView.swift +++ b/VDS/Components/Toggle/ToggleView.swift @@ -109,12 +109,10 @@ open class ToggleView: Control, Changeable, FormFieldable { open override func setup() { super.setup() - publisher(for: .touchUpInside) - .sink(receiveValue: { [weak self] _ in - guard let self, isEnabled else { return } - toggle() - }) - .store(in: &subscribers) + onClick = { [weak self] _ in + guard let self else { return } + toggle() + } isAccessibilityElement = true if #available(iOS 17.0, *) { From 59294ebd511803b2926dba88223536fde8f76ed8 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 14:37:36 -0500 Subject: [PATCH 086/113] using setup() now Signed-off-by: Matt Bruce --- VDS/BaseClasses/Selector/SelectorBase.swift | 16 +++++++--------- VDS/BaseClasses/Selector/SelectorItemBase.swift | 16 ++++++---------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/VDS/BaseClasses/Selector/SelectorBase.swift b/VDS/BaseClasses/Selector/SelectorBase.swift index 1e2f5fba..fd794479 100644 --- a/VDS/BaseClasses/Selector/SelectorBase.swift +++ b/VDS/BaseClasses/Selector/SelectorBase.swift @@ -100,9 +100,11 @@ open class SelectorBase: Control, SelectorControlable { //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - /// Executed on initialization for this View. - open override func initialSetup() { - super.initialSetup() + + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. + open override func setup() { + super.setup() + onClick = { control in control.toggle() } @@ -116,14 +118,10 @@ open class SelectorBase: Control, SelectorControlable { guard let self else { return "" } return !isEnabled ? "" : "Double tap to activate." } - } - - /// 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 + } open override func updateView() { diff --git a/VDS/BaseClasses/Selector/SelectorItemBase.swift b/VDS/BaseClasses/Selector/SelectorItemBase.swift index 44ed01b1..e27a9f94 100644 --- a/VDS/BaseClasses/Selector/SelectorItemBase.swift +++ b/VDS/BaseClasses/Selector/SelectorItemBase.swift @@ -157,9 +157,11 @@ open class SelectorItemBase: Control, Errorable, Changea //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - /// Executed on initialization for this View. - open override func initialSetup() { - super.initialSetup() + + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. + open override func setup() { + super.setup() + onClick = { [weak self] control in guard let self, isEnabled else { return } toggle() @@ -204,13 +206,7 @@ open class SelectorItemBase: Control, Errorable, Changea return !isEnabled ? "" : "Double tap to activate." } - } - - /// 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() - - selectorView.isAccessibilityElement = true + selectorView.isAccessibilityElement = true isAccessibilityElement = false addSubview(mainStackView) From ac1957d1c3ef71f2dc5488cee23e687abcade1f1 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 14:38:32 -0500 Subject: [PATCH 087/113] using submit now Signed-off-by: Matt Bruce --- VDS/Components/RadioBox/RadioBoxItem.swift | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/VDS/Components/RadioBox/RadioBoxItem.swift b/VDS/Components/RadioBox/RadioBoxItem.swift index 1da80c0a..4ea010e6 100644 --- a/VDS/Components/RadioBox/RadioBoxItem.swift +++ b/VDS/Components/RadioBox/RadioBoxItem.swift @@ -165,11 +165,13 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable { //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - /// Executed on initialization for this View. - open override func initialSetup() { - super.initialSetup() - onClick = { control in - control.toggle() + /// 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() + + onClick = { [weak self] _ in + guard let self, isEnabled else { return } + toggle() } selectorView.bridge_accessibilityLabelBlock = { [weak self] in @@ -205,12 +207,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable { return accessibilityLabels.joined(separator: ", ") } - } - - /// 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 = false selectorView.isAccessibilityElement = true selectorView.accessibilityTraits = .button From fc7650f7d9f08b091d6c8ea7e3a8b7995fecb178 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 16:45:18 -0500 Subject: [PATCH 088/113] refactor toggle with setDefaults Signed-off-by: Matt Bruce --- VDS/Components/Toggle/Toggle.swift | 31 ++++++++++++++------------ VDS/Components/Toggle/ToggleView.swift | 22 ++++++++---------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index 260fdc31..304b18ce 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -158,11 +158,6 @@ open class Toggle: Control, Changeable, FormFieldable { open override func setup() { super.setup() - onClick = { [weak self] _ in - guard let self else { return } - toggle() - } - isAccessibilityElement = true if #available(iOS 17.0, *) { accessibilityTraits = .toggleButton @@ -237,6 +232,16 @@ open class Toggle: Control, Changeable, FormFieldable { label.trailingAnchor.constraint(equalTo: trailingAnchor) ] + } + + open override func setDefaults() { + super.setDefaults() + + onClick = { [weak self] _ in + guard let self else { return } + toggle() + } + bridge_accessibilityValueBlock = { [weak self] in guard let self else { return "" } if showText { @@ -245,13 +250,7 @@ open class Toggle: Control, Changeable, FormFieldable { return isSelected ? "On" : "Off" } } - } - - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false - label.reset() + isEnabled = true isOn = false isAnimated = true @@ -263,8 +262,12 @@ open class Toggle: Control, Changeable, FormFieldable { textPosition = .left inputId = nil onChange = nil - shouldUpdateView = true - setNeedsUpdate() + } + + /// Resets to default settings. + open override func reset() { + label.reset() + super.reset() } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/Toggle/ToggleView.swift b/VDS/Components/Toggle/ToggleView.swift index 1ba5d605..2ecfd44a 100644 --- a/VDS/Components/Toggle/ToggleView.swift +++ b/VDS/Components/Toggle/ToggleView.swift @@ -109,11 +109,6 @@ open class ToggleView: Control, Changeable, FormFieldable { open override func setup() { super.setup() - onClick = { [weak self] _ in - guard let self else { return } - toggle() - } - isAccessibilityElement = true if #available(iOS 17.0, *) { accessibilityTraits = .toggleButton @@ -154,20 +149,21 @@ open class ToggleView: Control, Changeable, FormFieldable { accessibilityLabel = "Toggle" } - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false + open override func setDefaults() { + super.setDefaults() isOn = false isAnimated = true inputId = nil toggleView.backgroundColor = toggleColorConfiguration.getColor(self) knobView.backgroundColor = knobColorConfiguration.getColor(self) - onChange = nil - shouldUpdateView = true - setNeedsUpdate() + onChange = nil + + onClick = { [weak self] _ in + guard let self else { return } + toggle() + } } - + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() From bf4ff94933014b3b42e093614f2bdd53dabd87f1 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 9 Aug 2024 16:45:35 -0500 Subject: [PATCH 089/113] moved default values into setDefaults() Signed-off-by: Matt Bruce --- VDS/BaseClasses/Selector/SelectorBase.swift | 12 +-- .../Selector/SelectorGroupBase.swift | 36 +++++---- .../Selector/SelectorItemBase.swift | 73 ++++++++++--------- VDS/Components/Badge/Badge.swift | 21 +++--- .../BadgeIndicator/BadgeIndicator.swift | 28 +++++-- .../Breadcrumbs/BreadcrumbItem.swift | 25 ++----- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 5 +- VDS/Components/Buttons/Button/Button.swift | 10 +-- .../Buttons/ButtonGroup/ButtonGroup.swift | 12 +-- .../Buttons/TextLink/TextLink.swift | 33 +++------ .../Buttons/TextLinkCaret/TextLinkCaret.swift | 18 ++--- VDS/Components/Calendar/Calendar.swift | 24 +++--- VDS/Components/Carousel/Carousel.swift | 26 +++---- VDS/Components/DatePicker/DatePicker.swift | 14 ++-- .../DropdownSelect/DropdownSelect.swift | 22 +++--- .../Icon/ButtonIcon/ButtonIcon.swift | 41 ++++++----- VDS/Components/Icon/Icon.swift | 27 +++---- VDS/Components/Line/Line.swift | 9 +-- .../Notification/Notification.swift | 23 +++--- VDS/Components/RadioBox/RadioBoxItem.swift | 66 ++++++++--------- .../RadioButton/RadioButtonGroup.swift | 9 ++- VDS/Components/Table/Table.swift | 8 +- VDS/Components/Tabs/Tabs.swift | 15 ++-- .../TextFields/EntryFieldBase.swift | 66 +++++++++-------- .../TextFields/InputField/InputField.swift | 30 ++++---- .../TextFields/TextArea/TextArea.swift | 13 +++- .../TileContainer/TileContainer.swift | 13 ++-- VDS/Components/Tilelet/Tilelet.swift | 11 ++- VDS/Components/TitleLockup/TitleLockup.swift | 9 +-- VDS/Components/Tooltip/Tooltip.swift | 18 ++--- .../Tooltip/TrailingTooltipLabel.swift | 8 +- 31 files changed, 361 insertions(+), 364 deletions(-) diff --git a/VDS/BaseClasses/Selector/SelectorBase.swift b/VDS/BaseClasses/Selector/SelectorBase.swift index fd794479..05c8a1e8 100644 --- a/VDS/BaseClasses/Selector/SelectorBase.swift +++ b/VDS/BaseClasses/Selector/SelectorBase.swift @@ -104,11 +104,17 @@ open class SelectorBase: Control, SelectorControlable { /// 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 + } + + open override func setDefaults() { + super.setDefaults() onClick = { control in control.toggle() } - + bridge_accessibilityLabelBlock = { [weak self] in guard let self else { return "" } return "\(Self.self)\(showError ? ", error" : "")" @@ -118,10 +124,6 @@ open class SelectorBase: Control, SelectorControlable { guard let self else { return "" } return !isEnabled ? "" : "Double tap to activate." } - - isAccessibilityElement = true - accessibilityTraits = .button - } open override func updateView() { diff --git a/VDS/BaseClasses/Selector/SelectorGroupBase.swift b/VDS/BaseClasses/Selector/SelectorGroupBase.swift index 4df1ca5c..5cda9c82 100644 --- a/VDS/BaseClasses/Selector/SelectorGroupBase.swift +++ b/VDS/BaseClasses/Selector/SelectorGroupBase.swift @@ -65,25 +65,30 @@ open class SelectorGroupBase: Control, SelectorGrou } didSet { + setItemsActions() for selector in items { - selector.onClick = { [weak self] handler in - self?.didSelect(handler) - self?.setNeedsUpdate() - } - - selector.accessibilityAction = { [weak self] handler in - guard let handler = handler as? SelectorItemType else { return } - self?.didSelect(handler) - self?.setNeedsUpdate() - } - mainStackView.addArrangedSubview(selector) } } } open var onChangeSubscriber: AnyCancellable? - + + private func setItemsActions() { + for selector in items { + selector.onClick = { [weak self] handler in + self?.didSelect(handler) + self?.setNeedsUpdate() + } + + selector.accessibilityAction = { [weak self] handler in + guard let handler = handler as? SelectorItemType else { return } + self?.didSelect(handler) + self?.setNeedsUpdate() + } + } + } + /// Whether the Control is enabled or not. override open var isEnabled: Bool { didSet { @@ -115,6 +120,11 @@ open class SelectorGroupBase: Control, SelectorGrou .pinBottom(0, .defaultHigh) } + open override func setDefaults() { + super.setDefaults() + onChange = nil + } + /// Handler for the Group to override on a select event. /// - Parameter selectedControl: Selected Control the user interacted. open func didSelect(_ selectedControl: SelectorItemType) { @@ -131,8 +141,8 @@ open class SelectorGroupBase: Control, SelectorGrou /// Resets to default settings. open override func reset() { super.reset() - onChange = nil items.forEach{ $0.reset() } + setItemsActions() } } diff --git a/VDS/BaseClasses/Selector/SelectorItemBase.swift b/VDS/BaseClasses/Selector/SelectorItemBase.swift index e27a9f94..90aaed14 100644 --- a/VDS/BaseClasses/Selector/SelectorItemBase.swift +++ b/VDS/BaseClasses/Selector/SelectorItemBase.swift @@ -161,6 +161,27 @@ open class SelectorItemBase: Control, Errorable, Changea /// 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() + + selectorView.isAccessibilityElement = true + isAccessibilityElement = false + addSubview(mainStackView) + + mainStackView.isUserInteractionEnabled = false + mainStackView.addArrangedSubview(selectorStackView) + mainStackView.addArrangedSubview(errorLabel) + selectorStackView.addArrangedSubview(selectorView) + selectorStackView.addArrangedSubview(selectorLabelStackView) + selectorLabelStackView.addArrangedSubview(label) + selectorLabelStackView.addArrangedSubview(childLabel) + mainStackView + .pinTop() + .pinLeading() + .pinTrailing() + .pinBottom(0, .defaultHigh) + } + + open override func setDefaults() { + super.setDefaults() onClick = { [weak self] control in guard let self, isEnabled else { return } @@ -205,23 +226,23 @@ open class SelectorItemBase: Control, Errorable, Changea guard let self else { return "" } return !isEnabled ? "" : "Double tap to activate." } + + label.textStyle = .boldBodyLarge + childLabel.textStyle = .bodyLarge + errorLabel.textStyle = .bodyMedium - selectorView.isAccessibilityElement = true - isAccessibilityElement = false - addSubview(mainStackView) + labelText = nil + labelTextAttributes = nil + labelAttributedText = nil + childText = nil + childTextAttributes = nil + childAttributedText = nil + showError = false + errorText = nil + inputId = nil + isSelected = false - mainStackView.isUserInteractionEnabled = false - mainStackView.addArrangedSubview(selectorStackView) - mainStackView.addArrangedSubview(errorLabel) - selectorStackView.addArrangedSubview(selectorView) - selectorStackView.addArrangedSubview(selectorLabelStackView) - selectorLabelStackView.addArrangedSubview(label) - selectorLabelStackView.addArrangedSubview(childLabel) - mainStackView - .pinTop() - .pinLeading() - .pinTrailing() - .pinBottom(0, .defaultHigh) + onChange = nil } /// Used to make changes to the View based off a change events or from local properties. @@ -277,30 +298,10 @@ open class SelectorItemBase: Control, Errorable, Changea /// Resets to default settings. open override func reset() { - super.reset() - shouldUpdateView = false label.reset() childLabel.reset() errorLabel.reset() - - label.textStyle = .boldBodyLarge - childLabel.textStyle = .bodyLarge - errorLabel.textStyle = .bodyMedium - - labelText = nil - labelTextAttributes = nil - labelAttributedText = nil - childText = nil - childTextAttributes = nil - childAttributedText = nil - showError = false - errorText = nil - inputId = nil - isSelected = false - - onChange = nil - shouldUpdateView = true - setNeedsUpdate() + super.reset() } //-------------------------------------------------- diff --git a/VDS/Components/Badge/Badge.swift b/VDS/Components/Badge/Badge.swift index 07477f5f..1c47133f 100644 --- a/VDS/Components/Badge/Badge.swift +++ b/VDS/Components/Badge/Badge.swift @@ -149,25 +149,28 @@ open class Badge: View { maxWidthConstraint = label.widthLessThanEqualTo(constant: 0).with { $0.isActive = false } clipsToBounds = true + } + + open override func setDefaults() { + super.setDefaults() + bridge_accessibilityLabelBlock = { [weak self] in guard let self else { return "" } return text } - } - - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false - label.reset() + label.lineBreakMode = .byTruncatingTail label.textStyle = .boldBodySmall fillColor = .red text = "" maxWidth = nil numberOfLines = 1 - shouldUpdateView = true - setNeedsUpdate() + } + + /// Resets to default settings. + open override func reset() { + label.reset() + super.reset() } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/BadgeIndicator/BadgeIndicator.swift b/VDS/Components/BadgeIndicator/BadgeIndicator.swift index 13a6cb21..dc200cd8 100644 --- a/VDS/Components/BadgeIndicator/BadgeIndicator.swift +++ b/VDS/Components/BadgeIndicator/BadgeIndicator.swift @@ -305,17 +305,31 @@ open class BadgeIndicator: View { } } - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false - label.reset() + open override func setDefaults() { + super.setDefaults() label.lineBreakMode = .byTruncatingTail label.textAlignment = .center fillColor = .red number = nil - shouldUpdateView = true - setNeedsUpdate() + kind = .simple + leadingCharacter = nil + trailingText = nil + size = .xxlarge + dotSize = nil + verticalPadding = nil + horizontalPadding = nil + hideDot = false + hideBorder = false + width = nil + height = nil + accessibilityText = nil + maximumDigits = .two + } + + /// Resets to default settings. + open override func reset() { + label.reset() + super.reset() } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift index 66f62989..cdb6011e 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift @@ -72,17 +72,15 @@ open class BreadcrumbItem: ButtonBase { //-------------------------------------------------- // 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() - + open override func setDefaults() { + super.setDefaults() + isAccessibilityElement = true + accessibilityTraits = .link + titleLabel?.numberOfLines = 0 titleLabel?.lineBreakMode = .byWordWrapping contentHorizontalAlignment = .left - isAccessibilityElement = true - accessibilityTraits = .link - bridge_accessibilityHintBlock = { [weak self] in guard let self else { return "" } return !isEnabled ? "" : "Double tap to open." @@ -131,17 +129,4 @@ open class BreadcrumbItem: ButtonBase { } } - - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false - text = nil - accessibilityCustomActions = [] - isAccessibilityElement = true - accessibilityTraits = .button - shouldUpdateView = true - setNeedsUpdate() - } - } diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index 6c45383d..1179807d 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -116,14 +116,11 @@ open class Breadcrumbs: View { addSubview(containerView) containerView.pinToSuperView() } - + /// Resets to default settings. open override func reset() { super.reset() - shouldUpdateView = false breadcrumbs.forEach { $0.reset() } - shouldUpdateView = true - setNeedsUpdate() } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/Buttons/Button/Button.swift b/VDS/Components/Buttons/Button/Button.swift index 0b9d13f1..9569e3e7 100644 --- a/VDS/Components/Buttons/Button/Button.swift +++ b/VDS/Components/Buttons/Button/Button.swift @@ -223,16 +223,12 @@ open class Button: ButtonBase, Useable { isAccessibilityElement = true accessibilityTraits = .button } - - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false + + open override func setDefaults() { + super.setDefaults() use = .primary width = nil size = .large - shouldUpdateView = true - setNeedsUpdate() } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift index 8b662070..486301c7 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift @@ -167,15 +167,17 @@ open class ButtonGroup: View { collectionView.reloadData() } - open override func reset() { - super.reset() - shouldUpdateView = false + open override func setDefaults() { + super.setDefaults() rowQuantityPhone = 0 rowQuantityTablet = 0 alignment = .center + childWidth = nil + } + + open override func reset() { buttons.forEach { $0.reset() } - shouldUpdateView = true - setNeedsUpdate() + super.reset() } open override func layoutSubviews() { diff --git a/VDS/Components/Buttons/TextLink/TextLink.swift b/VDS/Components/Buttons/TextLink/TextLink.swift index 70f2840b..b002c712 100644 --- a/VDS/Components/Buttons/TextLink/TextLink.swift +++ b/VDS/Components/Buttons/TextLink/TextLink.swift @@ -91,12 +91,7 @@ open class TextLink: ButtonBase { open override func setup() { super.setup() 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) @@ -106,12 +101,21 @@ open class TextLink: ButtonBase { lineHeightConstraint = line.height(constant: 1) lineHeightConstraint?.isActive = true } + } + + open override func setDefaults() { + super.setDefaults() + size = .large + accessibilityTraits = .link + //left align titleLabel in case this is pinned leading/trailing + //default is always set to center + contentHorizontalAlignment = .left + bridge_accessibilityHintBlock = { [weak self] in guard let self else { return "" } return !isEnabled ? "" : "Double tap to open." } - } /// Used to make changes to the View based off a change events or from local properties. @@ -123,18 +127,5 @@ open class TextLink: ButtonBase { //always call last so the label is rendered super.updateView() } - - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false - text = nil - size = .large - accessibilityCustomActions = [] - isAccessibilityElement = true - accessibilityTraits = .link - shouldUpdateView = true - setNeedsUpdate() - } - + } diff --git a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift index f8c86a44..e8806daa 100644 --- a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift +++ b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift @@ -76,10 +76,8 @@ open class TextLinkCaret: ButtonBase { //-------------------------------------------------- // 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() - + open override func setDefaults() { + super.setDefaults() //left align titleLabel in case this is pinned leading/trailing //default is always set to center contentHorizontalAlignment = .left @@ -88,11 +86,12 @@ open class TextLinkCaret: ButtonBase { titleLabel?.numberOfLines = 0 titleLabel?.lineBreakMode = .byWordWrapping + iconPosition = .right + bridge_accessibilityHintBlock = { [weak self] in guard let self else { return "" } return !isEnabled ? "" : "Double tap to open." } - } /// Used to make changes to the View based off a change events or from local properties. @@ -100,14 +99,7 @@ open class TextLinkCaret: ButtonBase { imageAttribute = CaretLabelAttribute(tintColor: textColor, position: iconPosition) super.updateView() } - - /// Resets to default settings. - open override func reset() { - super.reset() - iconPosition = .right - text = nil - } - + /// The natural size for the receiving view, considering only properties of the view itself. open override var intrinsicContentSize: CGSize { guard let titleLabel else { return super.intrinsicContentSize } diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index e7c2cb20..0a6ba18c 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -150,6 +150,19 @@ open class CalendarBase: Control, Changeable { collectionView.pinCenterX(anchor: containerView.centerXAnchor) } + + open override func setDefaults() { + super.setDefaults() + hideContainerBorder = false + hideCurrentDateIndicator = false + transparentBackground = false + activeDates = [] + inactiveDates = [] + indicators = [] + minDate = Date() + maxDate = Date() + selectedDate = Date() + } open override func updateView() { super.updateView() @@ -170,17 +183,6 @@ open class CalendarBase: Control, Changeable { containerView.layer.borderWidth = VDSFormControls.borderWidth } } - - /// Resets to default settings. - open override func reset() { - super.reset() - hideContainerBorder = false - hideCurrentDateIndicator = false - transparentBackground = false - activeDates = [] - inactiveDates = [] - indicators = [] - } //-------------------------------------------------- // MARK: - Private Methods diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index 766a7a70..f8d62520 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -244,6 +244,18 @@ open class Carousel: View { updatePaginationInset() } + open override func setDefaults() { + super.setDefaults() + gutter = UIDevice.isIPad ? .gutter6X : .gutter3X + layout = UIDevice.isIPad ? .threeUP : .oneUP + onChange = nil + pagination = .init(kind: .lowContrast, floating: true) + paginationDisplay = .none + paginationInset = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X + peek = .standard + groupIndex = 0 + } + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() @@ -274,19 +286,7 @@ open class Carousel: View { updatePaginationControls() addCarouselSlots() } - - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false - layout = UIDevice.isIPad ? .threeUP : .oneUP - pagination = .init(kind: .lowContrast, floating: true) - paginationDisplay = .none - paginationInset = UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space2X - gutter = UIDevice.isIPad ? .gutter6X : .gutter3X - peek = .standard - } - + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index df7cc7c9..9a95124c 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -174,6 +174,14 @@ open class DatePicker: EntryFieldBase { popoverOverlayView.isHidden = true } + open override func setDefaults() { + super.setDefaults() + selectedDate = nil + calendarModel = .init() + dateFormat = .shortNumeric + selectedDateLabel.textStyle = .bodyLarge + } + open override func getFieldContainer() -> UIView { // stackview for controls in EntryFieldBase.controlContainerView let controlStackView = UIStackView().with { @@ -200,12 +208,6 @@ open class DatePicker: EntryFieldBase { calendarIcon.color = iconColorConfiguration.getColor(self) } - /// Resets to default settings. - open override func reset() { - super.reset() - selectedDateLabel.textStyle = .bodyLarge - } - internal func formatDate(_ date: Date) { let formatter = DateFormatter() formatter.dateFormat = dateFormat.format diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 947210f4..ab2c1195 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -162,6 +162,17 @@ open class DropdownSelect: EntryFieldBase { containerView.height(44) } + open override func setDefaults() { + super.setDefaults() + showInlineLabel = false + selectId = nil + inlineDisplayLabel.textStyle = .boldBodyLarge + selectedOptionLabel.textStyle = .bodyLarge + showInlineLabel = false + options = [] + selectId = nil + } + open override func getFieldContainer() -> UIView { let controlStackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -188,17 +199,6 @@ open class DropdownSelect: EntryFieldBase { selectedOptionLabel.surface = surface selectedOptionLabel.isEnabled = isEnabled } - - /// Resets to default settings. - open override func reset() { - super.reset() - - inlineDisplayLabel.textStyle = .boldBodyLarge - selectedOptionLabel.textStyle = .bodyLarge - showInlineLabel = false - options = [] - selectId = nil - } //-------------------------------------------------- // MARK: - Public Methods diff --git a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift index 95406fb4..b6b51601 100644 --- a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift +++ b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift @@ -413,6 +413,27 @@ open class ButtonIcon: Control, Changeable { .store(in: &subscribers) } + open override func setDefaults() { + super.setDefaults() + badgeIndicatorModel = nil + kind = .ghost + surfaceType = .colorFill + iconName = nil + selectedIconName = nil + selectedIconColorConfiguration = nil + size = .large + floating = false + fitToIcon = false + hideBorder = true + showBadgeIndicator = false + selectable = false + iconOffset = .init(x: 0, y: 0) + customContainerSize = nil + customIconSize = nil + customBadgeIndicatorOffset = nil + onChange = nil + } + /// This will change the state of the Selector and execute the actionBlock if provided. open func toggle() { //removed error @@ -420,26 +441,6 @@ open class ButtonIcon: Control, Changeable { sendActions(for: .valueChanged) } - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false - kind = .ghost - surfaceType = .colorFill - size = .large - floating = false - hideBorder = true - iconOffset = .init(x: 0, y: 0) - iconName = nil - selectedIconName = nil - showBadgeIndicator = false - selectable = false - badgeIndicatorModel = nil - onChange = nil - shouldUpdateView = true - setNeedsUpdate() - } - /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() diff --git a/VDS/Components/Icon/Icon.swift b/VDS/Components/Icon/Icon.swift index fd5c5fb2..8e19bf5c 100644 --- a/VDS/Components/Icon/Icon.swift +++ b/VDS/Components/Icon/Icon.swift @@ -90,22 +90,29 @@ open class Icon: View { addSubview(imageView) imageView.pinToSuperView() - - backgroundColor = .clear - + isAccessibilityElement = true accessibilityTraits = .none - accessibilityHint = "image" + + } + + open override func setDefaults() { + super.setDefaults() + backgroundColor = .clear + color = VDSColor.paletteBlack + size = .medium + name = nil + customSize = nil + imageView.image = nil + accessibilityHint = "image" bridge_accessibilityLabelBlock = { [weak self] in guard let self else { return "" } return name?.rawValue ?? "icon" } - - - } + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() @@ -123,12 +130,6 @@ open class Icon: View { invalidateIntrinsicContentSize() } - /// Resets to default settings. - open override func reset() { - super.reset() - color = VDSColor.paletteBlack - imageView.image = nil - } } extension UIImage { diff --git a/VDS/Components/Line/Line.swift b/VDS/Components/Line/Line.swift index 627210f9..06accf07 100644 --- a/VDS/Components/Line/Line.swift +++ b/VDS/Components/Line/Line.swift @@ -81,11 +81,6 @@ open class Line: View { //-------------------------------------------------- // 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() - } - /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() @@ -94,8 +89,8 @@ open class Line: View { } /// Resets to default settings. - open override func reset() { - super.reset() + open override func setDefaults() { + super.setDefaults() style = .primary orientation = .horizontal } diff --git a/VDS/Components/Notification/Notification.swift b/VDS/Components/Notification/Notification.swift index 6df7da68..e188faf7 100644 --- a/VDS/Components/Notification/Notification.swift +++ b/VDS/Components/Notification/Notification.swift @@ -274,20 +274,13 @@ open class Notification: View { } } - /// Resets to default settings. - open override func reset() { - super.reset() - - shouldUpdateView = false - - titleLabel.reset() + open override func setDefaults() { + super.setDefaults() titleLabel.text = "" titleLabel.textStyle = UIDevice.isIPad ? .boldBodyLarge : .boldBodySmall - subTitleLabel.reset() subTitleLabel.textStyle = UIDevice.isIPad ? .bodyLarge : .bodySmall - buttonGroup.reset() buttonGroup.alignment = .left primaryButtonModel = nil @@ -302,9 +295,15 @@ open class Notification: View { closeButton.name = .close hideCloseButton = false - - shouldUpdateView = true - setNeedsUpdate() + + } + + /// Resets to default settings. + open override func reset() { + titleLabel.reset() + subTitleLabel.reset() + buttonGroup.reset() + super.reset() } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/RadioBox/RadioBoxItem.swift b/VDS/Components/RadioBox/RadioBoxItem.swift index 4ea010e6..a5ca1d8b 100644 --- a/VDS/Components/RadioBox/RadioBoxItem.swift +++ b/VDS/Components/RadioBox/RadioBoxItem.swift @@ -168,6 +168,31 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable { /// 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 = false + selectorView.isAccessibilityElement = true + selectorView.accessibilityTraits = .button + addSubview(selectorView) + selectorView.isUserInteractionEnabled = false + + selectorView.addSubview(selectorStackView) + + selectorStackView.addArrangedSubview(selectorLeftLabelStackView) + selectorStackView.addArrangedSubview(subTextRightLabel) + selectorLeftLabelStackView.addArrangedSubview(textLabel) + selectorLeftLabelStackView.addArrangedSubview(subTextLabel) + + selectorView + .pinTop() + .pinLeading() + .pinTrailing(0, .defaultHigh) + .pinBottom(0, .defaultHigh) + + selectorStackView.pinToSuperView(.uniform(VDSLayout.space3X)) + } + + open override func setDefaults() { + super.setDefaults() onClick = { [weak self] _ in guard let self, isEnabled else { return } @@ -206,37 +231,7 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable { return accessibilityLabels.joined(separator: ", ") } - - isAccessibilityElement = false - selectorView.isAccessibilityElement = true - selectorView.accessibilityTraits = .button - addSubview(selectorView) - selectorView.isUserInteractionEnabled = false - - selectorView.addSubview(selectorStackView) - - selectorStackView.addArrangedSubview(selectorLeftLabelStackView) - selectorStackView.addArrangedSubview(subTextRightLabel) - selectorLeftLabelStackView.addArrangedSubview(textLabel) - selectorLeftLabelStackView.addArrangedSubview(subTextLabel) - - selectorView - .pinTop() - .pinLeading() - .pinTrailing(0, .defaultHigh) - .pinBottom(0, .defaultHigh) - - selectorStackView.pinToSuperView(.uniform(VDSLayout.space3X)) - } - - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false - textLabel.reset() - subTextLabel.reset() - subTextRightLabel.reset() - + textLabel.textStyle = .boldBodyLarge subTextLabel.textStyle = .bodyLarge subTextRightLabel.textStyle = .bodyLarge @@ -256,9 +251,14 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable { isSelected = false onChange = nil + } - shouldUpdateView = true - setNeedsUpdate() + /// Resets to default settings. + open override func reset() { + textLabel.reset() + subTextLabel.reset() + subTextRightLabel.reset() + super.reset() } /// This will change the state of the Selector and execute the actionBlock if provided. diff --git a/VDS/Components/RadioButton/RadioButtonGroup.swift b/VDS/Components/RadioButton/RadioButtonGroup.swift index 13adf080..5d323997 100644 --- a/VDS/Components/RadioButton/RadioButtonGroup.swift +++ b/VDS/Components/RadioButton/RadioButtonGroup.swift @@ -77,12 +77,13 @@ open class RadioButtonGroup: SelectorGroupBase, SelectorGroupSi } } - open override func reset() { - super.reset() + open override func setDefaults() { + super.setDefaults() + inputId = nil showError = false } - - public override func didSelect(_ selectedControl: RadioButtonItem) { + + open override func didSelect(_ selectedControl: RadioButtonItem) { if let selectedItem { updateToggle(selectedItem) } diff --git a/VDS/Components/Table/Table.swift b/VDS/Components/Table/Table.swift index 95873d33..21fe00a4 100644 --- a/VDS/Components/Table/Table.swift +++ b/VDS/Components/Table/Table.swift @@ -109,18 +109,16 @@ open class Table: View { matrixView.collectionViewLayout.invalidateLayout() } - /// Resets to default settings. - open override func reset() { - super.reset() + open override func setDefaults() { + super.setDefaults() striped = false padding = .standard tableHeader = [] tableRows = [] fillContainer = true columnWidths = nil - setNeedsUpdate() } - + func calculateColumnWidths() -> [CGFloat] { guard let noOfColumns = tableData.first?.columnsCount else { return [] } let itemWidth = floor(matrixView.safeAreaLayoutGuide.layoutFrame.width / CGFloat(noOfColumns)) diff --git a/VDS/Components/Tabs/Tabs.swift b/VDS/Components/Tabs/Tabs.swift index e909d43b..ac9e7269 100644 --- a/VDS/Components/Tabs/Tabs.swift +++ b/VDS/Components/Tabs/Tabs.swift @@ -221,10 +221,11 @@ open class Tabs: View { super.layoutSubviews() updateContentView() } - - open override func reset() { - super.reset() - shouldUpdateView = false + + open override func setDefaults() { + super.setDefaults() + onTabDidSelect = nil + onTabShouldSelect = nil orientation = .horizontal borderLine = true fillContainer = false @@ -235,11 +236,9 @@ open class Tabs: View { selectedIndex = 0 size = .medium sticky = false - tabViews.forEach{ $0.reset() } - shouldUpdateView = true - setNeedsUpdate() + tabModels = [] } - + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 481318b6..a3a2918e 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -316,6 +316,41 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { errorLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() helperLabel.textColorConfiguration = secondaryColorConfiguration.eraseToAnyColorable() + } + + /// Updates the UI + open override func updateView() { + super.updateView() + updateRules() + updateContainerView() + updateContainerWidth() + updateTitleLabel() + updateErrorLabel() + updateHelperLabel() + } + + open override func setDefaults() { + super.setDefaults() + + titleLabel.textStyle = .bodySmall + errorLabel.textStyle = .bodySmall + helperLabel.textStyle = .bodySmall + + labelText = nil + helperText = nil + showError = false + errorText = nil + tooltipModel = nil + transparentBackground = false + width = nil + inputId = nil + defaultValue = nil + isRequired = false + isReadOnly = false + helperTextPlacement = .bottom + rules = [] + onChange = nil + containerView.bridge_accessibilityLabelBlock = { [weak self] in guard let self else { return "" } var accessibilityLabels = [String]() @@ -350,42 +385,15 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { guard let self else { return "" } return showError || hasInternalError ? "error" : nil } - } - - /// Updates the UI - open override func updateView() { - super.updateView() - updateRules() - updateContainerView() - updateContainerWidth() - updateTitleLabel() - updateErrorLabel() - updateHelperLabel() + } /// Resets to default settings. open override func reset() { - super.reset() titleLabel.reset() errorLabel.reset() helperLabel.reset() - - titleLabel.textStyle = .bodySmall - errorLabel.textStyle = .bodySmall - helperLabel.textStyle = .bodySmall - - labelText = nil - helperText = nil - showError = false - errorText = nil - tooltipModel = nil - transparentBackground = false - width = nil - inputId = nil - defaultValue = nil - isRequired = false - isReadOnly = false - onChange = nil + super.reset() } open override var canBecomeFirstResponder: Bool { diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index a65df78e..92d4c873 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -205,6 +205,22 @@ open class InputField: EntryFieldBase { textField.textColorConfiguration = textFieldTextColorConfiguration + } + + open override func getFieldContainer() -> UIView { + return textField + } + + open override func setDefaults() { + super.setDefaults() + textField.text = "" + + successLabel.textStyle = .bodySmall + + fieldType = .text + showSuccess = false + successText = nil + containerView.bridge_accessibilityLabelBlock = { [weak self] in guard let self else { return "" } var accessibilityLabels = [String]() @@ -259,22 +275,10 @@ open class InputField: EntryFieldBase { } } - open override func getFieldContainer() -> UIView { - return textField - } - /// Resets to default settings. open override func reset() { - super.reset() - textField.text = "" - successLabel.reset() - successLabel.textStyle = .bodySmall - - fieldType = .text - showSuccess = false - successText = nil - helperTextPlacement = .bottom + super.reset() } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 07c79ca6..5be37d05 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -166,14 +166,19 @@ open class TextArea: EntryFieldBase { bottomContainerStackView.spacing = VDSLayout.space2X } + + open override func setDefaults() { + super.setDefaults() + minHeight = .twoX + maxLength = nil + textView.text = "" + characterCounterLabel.textStyle = .bodySmall + } /// Resets to default settings. open override func reset() { - super.reset() - textView.text = "" characterCounterLabel.reset() - characterCounterLabel.textStyle = .bodySmall - setNeedsUpdate() + super.reset() } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index f35d2614..c2ec56ef 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -279,19 +279,18 @@ open class TileContainerBase: Control where Padding return view } - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false + open override func setDefaults() { + super.setDefaults() + backgroundImage = nil color = .white - aspectRatio = .none + backgroundEffect = .none + padding = .defaultValue + aspectRatio = .ratio1x1 imageFallbackColor = .light width = nil height = nil showBorder = false showDropShadow = false - shouldUpdateView = true - setNeedsUpdate() } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 4391bfb3..263d6b77 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -390,20 +390,19 @@ open class Tilelet: TileContainerBase { } } - /// Resets to default settings. - open override func reset() { - shouldUpdateView = false - super.reset() + open override func setDefaults() { + super.setDefaults() aspectRatio = .none color = .black + textWidth = nil + textPostion = .top + //models badgeModel = nil titleModel = nil subTitleModel = nil descriptiveIconModel = nil directionalIconModel = nil - shouldUpdateView = true - setNeedsUpdate() } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index 47ae5421..25aaf248 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -280,15 +280,12 @@ open class TitleLockup: View { set {} } - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false + open override func setDefaults() { + super.setDefaults() + textAlignment = .left eyebrowModel = nil titleModel = nil subTitleModel = nil - shouldUpdateView = true - setNeedsUpdate() } var labelViews = [UIView]() diff --git a/VDS/Components/Tooltip/Tooltip.swift b/VDS/Components/Tooltip/Tooltip.swift index 6eaf7ef4..effafa07 100644 --- a/VDS/Components/Tooltip/Tooltip.swift +++ b/VDS/Components/Tooltip/Tooltip.swift @@ -159,18 +159,16 @@ open class Tooltip: Control, TooltipLaunchable { } } - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false - size = .medium - title = "" - content = "" - fillColor = .primary + open override func setDefaults() { + super.setDefaults() closeButtonText = "Close" - shouldUpdateView = true - setNeedsUpdate() + fillColor = .primary + size = .medium + title = nil + content = nil + contentView = nil } + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { diff --git a/VDS/Components/Tooltip/TrailingTooltipLabel.swift b/VDS/Components/Tooltip/TrailingTooltipLabel.swift index f1f26abc..8b5011ef 100644 --- a/VDS/Components/Tooltip/TrailingTooltipLabel.swift +++ b/VDS/Components/Tooltip/TrailingTooltipLabel.swift @@ -84,17 +84,13 @@ open class TrailingTooltipLabel: View, TooltipLaunchable { } } - /// Resets to default settings. - open override func reset() { - super.reset() - shouldUpdateView = false + open override func setDefaults() { + super.setDefaults() labelText = nil labelAttributes = nil labelTextStyle = .defaultStyle labelTextAlignment = .left tooltipModel = nil - shouldUpdateView = true - setNeedsUpdate() } } From 47a38e5d3f3470b8741f9cb7a9e294ecb8d02e52 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Sat, 10 Aug 2024 11:46:51 -0500 Subject: [PATCH 090/113] release notes update Signed-off-by: Matt Bruce --- VDS/SupportingFiles/ReleaseNotes.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/VDS/SupportingFiles/ReleaseNotes.txt b/VDS/SupportingFiles/ReleaseNotes.txt index 69b82096..80d0131b 100644 --- a/VDS/SupportingFiles/ReleaseNotes.txt +++ b/VDS/SupportingFiles/ReleaseNotes.txt @@ -1,3 +1,8 @@ +1.0.72 +---------------- +- ONEAPP-9311 - InputStepper - Finished +- CXTDT-599736 - All classes refactored workflow setup(), reset(), setDefaults() + 1.0.71 ---------------- - CXTDT-581800 - DatePicker - Selected Error state icon From b1adbd7e0d0240eee87b3e3a98d2103a18c2d6a6 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Mon, 12 Aug 2024 15:45:18 +0530 Subject: [PATCH 091/113] Digital ACT-191 ONEAPP-9314 story: update text --- VDS/Components/PriceLockup/PriceLockup.swift | 75 ++++++++++++++++++-- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/VDS/Components/PriceLockup/PriceLockup.swift b/VDS/Components/PriceLockup/PriceLockup.swift index ef11a2cb..e127fd9a 100644 --- a/VDS/Components/PriceLockup/PriceLockup.swift +++ b/VDS/Components/PriceLockup/PriceLockup.swift @@ -39,6 +39,23 @@ open class PriceLockup: View { /// Enum used to describe the term of PriceLockup. public enum Term: String, CaseIterable { case month, year, biennial, none + + /// The default term is 'month'. + public static var defaultValue : Self { .month } + + /// Text for this term of PriceLockup. + public var text: String { + switch self { + case .month: + return "mo" + case .year: + return "yr" + case .biennial: + return "biennial" + case .none: + return "" + } + } } /// Enum type describing size of PriceLockup. @@ -80,9 +97,9 @@ open class PriceLockup: View { /// Does not apply a strikethrough format to leading and trailing text. open var strikethrough: Bool = false { didSet { setNeedsUpdate() } } - /// Term text for the component. Superscript placement can vary when term and delimeter are "none". - /// The default term is 'month'. - open var term: Term = .month { didSet { setNeedsUpdate() } } + /// Term text for the component. The default term is 'month'. + /// Superscript placement can vary when term and delimeter are "none". + open var term: Term = Term.defaultValue { didSet { setNeedsUpdate() } } /// Trailing text for the component. open var trailingText: String? { didSet { setNeedsUpdate() } } @@ -101,11 +118,31 @@ open class PriceLockup: View { internal var containerView = View().with { $0.clipsToBounds = true } + + internal var label = Label().with { + $0.isAccessibilityElement = false + $0.lineBreakMode = .byWordWrapping + } + //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- internal var containerSize: CGSize { CGSize(width: 45, height: 44) } + private var kindColorConfiguration: AnyColorable { + switch kind { + case .primary: + return ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal)}.eraseToAnyColorable() + case .secondary: + return ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forState: .normal)}.eraseToAnyColorable() + case .savings: + return ControlColorConfiguration().with { + $0.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen36, forState: .normal)}.eraseToAnyColorable() + } + } + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -121,19 +158,24 @@ open class PriceLockup: View { .height(containerSize.height) containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + + // Price lockup + containerView.addSubview(label) + label.pinToSuperView() + label.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() - + label.text = fetchText() } /// Resets to default settings. open override func reset() { super.reset() shouldUpdateView = false - + label.reset() shouldUpdateView = true setNeedsUpdate() } @@ -144,4 +186,27 @@ open class PriceLockup: View { } set {} } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + open func fetchText() -> String { + var text : String = "" + let currency: String = hideCurrency ? "" : "$" + if let leadingStr = leadingText { + text = text + leadingStr + " " + } + if let value = price { + text = text + currency + "\(value)" + } + if term != .none { + text = text + "/" + term.text + } + if let trailingStr = trailingText { + text = text + " " + trailingStr + } + text = text + (superscript ?? "") + return text + + } } From 3809e96b1605c10a5189e7d0419b46eaddee05f2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 12 Aug 2024 09:58:06 -0500 Subject: [PATCH 092/113] fixed bug within radiobox Signed-off-by: Matt Bruce --- VDS/Components/RadioBox/RadioBoxGroup.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/VDS/Components/RadioBox/RadioBoxGroup.swift b/VDS/Components/RadioBox/RadioBoxGroup.swift index b5531e33..8c955999 100644 --- a/VDS/Components/RadioBox/RadioBoxGroup.swift +++ b/VDS/Components/RadioBox/RadioBoxGroup.swift @@ -71,13 +71,13 @@ open class RadioBoxGroup: SelectorGroupBase, SelectorGroupSingleSe mainStackView.axis = .horizontal mainStackView.distribution = .fillEqually } else { - if UIDevice.current.orientation.isPortrait || UIDevice.current.orientation == .unknown { - mainStackView.axis = .vertical - mainStackView.distribution = .fill - - } else { + let orientation = UIDevice.current.orientation + if orientation == .landscapeLeft || orientation == .landscapeRight { mainStackView.axis = .horizontal mainStackView.distribution = .fillEqually + } else { + mainStackView.axis = .vertical + mainStackView.distribution = .fill } } } From 3ee592f63814ef4435c672638b1180119a1c36e2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 12 Aug 2024 12:27:49 -0500 Subject: [PATCH 093/113] fixed bug in radiobox Signed-off-by: Matt Bruce --- VDS/Components/RadioBox/RadioBoxGroup.swift | 25 ++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/VDS/Components/RadioBox/RadioBoxGroup.swift b/VDS/Components/RadioBox/RadioBoxGroup.swift index 8c955999..000e3433 100644 --- a/VDS/Components/RadioBox/RadioBoxGroup.swift +++ b/VDS/Components/RadioBox/RadioBoxGroup.swift @@ -67,17 +67,26 @@ open class RadioBoxGroup: SelectorGroupBase, SelectorGroupSingleSe // MARK: - Overrides //-------------------------------------------------- private func ensureDevice() { + var axis: NSLayoutConstraint.Axis = .vertical + var distribution: UIStackView.Distribution = .fill + + defer { + mainStackView.axis = axis + mainStackView.distribution = distribution + } + if UIDevice.isIPad { - mainStackView.axis = .horizontal - mainStackView.distribution = .fillEqually + axis = .horizontal + distribution = .fillEqually } else { + guard let supportedOrientations = UIApplication.shared.windows.first?.rootViewController?.supportedInterfaceOrientations else { + return + } + let orientation = UIDevice.current.orientation - if orientation == .landscapeLeft || orientation == .landscapeRight { - mainStackView.axis = .horizontal - mainStackView.distribution = .fillEqually - } else { - mainStackView.axis = .vertical - mainStackView.distribution = .fill + if supportedOrientations.contains(.landscape) && (orientation == .landscapeLeft || orientation == .landscapeRight) { + axis = .horizontal + distribution = .fillEqually } } } From 1bb54d174f8b849ad998b903e7ec7dd3772156a0 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Tue, 13 Aug 2024 15:37:02 +0530 Subject: [PATCH 094/113] Digital ACT-191 ONEAPP-9314 story: applying textstyle and color --- VDS/Components/PriceLockup/PriceLockup.swift | 124 +++++++++++++++++-- 1 file changed, 111 insertions(+), 13 deletions(-) diff --git a/VDS/Components/PriceLockup/PriceLockup.swift b/VDS/Components/PriceLockup/PriceLockup.swift index e127fd9a..3bac1779 100644 --- a/VDS/Components/PriceLockup/PriceLockup.swift +++ b/VDS/Components/PriceLockup/PriceLockup.swift @@ -68,6 +68,8 @@ open class PriceLockup: View { case large case xlarge case xxlarge + + public var defaultValue: Self { .medium } } //-------------------------------------------------- @@ -111,7 +113,6 @@ open class PriceLockup: View { /// This will render the pricing and term sections as a uniform size. open var uniformSize: Bool = false { didSet { setNeedsUpdate() } } - //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -124,25 +125,79 @@ open class PriceLockup: View { $0.lineBreakMode = .byWordWrapping } + internal var index = 0 + + internal var textContentType:TextContentType = .preDelimiter + enum TextContentType: String, CaseIterable { + case preDelimiter, postDelimiter + } + //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- internal var containerSize: CGSize { CGSize(width: 45, height: 44) } - private var kindColorConfiguration: AnyColorable { - switch kind { - case .primary: - return ControlColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal)}.eraseToAnyColorable() - case .secondary: - return ControlColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forState: .normal)}.eraseToAnyColorable() - case .savings: - return ControlColorConfiguration().with { - $0.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen36, forState: .normal)}.eraseToAnyColorable() + private var contentFontSize: VDS.TextStyle { + switch (size, textContentType) { + case (.xxxsmall, .preDelimiter), (.xxxsmall, .postDelimiter): + return TextStyle.micro + + case (.xxsmall, .preDelimiter), (.xxsmall, .postDelimiter): + return TextStyle.bodySmall + + case (.xsmall, .preDelimiter), (.xsmall, .postDelimiter): + return TextStyle.bodyMedium + + case (.small, .preDelimiter), (.small, .postDelimiter): + return TextStyle.bodyLarge + + case (.medium, .preDelimiter): + return UIDevice.isIPad ? TextStyle.titleSmall : TextStyle.titleMedium + + case (.medium, .postDelimiter): + return TextStyle.bodyLarge + + case (.large, .preDelimiter): + return UIDevice.isIPad ? TextStyle.titleMedium : TextStyle.titleLarge + + case (.large, .postDelimiter): + return UIDevice.isIPad ? TextStyle.titleSmall : TextStyle.titleMedium + + case (.xlarge, .preDelimiter): + return UIDevice.isIPad ? TextStyle.titleLarge : TextStyle.titleXLarge + + case (.xlarge, .postDelimiter): + return UIDevice.isIPad ? TextStyle.titleMedium : TextStyle.titleLarge + + case (.xxlarge, .preDelimiter): + return UIDevice.isIPad ? TextStyle.titleXLarge : TextStyle.featureSmall + + case (.xxlarge, .postDelimiter): + return UIDevice.isIPad ? TextStyle.titleLarge : TextStyle.titleXLarge } } + private var kindViewColorConfiguration: ViewColorConfiguration { + switch kind { + + case .primary: + return ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) + } + + case .secondary: + return ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false) + } + case .savings: + return ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen36, forDisabled: false) + } + } + } + + private var heightConstraint: NSLayoutConstraint? + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -163,12 +218,51 @@ open class PriceLockup: View { containerView.addSubview(label) label.pinToSuperView() label.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + heightConstraint = label.heightAnchor.constraint(equalToConstant: 0) + heightConstraint?.activate() + } + + func updateLabel() { + var attributes: [any LabelAttributeModel] = [] + let colorAttr = ColorLabelAttribute(location: 0, + length: label.text.count, + color: kindViewColorConfiguration.getColor(self)) + attributes.append(colorAttr) + if index > 0 { + textContentType = .preDelimiter + let textStyleAttr = TextStyleLabelAttribute(location: 0, + length: index, + textStyle: contentFontSize) + textContentType = .postDelimiter + let othertextStyleAttr = TextStyleLabelAttribute(location: index+1, + length: label.text.count-index-1, + textStyle: contentFontSize) + attributes.append(textStyleAttr) + attributes.append(othertextStyleAttr) + } + label.attributes = attributes } /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() label.text = fetchText() + label.surface = surface + + // Set the attributed text + updateLabel() + + if uniformSize { + // currency and value have the same font text style as delimeter, term, trailing text and superscript. + textContentType = .postDelimiter + var uniformSizeAttributes: [any LabelAttributeModel]? { + [TextStyleLabelAttribute(location: 0, + length: label.text.count, + textStyle: contentFontSize)] + } + label.attributes = uniformSizeAttributes + } + } /// Resets to default settings. @@ -192,12 +286,16 @@ open class PriceLockup: View { //-------------------------------------------------- open func fetchText() -> String { var text : String = "" + index = 0 let currency: String = hideCurrency ? "" : "$" if let leadingStr = leadingText { text = text + leadingStr + " " + index = index + leadingStr.count + 1 } if let value = price { - text = text + currency + "\(value)" + let valueStr = "\(value)" + text = text + currency + valueStr + index = index + valueStr.count + 1 } if term != .none { text = text + "/" + term.text From 73db6dff2df9f5cb94bd27c90c81398a09ea810a Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Wed, 14 Aug 2024 14:53:27 +0530 Subject: [PATCH 095/113] Digital ACT-191 ONEAPP-9314 story: update label with strikethrough if its enabled, and other minor changes --- VDS/Components/PriceLockup/PriceLockup.swift | 30 ++++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/VDS/Components/PriceLockup/PriceLockup.swift b/VDS/Components/PriceLockup/PriceLockup.swift index 3bac1779..6cda4cf4 100644 --- a/VDS/Components/PriceLockup/PriceLockup.swift +++ b/VDS/Components/PriceLockup/PriceLockup.swift @@ -121,11 +121,13 @@ open class PriceLockup: View { } internal var label = Label().with { - $0.isAccessibilityElement = false + $0.isAccessibilityElement = true $0.lineBreakMode = .byWordWrapping } internal var index = 0 + internal var strikethroughLocation = 0 + internal var strikethroughlength = 0 internal var textContentType:TextContentType = .preDelimiter enum TextContentType: String, CaseIterable { @@ -177,7 +179,7 @@ open class PriceLockup: View { } } - private var kindViewColorConfiguration: ViewColorConfiguration { + private var textColorConfiguration: ViewColorConfiguration { switch kind { case .primary: @@ -195,9 +197,7 @@ open class PriceLockup: View { } } } - - private var heightConstraint: NSLayoutConstraint? - + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -218,15 +218,13 @@ open class PriceLockup: View { containerView.addSubview(label) label.pinToSuperView() label.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - heightConstraint = label.heightAnchor.constraint(equalToConstant: 0) - heightConstraint?.activate() } func updateLabel() { var attributes: [any LabelAttributeModel] = [] let colorAttr = ColorLabelAttribute(location: 0, length: label.text.count, - color: kindViewColorConfiguration.getColor(self)) + color: textColorConfiguration.getColor(self)) attributes.append(colorAttr) if index > 0 { textContentType = .preDelimiter @@ -252,6 +250,18 @@ open class PriceLockup: View { // Set the attributed text updateLabel() + if strikethrough { + // strike applies only when uniformSize true. Does not apply a strikethrough format to leading, trailing, and superscript text. + textContentType = .postDelimiter + var strikethroughAttributes: [any LabelAttributeModel]? { + [TextStyleLabelAttribute(location: 0, + length: label.text.count, + textStyle: contentFontSize), + StrikeThroughLabelAttribute(location:strikethroughLocation, length: strikethroughlength)] + } + label.attributes = strikethroughAttributes + } + if uniformSize { // currency and value have the same font text style as delimeter, term, trailing text and superscript. textContentType = .postDelimiter @@ -262,7 +272,6 @@ open class PriceLockup: View { } label.attributes = uniformSizeAttributes } - } /// Resets to default settings. @@ -293,12 +302,15 @@ open class PriceLockup: View { index = index + leadingStr.count + 1 } if let value = price { + strikethroughLocation = index let valueStr = "\(value)" text = text + currency + valueStr index = index + valueStr.count + 1 + strikethroughlength = valueStr.count + 1 } if term != .none { text = text + "/" + term.text + strikethroughlength = strikethroughlength + term.text.count + 1 } if let trailingStr = trailingText { text = text + " " + trailingStr From 20c407f82af725d7dae03a224af8959bc55bcefe Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Wed, 14 Aug 2024 15:27:47 +0530 Subject: [PATCH 096/113] Digital ACT-191 ONEAPP-9314 story: remove a decimal for price if decimal is 0 --- VDS/Components/PriceLockup/PriceLockup.swift | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/VDS/Components/PriceLockup/PriceLockup.swift b/VDS/Components/PriceLockup/PriceLockup.swift index 6cda4cf4..4d21b42c 100644 --- a/VDS/Components/PriceLockup/PriceLockup.swift +++ b/VDS/Components/PriceLockup/PriceLockup.swift @@ -86,7 +86,7 @@ open class PriceLockup: View { open var leadingText: String? { didSet { setNeedsUpdate() } } /// Value rendered for the component. - open var price: CGFloat? { didSet { setNeedsUpdate() } } + open var price: Float? { didSet { setNeedsUpdate() } } /// Color to the component. The default kind is primary. open var kind: Kind = .primary { didSet { setNeedsUpdate() } } @@ -197,7 +197,7 @@ open class PriceLockup: View { } } } - + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -303,7 +303,7 @@ open class PriceLockup: View { } if let value = price { strikethroughLocation = index - let valueStr = "\(value)" + let valueStr = "\(value.clean)" text = text + currency + valueStr index = index + valueStr.count + 1 strikethroughlength = valueStr.count + 1 @@ -320,3 +320,10 @@ open class PriceLockup: View { } } + +extension Float { + // remove a decimal from a float if the decimal is equal to 0 + var clean: String { + return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self) + } +} From 8f7581d0d6bed9fea2f4b19bec560bc88a81302d Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Thu, 15 Aug 2024 14:55:32 +0530 Subject: [PATCH 097/113] Digital ACT-191 ONEAPP-9314 story: refactored code --- VDS/Components/PriceLockup/PriceLockup.swift | 206 +++++++++---------- 1 file changed, 95 insertions(+), 111 deletions(-) diff --git a/VDS/Components/PriceLockup/PriceLockup.swift b/VDS/Components/PriceLockup/PriceLockup.swift index 4d21b42c..059a94a6 100644 --- a/VDS/Components/PriceLockup/PriceLockup.swift +++ b/VDS/Components/PriceLockup/PriceLockup.swift @@ -31,11 +31,6 @@ open class PriceLockup: View { //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- - /// Enum used to describe the kind of PriceLockup. - public enum Kind: String, CaseIterable { - case primary, secondary, savings - } - /// Enum used to describe the term of PriceLockup. public enum Term: String, CaseIterable { case month, year, biennial, none @@ -44,7 +39,7 @@ open class PriceLockup: View { public static var defaultValue : Self { .month } /// Text for this term of PriceLockup. - public var text: String { + public var type: String { switch self { case .month: return "mo" @@ -69,7 +64,30 @@ open class PriceLockup: View { case xlarge case xxlarge - public var defaultValue: Self { .medium } + public static var defaultValue: Self { .medium } + } + + /// Enum used to describe the kind of PriceLockup. + public enum Kind: String, CaseIterable { + case primary, secondary, savings + + /// The default kind is 'primary'. + public static var defaultValue : Self { .primary } + + /// Color configuation relative to kind. + public var colorConfiguration: ViewColorConfiguration { + switch self { + case .primary: + return ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false)} + case .secondary: + return ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false)} + case .savings: + return ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen36, forDisabled: false)} + } + } } //-------------------------------------------------- @@ -89,11 +107,11 @@ open class PriceLockup: View { open var price: Float? { didSet { setNeedsUpdate() } } /// Color to the component. The default kind is primary. - open var kind: Kind = .primary { didSet { setNeedsUpdate() } } + open var kind: Kind = Kind.defaultValue { didSet { setNeedsUpdate() } } /// Size of the component. It varies by size and viewport(mobile/Tablet). /// The default size is medium with viewport mobile. - open var size: Size = .medium { didSet { setNeedsUpdate() } } + open var size: Size = Size.defaultValue { didSet { setNeedsUpdate() } } /// If true, the component with a strikethrough. It applies only when uniformSize is true. /// Does not apply a strikethrough format to leading and trailing text. @@ -116,21 +134,17 @@ open class PriceLockup: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var containerView = View().with { - $0.clipsToBounds = true - } - - internal var label = Label().with { + internal var priceLockupLabel = Label().with { $0.isAccessibilityElement = true $0.lineBreakMode = .byWordWrapping } - internal var index = 0 + internal var delimiterIndex = 0 internal var strikethroughLocation = 0 - internal var strikethroughlength = 0 + internal var strikethroughLength = 0 - internal var textContentType:TextContentType = .preDelimiter - enum TextContentType: String, CaseIterable { + internal var textPosition:TextPosition = .preDelimiter + enum TextPosition: String, CaseIterable { case preDelimiter, postDelimiter } @@ -139,8 +153,8 @@ open class PriceLockup: View { //-------------------------------------------------- internal var containerSize: CGSize { CGSize(width: 45, height: 44) } - private var contentFontSize: VDS.TextStyle { - switch (size, textContentType) { + private var sizeTextStyle: VDS.TextStyle { + switch (size, textPosition) { case (.xxxsmall, .preDelimiter), (.xxxsmall, .postDelimiter): return TextStyle.micro @@ -179,106 +193,34 @@ open class PriceLockup: View { } } - private var textColorConfiguration: ViewColorConfiguration { - switch kind { - - case .primary: - return ViewColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) - } - - case .secondary: - return ViewColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false) - } - case .savings: - return ViewColorConfiguration().with { - $0.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen36, forDisabled: false) - } - } - } - //-------------------------------------------------- // 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() - addSubview(containerView) - containerView - .pinTop() - .pinBottom() - .pinLeadingGreaterThanOrEqualTo() - .pinTrailingLessThanOrEqualTo() - .height(containerSize.height) - containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - - // Price lockup - containerView.addSubview(label) - label.pinToSuperView() - label.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + // Price lockup label + addSubview(priceLockupLabel) + priceLockupLabel.pinToSuperView() } - - func updateLabel() { - var attributes: [any LabelAttributeModel] = [] - let colorAttr = ColorLabelAttribute(location: 0, - length: label.text.count, - color: textColorConfiguration.getColor(self)) - attributes.append(colorAttr) - if index > 0 { - textContentType = .preDelimiter - let textStyleAttr = TextStyleLabelAttribute(location: 0, - length: index, - textStyle: contentFontSize) - textContentType = .postDelimiter - let othertextStyleAttr = TextStyleLabelAttribute(location: index+1, - length: label.text.count-index-1, - textStyle: contentFontSize) - attributes.append(textStyleAttr) - attributes.append(othertextStyleAttr) - } - label.attributes = attributes - } - + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() - label.text = fetchText() - label.surface = surface + + priceLockupLabel.text = fetchText() + priceLockupLabel.surface = surface // Set the attributed text - updateLabel() - - if strikethrough { - // strike applies only when uniformSize true. Does not apply a strikethrough format to leading, trailing, and superscript text. - textContentType = .postDelimiter - var strikethroughAttributes: [any LabelAttributeModel]? { - [TextStyleLabelAttribute(location: 0, - length: label.text.count, - textStyle: contentFontSize), - StrikeThroughLabelAttribute(location:strikethroughLocation, length: strikethroughlength)] - } - label.attributes = strikethroughAttributes - } - - if uniformSize { - // currency and value have the same font text style as delimeter, term, trailing text and superscript. - textContentType = .postDelimiter - var uniformSizeAttributes: [any LabelAttributeModel]? { - [TextStyleLabelAttribute(location: 0, - length: label.text.count, - textStyle: contentFontSize)] - } - label.attributes = uniformSizeAttributes - } + updateLabelAttributes() } /// Resets to default settings. open override func reset() { super.reset() shouldUpdateView = false - label.reset() + priceLockupLabel.reset() shouldUpdateView = true setNeedsUpdate() } @@ -293,31 +235,73 @@ open class PriceLockup: View { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- + func updateLabelAttributes() { + var attributes: [any LabelAttributeModel] = [] + attributes.append(ColorLabelAttribute(location: 0, + length: priceLockupLabel.text.count, + color: kind.colorConfiguration.getColor(self))) + textPosition = .postDelimiter + if strikethrough { + + // strike applies only when uniformSize true. Does not apply a strikethrough format to leading, trailing, and superscript text. + attributes.append(TextStyleLabelAttribute(location: 0, + length: priceLockupLabel.text.count, + textStyle: sizeTextStyle, + textPosition: .left)) + attributes.append(StrikeThroughLabelAttribute(location:strikethroughLocation, length: strikethroughLength)) + } else if uniformSize { + + // currency and value have the same font text style as delimeter, term, trailing text and superscript. + attributes.append(TextStyleLabelAttribute(location: 0, + length: priceLockupLabel.text.count, + textStyle: sizeTextStyle, + textPosition: .left)) + } else { + + // size updates relative to predelimiter, postdelimiter + if delimiterIndex > 0 { + textPosition = .preDelimiter + attributes.append(TextStyleLabelAttribute(location: 0, + length: delimiterIndex, + textStyle: sizeTextStyle, + textPosition: .left)) + + textPosition = .postDelimiter + attributes.append(TextStyleLabelAttribute(location: delimiterIndex, + length: priceLockupLabel.text.count-delimiterIndex, + textStyle: sizeTextStyle, + textPosition: .left)) + } + } + priceLockupLabel.attributes = attributes + } + open func fetchText() -> String { var text : String = "" - index = 0 + let space = " " + let delimiter = "/" + delimiterIndex = 0 let currency: String = hideCurrency ? "" : "$" if let leadingStr = leadingText { - text = text + leadingStr + " " - index = index + leadingStr.count + 1 + text = text + leadingStr + space + delimiterIndex = delimiterIndex + leadingStr.count + space.count } if let value = price { - strikethroughLocation = index + strikethroughLocation = delimiterIndex let valueStr = "\(value.clean)" text = text + currency + valueStr - index = index + valueStr.count + 1 - strikethroughlength = valueStr.count + 1 + delimiterIndex = delimiterIndex + valueStr.count + currency.count + strikethroughLength = valueStr.count + currency.count } if term != .none { - text = text + "/" + term.text - strikethroughlength = strikethroughlength + term.text.count + 1 + text = text + delimiter + term.type + strikethroughLength = strikethroughLength + delimiter.count + term.type.count } if let trailingStr = trailingText { - text = text + " " + trailingStr + text = text + space + trailingStr } text = text + (superscript ?? "") return text - } } From 7ae5f9853dc5a4f84091247c323b9fa5dc3dc659 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Thu, 15 Aug 2024 16:27:23 +0530 Subject: [PATCH 098/113] Digital ACT-191 ONEAPP-9314 story: bold configuration, refactored strikethrough --- VDS/Components/PriceLockup/PriceLockup.swift | 49 ++++++++++---------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/VDS/Components/PriceLockup/PriceLockup.swift b/VDS/Components/PriceLockup/PriceLockup.swift index 059a94a6..bde3123e 100644 --- a/VDS/Components/PriceLockup/PriceLockup.swift +++ b/VDS/Components/PriceLockup/PriceLockup.swift @@ -53,7 +53,7 @@ open class PriceLockup: View { } } - /// Enum type describing size of PriceLockup. + /// Enum that represents the size availble for PriceLockup. public enum Size: String, CaseIterable { case xxxsmall case xxsmall @@ -153,43 +153,43 @@ open class PriceLockup: View { //-------------------------------------------------- internal var containerSize: CGSize { CGSize(width: 45, height: 44) } - private var sizeTextStyle: VDS.TextStyle { + private var textStyle: TextStyle.StandardStyle { switch (size, textPosition) { case (.xxxsmall, .preDelimiter), (.xxxsmall, .postDelimiter): - return TextStyle.micro + return .micro case (.xxsmall, .preDelimiter), (.xxsmall, .postDelimiter): - return TextStyle.bodySmall + return .bodySmall case (.xsmall, .preDelimiter), (.xsmall, .postDelimiter): - return TextStyle.bodyMedium + return .bodyMedium case (.small, .preDelimiter), (.small, .postDelimiter): - return TextStyle.bodyLarge + return .bodyLarge case (.medium, .preDelimiter): - return UIDevice.isIPad ? TextStyle.titleSmall : TextStyle.titleMedium + return UIDevice.isIPad ? .titleSmall : .titleMedium case (.medium, .postDelimiter): - return TextStyle.bodyLarge + return .bodyLarge case (.large, .preDelimiter): - return UIDevice.isIPad ? TextStyle.titleMedium : TextStyle.titleLarge + return UIDevice.isIPad ? .titleMedium : .titleLarge case (.large, .postDelimiter): - return UIDevice.isIPad ? TextStyle.titleSmall : TextStyle.titleMedium + return UIDevice.isIPad ? .titleSmall : .titleMedium case (.xlarge, .preDelimiter): - return UIDevice.isIPad ? TextStyle.titleLarge : TextStyle.titleXLarge + return UIDevice.isIPad ? .titleLarge : .titleXLarge case (.xlarge, .postDelimiter): - return UIDevice.isIPad ? TextStyle.titleMedium : TextStyle.titleLarge + return UIDevice.isIPad ? .titleMedium : .titleLarge case (.xxlarge, .preDelimiter): - return UIDevice.isIPad ? TextStyle.titleXLarge : TextStyle.featureSmall + return UIDevice.isIPad ? .titleXLarge : .featureSmall case (.xxlarge, .postDelimiter): - return UIDevice.isIPad ? TextStyle.titleLarge : TextStyle.titleXLarge + return UIDevice.isIPad ? .titleLarge : .titleXLarge } } @@ -245,17 +245,17 @@ open class PriceLockup: View { // strike applies only when uniformSize true. Does not apply a strikethrough format to leading, trailing, and superscript text. attributes.append(TextStyleLabelAttribute(location: 0, - length: priceLockupLabel.text.count, - textStyle: sizeTextStyle, - textPosition: .left)) + length: priceLockupLabel.text.count, + textStyle: bold ? textStyle.bold : textStyle.regular, + textPosition: .left)) attributes.append(StrikeThroughLabelAttribute(location:strikethroughLocation, length: strikethroughLength)) } else if uniformSize { // currency and value have the same font text style as delimeter, term, trailing text and superscript. attributes.append(TextStyleLabelAttribute(location: 0, - length: priceLockupLabel.text.count, - textStyle: sizeTextStyle, - textPosition: .left)) + length: priceLockupLabel.text.count, + textStyle: bold ? textStyle.bold : textStyle.regular, + textPosition: .left)) } else { // size updates relative to predelimiter, postdelimiter @@ -263,13 +263,13 @@ open class PriceLockup: View { textPosition = .preDelimiter attributes.append(TextStyleLabelAttribute(location: 0, length: delimiterIndex, - textStyle: sizeTextStyle, + textStyle: bold ? textStyle.bold : textStyle.regular, textPosition: .left)) - + textPosition = .postDelimiter attributes.append(TextStyleLabelAttribute(location: delimiterIndex, length: priceLockupLabel.text.count-delimiterIndex, - textStyle: sizeTextStyle, + textStyle: bold ? textStyle.bold : textStyle.regular, textPosition: .left)) } } @@ -281,13 +281,14 @@ open class PriceLockup: View { let space = " " let delimiter = "/" delimiterIndex = 0 + strikethroughLength = 0 let currency: String = hideCurrency ? "" : "$" if let leadingStr = leadingText { text = text + leadingStr + space delimiterIndex = delimiterIndex + leadingStr.count + space.count } + strikethroughLocation = delimiterIndex if let value = price { - strikethroughLocation = delimiterIndex let valueStr = "\(value.clean)" text = text + currency + valueStr delimiterIndex = delimiterIndex + valueStr.count + currency.count From 01c05deebd685b3961e574f963fa5527bd70efa7 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Thu, 15 Aug 2024 16:37:01 +0530 Subject: [PATCH 099/113] Digital ACT-191 ONEAPP-9314 story: removed unused code --- VDS/Components/PriceLockup/PriceLockup.swift | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/VDS/Components/PriceLockup/PriceLockup.swift b/VDS/Components/PriceLockup/PriceLockup.swift index bde3123e..6f6b21b5 100644 --- a/VDS/Components/PriceLockup/PriceLockup.swift +++ b/VDS/Components/PriceLockup/PriceLockup.swift @@ -224,14 +224,7 @@ open class PriceLockup: View { shouldUpdateView = true setNeedsUpdate() } - - open override var accessibilityElements: [Any]? { - get { - return nil - } - set {} - } - + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- From e5d9f77d3d1e652b8d77ad6090e6a9efc5507ca3 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Thu, 15 Aug 2024 16:55:55 +0530 Subject: [PATCH 100/113] Digital ACT-191 ONEAPP-9314 story: added missed comments --- VDS/Components/PriceLockup/PriceLockup.swift | 39 +++++++++++--------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/VDS/Components/PriceLockup/PriceLockup.swift b/VDS/Components/PriceLockup/PriceLockup.swift index 6f6b21b5..0c776377 100644 --- a/VDS/Components/PriceLockup/PriceLockup.swift +++ b/VDS/Components/PriceLockup/PriceLockup.swift @@ -99,24 +99,24 @@ open class PriceLockup: View { /// Currency - If hideCurrency true, the component will render without currency. open var hideCurrency: Bool = false { didSet { setNeedsUpdate() } } - + /// Leading text for the component. open var leadingText: String? { didSet { setNeedsUpdate() } } - + /// Value rendered for the component. open var price: Float? { didSet { setNeedsUpdate() } } /// Color to the component. The default kind is primary. open var kind: Kind = Kind.defaultValue { didSet { setNeedsUpdate() } } - + /// Size of the component. It varies by size and viewport(mobile/Tablet). /// The default size is medium with viewport mobile. open var size: Size = Size.defaultValue { didSet { setNeedsUpdate() } } - + /// If true, the component with a strikethrough. It applies only when uniformSize is true. /// Does not apply a strikethrough format to leading and trailing text. open var strikethrough: Bool = false { didSet { setNeedsUpdate() } } - + /// Term text for the component. The default term is 'month'. /// Superscript placement can vary when term and delimeter are "none". open var term: Term = Term.defaultValue { didSet { setNeedsUpdate() } } @@ -130,7 +130,7 @@ open class PriceLockup: View { /// If true, currency and value have the same font text style as delimeter, term label and superscript. /// This will render the pricing and term sections as a uniform size. open var uniformSize: Bool = false { didSet { setNeedsUpdate() } } - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -142,7 +142,7 @@ open class PriceLockup: View { internal var delimiterIndex = 0 internal var strikethroughLocation = 0 internal var strikethroughLength = 0 - + internal var textPosition:TextPosition = .preDelimiter enum TextPosition: String, CaseIterable { case preDelimiter, postDelimiter @@ -152,7 +152,8 @@ open class PriceLockup: View { // MARK: - Configuration Properties //-------------------------------------------------- internal var containerSize: CGSize { CGSize(width: 45, height: 44) } - + + // TextStyle for the size. private var textStyle: TextStyle.StandardStyle { switch (size, textPosition) { case (.xxxsmall, .preDelimiter), (.xxxsmall, .postDelimiter): @@ -175,19 +176,19 @@ open class PriceLockup: View { case (.large, .preDelimiter): return UIDevice.isIPad ? .titleMedium : .titleLarge - + case (.large, .postDelimiter): return UIDevice.isIPad ? .titleSmall : .titleMedium - + case (.xlarge, .preDelimiter): return UIDevice.isIPad ? .titleLarge : .titleXLarge - + case (.xlarge, .postDelimiter): return UIDevice.isIPad ? .titleMedium : .titleLarge - + case (.xxlarge, .preDelimiter): return UIDevice.isIPad ? .titleXLarge : .featureSmall - + case (.xxlarge, .postDelimiter): return UIDevice.isIPad ? .titleLarge : .titleXLarge } @@ -204,11 +205,11 @@ open class PriceLockup: View { addSubview(priceLockupLabel) priceLockupLabel.pinToSuperView() } - + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() - + priceLockupLabel.text = fetchText() priceLockupLabel.surface = surface @@ -224,10 +225,11 @@ open class PriceLockup: View { shouldUpdateView = true setNeedsUpdate() } - + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- + // Update PriceLockup text attributes func updateLabelAttributes() { var attributes: [any LabelAttributeModel] = [] attributes.append(ColorLabelAttribute(location: 0, @@ -242,6 +244,7 @@ open class PriceLockup: View { textStyle: bold ? textStyle.bold : textStyle.regular, textPosition: .left)) attributes.append(StrikeThroughLabelAttribute(location:strikethroughLocation, length: strikethroughLength)) + } else if uniformSize { // currency and value have the same font text style as delimeter, term, trailing text and superscript. @@ -249,6 +252,7 @@ open class PriceLockup: View { length: priceLockupLabel.text.count, textStyle: bold ? textStyle.bold : textStyle.regular, textPosition: .left)) + } else { // size updates relative to predelimiter, postdelimiter @@ -269,6 +273,7 @@ open class PriceLockup: View { priceLockupLabel.attributes = attributes } + // Get text for PriceLockup. open func fetchText() -> String { var text : String = "" let space = " " @@ -302,6 +307,6 @@ open class PriceLockup: View { extension Float { // remove a decimal from a float if the decimal is equal to 0 var clean: String { - return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self) + return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self) } } From bbf521b1e52196d0d172707b90d12f20022c51a4 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Aug 2024 14:00:17 -0500 Subject: [PATCH 101/113] updated setDefaults() Signed-off-by: Matt Bruce --- VDS/BaseClasses/Selector/SelectorBase.swift | 4 ++ .../Selector/SelectorGroupBase.swift | 8 +-- VDS/Components/Breadcrumbs/Breadcrumbs.swift | 9 +-- .../Buttons/ButtonGroup/ButtonGroup.swift | 1 + .../CarouselScrollbar/CarouselScrollbar.swift | 58 ++++++++++++------- VDS/Components/Checkbox/Checkbox.swift | 5 ++ VDS/Components/Checkbox/CheckboxGroup.swift | 6 ++ VDS/Components/Checkbox/CheckboxItem.swift | 5 ++ .../DropdownSelect/DropdownSelect.swift | 21 ++++--- 9 files changed, 77 insertions(+), 40 deletions(-) diff --git a/VDS/BaseClasses/Selector/SelectorBase.swift b/VDS/BaseClasses/Selector/SelectorBase.swift index 05c8a1e8..6af003fd 100644 --- a/VDS/BaseClasses/Selector/SelectorBase.swift +++ b/VDS/BaseClasses/Selector/SelectorBase.swift @@ -111,9 +111,13 @@ open class SelectorBase: Control, SelectorControlable { open override func setDefaults() { super.setDefaults() + showError = false + onClick = { control in control.toggle() } + + onChange = nil bridge_accessibilityLabelBlock = { [weak self] in guard let self else { return "" } diff --git a/VDS/BaseClasses/Selector/SelectorGroupBase.swift b/VDS/BaseClasses/Selector/SelectorGroupBase.swift index 5cda9c82..e04b5e5a 100644 --- a/VDS/BaseClasses/Selector/SelectorGroupBase.swift +++ b/VDS/BaseClasses/Selector/SelectorGroupBase.swift @@ -123,6 +123,7 @@ open class SelectorGroupBase: Control, SelectorGrou open override func setDefaults() { super.setDefaults() onChange = nil + items = [] } /// Handler for the Group to override on a select event. @@ -137,13 +138,6 @@ open class SelectorGroupBase: Control, SelectorGrou self?.sendActions(for: .valueChanged) } } - - /// Resets to default settings. - open override func reset() { - super.reset() - items.forEach{ $0.reset() } - setItemsActions() - } } diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index 1179807d..808b5548 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -117,10 +117,11 @@ open class Breadcrumbs: View { containerView.pinToSuperView() } - /// Resets to default settings. - open override func reset() { - super.reset() - breadcrumbs.forEach { $0.reset() } + open override func setDefaults() { + super.setDefaults() + breadcrumbs = [] + breadcrumbModels = [] + isEnabled = true } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift index 486301c7..520c8d1f 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift @@ -173,6 +173,7 @@ open class ButtonGroup: View { rowQuantityTablet = 0 alignment = .center childWidth = nil + buttons = [] } open override func reset() { diff --git a/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift b/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift index 8a4d4a50..5b91acb7 100644 --- a/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift +++ b/VDS/Components/CarouselScrollbar/CarouselScrollbar.swift @@ -106,14 +106,15 @@ open class CarouselScrollbar: View { /// A callback when the scrubber position changes. Passes parameters (position). open var onScrubberDrag: ((Int) -> Void)? { - get { nil } - set { + didSet { onScrubberDragCancellable?.cancel() - if let newValue { + if let onScrubberDrag { onScrubberDragCancellable = onScrubberDragPublisher .sink { c in - newValue(c) + onScrubberDrag(c) } + } else { + onScrubberDragCancellable = nil } } } @@ -124,14 +125,15 @@ open class CarouselScrollbar: View { /// A callback when the thumb move forward. Passes parameters (position). open var onMoveForward: ((Int) -> Void)? { - get { nil } - set { + didSet { onMoveForwardCancellable?.cancel() - if let newValue { + if let onMoveForward { onMoveForwardCancellable = onMoveForwardPublisher .sink { c in - newValue(c) + onMoveForward(c) } + } else { + onMoveForwardCancellable = nil } } } @@ -142,14 +144,15 @@ open class CarouselScrollbar: View { /// A callback when the thumb move backward. Passes parameters (position). open var onMoveBackward: ((Int) -> Void)? { - get { nil } - set { + didSet { onMoveBackwardCancellable?.cancel() - if let newValue { + if let onMoveBackward { onMoveBackwardCancellable = onMoveBackwardPublisher .sink { c in - newValue(c) + onMoveBackward(c) } + } else { + onMoveBackwardCancellable = nil } } } @@ -160,14 +163,15 @@ open class CarouselScrollbar: View { /// A callback when the thumb touch start. Passes parameters (position). open var onThumbTouchStart: ((Int) -> Void)? { - get { nil } - set { + didSet { onThumbTouchStartCancellable?.cancel() - if let newValue { + if let onThumbTouchStart { onThumbTouchStartCancellable = onThumbTouchStartPublisher .sink { c in - newValue(c) + onThumbTouchStart(c) } + } else { + onThumbTouchStartCancellable = nil } } } @@ -178,14 +182,15 @@ open class CarouselScrollbar: View { /// A callback when the thumb touch end. Passes parameters (position). open var onThumbTouchEnd: ((Int) -> Void)? { - get { nil } - set { + didSet { onThumbTouchEndCancellable?.cancel() - if let newValue { + if let onThumbTouchEnd { onThumbTouchEndCancellable = onThumbTouchEndPublisher .sink { c in - newValue(c) + onThumbTouchEnd(c) } + } else { + onThumbTouchEndCancellable = nil } } } @@ -294,6 +299,19 @@ open class CarouselScrollbar: View { thumbView.layer.addSublayer(thumbViewLayer) } + open override func setDefaults() { + super.setDefaults() + onMoveForward = nil + onMoveBackward = nil + onScrubberDrag = nil + onThumbTouchEnd = nil + onThumbTouchStart = nil + layout = .oneUP + numberOfSlides = 1 + totalPositions = 1 + position = 1 + } + open override func updateView() { super.updateView() trackView.backgroundColor = trackColorConfiguration.getColor(surface) diff --git a/VDS/Components/Checkbox/Checkbox.swift b/VDS/Components/Checkbox/Checkbox.swift index 5b2afdc5..f1c99691 100644 --- a/VDS/Components/Checkbox/Checkbox.swift +++ b/VDS/Components/Checkbox/Checkbox.swift @@ -62,6 +62,11 @@ open class Checkbox: SelectorBase { selectorColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .selected) } + open override func setDefaults() { + super.setDefaults() + isAnimated = false + } + /// This will change the state of the Selector and execute the actionBlock if provided. open override func toggle() { guard isEnabled else { return } diff --git a/VDS/Components/Checkbox/CheckboxGroup.swift b/VDS/Components/Checkbox/CheckboxGroup.swift index 1431d9dc..226083fe 100644 --- a/VDS/Components/Checkbox/CheckboxGroup.swift +++ b/VDS/Components/Checkbox/CheckboxGroup.swift @@ -87,6 +87,12 @@ open class CheckboxGroup: SelectorGroupBase, SelectorGroupMultiSel mainStackView.spacing = VDSLayout.space6X } + open override func setDefaults() { + super.setDefaults() + showError = false + inputId = nil + } + public override func didSelect(_ selectedControl: CheckboxItem) { selectedControl.toggle() if selectedControl.isSelected, showError{ diff --git a/VDS/Components/Checkbox/CheckboxItem.swift b/VDS/Components/Checkbox/CheckboxItem.swift index c115fe79..d3af344b 100644 --- a/VDS/Components/Checkbox/CheckboxItem.swift +++ b/VDS/Components/Checkbox/CheckboxItem.swift @@ -55,6 +55,11 @@ open class CheckboxItem: SelectorItemBase { print(foo.customView.isAnimated) } + + open override func setDefaults() { + super.setDefaults() + isAnimated = false + } /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { diff --git a/VDS/Components/DropdownSelect/DropdownSelect.swift b/VDS/Components/DropdownSelect/DropdownSelect.swift index 794fe808..2c243a26 100644 --- a/VDS/Components/DropdownSelect/DropdownSelect.swift +++ b/VDS/Components/DropdownSelect/DropdownSelect.swift @@ -82,24 +82,15 @@ open class DropdownSelect: EntryFieldBase { open var inlineDisplayLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .horizontal) - $0.textAlignment = .left - $0.textStyle = .boldBodyLarge - $0.numberOfLines = 1 - $0.sizeToFit() } open var selectedOptionLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .horizontal) - $0.textAlignment = .left - $0.textStyle = .bodyLarge - $0.numberOfLines = 1 } open var dropdownField = UITextField().with { $0.translatesAutoresizingMaskIntoConstraints = false - $0.tintColor = UIColor.clear - $0.font = TextStyle.bodyLarge.font } open var optionsPicker = UIPickerView() @@ -163,13 +154,25 @@ open class DropdownSelect: EntryFieldBase { super.setDefaults() showInlineLabel = false selectId = nil + inlineDisplayLabel.textAlignment = .left inlineDisplayLabel.textStyle = .boldBodyLarge + inlineDisplayLabel.numberOfLines = 1 + selectedOptionLabel.textAlignment = .left selectedOptionLabel.textStyle = .bodyLarge + selectedOptionLabel.numberOfLines = 1 + dropdownField.tintColor = UIColor.clear + dropdownField.font = TextStyle.bodyLarge.font showInlineLabel = false options = [] selectId = nil } + open override func reset() { + inlineDisplayLabel.reset() + selectedOptionLabel.reset() + super.reset() + } + open override func getFieldContainer() -> UIView { let controlStackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false From 67bdcf51040d1c1dab6656d60e86a8d6afc3b245 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Aug 2024 14:41:36 -0500 Subject: [PATCH 102/113] more updaets for setDefaults Signed-off-by: Matt Bruce --- VDS/Components/Pagination/Pagination.swift | 4 ++-- VDS/Components/TitleLockup/TitleLockup.swift | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index 6234e155..1e51f4ef 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -57,7 +57,7 @@ open class Pagination: View { ///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)? + open var onPageDidSelect: ((Int) -> Void)? /// Total number of pages, allows limit ranging from 0 to 9999. @Clamping(range: 0...9999) public var total: Int { @@ -70,7 +70,7 @@ open class Pagination: View { } } ///Selected active page number and clips to total pages if selected index is greater than the total pages. - public var selectedPage: Int { + open var selectedPage: Int { set { if newValue >= total { _selectedPageIndex = total - 1 diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index 25aaf248..1711939c 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -288,7 +288,6 @@ open class TitleLockup: View { subTitleModel = nil } - var labelViews = [UIView]() /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() From d0c40aaecaf3a4bf868f4a7c487b65f9c24ed43f Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Aug 2024 15:41:46 -0500 Subject: [PATCH 103/113] refactored DefaultValuing to be its own thing Signed-off-by: Matt Bruce --- VDS/Protocols/DefaultValuing.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Protocols/DefaultValuing.swift b/VDS/Protocols/DefaultValuing.swift index ffeaf0eb..7d156053 100644 --- a/VDS/Protocols/DefaultValuing.swift +++ b/VDS/Protocols/DefaultValuing.swift @@ -7,6 +7,6 @@ import Foundation -public protocol DefaultValuing: Valuing { +public protocol DefaultValuing { static var defaultValue: Self { get } } From 393b791a40261af6045e2ad07d23234fe7615068 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Aug 2024 15:42:10 -0500 Subject: [PATCH 104/113] refactored due to the DefaultValueing change Signed-off-by: Matt Bruce --- VDS/Components/TileContainer/TileContainer.swift | 4 ++-- VDS/Components/Tilelet/Tilelet.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index edb369dd..b00d0388 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -15,7 +15,7 @@ import Combine open class TileContainer: TileContainerBase { /// Enum used to describe the padding choices used for this component. - public enum Padding: DefaultValuing { + public enum Padding: DefaultValuing, Valuing { case padding3X case padding4X case padding6X @@ -44,7 +44,7 @@ open class TileContainer: TileContainerBase { } } -open class TileContainerBase: View where PaddingType.ValueType == CGFloat { +open class TileContainerBase: View where PaddingType.ValueType == CGFloat { //-------------------------------------------------- // MARK: - Initializers diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 263d6b77..86dd346a 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -20,7 +20,7 @@ import Combine open class Tilelet: TileContainerBase { /// Enum used to describe the padding choices used for this component. - public enum Padding: String, DefaultValuing, CaseIterable { + public enum Padding: String, DefaultValuing, Valuing, CaseIterable { case small case large From a95499b18636377d05da84ba2711906b5b6736f5 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Aug 2024 15:42:28 -0500 Subject: [PATCH 105/113] refactored due to the DefaultValuing update Signed-off-by: Matt Bruce --- VDS/Components/Carousel/Carousel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Carousel/Carousel.swift b/VDS/Components/Carousel/Carousel.swift index f8d62520..9e14d3d3 100644 --- a/VDS/Components/Carousel/Carousel.swift +++ b/VDS/Components/Carousel/Carousel.swift @@ -58,7 +58,7 @@ open class Carousel: View { } /// Space between each tile. The default value will be 6X in tablet and 3X in mobile. - public enum Gutter: String, CaseIterable , DefaultValuing { + public enum Gutter: String, CaseIterable , DefaultValuing, Valuing { case gutter3X = "3X" case gutter6X = "6X" From 69cfb38149fa4ea439a5cca1e67e3184e4252b2b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Aug 2024 15:43:01 -0500 Subject: [PATCH 106/113] cleaned up code Signed-off-by: Matt Bruce --- VDS/Components/PriceLockup/PriceLockup.swift | 95 ++++++++++++-------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/VDS/Components/PriceLockup/PriceLockup.swift b/VDS/Components/PriceLockup/PriceLockup.swift index 0c776377..990e5b76 100644 --- a/VDS/Components/PriceLockup/PriceLockup.swift +++ b/VDS/Components/PriceLockup/PriceLockup.swift @@ -32,7 +32,7 @@ open class PriceLockup: View { // MARK: - Enums //-------------------------------------------------- /// Enum used to describe the term of PriceLockup. - public enum Term: String, CaseIterable { + public enum Term: String, DefaultValuing, CaseIterable { case month, year, biennial, none /// The default term is 'month'. @@ -54,38 +54,35 @@ open class PriceLockup: View { } /// Enum that represents the size availble for PriceLockup. - public enum Size: String, CaseIterable { - case xxxsmall - case xxsmall - case xsmall + public enum Size: String, DefaultValuing, CaseIterable { + case xxxsmall = "3XSmall" + case xxsmall = "2XSmall" + case xsmall = "XSmall" case small case medium case large - case xlarge - case xxlarge + case xlarge = "XLarge" + case xxlarge = "2XLarge" public static var defaultValue: Self { .medium } } /// Enum used to describe the kind of PriceLockup. - public enum Kind: String, CaseIterable { + public enum Kind: String, DefaultValuing, CaseIterable { case primary, secondary, savings /// The default kind is 'primary'. public static var defaultValue : Self { .primary } /// Color configuation relative to kind. - public var colorConfiguration: ViewColorConfiguration { + public var colorConfiguration: SurfaceColorConfiguration { switch self { case .primary: - return ViewColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false)} + return SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) case .secondary: - return ViewColorConfiguration().with { - $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false)} + return SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) case .savings: - return ViewColorConfiguration().with { - $0.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen36, forDisabled: false)} + return SurfaceColorConfiguration(VDSColor.paletteGreen26, VDSColor.paletteGreen36) } } } @@ -107,11 +104,11 @@ open class PriceLockup: View { open var price: Float? { didSet { setNeedsUpdate() } } /// Color to the component. The default kind is primary. - open var kind: Kind = Kind.defaultValue { didSet { setNeedsUpdate() } } + open var kind: Kind = .defaultValue { didSet { setNeedsUpdate() } } /// Size of the component. It varies by size and viewport(mobile/Tablet). /// The default size is medium with viewport mobile. - open var size: Size = Size.defaultValue { didSet { setNeedsUpdate() } } + open var size: Size = .defaultValue { didSet { setNeedsUpdate() } } /// If true, the component with a strikethrough. It applies only when uniformSize is true. /// Does not apply a strikethrough format to leading and trailing text. @@ -119,7 +116,7 @@ open class PriceLockup: View { /// Term text for the component. The default term is 'month'. /// Superscript placement can vary when term and delimeter are "none". - open var term: Term = Term.defaultValue { didSet { setNeedsUpdate() } } + open var term: Term = .defaultValue { didSet { setNeedsUpdate() } } /// Trailing text for the component. open var trailingText: String? { didSet { setNeedsUpdate() } } @@ -210,20 +207,32 @@ open class PriceLockup: View { open override func updateView() { super.updateView() - priceLockupLabel.text = fetchText() + priceLockupLabel.text = formatText() priceLockupLabel.surface = surface // Set the attributed text updateLabelAttributes() } + open override func setDefaults() { + super.setDefaults() + bold = false + hideCurrency = false + leadingText = nil + price = nil + kind = .defaultValue + size = .defaultValue + strikethrough = false + term = .defaultValue + trailingText = nil + superscript = nil + uniformSize = false + } + /// Resets to default settings. open override func reset() { - super.reset() - shouldUpdateView = false priceLockupLabel.reset() - shouldUpdateView = true - setNeedsUpdate() + super.reset() } //-------------------------------------------------- @@ -274,32 +283,44 @@ open class PriceLockup: View { } // Get text for PriceLockup. - open func fetchText() -> String { + private func formatText() -> String { var text : String = "" let space = " " let delimiter = "/" delimiterIndex = 0 strikethroughLength = 0 let currency: String = hideCurrency ? "" : "$" - if let leadingStr = leadingText { - text = text + leadingStr + space - delimiterIndex = delimiterIndex + leadingStr.count + space.count + + if let leadingText { + text.append(leadingText) + text.append(space) + delimiterIndex = delimiterIndex + leadingText.count + space.count } + strikethroughLocation = delimiterIndex - if let value = price { - let valueStr = "\(value.clean)" - text = text + currency + valueStr - delimiterIndex = delimiterIndex + valueStr.count + currency.count - strikethroughLength = valueStr.count + currency.count + + if let price = price?.clean { + text.append(currency) + text.append(price) + delimiterIndex = delimiterIndex + price.count + currency.count + strikethroughLength = price.count + currency.count } + if term != .none { - text = text + delimiter + term.type + text.append(delimiter) + text.append(term.type) strikethroughLength = strikethroughLength + delimiter.count + term.type.count } - if let trailingStr = trailingText { - text = text + space + trailingStr + + if let trailingText { + text.append(space) + text.append(trailingText) } - text = text + (superscript ?? "") + + if let superscript { + text.append(superscript) + } + return text } } @@ -307,6 +328,6 @@ open class PriceLockup: View { extension Float { // remove a decimal from a float if the decimal is equal to 0 var clean: String { - return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self) + return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(describing: self) } } From aae72b435b132425d15bb14bceca7a9f17b2ed53 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Aug 2024 15:48:29 -0500 Subject: [PATCH 107/113] updated notes Signed-off-by: Matt Bruce --- VDS/SupportingFiles/ReleaseNotes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/VDS/SupportingFiles/ReleaseNotes.txt b/VDS/SupportingFiles/ReleaseNotes.txt index 80d0131b..ec14a5f7 100644 --- a/VDS/SupportingFiles/ReleaseNotes.txt +++ b/VDS/SupportingFiles/ReleaseNotes.txt @@ -1,6 +1,7 @@ 1.0.72 ---------------- - ONEAPP-9311 - InputStepper - Finished +- ONEAPP-9314 - PriceLockup - Finished - CXTDT-599736 - All classes refactored workflow setup(), reset(), setDefaults() 1.0.71 From 5a255aac52ce5a80f7031fab03bb6526d539cb88 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 16 Aug 2024 15:18:33 -0500 Subject: [PATCH 108/113] refactored for bugs in missing buttons for 2 rowCount Signed-off-by: Matt Bruce --- .../ButtonGroupPositionLayout.swift | 81 +++++++++---------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift index 5d3a339f..8c71ac4e 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift @@ -49,52 +49,45 @@ class ButtonCollectionViewRow { func layout(for position: ButtonGroup.Alignment, with collectionViewWidth: CGFloat){ var offset = 0.0 let height = rowHeight + let buttonAttributes = attributes.filter{$0.isButton} + attributes.last?.spacing = 0 + ///Calculate the available space in the row + let totalSpacingBetweenAttributes = attributes.reduce(0.0) { $0 + $1.spacing } + let availableWidthAfterSpacing = collectionViewWidth - totalSpacingBetweenAttributes + let buttonCount = CGFloat(buttonAttributes.count) + + //see how much of the rows width is used for + //non-buttons that are BaseButton Subclasses that are not "Button" + let nonButtonSpace = attributes.filter { !$0.isButton }.reduce(0.0) { $0 + $1.frame.width + $1.spacing } + let maxButtonWidth = availableWidthAfterSpacing / buttonCount + + //getting available button space since textlinks need their space + let buttonsAvailableSpace = collectionViewWidth - nonButtonSpace + var buttonWidth = maxButtonWidth + var buttonCalculatedPercentage: CGFloat = 0.0 + let buttonEqualSpacing = buttonsAvailableSpace / buttonCount + + // test sizing + let testSize = buttonWidth * CGFloat(buttonAttributes.count) + //check to see if you have buttons and there is a percentage if let buttonPercentage, hasButtons, buttonPercentage > 0 { - - var usedSpace = 0.0 - //get the width for the buttons - for attribute in attributes { - if !attribute.isButton { - usedSpace += attribute.frame.width + buttonCalculatedPercentage = CGFloat(buttonPercentage / 100.0) + let buttonPercentageWidth = buttonCalculatedPercentage * buttonsAvailableSpace + buttonWidth = min(max(buttonPercentageWidth, Button.Size.large.minimumWidth), maxButtonWidth) + } + + //resize the buttonAttributes + if buttonWidth >= Button.Size.large.minimumWidth && !buttonAttributes.isEmpty { + if testSize <= buttonsAvailableSpace { + for attribute in buttonAttributes { + attribute.frame.size.width = buttonCalculatedPercentage.isZero ? min(attribute.frame.size.width, buttonWidth) : buttonWidth } - usedSpace += attribute.spacing - } - let buttonAvailableSpace = collectionViewWidth - usedSpace - let realPercentage = (buttonPercentage / 100) - let buttonWidth = realPercentage * buttonAvailableSpace -// print("buttonPercentage :\(realPercentage)") -// print("collectionView width:\(collectionViewWidth)") -// print("usedSpace width:\(usedSpace)") -// print("button available width:\(buttonAvailableSpace)") -// print("each button width:\(buttonWidth)\n") -// print("minimum widht:\(ButtonSize.large.minimumWidth)") - // test sizing - var testSize = 0.0 - var buttonCount = 0.0 - for attribute in attributes { - if attribute.isButton { - testSize += buttonWidth - buttonCount += 1 - } - } - - if buttonWidth >= Button.Size.large.minimumWidth { - if testSize <= buttonAvailableSpace { - for attribute in attributes { - if attribute.isButton { - attribute.frame.size.width = buttonWidth - } - } - } else { - let distributedSize = buttonAvailableSpace / buttonCount - for attribute in attributes { - if attribute.isButton { - attribute.frame.size.width = distributedSize - } - } + } else { + for attribute in buttonAttributes { + attribute.frame.size.width = buttonEqualSpacing } } } @@ -181,7 +174,7 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { var rows = [ButtonCollectionViewRow]() rows.append(ButtonCollectionViewRow()) - let collectionViewWidth = collectionView.frame.width + let collectionViewWidth = collectionView.horizontalPinnedWidth() ?? collectionView.frame.width for item in 0.. collectionViewWidth || (rowQuantity > 0 && rowItemCount == rowQuantity) { + if (layoutWidthIterator + itemSize.width) > collectionViewWidth && rowQuantity == 0 + || (rowQuantity > 0 && rowItemCount == rowQuantity) { // If the current row width (after this item being laid out) is exceeding // the width of the collection view content, put it in the next line @@ -318,3 +312,4 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { } } + From 7efc0096afddea6c949e7c6ce4ddc41ade2dd592 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 16 Aug 2024 15:25:26 -0500 Subject: [PATCH 109/113] refactored more code Signed-off-by: Matt Bruce --- .../ButtonGroupPositionLayout.swift | 80 ++++++++++--------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift index 8c71ac4e..02a93556 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift @@ -49,49 +49,54 @@ class ButtonCollectionViewRow { func layout(for position: ButtonGroup.Alignment, with collectionViewWidth: CGFloat){ var offset = 0.0 let height = rowHeight + + attributes.last?.spacing = 0 + + //filter only the buttons since this is the only + //object we can change the frames for. let buttonAttributes = attributes.filter{$0.isButton} - attributes.last?.spacing = 0 - - ///Calculate the available space in the row - let totalSpacingBetweenAttributes = attributes.reduce(0.0) { $0 + $1.spacing } - let availableWidthAfterSpacing = collectionViewWidth - totalSpacingBetweenAttributes - let buttonCount = CGFloat(buttonAttributes.count) - - //see how much of the rows width is used for - //non-buttons that are BaseButton Subclasses that are not "Button" - let nonButtonSpace = attributes.filter { !$0.isButton }.reduce(0.0) { $0 + $1.frame.width + $1.spacing } - let maxButtonWidth = availableWidthAfterSpacing / buttonCount - - //getting available button space since textlinks need their space - let buttonsAvailableSpace = collectionViewWidth - nonButtonSpace - var buttonWidth = maxButtonWidth - var buttonCalculatedPercentage: CGFloat = 0.0 - let buttonEqualSpacing = buttonsAvailableSpace / buttonCount - - // test sizing - let testSize = buttonWidth * CGFloat(buttonAttributes.count) - - //check to see if you have buttons and there is a percentage - if let buttonPercentage, hasButtons, buttonPercentage > 0 { - buttonCalculatedPercentage = CGFloat(buttonPercentage / 100.0) - let buttonPercentageWidth = buttonCalculatedPercentage * buttonsAvailableSpace - buttonWidth = min(max(buttonPercentageWidth, Button.Size.large.minimumWidth), maxButtonWidth) - } - - //resize the buttonAttributes - if buttonWidth >= Button.Size.large.minimumWidth && !buttonAttributes.isEmpty { - if testSize <= buttonsAvailableSpace { - for attribute in buttonAttributes { - attribute.frame.size.width = buttonCalculatedPercentage.isZero ? min(attribute.frame.size.width, buttonWidth) : buttonWidth - } - } else { - for attribute in buttonAttributes { - attribute.frame.size.width = buttonEqualSpacing + if !buttonAttributes.isEmpty { + let buttonCount = CGFloat(buttonAttributes.count) + + ///Calculate the spaces between items in the row + let totalSpacingBetweenAttributes = attributes.reduce(0.0) { $0 + $1.spacing } + + //see how much of the rows width is used for + //non-buttons that are BaseButton Subclasses that are not "Button" + let nonButtonSpace = attributes.filter { !$0.isButton }.reduce(0.0) { $0 + $1.frame.width + $1.spacing } + + //getting available button space since textlinks need their space + let buttonsAvailableSpace = collectionViewWidth - nonButtonSpace - totalSpacingBetweenAttributes + var maxButtonWidth = buttonsAvailableSpace / buttonCount + var buttonCalculatedPercentage: CGFloat = 0.0 + let buttonEqualSpacing = buttonsAvailableSpace / buttonCount + + // test sizing + let testSize = maxButtonWidth * CGFloat(buttonAttributes.count) + + //check to see if you have buttons and there is a percentage + if let buttonPercentage, hasButtons, buttonPercentage > 0 { + buttonCalculatedPercentage = CGFloat(buttonPercentage / 100.0) + let buttonPercentageWidth = buttonCalculatedPercentage * buttonsAvailableSpace + maxButtonWidth = min(max(buttonPercentageWidth, Button.Size.large.minimumWidth), maxButtonWidth) + } + + //resize the buttonAttributes + if maxButtonWidth >= Button.Size.large.minimumWidth && !buttonAttributes.isEmpty { + if testSize <= buttonsAvailableSpace { + for attribute in buttonAttributes { + attribute.frame.size.width = buttonCalculatedPercentage.isZero ? min(attribute.frame.size.width, maxButtonWidth) : maxButtonWidth + } + } else { + for attribute in buttonAttributes { + attribute.frame.size.width = buttonEqualSpacing + } } } } + //update the offset based on position switch position { case .left: break @@ -313,3 +318,4 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { } + From 30a4b1e0f749f62671334d57fd1b706f042e537c Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 16 Aug 2024 15:41:50 -0500 Subject: [PATCH 110/113] refactored more code Signed-off-by: Matt Bruce --- .../ButtonGroup/ButtonGroupPositionLayout.swift | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift index 02a93556..00ff747c 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift @@ -68,13 +68,10 @@ class ButtonCollectionViewRow { //getting available button space since textlinks need their space let buttonsAvailableSpace = collectionViewWidth - nonButtonSpace - totalSpacingBetweenAttributes - var maxButtonWidth = buttonsAvailableSpace / buttonCount - var buttonCalculatedPercentage: CGFloat = 0.0 let buttonEqualSpacing = buttonsAvailableSpace / buttonCount - - // test sizing - let testSize = maxButtonWidth * CGFloat(buttonAttributes.count) - + var maxButtonWidth = buttonEqualSpacing //default to equal spacing + var buttonCalculatedPercentage: CGFloat = 0.0 + //check to see if you have buttons and there is a percentage if let buttonPercentage, hasButtons, buttonPercentage > 0 { buttonCalculatedPercentage = CGFloat(buttonPercentage / 100.0) @@ -83,12 +80,14 @@ class ButtonCollectionViewRow { } //resize the buttonAttributes - if maxButtonWidth >= Button.Size.large.minimumWidth && !buttonAttributes.isEmpty { - if testSize <= buttonsAvailableSpace { + if maxButtonWidth >= Button.Size.large.minimumWidth { + //if there is enough room for all buttons + if maxButtonWidth * buttonCount <= buttonsAvailableSpace { for attribute in buttonAttributes { attribute.frame.size.width = buttonCalculatedPercentage.isZero ? min(attribute.frame.size.width, maxButtonWidth) : maxButtonWidth } } else { + //if not enough room, give all buttons the same width for attribute in buttonAttributes { attribute.frame.size.width = buttonEqualSpacing } From dcc0307e9b3ae881a9990103cc0f470bfedf22e7 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 16 Aug 2024 15:56:48 -0500 Subject: [PATCH 111/113] fixed bug of adding space between 2 times Signed-off-by: Matt Bruce --- .../Buttons/ButtonGroup/ButtonGroupPositionLayout.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift index 00ff747c..433fa58f 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift @@ -64,7 +64,7 @@ class ButtonCollectionViewRow { //see how much of the rows width is used for //non-buttons that are BaseButton Subclasses that are not "Button" - let nonButtonSpace = attributes.filter { !$0.isButton }.reduce(0.0) { $0 + $1.frame.width + $1.spacing } + let nonButtonSpace = attributes.filter { !$0.isButton }.reduce(0.0) { $0 + $1.frame.width } //getting available button space since textlinks need their space let buttonsAvailableSpace = collectionViewWidth - nonButtonSpace - totalSpacingBetweenAttributes From 3c92411703ce5c13bef7a55e23f719001ad6ddb0 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 16 Aug 2024 16:03:38 -0500 Subject: [PATCH 112/113] updated version Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 5cc7ae1c..3e5d50bc 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -1589,7 +1589,7 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1627,7 +1627,7 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; From baf9136e26054553b5641576b74cdd5efba42cc3 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Sat, 17 Aug 2024 14:36:59 -0500 Subject: [PATCH 113/113] code refactor into setDefaults() Signed-off-by: Matt Bruce --- .../BadgeIndicator/BadgeIndicator.swift | 21 +++++++------- VDS/Components/Checkbox/CheckboxItem.swift | 28 ------------------- VDS/Components/DatePicker/DatePicker.swift | 18 ++++++------ .../Notification/Notification.swift | 8 +++--- VDS/Components/Pagination/Pagination.swift | 4 +++ VDS/Components/Tabs/Tab.swift | 4 +++ VDS/Components/Tilelet/Tilelet.swift | 19 +++++++------ VDS/Components/Tooltip/Tooltip.swift | 20 ++++++------- 8 files changed, 52 insertions(+), 70 deletions(-) diff --git a/VDS/Components/BadgeIndicator/BadgeIndicator.swift b/VDS/Components/BadgeIndicator/BadgeIndicator.swift index dc200cd8..daecddf4 100644 --- a/VDS/Components/BadgeIndicator/BadgeIndicator.swift +++ b/VDS/Components/BadgeIndicator/BadgeIndicator.swift @@ -293,16 +293,6 @@ open class BadgeIndicator: View { label.centerYAnchor.constraint(equalTo: badgeView.centerYAnchor).isActive = true labelContraints.isActive = true - bridge_accessibilityLabelBlock = { [weak self] in - guard let self else { return "" } - if let accessibilityText { - return kind == .numbered ? label.text + " " + accessibilityText : accessibilityText - } else if kind == .numbered { - return label.text - } else { - return "Simple" - } - } } open override func setDefaults() { @@ -324,6 +314,17 @@ open class BadgeIndicator: View { height = nil accessibilityText = nil maximumDigits = .two + + bridge_accessibilityLabelBlock = { [weak self] in + guard let self else { return "" } + if let accessibilityText { + return kind == .numbered ? label.text + " " + accessibilityText : accessibilityText + } else if kind == .numbered { + return label.text + } else { + return "Simple" + } + } } /// Resets to default settings. diff --git a/VDS/Components/Checkbox/CheckboxItem.swift b/VDS/Components/Checkbox/CheckboxItem.swift index d3af344b..e489edea 100644 --- a/VDS/Components/Checkbox/CheckboxItem.swift +++ b/VDS/Components/Checkbox/CheckboxItem.swift @@ -49,13 +49,6 @@ open class CheckboxItem: SelectorItemBase { sendActions(for: .valueChanged) } - open override func setup() { - super.setup() - let foo = ConcreteClass(customView: Checkbox()) - - print(foo.customView.isAnimated) - } - open override func setDefaults() { super.setDefaults() isAnimated = false @@ -67,24 +60,3 @@ open class CheckboxItem: SelectorItemBase { super.updateView() } } - - -@objcMembers -open class GenericClass: NSObject { - public var customView: T - - public init(customView: T = T()) { - self.customView = customView - } -} - -@objcMembers -@objc(VDSConcreteClass) -open class ConcreteClass: GenericClass { - -} - -@objcMembers -@objc(VDSConcreteCheckboxClass) -open class ConcreteCheckboxClass: ConcreteClass {} - diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index d2d64380..e8802c69 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -152,15 +152,7 @@ open class DatePicker: EntryFieldBase { // setting color config selectedDateLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() - - // tap gesture - containerView.onClick = { [weak self] _ in - guard let self else { return } - if isEnabled && !isReadOnly { - showPopover() - } - } - + NotificationCenter.default .publisher(for: UIDevice.orientationDidChangeNotification).sink { [weak self] _ in guard let self else { return } @@ -177,6 +169,14 @@ open class DatePicker: EntryFieldBase { calendarModel = .init() dateFormat = .shortNumeric selectedDateLabel.textStyle = .bodyLarge + + // tap gesture + containerView.onClick = { [weak self] _ in + guard let self else { return } + if isEnabled && !isReadOnly { + showPopover() + } + } } open override func getFieldContainer() -> UIView { diff --git a/VDS/Components/Notification/Notification.swift b/VDS/Components/Notification/Notification.swift index e188faf7..59d2b78f 100644 --- a/VDS/Components/Notification/Notification.swift +++ b/VDS/Components/Notification/Notification.swift @@ -268,10 +268,6 @@ open class Notification: View { closeButton.accessibilityTraits = [.button] closeButton.accessibilityLabel = "Close Notification" - typeIcon.bridge_accessibilityLabelBlock = { [weak self] in - guard let self else { return "" } - return style.accessibleText - } } open override func setDefaults() { @@ -296,6 +292,10 @@ open class Notification: View { hideCloseButton = false + typeIcon.bridge_accessibilityLabelBlock = { [weak self] in + guard let self else { return "" } + return style.accessibleText + } } /// Resets to default settings. diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index 1e51f4ef..cf4f9183 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -145,6 +145,10 @@ open class Pagination: View { .sink { [weak self] value in 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) + } + + open override func setDefaults() { + super.setDefaults() collectionContainerView.onAccessibilityIncrement = { [weak self] in guard let self else { return } self.selectedPage = max(0, self.selectedPage + 1) diff --git a/VDS/Components/Tabs/Tab.swift b/VDS/Components/Tabs/Tab.swift index edc8af55..0b97568b 100644 --- a/VDS/Components/Tabs/Tab.swift +++ b/VDS/Components/Tabs/Tab.swift @@ -150,6 +150,10 @@ extension Tabs { labelLeadingConstraint = label.pinLeading(anchor: layoutGuide.leadingAnchor) labelBottomConstraint = label.pinBottom(anchor: layoutGuide.bottomAnchor, priority: .defaultHigh) + } + + open override func setDefaults() { + super.setDefaults() bridge_accessibilityLabelBlock = { [weak self] in guard let self else { return "" } return text diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 86dd346a..cb154056 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -379,15 +379,6 @@ open class Tilelet: TileContainerBase { titleLockupSubTitleLabelHeightGreaterThanConstraint?.priority = .defaultHigh titleLockupSubTitleLabelHeightGreaterThanConstraint?.activate() - directionalIcon.bridge_accessibilityLabelBlock = { [weak self] in - guard let self, let directionalIconModel else { return nil } - return directionalIconModel.accessibleText - } - - descriptiveIcon.bridge_accessibilityLabelBlock = { [weak self] in - guard let self, let descriptiveIconModel else { return nil } - return descriptiveIconModel.accessibleText - } } open override func setDefaults() { @@ -403,6 +394,16 @@ open class Tilelet: TileContainerBase { subTitleModel = nil descriptiveIconModel = nil directionalIconModel = nil + + directionalIcon.bridge_accessibilityLabelBlock = { [weak self] in + guard let self, let directionalIconModel else { return nil } + return directionalIconModel.accessibleText + } + + descriptiveIcon.bridge_accessibilityLabelBlock = { [weak self] in + guard let self, let descriptiveIconModel else { return nil } + return descriptiveIconModel.accessibleText + } } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/Tooltip/Tooltip.swift b/VDS/Components/Tooltip/Tooltip.swift index effafa07..1c2e2061 100644 --- a/VDS/Components/Tooltip/Tooltip.swift +++ b/VDS/Components/Tooltip/Tooltip.swift @@ -129,6 +129,16 @@ open class Tooltip: Control, TooltipLaunchable { isAccessibilityElement = true accessibilityTraits = .button + } + + open override func setDefaults() { + super.setDefaults() + closeButtonText = "Close" + fillColor = .primary + size = .medium + title = nil + content = nil + contentView = nil onClick = { [weak self] tooltip in guard let self else { return} @@ -158,16 +168,6 @@ open class Tooltip: Control, TooltipLaunchable { return isEnabled ? "Double tap to open." : "" } } - - open override func setDefaults() { - super.setDefaults() - closeButtonText = "Close" - fillColor = .primary - size = .medium - title = nil - content = nil - contentView = nil - } /// Used to make changes to the View based off a change events or from local properties.