Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: Calendar component inherits the VCalendar slots #285

Merged
merged 6 commits into from
Jan 18, 2024
Merged
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
14 changes: 14 additions & 0 deletions apps/www/__registry__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,13 @@ export const Index = {
component: () => import('../src/lib/registry/default/example/RadioGroupForm.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/RadioGroupForm.vue'],
},
RangePickerWithSlot: {
name: 'RangePickerWithSlot',
type: 'components:example',
registryDependencies: ['utils', 'button', 'calendar', 'popover'],
component: () => import('../src/lib/registry/default/example/RangePickerWithSlot.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/RangePickerWithSlot.vue'],
},
ScrollAreaDemo: {
name: 'ScrollAreaDemo',
type: 'components:example',
Expand Down Expand Up @@ -1390,6 +1397,13 @@ export const Index = {
component: () => import('../src/lib/registry/new-york/example/RadioGroupForm.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/RadioGroupForm.vue'],
},
RangePickerWithSlot: {
name: 'RangePickerWithSlot',
type: 'components:example',
registryDependencies: ['utils', 'button', 'calendar', 'popover'],
component: () => import('../src/lib/registry/new-york/example/RangePickerWithSlot.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/RangePickerWithSlot.vue'],
},
ScrollAreaDemo: {
name: 'ScrollAreaDemo',
type: 'components:example',
Expand Down
37 changes: 36 additions & 1 deletion apps/www/src/content/docs/components/calendar.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,40 @@ import { Calendar } from '@/components/ui/calendar'
</template>
```

See the [VCalendar](https://vcalendar.io/getting-started/installation.html) documentation for more information.
The API is essentially the same, i.e. props and slots. See the [VCalendar](https://vcalendar.io/getting-started/installation.html) documentation for more information.

### Slots

The slots available are [those currently supported](https://github.com/nathanreyes/v-calendar/blob/v3.1.2/src/components/Calendar/CalendarSlot.vue#L16-L28) by VCalendar, namely :

- `day-content`
- `day-popover`
- `dp-footer`
- `footer`
- `header-title-wrapper`
- `header-title`
- `header-prev-button`
- `header-next-button`
- `nav`
- `nav-prev-button`
- `nav-next-button`
- `page`
- `time-header`

Example using the `day-content` slot:

```vue
<script setup lang="ts">
import { Calendar } from '@/components/ui/calendar'
</script>

<template>
<Calendar>
<template #day-content="{ day, dayProps, dayEvents }">
<div v-bind="dayProps" v-on="dayEvents">
{{ day.label }}
</div>
</template>
</Calendar>
</template>
```
4 changes: 4 additions & 0 deletions apps/www/src/content/docs/components/date-picker.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ const date = ref<Date>()

<ComponentPreview name="DatePickerWithPresets" />

### With Slot

<ComponentPreview name="RangePickerWithSlot" />

### Form

<ComponentPreview name="DatePickerForm" />
69 changes: 69 additions & 0 deletions apps/www/src/lib/registry/default/example/RangePickerWithSlot.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<script setup lang="ts">
import { addDays, format } from 'date-fns'
import { Calendar as CalendarIcon } from 'lucide-vue-next'

import { ref } from 'vue'
import { cn } from '@/lib/utils'
import { Button } from '@/lib/registry/default/ui/button'
import { Calendar } from '@/lib/registry/default/ui/calendar'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/lib/registry/default/ui/popover'

const date = ref({
start: new Date(2022, 0, 20),
end: addDays(new Date(2022, 0, 20), 20),
})
</script>

<template>
<div :class="cn('grid gap-2', $attrs.class ?? '')">
<Popover>
<PopoverTrigger as-child>
<Button
id="date"
:variant="'outline'"
:class="cn(
'w-[300px] justify-start text-left font-normal',
!date && 'text-muted-foreground',
)"
>
<CalendarIcon class="mr-2 h-4 w-4" />

<span>
{{ date.start ? (
date.end ? `${format(date.start, 'LLL dd, y')} - ${format(date.end, 'LLL dd, y')}`
: format(date.start, 'LLL dd, y')
) : 'Pick a date' }}
</span>
</Button>
</PopoverTrigger>
<PopoverContent class="w-auto p-0" align="start" :avoid-collisions="true">
<Calendar
v-model.range="date"
mode="date"
:columns="2"
>
<template #footer>
<div class="w-full px-3 pb-3">
Entry time
<Calendar
v-model="date.start"
mode="time"
hide-time-header
/>
Exit time
<Calendar
v-model="date.end"
mode="time"
hide-time-header
/>
</div>
</template>
</Calendar>
</PopoverContent>
</Popover>
</div>
</template>
19 changes: 17 additions & 2 deletions apps/www/src/lib/registry/default/ui/calendar/Calendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { useVModel } from '@vueuse/core'
import type { Calendar } from 'v-calendar'
import { DatePicker } from 'v-calendar'
import { ChevronLeft, ChevronRight } from 'lucide-vue-next'
import { computed, nextTick, onMounted, ref } from 'vue'
import { computed, nextTick, onMounted, ref, useSlots } from 'vue'
import { isVCalendarSlot } from '.'
import { buttonVariants } from '@/lib/registry/default/ui/button'
import { cn } from '@/lib/utils'

Expand Down Expand Up @@ -64,6 +65,16 @@ onMounted(async () => {
if (modelValue.value instanceof Date && calendarRef.value)
calendarRef.value.focusDate(modelValue.value)
})

const $slots = useSlots()
const vCalendarSlots = computed(() => {
return Object.keys($slots)
.filter(name => isVCalendarSlot(name))
.reduce((obj: Record<string, any>, key: string) => {
obj[key] = $slots[key]
return obj
}, {})
})
</script>

<template>
Expand All @@ -86,7 +97,11 @@ onMounted(async () => {
trim-weeks
:transition="'none'"
:columns="columns"
/>
>
<template v-for="(_, slot) of vCalendarSlots" #[slot]="scope">
<slot :name="slot" v-bind="scope" />
</template>
</DatePicker>
</div>
</template>

Expand Down
21 changes: 21 additions & 0 deletions apps/www/src/lib/registry/default/ui/calendar/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
export { default as Calendar } from './Calendar.vue'
import type { CalendarSlotName } from 'v-calendar/dist/types/src/components/Calendar/CalendarSlot.vue.d.ts'

export function isVCalendarSlot(slotName: string): slotName is CalendarSlotName {
const validSlots: CalendarSlotName[] = [
'day-content',
'day-popover',
'dp-footer',
'footer',
'header-title-wrapper',
'header-title',
'header-prev-button',
'header-next-button',
'nav',
'nav-prev-button',
'nav-next-button',
'page',
'time-header',
]

return validSlots.includes(slotName as CalendarSlotName)
}
73 changes: 73 additions & 0 deletions apps/www/src/lib/registry/new-york/example/RangePickerWithSlot.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script setup lang="ts">
import { addDays, format } from 'date-fns'
import { ref } from 'vue'
import { CalendarIcon } from '@radix-icons/vue'

import { cn } from '@/lib/utils'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Calendar } from '@/lib/registry/new-york/ui/calendar'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/lib/registry/new-york/ui/popover'

const date = ref({
start: new Date(2022, 0, 20),
end: addDays(new Date(2022, 0, 20), 20),
})
</script>

<template>
<div :class="cn('grid gap-2', $attrs.class ?? '')">
<Popover>
<PopoverTrigger as-child>
<Button
id="date"
:variant="'outline'"
:class="cn(
'w-[300px] justify-start text-left font-normal',
!date && 'text-muted-foreground',
)"
>
<CalendarIcon class="mr-2 h-4 w-4" />

<span>
{{ date.start ? (
date.end ? `${format(date.start, 'LLL dd, y')} - ${format(date.end, 'LLL dd, y')}`
: format(date.start, 'LLL dd, y')
) : 'Pick a date' }}
</span>
</Button>
</PopoverTrigger>
<PopoverContent class="w-auto p-0" align="start" :avoid-collisions="true">
<Calendar
v-model.range="date"
mode="date"
:columns="2"
>
<template #footer>
<div class="flex w-full mt-6 border-t border-accent pt-4">
<div class="w-1/2">
<strong>Entry time</strong>
<Calendar
v-model="date.start"
mode="time"
hide-time-header
/>
</div>
<div class="w-1/2">
<strong>Exit time</strong>
<Calendar
v-model="date.end"
mode="time"
hide-time-header
/>
</div>
</div>
</template>
</Calendar>
</PopoverContent>
</Popover>
</div>
</template>
19 changes: 17 additions & 2 deletions apps/www/src/lib/registry/new-york/ui/calendar/Calendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { useVModel } from '@vueuse/core'
import type { Calendar } from 'v-calendar'
import { DatePicker } from 'v-calendar'
import { ChevronLeftIcon, ChevronRightIcon } from '@radix-icons/vue'
import { computed, nextTick, onMounted, ref } from 'vue'
import { computed, nextTick, onMounted, ref, useSlots } from 'vue'
import { isVCalendarSlot } from '.'
import { buttonVariants } from '@/lib/registry/new-york/ui/button'
import { cn } from '@/lib/utils'

Expand Down Expand Up @@ -63,6 +64,16 @@ onMounted(async () => {
if (modelValue.value instanceof Date && calendarRef.value)
calendarRef.value.focusDate(modelValue.value)
})

const $slots = useSlots()
const vCalendarSlots = computed(() => {
return Object.keys($slots)
.filter(name => isVCalendarSlot(name))
.reduce((obj: Record<string, any>, key: string) => {
obj[key] = $slots[key]
return obj
}, {})
})
</script>

<template>
Expand All @@ -85,7 +96,11 @@ onMounted(async () => {
trim-weeks
:transition="'none'"
:columns="columns"
/>
>
<template v-for="(_, slot) of vCalendarSlots" #[slot]="scope">
<slot :name="slot" v-bind="scope" />
</template>
</DatePicker>
</div>
</template>

Expand Down
21 changes: 21 additions & 0 deletions apps/www/src/lib/registry/new-york/ui/calendar/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
export { default as Calendar } from './Calendar.vue'
import type { CalendarSlotName } from 'v-calendar/dist/types/src/components/Calendar/CalendarSlot.vue.d.ts'

export function isVCalendarSlot(slotName: string): slotName is CalendarSlotName {
const validSlots: CalendarSlotName[] = [
'day-content',
'day-popover',
'dp-footer',
'footer',
'header-title-wrapper',
'header-title',
'header-prev-button',
'header-next-button',
'nav',
'nav-prev-button',
'nav-next-button',
'page',
'time-header',
]

return validSlots.includes(slotName as CalendarSlotName)
}
4 changes: 2 additions & 2 deletions apps/www/src/public/registry/styles/default/calendar.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions apps/www/src/public/registry/styles/new-york/calendar.json

Large diffs are not rendered by default.