# 日期选择

点击输入框弹出日历，也可以直接键入日期。支持最早 / 最晚日期、禁用周末、禁用指定日期，键盘完整可用。声明式写一个空 <div data-zh-date>，命令式用 ZhaoUI.datePicker。周从周一开始。

**依赖**：`zhaoui.css` 与 `zhaoui.js`（原生 HTML、CSS、JS，不需要框架或 jQuery）。只用核心包时，还要加 `modular/date.min.js` 和 `modular/date.min.css`；全量版已包含。

## 用法

```html
<div data-zh-date data-name="birthday" data-value="2026-10-15" data-min="2026-10-05" data-max="2026-10-25" data-msg="#birthdayMsg" aria-label="日期"></div>
<div id="birthdayMsg" aria-live="polite"></div>

<div data-zh-date data-disable-weekends data-disabled-dates="2026-10-14" aria-label="工作日"></div>

<div id="custom"></div>

<script>
document.querySelector('[data-zh-date]').addEventListener('zh:date-change', function (e) {
  console.log(e.detail.value);   // 2026-10-20
});

ZhaoUI.datePicker('#custom', {
  size: 'lg',
  disabledDate: function (d) { return d.getDate() === 1 ? '每月 1 号不开放' : ''; },
  onChange: function (d) { console.log(d); }
});
</script>
```

## 样式类

`zh-dp`、`zh-dp-panel`、`zh-dp-cal`、`zh-dp-day`

## 属性

| 属性 | 用在 | 取值 | 说明 |
|---|---|---|---|
| `data-zh-date` | <div> | 无 | 声明这是日期选择；控件会构建在这个 div 里 |
| `data-value` | 容器 | YYYY-MM-DD | 初始日期 |
| `data-min / data-max` | 容器 | YYYY-MM-DD | 最早 / 最晚可选日期，范围外的日子禁用 |
| `data-disable-weekends` | 容器 | 布尔属性 | 周六周日不可选 |
| `data-disabled-dates` | 容器 | 逗号分隔的日期，如 2026-10-01,2026-10-02 | 指定日期不可选 |
| `data-placeholder` | 容器 | 字符串，默认 YYYY-MM-DD | 占位文字 |
| `data-name` | 容器 | 字段名 | 生成隐藏 <input name=…> 参与表单提交 |
| `data-size` | 容器 | sm \| md（默认）\| lg | 尺寸 |
| `aria-label / data-labelledby / data-label` | 容器 | 字符串 / 标签元素 id / 弹层标题 | 输入框的无障碍名称；data-label 是弹出面板的标题 |
| `data-msg` | 容器 | 一个元素的选择器，如 #status | 可选的状态提示元素：选择、报错时会把文字写进去（读屏可以朗读），元素上可预先写好默认文字 |
| `<label for="容器 id">` | 容器所在页面的 <label> | for 指向容器的 id | label 会成为输入框的无障碍名称，点击 label 会聚焦输入框；容器是空 <div>，label 的 for 指向它的 id 即可 |

## 方法

- `ZhaoUI.datePicker(host, { value, min, max, disabledDate, size, placeholder, label, labelledby, msg, onChange })`：命令式创建。value / min / max 可以是 'YYYY-MM-DD' 字符串或 Date；disabledDate(date) 返回空串表示可选，返回文字表示不可选并作为原因；onChange(date)
- `el.zhDate.get() / .set(value) / .open() / .close()`：get 返回 Date 或 null；set 接受字符串或 Date，不触发事件

## 事件

| 事件 | 触发于 | detail | 说明 |
|---|---|---|---|
| `zh:date-change` | 容器 | { value: 'YYYY-MM-DD' \| '', date: Date \| null } | 用户选择、键入确认或清除后触发（冒泡）；用 set() 设置不触发 |

## 无障碍

- 输入框是 role="combobox"，弹层是 role="dialog"；日格是按钮，aria-label 为「2026年10月15日 星期四」，今天带 aria-current="date"，不可选的带 aria-disabled
- 日格上：← → 前后一天，↑ ↓ 前后一周，Home / End 到本周首尾，PageUp / PageDown 前后一个月，加 Shift 则一年；Enter 或空格选中；Esc 关闭
- 输入框里可直接键入，支持 2026-10-15、2026/10/15、2026年10月15日；不合法或不可选时标记 aria-invalid，并把原因写入 data-msg 元素
- 点击月份标题可切到月份 / 年份选择
- 按「今天」时如果今天不可选，会弹出说明

## 常见问题

### 日期选择是什么？

点击输入框弹出日历，也可以直接键入日期。支持最早 / 最晚日期、禁用周末、禁用指定日期，键盘完整可用。声明式写一个空 <div data-zh-date>，命令式用 ZhaoUI.datePicker。周从周一开始。

### 怎么使用日期选择？

先在页面里引入 zhaoui.css 和 zhaoui.js（放在 </body> 前，不加 defer），再照本页「用法」里的标记书写；需要用代码控制时，用「方法」里列出的接口。

### 日期选择有哪些事件？

zh:date-change（用户选择、键入确认或清除后触发（冒泡）；用 set() 设置不触发）。

### 日期选择有哪些方法？

ZhaoUI.datePicker(host, { value, min, max, disabledDate, size, placeholder, label, labelledby, msg, onChange })：命令式创建。value / min / max 可以是 'YYYY-MM-DD' 字符串或 Date；disabledDate(date) 返回空串表示可选，返回文字表示不可选并作为原因；onChange(date)；el.zhDate.get() / .set(value) / .open() / .close()：get 返回 Date 或 null；set 接受字符串或 Date，不触发事件。

### 日期选择支持键盘和读屏吗？

输入框是 role="combobox"，弹层是 role="dialog"；日格是按钮，aria-label 为「2026年10月15日 星期四」，今天带 aria-current="date"，不可选的带 aria-disabled；日格上：← → 前后一天，↑ ↓ 前后一周，Home / End 到本周首尾，PageUp / PageDown 前后一个月，加 Shift 则一年；Enter 或空格选中；Esc 关闭；输入框里可直接键入，支持 2026-10-15、2026/10/15、2026年10月15日；不合法或不可选时标记 aria-invalid，并把原因写入 data-msg 元素。

### 日期选择需要依赖框架吗？

不需要。ZhaoUI 是原生 HTML、CSS 和 JavaScript，不依赖 jQuery、Vue 或 React。
