desktop-patterns
应用程序外壳和结构布局模式组装在上面
desktop-compact原语。体系的中立者自定义
元素(点燃),零内置字符串,每个内容区域都有一个槽。
仅结构-原子控件(按钮,输入,对话框)属于 desktop-compact;本地的
平台集成(文件对话框、托盘、窗口控制)属于桌面运行时本身
适配器层,无论应用使用哪个运行时。这个包本身不调用任何平台API,
并且对任何特定的消费应用程序一无所知。
安装
要求节点≥22。
npm install @iyulab/desktop-patterns @iyulab/desktop-compact
只导入你使用的组件——每个船都是它自己的树摇子路径:
import '@iyulab/desktop-patterns/shell'
import '@iyulab/desktop-patterns/sidebar'
或者进口一切通过桶:
import '@iyulab/desktop-patterns'
使用
<dp-shell>
<dp-sidebar slot="sidebar" active-id="overview" header="My App" id="nav"></dp-sidebar>
<dp-toolbar slot="toolbar" heading="My App" show-toggle></dp-toolbar>
<dp-page>
<h2>Overview</h2>
<p>Main content.</p>
</dp-page>
</dp-shell>
items/bottomItems 是数组,所以它们被设置为JS属性(不是普通的HTML属性)-相同
作为任何Lit自定义元素:
document.querySelector<HTMLElementTagNameMap['dp-sidebar']>('#nav')!.items = [
{ id: 'overview', icon: '■', label: 'Overview' },
]
import { desktopMinWidth } from '@iyulab/desktop-patterns/breakpoints' // no elements registered
const shell = document.querySelector('dp-shell')!
const sidebar = document.querySelector('dp-sidebar')!
const toolbar = document.querySelector('dp-toolbar')!
const wide = matchMedia(`(min-width: ${desktopMinWidth}px)`)
let collapsed = false // the consumer's own state: a wide window's sidebar folded to its rail
function sync() {
sidebar.collapsed = collapsed && wide.matches // a drawer always shows the whole sidebar
toolbar.expanded = wide.matches ? !collapsed : shell.sidebarOpen
}
wide.addEventListener('change', sync)
sidebar.addEventListener('dp-sidebar-select', () => {
// consumer owns activeId / routing
})
sidebar.addEventListener('dp-sidebar-activate', () => {
shell.sidebarOpen = false // a drawer gives way to what was picked — the place already shown included
sync()
})
toolbar.addEventListener('dp-toolbar-toggle', () => {
if (wide.matches) collapsed = !collapsed
else shell.sidebarOpen = !shell.sidebarOpen
sync()
})
shell.addEventListener('dp-shell-sidebar-close', () => { shell.sidebarOpen = false; sync() }) // backdrop, Escape
在桌面断点(desktopMinWidth, 1024px - desktopMedia (对于组件的样式)
侧边栏是内容上方的一个抽屉,打开时打开 sidebar-open 设置;在它上面
侧边栏位于内容和旁边 sidebar-open 改变不了什么。使用相同的工具栏开关
两种宽度:在窄窗中打开抽屉,在宽窗中折叠侧边栏
铁轨倒塌(dp-sidebar collapsed). 打开开关 expanded 所以辅助技术听到了
它处于哪个状态。消费者拥有两种状态——启动时抽屉关闭,关闭时抽屉关闭
shell问(背景点击,逃离),当一个地方被选中,或一个狭窄的窗口保持覆盖。
外壳控制着窗口的高度,所以文档永远不会滚动——主内容框会滚动。
任何保存和恢复用户正在阅读的位置的操作都会到达该框
shell.mainElement;与 @iyulab/router,把它交给路由器的滚动恢复:
scrollRestoration: { container: () => document.querySelector('dp-shell')?.mainElement }.
被选中的项目旁边的列表
<dp-page fill max-width="full">
<dp-list-detail>
<nav slot="list" aria-label="People">…</nav>
<article>…the person picked…</article>
</dp-list-detail>
</dp-page>
dp-list-detail 把 list 槽位旁边默认槽位,各自滚动,列表
--dp-list-detail-list-width 宽度(默认情况下侧边栏的宽度——导航、列表和项目读取为
1:1:休息)。在桌面断点下面有一个窗格的空间:项目while detail-open 是
设置,否则列表。消费者拥有 detail-open -当一个项目被选中时设置它,并给予
该项目返回清除它的方式,如下所示 desktopMinWidth.
侧边栏项:链接、组、图标、动作
nav.items = [
{ id: 'overview', icon: '■', label: 'Overview', href: '/overview' }, // href → rendered as <a>
{
id: 'work', label: 'Work', collapsed: false, // a group: a disclosure over its items
items: [{ id: 'runs', icon: '▶', label: 'Runs', href: '/runs' }],
},
]
nav.pinnedItems = [{ id: 'settings', icon: '⚙', label: 'Settings' }] // places kept at the foot of the rail
nav.bottomItems = [
{ id: 'dense', icon: '▤', label: 'Dense mode', pressed: false }, // a toggle: aria-pressed, owned by you
{ id: 'sign-out', icon: '⎋', label: 'Sign out' }, // actions, not places
]
nav.setAttribute('nav-label', 'Main') // names the navigation landmark
href将项目呈现为链接,这样路由器的链接拦截器(或浏览器)就会进行导航。dp-sidebar-select仍然可以发射并且可以取消preventDefault()取消导航并保留 活动项目。修改后的单击(Ctrl/⌘/Shift/Alt,中间按钮)会在其他地方打开链接,但不选择任何内容。 项目没有href停留按钮和用户导航dp-sidebar-select.- 组 (
{ id, label, items, icon?, collapsed? })渲染为一个命名的role="group"根据披露 按钮(aria-expanded). 持有活动项的组始终处于打开状态。在倒塌的铁轨上,每一组 项目显示,由规则分隔。 - 图标:每个物品的图标是一个插槽,命名为
icon-<id>项目的退路是谁的icon文本槽任何图标 元素(<svg slot="icon-overview">)来替换它。 dp-sidebar-activate每次选择导航项时触发,活动项包括(在dp-sidebar-select当地点改变时;当这个节目被取消的时候就没有了)。关上抽屉就好了:挑着 已经显示的地方仍然意味着“带我去那里”,而且dp-sidebar-select不为它而火。- 固定的地方 (
pinnedItems)坐在轨道脚下,内部和外部相同的导航地标 滚动列表。它们是这样的地方items- - - - - -dp-sidebar-select,aria-current,href都适用-所以a 显示的设置或帮助页面被标记为当前页面。 - 底部的项是操作:每次点击触发
dp-sidebar-action(itemId) -每次,所以失败的操作可以 他被重新试了一次,然后再也不动了aria-current. (在0.2。他们开枪了dp-sidebar-select然后做了主动标记。) - 切换操作底部的动作
pressed({ id, icon, label, pressed: true | false })是一种设置 打开和关闭。按钮携带aria-pressed,打开按钮会得到一个安静的背景和一个重音图标 (--dp-sidebar-pressed-bg,--dp-sidebar-pressed-icon-color),这在倒塌的铁轨上仍能看到。以…命名 设置(“密集模式”),而不是点击(“打开密集模式”)。侧边栏不会翻转它-更改值 在dp-sidebar-action并设置bottomItems再一次。
每个组件都将其面向用户的文本作为纯属性/属性或插槽——没有
内置i18n层。查看每个组件的Storybook故事(npm run storybook)下载完整的API。
主题
在应用的全局样式表中导入这两个令牌表:
import '@iyulab/desktop-compact/tokens.css'
import '@iyulab/desktop-patterns/tokens.css'
颜色/间距/半径/排版都来自 desktop-compact的 --dc-* 令牌-这个包添加
只有结构尺寸 desktop-compact 对…没有概念--dp-sidebar-width,
--dp-sidebar-width-collapsed, --dp-header-height, --dp-list-detail-list-width). 每个组件都带有相同的回退
值,因此即使没有样式表也能正确呈现。
平台边界-阻力区域
dp-toolbar 暴露中立 drag-region 旗帜(不是本地的) draggable,这已经意味着
(其他的),但从不发出任何特定于平台的CSS。旗帜向主人反映
属性,但实际拖动面是专用的 part="drag-handle" 起动之间的垫片
组和 actions 槽-从来没有整个主机-所以映射永远不能达到切换
按钮或插槽操作。将该部分映射到实际的窗口拖动行为是控件的工作
平台适配器层,以及如何取决于运行时。webview在哪里尊重CSS
app-region 属性(基于chromium的运行时,如Electron),映射是一个规则:
dp-toolbar[drag-region]::part(drag-handle) {
-webkit-app-region: drag;
}
而从指针事件或标记属性开始窗口拖动的运行时则会执行此操作 同样的部分,不在主机上。
dp-shell 不标记任何自己的拖动区域-它对插入的内容没有意见
toolbar,因此拖拽句柄的所有权完全保留在那里呈现的任何东西(通常是
dp-toolbar)。
组件
| 组件 | 描述 |
|---|---|
dp-page | 具有居中、最大宽度列的可滚动内容区域——每个视图呈现到的页面主体。与 fill 它不滚动,并且它的列与区域一样高,对于滚动其自己的部分的视图(打开的项旁边的列表) |
dp-list-detail | 一个列表旁边的项目从中选择(主-细节)-每个窗格滚动自己;在1024px以下,每次一个窗格 detail-open 设置 |
dp-sidebar | App-shell导航栏-展开/折叠状态,可选的固定底部组,a footer 任何消费者想要的频道 |
dp-toolbar | 应用程序外壳标题栏 heading/subtitle一个可选的侧边栏开关(窄窗口中的抽屉,宽窗口中的折叠),一个右侧 actions 槽 |
dp-shell | 顶层布局构成 sidebar/toolbar/banner/main-content区域,在1024px的断点下面有一个响应式抽屉(背景+覆盖侧边栏) |
dp-shortcut-overlay | 只显示键盘快捷键帮助面板- a shortcuts 列表+ open 国旗;没有注册/绑定基础设施(仍然是消费者的) |
运行 npm run storybook 以交互方式浏览每个组件,包括 dp-shell 构成真正的
dp-sidebar+dp-toolbar+dp-page 在一起。
可访问性
每个组件都附带一个可访问性测试,作为其测试套件的一部分(npm test). 地标性建筑
遵循ARIA APG惯例- dp-sidebar 使用语义 <nav> (没有冗余的主机级角色),
dp-shortcut-overlay 使用 role="dialog" 与 aria-labelledby 指向其可见的标题。
开发
npm install
npm test # @web/test-runner, real Chromium
npm run typecheck
npm run guard # platform-neutrality scan (src/ must not call or name a platform API)
npm run build # per-component ESM output, type declarations
npm run storybook # interactive component browser
tsc 是TypeScript 7 (@typescript/native),而 typescript 解析为 @typescript/typescript6:
TypeScript 7还没有JavaScript编译器API,而声明build会读取一个。别名是
一次 vite-plugin-dts 仅在TypeScript 7上运行。
@iyulab/desktop-compact 是一个 同行 依赖:这个包读取它的 --dc-* 令牌,但从不
导入它的代码,这样使用者安装一个副本,两个包共享它。常规依赖
会给消费者一个更新的吗 desktop-compact 次要的第二个,未使用的副本嵌套在此
包中。
许可证
麻省理工学院