import { h, signal } from '@ui-arch/frame'; import type { AccordionItem, AccordionProps } from './types.js'; /*** 折叠面板:标题按钮 + grid 行高过渡;单开(手风琴)或多开,展开态由 active 信号驱动或内部持有 ***/ export function accordion(props: AccordionProps): HTMLElement { // —— 状态集中声明 —— const internal = signal([]); // 内部展开集合(未传 active 时使用) const opened = (v: string): boolean => (props.active ? props.active() === v : internal().includes(v)); const toggle = (item: AccordionItem): void => { if (props.active) { const next = props.active() === item.value ? '' : item.value; // 单开:再点收起 props.active(next); props.onChange?.(next); return; } const cur = internal(); const next = cur.includes(item.value) ? cur.filter((v) => v !== item.value) : props.multiple ? [...cur, item.value] : [item.value]; internal(next); props.onChange?.(next[0] ?? ''); }; // —— 面板:标题为原生 button(键盘可达),内容包一层 overflow 容器供过渡裁剪 —— const rows = props.items.map((item, i) => h( 'div', { class: () => `ua-acc-item${opened(item.value) ? ' ua-open' : ''}` }, h( 'button', { type: 'button', class: 'ua-acc-head', id: `ua-acc-h-${i}`, 'aria-expanded': () => String(opened(item.value)), 'aria-controls': `ua-acc-b-${i}`, disabled: item.disabled ?? false, // 布尔透传即得禁用态 onClick: () => toggle(item), }, h('span', { class: 'ua-acc-title' }, item.title), h('span', { class: 'ua-acc-caret' }, '▸'), ), h( 'div', { class: 'ua-acc-body', id: `ua-acc-b-${i}`, role: 'region', 'aria-labelledby': `ua-acc-h-${i}` }, h('div', { class: 'ua-acc-inner' }, item.content), ), ), ); return h('div', { class: 'ua-accordion' }, ...rows); }