diff --git a/demo/sections/components/DateRangeSelect.vue b/demo/sections/components/DateRangeSelect.vue new file mode 100644 index 000000000..306abd620 --- /dev/null +++ b/demo/sections/components/DateRangeSelect.vue @@ -0,0 +1,44 @@ + + + \ No newline at end of file diff --git a/demo/sections/components/index.ts b/demo/sections/components/index.ts index 5c8413e2f..15ae89e13 100644 --- a/demo/sections/components/index.ts +++ b/demo/sections/components/index.ts @@ -17,6 +17,7 @@ export const components: Section = { contextSidebar: () => import('./ContextSidebar.vue'), dateInput: () => import('./DateInput.vue'), dateRangeInput: () => import('./DateRangeInput.vue'), + dateRangeSelect: () => import('./DateRangeSelect.vue'), dialog: () => import('./Dialog.vue'), divider: () => import('./Divider.vue'), drawer: () => import('./Drawer.vue'), diff --git a/src/components/DateInput/PDateInput.vue b/src/components/DateInput/PDateInput.vue index 29970500e..d49e187e4 100644 --- a/src/components/DateInput/PDateInput.vue +++ b/src/components/DateInput/PDateInput.vue @@ -21,7 +21,6 @@ @@ -88,13 +87,6 @@ \ No newline at end of file diff --git a/src/components/DateRangeSelect/PDateRangeSelectOptions.vue b/src/components/DateRangeSelect/PDateRangeSelectOptions.vue new file mode 100644 index 000000000..9aa308765 --- /dev/null +++ b/src/components/DateRangeSelect/PDateRangeSelectOptions.vue @@ -0,0 +1,100 @@ + + + + + \ No newline at end of file diff --git a/src/components/DateRangeSelect/index.ts b/src/components/DateRangeSelect/index.ts new file mode 100644 index 000000000..12f04cce1 --- /dev/null +++ b/src/components/DateRangeSelect/index.ts @@ -0,0 +1,8 @@ +import { App } from 'vue' +import PDateRangeSelect from '@/components/DateRangeSelect/PDateRangeSelect.vue' + +const install = (app: App): void => { + app.component('PDateRangeSelect', PDateRangeSelect) +} + +export { PDateRangeSelect, install } \ No newline at end of file diff --git a/src/components/DateRangeSelect/utilities.ts b/src/components/DateRangeSelect/utilities.ts new file mode 100644 index 000000000..d7c08f954 --- /dev/null +++ b/src/components/DateRangeSelect/utilities.ts @@ -0,0 +1,53 @@ +import { addSeconds, endOfDay, format, intervalToDuration, isSameDay, startOfDay } from 'date-fns' +import { toPluralString } from '@/utilities' + +type DateRange = { + startDate: Date, + endDate: Date, +} + +const dateFormat = 'MMM do, yyyy' +const timeFormat = 'hh:mm a' +const dateTimeFormat = `${dateFormat} 'at' ${timeFormat}` + +export function getDateSpanLabel(seconds: number): string { + + const now = new Date() + const duration = intervalToDuration({ + start: now, + end: addSeconds(now, seconds), + }) + + const reduced = Object.entries(duration).reduce((durations, [key, value]) => { + if (value) { + const unit = key.slice(0, -1) + durations.push(`${value} ${toPluralString(unit, value)}`) + } + + return durations + }, []) + + const direction = seconds < 0 ? 'Past' : 'Next' + + return `${direction} ${reduced.join(' ')}` +} + +export function getDateRangeLabel({ startDate, endDate }: DateRange): string { + if (isPickerSingleDayRange({ startDate, endDate })) { + return format(startDate, dateFormat) + } + + if (isFullDateRange({ startDate, endDate })) { + return `${format(startDate, dateFormat)} - ${format(endDate, dateFormat)}` + } + + return `${format(startDate, dateTimeFormat)} - ${format(endDate, dateTimeFormat)}` +} + +function isPickerSingleDayRange({ startDate, endDate }: DateRange): boolean { + return isFullDateRange({ startDate, endDate }) && isSameDay(startDate, endDate) +} + +export function isFullDateRange({ startDate, endDate }: DateRange): boolean { + return startOfDay(startDate).getTime() === startDate.getTime() && endOfDay(endDate).getTime() === endDate.getTime() +} \ No newline at end of file diff --git a/src/components/DateTimeInputGroup/PDateTimeInputGroup.vue b/src/components/DateTimeInputGroup/PDateTimeInputGroup.vue index f679a361f..167434cd9 100644 --- a/src/components/DateTimeInputGroup/PDateTimeInputGroup.vue +++ b/src/components/DateTimeInputGroup/PDateTimeInputGroup.vue @@ -10,10 +10,10 @@ - + @@ -38,7 +38,7 @@ (event: 'update:modelValue', value: Date | null): void, }>() - const date = computed({ + const startDate = computed({ get() { return props.modelValue ?? null }, @@ -47,6 +47,18 @@ }, }) + const startTime = computed({ + get() { + return props.modelValue ?? null + }, + set(value) { + if (!value) { + return + } + + emit('update:modelValue', value) + }, + }) const nowOrTodayLabel = computed(() => props.showTime ? 'Now' : 'Today') function setToTodayOrNow(): void { diff --git a/src/components/NativeDateInput/PNativeDateInput.vue b/src/components/NativeDateInput/PNativeDateInput.vue index 85408a9fa..dca50cd0c 100644 --- a/src/components/NativeDateInput/PNativeDateInput.vue +++ b/src/components/NativeDateInput/PNativeDateInput.vue @@ -64,7 +64,14 @@ }) const stringMin = computed(() => props.min ? format(props.min, 'yyyy-MM-dd') : undefined) - const stringMax = computed(() => props.max ? format(props.max, 'yyyy-MM-dd') : undefined) + const stringMax = computed(() => { + if (props.max) { + return format(props.max, 'yyyy-MM-dd') + } + + // max date with a 4 digit year + return '2999-12-31' + }) function showPicker(): void { inputElement.value?.showPicker() diff --git a/src/components/SelectOption/PSelectOption.vue b/src/components/SelectOption/PSelectOption.vue index b2d088115..1d9255a96 100644 --- a/src/components/SelectOption/PSelectOption.vue +++ b/src/components/SelectOption/PSelectOption.vue @@ -125,6 +125,7 @@ flex gap-2 items-center + cursor-pointer } .p-select-option--selected { @apply diff --git a/src/components/index.ts b/src/components/index.ts index e08f47f61..f3e636ff3 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -21,6 +21,7 @@ import { PDateInput, install as installPDateInput } from '@/components/DateInput import { PDatePicker, install as installPDatePicker } from '@/components/DatePicker' import { PDateRangeInput, install as installPDateRangeInput } from '@/components/DateRangeInput' import { PDateRangePicker, install as installPDateRangePicker } from '@/components/DateRangePicker' +import { PDateRangeSelect, install as installPDateRangeSelect } from '@/components/DateRangeSelect' import { PDialog, install as installPDialog } from '@/components/Dialog' import { PDivider, install as installPDivider } from '@/components/Divider' import { PDrawer, install as installPDrawer } from '@/components/Drawer' @@ -111,6 +112,7 @@ export { PDatePicker, PDateRangeInput, PDateRangePicker, + PDateRangeSelect, PDialog, PDivider, PDrawer, @@ -212,6 +214,7 @@ export const installs = [ installPDatePicker, installPDateRangeInput, installPDateRangePicker, + installPDateRangeSelect, installPDialog, installPDivider, installPDrawer, @@ -303,6 +306,7 @@ declare module '@vue/runtime-core' { PDatePicker: typeof PDatePicker, PDateRangeInput: typeof PDateRangeInput, PDateRangePicker: typeof PDateRangePicker, + PDateRangeSelect: typeof PDateRangeSelect, PDialog: typeof PDialog, PDivider: typeof PDivider, PDrawer: typeof PDrawer,