import React, { useRef, useState } from 'react'; import './DatePicker.css'; /* 日期选择器(移动端)— 规格 §18。 底部浮层 = 装饰性遮罩(aria-hidden)+ 面板;遮罩点击关闭,closeOnMask=false 时不关(规格 §18.5)。 面板 role="dialog" + aria-modal="true",标题元素 id 由 aria-labelledby 指向(规格 §18.6)。 每列 role="listbox"、选项 role="option" + aria-selected;选中项的 is-selected 与 aria-selected 同步 (选中值以 YYYY-MM-DD 文本呈现:value 直接消费,不依赖视觉滚动位置)。 本端不发明选择事件(契约只有 confirm / close):点选只改本地高亮,确认时由宿主读 confirm 后的语义。 */ /* 面板标题元素 id:同页多实例不撞号(不依赖 React 版本特性,模块级自增即可) */ let seq = 0; const COLUMN_LABELS = { year: '年', month: '月', day: '日' }; /* value 形如 YYYY-MM-DD;mode=month 时只有 YYYY-MM,day 为空串 */ function partsOf(value) { const seg = String(value || '').split('-'); return { year: seg[0] || '', month: seg[1] || '', day: seg[2] || '' }; } /* 选项文字可能带单位(「9 月」「9月」),取首个数字段比较;纯符号选项退回逐字比较 */ function sameValue(option, part) { if (part === undefined || part === null || part === '') return false; const digits = String(option).match(/[0-9]+/); if (digits) return String(Number(digits[0])) === String(Number(part)); return String(option) === String(part); } /* mode=date → 年/月/日 三列;mode=month → 年/月 两列(规格 §18.3) */ function columnsOf(mode, years, months, days) { const cols = [ { key: 'year', label: COLUMN_LABELS.year, options: years }, { key: 'month', label: COLUMN_LABELS.month, options: months } ]; if (mode !== 'month') cols.push({ key: 'day', label: COLUMN_LABELS.day, options: days }); return cols; } export default function DatePicker({ mode = 'date', round = false, open = false, closeOnMask = true, value = '', years = [], months = [], days = [], onConfirm, onClose, children = null, }) { const idRef = useRef(null); if (idRef.current === null) { seq += 1; idRef.current = 'kole-m-datepicker-title-' + seq; } const titleId = idRef.current; /* null = 跟随 value 属性;点选后本地记账,确认前不改宿主的值 */ const [draft, setDraft] = useState(null); const picked = draft || partsOf(value); const title = mode === 'month' ? '选择月份' : '选择日期'; const label = title + (value ? ':' + value : ''); const columns = columnsOf(mode, years, months, days); function select(key, option) { setDraft(Object.assign({}, picked, { [key]: String(option) })); } return ( <> {children}