import React, { useRef, useState } from 'react'; import './Picker.css'; /* 选择器(移动端)— 规格 §43。 底部浮层 = 装饰性遮罩(aria-hidden)+ 面板;遮罩点击关闭,closeOnMask=false 时不关(规格 §43.5)。 面板 role="dialog" + aria-modal="true",标题元素 id 由 aria-labelledby 指向(规格 §43.6)。 每列 role="listbox"、选项 role="option" + aria-selected;选中项的 is-selected 与 aria-selected 同步。 mode=single 一列、mode=multiple 多列(各列独立选中,联动与否由宿主给定的选项决定)。 本端不发明选择事件(契约只有 confirm / close):点选只改本地高亮,确认时由宿主读 confirm 后的语义。 */ /* 面板标题元素 id:同页多实例不撞号(不依赖 React 版本特性,模块级自增即可) */ let seq = 0; /* 选项可能是字符串,也可能是 { label, value, disabled } 对象(规格 §43.7 只允许宿主提供) */ function labelOf(option) { if (option === null || option === undefined) return ''; return typeof option === 'object' ? String(option.label === undefined ? '' : option.label) : String(option); } function disabledOf(option) { return !!(option && typeof option === 'object' && option.disabled); } /* 选中判据:选项是对象时比 value,否则比文本(与演示页同一口径) */ function sameValue(option, picked) { if (picked === undefined || picked === null || picked === '') return false; var value = option && typeof option === 'object' ? option.value : option; return String(value) === String(picked); } /* mode=single → 取 columns 第一列;mode=multiple → 全部列(规格 §43.3) */ function columnsOf(mode, columns) { var list = Array.isArray(columns) ? columns : []; return mode === 'single' ? list.slice(0, 1) : list; } export default function Picker({ mode = 'single', round = false, open = false, closeOnMask = true, value = [], columns = [], disabled = false, title = '', onConfirm, onClose, children = null, }) { const idRef = useRef(null); if (idRef.current === null) { seq += 1; idRef.current = 'kole-m-picker-title-' + seq; } const titleId = idRef.current; /* null = 跟随 value 属性;点选后本地记账,确认前不改宿主的值 */ const [draft, setDraft] = useState(null); const picked = draft || (Array.isArray(value) ? value : []); const cols = columnsOf(mode, columns); const panelLabel = title || '选择'; const label = panelLabel + (picked.length ? ':' + picked.join(' ') : ''); function select(colIndex, option) { if (disabled || disabledOf(option)) return; const next = picked.slice(); next[colIndex] = String(option && typeof option === 'object' ? option.value : option); setDraft(next); } return ( <> {children}