-
Notifications
You must be signed in to change notification settings - Fork 276
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #62 from shariquerik/datetime-in-filter
fix: Allow more fieldtypes in filter
- Loading branch information
Showing
5 changed files
with
794 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,265 @@ | ||
<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="my-2 w-fit select-none space-y-3 rounded border border-gray-50 bg-white p-3 text-base shadow" | ||
> | ||
<div class="flex items-center text-gray-700"> | ||
<div | ||
class="flex h-6 w-6 cursor-pointer items-center justify-center rounded hover:bg-gray-100" | ||
> | ||
<FeatherIcon | ||
@click="prevMonth" | ||
name="chevron-left" | ||
class="h-5 w-5" | ||
/> | ||
</div> | ||
<div class="flex-1 text-center text-lg font-medium text-blue-500"> | ||
{{ formatMonth }} | ||
</div> | ||
<div | ||
class="flex h-6 w-6 cursor-pointer items-center justify-center rounded hover:bg-gray-100" | ||
> | ||
<FeatherIcon | ||
@click="nextMonth" | ||
name="chevron-right" | ||
class="h-5 w-5" | ||
/> | ||
</div> | ||
</div> | ||
<div class="flex space-x-2"> | ||
<Input | ||
type="text" | ||
:value="value" | ||
@change="selectDate(getDate($event)) || togglePopover()" | ||
></Input> | ||
<Button class="h-7" @click="selectDate(getDate()) || togglePopover()"> | ||
Today | ||
</Button> | ||
</div> | ||
<div class="mt-2 flex flex-col items-center justify-center text-base"> | ||
<div class="flex w-full items-center space-x-1 text-gray-600"> | ||
<div | ||
class="flex h-[30px] w-[30px] items-center justify-center text-center" | ||
v-for="(d, i) in ['S', 'M', 'T', 'W', 'T', 'F', 'S']" | ||
:key="i" | ||
> | ||
{{ d }} | ||
</div> | ||
</div> | ||
<div v-for="(week, i) in datesAsWeeks" :key="i" class="mt-1"> | ||
<div class="flex w-full items-center"> | ||
<div | ||
v-for="date in week" | ||
:key="toValue(date)" | ||
class="mr-1 flex h-[30px] w-[30px] cursor-pointer items-center justify-center rounded last:mr-0 hover:bg-blue-50 hover:text-blue-500" | ||
:class="{ | ||
'text-gray-600': date.getMonth() !== currentMonth - 1, | ||
'text-blue-500': toValue(date) === toValue(today), | ||
'bg-blue-50 font-semibold text-blue-500': | ||
toValue(date) === value, | ||
}" | ||
@click=" | ||
() => { | ||
selectDate(date) | ||
togglePopover() | ||
} | ||
" | ||
> | ||
{{ date.getDate() }} | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div class="mt-1 flex w-full justify-end"> | ||
<div | ||
class="cursor-pointer rounded px-2 py-1 hover:bg-gray-100" | ||
@click=" | ||
() => { | ||
selectDate('') | ||
togglePopover() | ||
} | ||
" | ||
> | ||
Clear | ||
</div> | ||
</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, | ||
} | ||
}, | ||
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: 'short', | ||
year: 'numeric', | ||
}) | ||
}, | ||
}, | ||
methods: { | ||
selectDate(date) { | ||
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 | ||
}, | ||
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 | ||
}, | ||
toValue(date) { | ||
if (!date) { | ||
return '' | ||
} | ||
// toISOString is buggy and reduces the day by one | ||
// this is because it considers the UTC timestamp | ||
// in order to circumvent that we need to use luxon/moment | ||
// but that refactor could take some time, so fixing the time difference | ||
// as suggested in this answer. | ||
// https://stackoverflow.com/a/16084846/3541205 | ||
date.setHours(0, -date.getTimezoneOffset(), 0, 0) | ||
return date.toISOString().slice(0, 10) | ||
}, | ||
getDate(...args) { | ||
let d = new Date(...args) | ||
return d | ||
}, | ||
}, | ||
} | ||
</script> |
Oops, something went wrong.