diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index b09a8626..7c65a8b0 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -20,10 +20,12 @@ 44604AD729CE196600E62B51 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD629CE196600E62B51 /* Line.swift */; }; 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; }; 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; + 710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 710607942B91A99500F2863F /* TitleletChangeLog.txt */; }; 7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; }; 71ACE89C2BA0451200FB6ADC /* PaginationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89B2BA0451200FB6ADC /* PaginationContainer.swift */; }; 71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71B23C2C2B91FA690027F7D9 /* Pagination.swift */; }; 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */; }; + 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */; }; 71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */; }; 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; }; 71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86D92B96F44C00700965 /* PaginationButton.swift */; }; @@ -114,6 +116,8 @@ EAA5EEF128F5C909003B3210 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */; }; EAA5EEF328F5C909003B3210 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */; }; EAA7456C2AB23E2000C1841F /* TooltipModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA7456B2AB23E2000C1841F /* TooltipModel.swift */; }; + EAACB8982B92706F006A3869 /* DefaultValuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8972B92706F006A3869 /* DefaultValuing.swift */; }; + EAACB89A2B927108006A3869 /* Valuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8992B927108006A3869 /* Valuing.swift */; }; EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */; }; EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CC28ABE76000DAE764 /* Withable.swift */; }; EAB1D2CF28ABEF2B00DAE764 /* Typography+Base.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CE28ABEF2B00DAE764 /* Typography+Base.swift */; }; @@ -204,10 +208,12 @@ 44604AD629CE196600E62B51 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = ""; }; 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; + 710607942B91A99500F2863F /* TitleletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TitleletChangeLog.txt; sourceTree = ""; }; 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = ""; }; 71ACE89B2BA0451200FB6ADC /* PaginationContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationContainer.swift; sourceTree = ""; }; 71B23C2C2B91FA690027F7D9 /* Pagination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pagination.swift; sourceTree = ""; }; 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = PaginationChangeLog.txt; sourceTree = ""; }; + 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TiletEyebrowModel.swift; sourceTree = ""; }; 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowable.swift; sourceTree = ""; }; 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = ""; }; 71FC86D92B96F44C00700965 /* PaginationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationButton.swift; sourceTree = ""; }; @@ -300,6 +306,8 @@ EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; EAA7456B2AB23E2000C1841F /* TooltipModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipModel.swift; sourceTree = ""; }; + EAACB8972B92706F006A3869 /* DefaultValuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultValuing.swift; sourceTree = ""; }; + EAACB8992B927108006A3869 /* Valuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Valuing.swift; sourceTree = ""; }; EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = ""; }; EAB1D2CC28ABE76000DAE764 /* Withable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = ""; }; EAB1D2CE28ABEF2B00DAE764 /* Typography+Base.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Typography+Base.swift"; sourceTree = ""; }; @@ -612,6 +620,7 @@ EAF1FE9A29DB1A6000101452 /* Changeable.swift */, EAF1FE9829D4850E00101452 /* Clickable.swift */, EAA5EEDF28F49DB3003B3210 /* Colorable.swift */, + EAACB8972B92706F006A3869 /* DefaultValuing.swift */, EA3361A9288B25E40071C351 /* Disabling.swift */, EAF978202A99035B00C2FEA9 /* Enabling.swift */, EA5E305929510F8B0082B959 /* EnumSubset.swift */, @@ -625,6 +634,7 @@ EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */, EAB1D2CC28ABE76000DAE764 /* Withable.swift */, 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */, + EAACB8992B927108006A3869 /* Valuing.swift */, 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */, ); path = Protocols; @@ -741,9 +751,11 @@ children = ( EA5E3057295105A40082B959 /* Tilelet.swift */, EA985BE529688F6A00F2FF2E /* TileletBadgeModel.swift */, - EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */, + 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */, EA985BE72968951C00F2FF2E /* TileletTitleModel.swift */, + EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */, EA985C2C296F03FE00F2FF2E /* TileletIconModels.swift */, + 710607942B91A99500F2863F /* TitleletChangeLog.txt */, ); path = Tilelet; sourceTree = ""; @@ -1018,6 +1030,7 @@ EAEEECA72B1F952000531FC2 /* TabsChangeLog.txt in Resources */, 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */, EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */, + 710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */, EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */, EAEEEC982B1F8DD100531FC2 /* LineChangeLog.txt in Resources */, EAEEECA22B1F92AD00531FC2 /* LabelChangeLog.txt in Resources */, @@ -1073,6 +1086,7 @@ EAC9258C2911C9DE00091998 /* InputField.swift in Sources */, EA3362402892EF6C0071C351 /* Label.swift in Sources */, EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */, + EAACB8982B92706F006A3869 /* DefaultValuing.swift in Sources */, EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */, 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */, EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */, @@ -1091,6 +1105,7 @@ EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */, EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */, + EAACB89A2B927108006A3869 /* Valuing.swift in Sources */, EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, @@ -1098,6 +1113,7 @@ EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */, + 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */, EAF7F11728A1475A00B287F5 /* RadioButtonItem.swift in Sources */, EA985BEE2968A92400F2FF2E /* TitleLockupSubTitleModel.swift in Sources */, EA985BF22968B5BB00F2FF2E /* TitleLockupTextStyle.swift in Sources */, diff --git a/VDS/Components/Badge/Badge.swift b/VDS/Components/Badge/Badge.swift index f6bb36d5..bbe1233d 100644 --- a/VDS/Components/Badge/Badge.swift +++ b/VDS/Components/Badge/Badge.swift @@ -147,6 +147,7 @@ open class Badge: View { label.widthGreaterThanEqualTo(constant: minWidth) maxWidthConstraint = label.widthLessThanEqualTo(constant: 0).with { $0.isActive = false } + clipsToBounds = true } /// Resets to default settings. diff --git a/VDS/Components/Notification/Notification.swift b/VDS/Components/Notification/Notification.swift index 126f83f0..cc011249 100644 --- a/VDS/Components/Notification/Notification.swift +++ b/VDS/Components/Notification/Notification.swift @@ -53,11 +53,6 @@ open class Notification: View { } } - /// Enum used to describe the orientation of Notification. - public enum Layout: String, CaseIterable { - case vertical, horizontal - } - //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -80,19 +75,9 @@ open class Notification: View { $0.translatesAutoresizingMaskIntoConstraints = false } - private var labelButtonViewSpacing: CGFloat { - let spacing: CGFloat = UIDevice.isIPad ? 20 : 16 - return switch layout { - case .vertical: - 0 - case .horizontal: - spacing - } - } + private var labelButtonViewSpacing: CGFloat { UIDevice.isIPad ? 20 : 16 } internal var onCloseSubscriber: AnyCancellable? - - private var maxWidthConstraint: NSLayoutConstraint? private var leadingConstraint: NSLayoutConstraint? @@ -172,19 +157,6 @@ open class Notification: View { /// Add this attribute determine your type of Notification. open var style: Style = .info { didSet { setNeedsUpdate()}} - - private var _layout: Layout = .vertical - - /// Determines the orientation of buttons and text in the Notification. - open var layout: Layout { - set { - if !UIDevice.isIPad, newValue == .horizontal { return } - _layout = newValue - buttonGroup.alignment = _layout == .horizontal ? .center : .left - setNeedsUpdate() - } - get { _layout } - } //-------------------------------------------------- // MARK: - Configuration @@ -219,18 +191,11 @@ open class Notification: View { return 288 } - private var maxViewWidth: CGFloat { - return 1232 - } - - private var labelViewWidthConstraint: NSLayoutConstraint? private var labelViewBottomConstraint: NSLayoutConstraint? private var labelViewAndButtonViewConstraint: NSLayoutConstraint? private var buttonViewTopConstraint: NSLayoutConstraint? private var typeIconWidthConstraint: NSLayoutConstraint? private var closeIconWidthConstraint: NSLayoutConstraint? - private var buttonGroupCenterYConstraint: NSLayoutConstraint? - private var buttonGroupBottomConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Overrides @@ -255,21 +220,18 @@ open class Notification: View { mainStackView.heightAnchor.constraint(greaterThanOrEqualToConstant: minContentHeight), layoutGuide.widthAnchor.constraint(greaterThanOrEqualToConstant: minViewWidth) ]) - maxWidthConstraint = layoutGuide.widthAnchor.constraint(lessThanOrEqualToConstant: maxViewWidth) labelButtonView.addSubview(labelsView) labelsView .pinTop() .pinLeading() - labelViewWidthConstraint = labelsView.widthAnchor.constraint(equalTo: labelButtonView.widthAnchor, multiplier: 1.0) - labelViewWidthConstraint?.activate() + labelsView.widthAnchor.constraint(equalTo: labelButtonView.widthAnchor, multiplier: 1.0).activate() labelViewBottomConstraint = labelButtonView.bottomAnchor.constraint(equalTo: labelsView.bottomAnchor) labelButtonView.addSubview(buttonGroup) buttonGroup .pinTrailing() - buttonGroupBottomConstraint = labelButtonView.bottomAnchor.constraint(equalTo: buttonGroup.bottomAnchor) - buttonGroupCenterYConstraint = buttonGroup.centerYAnchor.constraint(equalTo: labelButtonView.centerYAnchor) + labelButtonView.bottomAnchor.constraint(equalTo: buttonGroup.bottomAnchor).activate() labelViewAndButtonViewConstraint = buttonGroup.topAnchor.constraint(equalTo: labelsView.bottomAnchor, constant: VDSLayout.Spacing.space3X.value) buttonGroup.widthAnchor.constraint(equalTo: labelsView.widthAnchor).activate() @@ -314,7 +276,6 @@ open class Notification: View { closeButton.size = UIDevice.isIPad ? .medium : .small closeButton.name = .close - layout = .vertical hideCloseButton = false shouldUpdateView = true @@ -338,6 +299,16 @@ open class Notification: View { layer.cornerRadius = UIScreen.main.bounds.width == bounds.width ? 0 : 4.0 } + ///Updating the accessiblity values i.e elements, label, value other items for the component. + open override func updateAccessibility() { + super.updateAccessibility() + accessibilityElements = [closeButton, typeIcon, titleLabel, subTitleLabel, buttonGroup] + typeIcon.accessibilityLabel = style.rawValue + typeIcon.imageView.image?.isAccessibilityElement = false + closeButton.accessibilityTraits = [.button] + closeButton.accessibilityLabel = "Close Notification" + } + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- @@ -383,32 +354,21 @@ open class Notification: View { secondaryButton.onClick = secondaryButtonModel.onClick buttons.append(secondaryButton) } - labelViewWidthConstraint?.deactivate() if buttons.isEmpty { buttonGroup.isHidden = true - labelViewWidthConstraint = labelsView.widthAnchor.constraint(equalTo: labelButtonView.widthAnchor) buttonGroup.buttons.removeAll() } else { labelsView.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: subTitleLabel) buttonGroup.buttons = buttons buttonGroup.isHidden = false - labelViewWidthConstraint = labelsView.widthAnchor.constraint(equalTo: labelButtonView.widthAnchor, multiplier: layout == .vertical ? 1.0 : 0.5, constant: layout == .vertical ? 0 : -labelButtonViewSpacing) } - labelViewWidthConstraint?.activate() } private func setConstraints() { - maxWidthConstraint?.deactivate() labelViewAndButtonViewConstraint?.deactivate() labelViewBottomConstraint?.deactivate() - buttonGroupCenterYConstraint?.deactivate() - buttonGroupBottomConstraint?.deactivate() - maxWidthConstraint?.constant = maxViewWidth - maxWidthConstraint?.isActive = UIDevice.isIPad - labelViewAndButtonViewConstraint?.isActive = layout == .vertical && !buttonGroup.buttons.isEmpty - labelViewBottomConstraint?.isActive = layout == .horizontal || buttonGroup.buttons.isEmpty - buttonGroupCenterYConstraint?.isActive = layout == .horizontal - buttonGroupBottomConstraint?.isActive = layout == .vertical + labelViewAndButtonViewConstraint?.isActive = !buttonGroup.buttons.isEmpty + labelViewBottomConstraint?.isActive = buttonGroup.buttons.isEmpty typeIconWidthConstraint?.constant = typeIcon.size.dimensions.width closeIconWidthConstraint?.constant = closeButton.size.dimensions.width } diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index ae91cc7a..5d8957fe 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -13,7 +13,7 @@ import Combine /// Base Class used to build out a Input controls. @objc(VDSEntryField) -open class EntryFieldBase: Control, Changeable, FormFieldable { +open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //-------------------------------------------------- // MARK: - Initializers @@ -153,8 +153,11 @@ open class EntryFieldBase: Control, Changeable, FormFieldable { /// Whether not to show the error. open var showError: Bool = false { didSet { setNeedsUpdate() } } + /// FormFieldValidator + internal var validator: (any FormFieldValidatorable)? + /// Whether or not to show the internal error - open internal(set) var hasInternalError: Bool = false { didSet { setNeedsUpdate() } } + open var hasInternalError: Bool { !(validator?.isValid ?? true) } /// Override UIControl state to add the .error state if showError is true. open override var state: UIControl.State { @@ -175,7 +178,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldable { } } - internal var internalErrorText: String? { + open var internalErrorText: String? { didSet { updateContainerView() updateErrorLabel() @@ -200,19 +203,18 @@ open class EntryFieldBase: Control, Changeable, FormFieldable { open var inputId: String? { didSet { setNeedsUpdate() } } /// The text of this textField. - private var _value: AnyHashable? - open var value: AnyHashable? { + private var _value: String? + open var value: String? { get { _value } set { if let newValue, newValue != _value { _value = newValue - text = newValue as? String + text = newValue } setNeedsUpdate() } } - open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } open var required: Bool = false { didSet { setNeedsUpdate() } } @@ -324,6 +326,8 @@ open class EntryFieldBase: Control, Changeable, FormFieldable { updateHelperLabel() backgroundColor = surface.color + validator?.validate() + internalErrorText = validator?.errorMessage } //-------------------------------------------------- diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index ac350bc1..ebd76525 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -89,7 +89,7 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { setNeedsUpdate() } } - + var _showError: Bool = false /// Whether not to show the error. open override var showError: Bool { diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index d30c187f..b5fada0c 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -107,17 +107,19 @@ open class TextArea: EntryFieldBase { } /// The text of this textView + private var _text: String? open override var text: String? { get { textView.text } set { - if let newValue, newValue != text { + if let newValue, newValue != _text { + _text = newValue textView.text = newValue value = newValue } setNeedsUpdate() } } - + /// UITextView shown in the TextArea. open var textView = TextView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -125,6 +127,8 @@ open class TextArea: EntryFieldBase { $0.isScrollEnabled = false } + open override var maxLength: Int? { willSet { countRule.maxLength = newValue }} + /// Color configuration for error icon. internal var iconColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) @@ -147,6 +151,7 @@ open class TextArea: EntryFieldBase { open override func setup() { super.setup() accessibilityLabel = "TextArea" + validator = FormFieldValidator