feat: added datetimepicker in filter for datetime fieldtype

This commit is contained in:
Shariq Ansari 2024-02-08 00:30:20 +05:30
parent 2aaf51f681
commit d298e0f83e
2 changed files with 372 additions and 2 deletions

View File

@ -0,0 +1,368 @@
<template>
<Popover
@open="selectCurrentMonthYear"
class="flex w-full [&>div:first-child]:w-full"
>
<template #target="{ togglePopover }">
<input
readonly
type="text"
:placeholder="placeholder"
:value="value && formatter ? formatter(value) : value"
@focus="!readonly ? togglePopover() : null"
:class="[
'form-input block h-7 w-full cursor-pointer select-none rounded border-gray-400 text-sm placeholder-gray-500',
inputClass,
]"
/>
</template>
<template #body="{ togglePopover }">
<div
class="mt-2 w-fit select-none divide-y rounded-lg bg-white text-base shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
>
<div class="flex items-center p-1 text-gray-500">
<Button variant="ghost" class="h-7 w-7" @click="prevMonth">
<FeatherIcon stroke-width="2" name="chevron-left" class="h-4 w-4" />
</Button>
<div class="flex-1 text-center text-base font-medium text-gray-700">
{{ formatMonth }}
</div>
<Button variant="ghost" class="h-7 w-7" @click="nextMonth">
<FeatherIcon
stroke-width="2"
name="chevron-right"
class="h-4 w-4"
/>
</Button>
</div>
<div class="flex items-center justify-center gap-1 p-1">
<TextInput
class="text-sm"
type="text"
:value="value"
@change="selectDate(getDate($event)) || togglePopover()"
/>
<Button
label="Now"
class="text-sm"
@click="selectDate(getDate(), false, true) || togglePopover()"
/>
</div>
<div
class="flex flex-col items-center justify-center p-1 text-gray-800"
>
<div class="flex items-center text-xs uppercase">
<div
class="flex h-6 w-8 items-center justify-center text-center"
v-for="(d, i) in ['su', 'mo', 'tu', 'we', 'th', 'fr', 'sa']"
:key="i"
>
{{ d }}
</div>
</div>
<div
class="flex items-center"
v-for="(week, i) in datesAsWeeks"
:key="i"
>
<div
v-for="date in week"
:key="toValue(date)"
class="flex h-8 w-8 cursor-pointer items-center justify-center rounded hover:bg-gray-50"
:class="{
'text-gray-400': date.getMonth() !== currentMonth - 1,
'font-extrabold text-gray-900':
toValue(date) === toValue(today),
'bg-gray-800 text-white hover:bg-gray-800':
toValue(date) === value,
}"
@click="
() => {
selectDate(date)
togglePopover()
}
"
>
{{ date.getDate() }}
</div>
</div>
</div>
<div class="flex items-center justify-around gap-2 p-1">
<div>
{{ twoDigit(hour) }} : {{ twoDigit(minute) }} :
{{ twoDigit(second) }}
</div>
<div class="flex flex-col items-center justify-center">
<div class="slider flex min-h-4 items-center justify-center">
<TextInput
v-model="hour"
name="hours"
type="range"
min="0"
max="23"
step="1"
@change="() => changeTime() || togglePopover()"
/>
</div>
<div class="slider flex min-h-4 items-center justify-center">
<TextInput
v-model="minute"
name="minutes"
type="range"
min="0"
max="59"
step="1"
@change="() => changeTime() || togglePopover()"
/>
</div>
<div class="slider flex min-h-4 items-center justify-center">
<TextInput
v-model="second"
name="seconds"
type="range"
min="0"
max="59"
step="1"
@change="() => changeTime() || togglePopover()"
/>
</div>
</div>
</div>
<div class="flex justify-end p-1">
<Button
label="Clear"
class="text-sm"
@click="
() => {
selectDate('')
togglePopover()
}
"
/>
</div>
</div>
</template>
</Popover>
</template>
<script>
import { Popover } from 'frappe-ui'
export default {
name: 'DatePicker',
props: ['value', 'placeholder', 'formatter', 'readonly', 'inputClass'],
emits: ['change'],
components: {
Popover,
},
data() {
return {
currentYear: null,
currentMonth: null,
hour: 0,
minute: 0,
second: 0,
}
},
created() {
this.selectCurrentMonthYear()
},
computed: {
today() {
return this.getDate()
},
datesAsWeeks() {
let datesAsWeeks = []
let dates = this.dates.slice()
while (dates.length) {
let week = dates.splice(0, 7)
datesAsWeeks.push(week)
}
return datesAsWeeks
},
dates() {
if (!(this.currentYear && this.currentMonth)) {
return []
}
let monthIndex = this.currentMonth - 1
let year = this.currentYear
let firstDayOfMonth = this.getDate(year, monthIndex, 1)
let lastDayOfMonth = this.getDate(year, monthIndex + 1, 0)
let leftPaddingCount = firstDayOfMonth.getDay()
let rightPaddingCount = 6 - lastDayOfMonth.getDay()
let leftPadding = this.getDatesAfter(firstDayOfMonth, -leftPaddingCount)
let rightPadding = this.getDatesAfter(lastDayOfMonth, rightPaddingCount)
let daysInMonth = this.getDaysInMonth(monthIndex, year)
let datesInMonth = this.getDatesAfter(firstDayOfMonth, daysInMonth - 1)
let dates = [
...leftPadding,
firstDayOfMonth,
...datesInMonth,
...rightPadding,
]
if (dates.length < 42) {
const finalPadding = this.getDatesAfter(dates.at(-1), 42 - dates.length)
dates = dates.concat(...finalPadding)
}
return dates
},
formatMonth() {
let date = this.getDate(this.currentYear, this.currentMonth - 1, 1)
return date.toLocaleString('en-US', {
month: 'long',
year: 'numeric',
})
},
},
methods: {
changeTime() {
let date = this.value ? this.getDate(this.value) : this.getDate()
this.selectDate(date, true)
},
selectDate(date, isTimeChange = false, isNow = false) {
if (!isTimeChange) {
let currentDate =
this.value && !isNow ? this.getDate(this.value) : this.getDate()
this.hour = currentDate.getHours()
this.minute = currentDate.getMinutes()
this.second = currentDate.getSeconds()
}
this.$emit('change', this.toValue(date))
},
selectCurrentMonthYear() {
let date = this.value ? this.getDate(this.value) : this.getDate()
if (date === 'Invalid Date') {
date = this.getDate()
}
this.currentYear = date.getFullYear()
this.currentMonth = date.getMonth() + 1
this.hour = date.getHours()
this.minute = date.getMinutes()
this.second = date.getSeconds()
},
prevMonth() {
this.changeMonth(-1)
},
nextMonth() {
this.changeMonth(1)
},
changeMonth(adder) {
this.currentMonth = this.currentMonth + adder
if (this.currentMonth < 1) {
this.currentMonth = 12
this.currentYear = this.currentYear - 1
}
if (this.currentMonth > 12) {
this.currentMonth = 1
this.currentYear = this.currentYear + 1
}
},
getDatesAfter(date, count) {
let incrementer = 1
if (count < 0) {
incrementer = -1
count = Math.abs(count)
}
let dates = []
while (count) {
date = this.getDate(
date.getFullYear(),
date.getMonth(),
date.getDate() + incrementer
)
dates.push(date)
count--
}
if (incrementer === -1) {
return dates.reverse()
}
return dates
},
getDaysInMonth(monthIndex, year) {
let daysInMonthMap = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
let daysInMonth = daysInMonthMap[monthIndex]
if (monthIndex === 1 && this.isLeapYear(year)) {
return 29
}
return daysInMonth
},
isLeapYear(year) {
if (year % 400 === 0) return true
if (year % 100 === 0) return false
if (year % 4 === 0) return true
return false
},
twoDigit(number) {
return number.toString().padStart(2, '0')
},
toValue(date) {
if (!date) return ''
date.setHours(this.hour, this.minute, this.second, 0)
// "YYYY-MM-DD HH:MM:SS"
return `${date.getFullYear()}-${this.twoDigit(
date.getMonth() + 1
)}-${this.twoDigit(date.getDate())} ${this.twoDigit(
date.getHours()
)}:${this.twoDigit(date.getMinutes())}:${this.twoDigit(
date.getSeconds()
)}`
},
getDate(...args) {
let d = new Date(...args)
return d
},
},
}
</script>
<style scoped>
.slider {
--trackHeight: 1px;
--thumbRadius: 10px;
}
:deep(.slider input[type='range']) {
-webkit-appearance: none;
appearance: none;
height: 100%;
background: transparent;
padding: 0;
margin: 0;
cursor: pointer;
}
:deep(.slider input[type='range']::-webkit-slider-runnable-track) {
appearance: none;
background: #000;
height: var(--trackHeight);
border-radius: 999px;
}
:deep(.slider input[type='range']::-webkit-slider-thumb) {
width: var(--thumbRadius);
height: var(--thumbRadius);
margin-top: calc((var(--trackHeight) - var(--thumbRadius)) / 2);
background: #fff;
border-radius: 3px;
pointer-events: all;
appearance: none;
outline: 1px solid #777777;
z-index: 1;
}
:deep(.slider:hover input[type='range']::-webkit-slider-thumb) {
outline: 1px solid #000;
}
:deep(.slider input[type='range']::-webkit-slider-thumb) {
width: var(--thumbRadius);
height: var(--thumbRadius);
margin-top: calc((var(--trackHeight) - var(--thumbRadius)) / 2);
background: #fff;
border-radius: 3px;
pointer-events: all;
appearance: none;
z-index: 1;
}
</style>

View File

@ -95,6 +95,7 @@
</template>
<script setup>
import DatePicker from '@/components/Controls/DatePicker.vue'
import DatetimePicker from '@/components/Controls/DatetimePicker.vue'
import DateRangePicker from '@/components/Controls/DateRangePicker.vue'
import NestedPopover from '@/components/NestedPopover.vue'
import FilterIcon from '@/components/Icons/FilterIcon.vue'
@ -150,7 +151,8 @@ onMounted(() => {
const filters = computed(() => {
if (!list.value?.data) return new Set()
let allFilters = list.value?.params?.filters || list.value.data?.params?.filters
let allFilters =
list.value?.params?.filters || list.value.data?.params?.filters
if (!allFilters || !filterableFields.data) return new Set()
// remove default filters
if (props.default_filters) {
@ -338,7 +340,7 @@ function getValSelect(f) {
} else if (typeDate.includes(fieldtype) && operator == 'between') {
return h(DateRangePicker)
} else if (typeDate.includes(fieldtype)) {
return h(DatePicker)
return h(fieldtype == 'Date' ? DatePicker : DatetimePicker)
} else {
return h(FormControl, { type: 'text' })
}