492 lines
20 KiB
Swift
492 lines
20 KiB
Swift
//
|
|
// DatePickerViewController.swift
|
|
// VDSSample
|
|
//
|
|
// Created by Matt Bruce on 4/11/24.
|
|
//
|
|
|
|
import Foundation
|
|
import VDS
|
|
import UIKit
|
|
|
|
class DatePickerViewController: BaseViewController<DatePicker> {
|
|
|
|
// Datepicker
|
|
var label = Label()
|
|
var disabledSwitch = Toggle()
|
|
var requiredSwitch = Toggle()
|
|
var labelTextField = TextField()
|
|
var errorTextField = TextField()
|
|
var helperTextField = TextField()
|
|
var inlineLabelSwitch = Toggle()
|
|
var readonlySwitch = Toggle()
|
|
var transparentBgSwitch = Toggle()
|
|
var errorSwitch = Toggle()
|
|
var widthTextField = NumericField()
|
|
var tooltipTitleTextField = TextField()
|
|
var tooltipContentTextField = TextField()
|
|
lazy var dateFormatPickerSelectorView = {
|
|
PickerSelectorView(title: "shortNumeric",
|
|
picker: self.picker,
|
|
items: DatePicker.DateFormat.allCases )
|
|
}()
|
|
|
|
// Calendar
|
|
//props
|
|
var indicators: [CalendarBase.CalendarIndicatorModel] = []
|
|
var activeDates: [Date] = []
|
|
var inactiveDates: [Date] = []
|
|
var minDate: Date = Date().startOfMonth
|
|
var maxDate: Date = Date().endOfMonth
|
|
|
|
//form
|
|
var containerBorderSwitch = Toggle()
|
|
var hideCurrentDateIndicatorSwitch = Toggle()
|
|
var indicatorOneSwitch = Toggle()
|
|
var indicatorTwoSwitch = Toggle()
|
|
var indicatorThreeSwitch = Toggle()
|
|
var clearActiveDatesSwitch = Toggle()
|
|
var clearInactiveDatesSwitch = Toggle()
|
|
var activeDatesField = TextField()
|
|
var inactiveDatesField = TextField()
|
|
var legendOneField = TextField()
|
|
var legendTwoField = TextField()
|
|
var legendThreeField = TextField()
|
|
|
|
private var minDatePicker: UIDatePicker = UIDatePicker()
|
|
private var maxDatePicker: UIDatePicker = UIDatePicker()
|
|
private var indicatorOnePicker: UIDatePicker = UIDatePicker()
|
|
private var indicatorTwoPicker: UIDatePicker = UIDatePicker()
|
|
private var indicatorThreePicker: UIDatePicker = UIDatePicker()
|
|
private var activeDatePicker: UIDatePicker = UIDatePicker().with { $0.datePickerMode = .date }
|
|
private var inactiveDatePicker: UIDatePicker = UIDatePicker().with { $0.datePickerMode = .date }
|
|
|
|
let indicatorOnePickerTag = 1
|
|
let indicatorTwoPickerTag = 2
|
|
let indicatorThreePickerTag = 3
|
|
let minDatePickerTag = 4
|
|
let maxDatePickerTag = 5
|
|
let activeDatePickerTag = 6
|
|
let inactiveDatePickerTag = 7
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
addContentTopView(view: component)
|
|
setupModel()
|
|
setupPicker()
|
|
setupCalendar()
|
|
}
|
|
|
|
override func setupForm(){
|
|
addFormRow(label: "onChange", view: label)
|
|
addFormRow(label: "Surface", view: surfacePickerSelectorView)
|
|
addFormRow(label: "Disabled", view: disabledSwitch, pinTrailing: false)
|
|
addFormRow(label: "Read Only", view: readonlySwitch, pinTrailing: false)
|
|
addFormRow(label: "Required", view: requiredSwitch, pinTrailing: false)
|
|
addFormRow(label: "Transparent Background", view: transparentBgSwitch, pinTrailing: false)
|
|
addFormRow(label: "Date Format", view: dateFormatPickerSelectorView)
|
|
addFormRow(label: "Label Text", view: labelTextField)
|
|
addFormRow(label: "Helper Text", view: helperTextField)
|
|
addFormRow(label: "Error", view: errorSwitch, pinTrailing: false)
|
|
addFormRow(label: "Error Text", view: errorTextField)
|
|
addFormRow(label: "Width", view: widthTextField)
|
|
addFormRow(label: "ToolTip Title", view: tooltipTitleTextField)
|
|
addFormRow(label: "ToolTip Content", view: tooltipContentTextField)
|
|
append(section: getCalendarSection())
|
|
append(section: .init().with({
|
|
func datePicker() -> VDS.DatePicker {
|
|
VDS.DatePicker().with {
|
|
$0.calendarModel = .init(minDate: Date().startOfMonth, maxDate: Calendar.current.date(byAdding: .month, value: 2, to: Date())!)
|
|
}
|
|
}
|
|
$0.title = "UI Testing"
|
|
$0.addFormRow(label: "Below Test", view: datePicker())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Test Space", view: View())
|
|
$0.addFormRow(label: "Above Test", view: datePicker())
|
|
}))
|
|
|
|
disabledSwitch.onChange = { [weak self] sender in
|
|
self?.component.isEnabled = !sender.isOn
|
|
}
|
|
|
|
requiredSwitch.onChange = { [weak self] sender in
|
|
self?.component.isRequired = sender.isOn
|
|
}
|
|
|
|
readonlySwitch.onChange = { [weak self] sender in
|
|
self?.component.isReadOnly = sender.isOn
|
|
}
|
|
|
|
transparentBgSwitch.onChange = { [weak self] sender in
|
|
self?.component.transparentBackground = sender.isOn
|
|
}
|
|
|
|
errorSwitch
|
|
.publisher(for: .valueChanged)
|
|
.sink { [weak self] sender in
|
|
guard let self else { return }
|
|
component.showError = sender.isOn
|
|
if component.showError != sender.isOn {
|
|
self.errorSwitch.isOn = self.component.showError
|
|
}
|
|
}.store(in: &subscribers)
|
|
|
|
labelTextField
|
|
.textPublisher
|
|
.sink { [weak self] text in
|
|
self?.component.labelText = text
|
|
}.store(in: &subscribers)
|
|
|
|
helperTextField
|
|
.textPublisher
|
|
.sink { [weak self] text in
|
|
self?.component.helperText = text
|
|
}.store(in: &subscribers)
|
|
|
|
errorTextField
|
|
.textPublisher
|
|
.sink { [weak self] text in
|
|
self?.component.errorText = text
|
|
}.store(in: &subscribers)
|
|
|
|
widthTextField
|
|
.numberPublisher
|
|
.sink { [weak self] number in
|
|
self?.updateComponentConstraint(pinTrailing: number == nil)
|
|
self?.component.width = number?.cgFloatValue
|
|
}.store(in: &subscribers)
|
|
|
|
tooltipTitleTextField
|
|
.textPublisher
|
|
.sink { [weak self] text in
|
|
self?.updateTooltip()
|
|
}.store(in: &subscribers)
|
|
|
|
tooltipContentTextField
|
|
.textPublisher
|
|
.sink { [weak self] text in
|
|
self?.updateTooltip()
|
|
}.store(in: &subscribers)
|
|
}
|
|
|
|
func setupModel() {
|
|
component.selectedDate = Calendar.current.date(byAdding: .day, value: -5, to: Date())
|
|
component.labelText = "Date"
|
|
component.helperText = "Pick a date for your needs."
|
|
component.errorText = "Enter a date."
|
|
component.tooltipModel = .init(title: "Check your date.", content:"Here is the content for your date component")
|
|
|
|
component.onChange = { [weak self] control in
|
|
self?.label.text = DateFormatter.localizedString(from: control.selectedDate!, dateStyle: .short, timeStyle: .none)
|
|
}
|
|
|
|
//setup UI
|
|
disabledSwitch.isOn = !component.isEnabled
|
|
requiredSwitch.isOn = component.isRequired
|
|
surfacePickerSelectorView.text = component.surface.rawValue
|
|
labelTextField.text = component.labelText
|
|
helperTextField.text = component.helperText
|
|
readonlySwitch.isOn = false
|
|
transparentBgSwitch.isOn = false
|
|
errorSwitch.isOn = component.showError
|
|
errorTextField.text = component.errorText
|
|
tooltipTitleTextField.text = component.tooltipModel?.title
|
|
tooltipContentTextField.text = component.tooltipModel?.content
|
|
}
|
|
|
|
func setupPicker() {
|
|
surfacePickerSelectorView.onPickerDidSelect = { [weak self] item in
|
|
self?.component.surface = item
|
|
self?.contentTopView.backgroundColor = item.color
|
|
}
|
|
|
|
dateFormatPickerSelectorView.onPickerDidSelect = { [weak self] item in
|
|
self?.component.dateFormat = item
|
|
}
|
|
|
|
}
|
|
|
|
func updateTooltip() {
|
|
let title = tooltipTitleTextField.text ?? ""
|
|
let content = tooltipContentTextField.text ?? ""
|
|
|
|
component.tooltipModel = !title.isEmpty || !content.isEmpty ? .init(title: title,
|
|
content: content) : nil
|
|
}
|
|
}
|
|
|
|
extension DatePickerViewController {
|
|
|
|
func getCalendarSection() -> FormSection {
|
|
let section = FormSection()
|
|
section.title = "Calendar Options"
|
|
section.addFormRow(label: "Hide Container Border", view: containerBorderSwitch, pinTrailing: false)
|
|
section.addFormRow(label: "Hide Current Date Indicator", view: hideCurrentDateIndicatorSwitch, pinTrailing: false)
|
|
section.addFormRow(label: "Min Date", view: minDatePicker)
|
|
section.addFormRow(label: "Max Date", view: maxDatePicker)
|
|
section.addFormRow(label: "Active Dates", view: activeDatesField)
|
|
section.addFormRow(label: "Select ActiveDate", view: activeDatePicker)
|
|
section.addFormRow(label: "Clear Active Dates", view: clearActiveDatesSwitch, pinTrailing: false)
|
|
section.addFormRow(label: "Inactive Dates", view: inactiveDatesField)
|
|
section.addFormRow(label: "Select InActiveDate", view: inactiveDatePicker)
|
|
section.addFormRow(label: "Clear Inactive Dates", view: clearInactiveDatesSwitch, pinTrailing: false)
|
|
section.addFormRow(label: "Indicator One", view: indicatorOneSwitch, pinTrailing: false)
|
|
section.addFormRow(label: "Indicator Two", view: indicatorTwoSwitch, pinTrailing: false)
|
|
section.addFormRow(label: "Indicator Three", view: indicatorThreeSwitch, pinTrailing: false)
|
|
section.addFormRow(label: "Legend One", view: legendOneField)
|
|
section.addFormRow(label: "Legend Two", view: legendTwoField)
|
|
section.addFormRow(label: "Legend Three", view: legendThreeField)
|
|
section.addFormRow(label: "Indicator One Date", view: indicatorOnePicker)
|
|
section.addFormRow(label: "Indicator Two Date", view: indicatorTwoPicker)
|
|
section.addFormRow(label: "Indicator Three Date", view: indicatorThreePicker)
|
|
|
|
containerBorderSwitch.onChange = { [weak self] sender in
|
|
self?.updateCalendarModel()
|
|
}
|
|
|
|
hideCurrentDateIndicatorSwitch.onChange = { [weak self] sender in
|
|
self?.updateCalendarModel()
|
|
}
|
|
|
|
clearActiveDatesSwitch.onChange = { [weak self] sender in
|
|
self?.updateCalendarModel()
|
|
}
|
|
|
|
clearInactiveDatesSwitch.onChange = { [weak self] sender in
|
|
self?.updateCalendarModel()
|
|
}
|
|
|
|
legendOneField
|
|
.textPublisher
|
|
.sink {
|
|
[weak self] text in
|
|
self?.updateIndicatorData(label: text, date: self?.indicatorOnePicker.date, index: 0)
|
|
}.store(in: &subscribers)
|
|
|
|
legendTwoField
|
|
.textPublisher
|
|
.sink {
|
|
[weak self] text in
|
|
self?.updateIndicatorData(label: text, date: self?.indicatorTwoPicker.date, index: 1)
|
|
}.store(in: &subscribers)
|
|
|
|
legendThreeField
|
|
.textPublisher
|
|
.sink {
|
|
[weak self] text in
|
|
self?.updateIndicatorData(label: text, date: self?.indicatorThreePicker.date, index: 2)
|
|
}.store(in: &subscribers)
|
|
|
|
indicatorOneSwitch.onChange = { [weak self] sender in
|
|
guard let self else { return }
|
|
if !sender.isOn {
|
|
self.indicators.removeAll()
|
|
} else {
|
|
self.setIndicatorsData()
|
|
}
|
|
self.updateCalendarModel()
|
|
}
|
|
|
|
indicatorTwoSwitch.onChange = { [weak self] sender in
|
|
guard let self else { return }
|
|
if !sender.isOn {
|
|
if self.indicators.count > 2 {
|
|
self.indicators.removeLast()
|
|
self.indicators.removeLast()
|
|
} else if self.indicators.count == 2 {
|
|
self.indicators.removeLast()
|
|
}
|
|
} else {
|
|
self.setIndicatorsData()
|
|
}
|
|
self.updateCalendarModel()
|
|
}
|
|
|
|
indicatorThreeSwitch.onChange = { [weak self] sender in
|
|
guard let self else { return }
|
|
if !sender.isOn {
|
|
if self.indicators.count > 2 {
|
|
self.indicators.removeLast()
|
|
}
|
|
} else {
|
|
self.setIndicatorsData()
|
|
}
|
|
self.updateCalendarModel()
|
|
}
|
|
|
|
return section
|
|
}
|
|
|
|
func updateCalendarModel() {
|
|
component.calendarModel = .init(hideContainerBorder: containerBorderSwitch.isOn,
|
|
hideCurrentDateIndicator: hideCurrentDateIndicatorSwitch.isOn,
|
|
activeDates: activeDates,
|
|
inactiveDates: inactiveDates,
|
|
minDate: minDate,
|
|
maxDate: maxDate,
|
|
indicators: indicators)
|
|
}
|
|
|
|
func setupCalendar() {
|
|
configurePicker(indicatorOnePicker)
|
|
indicatorOnePicker.tag = indicatorOnePickerTag
|
|
configurePicker(indicatorTwoPicker)
|
|
indicatorTwoPicker.tag = indicatorTwoPickerTag
|
|
configurePicker(indicatorThreePicker)
|
|
indicatorThreePicker.tag = indicatorThreePickerTag
|
|
configurePicker(minDatePicker)
|
|
minDatePicker.tag = minDatePickerTag
|
|
configurePicker(maxDatePicker)
|
|
maxDatePicker.tag = maxDatePickerTag
|
|
configurePicker(activeDatePicker)
|
|
activeDatePicker.tag = activeDatePickerTag
|
|
configurePicker(inactiveDatePicker)
|
|
inactiveDatePicker.tag = inactiveDatePickerTag
|
|
indicators = [
|
|
.init(label: "Due Date", date: indicatorOnePicker.date),
|
|
.init(label: "Auto Pay", date: indicatorTwoPicker.date),
|
|
.init(label: "Scheduled", date: indicatorThreePicker.date)
|
|
]
|
|
|
|
let calendar = Calendar.current
|
|
let indicatorDate = calendar.startOfDay(for: calendar.date(byAdding: .day, value: 1, to: Date())!)
|
|
legendOneField.text = "Due Date"
|
|
legendTwoField.text = "Auto Pay"
|
|
legendThreeField.text = "Scheduled"
|
|
indicatorOneSwitch.isOn = true
|
|
indicatorTwoSwitch.isOn = true
|
|
indicatorThreeSwitch.isOn = true
|
|
hideCurrentDateIndicatorSwitch.isOn = false
|
|
indicatorOnePicker.date = indicatorDate
|
|
indicatorTwoPicker.date = indicatorDate
|
|
indicatorThreePicker.date = indicatorDate
|
|
updateIndicatorData(label: legendOneField.text ?? "", date: indicatorOnePicker.date, index: 0)
|
|
updateIndicatorData(label: legendTwoField.text ?? "", date: indicatorTwoPicker.date, index: 1)
|
|
updateIndicatorData(label: legendThreeField.text ?? "", date: indicatorThreePicker.date, index: 2)
|
|
|
|
activeDatesField.isUserInteractionEnabled = false
|
|
inactiveDatesField.isUserInteractionEnabled = false
|
|
activeDatesField.isEnabled = false
|
|
inactiveDatesField.isEnabled = false
|
|
|
|
minDatePicker.date = minDate
|
|
maxDatePicker.date = maxDate
|
|
|
|
updateCalendarModel()
|
|
}
|
|
|
|
func updateIndicatorData(label: String = "", date: Date?, index:Int) {
|
|
indicators[index].label = label
|
|
indicators[index].date = date ?? Date()
|
|
updateCalendarModel()
|
|
}
|
|
|
|
func setIndicatorsData() {
|
|
if indicatorOneSwitch.isOn && indicatorTwoSwitch.isOn && indicatorThreeSwitch.isOn {
|
|
indicators = [
|
|
.init(label: self.legendOneField.text ?? "", date: indicatorOnePicker.date),
|
|
.init(label: self.legendTwoField.text ?? "", date: indicatorTwoPicker.date),
|
|
.init(label: self.legendThreeField.text ?? "", date: indicatorThreePicker.date)
|
|
]
|
|
} else if indicatorOneSwitch.isOn && indicatorTwoSwitch.isOn && !indicatorThreeSwitch.isOn {
|
|
indicators = [
|
|
.init(label: self.legendOneField.text ?? "", date: indicatorOnePicker.date),
|
|
.init(label: self.legendTwoField.text ?? "", date: indicatorTwoPicker.date),
|
|
]
|
|
|
|
} else if indicatorOneSwitch.isOn && !indicatorTwoSwitch.isOn {
|
|
indicators = [
|
|
.init(label: self.legendOneField.text ?? "", date: indicatorOnePicker.date),
|
|
]
|
|
}
|
|
}
|
|
|
|
func configurePicker(_ sender:UIDatePicker) {
|
|
// Set some of UIDatePicker properties
|
|
sender.timeZone = NSTimeZone.local
|
|
sender.backgroundColor = UIColor.white
|
|
|
|
// Add an event to call onDidChangeDate function when value is changed.
|
|
sender.addTarget(self, action: #selector(self.datePickerValueChanged(_:)), for: .valueChanged)
|
|
}
|
|
|
|
@objc func datePickerValueChanged(_ sender: UIDatePicker){
|
|
|
|
switch sender.tag {
|
|
case indicatorOnePickerTag:
|
|
updateIndicatorData(label: legendOneField.text ?? "", date: sender.date, index: 0)
|
|
case indicatorTwoPickerTag:
|
|
updateIndicatorData(label: legendTwoField.text ?? "", date: sender.date, index: 1)
|
|
case indicatorThreePickerTag:
|
|
updateIndicatorData(label: legendThreeField.text ?? "", date: sender.date, index: 2)
|
|
case minDatePickerTag:
|
|
minDate = sender.date
|
|
updateCalendarModel()
|
|
case maxDatePickerTag:
|
|
maxDate = sender.date
|
|
updateCalendarModel()
|
|
case activeDatePickerTag:
|
|
activeDates.append(sender.date)
|
|
var text = activeDatesField.text
|
|
if let textEmpty = text?.isEmpty, textEmpty == true {
|
|
text?.append("")
|
|
} else {
|
|
text?.append(", ")
|
|
}
|
|
text?.append("\(self.getSelectedDate(with: sender.date))")
|
|
clearActiveDatesSwitch.isOn = false
|
|
activeDatesField.text = text
|
|
updateCalendarModel()
|
|
case inactiveDatePickerTag:
|
|
inactiveDates.append(sender.date)
|
|
var text = inactiveDatesField.text
|
|
if let textEmpty = text?.isEmpty, textEmpty == true {
|
|
text?.append("")
|
|
} else {
|
|
text?.append(", ")
|
|
}
|
|
text?.append("\(self.getSelectedDate(with: sender.date))")
|
|
clearInactiveDatesSwitch.isOn = false
|
|
inactiveDatesField.text = text
|
|
updateCalendarModel()
|
|
default: break
|
|
}
|
|
}
|
|
|
|
func getSelectedDate(with date:Date) -> String {
|
|
let dateFormatter: DateFormatter = DateFormatter()
|
|
dateFormatter.dateFormat = "MM/dd/yyyy"
|
|
let day: String = dateFormatter.string(from: date)
|
|
return day
|
|
}
|
|
|
|
}
|
|
|
|
extension DatePickerViewController: ComponentSampleable {
|
|
static func makeSample() -> ComponentSample {
|
|
let c = Self()
|
|
c.setupModel()
|
|
c.setupCalendar()
|
|
return ComponentSample(component: c.component)
|
|
}
|
|
}
|