// // CalendarHeaderReusableView.swift // VDS // // Created by Kanamarlapudi, Vasavi on 30/04/24. // import UIKit import VDSTokens /// Header view to display month and year along with days of week. class CalendarHeaderReusableView: UICollectionReusableView { ///Identifier for the Calendar Header Reusable View. static let identifier: String = String(describing: CalendarHeaderReusableView.self) //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- /// A callback when the next button clicked. public var nextClicked: (() -> (Void))? /// A callback when the previous button clicked. public var previousClicked: (() -> (Void))? //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- internal var containerSize: CGSize { CGSize(width: 304, height: 88) } private lazy var stackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.distribution = .fill $0.spacing = VDSLayout.space1X $0.axis = .vertical $0.backgroundColor = .clear } private lazy var topHeaderView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.distribution = .fill $0.spacing = VDSLayout.space1X $0.axis = .horizontal $0.backgroundColor = .clear } private lazy var daysCollectionView: UICollectionView = { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) collectionView.isScrollEnabled = false collectionView.translatesAutoresizingMaskIntoConstraints = false collectionView.delegate = self collectionView.dataSource = self collectionView.showsHorizontalScrollIndicator = false collectionView.showsVerticalScrollIndicator = false collectionView.backgroundColor = .clear collectionView.register(collectionViewCell.self, forCellWithReuseIdentifier: collectionViewCell.identifier) return collectionView }() private var surface: Surface = .light private var displayDate: Date = Date() internal var previousMonthView = View() internal var nextMonthView = View() let viewSize = 40.0 internal var previousButton = ButtonIcon().with { $0.kind = .ghost $0.iconName = .leftCaret $0.iconOffset = .init(x: -2, y: 0) $0.icon.size = .small $0.size = .small } internal var nextButton = ButtonIcon().with { $0.kind = .ghost $0.iconName = .rightCaret $0.iconOffset = .init(x: 2, y: 0) $0.icon.size = .small $0.size = .small } internal var headerTitle = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .center $0.numberOfLines = 1 $0.textStyle = .boldBodySmall $0.backgroundColor = .clear $0.isAccessibilityElement = true } internal let daysOfWeek = Date.capitalizedFirstLettersOfWeekdays internal let headerTitleTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- override init(frame: CGRect) { super.init(frame: frame) setUp() } required init?(coder: NSCoder) { super.init(coder: coder) setUp() } //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- /// Configuring the cell with default setup. private func setUp() { isAccessibilityElement = false // stackview addSubview(stackView) stackView .pinTop() .pinBottom(VDSLayout.space1X) .pinLeading() .pinTrailing() .height(containerSize.height - VDSLayout.space1X) .width(containerSize.width) stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() // top header stack view stackView.addArrangedSubview(topHeaderView) topHeaderView.heightAnchor.constraint(equalToConstant: viewSize).isActive = true topHeaderView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() // previous button topHeaderView.addArrangedSubview(previousMonthView) previousMonthView.widthAnchor.constraint(equalToConstant: viewSize).activate() previousMonthView.addSubview(previousButton) previousButton.pinCenterY().pinCenterX() previousButton.onClick = { _ in self.previousButtonClick() } // month year label topHeaderView.addArrangedSubview(headerTitle) // next button topHeaderView.addArrangedSubview(nextMonthView) nextMonthView.widthAnchor.constraint(equalToConstant: viewSize).activate() nextMonthView.addSubview(nextButton) nextButton.pinCenterY().pinCenterX() nextButton.onClick = { _ in self.nextButtonClick() } // days Collection View stackView.addArrangedSubview(daysCollectionView) topHeaderView.heightAnchor.constraint(equalToConstant: viewSize).isActive = true daysCollectionView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } /// Updating UI based on next/previous clicks along with surface. /// Updating UI to enable/disable the next & previous buttons, updating header title. func update(with surface: Surface, date: Date, nextEnabled: Bool, previousEnabled: Bool) { self.surface = surface headerTitle.surface = surface previousButton.surface = surface nextButton.surface = surface nextButton.isEnabled = nextEnabled previousButton.isEnabled = previousEnabled daysCollectionView.reloadData() let labelText = date.getMonthName() + " \(date.yearInt)" headerTitle.text = labelText headerTitle.textColor = headerTitleTextColorConfiguration.getColor(surface) } func nextButtonClick() { nextClicked?() } func previousButtonClick() { previousClicked?() } } extension CalendarHeaderReusableView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return daysOfWeek.count } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCell.identifier, for: indexPath) as? collectionViewCell else { return UICollectionViewCell() } cell.updateTitle(text: daysOfWeek[indexPath.row], surface: self.surface) return cell } public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: viewSize, height: viewSize) } public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return VDSLayout.space1X } public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { return VDSLayout.space1X } } private class collectionViewCell: UICollectionViewCell { static let identifier: String = String(describing: collectionViewCell.self) private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) private var title = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .center $0.numberOfLines = 1 $0.textStyle = .bodySmall $0.backgroundColor = .clear $0.isAccessibilityElement = true } //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- override init(frame: CGRect) { super.init(frame: frame) setupCell() } required init?(coder: NSCoder) { super.init(coder: coder) setupCell() } func setupCell() { addSubview(title) title.pinToSuperView() } func updateTitle(text: String, surface: Surface) { title.surface = surface title.text = text title.textColor = textColorConfiguration.getColor(surface) } }