33 lines
2.2 KiB
TypeScript
33 lines
2.2 KiB
TypeScript
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;
|
||
}
|