docs: add example for multiple month date range picker

This commit is contained in:
Eduard Predescu 2024-04-15 22:00:09 +03:00
parent 8052eb1a33
commit 54adf5760d
4 changed files with 580 additions and 0 deletions

View File

@ -423,6 +423,13 @@ export const Index = {
component: () => import("../src/lib/registry/default/example/DatePickerForm.vue").then((m) => m.default), component: () => import("../src/lib/registry/default/example/DatePickerForm.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/DatePickerForm.vue"], files: ["../src/lib/registry/default/example/DatePickerForm.vue"],
}, },
"DatePickerWithIndependentMonths": {
name: "DatePickerWithIndependentMonths",
type: "components:example",
registryDependencies: ["range-calendar","button","popover","utils"],
component: () => import("../src/lib/registry/default/example/DatePickerWithIndependentMonths.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/DatePickerWithIndependentMonths.vue"],
},
"DatePickerWithPresets": { "DatePickerWithPresets": {
name: "DatePickerWithPresets", name: "DatePickerWithPresets",
type: "components:example", type: "components:example",
@ -1656,6 +1663,13 @@ export const Index = {
component: () => import("../src/lib/registry/new-york/example/DatePickerForm.vue").then((m) => m.default), component: () => import("../src/lib/registry/new-york/example/DatePickerForm.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/DatePickerForm.vue"], files: ["../src/lib/registry/new-york/example/DatePickerForm.vue"],
}, },
"DatePickerWithIndependentMonths": {
name: "DatePickerWithIndependentMonths",
type: "components:example",
registryDependencies: ["range-calendar","button","popover","utils"],
component: () => import("../src/lib/registry/new-york/example/DatePickerWithIndependentMonths.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/DatePickerWithIndependentMonths.vue"],
},
"DatePickerWithPresets": { "DatePickerWithPresets": {
name: "DatePickerWithPresets", name: "DatePickerWithPresets",
type: "components:example", type: "components:example",

View File

@ -29,6 +29,10 @@ See installations instructions for the [Popover](/docs/components/popover), [Cal
<ComponentPreview name="DatePickerWithRange" /> <ComponentPreview name="DatePickerWithRange" />
### Date Range Picker with Independent Months
<ComponentPreview name="DatePickerWithIndependentMonths" />
### With Presets ### With Presets
<ComponentPreview name="DatePickerWithPresets" /> <ComponentPreview name="DatePickerWithPresets" />

View File

@ -0,0 +1,283 @@
<script setup lang="ts">
import { type Ref, ref, watch } from 'vue'
import {
Calendar as CalendarIcon,
ChevronLeft,
ChevronRight,
} from 'lucide-vue-next'
import {
CalendarDate,
type DateValue,
isEqualMonth,
} from '@internationalized/date'
import { type DateRange, RangeCalendarRoot, useDateFormatter } from 'radix-vue'
import { createMonth, toDate } from 'radix-vue/date'
import {
RangeCalendarCell,
RangeCalendarCellTrigger,
RangeCalendarGrid,
RangeCalendarGridBody,
RangeCalendarGridRow,
RangeCalendarHeadCell,
} from '@/lib/registry/default/ui/range-calendar'
import { Button, buttonVariants } from '@/lib/registry/default/ui/button'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/lib/registry/default/ui/popover'
import { cn } from '@/lib/utils'
const value = ref({
start: new CalendarDate(2022, 1, 20),
end: new CalendarDate(2022, 1, 20).add({ days: 20 }),
}) as Ref<DateRange>
const locale = ref('en-US')
const formatter = useDateFormatter(locale.value)
const placeholder = ref(value.value.start) as Ref<DateValue>
const secondMonthPlaceholder = ref(value.value.end) as Ref<DateValue>
const firstMonth = ref(
createMonth({
dateObj: placeholder.value,
locale: locale.value,
fixedWeeks: true,
weekStartsOn: 0,
}),
)
const secondMonth = ref(
createMonth({
dateObj: secondMonthPlaceholder.value,
locale: locale.value,
fixedWeeks: true,
weekStartsOn: 0,
}),
)
function updateMonth(reference: 'first' | 'second', months: number) {
if (reference === 'first') {
placeholder.value = placeholder.value.add({ months })
}
else {
secondMonthPlaceholder.value = secondMonthPlaceholder.value.add({
months,
})
}
}
watch(placeholder, (_placeholder) => {
firstMonth.value = createMonth({
dateObj: _placeholder,
weekStartsOn: 0,
fixedWeeks: false,
locale: locale.value,
})
if (isEqualMonth(secondMonthPlaceholder.value, _placeholder)) {
secondMonthPlaceholder.value = secondMonthPlaceholder.value.add({
months: 1,
})
}
})
watch(secondMonthPlaceholder, (_secondMonthPlaceholder) => {
secondMonth.value = createMonth({
dateObj: _secondMonthPlaceholder,
weekStartsOn: 0,
fixedWeeks: false,
locale: locale.value,
})
if (isEqualMonth(_secondMonthPlaceholder, placeholder.value))
placeholder.value = placeholder.value.subtract({ months: 1 })
})
</script>
<template>
<Popover>
<PopoverTrigger as-child>
<Button
variant="outline"
:class="
cn(
'w-[280px] justify-start text-left font-normal',
!value && 'text-muted-foreground',
)
"
>
<CalendarIcon class="mr-2 h-4 w-4" />
<template v-if="value.start">
<template v-if="value.end">
{{
formatter.custom(toDate(value.start), {
dateStyle: "medium",
})
}}
-
{{
formatter.custom(toDate(value.end), {
dateStyle: "medium",
})
}}
</template>
<template v-else>
{{
formatter.custom(toDate(value.start), {
dateStyle: "medium",
})
}}
</template>
</template>
<template v-else>
Pick a date
</template>
</Button>
</PopoverTrigger>
<PopoverContent class="w-auto p-0">
<RangeCalendarRoot v-slot="{ weekDays }" class="p-3">
<div
class="flex flex-col gap-y-4 mt-4 sm:flex-row sm:gap-x-4 sm:gap-y-0"
>
<div class="flex flex-col gap-4">
<div class="flex items-center justify-between">
<button
:class="
cn(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
)
"
@click="updateMonth('first', -1)"
>
<ChevronLeft class="h-4 w-4" />
</button>
<div
:class="cn('text-sm font-medium')"
>
{{
formatter.fullMonthAndYear(
toDate(firstMonth.value),
)
}}
</div>
<button
:class="
cn(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
)
"
@click="updateMonth('first', 1)"
>
<ChevronRight class="h-4 w-4" />
</button>
</div>
<RangeCalendarGrid>
<RangeCalendarGridHead>
<RangeCalendarGridRow>
<RangeCalendarHeadCell
v-for="day in weekDays"
:key="day"
>
{{ day }}
</RangeCalendarHeadCell>
</RangeCalendarGridRow>
</RangeCalendarGridHead>
<RangeCalendarGridBody>
<RangeCalendarGridRow
v-for="(
weekDates, index
) in firstMonth.rows"
:key="`weekDate-${index}`"
class="mt-2 w-full"
>
<RangeCalendarCell
v-for="weekDate in weekDates"
:key="weekDate.toString()"
:date="weekDate"
>
<RangeCalendarCellTrigger
:day="weekDate"
:month="firstMonth.value"
/>
</RangeCalendarCell>
</RangeCalendarGridRow>
</RangeCalendarGridBody>
</RangeCalendarGrid>
</div>
<div class="flex flex-col gap-4">
<div class="flex items-center justify-between">
<button
:class="
cn(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
)
"
@click="updateMonth('second', 1)"
>
<ChevronRight class="h-4 w-4" />
</button>
<div
:class="cn('text-sm font-medium')"
>
{{
formatter.fullMonthAndYear(
toDate(secondMonth.value),
)
}}
</div>
<button
:class="
cn(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
)
"
@click="updateMonth('second', 1)"
>
<ChevronRight class="h-4 w-4" />
</button>
</div>
<RangeCalendarGrid>
<RangeCalendarGridHead>
<RangeCalendarGridRow>
<RangeCalendarHeadCell
v-for="day in weekDays"
:key="day"
>
{{ day }}
</RangeCalendarHeadCell>
</RangeCalendarGridRow>
</RangeCalendarGridHead>
<RangeCalendarGridBody>
<RangeCalendarGridRow
v-for="(
weekDates, index
) in secondMonth.rows"
:key="`weekDate-${index}`"
class="mt-2 w-full"
>
<RangeCalendarCell
v-for="weekDate in weekDates"
:key="weekDate.toString()"
:date="weekDate"
>
<RangeCalendarCellTrigger
:day="weekDate"
:month="secondMonth.value"
/>
</RangeCalendarCell>
</RangeCalendarGridRow>
</RangeCalendarGridBody>
</RangeCalendarGrid>
</div>
</div>
</RangeCalendarRoot>
</PopoverContent>
</Popover>
</template>

View File

@ -0,0 +1,279 @@
<script setup lang="ts">
import { type Ref, ref, watch } from 'vue'
import {
Calendar as CalendarIcon,
ChevronLeft,
ChevronRight,
} from 'lucide-vue-next'
import {
CalendarDate,
type DateValue,
isEqualMonth,
} from '@internationalized/date'
import { type DateRange, RangeCalendarRoot, useDateFormatter } from 'radix-vue'
import { createMonth, toDate } from 'radix-vue/date'
import {
RangeCalendarCell,
RangeCalendarCellTrigger,
RangeCalendarGrid,
RangeCalendarGridBody,
RangeCalendarGridRow,
RangeCalendarHeadCell,
} from '@/lib/registry/new-york/ui/range-calendar'
import { Button, buttonVariants } from '@/lib/registry/new-york/ui/button'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/lib/registry/new-york/ui/popover'
import { cn } from '@/lib/utils'
const value = ref({
start: new CalendarDate(2022, 1, 20),
end: new CalendarDate(2022, 1, 20).add({ days: 20 }),
}) as Ref<DateRange>
const locale = ref('en-US')
const formatter = useDateFormatter(locale.value)
const placeholder = ref(value.value.start) as Ref<DateValue>
const secondMonthPlaceholder = ref(value.value.end) as Ref<DateValue>
const firstMonth = ref(
createMonth({
dateObj: placeholder.value,
locale: locale.value,
fixedWeeks: true,
weekStartsOn: 0,
}),
)
const secondMonth = ref(
createMonth({
dateObj: secondMonthPlaceholder.value,
locale: locale.value,
fixedWeeks: true,
weekStartsOn: 0,
}),
)
function updateMonth(reference: 'first' | 'second', months: number) {
if (reference === 'first') {
placeholder.value = placeholder.value.add({ months })
}
else {
secondMonthPlaceholder.value = secondMonthPlaceholder.value.add({
months,
})
}
}
watch(placeholder, (_placeholder) => {
firstMonth.value = createMonth({
dateObj: _placeholder,
weekStartsOn: 0,
fixedWeeks: false,
locale: locale.value,
})
if (isEqualMonth(secondMonthPlaceholder.value, _placeholder)) {
secondMonthPlaceholder.value = secondMonthPlaceholder.value.add({
months: 1,
})
}
})
watch(secondMonthPlaceholder, (_secondMonthPlaceholder) => {
secondMonth.value = createMonth({
dateObj: _secondMonthPlaceholder,
weekStartsOn: 0,
fixedWeeks: false,
locale: locale.value,
})
if (isEqualMonth(_secondMonthPlaceholder, placeholder.value))
placeholder.value = placeholder.value.subtract({ months: 1 })
})
</script>
<template>
<Popover>
<PopoverTrigger as-child>
<Button
variant="outline"
:class="
cn(
'w-[280px] justify-start text-left font-normal',
!value && 'text-muted-foreground',
)
"
>
<CalendarIcon class="mr-2 h-4 w-4" />
<template v-if="value.start">
<template v-if="value.end">
{{
formatter.custom(toDate(value.start), {
dateStyle: "medium",
})
}}
-
{{
formatter.custom(toDate(value.end), {
dateStyle: "medium",
})
}}
</template>
<template v-else>
{{
formatter.custom(toDate(value.start), {
dateStyle: "medium",
})
}}
</template>
</template>
<template v-else>
Pick a date
</template>
</Button>
</PopoverTrigger>
<PopoverContent class="w-auto p-0">
<RangeCalendarRoot v-slot="{ weekDays }" class="p-3">
<div
class="flex flex-col gap-y-4 mt-4 sm:flex-row sm:gap-x-4 sm:gap-y-0"
>
<div class="flex flex-col gap-4">
<div class="flex items-center justify-between">
<button
:class="
cn(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
)
"
@click="updateMonth('first', -1)"
>
<ChevronLeft class="h-4 w-4" />
</button>
<div :class="cn('text-sm font-medium')">
{{
formatter.fullMonthAndYear(
toDate(firstMonth.value),
)
}}
</div>
<button
:class="
cn(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
)
"
@click="updateMonth('first', 1)"
>
<ChevronRight class="h-4 w-4" />
</button>
</div>
<RangeCalendarGrid>
<RangeCalendarGridHead>
<RangeCalendarGridRow>
<RangeCalendarHeadCell
v-for="day in weekDays"
:key="day"
>
{{ day }}
</RangeCalendarHeadCell>
</RangeCalendarGridRow>
</RangeCalendarGridHead>
<RangeCalendarGridBody>
<RangeCalendarGridRow
v-for="(
weekDates, index
) in firstMonth.rows"
:key="`weekDate-${index}`"
class="mt-2 w-full"
>
<RangeCalendarCell
v-for="weekDate in weekDates"
:key="weekDate.toString()"
:date="weekDate"
>
<RangeCalendarCellTrigger
:day="weekDate"
:month="firstMonth.value"
/>
</RangeCalendarCell>
</RangeCalendarGridRow>
</RangeCalendarGridBody>
</RangeCalendarGrid>
</div>
<div class="flex flex-col gap-4">
<div class="flex items-center justify-between">
<button
:class="
cn(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
)
"
@click="updateMonth('second', 1)"
>
<ChevronRight class="h-4 w-4" />
</button>
<div :class="cn('text-sm font-medium')">
{{
formatter.fullMonthAndYear(
toDate(secondMonth.value),
)
}}
</div>
<button
:class="
cn(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
)
"
@click="updateMonth('second', 1)"
>
<ChevronRight class="h-4 w-4" />
</button>
</div>
<RangeCalendarGrid>
<RangeCalendarGridHead>
<RangeCalendarGridRow>
<RangeCalendarHeadCell
v-for="day in weekDays"
:key="day"
>
{{ day }}
</RangeCalendarHeadCell>
</RangeCalendarGridRow>
</RangeCalendarGridHead>
<RangeCalendarGridBody>
<RangeCalendarGridRow
v-for="(
weekDates, index
) in secondMonth.rows"
:key="`weekDate-${index}`"
class="mt-2 w-full"
>
<RangeCalendarCell
v-for="weekDate in weekDates"
:key="weekDate.toString()"
:date="weekDate"
>
<RangeCalendarCellTrigger
:day="weekDate"
:month="secondMonth.value"
/>
</RangeCalendarCell>
</RangeCalendarGridRow>
</RangeCalendarGridBody>
</RangeCalendarGrid>
</div>
</div>
</RangeCalendarRoot>
</PopoverContent>
</Popover>
</template>