Compare commits

...

106 Commits

Author SHA1 Message Date
98dde01e18 Merge branch 'develop' of ssh://git@192.168.1.128:220/mbrucedogs/vds_ios.git into develop 2025-01-23 13:25:16 -06:00
78dd2d5df8 removed obj-c members
Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
2025-01-23 13:24:48 -06:00
58850aeb6e Merge branch 'develop' into vasavk/listUnordered 2025-01-06 17:16:27 -06:00
Sumanth Nadigadda
15a53964c5 Adding release note for CXTDT-586383 changes 2024-10-23 13:08:09 +05:30
Bruce, Matt R
762868609b Merge branch 'bugfix/CXTDT-626180' into 'develop'
Footnote Item - Padding issue fixed for the Symbol type ‘none'

See merge request BPHV_MIPS/vds_ios!320
2024-10-22 13:20:02 +00:00
Vasavi Kanamarlapudi
e256dfd209 Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios into vasavk/listUnordered
# Conflicts:
#	VDS/SupportingFiles/ReleaseNotes.txt
2024-10-22 14:48:36 +05:30
Vasavi Kanamarlapudi
a1236df9a9 Digital ACT-191 CXTDT-626180 defect: Footnote Item - Padding issue fixed for the Symbol type ‘none'. 2024-10-22 14:43:36 +05:30
Bruce, Matt R
539f10ffbf Merge branch 'bugfix/priceLockup' into 'develop'
Fixed the PriceLockup accessibility Issue - CXTDT-630735

See merge request BPHV_MIPS/vds_ios!318
2024-10-21 14:30:23 +00:00
Bruce, Matt R
cc530e5903 Merge branch 'develop' into 'bugfix/priceLockup'
# Conflicts:
#   VDS/SupportingFiles/ReleaseNotes.txt
2024-10-21 14:29:53 +00:00
Bruce, Matt R
482212e88c Merge branch 'bugfix/CXTDT-626164-footnoteGroup' into 'develop'
Fixed the defect CXTDT-626164 of FootnoteGroup

See merge request BPHV_MIPS/vds_ios!319
2024-10-21 14:28:34 +00:00
Vasavi Kanamarlapudi
47409d2905 Digital ACT-191 CXTDT-626164 defect: FootnoteGroup - Text not showing in Dark Mode 2024-10-21 15:26:58 +05:30
Vasavi Kanamarlapudi
16516fbeed Digital ACT-191 ONEAPP-11355 story: updated release notes 2024-10-21 12:47:43 +05:30
Vasavi Kanamarlapudi
484d12626e Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios into vasavk/listUnordered 2024-10-21 12:44:59 +05:30
Vasavi Kanamarlapudi
9856c0a482 Digital ACT-191 CXTDT-630735 defect: PriceLockup accessibility - to render equivalent information for the strike through price visually represents. 2024-10-21 12:39:14 +05:30
Vasavi Kanamarlapudi
4797e5a7f7 Digital ACT-191 ONEAPP-11355 story: adjusted alignments 2024-10-19 14:38:03 +05:30
Bruce, Matt R
942834ec3e Merge branch 'mbruce/bugfix' into 'develop'
updated version

See merge request BPHV_MIPS/vds_ios!316
2024-10-18 21:31:53 +00:00
Vasavi Kanamarlapudi
62aedc7c5a Digital ACT-191 ONEAPP-11355 story: changes done as per anatomy 2024-10-18 19:52:55 +05:30
Matt Bruce
2e3524cde6 updated version
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-17 16:19:41 -05:00
Bruce, Matt R
735749eaa5 Merge branch 'mbruce/bugfix' into 'develop'
bug fixes

See merge request BPHV_MIPS/vds_ios!315
2024-10-16 20:17:26 +00:00
Matt Bruce
4e581491fa added initial actionable elements
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-16 15:16:05 -05:00
Bruce, Matt R
4fbbe485f7 Merge branch 'bugfix/CXTDT-578885' into 'develop'
Fix for CXTDT-578885, Settings appropriate accessibility label for each cell

See merge request BPHV_MIPS/vds_ios!314
2024-10-16 16:17:46 +00:00
Sumanth Nadigadda
216971b878 Fix for CXTDT-578885, Settings appropriate accessibility label for each cell 2024-10-16 21:37:01 +05:30
Matt Bruce
d66d4d0e26 removed surface since it isn't used anywhere
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-16 08:39:48 -05:00
Vasavi Kanamarlapudi
c352fbaac9 Digital ACT-191 ONEAPP-11355 story: added list unordered item model 2024-10-16 14:26:01 +05:30
Vasavi Kanamarlapudi
759ef0db5b Digital ACT-191 ONEAPP-11355 story: added change log and added class for docs 2024-10-16 14:24:45 +05:30
Vasavi Kanamarlapudi
dc7dbffcf6 Digital ACT-191 ONEAPP-11355 story: added new file for List Unordered 2024-10-16 14:23:30 +05:30
Bruce, Matt R
a019c1ba36 Merge branch 'mbruce/bugfix' into 'develop'
fixed issue with icon

See merge request BPHV_MIPS/vds_ios!313
2024-10-15 20:12:05 +00:00
Matt Bruce
fc6b2991b8 fixed issue with icon
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-14 13:23:16 -05:00
Hedden, Kyle Matthew
dd437d7423 Merge branch 'feature/CXTDT-624895-Badge-Color-Updates' into 'develop'
VDS - iOS Badge - Include ability to select custom color for Label and Background

See merge request BPHV_MIPS/vds_ios!311
2024-10-09 13:04:03 +00:00
Matt Bruce
025700a632 made internal since there is a duplicate in coreUI that is obj-c
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-08 09:18:51 -05:00
Matt Bruce
50117f3f2f updated to include badge color changes to dependent components
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-07 15:58:56 -05:00
Matt Bruce
8057b16e5b added badge coloring in text/fill
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-07 15:58:30 -05:00
Bruce, Matt R
1728592bff Merge branch 'mbruce/bugfix' into 'develop'
mreged in Modal

See merge request BPHV_MIPS/vds_ios!310
2024-10-04 19:14:55 +00:00
Matt Bruce
a336066387 updated version
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-04 14:07:12 -05:00
Matt Bruce
049234831f updated notes
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-04 14:04:05 -05:00
Matt Bruce
dc83d1f565 mreged in Modal
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-04 13:55:20 -05:00
Bruce, Matt R
92a80b6496 Merge branch 'mbruce/modal' into 'develop'
removed width/height contraints in dialog and pushed down to the viewcontroller.

See merge request BPHV_MIPS/vds_ios!309
2024-10-04 18:47:19 +00:00
Matt Bruce
83ce02e879 updated spacing issue
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-04 13:46:43 -05:00
Bruce, Matt R
9f2da6f072 Merge branch 'bugfix/UIFont-Converter' into 'develop'
updated the UIFont to TextStyle conversion

See merge request BPHV_MIPS/vds_ios!307
2024-10-04 17:00:05 +00:00
Matt Bruce
c91da2189c updated the UIFont to TextStyle conversion
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-04 11:59:05 -05:00
Bruce, Matt R
2febe4595c Merge branch 'bugfix/TextStyleLabelAttribute' into 'develop'
fixed bug in TextStyle Attribute dealing with lineHeight and baseline

See merge request BPHV_MIPS/vds_ios!306
2024-10-03 21:04:11 +00:00
Matt Bruce
c68faaec31 fixed bug in TextStyle Attribute dealing with lineHeight and baseline
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-03 15:09:16 -05:00
Matt Bruce
0aab83c4cb Merge branch 'vasavk/modal' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios.git into mbruce/modal 2024-10-03 09:19:29 -05:00
Vasavi Kanamarlapudi
ff125d388d Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios into vasavk/modal 2024-10-03 12:50:04 +05:30
Matt Bruce
f741b3df8b removed width/height contraints in dialog and pushed down to the viewcontroller.
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-10-01 16:32:17 -05:00
Vasavi Kanamarlapudi
a3a714dfc1 Digital ACT-191 ONEAPP-10928 story: documentation changes 2024-09-27 19:47:55 +05:30
Vasavi Kanamarlapudi
b210135566 Digital ACT-191 ONEAPP-10928 story: show/hide close button based on flag 2024-09-27 18:58:53 +05:30
Vasavi Kanamarlapudi
81725ffdeb Digital ACT-191 ONEAPP-10928 story: Modal to be displayed at full screen if fullScreenDialog true 2024-09-27 18:11:54 +05:30
Vasavi Kanamarlapudi
08be77242d Digital ACT-191 ONEAPP-10928 story: onClick for close button 2024-09-27 17:15:33 +05:30
Vasavi Kanamarlapudi
8a63ea5c75 Digital ACT-191 ONEAPP-10928 story: loading button data if provided 2024-09-27 17:05:19 +05:30
Vasavi Kanamarlapudi
e71b94bc4f Digital ACT-191 ONEAPP-10928 story: resolved constraint conflicts 2024-09-26 13:40:35 +05:30
Bruce, Matt R
183630f6b5 Merge branch 'mbruce/bugfix' into 'develop'
bugs

See merge request BPHV_MIPS/vds_ios!304
2024-09-25 21:31:44 +00:00
Matt Bruce
c17e74dea0 first cut of language
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-25 15:03:14 -05:00
Vasavi Kanamarlapudi
12ad80643d Digital ACT-191 ONEAPP-10928 story: documentation changes for modal dialog 2024-09-25 18:15:27 +05:30
Vasavi Kanamarlapudi
4d5704d47b Digital ACT-191 ONEAPP-10928 story: content text style and surface updates 2024-09-25 16:08:13 +05:30
Vasavi Kanamarlapudi
56155abbb0 Digital ACT-191 ONEAPP-10928 story: updated modal with alignments and spacing 2024-09-25 12:02:33 +05:30
Matt Bruce
821b06360e CXTDT-595965 - Tilelet - Incorrect Mobile Padding
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-24 15:25:14 -05:00
Matt Bruce
663c6072be CXTDT-595956 - Tilelet - Text Position does not work on Light surface
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-24 14:55:09 -05:00
Matt Bruce
d5cca1626e fixed width issue
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-24 14:54:19 -05:00
Matt Bruce
5e932000da updated tilelet
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-24 14:54:08 -05:00
Vasavi Kanamarlapudi
3c1caa7f5a Digital ACT-191 ONEAPP-10928 story: launching modal with fullscreen for mobile 2024-09-24 10:52:09 +05:30
Matt Bruce
c39a89a3f3 CXTDT-591307 - DatePicker - Accessibility - #1 & #2
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-20 16:10:37 -05:00
Bruce, Matt R
cb37d80f65 Merge branch 'mbruce/bugfix' into 'develop'
Width Updates

See merge request BPHV_MIPS/vds_ios!303
2024-09-20 19:24:17 +00:00
Matt Bruce
ebbbffc86e updated maxWidth
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-20 14:13:27 -05:00
Matt Bruce
26a3206639 removed constraints
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-20 14:13:11 -05:00
Matt Bruce
b5f3e37a4c constraindWidth
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-20 14:12:18 -05:00
Matt Bruce
ae460b8e4f removed
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-20 14:12:05 -05:00
Matt Bruce
905c5e9fb0 updated width
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-20 14:11:28 -05:00
Vasavi Kanamarlapudi
1f37597117 Digital ACT-191 ONEAPP-10928 story: added change log file 2024-09-20 13:04:47 +05:30
Vasavi Kanamarlapudi
f868389cb8 Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios into vasavk/modal 2024-09-20 12:46:28 +05:30
Vasavi Kanamarlapudi
aa092023de Digital ACT-191 ONEAPP-10928 story: added class for docs 2024-09-20 12:40:57 +05:30
Vasavi Kanamarlapudi
e951e82b88 Digital ACT-191 ONEAPP-10928 story: added new file for Modal 2024-09-20 12:39:41 +05:30
Bruce, Matt R
e77bbea163 Merge branch 'mbruce/bugfix' into 'develop'
rename class for docs

See merge request BPHV_MIPS/vds_ios!302
2024-09-19 19:35:37 +00:00
Matt Bruce
e1f88e600b fixed issue with SelectorItemBase
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-19 14:34:39 -05:00
Matt Bruce
8028c8a785 removed constraints and for this class to be setup correctly in its parent rather than doing it in the view. similar to how UIButton works. 2024-09-19 14:34:24 -05:00
Matt Bruce
58b83fc37f rename class for docs
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-19 14:33:31 -05:00
Bruce, Matt R
fe8fb7f3cc Merge branch 'vasavk/footnote' into 'develop'
VDS Brand 3.0 Footnote for IOS

See merge request BPHV_MIPS/vds_ios!299
2024-09-18 20:16:24 +00:00
Matt Bruce
8ce9108d45 refactored the adding of footnotes similar to the Tabs
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-18 15:14:39 -05:00
Matt Bruce
9f6c115f50 made internal since only group uses this
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-18 15:00:27 -05:00
Vasavi Kanamarlapudi
2854aea8c2 refactored naming
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-09-18 14:56:04 -05:00
Vasavi Kanamarlapudi
e08de8fb3e Digital ACT-191 ONEAPP-10586 story: alignment changes to stackview 2024-09-12 18:26:21 +05:30
Vasavi Kanamarlapudi
9b885da7ca using .isHidden feature 2024-09-12 15:46:32 +05:30
Vasavi Kanamarlapudi
09351db136 Digital ACT-191 ONEAPP-10586 story: updated symbolType to use as String, camelCase applied to footnoteItems 2024-09-12 13:26:06 +05:30
Hedden, Kyle Matthew
6df8932d5d Merge branch 'bugfix/toggleAlignment' into 'develop'
Fix toggle alignment issue

See merge request BPHV_MIPS/vds_ios!300
2024-09-06 16:18:05 +00:00
Xi Zhang
f27f04c23d Fix toggle alignment issue 2024-09-04 15:40:07 -04:00
Vasavi Kanamarlapudi
e22b64095b Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios into vasavk/footnote
# Conflicts:
#	VDS.xcodeproj/project.pbxproj
2024-09-03 10:32:52 +05:30
Vasavi Kanamarlapudi
a0b139b0af Digital ACT-191 ONEAPP-10586 story: added footnote group and updating with footnote items 2024-09-02 11:33:59 +05:30
Vasavi Kanamarlapudi
73caedce5f Digital ACT-191 ONEAPP-10586 story: updating content as per configuration 2024-08-29 19:09:22 +05:30
Bruce, Matt R
973c45adbd Merge branch 'mbruce/bugfix' into 'develop'
Bug fixes

See merge request BPHV_MIPS/vds_ios!298
2024-08-27 20:28:14 +00:00
Matt Bruce
2365f8dbf4 updated version
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-08-27 14:57:20 -05:00
Matt Bruce
296703076e updated from Control to View and add publisher
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-08-27 14:28:51 -05:00
Matt Bruce
c76d3e7b76 updated Pagination to a Control
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-08-27 14:04:41 -05:00
Matt Bruce
35036ca804 more updates on carousel
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-08-27 13:31:48 -05:00
Matt Bruce
ad6291af30 force component to redraw
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-08-27 11:56:19 -05:00
Matt Bruce
31f01773c4 added borders to the cells for carousel
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-08-27 11:44:10 -05:00
Matt Bruce
63499838c6 Merge branch 'develop' into mbruce/bugfix 2024-08-27 11:43:48 -05:00
Bruce, Matt R
e4630b65ea Merge branch 'bugfix/carousel' into 'develop'
refactoring Carousel to use a UICollectionView

See merge request BPHV_MIPS/vds_ios!297
2024-08-27 15:01:44 +00:00
Bruce, Matt R
47747c801e Merge branch 'bugfix/carouselPaginationCaret' into 'develop'
Pagination caret size and horizontal offset

See merge request BPHV_MIPS/vds_ios!296
2024-08-27 14:09:20 +00:00
Vasavi Kanamarlapudi
1f2637ed04 refactoring Carousel to use a UICollectionView 2024-08-27 11:54:41 +05:30
Matt Bruce
343f674251 Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios.git into mbruce/bugfix 2024-08-23 09:04:59 -05:00
Vasavi Kanamarlapudi
9cbfe1d46a Digital ACT-191 ONEAPP-10586 story: added public properties 2024-08-22 17:08:12 +05:30
Matt Bruce
f2c6eb0fc2 Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios.git into mbruce/bugfix 2024-08-21 15:32:53 -05:00
Matt Bruce
7536774c0f fixed bug in checkbox getting crushed.
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-08-21 15:32:20 -05:00
Vasavi Kanamarlapudi
7667908ffd Digital ACT-191 ONEAPP-10586 story: added new file for footnote 2024-08-21 18:32:40 +05:30
Vasavi Kanamarlapudi
030ebdf897 added release note 2024-08-21 13:42:48 +05:30
Vasavi Kanamarlapudi
592d2c1f1f Digital ACT-191 CXTDT-603719 defect: Added fix for pagination caret icon 2024-08-21 13:39:35 +05:30
82 changed files with 2339 additions and 479 deletions

View File

@ -10,15 +10,25 @@
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 */; };
1818D04D2C9BD2170053E73C /* ModalDialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1818D04C2C9BD2170053E73C /* ModalDialogViewController.swift */; };
1818D04F2C9BD3F60053E73C /* ModalDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1818D04E2C9BD3F60053E73C /* ModalDialog.swift */; };
1818D0512C9BD4090053E73C /* ModalLaunchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1818D0502C9BD4090053E73C /* ModalLaunchable.swift */; };
1818D0532C9BD47C0053E73C /* ModalModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1818D0522C9BD47C0053E73C /* ModalModel.swift */; };
1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; };
183B16F32C78CF7C00BA6A10 /* CarouselSlotCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183B16F22C78CF7C00BA6A10 /* CarouselSlotCell.swift */; };
183B16F72C80B32200BA6A10 /* FootnoteGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183B16F62C80B32200BA6A10 /* FootnoteGroup.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 */; };
1855EC662BAABF2A002ACAC2 /* BreadcrumbItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */; };
1859B30F2CBF6FEB0031CD70 /* ListUnordered.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1859B30E2CBF6FDD0031CD70 /* ListUnordered.swift */; };
1859B31B2CBFA0180031CD70 /* ListUnorderedItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1859B31A2CBFA0180031CD70 /* ListUnorderedItemModel.swift */; };
186D13CB2BBA8B1500986B53 /* DropdownSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */; };
18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; };
18926F5B2C7616A500C55BF6 /* FootnoteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18926F5A2C7616A500C55BF6 /* FootnoteItem.swift */; };
18926F5D2C7616C600C55BF6 /* FootnoteChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18926F5C2C7616C600C55BF6 /* FootnoteChangeLog.txt */; };
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 */; };
@ -26,6 +36,8 @@
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 */; };
18C0F9462C98175900E1DD71 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C0F9452C98175900E1DD71 /* Modal.swift */; };
18C0F94A2C9817C100E1DD71 /* ModalChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18C0F9492C9817C100E1DD71 /* ModalChangeLog.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 */; };
@ -59,6 +71,8 @@
EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0D1C402A6AD61C00E5C127 /* Typography+Additional.swift */; };
EA0D1C452A6AD73000E5C127 /* RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0D1C442A6AD73000E5C127 /* RawRepresentable.swift */; };
EA0FC2C62914222900DF80B4 /* ButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0FC2C52914222900DF80B4 /* ButtonGroup.swift */; };
EA225FC72CA4845100B6B3B3 /* LanguageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA225FC62CA4845100B6B3B3 /* LanguageManager.swift */; };
EA225FC92CA4932900B6B3B3 /* Typography+StyleProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA225FC82CA4932900B6B3B3 /* Typography+StyleProvider.swift */; };
EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA297A5429FB07760031ED56 /* TooltipLabelAttribute.swift */; };
EA297A5729FB0A360031ED56 /* AppleGuidelinesTouchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA297A5629FB0A360031ED56 /* AppleGuidelinesTouchable.swift */; };
EA2DC9B02BE175BA004F58C5 /* RequiredRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA2DC9AF2BE175BA004F58C5 /* RequiredRule.swift */; };
@ -215,7 +229,13 @@
180636C82C29B0DF00C92D86 /* InputStepperLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = InputStepperLog.txt; sourceTree = "<group>"; };
1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = "<group>"; };
1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = "<group>"; };
1818D04C2C9BD2170053E73C /* ModalDialogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalDialogViewController.swift; sourceTree = "<group>"; };
1818D04E2C9BD3F60053E73C /* ModalDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalDialog.swift; sourceTree = "<group>"; };
1818D0502C9BD4090053E73C /* ModalLaunchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalLaunchable.swift; sourceTree = "<group>"; };
1818D0522C9BD47C0053E73C /* ModalModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalModel.swift; sourceTree = "<group>"; };
1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = "<group>"; };
183B16F22C78CF7C00BA6A10 /* CarouselSlotCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselSlotCell.swift; sourceTree = "<group>"; };
183B16F62C80B32200BA6A10 /* FootnoteGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FootnoteGroup.swift; sourceTree = "<group>"; };
184023442C61E7AD00A412C8 /* PriceLockup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceLockup.swift; sourceTree = "<group>"; };
184023462C61E7EC00A412C8 /* PriceLockupChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = PriceLockupChangeLog.txt; sourceTree = "<group>"; };
1842B1DE2BECE28B0021AFCA /* CalendarDateViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarDateViewCell.swift; sourceTree = "<group>"; };
@ -223,10 +243,15 @@
1842B1E22BECF0A10021AFCA /* CalendarFooterReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarFooterReusableView.swift; sourceTree = "<group>"; };
18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = BreadcrumbsChangeLog.txt; sourceTree = "<group>"; };
1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItemModel.swift; sourceTree = "<group>"; };
1859B30E2CBF6FDD0031CD70 /* ListUnordered.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListUnordered.swift; sourceTree = "<group>"; };
1859B3122CBF70AB0031CD70 /* ListUnordered.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ListUnordered.txt; sourceTree = "<group>"; };
1859B31A2CBFA0180031CD70 /* ListUnorderedItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListUnorderedItemModel.swift; sourceTree = "<group>"; };
186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = "<group>"; };
186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownSelect.swift; sourceTree = "<group>"; };
186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = DropdownSelectChangeLog.txt; sourceTree = "<group>"; };
18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = "<group>"; };
18926F5A2C7616A500C55BF6 /* FootnoteItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FootnoteItem.swift; sourceTree = "<group>"; };
18926F5C2C7616C600C55BF6 /* FootnoteChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = FootnoteChangeLog.txt; sourceTree = "<group>"; };
18A3F1292BD9298900498E4A /* Calendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Calendar.swift; sourceTree = "<group>"; };
18A65A012B96E848006602CC /* Breadcrumbs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Breadcrumbs.swift; sourceTree = "<group>"; };
18A65A032B96F050006602CC /* BreadcrumbItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItem.swift; sourceTree = "<group>"; };
@ -236,6 +261,8 @@
18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = "<group>"; };
18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselPaginationModel.swift; sourceTree = "<group>"; };
18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = "<group>"; };
18C0F9452C98175900E1DD71 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = "<group>"; };
18C0F9492C9817C100E1DD71 /* ModalChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ModalChangeLog.txt; sourceTree = "<group>"; };
18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarIndicatorModel.swift; sourceTree = "<group>"; };
18FEA1B42BE0E63600A56439 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = "<group>"; };
18FEA1B82BE1301700A56439 /* CalendarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CalendarChangeLog.txt; sourceTree = "<group>"; };
@ -250,7 +277,7 @@
44CCF4942C0493A1005C9C5E /* TableChangeLog.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TableChangeLog.txt; sourceTree = "<group>"; };
5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = "<group>"; };
5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
710607942B91A99500F2863F /* TitleletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TitleletChangeLog.txt; sourceTree = "<group>"; };
710607942B91A99500F2863F /* TileletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileletChangeLog.txt; sourceTree = "<group>"; };
7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = "<group>"; };
71ACE89B2BA0451200FB6ADC /* PaginationContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationContainer.swift; sourceTree = "<group>"; };
71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TiletEyebrowModel.swift; sourceTree = "<group>"; };
@ -278,6 +305,8 @@
EA0D1C442A6AD73000E5C127 /* RawRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawRepresentable.swift; sourceTree = "<group>"; };
EA0FC2C52914222900DF80B4 /* ButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroup.swift; sourceTree = "<group>"; };
EA21C5DA2B600EDD00CFC139 /* VDSTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSTokens.xcframework; path = ../SharedFrameworks/VDSTokens.xcframework; sourceTree = "<group>"; };
EA225FC62CA4845100B6B3B3 /* LanguageManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageManager.swift; sourceTree = "<group>"; };
EA225FC82CA4932900B6B3B3 /* Typography+StyleProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Typography+StyleProvider.swift"; sourceTree = "<group>"; };
EA297A5429FB07760031ED56 /* TooltipLabelAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipLabelAttribute.swift; sourceTree = "<group>"; };
EA297A5629FB0A360031ED56 /* AppleGuidelinesTouchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleGuidelinesTouchable.swift; sourceTree = "<group>"; };
EA2DC9AF2BE175BA004F58C5 /* RequiredRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequiredRule.swift; sourceTree = "<group>"; };
@ -484,6 +513,16 @@
path = PriceLockup;
sourceTree = "<group>";
};
1859B30D2CBF6EF80031CD70 /* ListUnordered */ = {
isa = PBXGroup;
children = (
1859B30E2CBF6FDD0031CD70 /* ListUnordered.swift */,
1859B31A2CBFA0180031CD70 /* ListUnorderedItemModel.swift */,
1859B3122CBF70AB0031CD70 /* ListUnordered.txt */,
);
path = ListUnordered;
sourceTree = "<group>";
};
186D13C92BBA8A3500986B53 /* DropdownSelect */ = {
isa = PBXGroup;
children = (
@ -494,6 +533,16 @@
path = DropdownSelect;
sourceTree = "<group>";
};
18926F592C76168300C55BF6 /* Footnote */ = {
isa = PBXGroup;
children = (
18926F5A2C7616A500C55BF6 /* FootnoteItem.swift */,
183B16F62C80B32200BA6A10 /* FootnoteGroup.swift */,
18926F5C2C7616C600C55BF6 /* FootnoteChangeLog.txt */,
);
path = Footnote;
sourceTree = "<group>";
};
18A3F1202BD8F5DE00498E4A /* Calendar */ = {
isa = PBXGroup;
children = (
@ -524,6 +573,7 @@
isa = PBXGroup;
children = (
18AE874F2C06FDA60075F181 /* Carousel.swift */,
183B16F22C78CF7C00BA6A10 /* CarouselSlotCell.swift */,
18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */,
18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */,
18AE87532C06FE610075F181 /* CarouselChangeLog.txt */,
@ -531,6 +581,19 @@
path = Carousel;
sourceTree = "<group>";
};
18C0F9442C980CE500E1DD71 /* Modal */ = {
isa = PBXGroup;
children = (
18C0F9452C98175900E1DD71 /* Modal.swift */,
1818D04E2C9BD3F60053E73C /* ModalDialog.swift */,
1818D04C2C9BD2170053E73C /* ModalDialogViewController.swift */,
1818D0502C9BD4090053E73C /* ModalLaunchable.swift */,
1818D0522C9BD47C0053E73C /* ModalModel.swift */,
18C0F9492C9817C100E1DD71 /* ModalChangeLog.txt */,
);
path = Modal;
sourceTree = "<group>";
};
440B84C82BD8E0CE004A732A /* Table */ = {
isa = PBXGroup;
children = (
@ -706,11 +769,14 @@
EAF7F092289985E200B287F5 /* Checkbox */,
EAC58C1F2BF127F000BA39FA /* DatePicker */,
186D13C92BBA8A3500986B53 /* DropdownSelect */,
18926F592C76168300C55BF6 /* Footnote */,
EA985BF3296C609E00F2FF2E /* Icon */,
180636C52C29B06200C92D86 /* InputStepper */,
EA3362412892EF700071C351 /* Label */,
44604AD529CE195300E62B51 /* Line */,
1859B30D2CBF6EF80031CD70 /* ListUnordered */,
EAD0688C2A55F801002E3A2D /* Loader */,
18C0F9442C980CE500E1DD71 /* Modal */,
445BA07629C07ABA0036A7C5 /* Notification */,
71B23C2B2B91FA510027F7D9 /* Pagination */,
184023432C61E78D00A412C8 /* PriceLockup */,
@ -794,12 +860,13 @@
EA3361B4288B2A360071C351 /* Classes */ = {
isa = PBXGroup;
children = (
EAF2F4752C231EAA007BFEDC /* AccessibilityActionElement.swift */,
EAF2F4882C2A1075007BFEDC /* AlertViewController.swift */,
EA985C1C296CD13600F2FF2E /* BundleManager.swift */,
EAC58C282BF4118C00BA39FA /* ClearPopoverViewController.swift */,
EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */,
EA225FC62CA4845100B6B3B3 /* LanguageManager.swift */,
EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */,
EAF2F4752C231EAA007BFEDC /* AccessibilityActionElement.swift */,
EAF2F4882C2A1075007BFEDC /* AlertViewController.swift */,
);
path = Classes;
sourceTree = "<group>";
@ -912,7 +979,7 @@
EA985BE72968951C00F2FF2E /* TileletTitleModel.swift */,
EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */,
EA985C2C296F03FE00F2FF2E /* TileletIconModels.swift */,
710607942B91A99500F2863F /* TitleletChangeLog.txt */,
710607942B91A99500F2863F /* TileletChangeLog.txt */,
);
path = Tilelet;
sourceTree = "<group>";
@ -967,6 +1034,7 @@
EA0D1C3C2A6AD57600E5C127 /* Typography+Enums.swift */,
EA0D1C382A6AD4DF00E5C127 /* Typography+SpacingConfig.swift */,
EA0D1C3A2A6AD51B00E5C127 /* Typogprahy+Styles.swift */,
EA225FC82CA4932900B6B3B3 /* Typography+StyleProvider.swift */,
);
path = Typography;
sourceTree = "<group>";
@ -1212,12 +1280,14 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
18926F5D2C7616C600C55BF6 /* FootnoteChangeLog.txt in Resources */,
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 */,
180636C92C29B0DF00C92D86 /* InputStepperLog.txt in Resources */,
18C0F94A2C9817C100E1DD71 /* ModalChangeLog.txt in Resources */,
EAA5EEB928ECD24B003B3210 /* Icons.xcassets in Resources */,
EAA5EEE428F5B855003B3210 /* VerizonNHGDS-Light.otf in Resources */,
);
@ -1276,6 +1346,7 @@
EA3361C328902D960071C351 /* Toggle.swift in Sources */,
EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */,
EAC58C232BF2824200BA39FA /* DatePicker.swift in Sources */,
183B16F72C80B32200BA6A10 /* FootnoteGroup.swift in Sources */,
EA89201328B568D8006B9984 /* RadioBoxItem.swift in Sources */,
71FC86E42B9841AC00700965 /* PaginationFlowLayout.swift in Sources */,
EAC9258C2911C9DE00091998 /* InputField.swift in Sources */,
@ -1292,6 +1363,7 @@
EA6642952BCEBF9500D81DC4 /* TextLinkModel.swift in Sources */,
71FC86E22B97483000700965 /* Clamping.swift in Sources */,
EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */,
1859B30F2CBF6FEB0031CD70 /* ListUnordered.swift in Sources */,
1855EC662BAABF2A002ACAC2 /* BreadcrumbItemModel.swift in Sources */,
EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */,
EA33622E2891EA3C0071C351 /* DispatchQueue+Once.swift in Sources */,
@ -1302,10 +1374,12 @@
EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */,
EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */,
EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */,
18C0F9462C98175900E1DD71 /* Modal.swift in Sources */,
EAC925842911C63100091998 /* Colorable.swift in Sources */,
18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */,
EAF2F4762C231EAA007BFEDC /* AccessibilityActionElement.swift in Sources */,
EAC58BFD2BE935C300BA39FA /* TitleLockupTextColor.swift in Sources */,
18926F5B2C7616A500C55BF6 /* FootnoteItem.swift in Sources */,
EAACB89A2B927108006A3869 /* Valuing.swift in Sources */,
EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */,
EAF193422C134F3400C68D18 /* Table.swift in Sources */,
@ -1318,6 +1392,7 @@
EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */,
71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */,
EAF7F11728A1475A00B287F5 /* RadioButtonItem.swift in Sources */,
1818D0512C9BD4090053E73C /* ModalLaunchable.swift in Sources */,
EA985BEE2968A92400F2FF2E /* TitleLockupSubTitleModel.swift in Sources */,
EA2DC9B22BE175E6004F58C5 /* CharacterCountRule.swift in Sources */,
EA985BF22968B5BB00F2FF2E /* TitleLockupTextStyle.swift in Sources */,
@ -1347,6 +1422,7 @@
44BD43B62C04866600644F87 /* TableRowModel.swift in Sources */,
71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */,
EABFEB642A26473700C4C106 /* NSAttributedString.swift in Sources */,
EA225FC72CA4845100B6B3B3 /* LanguageManager.swift in Sources */,
EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */,
EA0FC2C62914222900DF80B4 /* ButtonGroup.swift in Sources */,
EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */,
@ -1355,6 +1431,7 @@
EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */,
18B42AC62C09D197008D6262 /* CarouselSlotAlignmentModel.swift in Sources */,
71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */,
1859B31B2CBFA0180031CD70 /* ListUnorderedItemModel.swift in Sources */,
EA0D1C372A681CCE00E5C127 /* ToggleView.swift in Sources */,
EAF7F0B9289C139800B287F5 /* ColorConfiguration.swift in Sources */,
EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */,
@ -1369,6 +1446,7 @@
EAF2F4892C2A1075007BFEDC /* AlertViewController.swift in Sources */,
EA0D1C3D2A6AD57600E5C127 /* Typography+Enums.swift in Sources */,
EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */,
1818D04F2C9BD3F60053E73C /* ModalDialog.swift in Sources */,
EAC58C0C2BED01D500BA39FA /* Telephone.swift in Sources */,
EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */,
EA8E40912A7D3F6300934ED3 /* UIView+Accessibility.swift in Sources */,
@ -1383,6 +1461,7 @@
44604AD729CE196600E62B51 /* Line.swift in Sources */,
1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */,
EAF978212A99035B00C2FEA9 /* Enabling.swift in Sources */,
EA225FC92CA4932900B6B3B3 /* Typography+StyleProvider.swift in Sources */,
EAC58C062BED000200BA39FA /* CreditCard.swift in Sources */,
EA5E3058295105A40082B959 /* Tilelet.swift in Sources */,
186D13CB2BBA8B1500986B53 /* DropdownSelect.swift in Sources */,
@ -1400,6 +1479,7 @@
EA985BF02968A93600F2FF2E /* TitleLockupEyebrowModel.swift in Sources */,
EA5E30532950DDA60082B959 /* TitleLockup.swift in Sources */,
EAD062B02A3B873E0015965D /* BadgeIndicator.swift in Sources */,
183B16F32C78CF7C00BA6A10 /* CarouselSlotCell.swift in Sources */,
44A952DD2BE3DA820009F874 /* TableFlowLayout.swift in Sources */,
EAA5EEB528ECBFB4003B3210 /* ImageLabelAttribute.swift in Sources */,
18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */,
@ -1419,6 +1499,7 @@
EAB5FED429267EB300998C17 /* UIView+NSLayoutConstraint.swift in Sources */,
EAB2376829E9992800AABE9A /* TooltipAlertViewController.swift in Sources */,
EA33623E2892EE950071C351 /* UIDevice.swift in Sources */,
1818D04D2C9BD2170053E73C /* ModalDialogViewController.swift in Sources */,
EA985C692971B90B00F2FF2E /* IconSize.swift in Sources */,
71FC86E02B973AE500700965 /* DropShadowConfiguration.swift in Sources */,
EA3362302891EB4A0071C351 /* Font.swift in Sources */,
@ -1427,6 +1508,7 @@
EAB5FEF12927F4AA00998C17 /* SelfSizingCollectionView.swift in Sources */,
184023452C61E7AD00A412C8 /* PriceLockup.swift in Sources */,
EA3361B8288B2AAA0071C351 /* ViewProtocol.swift in Sources */,
1818D0532C9BD47C0053E73C /* ModalModel.swift in Sources */,
EA3361A8288B23300071C351 /* UIColor.swift in Sources */,
EA2DC9B42BE2C6FE004F58C5 /* TextField.swift in Sources */,
EAC58C182BED0E2300BA39FA /* SecurityCode.swift in Sources */,
@ -1589,7 +1671,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 72;
CURRENT_PROJECT_VERSION = 75;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
@ -1627,7 +1709,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 72;
CURRENT_PROJECT_VERSION = 75;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;

View File

@ -10,7 +10,6 @@ import UIKit
import Combine
/// Base Class use to build Controls.
@objcMembers
@objc(VDSControl)
open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
//--------------------------------------------------

View File

@ -28,7 +28,6 @@ public protocol SelectorControlable: Control, Changeable {
}
/// Base Class used to build out a Selector control.
@objcMembers
@objc(VDSSelectorBase)
open class SelectorBase: Control, SelectorControlable, ParentViewProtocol {
@ -87,9 +86,6 @@ open class SelectorBase: Control, SelectorControlable, ParentViewProtocol {
open var selectorColorConfiguration = ControlColorConfiguration() { didSet { setNeedsUpdate() } }
/// The natural size for the receiving view, considering only properties of the view itself.
open override var intrinsicContentSize: CGSize { size }
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
@ -109,6 +105,16 @@ open class SelectorBase: Control, SelectorControlable, ParentViewProtocol {
super.setup()
isAccessibilityElement = true
accessibilityTraits = .button
let layoutGuide = UILayoutGuide()
addLayoutGuide(layoutGuide)
layoutGuide
.pinTop(0)
.pinLeading(0)
.pinTrailing(0, .defaultHigh)
.pinBottom(0, .defaultHigh)
.width(size.width)
.height(size.height)
}
open override func setDefaults() {

View File

@ -43,13 +43,14 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
private var mainStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.alignment = .fill
$0.axis = .vertical
}
private var selectorStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.alignment = .fill
$0.axis = .horizontal
}
@ -171,10 +172,16 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
isAccessibilityElement = false
addSubview(mainStackView)
mainStackView.isUserInteractionEnabled = false
//wrap the selectorView in a view that won't stretch it
//do this by not pinning the bottom
let selectorViewWrapper = UIView().with { $0.translatesAutoresizingMaskIntoConstraints = false }
selectorViewWrapper.addSubview(selectorView)
selectorView.pinTop().pinLeading().pinTrailing().pinBottomLessThanOrEqualTo()
mainStackView.isUserInteractionEnabled = false
mainStackView.addArrangedSubview(selectorStackView)
mainStackView.addArrangedSubview(errorLabel)
selectorStackView.addArrangedSubview(selectorView)
selectorStackView.addArrangedSubview(selectorViewWrapper)
selectorStackView.addArrangedSubview(selectorLabelStackView)
selectorLabelStackView.addArrangedSubview(label)
selectorLabelStackView.addArrangedSubview(childLabel)

View File

@ -10,7 +10,6 @@ import UIKit
import Combine
/// Base Class used to build Views.
@objcMembers
@objc(VDSView)
open class View: UIView, ViewProtocol, UserInfoable, Clickable {

View File

@ -10,7 +10,6 @@ import UIKit
import Combine
import VDSCoreTokens
@objcMembers
@objc(VDSAlertViewController)
open class AlertViewController: UIViewController, Surfaceable {

View File

@ -8,7 +8,6 @@
import Foundation
import UIKit
@objcMembers
@objc(VDSClearPopoverViewController)
open class ClearPopoverViewController: UIViewController, UIPopoverPresentationControllerDelegate {

View File

@ -0,0 +1,63 @@
//
// LanguageManager.swift
// VDS
//
// Created by Matt Bruce on 9/25/24.
//
import Foundation
// Language Manager to control the current language setting
public class LanguageManager {
// Enum to define supported languages
public enum SupportedLanguage: String, CustomStringConvertible {
case english = "en"
case spanish = "es"
public var description: String { self == .english ? "English" : "Spanish"}
}
// Private static variable to hold the in-memory current language
private static var _currentLanguage: SupportedLanguage? {
didSet {
TextStyle.Provider.updateCurrentStyles()
}
}
// Static property to manage the current language setting
public static var currentLanguage: SupportedLanguage {
get {
// Check if there is an in-memory language setting
guard let _currentLanguage else {
// set default
var deviceCurrentLanguage: SupportedLanguage = .english
// Check device's preferred language
let deviceLanguage = Locale.preferredLanguages.first ?? "en"
if deviceLanguage.starts(with: "es") {
deviceCurrentLanguage = .spanish
}
_currentLanguage = deviceCurrentLanguage
return deviceCurrentLanguage
}
return _currentLanguage
}
set {
// Set the in-memory language
_currentLanguage = newValue
}
}
// Method to set language using a language code string
public static func setLanguage(with code: String) {
if code.starts(with: "es") {
_currentLanguage = .spanish
} else {
_currentLanguage = .english
}
}
}

View File

@ -15,7 +15,6 @@ 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, ParentViewProtocol {
@ -38,8 +37,16 @@ open class Badge: View, ParentViewProtocol {
// MARK: - Enums
//--------------------------------------------------
/// Enum used to describe the primary color for the view.
public enum FillColor: String, CaseIterable {
public enum FillColor: Equatable {
case red, yellow, green, orange, blue, black, white
case token(UIColor.VDSColor)
case custom(UIColor)
private var reflectedValue: String { String(reflecting: self) }
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.reflectedValue == rhs.reflectedValue
}
}
//--------------------------------------------------
@ -61,10 +68,12 @@ open class Badge: View, ParentViewProtocol {
/// This will render the badges fill color based on the available options.
/// When used in conjunction with the surface prop, this fill color will change its tint automatically based on a light or dark surface.
open var fillColor: FillColor = .red { didSet { setNeedsUpdate() }}
/// The text that will be shown in the label.
open var text: String = "" { didSet { setNeedsUpdate() }}
open var textColor: TextColor? { didSet { setNeedsUpdate() }}
/// When applied, this property takes a px value that will restrict the width at that point.
open var maxWidth: CGFloat? { didSet { setNeedsUpdate() }}
@ -93,38 +102,91 @@ open class Badge: View, ParentViewProtocol {
right: VDSLayout.space1X)
/// ColorConfiguration that is mapped to the 'fillColor' for the surface.
private var backgroundColorConfiguration: AnyColorable = {
let config = KeyedColorConfiguration<Badge, FillColor>(keyPath: \.fillColor)
config.setSurfaceColors(VDSColor.badgesBackgroundRedOnlight, VDSColor.badgesBackgroundRedOndark, forKey: .red)
config.setSurfaceColors(VDSColor.badgesBackgroundYellowOnlight, VDSColor.badgesBackgroundYellowOndark, forKey: .yellow)
config.setSurfaceColors(VDSColor.badgesBackgroundGreenOnlight, VDSColor.badgesBackgroundGreenOndark, forKey: .green)
config.setSurfaceColors(VDSColor.badgesBackgroundOrangeOnlight, VDSColor.badgesBackgroundOrangeOndark, forKey: .orange)
config.setSurfaceColors(VDSColor.badgesBackgroundBlueOnlight, VDSColor.badgesBackgroundBlueOndark, forKey: .blue)
config.setSurfaceColors(VDSColor.badgesBackgroundBlackOnlight, VDSColor.badgesBackgroundBlackOndark, forKey: .black)
config.setSurfaceColors(VDSColor.badgesBackgroundWhiteOnlight, VDSColor.badgesBackgroundWhiteOndark, forKey: .white)
return config.eraseToAnyColorable()
}()
private var backgroundColorConfiguration = SurfaceColorConfiguration()
/// ColorConfiguration for the Text.
private var textColorConfiguration = ViewColorConfiguration()
/// Updates the textColorConfiguration based on the fillColor.
public func updateTextColorConfig() {
public func updateColorConfig() {
var config = backgroundColorConfiguration
switch fillColor {
case .red:
config.lightColor = VDSColor.badgesBackgroundRedOnlight
config.darkColor = VDSColor.badgesBackgroundRedOndark
case .yellow:
config.lightColor = VDSColor.badgesBackgroundYellowOnlight
config.darkColor = VDSColor.badgesBackgroundYellowOndark
case .green:
config.lightColor = VDSColor.badgesBackgroundGreenOnlight
config.darkColor = VDSColor.badgesBackgroundGreenOndark
case .orange:
config.lightColor = VDSColor.badgesBackgroundOrangeOnlight
config.darkColor = VDSColor.badgesBackgroundOrangeOndark
case .blue:
config.lightColor = VDSColor.badgesBackgroundBlueOnlight
config.darkColor = VDSColor.badgesBackgroundBlueOndark
case .black:
config.lightColor = VDSColor.badgesBackgroundBlackOnlight
config.darkColor = VDSColor.badgesBackgroundBlackOndark
case .white:
config.lightColor = VDSColor.badgesBackgroundWhiteOnlight
config.darkColor = VDSColor.badgesBackgroundWhiteOndark
case .token(let color):
config.lightColor = color.uiColor
config.darkColor = color.uiColor
case .custom(let color):
config.lightColor = color
config.darkColor = color
}
textColorConfiguration.reset()
switch fillColor {
case .red, .black:
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: false)
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: true)
case .yellow, .white:
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forDisabled: false)
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forDisabled: true)
case .orange, .green, .blue:
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forDisabled: false)
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forDisabled: true)
func update(for color: UIColor) {
if let configuration = textColor?.configuration {
textColorConfiguration = configuration
} else {
if color.isDark() {
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: false)
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: true)
} else {
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forDisabled: false)
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forDisabled: true)
}
}
}
if let textColor {
switch textColor {
case .token(let color):
textColorConfiguration.setSurfaceColors(color.uiColor, color.uiColor, forDisabled: false)
textColorConfiguration.setSurfaceColors(color.uiColor, color.uiColor, forDisabled: true)
case .custom(let color):
textColorConfiguration.setSurfaceColors(color, color, forDisabled: false)
textColorConfiguration.setSurfaceColors(color, color, forDisabled: true)
}
} else {
switch fillColor {
case .red, .black:
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: false)
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: true)
case .yellow, .white:
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forDisabled: false)
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forDisabled: true)
case .orange, .green, .blue:
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forDisabled: false)
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forDisabled: true)
case .token(let color):
update(for: color.uiColor)
case .custom(let color):
update(for: color)
}
}
}
@ -179,7 +241,7 @@ open class Badge: View, ParentViewProtocol {
open override func updateView() {
super.updateView()
updateTextColorConfig()
updateColorConfig()
updateMaxWidth()
backgroundColor = backgroundColorConfiguration.getColor(self)
@ -191,3 +253,29 @@ open class Badge: View, ParentViewProtocol {
label.isEnabled = isEnabled
}
}
extension Badge{
public enum TextColor: Equatable {
case token(UIColor.VDSColor)
case custom(UIColor)
private var reflectedValue: String { String(reflecting: self) }
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.reflectedValue == rhs.reflectedValue
}
public var configuration: ViewColorConfiguration {
let config = ViewColorConfiguration()
switch self {
case .token(let color):
config.setSurfaceColors(color.uiColor, color.uiColor, forDisabled: true)
config.setSurfaceColors(color.uiColor, color.uiColor, forDisabled: false)
case .custom(let color):
config.setSurfaceColors(color, color, forDisabled: true)
config.setSurfaceColors(color, color, forDisabled: false)
}
return config
}
}
}

View File

@ -11,7 +11,6 @@ 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, ParentViewProtocol {

View File

@ -13,7 +13,6 @@ 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 {

View File

@ -13,7 +13,6 @@ 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, ParentViewProtocol {

View File

@ -15,7 +15,6 @@ 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 {

View File

@ -11,7 +11,6 @@ import VDSCoreTokens
import Combine
/// Base class used for UIButton type classes.
@objcMembers
@objc(VDSButtonBase)
open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {

View File

@ -11,7 +11,6 @@ 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 {

View File

@ -16,7 +16,6 @@ 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 {
//--------------------------------------------------

View File

@ -16,7 +16,6 @@ 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 {
//--------------------------------------------------

View File

@ -11,7 +11,6 @@ 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 {

View File

@ -12,7 +12,6 @@ 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 {
@ -154,27 +153,38 @@ open class Carousel: View {
$0.backgroundColor = .clear
}
private var scrollView = UIScrollView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.backgroundColor = .clear
}
private lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)
collectionView.isScrollEnabled = true
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.delegate = self
collectionView.dataSource = self
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
collectionView.backgroundColor = .clear
collectionView.register(CarouselSlotCell.self,
forCellWithReuseIdentifier: CarouselSlotCell.identifier)
return collectionView
}()
/// Previous button to show previous slide.
private var previousButton = ButtonIcon().with {
$0.kind = .lowContrast
$0.iconName = .leftCaret
$0.iconName = .paginationLeftCaret
$0.iconOffset = .init(x: -2, y: 0)
$0.customContainerSize = UIDevice.isIPad ? 40 : 28
$0.icon.customSize = UIDevice.isIPad ? 16 : 12
$0.customIconSize = UIDevice.isIPad ? 16 : 12
}
/// Next button to show next slide.
private var nextButton = ButtonIcon().with {
$0.kind = .lowContrast
$0.iconName = .rightCaret
$0.iconName = .paginationRightCaret
$0.iconOffset = .init(x: 2, y: 0)
$0.customContainerSize = UIDevice.isIPad ? 40 : 28
$0.icon.customSize = UIDevice.isIPad ? 16 : 12
$0.customIconSize = UIDevice.isIPad ? 16 : 12
}
/// A publisher for when moving the carousel. Passes parameters selectedGroupIndex (position).
@ -215,9 +225,9 @@ open class Carousel: View {
containerView.addSubview(contentStackView)
// Add scrollview
scrollContainerView.addSubview(scrollView)
scrollView.pinToSuperView()
scrollContainerView.addSubview(collectionView)
collectionView.pinToSuperView()
// Add pagination button icons
scrollContainerView.addSubview(previousButton)
previousButton
@ -259,14 +269,25 @@ open class Carousel: View {
/// Used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
updateScrollbar()
updateCarousel()
collectionView.collectionViewLayout.invalidateLayout()
collectionView.reloadData()
}
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------
private func updateScrollbar() {
carouselScrollBar.numberOfSlides = views.count
carouselScrollBar.layout = layout
if (carouselScrollBar.position == 0 || carouselScrollBar.position > carouselScrollBar.numberOfSlides) {
carouselScrollBar.position = 1
}
carouselScrollBar.isHidden = (totalPositions() <= 1) ? true : false
}
private func updateCarousel() {
// Mobile/Tablet layouts without peek - must show pagination controls.
// If peek is none, pagination controls should show. So set to persistent.
if peek == .none {
@ -284,12 +305,9 @@ open class Carousel: View {
}
updatePaginationControls()
addCarouselSlots()
updateContainerHeight()
}
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------
private func addlisteners() {
nextButton.onClick = { _ in self.nextButtonClick() }
previousButton.onClick = { _ in self.previousButtonClick() }
@ -365,47 +383,13 @@ open class Carousel: View {
return height
}
// Add carousel slots and load data if any
private func addCarouselSlots() {
// update carousel size and load data if any
private func updateContainerHeight() {
getSlotWidth()
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()
}
// Add carousel items
if views.count > 0 {
var xPos = 0.0
for index in 0...views.count - 1 {
// Add Carousel Slot
let carouselSlot = View().with {
$0.clipsToBounds = true
}
scrollView.addSubview(carouselSlot)
scrollView.delegate = self
carouselSlot
.pinTop()
.pinBottom()
.pinLeading(xPos)
.width(minimumSlotWidth)
.height(slotHeight)
xPos = xPos + minimumSlotWidth + gutter.value
let component = views[index]
carouselSlot.addSubview(component)
setSlotAlignment(contentView: component)
}
scrollView.contentSize = CGSize(width: xPos - gutter.value, height: slotHeight)
}
let containerHeight = slotHeight + scrollbarTopSpace + containerSize.height
if carouselScrollBar.isHidden {
containerStackHeightConstraint = contentStackView.heightAnchor.constraint(equalToConstant: slotHeight)
@ -419,43 +403,6 @@ open class Carousel: View {
}
}
// Set slot alignment if provided. Used only when slot content have different heights or widths.
private func setSlotAlignment(contentView: UIView) {
switch slotAlignment?.vertical {
case .top:
contentView
.pinTop()
.pinBottomLessThanOrEqualTo()
case .middle:
contentView
.pinTopGreaterThanOrEqualTo()
.pinBottomLessThanOrEqualTo()
.pinCenterY()
case .bottom:
contentView
.pinTopGreaterThanOrEqualTo()
.pinBottom()
default: break
}
switch slotAlignment?.horizontal {
case .left:
contentView
.pinLeading()
.pinTrailingLessThanOrEqualTo()
case .center:
contentView
.pinLeadingGreaterThanOrEqualTo()
.pinTrailingLessThanOrEqualTo()
.pinCenterX()
case .right:
contentView
.pinLeadingGreaterThanOrEqualTo()
.pinTrailing()
default: break
}
}
// Get the slot width relative to the peak
private func getSlotWidth() {
let actualWidth = containerView.frame.size.width
@ -505,7 +452,7 @@ open class Carousel: View {
}
private func updateScrollbarPosition(targetContentOffsetXPos:CGFloat) {
let scrollContentSizeWidth = scrollView.contentSize.width
let scrollContentSizeWidth = collectionView.contentSize.width
let totalPositions = totalPositions()
let layoutSpace = Int (floor( Double(scrollContentSizeWidth / Double(totalPositions))))
let remindSpace = Int(targetContentOffsetXPos) % layoutSpace
@ -515,10 +462,11 @@ open class Carousel: View {
updateScrollPosition(position: contentPos, callbackText: "ScrollViewMoved")
}
// Update scrollview offset relative to scrollbar thumb position
// Update collectionview offset relative to scrollbar thumb position
private func updateScrollPosition(position: Int, callbackText: String) {
if carouselScrollBar.numberOfSlides > 0 {
let scrollContentSizeWidth = scrollView.contentSize.width
let scrollContentSizeWidth = collectionView.contentSize.width
let totalPositions = totalPositions()
var xPos = 0.0
if position == 1 {
@ -536,8 +484,8 @@ open class Carousel: View {
}
}
carouselScrollBar.scrubberId = position+1
let yPos = scrollView.contentOffset.y
scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true)
let yPos = collectionView.contentOffset.y
collectionView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true)
showPaginationControls()
groupIndex = position-1
onChangePublisher.send(groupIndex)
@ -557,5 +505,35 @@ extension Carousel: UIScrollViewDelegate {
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
updateScrollbarPosition(targetContentOffsetXPos: targetContentOffset.pointee.x)
}
}
extension Carousel: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
//--------------------------------------------------
// MARK: - UICollectionView Delegate & Datasource
//--------------------------------------------------
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
views.count
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CarouselSlotCell.identifier, for: indexPath) as? CarouselSlotCell else { return UICollectionViewCell() }
let component = views[indexPath.row]
cell.update(with: component, slotAlignment: slotAlignment, surface: surface)
cell.layoutIfNeeded()
//component.setNeedsLayout()
if hasDebugBorder {
cell.addDebugBorder()
} else {
cell.removeDebugBorder()
}
return cell
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return gutter.value
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: minimumSlotWidth, height: fetchCarouselHeight())
}
}

View File

@ -0,0 +1,85 @@
//
// CarouselSlotCell.swift
// VDS
//
// Created by Kanamarlapudi, Vasavi on 23/08/24.
//
import Foundation
import UIKit
final class CarouselSlotCell: UICollectionViewCell {
///Identifier for the Calendar Date Cell.
static let identifier: String = String(describing: CarouselSlotCell.self)
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
override init(frame: CGRect) {
super.init(frame: frame)
setUp()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUp()
}
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------
/// Configuring the cell with default setup.
private func setUp() {
isAccessibilityElement = true
}
/// Updating UI based on data along with surface.
func update(with component: UIView, slotAlignment: Carousel.CarouselSlotAlignmentModel?, surface: Surface) {
contentView.subviews.forEach { $0.removeFromSuperview() }
contentView.addSubview(component)
if var surfacedView = component as? Surfaceable {
surfacedView.surface = surface
}
setSlotAlignment(alignment: slotAlignment, contentView: component)
}
// Set slot alignment if provided. Used only when slot content have different heights or widths.
private func setSlotAlignment(alignment: Carousel.CarouselSlotAlignmentModel?, contentView: UIView) {
switch alignment?.vertical {
case .top:
contentView
.pinTop()
.pinBottomLessThanOrEqualTo()
case .middle:
contentView
.pinTopGreaterThanOrEqualTo()
.pinBottomLessThanOrEqualTo()
.pinCenterY()
case .bottom:
contentView
.pinTopGreaterThanOrEqualTo()
.pinBottom()
default: break
}
switch alignment?.horizontal {
case .left:
contentView
.pinLeading()
.pinTrailingLessThanOrEqualTo()
case .center:
contentView
.pinLeadingGreaterThanOrEqualTo()
.pinTrailingLessThanOrEqualTo()
.pinCenterX()
case .right:
contentView
.pinLeadingGreaterThanOrEqualTo()
.pinTrailing()
default: break
}
}
}

View File

@ -12,7 +12,6 @@ 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 {

View File

@ -12,7 +12,6 @@ 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 {

View File

@ -12,7 +12,6 @@ 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<CheckboxItem>, SelectorGroupMultiSelect {

View File

@ -9,7 +9,6 @@ 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<Checkbox> {

View File

@ -4,7 +4,6 @@ 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<String> {
//--------------------------------------------------
@ -183,6 +182,15 @@ open class DatePicker: EntryFieldBase<String> {
showPopover()
}
}
containerView.accessibilityTraits = [.button]
containerView.bridge_accessibilityHintBlock = { [weak self] in
guard let self else { return "" }
return isReadOnly || !isEnabled
? ""
: isCalendarShowing ? "Expanded, Double tap to close" : "Collapsed, \(accessibilityHintText)"
}
}
open override func getFieldContainer() -> UIView {

View File

@ -11,7 +11,6 @@ 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<String> {
//--------------------------------------------------
@ -61,8 +60,9 @@ open class DropdownSelect: EntryFieldBase<String> {
internal var minWidthInlineLabel = 102.0
internal override var minWidth: CGFloat { showInlineLabel ? minWidthInlineLabel : minWidthDefault }
internal override var maxWidth: CGFloat {
let frameWidth = frame.size.width
return helperTextPlacement == .right ? (frameWidth - horizontalStackView.spacing) / 2 : frameWidth
let frameWidth = constrainedWidth
let halfWidth = (frameWidth - horizontalStackView.spacing) / 2
return helperTextPlacement == .right && halfWidth > minWidth * 2 ? halfWidth : frameWidth
}
/// The is used for the for adding the helperLabel to the right of the containerView.

View File

@ -0,0 +1,17 @@
MM/DD/YYYY
----------------
Initial Brand 3.0 handoff
12/18/2023
----------------
- New
12/28/2023
----------------
- hideSymbol updated to showSymbol and default set to True.
- Figma-only properties section added in Footnote Item Configurations section.
01/16/2024
----------------
- hideSymbol reverted to hideSymbol and default set to False.
- Figma-only properties section removed.

View File

@ -0,0 +1,165 @@
//
// FootnoteGroup.swift
// VDS
//
// Created by Kanamarlapudi, Vasavi on 29/08/24.
//
import Foundation
import UIKit
import VDSCoreTokens
/// This must always be paired with one or more ``Footnote`` in a FootnoteGroup.
@objc(VDSFootnoteGroup)
open class FootnoteGroup: 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
//--------------------------------------------------
/// 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
//--------------------------------------------------
/// Array of ``Footnote`` for the Footnote items.
open var footnoteItems: [FootnoteItem] = [] { didSet { updateFootnoteItems() } }
/// Any percentage or pixel value and cannot exceed container size.
/// If there is a width that is larger than container size, the footnote will resize to container's width.
open var width: Width? {
get { _width }
set {
if let newValue {
switch newValue {
case .percentage(let percentage):
if percentage <= 100.0 {
_width = newValue
}
case .value(let value):
if value > 0 {
_width = newValue
}
}
} else {
_width = nil
}
updateContainerWidth()
}
}
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var _width: Width? = nil
private lazy var stackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
$0.distribution = .fill
$0.spacing = VDSLayout.space3X
$0.backgroundColor = .clear
}
//--------------------------------------------------
// MARK: - Configuration Properties
//--------------------------------------------------
internal var maxWidth: CGFloat { constrainedWidth }
internal var minWidth: CGFloat { containerSize.width }
internal var containerSize: CGSize { CGSize(width: 55, height: 44) }
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
internal var widthConstraint: NSLayoutConstraint?
//--------------------------------------------------
// 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()
// add footnote item stackview.
addSubview(stackView)
stackView.pinToSuperView()
widthConstraint = widthAnchor.constraint(equalToConstant: 0).deactivate()
}
open override func setDefaults() {
super.setDefaults()
width = nil
footnoteItems = []
}
/// Used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
updateFootnoteItems()
}
internal func updateFootnoteItems() {
// symbol containers are as wide as the widest symbol container in the group.
var symbolMaxWidth = 0.0
footnoteItems.forEach { footnote in
let separatorWidth = Label().with {
$0.text = footnote.symbolType
$0.textStyle = footnote.symbolLabel.textStyle
$0.sizeToFit()
}.intrinsicContentSize.width
symbolMaxWidth = max(separatorWidth, symbolMaxWidth)
}
stackView.removeArrangedSubviews()
// add symbol label, text label to stack.
footnoteItems.forEach { footnote in
footnote.symbolWidth = symbolMaxWidth
footnote.surface = surface
stackView.addArrangedSubview(footnote)
}
}
/// Update container width after updating content.
internal func updateContainerWidth() {
var newWidth = 0.0
switch width {
case .percentage(let percentage):
newWidth = max(maxWidth * ((percentage) / 100), minWidth)
case .value(let value):
newWidth = value > maxWidth ? maxWidth : value
case nil: break
}
widthConstraint?.deactivate()
if newWidth > minWidth && newWidth < maxWidth {
widthConstraint?.constant = newWidth
widthConstraint?.activate()
}
}
}

View File

@ -0,0 +1,256 @@
//
// Footnote.swift
// VDS
//
// Created by Kanamarlapudi, Vasavi on 21/08/24.
//
import Foundation
import UIKit
import VDSCoreTokens
/// A footnote is text that provides supporting details, legal copy and links to related content.
/// It exists at the bottom or "foot" of a page or section.
@objc(VDSFootnoteItem)
open class FootnoteItem: 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
//--------------------------------------------------
/// Enum used to describe the kind of component.
public enum Kind: String, DefaultValuing, CaseIterable {
case primary, secondary
/// The default kind is 'primary'.
public static var defaultValue : Self { .secondary }
/// Color configuation to Symbol and Text relative to kind.
public var colorConfiguration: SurfaceColorConfiguration {
switch self {
case .primary:
return SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
case .secondary:
return SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark)
}
}
}
/// Enum that represents the size availble for component.
public enum Size: String, DefaultValuing, CaseIterable {
case micro
case small
case large
public static var defaultValue: Self { .micro }
/// TextStyle relative to Size.
public var textStyle: TextStyle.StandardStyle {
switch self {
case .micro:
return .micro
case .small:
return .bodySmall
case .large:
return .bodyLarge
}
}
}
/// 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
//--------------------------------------------------
/// Color to the component. The default kind is Secondary.
open var kind: Kind = .defaultValue { didSet { setNeedsUpdate() } }
/// Size of the component. The default size is Micro.
open var size: Size = .defaultValue { didSet { setNeedsUpdate() } }
/// If hideSymbol true, the component will show text without symbol.
open var hideSymbol: Bool = false { didSet { setNeedsUpdate() } }
/// symbol type will be shown for the footnote item. The default symbolType is 'asterisk'.
open var symbolType: String = "*" { didSet { setNeedsUpdate() } }
/// Text of the footnote item.
open var text: String? { didSet { setNeedsUpdate() } }
open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } }
/// Any percentage or pixel value and cannot exceed container size.
/// If there is a width that is larger than container size, the footnote will resize to container's width.
open var width: Width? {
get { _width }
set {
if let newValue {
switch newValue {
case .percentage(let percentage):
if percentage <= 100.0 {
_width = newValue
}
case .value(let value):
if value > 0 {
_width = newValue
}
}
} else {
_width = nil
}
updateContainerWidth()
}
}
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var _width: Width? = nil
/// To set the widest symbol width from the symbol container in the group.
internal var symbolWidth: CGFloat? { didSet { setNeedsUpdate() } }
private lazy var itemStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .horizontal
$0.alignment = .leading
$0.distribution = .fill
$0.spacing = VDSLayout.space1X
$0.backgroundColor = .clear
}
internal var symbolLabel = Label().with {
$0.isAccessibilityElement = true
$0.numberOfLines = 1
$0.sizeToFit()
}
internal var textLabel = Label().with {
$0.isAccessibilityElement = true
$0.lineBreakMode = .byWordWrapping
}
//--------------------------------------------------
// MARK: - Configuration Properties
//--------------------------------------------------
internal var maxWidth: CGFloat { constrainedWidth }
internal var minWidth: CGFloat { containerSize.width }
internal var containerSize: CGSize { CGSize(width: 45, height: 44) }
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
internal var symbolWidthConstraint: NSLayoutConstraint?
internal var itemWidthConstraint: NSLayoutConstraint?
//--------------------------------------------------
// 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()
// add footnote item stackview.
addSubview(itemStackView)
itemStackView.pinToSuperView()
// width constraints
itemWidthConstraint = widthAnchor.constraint(equalToConstant: 0).deactivate()
// add symbol label, text label to stack.
itemStackView.addArrangedSubview(symbolLabel)
itemStackView.addArrangedSubview(textLabel)
itemStackView.setCustomSpacing(VDSLayout.space1X, after: symbolLabel)
symbolWidthConstraint = symbolLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 0)
symbolWidthConstraint?.isActive = true
}
open override func setDefaults() {
super.setDefaults()
hideSymbol = false
text = nil
tooltipModel = nil
width = nil
}
/// Resets to default settings.
open override func reset() {
symbolLabel.reset()
textLabel.reset()
super.reset()
}
/// Used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
// Update symbolLabel
symbolLabel.text = symbolType
symbolLabel.isHidden = symbolType.isEmpty ? true : hideSymbol
symbolLabel.textColor = kind.colorConfiguration.getColor(self)
symbolLabel.textStyle = size.textStyle.regular
symbolLabel.surface = surface
//Set width to the symbol label
if let symbolWidth, symbolWidth > 0 {
// Set the widest symbol width from the symbol container in the group.
symbolWidthConstraint?.constant = symbolWidth
} else {
symbolWidthConstraint?.constant = symbolLabel.intrinsicContentSize.width
}
// Update textLabel
textLabel.text = text
textLabel.textColor = kind.colorConfiguration.getColor(self)
textLabel.textStyle = size.textStyle.regular
textLabel.surface = surface
// Set the textLabel attributes
if let tooltipModel {
var attributes: [any LabelAttributeModel] = []
attributes.append(TooltipLabelAttribute(surface: surface, model: tooltipModel, presenter: self))
textLabel.attributes = attributes
}
}
/// Update container width after updating content.
internal func updateContainerWidth() {
var newWidth = 0.0
switch width {
case .percentage(let percentage):
newWidth = max(maxWidth * ((percentage) / 100), minWidth)
case .value(let value):
newWidth = value > maxWidth ? maxWidth : value
case nil:
break
}
itemWidthConstraint?.deactivate()
if newWidth > minWidth && newWidth < maxWidth {
itemWidthConstraint?.constant = newWidth
itemWidthConstraint?.activate()
}
}
}

View File

@ -13,7 +13,6 @@ 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 {

View File

@ -11,7 +11,6 @@ import VDSCoreTokens
import Combine
/// A stepper is a two-segment control that people use to increase or decrease an incremental value.'
@objcMembers
@objc(VDSInputStepper)
open class InputStepper: EntryFieldBase<Int> {
@ -331,9 +330,7 @@ open class InputStepper: EntryFieldBase<Int> {
stepperWidthConstraint?.deactivate()
widthConstraint?.deactivate()
trailingLessThanEqualsConstraint?.deactivate()
trailingEqualsConstraint?.deactivate()
var widthConstraintConstant: CGFloat?
if let widthPercentage, let superWidth = horizontalPinnedWidth() {
@ -350,9 +347,6 @@ open class InputStepper: EntryFieldBase<Int> {
if let widthConstraintConstant {
widthConstraint?.constant = widthConstraintConstant
widthConstraint?.activate()
trailingLessThanEqualsConstraint?.activate()
} else {
trailingEqualsConstraint?.activate()
}
// Update Edge insets if size changes applied.

View File

@ -39,6 +39,27 @@ public extension String {
func isValid(range: NSRange) -> Bool {
range.location >= 0 && range.length > 0 && range.location + range.length <= count
}
func index(from: Int) -> Index {
return self.index(startIndex, offsetBy: from)
}
func substring(from: Int) -> String {
let fromIndex = index(from: from)
return String(self[fromIndex...])
}
func substring(to: Int) -> String {
let toIndex = index(from: to)
return String(self[..<toIndex])
}
func substring(with r: Range<Int>) -> String {
let startIndex = index(from: r.lowerBound)
let endIndex = index(from: r.upperBound)
return String(self[startIndex..<endIndex])
}
}
public extension NSAttributedString {

View File

@ -63,18 +63,15 @@ public struct TextStyleLabelAttribute: LabelAttributeModel {
//set lineHeight
if textStyle.lineHeight > 0.0 {
let lineHeight = textStyle.lineHeight
let adjustment = lineHeight > textStyle.font.lineHeight ? 2.0 : 1.0
let baselineOffset = (lineHeight - textStyle.font.lineHeight) / 2.0 / adjustment
let lineHeight = textStyle.lineHeight + abs(textStyle.edgeInsets.bottom) + abs(textStyle.edgeInsets.top)
let paragraph = NSMutableParagraphStyle().with {
$0.maximumLineHeight = lineHeight
$0.maximumLineHeight = lineHeight
$0.minimumLineHeight = lineHeight
$0.alignment = textPosition.value
$0.lineBreakMode = lineBreakMode
}
attributedString.removeAttribute(.baselineOffset, range: range)
attributedString.removeAttribute(.paragraphStyle, range: range)
attributedString.addAttribute(.baselineOffset, value: baselineOffset, range: range)
attributedString.addAttribute(.paragraphStyle, value: paragraph, range: range)
} else if textPosition != .left {
@ -87,3 +84,4 @@ public struct TextStyleLabelAttribute: LabelAttributeModel {
}
}
}

View File

@ -12,7 +12,6 @@ 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 {

View File

@ -10,7 +10,6 @@ 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 {

View File

@ -0,0 +1,239 @@
//
// ListUnordered.swift
// VDS
//
// Created by Vasavi Kanamarlapudi on 16/10/24.
//
import Foundation
import UIKit
import VDSCoreTokens
/// List unordered breaks up related content into distinct phrases or sentences, which improves scannability.
/// This component should be used when the text items dont need to be in numeric order.
@objcMembers
@objc(VDSListUnordered)
open class ListUnordered: 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
//--------------------------------------------------
/// Enum that represents the size availble for the component.
public enum Size: String, DefaultValuing, CaseIterable {
case large
case medium
case small
case micro
public static var defaultValue: Self { .large }
/// TextStyle relative to Size.
public var textStyle: TextStyle.StandardStyle {
switch self {
case .large:
return .bodyLarge
case .medium:
return .bodyMedium
case .small:
return .bodySmall
case .micro:
return .micro
}
}
}
/// Enum that represents the type of spacing available for the component.
public enum Spacing: String, CaseIterable {
case standard, compact
}
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
/// Size of the component. The default size is Large.
open var size: Size = .defaultValue { didSet { setNeedsUpdate() } }
/// Spacing type of the component.
open var spacing: Spacing = .standard { didSet { setNeedsUpdate() } }
/// Lead-in text that shows as the top text for the component. This is optional.
open var leadInText: String? = nil { didSet { setNeedsUpdate() } }
/// Array of unordered list items to show for the component.
open var unorderedList: [ListUnorderedItemModel] = [] { didSet { setNeedsUpdate() }}
//--------------------------------------------------
// MARK: - Configuration Properties
//--------------------------------------------------
// It can be used for Glyph level 1.
private var disc = ""
// It can be used for Glyph Level 2.
private var endash = ""
// Spacing between the list items.
private var spaceBetweenItems: CGFloat {
switch (size, spacing) {
case (.large, .standard):
return VDSLayout.space4X
case (.medium, .standard), (.small, .standard), (.micro, .standard):
return VDSLayout.space3X
case (.large, .compact):
return VDSLayout.space2X
case (.medium, .compact), (.small, .compact), (.micro, .compact):
return VDSLayout.space1X
}
}
// Padding that can be used in an item between the glyph and the item text.
private var padding: CGFloat {
switch (size, spacing) {
case (.large, .standard), (.large, .compact):
return VDSLayout.space3X
case (.medium, .standard), (.small, .standard), (.micro, .standard), (.medium, .compact), (.small, .compact), (.micro, .compact):
return VDSLayout.space2X
}
}
private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private lazy var listStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
$0.distribution = .fill
$0.spacing = spaceBetweenItems
$0.backgroundColor = .clear
}
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
/// Called once when a view is initialized and is used to Setup additional UI or other constants and config.texturations.
open override func setup() {
super.setup()
// add stackview
addSubview(listStackView)
listStackView.heightGreaterThanEqualTo(constant:0)
listStackView.pinToSuperView()
}
open override func setDefaults() {
super.setDefaults()
leadInText = nil
unorderedList = []
}
/// Resets to default settings.
open override func reset() {
super.reset()
}
/// Used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
listStackView.removeArrangedSubviews()
listStackView.subviews.forEach { $0.removeFromSuperview() }
listStackView.spacing = spaceBetweenItems
if leadInText != nil {
let listItem = getListItem(with:self.leadInText, surface: surface)
listStackView.addArrangedSubview(listItem)
}
unorderedList.forEach { item in
let listItem = getListItem(levelOneText: item.levelOneText, surface: surface)
listStackView.addArrangedSubview(listItem)
item.levelTwoText?.forEach { text in
let listItem = getListItem(levelTwoText: text, surface: surface)
listStackView.addArrangedSubview(listItem)
}
}
}
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------
// Get Label with the required text and text formats.
func getLabel(with text: String?, surface: Surface) -> Label {
let textLabel = Label().with {
$0.isAccessibilityElement = true
$0.lineBreakMode = .byWordWrapping
$0.text = text
$0.textStyle = size.textStyle.regular
$0.textColor = textColorConfiguration.getColor(surface)
$0.surface = surface
}
return textLabel
}
// Get the list item with the required text (LeadInText, Level 1 Text, Level 2 Text).
func getListItem(with leadInText:String? = nil, levelOneText: String? = nil, levelTwoText: String? = nil, surface:Surface) -> UIView {
let itemStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .horizontal
$0.alignment = .leading
$0.distribution = .fill
$0.spacing = padding
$0.backgroundColor = .clear
}
// StackView with LeadIntext if provided.
if leadInText != nil {
let leadTextLabel = getLabel(with: leadInText, surface: surface)
itemStackView.addArrangedSubview(leadTextLabel)
}
// StackView with Level 1 Text if provided.
if levelOneText != nil {
// Add level 1 glyph: 'disc, bold'
let discLabel = getLabel(with: disc, surface: surface)
discLabel.widthAnchor.constraint(equalToConstant: discLabel.intrinsicContentSize.width).activate()
itemStackView.addArrangedSubview(discLabel)
// Add level 1 Text
let levelOneLabel = getLabel(with: levelOneText, surface: surface)
itemStackView.addArrangedSubview(levelOneLabel)
}
// StackView with Level 2 Text if provided.
if levelTwoText != nil {
// Set level 2 leading space as needed for alignment.
let discSpaceView = View()
let discLabel = getLabel(with: disc, surface: surface)
discSpaceView.widthAnchor.constraint(equalToConstant: discLabel.intrinsicContentSize.width).activate()
itemStackView.addArrangedSubview(discSpaceView)
// Add level 2 glyph: 'en dash, regular'
let endashLabel = getLabel(with: endash, surface: surface)
endashLabel.widthAnchor.constraint(equalToConstant: endashLabel.intrinsicContentSize.width).activate()
itemStackView.addArrangedSubview(endashLabel)
// Add level 2 Text
let levelTwoLabel = getLabel(with: levelTwoText, surface: surface)
itemStackView.addArrangedSubview(levelTwoLabel)
}
return itemStackView
}
}

View File

@ -0,0 +1,41 @@
MM/DD/YYYY
----------------
- Initial Brand 3.0 handoff
05/2/2022
----------------
- Added Body Medium to size configuration
05/5/2022
----------------
- Added Spacing configuration (Standard, Compact) Web handoff
08/2/2022
----------------
- Included a VDS Note about the Spacing prop naming rationale
08/10/2022
----------------
- Updated default and inverted prop to light and dark surface.
12/13/2022
----------------
- Replaced focus border pixel and style & spacing values with tokens.
01/10/2023
----------------
- Removed from Anatomy section: “List item text”
- Updated “Glyph level 1” to “List Item Level 1”
- Updated “Glyph level 2” to “List Item Level 2”
- Updated image markers to reflect changes
02/02/2023
----------------
- Reduced left padding for all Level 2 sizes so that the Glyph aligns with the text in Level 1.
- Added dashed line on all sizes to indicate Level 2 alignment under Level 1.
- Changed “endash” to “endash, regular” under Size section.
- Updated all Level 1 and Level 2 glyph widths to “Hug”
12/26/23
----------------
- Deleted Decisions log

View File

@ -0,0 +1,23 @@
//
// ListUnorderedItemModel.swift
// VDS
//
// Created by Vasavi Kanamarlapudi on 16/10/24.
//
import Foundation
extension ListUnordered {
public struct ListUnorderedItemModel: Equatable {
/// Item Level 1 that shows text with glyph - disc, bold.
public var levelOneText: String
/// Item Level 2 that shows text (one or many) with glyph - en dash. This is optional.
public var levelTwoText: [String?]?
public init(itemLevelOneText: String, itemLevelTwoTexts: [String?]? = nil) {
self.levelOneText = itemLevelOneText
self.levelTwoText = itemLevelTwoTexts
}
}
}

View File

@ -11,7 +11,6 @@ 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 {

View File

@ -10,7 +10,6 @@ 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 {
//--------------------------------------------------

View File

@ -0,0 +1,149 @@
//
// Modal.swift
// VDS
//
// Created by Kanamarlapudi, Vasavi on 05/09/24.
//
import Foundation
import UIKit
import VDSCoreTokens
import Combine
/// A Modal is an overlay that interrupts the user flow to force the customer to provide information or a response.
/// After the customer interacts with the modal, they can return to the parent content.
@objc(VDSModal)
open class Modal: Control, ModalLaunchable {
//--------------------------------------------------
// 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: - Private Properties
//--------------------------------------------------
internal var showModalButton = Button().with {
$0.use = .primary
$0.text = "Show Modal"
}
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
/// Text rendered for the title of the modal
open var title: String? { didSet { setNeedsUpdate() } }
/// Text rendered for the content of the modal
open var content: String? { didSet { setNeedsUpdate() } }
/// UIView rendered for the content area of the modal
open var contentView: UIView? { didSet { setNeedsUpdate() } }
/// Array of Buttonable Views that are shown as Modal Footer. Primary and Close button data for modal button group.
open var buttonData: [ButtonBase]? { didSet { setNeedsUpdate() } }
/// If provided, the Modal has the option to be displayed at full screen.
open var fullScreenDialog: Bool = false { didSet { setNeedsUpdate() } }
/// If provided, close button can not be present.
open var hideCloseButton: Bool = false { didSet { setNeedsUpdate() } }
//--------------------------------------------------
// 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(showModalButton)
showModalButton.pinToSuperView()
backgroundColor = .clear
isAccessibilityElement = true
accessibilityTraits = .button
}
open override func setDefaults() {
super.setDefaults()
title = nil
content = nil
contentView = nil
buttonData = nil
fullScreenDialog = false
hideCloseButton = false
showModalButton.onClick = { _ in self.showModalButtonClick() }
bridge_accessibilityLabelBlock = { [weak self] in
guard let self else { return "" }
var label = title
if label == nil {
label = content
}
if let label, !label.isEmpty {
return label
} else {
return "Modal"
}
}
bridge_accessibilityHintBlock = { [weak self] in
guard let self else { return "" }
return isEnabled ? "Double tap to open." : ""
}
}
internal func showModalButtonClick() {
self.presentModal(surface: self.surface,
modalModel: .init(closeButtonText: showModalButton.text ?? "",
title: title,
content: content,
contentView: contentView,
buttonData: buttonData,
fullScreenDialog: fullScreenDialog,
hideCloseButton: hideCloseButton),
presenter: self)
}
/// Used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
showModalButton.surface = surface
}
public static func accessibleText(for title: String?, content: String?, closeButtonText: String) -> String {
var label = ""
if let title {
label = title
}
if let content {
if !label.isEmpty {
label += ","
}
label += content
}
return label
}
}
// MARK: AppleGuidelinesTouchable
extension Modal: AppleGuidelinesTouchable {
/// Overrides to ensure that the touch point meets a minimum of the minimumTappableArea.
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
Self.acceptablyOutsideBounds(point: point, bounds: bounds)
}
}

View File

@ -0,0 +1,67 @@
MM/DD/YYYY
----------------
- Initial Brand 3.0 handoff
12/17/2021
----------------
- Replaced focusring colors (previously interactive/onlight/ondark) with accessibility/onlight/ondark colors
- Updated focus border name (previously interactive.focusring.onlight) with focusring.onlight/ondark
12/31/2021
----------------
- Updated Hover and Active state trigger specs. If triggered by mouse, Active same as Hover. If not, Active same as Default.
03/01/2022
----------------
- Replaced Close Non-Scaling icon with VDS Icon.
- Removed “vector effect” from Anatomy.
- Removed “weight” from Configurations.
08/10/2022
----------------
- Updated default and inverted prop to light and dark surface.
- Noted that button is optional within anatomy
09/06/2022
----------------
- Updated Anatomy element names to remove the word “Modal” from text elements, updated Button to be Button Group,
and noted Button Group as optional across all visuals within Anatomy.
11/30/2022
----------------
- Added "(web only)" to any instance of "keyboard focus"
12/13/2022
----------------
- Replaced focus border pixel and style & spacing values with tokens.
04/24/2023
----------------
- Updated all instances of Close Button (VDS Icon) with VDS Button Icon (size small)
- Button Icon placed 8px from top/right edge
- Use the Ghost variant of Button Icon
- Added Button Icon props to Elements spec
10/17/2023
----------------
- Added component tokens table
- Applied component tokens to light, dark surface configurations
11/22/2023
----------------
- Updated tab/desk visuals to reflect new corner radius value - 12px
- Updated border radius value in Anatomy
11/27/2023
----------------
- Updated border radius” to “corner radius” in Anatomy
12/1/2023
----------------
- Applied palette tokens instead of hardcoded values where component tokens included an opacity
- Removed layer opacity annotation for instances where opacity is built into a component token
07/18/2024
----------------
- Added Scrollbar hit area with z-index specifications to the Behaviors page
- Decreased the height of the Grab zone to equal the height of the scrollbar thumb on the Behaviors page

View File

@ -0,0 +1,237 @@
//
// ModalDialog.swift
// VDS
//
// Created by Kanamarlapudi, Vasavi on 09/09/24.
//
import Foundation
import UIKit
import VDSCoreTokens
@objc(VDSModalDialog)
open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
required public init() {
super.init(frame: .zero)
}
public override init(frame: CGRect) {
super.init(frame: .zero)
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
}
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [closeCrossButton, titleLabel, contentLabel, buttonGroupData] }
open var modalModel = Modal.ModalModel() { didSet { setNeedsUpdate() } }
open var titleLabel = Label().with { label in
label.isAccessibilityElement = true
label.textStyle = .boldTitleLarge
}
open var contentLabel = Label().with { label in
label.isAccessibilityElement = true
label.textStyle = .bodyLarge
}
open lazy var closeCrossButton = ButtonIcon().with {
$0.kind = .ghost
$0.surfaceType = .colorFill
$0.iconName = .close
$0.size = .small
$0.customContainerSize = UIDevice.isIPad ? 48 : 48
$0.customIconSize = UIDevice.isIPad ? 32 : 32
}
open var buttonGroupData = ButtonGroup().with {
$0.alignment = .left
}
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var scrollView = UIScrollView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.backgroundColor = .clear
}
private var contentStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
$0.alignment = .leading
$0.distribution = .fillProportionally
$0.spacing = 0
}
lazy var primaryAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self).with {
$0.accessibilityLabel = "Modal"
}
// close button with the 48 x 48 px
private var closeCrossButtonSize = 48.0
private let containerViewInset = UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space4X
private let contentLabelTopSpace = UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space6X
private let contentLabelBottomSpace = UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space12X
private let gapBetweenButtonItems = VDSLayout.space3X
//--------------------------------------------------
// MARK: - Configuration Properties
//--------------------------------------------------
private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark)
private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
private var contentStackViewBottomConstraint: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
open override func setup() {
super.setup()
titleLabel.accessibilityTraits = .header
layer.cornerRadius = 12
// Add titleLabel, contentLabel to contentStack.
contentStackView.addArrangedSubview(titleLabel)
contentStackView.addArrangedSubview(contentLabel)
contentStackView.setCustomSpacing(contentLabelTopSpace, after: titleLabel)
scrollView.addSubview(contentStackView)
// Add crossButon, scrollView, buttonsData.
addSubview(closeCrossButton)
addSubview(scrollView)
addSubview(buttonGroupData)
self.bringSubviewToFront(closeCrossButton)
let crossTopSpace = UIDevice.isIPad && !modalModel.fullScreenDialog ? 0 : VDSLayout.space12X
let scrollTopSpace = UIDevice.isIPad && !modalModel.fullScreenDialog ? containerViewInset : (crossTopSpace + closeCrossButtonSize)
let contentTrailingSpace = UIDevice.isIPad ? (containerViewInset/2) - 6 : containerViewInset
// Activate constraints
NSLayoutConstraint.activate([
// Constraints for the closeCrossButton
closeCrossButton.topAnchor.constraint(equalTo: topAnchor, constant: crossTopSpace),
closeCrossButton.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor),
closeCrossButton.trailingAnchor.constraint(equalTo: trailingAnchor),
closeCrossButton.heightAnchor.constraint(equalToConstant: closeCrossButtonSize),
// Constraints for the bottom button view
buttonGroupData.leadingAnchor.constraint(equalTo: leadingAnchor, constant:containerViewInset),
buttonGroupData.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -containerViewInset),
buttonGroupData.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -containerViewInset),
// Constraints for the scrollView
scrollView.topAnchor.constraint(equalTo: topAnchor, constant: scrollTopSpace),
scrollView.leadingAnchor.constraint(equalTo: leadingAnchor, constant:containerViewInset),
scrollView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -contentTrailingSpace),
scrollView.bottomAnchor.constraint(equalTo: buttonGroupData.topAnchor, constant: -contentLabelBottomSpace),
// Constraints for the contentStackView
contentStackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentStackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
contentStackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -contentTrailingSpace),
contentStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -contentTrailingSpace),
contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
])
contentStackViewBottomConstraint = contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
contentStackViewBottomConstraint?.activate()
}
/// Used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
// Update surface and background
backgroundColor = backgroundColorConfiguration.getColor(self)
scrollView.indicatorStyle = surface == .light ? .black : .white
closeCrossButton.surface = surface
buttonGroupData.surface = surface
titleLabel.surface = surface
contentLabel.surface = surface
// Re-arrange contentStack
contentStackView.removeArrangedSubviews()
titleLabel.text = modalModel.title
contentLabel.text = modalModel.content
titleLabel.textColor = textColorConfiguration.getColor(self)
contentLabel.textColor = textColorConfiguration.getColor(self)
titleLabel.sizeToFit()
contentLabel.sizeToFit()
// Add buttons data if provided
if let buttons = modalModel.buttonData, buttons.count > 0 {
buttonGroupData.buttons = buttons
let percent = UIDevice.isIPad ? 50.0 : 100.0
buttonGroupData.rowQuantityTablet = 2
buttonGroupData.rowQuantityPhone = 1
buttonGroupData.childWidth = .percentage(percent)
}
// Update title, content and contentview
var addedTitle = false
if let titleText = modalModel.title, !titleText.isEmpty {
contentStackView.addArrangedSubview(titleLabel)
addedTitle = true
}
var addedContent = false
if let contentText = modalModel.content, !contentText.isEmpty {
contentStackView.addArrangedSubview(contentLabel)
addedContent = true
} else if let contentView = modalModel.contentView {
contentView.translatesAutoresizingMaskIntoConstraints = false
if var surfaceable = contentView as? Surfaceable {
surfaceable.surface = surface
}
contentStackView.addArrangedSubview(contentView)
addedContent = true
}
if addedTitle && addedContent {
contentStackView.spacing = contentLabelTopSpace
}
closeCrossButton.isHidden = modalModel.hideCloseButton
contentStackView.setNeedsLayout()
contentStackView.layoutIfNeeded()
scrollView.setNeedsLayout()
scrollView.layoutIfNeeded()
}
/// Used to update any Accessibility properties.
open override func updateAccessibility() {
super.updateAccessibility()
primaryAccessibilityElement.accessibilityHint = "Double tap on the cross button to close."
primaryAccessibilityElement.accessibilityFrameInContainerSpace = .init(origin: .zero, size: frame.size)
}
open override var accessibilityElements: [Any]? {
get {
var elements: [Any] = [primaryAccessibilityElement]
contentStackView.arrangedSubviews.forEach{ elements.append($0) }
elements.append(buttonGroupData)
return elements
}
set {}
}
}

View File

@ -0,0 +1,123 @@
//
// ModalDialogViewController.swift
// VDS
//
// Created by Kanamarlapudi, Vasavi on 09/09/24.
//
import Foundation
import UIKit
import Combine
import VDSCoreTokens
@objc(VDSModalDialogViewController)
open class ModalDialogViewController: UIViewController, Surfaceable {
/// Set of Subscribers for any Publishers for this Control.
open var subscribers = Set<AnyCancellable>()
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var onClickSubscriber: AnyCancellable? {
willSet {
if let onClickSubscriber {
onClickSubscriber.cancel()
}
}
}
private let modalDialog = ModalDialog()
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
/// Current Surface and this is used to pass down to child objects that implement Surfacable
open var surface: Surface = .light { didSet { updateView() }}
open var modalModel = Modal.ModalModel() { didSet { updateView() }}
open var presenter: UIView? { didSet { updateView() }}
//--------------------------------------------------
// MARK: - Configuration
//--------------------------------------------------
private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite)
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
open override func viewDidLoad() {
super.viewDidLoad()
isModalInPresentation = true
setup()
}
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIAccessibility.post(notification: .screenChanged, argument: modalDialog)
}
private func dismiss() {
dismiss(animated: true) { [weak self] in
guard let self, let presenter else { return }
UIAccessibility.post(notification: .layoutChanged, argument: presenter)
}
}
open func setup() {
view.accessibilityElements = [modalDialog]
//left-right swipe
view.publisher(for: UISwipeGestureRecognizer().with{ $0.direction = .right })
.sink { [weak self] swipe in
guard let self, !UIAccessibility.isVoiceOverRunning else { return }
self.dismiss()
}.store(in: &subscribers)
//tapping in background
view.publisher(for: UITapGestureRecognizer().with{ $0.numberOfTapsRequired = 1 })
.sink { [weak self] swipe in
guard let self, !UIAccessibility.isVoiceOverRunning else { return }
self.dismiss()
}.store(in: &subscribers)
//clicking button
onClickSubscriber = modalDialog.closeCrossButton.publisher(for: .touchUpInside)
.sink {[weak self] button in
guard let self else { return }
self.dismiss()
}
view.addSubview(modalDialog)
}
/// Used to make changes to the View based off a change events or from local properties.
open func updateView() {
modalDialog.surface = surface
modalDialog.modalModel = modalModel
// Activate constraints
modalDialog.removeConstraints()
let isFullScreen = UIDevice.isIPad && !modalModel.fullScreenDialog ? false : true
if isFullScreen {
view.backgroundColor = modalDialog.backgroundColor
modalDialog
.pinLeading()
.pinTrailing()
modalDialog.pinTop(anchor: UIDevice.isIPad ? view.safeAreaLayoutGuide.topAnchor : view.topAnchor)
modalDialog.pinBottom(UIDevice.isIPad ? view.bottomAnchor : view.safeAreaLayoutGuide.bottomAnchor)
} else {
view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.8)
NSLayoutConstraint.activate([
// Constraints for the floating modal view for Tablet.
modalDialog.centerXAnchor.constraint(equalTo: view.centerXAnchor),
modalDialog.centerYAnchor.constraint(equalTo: view.centerYAnchor),
modalDialog.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.7),
modalDialog.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.7)
])
}
}
}

View File

@ -0,0 +1,28 @@
//
// ModalLaunchable.swift
// VDS
//
// Created by Kanamarlapudi, Vasavi on 09/09/24.
//
import Foundation
import UIKit
public protocol ModalLaunchable {
func presentModal(surface: Surface, modalModel: Modal.ModalModel, presenter: UIView?)
}
extension ModalLaunchable {
public func presentModal(surface: Surface, modalModel: Modal.ModalModel, presenter: UIView? = nil) {
if let presenting = UIApplication.topViewController() {
let modalViewController = ModalDialogViewController(nibName: nil, bundle: nil).with {
$0.surface = surface
$0.modalModel = modalModel
$0.presenter = presenter
$0.modalPresentationStyle = UIDevice.isIPad && !modalModel.fullScreenDialog ? .overCurrentContext : .fullScreen
$0.modalTransitionStyle = .crossDissolve
}
presenting.present(modalViewController, animated: true)
}
}
}

View File

@ -0,0 +1,45 @@
//
// ModalModel.swift
// VDS
//
// Created by Kanamarlapudi, Vasavi on 09/09/24.
//
import Foundation
import UIKit
extension Modal {
/// Model used to represent the modal.
public struct ModalModel: Equatable {
/// Current Surface and this is used to pass down to child objects that implement Surfacable
public var closeButtonText: String
public var title: String?
public var content: String?
public var contentView: UIView?
public var accessibleText: String?
public var contentViewAlignment: UIStackView.Alignment?
public var buttonData: [ButtonBase]?
public var fullScreenDialog: Bool
public var hideCloseButton: Bool
public init(closeButtonText: String = "Close",
title: String? = nil,
content: String? = nil,
contentView: UIView? = nil,
buttonData: [ButtonBase]? = nil,
fullScreenDialog: Bool = false,
hideCloseButton: Bool = false,
accessibleText: String? = "Modal",
contentViewAlignment: UIStackView.Alignment = .leading) {
self.closeButtonText = closeButtonText
self.title = title
self.content = content
self.contentView = contentView
self.accessibleText = accessibleText
self.contentViewAlignment = contentViewAlignment
self.buttonData = buttonData
self.fullScreenDialog = fullScreenDialog
self.hideCloseButton = hideCloseButton
}
}
}

View File

@ -14,7 +14,6 @@ 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, ParentViewProtocol {

View File

@ -11,13 +11,14 @@ 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 {
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private let pageChangedSubject = PassthroughSubject<Pagination, Never>()
///Maximum component width
private let maxWidth: CGFloat = 288.0
///Collectionview width anchor
@ -52,6 +53,8 @@ open class Pagination: View {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
public var pageChangedPublisher: AnyPublisher<Pagination, Never> { pageChangedSubject.eraseToAnyPublisher() }
///Previous button to select previous page
public let previousButton: PaginationButton = .init(type: .previous)
///Next button to select next page
@ -145,6 +148,11 @@ 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)
pageChangedPublisher.sink { [weak self] control in
guard let self else { return }
onPageDidSelect?(control.selectedPage)
}.store(in: &subscribers)
}
open override func setDefaults() {
@ -195,6 +203,7 @@ open class Pagination: View {
let isNextAction = sender == nextButton
_selectedPageIndex = if isNextAction { _selectedPageIndex + 1 } else { _selectedPageIndex - 1 }
updateSelection()
pageChangedSubject.send(self)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
guard let self else { return }
UIAccessibility.post(notification: .announcement, argument: paginationDescription)
@ -249,7 +258,7 @@ extension Pagination: UICollectionViewDelegate, UICollectionViewDataSource, UICo
guard _selectedPageIndex != indexPath.row else { return }
_selectedPageIndex = indexPath.row
updateSelection()
onPageDidSelect?(selectedPage)
pageChangedSubject.send(self)
}
}

View File

@ -9,7 +9,6 @@ import UIKit
import VDSCoreTokens
///This is customised button for Pagination view
@objcMembers
@objc(PaginationButton)
open class PaginationButton: ButtonBase {
//--------------------------------------------------

View File

@ -9,7 +9,6 @@ import Foundation
import UIKit
import VDSCoreTokens
@objcMembers
@objc(VDSPriceLockup)
open class PriceLockup: View, ParentViewProtocol {
@ -140,6 +139,7 @@ open class PriceLockup: View, ParentViewProtocol {
internal var delimiterIndex = 0
internal var strikethroughLocation = 0
internal var strikethroughLength = 0
internal var strikethroughAccessibilityText: String = "price not offering anymore"
internal var textPosition:TextPosition = .preDelimiter
enum TextPosition: String, CaseIterable {
@ -217,6 +217,24 @@ open class PriceLockup: View, ParentViewProtocol {
open override func setDefaults() {
super.setDefaults()
priceLockupLabel.bridge_accessibilityLabelBlock = { [weak self] in
guard let self else { return "" }
var accessibilityLabels = [String]()
if let text = priceLockupLabel.text, !text.isEmpty {
if strikethrough, strikethroughLength > 0 {
let preText = text.substring(to: strikethroughLocation)
let postText = text.substring(from: strikethroughLocation)
accessibilityLabels.append(preText)
accessibilityLabels.append(strikethroughAccessibilityText)
accessibilityLabels.append(postText)
} else {
accessibilityLabels.append(text)
}
}
return accessibilityLabels.joined(separator: " ")
}
bold = false
hideCurrency = false
leadingText = nil

View File

@ -11,7 +11,6 @@ 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<RadioBoxItem>, SelectorGroupSingleSelect {

View File

@ -12,7 +12,6 @@ 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, ParentViewProtocol {

View File

@ -13,7 +13,6 @@ 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 {

View File

@ -11,7 +11,6 @@ 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<RadioButtonItem>, SelectorGroupSingleSelect {

View File

@ -11,7 +11,6 @@ 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<RadioButton> {

View File

@ -10,7 +10,6 @@ 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 {
@ -27,7 +26,6 @@ open class Table: View {
$0.allowsSelection = false
$0.showsVerticalScrollIndicator = false
$0.showsHorizontalScrollIndicator = false
$0.isAccessibilityElement = true
$0.backgroundColor = .clear
}
@ -148,6 +146,7 @@ extension Table: UICollectionViewDelegate, UICollectionViewDataSource, TableColl
var edgePadding = UIEdgeInsets(top: padding.verticalValue(), left: 0, bottom: padding.verticalValue(), right: padding.horizontalValue())
edgePadding.left = (indexPath.row == 0 && !striped) ? VDSLayout.space1X : padding.horizontalValue()
cell.updateCell(content: currentItem, surface: surface, striped: shouldStrip, padding: edgePadding, isHeader: isHeader)
setAccessibilityForCell(cell: cell, content: currentItem, path: indexPath)
return cell
}
@ -162,4 +161,38 @@ extension Table: UICollectionViewDelegate, UICollectionViewDataSource, TableColl
func collectionView(_ collectionView: UICollectionView, widthForItemAt indexPath: IndexPath) -> CGFloat {
return columnWidths?[indexPath.row] ?? 0.0
}
//--------------------------------------------------
// MARK: - Accessibility
//--------------------------------------------------
/// To set the accessibility label for the each cell based on the criteria. Table name along with total no of column & row information should be passed in the first cell's accessibility label.
private func setAccessibilityForCell(cell: TableCellItem, content: TableItemModel, path: IndexPath) {
var accLabel = content.component?.accessibilityLabel ?? "Empty"
///Set the type of header label
if path.section == 0 {
accLabel.append(", Column Header")
} else if path.row == 0 {
///As per design team, inspite of column 0 may not look like a header, it should be read as header.
accLabel.append(", Row Header")
}
///Set the Row/Column number for each cell
if path.row == 0 {
accLabel.append(", Row \(path.section + 1), Column \(path.row + 1)")
} else {
accLabel.append(", Column \(path.row + 1)")
}
///Set the Row header accessibilityLabel at the end of the non-header cells accessibilityLabel
if path.section != 0,
path.row != 0,
let columnHeaderAccLabel = tableHeader.first?.columns[path.row].component?.accessibilityLabel {
accLabel.append(", \(columnHeaderAccLabel)")
}
cell.accessibilityLabel = accLabel
}
}

View File

@ -45,6 +45,7 @@ final class TableCellItem: UICollectionViewCell {
private func setupCell() {
contentView.backgroundColor = .clear
isAccessibilityElement = true
addSubview(containerView)
containerView.pinToSuperView()

View File

@ -11,8 +11,7 @@ import VDSCoreTokens
import Combine
extension Tabs {
@objcMembers
@objc(VDSTab)
@objc(VDSTab)
open class Tab: Control, Groupable {
//--------------------------------------------------

View File

@ -10,7 +10,6 @@ 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 doesnt need to be compared.
@objcMembers
@objc(VDSTabs)
open class Tabs: View, ParentViewProtocol {

View File

@ -8,7 +8,6 @@
import Foundation
import UIKit
@objcMembers
@objc(VDSTabsContainer)
open class TabsContainer: View {

View File

@ -105,14 +105,12 @@ open class EntryFieldBase<ValueType>: Control, Changeable, FormFieldInternalVali
// MARK: - Constraints
//--------------------------------------------------
internal var widthConstraint: NSLayoutConstraint?
internal var trailingEqualsConstraint: NSLayoutConstraint?
internal var trailingLessThanEqualsConstraint: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Configuration Properties
//--------------------------------------------------
// Sizes are from InVision design specs.
internal var maxWidth: CGFloat { frame.size.width }
internal var maxWidth: CGFloat { constrainedWidth }
internal var minWidth: CGFloat { containerSize.width }
internal var containerSize: CGSize { CGSize(width: minWidth, height: 44) }
@ -258,15 +256,9 @@ open class EntryFieldBase<ValueType>: Control, Changeable, FormFieldInternalVali
let layoutGuide = UILayoutGuide()
addLayoutGuide(layoutGuide)
layoutGuide
.pinTop()
.pinLeading()
.pinBottom()
trailingEqualsConstraint = layoutGuide.pinTrailing(anchor: trailingAnchor)
layoutGuide.pinToSuperView()
// width constraints
trailingLessThanEqualsConstraint = layoutGuide.pinTrailingLessThanOrEqualTo(anchor: trailingAnchor)?.deactivate()
widthConstraint = layoutGuide.widthAnchor.constraint(equalToConstant: 0).deactivate()
// Add mainStackView to the view
@ -549,18 +541,13 @@ open class EntryFieldBase<ValueType>: Control, Changeable, FormFieldInternalVali
containerView.layer.borderWidth = VDSFormControls.borderWidth
containerView.layer.cornerRadius = VDSFormControls.borderRadius
}
internal func updateContainerWidth() {
widthConstraint?.deactivate()
trailingLessThanEqualsConstraint?.deactivate()
trailingEqualsConstraint?.deactivate()
if let width, width >= minWidth, width <= maxWidth {
widthConstraint?.constant = width
widthConstraint?.activate()
trailingLessThanEqualsConstraint?.activate()
} else {
trailingEqualsConstraint?.activate()
}
}

View File

@ -13,7 +13,6 @@ 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<String> {
@ -47,8 +46,9 @@ open class InputField: EntryFieldBase<String> {
internal override var minWidth: CGFloat { fieldType.handler().minWidth }
internal override var maxWidth: CGFloat {
let frameWidth = frame.size.width
return helperTextPlacement == .right ? (frameWidth - horizontalStackView.spacing) / 2 : frameWidth
let frameWidth = constrainedWidth
let halfWidth = (frameWidth - horizontalStackView.spacing) / 2
return helperTextPlacement == .right && halfWidth > minWidth * 2 ? halfWidth : frameWidth
}
/// The is used for the for adding the helperLabel to the right of the containerView.
@ -320,15 +320,12 @@ open class InputField: EntryFieldBase<String> {
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()
}

View File

@ -10,7 +10,6 @@ import UIKit
import Combine
import VDSCoreTokens
@objcMembers
@objc(VDSTextField)
open class TextField: UITextField, ViewProtocol, Errorable {

View File

@ -12,7 +12,6 @@ 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 thats longer than a single line.
@objcMembers
@objc(VDSTextArea)
open class TextArea: EntryFieldBase<String> {
//--------------------------------------------------

View File

@ -10,7 +10,6 @@ import UIKit
import Combine
import VDSCoreTokens
@objcMembers
@objc(VDSTextView)
open class TextView: UITextView, ViewProtocol, Errorable {

View File

@ -10,7 +10,6 @@ import VDSCoreTokens
import UIKit
import Combine
@objcMembers
@objc(VDSTileContainer)
open class TileContainer: TileContainerBase<TileContainer.Padding> {
@ -346,8 +345,15 @@ open class TileContainerBase<PaddingType: DefaultValuing & Valuing>: View where
containerView.setAccessibilityLabel(for: views)
//append all children that are accessible
items.append(contentsOf: elements)
if containerView.isAccessibilityElement {
elements.forEach({ element in
if element.accessibilityTraits.contains(.button) || element.accessibilityTraits.contains(.link) {
items.append(element)
}
})
} else {
items.append(contentsOf: elements)
}
return items
}
set {}
@ -447,33 +453,6 @@ open class TileContainerBase<PaddingType: DefaultValuing & Valuing>: View where
//-------------------------------------------------------------------------
//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
//-------------------------------------------------------------------------
@ -499,6 +478,14 @@ open class TileContainerBase<PaddingType: DefaultValuing & Valuing>: View where
aspectRatioConstraint = widthAnchor.constraint(equalTo: heightAnchor, multiplier: multiplier)
aspectRatioConstraint?.activate()
}
//-------------------------------------------------------------------------
//Multiplier, meaning it was pinned with width only Constraint
//-------------------------------------------------------------------------
else if let multiplier {
aspectRatioConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: multiplier)
aspectRatioConstraint?.activate()
} else {
//-------------------------------------------------------------------------
@ -520,12 +507,6 @@ open class TileContainerBase<PaddingType: DefaultValuing & Valuing>: View where
}
}
}
/// 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 {

View File

@ -15,7 +15,6 @@ 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<Tilelet.Padding>, ParentViewProtocol {
@ -29,9 +28,9 @@ open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
public var value: CGFloat {
switch self {
case .small:
return UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space4X
return UIDevice.isIPad ? VDSLayout.space4X : VDSLayout.space3X
case .large:
return UIDevice.isIPad ? VDSLayout.space4X : VDSLayout.space6X
return UIDevice.isIPad ? VDSLayout.space6X : VDSLayout.space4X
}
}
@ -104,7 +103,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
}
private var backgroundColorSurface: Surface {
backgroundColorConfiguration.getColor(self).surface
backgroundColorConfiguration.getColor(self).isDark() ? .dark : .light
}
//--------------------------------------------------
@ -280,6 +279,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
internal var iconContainerHeightConstraint: NSLayoutConstraint?
internal var titleLockupWidthConstraint: NSLayoutConstraint?
internal var titleLockupTrailingConstraint: NSLayoutConstraint?
internal var titleLockupTopConstraint: NSLayoutConstraint?
@ -328,15 +328,15 @@ open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
iconContainerView.addSubview(descriptiveIcon)
iconContainerView.addSubview(directionalIcon)
iconContainerHeightConstraint = iconContainerView.height(constant: 0)
descriptiveIcon
.pinLeading()
.pinTop()
.pinTopGreaterThanOrEqualTo()
.pinBottom()
directionalIcon
.pinTrailing()
.pinTop()
.pinTopGreaterThanOrEqualTo()
.pinBottom()
badge.bottomAnchor.constraint(equalTo: badge.label.bottomAnchor, constant: 2).activate()
@ -468,6 +468,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
private func updateBadge() {
if let badgeModel {
badge.text = badgeModel.text
badge.textColor = badgeModel.textColor
badge.fillColor = badgeModel.fillColor
badge.numberOfLines = badgeModel.numberOfLines
badge.surface = backgroundColorSurface
@ -558,6 +559,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
descriptiveIcon.color = color
}
descriptiveIcon.size = descriptiveIconModel.size
iconContainerHeightConstraint?.constant = descriptiveIcon.size.dimensions.height
descriptiveIcon.surface = backgroundColorSurface
showIconContainerView = true
}
@ -568,6 +570,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
directionalIcon.color = color
}
directionalIcon.size = directionalIconModel.size.value
iconContainerHeightConstraint?.constant = directionalIcon.size.dimensions.height
directionalIcon.surface = backgroundColorSurface
showIconContainerView = true
}
@ -598,7 +601,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
}
private func updateTextPositionAlignment() {
guard width != nil && (aspectRatio != .none || height != nil) else { return }
guard aspectRatio != .none || height != nil else { return }
switch textPostion {
case .top:
titleLockupTopConstraint?.activate()

View File

@ -15,6 +15,9 @@ extension Tilelet {
/// Text that will be used for the badge.
public var text: String = ""
/// Text color that will be used for the badge.
public var textColor: Badge.TextColor?
/// Fill color that will be used for the badge.
public var fillColor: Badge.FillColor
@ -30,8 +33,9 @@ extension Tilelet {
/// LineBreakMode used in Badge label.
public var lineBreakMode: NSLineBreakMode
public init(text: String, fillColor: Badge.FillColor = .red, surface: Surface = .light, numberOfLines: Int = 0, maxWidth: CGFloat? = nil, lineBreakMode: NSLineBreakMode = .byTruncatingTail) {
public init(text: String, textColor: Badge.TextColor? = nil, fillColor: Badge.FillColor = .red, surface: Surface = .light, numberOfLines: Int = 0, maxWidth: CGFloat? = nil, lineBreakMode: NSLineBreakMode = .byTruncatingTail) {
self.text = text
self.textColor = textColor
self.fillColor = fillColor
self.surface = surface
self.numberOfLines = numberOfLines

View File

@ -12,7 +12,6 @@ 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, ParentViewProtocol {

View File

@ -12,7 +12,6 @@ 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, ParentViewProtocol {
@ -208,8 +207,8 @@ open class Toggle: Control, Changeable, FormFieldable, ParentViewProtocol {
//toggle
toggleConstraints = [
toggleView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor),
toggleView.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor)
toggleView.leadingAnchor.constraint(equalTo: leadingAnchor),
toggleView.trailingAnchor.constraint(equalTo: trailingAnchor)
]
//toggle and label variants

View File

@ -12,7 +12,6 @@ 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 {

View File

@ -13,7 +13,6 @@ 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 {

View File

@ -10,7 +10,6 @@ import UIKit
import Combine
import VDSCoreTokens
@objcMembers
@objc(VDSTooltipAlertViewController)
open class TooltipAlertViewController: UIViewController, Surfaceable {

View File

@ -9,7 +9,6 @@ import Foundation
import UIKit
import VDSCoreTokens
@objcMembers
@objc(VDSTooltipDialog)
open class TooltipDialog: View, UIScrollViewDelegate, ParentViewProtocol {

View File

@ -187,10 +187,4 @@ extension UIColor {
guard let found else { return nil}
return found
}
public var surface: Surface {
var greyScale: CGFloat = 0
getWhite(&greyScale, alpha: nil)
return greyScale < 0.5 ? .dark : .light
}
}

View File

@ -192,4 +192,14 @@ extension UIColor {
}
self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
}
internal func isDark() -> Bool {
var white: CGFloat = 0
var alpha: CGFloat = 0
if getWhite(&white, alpha: &alpha) {
return white < 0.5
}
return false
}
}

View File

@ -647,7 +647,14 @@ public enum LayoutDistribution: String, CaseIterable {
case fillProportionally
}
extension LayoutConstraintable where Self: UIView {
public var constrainedWidth: CGFloat {
horizontalPinnedWidth() ?? (superview?.frame.size.width ?? frame.size.width)
}
}
extension LayoutConstraintable {
public func removeConstraints() {
guard let view = self as? UIView, let superview = view.superview else { return }

View File

@ -1,8 +1,32 @@
1.0.76
----------------
- ONEAPP-11355 - ListUnordered - Finished Development
- CXTDT-630735 - PriceLockup - Accessibility
- CXTDT-626164 - FootnoteGroup - Dark mode
- CXTDT-626180 - FootnoteItem - Symbol type padding
- CXTDT-586383 - Table - Line style can be selected for each row.
1.0.75
----------------
- CXTDT-624895 - Badge - Custom FillColor and TextColor
- CXTDT-578885 - Table - Setting appropriate accessiblity label for each cell.
1.0.74
----------------
- CXTDT-591307 - DatePicker - Accessibility - #1 & #2
- CXTDT-577374 - Label - Spanish - Accent mark displays incorrectly
- CXTDT-595956 - Tilelet - Text Position does not work on Light surface
- CXTDT-595952 - Tilelet - Aspect Ratio Width/height setting
- CXTDT-595970 - Tilelet - Subtitle size options missing
- CXTDT-595965 - Tilelet - Incorrect Mobile Padding
- ONEAPP-10583 - Modal - Finished Development
1.0.73
----------------
- CXTDT-597984 - Table - Text wrap
- CXTDT-586372 - Table - Stripes defect
- CXTDT-586383 - Table - Line style
- CXTDT-603719 - Carousel - Pagination caret icon
1.0.72
----------------

View File

@ -11,209 +11,72 @@ import VDSCoreTokens
//MARK: Definitions
extension TextStyle {
// Static properties for different text styles
public static let boldFeatureXLarge = TextStyle(rawValue: "boldFeatureXLarge",
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature144 : VDSTypography.fontSizeFeature96,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature136 : VDSTypography.lineHeightFeature88,
edgeInsets: .bottom(UIDevice.isIPad ? -6: -4))
public static let featureXLarge = TextStyle(rawValue: "featureXLarge",
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature144 : VDSTypography.fontSizeFeature96,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature136 : VDSTypography.lineHeightFeature88,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -6: -4))
public static let boldFeatureLarge = TextStyle(rawValue: "boldFeatureLarge",
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature128 : VDSTypography.fontSizeFeature80,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature120 : VDSTypography.lineHeightFeature76,
edgeInsets: .bottom(UIDevice.isIPad ? -6: -2))
public static let featureLarge = TextStyle(rawValue: "featureLarge",
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature128 : VDSTypography.fontSizeFeature80,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature120 : VDSTypography.lineHeightFeature76,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -6: -2))
public static let boldFeatureMedium = TextStyle(rawValue: "boldFeatureMedium",
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature96 : VDSTypography.fontSizeFeature64,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature88 : VDSTypography.lineHeightFeature64,
edgeInsets: .bottom(UIDevice.isIPad ? -4: -2))
public static let featureMedium = TextStyle(rawValue: "featureMedium",
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature96 : VDSTypography.fontSizeFeature64,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature88 : VDSTypography.lineHeightFeature64,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -4: -2))
public static let boldFeatureSmall = TextStyle(rawValue: "boldFeatureSmall",
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature80 : VDSTypography.fontSizeFeature48,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature76 : VDSTypography.lineHeightFeature48,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0))
public static let featureSmall = TextStyle(rawValue: "featureSmall",
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature80 : VDSTypography.fontSizeFeature48,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature76 : VDSTypography.lineHeightFeature48,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0))
public static let boldFeatureXSmall = TextStyle(rawValue: "boldFeatureXSmall",
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature64 : VDSTypography.fontSizeFeature40,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature64 : VDSTypography.lineHeightFeature40,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0))
public static let featureXSmall = TextStyle(rawValue: "featureXSmall",
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature64 : VDSTypography.fontSizeFeature40,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature64 : VDSTypography.lineHeightFeature40,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0))
public static let boldTitle2XLarge = TextStyle(rawValue: "boldTitle2XLarge",
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle64 : VDSTypography.fontSizeTitle40,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle64 : VDSTypography.lineHeightTitle40,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0))
public static let title2XLarge = TextStyle(rawValue: "title2XLarge",
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle64 : VDSTypography.fontSizeTitle40,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle64 : VDSTypography.lineHeightTitle40,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0))
public static let boldTitleXLarge = TextStyle(rawValue: "boldTitleXLarge",
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle48 : VDSTypography.fontSizeTitle32,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle48 : VDSTypography.lineHeightTitle36)
public static let titleXLarge = TextStyle(rawValue: "titleXLarge",
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle48 : VDSTypography.fontSizeTitle32,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle48 : VDSTypography.lineHeightTitle36,
letterSpacing: VDSTypography.letterSpacingSemiwide)
public static let boldTitleLarge = TextStyle(rawValue: "boldTitleLarge",
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle32 : VDSTypography.fontSizeTitle24,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle36 : VDSTypography.lineHeightTitle28)
public static let titleLarge = TextStyle(rawValue: "titleLarge",
fontFace: UIDevice.isIPad ? .dsLight : .edsRegular,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle32 : VDSTypography.fontSizeTitle24,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle36 : VDSTypography.lineHeightTitle28,
letterSpacing: UIDevice.isIPad ? VDSTypography.letterSpacingSemiwide : 0)
public static let boldTitleMedium = TextStyle(rawValue: "boldTitleMedium",
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle24 : VDSTypography.fontSizeTitle20,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle28 : VDSTypography.lineHeightTitle24)
public static let titleMedium = TextStyle(rawValue: "titleMedium",
fontFace: .edsRegular,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle24 : VDSTypography.fontSizeTitle20,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle28 : VDSTypography.lineHeightTitle24)
public static let boldTitleSmall = TextStyle(rawValue: "boldTitleSmall",
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle20 : VDSTypography.fontSizeTitle16,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle24 : VDSTypography.lineHeightTitle20)
public static let titleSmall = TextStyle(rawValue: "titleSmall",
fontFace: .edsRegular,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle20 : VDSTypography.fontSizeTitle16,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle24 : VDSTypography.lineHeightTitle20)
public static let boldBodyLarge = TextStyle(rawValue: "boldBodyLarge",
fontFace: .edsBold,
pointSize: VDSTypography.fontSizeBody16,
lineHeight: VDSTypography.lineHeightBody20,
letterSpacing: VDSTypography.letterSpacingWide)
public static let bodyLarge = TextStyle(rawValue: "bodyLarge",
fontFace: .edsRegular,
pointSize: VDSTypography.fontSizeBody16,
lineHeight: VDSTypography.lineHeightBody20,
letterSpacing:VDSTypography.letterSpacingWide)
public static let boldBodyMedium = TextStyle(rawValue: "boldBodyMedium",
fontFace: .edsBold,
pointSize: VDSTypography.fontSizeBody14,
lineHeight: VDSTypography.lineHeightBody18,
letterSpacing: VDSTypography.letterSpacingWide)
public static let bodyMedium = TextStyle(rawValue: "bodyMedium",
fontFace: .edsRegular,
pointSize: VDSTypography.fontSizeBody14,
lineHeight: VDSTypography.lineHeightBody18,
letterSpacing: VDSTypography.letterSpacingWide)
public static let boldBodySmall = TextStyle(rawValue: "boldBodySmall",
fontFace: .etxBold,
pointSize: VDSTypography.fontSizeBody12,
lineHeight: VDSTypography.lineHeightBody16)
public static let bodySmall = TextStyle(rawValue: "bodySmall",
fontFace: .etxRegular,
pointSize: VDSTypography.fontSizeBody12,
lineHeight: VDSTypography.lineHeightBody16)
public static let boldMicro = TextStyle(rawValue: "boldMicro",
fontFace: .etxBold,
pointSize: VDSTypography.fontSizeMicro11,
lineHeight: VDSTypography.lineHeightMicro16)
public static let micro = TextStyle(rawValue: "micro",
fontFace: .etxRegular,
pointSize: VDSTypography.fontSizeMicro11,
lineHeight: VDSTypography.lineHeightMicro16)
public static var allCases: [TextStyle] {
return [
featureXLarge,
boldFeatureXLarge,
featureLarge,
boldFeatureLarge,
featureMedium,
boldFeatureMedium,
featureSmall,
boldFeatureSmall,
featureXSmall,
boldFeatureXSmall,
title2XLarge,
boldTitle2XLarge,
titleXLarge,
boldTitleXLarge,
titleLarge,
boldTitleLarge,
titleMedium,
boldTitleMedium,
titleSmall,
boldTitleSmall,
bodyLarge,
boldBodyLarge,
bodyMedium,
boldBodyMedium,
bodySmall,
boldBodySmall,
micro,
boldMicro
]
internal enum Style: String, CaseIterable {
case boldFeatureXLarge
case featureXLarge
case boldFeatureLarge
case featureLarge
case boldFeatureMedium
case featureMedium
case boldFeatureSmall
case featureSmall
case boldFeatureXSmall
case featureXSmall
case boldTitle2XLarge
case title2XLarge
case boldTitleXLarge
case titleXLarge
case boldTitleLarge
case titleLarge
case boldTitleMedium
case titleMedium
case boldTitleSmall
case titleSmall
case boldBodyLarge
case bodyLarge
case boldBodyMedium
case bodyMedium
case boldBodySmall
case bodySmall
case boldMicro
case micro
}
// Static properties for different text styles
public static var boldFeatureXLarge: TextStyle { Provider.style(for: .boldFeatureXLarge) }
public static var featureXLarge: TextStyle { Provider.style(for: .featureXLarge) }
public static var boldFeatureLarge: TextStyle { Provider.style(for: .boldFeatureLarge) }
public static var featureLarge: TextStyle { Provider.style(for: .featureLarge) }
public static var boldFeatureMedium: TextStyle { Provider.style(for: .boldFeatureMedium) }
public static var featureMedium: TextStyle { Provider.style(for: .featureMedium) }
public static var boldFeatureSmall: TextStyle { Provider.style(for: .boldFeatureSmall) }
public static var featureSmall: TextStyle { Provider.style(for: .featureSmall) }
public static var boldFeatureXSmall: TextStyle { Provider.style(for: .boldFeatureXSmall) }
public static var featureXSmall: TextStyle { Provider.style(for: .featureXSmall) }
public static var boldTitle2XLarge: TextStyle { Provider.style(for: .boldTitle2XLarge) }
public static var title2XLarge: TextStyle { Provider.style(for: .title2XLarge) }
public static var boldTitleXLarge: TextStyle { Provider.style(for: .boldTitleXLarge) }
public static var titleXLarge: TextStyle { Provider.style(for: .titleXLarge) }
public static var boldTitleLarge: TextStyle { Provider.style(for: .boldTitleLarge) }
public static var titleLarge: TextStyle { Provider.style(for: .titleLarge) }
public static var boldTitleMedium: TextStyle { Provider.style(for: .boldTitleMedium) }
public static var titleMedium: TextStyle { Provider.style(for: .titleMedium) }
public static var boldTitleSmall: TextStyle { Provider.style(for: .boldTitleSmall) }
public static var titleSmall: TextStyle { Provider.style(for: .titleSmall) }
public static var boldBodyLarge: TextStyle { Provider.style(for: .boldBodyLarge) }
public static var bodyLarge: TextStyle { Provider.style(for: .bodyLarge) }
public static var boldBodyMedium: TextStyle { Provider.style(for: .boldBodyMedium) }
public static var bodyMedium: TextStyle { Provider.style(for: .bodyMedium) }
public static var boldBodySmall: TextStyle { Provider.style(for: .boldBodySmall) }
public static var bodySmall: TextStyle { Provider.style(for: .bodySmall) }
public static var boldMicro: TextStyle { Provider.style(for: .boldMicro) }
public static var micro: TextStyle { Provider.style(for: .micro) }
public static var allCases: [TextStyle] { Style.allCases.compactMap { Provider.style(for: $0) } }
public static func convert(font: UIFont) -> TextStyle {
guard let found = allCases.first(where: { font.fontName == $0.fontFace.fontName && font.pointSize == $0.pointSize} ) else {
return TextStyle(rawValue: "Custom\(font.fontName)", fontFace: .custom(font), pointSize: font.pointSize)
return TextStyle(rawValue: "Custom\(font.fontName)", fontFace: .custom(font), pointSize: font.pointSize, lineHeight: min(font.lineHeight, font.pointSize))
}
return found
}

View File

@ -0,0 +1,246 @@
//
// Typography+StyleProvider.swift
// VDS
//
// Created by Matt Bruce on 9/25/24.
//
import Foundation
import UIKit
import VDSCoreTokens
extension TextStyle {
// Class responsible for providing the correct TextStyles based on language
public class Provider {
// Base styles for English (default)
private static let baseStyles: [Style: TextStyle] = [
.boldFeatureXLarge: TextStyle(rawValue: Style.boldFeatureXLarge.rawValue,
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature144 : VDSTypography.fontSizeFeature96,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature136 : VDSTypography.lineHeightFeature88,
edgeInsets: .bottom(UIDevice.isIPad ? -6: -4)),
.featureXLarge: TextStyle(rawValue: Style.featureXLarge.rawValue,
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature144 : VDSTypography.fontSizeFeature96,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature136 : VDSTypography.lineHeightFeature88,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -6: -4)),
.boldFeatureLarge: TextStyle(rawValue: Style.boldFeatureLarge.rawValue,
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature128 : VDSTypography.fontSizeFeature80,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature120 : VDSTypography.lineHeightFeature76,
edgeInsets: .bottom(UIDevice.isIPad ? -6: -2)),
.featureLarge: TextStyle(rawValue: Style.featureLarge.rawValue,
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature128 : VDSTypography.fontSizeFeature80,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature120 : VDSTypography.lineHeightFeature76,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -6: -2)),
.boldFeatureMedium: TextStyle(rawValue: Style.boldFeatureMedium.rawValue,
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature96 : VDSTypography.fontSizeFeature64,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature88 : VDSTypography.lineHeightFeature64,
edgeInsets: .bottom(UIDevice.isIPad ? -4: -2)),
.featureMedium: TextStyle(rawValue: Style.featureMedium.rawValue,
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature96 : VDSTypography.fontSizeFeature64,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature88 : VDSTypography.lineHeightFeature64,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -4: -2)),
.boldFeatureSmall: TextStyle(rawValue: Style.boldFeatureSmall.rawValue,
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature80 : VDSTypography.fontSizeFeature48,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature76 : VDSTypography.lineHeightFeature48,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)),
.featureSmall: TextStyle(rawValue: Style.featureSmall.rawValue,
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature80 : VDSTypography.fontSizeFeature48,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature76 : VDSTypography.lineHeightFeature48,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)),
.boldFeatureXSmall: TextStyle(rawValue: Style.boldFeatureXSmall.rawValue,
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature64 : VDSTypography.fontSizeFeature40,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature64 : VDSTypography.lineHeightFeature40,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)),
.featureXSmall: TextStyle(rawValue: Style.featureXSmall.rawValue,
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature64 : VDSTypography.fontSizeFeature40,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature64 : VDSTypography.lineHeightFeature40,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)),
.boldTitle2XLarge: TextStyle(rawValue: Style.boldTitle2XLarge.rawValue,
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle64 : VDSTypography.fontSizeTitle40,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle64 : VDSTypography.lineHeightTitle40,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)),
.title2XLarge: TextStyle(rawValue: Style.title2XLarge.rawValue,
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle64 : VDSTypography.fontSizeTitle40,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle64 : VDSTypography.lineHeightTitle40,
letterSpacing: VDSTypography.letterSpacingSemiwide,
edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)),
.boldTitleXLarge: TextStyle(rawValue: Style.boldTitleXLarge.rawValue,
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle48 : VDSTypography.fontSizeTitle32,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle48 : VDSTypography.lineHeightTitle36),
.titleXLarge: TextStyle(rawValue: Style.titleXLarge.rawValue,
fontFace: .dsLight,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle48 : VDSTypography.fontSizeTitle32,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle48 : VDSTypography.lineHeightTitle36,
letterSpacing: VDSTypography.letterSpacingSemiwide),
.boldTitleLarge: TextStyle(rawValue: Style.boldTitleLarge.rawValue,
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle32 : VDSTypography.fontSizeTitle24,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle36 : VDSTypography.lineHeightTitle28),
.titleLarge: TextStyle(rawValue: Style.titleLarge.rawValue,
fontFace: UIDevice.isIPad ? .dsLight : .edsRegular,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle32 : VDSTypography.fontSizeTitle24,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle36 : VDSTypography.lineHeightTitle28,
letterSpacing: UIDevice.isIPad ? VDSTypography.letterSpacingSemiwide : 0),
.boldTitleMedium: TextStyle(rawValue: Style.boldTitleMedium.rawValue,
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle24 : VDSTypography.fontSizeTitle20,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle28 : VDSTypography.lineHeightTitle24),
.titleMedium: TextStyle(rawValue: Style.titleMedium.rawValue,
fontFace: .edsRegular,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle24 : VDSTypography.fontSizeTitle20,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle28 : VDSTypography.lineHeightTitle24),
.boldTitleSmall: TextStyle(rawValue: Style.boldTitleSmall.rawValue,
fontFace: .edsBold,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle20 : VDSTypography.fontSizeTitle16,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle24 : VDSTypography.lineHeightTitle20),
.titleSmall: TextStyle(rawValue: Style.titleSmall.rawValue,
fontFace: .edsRegular,
pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle20 : VDSTypography.fontSizeTitle16,
lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle24 : VDSTypography.lineHeightTitle20),
.boldBodyLarge: TextStyle(rawValue: Style.boldBodyLarge.rawValue,
fontFace: .edsBold,
pointSize: VDSTypography.fontSizeBody16,
lineHeight: VDSTypography.lineHeightBody20,
letterSpacing: VDSTypography.letterSpacingWide),
.bodyLarge: TextStyle(rawValue: Style.bodyLarge.rawValue,
fontFace: .edsRegular,
pointSize: VDSTypography.fontSizeBody16,
lineHeight: VDSTypography.lineHeightBody20,
letterSpacing:VDSTypography.letterSpacingWide),
.boldBodyMedium: TextStyle(rawValue: Style.boldBodyMedium.rawValue,
fontFace: .edsBold,
pointSize: VDSTypography.fontSizeBody14,
lineHeight: VDSTypography.lineHeightBody18,
letterSpacing: VDSTypography.letterSpacingWide),
.bodyMedium: TextStyle(rawValue: Style.bodyMedium.rawValue,
fontFace: .edsRegular,
pointSize: VDSTypography.fontSizeBody14,
lineHeight: VDSTypography.lineHeightBody18,
letterSpacing: VDSTypography.letterSpacingWide),
.boldBodySmall: TextStyle(rawValue: Style.boldBodySmall.rawValue,
fontFace: .etxBold,
pointSize: VDSTypography.fontSizeBody12,
lineHeight: VDSTypography.lineHeightBody16),
.bodySmall: TextStyle(rawValue: Style.bodySmall.rawValue,
fontFace: .etxRegular,
pointSize: VDSTypography.fontSizeBody12,
lineHeight: VDSTypography.lineHeightBody16),
.boldMicro: TextStyle(rawValue: Style.boldMicro.rawValue,
fontFace: .etxBold,
pointSize: VDSTypography.fontSizeMicro11,
lineHeight: VDSTypography.lineHeightMicro16),
.micro: TextStyle(rawValue: Style.micro.rawValue,
fontFace: .etxRegular,
pointSize: VDSTypography.fontSizeMicro11,
lineHeight: VDSTypography.lineHeightMicro16)
]
// Spanish lineHeight overrides
private static let spanishLineHeightOverrides: [Style: CGFloat] = [
.boldFeatureXLarge: UIDevice.isIPad ? 156 : 104,
.featureXLarge: UIDevice.isIPad ? 156 : 104,
.boldFeatureLarge: UIDevice.isIPad ? 140 : 88,
.featureLarge: UIDevice.isIPad ? 140 : 88,
.boldFeatureMedium: UIDevice.isIPad ? 104 : 72,
.featureMedium: UIDevice.isIPad ? 104 : 72,
.boldFeatureSmall: UIDevice.isIPad ? 88 : 56,
.featureSmall: UIDevice.isIPad ? 88 : 56,
.boldFeatureXSmall: UIDevice.isIPad ? 72 : 48,
.featureXSmall: UIDevice.isIPad ? 72 : 48,
.boldTitle2XLarge: UIDevice.isIPad ? 72 : 48,
.title2XLarge: UIDevice.isIPad ? 72 : 48,
.boldTitleXLarge: UIDevice.isIPad ? 56 : 36,
.titleXLarge: UIDevice.isIPad ? 56 : 36
]
// Cache for the current styles based on the current language
private static var currentStyles: [Style: TextStyle] = [:]
// Function to get the style with conditional lineHeight adjustment
static func style(for key: Style) -> TextStyle {
DispatchQueue.once(block: { TextStyle.Provider.initialize() })
guard let style = currentStyles[key] else {
fatalError("TextStyle for \(key.rawValue) is not defined.")
}
return style
}
// Update current styles only once when language changes
static func updateCurrentStyles() {
// Start with the base styles
currentStyles = baseStyles
// If the language is Spanish, apply the lineHeight overrides
if LanguageManager.currentLanguage == .spanish {
for (styleKey, spanishLineHeight) in spanishLineHeightOverrides {
if var style = currentStyles[styleKey] {
style = TextStyle(
rawValue: style.rawValue,
fontFace: style.fontFace,
pointSize: style.pointSize,
lineHeight: spanishLineHeight, // Apply the Spanish lineHeight
letterSpacing: style.letterSpacing,
edgeInsets: style.edgeInsets
)
currentStyles[styleKey] = style
}
}
}
}
// Initial setup to populate the current styles based on the initial language
static func initialize() {
updateCurrentStyles()
Style.allCases.forEach { style in
let found = Provider.currentStyles[style]
assert(found != nil, "\(style.rawValue) has not been set in the TextStyleProvider.baseStyles")
}
}
}
}

View File

@ -32,12 +32,16 @@ Using the system allows designers and developers to collaborate more easily and
- ``CheckboxItem``
- ``CheckboxGroup``
- ``DropdownSelect``
- ``FootnoteItem``
- ``FootnoteGroup``
- ``Icon``
- ``InputStepper``
- ``InputField``
- ``Label``
- ``Line``
- ``ListUnordered``
- ``Loader``
- ``Modal``
- ``Notification``
- ``Pagination``
- ``PriceLockup``