Calendar
A date field component that allows users to enter and edit a single date or a range of dates.
Overview
单月日历:‹ › 翻月、outside 日期灰显、today 与选中态用 primary 底色。
Installation
Calendar 是单个 Rust 文件(src/calendar.rs),日期数学用 Howard Hinnant 的 civil-from-days 算法自实现 —— 不依赖 chrono。浏览器里 "today" 走 js_sys::Date,因为 SystemTime::now() 在 wasm32 上会 panic。
[dependencies]gpui-kit = "0.6.6"[target.'cfg(target_family = "wasm")'.dependencies]js-sys = "=0.3.98" # browser "today" — SystemTime::now panics on wasm# Download the runnable example crate, then:cargo run --locked -- --calendar# Other demos:cargo run --locked -- --calendar rangecargo run --locked -- --calendar dropdowncargo run --locked -- --calendar presetscargo run --locked -- --calendar datetimecargo run --locked -- --calendar bookedUsage
Calendar 是一个 entity(持 visible month、selection、disabled 集合),宿主在 cx.new(..) 里构造并 cx.subscribe 收 CalendarSelectEvent。
use justdo_command::calendar::{Calendar, CalendarMode, CalendarSelectEvent, Date};// Calendar is an entity — host it in a view and subscribe for selections.let calendar = cx.new(|_| { Calendar::new() .selected(Date::today()) .dropdown(true)});cx.subscribe(&calendar, |_, _, event: &CalendarSelectEvent, cx| { // CalendarSelection::Single(date) | ::Range { start, end } cx.notify();}).detach();// in render(): .child(calendar.clone())网页预览与桌面端运行同一份 Rust / GPUI 组件。
Composition
结构对齐 shadcn/ui 的 Calendar / RangeCalendar。
Calendar entity — owns visible month + selection state├── header ‹ prev · caption · next ›│ caption text, or "September ⌄ 2026 ⌄" with .dropdown(true)│ popup 4×3 month grid / ±5-year grid overlay├── weekday row S M T W T F S└── day grid 32px cells — outside days dimmed Date proleptic-Gregorian math, no external crate CalendarSelectEvent emitted on every pick (Single / Range)// Cell states are computed per day in day_cell():is_today → accent backgroundis_selected → primary bg + primary-foreground inkrange start/end → primary, outer rounded cornerrange middle → accent bg, square cornersoutside → muted inkdisabled_dates → muted + 45% opacity, clicks interceptedNotes
Range picking. .mode(CalendarMode::Range) 下首点定 start、次点定 end(点在 start 之前会自动交换),第三次点击重开新 range。.months(2) 并排渲染相邻两月,‹ › 只出现在首/末格。
Caption dropdown. .dropdown(true) 把 caption 换成 "September ⌄ / 2026 ⌄" 两个 ghost 按钮,点击展开 4×3 月格或前后 ±5 年网格;弹层用 block_mouse_except_scroll() 拦截点击,不会穿透到下层日期格。
Outside days. 上/下月补位的日期灰显但可点 —— 点了之后视图跳到该日期所在月(shadcn 同款行为)。
Accessibility
Keyboard & focus
每一天是 gpui_base::Button —— Tab 聚焦、Enter/Space 激活、focus-visible 描边都由原语提供;icon-only 翻月按钮带 a11y_label("Previous month")。
Disabled dates
.disabled_dates(..) 内的日期被原语标记为 disabled —— 半透明渲染且点击拦截,配合预设表示已订/不可用。
API Reference
Calendar entity 的 builder API 与事件。
| API | Signature | Notes |
|---|---|---|
Calendar::new() | -> Calendar | Entity; visible month defaults to today. |
.mode(..) | CalendarMode | Single | Range picking. |
.months(..) | usize | 1 or 2 month columns side by side. |
.visible_month(..) | (year, month) | First visible month. |
.selected(..) | Date | Initial single selection. |
.range(..) | (start, end) | Initial range selection. |
.disabled_dates(..) | Vec<Date> | Unpickable dates (booked). |
.dropdown(..) | bool | Caption → month/year select buttons. |
CalendarSelectEvent | selection: CalendarSelection | Single(Date) | Range { start, end }. |
Date | { year, month, day } | today(), add_days(..); no external crate. |
Source & inspiration
结构与视觉参考 shadcn/ui Calendar (React Aria) 。本页交互由 Rust / GPUI 绘制,基础样式来自 gpui-kit 。