Compare commits

..

No commits in common. "develop" and "release/20_2_0" have entirely different histories.

82 changed files with 479 additions and 2339 deletions

View File

@ -10,25 +10,15 @@
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 */; };
@ -36,8 +26,6 @@
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 */; };
@ -71,8 +59,6 @@
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 */; };
@ -229,13 +215,7 @@
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>"; };
@ -243,15 +223,10 @@
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>"; };
@ -261,8 +236,6 @@
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>"; };
@ -277,7 +250,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 /* TileletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileletChangeLog.txt; sourceTree = "<group>"; };
710607942B91A99500F2863F /* TitleletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TitleletChangeLog.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>"; };
@ -305,8 +278,6 @@
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>"; };
@ -513,16 +484,6 @@
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 = (
@ -533,16 +494,6 @@
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 = (
@ -573,7 +524,6 @@
isa = PBXGroup;
children = (
18AE874F2C06FDA60075F181 /* Carousel.swift */,
183B16F22C78CF7C00BA6A10 /* CarouselSlotCell.swift */,
18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */,
18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */,
18AE87532C06FE610075F181 /* CarouselChangeLog.txt */,
@ -581,19 +531,6 @@
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 = (
@ -769,14 +706,11 @@
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 */,
@ -860,13 +794,12 @@
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>";
@ -979,7 +912,7 @@
EA985BE72968951C00F2FF2E /* TileletTitleModel.swift */,
EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */,
EA985C2C296F03FE00F2FF2E /* TileletIconModels.swift */,
710607942B91A99500F2863F /* TileletChangeLog.txt */,
710607942B91A99500F2863F /* TitleletChangeLog.txt */,
);
path = Tilelet;
sourceTree = "<group>";
@ -1034,7 +967,6 @@
EA0D1C3C2A6AD57600E5C127 /* Typography+Enums.swift */,
EA0D1C382A6AD4DF00E5C127 /* Typography+SpacingConfig.swift */,
EA0D1C3A2A6AD51B00E5C127 /* Typogprahy+Styles.swift */,
EA225FC82CA4932900B6B3B3 /* Typography+StyleProvider.swift */,
);
path = Typography;
sourceTree = "<group>";
@ -1280,14 +1212,12 @@
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 */,
);
@ -1346,7 +1276,6 @@
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 */,
@ -1363,7 +1292,6 @@
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 */,
@ -1374,12 +1302,10 @@
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 */,
@ -1392,7 +1318,6 @@
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 */,
@ -1422,7 +1347,6 @@
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 */,
@ -1431,7 +1355,6 @@
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 */,
@ -1446,7 +1369,6 @@
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 */,
@ -1461,7 +1383,6 @@
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 */,
@ -1479,7 +1400,6 @@
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 */,
@ -1499,7 +1419,6 @@
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 */,
@ -1508,7 +1427,6 @@
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 */,
@ -1671,7 +1589,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 75;
CURRENT_PROJECT_VERSION = 72;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
@ -1709,7 +1627,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 75;
CURRENT_PROJECT_VERSION = 72;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;

View File

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

View File

@ -28,6 +28,7 @@ public protocol SelectorControlable: Control, Changeable {
}
/// Base Class used to build out a Selector control.
@objcMembers
@objc(VDSSelectorBase)
open class SelectorBase: Control, SelectorControlable, ParentViewProtocol {
@ -86,6 +87,9 @@ 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
//--------------------------------------------------
@ -105,16 +109,6 @@ 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,14 +43,13 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
private var mainStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .fill
$0.alignment = .top
$0.axis = .vertical
}
private var selectorStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .fill
$0.alignment = .top
$0.axis = .horizontal
}
@ -172,16 +171,10 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
isAccessibilityElement = false
addSubview(mainStackView)
//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(selectorViewWrapper)
selectorStackView.addArrangedSubview(selectorView)
selectorStackView.addArrangedSubview(selectorLabelStackView)
selectorLabelStackView.addArrangedSubview(label)
selectorLabelStackView.addArrangedSubview(childLabel)

View File

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

View File

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

View File

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

View File

@ -1,63 +0,0 @@
//
// 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,6 +15,7 @@ import Combine
/// If you are using AutoLayoutConstraints you have a combination of Leading/Left and Trailing/Right NSLayoutConstraints,
/// you need to ensure that one of these Horizontal Contraints is not constraint of "equatTo". If you are to pin the left/right edges
/// to its parent this object will stretch to the parent's width.
@objcMembers
@objc(VDSBadge)
open class Badge: View, ParentViewProtocol {
@ -37,16 +38,8 @@ open class Badge: View, ParentViewProtocol {
// MARK: - Enums
//--------------------------------------------------
/// Enum used to describe the primary color for the view.
public enum FillColor: Equatable {
public enum FillColor: String, CaseIterable {
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
}
}
//--------------------------------------------------
@ -72,8 +65,6 @@ open class Badge: View, ParentViewProtocol {
/// 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() }}
@ -102,71 +93,25 @@ open class Badge: View, ParentViewProtocol {
right: VDSLayout.space1X)
/// ColorConfiguration that is mapped to the 'fillColor' for the surface.
private var backgroundColorConfiguration = SurfaceColorConfiguration()
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()
}()
/// ColorConfiguration for the Text.
private var textColorConfiguration = ViewColorConfiguration()
/// Updates the textColorConfiguration based on the fillColor.
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
}
public func updateTextColorConfig() {
textColorConfiguration.reset()
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:
@ -180,13 +125,6 @@ open class Badge: View, ParentViewProtocol {
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)
}
}
}
@ -241,7 +179,7 @@ open class Badge: View, ParentViewProtocol {
open override func updateView() {
super.updateView()
updateColorConfig()
updateTextColorConfig()
updateMaxWidth()
backgroundColor = backgroundColorConfiguration.getColor(self)
@ -253,29 +191,3 @@ 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,6 +11,7 @@ import VDSCoreTokens
import Combine
/// A badge indicator is a visual label used to convey status or highlight supplemental information.
@objcMembers
@objc(VDSBadgeIndicator)
open class BadgeIndicator: View, ParentViewProtocol {

View File

@ -13,6 +13,7 @@ import Combine
/// A Breadcrumb Item contains href(link) and selected flag.
/// Breadcrumb links to its respective page if it is not disabled.
/// Breadcrumb contains text with a separator by default, highlights text in bold without a separator if selected.
@objcMembers
@objc (VDSBreadcrumbItem)
open class BreadcrumbItem: ButtonBase {

View File

@ -13,6 +13,7 @@ import Combine
/// A Breadcrumbs contains BreadcrumbItems.
/// It contains Breadcrumb Item Default, Breadcrumb Item Selected, Separator.
/// Breadcrumbs are secondary navigation that use a hierarchy of internal links to tell customers where they are in an experience. Each breadcrumb links to its respective page, except for that of current page.
@objcMembers
@objc(VDSBreadcrumbs)
open class Breadcrumbs: View, ParentViewProtocol {

View File

@ -15,6 +15,7 @@ import Combine
/// If you are using AutoLayoutConstraints you have a combination of Leading/Left and Trailing/Right NSLayoutConstraints,
/// you need to ensure that one of these Horizontal Contraints is not constraint of "equatTo". If you are to pin the left/right edges
/// to its parent this object will stretch to the parent's width.
@objcMembers
@objc(VDSButton)
open class Button: ButtonBase, Useable {

View File

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

View File

@ -11,6 +11,7 @@ import VDSCoreTokens
import Combine
/// A button group contains combinations of related CTAs including ``Button``, ``TextLink``, and ``TextLinkCaret``. This group component controls a combination's orientation, spacing, size and allowable size pairings.
@objcMembers
@objc(VDSButtonGroup)
open class ButtonGroup: View {

View File

@ -16,6 +16,7 @@ import Combine
/// If you are using AutoLayoutConstraints you have a combination of Leading/Left and Trailing/Right NSLayoutConstraints,
/// you need to ensure that one of these Horizontal Contraints is not constraint of "equatTo". If you are to pin the left/right edges
/// to its parent this object will stretch to the parent's width.
@objcMembers
@objc(VDSTextLink)
open class TextLink: ButtonBase {
//--------------------------------------------------

View File

@ -16,6 +16,7 @@ import Combine
/// If you are using AutoLayoutConstraints you have a combination of Leading/Left and Trailing/Right NSLayoutConstraints,
/// you need to ensure that one of these Horizontal Contraints is not constraint of "equatTo". If you are to pin the left/right edges
/// to its parent this object will stretch to the parent's width.
@objcMembers
@objc(VDSTextLinkCaret)
open class TextLinkCaret: ButtonBase {
//--------------------------------------------------

View File

@ -11,6 +11,7 @@ import VDSCoreTokens
import Combine
/// A calendar is a monthly view that lets customers select a single date.
@objcMembers
@objc(VDSCalendar)
open class CalendarBase: Control, Changeable {

View File

@ -12,6 +12,7 @@ import Combine
/// A carousel is a collection of related content in a row that a customer can navigate through horizontally.
/// Use this component to show content that is supplementary, not essential for task completion.
@objcMembers
@objc(VDSCarousel)
open class Carousel: View {
@ -153,38 +154,27 @@ open class Carousel: View {
$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
}()
private var scrollView = UIScrollView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.backgroundColor = .clear
}
/// Previous button to show previous slide.
private var previousButton = ButtonIcon().with {
$0.kind = .lowContrast
$0.iconName = .paginationLeftCaret
$0.iconName = .leftCaret
$0.iconOffset = .init(x: -2, y: 0)
$0.customContainerSize = UIDevice.isIPad ? 40 : 28
$0.customIconSize = UIDevice.isIPad ? 16 : 12
$0.icon.customSize = UIDevice.isIPad ? 16 : 12
}
/// Next button to show next slide.
private var nextButton = ButtonIcon().with {
$0.kind = .lowContrast
$0.iconName = .paginationRightCaret
$0.iconName = .rightCaret
$0.iconOffset = .init(x: 2, y: 0)
$0.customContainerSize = UIDevice.isIPad ? 40 : 28
$0.customIconSize = UIDevice.isIPad ? 16 : 12
$0.icon.customSize = UIDevice.isIPad ? 16 : 12
}
/// A publisher for when moving the carousel. Passes parameters selectedGroupIndex (position).
@ -225,8 +215,8 @@ open class Carousel: View {
containerView.addSubview(contentStackView)
// Add scrollview
scrollContainerView.addSubview(collectionView)
collectionView.pinToSuperView()
scrollContainerView.addSubview(scrollView)
scrollView.pinToSuperView()
// Add pagination button icons
scrollContainerView.addSubview(previousButton)
@ -269,25 +259,14 @@ 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 {
@ -305,9 +284,12 @@ open class Carousel: View {
}
updatePaginationControls()
updateContainerHeight()
addCarouselSlots()
}
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------
private func addlisteners() {
nextButton.onClick = { _ in self.nextButtonClick() }
previousButton.onClick = { _ in self.previousButtonClick() }
@ -383,13 +365,47 @@ open class Carousel: View {
return height
}
// update carousel size and load data if any
private func updateContainerHeight() {
// Add carousel slots and load data if any
private func addCarouselSlots() {
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)
@ -403,6 +419,43 @@ 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
@ -452,7 +505,7 @@ open class Carousel: View {
}
private func updateScrollbarPosition(targetContentOffsetXPos:CGFloat) {
let scrollContentSizeWidth = collectionView.contentSize.width
let scrollContentSizeWidth = scrollView.contentSize.width
let totalPositions = totalPositions()
let layoutSpace = Int (floor( Double(scrollContentSizeWidth / Double(totalPositions))))
let remindSpace = Int(targetContentOffsetXPos) % layoutSpace
@ -462,11 +515,10 @@ open class Carousel: View {
updateScrollPosition(position: contentPos, callbackText: "ScrollViewMoved")
}
// Update collectionview offset relative to scrollbar thumb position
// Update scrollview offset relative to scrollbar thumb position
private func updateScrollPosition(position: Int, callbackText: String) {
if carouselScrollBar.numberOfSlides > 0 {
let scrollContentSizeWidth = collectionView.contentSize.width
let scrollContentSizeWidth = scrollView.contentSize.width
let totalPositions = totalPositions()
var xPos = 0.0
if position == 1 {
@ -484,8 +536,8 @@ open class Carousel: View {
}
}
carouselScrollBar.scrubberId = position+1
let yPos = collectionView.contentOffset.y
collectionView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true)
let yPos = scrollView.contentOffset.y
scrollView.setContentOffset(CGPoint(x: xPos, y: yPos), animated: true)
showPaginationControls()
groupIndex = position-1
onChangePublisher.send(groupIndex)
@ -505,35 +557,5 @@ 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

@ -1,85 +0,0 @@
//
// 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,6 +12,7 @@ import Combine
/// A carousel scrollbar is a control that allows to navigate between items in a carousel.
/// It's also a status indicator that conveys the relative amount of content in a carousel and a location within it.
@objcMembers
@objc(VDSCarouselScrollbar)
open class CarouselScrollbar: View {

View File

@ -12,6 +12,7 @@ import VDSCoreTokens
/// Checkboxes are a multi-select component through which a customer indicates a choice. This is also used within
/// ``CheckboxItem`` and ``CheckboxGroup``
@objcMembers
@objc(VDSCheckbox)
open class Checkbox: SelectorBase {

View File

@ -12,6 +12,7 @@ import VDSCoreTokens
/// When the choice has multiple options, use a checkbox group. For example, use a checkbox group when
/// asking a customer which attributes they would like to filter their search by. This uses ``CheckboxItem``
/// to allow user selection.
@objcMembers
@objc(VDSCheckboxGroup)
open class CheckboxGroup: SelectorGroupBase<CheckboxItem>, SelectorGroupMultiSelect {

View File

@ -9,6 +9,7 @@ import Foundation
import UIKit
/// Checkboxes are a multi-select component through which a customer indicates a choice. If a binary choice, the component is a checkbox. If the choice has multiple options, the component is a ``CheckboxGroup``.
@objcMembers
@objc(VDSCheckboxItem)
open class CheckboxItem: SelectorItemBase<Checkbox> {

View File

@ -4,6 +4,7 @@ import VDSCoreTokens
import Combine
/// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection.
@objcMembers
@objc(VDSDatePicker)
open class DatePicker: EntryFieldBase<String> {
//--------------------------------------------------
@ -182,15 +183,6 @@ 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,6 +11,7 @@ import VDSCoreTokens
import Combine
/// A dropdown select is an expandable menu of predefined options that allows a customer to make a single selection.
@objcMembers
@objc(VDSDropdownSelect)
open class DropdownSelect: EntryFieldBase<String> {
//--------------------------------------------------
@ -60,9 +61,8 @@ 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 = constrainedWidth
let halfWidth = (frameWidth - horizontalStackView.spacing) / 2
return helperTextPlacement == .right && halfWidth > minWidth * 2 ? halfWidth : frameWidth
let frameWidth = frame.size.width
return helperTextPlacement == .right ? (frameWidth - horizontalStackView.spacing) / 2 : frameWidth
}
/// The is used for the for adding the helperLabel to the right of the containerView.

View File

@ -1,17 +0,0 @@
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

@ -1,165 +0,0 @@
//
// 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

@ -1,256 +0,0 @@
//
// 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,6 +13,7 @@ import Combine
/// An icon is a graphical element that conveys information at a glance. It helps orient
/// a customer, explain functionality and draw attention to interactive elements. Icons
/// should have a functional purpose and should never be used for decoration.
@objcMembers
@objc(VDSIcon)
open class Icon: View {

View File

@ -11,6 +11,7 @@ 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> {
@ -330,6 +331,8 @@ open class InputStepper: EntryFieldBase<Int> {
stepperWidthConstraint?.deactivate()
widthConstraint?.deactivate()
trailingLessThanEqualsConstraint?.deactivate()
trailingEqualsConstraint?.deactivate()
var widthConstraintConstant: CGFloat?
@ -347,6 +350,9 @@ 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,27 +39,6 @@ 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,7 +63,9 @@ public struct TextStyleLabelAttribute: LabelAttributeModel {
//set lineHeight
if textStyle.lineHeight > 0.0 {
let lineHeight = textStyle.lineHeight + abs(textStyle.edgeInsets.bottom) + abs(textStyle.edgeInsets.top)
let lineHeight = textStyle.lineHeight
let adjustment = lineHeight > textStyle.font.lineHeight ? 2.0 : 1.0
let baselineOffset = (lineHeight - textStyle.font.lineHeight) / 2.0 / adjustment
let paragraph = NSMutableParagraphStyle().with {
$0.maximumLineHeight = lineHeight
$0.minimumLineHeight = lineHeight
@ -72,6 +74,7 @@ public struct TextStyleLabelAttribute: LabelAttributeModel {
}
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 {
@ -84,4 +87,3 @@ public struct TextStyleLabelAttribute: LabelAttributeModel {
}
}
}

View File

@ -12,6 +12,7 @@ import Combine
/// Label is a standard view used to draw text with applying Typography through ``TextStyle`` as well
/// as other attributes using any implemetation of ``LabelAttributeModel``.
@objcMembers
@objc(VDSLabel)
open class Label: UILabel, ViewProtocol, UserInfoable {

View File

@ -10,6 +10,7 @@ import UIKit
import VDSCoreTokens
/// A line visually separates content sections or elements in lists, tables and layouts to indicate content hierarchy.
@objcMembers
@objc(VDSLine)
open class Line: View {

View File

@ -1,239 +0,0 @@
//
// 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

@ -1,41 +0,0 @@
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

@ -1,23 +0,0 @@
//
// 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,6 +11,7 @@ import VDSCoreTokens
/// A loader is an indicator that uses animation to show customers that there is an indefinite amount of wait time while a task is ongoing, e.g. a page is loading, a form is being submitted. The component disappears when the task is complete.
@objcMembers
@objc(VDSLoader)
open class Loader: View {

View File

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

@ -1,149 +0,0 @@
//
// 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

@ -1,67 +0,0 @@
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

@ -1,237 +0,0 @@
//
// 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

@ -1,123 +0,0 @@
//
// 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

@ -1,28 +0,0 @@
//
// 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

@ -1,45 +0,0 @@
//
// 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,6 +14,7 @@ import Combine
/// in context. There are four types: information, success, warning and error; each
/// with different color and content. They may be screen-specific, flow-specific or
/// experience-wide.
@objcMembers
@objc(VDSNotification)
open class Notification: View, ParentViewProtocol {

View File

@ -11,14 +11,13 @@ 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
@ -53,8 +52,6 @@ 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
@ -148,11 +145,6 @@ 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() {
@ -203,7 +195,6 @@ 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)
@ -258,7 +249,7 @@ extension Pagination: UICollectionViewDelegate, UICollectionViewDataSource, UICo
guard _selectedPageIndex != indexPath.row else { return }
_selectedPageIndex = indexPath.row
updateSelection()
pageChangedSubject.send(self)
onPageDidSelect?(selectedPage)
}
}

View File

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

View File

@ -9,6 +9,7 @@ import Foundation
import UIKit
import VDSCoreTokens
@objcMembers
@objc(VDSPriceLockup)
open class PriceLockup: View, ParentViewProtocol {
@ -139,7 +140,6 @@ 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,24 +217,6 @@ 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,6 +11,7 @@ import UIKit
/// Radio boxes are single-select components through which a customer indicates a choice.
/// They're stylized ``RadioButtons`` that must always be paired with one or more ``RadioBoxItem``
/// in a radio box group. Use radio boxes to display choices like device storage.
@objcMembers
@objc(VDSRadioBoxGroup)
open class RadioBoxGroup: SelectorGroupBase<RadioBoxItem>, SelectorGroupSingleSelect {

View File

@ -12,6 +12,7 @@ import VDSCoreTokens
/// Radio boxes are single-select components through which a customer indicates a choice
/// that are used within a ``RadioBoxGroup``.
@objcMembers
@objc(VDSRadioBoxItem)
open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable, ParentViewProtocol {

View File

@ -13,6 +13,7 @@ import VDSCoreTokens
/// Radio buttons are single-select components through which a customer indicates a choice.
/// They must always be paired with one or more ``RadioButtonItem`` within a ``RadioButtonGroup``.
/// Use radio buttons to display choices like delivery method.
@objcMembers
@objc(VDSRadioButton)
open class RadioButton: SelectorBase {

View File

@ -11,6 +11,7 @@ import UIKit
/// Radio buttons items are single-select components through which a customer indicates a choice.
/// They must always be paired with one or more other ``RadioButtonItem`` within a radio button group.
/// Use radio buttons to display choices like delivery method.
@objcMembers
@objc(VDSRadioButtonGroup)
open class RadioButtonGroup: SelectorGroupBase<RadioButtonItem>, SelectorGroupSingleSelect {

View File

@ -11,6 +11,7 @@ import UIKit
/// Radio buttons items are single-select components through which a customer indicates a choice.
/// They must always be paired with one or more other radio button items within a ``RadioButtonGroup``.
/// Use radio buttons to display choices like delivery method.
@objcMembers
@objc(VDSRadioButtonItem)
open class RadioButtonItem: SelectorItemBase<RadioButton> {

View File

@ -10,6 +10,7 @@ import UIKit
import VDSCoreTokens
///Table is view composed of rows and columns, which takes any view into each cell and resizes based on the highest cell height.
@objcMembers
@objc(VDSTable)
open class Table: View {
@ -26,6 +27,7 @@ open class Table: View {
$0.allowsSelection = false
$0.showsVerticalScrollIndicator = false
$0.showsHorizontalScrollIndicator = false
$0.isAccessibilityElement = true
$0.backgroundColor = .clear
}
@ -146,7 +148,6 @@ 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
}
@ -161,38 +162,4 @@ 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,7 +45,6 @@ final class TableCellItem: UICollectionViewCell {
private func setupCell() {
contentView.backgroundColor = .clear
isAccessibilityElement = true
addSubview(containerView)
containerView.pinToSuperView()

View File

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

View File

@ -10,6 +10,7 @@ import UIKit
import VDSCoreTokens
/// Tabs are organizational components that group content and allow customers to navigate its display. Use them to separate content when the content is related but doesnt need to be compared.
@objcMembers
@objc(VDSTabs)
open class Tabs: View, ParentViewProtocol {

View File

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

View File

@ -105,12 +105,14 @@ 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 { constrainedWidth }
internal var maxWidth: CGFloat { frame.size.width }
internal var minWidth: CGFloat { containerSize.width }
internal var containerSize: CGSize { CGSize(width: minWidth, height: 44) }
@ -256,9 +258,15 @@ open class EntryFieldBase<ValueType>: Control, Changeable, FormFieldInternalVali
let layoutGuide = UILayoutGuide()
addLayoutGuide(layoutGuide)
layoutGuide.pinToSuperView()
layoutGuide
.pinTop()
.pinLeading()
.pinBottom()
trailingEqualsConstraint = layoutGuide.pinTrailing(anchor: trailingAnchor)
// width constraints
trailingLessThanEqualsConstraint = layoutGuide.pinTrailingLessThanOrEqualTo(anchor: trailingAnchor)?.deactivate()
widthConstraint = layoutGuide.widthAnchor.constraint(equalToConstant: 0).deactivate()
// Add mainStackView to the view
@ -544,10 +552,15 @@ open class EntryFieldBase<ValueType>: Control, Changeable, FormFieldInternalVali
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,6 +13,7 @@ import Combine
/// An input field is an input wherein a customer enters information. They typically appear in forms.
/// Specialized input fields capture credit card numbers, inline actions, passwords, phone numbers,
/// dates and security codes in their correct formats.
@objcMembers
@objc(VDSInputField)
open class InputField: EntryFieldBase<String> {
@ -46,9 +47,8 @@ open class InputField: EntryFieldBase<String> {
internal override var minWidth: CGFloat { fieldType.handler().minWidth }
internal override var maxWidth: CGFloat {
let frameWidth = constrainedWidth
let halfWidth = (frameWidth - horizontalStackView.spacing) / 2
return helperTextPlacement == .right && halfWidth > minWidth * 2 ? halfWidth : frameWidth
let frameWidth = frame.size.width
return helperTextPlacement == .right ? (frameWidth - horizontalStackView.spacing) / 2 : frameWidth
}
/// The is used for the for adding the helperLabel to the right of the containerView.
@ -320,12 +320,15 @@ 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,6 +10,7 @@ import UIKit
import Combine
import VDSCoreTokens
@objcMembers
@objc(VDSTextField)
open class TextField: UITextField, ViewProtocol, Errorable {

View File

@ -12,6 +12,7 @@ import Combine
/// A text area is an input wherein a customer enters long-form information.
/// Use a text area when you want customers to enter text thats longer than a single line.
@objcMembers
@objc(VDSTextArea)
open class TextArea: EntryFieldBase<String> {
//--------------------------------------------------

View File

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

View File

@ -10,6 +10,7 @@ import VDSCoreTokens
import UIKit
import Combine
@objcMembers
@objc(VDSTileContainer)
open class TileContainer: TileContainerBase<TileContainer.Padding> {
@ -345,15 +346,8 @@ open class TileContainerBase<PaddingType: DefaultValuing & Valuing>: View where
containerView.setAccessibilityLabel(for: views)
//append all children that are accessible
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 {}
@ -453,6 +447,33 @@ 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
//-------------------------------------------------------------------------
@ -478,14 +499,6 @@ 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 {
//-------------------------------------------------------------------------
@ -507,6 +520,12 @@ 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,6 +15,7 @@ import Combine
/// support quick scanning and engagement. A Tilelet is fully clickable and
/// while it can include an arrow CTA, it does not require one in order to
/// function.
@objcMembers
@objc(VDSTilelet)
open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
@ -28,9 +29,9 @@ open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
public var value: CGFloat {
switch self {
case .small:
return UIDevice.isIPad ? VDSLayout.space4X : VDSLayout.space3X
return UIDevice.isIPad ? VDSLayout.space3X : VDSLayout.space4X
case .large:
return UIDevice.isIPad ? VDSLayout.space6X : VDSLayout.space4X
return UIDevice.isIPad ? VDSLayout.space4X : VDSLayout.space6X
}
}
@ -103,7 +104,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
}
private var backgroundColorSurface: Surface {
backgroundColorConfiguration.getColor(self).isDark() ? .dark : .light
backgroundColorConfiguration.getColor(self).surface
}
//--------------------------------------------------
@ -279,7 +280,6 @@ 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()
.pinTopGreaterThanOrEqualTo()
.pinTop()
.pinBottom()
directionalIcon
.pinTrailing()
.pinTopGreaterThanOrEqualTo()
.pinTop()
.pinBottom()
badge.bottomAnchor.constraint(equalTo: badge.label.bottomAnchor, constant: 2).activate()
@ -468,7 +468,6 @@ 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
@ -559,7 +558,6 @@ 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
}
@ -570,7 +568,6 @@ 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
}
@ -601,7 +598,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
}
private func updateTextPositionAlignment() {
guard aspectRatio != .none || height != nil else { return }
guard width != nil && (aspectRatio != .none || height != nil) else { return }
switch textPostion {
case .top:
titleLockupTopConstraint?.activate()

View File

@ -15,9 +15,6 @@ 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
@ -33,9 +30,8 @@ extension Tilelet {
/// LineBreakMode used in Badge label.
public var lineBreakMode: NSLineBreakMode
public init(text: String, textColor: Badge.TextColor? = nil, fillColor: Badge.FillColor = .red, surface: Surface = .light, numberOfLines: Int = 0, maxWidth: CGFloat? = nil, lineBreakMode: NSLineBreakMode = .byTruncatingTail) {
public init(text: String, 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,6 +12,7 @@ import Combine
/// Title Lockup ensures the readability of words on the screen
/// with approved built in text size configurations.
@objcMembers
@objc(VDSTitleLockup)
open class TitleLockup: View, ParentViewProtocol {

View File

@ -12,6 +12,7 @@ import Combine
/// A toggle is a control that lets customers instantly turn on
/// or turn off a single option, setting or function.
@objcMembers
@objc(VDSToggle)
open class Toggle: Control, Changeable, FormFieldable, ParentViewProtocol {
@ -207,8 +208,8 @@ open class Toggle: Control, Changeable, FormFieldable, ParentViewProtocol {
//toggle
toggleConstraints = [
toggleView.leadingAnchor.constraint(equalTo: leadingAnchor),
toggleView.trailingAnchor.constraint(equalTo: trailingAnchor)
toggleView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor),
toggleView.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor)
]
//toggle and label variants

View File

@ -12,6 +12,7 @@ import Combine
/// A toggle is a control that lets customers instantly turn on
/// or turn off a single option, setting or function.
@objcMembers
@objc(VDSToggleView)
open class ToggleView: Control, Changeable, FormFieldable {

View File

@ -13,6 +13,7 @@ import Combine
/// A tooltip is an overlay that clarifies another component or content
/// element. It is triggered when a customer hovers, clicks or taps
/// the tooltip icon.
@objcMembers
@objc(VDSTooltip)
open class Tooltip: Control, TooltipLaunchable {

View File

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

View File

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

View File

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

View File

@ -192,14 +192,4 @@ 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,14 +647,7 @@ 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,32 +1,8 @@
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,72 +11,209 @@ import VDSCoreTokens
//MARK: Definitions
extension TextStyle {
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 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
]
}
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, lineHeight: min(font.lineHeight, font.pointSize))
return TextStyle(rawValue: "Custom\(font.fontName)", fontFace: .custom(font), pointSize: font.pointSize)
}
return found
}

View File

@ -1,246 +0,0 @@
//
// 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,16 +32,12 @@ 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``