Date & Time: components 051-058 (wip)

This commit is contained in:
ZacharyZhang-NY committed 2026-10-05 12:22:05 -04:00
1 parent cfc7ab023d
commit e61e38b656
45 files changed
+3943 -1

No files matched your search

+55
View File
@@ -0,0 +1,55 @@
# 051 Calendar · 日历
## 用途
在页面中直接显示一个月份,用于选择单个日期。支持今日标记、不可选日期与周起始日配置。
## 最小示例
```go
day := mujica.Date{Year: 2026, Month: time.October, Day: 5}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if mujica.Calendar(c, &day, mujica.CalendarOptions{WeekStart: mujica.WeekStartMonday}).Changed() {
fmt.Println("chosen", day)
}
}
ui.Render(view, 400, 300, 1)
```
## 参数
`func Calendar(c *ui.Context, value *Date, opts CalendarOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。
| 字段 | 类型 | 默认值 | 含义 |
| --- | --- | --- | --- |
| `Min` | `Date` | 零值 | 最早可选日期;零值不限制。 |
| `Max` | `Date` | 零值 | 最晚可选日期;零值不限制。 |
| `DisableDate` | `func(Date) bool` | nil | 返回 true 的日期不可选,显示删除线。 |
| `WeekStart` | `WeekStart` | `WeekStartLocale` | 首列星期:跟随语言(zh-CN 周一、en-US 周日),或 `WeekStartSunday`/`WeekStartMonday`/`WeekStartSaturday`。 |
| `Today` | `Date` | 零值(系统时钟) | 标记为今天的日期:加粗并在下方绘制菱形。 |
| `Disabled` | `bool` | false | 禁用整个日历:不能翻页、不能选择,退出 Tab 序列。 |
| `Label` | `string` | 内置“日历” | 辅助技术读出的名称。 |
## 状态
默认、悬停、按下、聚焦(键盘焦点环)、选中(酒红填充 + 单选语义)、今日(加粗 + 菱形)、不可选(删除线并由 MyGo 置灰)、非本月(弱化文字)、禁用。
## 事件
`Calendar(...).Changed()` 在用户选择了与原值不同的日期的那一帧返回 true,`*value` 已更新。翻月不触发事件。
## 键盘操作
- Tab:进入日期格,落在已选日期或上次聚焦的日期。
- ←/→:前后一天;↑/↓:前后一周(自动跳过不可选日期,最多 31 天)。
- PageUp/PageDown:上/下一月;Shift+PageUp/PageDown:上/下一年。
- Home/End:本月第一天/最后一天。
- Enter/Space:选择当前日期。
## 限制
- 不可选日期不能获得焦点;若方向键 31 天内找不到可选日期,视图会翻页但焦点停在原处。
- MyGo 的 `ui.Calendar` 固定周一起始、只有英文且不支持禁用日期(ui/date.go:85-160),因此本组件基于 `RadioBase` 自绘网格,不复用 `ui.Calendar`。
- 日期与时间值不带时区:`Date`、`YearMonth`、`TimeOfDay` 只保存日历字段,组件内部计算不做 UTC 转换。
- `Today` 为零值时读取 `c.Now()`(系统本地时区);需要固定“今天”(测试、跨时区应用)时显式传入。
+59
View File
@@ -0,0 +1,59 @@
# 052 DatePicker · 日期选择器
## 用途
可键入的日期输入框,带日历弹层。用于表单中录入单个日期。
## 最小示例
```go
due := mujica.Date{Year: 2026, Month: time.October, Day: 5}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
opts := mujica.DatePickerOptions{Label: "Due date", Clearable: true,
Min: mujica.Date{Year: 2026, Month: time.January, Day: 1}}
if mujica.DatePicker(c, &due, opts).Changed() {
fmt.Println("due", due)
}
}
ui.Render(view, 400, 300, 1)
```
## 参数
`func DatePicker(c *ui.Context, value *Date, opts DatePickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。
| 字段 | 类型 | 默认值 | 含义 |
| --- | --- | --- | --- |
| `Layout` | `string` | `2006-01-02` | Go 时间布局,用于显示与解析输入。 |
| `Min` | `Date` | 零值 | 最早可接受日期。 |
| `Max` | `Date` | 零值 | 最晚可接受日期。 |
| `DisableDate` | `func(Date) bool` | nil | 不可选日期;键入此类日期会报告错误。 |
| `WeekStart` | `WeekStart` | `WeekStartLocale` | 弹层日历的周起始日。 |
| `Today` | `Date` | 零值(系统时钟) | 弹层日历中的今日标记;空值时弹层打开到此月份。 |
| `Clearable` | `bool` | false | 显示清空按钮;清空输入后提交表示“无日期”。 |
| `Placeholder` | `string` | 内置“选择日期” | 空输入框的占位文字。 |
| `Disabled` | `bool` | false | 禁用输入与弹层。 |
| `Label` | `string` | 占位文字 | 辅助技术名称。 |
## 状态
默认、悬停(边框加深)、聚焦(焦点色边框)、错误(危险色边框 + 下方说明,输入保留)、禁用。弹层内日历状态同 Calendar。
## 事件
`Changed()`:键入后按 Enter 或离开输入框并解析成功、在日历中选择、或点击清空导致值变化时为 true。
## 键盘操作
- 键入日期后 Enter 提交;Tab 离开时也会提交。
- ↓ 或 Alt+↓:打开日历;日历内按键同 Calendar,Enter 选择后关闭并写入。
- Escape:关闭日历,不改值。
## 限制
- 无效或越界的输入(如 2026-02-30)显示具体错误并保留原文,不会被修正为相邻日期。
- MyGo 的 `ui.DateInput` 固定格式且不可键入(ui/date.go:13),因此本组件组合 `TextInputBase` 与自绘日历弹层(`PopoverBase`)。
- 暂不提供只读模式:MyGo v0.2.10 未公开 TextInputBase 的只读开关。
- 日期与时间值不带时区:`Date`、`YearMonth`、`TimeOfDay` 只保存日历字段,组件内部计算不做 UTC 转换。
- `Today` 为零值时读取 `c.Now()`(系统本地时区);需要固定“今天”(测试、跨时区应用)时显式传入。
+57
View File
@@ -0,0 +1,57 @@
# 053 DateRangePicker · 日期范围选择器
## 用途
在弹层中先选开始日期、再选结束日期,悬停时预览范围,确认后写入。
## 最小示例
```go
var stay mujica.DateRange
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.DateRangePicker(c, &stay, mujica.DateRangePickerOptions{Label: "Stay"})
if r.Changed() {
fmt.Println("stay", stay.Start, stay.End, stay.Days())
}
}
ui.Render(view, 400, 300, 1)
```
## 参数
`func DateRangePicker(c *ui.Context, value *DateRange, opts DateRangePickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。
| 字段 | 类型 | 默认值 | 含义 |
| --- | --- | --- | --- |
| `Layout` | `string` | `2006-01-02` | 触发器与摘要中的日期格式。 |
| `Min` | `Date` | 零值 | 两端最早日期。 |
| `Max` | `Date` | 零值 | 两端最晚日期。 |
| `DisableDate` | `func(Date) bool` | nil | 不可选日期;范围内包含此类日期时拒绝并提示。 |
| `WeekStart` | `WeekStart` | `WeekStartLocale` | 周起始日。 |
| `Today` | `Date` | 零值(系统时钟) | 今日标记与空值时的起始月份。 |
| `Disabled` | `bool` | false | 禁用。 |
| `Label` | `string` | 内置“日期范围” | 辅助技术名称。 |
## 状态
触发器:默认、悬停、聚焦、禁用。弹层:起止日期酒红填充,中间日期浅色带;选择结束前随指针或焦点预览范围;错误说明;“确定”在两端选齐前禁用。
## 事件
`Changed()`:点击“确定”或“清空”使值改变时为 true。`Cancelled()`:按 Escape、点击“取消”或点击弹层外关闭、草稿被丢弃时为 true。
## 键盘操作
- Enter/Space/↓:打开弹层。
- 日历内方向键移动,Enter 依次选择开始与结束。
- 早于开始日期的选择会重新开始范围。
- Tab 到“清空”“取消”“确定”。
- Escape:放弃草稿。
## 限制
- 弹层一次显示一个月;跨月范围通过翻页完成。
- `DateRange` 为闭区间,`Days()` 计入两端。
- 日期与时间值不带时区:`Date`、`YearMonth`、`TimeOfDay` 只保存日历字段,组件内部计算不做 UTC 转换。
- `Today` 为零值时读取 `c.Now()`(系统本地时区);需要固定“今天”(测试、跨时区应用)时显式传入。
+59
View File
@@ -0,0 +1,59 @@
# 054 TimePicker · 时间选择器
## 用途
分段编辑时、分、秒的时间输入,可在弹层列表中选择;支持 12/24 小时制、步长和范围。
## 最小示例
```go
alarm := mujica.TimeOfDay{Hour: 7, Minute: 30}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
opts := mujica.TimePickerOptions{Label: "Alarm", Hour12: true, MinuteStep: 5}
if mujica.TimePicker(c, &alarm, opts).Changed() {
fmt.Println("alarm", alarm)
}
}
ui.Render(view, 400, 300, 1)
```
## 参数
`func TimePicker(c *ui.Context, value *TimeOfDay, opts TimePickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。
| 字段 | 类型 | 默认值 | 含义 |
| --- | --- | --- | --- |
| `Seconds` | `bool` | false | 显示并编辑秒。 |
| `Hour12` | `bool` | false | 12 小时制,附加上午/下午段。 |
| `MinuteStep` | `int` | 0(即 1) | 分钟步长,必须整除 60。 |
| `SecondStep` | `int` | 0(即 1) | 秒步长,必须整除 60。 |
| `Min` | `TimeOfDay` | 00:00:00 | 最早时间。 |
| `Max` | `TimeOfDay` | 零值(即 23:59:59) | 最晚时间。 |
| `Disabled` | `bool` | false | 禁用。 |
| `Label` | `string` | 空 | 组名;各段名称为“Label: 时/分/秒”。 |
## 状态
默认、悬停、聚焦段(酒红填充)、错误(危险色边框 + 说明,草稿保留)、禁用。弹层列表中超出范围的项禁用。
## 事件
`Changed()`:编辑后的时间合法(在范围内且符合步长)并与原值不同时为 true。不合法时不写入,显示错误。
## 键盘操作
- Tab:进入时间段(整组一个 Tab 停靠点)。
- ←/→:切换时、分、秒、上午/下午。
- ↑/↓:按步长增减,整圈循环。
- 数字键:直接键入,时完成后自动跳到分。
- A/P:设置上午/下午。
- Alt+↓ 或时钟按钮:打开列表弹层;Escape 关闭。
## 限制
- 超出范围或不符合步长的时间会提示,不会被自动夹取或取整。
- 段顺序固定为“时:分:秒 上午/下午”,zh-CN 下上午/下午也位于末尾。
- MyGo 的 `ui.TimeInput` 只有时分且无 12 小时制、步长和范围(ui/timeinput.go:18),因此本组件自绘分段。
- 日期与时间值不带时区:`Date`、`YearMonth`、`TimeOfDay` 只保存日历字段,组件内部计算不做 UTC 转换。
- `Today` 为零值时读取 `c.Now()`(系统本地时区);需要固定“今天”(测试、跨时区应用)时显式传入。
+55
View File
@@ -0,0 +1,55 @@
# 055 TimeRangePicker · 时间范围选择器
## 用途
编辑开始与结束时间,校验顺序;跨午夜必须显式开启。
## 最小示例
```go
shift := mujica.TimeRange{Start: mujica.TimeOfDay{Hour: 22}, End: mujica.TimeOfDay{Hour: 6}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
opts := mujica.TimeRangePickerOptions{Label: "Night shift", AllowOvernight: true}
if mujica.TimeRangePicker(c, &shift, opts).Changed() {
fmt.Println("shift", shift.Start, shift.End, shift.Overnight())
}
}
ui.Render(view, 400, 300, 1)
```
## 参数
`func TimeRangePicker(c *ui.Context, value *TimeRange, opts TimeRangePickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。
| 字段 | 类型 | 默认值 | 含义 |
| --- | --- | --- | --- |
| `Seconds` | `bool` | false | 显示秒。 |
| `Hour12` | `bool` | false | 12 小时制。 |
| `MinuteStep` | `int` | 0(即 1) | 分钟步长。 |
| `SecondStep` | `int` | 0(即 1) | 秒步长。 |
| `Min` | `TimeOfDay` | 00:00:00 | 两端最早时间。 |
| `Max` | `TimeOfDay` | 零值(即 23:59:59) | 两端最晚时间。 |
| `AllowOvernight` | `bool` | false | 允许结束早于开始,表示次日结束,并显示“次日”。 |
| `Disabled` | `bool` | false | 禁用。 |
| `Label` | `string` | 空 | 整组名称。 |
## 状态
同 TimePicker;范围不合法时两端都显示错误边框;跨午夜时显示“+1 day / 次日”。
## 事件
`Changed()`:两端都合法且顺序正确、与原值不同时为 true。
## 键盘操作
- 两端各自按 TimePicker 操作;Tab 从开始移到结束。
## 限制
- 未开启 `AllowOvernight` 时传入跨午夜的值会 panic(编程错误)。
- 开始等于结束视为错误。
- `TimeRange.Overnight()` 报告是否跨午夜。
- 日期与时间值不带时区:`Date`、`YearMonth`、`TimeOfDay` 只保存日历字段,组件内部计算不做 UTC 转换。
- `Today` 为零值时读取 `c.Now()`(系统本地时区);需要固定“今天”(测试、跨时区应用)时显式传入。
+62
View File
@@ -0,0 +1,62 @@
# 056 DateTimePicker · 日期时间选择器
## 用途
在调用方指定的时区中编辑日期和时间,确认后写入 `time.Time`。
## 最小示例
```go
tokyo, err := time.LoadLocation("Asia/Tokyo")
if err != nil {
panic(err)
}
var meeting time.Time
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
opts := mujica.DateTimePickerOptions{Label: "Meeting", Location: tokyo, MinuteStep: 15}
if mujica.DateTimePicker(c, &meeting, opts).Changed() {
fmt.Println("meeting", meeting)
}
}
ui.Render(view, 400, 300, 1)
```
## 参数
`func DateTimePicker(c *ui.Context, value *time.Time, opts DateTimePickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。
| 字段 | 类型 | 默认值 | 含义 |
| --- | --- | --- | --- |
| `Location` | `*time.Location` | 必填 | 用户编辑所用时区;为 nil 时 panic。 |
| `Min` | `time.Time` | 零值 | 最早时刻。 |
| `Max` | `time.Time` | 零值 | 最晚时刻。 |
| `Seconds` | `bool` | false | 显示秒。 |
| `Hour12` | `bool` | false | 12 小时制。 |
| `MinuteStep` | `int` | 0(即 1) | 分钟步长。 |
| `WeekStart` | `WeekStart` | `WeekStartLocale` | 周起始日。 |
| `Today` | `Date` | 零值(系统时钟) | 今日标记与空值时的默认日期。 |
| `Disabled` | `bool` | false | 禁用。 |
| `Label` | `string` | 内置占位文字 | 辅助技术名称。 |
## 状态
触发器显示该时区下的时间与时区缩写。弹层:日历、时间段、时区名、错误说明、“取消/确定”。
## 事件
`Changed()`:确认且时刻改变时为 true,写入的值位于 `Location`。`Cancelled()`:取消、Escape 或点击外部关闭时为 true。
## 键盘操作
- Enter/Space/↓:打开。
- 日历与时间段按键同 DatePicker、TimePicker。
- Tab 到“取消”“确定”。
- Escape:取消。
## 限制
- 夏令时跳过的时间(如纽约 2026-03-08 02:30)和重复的时间(如 2026-11-01 01:30)都会提示,不会自动移到相邻时刻或选择其中之一。
- 空值(零 `time.Time`)打开时默认今天 00:00。
- 日期与时间值不带时区:`Date`、`YearMonth`、`TimeOfDay` 只保存日历字段,组件内部计算不做 UTC 转换。
- `Today` 为零值时读取 `c.Now()`(系统本地时区);需要固定“今天”(测试、跨时区应用)时显式传入。
+55
View File
@@ -0,0 +1,55 @@
# 057 MonthPicker · 月份选择器
## 用途
按年份分页选择月份。
## 最小示例
```go
var month mujica.YearMonth
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
opts := mujica.MonthPickerOptions{Label: "Billing month", Clearable: true}
if mujica.MonthPicker(c, &month, opts).Changed() {
fmt.Println("month", month)
}
}
ui.Render(view, 400, 300, 1)
```
## 参数
`func MonthPicker(c *ui.Context, value *YearMonth, opts MonthPickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。
| 字段 | 类型 | 默认值 | 含义 |
| --- | --- | --- | --- |
| `Min` | `YearMonth` | 零值 | 最早月份。 |
| `Max` | `YearMonth` | 零值 | 最晚月份。 |
| `DisableMonth` | `func(YearMonth) bool` | nil | 不可选月份。 |
| `Clearable` | `bool` | false | 有值时显示清空按钮。 |
| `Today` | `Date` | 零值(系统时钟) | 标记当前月份。 |
| `Disabled` | `bool` | false | 禁用。 |
| `Label` | `string` | 内置“选择月份” | 辅助技术名称。 |
## 状态
默认、悬停、聚焦、选中(酒红填充)、当前月(加粗 + 菱形)、不可选(删除线)、禁用。
## 事件
`Changed()`:选择或清空导致值变化时为 true。
## 键盘操作
- Enter/Space/↓:打开。
- ←/→:前后一月;↑/↓:前后三月(一行)。
- PageUp/PageDown:上/下一年。
- Home/End:一月/十二月。
- Enter:选择并关闭;Escape:关闭。
## 限制
- 键盘移到不可选月份时焦点保持在原处,可继续移动。
- 日期与时间值不带时区:`Date`、`YearMonth`、`TimeOfDay` 只保存日历字段,组件内部计算不做 UTC 转换。
- `Today` 为零值时读取 `c.Now()`(系统本地时区);需要固定“今天”(测试、跨时区应用)时显式传入。
+55
View File
@@ -0,0 +1,55 @@
# 058 YearPicker · 年份选择器
## 用途
按十年分页浏览并选择年份。
## 最小示例
```go
year := 2026
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
opts := mujica.YearPickerOptions{Label: "Vintage", Min: 1990, Max: 2030}
if mujica.YearPicker(c, &year, opts).Changed() {
fmt.Println("year", year)
}
}
ui.Render(view, 400, 300, 1)
```
## 参数
`func YearPicker(c *ui.Context, value *int, opts YearPickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。
| 字段 | 类型 | 默认值 | 含义 |
| --- | --- | --- | --- |
| `Min` | `int` | 0 | 最早年份;0 不限制。 |
| `Max` | `int` | 0 | 最晚年份;0 不限制。 |
| `DisableYear` | `func(int) bool` | nil | 不可选年份。 |
| `Clearable` | `bool` | false | 有值时显示清空按钮。 |
| `Today` | `Date` | 零值(系统时钟) | 标记当前年份。 |
| `Disabled` | `bool` | false | 禁用。 |
| `Label` | `string` | 内置“选择年份” | 辅助技术名称。 |
## 状态
默认、悬停、聚焦、选中、当前年、不可选、相邻十年(弱化)、禁用。
## 事件
`Changed()`:选择或清空导致值变化时为 true。`*value` 为 0 表示未选择。
## 键盘操作
- Enter/Space/↓:打开。
- ←/→:前后一年;↑/↓:前后三年。
- PageUp/PageDown:上/下一个十年。
- Home/End:页首/页尾。
- Enter:选择并关闭;Escape:关闭。
## 限制
- 只支持公元 1 年及以后;值为负数时 panic。
- 日期与时间值不带时区:`Date`、`YearMonth`、`TimeOfDay` 只保存日历字段,组件内部计算不做 UTC 转换。
- `Today` 为零值时读取 `c.Now()`(系统本地时区);需要固定“今天”(测试、跨时区应用)时显式传入。