diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index deef6cdb..580022a0 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -22,8 +22,10 @@ 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 */; }; + 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */; }; 71B23C2D2B91FA690027F7D9 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71B23C2C2B91FA690027F7D9 /* Pagination.swift */; }; 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */; }; 71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */; }; @@ -116,6 +118,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 */; }; @@ -163,7 +167,6 @@ EAEEECAF2B1FC2BA00531FC2 /* ToggleViewChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEECAE2B1FC2BA00531FC2 /* ToggleViewChangeLog.txt */; }; EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9829D4850E00101452 /* Clickable.swift */; }; EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9A29DB1A6000101452 /* Changeable.swift */; }; - EAF4A6A12BAC8B94006BCC2C /* BreadcrumbsFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF4A6A02BAC8B94006BCC2C /* BreadcrumbsFlowLayout.swift */; }; EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* CheckboxItem.swift */; }; EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0992899B17200B287F5 /* CATransaction.swift */; }; EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F09F289AB7EC00B287F5 /* View.swift */; }; @@ -208,8 +211,10 @@ 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 = ""; }; + 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TiletEyebrowModel.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 = ""; }; 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowable.swift; sourceTree = ""; }; @@ -304,6 +309,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 = ""; }; @@ -351,7 +358,6 @@ EAEEECAE2B1FC2BA00531FC2 /* ToggleViewChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ToggleViewChangeLog.txt; sourceTree = ""; }; EAF1FE9829D4850E00101452 /* Clickable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clickable.swift; sourceTree = ""; }; EAF1FE9A29DB1A6000101452 /* Changeable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Changeable.swift; sourceTree = ""; }; - EAF4A6A02BAC8B94006BCC2C /* BreadcrumbsFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbsFlowLayout.swift; sourceTree = ""; }; EAF7F0932899861000B287F5 /* CheckboxItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxItem.swift; sourceTree = ""; }; EAF7F0992899B17200B287F5 /* CATransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CATransaction.swift; sourceTree = ""; }; EAF7F09F289AB7EC00B287F5 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; @@ -396,7 +402,6 @@ isa = PBXGroup; children = ( 18A65A012B96E848006602CC /* Breadcrumbs.swift */, - EAF4A6A02BAC8B94006BCC2C /* BreadcrumbsFlowLayout.swift */, 18A65A032B96F050006602CC /* BreadcrumbItem.swift */, 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */, 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */, @@ -626,6 +631,7 @@ EAF1FE9A29DB1A6000101452 /* Changeable.swift */, EAF1FE9829D4850E00101452 /* Clickable.swift */, EAA5EEDF28F49DB3003B3210 /* Colorable.swift */, + EAACB8972B92706F006A3869 /* DefaultValuing.swift */, EA3361A9288B25E40071C351 /* Disabling.swift */, EAF978202A99035B00C2FEA9 /* Enabling.swift */, EA5E305929510F8B0082B959 /* EnumSubset.swift */, @@ -639,6 +645,7 @@ EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */, EAB1D2CC28ABE76000DAE764 /* Withable.swift */, 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */, + EAACB8992B927108006A3869 /* Valuing.swift */, 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */, ); path = Protocols; @@ -755,9 +762,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 = ""; @@ -1032,6 +1041,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 */, @@ -1088,6 +1098,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 */, @@ -1106,6 +1117,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 */, @@ -1113,6 +1125,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 */, @@ -1138,7 +1151,6 @@ 71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */, EABFEB642A26473700C4C106 /* NSAttributedString.swift in Sources */, EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */, - EAF4A6A12BAC8B94006BCC2C /* BreadcrumbsFlowLayout.swift in Sources */, EA0FC2C62914222900DF80B4 /* ButtonGroup.swift in Sources */, EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */, EA8E40932A82889500934ED3 /* TooltipDialog.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/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift index 9f0ea68b..bd04304c 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -24,6 +24,8 @@ final class BreadcrumbCellItem: UICollectionViewCell { $0.distribution = .fill $0.alignment = .fill $0.spacing = VDSLayout.Spacing.space1X.value + $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) } }() @@ -64,13 +66,14 @@ final class BreadcrumbCellItem: UICollectionViewCell { ///Updating the breadCrumbItem and UI based on the selected flag along with the surface func update(surface: Surface, hideSlash: Bool, breadCrumbItem: BreadcrumbItem) { - //remove views from stack - separator.removeFromSuperview() - self.breadCrumbItem?.removeFromSuperview() - //update surface separator.surface = surface breadCrumbItem.surface = surface + breadCrumbItem.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + breadCrumbItem.setContentHuggingPriority(.defaultLow, for: .horizontal) + + //remove previous views + stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } //add to stack stackView.addArrangedSubview(separator) @@ -80,15 +83,8 @@ final class BreadcrumbCellItem: UICollectionViewCell { //update separator separator.textColor = textColorConfiguration.getColor(surface) separator.isHidden = hideSlash - self.breadCrumbItem = breadCrumbItem layoutIfNeeded() } - - /// Remove views from StackView. - override func prepareForReuse() { - super.prepareForReuse() - separator.removeFromSuperview() - breadCrumbItem?.removeFromSuperview() - } } + diff --git a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift index 3b279551..d549dd9a 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbItem.swift @@ -44,9 +44,15 @@ open class BreadcrumbItem: ButtonBase { /// The natural size for the receiving view, considering only properties of the view itself. open override var intrinsicContentSize: CGSize { - return titleLabel?.intrinsicContentSize ?? super.intrinsicContentSize + guard let titleLabel else { return super.intrinsicContentSize } + // Calculate the titleLabel's intrinsic content size + let labelSize = titleLabel.sizeThatFits(CGSize(width: self.frame.width, height: CGFloat.greatestFiniteMagnitude)) + // Adjust the size if needed (add any additional padding if your design requires) + let adjustedSize = CGSize(width: labelSize.width + contentEdgeInsets.left + contentEdgeInsets.right, + height: labelSize.height + contentEdgeInsets.top + contentEdgeInsets.bottom) + return adjustedSize } - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -64,8 +70,8 @@ open class BreadcrumbItem: ButtonBase { open override func setup() { super.setup() isAccessibilityElement = true - accessibilityTraits = .button - accessibilityLabel = "Breadcrumb" + accessibilityTraits = .link + contentHorizontalAlignment = .leading } /// Used to make changes to the View based off a change events or from local properties. @@ -86,4 +92,11 @@ open class BreadcrumbItem: ButtonBase { shouldUpdateView = true setNeedsUpdate() } + + /// Used to update any Accessibility properties. + open override func updateAccessibility() { + super.updateAccessibility() + accessibilityLabel = "Breadcrumb \(text ?? "")" + } + } diff --git a/VDS/Components/Breadcrumbs/Breadcrumbs.swift b/VDS/Components/Breadcrumbs/Breadcrumbs.swift index 0ed7585d..2dd14d18 100644 --- a/VDS/Components/Breadcrumbs/Breadcrumbs.swift +++ b/VDS/Components/Breadcrumbs/Breadcrumbs.swift @@ -47,14 +47,17 @@ open class Breadcrumbs: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - let layout: UICollectionViewFlowLayout = BreadcrumbsFlowLayout().with { - $0.estimatedItemSize = UICollectionViewFlowLayout.automaticSize - $0.minimumInteritemSpacing = VDSLayout.Spacing.space1X.value - $0.minimumLineSpacing = VDSLayout.Spacing.space1X.value - $0.sectionInset = .zero - $0.scrollDirection = .vertical + fileprivate lazy var layout = ButtonGroupPositionLayout().with { + $0.position = .left + $0.delegate = self + $0.axisSpacer = { _, _, _ in + return VDSLayout.Spacing.space1X.value + } + $0.verticalSpacer = { _, _ in + return VDSLayout.Spacing.space1X.value + } } - + ///Collectionview to render Breadcrumb Items private lazy var collectionView: SelfSizingCollectionView = { let collectionView = SelfSizingCollectionView(frame: .zero, collectionViewLayout: layout) @@ -134,9 +137,10 @@ open class Breadcrumbs: View { self.collectionView.collectionViewLayout.invalidateLayout() } } + private var separatorWidth = Label().with { $0.text = "/"; $0.sizeToFit() }.intrinsicContentSize.width } -extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource { +extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource, ButtongGroupPositionLayoutDelegate { //-------------------------------------------------- // MARK: - UICollectionView Delegate & Datasource //-------------------------------------------------- @@ -150,5 +154,16 @@ extension Breadcrumbs: UICollectionViewDelegate, UICollectionViewDataSource { cell.update(surface: surface, hideSlash: hideSlash, breadCrumbItem: breadcrumbs[indexPath.row]) return cell } - + + public func collectionView(_ collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize { + let breadcrumb = breadcrumbs[indexPath.row] + let intrinsicSize = breadcrumb.intrinsicContentSize + let separatorFullWidth: CGFloat = indexPath.row == 0 ? 0 : VDSLayout.Spacing.space1X.value + separatorWidth + let cellwidth = intrinsicSize.width + separatorFullWidth + return .init(width: min(cellwidth, collectionView.frame.width), height: intrinsicSize.height) + } + + public func collectionView(_ collectionView: UICollectionView, buttonBaseAtIndexPath indexPath: IndexPath) -> ButtonBase { + breadcrumbs[indexPath.row] + } } diff --git a/VDS/Components/Breadcrumbs/BreadcrumbsFlowLayout.swift b/VDS/Components/Breadcrumbs/BreadcrumbsFlowLayout.swift deleted file mode 100644 index d6daf151..00000000 --- a/VDS/Components/Breadcrumbs/BreadcrumbsFlowLayout.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// BreadcrumsFlowLayout.swift -// VDS -// -// Created by Matt Bruce on 3/21/24. -// - -import Foundation -import UIKit - -class BreadcrumbsFlowLayout: UICollectionViewFlowLayout { - - override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { - let attributes = super.layoutAttributesForElements(in: rect) - - var leftMargin = sectionInset.left - var maxY: CGFloat = -1.0 - attributes?.forEach { layoutAttribute in - if layoutAttribute.frame.origin.y >= maxY { - leftMargin = sectionInset.left - } - - layoutAttribute.frame.origin.x = leftMargin - - leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing - maxY = max(layoutAttribute.frame.maxY , maxY) - } - - return attributes - } -} diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift index 150b8928..3e02465c 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift @@ -6,21 +6,18 @@ // import Foundation +import UIKit struct ButtonGroupConstants { static let defaultSpace = 12.0 - enum ButtonSpacingAxis { - case horizontal, vertical - } - /// This will determine the spacing that will go between 2 ButtonBases either horizontally or vertically /// - Parameters: /// - axis: horizontal/vertical /// - primary: first ButtonBase /// - neighboring: next ButtonBase based off of axis /// - Returns: float value - static func getSpacing(for axis: ButtonSpacingAxis, with primary: ButtonBase, neighboring: ButtonBase) -> CGFloat { + static func getSpacing(for axis: NSLayoutConstraint.Axis, with primary: ButtonBase, neighboring: ButtonBase) -> CGFloat { //large button if let button = primary as? Button, button.size == .large { diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift index f4ccf276..5d3a339f 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift @@ -146,6 +146,8 @@ class ButtonLayoutAttributes: UICollectionViewLayoutAttributes{ class ButtonGroupPositionLayout: UICollectionViewLayout { weak var delegate: ButtongGroupPositionLayoutDelegate? + var verticalSpacer: ((ButtonCollectionViewRow, ButtonCollectionViewRow?) -> CGFloat)? + var axisSpacer: ((NSLayoutConstraint.Axis, ButtonBase, ButtonBase) -> CGFloat)? // Total height of the content. Will be used to configure the scrollview content var layoutHeight: CGFloat = 0.0 @@ -154,7 +156,7 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { var buttonPercentage: CGFloat? private var itemCache: [ButtonLayoutAttributes] = [] - + override func prepare() { super.prepare() @@ -226,7 +228,7 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { let neighbor = delegate.collectionView(collectionView, buttonBaseAtIndexPath: IndexPath(item: nextItem, section: section)) // get the spacing to go between the current and next button - itemSpacing = ButtonGroupConstants.getSpacing(for: .horizontal, with: itemButtonBase, neighboring: neighbor) + itemSpacing = getAxisSpacing(for: .horizontal, with: itemButtonBase, neighboring: neighbor) } // create the custom layout attribute @@ -255,7 +257,7 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { if item > 0 { let prevRow = rows[item - 1] - rowSpacing = ButtonGroupConstants.getVerticalSpacing(for: prevRow, neighboringRow: row) + rowSpacing = getVerticalSpacing(for: prevRow, neighboringRow: row) row.rowY = layoutHeight + rowSpacing layoutHeight += rowSpacing } @@ -300,5 +302,19 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { } return collectionView.bounds.width } + + private func getAxisSpacing(for axis: NSLayoutConstraint.Axis, with primary: ButtonBase, neighboring: ButtonBase) -> CGFloat { + guard let axisSpacer else { + return ButtonGroupConstants.getSpacing(for: axis, with: primary, neighboring: neighboring) + } + return axisSpacer(axis, primary, neighboring) + } + + private func getVerticalSpacing(for row: ButtonCollectionViewRow, neighboringRow: ButtonCollectionViewRow?) -> CGFloat { + guard let verticalSpacer else { + return ButtonGroupConstants.getVerticalSpacing(for: row, neighboringRow: neighboringRow) + } + return verticalSpacer(row, neighboringRow) + } } 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