import { effect, h, signal } from '@ui-arch/frame'; import type { BreadcrumbProps } from './types.js'; /*** 面包屑:nav + ol 语义结构,末项为当前页(不可点、aria-current);条目超过 maxItems 时把中段折叠成「…」按钮 ***/ export function breadcrumb(props: BreadcrumbProps): HTMLElement { // —— 状态集中声明 —— const items = props.items; const sep = props.separator ?? '/'; // 分隔符 const max = props.maxItems ?? Number.POSITIVE_INFINITY; // 展示条数上限:超出折叠中段 const expanded = signal(false); // 折叠段是否已展开(展开后不再折叠) const list = h('ol', { class: 'ua-crumb-list' }); /*** 逐项渲染:有 href 用 ,无 href 用 button 交 onNavigate;折叠按钮紧跟首项插入一次 ***/ const render = (): void => { const fold = !expanded() && items.length > max; // 是否需要折叠 const tail = Math.max(2, max - 1); // 尾部保留条数:至少当前页 + 上一级 const kept = fold ? [0, ...items.slice(-tail).map((_, k) => items.length - tail + k)] : items.map((_, i) => i); const nodes: HTMLElement[] = []; kept.forEach((idx, pos) => { const item = items[idx]; const last = idx === items.length - 1; // 末项即当前页 const node = last ? h('span', { class: 'ua-crumb-current', 'aria-current': 'page' }, item.label) : item.href ? h('a', { class: 'ua-crumb-link', href: item.href }, item.label) : h('button', { type: 'button', class: 'ua-crumb-link', onClick: () => props.onNavigate?.(item, idx) }, item.label); nodes.push(h('li', { class: 'ua-crumb-item' }, node, last ? null : h('span', { class: 'ua-crumb-sep', 'aria-hidden': 'true' }, sep))); if (!fold || pos > 0) return; // 卫语句:折叠按钮只插一次 const hidden = items.length - kept.length; // 被折叠的条数 nodes.push(h('li', { class: 'ua-crumb-item' }, h( 'button', { type: 'button', class: 'ua-crumb-fold', 'aria-label': `展开折叠的 ${hidden} 项`, onClick: () => expanded(true) }, '…', ), h('span', { class: 'ua-crumb-sep', 'aria-hidden': 'true' }, sep))); }); list.replaceChildren(...nodes); }; effect(render); // expanded 变化即重绘 return h('nav', { class: 'ua-breadcrumb', 'aria-label': '面包屑导航' }, list); }