跳到正文
GitHub

表单

DatePicker

一个日期——或者一段日期——从日历里选。

什么时候用它

刻意不做成“输入框加日历”:解析手打的日期需要格式,而 03/04 在一个国家是 3 月 4 日,在下一个国家是 4 月 3 日。日期很久远时,日历的月份和年份是下拉。

示例

default

Month and year are dropdowns — reaching two years back is one click, not twenty-four.

range

Last 30 days and its neighbours are one click; the grid is for everything else.

presets

Shortcuts are computed when clicked, so “today” means today even on a tab left open overnight.

说明

一个日期,从日历里选。

一个触发器加一个放在 Popover 里的 Calendar——与其说是个新组件,不如说是大家本来就会在每个界面上各拼一遍、还都拼得略有不同的那个组合。

刻意不是“输入框加一个日历”。手打的日期需要解析,解析需要格式,而格式是一场没人赢得了的地区之争。当打字确实要紧的时候——比如很久以前的生日——日历的月份和年份是下拉框,那是同一条路径,但没有歧义。

属性

DatePicker props
属性类型默认值说明
label必填string给控件命名。必填——触发器上的文字是值,不是名字。
classNamestring
defaultValueDate
disabledbooleanfalse
disabledDatesComponentProps<typeof Calendar>['disabled']读者不能选的日期。原样传给日历。
format(date: Date) => stringformatDate选中的日期在触发器上怎么打印。
onValueChange(value: Date | undefined) => void
placeholderstring'Pick a date'
presetsboolean | DatePreset<Date>[]显示在日历格旁边的快捷项。传 `true` 用内置的那一套,传数组用你自己的,不传就没有。
valueDate

组成部分

Composed at the call site rather than configured through props, so a layout this component did not anticipate is still expressible.

DateRangePicker

一段日期——一次入住、一个报表周期、一个筛选条件。

两个月并排,因为跨月的范围才是常见情况,而为了看清两端来回翻页正是范围选择器让人累的地方。在 sm 以下它们会堆叠,因为那里放不下两个——日历自己的 months 类已经带了这个规则,所以这里没什么要覆盖的。

面板会一直开着,直到两端都选完:一段范围在有第二个日期之前不算一个值,选完第一个就关掉意味着还得再打开一次才能选完。

DateRangePicker props
属性类型默认值说明
label必填string
classNamestring
defaultValueDateRange
disabledbooleanfalse
disabledDatesComponentProps<typeof Calendar>['disabled']
format(date: Date) => stringformatDate
monthsnumber2并排显示几个月。在 `sm` 以下退回一个。
onValueChange(value: DateRange | undefined) => void
placeholderstring'Pick a range'
presetsboolean | DatePreset<DateRange>[]true显示在日历格旁边的快捷项——“最近 30 天”和它的邻居们。传 `true` 用内置那一套,传数组用你自己的。 这里默认开启,单日选择器上默认关闭,因为“最近 30 天”几乎就是范围选择器被问到的全部,而单个日期通常是某个具体的日子。
valueDateRange

再导出

RANGE_PRESETS = [ { label: 'Last 7 days', value: () => daysAgo(7) }, { label: 'Last 30 days', value: () => daysAgo(30) }, { label: 'Last 90 days', value: () => daysAgo(90) }, { label: 'Last 12 months', value: () => daysAgo(365) }, { label: 'Month to date', value: () => { const to = new Date() return { from: new Date(to.getFullYear(), to.getMonth(), 1), to } }, }, { label: 'Year to date', value: () => { const to = new Date() return { from: new Date(to.getFullYear(), 0, 1), to } }, }, ]

范围选择器几乎在每个出现它的界面上都会被要求的那几个快捷项,所以直接内置,而不是每个仪表盘再造一遍。

点击时才计算:渲染时就建好的预设列表,会把“今天”冻结在页面加载的那一刻,对任何开了一整夜的标签页来说都是错的。

DATE_PRESETS = [ { label: 'Today', value: () => new Date() }, { label: 'Tomorrow', value: () => { const date = new Date() date.setDate(date.getDate() + 1) return date }, }, { label: 'In a week', value: () => { const date = new Date() date.setDate(date.getDate() + 7) return date }, }, { label: 'In a month', value: () => { const date = new Date() date.setMonth(date.getMonth() + 1) return date }, }, ]

单个日期的对应版本。

键盘操作

DatePicker keyboard interactions
按键作用
EnterSpace打开日历。
Escape不选日期直接关闭。

无障碍

  • 触发器按访问者自己的地区格式打印日期,而不是写死的 dd/mm/yyyy。
  • DateRangePicker 会等到两端都选完才关——一段范围在有第二个日期之前不算一个值。
  • 右侧快捷选项是普通按钮,不是菜单:它们设的值和旁边的日历格设的是同一个,所以它们属于同一个控件,Tab 也在同一轮里走到。
  • 快捷值是点击那一刻才算的,所以标签页开了一整夜,“今天”仍然是今天。