Skip to content
This repository has been archived by the owner on Jan 7, 2022. It is now read-only.

Datetime check function prop #247

Open
wants to merge 9 commits into
base: v1.x
Choose a base branch
from
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ flow | `Array` | Depends of *type* | Customize steps flow, steps available: time
title | `String` | `''` | Popup title.
hide-backdrop | `Boolean` | `false` | Show/Hide backdrop.
backdrop-click | `Boolean` | `true` | Enable/Disable backdrop click to cancel (outside click).

datetime-disabled-checker | `Function` | `(year, month, day, hour, minute, second) => false` | For each possible option selection the appropriate date parts are provided to allow for a custom validation function for allowed selections
Input inherits all props not defined above but `style` and `class` will be inherited by root element. [See inheritAttrs option](https://vuejs.org/v2/api/#inheritAttrs)

The component is based on [Luxon](https://github.com/moment/luxon), check out [documentation](https://moment.github.io/luxon/docs/index.html) to set [time zones](https://moment.github.io/luxon/docs/manual/zones.html) and [format](https://moment.github.io/luxon/docs/manual/formatting.html).
Expand Down
2 changes: 2 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ <h2>Complete demo</h2>
:minute-step="15"
:min-datetime="minDatetime"
:max-datetime="maxDatetime"
:datetime-disabled-checker="datetimeDisabledChecker"
:week-start="7"
use12-hour
auto
Expand All @@ -139,6 +140,7 @@ <h2>Complete demo</h2>
:minute-step=&#x22;15&#x22;
:min-datetime=&#x22;minDatetime&#x22;
:max-datetime=&#x22;maxDatetime&#x22;
:datetime-disabled-checker=&#x22;datetimeDisabledChecker&#x22;
:week-start=&#x22;7&#x22;
use12-hour
auto
Expand Down
72 changes: 69 additions & 3 deletions demo/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,76 @@ new Vue({
datetime12: '2018-05-12T17:19:06.151Z',
datetime13: '2018-05-12T17:19:06.151Z',
datetimeEmpty: '',
minDatetime: LuxonDateTime.local().minus({ month: 1, days: 3 }).toISO(),
maxDatetime: LuxonDateTime.local().plus({ days: 3 }).toISO(),
minDatetime: LuxonDateTime.local().minus({
month: 1,
days: 3
}).toISO(),
maxDatetime: LuxonDateTime.local().plus({
days: 3
}).toISO(),
datetimeDisabledChecker: (year, month, day, hour, minute, second) => {
const invalidDatetimes = [
[LuxonDateTime.local().minus({
days: 8
}), LuxonDateTime.local().minus({
days: 6
})],
[LuxonDateTime.local().minus({
days: 3
}), LuxonDateTime.local().minus({
days: 2,
hours: 12
})]
]
const dateToCheck = LuxonDateTime.fromObject({
year,
month,
day,
hour,
minute,
second,
zone: 'UTC'
})

const res = invalidDatetimes.reduce((acc, val) => {
// if still false check otherwise its already invalid
if (!acc) {
const startDateObject = {
zone: 'UTC'
}
const endDateObject = {
zone: 'UTC'
}
let startDate = val[0]
let endDate = val[1]

const dateOptions = ['year', 'month']
if (day != null) {
dateOptions.push('day')
}
if (minute != null) {
dateOptions.push('minute')
}
if (hour != null) {
dateOptions.push('hour')
}

dateOptions.map(option => {
startDateObject[option] = startDate.c[option]
})
dateOptions.map(option => {
endDateObject[option] = endDate.c[option]
})
startDate = LuxonDateTime.fromObject(startDateObject)
endDate = LuxonDateTime.fromObject(endDateObject)

acc = (dateToCheck > startDate) && (dateToCheck < endDate)
}
return acc
}, false)
return res
},
datetimeTheming: LuxonDateTime.local().toISO()
}
}
})

5 changes: 5 additions & 0 deletions src/Datetime.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
:phrases="phrases"
:use12-hour="use12Hour"
:hour-step="hourStep"
:datetime-disabled-checker="datetimeDisabledChecker"
:minute-step="minuteStep"
:min-datetime="popupMinDatetime"
:max-datetime="popupMaxDatetime"
Expand Down Expand Up @@ -119,6 +120,10 @@ export default {
type: String,
default: null
},
datetimeDisabledChecker: {
type: Function,
default: (year, month, day, hour, minute, second) => false
},
auto: {
type: Boolean,
default: false
Expand Down
6 changes: 5 additions & 1 deletion src/DatetimeCalendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export default {
disabled: {
type: Array
},
datetimeDisabledChecker: {
type: Function,
default: (year, month, day, hour, minute, second) => false
},
minDate: {
type: DateTime,
default: null
Expand Down Expand Up @@ -79,7 +83,7 @@ export default {
return monthDays(this.newYear, this.newMonth, this.weekStart).map(day => ({
number: day,
selected: day && this.year === this.newYear && this.month === this.newMonth && this.day === day,
disabled: !day || monthDayIsDisabled(this.minDate, this.maxDate, this.newYear, this.newMonth, day)
disabled: !day || monthDayIsDisabled(this.minDate, this.maxDate, this.newYear, this.newMonth, day) || this.datetimeDisabledChecker(this.newYear, this.newMonth, day)
}))
}
},
Expand Down
6 changes: 5 additions & 1 deletion src/DatetimeMonthPicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export default {
maxDate: {
type: DateTime,
default: null
},
datetimeDisabledChecker: {
type: Function,
default: (year, month, day, hour, minute, second) => false
}
},

Expand All @@ -37,7 +41,7 @@ export default {
number: ++index,
label: month,
selected: index === this.month,
disabled: !index || monthIsDisabled(this.minDate, this.maxDate, this.year, index)
disabled: !index || monthIsDisabled(this.minDate, this.maxDate, this.year, index) || this.datetimeDisabledChecker(this.year, index)
}))
}
},
Expand Down
9 changes: 9 additions & 0 deletions src/DatetimePopup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
@change="onChangeYear"
:min-date="minDatetime"
:max-date="maxDatetime"
:datetime-disabled-checker="datetimeDisabledChecker"
:year="year"></datetime-year-picker>
<datetime-month-picker
v-if="step === 'month'"
@change="onChangeMonth"
:min-date="minDatetime"
:max-date="maxDatetime"
:datetime-disabled-checker="datetimeDisabledChecker"
:year="year"
:month="month"></datetime-month-picker>
<datetime-calendar
Expand All @@ -27,6 +29,7 @@
:day="day"
:min-date="minDatetime"
:max-date="maxDatetime"
:datetime-disabled-checker="datetimeDisabledChecker"
:week-start="weekStart"
></datetime-calendar>
<datetime-time-picker
Expand All @@ -37,6 +40,8 @@
:use12-hour="use12Hour"
:hour-step="hourStep"
:minute-step="minuteStep"
:current-date-time="newDatetime"
:datetime-disabled-checker="datetimeDisabledChecker"
:min-time="minTime"
:max-time="maxTime"></datetime-time-picker>
</div>
Expand Down Expand Up @@ -85,6 +90,10 @@ export default {
}
}
},
datetimeDisabledChecker: {
type: Function,
default: (year, month, day, hour, minute, second) => false
},
type: {
type: String,
default: 'date'
Expand Down
85 changes: 66 additions & 19 deletions src/DatetimeTimePicker.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
<template>
<div :class="{'vdatetime-time-picker': true, 'vdatetime-time-picker__with-suffix': use12Hour}">
<div class="vdatetime-time-picker__list vdatetime-time-picker__list--hours" ref="hourList">
<div class="vdatetime-time-picker__item" v-for="hour in hours" @click="selectHour(hour)" :class="{'vdatetime-time-picker__item--selected': hour.selected, 'vdatetime-time-picker__item--disabled': hour.disabled}">{{ formatHour(hour.number) }}</div>
<div class="vdatetime-time-picker__item" v-for="hour in displayedHours" @click="selectHour(hour)" :class="{'vdatetime-time-picker__item--selected': hour.selected, 'vdatetime-time-picker__item--disabled': hour.disabled}">{{ formatHour(hour.number) }}</div>
</div>
<div class="vdatetime-time-picker__list vdatetime-time-picker__list--minutes" ref="minuteList">
<div class="vdatetime-time-picker__item" v-for="minute in minutes" @click="selectMinute(minute)" :class="{'vdatetime-time-picker__item--selected': minute.selected, 'vdatetime-time-picker__item--disabled': minute.disabled}">{{ minute.number }}</div>
</div>
<div class="vdatetime-time-picker__list vdatetime-time-picker__list--suffix" ref="suffixList" v-if="use12Hour">
<div class="vdatetime-time-picker__item" @click="selectSuffix('am')" :class="{'vdatetime-time-picker__item--selected': hour < 12}">am</div>
<div class="vdatetime-time-picker__item" @click="selectSuffix('pm')" :class="{'vdatetime-time-picker__item--selected': hour >= 12}">pm</div>
<div
class="vdatetime-time-picker__list vdatetime-time-picker__list--suffix"
ref="suffixList"
v-if="use12Hour"
>
<div
class="vdatetime-time-picker__item"
v-for="timeSelection in timeSelections"
:key="`selection-${timeSelection.id}`"
@click="selectSuffix(timeSelection)"
:class="{'vdatetime-time-picker__item--selected': timeSelection.comparison(hour) , 'vdatetime-time-picker__item--disabled': timeSelection.disabled }"
>{{ timeSelection.id }}</div>
</div>
</div>
</template>

<script>
import { hours, minutes, pad, timeComponentIsDisabled } from './util'
import { hours, minutes, pad, timeComponentIsDisabled, selectionIsDisabled } from './util'
import { DateTime } from 'luxon'

export default {
props: {
Expand All @@ -34,6 +44,14 @@ export default {
type: Number,
default: 1
},
datetimeDisabledChecker: {
type: Function,
default: (year, month, day, hour, minute, second) => false
},
currentDateTime: {
type: DateTime,
default: () => DateTime.utc()
},
minuteStep: {
type: Number,
default: 1
Expand All @@ -49,28 +67,54 @@ export default {
},

computed: {
timeSelections () {
return this.use12Hour
? [
{
id: 'am',
comparison: (hour) => hour < 12,
disabled: selectionIsDisabled(this.hours, this.use12Hour, 'am')
},
{
id: 'pm',
comparison: (hour) => hour >= 12,
disabled: selectionIsDisabled(this.hours, this.use12Hour, 'pm')
}
]
: []
},
displayedHours () {
return this.hours.filter(hour => hour.display)
},
hours () {
return hours(this.hourStep).filter(hour => {
if (!this.use12Hour) {
return true
} else {
const year = this.currentDateTime.c.year
const month = this.currentDateTime.c.month
const day = this.currentDateTime.c.day
return hours(this.hourStep).map(hour => {
let isVisible = true
if (this.use12Hour) {
if (this.hour < 12) {
return hour < 12
isVisible = hour < 12
} else {
return hour >= 12
isVisible = hour >= 12
}
}
}).map(hour => ({
number: pad(hour),
selected: hour === this.hour,
disabled: timeComponentIsDisabled(this.minHour, this.maxHour, hour)
}))
return {
number: pad(hour),
display: isVisible,
selected: hour === this.hour,
disabled: timeComponentIsDisabled(this.minHour, this.maxHour, hour) || this.datetimeDisabledChecker(year, month, day, hour)
}
})
},
minutes () {
const year = this.currentDateTime.c.year
const month = this.currentDateTime.c.month
const day = this.currentDateTime.c.day
return minutes(this.minuteStep).map(minute => ({
number: pad(minute),
selected: minute === this.minute,
disabled: timeComponentIsDisabled(this.minMinute, this.maxMinute, minute)
disabled: timeComponentIsDisabled(this.minMinute, this.maxMinute, minute) || this.datetimeDisabledChecker(year, month, day, this.hour, minute)
}))
},
minHour () {
Expand Down Expand Up @@ -103,12 +147,15 @@ export default {
this.$emit('change', { minute: parseInt(minute.number) })
},
selectSuffix (suffix) {
if (suffix === 'am') {
if (suffix.disabled) {
return
}
if (suffix.id === 'am') {
if (this.hour >= 12) {
this.$emit('change', { hour: parseInt(this.hour - 12), suffixTouched: true })
}
}
if (suffix === 'pm') {
if (suffix.id === 'pm') {
if (this.hour < 12) {
this.$emit('change', { hour: parseInt(this.hour + 12), suffixTouched: true })
}
Expand Down
6 changes: 5 additions & 1 deletion src/DatetimeYearPicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export default {
maxDate: {
type: DateTime,
default: null
},
datetimeDisabledChecker: {
type: Function,
default: (year, month, day, hour, minute, second) => false
}
},

Expand All @@ -32,7 +36,7 @@ export default {
return years(this.year).map(year => ({
number: year,
selected: year === this.year,
disabled: !year || yearIsDisabled(this.minDate, this.maxDate, year)
disabled: !year || yearIsDisabled(this.minDate, this.maxDate, year) || this.datetimeDisabledChecker(year)
}))
}
},
Expand Down
12 changes: 12 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ export function yearIsDisabled (minDate, maxDate, year) {
(maxYear && year > maxYear)
}

export function selectionIsDisabled (hours, use12Hour, selection) {
// if not use12hours
const enabledHours = hours.filter(hour => !hour.disabled)
let hasSelection = enabledHours.length > 0
if (use12Hour) {
hasSelection = !!enabledHours.find(hour =>
selection === 'am' ? hour.number < 12 : hour.number >= 12
)
}
return !hasSelection
}

export function timeComponentIsDisabled (min, max, component) {
return (min !== null && component < min) ||
(max !== null && component > max)
Expand Down
Loading