diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 79deb46b..d37e3f8c 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -17,8 +17,11 @@ 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; 7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; }; 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 */; }; 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; }; + 71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86D92B96F44C00700965 /* PaginationButton.swift */; }; + 71FC86DC2B96F4C800700965 /* PaginationCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */; }; EA0B18022A9E236900F2D0CD /* SelectorGroupBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */; }; EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */; }; EA0B18062A9E2D2D00F2D0CD /* SelectorItemBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */; }; @@ -186,8 +189,11 @@ 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; 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 = ""; }; 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = ""; }; + 71FC86D92B96F44C00700965 /* PaginationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationButton.swift; sourceTree = ""; }; + 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationCellItem.swift; sourceTree = ""; }; EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorGroupBase.swift; sourceTree = ""; }; EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = ""; }; EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorItemBase.swift; sourceTree = ""; }; @@ -390,6 +396,9 @@ isa = PBXGroup; children = ( 71B23C2C2B91FA690027F7D9 /* Pagination.swift */, + 71FC86D92B96F44C00700965 /* PaginationButton.swift */, + 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */, + 71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */, ); path = Pagination; sourceTree = ""; @@ -981,6 +990,7 @@ EAEEECA92B1F969700531FC2 /* TooltipChangeLog.txt in Resources */, EAEEEC9C2B1F8F0700531FC2 /* TextLinkCaretChangeLog.txt in Resources */, EAA5EEE428F5B855003B3210 /* VerizonNHGDS-Light.otf in Resources */, + 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */, EAEEECAD2B1FC1A600531FC2 /* TitleLockupChangeLog.txt in Resources */, EAEEECAB2B1FBF2A00531FC2 /* ToggleChangeLog.txt in Resources */, ); @@ -1039,6 +1049,7 @@ EA985BEE2968A92400F2FF2E /* TitleLockupSubTitleModel.swift in Sources */, EA985BF22968B5BB00F2FF2E /* TitleLockupTextStyle.swift in Sources */, EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */, + 71FC86DC2B96F4C800700965 /* PaginationCellItem.swift in Sources */, EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */, EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */, EA985BE82968951C00F2FF2E /* TileletTitleModel.swift in Sources */, @@ -1055,6 +1066,7 @@ EAC9258F2911C9DE00091998 /* EntryFieldBase.swift in Sources */, EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */, EAD068922A560B65002E3A2D /* LoaderViewController.swift in Sources */, + 71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */, EABFEB642A26473700C4C106 /* NSAttributedString.swift in Sources */, EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */, EA0FC2C62914222900DF80B4 /* ButtonGroup.swift in Sources */, diff --git a/VDS/Components/Pagination/Pagination.swift b/VDS/Components/Pagination/Pagination.swift index 4df0a034..170e1935 100644 --- a/VDS/Components/Pagination/Pagination.swift +++ b/VDS/Components/Pagination/Pagination.swift @@ -12,37 +12,57 @@ import Combine @objc(VDSPagination) open class Pagination: View { - @Published var onPreviousTapped: PassthroughSubject = PassthroughSubject() - @Published var onNextTapped: PassthroughSubject = PassthroughSubject() - @Published var onPageWillChange: PassthroughSubject = PassthroughSubject() - @Published var onPageChanged: PassthroughSubject = PassthroughSubject() + open var onPageDidSelect: ((Int) -> Void)? + + public let previousButton: PaginationButton = .init(type: .previous) + public let nextButton: PaginationButton = .init(type: .next) public var total: Int = 0 { didSet { + previousButton.isHidden = true + nextButton.isHidden = total <= 1 + _selectedPage = 0 setNeedsUpdate() } } - public var selectedPage: Int = 0 { didSet { setNeedsUpdate() } } - private var numberOfRows: Int = 0 { - didSet { - collectionView.collectionViewLayout.invalidateLayout() + + public var selectedPage: Int { + set { + if newValue >= total { + _selectedPage = total - 1 + } else if newValue < 0 { + _selectedPage = 0 + } else { + _selectedPage = max(newValue - 1, 0) + } setNeedsUpdate() + updateSelection() + } + get { + _selectedPage } } + + private var _selectedPage: Int = 0 private let pageItemCellSize: CGSize = .init(width: 20, height: 16) private let spacingBetweenCell: CGFloat = VDSLayout.Spacing.space1X.value - private let buttonTintColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) - private let buttonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) - private lazy var collectionView: UICollectionView = { + private let containerView: View = View().with { + $0.translatesAutoresizingMaskIntoConstraints = false + } + + private lazy var flowLayout: UICollectionViewFlowLayout = { let layout = UICollectionViewFlowLayout() - layout.itemSize = pageItemCellSize layout.scrollDirection = .horizontal layout.minimumInteritemSpacing = spacingBetweenCell - layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize layout.minimumLineSpacing = spacingBetweenCell layout.sectionInset = .zero - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + layout.estimatedItemSize = pageItemCellSize + return layout + }() + + private lazy var collectionView: UICollectionView = { + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) collectionView.isScrollEnabled = false collectionView.translatesAutoresizingMaskIntoConstraints = false collectionView.delegate = self @@ -54,59 +74,6 @@ open class Pagination: View { return collectionView }() - //TODO: Need to check with textStyle with Matt as its getter only in ButtonBase - private lazy var previousButton: ButtonBase = { - let previousButton: ButtonBase - if #available(iOS 15.0, *) { - var configuration = ButtonBase.Configuration.plain() - configuration.imagePadding = VDSLayout.Spacing.space2X.value - configuration.attributedTitle = AttributedString("Previous", attributes: AttributeContainer([NSAttributedString.Key.font: TextStyle.boldBodySmall.font])) - configuration.titleAlignment = .leading - configuration.imagePlacement = .leading - configuration.contentInsets = .zero - previousButton = ButtonBase(configuration: configuration) - } else { - previousButton = ButtonBase() - previousButton.imageEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: VDSLayout.Spacing.space2X.value) - previousButton.setTitle("Previous", for: .normal) - previousButton.titleLabel?.font = TextStyle.boldBodySmall.font - } - previousButton.contentHorizontalAlignment = .leading - previousButton.translatesAutoresizingMaskIntoConstraints = false - previousButton.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - previousButton.setImage(BundleManager.shared.image(for: "pagination-arrow-left")?.withRenderingMode(.alwaysTemplate), for: .normal) - return previousButton - }() - - private let nextButton: ButtonBase = { - let nextButton: ButtonBase - if #available(iOS 15.0, *) { - var configuration = ButtonBase.Configuration.plain() - configuration.imagePadding = VDSLayout.Spacing.space2X.value - configuration.attributedTitle = AttributedString("Next", attributes: AttributeContainer([NSAttributedString.Key.font: TextStyle.boldBodySmall.font])) - configuration.imagePlacement = .trailing - configuration.titleAlignment = .trailing - configuration.contentInsets = .zero - nextButton = ButtonBase(configuration: configuration) - } else { - nextButton = ButtonBase() - nextButton.imageEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: VDSLayout.Spacing.space2X.value) - nextButton.semanticContentAttribute = .forceRightToLeft - nextButton.titleLabel?.font = TextStyle.boldBodySmall.font - nextButton.setTitle("Next", for: .normal) - } - //nextButton.textStyle = .boldBodySmall - nextButton.translatesAutoresizingMaskIntoConstraints = false - nextButton.contentHorizontalAlignment = .trailing - nextButton.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - nextButton.setImage(BundleManager.shared.image(for: "pagination-arrow-right")?.withRenderingMode(.alwaysTemplate), for: .normal) - return nextButton - }() - - private let containerView: View = View().with { - $0.translatesAutoresizingMaskIntoConstraints = false - } - open override func initialSetup() { super.initialSetup() @@ -140,27 +107,27 @@ open class Pagination: View { open override func updateView() { super.updateView() - previousButton.tintColor = buttonTintColorConfiguration.getColor(surface) - nextButton.tintColor = buttonTintColorConfiguration.getColor(surface) - previousButton.setTitleColor(buttonTextColorConfiguration.getColor(surface), for: .normal) - nextButton.setTitleColor(buttonTextColorConfiguration.getColor(surface), for: .normal) + nextButton.surface = surface + previousButton.surface = surface collectionView.reloadData() } private func onbuttonTapped(_ sender: UIButton) { let isNextAction = sender == nextButton if isNextAction { - selectedPage += 1 + _selectedPage += 1 } else { - selectedPage -= 1 + _selectedPage -= 1 } updateSelection() } private func updateSelection() { - collectionView.scrollToItem(at: IndexPath(row: max(selectedPage-1, 0), section: 0), at: .left, animated: false) + let indexPath = IndexPath(row: selectedPage, section: 0) + collectionView.scrollToItem(at: indexPath, at: .left, animated: false) previousButton.isHidden = selectedPage == 0 nextButton.isHidden = selectedPage == total - 1 + collectionView.reloadData() } } @@ -174,55 +141,9 @@ extension Pagination: UICollectionViewDelegate, UICollectionViewDataSource, UICo return cell } - public func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool { - onPageWillChange.send(selectedPage) - return true - } - public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - selectedPage = indexPath.row + _selectedPage = indexPath.row updateSelection() - onPageChanged.send(indexPath.row) - } -} - -internal final class PaginationCellItem: UICollectionViewCell { - - static let identifier: String = String(describing: PaginationCellItem.self) - private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) - - private var indexLabel: Label = Label().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.textAlignment = .center - $0.numberOfLines = 1 - } - - override init(frame: CGRect) { - super.init(frame: frame) - setUp() - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - setUp() - } - - private func setUp() { - let containerView = View() - containerView.translatesAutoresizingMaskIntoConstraints = false - containerView.addSubview(indexLabel) - contentView.addSubview(containerView) - containerView.pinToSuperView() - indexLabel.pinToSuperView() - indexLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: VDSLayout.Spacing.space5X.value).activate() - contentView.backgroundColor = .clear - containerView.backgroundColor = .clear - indexLabel.backgroundColor = .clear - } - - internal func update(_ selectedIndex: Int, currentIndex: Int, surface: Surface) { - indexLabel.textStyle = selectedIndex == currentIndex ? .boldBodySmall : .bodySmall - indexLabel.text = "\(currentIndex)" - indexLabel.textColor = textColorConfiguration.getColor(surface) + onPageDidSelect?(indexPath.row) } } diff --git a/VDS/Components/Pagination/PaginationButton.swift b/VDS/Components/Pagination/PaginationButton.swift new file mode 100644 index 00000000..6a1cd3fa --- /dev/null +++ b/VDS/Components/Pagination/PaginationButton.swift @@ -0,0 +1,87 @@ +// +// PaginationButton.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 05/03/24. +// + +import UIKit +import VDSColorTokens + +open class PaginationButton: ButtonBase { + + private let buttonTintColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) + private let buttonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) + + @available(iOS 15.0, *) + var buttonConfiguration: Button.Configuration { + var configuration = ButtonBase.Configuration.plain() + configuration.imagePadding = VDSLayout.Spacing.space2X.value + configuration.imagePlacement = type == .next ? .trailing : .leading + configuration.titleAlignment = type == .next ? .trailing : .leading + configuration.contentInsets = .zero + return configuration + } + + open override var textStyle: TextStyle { TextStyle.boldBodySmall } + + open override var textColor: UIColor { buttonTextColorConfiguration.getColor(surface) } + + private var type: Type = .next + + init(type: Type) { + self.type = type + super.init() + } + + required public init() { + super.init() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + + open override func initialSetup() { + super.initialSetup() + if #available(iOS 15.0, *) { + configuration = buttonConfiguration + } else { + semanticContentAttribute = type == .next ? .forceRightToLeft : .forceLeftToRight + imageEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: VDSLayout.Spacing.space2X.value) + } + contentHorizontalAlignment = type == .next ? .trailing : .leading + } + + open override func updateView() { + text = type.title + setImage(type.image, for: .normal) + tintColor = buttonTintColorConfiguration.getColor(surface) + super.updateView() + } +} + +extension PaginationButton { + + enum `Type` { + case previous, next + + var title: String { + switch self { + case .next: + "Next" + case .previous: + "Previous" + } + } + + var image: UIImage? { + switch self { + case .previous: + BundleManager.shared.image(for: "pagination-arrow-left")?.withRenderingMode(.alwaysTemplate) + case .next: + BundleManager.shared.image(for: "pagination-arrow-right")?.withRenderingMode(.alwaysTemplate) + } + } + } +} diff --git a/VDS/Components/Pagination/PaginationCellItem.swift b/VDS/Components/Pagination/PaginationCellItem.swift new file mode 100644 index 00000000..1a546551 --- /dev/null +++ b/VDS/Components/Pagination/PaginationCellItem.swift @@ -0,0 +1,51 @@ +// +// PaginationCellItem.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 05/03/24. +// + +import UIKit +import VDSColorTokens + +final class PaginationCellItem: UICollectionViewCell { + + static let identifier: String = String(describing: PaginationCellItem.self) + + private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + + private var indexLabel: Label = Label().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.textAlignment = .center + $0.numberOfLines = 1 + } + + override init(frame: CGRect) { + super.init(frame: frame) + setUp() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setUp() + } + + private func setUp() { + let containerView = View() + containerView.translatesAutoresizingMaskIntoConstraints = false + containerView.addSubview(indexLabel) + contentView.addSubview(containerView) + containerView.pinToSuperView() + indexLabel.pinToSuperView() + indexLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: VDSLayout.Spacing.space5X.value).activate() + contentView.backgroundColor = .clear + containerView.backgroundColor = .clear + indexLabel.backgroundColor = .clear + } + + func update(_ selectedIndex: Int, currentIndex: Int, surface: Surface) { + indexLabel.textStyle = selectedIndex == currentIndex ? .boldBodySmall : .bodySmall + indexLabel.text = "\(currentIndex + 1)" + indexLabel.textColor = textColorConfiguration.getColor(surface) + } +} diff --git a/VDS/Components/Pagination/PaginationChangeLog.txt b/VDS/Components/Pagination/PaginationChangeLog.txt new file mode 100644 index 00000000..66f0ac14 --- /dev/null +++ b/VDS/Components/Pagination/PaginationChangeLog.txt @@ -0,0 +1,34 @@ +MM/DD/YYYY +---------------- + +Initial Brand 3.0 handoff + +12/17/2021 +---------------- +- Replaced focusring colors (previously interactive/onlight/ondark) with accessibility/onlight/ondark colors +- Updated focus border name (previously interactive.focusring.onlight) with focusring.onlight/ondark + +02/28/2022 +---------------- +- Change Page Item Active to Page Item Selected. All Active references changed to Selected. + +03/01/2022 +---------------- +- Replaced Left and Right Arrow Non-Scaling icons with VDS Icon. +- Removed “weight” and “vector effect” from Anatomy frame. + +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 focus border pixel and style & spacing values with tokens. + +01/12/2023 +---------------- +- Removed “Page Item Selected” from Anatomy.