跳到正文
册页

图示

DiagramToolbar - 图工具条

一排属于它下面那个界面的动作。

用法

什么时候用它

FloatingIconButton 是一个钉住的动作。这个是装好几个的容器,让它们读起来像一个物件,而不是散落的几颗按钮。

来自@misoto22/folio/diagrams

TSX
import { DiagramToolbar } from '@misoto22/folio/diagrams'

说明

一排属于它下面那个界面的动作。

这个包里已经有 FloatingIconButton 来放一个钉住的动作。这个是装好几个的容器——那是另一个问题,因为几颗钉住的按钮需要读起来像一个物件而不是一堆散落:一块底、一条边框、组与组之间的细线,以及一个 role="toolbar",让读屏软件播报的是一个工具条而不是六个互不相干的按钮。

role="toolbar" 同时也改变了键盘约定,调用方应该知道自己选了什么:方向键被期待在控件之间移动,Tab 被期待离开这一条。这个组件不替你实现漫游焦点——一条三颗按钮、Tab 逐个访问的工具条是诚实且够用的——所以如果这不是你要的,就传 placement="inline",并用一个普通的 <div> 跳过这个 role。

结构

DiagramToolbar anatomy
部件说明
条必填一个横向排列的 role="toolbar",落在一块带细线和系统圆角的面板底上。一块底加一圈边,正是让六颗钉住的按钮读作一个物件、而不是六颗按钮的原因。
名称必填label,它成为这条工具条的无障碍名称。这是一个必填、没有默认值的属性,因为一条只播报自己角色、别的什么都不说的工具条,在读屏软件那里和下一条根本分不出来。
分组必填DiagramToolbarGroup:一串相关的控件,靠行首那一侧的一条细线分隔,而不是靠间距。第一组不带这条线,所以工具条不会以一条分隔线开头。
控件必填children,尺寸由调用方给成什么样就是什么样。这条工具条只设间距,自己不画任何状态——放进来的按钮,和它在别处是同一颗按钮。
钉住placement="floating" 把这条工具条移出文档流,钉在最近那个真正设置了定位的祖先的上边缘,层级取 sticky 那一档——在每一块锚定面板和每一个对话框之下——由 align 挑哪个角。placement="inline" 让它留在文档流里,什么都不钉。

实践建议

推荐

  • label 要写这条工具条干什么,而不是它长什么样——写 "Diagram actions",不要写 "Toolbar"——否则一个既有图又有表格的页面,会播报出两条工具条,一条都区分不了。
  • 要分隔就用 DiagramToolbarGroup,不要靠拉开间距。那条线是画在分组上的;而宽到能读作一道界限的间距,也就宽到让这条工具条不再读作一个物件——而把这些控件收拢起来,图的正是这一点。
  • 控件数量控制在少数几个。这里没有漫游焦点,所以 Tab 会逐个访问:条上放十个动作,就是读者和页面其余部分之间隔着十个停靠点。

避免

  • 浮动的工具条是压在界面上面的,不是待在它旁边。图的那个角上有什么,就被它盖住什么——要么给界面留出内边距,要么用 placement="inline",让这条工具条自己占一行。
  • role="toolbar" 除了是一个名字,也是一份关于键盘的承诺:读者被告知这是一条工具条,就会指望方向键能在里面移动。只是碰巧挨在一起的两颗按钮,用一个普通的 div 就不会许下这个组件随后必须兑现的承诺。
  • 浮动的工具条是画布在同一个定位包裹元素里的兄弟节点,绝不是它的子节点。交给 DiagramCanvas 的一切都渲染在那层被 transform 过的舞台里面,所以作为子节点传进去的工具条,会跟着它本来要控制的那张图一起缩放、一起平移走掉——而在完全没有定位祖先时,它会钉到页面上方碰巧带定位的那个盒子上。

示例

default

两组共四个控件,用一条细线分开、而不是用空白——一道宽到读起来像边界的空隙,同时也宽到让这条栏不再读起来像一个整体,而把这些控件收进一条栏里,本来就是唯一的理由。外层容器带着 role="toolbar",这也是为什么 label 是必填的:没有它,读屏软件会播报出一个没有名字的工具栏。这里没有实现漫游焦点,所以 Tab 会依次走过四个按钮;当这不对时,就换成一个普通的 div、把这个 role 去掉。

floating over a canvas

placement="floating" 把这条栏钉在它所作用的那片表面之上,而 align 挑的是哪个上角。它必须是画布在一个定位容器里的兄弟节点,不是它的子节点:任何作为 children 传给 DiagramCanvas 的东西都会进到那个被变换的舞台里面,所以一条交给它的工具栏,会跟着它本该控制的那张图一起缩放和平移。而导出菜单取 ref 的正是同一层容器,于是这幅画和那个保存它的按钮是同一个对象。

Request path3 elements and 2 relationships.CLOUDCloudFrontCDNSERVICEAPIFastAPIDATAPostgres

3 elements and 2 relationships.

  • CloudFront (cloud) — CDN
  • API (backend) — FastAPI
  • Postgres (database)
  • CloudFront → API: HTTPS
  • API → Postgres: SQL

Drag to pan. Plus and minus zoom, zero resets, the arrow keys pan.

a different set of controls

同一条栏,装着的控件改变的是这张图说什么、而不是它被怎么看——一次引导式阅读,一次一章。当前这一章除了用 secondary 变体标出来,还带着 aria-pressed,因为变体是颜料上的差别,而读屏软件看不见颜料。第二组装的是一个读数、不是一个控件:group 就是一个普通的 div,而那条细线正是说明这个计数属于这条栏、而不是跟在它后面拖着的东西。

组成部分

在调用处组合,而不是通过 props 配置——所以这个组件没有预料到的布局,依然表达得出来。

DiagramToolbarGroup

工具条内部一组相关控件。

用一条细线而不是留白来分隔,因为一段大到能读成组边界的间距,也大到会让这一条不再读成一个物件——而把这些控件收进一条工具条,本来就是为了这个。

这个组件没有自己的属性。

无障碍

  • role="toolbar" 播报的是一个工具条,而不是六个互不相干的按钮。它不实现漫游焦点——Tab 会逐个访问,对只有三个控件的一条来说这是诚实的做法。