From 2e99210dfe6481974b75f6d562028915ea10dda8 Mon Sep 17 00:00:00 2001 From: vasavk Date: Wed, 24 Apr 2024 18:06:21 +0530 Subject: [PATCH 01/23] Digital ACT-191 ONEAPP-7016 story: added new page --- VDS.xcodeproj/project.pbxproj | 12 ++++++ VDS/Components/Calendar/Calendar.swift | 56 ++++++++++++++++++++++++++ VDS/VDS.docc/VDS.md | 1 + 3 files changed, 69 insertions(+) create mode 100644 VDS/Components/Calendar/Calendar.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index f805c10f..35f61ed9 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 186D13CB2BBA8B1500986B53 /* DropdownSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */; }; 186D13CF2BBC36EF00986B53 /* DropdownSelectChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */; }; 18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; }; + 18A3F12A2BD9298900498E4A /* Calendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A3F1292BD9298900498E4A /* Calendar.swift */; }; 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A012B96E848006602CC /* Breadcrumbs.swift */; }; 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A032B96F050006602CC /* BreadcrumbItem.swift */; }; 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; @@ -205,6 +206,7 @@ 186D13CA2BBA8B1500986B53 /* DropdownSelect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownSelect.swift; sourceTree = ""; }; 186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = DropdownSelectChangeLog.txt; sourceTree = ""; }; 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = ""; }; + 18A3F1292BD9298900498E4A /* Calendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Calendar.swift; sourceTree = ""; }; 18A65A012B96E848006602CC /* Breadcrumbs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Breadcrumbs.swift; sourceTree = ""; }; 18A65A032B96F050006602CC /* BreadcrumbItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItem.swift; sourceTree = ""; }; 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = ""; }; @@ -415,6 +417,14 @@ path = DropdownSelect; sourceTree = ""; }; + 18A3F1202BD8F5DE00498E4A /* Calendar */ = { + isa = PBXGroup; + children = ( + 18A3F1292BD9298900498E4A /* Calendar.swift */, + ); + path = Calendar; + sourceTree = ""; + }; 18A65A002B96E7E1006602CC /* Breadcrumbs */ = { isa = PBXGroup; children = ( @@ -573,6 +583,7 @@ EAD062AE2A3B87210015965D /* BadgeIndicator */, 18A65A002B96E7E1006602CC /* Breadcrumbs */, EA0FC2BE2912D18200DF80B4 /* Buttons */, + 18A3F1202BD8F5DE00498E4A /* Calendar */, 1808BEBA2BA41B1D00129230 /* CarouselScrollbar */, EAF7F092289985E200B287F5 /* Checkbox */, 186D13C92BBA8A3500986B53 /* DropdownSelect */, @@ -1106,6 +1117,7 @@ EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */, EAACB8982B92706F006A3869 /* DefaultValuing.swift in Sources */, EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */, + 18A3F12A2BD9298900498E4A /* Calendar.swift in Sources */, 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */, EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */, 71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */, diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift new file mode 100644 index 00000000..b6aa9ecf --- /dev/null +++ b/VDS/Components/Calendar/Calendar.swift @@ -0,0 +1,56 @@ +// +// Calendar.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 19/04/24. +// + +import Foundation +import UIKit +import VDSTokens +import Combine + +/// A calendar is a monthly view that lets customers select a single date. +@objc(VDSCalendar) +open class CalendarBase: View { + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + internal var containerSize: CGSize { CGSize(width: 290, height: 300) } + + internal var containerView = View().with { + $0.clipsToBounds = true + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func initialSetup() { + super.initialSetup() + } + + open override func setup() { + super.setup() + isAccessibilityElement = false + } + + open override func reset() { + super.reset() + } +} diff --git a/VDS/VDS.docc/VDS.md b/VDS/VDS.docc/VDS.md index 607e09f5..d145ea0a 100755 --- a/VDS/VDS.docc/VDS.md +++ b/VDS/VDS.docc/VDS.md @@ -25,6 +25,7 @@ Using the system allows designers and developers to collaborate more easily and - ``Button`` - ``ButtonIcon`` - ``ButtonGroup`` +- ``CalendarBase`` - ``CarouselScrollbar`` - ``Checkbox`` - ``CheckboxItem`` From 440f516522551cac8d1942cfc627db5e17cb16a5 Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 26 Apr 2024 17:02:18 +0530 Subject: [PATCH 02/23] Digital ACT-191 ONEAPP-7016 story: changes for calendar base view with header and footer. --- VDS.xcodeproj/project.pbxproj | 8 ++ VDS/Components/Calendar/Calendar.swift | 100 +++++++++++++++++- .../CalendarDateCollectionViewCell.swift | 54 ++++++++++ .../Calendar/CalendarHeaderReusableView.swift | 57 ++++++++++ 4 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 VDS/Components/Calendar/CalendarDateCollectionViewCell.swift create mode 100644 VDS/Components/Calendar/CalendarHeaderReusableView.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 35f61ed9..5a2e0c91 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -17,6 +17,8 @@ 186D13CF2BBC36EF00986B53 /* DropdownSelectChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */; }; 18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; }; 18A3F12A2BD9298900498E4A /* Calendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A3F1292BD9298900498E4A /* Calendar.swift */; }; + 18A3F1322BD944E800498E4A /* CalendarDateCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A3F1312BD944E800498E4A /* CalendarDateCollectionViewCell.swift */; }; + 18A3F1382BDA693000498E4A /* CalendarHeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A3F1372BDA693000498E4A /* CalendarHeaderReusableView.swift */; }; 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A012B96E848006602CC /* Breadcrumbs.swift */; }; 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A032B96F050006602CC /* BreadcrumbItem.swift */; }; 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; @@ -207,6 +209,8 @@ 186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = DropdownSelectChangeLog.txt; sourceTree = ""; }; 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = ""; }; 18A3F1292BD9298900498E4A /* Calendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Calendar.swift; sourceTree = ""; }; + 18A3F1312BD944E800498E4A /* CalendarDateCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarDateCollectionViewCell.swift; sourceTree = ""; }; + 18A3F1372BDA693000498E4A /* CalendarHeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarHeaderReusableView.swift; sourceTree = ""; }; 18A65A012B96E848006602CC /* Breadcrumbs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Breadcrumbs.swift; sourceTree = ""; }; 18A65A032B96F050006602CC /* BreadcrumbItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItem.swift; sourceTree = ""; }; 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = ""; }; @@ -421,6 +425,8 @@ isa = PBXGroup; children = ( 18A3F1292BD9298900498E4A /* Calendar.swift */, + 18A3F1312BD944E800498E4A /* CalendarDateCollectionViewCell.swift */, + 18A3F1372BDA693000498E4A /* CalendarHeaderReusableView.swift */, ); path = Calendar; sourceTree = ""; @@ -1144,6 +1150,7 @@ 71ACE89C2BA0451200FB6ADC /* PaginationContainer.swift in Sources */, EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, + 18A3F1322BD944E800498E4A /* CalendarDateCollectionViewCell.swift in Sources */, EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */, 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */, EAF7F11728A1475A00B287F5 /* RadioButtonItem.swift in Sources */, @@ -1153,6 +1160,7 @@ 71FC86DC2B96F4C800700965 /* PaginationCellItem.swift in Sources */, EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */, EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */, + 18A3F1382BDA693000498E4A /* CalendarHeaderReusableView.swift in Sources */, EA985BE82968951C00F2FF2E /* TileletTitleModel.swift in Sources */, 71FC86DE2B9738B900700965 /* SurfaceConfigurationValue.swift in Sources */, EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */, diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index b6aa9ecf..641aee5d 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -32,12 +32,35 @@ open class CalendarBase: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var containerSize: CGSize { CGSize(width: 290, height: 300) } + internal var containerSize: CGSize { CGSize(width: 320, height: 376) } //width:320/328 + private let cellItemSize = CGSize(width: 40, height: 40) + private let headerHeight = 104.0 + private let footerHeight = 56.0 + private let items = 35 internal var containerView = View().with { $0.clipsToBounds = true } + ///Collectionview to render Breadcrumb Items + private lazy var collectionView: 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.register(CalendarDateCollectionViewCell.self, forCellWithReuseIdentifier: CalendarDateCollectionViewCell.identifier) + collectionView.register(CalendarHeaderReusableView.self, + forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, + withReuseIdentifier: CalendarHeaderReusableView.identifier) + collectionView.register(CalendarFooterReusableView.self, + forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, + withReuseIdentifier: CalendarFooterReusableView.identifier) + return collectionView + }() + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -48,9 +71,84 @@ open class CalendarBase: View { open override func setup() { super.setup() isAccessibilityElement = false + + addSubview(containerView) + containerView + .pinTop() + .pinBottom() + .pinLeadingGreaterThanOrEqualTo() + .pinTrailingLessThanOrEqualTo() + .height(containerSize.height) + .width(containerSize.width) + + containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + + // Calendar View + containerView.addSubview(collectionView) + collectionView.pinToSuperView() } + override open func layoutSubviews() { + super.layoutSubviews() + } + open override func reset() { super.reset() } } + +extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { + //-------------------------------------------------- + // MARK: - UICollectionView Delegate & Datasource + //-------------------------------------------------- + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + items + } + + public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CalendarDateCollectionViewCell.identifier, for: indexPath) as? CalendarDateCollectionViewCell else { return UICollectionViewCell() } + return cell + } + + public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + if kind == UICollectionView.elementKindSectionHeader { + // Header + guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CalendarHeaderReusableView.identifier, for: indexPath) as? CalendarHeaderReusableView else { + return UICollectionReusableView() + } + header.configure(with: true) + return header + } else { + // Footer + if kind == UICollectionView.elementKindSectionFooter { + guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CalendarFooterReusableView.identifier, for: indexPath) as? CalendarFooterReusableView else { + return UICollectionReusableView() + } + footer.configure(with: true) + return footer + } + } + return UICollectionReusableView() + } + + public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + return CGSize(width: collectionView.frame.size.width, height: headerHeight) + } + + public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { + return CGSize(width: collectionView.frame.size.width, height: footerHeight) + } + + public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return VDSLayout.space1X + } + + public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return VDSLayout.space1X + } + + public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return cellItemSize + } + +} diff --git a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift new file mode 100644 index 00000000..156cce04 --- /dev/null +++ b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift @@ -0,0 +1,54 @@ +// +// CalendarDateCollectionViewCell.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 24/04/24. +// + +import Foundation +import UIKit +import VDSTokens + +///This is customised view for Calendar cell item +final class CalendarDateCollectionViewCell: UICollectionViewCell { + + ///Identifier for the Calendar Date Cell + static let identifier: String = String(describing: CalendarDateCollectionViewCell.self) + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- +// internal var stackView: UIStackView = { +// return UIStackView().with { +// $0.translatesAutoresizingMaskIntoConstraints = false +// $0.axis = .horizontal +// $0.distribution = .fill +// $0.alignment = .center +// $0.spacing = VDSLayout.space2X +// $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) +// $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) +// } +// }() +// +// private lazy var selectionBackgroundView = View().with { +// $0.translatesAutoresizingMaskIntoConstraints = false +// $0.clipsToBounds = true +// $0.backgroundColor = .systemRed +// } +// +// private lazy var numberLabel = Label().with { +// $0.translatesAutoresizingMaskIntoConstraints = false +// $0.textAlignment = .center +// // $0.font +// // $0.textColor +// } + + override init(frame:CGRect) { + super.init(frame: frame) + contentView.backgroundColor = .link + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/VDS/Components/Calendar/CalendarHeaderReusableView.swift b/VDS/Components/Calendar/CalendarHeaderReusableView.swift new file mode 100644 index 00000000..3f139a10 --- /dev/null +++ b/VDS/Components/Calendar/CalendarHeaderReusableView.swift @@ -0,0 +1,57 @@ +// +// CalendarHeaderReusableView.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 24/04/24. +// + +import UIKit + +/// Custom header view +class CalendarHeaderReusableView: UICollectionReusableView { + + ///Identifier for the Calendar Header Reusable View + static let identifier: String = String(describing: CalendarHeaderReusableView.self) + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configure(with color: Bool) { + // Make a view and make in generic and dynamic + self.backgroundColor = .orange + } + + override func layoutSubviews() { + super.layoutSubviews() + } +} + +/// Custom footer view +class CalendarFooterReusableView: UICollectionReusableView { + + ///Identifier for the Calendar Footer Reusable View + static let identifier: String = String(describing: CalendarFooterReusableView.self) + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configure(with color: Bool) { + // Make a view and make in generic and dynamic + self.backgroundColor = .green + } + + override func layoutSubviews() { + super.layoutSubviews() + } +} + From 2b16fecdc353020b3dadc998913042413072c1e4 Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 29 Apr 2024 10:10:26 +0530 Subject: [PATCH 03/23] Digital ACT-191 ONEAPP-7016 story: added calendar indicator model --- VDS.xcodeproj/project.pbxproj | 4 +++ .../Calendar/CalendarIndicatorModel.swift | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 VDS/Components/Calendar/CalendarIndicatorModel.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 5a2e0c91..1890cb34 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A032B96F050006602CC /* BreadcrumbItem.swift */; }; 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; + 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */; }; 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; 44604AD429CE186A00E62B51 /* NotificationButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */; }; 44604AD729CE196600E62B51 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD629CE196600E62B51 /* Line.swift */; }; @@ -215,6 +216,7 @@ 18A65A032B96F050006602CC /* BreadcrumbItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItem.swift; sourceTree = ""; }; 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = ""; }; 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = ""; }; + 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarIndicatorModel.swift; sourceTree = ""; }; 445BA07729C07B3D0036A7C5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationButtonModel.swift; sourceTree = ""; }; 44604AD629CE196600E62B51 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; @@ -427,6 +429,7 @@ 18A3F1292BD9298900498E4A /* Calendar.swift */, 18A3F1312BD944E800498E4A /* CalendarDateCollectionViewCell.swift */, 18A3F1372BDA693000498E4A /* CalendarHeaderReusableView.swift */, + 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */, ); path = Calendar; sourceTree = ""; @@ -1247,6 +1250,7 @@ EA596ABF2A16B4F500300C4B /* Tabs.swift in Sources */, EAD062A72A3B67770015965D /* UIView+CALayer.swift in Sources */, EAD068942A560C13002E3A2D /* LoaderLaunchable.swift in Sources */, + 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */, EA985BEC2968A91200F2FF2E /* TitleLockupTitleModel.swift in Sources */, 5FC35BE328D51405004EBEAC /* Button.swift in Sources */, ); diff --git a/VDS/Components/Calendar/CalendarIndicatorModel.swift b/VDS/Components/Calendar/CalendarIndicatorModel.swift new file mode 100644 index 00000000..04ad9714 --- /dev/null +++ b/VDS/Components/Calendar/CalendarIndicatorModel.swift @@ -0,0 +1,25 @@ +// +// CalendarIndicatorModel.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 26/04/24. +// + +import Foundation + +/// Custom data type for indicators prop +extension CalendarBase { + public struct CalendarIndicatorModel { + + /// Text that shown to an indicator for legend + public var label: String + + /// Date to an indicator + public var date: Date + + public init(label: String, date: Date) { + self.label = label + self.date = date + } + } +} From 58ceee5d47f8f9f006609a51b1e6a3410679cddf Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 29 Apr 2024 10:13:58 +0530 Subject: [PATCH 04/23] Digital ACT-191 ONEAPP-7016 story: added properties --- VDS/Components/Calendar/Calendar.swift | 58 +++++++++++++++++++++----- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index 641aee5d..cb938dde 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -29,6 +29,45 @@ open class CalendarBase: View { super.init(coder: coder) } + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + /// If set to true, the calendar will not have a border. + open var hideContainerBorder: Bool = false + + /// If set to true, the calendar will not have current date indication. + open var hideCurrentDateIndicator: Bool = false + + /// Enable specific days. Pass an array of string value in date format e.g. ['07/21/2024', '07/24/2024', 07/28/2024']. + /// All other dates will be inactive + open var activeDates: [Date] = [] + + /// Disable specific days. Pass an array of string value in date format e.g. ['07/21/2024', '07/24/2024', 07/28/2024']. + /// All other dates will be active. + open var inactiveDates: [Date] = [] + + /// If provided, the calendar will allow a selection to be made from this date forward. Defaults to today. + open var minDate: Date? + + /// If provided, the calendar will allow a selection to be made up to this date. + open var maxDate: Date? + + /// If provided, this is the date that will show as selected by the Calendar. + /// If no value is provided, the current date will be used. If null is provided, no date will be selected. + open var selectedDate: Date? + + /// If provided, the calendar will be rendered with transparent background. + open var transparentBackground: Bool = false + + /// Array of ``CalendarIndicatorModel`` you are wanting to show on legend. + open var indicators: [CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } + + /// Array of indicators for the legend. + open var indicatorData: [CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } + + /// A callback when the selected date changes.. + open var onChangeSelectedDate: ((Date) -> String)? + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -37,12 +76,12 @@ open class CalendarBase: View { private let headerHeight = 104.0 private let footerHeight = 56.0 private let items = 35 - + internal var containerView = View().with { $0.clipsToBounds = true } - ///Collectionview to render Breadcrumb Items + /// Collectionview to load calendar month view private lazy var collectionView: UICollectionView = { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) collectionView.isScrollEnabled = false @@ -53,11 +92,11 @@ open class CalendarBase: View { collectionView.showsVerticalScrollIndicator = false collectionView.register(CalendarDateCollectionViewCell.self, forCellWithReuseIdentifier: CalendarDateCollectionViewCell.identifier) collectionView.register(CalendarHeaderReusableView.self, - forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, - withReuseIdentifier: CalendarHeaderReusableView.identifier) + forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, + withReuseIdentifier: CalendarHeaderReusableView.identifier) collectionView.register(CalendarFooterReusableView.self, - forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, - withReuseIdentifier: CalendarFooterReusableView.identifier) + forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, + withReuseIdentifier: CalendarFooterReusableView.identifier) return collectionView }() @@ -85,9 +124,9 @@ open class CalendarBase: View { // Calendar View containerView.addSubview(collectionView) - collectionView.pinToSuperView() + collectionView.pinToSuperView() } - + override open func layoutSubviews() { super.layoutSubviews() } @@ -146,9 +185,8 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return VDSLayout.space1X } - + public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return cellItemSize } - } From 7438d65fe53d2a46959c26be258eab2945cbf346 Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 30 Apr 2024 11:17:14 +0530 Subject: [PATCH 05/23] Digital ACT-191 ONEAPP-7016 story: updating footer with indicators data --- VDS.xcodeproj/project.pbxproj | 12 +- VDS/Components/Calendar/Calendar.swift | 13 +- .../Calendar/CalendarIndicatorModel.swift | 4 +- .../Calendar/CalendarLegendView.swift | 204 ++++++++++++++++++ ...eView.swift => CalendarReusableView.swift} | 13 +- 5 files changed, 230 insertions(+), 16 deletions(-) create mode 100644 VDS/Components/Calendar/CalendarLegendView.swift rename VDS/Components/Calendar/{CalendarHeaderReusableView.swift => CalendarReusableView.swift} (83%) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 1890cb34..354969af 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -18,12 +18,13 @@ 18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; }; 18A3F12A2BD9298900498E4A /* Calendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A3F1292BD9298900498E4A /* Calendar.swift */; }; 18A3F1322BD944E800498E4A /* CalendarDateCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A3F1312BD944E800498E4A /* CalendarDateCollectionViewCell.swift */; }; - 18A3F1382BDA693000498E4A /* CalendarHeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A3F1372BDA693000498E4A /* CalendarHeaderReusableView.swift */; }; 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A012B96E848006602CC /* Breadcrumbs.swift */; }; 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A032B96F050006602CC /* BreadcrumbItem.swift */; }; 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */; }; + 18FEA1B12BE0B69300A56439 /* CalendarLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B02BE0B69300A56439 /* CalendarLegendView.swift */; }; + 18FEA1B32BE0BC8700A56439 /* CalendarReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */; }; 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; 44604AD429CE186A00E62B51 /* NotificationButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */; }; 44604AD729CE196600E62B51 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD629CE196600E62B51 /* Line.swift */; }; @@ -211,12 +212,13 @@ 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = ""; }; 18A3F1292BD9298900498E4A /* Calendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Calendar.swift; sourceTree = ""; }; 18A3F1312BD944E800498E4A /* CalendarDateCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarDateCollectionViewCell.swift; sourceTree = ""; }; - 18A3F1372BDA693000498E4A /* CalendarHeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarHeaderReusableView.swift; sourceTree = ""; }; 18A65A012B96E848006602CC /* Breadcrumbs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Breadcrumbs.swift; sourceTree = ""; }; 18A65A032B96F050006602CC /* BreadcrumbItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItem.swift; sourceTree = ""; }; 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = ""; }; 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = ""; }; 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarIndicatorModel.swift; sourceTree = ""; }; + 18FEA1B02BE0B69300A56439 /* CalendarLegendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarLegendView.swift; sourceTree = ""; }; + 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarReusableView.swift; sourceTree = ""; }; 445BA07729C07B3D0036A7C5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationButtonModel.swift; sourceTree = ""; }; 44604AD629CE196600E62B51 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; @@ -428,8 +430,9 @@ children = ( 18A3F1292BD9298900498E4A /* Calendar.swift */, 18A3F1312BD944E800498E4A /* CalendarDateCollectionViewCell.swift */, - 18A3F1372BDA693000498E4A /* CalendarHeaderReusableView.swift */, 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */, + 18FEA1B02BE0B69300A56439 /* CalendarLegendView.swift */, + 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */, ); path = Calendar; sourceTree = ""; @@ -1163,7 +1166,6 @@ 71FC86DC2B96F4C800700965 /* PaginationCellItem.swift in Sources */, EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */, EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */, - 18A3F1382BDA693000498E4A /* CalendarHeaderReusableView.swift in Sources */, EA985BE82968951C00F2FF2E /* TileletTitleModel.swift in Sources */, 71FC86DE2B9738B900700965 /* SurfaceConfigurationValue.swift in Sources */, EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */, @@ -1216,7 +1218,9 @@ EA0B180A2AA78F9000F2D0CD /* UIEdgeInsets.swift in Sources */, EA985C1D296CD13600F2FF2E /* BundleManager.swift in Sources */, EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */, + 18FEA1B12BE0B69300A56439 /* CalendarLegendView.swift in Sources */, EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */, + 18FEA1B32BE0BC8700A56439 /* CalendarReusableView.swift in Sources */, EAF7F0AB289B13FD00B287F5 /* TextStyleLabelAttribute.swift in Sources */, EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */, EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */, diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index cb938dde..c9312c4e 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -62,8 +62,8 @@ open class CalendarBase: View { /// Array of ``CalendarIndicatorModel`` you are wanting to show on legend. open var indicators: [CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } - /// Array of indicators for the legend. - open var indicatorData: [CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } +// /// Array of indicators for the legend. +// open var indicatorData: [CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } /// A callback when the selected date changes.. open var onChangeSelectedDate: ((Date) -> String)? @@ -76,7 +76,7 @@ open class CalendarBase: View { private let headerHeight = 104.0 private let footerHeight = 56.0 private let items = 35 - + internal var containerView = View().with { $0.clipsToBounds = true } @@ -127,6 +127,11 @@ open class CalendarBase: View { collectionView.pinToSuperView() } + open override func updateView() { + super.updateView() + collectionView.reloadData() + } + override open func layoutSubviews() { super.layoutSubviews() } @@ -163,7 +168,7 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CalendarFooterReusableView.identifier, for: indexPath) as? CalendarFooterReusableView else { return UICollectionReusableView() } - footer.configure(with: true) + footer.configure(with: indicators) return footer } } diff --git a/VDS/Components/Calendar/CalendarIndicatorModel.swift b/VDS/Components/Calendar/CalendarIndicatorModel.swift index 04ad9714..911afe36 100644 --- a/VDS/Components/Calendar/CalendarIndicatorModel.swift +++ b/VDS/Components/Calendar/CalendarIndicatorModel.swift @@ -8,7 +8,7 @@ import Foundation /// Custom data type for indicators prop -extension CalendarBase { +//extension CalendarBase { public struct CalendarIndicatorModel { /// Text that shown to an indicator for legend @@ -22,4 +22,4 @@ extension CalendarBase { self.date = date } } -} +//} diff --git a/VDS/Components/Calendar/CalendarLegendView.swift b/VDS/Components/Calendar/CalendarLegendView.swift new file mode 100644 index 00000000..df1bbdf6 --- /dev/null +++ b/VDS/Components/Calendar/CalendarLegendView.swift @@ -0,0 +1,204 @@ +// +// CalendarLegendView.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 29/04/24. +// + +import Foundation +import UIKit +import VDSTokens +import Combine + +/// Legend view to show array of indicators as calendar footer view. +open class CalendarLegendView: View { + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + open var items: [CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + internal var containerSize: CGSize { CGSize(width: 320, height: 56) } //width:320/328 + + internal var containerView = View().with { + $0.clipsToBounds = true + } + + private let flowLayout = UICollectionViewFlowLayout().with { + $0.estimatedItemSize = UICollectionViewFlowLayout.automaticSize + $0.minimumLineSpacing = VDSLayout.space1X + $0.minimumInteritemSpacing = VDSLayout.space4X + $0.scrollDirection = .vertical + } + open lazy var legendCollectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout).with { + $0.isScrollEnabled = false + $0.translatesAutoresizingMaskIntoConstraints = false + $0.showsVerticalScrollIndicator = false + $0.showsHorizontalScrollIndicator = false + $0.isAccessibilityElement = true + $0.backgroundColor = .clear + $0.delegate = self + $0.dataSource = self + $0.register(LegendCollectionViewCell.self, forCellWithReuseIdentifier: LegendCollectionViewCell.identifier) + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func initialSetup() { + super.initialSetup() + } + + open override func setup() { + super.setup() + isAccessibilityElement = false + + addSubview(containerView) + containerView + .pinTop(VDSLayout.space6X) + .pinBottom(VDSLayout.space4X) + .pinLeadingGreaterThanOrEqualTo(leadingAnchor, VDSLayout.space5X, .defaultLow) + .pinTrailingLessThanOrEqualTo(trailingAnchor, VDSLayout.space5X, .defaultHigh) + .height(containerSize.height) + .width(containerSize.width) + + containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + + // legend Collection View + containerView.addSubview(legendCollectionView) + legendCollectionView.pinToSuperView() + + } + + open override func updateView() { + super.updateView() + legendCollectionView.reloadData() + } + + override open func layoutSubviews() { + super.layoutSubviews() + } + + open override func reset() { + super.reset() + } +} + +extension CalendarLegendView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { + + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return items.count + } + + public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard collectionView == legendCollectionView, + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LegendCollectionViewCell.identifier, for: indexPath) as? LegendCollectionViewCell, + indexPath.row <= items.count else { return UICollectionViewCell() } + let text = items[indexPath.row].label + cell.updateTitle(text: text, color: VDSColor.elementsSecondaryOnlight, surface: surface, clearFullcircle: indexPath.row == 1, drawSemiCircle: indexPath.row == 2) + return cell + } +} + +private class LegendCollectionViewCell: UICollectionViewCell { + + static let identifier: String = String(describing: LegendCollectionViewCell.self) + + private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + + private let indicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) + + private var title = Label().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.textAlignment = .left + $0.numberOfLines = 1 + $0.textStyle = .bodySmall + $0.backgroundColor = .clear + $0.isAccessibilityElement = false + } + private var legendIndicatorWrapper: View = View().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundColor = .clear + } + private var legendIndicator: View = View().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundColor = .clear + $0.layer.borderWidth = 1.0 + } + + private lazy var stackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.distribution = .equalSpacing + $0.spacing = VDSLayout.space2X + $0.axis = .horizontal + $0.backgroundColor = .clear + } + + private lazy var shapeLayer = CAShapeLayer() + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + override init(frame: CGRect) { + super.init(frame: frame) + setupCell() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupCell() + } + + func setupCell() { + addSubview(stackView) + stackView.pinToSuperView() + + legendIndicatorWrapper.addSubview(legendIndicator) + legendIndicator.pinLeading().pinTrailing().width(8).height(8).pinCenterY() + + stackView.addArrangedSubview(legendIndicatorWrapper) + stackView.addArrangedSubview(title) + } + + func updateTitle(text: String, color: UIColor, surface: Surface, clearFullcircle: Bool, drawSemiCircle: Bool) { + title.text = text + title.textColor = textColorConfiguration.getColor(surface) + + legendIndicator.backgroundColor = drawSemiCircle ? .clear : (clearFullcircle ? .clear : color) + legendIndicator.layer.borderColor = indicatorColorConfiguration.getColor(surface).cgColor + + self.layoutIfNeeded() + + legendIndicator.layer.cornerRadius = legendIndicator.frame.size.height / 2.0 + + guard drawSemiCircle else { return } + + let center = CGPoint(x: legendIndicator.frame.size.width/2, y: legendIndicator.frame.size.height/2) + let path = UIBezierPath() + path.move(to: center) + path.addArc(withCenter: center, radius: center.x, startAngle: 2 * .pi, endAngle: .pi, clockwise: true) + path.close() + shapeLayer.path = path.cgPath + shapeLayer.fillColor = color.cgColor + + guard legendIndicator.layer.sublayers?.contains(shapeLayer) ?? true else { return } + legendIndicator.layer.addSublayer(shapeLayer) + } +} diff --git a/VDS/Components/Calendar/CalendarHeaderReusableView.swift b/VDS/Components/Calendar/CalendarReusableView.swift similarity index 83% rename from VDS/Components/Calendar/CalendarHeaderReusableView.swift rename to VDS/Components/Calendar/CalendarReusableView.swift index 3f139a10..8131ce33 100644 --- a/VDS/Components/Calendar/CalendarHeaderReusableView.swift +++ b/VDS/Components/Calendar/CalendarReusableView.swift @@ -1,11 +1,12 @@ // -// CalendarHeaderReusableView.swift +// CalendarReusableView.swift // VDS // // Created by Kanamarlapudi, Vasavi on 24/04/24. // import UIKit +import VDSTokens /// Custom header view class CalendarHeaderReusableView: UICollectionReusableView { @@ -23,7 +24,6 @@ class CalendarHeaderReusableView: UICollectionReusableView { func configure(with color: Bool) { // Make a view and make in generic and dynamic - self.backgroundColor = .orange } override func layoutSubviews() { @@ -37,6 +37,8 @@ class CalendarFooterReusableView: UICollectionReusableView { ///Identifier for the Calendar Footer Reusable View static let identifier: String = String(describing: CalendarFooterReusableView.self) + private lazy var footerView = CalendarLegendView() + override init(frame: CGRect) { super.init(frame: frame) } @@ -45,13 +47,12 @@ class CalendarFooterReusableView: UICollectionReusableView { fatalError("init(coder:) has not been implemented") } - func configure(with color: Bool) { - // Make a view and make in generic and dynamic - self.backgroundColor = .green + func configure(with indicators: [CalendarIndicatorModel]) { + footerView.items = indicators + addSubview(footerView) } override func layoutSubviews() { super.layoutSubviews() } } - From ef80db9e73c22888208b3c607784c364abc86175 Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 30 Apr 2024 14:21:43 +0530 Subject: [PATCH 06/23] Digital ACT-191 ONEAPP-7016 story: minor changes --- VDS/Components/Calendar/Calendar.swift | 1 + VDS/Components/Calendar/CalendarIndicatorModel.swift | 4 ++-- VDS/Components/Calendar/CalendarLegendView.swift | 4 +++- VDS/Components/Calendar/CalendarReusableView.swift | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index c9312c4e..a7ed560a 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -90,6 +90,7 @@ open class CalendarBase: View { collectionView.dataSource = self collectionView.showsHorizontalScrollIndicator = false collectionView.showsVerticalScrollIndicator = false + collectionView.backgroundColor = .clear collectionView.register(CalendarDateCollectionViewCell.self, forCellWithReuseIdentifier: CalendarDateCollectionViewCell.identifier) collectionView.register(CalendarHeaderReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, diff --git a/VDS/Components/Calendar/CalendarIndicatorModel.swift b/VDS/Components/Calendar/CalendarIndicatorModel.swift index 911afe36..04ad9714 100644 --- a/VDS/Components/Calendar/CalendarIndicatorModel.swift +++ b/VDS/Components/Calendar/CalendarIndicatorModel.swift @@ -8,7 +8,7 @@ import Foundation /// Custom data type for indicators prop -//extension CalendarBase { +extension CalendarBase { public struct CalendarIndicatorModel { /// Text that shown to an indicator for legend @@ -22,4 +22,4 @@ import Foundation self.date = date } } -//} +} diff --git a/VDS/Components/Calendar/CalendarLegendView.swift b/VDS/Components/Calendar/CalendarLegendView.swift index df1bbdf6..78826ffd 100644 --- a/VDS/Components/Calendar/CalendarLegendView.swift +++ b/VDS/Components/Calendar/CalendarLegendView.swift @@ -30,7 +30,7 @@ open class CalendarLegendView: View { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - open var items: [CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } + open var items: [CalendarBase.CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } //-------------------------------------------------- // MARK: - Private Properties @@ -90,6 +90,8 @@ open class CalendarLegendView: View { open override func updateView() { super.updateView() legendCollectionView.reloadData() + setNeedsLayout() + layoutIfNeeded() } override open func layoutSubviews() { diff --git a/VDS/Components/Calendar/CalendarReusableView.swift b/VDS/Components/Calendar/CalendarReusableView.swift index 8131ce33..96995006 100644 --- a/VDS/Components/Calendar/CalendarReusableView.swift +++ b/VDS/Components/Calendar/CalendarReusableView.swift @@ -47,7 +47,7 @@ class CalendarFooterReusableView: UICollectionReusableView { fatalError("init(coder:) has not been implemented") } - func configure(with indicators: [CalendarIndicatorModel]) { + func configure(with indicators: [CalendarBase.CalendarIndicatorModel]) { footerView.items = indicators addSubview(footerView) } From 23831589f0dba211193205d47b51a43424f0e6be Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 30 Apr 2024 19:19:28 +0530 Subject: [PATCH 07/23] Digital ACT-191 ONEAPP-7016 story: added header view and updated with week days --- VDS.xcodeproj/project.pbxproj | 8 + VDS/Components/Calendar/Calendar.swift | 13 +- .../Calendar/CalendarHeaderView.swift | 195 ++++++++++++++++++ .../Calendar/CalendarReusableView.swift | 4 +- VDS/Components/Calendar/Date+Extension.swift | 20 ++ 5 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 VDS/Components/Calendar/CalendarHeaderView.swift create mode 100644 VDS/Components/Calendar/Date+Extension.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 514e3fa0..33835770 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -25,6 +25,8 @@ 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */; }; 18FEA1B12BE0B69300A56439 /* CalendarLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B02BE0B69300A56439 /* CalendarLegendView.swift */; }; 18FEA1B32BE0BC8700A56439 /* CalendarReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */; }; + 18FEA1B52BE0E63600A56439 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */; }; + 18FEA1B72BE0EBFE00A56439 /* CalendarHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */; }; 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; 44604AD429CE186A00E62B51 /* NotificationButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */; }; 44604AD729CE196600E62B51 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD629CE196600E62B51 /* Line.swift */; }; @@ -219,6 +221,8 @@ 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarIndicatorModel.swift; sourceTree = ""; }; 18FEA1B02BE0B69300A56439 /* CalendarLegendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarLegendView.swift; sourceTree = ""; }; 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarReusableView.swift; sourceTree = ""; }; + 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = ""; }; + 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarHeaderView.swift; sourceTree = ""; }; 445BA07729C07B3D0036A7C5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationButtonModel.swift; sourceTree = ""; }; 44604AD629CE196600E62B51 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; @@ -433,6 +437,8 @@ 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */, 18FEA1B02BE0B69300A56439 /* CalendarLegendView.swift */, 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */, + 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */, + 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */, ); path = Calendar; sourceTree = ""; @@ -1170,6 +1176,7 @@ 71FC86DE2B9738B900700965 /* SurfaceConfigurationValue.swift in Sources */, EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */, EA985BEA29689B6D00F2FF2E /* TileletSubTitleModel.swift in Sources */, + 18FEA1B72BE0EBFE00A56439 /* CalendarHeaderView.swift in Sources */, EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */, EAB5FEED2927E1B200998C17 /* ButtonGroupPositionLayout.swift in Sources */, EA4DB30228DCBCA500103EE3 /* Badge.swift in Sources */, @@ -1239,6 +1246,7 @@ 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */, EAF7F0B7289C12A600B287F5 /* UITapGestureRecognizer.swift in Sources */, EA0D1C392A6AD4DF00E5C127 /* Typography+SpacingConfig.swift in Sources */, + 18FEA1B52BE0E63600A56439 /* Date+Extension.swift in Sources */, EAB2376629E9952D00AABE9A /* UIApplication.swift in Sources */, EAB5FED429267EB300998C17 /* UIView+NSLayoutConstraint.swift in Sources */, EAB2376829E9992800AABE9A /* TooltipAlertViewController.swift in Sources */, diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index a7ed560a..0700bb55 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -101,6 +101,15 @@ open class CalendarBase: View { return collectionView }() + //-------------------------------------------------- + // MARK: - Configuration + //-------------------------------------------------- + internal var containerBorderColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight , VDSColor.elementsPrimaryOndark) + internal var backgroundColorConfiguration = ControlColorConfiguration().with { + $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .normal) + $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .disabled) + } + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -122,7 +131,8 @@ open class CalendarBase: View { .width(containerSize.width) containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - + containerView.layer.borderWidth = VDSFormControls.borderWidth + // Calendar View containerView.addSubview(collectionView) collectionView.pinToSuperView() @@ -131,6 +141,7 @@ open class CalendarBase: View { open override func updateView() { super.updateView() collectionView.reloadData() + containerView.layer.borderColor = containerBorderColorConfiguration.getColor(self).cgColor } override open func layoutSubviews() { diff --git a/VDS/Components/Calendar/CalendarHeaderView.swift b/VDS/Components/Calendar/CalendarHeaderView.swift new file mode 100644 index 00000000..2e591b94 --- /dev/null +++ b/VDS/Components/Calendar/CalendarHeaderView.swift @@ -0,0 +1,195 @@ +// +// CalendarHeaderView.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 30/04/24. +// + +import Foundation +import UIKit +import VDSTokens + +/// Header view to display month and year along with days of week +open class CalendarHeaderView: View { + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + internal var containerSize: CGSize { CGSize(width: 320, height: 104) } //width:320/328 + + 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 monthHeaderStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.distribution = .fill + $0.spacing = VDSLayout.space1X + $0.axis = .horizontal + $0.backgroundColor = .clear + } + + internal var containerView = View().with { + $0.clipsToBounds = true + $0.backgroundColor = .clear + } + internal var monthView = View() + internal var daysView = View() + internal let daysOfWeek = Date.capitalizedFirstLettersOfWeekdays + + 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 + }() + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func initialSetup() { + super.initialSetup() + } + + open override func setup() { + super.setup() + isAccessibilityElement = false + + // stackView + addSubview(containerView) + containerView + .pinTop() + .pinBottom() + .pinLeadingGreaterThanOrEqualTo() + .pinTrailingLessThanOrEqualTo() + .height(containerSize.height) + .width(containerSize.width) + containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + + // stackview + containerView.addSubview(stackView) + stackView + .pinTop(VDSLayout.space4X) + .pinLeading() + .pinTrailing() + .pinBottom(VDSLayout.space1X) + + // month label view, previous and next buttons + stackView.addArrangedSubview(monthView) + monthView.backgroundColor = .orange + monthView.heightAnchor.constraint(equalToConstant: 40).isActive = true + + // days Collection View + stackView.addArrangedSubview(daysCollectionView) + daysCollectionView.widthAnchor.constraint(equalTo: widthAnchor).activate() + daysCollectionView.heightAnchor.constraint(equalTo: monthView.heightAnchor).activate() + print("daysOfWeek: \(daysOfWeek)") + } + + open override func updateView() { + super.updateView() + daysCollectionView.reloadData() + } + + override open func layoutSubviews() { + super.layoutSubviews() + } + + open override func reset() { + super.reset() + } +} + +extension CalendarHeaderView: 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: surface) + return cell + } + + public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: 40, height: 40) + } + + 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 = false + } + + //-------------------------------------------------- + // 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) + title.backgroundColor = .clear + } +} diff --git a/VDS/Components/Calendar/CalendarReusableView.swift b/VDS/Components/Calendar/CalendarReusableView.swift index 96995006..b348cc30 100644 --- a/VDS/Components/Calendar/CalendarReusableView.swift +++ b/VDS/Components/Calendar/CalendarReusableView.swift @@ -14,6 +14,8 @@ class CalendarHeaderReusableView: UICollectionReusableView { ///Identifier for the Calendar Header Reusable View static let identifier: String = String(describing: CalendarHeaderReusableView.self) + private lazy var headerView = CalendarHeaderView() + override init(frame: CGRect) { super.init(frame: frame) } @@ -23,7 +25,7 @@ class CalendarHeaderReusableView: UICollectionReusableView { } func configure(with color: Bool) { - // Make a view and make in generic and dynamic + addSubview(headerView) } override func layoutSubviews() { diff --git a/VDS/Components/Calendar/Date+Extension.swift b/VDS/Components/Calendar/Date+Extension.swift new file mode 100644 index 00000000..60036ee2 --- /dev/null +++ b/VDS/Components/Calendar/Date+Extension.swift @@ -0,0 +1,20 @@ +// +// Date+Extension.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 26/04/24. +// + +import Foundation + +extension Date { + static var capitalizedFirstLettersOfWeekdays: [String] { + let calendar = Calendar.current + let weekdays = calendar.shortWeekdaySymbols + + return weekdays.map { weekday in + guard let firstLetter = weekday.first else { return "" } + return String(firstLetter).capitalized + } + } +} From 36238890c79cf227ebfebb3d473a2f40614616bf Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 30 Apr 2024 19:39:50 +0530 Subject: [PATCH 08/23] Digital ACT-191 ONEAPP-7016 story: added change log --- VDS.xcodeproj/project.pbxproj | 4 + VDS/Components/Calendar/CalendarChangeLog.txt | 84 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 VDS/Components/Calendar/CalendarChangeLog.txt diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 33835770..e75dc4c1 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 18FEA1B32BE0BC8700A56439 /* CalendarReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */; }; 18FEA1B52BE0E63600A56439 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */; }; 18FEA1B72BE0EBFE00A56439 /* CalendarHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */; }; + 18FEA1B92BE1301700A56439 /* CalendarChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18FEA1B82BE1301700A56439 /* CalendarChangeLog.txt */; }; 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; 44604AD429CE186A00E62B51 /* NotificationButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */; }; 44604AD729CE196600E62B51 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD629CE196600E62B51 /* Line.swift */; }; @@ -223,6 +224,7 @@ 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarReusableView.swift; sourceTree = ""; }; 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = ""; }; 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarHeaderView.swift; sourceTree = ""; }; + 18FEA1B82BE1301700A56439 /* CalendarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CalendarChangeLog.txt; sourceTree = ""; }; 445BA07729C07B3D0036A7C5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationButtonModel.swift; sourceTree = ""; }; 44604AD629CE196600E62B51 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; @@ -439,6 +441,7 @@ 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */, 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */, 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */, + 18FEA1B82BE1301700A56439 /* CalendarChangeLog.txt */, ); path = Calendar; sourceTree = ""; @@ -1094,6 +1097,7 @@ EAA5EEB928ECD24B003B3210 /* Icons.xcassets in Resources */, EAEEECA92B1F969700531FC2 /* TooltipChangeLog.txt in Resources */, 186D13CF2BBC36EF00986B53 /* DropdownSelectChangeLog.txt in Resources */, + 18FEA1B92BE1301700A56439 /* CalendarChangeLog.txt in Resources */, EAEEEC9C2B1F8F0700531FC2 /* TextLinkCaretChangeLog.txt in Resources */, EAA5EEE428F5B855003B3210 /* VerizonNHGDS-Light.otf in Resources */, 71B5FCBB2B95A0CA00269BCC /* PaginationChangeLog.txt in Resources */, diff --git a/VDS/Components/Calendar/CalendarChangeLog.txt b/VDS/Components/Calendar/CalendarChangeLog.txt new file mode 100644 index 00000000..ee6928a3 --- /dev/null +++ b/VDS/Components/Calendar/CalendarChangeLog.txt @@ -0,0 +1,84 @@ +MM/DD/YYYY +---------------- +- Initial Brand 3.0 handoff + +12/24/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 +- Updated the SPECS with FormControl tokens and corner radius tokens + +02/24/2022 +---------------- +- Replaced Caret Left and Right icons with bold assets. + +02/28/2022 +---------------- +- Removed dev note from Hover state and added a dev note to Active state. + +03/11/2022 +---------------- +- Update Hover and Active states triggers for carets. Icon swap and color change for mouse-only, and color change for touch. + +05/25/2022 +---------------- +- Added date indicator feature (including legend), today’s date indicator, and size/spacing adjustments to support this. + +07/20/2022 +---------------- +- Added configuration page. +- To configuration added transparent background and border suppression. + +08/10/2022 +---------------- +- Updated inverted and default to light and dark surface. Also, updated dark to selected. + +08/16/2022 +---------------- +- Updated default date background to be transparent. Updated border configuration prop name to hideContainerBorder. +- Moved Width from Configurations to Layout and Spacing, and Calendar Indicator specs under Elements. + +11/30/2022 +---------------- +- Added "(web only)" to any instance of "keyboard focus" + +12/13/2022 +---------------- +- Replaced form border and focus border pixel values, styles & spacing with tokens. + +01/09/2023 +---------------- +- Updated Specs to use new SPEC Templates and SPEC DOC Components. + +01/10/2023 +---------------- +- Updated Anatomy item #8 to “Current Date” to match design doc (originally “Today’s Date) + +04/12/2023 +---------------- +- Updated palette colors for Current date. + +05/01/2023 +---------------- +- Updated Day Header Text Style from Bold to Regular +- Updated Date Disabled Text Style from Bold to Regular +- Updated Date Text Style from Bold to Regular +- Updated Current Date font color from blue to black on light and white on dark. +- Updated all frames to reflect new designs. + +05/09/2023 +---------------- +- Replaced Previous and Next carets with Button Icon in: +Anatomy +Configurations +States + +11/09/2023 +---------------- +- Added component tokens +- Applied component tokens to selected states on light and dark surfaces +- Removed redundant color specifications from other sections + +11/30/2023 +---------------- +- Revised selected container background inverse tokens from onlight/ondark to light/dark From 0d9c409736c357c69fb0981b029e3587ca4cba6b Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 2 May 2024 07:53:42 +0530 Subject: [PATCH 09/23] Digital ACT-191 ONEAPP-7016 story: updated constraints and header month view --- VDS/Components/Calendar/Calendar.swift | 18 +++++-- .../Calendar/CalendarHeaderView.swift | 54 +++++++++++++++++-- .../Calendar/CalendarLegendView.swift | 8 +-- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index 0700bb55..3fa9de4a 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -71,10 +71,10 @@ open class CalendarBase: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var containerSize: CGSize { CGSize(width: 320, height: 376) } //width:320/328 + internal var containerSize: CGSize { CGSize(width: 328, height: 376) } //width:320/328 private let cellItemSize = CGSize(width: 40, height: 40) - private let headerHeight = 104.0 - private let footerHeight = 56.0 + private let headerHeight = 88.0 + private let footerHeight = 40.0 private let items = 35 internal var containerView = View().with { @@ -135,7 +135,17 @@ open class CalendarBase: View { // Calendar View containerView.addSubview(collectionView) - collectionView.pinToSuperView() + collectionView + .pinTop(VDSLayout.space4X) + .pinBottom(VDSLayout.space4X) + .pinLeading(VDSFormControls.spaceInset) + .pinTrailing(VDSFormControls.spaceInset) + let width = containerSize.width - (2 * VDSFormControls.spaceInset) + collectionView.widthAnchor.constraint(equalToConstant: width).activate() + let height = containerSize.height - (2 * VDSLayout.space4X) + collectionView.heightAnchor.constraint(equalToConstant: height).activate() + collectionView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).activate() + collectionView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).activate() } open override func updateView() { diff --git a/VDS/Components/Calendar/CalendarHeaderView.swift b/VDS/Components/Calendar/CalendarHeaderView.swift index 2e591b94..bba9805c 100644 --- a/VDS/Components/Calendar/CalendarHeaderView.swift +++ b/VDS/Components/Calendar/CalendarHeaderView.swift @@ -33,7 +33,7 @@ open class CalendarHeaderView: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var containerSize: CGSize { CGSize(width: 320, height: 104) } //width:320/328 + internal var containerSize: CGSize { CGSize(width: 304, height: 88) } //width:320/328 private lazy var stackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -56,9 +56,36 @@ open class CalendarHeaderView: View { $0.backgroundColor = .clear } internal var monthView = View() + internal var previousMonthView = View() + internal var nextMonthView = View() internal var daysView = View() internal let daysOfWeek = Date.capitalizedFirstLettersOfWeekdays + 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 monthYearLabel = Label().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.textAlignment = .center + $0.numberOfLines = 1 + $0.textStyle = .boldBodySmall + $0.backgroundColor = .clear + $0.isAccessibilityElement = false + } + private lazy var daysCollectionView: UICollectionView = { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) collectionView.isScrollEnabled = false @@ -72,6 +99,8 @@ open class CalendarHeaderView: View { return collectionView }() + internal var monthYearLabelTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -97,16 +126,33 @@ open class CalendarHeaderView: View { // stackview containerView.addSubview(stackView) stackView - .pinTop(VDSLayout.space4X) + .pinTop() .pinLeading() .pinTrailing() .pinBottom(VDSLayout.space1X) // month label view, previous and next buttons stackView.addArrangedSubview(monthView) - monthView.backgroundColor = .orange + monthView.backgroundColor = .clear monthView.heightAnchor.constraint(equalToConstant: 40).isActive = true + // month Header stack view + monthView.addSubview(monthHeaderStackView) + monthHeaderStackView.pinToSuperView() + + // previous button + monthHeaderStackView.addArrangedSubview(previousMonthView) + previousMonthView.widthAnchor.constraint(equalToConstant: 40).activate() + previousMonthView.addSubview(previousButton) + + // month year label + monthHeaderStackView.addArrangedSubview(monthYearLabel) + + // next button + monthHeaderStackView.addArrangedSubview(nextMonthView) + nextMonthView.widthAnchor.constraint(equalToConstant: 40).activate() + nextMonthView.addSubview(nextButton) + // days Collection View stackView.addArrangedSubview(daysCollectionView) daysCollectionView.widthAnchor.constraint(equalTo: widthAnchor).activate() @@ -117,6 +163,8 @@ open class CalendarHeaderView: View { open override func updateView() { super.updateView() daysCollectionView.reloadData() + monthYearLabel.text = "May 2024" + monthYearLabel.textColor = monthYearLabelTextColorConfiguration.getColor(surface) } override open func layoutSubviews() { diff --git a/VDS/Components/Calendar/CalendarLegendView.swift b/VDS/Components/Calendar/CalendarLegendView.swift index 78826ffd..9345483d 100644 --- a/VDS/Components/Calendar/CalendarLegendView.swift +++ b/VDS/Components/Calendar/CalendarLegendView.swift @@ -35,7 +35,7 @@ open class CalendarLegendView: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var containerSize: CGSize { CGSize(width: 320, height: 56) } //width:320/328 + internal var containerSize: CGSize { CGSize(width: 304, height: 40) } //width:320/328 internal var containerView = View().with { $0.clipsToBounds = true @@ -73,9 +73,9 @@ open class CalendarLegendView: View { addSubview(containerView) containerView .pinTop(VDSLayout.space6X) - .pinBottom(VDSLayout.space4X) - .pinLeadingGreaterThanOrEqualTo(leadingAnchor, VDSLayout.space5X, .defaultLow) - .pinTrailingLessThanOrEqualTo(trailingAnchor, VDSLayout.space5X, .defaultHigh) + .pinBottom() + .pinLeadingGreaterThanOrEqualTo(leadingAnchor, VDSLayout.space3X, .defaultLow) + .pinTrailingLessThanOrEqualTo(trailingAnchor, VDSLayout.space3X, .defaultHigh) .height(containerSize.height) .width(containerSize.width) From 408effa9f0c9ea7fd8e9c27e035f3be32e50246d Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 2 May 2024 17:51:56 +0530 Subject: [PATCH 10/23] Digital ACT-191 ONEAPP-7016 story: displaying days for calendar month view --- VDS.xcodeproj/project.pbxproj | 10 +- VDS/Components/Calendar/Calendar.swift | 49 ++++- .../CalendarDateCollectionViewCell.swift | 193 +++++++++++++++--- ...endView.swift => CalendarFooterView.swift} | 8 +- .../Calendar/CalendarHeaderView.swift | 12 +- .../Calendar/CalendarReusableView.swift | 2 +- VDS/Components/Calendar/Date+Extension.swift | 45 ++++ 7 files changed, 271 insertions(+), 48 deletions(-) rename VDS/Components/Calendar/{CalendarLegendView.swift => CalendarFooterView.swift} (97%) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index e75dc4c1..e34e7772 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; }; 1808BEC02BA456B700129230 /* CarouselScrollbarChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */; }; 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; + 18408EDE2BE32C9900E8646B /* CalendarFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18408EDD2BE32C9900E8646B /* CalendarFooterView.swift */; }; 18450CF12BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */; }; 1855EC662BAABF2A002ACAC2 /* BreadcrumbItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */; }; 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */; }; @@ -23,7 +24,6 @@ 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */; }; - 18FEA1B12BE0B69300A56439 /* CalendarLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B02BE0B69300A56439 /* CalendarLegendView.swift */; }; 18FEA1B32BE0BC8700A56439 /* CalendarReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */; }; 18FEA1B52BE0E63600A56439 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */; }; 18FEA1B72BE0EBFE00A56439 /* CalendarHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */; }; @@ -207,6 +207,7 @@ 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = ""; }; 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = ""; }; 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; + 18408EDD2BE32C9900E8646B /* CalendarFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarFooterView.swift; sourceTree = ""; }; 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = BreadcrumbsChangeLog.txt; sourceTree = ""; }; 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItemModel.swift; sourceTree = ""; }; 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = ""; }; @@ -220,7 +221,6 @@ 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = ""; }; 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = ""; }; 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarIndicatorModel.swift; sourceTree = ""; }; - 18FEA1B02BE0B69300A56439 /* CalendarLegendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarLegendView.swift; sourceTree = ""; }; 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarReusableView.swift; sourceTree = ""; }; 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = ""; }; 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarHeaderView.swift; sourceTree = ""; }; @@ -436,11 +436,11 @@ children = ( 18A3F1292BD9298900498E4A /* Calendar.swift */, 18A3F1312BD944E800498E4A /* CalendarDateCollectionViewCell.swift */, + 18408EDD2BE32C9900E8646B /* CalendarFooterView.swift */, + 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */, 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */, - 18FEA1B02BE0B69300A56439 /* CalendarLegendView.swift */, 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */, 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */, - 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */, 18FEA1B82BE1301700A56439 /* CalendarChangeLog.txt */, ); path = Calendar; @@ -1216,6 +1216,7 @@ EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */, EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */, EAD0688E2A55F819002E3A2D /* Loader.swift in Sources */, + 18408EDE2BE32C9900E8646B /* CalendarFooterView.swift in Sources */, EAB5FEF829393A7200998C17 /* ButtonGroupConstants.swift in Sources */, EAA7456C2AB23E2000C1841F /* TooltipModel.swift in Sources */, EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */, @@ -1229,7 +1230,6 @@ EA0B180A2AA78F9000F2D0CD /* UIEdgeInsets.swift in Sources */, EA985C1D296CD13600F2FF2E /* BundleManager.swift in Sources */, EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */, - 18FEA1B12BE0B69300A56439 /* CalendarLegendView.swift in Sources */, EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */, 18FEA1B32BE0BC8700A56439 /* CalendarReusableView.swift in Sources */, EAF7F0AB289B13FD00B287F5 /* TextStyleLabelAttribute.swift in Sources */, diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index 3fa9de4a..faa1bed4 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -54,7 +54,18 @@ open class CalendarBase: View { /// If provided, this is the date that will show as selected by the Calendar. /// If no value is provided, the current date will be used. If null is provided, no date will be selected. - open var selectedDate: Date? + open var selectedDate: Date? { + get { return _selectedDate } + set { + if let newValue { + _selectedDate = newValue + } else { + _selectedDate = Date() + } + //update views what needed + setNeedsUpdate() + } + } /// If provided, the calendar will be rendered with transparent background. open var transparentBackground: Bool = false @@ -71,6 +82,9 @@ open class CalendarBase: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + internal var _selectedDate: Date = Date() + private var dates: [Date] = [] + private var days: [String] = [] internal var containerSize: CGSize { CGSize(width: 328, height: 376) } //width:320/328 private let cellItemSize = CGSize(width: 40, height: 40) private let headerHeight = 88.0 @@ -150,6 +164,7 @@ open class CalendarBase: View { open override func updateView() { super.updateView() + self.fetchDates(with: selectedDate ?? Date()) collectionView.reloadData() containerView.layer.borderColor = containerBorderColorConfiguration.getColor(self).cgColor } @@ -161,6 +176,30 @@ open class CalendarBase: View { open override func reset() { super.reset() } + + func fetchDates(with date:Date) { + days.removeAll() + if let dates = selectedDate?.calendarDisplayDays { + self.dates = dates + for day in dates { + // code to be executed + if day.monthInt != selectedDate?.monthInt { + days.append("") + } else { + if #available(iOS 15.0, *) { + days.append(day.formatted(.dateTime.day())) + } else { + // Fallback on earlier versions + let dateFormatter: DateFormatter = DateFormatter() + dateFormatter.dateFormat = "d" + let dayStr: String = dateFormatter.string(from: day) + days.append(dayStr) + } + } + } +// print("days: \(days)") + } + } } extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { @@ -168,11 +207,12 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI // MARK: - UICollectionView Delegate & Datasource //-------------------------------------------------- public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - items + days.count } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CalendarDateCollectionViewCell.identifier, for: indexPath) as? CalendarDateCollectionViewCell else { return UICollectionViewCell() } + cell.configure(with: indicators, text: days[indexPath.row]) return cell } @@ -197,6 +237,11 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI return UICollectionReusableView() } + public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let date = Calendar.current.date(byAdding: .day, value: 1, to: self.dates[indexPath.row])! + print("selected day: \(days[indexPath.row]), date: \(date)") + } + public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { return CGSize(width: collectionView.frame.size.width, height: headerHeight) } diff --git a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift index 156cce04..f1c50f15 100644 --- a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift +++ b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift @@ -9,46 +9,179 @@ import Foundation import UIKit import VDSTokens -///This is customised view for Calendar cell item +/// Calendar collection view cell final class CalendarDateCollectionViewCell: UICollectionViewCell { ///Identifier for the Calendar Date Cell static let identifier: String = String(describing: CalendarDateCollectionViewCell.self) - //-------------------------------------------------- - // MARK: - Private Properties - //-------------------------------------------------- -// internal var stackView: UIStackView = { -// return UIStackView().with { -// $0.translatesAutoresizingMaskIntoConstraints = false -// $0.axis = .horizontal -// $0.distribution = .fill -// $0.alignment = .center -// $0.spacing = VDSLayout.space2X -// $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) -// $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) -// } -// }() -// -// private lazy var selectionBackgroundView = View().with { -// $0.translatesAutoresizingMaskIntoConstraints = false -// $0.clipsToBounds = true -// $0.backgroundColor = .systemRed -// } -// -// private lazy var numberLabel = Label().with { -// $0.translatesAutoresizingMaskIntoConstraints = false -// $0.textAlignment = .center -// // $0.font -// // $0.textColor -// } - override init(frame:CGRect) { + private lazy var dateView = DateView() + + override init(frame: CGRect) { super.init(frame: frame) - contentView.backgroundColor = .link } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + func configure(with indicators: [CalendarBase.CalendarIndicatorModel], text: String) { + addSubview(dateView) + dateView.dateIndicators = indicators + dateView.numberLabel.text = text + } + + override func layoutSubviews() { + super.layoutSubviews() + } +} + +/// Date view to show Date number and indicator if applies +private class DateView : View { + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + open var dateIndicators: [CalendarBase.CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } + + open var numberLabel = Label().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.textAlignment = .center + $0.textStyle = .bodySmall //isCurrentDate: .boldBodySmall + } + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + internal var containerSize: CGSize { CGSize(width: 40, height: 40) } + + internal var containerView = View().with { + $0.clipsToBounds = true + } + + private lazy var stackView: UIStackView = { + return UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .horizontal + $0.distribution = .fill + $0.alignment = .center + $0.spacing = VDSLayout.space2X + $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) + } + }() + +// private lazy var indicatorsView = View().with { +// $0.translatesAutoresizingMaskIntoConstraints = false +// $0.clipsToBounds = true +// $0.backgroundColor = .systemRed +// } + + private var legendIndicator: View = View().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundColor = .clear + $0.layer.borderWidth = 1.0 + } + + private lazy var shapeLayer = CAShapeLayer() + + private let indicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) + + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func initialSetup() { + super.initialSetup() + } + + open override func setup() { + super.setup() + isAccessibilityElement = false + + addSubview(containerView) + containerView + .pinTop() + .pinBottom() + .pinLeadingGreaterThanOrEqualTo() + .pinTrailingLessThanOrEqualTo() + .height(containerSize.height) + .width(containerSize.width) + containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + + // Number label + containerView.addSubview(numberLabel) + numberLabel.pinToSuperView() + + // Indicators + if dateIndicators.count > 0 { + containerView.addSubview(stackView) + let topPos = containerSize.height * 0.6 + stackView.pinTop(topPos).pinBottom().pinLeading().pinTrailing().pinCenterY() + stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() + let width = (dateIndicators.count * 8) + (8 * (dateIndicators.count - 1)) + stackView.widthAnchor.constraint(equalToConstant: CGFloat(width)).activate() + } + } + + open override func updateView() { + super.updateView() + } + + override open func layoutSubviews() { + super.layoutSubviews() + } + + open override func reset() { + super.reset() + } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + func configure(with indicators: [CalendarBase.CalendarIndicatorModel]) { + //TO DO: handling indicators - in progress + //show indicator + legendIndicator.pinLeading().pinTrailing().width(8).height(8).pinCenterY() + stackView.addArrangedSubview(legendIndicator) + updateIndicator(with: VDSColor.elementsSecondaryOnlight, surface: surface, clearFullCircle: false, drawSemiCircle: false) + } + + func updateIndicator(with color: UIColor, surface: Surface, clearFullCircle: Bool, drawSemiCircle: Bool){ + legendIndicator.backgroundColor = drawSemiCircle ? .clear : (clearFullCircle ? .clear : color) + legendIndicator.layer.borderColor = indicatorColorConfiguration.getColor(surface).cgColor + + self.layoutIfNeeded() + + legendIndicator.layer.cornerRadius = legendIndicator.frame.size.height / 2.0 + + guard drawSemiCircle else { return } + + let center = CGPoint(x: legendIndicator.frame.size.width/2, y: legendIndicator.frame.size.height/2) + let path = UIBezierPath() + path.move(to: center) + path.addArc(withCenter: center, radius: center.x, startAngle: 2 * .pi, endAngle: .pi, clockwise: true) + path.close() + shapeLayer.path = path.cgPath + shapeLayer.fillColor = color.cgColor + + guard legendIndicator.layer.sublayers?.contains(shapeLayer) ?? true else { return } + legendIndicator.layer.addSublayer(shapeLayer) + } } diff --git a/VDS/Components/Calendar/CalendarLegendView.swift b/VDS/Components/Calendar/CalendarFooterView.swift similarity index 97% rename from VDS/Components/Calendar/CalendarLegendView.swift rename to VDS/Components/Calendar/CalendarFooterView.swift index 9345483d..84d76cab 100644 --- a/VDS/Components/Calendar/CalendarLegendView.swift +++ b/VDS/Components/Calendar/CalendarFooterView.swift @@ -1,5 +1,5 @@ // -// CalendarLegendView.swift +// CalendarFooterView.swift // VDS // // Created by Kanamarlapudi, Vasavi on 29/04/24. @@ -10,8 +10,8 @@ import UIKit import VDSTokens import Combine -/// Legend view to show array of indicators as calendar footer view. -open class CalendarLegendView: View { +/// Footer view to show indicators data. +open class CalendarFooterView: View { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -103,7 +103,7 @@ open class CalendarLegendView: View { } } -extension CalendarLegendView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { +extension CalendarFooterView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count diff --git a/VDS/Components/Calendar/CalendarHeaderView.swift b/VDS/Components/Calendar/CalendarHeaderView.swift index bba9805c..44f5462f 100644 --- a/VDS/Components/Calendar/CalendarHeaderView.swift +++ b/VDS/Components/Calendar/CalendarHeaderView.swift @@ -43,7 +43,7 @@ open class CalendarHeaderView: View { $0.backgroundColor = .clear } - private lazy var monthHeaderStackView = UIStackView().with { + private lazy var monthYearHeaderStackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.distribution = .fill $0.spacing = VDSLayout.space1X @@ -137,19 +137,19 @@ open class CalendarHeaderView: View { monthView.heightAnchor.constraint(equalToConstant: 40).isActive = true // month Header stack view - monthView.addSubview(monthHeaderStackView) - monthHeaderStackView.pinToSuperView() + monthView.addSubview(monthYearHeaderStackView) + monthYearHeaderStackView.pinToSuperView() // previous button - monthHeaderStackView.addArrangedSubview(previousMonthView) + monthYearHeaderStackView.addArrangedSubview(previousMonthView) previousMonthView.widthAnchor.constraint(equalToConstant: 40).activate() previousMonthView.addSubview(previousButton) // month year label - monthHeaderStackView.addArrangedSubview(monthYearLabel) + monthYearHeaderStackView.addArrangedSubview(monthYearLabel) // next button - monthHeaderStackView.addArrangedSubview(nextMonthView) + monthYearHeaderStackView.addArrangedSubview(nextMonthView) nextMonthView.widthAnchor.constraint(equalToConstant: 40).activate() nextMonthView.addSubview(nextButton) diff --git a/VDS/Components/Calendar/CalendarReusableView.swift b/VDS/Components/Calendar/CalendarReusableView.swift index b348cc30..33bedfe2 100644 --- a/VDS/Components/Calendar/CalendarReusableView.swift +++ b/VDS/Components/Calendar/CalendarReusableView.swift @@ -39,7 +39,7 @@ class CalendarFooterReusableView: UICollectionReusableView { ///Identifier for the Calendar Footer Reusable View static let identifier: String = String(describing: CalendarFooterReusableView.self) - private lazy var footerView = CalendarLegendView() + private lazy var footerView = CalendarFooterView() override init(frame: CGRect) { super.init(frame: frame) diff --git a/VDS/Components/Calendar/Date+Extension.swift b/VDS/Components/Calendar/Date+Extension.swift index 60036ee2..07284507 100644 --- a/VDS/Components/Calendar/Date+Extension.swift +++ b/VDS/Components/Calendar/Date+Extension.swift @@ -8,6 +8,7 @@ import Foundation extension Date { + static var firstDayOfWeek = Calendar.current.firstWeekday static var capitalizedFirstLettersOfWeekdays: [String] { let calendar = Calendar.current let weekdays = calendar.shortWeekdaySymbols @@ -17,4 +18,48 @@ extension Date { return String(firstLetter).capitalized } } + + var startOfMonth: Date { + Calendar.current.dateInterval(of: .month, for: self)!.start + } + + var endOfMonth: Date { + let lastDay = Calendar.current.dateInterval(of: .month, for: self)!.end + return Calendar.current.date(byAdding: .day, value: -1, to: lastDay)! + } + + var numberOfDaysInMonth: Int { + Calendar.current.component(.day, from: endOfMonth) + } + + var firstWeekDayBeforeStart: Date { + let startOfMonthWeekday = Calendar.current.component(.weekday, from: startOfMonth) + var numberFromPreviousMonth = startOfMonthWeekday - Self.firstDayOfWeek + if numberFromPreviousMonth < 0 { + numberFromPreviousMonth += 7 // Adjust to a 0-6 range if negative + } + return Calendar.current.date(byAdding: .day, value: -numberFromPreviousMonth, to: startOfMonth)! + } + + var calendarDisplayDays: [Date] { + var days: [Date] = [] + // Start with days from the previous month to fill the grid + let firstDisplayDay = firstWeekDayBeforeStart + var day = firstDisplayDay + while day < startOfMonth { + days.append(day) + day = Calendar.current.date(byAdding: .day, value: 1, to: day)! + } + // Add days of the current month + for dayOffset in 0.. Date: Fri, 3 May 2024 18:29:32 +0530 Subject: [PATCH 11/23] Digital ACT-191 ONEAPP-7016 story: Displays indicator for day based on the indicators data --- VDS/Components/Calendar/Calendar.swift | 51 +++++--- .../CalendarDateCollectionViewCell.swift | 112 ++++++++++-------- 2 files changed, 95 insertions(+), 68 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index faa1bed4..f9cb16cf 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -134,7 +134,6 @@ open class CalendarBase: View { open override func setup() { super.setup() isAccessibilityElement = false - addSubview(containerView) containerView .pinTop() @@ -147,19 +146,20 @@ open class CalendarBase: View { containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() containerView.layer.borderWidth = VDSFormControls.borderWidth + let spacing = CGFloat(VDSLayout.space2X) //CGFloat(VDSFormControls.spaceInset) // + // Calendar View containerView.addSubview(collectionView) collectionView .pinTop(VDSLayout.space4X) .pinBottom(VDSLayout.space4X) - .pinLeading(VDSFormControls.spaceInset) - .pinTrailing(VDSFormControls.spaceInset) - let width = containerSize.width - (2 * VDSFormControls.spaceInset) + .pinLeading(spacing) + .pinTrailing(spacing) + let width = containerSize.width - (2 * spacing) collectionView.widthAnchor.constraint(equalToConstant: width).activate() let height = containerSize.height - (2 * VDSLayout.space4X) collectionView.heightAnchor.constraint(equalToConstant: height).activate() collectionView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).activate() - collectionView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).activate() } open override func updateView() { @@ -177,29 +177,36 @@ open class CalendarBase: View { super.reset() } + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- func fetchDates(with date:Date) { days.removeAll() if let dates = selectedDate?.calendarDisplayDays { self.dates = dates - for day in dates { + for date in dates { // code to be executed - if day.monthInt != selectedDate?.monthInt { + if date.monthInt != selectedDate?.monthInt { days.append("") } else { - if #available(iOS 15.0, *) { - days.append(day.formatted(.dateTime.day())) - } else { - // Fallback on earlier versions - let dateFormatter: DateFormatter = DateFormatter() - dateFormatter.dateFormat = "d" - let dayStr: String = dateFormatter.string(from: day) - days.append(dayStr) - } + days.append(getDay(with: date)) } } -// print("days: \(days)") } } + + func getDay(with date:Date) -> String { + if #available(iOS 15.0, *) { + return date.formatted(.dateTime.day()) + } else { + // Fallback on earlier versions + let dateFormatter: DateFormatter = DateFormatter() + dateFormatter.dateFormat = "d" + let day: String = dateFormatter.string(from: date) + return day + } + } + } extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { @@ -212,7 +219,13 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CalendarDateCollectionViewCell.identifier, for: indexPath) as? CalendarDateCollectionViewCell else { return UICollectionViewCell() } - cell.configure(with: indicators, text: days[indexPath.row]) + var indicatorCount = 0 + for x in (0...(self.indicators.count-1)) { + if (self.days[indexPath.row] == self.getDay(with: self.indicators[x].date)) { + indicatorCount += 1 + } + } + cell.configure(with: surface, indicators: indicators, text: days[indexPath.row], indicatorCount: indicatorCount) return cell } @@ -239,7 +252,7 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let date = Calendar.current.date(byAdding: .day, value: 1, to: self.dates[indexPath.row])! - print("selected day: \(days[indexPath.row]), date: \(date)") +// print("selected day: \(days[indexPath.row]), date: \(date)") } public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { diff --git a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift index f1c50f15..fd33611b 100644 --- a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift +++ b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift @@ -26,10 +26,11 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { fatalError("init(coder:) has not been implemented") } - func configure(with indicators: [CalendarBase.CalendarIndicatorModel], text: String) { + func configure(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int) { addSubview(dateView) - dateView.dateIndicators = indicators dateView.numberLabel.text = text + dateView.indicatorCount = indicatorCount + dateView.dateIndicators = indicators } override func layoutSubviews() { @@ -58,8 +59,10 @@ private class DateView : View { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + open var indicatorCount: Int = 0 + open var dateIndicators: [CalendarBase.CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } - + open var numberLabel = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .center @@ -70,39 +73,26 @@ private class DateView : View { // MARK: - Private Properties //-------------------------------------------------- internal var containerSize: CGSize { CGSize(width: 40, height: 40) } - + internal var containerView = View().with { $0.clipsToBounds = true } - + private lazy var stackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .horizontal $0.distribution = .fill $0.alignment = .center - $0.spacing = VDSLayout.space2X + $0.spacing = VDSLayout.space1X $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) } }() -// private lazy var indicatorsView = View().with { -// $0.translatesAutoresizingMaskIntoConstraints = false -// $0.clipsToBounds = true -// $0.backgroundColor = .systemRed -// } - - private var legendIndicator: View = View().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.backgroundColor = .clear - $0.layer.borderWidth = 1.0 - } - private lazy var shapeLayer = CAShapeLayer() - - private let indicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) + private let indicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) //-------------------------------------------------- // MARK: - Lifecycle @@ -114,7 +104,6 @@ private class DateView : View { open override func setup() { super.setup() isAccessibilityElement = false - addSubview(containerView) containerView .pinTop() @@ -124,24 +113,33 @@ private class DateView : View { .height(containerSize.height) .width(containerSize.width) containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - + // Number label containerView.addSubview(numberLabel) numberLabel.pinToSuperView() - + // Indicators - if dateIndicators.count > 0 { - containerView.addSubview(stackView) - let topPos = containerSize.height * 0.6 - stackView.pinTop(topPos).pinBottom().pinLeading().pinTrailing().pinCenterY() - stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - let width = (dateIndicators.count * 8) + (8 * (dateIndicators.count - 1)) - stackView.widthAnchor.constraint(equalToConstant: CGFloat(width)).activate() - } + containerView.addSubview(stackView) + let topPos = containerSize.height * 0.6 + stackView.pinTop(topPos).pinBottom().pinTopGreaterThanOrEqualTo().pinTrailingLessThanOrEqualTo().pinCenterY() + stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } open override func updateView() { super.updateView() + numberLabel.surface = surface + numberLabel.isEnabled = isEnabled + + stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } + if indicatorCount > 0 { + let width = (indicatorCount * 8) + (Int(VDSLayout.space1X) * (indicatorCount - 1)) + stackView.widthAnchor.constraint(equalToConstant: CGFloat(width)).activate() + for x in (0...(dateIndicators.count-1)) { + if (self.numberLabel.text == self.getDay(with: dateIndicators[x].date)) { + addIndicator(with: VDSColor.elementsSecondaryOnlight, surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) + } + } + } } override open func layoutSubviews() { @@ -150,30 +148,33 @@ private class DateView : View { open override func reset() { super.reset() + numberLabel.textStyle = .bodySmall } - //-------------------------------------------------- - // MARK: - Private Methods - //-------------------------------------------------- - func configure(with indicators: [CalendarBase.CalendarIndicatorModel]) { - //TO DO: handling indicators - in progress - //show indicator - legendIndicator.pinLeading().pinTrailing().width(8).height(8).pinCenterY() - stackView.addArrangedSubview(legendIndicator) - updateIndicator(with: VDSColor.elementsSecondaryOnlight, surface: surface, clearFullCircle: false, drawSemiCircle: false) - } - - func updateIndicator(with color: UIColor, surface: Surface, clearFullCircle: Bool, drawSemiCircle: Bool){ - legendIndicator.backgroundColor = drawSemiCircle ? .clear : (clearFullCircle ? .clear : color) - legendIndicator.layer.borderColor = indicatorColorConfiguration.getColor(surface).cgColor + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + func addIndicator(with color: UIColor, surface: Surface, clearFullCircle: Bool, drawSemiCircle: Bool) { + // add indicator + let indicatorView: View = View().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundColor = .clear + $0.layer.borderWidth = 1.0 + } + indicatorView.pinLeading().pinTrailing().width(8).height(8).pinCenterY() + stackView.addArrangedSubview(indicatorView) + + // update indicator + indicatorView.backgroundColor = drawSemiCircle ? .clear : (clearFullCircle ? .clear : color) + indicatorView.layer.borderColor = indicatorColorConfiguration.getColor(surface).cgColor self.layoutIfNeeded() - legendIndicator.layer.cornerRadius = legendIndicator.frame.size.height / 2.0 + indicatorView.layer.cornerRadius = indicatorView.frame.size.height / 2.0 guard drawSemiCircle else { return } - let center = CGPoint(x: legendIndicator.frame.size.width/2, y: legendIndicator.frame.size.height/2) + let center = CGPoint(x: indicatorView.frame.size.width/2, y: indicatorView.frame.size.height/2) let path = UIBezierPath() path.move(to: center) path.addArc(withCenter: center, radius: center.x, startAngle: 2 * .pi, endAngle: .pi, clockwise: true) @@ -181,7 +182,20 @@ private class DateView : View { shapeLayer.path = path.cgPath shapeLayer.fillColor = color.cgColor - guard legendIndicator.layer.sublayers?.contains(shapeLayer) ?? true else { return } - legendIndicator.layer.addSublayer(shapeLayer) + guard indicatorView.layer.sublayers?.contains(shapeLayer) ?? true else { return } + indicatorView.layer.addSublayer(shapeLayer) } + + func getDay(with date:Date) -> String { + if #available(iOS 15.0, *) { + return date.formatted(.dateTime.day()) + } else { + // Fallback on earlier versions + let dateFormatter: DateFormatter = DateFormatter() + dateFormatter.dateFormat = "d" + let day: String = dateFormatter.string(from: date) + return day + } + } + } From f043ac74d38787e390148ab6a0ac1bc068f4117b Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 3 May 2024 19:59:49 +0530 Subject: [PATCH 12/23] Digital ACT-191 ONEAPP-7016 story: updating surface for all childviews --- VDS/Components/Calendar/Calendar.swift | 8 ++++---- .../Calendar/CalendarDateCollectionViewCell.swift | 3 ++- VDS/Components/Calendar/CalendarFooterView.swift | 5 +++-- VDS/Components/Calendar/CalendarHeaderView.swift | 6 +++++- VDS/Components/Calendar/CalendarReusableView.swift | 6 ++++-- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index f9cb16cf..3e1442e1 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -146,7 +146,7 @@ open class CalendarBase: View { containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() containerView.layer.borderWidth = VDSFormControls.borderWidth - let spacing = CGFloat(VDSLayout.space2X) //CGFloat(VDSFormControls.spaceInset) // + let spacing = CGFloat(VDSLayout.space2X) //CGFloat(VDSFormControls.spaceInset) // // Calendar View containerView.addSubview(collectionView) @@ -220,7 +220,7 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CalendarDateCollectionViewCell.identifier, for: indexPath) as? CalendarDateCollectionViewCell else { return UICollectionViewCell() } var indicatorCount = 0 - for x in (0...(self.indicators.count-1)) { + for x in (0...(self.indicators.count-1)) { if (self.days[indexPath.row] == self.getDay(with: self.indicators[x].date)) { indicatorCount += 1 } @@ -235,7 +235,7 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CalendarHeaderReusableView.identifier, for: indexPath) as? CalendarHeaderReusableView else { return UICollectionReusableView() } - header.configure(with: true) + header.configure(with: surface) return header } else { // Footer @@ -243,7 +243,7 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CalendarFooterReusableView.identifier, for: indexPath) as? CalendarFooterReusableView else { return UICollectionReusableView() } - footer.configure(with: indicators) + footer.configure(with: surface, indicators: indicators) return footer } } diff --git a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift index fd33611b..68917069 100644 --- a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift +++ b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift @@ -28,6 +28,7 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { func configure(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int) { addSubview(dateView) + dateView.surface = surface dateView.numberLabel.text = text dateView.indicatorCount = indicatorCount dateView.dateIndicators = indicators @@ -120,7 +121,7 @@ private class DateView : View { // Indicators containerView.addSubview(stackView) - let topPos = containerSize.height * 0.6 + let topPos = containerSize.height * 0.7 stackView.pinTop(topPos).pinBottom().pinTopGreaterThanOrEqualTo().pinTrailingLessThanOrEqualTo().pinCenterY() stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } diff --git a/VDS/Components/Calendar/CalendarFooterView.swift b/VDS/Components/Calendar/CalendarFooterView.swift index 84d76cab..6574dc84 100644 --- a/VDS/Components/Calendar/CalendarFooterView.swift +++ b/VDS/Components/Calendar/CalendarFooterView.swift @@ -127,14 +127,14 @@ private class LegendCollectionViewCell: UICollectionViewCell { private let indicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) - private var title = Label().with { + private var title: Label = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .left $0.numberOfLines = 1 $0.textStyle = .bodySmall - $0.backgroundColor = .clear $0.isAccessibilityElement = false } + private var legendIndicatorWrapper: View = View().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.backgroundColor = .clear @@ -180,6 +180,7 @@ private class LegendCollectionViewCell: UICollectionViewCell { } func updateTitle(text: String, color: UIColor, surface: Surface, clearFullcircle: Bool, drawSemiCircle: Bool) { + title.surface = surface title.text = text title.textColor = textColorConfiguration.getColor(surface) diff --git a/VDS/Components/Calendar/CalendarHeaderView.swift b/VDS/Components/Calendar/CalendarHeaderView.swift index 44f5462f..f217ae40 100644 --- a/VDS/Components/Calendar/CalendarHeaderView.swift +++ b/VDS/Components/Calendar/CalendarHeaderView.swift @@ -130,6 +130,7 @@ open class CalendarHeaderView: View { .pinLeading() .pinTrailing() .pinBottom(VDSLayout.space1X) + stackView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).activate() // month label view, previous and next buttons stackView.addArrangedSubview(monthView) @@ -157,11 +158,14 @@ open class CalendarHeaderView: View { stackView.addArrangedSubview(daysCollectionView) daysCollectionView.widthAnchor.constraint(equalTo: widthAnchor).activate() daysCollectionView.heightAnchor.constraint(equalTo: monthView.heightAnchor).activate() - print("daysOfWeek: \(daysOfWeek)") + daysCollectionView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).activate() } open override func updateView() { super.updateView() + monthYearLabel.surface = surface + previousButton.surface = surface + nextButton.surface = surface daysCollectionView.reloadData() monthYearLabel.text = "May 2024" monthYearLabel.textColor = monthYearLabelTextColorConfiguration.getColor(surface) diff --git a/VDS/Components/Calendar/CalendarReusableView.swift b/VDS/Components/Calendar/CalendarReusableView.swift index 33bedfe2..3a61e635 100644 --- a/VDS/Components/Calendar/CalendarReusableView.swift +++ b/VDS/Components/Calendar/CalendarReusableView.swift @@ -24,7 +24,8 @@ class CalendarHeaderReusableView: UICollectionReusableView { fatalError("init(coder:) has not been implemented") } - func configure(with color: Bool) { + func configure(with surface: Surface) { + headerView.surface = surface addSubview(headerView) } @@ -49,8 +50,9 @@ class CalendarFooterReusableView: UICollectionReusableView { fatalError("init(coder:) has not been implemented") } - func configure(with indicators: [CalendarBase.CalendarIndicatorModel]) { + func configure(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel]) { footerView.items = indicators + footerView.surface = surface addSubview(footerView) } From 3eaf29f3f1ec7f0c3ef482b99f00c683a4bf6d55 Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 6 May 2024 09:47:02 +0530 Subject: [PATCH 13/23] Digital ACT-191 ONEAPP-7016 story: hide/show container border --- VDS/Components/Calendar/Calendar.swift | 31 ++++++++++++------- .../Calendar/CalendarReusableView.swift | 8 ----- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index 3e1442e1..41890756 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -33,7 +33,7 @@ open class CalendarBase: View { // MARK: - Public Properties //-------------------------------------------------- /// If set to true, the calendar will not have a border. - open var hideContainerBorder: Bool = false + open var hideContainerBorder: Bool = false { didSet { setNeedsUpdate() } } /// If set to true, the calendar will not have current date indication. open var hideCurrentDateIndicator: Bool = false @@ -123,7 +123,7 @@ open class CalendarBase: View { $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .normal) $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .disabled) } - + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -144,9 +144,8 @@ open class CalendarBase: View { .width(containerSize.width) containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - containerView.layer.borderWidth = VDSFormControls.borderWidth - - let spacing = CGFloat(VDSLayout.space2X) //CGFloat(VDSFormControls.spaceInset) // + + let spacing = CGFloat(VDSFormControls.spaceInset) //CGFloat(VDSLayout.space2X) // Calendar View containerView.addSubview(collectionView) @@ -166,7 +165,15 @@ open class CalendarBase: View { super.updateView() self.fetchDates(with: selectedDate ?? Date()) collectionView.reloadData() - containerView.layer.borderColor = containerBorderColorConfiguration.getColor(self).cgColor + if hideContainerBorder { + layer.borderColor = nil + layer.borderWidth = 0 + layer.cornerRadius = 0 + } else { + layer.borderColor = containerBorderColorConfiguration.getColor(self).cgColor + layer.borderWidth = VDSFormControls.borderWidth + layer.cornerRadius = VDSFormControls.borderRadius + } } override open func layoutSubviews() { @@ -220,9 +227,11 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CalendarDateCollectionViewCell.identifier, for: indexPath) as? CalendarDateCollectionViewCell else { return UICollectionViewCell() } var indicatorCount = 0 - for x in (0...(self.indicators.count-1)) { - if (self.days[indexPath.row] == self.getDay(with: self.indicators[x].date)) { - indicatorCount += 1 + if self.indicators.count > 0 { + for x in (0...(self.indicators.count-1)) { + if (self.days[indexPath.row] == self.getDay(with: self.indicators[x].date)) { + indicatorCount += 1 + } } } cell.configure(with: surface, indicators: indicators, text: days[indexPath.row], indicatorCount: indicatorCount) @@ -251,8 +260,8 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI } public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - let date = Calendar.current.date(byAdding: .day, value: 1, to: self.dates[indexPath.row])! -// print("selected day: \(days[indexPath.row]), date: \(date)") +// let selectedDate = Calendar.current.date(byAdding: .day, value: 1, to: self.dates[indexPath.row])! +// print("selected day: \(days[indexPath.row]), date: \(selectedDate)") } public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { diff --git a/VDS/Components/Calendar/CalendarReusableView.swift b/VDS/Components/Calendar/CalendarReusableView.swift index 3a61e635..ebb2d269 100644 --- a/VDS/Components/Calendar/CalendarReusableView.swift +++ b/VDS/Components/Calendar/CalendarReusableView.swift @@ -28,10 +28,6 @@ class CalendarHeaderReusableView: UICollectionReusableView { headerView.surface = surface addSubview(headerView) } - - override func layoutSubviews() { - super.layoutSubviews() - } } /// Custom footer view @@ -55,8 +51,4 @@ class CalendarFooterReusableView: UICollectionReusableView { footerView.surface = surface addSubview(footerView) } - - override func layoutSubviews() { - super.layoutSubviews() - } } From e980d816b327feca616b095c89f5f65b9e38edfd Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 6 May 2024 12:30:32 +0530 Subject: [PATCH 14/23] Digital ACT-191 ONEAPP-7016 story: applying selected/unselected properties and updating selected date on change --- VDS/Components/Calendar/Calendar.swift | 55 +++++++++-------- .../CalendarDateCollectionViewCell.swift | 59 +++++++++++++++---- 2 files changed, 78 insertions(+), 36 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index 41890756..cfd2c11d 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -54,15 +54,10 @@ open class CalendarBase: View { /// If provided, this is the date that will show as selected by the Calendar. /// If no value is provided, the current date will be used. If null is provided, no date will be selected. - open var selectedDate: Date? { + open var selectedDate: Date { get { return _selectedDate } set { - if let newValue { - _selectedDate = newValue - } else { - _selectedDate = Date() - } - //update views what needed + _selectedDate = newValue setNeedsUpdate() } } @@ -75,22 +70,25 @@ open class CalendarBase: View { // /// Array of indicators for the legend. // open var indicatorData: [CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } - - /// A callback when the selected date changes.. - open var onChangeSelectedDate: ((Date) -> String)? + + /// A callback when the date changes. Passes parameters (selectedDate). + public var onChangeSelectedDate: ((Date) -> Void)? //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- internal var _selectedDate: Date = Date() - private var dates: [Date] = [] - private var days: [String] = [] internal var containerSize: CGSize { CGSize(width: 328, height: 376) } //width:320/328 + private let cellItemSize = CGSize(width: 40, height: 40) private let headerHeight = 88.0 private let footerHeight = 40.0 private let items = 35 + private var selectedIndexPath : IndexPath? + private var dates: [Date] = [] + private var days: [String] = [] + internal var containerView = View().with { $0.clipsToBounds = true } @@ -163,7 +161,7 @@ open class CalendarBase: View { open override func updateView() { super.updateView() - self.fetchDates(with: selectedDate ?? Date()) + self.fetchDates(with: selectedDate) collectionView.reloadData() if hideContainerBorder { layer.borderColor = nil @@ -189,15 +187,13 @@ open class CalendarBase: View { //-------------------------------------------------- func fetchDates(with date:Date) { days.removeAll() - if let dates = selectedDate?.calendarDisplayDays { - self.dates = dates - for date in dates { - // code to be executed - if date.monthInt != selectedDate?.monthInt { - days.append("") - } else { - days.append(getDay(with: date)) - } + self.dates = selectedDate.calendarDisplayDays + for date in dates { + // code to be executed + if date.monthInt != selectedDate.monthInt { + days.append("") + } else { + days.append(getDay(with: date)) } } } @@ -234,7 +230,8 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI } } } - cell.configure(with: surface, indicators: indicators, text: days[indexPath.row], indicatorCount: indicatorCount) + cell.configure(with: surface, indicators: indicators, text: days[indexPath.row], indicatorCount: indicatorCount, selectedDate: selectedDate) + if (self.days[indexPath.row] == self.getDay(with: selectedDate)) { selectedIndexPath = indexPath } return cell } @@ -260,8 +257,16 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI } public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { -// let selectedDate = Calendar.current.date(byAdding: .day, value: 1, to: self.dates[indexPath.row])! -// print("selected day: \(days[indexPath.row]), date: \(selectedDate)") + let selectedItem = Calendar.current.date(byAdding: .day, value: 1, to: self.dates[indexPath.row])! + onChangeSelectedDate?(selectedItem) + + selectedDate = self.dates[indexPath.row] + var reloadIndexPaths = [indexPath] + + // If an cell is already selected, then it needs to be deselected. + // Add its index path to the array of index paths to be reloaded. + if let deselectIndexPath = selectedIndexPath { reloadIndexPaths.append(deselectIndexPath) } + self.collectionView.reloadItems(at: reloadIndexPaths) } public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { diff --git a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift index 68917069..eb66cde7 100644 --- a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift +++ b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift @@ -9,15 +9,20 @@ import Foundation import UIKit import VDSTokens -/// Calendar collection view cell +/// Calendar collection view for Date view final class CalendarDateCollectionViewCell: UICollectionViewCell { ///Identifier for the Calendar Date Cell static let identifier: String = String(describing: CalendarDateCollectionViewCell.self) - + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- private lazy var dateView = DateView() + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- override init(frame: CGRect) { super.init(frame: frame) } @@ -26,17 +31,15 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { fatalError("init(coder:) has not been implemented") } - func configure(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int) { + func configure(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int, selectedDate: Date) { addSubview(dateView) dateView.surface = surface dateView.numberLabel.text = text dateView.indicatorCount = indicatorCount dateView.dateIndicators = indicators + dateView.selectedDate = selectedDate } - override func layoutSubviews() { - super.layoutSubviews() - } } /// Date view to show Date number and indicator if applies @@ -67,9 +70,11 @@ private class DateView : View { open var numberLabel = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .center - $0.textStyle = .bodySmall //isCurrentDate: .boldBodySmall + $0.textStyle = .bodySmall } + open var selectedDate: Date = Date() //? { didSet { setNeedsUpdate() } } + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -93,8 +98,15 @@ private class DateView : View { private lazy var shapeLayer = CAShapeLayer() - private let indicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) + private let selectedTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryInverseOnlight, VDSColor.elementsPrimaryInverseOndark) + private let selectedBackgroundColor = SurfaceColorConfiguration(VDSColor.backgroundPrimaryInverseLight, VDSColor.backgroundPrimaryInverseDark) + private let selectedCellIndicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteGray65, VDSColor.paletteGray44) + + private let unselectedTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + private let unselectedCellIndicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) + private let currentDate = Date() + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -130,17 +142,42 @@ private class DateView : View { super.updateView() numberLabel.surface = surface numberLabel.isEnabled = isEnabled - stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } + + // update text color, bg color, corner radius + if numberLabel.text == self.getDay(with: selectedDate) { + numberLabel.textColor = selectedTextColorConfiguration.getColor(self) + layer.backgroundColor = selectedBackgroundColor.getColor(self).cgColor + layer.cornerRadius = VDSFormControls.borderRadius + } else { + numberLabel.textColor = unselectedTextColorConfiguration.getColor(self) + layer.backgroundColor = nil + layer.cornerRadius = 0 + } + + // add indicators if indicatorCount > 0 { let width = (indicatorCount * 8) + (Int(VDSLayout.space1X) * (indicatorCount - 1)) stackView.widthAnchor.constraint(equalToConstant: CGFloat(width)).activate() for x in (0...(dateIndicators.count-1)) { if (self.numberLabel.text == self.getDay(with: dateIndicators[x].date)) { - addIndicator(with: VDSColor.elementsSecondaryOnlight, surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) + if numberLabel.text == self.getDay(with: selectedDate) { + addIndicator(with: selectedCellIndicatorColorConfiguration.getColor(self), surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) + } else { + addIndicator(with: unselectedCellIndicatorColorConfiguration.getColor(self), surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) + } } } } + + // update text style for current date + if numberLabel.text == self.getDay(with: currentDate) { + numberLabel.textStyle = .boldBodySmall + } else { + numberLabel.textStyle = .bodySmall + } + + } override open func layoutSubviews() { @@ -167,7 +204,7 @@ private class DateView : View { // update indicator indicatorView.backgroundColor = drawSemiCircle ? .clear : (clearFullCircle ? .clear : color) - indicatorView.layer.borderColor = indicatorColorConfiguration.getColor(surface).cgColor + indicatorView.layer.borderColor = color.cgColor self.layoutIfNeeded() From 7fe5123a69b0478921a782717622672d652ef939 Mon Sep 17 00:00:00 2001 From: vasavk Date: Mon, 6 May 2024 16:01:56 +0530 Subject: [PATCH 15/23] Digital ACT-191 ONEAPP-7016 story: config container background, hide/show current date indicator --- VDS/Components/Calendar/Calendar.swift | 10 +- .../CalendarDateCollectionViewCell.swift | 131 +++++------------- 2 files changed, 39 insertions(+), 102 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index cfd2c11d..dd0f08eb 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -36,7 +36,7 @@ open class CalendarBase: View { open var hideContainerBorder: Bool = false { didSet { setNeedsUpdate() } } /// If set to true, the calendar will not have current date indication. - open var hideCurrentDateIndicator: Bool = false + open var hideCurrentDateIndicator: Bool = false { didSet { setNeedsUpdate() } } /// Enable specific days. Pass an array of string value in date format e.g. ['07/21/2024', '07/24/2024', 07/28/2024']. /// All other dates will be inactive @@ -117,10 +117,7 @@ open class CalendarBase: View { // MARK: - Configuration //-------------------------------------------------- internal var containerBorderColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight , VDSColor.elementsPrimaryOndark) - internal var backgroundColorConfiguration = ControlColorConfiguration().with { - $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .normal) - $0.setSurfaceColors(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark, forState: .disabled) - } + internal var backgroundColorConfiguration = SurfaceColorConfiguration(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark) //-------------------------------------------------- // MARK: - Lifecycle @@ -163,6 +160,7 @@ open class CalendarBase: View { super.updateView() self.fetchDates(with: selectedDate) collectionView.reloadData() + layer.backgroundColor = backgroundColorConfiguration.getColor(self).cgColor if hideContainerBorder { layer.borderColor = nil layer.borderWidth = 0 @@ -230,7 +228,7 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI } } } - cell.configure(with: surface, indicators: indicators, text: days[indexPath.row], indicatorCount: indicatorCount, selectedDate: selectedDate) + cell.update(with: surface, indicators: indicators, text: days[indexPath.row], indicatorCount: indicatorCount, selectedDate: selectedDate, hideDate: hideCurrentDateIndicator) if (self.days[indexPath.row] == self.getDay(with: selectedDate)) { selectedIndexPath = indexPath } return cell } diff --git a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift index eb66cde7..d822c820 100644 --- a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift +++ b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift @@ -15,66 +15,6 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { ///Identifier for the Calendar Date Cell static let identifier: String = String(describing: CalendarDateCollectionViewCell.self) - //-------------------------------------------------- - // MARK: - Private Properties - //-------------------------------------------------- - private lazy var dateView = DateView() - - //-------------------------------------------------- - // MARK: - Initializers - //-------------------------------------------------- - override init(frame: CGRect) { - super.init(frame: frame) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func configure(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int, selectedDate: Date) { - addSubview(dateView) - dateView.surface = surface - dateView.numberLabel.text = text - dateView.indicatorCount = indicatorCount - dateView.dateIndicators = indicators - dateView.selectedDate = selectedDate - } - -} - -/// Date view to show Date number and indicator if applies -private class DateView : View { - - //-------------------------------------------------- - // MARK: - Initializers - //-------------------------------------------------- - required public init() { - super.init(frame: .zero) - } - - public override init(frame: CGRect) { - super.init(frame: .zero) - } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - } - - //-------------------------------------------------- - // MARK: - Public Properties - //-------------------------------------------------- - open var indicatorCount: Int = 0 - - open var dateIndicators: [CalendarBase.CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } - - open var numberLabel = Label().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.textAlignment = .center - $0.textStyle = .bodySmall - } - - open var selectedDate: Date = Date() //? { didSet { setNeedsUpdate() } } - //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -95,7 +35,13 @@ private class DateView : View { $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) } }() - + + private var numberLabel = Label().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.textAlignment = .center + $0.textStyle = .bodySmall + } + private lazy var shapeLayer = CAShapeLayer() private let selectedTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryInverseOnlight, VDSColor.elementsPrimaryInverseOndark) @@ -104,20 +50,25 @@ private class DateView : View { private let unselectedTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) private let unselectedCellIndicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) - - private let currentDate = Date() + private let currentDate = Date() + //-------------------------------------------------- - // MARK: - Lifecycle + // MARK: - Initializers //-------------------------------------------------- - open override func initialSetup() { - super.initialSetup() + override init(frame: CGRect) { + super.init(frame: frame) + setUp() } - open override func setup() { - super.setup() + required init?(coder: NSCoder) { + super.init(coder: coder) + setUp() + } + + private func setUp() { isAccessibilityElement = false - addSubview(containerView) + contentView.addSubview(containerView) containerView .pinTop() .pinBottom() @@ -130,27 +81,27 @@ private class DateView : View { // Number label containerView.addSubview(numberLabel) numberLabel.pinToSuperView() - + // Indicators containerView.addSubview(stackView) let topPos = containerSize.height * 0.7 stackView.pinTop(topPos).pinBottom().pinTopGreaterThanOrEqualTo().pinTrailingLessThanOrEqualTo().pinCenterY() stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } - - open override func updateView() { - super.updateView() + + func update(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int, selectedDate: Date, hideDate: Bool) { + numberLabel.text = text numberLabel.surface = surface - numberLabel.isEnabled = isEnabled +// numberLabel.isEnabled = isEnabled stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } - + // update text color, bg color, corner radius if numberLabel.text == self.getDay(with: selectedDate) { - numberLabel.textColor = selectedTextColorConfiguration.getColor(self) - layer.backgroundColor = selectedBackgroundColor.getColor(self).cgColor + numberLabel.textColor = selectedTextColorConfiguration.getColor(surface) + layer.backgroundColor = selectedBackgroundColor.getColor(surface).cgColor layer.cornerRadius = VDSFormControls.borderRadius } else { - numberLabel.textColor = unselectedTextColorConfiguration.getColor(self) + numberLabel.textColor = unselectedTextColorConfiguration.getColor(surface) layer.backgroundColor = nil layer.cornerRadius = 0 } @@ -159,34 +110,23 @@ private class DateView : View { if indicatorCount > 0 { let width = (indicatorCount * 8) + (Int(VDSLayout.space1X) * (indicatorCount - 1)) stackView.widthAnchor.constraint(equalToConstant: CGFloat(width)).activate() - for x in (0...(dateIndicators.count-1)) { - if (self.numberLabel.text == self.getDay(with: dateIndicators[x].date)) { + for x in (0...(indicators.count-1)) { + if (self.numberLabel.text == self.getDay(with: indicators[x].date)) { if numberLabel.text == self.getDay(with: selectedDate) { - addIndicator(with: selectedCellIndicatorColorConfiguration.getColor(self), surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) + addIndicator(with: selectedCellIndicatorColorConfiguration.getColor(surface), surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) } else { - addIndicator(with: unselectedCellIndicatorColorConfiguration.getColor(self), surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) + addIndicator(with: unselectedCellIndicatorColorConfiguration.getColor(surface), surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) } } } } // update text style for current date - if numberLabel.text == self.getDay(with: currentDate) { - numberLabel.textStyle = .boldBodySmall + if (numberLabel.text == self.getDay(with: currentDate)) { + numberLabel.textStyle = hideDate ? .bodySmall : .boldBodySmall } else { numberLabel.textStyle = .bodySmall } - - - } - - override open func layoutSubviews() { - super.layoutSubviews() - } - - open override func reset() { - super.reset() - numberLabel.textStyle = .bodySmall } //-------------------------------------------------- @@ -235,5 +175,4 @@ private class DateView : View { return day } } - } From 8560d7a6d4426fe51bca1659056b16cc47194bfc Mon Sep 17 00:00:00 2001 From: vasavk Date: Tue, 7 May 2024 11:13:56 +0530 Subject: [PATCH 16/23] Digital ACT-191 ONEAPP-7016 story: handling active dates, inactive dates, min date, max date with updated values --- VDS/Components/Calendar/Calendar.swift | 32 +++------ .../CalendarDateCollectionViewCell.swift | 68 +++++++++++++------ .../Calendar/CalendarHeaderView.swift | 15 ++-- 3 files changed, 69 insertions(+), 46 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index dd0f08eb..179f3efa 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -40,46 +40,36 @@ open class CalendarBase: View { /// Enable specific days. Pass an array of string value in date format e.g. ['07/21/2024', '07/24/2024', 07/28/2024']. /// All other dates will be inactive - open var activeDates: [Date] = [] - + open var activeDates: [Date] = [] { didSet { setNeedsUpdate() } } + /// Disable specific days. Pass an array of string value in date format e.g. ['07/21/2024', '07/24/2024', 07/28/2024']. /// All other dates will be active. - open var inactiveDates: [Date] = [] + open var inactiveDates: [Date] = [] { didSet { setNeedsUpdate() } } /// If provided, the calendar will allow a selection to be made from this date forward. Defaults to today. - open var minDate: Date? - + open var minDate: Date = Date() { didSet { setNeedsUpdate() } } + /// If provided, the calendar will allow a selection to be made up to this date. - open var maxDate: Date? + open var maxDate: Date = Date() { didSet { setNeedsUpdate() } } /// If provided, this is the date that will show as selected by the Calendar. /// If no value is provided, the current date will be used. If null is provided, no date will be selected. - open var selectedDate: Date { - get { return _selectedDate } - set { - _selectedDate = newValue - setNeedsUpdate() - } - } + open var selectedDate: Date = Date() { didSet { setNeedsUpdate() } } /// If provided, the calendar will be rendered with transparent background. - open var transparentBackground: Bool = false + open var transparentBackground: Bool = false { didSet { setNeedsUpdate() } } /// Array of ``CalendarIndicatorModel`` you are wanting to show on legend. open var indicators: [CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } - -// /// Array of indicators for the legend. -// open var indicatorData: [CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } - + /// A callback when the date changes. Passes parameters (selectedDate). public var onChangeSelectedDate: ((Date) -> Void)? //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var _selectedDate: Date = Date() internal var containerSize: CGSize { CGSize(width: 328, height: 376) } //width:320/328 - + private let cellItemSize = CGSize(width: 40, height: 40) private let headerHeight = 88.0 private let footerHeight = 40.0 @@ -228,7 +218,7 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI } } } - cell.update(with: surface, indicators: indicators, text: days[indexPath.row], indicatorCount: indicatorCount, selectedDate: selectedDate, hideDate: hideCurrentDateIndicator) + cell.update(with: surface, indicators: indicators, text: days[indexPath.row], indicatorCount: indicatorCount, selectedDate: selectedDate, hideDate: hideCurrentDateIndicator, minDate: minDate, maxDate: maxDate, activeDates: activeDates, inactiveDates: inactiveDates) if (self.days[indexPath.row] == self.getDay(with: selectedDate)) { selectedIndexPath = indexPath } return cell } diff --git a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift index d822c820..71562de5 100644 --- a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift +++ b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift @@ -9,7 +9,7 @@ import Foundation import UIKit import VDSTokens -/// Calendar collection view for Date view +/// Calendar collection view cell for Date view final class CalendarDateCollectionViewCell: UICollectionViewCell { ///Identifier for the Calendar Date Cell @@ -35,22 +35,23 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) } }() - + private var numberLabel = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .center $0.textStyle = .bodySmall } - + private lazy var shapeLayer = CAShapeLayer() private let selectedTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryInverseOnlight, VDSColor.elementsPrimaryInverseOndark) private let selectedBackgroundColor = SurfaceColorConfiguration(VDSColor.backgroundPrimaryInverseLight, VDSColor.backgroundPrimaryInverseDark) private let selectedCellIndicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteGray65, VDSColor.paletteGray44) - private let unselectedTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) private let unselectedCellIndicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) - + private let disabledTextColorConfiguration = SurfaceColorConfiguration(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark) + private let disabledBackgroundColor = SurfaceColorConfiguration(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark) + private let currentDate = Date() //-------------------------------------------------- @@ -66,6 +67,7 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { setUp() } + /// Configuring the cell with default setup private func setUp() { isAccessibilityElement = false contentView.addSubview(containerView) @@ -81,22 +83,53 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { // Number label containerView.addSubview(numberLabel) numberLabel.pinToSuperView() - + // Indicators containerView.addSubview(stackView) let topPos = containerSize.height * 0.7 stackView.pinTop(topPos).pinBottom().pinTopGreaterThanOrEqualTo().pinTrailingLessThanOrEqualTo().pinCenterY() stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } - - func update(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int, selectedDate: Date, hideDate: Bool) { - numberLabel.text = text + + /// Updating UI based on selected date, modified indicators data along with surface + /// Enable/disable cell based on min date, max date, active dates, inactive dates + func update(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int, selectedDate: Date, hideDate: Bool, minDate: Date, maxDate: Date, activeDates: [Date], inactiveDates: [Date]) { numberLabel.surface = surface -// numberLabel.isEnabled = isEnabled + numberLabel.text = text stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } - + + // disabled cells based on min date, max date. + if let day:Int = Int(numberLabel.text), day < minDate.dayInt || day > maxDate.dayInt { + numberLabel.isEnabled = false + numberLabel.textColor = disabledTextColorConfiguration.getColor(surface) + layer.backgroundColor = disabledBackgroundColor.getColor(surface).cgColor + } else { + numberLabel.isEnabled = false + // handing active dates + if activeDates.count > 0 && inactiveDates.count == 0 { + for x in (0...(activeDates.count-1)) { + if let day:Int = Int(numberLabel.text), day == activeDates[x].dayInt { + numberLabel.isEnabled = true + } + } + } else { + numberLabel.isEnabled = true + } + } + + // handling inactive dates + if inactiveDates.count > 0 { + for x in (0...(inactiveDates.count-1)) { + if let day:Int = Int(numberLabel.text), day == inactiveDates[x].dayInt { + numberLabel.isEnabled = false + numberLabel.textColor = disabledTextColorConfiguration.getColor(surface) + layer.backgroundColor = disabledBackgroundColor.getColor(surface).cgColor + } + } + } + // update text color, bg color, corner radius - if numberLabel.text == self.getDay(with: selectedDate) { + if (numberLabel.text == self.getDay(with: selectedDate)) && numberLabel.isEnabled { numberLabel.textColor = selectedTextColorConfiguration.getColor(surface) layer.backgroundColor = selectedBackgroundColor.getColor(surface).cgColor layer.cornerRadius = VDSFormControls.borderRadius @@ -112,18 +145,15 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { stackView.widthAnchor.constraint(equalToConstant: CGFloat(width)).activate() for x in (0...(indicators.count-1)) { if (self.numberLabel.text == self.getDay(with: indicators[x].date)) { - if numberLabel.text == self.getDay(with: selectedDate) { - addIndicator(with: selectedCellIndicatorColorConfiguration.getColor(surface), surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) - } else { - addIndicator(with: unselectedCellIndicatorColorConfiguration.getColor(surface), surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) - } + let color = (numberLabel.text == self.getDay(with: selectedDate)) ? selectedCellIndicatorColorConfiguration.getColor(surface) : unselectedCellIndicatorColorConfiguration.getColor(surface) + addIndicator(with: color, surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) } } } // update text style for current date if (numberLabel.text == self.getDay(with: currentDate)) { - numberLabel.textStyle = hideDate ? .bodySmall : .boldBodySmall + numberLabel.textStyle = hideDate ? .bodySmall : .boldBodySmall } else { numberLabel.textStyle = .bodySmall } @@ -164,7 +194,7 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { indicatorView.layer.addSublayer(shapeLayer) } - func getDay(with date:Date) -> String { + func getDay(with date: Date) -> String { if #available(iOS 15.0, *) { return date.formatted(.dateTime.day()) } else { diff --git a/VDS/Components/Calendar/CalendarHeaderView.swift b/VDS/Components/Calendar/CalendarHeaderView.swift index f217ae40..c47f4df9 100644 --- a/VDS/Components/Calendar/CalendarHeaderView.swift +++ b/VDS/Components/Calendar/CalendarHeaderView.swift @@ -33,7 +33,7 @@ open class CalendarHeaderView: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var containerSize: CGSize { CGSize(width: 304, height: 88) } //width:320/328 + internal var containerSize: CGSize { CGSize(width: 304, height: 88) } private lazy var stackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -60,7 +60,7 @@ open class CalendarHeaderView: View { internal var nextMonthView = View() internal var daysView = View() internal let daysOfWeek = Date.capitalizedFirstLettersOfWeekdays - + internal var previousButton = ButtonIcon().with { $0.kind = .ghost $0.iconName = .leftCaret @@ -112,7 +112,7 @@ open class CalendarHeaderView: View { super.setup() isAccessibilityElement = false - // stackView + // containerView addSubview(containerView) containerView .pinTop() @@ -127,9 +127,9 @@ open class CalendarHeaderView: View { containerView.addSubview(stackView) stackView .pinTop() + .pinBottom(VDSLayout.space1X) .pinLeading() .pinTrailing() - .pinBottom(VDSLayout.space1X) stackView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).activate() // month label view, previous and next buttons @@ -145,7 +145,9 @@ open class CalendarHeaderView: View { monthYearHeaderStackView.addArrangedSubview(previousMonthView) previousMonthView.widthAnchor.constraint(equalToConstant: 40).activate() previousMonthView.addSubview(previousButton) - + let spacing = VDSLayout.space1X + previousButton.pinTop(spacing).pinBottom(spacing).pinLeading(spacing).pinTrailing(spacing) + // month year label monthYearHeaderStackView.addArrangedSubview(monthYearLabel) @@ -153,6 +155,7 @@ open class CalendarHeaderView: View { monthYearHeaderStackView.addArrangedSubview(nextMonthView) nextMonthView.widthAnchor.constraint(equalToConstant: 40).activate() nextMonthView.addSubview(nextButton) + nextButton.pinTop(spacing).pinBottom(spacing).pinLeading(spacing).pinTrailing(spacing) // days Collection View stackView.addArrangedSubview(daysCollectionView) @@ -177,6 +180,7 @@ open class CalendarHeaderView: View { open override func reset() { super.reset() + monthYearLabel.textStyle = .boldBodySmall } } @@ -242,6 +246,5 @@ private class collectionViewCell: UICollectionViewCell { title.surface = surface title.text = text title.textColor = textColorConfiguration.getColor(surface) - title.backgroundColor = .clear } } From 8d415a72f1986b0549a3f563b0d9e0945a07ec92 Mon Sep 17 00:00:00 2001 From: vasavk Date: Wed, 8 May 2024 12:02:08 +0530 Subject: [PATCH 17/23] Digital ACT-191 ONEAPP-7958 story: Fixed issues in constraints for the component and updated to flexible width --- VDS/Components/Calendar/Calendar.swift | 14 ++- .../CalendarDateCollectionViewCell.swift | 7 +- .../Calendar/CalendarFooterView.swift | 2 +- .../Calendar/CalendarHeaderView.swift | 90 ++++++++----------- 4 files changed, 46 insertions(+), 67 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index 179f3efa..f57b45be 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -68,11 +68,12 @@ open class CalendarBase: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var containerSize: CGSize { CGSize(width: 328, height: 376) } //width:320/328 + internal var containerSize: CGSize { CGSize(width: 328, height: 376) } private let cellItemSize = CGSize(width: 40, height: 40) private let headerHeight = 88.0 private let footerHeight = 40.0 + private let calendarWidth = 304.0 private let items = 35 private var selectedIndexPath : IndexPath? @@ -127,22 +128,19 @@ open class CalendarBase: View { .pinTrailingLessThanOrEqualTo() .height(containerSize.height) .width(containerSize.width) - containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - - let spacing = CGFloat(VDSFormControls.spaceInset) //CGFloat(VDSLayout.space2X) // Calendar View containerView.addSubview(collectionView) + let calendarHeight = containerSize.height - (2 * VDSLayout.space4X) + let spacing = (containerSize.width - calendarWidth) / 2 collectionView .pinTop(VDSLayout.space4X) .pinBottom(VDSLayout.space4X) .pinLeading(spacing) .pinTrailing(spacing) - let width = containerSize.width - (2 * spacing) - collectionView.widthAnchor.constraint(equalToConstant: width).activate() - let height = containerSize.height - (2 * VDSLayout.space4X) - collectionView.heightAnchor.constraint(equalToConstant: height).activate() + .width(calendarWidth) + .height(calendarHeight) collectionView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).activate() } diff --git a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift index 71562de5..f73a1ae1 100644 --- a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift +++ b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift @@ -33,6 +33,7 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { $0.spacing = VDSLayout.space1X $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) + $0.backgroundColor = .clear } }() @@ -87,7 +88,7 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { // Indicators containerView.addSubview(stackView) let topPos = containerSize.height * 0.7 - stackView.pinTop(topPos).pinBottom().pinTopGreaterThanOrEqualTo().pinTrailingLessThanOrEqualTo().pinCenterY() + stackView.pinTop(topPos).pinBottom().pinTopGreaterThanOrEqualTo().pinTrailingLessThanOrEqualTo() stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } @@ -141,8 +142,8 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { // add indicators if indicatorCount > 0 { - let width = (indicatorCount * 8) + (Int(VDSLayout.space1X) * (indicatorCount - 1)) - stackView.widthAnchor.constraint(equalToConstant: CGFloat(width)).activate() +// let width = (indicatorCount * 8) + (Int(VDSLayout.space1X) * (indicatorCount - 1)) +// stackView.widthAnchor.constraint(equalToConstant: CGFloat(width)).activate() for x in (0...(indicators.count-1)) { if (self.numberLabel.text == self.getDay(with: indicators[x].date)) { let color = (numberLabel.text == self.getDay(with: selectedDate)) ? selectedCellIndicatorColorConfiguration.getColor(surface) : unselectedCellIndicatorColorConfiguration.getColor(surface) diff --git a/VDS/Components/Calendar/CalendarFooterView.swift b/VDS/Components/Calendar/CalendarFooterView.swift index 6574dc84..1ebd73be 100644 --- a/VDS/Components/Calendar/CalendarFooterView.swift +++ b/VDS/Components/Calendar/CalendarFooterView.swift @@ -35,7 +35,7 @@ open class CalendarFooterView: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - internal var containerSize: CGSize { CGSize(width: 304, height: 40) } //width:320/328 + internal var containerSize: CGSize { CGSize(width: 304, height: 40) } internal var containerView = View().with { $0.clipsToBounds = true diff --git a/VDS/Components/Calendar/CalendarHeaderView.swift b/VDS/Components/Calendar/CalendarHeaderView.swift index c47f4df9..53facda5 100644 --- a/VDS/Components/Calendar/CalendarHeaderView.swift +++ b/VDS/Components/Calendar/CalendarHeaderView.swift @@ -43,7 +43,7 @@ open class CalendarHeaderView: View { $0.backgroundColor = .clear } - private lazy var monthYearHeaderStackView = UIStackView().with { + private lazy var topHeaderView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.distribution = .fill $0.spacing = VDSLayout.space1X @@ -51,15 +51,22 @@ open class CalendarHeaderView: View { $0.backgroundColor = .clear } - internal var containerView = View().with { - $0.clipsToBounds = true - $0.backgroundColor = .clear - } - internal var monthView = View() + 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 + }() + internal var previousMonthView = View() internal var nextMonthView = View() - internal var daysView = View() - internal let daysOfWeek = Date.capitalizedFirstLettersOfWeekdays + let viewSize = 40.0 internal var previousButton = ButtonIcon().with { $0.kind = .ghost @@ -86,20 +93,8 @@ open class CalendarHeaderView: View { $0.isAccessibilityElement = false } - 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 - }() - - internal var monthYearLabelTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + internal let daysOfWeek = Date.capitalizedFirstLettersOfWeekdays + internal let monthYearLabelTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) //-------------------------------------------------- // MARK: - Lifecycle @@ -112,56 +107,41 @@ open class CalendarHeaderView: View { super.setup() isAccessibilityElement = false - // containerView - addSubview(containerView) - containerView - .pinTop() - .pinBottom() - .pinLeadingGreaterThanOrEqualTo() - .pinTrailingLessThanOrEqualTo() - .height(containerSize.height) - .width(containerSize.width) - containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - // stackview - containerView.addSubview(stackView) + addSubview(stackView) stackView .pinTop() .pinBottom(VDSLayout.space1X) .pinLeading() .pinTrailing() - stackView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).activate() + .height(containerSize.height) + .width(containerSize.width) + stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - // month label view, previous and next buttons - stackView.addArrangedSubview(monthView) - monthView.backgroundColor = .clear - monthView.heightAnchor.constraint(equalToConstant: 40).isActive = true - - // month Header stack view - monthView.addSubview(monthYearHeaderStackView) - monthYearHeaderStackView.pinToSuperView() + // top header stack view + stackView.addArrangedSubview(topHeaderView) + topHeaderView.heightAnchor.constraint(equalToConstant: viewSize).isActive = true + topHeaderView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() // previous button - monthYearHeaderStackView.addArrangedSubview(previousMonthView) - previousMonthView.widthAnchor.constraint(equalToConstant: 40).activate() + topHeaderView.addArrangedSubview(previousMonthView) + previousMonthView.widthAnchor.constraint(equalToConstant: viewSize).activate() previousMonthView.addSubview(previousButton) - let spacing = VDSLayout.space1X - previousButton.pinTop(spacing).pinBottom(spacing).pinLeading(spacing).pinTrailing(spacing) + previousButton.pinCenterY().pinCenterX() // month year label - monthYearHeaderStackView.addArrangedSubview(monthYearLabel) + topHeaderView.addArrangedSubview(monthYearLabel) // next button - monthYearHeaderStackView.addArrangedSubview(nextMonthView) - nextMonthView.widthAnchor.constraint(equalToConstant: 40).activate() + topHeaderView.addArrangedSubview(nextMonthView) + nextMonthView.widthAnchor.constraint(equalToConstant: viewSize).activate() nextMonthView.addSubview(nextButton) - nextButton.pinTop(spacing).pinBottom(spacing).pinLeading(spacing).pinTrailing(spacing) + nextButton.pinCenterY().pinCenterX() // days Collection View stackView.addArrangedSubview(daysCollectionView) - daysCollectionView.widthAnchor.constraint(equalTo: widthAnchor).activate() - daysCollectionView.heightAnchor.constraint(equalTo: monthView.heightAnchor).activate() - daysCollectionView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).activate() + topHeaderView.heightAnchor.constraint(equalToConstant: viewSize).isActive = true + daysCollectionView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } open override func updateView() { @@ -197,7 +177,7 @@ extension CalendarHeaderView: UICollectionViewDelegate, UICollectionViewDataSour } public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - return CGSize(width: 40, height: 40) + return CGSize(width: viewSize, height: viewSize) } public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { From 80f90e0b56c735defb095c849a46c1c83264f510 Mon Sep 17 00:00:00 2001 From: vasavk Date: Wed, 8 May 2024 12:03:57 +0530 Subject: [PATCH 18/23] Digital ACT-191 ONEAPP-7958 story: added missing date function --- VDS/Components/Calendar/Date+Extension.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/VDS/Components/Calendar/Date+Extension.swift b/VDS/Components/Calendar/Date+Extension.swift index 07284507..13e80479 100644 --- a/VDS/Components/Calendar/Date+Extension.swift +++ b/VDS/Components/Calendar/Date+Extension.swift @@ -62,4 +62,7 @@ extension Date { Calendar.current.component(.month, from: self) } + var dayInt: Int { + Calendar.current.component(.day, from: self) + } } From 450dd1aae8f6847e108c2da1174952e6e4def50d Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 9 May 2024 09:43:13 +0530 Subject: [PATCH 19/23] Digital ACT-191 ONEAPP-7958 story: added action for next and previous on changing min / max dates --- VDS/Components/Calendar/Calendar.swift | 52 +++++++++-- .../CalendarDateCollectionViewCell.swift | 22 ++--- .../Calendar/CalendarHeaderView.swift | 87 ++++++++++--------- .../Calendar/CalendarReusableView.swift | 22 ----- VDS/Components/Calendar/Date+Extension.swift | 36 ++++++++ 5 files changed, 139 insertions(+), 80 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index f57b45be..ce183fac 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -79,7 +79,8 @@ open class CalendarBase: View { private var selectedIndexPath : IndexPath? private var dates: [Date] = [] private var days: [String] = [] - + private var displayDate: Date = Date() + internal var containerView = View().with { $0.clipsToBounds = true } @@ -146,8 +147,14 @@ open class CalendarBase: View { open override func updateView() { super.updateView() - self.fetchDates(with: selectedDate) - collectionView.reloadData() + // range check between min & max dates + if (minDate <= maxDate) { + // Check if current date falls between min & max dates. + let fallsBetween = displayDate.isBetweeen(date: minDate, andDate: maxDate) + displayDate = fallsBetween ? displayDate : minDate + self.fetchDates(with: displayDate) + collectionView.reloadData() + } layer.backgroundColor = backgroundColorConfiguration.getColor(self).cgColor if hideContainerBorder { layer.borderColor = nil @@ -171,12 +178,12 @@ open class CalendarBase: View { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- - func fetchDates(with date:Date) { + func fetchDates(with aDate:Date) { days.removeAll() - self.dates = selectedDate.calendarDisplayDays + self.dates = aDate.calendarDisplayDays for date in dates { // code to be executed - if date.monthInt != selectedDate.monthInt { + if date.monthInt != aDate.monthInt { days.append("") } else { days.append(getDay(with: date)) @@ -216,7 +223,7 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI } } } - cell.update(with: surface, indicators: indicators, text: days[indexPath.row], indicatorCount: indicatorCount, selectedDate: selectedDate, hideDate: hideCurrentDateIndicator, minDate: minDate, maxDate: maxDate, activeDates: activeDates, inactiveDates: inactiveDates) + cell.update(with: surface, indicators: indicators, text: days[indexPath.row], indicatorCount: indicatorCount, selectedDate: selectedDate, displayDate: displayDate, hideDate: hideCurrentDateIndicator, minDate: minDate, maxDate: maxDate, activeDates: activeDates, inactiveDates: inactiveDates) if (self.days[indexPath.row] == self.getDay(with: selectedDate)) { selectedIndexPath = indexPath } return cell } @@ -227,7 +234,36 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CalendarHeaderReusableView.identifier, for: indexPath) as? CalendarHeaderReusableView else { return UICollectionReusableView() } - header.configure(with: surface) + var nextEnabled = false + var prevEnabled = false + + // check the interval between min date, max date.. set enable/disable flag for next / previous buttons. + if ((displayDate.monthInt < maxDate.monthInt) && (displayDate.yearInt == maxDate.yearInt)) || (displayDate.yearInt < maxDate.yearInt) { + nextEnabled = true + } + if ((minDate.monthInt < displayDate.monthInt) && (minDate.yearInt == displayDate.yearInt)) || (minDate.yearInt < displayDate.yearInt) { + prevEnabled = true + } + + header.nextClicked = { [weak self] in + guard let self = self else { return } + let aDate = Calendar.current.date(byAdding: .month, value: 1, to:self.displayDate)! + if ((aDate.monthInt <= maxDate.monthInt) && (aDate.yearInt == maxDate.yearInt)) || (aDate.yearInt < maxDate.yearInt) { + displayDate = aDate + self.fetchDates(with: displayDate) + self.collectionView.reloadData() + } + } + header.previousClicked = { [weak self] in + guard let self = self else { return } + let aDate = Calendar.current.date(byAdding: .month, value: -1, to:self.displayDate)! + if ((minDate.monthInt <= aDate.monthInt) && (minDate.yearInt == aDate.yearInt)) || (minDate.yearInt < aDate.yearInt) { + displayDate = aDate + self.fetchDates(with: displayDate) + self.collectionView.reloadData() + } + } + header.update(with: surface, aDate: displayDate, nextEnabled: nextEnabled, previousEnabled: prevEnabled) return header } else { // Footer diff --git a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift index f73a1ae1..efc506d8 100644 --- a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift +++ b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift @@ -94,12 +94,12 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { /// Updating UI based on selected date, modified indicators data along with surface /// Enable/disable cell based on min date, max date, active dates, inactive dates - func update(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int, selectedDate: Date, hideDate: Bool, minDate: Date, maxDate: Date, activeDates: [Date], inactiveDates: [Date]) { + func update(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int, selectedDate: Date, displayDate: Date, hideDate: Bool, minDate: Date, maxDate: Date, activeDates: [Date], inactiveDates: [Date]) { numberLabel.surface = surface numberLabel.text = text stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } - - // disabled cells based on min date, max date. + + // enable/disable cells based on min date, max date. if let day:Int = Int(numberLabel.text), day < minDate.dayInt || day > maxDate.dayInt { numberLabel.isEnabled = false numberLabel.textColor = disabledTextColorConfiguration.getColor(surface) @@ -121,16 +121,18 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { // handling inactive dates if inactiveDates.count > 0 { for x in (0...(inactiveDates.count-1)) { - if let day:Int = Int(numberLabel.text), day == inactiveDates[x].dayInt { - numberLabel.isEnabled = false - numberLabel.textColor = disabledTextColorConfiguration.getColor(surface) - layer.backgroundColor = disabledBackgroundColor.getColor(surface).cgColor + if (activeDates[x].monthInt == displayDate.monthInt) && (activeDates[x].yearInt == displayDate.yearInt) { + if let day:Int = Int(numberLabel.text), day == inactiveDates[x].dayInt { + numberLabel.isEnabled = false + numberLabel.textColor = disabledTextColorConfiguration.getColor(surface) + layer.backgroundColor = disabledBackgroundColor.getColor(surface).cgColor + } } } } // update text color, bg color, corner radius - if (numberLabel.text == self.getDay(with: selectedDate)) && numberLabel.isEnabled { + if (numberLabel.text == self.getDay(with: selectedDate)) && (selectedDate.monthInt == displayDate.monthInt) && (selectedDate.yearInt == displayDate.yearInt) && numberLabel.isEnabled { numberLabel.textColor = selectedTextColorConfiguration.getColor(surface) layer.backgroundColor = selectedBackgroundColor.getColor(surface).cgColor layer.cornerRadius = VDSFormControls.borderRadius @@ -142,8 +144,6 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { // add indicators if indicatorCount > 0 { -// let width = (indicatorCount * 8) + (Int(VDSLayout.space1X) * (indicatorCount - 1)) -// stackView.widthAnchor.constraint(equalToConstant: CGFloat(width)).activate() for x in (0...(indicators.count-1)) { if (self.numberLabel.text == self.getDay(with: indicators[x].date)) { let color = (numberLabel.text == self.getDay(with: selectedDate)) ? selectedCellIndicatorColorConfiguration.getColor(surface) : unselectedCellIndicatorColorConfiguration.getColor(surface) @@ -153,7 +153,7 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { } // update text style for current date - if (numberLabel.text == self.getDay(with: currentDate)) { + if (numberLabel.text == self.getDay(with: currentDate)) && (currentDate.monthInt == displayDate.monthInt) { numberLabel.textStyle = hideDate ? .bodySmall : .boldBodySmall } else { numberLabel.textStyle = .bodySmall diff --git a/VDS/Components/Calendar/CalendarHeaderView.swift b/VDS/Components/Calendar/CalendarHeaderView.swift index 53facda5..cdb08a18 100644 --- a/VDS/Components/Calendar/CalendarHeaderView.swift +++ b/VDS/Components/Calendar/CalendarHeaderView.swift @@ -10,25 +10,19 @@ import UIKit import VDSTokens /// Header view to display month and year along with days of week -open class CalendarHeaderView: View { - //-------------------------------------------------- - // MARK: - Initializers - //-------------------------------------------------- - required public init() { - super.init(frame: .zero) - } +class CalendarHeaderReusableView: UICollectionReusableView { - public override init(frame: CGRect) { - super.init(frame: .zero) - } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - } + ///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 @@ -64,6 +58,9 @@ open class CalendarHeaderView: View { return collectionView }() + private var surface: Surface = .light + private var displayDate: Date = Date() + internal var previousMonthView = View() internal var nextMonthView = View() let viewSize = 40.0 @@ -84,7 +81,7 @@ open class CalendarHeaderView: View { $0.size = .small } - internal var monthYearLabel = Label().with { + internal var headerTitle = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .center $0.numberOfLines = 1 @@ -94,17 +91,23 @@ open class CalendarHeaderView: View { } internal let daysOfWeek = Date.capitalizedFirstLettersOfWeekdays - internal let monthYearLabelTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) - + internal let headerTitleTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + //-------------------------------------------------- - // MARK: - Lifecycle + // MARK: - Initializers //-------------------------------------------------- - open override func initialSetup() { - super.initialSetup() + override init(frame: CGRect) { + super.init(frame: frame) + setUp() } - open override func setup() { - super.setup() + required init?(coder: NSCoder) { + super.init(coder: coder) + setUp() + } + + /// Configuring the cell with default setup + private func setUp() { isAccessibilityElement = false // stackview @@ -114,7 +117,7 @@ open class CalendarHeaderView: View { .pinBottom(VDSLayout.space1X) .pinLeading() .pinTrailing() - .height(containerSize.height) + .height(containerSize.height - VDSLayout.space1X) .width(containerSize.width) stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() @@ -128,43 +131,49 @@ open class CalendarHeaderView: View { previousMonthView.widthAnchor.constraint(equalToConstant: viewSize).activate() previousMonthView.addSubview(previousButton) previousButton.pinCenterY().pinCenterX() - + previousButton.onClick = { _ in self.previousButtonClick() } + // month year label - topHeaderView.addArrangedSubview(monthYearLabel) + 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() } - open override func updateView() { - super.updateView() - monthYearLabel.surface = surface + /// 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, aDate: 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() - monthYearLabel.text = "May 2024" - monthYearLabel.textColor = monthYearLabelTextColorConfiguration.getColor(surface) + let labelText = aDate.getMonthName(date: aDate) + " \(aDate.yearInt)" + headerTitle.text = labelText + headerTitle.textColor = headerTitleTextColorConfiguration.getColor(surface) } - override open func layoutSubviews() { - super.layoutSubviews() + func nextButtonClick() { + nextClicked?() } - - open override func reset() { - super.reset() - monthYearLabel.textStyle = .boldBodySmall + + func previousButtonClick() { + previousClicked?() } } -extension CalendarHeaderView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { +extension CalendarHeaderReusableView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return daysOfWeek.count @@ -172,7 +181,7 @@ extension CalendarHeaderView: UICollectionViewDelegate, UICollectionViewDataSour 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: surface) + cell.updateTitle(text: daysOfWeek[indexPath.row], surface: self.surface) return cell } diff --git a/VDS/Components/Calendar/CalendarReusableView.swift b/VDS/Components/Calendar/CalendarReusableView.swift index ebb2d269..4a695ab0 100644 --- a/VDS/Components/Calendar/CalendarReusableView.swift +++ b/VDS/Components/Calendar/CalendarReusableView.swift @@ -8,28 +8,6 @@ import UIKit import VDSTokens -/// Custom header view -class CalendarHeaderReusableView: UICollectionReusableView { - - ///Identifier for the Calendar Header Reusable View - static let identifier: String = String(describing: CalendarHeaderReusableView.self) - - private lazy var headerView = CalendarHeaderView() - - override init(frame: CGRect) { - super.init(frame: frame) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func configure(with surface: Surface) { - headerView.surface = surface - addSubview(headerView) - } -} - /// Custom footer view class CalendarFooterReusableView: UICollectionReusableView { diff --git a/VDS/Components/Calendar/Date+Extension.swift b/VDS/Components/Calendar/Date+Extension.swift index 13e80479..2a016ecb 100644 --- a/VDS/Components/Calendar/Date+Extension.swift +++ b/VDS/Components/Calendar/Date+Extension.swift @@ -8,7 +8,10 @@ import Foundation extension Date { + static var firstDayOfWeek = Calendar.current.firstWeekday + + /// Capitalizes the first letter of the day of the week static var capitalizedFirstLettersOfWeekdays: [String] { let calendar = Calendar.current let weekdays = calendar.shortWeekdaySymbols @@ -19,6 +22,18 @@ extension Date { } } + /// Returns all month names + static var fullMonthNames: [String] { + let dateFormatter = DateFormatter() + dateFormatter.locale = Locale.current + + return (1...12).compactMap { month in + dateFormatter.setLocalizedDateFormatFromTemplate("MMMM") + let date = Calendar.current.date(from: DateComponents(year: 2000, month: month, day: 1)) + return date.map { dateFormatter.string(from: $0) } + } + } + var startOfMonth: Date { Calendar.current.dateInterval(of: .month, for: self)!.start } @@ -28,6 +43,7 @@ extension Date { return Calendar.current.date(byAdding: .day, value: -1, to: lastDay)! } + /// Get the number of days of the month var numberOfDaysInMonth: Int { Calendar.current.component(.day, from: endOfMonth) } @@ -41,6 +57,7 @@ extension Date { return Calendar.current.date(byAdding: .day, value: -numberFromPreviousMonth, to: startOfMonth)! } + /// Get the days of the month to display var calendarDisplayDays: [Date] { var days: [Date] = [] // Start with days from the previous month to fill the grid @@ -58,11 +75,30 @@ extension Date { return days } + /// Returns the year value of the given date + var yearInt: Int { + Calendar.current.component(.year, from: self) + } + + /// Returns the month value of the given date var monthInt: Int { Calendar.current.component(.month, from: self) } + /// Returns the day value of the given date var dayInt: Int { Calendar.current.component(.day, from: self) } + + /// Check if the date falls between the given dates + func isBetweeen(date date1: Date, andDate date2: Date) -> Bool { + return date1.compare(self) == self.compare(date2) + } + + /// Returns the month name of the given date + func getMonthName(date: Date) -> String { + let names = Date.fullMonthNames + return names[date.monthInt - 1] + } + } From 4b5c4a28b25cbf6b29b7004322d397164d29b735 Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 9 May 2024 13:38:02 +0530 Subject: [PATCH 20/23] Digital ACT-191 ONEAPP-7958 story: handling days of month based on min, max date and active, inactive dates selected. --- VDS/Components/Calendar/Calendar.swift | 7 +- .../CalendarDateCollectionViewCell.swift | 128 +++++++++++++----- 2 files changed, 102 insertions(+), 33 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index ce183fac..e2f101d4 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -39,7 +39,7 @@ open class CalendarBase: View { open var hideCurrentDateIndicator: Bool = false { didSet { setNeedsUpdate() } } /// Enable specific days. Pass an array of string value in date format e.g. ['07/21/2024', '07/24/2024', 07/28/2024']. - /// All other dates will be inactive + /// All other dates will be inactive. open var activeDates: [Date] = [] { didSet { setNeedsUpdate() } } /// Disable specific days. Pass an array of string value in date format e.g. ['07/21/2024', '07/24/2024', 07/28/2024']. @@ -112,12 +112,13 @@ open class CalendarBase: View { internal var backgroundColorConfiguration = SurfaceColorConfiguration(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark) //-------------------------------------------------- - // MARK: - Lifecycle + // MARK: - Overrides //-------------------------------------------------- open override func initialSetup() { super.initialSetup() } + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() isAccessibilityElement = false @@ -171,6 +172,7 @@ open class CalendarBase: View { super.layoutSubviews() } + /// Resets to default settings. open override func reset() { super.reset() } @@ -283,6 +285,7 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI onChangeSelectedDate?(selectedItem) selectedDate = self.dates[indexPath.row] + displayDate = selectedDate var reloadIndexPaths = [indexPath] // If an cell is already selected, then it needs to be deselected. diff --git a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift index efc506d8..40ba8b82 100644 --- a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift +++ b/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift @@ -68,6 +68,10 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { setUp() } + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + /// Configuring the cell with default setup private func setUp() { isAccessibilityElement = false @@ -95,37 +99,20 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { /// Updating UI based on selected date, modified indicators data along with surface /// Enable/disable cell based on min date, max date, active dates, inactive dates func update(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int, selectedDate: Date, displayDate: Date, hideDate: Bool, minDate: Date, maxDate: Date, activeDates: [Date], inactiveDates: [Date]) { + + stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } numberLabel.surface = surface numberLabel.text = text - stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } - - // enable/disable cells based on min date, max date. - if let day:Int = Int(numberLabel.text), day < minDate.dayInt || day > maxDate.dayInt { - numberLabel.isEnabled = false - numberLabel.textColor = disabledTextColorConfiguration.getColor(surface) - layer.backgroundColor = disabledBackgroundColor.getColor(surface).cgColor - } else { - numberLabel.isEnabled = false - // handing active dates - if activeDates.count > 0 && inactiveDates.count == 0 { - for x in (0...(activeDates.count-1)) { - if let day:Int = Int(numberLabel.text), day == activeDates[x].dayInt { - numberLabel.isEnabled = true - } - } - } else { - numberLabel.isEnabled = true - } - } + + // enable/disable cells based on min date, max date and active/inactive dates + self.updateLabel(with:surface, displayDate: displayDate, minDate: minDate, maxDate: maxDate, activeDates: activeDates, inactiveDates: inactiveDates) // handling inactive dates if inactiveDates.count > 0 { for x in (0...(inactiveDates.count-1)) { - if (activeDates[x].monthInt == displayDate.monthInt) && (activeDates[x].yearInt == displayDate.yearInt) { + if (inactiveDates[x].monthInt == displayDate.monthInt) && (inactiveDates[x].yearInt == displayDate.yearInt) { if let day:Int = Int(numberLabel.text), day == inactiveDates[x].dayInt { - numberLabel.isEnabled = false - numberLabel.textColor = disabledTextColorConfiguration.getColor(surface) - layer.backgroundColor = disabledBackgroundColor.getColor(surface).cgColor + disableLabel(with: surface) } } } @@ -145,24 +132,103 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { // add indicators if indicatorCount > 0 { for x in (0...(indicators.count-1)) { - if (self.numberLabel.text == self.getDay(with: indicators[x].date)) { - let color = (numberLabel.text == self.getDay(with: selectedDate)) ? selectedCellIndicatorColorConfiguration.getColor(surface) : unselectedCellIndicatorColorConfiguration.getColor(surface) - addIndicator(with: color, surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) + // irrespective of month and year, if it needs to show indicators on every month - comment below first if condition + if (indicators[x].date.monthInt == displayDate.monthInt) && (indicators[x].date.yearInt == displayDate.yearInt) { + if (self.numberLabel.text == self.getDay(with: indicators[x].date)) { + let color = (numberLabel.text == self.getDay(with: selectedDate)) ? selectedCellIndicatorColorConfiguration.getColor(surface) : unselectedCellIndicatorColorConfiguration.getColor(surface) + addIndicator(with: color, surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) + } } } } // update text style for current date - if (numberLabel.text == self.getDay(with: currentDate)) && (currentDate.monthInt == displayDate.monthInt) { + if (numberLabel.text == self.getDay(with: currentDate)) && (currentDate.monthInt == displayDate.monthInt) && (currentDate.yearInt == displayDate.yearInt) { numberLabel.textStyle = hideDate ? .bodySmall : .boldBodySmall } else { numberLabel.textStyle = .bodySmall } } - //-------------------------------------------------- - // MARK: - Private Methods - //-------------------------------------------------- + func disableLabel(with surface: Surface) { + numberLabel.isEnabled = false + numberLabel.textColor = disabledTextColorConfiguration.getColor(surface) + layer.backgroundColor = disabledBackgroundColor.getColor(surface).cgColor + } + + func showActiveDates(with displayDate: Date, activeDates: [Date], inactiveDates: [Date]) { + for x in (0...(activeDates.count-1)) { + if (activeDates[x].monthInt == displayDate.monthInt) && (activeDates[x].yearInt == displayDate.yearInt ) { + if let day:Int = Int(numberLabel.text), day == activeDates[x].dayInt { + numberLabel.isEnabled = true + } + } + } + } + + // handing active dates if exist, else enable numberLabel to display day + func handleActiveDates(with displayDate: Date, activeDates: [Date], inactiveDates: [Date]) { + if activeDates.count > 0 && inactiveDates.count == 0 { + showActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } else { + numberLabel.isEnabled = true + } + } + + // enable all days if no active dates, handing active dates if exist + func enableAllDaysAndCheckActiveDates(with surface:Surface, displayDate: Date, activeDates: [Date], inactiveDates: [Date]) { + if activeDates.count > 0 && inactiveDates.count == 0 { + disableLabel(with: surface) + showActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } else { + numberLabel.isEnabled = true + } + } + + // enable/disable cells based on min date, max date and active/inactive dates + func updateLabel(with surface: Surface, displayDate: Date, minDate: Date, maxDate: Date, activeDates: [Date], inactiveDates: [Date]) { + if (minDate.yearInt == displayDate.yearInt) || (maxDate.yearInt == displayDate.yearInt) { + if (minDate.monthInt == displayDate.monthInt) && (maxDate.monthInt == displayDate.monthInt) { + // validate days to enable/disable + if let day:Int = Int(numberLabel.text), day < minDate.dayInt || day > maxDate.dayInt { + disableLabel(with: surface) + } else { + numberLabel.isEnabled = false + handleActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } + } else if (minDate.monthInt == displayDate.monthInt) || (maxDate.monthInt == displayDate.monthInt) { + if (minDate.monthInt == displayDate.monthInt) { + // validate days to enable/disable + if let day:Int = Int(numberLabel.text), day < minDate.dayInt { + disableLabel(with: surface) + } else { + numberLabel.isEnabled = false + handleActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } + + } else if (maxDate.monthInt == displayDate.monthInt) { + // validate days to enable/disable + if let day:Int = Int(numberLabel.text), day > maxDate.dayInt { + disableLabel(with: surface) + } else { + numberLabel.isEnabled = false + handleActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } + + } + } else if ((minDate.monthInt < displayDate.monthInt) || (displayDate.monthInt < maxDate.monthInt)) { + // enable all days if no active dates + // handing active dates + enableAllDaysAndCheckActiveDates(with: surface, displayDate: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } + } else { + // enable all days if no active dates + // handing active dates + enableAllDaysAndCheckActiveDates(with: surface, displayDate: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } + + } + func addIndicator(with color: UIColor, surface: Surface, clearFullCircle: Bool, drawSemiCircle: Bool) { // add indicator let indicatorView: View = View().with { From 6bb6d31b39687be091d35f4b72b9c8b1933c73a1 Mon Sep 17 00:00:00 2001 From: vasavk Date: Thu, 9 May 2024 20:52:46 +0530 Subject: [PATCH 21/23] Digital ACT-191 ONEAPP-7958 story: code refactored --- VDS.xcodeproj/project.pbxproj | 28 ++-- VDS/Components/Calendar/Calendar.swift | 12 +- ...wCell.swift => CalendarDateViewCell.swift} | 133 +++++++++++------- ...swift => CalendarFooterReusableView.swift} | 84 +++++------ ...swift => CalendarHeaderReusableView.swift} | 18 +-- .../Calendar/CalendarReusableView.swift | 32 ----- 6 files changed, 148 insertions(+), 159 deletions(-) rename VDS/Components/Calendar/{CalendarDateCollectionViewCell.swift => CalendarDateViewCell.swift} (68%) rename VDS/Components/Calendar/{CalendarFooterView.swift => CalendarFooterReusableView.swift} (80%) rename VDS/Components/Calendar/{CalendarHeaderView.swift => CalendarHeaderReusableView.swift} (95%) delete mode 100644 VDS/Components/Calendar/CalendarReusableView.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index d6c8c32c..f8b92433 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -10,7 +10,9 @@ 1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; }; 1808BEC02BA456B700129230 /* CarouselScrollbarChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */; }; 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; - 18408EDE2BE32C9900E8646B /* CalendarFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18408EDD2BE32C9900E8646B /* CalendarFooterView.swift */; }; + 1842B1DF2BECE28B0021AFCA /* CalendarDateViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1842B1DE2BECE28B0021AFCA /* CalendarDateViewCell.swift */; }; + 1842B1E12BECE7B70021AFCA /* CalendarHeaderReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1842B1E02BECE7B70021AFCA /* CalendarHeaderReusableView.swift */; }; + 1842B1E32BECF0A20021AFCA /* CalendarFooterReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1842B1E22BECF0A10021AFCA /* CalendarFooterReusableView.swift */; }; 18450CF12BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */; }; 1855EC662BAABF2A002ACAC2 /* BreadcrumbItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */; }; 186B2A8A2B88DA7F001AB71F /* TextAreaChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */; }; @@ -18,15 +20,12 @@ 186D13CF2BBC36EF00986B53 /* DropdownSelectChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */; }; 18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */; }; 18A3F12A2BD9298900498E4A /* Calendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A3F1292BD9298900498E4A /* Calendar.swift */; }; - 18A3F1322BD944E800498E4A /* CalendarDateCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A3F1312BD944E800498E4A /* CalendarDateCollectionViewCell.swift */; }; 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A012B96E848006602CC /* Breadcrumbs.swift */; }; 18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A65A032B96F050006602CC /* BreadcrumbItem.swift */; }; 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; 18BDEE822B75316E00452358 /* ButtonIconChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */; }; 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */; }; - 18FEA1B32BE0BC8700A56439 /* CalendarReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */; }; 18FEA1B52BE0E63600A56439 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */; }; - 18FEA1B72BE0EBFE00A56439 /* CalendarHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */; }; 18FEA1B92BE1301700A56439 /* CalendarChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18FEA1B82BE1301700A56439 /* CalendarChangeLog.txt */; }; 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; 44604AD429CE186A00E62B51 /* NotificationButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */; }; @@ -212,7 +211,9 @@ 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = ""; }; 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = ""; }; 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; - 18408EDD2BE32C9900E8646B /* CalendarFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarFooterView.swift; sourceTree = ""; }; + 1842B1DE2BECE28B0021AFCA /* CalendarDateViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarDateViewCell.swift; sourceTree = ""; }; + 1842B1E02BECE7B70021AFCA /* CalendarHeaderReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarHeaderReusableView.swift; sourceTree = ""; }; + 1842B1E22BECF0A10021AFCA /* CalendarFooterReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarFooterReusableView.swift; sourceTree = ""; }; 18450CF02BA1B19C009FDF2A /* BreadcrumbsChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = BreadcrumbsChangeLog.txt; sourceTree = ""; }; 1855EC652BAABF2A002ACAC2 /* BreadcrumbItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItemModel.swift; sourceTree = ""; }; 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextAreaChangeLog.txt; sourceTree = ""; }; @@ -220,15 +221,12 @@ 186D13CE2BBC36EE00986B53 /* DropdownSelectChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = DropdownSelectChangeLog.txt; sourceTree = ""; }; 18792A8F2B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconBadgeIndicatorModel.swift; sourceTree = ""; }; 18A3F1292BD9298900498E4A /* Calendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Calendar.swift; sourceTree = ""; }; - 18A3F1312BD944E800498E4A /* CalendarDateCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarDateCollectionViewCell.swift; sourceTree = ""; }; 18A65A012B96E848006602CC /* Breadcrumbs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Breadcrumbs.swift; sourceTree = ""; }; 18A65A032B96F050006602CC /* BreadcrumbItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbItem.swift; sourceTree = ""; }; 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = ""; }; 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = ""; }; 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarIndicatorModel.swift; sourceTree = ""; }; - 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarReusableView.swift; sourceTree = ""; }; 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = ""; }; - 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarHeaderView.swift; sourceTree = ""; }; 18FEA1B82BE1301700A56439 /* CalendarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CalendarChangeLog.txt; sourceTree = ""; }; 445BA07729C07B3D0036A7C5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationButtonModel.swift; sourceTree = ""; }; @@ -445,11 +443,10 @@ isa = PBXGroup; children = ( 18A3F1292BD9298900498E4A /* Calendar.swift */, - 18A3F1312BD944E800498E4A /* CalendarDateCollectionViewCell.swift */, - 18408EDD2BE32C9900E8646B /* CalendarFooterView.swift */, - 18FEA1B62BE0EBFE00A56439 /* CalendarHeaderView.swift */, + 1842B1DE2BECE28B0021AFCA /* CalendarDateViewCell.swift */, + 1842B1E22BECF0A10021AFCA /* CalendarFooterReusableView.swift */, + 1842B1E02BECE7B70021AFCA /* CalendarHeaderReusableView.swift */, 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */, - 18FEA1B22BE0BC8700A56439 /* CalendarReusableView.swift */, 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */, 18FEA1B82BE1301700A56439 /* CalendarChangeLog.txt */, ); @@ -1149,6 +1146,7 @@ EA985C2D296F03FE00F2FF2E /* TileletIconModels.swift in Sources */, EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */, 18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */, + 1842B1E12BECE7B70021AFCA /* CalendarHeaderReusableView.swift in Sources */, EA0D1C3F2A6AD5E200E5C127 /* Typography+ContentSizeCategory.swift in Sources */, EA5F86C82A1BD99100BC83E4 /* TabModel.swift in Sources */, EA297A5729FB0A360031ED56 /* AppleGuidelinesTouchable.swift in Sources */, @@ -1190,7 +1188,6 @@ 71ACE89C2BA0451200FB6ADC /* PaginationContainer.swift in Sources */, EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, - 18A3F1322BD944E800498E4A /* CalendarDateCollectionViewCell.swift in Sources */, EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */, 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */, EAF7F11728A1475A00B287F5 /* RadioButtonItem.swift in Sources */, @@ -1206,7 +1203,6 @@ EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */, EA985BEA29689B6D00F2FF2E /* TileletSubTitleModel.swift in Sources */, EA2DC9B02BE175BA004F58C5 /* RequiredRule.swift in Sources */, - 18FEA1B72BE0EBFE00A56439 /* CalendarHeaderView.swift in Sources */, EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */, EAB5FEED2927E1B200998C17 /* ButtonGroupPositionLayout.swift in Sources */, EA4DB30228DCBCA500103EE3 /* Badge.swift in Sources */, @@ -1243,7 +1239,6 @@ EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */, EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */, EAD0688E2A55F819002E3A2D /* Loader.swift in Sources */, - 18408EDE2BE32C9900E8646B /* CalendarFooterView.swift in Sources */, EAB5FEF829393A7200998C17 /* ButtonGroupConstants.swift in Sources */, EAA7456C2AB23E2000C1841F /* TooltipModel.swift in Sources */, EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */, @@ -1258,7 +1253,6 @@ EA985C1D296CD13600F2FF2E /* BundleManager.swift in Sources */, EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */, EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */, - 18FEA1B32BE0BC8700A56439 /* CalendarReusableView.swift in Sources */, EAF7F0AB289B13FD00B287F5 /* TextStyleLabelAttribute.swift in Sources */, EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */, EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */, @@ -1272,10 +1266,12 @@ EA0B18062A9E2D2D00F2D0CD /* SelectorItemBase.swift in Sources */, EAB5FF0129424ACB00998C17 /* UIControl.swift in Sources */, EA985BF5296C60C000F2FF2E /* Icon.swift in Sources */, + 1842B1E32BECF0A20021AFCA /* CalendarFooterReusableView.swift in Sources */, EA3361AA288B25E40071C351 /* Disabling.swift in Sources */, EA3361B6288B2A410071C351 /* Control.swift in Sources */, 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */, EAF7F0B7289C12A600B287F5 /* UITapGestureRecognizer.swift in Sources */, + 1842B1DF2BECE28B0021AFCA /* CalendarDateViewCell.swift in Sources */, EA0D1C392A6AD4DF00E5C127 /* Typography+SpacingConfig.swift in Sources */, 18FEA1B52BE0E63600A56439 /* Date+Extension.swift in Sources */, EAB2376629E9952D00AABE9A /* UIApplication.swift in Sources */, diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index e2f101d4..fa843db0 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -95,7 +95,7 @@ open class CalendarBase: View { collectionView.showsHorizontalScrollIndicator = false collectionView.showsVerticalScrollIndicator = false collectionView.backgroundColor = .clear - collectionView.register(CalendarDateCollectionViewCell.self, forCellWithReuseIdentifier: CalendarDateCollectionViewCell.identifier) + collectionView.register(CalendarDateViewCell.self, forCellWithReuseIdentifier: CalendarDateViewCell.identifier) collectionView.register(CalendarHeaderReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: CalendarHeaderReusableView.identifier) @@ -175,6 +175,12 @@ open class CalendarBase: View { /// Resets to default settings. open override func reset() { super.reset() + hideContainerBorder = false + hideCurrentDateIndicator = false + transparentBackground = false + activeDates = [] + inactiveDates = [] + indicators = [] } //-------------------------------------------------- @@ -216,7 +222,7 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CalendarDateCollectionViewCell.identifier, for: indexPath) as? CalendarDateCollectionViewCell else { return UICollectionViewCell() } + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CalendarDateViewCell.identifier, for: indexPath) as? CalendarDateViewCell else { return UICollectionViewCell() } var indicatorCount = 0 if self.indicators.count > 0 { for x in (0...(self.indicators.count-1)) { @@ -273,7 +279,7 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CalendarFooterReusableView.identifier, for: indexPath) as? CalendarFooterReusableView else { return UICollectionReusableView() } - footer.configure(with: surface, indicators: indicators) + footer.update(with: surface, indicators: indicators) return footer } } diff --git a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift b/VDS/Components/Calendar/CalendarDateViewCell.swift similarity index 68% rename from VDS/Components/Calendar/CalendarDateCollectionViewCell.swift rename to VDS/Components/Calendar/CalendarDateViewCell.swift index 40ba8b82..7bbe3fd0 100644 --- a/VDS/Components/Calendar/CalendarDateCollectionViewCell.swift +++ b/VDS/Components/Calendar/CalendarDateViewCell.swift @@ -1,19 +1,17 @@ // -// CalendarDateCollectionViewCell.swift +// CalendarDateViewCell.swift // VDS // // Created by Kanamarlapudi, Vasavi on 24/04/24. // -import Foundation import UIKit import VDSTokens -/// Calendar collection view cell for Date view -final class CalendarDateCollectionViewCell: UICollectionViewCell { +final class CalendarDateViewCell: UICollectionViewCell { - ///Identifier for the Calendar Date Cell - static let identifier: String = String(describing: CalendarDateCollectionViewCell.self) + ///Identifier for the Calendar Date Cell. + static let identifier: String = String(describing: CalendarDateViewCell.self) //-------------------------------------------------- // MARK: - Private Properties @@ -71,8 +69,7 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- - - /// Configuring the cell with default setup + /// Configuring the cell with default setup. private func setUp() { isAccessibilityElement = false contentView.addSubview(containerView) @@ -96,18 +93,18 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } - /// Updating UI based on selected date, modified indicators data along with surface - /// Enable/disable cell based on min date, max date, active dates, inactive dates + /// Updating UI based on selected date, modified indicators data along with surface. + /// Enable/disable cell based on min date, max date, active dates, inactive dates. func update(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int, selectedDate: Date, displayDate: Date, hideDate: Bool, minDate: Date, maxDate: Date, activeDates: [Date], inactiveDates: [Date]) { stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } numberLabel.surface = surface numberLabel.text = text - // enable/disable cells based on min date, max date and active/inactive dates + // enable/disable cells based on min date, max date and active/inactive dates. self.updateLabel(with:surface, displayDate: displayDate, minDate: minDate, maxDate: maxDate, activeDates: activeDates, inactiveDates: inactiveDates) - // handling inactive dates + // handling inactive dates. if inactiveDates.count > 0 { for x in (0...(inactiveDates.count-1)) { if (inactiveDates[x].monthInt == displayDate.monthInt) && (inactiveDates[x].yearInt == displayDate.yearInt) { @@ -118,7 +115,7 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { } } - // update text color, bg color, corner radius + // update text color, bg color, corner radius. if (numberLabel.text == self.getDay(with: selectedDate)) && (selectedDate.monthInt == displayDate.monthInt) && (selectedDate.yearInt == displayDate.yearInt) && numberLabel.isEnabled { numberLabel.textColor = selectedTextColorConfiguration.getColor(surface) layer.backgroundColor = selectedBackgroundColor.getColor(surface).cgColor @@ -129,20 +126,20 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { layer.cornerRadius = 0 } - // add indicators + // add indicators. if indicatorCount > 0 { for x in (0...(indicators.count-1)) { - // irrespective of month and year, if it needs to show indicators on every month - comment below first if condition - if (indicators[x].date.monthInt == displayDate.monthInt) && (indicators[x].date.yearInt == displayDate.yearInt) { + // irrespective of month and year, if it needs to show indicators on every month - comment below first if condition, else uncomment it. + // if (indicators[x].date.monthInt == displayDate.monthInt) && (indicators[x].date.yearInt == displayDate.yearInt) { if (self.numberLabel.text == self.getDay(with: indicators[x].date)) { let color = (numberLabel.text == self.getDay(with: selectedDate)) ? selectedCellIndicatorColorConfiguration.getColor(surface) : unselectedCellIndicatorColorConfiguration.getColor(surface) addIndicator(with: color, surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) } - } + // } } } - // update text style for current date + // update text style for current date. if (numberLabel.text == self.getDay(with: currentDate)) && (currentDate.monthInt == displayDate.monthInt) && (currentDate.yearInt == displayDate.yearInt) { numberLabel.textStyle = hideDate ? .bodySmall : .boldBodySmall } else { @@ -166,7 +163,7 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { } } - // handing active dates if exist, else enable numberLabel to display day + // handing active dates if exist, else enable numberLabel to display day. func handleActiveDates(with displayDate: Date, activeDates: [Date], inactiveDates: [Date]) { if activeDates.count > 0 && inactiveDates.count == 0 { showActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) @@ -175,7 +172,7 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { } } - // enable all days if no active dates, handing active dates if exist + // enable all days if no active dates, handing active dates if exist. func enableAllDaysAndCheckActiveDates(with surface:Surface, displayDate: Date, activeDates: [Date], inactiveDates: [Date]) { if activeDates.count > 0 && inactiveDates.count == 0 { disableLabel(with: surface) @@ -184,49 +181,83 @@ final class CalendarDateCollectionViewCell: UICollectionViewCell { numberLabel.isEnabled = true } } + + func minDateValidation(with surface:Surface, minDate: Date, displayDate: Date, activeDates: [Date], inactiveDates: [Date]) { + // validate days to enable/disable with min date only. + if let day:Int = Int(numberLabel.text), day < minDate.dayInt { + disableLabel(with: surface) + } else { + numberLabel.isEnabled = false + handleActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } + } + + func maxDateValidation(with surface:Surface, maxDate: Date, displayDate: Date, activeDates: [Date], inactiveDates: [Date]) { + // validate days to enable/disable with max date only. + if let day:Int = Int(numberLabel.text), day > maxDate.dayInt { + disableLabel(with: surface) + } else { + numberLabel.isEnabled = false + handleActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } + } + + func minAndMaxDateValidation(with surface:Surface, minDate: Date, maxDate: Date, displayDate: Date, activeDates: [Date], inactiveDates: [Date]) { + // validate days to enable/disable with min and max date. + if let day:Int = Int(numberLabel.text), day < minDate.dayInt || day > maxDate.dayInt { + disableLabel(with: surface) + } else { + numberLabel.isEnabled = false + handleActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } + } - // enable/disable cells based on min date, max date and active/inactive dates + // enable/disable cells based on min date, max date and active/inactive dates. func updateLabel(with surface: Surface, displayDate: Date, minDate: Date, maxDate: Date, activeDates: [Date], inactiveDates: [Date]) { - if (minDate.yearInt == displayDate.yearInt) || (maxDate.yearInt == displayDate.yearInt) { + + if (minDate.yearInt == displayDate.yearInt) && !(maxDate.yearInt == displayDate.yearInt) { + // min year and max year are different, and matched to min year. + if (minDate.monthInt == displayDate.monthInt) { + // min year and max year are different, and matched to min year and min month. + minDateValidation(with: surface, minDate: minDate, displayDate: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } else { + // handing active dates - enable all days if no active dates. + enableAllDaysAndCheckActiveDates(with: surface, displayDate: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } + } else if (maxDate.yearInt == displayDate.yearInt) && !((minDate.yearInt == displayDate.yearInt)) { + // min year and max year are different, and matched to max year. + if (maxDate.monthInt == displayDate.monthInt) { + // min year and max year are different, and matched to max year and max month. + maxDateValidation(with: surface, maxDate: maxDate, displayDate: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } else { + // handing active dates - enable all days if no active dates. + enableAllDaysAndCheckActiveDates(with: surface, displayDate: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } + } else if (minDate.yearInt == displayDate.yearInt) && (maxDate.yearInt == displayDate.yearInt) { + // min year and max year same if (minDate.monthInt == displayDate.monthInt) && (maxDate.monthInt == displayDate.monthInt) { - // validate days to enable/disable - if let day:Int = Int(numberLabel.text), day < minDate.dayInt || day > maxDate.dayInt { - disableLabel(with: surface) - } else { - numberLabel.isEnabled = false - handleActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) - } + // min year and max year same, when choose dates in same month. + minAndMaxDateValidation(with: surface, minDate: minDate, maxDate: maxDate, displayDate: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) + } else if (minDate.monthInt == displayDate.monthInt) || (maxDate.monthInt == displayDate.monthInt) { + // min year and max year same, and choose dates in different months. if (minDate.monthInt == displayDate.monthInt) { - // validate days to enable/disable - if let day:Int = Int(numberLabel.text), day < minDate.dayInt { - disableLabel(with: surface) - } else { - numberLabel.isEnabled = false - handleActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) - } - + // min year and max year same, and matched to min month. + minDateValidation(with: surface, minDate: minDate, displayDate: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) } else if (maxDate.monthInt == displayDate.monthInt) { - // validate days to enable/disable - if let day:Int = Int(numberLabel.text), day > maxDate.dayInt { - disableLabel(with: surface) - } else { - numberLabel.isEnabled = false - handleActiveDates(with: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) - } - + // min year and max year same, and matched to max month. + maxDateValidation(with: surface, maxDate: maxDate, displayDate: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) } - } else if ((minDate.monthInt < displayDate.monthInt) || (displayDate.monthInt < maxDate.monthInt)) { - // enable all days if no active dates - // handing active dates + } else { + // min year and max year same, and not matched to min or max month. + // handing active dates - enable all days if no active dates. enableAllDaysAndCheckActiveDates(with: surface, displayDate: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) } } else { - // enable all days if no active dates - // handing active dates + // min year and max year are different, and not matched to min or max year. + // handing active dates - enable all days if no active dates. enableAllDaysAndCheckActiveDates(with: surface, displayDate: displayDate, activeDates: activeDates, inactiveDates: inactiveDates) } - } func addIndicator(with color: UIColor, surface: Surface, clearFullCircle: Bool, drawSemiCircle: Bool) { diff --git a/VDS/Components/Calendar/CalendarFooterView.swift b/VDS/Components/Calendar/CalendarFooterReusableView.swift similarity index 80% rename from VDS/Components/Calendar/CalendarFooterView.swift rename to VDS/Components/Calendar/CalendarFooterReusableView.swift index 1ebd73be..3e85a6ec 100644 --- a/VDS/Components/Calendar/CalendarFooterView.swift +++ b/VDS/Components/Calendar/CalendarFooterReusableView.swift @@ -1,40 +1,24 @@ // -// CalendarFooterView.swift +// CalendarFooterReusableView.swift // VDS // // Created by Kanamarlapudi, Vasavi on 29/04/24. // -import Foundation import UIKit import VDSTokens -import Combine /// Footer view to show indicators data. -open class CalendarFooterView: View { - //-------------------------------------------------- - // MARK: - Initializers - //-------------------------------------------------- - required public init() { - super.init(frame: .zero) - } +class CalendarFooterReusableView: UICollectionReusableView { - public override init(frame: CGRect) { - super.init(frame: .zero) - } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - } - - //-------------------------------------------------- - // MARK: - Public Properties - //-------------------------------------------------- - open var items: [CalendarBase.CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } - + ///Identifier for the Calendar Footer Reusable View. + static let identifier: String = String(describing: CalendarFooterReusableView.self) + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + private var surface: Surface = .light + private var items: [CalendarBase.CalendarIndicatorModel] = [] internal var containerSize: CGSize { CGSize(width: 304, height: 40) } internal var containerView = View().with { @@ -60,50 +44,51 @@ open class CalendarFooterView: View { } //-------------------------------------------------- - // MARK: - Lifecycle + // MARK: - Initializers //-------------------------------------------------- - open override func initialSetup() { - super.initialSetup() + override init(frame: CGRect) { + super.init(frame: frame) + setUp() } - open override func setup() { - super.setup() + required init?(coder: NSCoder) { + super.init(coder: coder) + setUp() + } + + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + /// Configuring the cell with default setup. + private func setUp() { isAccessibilityElement = false addSubview(containerView) containerView - .pinTop(VDSLayout.space6X) +// .pinTop(VDSLayout.space6X) + .pinTopLessThanOrEqualTo(topAnchor, VDSLayout.space6X, .defaultLow) .pinBottom() - .pinLeadingGreaterThanOrEqualTo(leadingAnchor, VDSLayout.space3X, .defaultLow) + .pinLeadingGreaterThanOrEqualTo(leadingAnchor, VDSLayout.space3X, .defaultHigh) .pinTrailingLessThanOrEqualTo(trailingAnchor, VDSLayout.space3X, .defaultHigh) - .height(containerSize.height) - .width(containerSize.width) - - containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() - + .width(containerSize.width - (2*VDSLayout.space3X)) + .heightLessThanEqualTo(containerSize.height) + // legend Collection View containerView.addSubview(legendCollectionView) - legendCollectionView.pinToSuperView() + legendCollectionView.pinTop().pinBottom().pinLeading().pinTrailing().pinCenterY().pinCenterX() } - open override func updateView() { - super.updateView() + /// Updating UI to show legend with titles. + func update(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel]) { + self.items = indicators + self.surface = surface legendCollectionView.reloadData() - setNeedsLayout() - layoutIfNeeded() } - override open func layoutSubviews() { - super.layoutSubviews() - } - - open override func reset() { - super.reset() - } } -extension CalendarFooterView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { +extension CalendarFooterReusableView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count @@ -114,7 +99,7 @@ extension CalendarFooterView: UICollectionViewDelegate, UICollectionViewDataSour let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LegendCollectionViewCell.identifier, for: indexPath) as? LegendCollectionViewCell, indexPath.row <= items.count else { return UICollectionViewCell() } let text = items[indexPath.row].label - cell.updateTitle(text: text, color: VDSColor.elementsSecondaryOnlight, surface: surface, clearFullcircle: indexPath.row == 1, drawSemiCircle: indexPath.row == 2) + cell.updateTitle(text: text, color: VDSColor.elementsSecondaryOnlight, surface: self.surface, clearFullcircle: indexPath.row == 1, drawSemiCircle: indexPath.row == 2) return cell } } @@ -133,6 +118,7 @@ private class LegendCollectionViewCell: UICollectionViewCell { $0.numberOfLines = 1 $0.textStyle = .bodySmall $0.isAccessibilityElement = false + $0.backgroundColor = .clear } private var legendIndicatorWrapper: View = View().with { diff --git a/VDS/Components/Calendar/CalendarHeaderView.swift b/VDS/Components/Calendar/CalendarHeaderReusableView.swift similarity index 95% rename from VDS/Components/Calendar/CalendarHeaderView.swift rename to VDS/Components/Calendar/CalendarHeaderReusableView.swift index cdb08a18..b1d18810 100644 --- a/VDS/Components/Calendar/CalendarHeaderView.swift +++ b/VDS/Components/Calendar/CalendarHeaderReusableView.swift @@ -1,27 +1,26 @@ // -// CalendarHeaderView.swift +// CalendarHeaderReusableView.swift // VDS // // Created by Kanamarlapudi, Vasavi on 30/04/24. // -import Foundation import UIKit import VDSTokens -/// Header view to display month and year along with days of week +/// Header view to display month and year along with days of week. class CalendarHeaderReusableView: UICollectionReusableView { - ///Identifier for the Calendar Header Reusable View + ///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 + /// A callback when the next button clicked. public var nextClicked: (() -> (Void))? - /// A callback when the previous button clicked + /// A callback when the previous button clicked. public var previousClicked: (() -> (Void))? //-------------------------------------------------- @@ -106,7 +105,10 @@ class CalendarHeaderReusableView: UICollectionReusableView { setUp() } - /// Configuring the cell with default setup + //-------------------------------------------------- + // MARK: - Private Methods + //-------------------------------------------------- + /// Configuring the cell with default setup. private func setUp() { isAccessibilityElement = false @@ -150,7 +152,7 @@ class CalendarHeaderReusableView: UICollectionReusableView { } /// Updating UI based on next/previous clicks along with surface. - /// Updating UI to enable/disable the next & previous buttons, updating header title + /// Updating UI to enable/disable the next & previous buttons, updating header title. func update(with surface: Surface, aDate: Date, nextEnabled: Bool, previousEnabled: Bool) { self.surface = surface headerTitle.surface = surface diff --git a/VDS/Components/Calendar/CalendarReusableView.swift b/VDS/Components/Calendar/CalendarReusableView.swift deleted file mode 100644 index 4a695ab0..00000000 --- a/VDS/Components/Calendar/CalendarReusableView.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// CalendarReusableView.swift -// VDS -// -// Created by Kanamarlapudi, Vasavi on 24/04/24. -// - -import UIKit -import VDSTokens - -/// Custom footer view -class CalendarFooterReusableView: UICollectionReusableView { - - ///Identifier for the Calendar Footer Reusable View - static let identifier: String = String(describing: CalendarFooterReusableView.self) - - private lazy var footerView = CalendarFooterView() - - override init(frame: CGRect) { - super.init(frame: frame) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func configure(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel]) { - footerView.items = indicators - footerView.surface = surface - addSubview(footerView) - } -} From 20f5926bbf6ad810f605adb4e54998a75a111bdf Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 10 May 2024 13:18:55 +0530 Subject: [PATCH 22/23] Digital ACT-191 ONEAPP-7958 story: callback when the date changes and is in enabled state only. --- VDS/Components/Calendar/Calendar.swift | 27 ++++++++++++------- .../Calendar/CalendarDateViewCell.swift | 5 ++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index fa843db0..bcba3885 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -287,17 +287,24 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI } public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - let selectedItem = Calendar.current.date(byAdding: .day, value: 1, to: self.dates[indexPath.row])! - onChangeSelectedDate?(selectedItem) - - selectedDate = self.dates[indexPath.row] - displayDate = selectedDate - var reloadIndexPaths = [indexPath] + // reload selected index, if it is in enabled state. + if let cell = collectionView.cellForItem(at: indexPath) as? CalendarDateViewCell { + let isEnabled: Bool = cell.isDateEnabled() + if isEnabled { + // Callback to pass selected date if it is enabled only. + let selectedItem = Calendar.current.date(byAdding: .day, value: 1, to: self.dates[indexPath.row])! + onChangeSelectedDate?(selectedItem) + + selectedDate = self.dates[indexPath.row] + displayDate = selectedDate + var reloadIndexPaths = [indexPath] - // If an cell is already selected, then it needs to be deselected. - // Add its index path to the array of index paths to be reloaded. - if let deselectIndexPath = selectedIndexPath { reloadIndexPaths.append(deselectIndexPath) } - self.collectionView.reloadItems(at: reloadIndexPaths) + // If an cell is already selected, then it needs to be deselected. + // Add its index path to the array of index paths to be reloaded. + if let deselectIndexPath = selectedIndexPath { reloadIndexPaths.append(deselectIndexPath) } + self.collectionView.reloadItems(at: reloadIndexPaths) + } + } } public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { diff --git a/VDS/Components/Calendar/CalendarDateViewCell.swift b/VDS/Components/Calendar/CalendarDateViewCell.swift index 7bbe3fd0..26886d0a 100644 --- a/VDS/Components/Calendar/CalendarDateViewCell.swift +++ b/VDS/Components/Calendar/CalendarDateViewCell.swift @@ -147,6 +147,11 @@ final class CalendarDateViewCell: UICollectionViewCell { } } + // returns cell enabled state. + func isDateEnabled() -> Bool { + return numberLabel.isEnabled + } + func disableLabel(with surface: Surface) { numberLabel.isEnabled = false numberLabel.textColor = disabledTextColorConfiguration.getColor(surface) From 843c8054ad10bffc630a62cbc2cf9bbf1aa42f88 Mon Sep 17 00:00:00 2001 From: vasavk Date: Fri, 10 May 2024 22:40:37 +0530 Subject: [PATCH 23/23] Digital ACT-191 ONEAPP-7958 story: dynamic height update for calendar --- VDS/Components/Calendar/Calendar.swift | 28 +++++++++++++------- VDS/Components/Calendar/Date+Extension.swift | 4 ++- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/VDS/Components/Calendar/Calendar.swift b/VDS/Components/Calendar/Calendar.swift index bcba3885..9ec8e6fc 100644 --- a/VDS/Components/Calendar/Calendar.swift +++ b/VDS/Components/Calendar/Calendar.swift @@ -74,8 +74,9 @@ open class CalendarBase: View { private let headerHeight = 88.0 private let footerHeight = 40.0 private let calendarWidth = 304.0 - private let items = 35 - + + private var heightConstraint: NSLayoutConstraint? + private var containerHeightConstraint: NSLayoutConstraint? private var selectedIndexPath : IndexPath? private var dates: [Date] = [] private var days: [String] = [] @@ -121,15 +122,16 @@ open class CalendarBase: View { /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() - isAccessibilityElement = false + isAccessibilityElement = true + accessibilityLabel = "Calendar" addSubview(containerView) containerView .pinTop() .pinBottom() .pinLeadingGreaterThanOrEqualTo() .pinTrailingLessThanOrEqualTo() - .height(containerSize.height) .width(containerSize.width) + .heightGreaterThanEqualTo(containerSize.height) containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() // Calendar View @@ -142,10 +144,10 @@ open class CalendarBase: View { .pinLeading(spacing) .pinTrailing(spacing) .width(calendarWidth) - .height(calendarHeight) + .heightGreaterThanEqualTo(calendarHeight) collectionView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).activate() } - + open override func updateView() { super.updateView() // range check between min & max dates @@ -154,8 +156,8 @@ open class CalendarBase: View { let fallsBetween = displayDate.isBetweeen(date: minDate, andDate: maxDate) displayDate = fallsBetween ? displayDate : minDate self.fetchDates(with: displayDate) - collectionView.reloadData() } + layer.backgroundColor = backgroundColorConfiguration.getColor(self).cgColor if hideContainerBorder { layer.borderColor = nil @@ -187,6 +189,8 @@ open class CalendarBase: View { // MARK: - Private Methods //-------------------------------------------------- func fetchDates(with aDate:Date) { + heightConstraint?.isActive = false + containerHeightConstraint?.isActive = false days.removeAll() self.dates = aDate.calendarDisplayDays for date in dates { @@ -197,6 +201,14 @@ open class CalendarBase: View { days.append(getDay(with: date)) } } + self.collectionView.reloadData() + var height = self.collectionView.collectionViewLayout.collectionViewContentSize.height + height = (height > 0) ? height : containerSize.height + heightConstraint = collectionView.heightAnchor.constraint(equalToConstant: height) + containerHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: (height + (2 * VDSLayout.space4X))) + heightConstraint?.isActive = true + containerHeightConstraint?.isActive = true + self.layoutIfNeeded() } func getDay(with date:Date) -> String { @@ -259,7 +271,6 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI if ((aDate.monthInt <= maxDate.monthInt) && (aDate.yearInt == maxDate.yearInt)) || (aDate.yearInt < maxDate.yearInt) { displayDate = aDate self.fetchDates(with: displayDate) - self.collectionView.reloadData() } } header.previousClicked = { [weak self] in @@ -268,7 +279,6 @@ extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UI if ((minDate.monthInt <= aDate.monthInt) && (minDate.yearInt == aDate.yearInt)) || (minDate.yearInt < aDate.yearInt) { displayDate = aDate self.fetchDates(with: displayDate) - self.collectionView.reloadData() } } header.update(with: surface, aDate: displayDate, nextEnabled: nextEnabled, previousEnabled: prevEnabled) diff --git a/VDS/Components/Calendar/Date+Extension.swift b/VDS/Components/Calendar/Date+Extension.swift index 2a016ecb..8d697049 100644 --- a/VDS/Components/Calendar/Date+Extension.swift +++ b/VDS/Components/Calendar/Date+Extension.swift @@ -92,7 +92,9 @@ extension Date { /// Check if the date falls between the given dates func isBetweeen(date date1: Date, andDate date2: Date) -> Bool { - return date1.compare(self) == self.compare(date2) + let from = Calendar.current.date(byAdding: .day, value: -1, to: date1)! + let to = Calendar.current.date(byAdding: .day, value: 1, to: date2)! + return from.compare(self) == self.compare(to) } /// Returns the month name of the given date