import { h, signal } from '@ui-arch/frame'; import type { LayoutProps } from './types.js'; // 侧栏展开态缺省宽度(px):够放下四个汉字的菜单项,再窄就要靠折叠态图标栏 const SIDER_DEFAULT = 220; /*** 布局骨架:页头 / 侧栏 / 内容 / 页脚四区一次成型,侧栏可折叠(受控或自管);只管骨架与间距,导航内容交给 menu / anchor ***/ export function layout(props: LayoutProps, ...children: unknown[]): HTMLElement { const collapsed = props.collapsed ?? signal(false); // 折叠态:外部给了信号就双向绑定,没给就内部自管 const width = props.siderWidth ?? SIDER_DEFAULT; // 侧栏宽度:折叠时由样式表收到 0,这里只给展开值 const toggle = (): void => { collapsed(!collapsed()); }; // 折叠开关:只翻信号,视觉由类名响应 const rootCls = ['ua-layout', props.sticky ? 'is-sticky' : ''].filter(Boolean).join(' '); // 根类名:吸顶页头是同一个容器上的修饰类 const header = props.header === undefined ? null // 没有页头就不渲染容器,避免多一层空盒子影响 :first-child 选择器 : h( 'header', { class: 'ua-layout-header' }, props.collapsible ? h('button', { class: 'ua-layout-toggle', type: 'button', 'aria-expanded': () => String(!collapsed()), 'aria-label': '折叠或展开侧栏', onClick: toggle }, '⟨⟩') : null, props.header, ); const sider = props.sider === undefined ? null // 没有侧栏时内容区独占整行,省掉一条 220px 的空白 : h('aside', { class: () => `ua-layout-sider${collapsed() ? ' is-collapsed' : ''}`, style: `width:${width}px`, // 展开宽度内联,折叠后的收拢宽度交给类名(0 + overflow hidden) 'aria-hidden': () => (collapsed() ? 'true' : null), // 折叠后视觉消失,读屏也不再报里面的菜单 }, props.sider); const content = h('main', { class: 'ua-layout-content' }, ...children); const body = sider ? h('div', { class: 'ua-layout-body' }, sider, content) : content; // 无侧栏时不套 body 层 const footer = props.footer === undefined ? null : h('footer', { class: 'ua-layout-footer' }, props.footer); return h('div', { class: rootCls }, header, body, footer) as HTMLElement; }