Date Picker
A date picker combines a button, a popover and a calendar so users can select a date or a range of dates.
Overview
按钮触发器 + Popover 内嵌 Calendar:「Pick a date」占位,选中后回显长格式日期并关闭弹层。
Installation
Date Picker 是单个 Rust 文件(src/date_picker.rs),复用 calendar.rs 的日期数学 —— 不依赖 chrono。弹层定位用 deferred(..) overlay pass + on_children_prepainted 捕获触发器 bounds,嵌在任何布局里都能锚定。
# Download the runnable example crate, then:cargo run --locked -- --date-picker# Other demos:cargo run --locked -- --date-picker rangecargo run --locked -- --date-picker dobcargo run --locked -- --date-picker inputcargo run --locked -- --date-picker timecargo run --locked -- --date-picker naturalUsage
DatePicker 是一个 entity:把(可选的)Calendar entity 交给它托管,订阅 DatePickerEvent 拿选中态。Calendar 自身的能力(Range / dropdown caption / disabled dates / months(2))原样透出。
use justdo_command::calendar::{Calendar, CalendarMode};use justdo_command::date_picker::{DatePick, DatePicker, DatePickerEvent};// DatePicker wraps a Calendar entity in a popover behind a trigger.let calendar = cx.new(|_| Calendar::new());let picker = cx.new(|cx| { DatePicker::new(Some(calendar), window, cx) .placeholder("Pick a date")});cx.subscribe(&picker, |_, _, ev: &DatePickerEvent, cx| { // ev.selection — Option<DatePick::Single(Date) | DatePick::Range{..}> // ev.time — Option<(hour, minute)> when .time_input(..) is wired cx.notify();}).detach();// in render(): .child(picker.clone())网页预览与桌面端运行同一份 Rust / GPUI 组件。
Composition
结构对齐 shadcn/ui 的 PopoverTrigger → Button + Popover → Calendar。
DatePicker entity — owns open state + echoed selection├── trigger button row: calendar icon + label + chevron ⌄│ label "Pick a date" → "September 12, 2025"│ variant text span, typed InputState, or natural input└── deferred popover absolute, anchored under the trigger ├── Calendar entity — same one used standalone └── Time row .time_input(..) adds a "Time [9:00]" footerDatePick Single(Date) | Range { start, end }DatePickerEvent { selection, time } — emitted on every changeInput & parsing
三个变体共用一套手写 parser(零依赖):ISO/US 日期、12/24 小时时间、自然语言短语。
// Three parsers ship in date_picker.rs — no external crate:parse_date("2026-09-30") // ISO — also YYYY/M/Dparse_date("9/30/2026") // US — also M-D-YYYYparse_time("9:00") // → (9, 0)parse_time("9:30 pm") // → (21, 30); "12 am" → (0, 0)parse_natural("tomorrow") // today + 1parse_natural("in 3 days") // today + 3 (days/weeks/months)parse_natural("next Friday") // nearest future Fridayparse_natural("2026-09-30") // falls through to parse_dateNotes
Open/close semantics. 单选模式下点日期即关弹层(Radix 同款);Range 模式点起点后弹层保持,点终点才关。点击弹层外的遮罩也关闭。
Typed input. .date_input(input, cx) 把触发器的文案换成真实单行编辑器 —— 解析成功即高亮日历对应日期;反向从日历选中会把 ISO 日期写回输入框(subscribe 回调拿不到 Window,回写在 render 里应用 pending 值)。
Natural language. .natural_input(..) 不挂日历 —— Enter 解析 "tomorrow" / "in 3 days" / "next Friday" / ISO 日期,并在下方回显 "Your post will be published on …" 式确认语。
Anchoring. 弹层宽度按 mode 取 296/560px,水平方向钳在组件根 bounds 内,永远贴着触发器下沿 +6px。
Accessibility
Pointer semantics
触发器整行可点;输入变体里只有图标格触发开关,输入框点击不会冒泡成 toggle。弹层外有全幅遮罩承接 "点击外部关闭"。
Calendar internals
弹层内的日历复用 Calendar entity —— 每天是 gpui_base::Button(Tab/Enter/Space 完备),翻月按钮带 a11y label。
API Reference
DatePicker entity 的 builder API、读取方法与事件。
| API | Signature | Notes |
|---|---|---|
DatePicker::new(..) | Option<Entity<Calendar>>, window, cx | Hosts the calendar; None = input-only. |
.placeholder(..) | impl Into<SharedString> | Trigger text before a pick. |
.label(..) | impl Into<SharedString> | Field caption above the trigger. |
.date_input(..) | Entity<InputState>, cx | Typed ISO/US date field. |
.natural_input(..) | Entity<InputState>, cx | Natural-language parse on Enter. |
.time_input(..) | Entity<InputState>, cx | Time row inside the popover. |
.disabled(..) | bool | Muted + click-inert trigger. |
set_open(..) | bool, cx | Programmatic open/close. |
date() / range() | Option<..> | Read the echoed selection. |
DatePickerEvent | { selection, time } | DatePick::Single | ::Range; Option<(h, m)>. |
Source & inspiration
结构与视觉参考 shadcn/ui Date Picker (React Aria) 。本页交互由 Rust / GPUI 绘制,基础样式来自 gpui-kit 。