import { effect, h, signal } from '@ui-arch/frame'; import type { Signal } from '@ui-arch/frame'; import type { CheckboxGroupProps } from './types.js'; /*** 多选组:value 数组信号双向绑定,min/max 用「禁用边界」而非报错,可选全选行(含半选) ***/ export function checkboxGroup(props: CheckboxGroupProps): HTMLElement { // —— 状态集中声明 —— const value: Signal = props.value ?? signal([]); // 选中值 const inputs: HTMLInputElement[] = []; // 各选项勾选框 const dir = props.direction ?? 'horizontal'; // 排列方向 const usable = props.options.filter((o) => !o.disabled); // 可勾选项(全选基数) /*** 整组禁用:布尔直接取,信号在此读取以纳入 effect 依赖 ***/ const groupOff = (): boolean => (typeof props.disabled === 'function' ? props.disabled() : props.disabled === true); /*** 变更落库:写信号 + 回调,边界不在此处拒绝——由 sync 提前禁用越界选项 ***/ const update = (next: string[]): void => { value(next); props.onChange?.([...next]); }; const all = h('input', { type: 'checkbox', class: 'ua-checkbox', 'aria-label': '全选' }) as HTMLInputElement; all.addEventListener('change', () => { update(all.checked ? usable.map((o) => o.value) : []); // 全选只覆盖未禁用项 }); const rows = props.options.map((opt) => { const input = h('input', { type: 'checkbox', class: 'ua-checkbox' }) as HTMLInputElement; input.value = opt.value; input.addEventListener('change', () => { const cur = new Set(value()); input.checked ? cur.add(opt.value) : cur.delete(opt.value); // 用户操作写回 update([...cur]); }); inputs.push(input); return h('label', { class: 'ua-check-row' }, input, opt.label); }); /*** 同步勾选与可见可用性:到上限禁用未选项、到下限禁用已选项(不改值只锁操作) ***/ const sync = (): void => { const cur = value(); const off = groupOff(); const atMax = props.max != null && cur.length >= props.max; const atMin = props.min != null && cur.length <= props.min; props.options.forEach((opt, i) => { const input = inputs[i]; const on = cur.includes(opt.value); input.checked = on; input.disabled = off || opt.disabled === true || (on ? atMin : atMax); // 边界项锁死,避免越界 }); const picked = usable.filter((o) => cur.includes(o.value)).length; all.checked = picked > 0 && picked === usable.length; all.indeterminate = picked > 0 && picked < usable.length; // 半选:部分已选 all.disabled = off || (picked === usable.length && atMin); // 全选行同样受边界约束 }; effect(sync); const body = h('div', { class: `ua-check-group ua-check-group-${dir}` }, ...(props.checkAll ? [h('label', { class: 'ua-check-row' }, all, '全选')] : []), ...rows); return h('div', { class: 'ua-checkbox-group', role: 'group' }, body) as HTMLElement; }