iter(g265): 新增布局骨架组件 layout
This commit is contained in:
@@ -76,3 +76,4 @@ export { countdown } from './countdown.js';
|
||||
export { kbd } from './kbd.js';
|
||||
export { highlight } from './highlight.js';
|
||||
export { inputTag } from './inputTag.js';
|
||||
export { layout } from './layout.js';
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
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;
|
||||
}
|
||||
@@ -1010,3 +1010,14 @@ export interface InputTagProps {
|
||||
ariaLabel?: string; // 输入框无障碍标签;缺省「标签输入」
|
||||
onChange?: (tags: string[]) => void; // 标签集合变化回调(参数是变化后的完整集合)
|
||||
}
|
||||
|
||||
/*** 布局骨架入参 ***/
|
||||
export interface LayoutProps {
|
||||
header?: unknown; // 页头内容(文案 / 节点);缺省不渲染页头
|
||||
sider?: unknown; // 侧栏内容(通常是 menu);缺省则内容区独占整行
|
||||
footer?: unknown; // 页脚内容;缺省不渲染页脚
|
||||
siderWidth?: number; // 侧栏展开宽度(px);缺省 220
|
||||
collapsible?: boolean; // 是否可折叠:true 时页头左侧出现折叠开关
|
||||
collapsed?: Signal<boolean>; // 折叠态信号;传入则双向绑定(外部可读数),缺省内部自管
|
||||
sticky?: boolean; // 页头是否吸顶;缺省 false(长内容里建议打开)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user