546 lines
15 KiB
JavaScript
546 lines
15 KiB
JavaScript
import dayjs from 'dayjs/esm'
|
|
import advancedFormat from 'dayjs/plugin/advancedFormat'
|
|
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
|
import localeData from 'dayjs/plugin/localeData'
|
|
import timezone from 'dayjs/plugin/timezone'
|
|
import utc from 'dayjs/plugin/utc'
|
|
|
|
dayjs.extend(advancedFormat)
|
|
dayjs.extend(customParseFormat)
|
|
dayjs.extend(localeData)
|
|
dayjs.extend(timezone)
|
|
dayjs.extend(utc)
|
|
|
|
window.dayjs = dayjs
|
|
|
|
export default function dateTimePickerFormComponent({
|
|
defaultFocusedDate,
|
|
displayFormat,
|
|
firstDayOfWeek,
|
|
isAutofocused,
|
|
locale,
|
|
shouldCloseOnDateSelection,
|
|
state,
|
|
}) {
|
|
const timezone = dayjs.tz.guess()
|
|
|
|
return {
|
|
daysInFocusedMonth: [],
|
|
|
|
displayText: '',
|
|
|
|
emptyDaysInFocusedMonth: [],
|
|
|
|
focusedDate: null,
|
|
|
|
focusedMonth: null,
|
|
|
|
focusedYear: null,
|
|
|
|
hour: null,
|
|
|
|
isClearingState: false,
|
|
|
|
minute: null,
|
|
|
|
second: null,
|
|
|
|
state,
|
|
|
|
defaultFocusedDate,
|
|
|
|
dayLabels: [],
|
|
|
|
months: [],
|
|
|
|
init: function () {
|
|
dayjs.locale(locales[locale] ?? locales['en'])
|
|
|
|
this.$nextTick(() => {
|
|
this.focusedDate ??= (
|
|
this.getDefaultFocusedDate() ?? dayjs()
|
|
).tz(timezone)
|
|
this.focusedMonth ??= this.focusedDate.month()
|
|
this.focusedYear ??= this.focusedDate.year()
|
|
})
|
|
|
|
let date =
|
|
this.getSelectedDate() ??
|
|
this.getDefaultFocusedDate() ??
|
|
dayjs().tz(timezone).hour(0).minute(0).second(0)
|
|
|
|
if (this.getMaxDate() !== null && date.isAfter(this.getMaxDate())) {
|
|
date = null
|
|
} else if (
|
|
this.getMinDate() !== null &&
|
|
date.isBefore(this.getMinDate())
|
|
) {
|
|
date = null
|
|
}
|
|
|
|
this.hour = date?.hour() ?? 0
|
|
this.minute = date?.minute() ?? 0
|
|
this.second = date?.second() ?? 0
|
|
|
|
this.setDisplayText()
|
|
this.setMonths()
|
|
this.setDayLabels()
|
|
|
|
if (isAutofocused) {
|
|
this.$nextTick(() =>
|
|
this.togglePanelVisibility(this.$refs.button),
|
|
)
|
|
}
|
|
|
|
this.$watch('focusedMonth', () => {
|
|
this.focusedMonth = +this.focusedMonth
|
|
|
|
if (this.focusedDate.month() === this.focusedMonth) {
|
|
return
|
|
}
|
|
|
|
this.focusedDate = this.focusedDate.month(this.focusedMonth)
|
|
})
|
|
|
|
this.$watch('focusedYear', () => {
|
|
if (this.focusedYear?.length > 4) {
|
|
this.focusedYear = this.focusedYear.substring(0, 4)
|
|
}
|
|
|
|
if (!this.focusedYear || this.focusedYear?.length !== 4) {
|
|
return
|
|
}
|
|
|
|
let year = +this.focusedYear
|
|
|
|
if (!Number.isInteger(year)) {
|
|
year = dayjs().tz(timezone).year()
|
|
|
|
this.focusedYear = year
|
|
}
|
|
|
|
if (this.focusedDate.year() === year) {
|
|
return
|
|
}
|
|
|
|
this.focusedDate = this.focusedDate.year(year)
|
|
})
|
|
|
|
this.$watch('focusedDate', () => {
|
|
let month = this.focusedDate.month()
|
|
let year = this.focusedDate.year()
|
|
|
|
if (this.focusedMonth !== month) {
|
|
this.focusedMonth = month
|
|
}
|
|
|
|
if (this.focusedYear !== year) {
|
|
this.focusedYear = year
|
|
}
|
|
|
|
this.setupDaysGrid()
|
|
})
|
|
|
|
this.$watch('hour', () => {
|
|
let hour = +this.hour
|
|
|
|
if (!Number.isInteger(hour)) {
|
|
this.hour = 0
|
|
} else if (hour > 23) {
|
|
this.hour = 0
|
|
} else if (hour < 0) {
|
|
this.hour = 23
|
|
} else {
|
|
this.hour = hour
|
|
}
|
|
|
|
if (this.isClearingState) {
|
|
return
|
|
}
|
|
|
|
let date = this.getSelectedDate() ?? this.focusedDate
|
|
|
|
this.setState(date.hour(this.hour ?? 0))
|
|
})
|
|
|
|
this.$watch('minute', () => {
|
|
let minute = +this.minute
|
|
|
|
if (!Number.isInteger(minute)) {
|
|
this.minute = 0
|
|
} else if (minute > 59) {
|
|
this.minute = 0
|
|
} else if (minute < 0) {
|
|
this.minute = 59
|
|
} else {
|
|
this.minute = minute
|
|
}
|
|
|
|
if (this.isClearingState) {
|
|
return
|
|
}
|
|
|
|
let date = this.getSelectedDate() ?? this.focusedDate
|
|
|
|
this.setState(date.minute(this.minute ?? 0))
|
|
})
|
|
|
|
this.$watch('second', () => {
|
|
let second = +this.second
|
|
|
|
if (!Number.isInteger(second)) {
|
|
this.second = 0
|
|
} else if (second > 59) {
|
|
this.second = 0
|
|
} else if (second < 0) {
|
|
this.second = 59
|
|
} else {
|
|
this.second = second
|
|
}
|
|
|
|
if (this.isClearingState) {
|
|
return
|
|
}
|
|
|
|
let date = this.getSelectedDate() ?? this.focusedDate
|
|
|
|
this.setState(date.second(this.second ?? 0))
|
|
})
|
|
|
|
this.$watch('state', () => {
|
|
if (this.state === undefined) {
|
|
return
|
|
}
|
|
|
|
let date = this.getSelectedDate()
|
|
|
|
if (date === null) {
|
|
this.clearState()
|
|
|
|
return
|
|
}
|
|
|
|
if (
|
|
this.getMaxDate() !== null &&
|
|
date?.isAfter(this.getMaxDate())
|
|
) {
|
|
date = null
|
|
}
|
|
if (
|
|
this.getMinDate() !== null &&
|
|
date?.isBefore(this.getMinDate())
|
|
) {
|
|
date = null
|
|
}
|
|
|
|
const newHour = date?.hour() ?? 0
|
|
if (this.hour !== newHour) {
|
|
this.hour = newHour
|
|
}
|
|
|
|
const newMinute = date?.minute() ?? 0
|
|
if (this.minute !== newMinute) {
|
|
this.minute = newMinute
|
|
}
|
|
|
|
const newSecond = date?.second() ?? 0
|
|
if (this.second !== newSecond) {
|
|
this.second = newSecond
|
|
}
|
|
|
|
this.setDisplayText()
|
|
})
|
|
},
|
|
|
|
clearState: function () {
|
|
this.isClearingState = true
|
|
|
|
this.setState(null)
|
|
|
|
this.hour = 0
|
|
this.minute = 0
|
|
this.second = 0
|
|
|
|
this.$nextTick(() => (this.isClearingState = false))
|
|
},
|
|
|
|
dateIsDisabled: function (date) {
|
|
if (
|
|
this.$refs?.disabledDates &&
|
|
JSON.parse(this.$refs.disabledDates.value ?? []).some(
|
|
(disabledDate) => {
|
|
disabledDate = dayjs(disabledDate)
|
|
|
|
if (!disabledDate.isValid()) {
|
|
return false
|
|
}
|
|
|
|
return disabledDate.isSame(date, 'day')
|
|
},
|
|
)
|
|
) {
|
|
return true
|
|
}
|
|
|
|
if (this.getMaxDate() && date.isAfter(this.getMaxDate(), 'day')) {
|
|
return true
|
|
}
|
|
if (this.getMinDate() && date.isBefore(this.getMinDate(), 'day')) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
},
|
|
|
|
dayIsDisabled: function (day) {
|
|
this.focusedDate ??= dayjs().tz(timezone)
|
|
|
|
return this.dateIsDisabled(this.focusedDate.date(day))
|
|
},
|
|
|
|
dayIsSelected: function (day) {
|
|
let selectedDate = this.getSelectedDate()
|
|
|
|
if (selectedDate === null) {
|
|
return false
|
|
}
|
|
|
|
this.focusedDate ??= dayjs().tz(timezone)
|
|
|
|
return (
|
|
selectedDate.date() === day &&
|
|
selectedDate.month() === this.focusedDate.month() &&
|
|
selectedDate.year() === this.focusedDate.year()
|
|
)
|
|
},
|
|
|
|
dayIsToday: function (day) {
|
|
let date = dayjs().tz(timezone)
|
|
this.focusedDate ??= date
|
|
|
|
return (
|
|
date.date() === day &&
|
|
date.month() === this.focusedDate.month() &&
|
|
date.year() === this.focusedDate.year()
|
|
)
|
|
},
|
|
|
|
focusPreviousDay: function () {
|
|
this.focusedDate ??= dayjs().tz(timezone)
|
|
|
|
this.focusedDate = this.focusedDate.subtract(1, 'day')
|
|
},
|
|
|
|
focusPreviousWeek: function () {
|
|
this.focusedDate ??= dayjs().tz(timezone)
|
|
|
|
this.focusedDate = this.focusedDate.subtract(1, 'week')
|
|
},
|
|
|
|
focusNextDay: function () {
|
|
this.focusedDate ??= dayjs().tz(timezone)
|
|
|
|
this.focusedDate = this.focusedDate.add(1, 'day')
|
|
},
|
|
|
|
focusNextWeek: function () {
|
|
this.focusedDate ??= dayjs().tz(timezone)
|
|
|
|
this.focusedDate = this.focusedDate.add(1, 'week')
|
|
},
|
|
|
|
getDayLabels: function () {
|
|
const labels = dayjs.weekdaysShort()
|
|
|
|
if (firstDayOfWeek === 0) {
|
|
return labels
|
|
}
|
|
|
|
return [
|
|
...labels.slice(firstDayOfWeek),
|
|
...labels.slice(0, firstDayOfWeek),
|
|
]
|
|
},
|
|
|
|
getMaxDate: function () {
|
|
let date = dayjs(this.$refs.maxDate?.value)
|
|
|
|
return date.isValid() ? date : null
|
|
},
|
|
|
|
getMinDate: function () {
|
|
let date = dayjs(this.$refs.minDate?.value)
|
|
|
|
return date.isValid() ? date : null
|
|
},
|
|
|
|
getSelectedDate: function () {
|
|
if (this.state === undefined) {
|
|
return null
|
|
}
|
|
|
|
if (this.state === null) {
|
|
return null
|
|
}
|
|
|
|
let date = dayjs(this.state)
|
|
|
|
if (!date.isValid()) {
|
|
return null
|
|
}
|
|
|
|
return date
|
|
},
|
|
|
|
getDefaultFocusedDate: function () {
|
|
if (this.defaultFocusedDate === null) {
|
|
return null
|
|
}
|
|
|
|
let defaultFocusedDate = dayjs(this.defaultFocusedDate)
|
|
|
|
if (!defaultFocusedDate.isValid()) {
|
|
return null
|
|
}
|
|
|
|
return defaultFocusedDate
|
|
},
|
|
|
|
togglePanelVisibility: function () {
|
|
if (!this.isOpen()) {
|
|
this.focusedDate =
|
|
this.getSelectedDate() ??
|
|
this.focusedDate ??
|
|
this.getMinDate() ??
|
|
dayjs().tz(timezone)
|
|
|
|
this.setupDaysGrid()
|
|
}
|
|
|
|
this.$refs.panel.toggle(this.$refs.button)
|
|
},
|
|
|
|
selectDate: function (day = null) {
|
|
if (day) {
|
|
this.setFocusedDay(day)
|
|
}
|
|
|
|
this.focusedDate ??= dayjs().tz(timezone)
|
|
|
|
this.setState(this.focusedDate)
|
|
|
|
if (shouldCloseOnDateSelection) {
|
|
this.togglePanelVisibility()
|
|
}
|
|
},
|
|
|
|
setDisplayText: function () {
|
|
this.displayText = this.getSelectedDate()
|
|
? this.getSelectedDate().format(displayFormat)
|
|
: ''
|
|
},
|
|
|
|
setMonths: function () {
|
|
this.months = dayjs.months()
|
|
},
|
|
|
|
setDayLabels: function () {
|
|
this.dayLabels = this.getDayLabels()
|
|
},
|
|
|
|
setupDaysGrid: function () {
|
|
this.focusedDate ??= dayjs().tz(timezone)
|
|
|
|
this.emptyDaysInFocusedMonth = Array.from(
|
|
{
|
|
length: this.focusedDate.date(8 - firstDayOfWeek).day(),
|
|
},
|
|
(_, i) => i + 1,
|
|
)
|
|
|
|
this.daysInFocusedMonth = Array.from(
|
|
{
|
|
length: this.focusedDate.daysInMonth(),
|
|
},
|
|
(_, i) => i + 1,
|
|
)
|
|
},
|
|
|
|
setFocusedDay: function (day) {
|
|
this.focusedDate = (this.focusedDate ?? dayjs().tz(timezone)).date(
|
|
day,
|
|
)
|
|
},
|
|
|
|
setState: function (date) {
|
|
if (date === null) {
|
|
this.state = null
|
|
this.setDisplayText()
|
|
|
|
return
|
|
}
|
|
|
|
if (this.dateIsDisabled(date)) {
|
|
return
|
|
}
|
|
|
|
this.state = date
|
|
.hour(this.hour ?? 0)
|
|
.minute(this.minute ?? 0)
|
|
.second(this.second ?? 0)
|
|
.format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
this.setDisplayText()
|
|
},
|
|
|
|
isOpen: function () {
|
|
return this.$refs.panel?.style.display === 'block'
|
|
},
|
|
}
|
|
}
|
|
|
|
const locales = {
|
|
ar: require('dayjs/locale/ar'),
|
|
bs: require('dayjs/locale/bs'),
|
|
ca: require('dayjs/locale/ca'),
|
|
ckb: require('dayjs/locale/ku'),
|
|
cs: require('dayjs/locale/cs'),
|
|
cy: require('dayjs/locale/cy'),
|
|
da: require('dayjs/locale/da'),
|
|
de: require('dayjs/locale/de'),
|
|
el: require('dayjs/locale/el'),
|
|
en: require('dayjs/locale/en'),
|
|
es: require('dayjs/locale/es'),
|
|
et: require('dayjs/locale/et'),
|
|
fa: require('dayjs/locale/fa'),
|
|
fi: require('dayjs/locale/fi'),
|
|
fr: require('dayjs/locale/fr'),
|
|
hi: require('dayjs/locale/hi'),
|
|
hu: require('dayjs/locale/hu'),
|
|
hy: require('dayjs/locale/hy-am'),
|
|
id: require('dayjs/locale/id'),
|
|
it: require('dayjs/locale/it'),
|
|
ja: require('dayjs/locale/ja'),
|
|
ka: require('dayjs/locale/ka'),
|
|
km: require('dayjs/locale/km'),
|
|
ku: require('dayjs/locale/ku'),
|
|
lt: require('dayjs/locale/lt'),
|
|
lv: require('dayjs/locale/lv'),
|
|
ms: require('dayjs/locale/ms'),
|
|
my: require('dayjs/locale/my'),
|
|
nl: require('dayjs/locale/nl'),
|
|
no: require('dayjs/locale/nb'),
|
|
pl: require('dayjs/locale/pl'),
|
|
pt_BR: require('dayjs/locale/pt-br'),
|
|
pt_PT: require('dayjs/locale/pt'),
|
|
ro: require('dayjs/locale/ro'),
|
|
ru: require('dayjs/locale/ru'),
|
|
sv: require('dayjs/locale/sv'),
|
|
th: require('dayjs/locale/th'),
|
|
tr: require('dayjs/locale/tr'),
|
|
uk: require('dayjs/locale/uk'),
|
|
vi: require('dayjs/locale/vi'),
|
|
zh_CN: require('dayjs/locale/zh-cn'),
|
|
zh_TW: require('dayjs/locale/zh-tw'),
|
|
}
|