Merge branch 'develop' into feature/tileletEnhancements
This commit is contained in:
commit
6fe16595c3
@ -7,6 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */; };
|
||||||
18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; };
|
18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; };
|
||||||
18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; };
|
18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; };
|
||||||
445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; };
|
445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; };
|
||||||
@ -16,8 +17,10 @@
|
|||||||
5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; };
|
5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; };
|
||||||
710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 710607942B91A99500F2863F /* TitleletChangeLog.txt */; };
|
710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 710607942B91A99500F2863F /* TitleletChangeLog.txt */; };
|
||||||
7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; };
|
7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; };
|
||||||
71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */; };
|
71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */; };
|
||||||
71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; };
|
71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; };
|
||||||
|
71FC86DE2B9738B900700965 /* SurfaceConfigurationValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */; };
|
||||||
|
71FC86E02B973AE500700965 /* DropShadowConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DF2B973AE500700965 /* DropShadowConfiguration.swift */; };
|
||||||
EA0B18022A9E236900F2D0CD /* SelectorGroupBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */; };
|
EA0B18022A9E236900F2D0CD /* SelectorGroupBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */; };
|
||||||
EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */; };
|
EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */; };
|
||||||
EA0B18062A9E2D2D00F2D0CD /* SelectorItemBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */; };
|
EA0B18062A9E2D2D00F2D0CD /* SelectorItemBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */; };
|
||||||
@ -68,6 +71,7 @@
|
|||||||
EA5F86C82A1BD99100BC83E4 /* TabModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86C72A1BD99100BC83E4 /* TabModel.swift */; };
|
EA5F86C82A1BD99100BC83E4 /* TabModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86C72A1BD99100BC83E4 /* TabModel.swift */; };
|
||||||
EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */; };
|
EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */; };
|
||||||
EA5F86D02A1F936100BC83E4 /* TabsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */; };
|
EA5F86D02A1F936100BC83E4 /* TabsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */; };
|
||||||
|
EA6F330E2B911E9000BACAB9 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6F330D2B911E9000BACAB9 /* TextView.swift */; };
|
||||||
EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */; };
|
EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */; };
|
||||||
EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */; };
|
EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */; };
|
||||||
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; };
|
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; };
|
||||||
@ -176,6 +180,7 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = "<group>"; };
|
||||||
18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = "<group>"; };
|
18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = "<group>"; };
|
||||||
18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = "<group>"; };
|
18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = "<group>"; };
|
||||||
445BA07729C07B3D0036A7C5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
|
445BA07729C07B3D0036A7C5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
|
||||||
@ -185,8 +190,10 @@
|
|||||||
5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
|
5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
|
||||||
710607942B91A99500F2863F /* TitleletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TitleletChangeLog.txt; sourceTree = "<group>"; };
|
710607942B91A99500F2863F /* TitleletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TitleletChangeLog.txt; sourceTree = "<group>"; };
|
||||||
7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = "<group>"; };
|
7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = "<group>"; };
|
||||||
71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dropshadowable.swift; sourceTree = "<group>"; };
|
71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowable.swift; sourceTree = "<group>"; };
|
||||||
71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = "<group>"; };
|
71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = "<group>"; };
|
||||||
|
71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceConfigurationValue.swift; sourceTree = "<group>"; };
|
||||||
|
71FC86DF2B973AE500700965 /* DropShadowConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowConfiguration.swift; sourceTree = "<group>"; };
|
||||||
EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorGroupBase.swift; sourceTree = "<group>"; };
|
EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorGroupBase.swift; sourceTree = "<group>"; };
|
||||||
EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = "<group>"; };
|
EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = "<group>"; };
|
||||||
EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorItemBase.swift; sourceTree = "<group>"; };
|
EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorItemBase.swift; sourceTree = "<group>"; };
|
||||||
@ -238,6 +245,7 @@
|
|||||||
EA5F86C72A1BD99100BC83E4 /* TabModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabModel.swift; sourceTree = "<group>"; };
|
EA5F86C72A1BD99100BC83E4 /* TabModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabModel.swift; sourceTree = "<group>"; };
|
||||||
EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
|
EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
|
||||||
EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsContainer.swift; sourceTree = "<group>"; };
|
EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsContainer.swift; sourceTree = "<group>"; };
|
||||||
|
EA6F330D2B911E9000BACAB9 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; };
|
||||||
EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIcon.swift; sourceTree = "<group>"; };
|
EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIcon.swift; sourceTree = "<group>"; };
|
||||||
EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+VDSColor.swift"; sourceTree = "<group>"; };
|
EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+VDSColor.swift"; sourceTree = "<group>"; };
|
||||||
EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = "<group>"; };
|
EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = "<group>"; };
|
||||||
@ -568,7 +576,7 @@
|
|||||||
EAB1D2CC28ABE76000DAE764 /* Withable.swift */,
|
EAB1D2CC28ABE76000DAE764 /* Withable.swift */,
|
||||||
5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */,
|
5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */,
|
||||||
EAACB8992B927108006A3869 /* Valuing.swift */,
|
EAACB8992B927108006A3869 /* Valuing.swift */,
|
||||||
71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */,
|
71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */,
|
||||||
);
|
);
|
||||||
path = Protocols;
|
path = Protocols;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -587,6 +595,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
EA3361BC288B2C760071C351 /* TypeAlias.swift */,
|
EA3361BC288B2C760071C351 /* TypeAlias.swift */,
|
||||||
|
71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */,
|
||||||
|
71FC86DF2B973AE500700965 /* DropShadowConfiguration.swift */,
|
||||||
);
|
);
|
||||||
path = Utilities;
|
path = Utilities;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -724,6 +734,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
EA985C22296E033A00F2FF2E /* TextArea.swift */,
|
EA985C22296E033A00F2FF2E /* TextArea.swift */,
|
||||||
|
EA6F330D2B911E9000BACAB9 /* TextView.swift */,
|
||||||
|
186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */,
|
||||||
);
|
);
|
||||||
path = TextArea;
|
path = TextArea;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -955,6 +967,7 @@
|
|||||||
EA3362042891E14D0071C351 /* VerizonNHGeTX-Bold.otf in Resources */,
|
EA3362042891E14D0071C351 /* VerizonNHGeTX-Bold.otf in Resources */,
|
||||||
71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */,
|
71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */,
|
||||||
EAEEECA72B1F952000531FC2 /* TabsChangeLog.txt in Resources */,
|
EAEEECA72B1F952000531FC2 /* TabsChangeLog.txt in Resources */,
|
||||||
|
186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */,
|
||||||
EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */,
|
EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */,
|
||||||
710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */,
|
710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */,
|
||||||
EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */,
|
EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */,
|
||||||
@ -1010,7 +1023,7 @@
|
|||||||
EAACB8982B92706F006A3869 /* DefaultValuing.swift in Sources */,
|
EAACB8982B92706F006A3869 /* DefaultValuing.swift in Sources */,
|
||||||
EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */,
|
EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */,
|
||||||
EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */,
|
EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */,
|
||||||
71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */,
|
71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */,
|
||||||
EA0D1C452A6AD73000E5C127 /* RawRepresentable.swift in Sources */,
|
EA0D1C452A6AD73000E5C127 /* RawRepresentable.swift in Sources */,
|
||||||
EA985C23296E033A00F2FF2E /* TextArea.swift in Sources */,
|
EA985C23296E033A00F2FF2E /* TextArea.swift in Sources */,
|
||||||
EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */,
|
EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */,
|
||||||
@ -1036,6 +1049,7 @@
|
|||||||
EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */,
|
EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */,
|
||||||
EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */,
|
EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */,
|
||||||
EA985BE82968951C00F2FF2E /* TileletTitleModel.swift in Sources */,
|
EA985BE82968951C00F2FF2E /* TileletTitleModel.swift in Sources */,
|
||||||
|
71FC86DE2B9738B900700965 /* SurfaceConfigurationValue.swift in Sources */,
|
||||||
EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */,
|
EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */,
|
||||||
EA985BEA29689B6D00F2FF2E /* TileletSubTitleModel.swift in Sources */,
|
EA985BEA29689B6D00F2FF2E /* TileletSubTitleModel.swift in Sources */,
|
||||||
EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */,
|
EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */,
|
||||||
@ -1067,6 +1081,7 @@
|
|||||||
EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */,
|
EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */,
|
||||||
EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */,
|
EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */,
|
||||||
EA8E40912A7D3F6300934ED3 /* UIView+Accessibility.swift in Sources */,
|
EA8E40912A7D3F6300934ED3 /* UIView+Accessibility.swift in Sources */,
|
||||||
|
EA6F330E2B911E9000BACAB9 /* TextView.swift in Sources */,
|
||||||
EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */,
|
EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */,
|
||||||
EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */,
|
EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */,
|
||||||
EAD0688E2A55F819002E3A2D /* Loader.swift in Sources */,
|
EAD0688E2A55F819002E3A2D /* Loader.swift in Sources */,
|
||||||
@ -1105,6 +1120,7 @@
|
|||||||
EAB2376829E9992800AABE9A /* TooltipAlertViewController.swift in Sources */,
|
EAB2376829E9992800AABE9A /* TooltipAlertViewController.swift in Sources */,
|
||||||
EA33623E2892EE950071C351 /* UIDevice.swift in Sources */,
|
EA33623E2892EE950071C351 /* UIDevice.swift in Sources */,
|
||||||
EA985C692971B90B00F2FF2E /* IconSize.swift in Sources */,
|
EA985C692971B90B00F2FF2E /* IconSize.swift in Sources */,
|
||||||
|
71FC86E02B973AE500700965 /* DropShadowConfiguration.swift in Sources */,
|
||||||
EA985C672970C21600F2FF2E /* VDSLayout.swift in Sources */,
|
EA985C672970C21600F2FF2E /* VDSLayout.swift in Sources */,
|
||||||
EA3362302891EB4A0071C351 /* Font.swift in Sources */,
|
EA3362302891EB4A0071C351 /* Font.swift in Sources */,
|
||||||
EAF7F0AD289B142900B287F5 /* StrikeThroughLabelAttribute.swift in Sources */,
|
EAF7F0AD289B142900B287F5 /* StrikeThroughLabelAttribute.swift in Sources */,
|
||||||
|
|||||||
@ -132,4 +132,8 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -86,4 +86,9 @@ open class View: UIView, ViewProtocol, UserInfoable {
|
|||||||
isEnabled = true
|
isEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -232,19 +232,26 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct LowContrastColorFillFloatingConfiguration: Configuration, Dropshadowable {
|
private struct LowContrastColorFillFloatingConfiguration: Configuration, DropShadowableConfiguration {
|
||||||
var kind: Kind = .lowContrast
|
var kind: Kind = .lowContrast
|
||||||
var surfaceType: SurfaceType = .colorFill
|
var surfaceType: SurfaceType = .colorFill
|
||||||
var floating: Bool = true
|
var floating: Bool = true
|
||||||
var backgroundColorConfiguration: AnyColorable = {
|
var backgroundColorConfiguration: AnyColorable = {
|
||||||
SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteGray20).eraseToAnyColorable()
|
SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteGray20).eraseToAnyColorable()
|
||||||
}()
|
}()
|
||||||
var shadowColorConfiguration: AnyColorable = {
|
private let dropshadow1Configuration = DropShadowConfiguration().with {
|
||||||
SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
}()
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.12), CGFloat(0.22))
|
||||||
var shadowOpacity: CGFloat = 0.16
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1))
|
||||||
var shadowOffset: CGSize = .init(width: 0, height: 2)
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(10), CGFloat(12))
|
||||||
var shadowRadius: CGFloat = 4
|
}
|
||||||
|
private let dropshadow2Configuration = DropShadowConfiguration().with {
|
||||||
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.05), CGFloat(0.15))
|
||||||
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2))
|
||||||
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(4), CGFloat(6))
|
||||||
|
}
|
||||||
|
var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] }
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct LowContrastMediaConfiguration: Configuration, Borderable {
|
private struct LowContrastMediaConfiguration: Configuration, Borderable {
|
||||||
@ -260,19 +267,26 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct LowContrastMediaFloatingConfiguration: Configuration, Dropshadowable {
|
private struct LowContrastMediaFloatingConfiguration: Configuration, DropShadowableConfiguration {
|
||||||
var kind: Kind = .lowContrast
|
var kind: Kind = .lowContrast
|
||||||
var surfaceType: SurfaceType = .media
|
var surfaceType: SurfaceType = .media
|
||||||
var floating: Bool = true
|
var floating: Bool = true
|
||||||
var backgroundColorConfiguration: AnyColorable = {
|
var backgroundColorConfiguration: AnyColorable = {
|
||||||
SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteGray20).eraseToAnyColorable()
|
SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteGray20).eraseToAnyColorable()
|
||||||
}()
|
}()
|
||||||
var shadowColorConfiguration: AnyColorable = {
|
private let dropshadow1Configuration = DropShadowConfiguration().with {
|
||||||
SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
}()
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.12), CGFloat(0.22))
|
||||||
var shadowOpacity: CGFloat = 0.16
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1))
|
||||||
var shadowOffset: CGSize = .init(width: 0, height: 2)
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(10), CGFloat(12))
|
||||||
var shadowRadius: CGFloat = 4
|
}
|
||||||
|
private let dropshadow2Configuration = DropShadowConfiguration().with {
|
||||||
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.05), CGFloat(0.15))
|
||||||
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2))
|
||||||
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(4), CGFloat(6))
|
||||||
|
}
|
||||||
|
var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] }
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct HighContrastConfiguration: Configuration {
|
private struct HighContrastConfiguration: Configuration {
|
||||||
@ -291,7 +305,7 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct HighContrastFloatingConfiguration: Configuration, Dropshadowable {
|
private struct HighContrastFloatingConfiguration: Configuration, DropShadowableConfiguration {
|
||||||
var kind: Kind = .highContrast
|
var kind: Kind = .highContrast
|
||||||
var surfaceType: SurfaceType = .colorFill
|
var surfaceType: SurfaceType = .colorFill
|
||||||
var floating: Bool = true
|
var floating: Bool = true
|
||||||
@ -305,12 +319,19 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled])
|
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled])
|
||||||
}.eraseToAnyColorable()
|
}.eraseToAnyColorable()
|
||||||
}()
|
}()
|
||||||
var shadowColorConfiguration: AnyColorable = {
|
private let dropshadow1Configuration = DropShadowConfiguration().with {
|
||||||
SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
}()
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.22), CGFloat(0.12))
|
||||||
var shadowOpacity: CGFloat = 0.16
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1))
|
||||||
var shadowOffset: CGSize = .init(width: 0, height: 2)
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(12), CGFloat(10))
|
||||||
var shadowRadius: CGFloat = 6
|
}
|
||||||
|
private let dropshadow2Configuration = DropShadowConfiguration().with {
|
||||||
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.15), CGFloat(0.05))
|
||||||
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2))
|
||||||
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(6), CGFloat(4))
|
||||||
|
}
|
||||||
|
var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] }
|
||||||
}
|
}
|
||||||
|
|
||||||
private var badgeIndicatorDefaultSize: CGSize = .zero
|
private var badgeIndicatorDefaultSize: CGSize = .zero
|
||||||
@ -322,7 +343,7 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
open override func setup() {
|
open override func setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
isAccessibilityElement = true
|
isAccessibilityElement = true
|
||||||
accessibilityTraits = .image
|
accessibilityTraits = .button
|
||||||
accessibilityElements = [badgeIndicator]
|
accessibilityElements = [badgeIndicator]
|
||||||
|
|
||||||
//create a layoutGuide for the icon to key off of
|
//create a layoutGuide for the icon to key off of
|
||||||
@ -452,12 +473,6 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
layer.borderColor = nil
|
layer.borderColor = nil
|
||||||
layer.borderWidth = 0
|
layer.borderWidth = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if let dropshadowable = currentConfig as? Dropshadowable {
|
|
||||||
addDropShadow(dropshadowable)
|
|
||||||
} else {
|
|
||||||
removeDropShadows()
|
|
||||||
}
|
|
||||||
|
|
||||||
badgeIndicatorCenterXConstraint?.constant = badgeIndicatorOffset.x + badgeIndicatorDefaultSize.width/2
|
badgeIndicatorCenterXConstraint?.constant = badgeIndicatorOffset.x + badgeIndicatorDefaultSize.width/2
|
||||||
badgeIndicatorCenterYConstraint?.constant = badgeIndicatorOffset.y + badgeIndicatorDefaultSize.height/2
|
badgeIndicatorCenterYConstraint?.constant = badgeIndicatorOffset.y + badgeIndicatorDefaultSize.height/2
|
||||||
@ -467,6 +482,12 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
if showBadgeIndicator {
|
if showBadgeIndicator {
|
||||||
updateExpandDirectionalConstraints()
|
updateExpandDirectionalConstraints()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let configurations = (currentConfig as? DropShadowableConfiguration)?.configurations {
|
||||||
|
addDropShadows(configurations)
|
||||||
|
} else {
|
||||||
|
removeDropShadows()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -30,6 +30,7 @@ public struct AnyAttribute: LabelAttributeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func setAttribute(on attributedString: NSMutableAttributedString) {
|
public func setAttribute(on attributedString: NSMutableAttributedString) {
|
||||||
|
guard isValidRange(on: attributedString) else { return }
|
||||||
attributedString.removeAttribute(key, range: range)
|
attributedString.removeAttribute(key, range: range)
|
||||||
attributedString.addAttribute(key, value: value, range: range)
|
attributedString.addAttribute(key, value: value, range: range)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,8 @@ public struct ColorLabelAttribute: LabelAttributeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func setAttribute(on attributedString: NSMutableAttributedString) {
|
public func setAttribute(on attributedString: NSMutableAttributedString) {
|
||||||
|
guard isValidRange(on: attributedString) else { return }
|
||||||
|
|
||||||
var colorRange = range
|
var colorRange = range
|
||||||
if length == 0 && location == 0 {
|
if length == 0 && location == 0 {
|
||||||
colorRange = .init(location: location, length: attributedString.length)
|
colorRange = .init(location: location, length: attributedString.length)
|
||||||
|
|||||||
@ -29,6 +29,10 @@ extension LabelAttributeModel {
|
|||||||
public static func == (lhs: any LabelAttributeModel, rhs: any LabelAttributeModel) -> Bool {
|
public static func == (lhs: any LabelAttributeModel, rhs: any LabelAttributeModel) -> Bool {
|
||||||
lhs.isEqual(rhs)
|
lhs.isEqual(rhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func isValidRange(on attributedString: NSMutableAttributedString) -> Bool {
|
||||||
|
range.location + range.length <= attributedString.string.count
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension NSAttributedString {
|
public extension NSAttributedString {
|
||||||
|
|||||||
@ -24,6 +24,7 @@ public struct StrikeThroughLabelAttribute: LabelAttributeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func setAttribute(on attributedString: NSMutableAttributedString) {
|
public func setAttribute(on attributedString: NSMutableAttributedString) {
|
||||||
|
guard isValidRange(on: attributedString) else { return }
|
||||||
attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range)
|
attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range)
|
||||||
attributedString.addAttribute(.baselineOffset, value: 0, range: range)
|
attributedString.addAttribute(.baselineOffset, value: 0, range: range)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,7 @@ public struct TextStyleLabelAttribute: LabelAttributeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func setAttribute(on attributedString: NSMutableAttributedString) {
|
public func setAttribute(on attributedString: NSMutableAttributedString) {
|
||||||
|
guard isValidRange(on: attributedString) else { return }
|
||||||
attributedString.removeAttribute(.font, range: range)
|
attributedString.removeAttribute(.font, range: range)
|
||||||
attributedString.addAttribute(.font, value: textStyle.font, range: range)
|
attributedString.addAttribute(.font, value: textStyle.font, range: range)
|
||||||
if let textColor {
|
if let textColor {
|
||||||
|
|||||||
@ -52,7 +52,8 @@ public struct UnderlineLabelAttribute: LabelAttributeModel {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Methods
|
// MARK: - Public Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
public func setAttribute(on attributedString: NSMutableAttributedString) {
|
public func setAttribute(on attributedString: NSMutableAttributedString) {
|
||||||
|
guard isValidRange(on: attributedString) else { return }
|
||||||
attributedString.addAttribute(.underlineStyle, value: underlineValue.rawValue, range: range)
|
attributedString.addAttribute(.underlineStyle, value: underlineValue.rawValue, range: range)
|
||||||
if let color = color {
|
if let color = color {
|
||||||
attributedString.addAttribute(.underlineColor, value: color, range: range)
|
attributedString.addAttribute(.underlineColor, value: color, range: range)
|
||||||
|
|||||||
@ -80,7 +80,7 @@ open class Loader: View {
|
|||||||
super.updateView()
|
super.updateView()
|
||||||
icon.color = iconColorConfiguration.getColor(self)
|
icon.color = iconColorConfiguration.getColor(self)
|
||||||
icon.customSize = size
|
icon.customSize = size
|
||||||
if isActive {
|
if isActive && isVisibleOnScreen {
|
||||||
startAnimating()
|
startAnimating()
|
||||||
} else {
|
} else {
|
||||||
stopAnimating()
|
stopAnimating()
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import Combine
|
|||||||
|
|
||||||
/// Base Class used to build out a Input controls.
|
/// Base Class used to build out a Input controls.
|
||||||
@objc(VDSEntryField)
|
@objc(VDSEntryField)
|
||||||
open class EntryFieldBase: Control, Changeable {
|
open class EntryFieldBase: Control, Changeable, FormFieldable {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
@ -59,7 +59,7 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
return UIStackView().with {
|
return UIStackView().with {
|
||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
$0.axis = .horizontal
|
$0.axis = .horizontal
|
||||||
$0.distribution = .fillProportionally
|
$0.distribution = .fill
|
||||||
$0.alignment = .top
|
$0.alignment = .top
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -70,6 +70,20 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
internal var bottomContainerView: UIView = {
|
||||||
|
return UIView().with {
|
||||||
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
internal var bottomContainerStackView: UIStackView = {
|
||||||
|
return UIStackView().with {
|
||||||
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
$0.axis = .vertical
|
||||||
|
$0.distribution = .fill
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Configuration Properties
|
// MARK: - Configuration Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -93,11 +107,15 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal var borderColorConfiguration = ControlColorConfiguration().with {
|
internal var borderColorConfiguration = ControlColorConfiguration().with {
|
||||||
$0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOnlight, forState: .normal)
|
$0.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOndark, forState: .normal)
|
||||||
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled)
|
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled)
|
||||||
$0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error)
|
$0.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal var readOnlyBorderColorConfiguration = ControlColorConfiguration().with {
|
||||||
|
$0.setSurfaceColors(VDSFormControlsColor.borderReadonlyOnlight, VDSFormControlsColor.borderReadonlyOndark, forState: .normal)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Properties
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -135,19 +153,42 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
/// Whether not to show the error.
|
/// Whether not to show the error.
|
||||||
open var showError: Bool = false { didSet { setNeedsUpdate() } }
|
open var showError: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// Whether or not to show the internal error
|
||||||
|
open internal(set) var hasInternalError: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// Override UIControl state to add the .error state if showError is true.
|
/// Override UIControl state to add the .error state if showError is true.
|
||||||
open override var state: UIControl.State {
|
open override var state: UIControl.State {
|
||||||
get {
|
get {
|
||||||
var state = super.state
|
var state = super.state
|
||||||
if showError {
|
if showError || hasInternalError {
|
||||||
state.insert(.error)
|
state.insert(.error)
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open var errorText: String? {
|
||||||
|
didSet {
|
||||||
|
updateContainerView()
|
||||||
|
updateErrorLabel()
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var internalErrorText: String? {
|
||||||
|
didSet {
|
||||||
|
updateContainerView()
|
||||||
|
updateErrorLabel()
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Override this to conveniently get/set the textfield(s).
|
||||||
|
open var text: String? {
|
||||||
|
get { nil }
|
||||||
|
set { fatalError("You MUST override EntryField's 'text' variable in your subclass.") }
|
||||||
|
}
|
||||||
|
|
||||||
open var errorText: String? { didSet { setNeedsUpdate() } }
|
|
||||||
|
|
||||||
open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } }
|
open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
open var transparentBackground: Bool = false { didSet { setNeedsUpdate() } }
|
open var transparentBackground: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
@ -157,8 +198,20 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
open var maxLength: Int? { didSet { setNeedsUpdate() } }
|
open var maxLength: Int? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
open var inputId: String? { didSet { setNeedsUpdate() } }
|
open var inputId: String? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// The text of this textField.
|
||||||
|
private var _value: AnyHashable?
|
||||||
|
open var value: AnyHashable? {
|
||||||
|
get { _value }
|
||||||
|
set {
|
||||||
|
if let newValue, newValue != _value {
|
||||||
|
_value = newValue
|
||||||
|
text = newValue as? String
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
open var value: AnyHashable? { didSet { setNeedsUpdate() } }
|
|
||||||
|
|
||||||
open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } }
|
open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
@ -184,7 +237,7 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
|
|
||||||
//create the wrapping view
|
//create the wrapping view
|
||||||
heightConstraint = containerView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height)
|
heightConstraint = containerView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height)
|
||||||
widthConstraint?.priority = .defaultHigh
|
heightConstraint?.priority = .defaultHigh
|
||||||
heightConstraint?.isActive = true
|
heightConstraint?.isActive = true
|
||||||
|
|
||||||
widthConstraint = containerView.widthAnchor.constraint(equalToConstant: 0)
|
widthConstraint = containerView.widthAnchor.constraint(equalToConstant: 0)
|
||||||
@ -203,15 +256,26 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
//add the view to add input fields
|
//add the view to add input fields
|
||||||
containerStackView.addArrangedSubview(controlContainerView)
|
containerStackView.addArrangedSubview(controlContainerView)
|
||||||
containerStackView.addArrangedSubview(icon)
|
containerStackView.addArrangedSubview(icon)
|
||||||
|
containerStackView.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: controlContainerView)
|
||||||
|
|
||||||
|
//get the container this is what show helper text, error text
|
||||||
|
//can include other for character count, max length
|
||||||
|
let bottomContainer = getBottomContainer()
|
||||||
|
|
||||||
|
//add bottomContainerStackView
|
||||||
|
//this is the vertical stack that contains error text, helper text
|
||||||
|
bottomContainer.addSubview(bottomContainerStackView)
|
||||||
|
bottomContainerStackView.pinToSuperView()
|
||||||
|
bottomContainerStackView.addArrangedSubview(errorLabel)
|
||||||
|
bottomContainerStackView.addArrangedSubview(helperLabel)
|
||||||
|
|
||||||
stackView.addArrangedSubview(titleLabel)
|
stackView.addArrangedSubview(titleLabel)
|
||||||
stackView.addArrangedSubview(container)
|
stackView.addArrangedSubview(container)
|
||||||
stackView.addArrangedSubview(errorLabel)
|
stackView.addArrangedSubview(bottomContainer)
|
||||||
stackView.addArrangedSubview(helperLabel)
|
|
||||||
|
|
||||||
stackView.setCustomSpacing(4, after: titleLabel)
|
stackView.setCustomSpacing(4, after: titleLabel)
|
||||||
stackView.setCustomSpacing(8, after: container)
|
stackView.setCustomSpacing(8, after: container)
|
||||||
stackView.setCustomSpacing(8, after: errorLabel)
|
stackView.setCustomSpacing(8, after: bottomContainer)
|
||||||
|
|
||||||
stackView
|
stackView
|
||||||
.pinTop()
|
.pinTop()
|
||||||
@ -254,11 +318,7 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
|
|
||||||
containerView.backgroundColor = backgroundColorConfiguration.getColor(self)
|
updateContainerView()
|
||||||
containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor
|
|
||||||
containerView.layer.borderWidth = VDSFormControls.widthBorder
|
|
||||||
containerView.layer.cornerRadius = VDSFormControls.borderradius
|
|
||||||
|
|
||||||
updateTitleLabel()
|
updateTitleLabel()
|
||||||
updateErrorLabel()
|
updateErrorLabel()
|
||||||
updateHelperLabel()
|
updateHelperLabel()
|
||||||
@ -266,6 +326,16 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
backgroundColor = surface.color
|
backgroundColor = surface.color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Methods
|
||||||
|
//--------------------------------------------------
|
||||||
|
private func updateContainerView() {
|
||||||
|
containerView.backgroundColor = backgroundColorConfiguration.getColor(self)
|
||||||
|
containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor
|
||||||
|
containerView.layer.borderWidth = VDSFormControls.widthBorder
|
||||||
|
containerView.layer.cornerRadius = VDSFormControls.borderradius
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Methods
|
// MARK: - Public Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -273,6 +343,11 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
open func getContainer() -> UIView {
|
open func getContainer() -> UIView {
|
||||||
return containerView
|
return containerView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Container for the area in which helper or error text presents.
|
||||||
|
open func getBottomContainer() -> UIView {
|
||||||
|
return bottomContainerView
|
||||||
|
}
|
||||||
|
|
||||||
open func updateTitleLabel() {
|
open func updateTitleLabel() {
|
||||||
|
|
||||||
@ -305,7 +380,16 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func updateErrorLabel(){
|
open func updateErrorLabel(){
|
||||||
if showError, let errorText {
|
if showError, hasInternalError, let errorText, let internalErrorText {
|
||||||
|
errorLabel.text = [internalErrorText, errorText].joined(separator: "\n")
|
||||||
|
errorLabel.surface = surface
|
||||||
|
errorLabel.isEnabled = isEnabled
|
||||||
|
errorLabel.isHidden = false
|
||||||
|
icon.name = .error
|
||||||
|
icon.color = VDSColor.paletteBlack
|
||||||
|
icon.surface = surface
|
||||||
|
icon.isHidden = !isEnabled
|
||||||
|
} else if showError, let errorText {
|
||||||
errorLabel.text = errorText
|
errorLabel.text = errorText
|
||||||
errorLabel.surface = surface
|
errorLabel.surface = surface
|
||||||
errorLabel.isEnabled = isEnabled
|
errorLabel.isEnabled = isEnabled
|
||||||
@ -314,6 +398,15 @@ open class EntryFieldBase: Control, Changeable {
|
|||||||
icon.color = VDSColor.paletteBlack
|
icon.color = VDSColor.paletteBlack
|
||||||
icon.surface = surface
|
icon.surface = surface
|
||||||
icon.isHidden = !isEnabled
|
icon.isHidden = !isEnabled
|
||||||
|
} else if hasInternalError, let internalErrorText {
|
||||||
|
errorLabel.text = internalErrorText
|
||||||
|
errorLabel.surface = surface
|
||||||
|
errorLabel.isEnabled = isEnabled
|
||||||
|
errorLabel.isHidden = false
|
||||||
|
icon.name = .error
|
||||||
|
icon.color = VDSColor.paletteBlack
|
||||||
|
icon.surface = surface
|
||||||
|
icon.isHidden = !isEnabled
|
||||||
} else {
|
} else {
|
||||||
icon.isHidden = true
|
icon.isHidden = true
|
||||||
errorLabel.isHidden = true
|
errorLabel.isHidden = true
|
||||||
|
|||||||
@ -78,6 +78,18 @@ open class InputField: EntryFieldBase, UITextFieldDelegate {
|
|||||||
/// Representing the type of input.
|
/// Representing the type of input.
|
||||||
open var fieldType: FieldType = .text { didSet { setNeedsUpdate() } }
|
open var fieldType: FieldType = .text { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// The text of this textField.
|
||||||
|
open override var text: String? {
|
||||||
|
get { textField.text }
|
||||||
|
set {
|
||||||
|
if let newValue, newValue != text {
|
||||||
|
textField.text = newValue
|
||||||
|
value = newValue
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var _showError: Bool = false
|
var _showError: Bool = false
|
||||||
/// Whether not to show the error.
|
/// Whether not to show the error.
|
||||||
open override var showError: Bool {
|
open override var showError: Bool {
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
// Created by Matt Bruce on 1/10/23.
|
// Created by Matt Bruce on 1/10/23.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
@ -36,64 +35,147 @@ open class TextArea: EntryFieldBase {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
internal var minWidthConstraint: NSLayoutConstraint?
|
internal var minWidthConstraint: NSLayoutConstraint?
|
||||||
internal var textViewHeightConstraint: NSLayoutConstraint?
|
internal var textViewHeightConstraint: NSLayoutConstraint?
|
||||||
|
|
||||||
internal var inputFieldStackView: UIStackView = {
|
internal var inputFieldStackView: UIStackView = {
|
||||||
return UIStackView().with {
|
return UIStackView().with {
|
||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
$0.axis = .horizontal
|
$0.axis = .horizontal
|
||||||
$0.distribution = .fill
|
$0.distribution = .fill
|
||||||
$0.spacing = 12
|
$0.spacing = VDSLayout.Spacing.space3X.value
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
internal var bottomView: UIView = {
|
||||||
|
return UIView().with {
|
||||||
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
internal var bottomStackView: UIStackView = {
|
||||||
|
return UIStackView().with {
|
||||||
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
$0.axis = .horizontal
|
||||||
|
$0.distribution = .fill
|
||||||
|
$0.alignment = .top
|
||||||
|
$0.spacing = VDSLayout.Spacing.space2X.value
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
open var characterCounterLabel = Label().with {
|
||||||
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
$0.textStyle = .bodySmall
|
||||||
|
$0.textAlignment = .right
|
||||||
|
$0.numberOfLines = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _minHeight: Height = .twoX
|
||||||
|
|
||||||
|
open var minHeight: Height? {
|
||||||
|
get { return _minHeight }
|
||||||
|
set {
|
||||||
|
if let newValue {
|
||||||
|
_minHeight = newValue
|
||||||
|
} else {
|
||||||
|
_minHeight = .twoX
|
||||||
|
}
|
||||||
|
textViewHeightConstraint?.constant = _minHeight.value
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Properties
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
override var containerSize: CGSize { CGSize(width: 45, height: 88) }
|
override var containerSize: CGSize { CGSize(width: 182, height: 88) }
|
||||||
|
|
||||||
|
/// Enum used to describe the the height of TextArea.
|
||||||
|
public enum Height: String, CaseIterable {
|
||||||
|
case twoX = "2X"
|
||||||
|
case fourX = "4X"
|
||||||
|
case eightX = "8X"
|
||||||
|
|
||||||
|
var value: CGFloat {
|
||||||
|
switch self {
|
||||||
|
case .twoX:
|
||||||
|
88
|
||||||
|
case .fourX:
|
||||||
|
176
|
||||||
|
case .eightX:
|
||||||
|
352
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The text of this textView
|
||||||
|
open override var text: String? {
|
||||||
|
get { textView.text }
|
||||||
|
set {
|
||||||
|
if let newValue, newValue != text {
|
||||||
|
textView.text = newValue
|
||||||
|
value = newValue
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// UITextView shown in the TextArea.
|
/// UITextView shown in the TextArea.
|
||||||
open var textView = UITextView().with {
|
open var textView = TextView().with {
|
||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
$0.font = TextStyle.bodyLarge.font
|
|
||||||
$0.sizeToFit()
|
$0.sizeToFit()
|
||||||
$0.isScrollEnabled = false
|
$0.isScrollEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Color configuration for the textView.
|
/// Color configuration for error icon.
|
||||||
open var textViewTextColorConfiguration: AnyColorable = ViewColorConfiguration().with {
|
internal var iconColorConfiguration = ControlColorConfiguration().with {
|
||||||
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true)
|
$0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal)
|
||||||
$0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false)
|
}
|
||||||
}.eraseToAnyColorable() { didSet { setNeedsUpdate() } }
|
|
||||||
|
/// Color configuration for character counter's highlight background color
|
||||||
|
internal var highlightBackgroundColor = ControlColorConfiguration().with {
|
||||||
|
$0.setSurfaceColors(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryLight, forState: .normal)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Color configuration for character counter's highlight text color
|
||||||
|
internal var highlightTextColor = ControlColorConfiguration().with {
|
||||||
|
$0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .normal)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||||
open override func setup() {
|
open override func setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
|
accessibilityLabel = "TextArea"
|
||||||
|
|
||||||
minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 0)
|
containerStackView.pinToSuperView(.uniform(VDSFormControls.spaceInset))
|
||||||
|
minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: containerSize.width)
|
||||||
minWidthConstraint?.isActive = true
|
minWidthConstraint?.isActive = true
|
||||||
|
|
||||||
controlContainerView.addSubview(textView)
|
controlContainerView.addSubview(textView)
|
||||||
textView
|
textView
|
||||||
.pinTop()
|
.pinTop()
|
||||||
.pinLeading()
|
.pinLeading()
|
||||||
.pinTrailingLessThanOrEqualTo(nil, 0, .defaultHigh)
|
.pinTrailingLessThanOrEqualTo(nil, 0, .defaultHigh)
|
||||||
.pinBottom(0, .defaultHigh)
|
.pinBottom(0, .defaultHigh)
|
||||||
|
textView.isScrollEnabled = true
|
||||||
textViewHeightConstraint = textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 64)
|
textView.autocorrectionType = .no
|
||||||
|
textViewHeightConstraint = textView.heightAnchor.constraint(greaterThanOrEqualToConstant: containerSize.height)
|
||||||
textViewHeightConstraint?.isActive = true
|
textViewHeightConstraint?.isActive = true
|
||||||
backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: .success)
|
backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: .success)
|
||||||
borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success)
|
borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success)
|
||||||
|
borderColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .focused)
|
||||||
textView.delegate = self
|
textView.delegate = self
|
||||||
|
characterCounterLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
|
||||||
|
bottomContainerStackView.spacing = VDSLayout.Spacing.space2X.value
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
open override func reset() {
|
open override func reset() {
|
||||||
super.reset()
|
super.reset()
|
||||||
textView.text = ""
|
textView.text = ""
|
||||||
|
characterCounterLabel.reset()
|
||||||
|
characterCounterLabel.textStyle = .bodySmall
|
||||||
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Container for the area in which the user interacts.
|
/// Container for the area in which the user interacts.
|
||||||
@ -107,7 +189,8 @@ open class TextArea: EntryFieldBase {
|
|||||||
super.updateView()
|
super.updateView()
|
||||||
|
|
||||||
textView.isEditable = isEnabled
|
textView.isEditable = isEnabled
|
||||||
textView.textColor = textViewTextColorConfiguration.getColor(self)
|
textView.isEnabled = isEnabled
|
||||||
|
textView.surface = surface
|
||||||
|
|
||||||
//set the width constraints
|
//set the width constraints
|
||||||
if let width {
|
if let width {
|
||||||
@ -119,6 +202,80 @@ open class TextArea: EntryFieldBase {
|
|||||||
widthConstraint?.isActive = false
|
widthConstraint?.isActive = false
|
||||||
minWidthConstraint?.isActive = true
|
minWidthConstraint?.isActive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let characterError = getCharacterCounterText()
|
||||||
|
if let maxLength, maxLength > 0 {
|
||||||
|
characterCounterLabel.text = characterError
|
||||||
|
} else {
|
||||||
|
characterCounterLabel.text = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
icon.size = .medium
|
||||||
|
icon.color = iconColorConfiguration.getColor(self)
|
||||||
|
containerView.layer.borderColor = readOnly ? readOnlyBorderColorConfiguration.getColor(self).cgColor : borderColorConfiguration.getColor(self).cgColor
|
||||||
|
textView.isEditable = readOnly ? false : true
|
||||||
|
textView.backgroundColor = backgroundColorConfiguration.getColor(self)
|
||||||
|
textView.tintColor = iconColorConfiguration.getColor(self)
|
||||||
|
characterCounterLabel.surface = surface
|
||||||
|
highlightCharacterOverflow()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Container for the area showing helper text, error text, character count, maximum length value.
|
||||||
|
open override func getBottomContainer() -> UIView {
|
||||||
|
bottomView.addSubview(bottomStackView)
|
||||||
|
bottomStackView.pinToSuperView()
|
||||||
|
bottomStackView.addArrangedSubview(bottomContainerView)
|
||||||
|
bottomStackView.addArrangedSubview(characterCounterLabel)
|
||||||
|
return bottomView
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to update any Accessibility properties.
|
||||||
|
open override func updateAccessibility() {
|
||||||
|
super.updateAccessibility()
|
||||||
|
if showError {
|
||||||
|
setAccessibilityLabel(for: [titleLabel, textView, errorLabel, helperLabel])
|
||||||
|
} else {
|
||||||
|
setAccessibilityLabel(for: [titleLabel, textView, helperLabel])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Methods
|
||||||
|
//--------------------------------------------------
|
||||||
|
private func getCharacterCounterText() -> String? {
|
||||||
|
let count = textView.text.count
|
||||||
|
let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)"
|
||||||
|
if let maxLength, maxLength > 0 {
|
||||||
|
if count > maxLength {
|
||||||
|
hasInternalError = true
|
||||||
|
internalErrorText = "You have exceeded the character limit."
|
||||||
|
return countStr
|
||||||
|
} else {
|
||||||
|
hasInternalError = false
|
||||||
|
internalErrorText = nil
|
||||||
|
return ("\(countStr)" + "/" + "\(maxLength)")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hasInternalError = false
|
||||||
|
internalErrorText = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open func highlightCharacterOverflow() {
|
||||||
|
let count = textView.text.count
|
||||||
|
guard let maxLength, maxLength > 0, count > maxLength else {
|
||||||
|
textView.textAttributes = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var textAttributes = [any LabelAttributeModel]()
|
||||||
|
let location = maxLength
|
||||||
|
let length = count - maxLength
|
||||||
|
textAttributes.append(ColorLabelAttribute(location: location, length: length, color: highlightBackgroundColor.getColor(self), isForegroundColor: false))
|
||||||
|
textAttributes.append(ColorLabelAttribute(location: location, length: length, color: highlightTextColor.getColor(self), isForegroundColor: true))
|
||||||
|
|
||||||
|
textView.textAttributes = textAttributes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,27 +284,42 @@ extension TextArea: UITextViewDelegate {
|
|||||||
// MARK: - UITextViewDelegate
|
// MARK: - UITextViewDelegate
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
public func textViewDidChange(_ textView: UITextView) {
|
public func textViewDidChange(_ textView: UITextView) {
|
||||||
|
|
||||||
//dynamic textView Height sizing based on Figma
|
//dynamic textView Height sizing based on Figma
|
||||||
//if you want it to work "as-is" delete this code
|
//if you want it to work "as-is" delete this code
|
||||||
//since it will autogrow with the current settings
|
//since it will autogrow with the current settings
|
||||||
if let textViewHeightConstraint, textView.isEditable {
|
if let textViewHeightConstraint, textView.isEditable {
|
||||||
let height = textView.frame.size.height
|
var height = textView.contentSize.height
|
||||||
let constraintHeight = textViewHeightConstraint.constant
|
height = max(height, _minHeight.value)
|
||||||
if height > constraintHeight {
|
if height > Height.twoX.value && height <= Height.fourX.value {
|
||||||
if height > 64 && height < 152 {
|
textViewHeightConstraint.constant = Height.fourX.value
|
||||||
textViewHeightConstraint.constant = 152
|
} else if height > Height.fourX.value {
|
||||||
} else if height > 152 {
|
textViewHeightConstraint.constant = Height.eightX.value
|
||||||
textViewHeightConstraint.constant = 328
|
} else {
|
||||||
} else {
|
textViewHeightConstraint.constant = Height.twoX.value
|
||||||
textViewHeightConstraint.constant = 64
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//setting the value and firing control event
|
//The exceeding characters will be highlighted to help users correct their entry.
|
||||||
value = textView.text
|
if let maxLength, maxLength > 0 {
|
||||||
sendActions(for: .valueChanged)
|
// allow - 20% of character limit
|
||||||
|
let overflowLimit = Double(maxLength) * 0.20
|
||||||
|
let allowCharCount = Int(overflowLimit) + maxLength
|
||||||
|
|
||||||
}
|
if textView.text.count <= allowCharCount {
|
||||||
|
highlightCharacterOverflow()
|
||||||
|
|
||||||
|
//setting the value and firing control event
|
||||||
|
text = textView.text
|
||||||
|
sendActions(for: .valueChanged)
|
||||||
|
} else {
|
||||||
|
textView.text.removeLast()
|
||||||
|
highlightCharacterOverflow()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//setting the value and firing control event
|
||||||
|
text = textView.text
|
||||||
|
sendActions(for: .valueChanged)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
VDS/Components/TextFields/TextArea/TextAreaChangeLog.txt
Normal file
38
VDS/Components/TextFields/TextArea/TextAreaChangeLog.txt
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
MM/DD/YYYY
|
||||||
|
----------------
|
||||||
|
- Initial Brand 3.0 handoff
|
||||||
|
|
||||||
|
12/27/2021
|
||||||
|
----------------
|
||||||
|
- Removed Max idth Updated the SPECS with FormControl tokens
|
||||||
|
|
||||||
|
02/25/2022
|
||||||
|
----------------
|
||||||
|
- Replaced Info and Error Non-Scaling icons with VDS Icon.
|
||||||
|
- Removed “weight” and “vector effect” from Anatomy and States.
|
||||||
|
|
||||||
|
07/27/2022
|
||||||
|
----------------
|
||||||
|
- Added Configurations section with transparentBackground principles.
|
||||||
|
|
||||||
|
08/10/2022
|
||||||
|
----------------
|
||||||
|
- Updated default and inverted prop to light and dark surface.
|
||||||
|
|
||||||
|
11/30/2022
|
||||||
|
----------------
|
||||||
|
- Added "(web only)" to any instance of "keyboard focus"
|
||||||
|
|
||||||
|
12/13/2022
|
||||||
|
----------------
|
||||||
|
- Replaced form border and focus border pixel values and style & spacing with tokens.
|
||||||
|
|
||||||
|
01/18/2023
|
||||||
|
----------------
|
||||||
|
- Updated Anatomy items:
|
||||||
|
- Added “Highlight” to item #10
|
||||||
|
- Changed item #7 to “Tooltip” from “Tooltip Component”
|
||||||
|
|
||||||
|
04/12/2023
|
||||||
|
----------------
|
||||||
|
- Updated hex colors for updated feedback tokens in error states.
|
||||||
150
VDS/Components/TextFields/TextArea/TextView.swift
Normal file
150
VDS/Components/TextFields/TextArea/TextView.swift
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
//
|
||||||
|
// TextView.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 2/29/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Combine
|
||||||
|
import VDSColorTokens
|
||||||
|
|
||||||
|
@objc(VDSTextView)
|
||||||
|
open class TextView: UITextView, ViewProtocol {
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Initializers
|
||||||
|
//--------------------------------------------------
|
||||||
|
required public init() {
|
||||||
|
super.init(frame: .zero, textContainer: nil)
|
||||||
|
initialSetup()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override init(frame: CGRect, textContainer: NSTextContainer?) {
|
||||||
|
super.init(frame: frame, textContainer: textContainer)
|
||||||
|
initialSetup()
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
initialSetup()
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Combine Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// Set of Subscribers for any Publishers for this Control.
|
||||||
|
open var subscribers = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
private var initialSetupPerformed = false
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||||
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
|
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// Array of LabelAttributeModel objects used in rendering the text.
|
||||||
|
open var textAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// TextStyle used on the titleLabel.
|
||||||
|
open var textStyle: TextStyle { .defaultStyle }
|
||||||
|
|
||||||
|
/// Will determine if a scaled font should be used for the titleLabel font.
|
||||||
|
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
open var isEnabled: Bool = true { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
open var textColorConfiguration: AnyColorable = ViewColorConfiguration().with {
|
||||||
|
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true)
|
||||||
|
$0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false)
|
||||||
|
}.eraseToAnyColorable(){ didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
open override var textColor: UIColor? {
|
||||||
|
get { textColorConfiguration.getColor(self) }
|
||||||
|
set { }
|
||||||
|
}
|
||||||
|
|
||||||
|
override public var text: String! {
|
||||||
|
get { super.text }
|
||||||
|
set {
|
||||||
|
super.text = newValue
|
||||||
|
updateLabel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public var textAlignment: NSTextAlignment {
|
||||||
|
didSet {
|
||||||
|
if textAlignment != oldValue {
|
||||||
|
// Text alignment can be part of our paragraph style, so we may need to
|
||||||
|
// re-style when changed
|
||||||
|
updateLabel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
//--------------------------------------------------
|
||||||
|
open func initialSetup() {
|
||||||
|
if !initialSetupPerformed {
|
||||||
|
backgroundColor = .clear
|
||||||
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
accessibilityCustomActions = []
|
||||||
|
setup()
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open func setup() {
|
||||||
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
}
|
||||||
|
|
||||||
|
open func updateView() {
|
||||||
|
updateLabel()
|
||||||
|
}
|
||||||
|
|
||||||
|
open func updateAccessibility() {}
|
||||||
|
|
||||||
|
open func reset() {
|
||||||
|
shouldUpdateView = false
|
||||||
|
surface = .light
|
||||||
|
text = nil
|
||||||
|
accessibilityCustomActions = []
|
||||||
|
shouldUpdateView = true
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Methods
|
||||||
|
//--------------------------------------------------
|
||||||
|
private func updateLabel() {
|
||||||
|
|
||||||
|
//clear the arrays holding actions
|
||||||
|
accessibilityCustomActions = []
|
||||||
|
if let text, !text.isEmpty {
|
||||||
|
//create the primary string
|
||||||
|
let mutableText = NSMutableAttributedString.mutableText(for: text,
|
||||||
|
textStyle: textStyle,
|
||||||
|
useScaledFont: useScaledFont,
|
||||||
|
textColor: textColor!,
|
||||||
|
alignment: textAlignment,
|
||||||
|
lineBreakMode: .byWordWrapping)
|
||||||
|
//apply any attributes
|
||||||
|
if let attributes = textAttributes {
|
||||||
|
mutableText.apply(attributes: attributes)
|
||||||
|
}
|
||||||
|
attributedText = mutableText
|
||||||
|
} else {
|
||||||
|
attributedText = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -189,9 +189,15 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
|
|||||||
// MARK: - Configuration
|
// MARK: - Configuration
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private let cornerRadius = VDSFormControls.borderradius * 2
|
private let cornerRadius = VDSFormControls.borderradius * 2
|
||||||
|
|
||||||
private var backgroundColorConfiguration = BackgroundColorConfiguration()
|
private var backgroundColorConfiguration = BackgroundColorConfiguration()
|
||||||
private var dropshadowConfiguration = DropshadowConfiguration()
|
private let dropShadowConfiguration = DropShadowConfiguration().with {
|
||||||
|
$0.shadowColorConfiguration = SurfaceColorConfiguration().with {
|
||||||
|
$0.lightColor = VDSColor.elementsPrimaryOnlight
|
||||||
|
}.eraseToAnyColorable()
|
||||||
|
$0.shadowOffsetConfiguration = .init(.init(width: 0, height: 6), .zero)
|
||||||
|
$0.shadowRadiusConfiguration = .init(3.0, 0.0)
|
||||||
|
$0.shadowOpacityConfiguration = .init(0.01, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
private var borderColorConfiguration = SurfaceColorConfiguration().with {
|
private var borderColorConfiguration = SurfaceColorConfiguration().with {
|
||||||
$0.lightColor = VDSColor.elementsLowcontrastOnlight
|
$0.lightColor = VDSColor.elementsLowcontrastOnlight
|
||||||
@ -315,7 +321,7 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
|
|||||||
heightConstraint?.isActive = false
|
heightConstraint?.isActive = false
|
||||||
}
|
}
|
||||||
if showDropShadows, surface == .light {
|
if showDropShadows, surface == .light {
|
||||||
addDropShadow(dropshadowConfiguration)
|
addDropShadow(dropShadowConfiguration)
|
||||||
} else {
|
} else {
|
||||||
removeDropShadows()
|
removeDropShadows()
|
||||||
}
|
}
|
||||||
@ -401,15 +407,6 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
|
|||||||
|
|
||||||
extension TileContainerBase {
|
extension TileContainerBase {
|
||||||
|
|
||||||
struct DropshadowConfiguration: Dropshadowable {
|
|
||||||
var shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().with {
|
|
||||||
$0.lightColor = VDSColor.elementsPrimaryOnlight
|
|
||||||
}.eraseToAnyColorable()
|
|
||||||
var shadowOpacity: CGFloat = 0.01
|
|
||||||
var shadowOffset: CGSize = .init(width: 0, height: 6)
|
|
||||||
var shadowRadius: CGFloat = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
final class BackgroundColorConfiguration: ObjectColorable {
|
final class BackgroundColorConfiguration: ObjectColorable {
|
||||||
|
|
||||||
typealias ObjectType = TileContainerBase
|
typealias ObjectType = TileContainerBase
|
||||||
|
|||||||
@ -15,7 +15,7 @@ extension Tilelet {
|
|||||||
// MARK: - Enums
|
// MARK: - Enums
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Enum used to describe the textStyle of the subTitle label.
|
/// Enum used to describe the textStyle of the subTitle label.
|
||||||
public enum StandardStyle: String, EnumSubset {
|
public enum OtherStandardStyle: String, EnumSubset {
|
||||||
case bodyLarge
|
case bodyLarge
|
||||||
case bodyMedium
|
case bodyMedium
|
||||||
case bodySmall
|
case bodySmall
|
||||||
@ -30,7 +30,7 @@ extension Tilelet {
|
|||||||
public var text: String = ""
|
public var text: String = ""
|
||||||
|
|
||||||
/// Text style that will be used for the subTitle label.
|
/// Text style that will be used for the subTitle label.
|
||||||
public var standardStyle: StandardStyle = .bodySmall
|
public var otherStandardStyle: OtherStandardStyle = .bodySmall
|
||||||
|
|
||||||
/// Text attributes that will be used for the subTitle label.
|
/// Text attributes that will be used for the subTitle label.
|
||||||
public var textAttributes: [any LabelAttributeModel]?
|
public var textAttributes: [any LabelAttributeModel]?
|
||||||
@ -45,11 +45,14 @@ extension Tilelet {
|
|||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
public init(text: String,
|
public init(text: String,
|
||||||
|
otherStandardStyle: OtherStandardStyle = .bodySmall,
|
||||||
textColor: Use = .primary,
|
textColor: Use = .primary,
|
||||||
textAttributes: [any LabelAttributeModel]? = nil,
|
textAttributes: [any LabelAttributeModel]? = nil,
|
||||||
standardStyle: StandardStyle = .bodySmall,
|
standardStyle: StandardStyle = .bodySmall,
|
||||||
lineBreakMode: NSLineBreakMode = .byTruncatingTail) {
|
lineBreakMode: NSLineBreakMode = .byTruncatingTail) {
|
||||||
|
textAttributes: [any LabelAttributeModel]? = nil) {
|
||||||
self.text = text
|
self.text = text
|
||||||
|
self.otherStandardStyle = otherStandardStyle
|
||||||
self.textAttributes = textAttributes
|
self.textAttributes = textAttributes
|
||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.standardStyle = standardStyle
|
self.standardStyle = standardStyle
|
||||||
@ -62,7 +65,7 @@ extension Tilelet {
|
|||||||
/// Converts this type of model to a TitleLockup.SubTitleModel.
|
/// Converts this type of model to a TitleLockup.SubTitleModel.
|
||||||
public func toTitleLockupSubTitleModel() -> TitleLockup.SubTitleModel {
|
public func toTitleLockupSubTitleModel() -> TitleLockup.SubTitleModel {
|
||||||
TitleLockup.SubTitleModel(text: text,
|
TitleLockup.SubTitleModel(text: text,
|
||||||
standardStyle: standardStyle.value,
|
otherStandardStyle: otherStandardStyle.value,
|
||||||
textColor: textColor,
|
textColor: textColor,
|
||||||
textAttributes: textAttributes, lineBreakMode: lineBreakMode)
|
textAttributes: textAttributes, lineBreakMode: lineBreakMode)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ open class TitleLockup: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private var otherStandardStyle: OtherStandardStyle {
|
private var otherStandardStyle: OtherStandardStyle {
|
||||||
if let subTitleModel, !subTitleModel.text.isEmpty {
|
if let subTitleModel, !subTitleModel.text.isEmpty {
|
||||||
return subTitleModel.standardStyle
|
return subTitleModel.otherStandardStyle
|
||||||
} else if let eyebrowModel, !eyebrowModel.text.isEmpty {
|
} else if let eyebrowModel, !eyebrowModel.text.isEmpty {
|
||||||
return eyebrowModel.standardStyle
|
return eyebrowModel.standardStyle
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -15,7 +15,7 @@ extension TitleLockup {
|
|||||||
public var text: String
|
public var text: String
|
||||||
|
|
||||||
/// Standard style that will be used for the subTitle label.
|
/// Standard style that will be used for the subTitle label.
|
||||||
public var standardStyle: OtherStandardStyle
|
public var otherStandardStyle: OtherStandardStyle
|
||||||
|
|
||||||
/// Text color used in the subtitle label.
|
/// Text color used in the subtitle label.
|
||||||
public var textColor: Use
|
public var textColor: Use
|
||||||
@ -30,13 +30,13 @@ extension TitleLockup {
|
|||||||
public var lineBreakMode: NSLineBreakMode
|
public var lineBreakMode: NSLineBreakMode
|
||||||
|
|
||||||
public init(text: String,
|
public init(text: String,
|
||||||
standardStyle: OtherStandardStyle = .bodyLarge,
|
otherStandardStyle: OtherStandardStyle = .bodyLarge,
|
||||||
textColor: Use = .primary,
|
textColor: Use = .primary,
|
||||||
textAttributes: [any LabelAttributeModel]? = nil,
|
textAttributes: [any LabelAttributeModel]? = nil,
|
||||||
numberOfLines: Int = 0,
|
numberOfLines: Int = 0,
|
||||||
lineBreakMode: NSLineBreakMode = .byWordWrapping) {
|
lineBreakMode: NSLineBreakMode = .byWordWrapping) {
|
||||||
self.text = text
|
self.text = text
|
||||||
self.standardStyle = standardStyle
|
self.otherStandardStyle = otherStandardStyle
|
||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.textAttributes = textAttributes
|
self.textAttributes = textAttributes
|
||||||
self.numberOfLines = numberOfLines
|
self.numberOfLines = numberOfLines
|
||||||
@ -44,7 +44,7 @@ extension TitleLockup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// TextStyle used to render the text.
|
/// TextStyle used to render the text.
|
||||||
public var textStyle: TextStyle { standardStyle.value.regular }
|
public var textStyle: TextStyle { otherStandardStyle.value.regular }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
89
VDS/Protocols/DropShadowable.swift
Normal file
89
VDS/Protocols/DropShadowable.swift
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// DropShadowable.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Bandaru, Krishna Kishore on 16/02/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
/**
|
||||||
|
DropShadowable protocol helps with the configuration values for adding drop shadows for light & dark surfaces.
|
||||||
|
*/
|
||||||
|
protocol DropShadowable {
|
||||||
|
///Shadow Color configuration for light and dark surfaces
|
||||||
|
var shadowColorConfiguration: AnyColorable { get set }
|
||||||
|
///Shadow Opacity configuration for light and dark surfaces
|
||||||
|
var shadowOpacityConfiguration: SurfaceConfigurationValue<CGFloat> { get set }
|
||||||
|
///Shadow Offset configuration for light and dark surfaces
|
||||||
|
var shadowOffsetConfiguration: SurfaceConfigurationValue<CGSize> { get set }
|
||||||
|
///Shadow Radius configuration for light and dark surfaces
|
||||||
|
var shadowRadiusConfiguration: SurfaceConfigurationValue<CGFloat> { get set }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
DropShadowableConfiguration protocol helps with multiple drop shadows configurations can be added to a view.
|
||||||
|
*/
|
||||||
|
protocol DropShadowableConfiguration {
|
||||||
|
|
||||||
|
///Configurations are the DropShadowable list, these are applied on the view
|
||||||
|
var configurations: [DropShadowable] { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Extension on ViewProtocol for adding drop shadows & gradient layer on view.
|
||||||
|
*/
|
||||||
|
extension ViewProtocol where Self: UIView {
|
||||||
|
|
||||||
|
func addDropShadow(_ config: DropShadowable) {
|
||||||
|
addDropShadows([config])
|
||||||
|
}
|
||||||
|
|
||||||
|
func addDropShadows(_ configs: [DropShadowable]) {
|
||||||
|
removeDropShadows()
|
||||||
|
layer.backgroundColor = backgroundColor?.cgColor
|
||||||
|
layer.masksToBounds = false
|
||||||
|
for config in configs {
|
||||||
|
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.cornerRadius)
|
||||||
|
let shadowLayer = CALayer()
|
||||||
|
shadowLayer.shadowPath = shadowPath.cgPath
|
||||||
|
shadowLayer.frame = bounds
|
||||||
|
shadowLayer.position = .init(x: bounds.midX, y: bounds.midY)
|
||||||
|
shadowLayer.backgroundColor = backgroundColor?.cgColor
|
||||||
|
shadowLayer.cornerRadius = layer.cornerRadius
|
||||||
|
shadowLayer.shadowColor = config.shadowColorConfiguration.getColor(self).cgColor
|
||||||
|
shadowLayer.shadowOpacity = Float(config.shadowOpacityConfiguration.value(for: self))
|
||||||
|
shadowLayer.shadowOffset = config.shadowOffsetConfiguration.value(for: self)
|
||||||
|
shadowLayer.shadowRadius = config.shadowRadiusConfiguration.value(for: self)
|
||||||
|
shadowLayer.name = "dropShadowLayer"
|
||||||
|
shadowLayer.shouldRasterize = true
|
||||||
|
shadowLayer.rasterizationScale = UIScreen.main.scale
|
||||||
|
layer.insertSublayer(shadowLayer, at: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeDropShadows() {
|
||||||
|
layer.sublayers?.removeAll { $0.name == "dropShadowLayer" }
|
||||||
|
}
|
||||||
|
|
||||||
|
func addGradientLayer(with firstColor: UIColor, secondColor: UIColor) {
|
||||||
|
removeGradientLayer()
|
||||||
|
let gradientLayer = CAGradientLayer()
|
||||||
|
gradientLayer.frame = bounds
|
||||||
|
gradientLayer.startPoint = CGPoint(x: 0, y: 1)
|
||||||
|
gradientLayer.endPoint = CGPoint(x: 1, y: 0)
|
||||||
|
gradientLayer.position = center
|
||||||
|
gradientLayer.shouldRasterize = true
|
||||||
|
gradientLayer.backgroundColor = UIColor.clear.cgColor
|
||||||
|
gradientLayer.rasterizationScale = UIScreen.main.scale
|
||||||
|
gradientLayer.cornerRadius = layer.cornerRadius
|
||||||
|
gradientLayer.colors = [firstColor.cgColor, secondColor.cgColor]
|
||||||
|
gradientLayer.name = "gradientLayer"
|
||||||
|
layer.insertSublayer(gradientLayer, at: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeGradientLayer() {
|
||||||
|
layer.sublayers?.removeAll { $0.name == "gradientLayer" }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,64 +0,0 @@
|
|||||||
//
|
|
||||||
// Dropshadowable.swift
|
|
||||||
// VDS
|
|
||||||
//
|
|
||||||
// Created by Bandaru, Krishna Kishore on 16/02/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
protocol Dropshadowable {
|
|
||||||
|
|
||||||
var shadowColorConfiguration: AnyColorable { get set }
|
|
||||||
var shadowOpacity: CGFloat { get set }
|
|
||||||
var shadowOffset: CGSize { get set }
|
|
||||||
var shadowRadius: CGFloat { get set }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ViewProtocol where Self: UIView {
|
|
||||||
|
|
||||||
func addDropShadow(_ config: Dropshadowable) {
|
|
||||||
removeDropShadows()
|
|
||||||
layer.backgroundColor = backgroundColor?.cgColor
|
|
||||||
layer.masksToBounds = false
|
|
||||||
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.cornerRadius)
|
|
||||||
let shadowLayer = CALayer()
|
|
||||||
shadowLayer.shadowPath = shadowPath.cgPath
|
|
||||||
shadowLayer.frame = bounds
|
|
||||||
shadowLayer.position = center
|
|
||||||
shadowLayer.backgroundColor = UIColor.clear.cgColor
|
|
||||||
shadowLayer.cornerRadius = layer.cornerRadius
|
|
||||||
shadowLayer.shadowColor = config.shadowColorConfiguration.getColor(self).cgColor
|
|
||||||
shadowLayer.shadowOpacity = Float(config.shadowOpacity)
|
|
||||||
shadowLayer.shadowOffset = .init(width: config.shadowOffset.width, height: config.shadowOffset.height)
|
|
||||||
shadowLayer.shadowRadius = config.shadowRadius
|
|
||||||
shadowLayer.name = "dropShadowLayer"
|
|
||||||
shadowLayer.shouldRasterize = true
|
|
||||||
shadowLayer.rasterizationScale = UIScreen.main.scale
|
|
||||||
layer.insertSublayer(shadowLayer, at: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDropShadows() {
|
|
||||||
layer.sublayers?.removeAll { $0.name == "dropShadowLayer" }
|
|
||||||
}
|
|
||||||
|
|
||||||
func addGradientLayer(with firstColor: UIColor, secondColor: UIColor) {
|
|
||||||
removeGradientLayer()
|
|
||||||
let gradientLayer = CAGradientLayer()
|
|
||||||
gradientLayer.frame = bounds
|
|
||||||
gradientLayer.startPoint = CGPoint(x: 0, y: 1)
|
|
||||||
gradientLayer.endPoint = CGPoint(x: 1, y: 0)
|
|
||||||
gradientLayer.position = center
|
|
||||||
gradientLayer.shouldRasterize = true
|
|
||||||
gradientLayer.rasterizationScale = UIScreen.main.scale
|
|
||||||
gradientLayer.cornerRadius = layer.cornerRadius
|
|
||||||
gradientLayer.colors = [firstColor.cgColor, secondColor.cgColor]
|
|
||||||
gradientLayer.name = "gradientLayer"
|
|
||||||
layer.insertSublayer(gradientLayer, at: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeGradientLayer() {
|
|
||||||
layer.sublayers?.removeAll { $0.name == "gradientLayer" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
33
VDS/Utilities/DropShadowConfiguration.swift
Normal file
33
VDS/Utilities/DropShadowConfiguration.swift
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// DropShadowConfiguration.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Bandaru, Krishna Kishore on 05/03/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/**
|
||||||
|
DropShadowConfiguration confirms to DropShadowable where it has configurable properties required for drop shadow
|
||||||
|
*/
|
||||||
|
final class DropShadowConfiguration: DropShadowable, ObjectWithable {
|
||||||
|
|
||||||
|
typealias CGFloatConfigurationValue = SurfaceConfigurationValue<CGFloat>
|
||||||
|
typealias CGSizeConfigurationValue = SurfaceConfigurationValue<CGSize>
|
||||||
|
|
||||||
|
///Shadow Color configuration for light and dark surfaces
|
||||||
|
var shadowColorConfiguration: AnyColorable
|
||||||
|
///Shadow Opacity configuration for light and dark surfaces
|
||||||
|
var shadowOpacityConfiguration: CGFloatConfigurationValue
|
||||||
|
///Shadow Offset configuration for light and dark surfaces
|
||||||
|
var shadowOffsetConfiguration: CGSizeConfigurationValue
|
||||||
|
///Shadow Radius configuration for light and dark surfaces
|
||||||
|
var shadowRadiusConfiguration: CGFloatConfigurationValue
|
||||||
|
|
||||||
|
init(shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().eraseToAnyColorable(), shadowOpacity: CGFloatConfigurationValue = CGFloatConfigurationValue(1.0, 1.0), shadowOffset: CGSizeConfigurationValue = CGSizeConfigurationValue(.zero, .zero), shadowRadius: CGFloatConfigurationValue = CGFloatConfigurationValue(1.0, 1.0)) {
|
||||||
|
self.shadowColorConfiguration = shadowColorConfiguration
|
||||||
|
self.shadowOpacityConfiguration = shadowOpacity
|
||||||
|
self.shadowOffsetConfiguration = shadowOffset
|
||||||
|
self.shadowRadiusConfiguration = shadowRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
31
VDS/Utilities/SurfaceConfigurationValue.swift
Normal file
31
VDS/Utilities/SurfaceConfigurationValue.swift
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// SurfaceConfigurationValue.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Bandaru, Krishna Kishore on 05/03/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/**
|
||||||
|
SurfaceConfiguration is a type that holds the generic datatype for light surface & dark surface and returns the value based on the surface.
|
||||||
|
*/
|
||||||
|
struct SurfaceConfigurationValue<ValueType> {
|
||||||
|
|
||||||
|
var lightValue: ValueType
|
||||||
|
var darkValue: ValueType
|
||||||
|
|
||||||
|
public init(_ lightValue: ValueType, _ darkValue: ValueType) {
|
||||||
|
self.lightValue = lightValue
|
||||||
|
self.darkValue = darkValue
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(value: ValueType) {
|
||||||
|
self.lightValue = value
|
||||||
|
self.darkValue = value
|
||||||
|
}
|
||||||
|
|
||||||
|
public func value(for object: Surfaceable) -> ValueType {
|
||||||
|
object.surface == .light ? lightValue : darkValue
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user