1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-01-15 21:18:41 +00:00
bramw_baserow/web-frontend/modules/builder/components/elements/baseComponents/ABDateTimePicker.vue
2024-12-12 21:46:58 +01:00

239 lines
7.1 KiB
Vue

<template>
<div ref="wrapper" class="ab-datetime-picker__wrapper">
<i
class="button-icon__icon iconoir-calendar"
@click="$refs.dateContext.show($refs.wrapper, 'bottom', 'left', 0)"
/>
<ABInput
v-model="dateInputValue"
class="ab-datetime-picker__input"
:placeholder="dateFormat"
@focus="$refs.dateContext.toggle($refs.wrapper, 'bottom', 'left', 0)"
@click="$refs.dateContext.show($refs.wrapper, 'bottom', 'left', 0)"
@blur="
updateDate($event.target.value)
$refs.dateContext.hide()
"
@input="updateCalendar"
/>
<div ref="timeWrapper">
<ABInput
v-if="includeTime"
v-model="timeInputValue"
class="ab-datetime-picker__input"
:placeholder="timeFormat"
@focus="$refs.timeContext.toggle($refs.timeWrapper, 'bottom', 'left')"
@click="$refs.timeContext.show($refs.timeWrapper, 'bottom', 'left')"
@blur="
updateTime($event.target.value)
$refs.timeContext.hide()
"
/>
</div>
<Context
ref="dateContext"
:hide-on-click-outside="true"
:style="calendarStyle"
class="ab-datetime-picker__calendar--context"
>
<client-only>
<date-picker
v-model="calendarValue"
:inline="true"
:monday-first="true"
:use-utc="true"
class="ab-datetime-picker__calendar"
@input="updateDate"
/>
</client-only>
</Context>
<Context
ref="timeContext"
:hide-on-click-outside="true"
class="ab-datetime-picker__clock"
:style="clockStyle"
>
<client-only>
<ul @mousedown="$event.preventDefault()">
<li v-for="time in getTimePickerTimes()" :key="time">
<a
:class="{ active: time === timeInputValue }"
@click="updateTime(time)"
>
{{ time }}
</a>
</li>
</ul>
</client-only>
</Context>
</div>
</template>
<script>
import { DATE_FORMATS, TIME_FORMATS } from '@baserow/modules/builder/enums'
import moment from '@baserow/modules/core/moment'
import { FormattedDate, FormattedDateTime } from '@baserow/modules/builder/date'
export default {
name: 'ABDateTimePicker',
props: {
value: {
type: [FormattedDate, FormattedDateTime],
required: false,
default: null,
},
dateFormat: {
type: String,
required: false,
default: DATE_FORMATS.ISO.format,
},
timeFormat: {
type: String,
required: false,
default: TIME_FORMATS['24'].format,
},
includeTime: {
type: Boolean,
required: false,
default: false,
},
calendarStyle: {
type: Object,
required: false,
default: () => ({}),
},
clockStyle: {
type: Object,
required: false,
default: () => ({}),
},
},
data() {
return {
dateInputValue: '',
timeInputValue: '',
calendarValue: null,
}
},
watch: {
value: {
handler(value) {
if (!value) {
// The input values should be empty when the value is initialized
// or when the user wipes out the fields
this.dateInputValue = ''
this.timeInputValue = ''
this.calendarValue = null
} else {
// If date and/or time are invalid, `toString` will return `Invalid Date`
this.dateInputValue = value.toString(this.dateFormat)
this.timeInputValue = this.includeTime
? value.toString(this.timeFormat)
: null
// It is not possible to display an invalid date in the calendar,
// so we clean it when the date is invalid
this.calendarValue = value.isValid() ? value.toDate() : null
}
},
immediate: true,
},
dateFormat: {
handler(value) {
this.dateInputValue = this.value ? this.value.toString(value) : ''
},
immediate: true,
},
timeFormat: {
handler(value) {
this.timeInputValue = this.value ? this.value.toString(value) : ''
},
immediate: true,
},
},
methods: {
/**
* Generate a list of times the user can choose from, spaced 30 min apart.
*/
getTimePickerTimes() {
const numberOfHalfHoursInADay = 24 * 2
return Array.from({ length: numberOfHalfHoursInADay }, (_, i) =>
moment()
.startOf('day')
.add(i * 30, 'minutes')
.format(this.timeFormat)
)
},
/**
* Update the current input 'value' with the given date.
* @param {string} value - The string date value.
*/
updateDate(value) {
// Clean the input value if there are no values in date and time inputs
if (!value && !this.timeInputValue) {
this.$emit('input', null)
return
}
if (this.includeTime) {
// We start by only including the date format as this is the only part
// we are updating. If we included the time the date validation would fail,
// because the date input only contains date values.
const datetime = new FormattedDateTime(value, this.dateFormat)
if (this.value && this.value.isValid()) {
datetime.set('hour', this.value.get('hour'))
datetime.set('minute', this.value.get('minute'))
}
// To keep the time part in future operations we update the format
// string to also include it
datetime.format = `${this.dateFormat} ${this.timeFormat}`
this.$emit('input', datetime)
} else {
const date = new FormattedDate(value, this.dateFormat)
this.$emit('input', date)
}
},
/**
* Update the current input 'value' with the given time.
* @param {string} value - The string time value.
*/
updateTime(value) {
// Clean the input value if there are no values in date and time inputs
if (!value && !this.dateInputValue) {
this.$emit('input', null)
return
}
// Try to parse the time using the chosen format
let datetime = new FormattedDateTime(value, this.timeFormat)
// If the parsed time is not valid, we set both hours and minutes to 0
if (!datetime.isValid()) {
datetime = new FormattedDateTime('00:00', 'HH:mm')
datetime.format = this.timeFormat
}
// Include the year month and date values from the date input
if (this.value && this.value.isValid()) {
datetime.set('year', this.value.get('year'))
datetime.set('month', this.value.get('month'))
datetime.set('date', this.value.get('date'))
// To keep the date part in future operations we update the format
// string to also include it
datetime.format = `${this.dateFormat} ${this.timeFormat}`
}
this.$emit('input', datetime)
},
/**
* Update the calendar value based on a string date value.
* @param {string} value - The string date value.
*/
updateCalendar(value) {
const date = new FormattedDate(value, this.dateFormat)
if (date.isValid()) {
this.calendarValue = date.toDate()
}
},
},
}
</script>