Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions resources/js/components/ui/DateRangePicker/DateRangePicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
import Card from '../Card/Card.vue';
import Button from '../Button/Button.vue';
import Calendar from '../Calendar/Calendar.vue';
import Icon from '../Icon/Icon.vue';
import { parseAbsoluteToLocal } from '@internationalized/date';

const emit = defineEmits(['update:modelValue']);
Expand All @@ -32,7 +31,7 @@ const props = defineProps({
badge: { type: String, default: null },
required: { type: Boolean, default: false },
/** The controlled date range value with `start` and `end` properties. <br><br> Each should be an ISO 8601 date and time string with a UTC offset (eg. `2021-11-07T07:45:00Z` or `2021-11-07T07:45:00-07:00`) */
modelValue: { type: [Object, String], default: null },
modelValue: { type: [Object, String], required: true, default: () => ({start: null, end: null}) },
/** The minimum selectable date. <br><br> Should be an ISO 8601 date and time string with a UTC offset (eg. `2021-11-07T07:45:00Z` or `2021-11-07T07:45:00-07:00`) */
min: { type: [String, Object], default: null },
/** The maximum selectable date. <br><br> Should be an ISO 8601 date and time string with a UTC offset (eg. `2021-11-07T07:45:00Z` or `2021-11-07T07:45:00-07:00`) */
Expand Down Expand Up @@ -76,6 +75,12 @@ const placeholder = parseAbsoluteToLocal(new Date().toISOString());
const calendarEvents = computed(() => ({
'update:model-value': (event) => {
if (props.granularity === 'day') {

// Avoid fatal error `Cannot set properties of undefined (setting 'hour')`
if (event.end == null) {
return
}

event.start.hour = 0;
event.start.minute = 0;
event.start.second = 0;
Expand All @@ -90,6 +95,12 @@ const calendarEvents = computed(() => ({
emit('update:modelValue', event)
},
}));

const hasDates = computed(() => {
return calendarBindings.value.modelValue != null
&& calendarBindings.value.modelValue.end != null
&& calendarBindings.value.modelValue?.start != null
})
</script>

<template>
Expand Down Expand Up @@ -155,7 +166,7 @@ const calendarEvents = computed(() => ({
</DateRangePickerInput>
</template>
<div class="flex-1" />
<Button v-if="!readOnly" @click="emit('update:modelValue', null)" variant="subtle" size="sm" icon="x" class="-me-2" :disabled="disabled" />
<Button v-if="!readOnly && hasDates" @click="emit('update:modelValue', {start: null, end: null})" variant="subtle" size="sm" icon="x" class="-me-2" :disabled="disabled" />
</div>
</DateRangePickerField>

Expand Down
27 changes: 24 additions & 3 deletions resources/js/stories/DateRangePicker.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ const meta = {
options: ['day', 'hour', 'minute', 'second'],
},
'update:modelValue': {
description: 'Event handler called when the date range value changes. <br><br> Returns an object with `start` and `end` properties, each as an ISO 8601 date and time string.',
description: 'Event handler called when the date range value changes. <br><br> Returns a range object with `start` and `end` values as `@internationalized/date` instances (e.g. `CalendarDate`, `CalendarDateTime`, `ZonedDateTime`) or `null` when the selection is cleared.',
table: {
category: 'events',
type: { summary: '(value: { start: string, end: string }) => void' }
type: { summary: '(value: { start: DateValue | null; end: DateValue | null } | null) => void' }
}
}
},
Expand Down Expand Up @@ -71,6 +71,27 @@ export const _MinMax: Story = {
}),
};

const granularityDayCode = `
<DateRangePicker v-model="range" granularity="day" />
`;

export const _GranularityDay: Story = {
tags: ['!dev'],
parameters: {
docs: {
source: { code: granularityDayCode }
}
},
render: () => ({
components: { DateRangePicker },
setup() {
const range = ref({start: null, end: null});
return { range };
},
template: granularityDayCode,
}),
};

const inlineCode = `
<DateRangePicker v-model="range" inline />
`;
Expand All @@ -85,7 +106,7 @@ export const _Inline: Story = {
render: () => ({
components: { DateRangePicker },
setup() {
const range = ref(null);
const range = ref({start: null, end: null});
return { range };
},
template: inlineCode,
Expand Down
4 changes: 4 additions & 0 deletions resources/js/stories/docs/DateRangePicker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ A date range picker component for selecting a start and end date. Perfect for ca
Restrict selectable dates using the `min` and `max` props.
<Canvas of={DateRangePickerStories._MinMax} sourceState={'shown'} />

## Granularity
The granularity of the date range picker. Options: `day`, `hour`, `minute`, `second`
<Canvas of={DateRangePickerStories._GranularityDay} sourceState={'shown'} />

## Inline
Display the calendar inline instead of in a popover.
<Canvas of={DateRangePickerStories._Inline} sourceState={'shown'} />
Expand Down