日历
AlphaS | M | T | W | T | F | S |
---|---|---|---|---|---|---|
29 | 30 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 |
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { CalendarCell, CalendarCellTrigger, CalendarGrid, CalendarGridBody, CalendarGridHead, CalendarGridRow, CalendarHeadCell, CalendarHeader, CalendarHeading, CalendarNext, CalendarPrev, CalendarRoot, type CalendarRootProps } from 'radix-vue'
const isDateUnavailable: CalendarRootProps['isDateUnavailable'] = (date) => {
return date.day === 17 || date.day === 18
}
</script>
<template>
<CalendarRoot
v-slot="{ weekDays, grid }"
:is-date-unavailable="isDateUnavailable"
class="mt-6 rounded-xl bg-white p-4 shadow-md"
fixed-weeks
>
<CalendarHeader class="flex items-center justify-between">
<CalendarPrev
class="inline-flex items-center cursor-pointer text-black justify-center rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
>
<Icon
icon="radix-icons:chevron-left"
class="w-6 h-6"
/>
</CalendarPrev>
<CalendarHeading class="text-[15px] text-black font-medium" />
<CalendarNext
class="inline-flex items-center cursor-pointer justify-center text-black rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
>
<Icon
icon="radix-icons:chevron-right"
class="w-6 h-6"
/>
</CalendarNext>
</CalendarHeader>
<div
class="flex flex-col space-y-4 pt-4 sm:flex-row sm:space-x-4 sm:space-y-0"
>
<CalendarGrid
v-for="month in grid"
:key="month.value.toString()"
class="w-full border-collapse select-none space-y-1"
>
<CalendarGridHead>
<CalendarGridRow class="mb-1 grid w-full grid-cols-7">
<CalendarHeadCell
v-for="day in weekDays"
:key="day"
class="rounded-md text-xs text-green8"
>
{{ day }}
</CalendarHeadCell>
</CalendarGridRow>
</CalendarGridHead>
<CalendarGridBody class="grid">
<CalendarGridRow
v-for="(weekDates, index) in month.rows"
:key="`weekDate-${index}`"
class="grid grid-cols-7"
>
<CalendarCell
v-for="weekDate in weekDates"
:key="weekDate.toString()"
:date="weekDate"
class="relative text-center text-sm"
>
<CalendarCellTrigger
:day="weekDate"
:month="month.value"
class="relative flex items-center justify-center rounded-full whitespace-nowrap text-sm font-normal text-black w-8 h-8 outline-none focus:shadow-[0_0_0_2px] focus:shadow-black data-[disabled]:text-black/30 data-[selected]:!bg-green10 data-[selected]:text-white hover:bg-green5 data-[highlighted]:bg-green5 data-[unavailable]:pointer-events-none data-[unavailable]:text-black/30 data-[unavailable]:line-through before:absolute before:top-[5px] before:hidden before:rounded-full before:w-1 before:h-1 before:bg-white data-[today]:before:block data-[today]:before:bg-green9 "
/>
</CalendarCell>
</CalendarGridRow>
</CalendarGridBody>
</CalendarGrid>
</div>
</CalendarRoot>
</template>
致谢
此组件的构建灵感来自 melt-ui 中的实现。
功能
- 完整的键盘导航
- 可以是受控或不受控的
- 焦点完全管理
- 本地化支持
- 高度可组合
前言
该组件依赖于 @internationalized/date 包,该包解决了 JavaScript 中处理日期和时间所带来的许多问题。
我们强烈建议您通读该包的文档,以深入了解其工作原理,并且您需要在项目中安装它才能使用日期相关的组件。
安装
安装日期包。
$ npm add @internationalized/date
从命令行安装组件。
$ npm add radix-vue
解剖
导入所有部分并将它们拼凑在一起。
<script setup>
import {
CalendarCell,
CalendarCellTrigger,
CalendarGrid,
CalendarGridBody,
CalendarGridHead,
CalendarGridRow,
CalendarHeadCell,
CalendarHeader,
CalendarHeading,
CalendarNext,
CalendarPrev,
CalendarRoot
} from 'radix-vue'
</script>
<template>
<CalendarRoot>
<CalendarHeader>
<CalendarPrev />
<CalendarHeading />
<CalendarNext />
</CalendarHeader>
<CalendarGrid>
<CalendarGridHead>
<CalendarGridRow>
<CalendarHeadCell />
</CalendarGridRow>
</CalendarGridHead>
<CalendarGridBody>
<CalendarGridRow>
<CalendarCell>
<CalendarCellTrigger />
</CalendarCell>
</CalendarGridRow>
</CalendarGridBody>
</CalendarGrid>
</CalendarRoot>
</template>
API 参考
根
包含日历的所有部分
道具 | 默认值 | 类型 |
---|---|---|
as | 'div' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 | |
calendarLabel | 字符串 日历的可访问标签 | |
defaultPlaceholder | DateValue 默认占位符日期 | |
defaultValue | DateValue 日历的默认值 | |
dir | 'ltr' | 'rtl' 日历在适用时的阅读方向。 | |
disabled | false | 布尔值 日历是否禁用 |
fixedWeeks | false | 布尔值 是否始终在日历中显示 6 周 |
initialFocus | false | 布尔值 如果为 true,则日历将根据日历挂载时可见的内容,将焦点设置在选定的日期、今天或当月的第一天 |
isDateDisabled | 匹配器 一个函数,返回日期是否禁用 | |
isDateUnavailable | 匹配器 一个函数,返回日期是否不可用 | |
locale | 'en' | 字符串 用于格式化日期的区域设置 |
maxValue | DateValue 可以选择的最大日期 | |
minValue | DateValue 可以选择的最早日期 | |
modelValue | DateValue | DateValue[] 日历的受控选中状态。 可以绑定为 | |
multiple | false | 布尔值 是否可以选择多个日期 |
nextPage | ((placeholder: DateValue) => DateValue) 一个函数,返回日历的下一页。 它接收组件内部的当前占位符作为参数。 | |
numberOfMonths | 1 | 数字 一次显示的月份数 |
pagedNavigation | false | 布尔值 此属性会导致上一个和下一个按钮按一次显示的月份数进行导航,而不是一个月 |
placeholder | DateValue 占位符日期,用于确定在未选择任何日期时显示哪个月份。 当用户在日历中导航时,它会更新,并且可以用于以编程方式控制日历视图 | |
preventDeselect | false | 布尔值 是否阻止用户在不先选择其他日期的情况下取消选择日期 |
prevPage | ((placeholder: DateValue) => DateValue) 一个函数,返回日历的上一页。 它接收组件内部的当前占位符作为参数。 | |
readonly | false | 布尔值 日历是否只读 |
weekdayFormat | 'narrow' | 'narrow' | 'short' | 'long' 用于通过 weekdays 插槽道具提供的星期几字符串的格式 |
weekStartsOn | 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 一周开始的星期几 |
发射 | 有效载荷 |
---|---|
update:modelValue | [date: DateValue] 每当模型值更改时调用的事件处理程序 |
update:placeholder | [date: DateValue] 每当占位符值更改时调用的事件处理程序 |
插槽 (默认) | 有效载荷 |
---|---|
date | DateValue 占位符的当前日期 |
grid | Grid<DateValue>[] 日期网格 |
weekDays | string[] 一周中的几天 |
weekStartsOn | 0 | 1 | 2 | 3 | 4 | 5 | 6 一周的开始 |
locale | 字符串 日历区域设置 |
fixedWeeks | 布尔值 是否始终在日历中显示 6 周 |
数据属性 | 价值 |
---|---|
[data-readonly] | 只读时出现 |
[data-disabled] | 禁用时出现 |
[data-invalid] | 无效时出现 |
标题
包含导航按钮和标题段。
道具 | 默认值 | 类型 |
---|---|---|
as | 'div' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 |
上一个按钮
日历导航按钮。 它根据当前日历视图,将日历向过去导航一个月/一年/十年。
道具 | 默认值 | 类型 |
---|---|---|
as | 'button' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 | |
prevPage | ((placeholder: DateValue) => DateValue) 用于上一页的函数。 覆盖在 | |
step | 'month' | 'month' | 'year' 要返回的日历单位 |
数据属性 | 价值 |
---|---|
[data-disabled] | 禁用时出现 |
下一个按钮
日历导航按钮。 它根据当前日历视图,将日历向前导航一个月/一年/十年。
道具 | 默认值 | 类型 |
---|---|---|
as | 'button' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 | |
nextPage | ((placeholder: DateValue) => DateValue) 用于下一页的函数。 覆盖在 | |
step | 'month' | 'month' | 'year' 要向前回退的日历单位 |
数据属性 | 价值 |
---|---|
[data-disabled] | 禁用时出现 |
标题
用于显示当前月份和年份的标题
道具 | 默认值 | 类型 |
---|---|---|
as | 'div' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 |
插槽 (默认) | 有效载荷 |
---|---|
headingValue | 字符串 当前月份和年份 |
数据属性 | 价值 |
---|---|
[data-disabled] | 禁用时出现 |
网格
用于包装日历网格的容器。
道具 | 默认值 | 类型 |
---|---|---|
as | 'table' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 |
数据属性 | 价值 |
---|---|
[data-readonly] | 只读时出现 |
[data-disabled] | 禁用时出现 |
网格头
用于包装网格头的容器。
道具 | 默认值 | 类型 |
---|---|---|
as | 'thead' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 |
网格主体
用于包装网格主体的容器。
道具 | 默认值 | 类型 |
---|---|---|
as | 'tbody' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 |
网格行
用于包装网格行的容器。
道具 | 默认值 | 类型 |
---|---|---|
as | 'tr' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 |
头部单元格
用于包装头部单元格的容器。 用于显示星期几。
道具 | 默认值 | 类型 |
---|---|---|
as | 'th' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 |
单元格
用于包装日历单元格的容器。
道具 | 默认值 | 类型 |
---|---|---|
as | 'td' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 | |
date* | DateValue 单元格的日期值 |
数据属性 | 价值 |
---|---|
[data-disabled] | 禁用时出现 |
单元格触发器
用于显示单元格日期的可交互容器。 点击它选择日期。
道具 | 默认值 | 类型 |
---|---|---|
as | 'div' | AsTag | 组件 此组件应渲染成的元素或组件。 可以被 |
asChild | 布尔值 更改传递为子元素的默认渲染元素,合并其属性和行为。 阅读我们的 组合 指南了解更多详细信息。 | |
day* | DateValue 提供给单元格触发器的日期值 | |
month* | DateValue 渲染单元格的月份 |
插槽 (默认) | 有效载荷 |
---|---|
dayValue | 字符串 当前日期 |
数据属性 | 价值 |
---|---|
[data-selected] | 选中时出现 |
[data-value] | 日期的 ISO 字符串值。 |
[data-disabled] | 禁用时出现 |
[data-unavailable] | 不可用时出现 |
[data-today] | 今天出现 |
[data-outside-view] | 日期不在当前显示月份时出现。 |
[data-outside-visible-view] | 日期不在日历上可见的月份时出现。 |
[data-focused] | 聚焦时出现 |
例子
带有年份递增的日历
此示例展示了一个允许递增年份的日历。
S | M | T | W | T | F | S |
---|---|---|---|---|---|---|
29 | 30 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 |
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import type { DateValue } from '@internationalized/date'
import { CalendarCell, CalendarCellTrigger, CalendarGrid, CalendarGridBody, CalendarGridHead, CalendarGridRow, CalendarHeadCell, CalendarHeader, CalendarHeading, CalendarNext, CalendarPrev, CalendarRoot, type CalendarRootProps } from 'radix-vue'
const isDateUnavailable: CalendarRootProps['isDateUnavailable'] = (date) => {
return date.day === 17 || date.day === 18
}
function pagingFunc(date: DateValue, sign: -1 | 1) {
if (sign === -1)
return date.subtract({ years: 1 })
return date.add({ years: 1 })
}
</script>
<template>
<CalendarRoot
v-slot="{ weekDays, grid }"
:is-date-unavailable="isDateUnavailable"
class="mt-6 rounded-xl bg-white p-4 shadow-md"
fixed-weeks
>
<CalendarHeader class="flex items-center justify-between">
<CalendarPrev
class="inline-flex items-center cursor-pointer text-black justify-center rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
:prev-page="(date: DateValue) => pagingFunc(date, -1)"
>
<Icon
icon="radix-icons:double-arrow-left"
class="w-6 h-6"
/>
</CalendarPrev>
<CalendarPrev
class="inline-flex items-center cursor-pointer text-black justify-center rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
>
<Icon
icon="radix-icons:chevron-left"
class="w-6 h-6"
/>
</CalendarPrev>
<CalendarHeading class="text-[15px] text-black font-medium" />
<CalendarNext
class="inline-flex items-center cursor-pointer justify-center text-black rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
>
<Icon
icon="radix-icons:chevron-right"
class="w-6 h-6"
/>
</CalendarNext>
<CalendarNext
class="inline-flex items-center cursor-pointer justify-center text-black rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
:next-page="(date: DateValue) => pagingFunc(date, 1)"
>
<Icon
icon="radix-icons:double-arrow-right"
class="w-6 h-6"
/>
</CalendarNext>
</CalendarHeader>
<div
class="flex flex-col space-y-4 pt-4 sm:flex-row sm:space-x-4 sm:space-y-0"
>
<CalendarGrid
v-for="month in grid"
:key="month.value.toString()"
class="w-full border-collapse select-none space-y-1"
>
<CalendarGridHead>
<CalendarGridRow class="mb-1 grid w-full grid-cols-7">
<CalendarHeadCell
v-for="day in weekDays"
:key="day"
class="rounded-md text-xs text-green8"
>
{{ day }}
</CalendarHeadCell>
</CalendarGridRow>
</CalendarGridHead>
<CalendarGridBody class="grid">
<CalendarGridRow
v-for="(weekDates, index) in month.rows"
:key="`weekDate-${index}`"
class="grid grid-cols-7"
>
<CalendarCell
v-for="weekDate in weekDates"
:key="weekDate.toString()"
:date="weekDate"
class="relative text-center text-sm"
>
<CalendarCellTrigger
:day="weekDate"
:month="month.value"
class="relative flex items-center justify-center rounded-full whitespace-nowrap text-sm font-normal text-black w-8 h-8 outline-none focus:shadow-[0_0_0_2px] focus:shadow-black data-[disabled]:text-black/30 data-[selected]:!bg-green10 data-[selected]:text-white hover:bg-green5 data-[highlighted]:bg-green5 data-[unavailable]:pointer-events-none data-[unavailable]:text-black/30 data-[unavailable]:line-through before:absolute before:top-[5px] before:hidden before:rounded-full before:w-1 before:h-1 before:bg-white data-[today]:before:block data-[today]:before:bg-green9 "
/>
</CalendarCell>
</CalendarGridRow>
</CalendarGridBody>
</CalendarGrid>
</div>
</CalendarRoot>
</template>
带有区域设置和日历系统选择的日历
此示例展示了一些可用的区域设置以及日历系统的显示方式。
S | M | T | W | T | F | S |
---|---|---|---|---|---|---|
29 | 30 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 |
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { createCalendar, getLocalTimeZone, toCalendar, today } from '@internationalized/date'
import { CalendarCell, CalendarCellTrigger, CalendarGrid, CalendarGridBody, CalendarGridHead, CalendarGridRow, CalendarHeadCell, CalendarHeader, CalendarHeading, CalendarNext, CalendarPrev, CalendarRoot, Label, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectPortal, SelectRoot, SelectScrollUpButton, SelectTrigger, SelectValue, SelectViewport } from 'radix-vue'
import { computed, ref } from 'vue'
const preferences = [
{ locale: 'en-US', label: 'Default', ordering: 'gregory' },
{ label: 'Arabic (Algeria)', locale: 'ar-DZ', territories: 'DJ DZ EH ER IQ JO KM LB LY MA MR OM PS SD SY TD TN YE', ordering: 'gregory islamic islamic-civil islamic-tbla' },
{ label: 'Arabic (United Arab Emirates)', locale: 'ar-AE', territories: 'AE BH KW QA', ordering: 'gregory islamic-umalqura islamic islamic-civil islamic-tbla' },
{ label: 'Arabic (Egypt)', locale: 'AR-EG', territories: 'EG', ordering: 'gregory coptic islamic islamic-civil islamic-tbla' },
{ label: 'Arabic (Saudi Arabia)', locale: 'ar-SA', territories: 'SA', ordering: 'islamic-umalqura gregory islamic islamic-rgsa' },
{ label: 'Farsi (Afghanistan)', locale: 'fa-AF', territories: 'AF IR', ordering: 'persian gregory islamic islamic-civil islamic-tbla' },
{ label: 'Amharic (Ethiopia)', locale: 'am-ET', territories: 'ET', ordering: 'gregory ethiopic ethioaa' },
{ label: 'Hebrew (Israel)', locale: 'he-IL', territories: 'IL', ordering: 'gregory hebrew islamic islamic-civil islamic-tbla' },
{ label: 'Hindi (India)', locale: 'hi-IN', territories: 'IN', ordering: 'gregory indian' },
{ label: 'Japanese (Japan)', locale: 'ja-JP', territories: 'JP', ordering: 'gregory japanese' },
{ label: 'Thai (Thailand)', locale: 'th-TH', territories: 'TH', ordering: 'buddhist gregory' },
{ label: 'Chinese (Taiwan)', locale: 'zh-TW', territories: 'TW', ordering: 'gregory roc chinese' },
]
const calendars = [
{ key: 'gregory', name: 'Gregorian' },
{ key: 'japanese', name: 'Japanese' },
{ key: 'buddhist', name: 'Buddhist' },
{ key: 'roc', name: 'Taiwan' },
{ key: 'persian', name: 'Persian' },
{ key: 'indian', name: 'Indian' },
{ key: 'islamic-umalqura', name: 'Islamic (Umm al-Qura)' },
{ key: 'islamic-civil', name: 'Islamic Civil' },
{ key: 'islamic-tbla', name: 'Islamic Tabular' },
{ key: 'hebrew', name: 'Hebrew' },
{ key: 'coptic', name: 'Coptic' },
{ key: 'ethiopic', name: 'Ethiopic' },
{ key: 'ethioaa', name: 'Ethiopic (Amete Alem)' },
]
const locale = ref(preferences[0].locale)
const calendar = ref(calendars[0].key)
const pref = computed(() => preferences.find(p => p.locale === locale.value))
const preferredCalendars = computed(() => pref.value ? pref.value.ordering.split(' ').map(p => calendars.find(c => c.key === p)).filter(Boolean) : [calendars[0]])
const otherCalendars = computed(() => calendars.filter(c => !preferredCalendars.value.some(p => p!.key === c.key)))
function updateLocale(newLocale: string) {
locale.value = newLocale
calendar.value = pref.value!.ordering.split(' ')[0]
}
const value = computed(() => toCalendar(today(getLocalTimeZone()), createCalendar(calendar.value)))
</script>
<template>
<div class="flex flex-col gap-4">
<Label class="text-white">Locale</Label>
<SelectRoot
v-model="locale"
@update:model-value="updateLocale"
>
<SelectTrigger
class="inline-flex min-w-[160px] items-center justify-between rounded px-[15px] text-[13px] leading-none h-[35px] gap-[5px] bg-white text-grass11 shadow-[0_2px_10px] shadow-black/10 hover:bg-mauve3 focus:shadow-[0_0_0_2px] focus:shadow-black data-[placeholder]:text-green9 outline-none"
aria-label="Select a locale"
>
<SelectValue placeholder="Please select a locale">
{{ pref!.label }}
</SelectValue>
<Icon
icon="radix-icons:chevron-down"
class="h-3.5 w-3.5"
/>
</SelectTrigger>
<SelectPortal>
<SelectContent
class="min-w-[160px] bg-white rounded shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade z-[100]"
:side-offset="5"
>
<SelectScrollUpButton class="flex items-center justify-center h-[25px] bg-white text-violet11 cursor-default">
<Icon icon="radix-icons:chevron-up" />
</SelectScrollUpButton>
<SelectViewport class="p-[5px]">
<SelectItem
v-for="(option, index) in preferences"
:key="index"
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] pr-[35px] pl-[25px] relative select-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
:value="option.locale"
>
<SelectItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:check" />
</SelectItemIndicator>
<SelectItemText>
{{ option.label }}
</SelectItemText>
</SelectItem>
</SelectViewport>
<SelectScrollDownButton class="flex items-center justify-center h-[25px] bg-white text-violet11 cursor-default">
<Icon icon="radix-icons:chevron-down" />
</SelectScrollDownButton>
</SelectContent>
</SelectPortal>
</SelectRoot>
<Label class="text-white">Calendar</Label>
<SelectRoot v-model="calendar">
<SelectTrigger
class="inline-flex min-w-[160px] items-center justify-between rounded px-[15px] text-[13px] leading-none h-[35px] gap-[5px] bg-white text-grass11 shadow-[0_2px_10px] shadow-black/10 hover:bg-mauve3 focus:shadow-[0_0_0_2px] focus:shadow-black data-[placeholder]:text-green9 outline-none"
aria-label="Select a calendar"
>
<SelectValue placeholder="Please select a calendar">
{{ calendars.find(c => c.key === calendar)?.name }}
</SelectValue>
<Icon
icon="radix-icons:chevron-down"
class="h-3.5 w-3.5"
/>
</SelectTrigger>
<SelectPortal>
<SelectContent
class="min-w-[160px] bg-white rounded shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade z-[100]"
:side-offset="5"
>
<SelectScrollUpButton class="flex items-center justify-center h-[25px] bg-white text-violet11 cursor-default">
<Icon icon="radix-icons:chevron-up" />
</SelectScrollUpButton>
<SelectViewport class="p-[5px]">
<SelectLabel class="px-[25px] text-xs leading-[25px] text-mauve11">
Preferred
</SelectLabel>
<SelectGroup>
<SelectItem
v-for="(option, index) in preferredCalendars"
:key="index"
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] pr-[35px] pl-[25px] relative select-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
:value="option!.key"
>
<SelectItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:check" />
</SelectItemIndicator>
<SelectItemText>
{{ option!.name }}
</SelectItemText>
</SelectItem>
</SelectGroup>
<SelectSeparator class="h-[1px] bg-green6 m-[5px]" />
<SelectLabel class="px-[25px] text-xs leading-[25px] text-mauve11">
Other
</SelectLabel>
<SelectGroup>
<SelectItem
v-for="(option, index) in otherCalendars"
:key="index"
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] pr-[35px] pl-[25px] relative select-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
:value="option.key"
>
<SelectItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:check" />
</SelectItemIndicator>
<SelectItemText>
{{ option.name }}
</SelectItemText>
</SelectItem>
</SelectGroup>
</SelectViewport>
<SelectScrollDownButton class="flex items-center justify-center h-[25px] bg-white text-violet11 cursor-default">
<Icon icon="radix-icons:chevron-down" />
</SelectScrollDownButton>
</SelectContent>
</SelectPortal>
</SelectRoot>
<CalendarRoot
v-slot="{ weekDays, grid }"
:model-value="value"
:locale="locale"
class="mt-6 rounded-xl bg-white p-4 shadow-md"
fixed-weeks
>
<CalendarHeader class="flex items-center justify-between">
<CalendarPrev
class="inline-flex items-center cursor-pointer text-black justify-center rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
>
<Icon
icon="radix-icons:chevron-left"
class="w-6 h-6"
/>
</CalendarPrev>
<CalendarHeading class="text-[15px] text-black font-medium" />
<CalendarNext
class="inline-flex items-center cursor-pointer justify-center text-black rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
>
<Icon
icon="radix-icons:chevron-right"
class="w-6 h-6"
/>
</CalendarNext>
</CalendarHeader>
<div
class="flex flex-col space-y-4 pt-4 sm:flex-row sm:space-x-4 sm:space-y-0"
>
<CalendarGrid
v-for="month in grid"
:key="month.value.toString()"
class="w-full border-collapse select-none space-y-1"
>
<CalendarGridHead>
<CalendarGridRow class="mb-1 grid w-full grid-cols-7">
<CalendarHeadCell
v-for="day in weekDays"
:key="day"
class="rounded-md text-xs text-green8"
>
{{ day }}
</CalendarHeadCell>
</CalendarGridRow>
</CalendarGridHead>
<CalendarGridBody class="grid">
<CalendarGridRow
v-for="(weekDates, index) in month.rows"
:key="`weekDate-${index}`"
class="grid grid-cols-7"
>
<CalendarCell
v-for="weekDate in weekDates"
:key="weekDate.toString()"
:date="weekDate"
class="relative text-center text-sm"
>
<CalendarCellTrigger
:day="weekDate"
:month="month.value"
class="relative flex items-center justify-center rounded-full whitespace-nowrap text-sm font-normal text-black w-8 h-8 outline-none focus:shadow-[0_0_0_2px] focus:shadow-black data-[disabled]:text-black/30 data-[selected]:!bg-green10 data-[selected]:text-white hover:bg-green5 data-[highlighted]:bg-green5 data-[unavailable]:pointer-events-none data-[unavailable]:text-black/30 data-[unavailable]:line-through before:absolute before:top-[5px] before:hidden before:rounded-full before:w-1 before:h-1 before:bg-white data-[today]:before:block data-[today]:before:bg-green9 "
/>
</CalendarCell>
</CalendarGridRow>
</CalendarGridBody>
</CalendarGrid>
</div>
</CalendarRoot>
</div>
</template>
无障碍性
键盘交互
键 | 描述 |
---|---|
Tab | 当焦点移动到日历时,会将焦点移至第一个导航按钮。 |
空格 |
当焦点位于 CalendarNext 或 CalendarPrev 上时,它会导航日历。否则,它会选择日期。
|
Enter |
当焦点位于 CalendarNext 或 CalendarPrev 上时,它会导航日历。否则,它会选择日期。
|
向左箭头向右箭头向上箭头向下箭头 | 当焦点位于 CalendarCellTrigger 上时,它会导航日期,必要时更改月份/年份/十年。 |