Skip to content

Commit

Permalink
feat(Calendar): implement component (#2618)
Browse files Browse the repository at this point in the history
Co-authored-by: Benjamin Canac <[email protected]>
  • Loading branch information
hywax and benjamincanac authored Nov 26, 2024
1 parent 86f2b48 commit 2e9aeb5
Show file tree
Hide file tree
Showing 40 changed files with 10,278 additions and 18 deletions.
2 changes: 2 additions & 0 deletions docs/app/components/content/ComponentCode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ const options = computed(() => {
chip: key.toLowerCase().endsWith('color') ? { color: variant } : undefined
}))

// TODO: process "undefined | Date | DateRange", https://github.com/nuxt/ui/issues/2651

return {
name: key,
label: key,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
const df = new DateFormatter('en-US', {
dateStyle: 'medium'
})
const modelValue = shallowRef(new CalendarDate(2022, 1, 10))
</script>

<template>
<UPopover>
<UButton color="neutral" variant="subtle" icon="i-lucide-calendar">
{{ df.format(modelValue.toDate(getLocalTimeZone())) }}
</UButton>

<template #content>
<UCalendar v-model="modelValue" class="p-2" />
</template>
</UPopover>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup lang="ts">
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
const df = new DateFormatter('en-US', {
dateStyle: 'medium'
})
const modelValue = shallowRef({
start: new CalendarDate(2022, 1, 20),
end: new CalendarDate(2022, 2, 10)
})
</script>

<template>
<UPopover>
<UButton color="neutral" variant="subtle" icon="i-lucide-calendar">
<template v-if="modelValue.start">
<template v-if="modelValue.end">
{{ df.format(modelValue.start.toDate(getLocalTimeZone())) }} - {{ df.format(modelValue.end.toDate(getLocalTimeZone())) }}
</template>

<template v-else>
{{ df.format(modelValue.start.toDate(getLocalTimeZone())) }}
</template>
</template>
<template v-else>
Pick a date
</template>
</UButton>

<template #content>
<UCalendar v-model="modelValue" class="p-2" :number-of-months="2" range />
</template>
</UPopover>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
import type { Matcher } from 'radix-vue/date'
const modelValue = shallowRef({
start: new CalendarDate(2022, 1, 1),
end: new CalendarDate(2022, 1, 9)
})
const isDateDisabled: Matcher = (date) => {
return date.day >= 10 && date.day <= 16
}
</script>

<template>
<UCalendar v-model="modelValue" :is-date-disabled="isDateDisabled" range />
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const modelValue = shallowRef(new CalendarDate(2022, 1, 10))
function getColorByDate(date: Date) {
const isWeekend = date.getDay() % 6 == 0
const isDayMeeting = date.getDay() % 3 == 0
if (isWeekend) {
return undefined
}
if (isDayMeeting) {
return 'error'
}
return 'success'
}
</script>

<template>
<UCalendar v-model="modelValue">
<template #day="{ day }">
<UChip :show="!!getColorByDate(day.toDate('UTC'))" :color="getColorByDate(day.toDate('UTC'))" size="2xs">
{{ day.day }}
</UChip>
</template>
</UCalendar>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const modelValue = shallowRef(new CalendarDate(2023, 9, 10))
const minDate = new CalendarDate(2023, 9, 1)
const maxDate = new CalendarDate(2023, 9, 30)
</script>

<template>
<UCalendar v-model="modelValue" :min-value="minDate" :max-value="maxDate" />
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
import type { Matcher } from 'radix-vue/date'
const modelValue = shallowRef({
start: new CalendarDate(2022, 1, 1),
end: new CalendarDate(2022, 1, 9)
})
const isDateUnavailable: Matcher = (date) => {
return date.day >= 10 && date.day <= 16
}
</script>

<template>
<UCalendar v-model="modelValue" :is-date-unavailable="isDateUnavailable" range />
</template>
210 changes: 210 additions & 0 deletions docs/content/3.components/calendar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
---
title: Calendar
description: A calendar component for selecting single dates, multiple dates or date ranges.
links:
- label: Calendar
icon: i-custom-radix-vue
to: https://www.radix-vue.com/components/calendar.html
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Calendar.vue
navigation.badge: New
---

::note
This component relies on the [@internationalized/date](https://react-spectrum.adobe.com/internationalized/date/index.html) package which provides objects and functions for representing and manipulating dates and times in a locale-aware manner.
::

## Usage

Use the `v-model` directive to control the selected date.

::component-code
---
---
::

<!-- TODO: Add example with default value -->

### Multiple

Use the `multiple` prop to allow multiple selections.

::component-code
---
ignore:
- multiple
props:
multiple: true
---
::

### Range

Use the `range` prop to select a range of dates.

::component-code
---
ignore:
- range
props:
range: true
---
::

### Color

Use the `color` prop to change the color of the calendar.

::component-code
---
props:
color: neutral
---
::

### Size

Use the `size` prop to change the size of the calendar.

::component-code
---
props:
size: xl
---
::

### Disabled

Use the `disabled` prop to disable the calendar.

::component-code
---
props:
disabled: true
---
::

### Number Of Months

Use the `numberOfMonths` prop to change the number of months in the calendar.

::component-code
---
props:
numberOfMonths: 3
---
::

### Month Controls

Use the `month-controls` prop to show the month controls. Defaults to `true`.

::component-code
---
props:
monthControls: false
---
::

### Year Controls

Use the `year-controls` prop to show the year controls. Defaults to `true`.

::component-code
---
props:
yearControls: false
---
::

### Fixed Weeks

Use the `fixed-weeks` prop to display the calendar with fixed weeks.

::component-code
---
props:
fixedWeeks: false
---
::

## Examples

### With chip events

Use the [Chip](/components/chip) component to add events to specific days.

::component-example
---
name: 'calendar-events-example'
---
::

### With disabled dates

Use the `is-date-disabled` prop with a function to mark specific dates as disabled.

::component-example
---
name: 'calendar-disabled-dates-example'
---
::

### With unavailable dates

Use the `is-date-unavailable` prop with a function to mark specific dates as unavailable.

::component-example
---
name: 'calendar-unavailable-dates-example'
---
::

### With min/max dates

Use the `min-value` and `max-value` props to limit the dates.

::component-example
---
name: 'calendar-min-max-dates-example'
---
::

### As a DatePicker

Use a [Button](/components/button) and a [Popover](/components/popover) component to create a date picker.

::component-example
---
name: 'calendar-date-picker-example'
---
::

### As a DateRangePicker

Use a [Button](/components/button) and a [Popover](/components/popover) component to create a date range picker.

::component-example
---
name: 'calendar-date-range-picker-example'
---
::

## API

### Props

:component-props

### Slots

:component-slots

### Emits

:component-emits

## Theme

:component-theme
2 changes: 1 addition & 1 deletion docs/content/3.components/input-menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ name: 'input-menu-filter-fields-example'
---
::

### As a country picker
### As a CountryPicker

This example demonstrates using the InputMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.

Expand Down
4 changes: 4 additions & 0 deletions docs/content/3.components/input-number.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ links:
navigation.badge: New
---

::note
This component relies on the [@internationalized/number](https://react-spectrum.adobe.com/internationalized/number/index.html) package which provides utilities for formatting and parsing numbers across locales and numbering systems.
::

## Usage

Use the `v-model` directive to control the value of the InputNumber.
Expand Down
2 changes: 1 addition & 1 deletion docs/content/3.components/select-menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,7 @@ name: 'select-menu-filter-fields-example'
---
::

### As a country picker
### As a CountryPicker

This example demonstrates using the SelectMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
},
"dependencies": {
"@iconify/vue": "^4.1.2",
"@internationalized/date": "^3.6.0",
"@internationalized/number": "^3.6.0",
"@nuxt/devtools-kit": "^1.6.1",
"@nuxt/fonts": "^0.10.2",
Expand Down
Loading

0 comments on commit 2e9aeb5

Please sign in to comment.