From 6f17c1b0dab09e4be8a272d7fbea6330ec30ce14 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 14 May 2024 11:23:00 -0500 Subject: [PATCH] refactored to use new calendar Signed-off-by: Matt Bruce --- VDS/Components/Calendar/Date+Extension.swift | 2 +- VDS/Components/DatePicker/DatePicker.swift | 195 +++++++++++++++---- 2 files changed, 159 insertions(+), 38 deletions(-) diff --git a/VDS/Components/Calendar/Date+Extension.swift b/VDS/Components/Calendar/Date+Extension.swift index 8d697049..ca4f5a3b 100644 --- a/VDS/Components/Calendar/Date+Extension.swift +++ b/VDS/Components/Calendar/Date+Extension.swift @@ -7,7 +7,7 @@ import Foundation -extension Date { +public extension Date { static var firstDayOfWeek = Calendar.current.firstWeekday diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index 2d6b620e..710de91a 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -52,6 +52,10 @@ open class DatePicker: EntryFieldBase, DatePickerPopoverViewControllerDelegate, open var selectedDate: Date? { didSet { setNeedsUpdate() } } + open var calendarModel: CalendarModel = .init() { + didSet { setNeedsUpdate() } + } + open override var value: String? { get { selectedDateLabel.text } set { } @@ -154,14 +158,15 @@ open class DatePicker: EntryFieldBase, DatePickerPopoverViewControllerDelegate, } internal func togglePicker() { - let calendarVC = DatePickerPopoverViewController(calendar: Calendar(identifier: .gregorian), delegate: self) + //let calendarVC = DatePickerPopoverViewController(calendar: Calendar(identifier: .gregorian), delegate: self) + let calendarVC = DatePickerPopoverViewController(calendarModel, delegate: self) calendarVC.modalPresentationStyle = .popover calendarVC.selectedDate = selectedDate ?? Date() if let popoverController = calendarVC.popoverPresentationController { popoverController.delegate = self popoverController.sourceView = containerView popoverController.sourceRect = containerView.bounds - popoverController.permittedArrowDirections = .any + popoverController.permittedArrowDirections = .up } if let viewController = UIApplication.topViewController() { viewController.present(calendarVC, animated: true, completion: nil) @@ -179,44 +184,160 @@ open class DatePicker: EntryFieldBase, DatePickerPopoverViewControllerDelegate, } } -protocol DatePickerPopoverViewControllerDelegate: NSObject { - func didSelectDate(_ controller: DatePickerPopoverViewController, date: Date) -} - -class DatePickerPopoverViewController: UIViewController { - - private let picker = UIDatePicker() - weak var delegate: DatePickerPopoverViewControllerDelegate? - - init(calendar: Calendar, delegate: DatePickerPopoverViewControllerDelegate?) { - self.delegate = delegate - super.init(nibName: nil, bundle: nil) - picker.datePickerMode = .date - picker.preferredDatePickerStyle = .inline - picker.addTarget(self, action: #selector(dateChanged(_:)), for: .valueChanged) - } - - var selectedDate: Date = Date() { - didSet { - picker.date = selectedDate +extension DatePicker { + public struct CalendarModel { + public let surface: Surface + + /// If set to true, the calendar will not have a border. + public let hideContainerBorder: Bool + + /// If set to true, the calendar will not have current date indication. + public let hideCurrentDateIndicator: Bool + + /// 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. + public let 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. + public let inactiveDates: [Date] + + /// If provided, the calendar will allow a selection to be made from this date forward. Defaults to today. + public let minDate: Date + + /// If provided, the calendar will allow a selection to be made up to this date. + public let 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. + public let selectedDate: Date + + /// Array of ``CalendarIndicatorModel`` you are wanting to show on legend. + public let indicators: [CalendarBase.CalendarIndicatorModel] + + public init(surface: Surface = .light, + hideContainerBorder: Bool = false, + hideCurrentDateIndicator: Bool = false, + selectedDate: Date = Date(), + activeDates: [Date] = [], + inactiveDates: [Date] = [], + minDate: Date = Date().startOfMonth, + maxDate: Date = Date().endOfMonth, + indicators: [CalendarBase.CalendarIndicatorModel] = []) { + self.surface = surface + self.hideContainerBorder = hideContainerBorder + self.hideCurrentDateIndicator = hideCurrentDateIndicator + self.selectedDate = selectedDate + self.activeDates = activeDates + self.inactiveDates = inactiveDates + self.minDate = minDate + self.maxDate = maxDate + self.indicators = indicators } } +} - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } +protocol DatePickerPopoverViewControllerDelegate: NSObject { + func didSelectDate(_ controller: DatePicker.DatePickerPopoverViewController, date: Date) +} - override func loadView() { - view = picker - view.backgroundColor = .white - } - - override func viewDidLoad() { - super.viewDidLoad() - preferredContentSize = CGSize(width: 300, height: 400) // Adjust as needed - } - - @objc private func dateChanged(_ sender: UIDatePicker) { - delegate?.didSelectDate(self, date: sender.date) +//class DatePickerPopoverViewController: UIViewController { +// +// private let picker = UIDatePicker() +// weak var delegate: DatePickerPopoverViewControllerDelegate? +// +// init(calendar: Calendar, delegate: DatePickerPopoverViewControllerDelegate?) { +// self.delegate = delegate +// super.init(nibName: nil, bundle: nil) +// picker.datePickerMode = .date +// picker.preferredDatePickerStyle = .inline +// picker.addTarget(self, action: #selector(dateChanged(_:)), for: .valueChanged) +// } +// +// var selectedDate: Date = Date() { +// didSet { +// picker.date = selectedDate +// } +// } +// +// required init?(coder: NSCoder) { +// fatalError("init(coder:) has not been implemented") +// } +// +// override func viewDidLoad() { +// super.viewDidLoad() +// let v = UIView().with { +// $0.translatesAutoresizingMaskIntoConstraints = false +// $0.backgroundColor = .white +// $0.width(constant: 250) +// $0.height(constant: 350) +// } +// view.addSubview(v) +// v.pinTop(25).pinLeading(15).pinTrailing(15).pinBottom(15) +// +// view.backgroundColor = .blue +// +// preferredContentSize = CGSize(width: 300, height: 400) // Adjust as needed +// } +// +// @objc private func dateChanged(_ sender: UIDatePicker) { +// delegate?.didSelectDate(self, date: sender.date) +// } +//} +extension DatePicker { + class DatePickerPopoverViewController: UIViewController { + private var padding: CGFloat = 15 + private var topPadding: CGFloat { 10 + padding } + private var calendarModel: CalendarModel + private let picker = CalendarBase() + weak var delegate: DatePickerPopoverViewControllerDelegate? + + init(_ calendarModel: CalendarModel, delegate: DatePickerPopoverViewControllerDelegate?) { + self.delegate = delegate + self.calendarModel = calendarModel + super.init(nibName: nil, bundle: nil) + self.picker.onChangeSelectedDate = { [weak self] date in + guard let self else { return } + self.delegate?.didSelectDate(self, date: date) + } + } + + var selectedDate: Date = Date() { + didSet { + picker.selectedDate = selectedDate + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + view.addSubview(picker) + picker.surface = calendarModel.surface + picker.hideContainerBorder = calendarModel.hideContainerBorder + picker.hideCurrentDateIndicator = calendarModel.hideCurrentDateIndicator + picker.activeDates = calendarModel.activeDates + picker.inactiveDates = calendarModel.inactiveDates + picker.selectedDate = calendarModel.selectedDate + picker.indicators = calendarModel.indicators + picker.minDate = calendarModel.minDate + picker.maxDate = calendarModel.maxDate + picker.pinToSuperView(.init(top: topPadding, left: padding, bottom: padding, right: padding)) + view.backgroundColor = picker.backgroundColor + } + + override var preferredContentSize: CGSize { + get { + var size = picker.containerSize + size.height += 40 + size.width += 30 + return size + } + set { + super.preferredContentSize = newValue + } + } } }