树视图
由可以展开和折叠的项组成的层级结构。每深一级,行就多缩进一些;凡是包含其他项的行,开头都有一个 V 形箭头。它可以只供阅读和展开,也可以在其中选择一项或多项;它在焦点顺序中只占一个停靠点。
- 类别
- 容器
- 令牌
comp.tree-view- 层级
- 基础
- Web
tanrei-tree-viewschedule - SwiftUI
TanreiTreeViewradio_button_unchecked - Jetpack Compose
TanreiTreeViewradio_button_unchecked - Flutter
TanreiTreeViewradio_button_unchecked - React Native
TreeViewradio_button_unchecked
生成自 docs/status/status.json, specs/tree-view.spec.json
文件树:按文件夹所在的行,或用方向键来展开它
- tokens/唯一可信来源,手工编辑
- ref/
- sys/
- comp/
- tanrei.resolver.json
- packages/
- web/src/generated/tokens.css
- react-native/src/generated/tokens.ts
树视图显示一个供用户浏览或在其中做选择的结构:项目的文件、设计系统的令牌、产品的文件夹。包含其他项的行带有 V 形箭头,可以展开和折叠。设置 selection 后,可以选择一项或多项,树会告诉你选中的是哪些。
它不是导航。产品的各个去处是导航抽屉,页面的各个标题是目录,折叠起来的内容是折叠面板。那些组件保留链接和按钮,每一个都是焦点顺序中独立的停靠点。树只占一个停靠点,在其中用方向键移动,因为它可能有成千上万行。
在 Web 上,它是包在你自己的嵌套列表外面的 <tanrei-tree-view>。列表留在文档里,所以在没有脚本运行的地方,页面会把整个层级结构显示为嵌套列表。其他平台尚未实现。
<link rel="stylesheet" href="@tanrei/web/icons.css" />
<link rel="stylesheet" href="@tanrei/web/tree-view.css" />
<tanrei-tree-view label="项目文件" selection="single" value="src/index.ts">
<ul>
<li data-value="src" data-open>
<span>
<span class="tanrei-icon" aria-hidden="true">folder</span>
<bdi>src<small data-supporting-text>发布的内容</small></bdi>
<span data-trailing-text>12 个文件</span>
</span>
<ul>
<li data-value="src/index.ts"><span><bdi>index.ts</bdi></span></li>
</ul>
</li>
<li data-value="node_modules" data-expandable><span><bdi>node_modules</bdi></span></li>
</ul>
</tanrei-tree-view>
<!-- 可以筛选行的树:在行上按“/”会移到它上方的输入框。文字由你提供。 -->
<tanrei-tree-view label="项目文件" filterable filter-label="筛选文件" filter-placeholder="筛选"
filter-count-text="匹配项:{count}" filter-empty-text="无匹配项" filter-clear-label="清除">
<ul>…</ul>
</tanrei-tree-view>
<script type="module">
import '@tanrei/web/components/tree-view';
const tree = document.querySelector('tanrei-tree-view');
tree.addEventListener('change', (event) => showFile(event.detail.value));
tree.addEventListener('toggle', (event) => {
if (event.detail.open && event.target.dataset.expandable !== undefined) loadItemsInto(event.target);
});
</script>import { TreeView } from '@tanrei/react';
import '@tanrei/web/tree-view.css';
<TreeView label="项目文件" selection="single" value={path} onChange={(event) => setPath(event.detail.value)}>
<ul>
{folders.map((folder) => (
<li key={folder.path} data-value={folder.path} data-open={folder.open ? '' : undefined}>
<span><bdi>{folder.name}</bdi></span>
<ul>
{folder.files.map((file) => (
<li key={file.path} data-value={file.path}><span><bdi>{file.name}</bdi></span></li>
))}
</ul>
</li>
))}
</ul>
</TreeView>@tanrei/web/tree-view.css 在任何脚本运行之前就画出这棵树,脚本加载时不会有任何东西发生位移。V 形箭头是图标集的字形,需要图标字体。
| 部件 | 在 Web 上 |
|---|---|
| 树的名称 | label 属性,使用你的产品的语言。 |
| 一个项 | 一个 <li>。它的第一个子元素就是它的行:一个 <span> 或一个 <div>。 |
| 标签 | 行的文本,放在一个元素里:<bdi>。它可以包含行内内容,例如一段行内代码。 |
| 辅助文本 | 带 data-supporting-text 的元素,位于标签的元素之内、标签之后。 |
| 图标 | 行里的一个 .tanrei-icon,里面是字形的名称。树自己没有代表文件夹或文件的字形。 |
| 展开时的图标 | 父项的 .tanrei-icon 上的 data-open-icon:展开期间画的字形,比如 folder 旁边的 folder_open。 |
| 后置内容 | 行里带 data-trailing 的元素:一个标签(Tag)、一个色块。树只安放它,不给它加样式。 |
| 后置文本 | 行里带 data-trailing-text 的元素:一个数量、一个大小、一个值。只占一行。 |
| 项自己的子项 | 行之后、同一个 <li> 之内的一个 <ul>。 |
行也可以把标签直接写成纯文本,外面不包 <bdi>。行有辅助文本时,以及名称的方向可能与页面不同时(例如阿拉伯语页面上的文件名),请使用 <bdi>。
写在 <li> 上 |
含义 |
|---|---|
data-value |
用来标识这个项。没有它,这个项就无法被选择。在树内必须唯一。 |
data-open |
父项显示它的子项。用户展开或折叠父项时,树会设置或移除它。 |
data-expandable |
这个项将会包含你尚未给出的子项。它会得到一个 V 形箭头;toggle 表明它已展开时,再给出它的子项。 |
data-loading |
你正在获取它的子项。它展开期间,下方会画出一个加载行,带有树的 loading-label。 |
data-disabled |
用户无法选择、展开或折叠它。方向键仍然可以到达它。 |
data-supporting-position |
设为 inline 时,把辅助文本放在标签后面、同一行上。不设时,辅助文本在标签下方。 |
| 事件 | 何时 | detail |
|---|---|---|
change |
用户更改了选择。在树上触发。 | 更改之后的 value(单选)或 values(多选) |
toggle |
用户或浏览器的页面内查找展开或折叠了一个父项。在 <li> 上触发,并且冒泡。 |
open |
rename |
用户就地给一个项起了新名字。在 <li> 上触发,并且冒泡。 |
label(去掉首尾空白的新名字)和 previous |
create |
用户为 create() 请求的新项起了名字。在树上触发。 |
label、parent、within 和 after(它所在的和它之前的 <li>,没有则为 null) |
filterchange |
用户更改了筛选文本:键入、删除、粘贴、清除按钮或 Escape。在树上触发。 | value,即新的文本(filter-value 已经是这个值) |
你自己做出的更改,不会触发这些事件中的任何一个。
| 方法 | 作用 |
|---|---|
edit(li) |
像 F2 一样,在一个项的名字上打开输入框。仅在树为 editable 时有效。 |
create({ parent, icon }) |
在选中项旁边加一行新行,带一个空输入框用来起名。返回是否加上了。 |
focusFilter() |
把焦点移到筛选输入框,并选中其中的文本。仅在树为 filterable 时有效。可以把它绑定到你自己的、整个窗口都有效的按键上。 |
选中一项:按行是选择,按 V 形箭头是展开
- 参考212
- palette.neutral
- 系统388
- color64
- primary#1f1f1f
- on-primary#ffffff
- outline-variant#d4d4d4
- space19
- color64
构成
| 部件 | 说明 |
|---|---|
1 tree | The column that holds the rows, named by its label. It draws no fill, no edge and no space of its own, and does not scroll. One stop in the focus order. |
2 filter | The filter field, while filterable is true and the tree has items: a Text input of type search above the first row, with the search glyph at its start and a clear button at its end while it holds text. Pinned at the top of the scrolling box the tree stands in, above the pinned rows. A stop of its own, before the tree. |
3 filter.status | The line at the top of the results while a filter is applied: how many rows match, in the product's filterCountText, unless filterCountHidden; or, where no row matches, the product's filterEmptyText. Takes no focus and no state. |
4 item | One thing in the hierarchy (an item of the item kind): its row and, while it is a parent and open, the rows of the items it holds, which begin one level further in. |
5 container | An item's row: the box that carries hover, focus and press and the selected fill. With highlight indented it begins further from the start edge for each level; with highlight full it spans the tree's width. It ends at the tree's end edge; the hit area is the tree's whole width. |
6 chevron | The mark of a parent's open and closed, where chevron says: at the start of the row (the icon set's chevron_right, pointing toward the end of the line while closed and turned a quarter turn to point down while open) or at its very end, after the trailing content (the icon set's expand_more, turned a half turn while open, as the navigation drawer's and the accordion's). A leaf keeps the box empty, so that the names (or the values) of one level stand in one column. None where chevron is none, or where the tree is not expandable. Hidden from assistive technology, which is given the state in words. |
7 icon · 可选 | A glyph of the system's icon set before the label, by name: what kind of thing the item is (a folder, a file, a group); a parent may name a second glyph for while it is open (folder_open beside folder). Where no icon is named, its room is kept while another item of the level has one (iconSpace). The product's choice; decorative. |
8 label | The item's name and its accessible name. One line, cut with an ellipsis where it does not fit (the whole name in a tooltip); with labelWrap it wraps instead and is never cut. |
9 supporting-text · 可选 | A few words about the item: below the label (supportingPosition below, the default) or after it on its line (inline), quieter than the label. One line, cut with an ellipsis; with supportingWrap it wraps instead. |
10 trailing · 可选 | The trailing slot's content at the end of the row: a tag, a swatch, a mark that the item changed. Not interactive; part of the row. |
11 trailing-text · 可选 | A short value at the end of the row, after the trailing content: a count, a size, the present value. One line. |
12 guide · 可选 | A hairline down from an open parent beside the rows it holds, under the parent's first leading mark, where guides says: always, or while the pointer is over the tree or focus is in it. Decorative. |
13 loading-row · 可选 | A row one level further in under an open parent that is fetching what it holds: a loading indicator and the words the product gives. Not an item: it takes no focus and cannot be chosen. |
14 full-text-tooltip · 可选 | The whole of a name or a note that is cut (TRV-50): a plain tooltip with the label, or a rich one with the label as its title and the supporting text, beside the row on hover and on keyboard focus. Only while something is cut. |
15 pinned-row · 可选 | The row of an open parent above the rows that are shown, held at the top of the scrolling box while what it holds scrolls under it (stickyParents): the same row, with an opaque fill and a hairline under the last one (TRV-53). |
16 edit-field · 可选 | While an item's name is being edited, or a new item named: an Embedded text input on one line in the place of the label and the supporting text, inside a box the tree draws with the focus indicator around it. The rest of the row stays as it was. |
17 new-row · 可选 | A row the tree adds for the time a new item is being named: at the level of the selected item, after its rows, with a closed chevron for a parent, the glyph the product names and the edit field. Not an item: it is gone when the name is given or given up. |
18 focus-ring | Gap-then-ring indicator shown for keyboard focus of an item, drawn inside its row's container. |
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
label | string | "" | The accessible name of the tree: what the hierarchy is ("Project files", "Every token"). Required: a tree is one stop in the focus order and is announced by this name. |
selection | none | single | multiple | "none" | Whether people choose items. none: a tree that is read and opened — a figure of a file system in a page, a structure to look through; its leaves answer nothing and its parents open and close. single: one item is chosen (the file shown in the pane beside the tree, the token being edited). multiple: any number are chosen, each by itself. An item is chosen by its value; an item without one cannot be chosen. |
value | string | null | The value of the selected item while selection is single; null selects none. The tree sets it when an item is chosen and emits change; the product may also set it. |
values | strings | | The values of the selected items while selection is multiple: a list of strings (the platforms take a set). An empty list selects none. |
density | regular | compact | "regular" | How much room a row takes, in a table's and an accordion's words: the row's least height, its paddings and its glyphs' size, and never the gap between the icon and the label (iconGap). regular (a 48 row with a 14 label in the default system, a navigation drawer's row) is the tree of a product used by touch as well as by pointer. compact (a 32 row with a 13 label, a table's compact row) is for a tool used with a pointer, where hundreds of rows are looked through, and for a small tree inside a page of text. For touch a row is never lower than the touch target in either. A design system may add values. |
chevron | start | end | none | "start" | Where a parent's chevron stands. start: at the start of its row, before the icon (a file tree, the tree pattern). end: at the very end of the row, after the trailing content and the trailing text (a settings tree, the navigation drawer's place). none: no chevron; a parent that names an icon and an open icon shows open and closed by its glyph, which a press opens and closes, and the arrow keys always open and close (TRV-44). |
expandable | boolean | true | Whether parents open and close at all. false: every parent is shown open, no chevron is drawn, nothing toggles, and no row is exposed as expanded or collapsed — a structure that is always shown whole (an outline, a table of contents of a document). An item's own expandable is read only while this is true. |
guides | none | shown | hover | "none" | Hairlines down from each open parent beside the rows it holds, for a deep tree where the indent alone no longer shows which rows belong together. none: no lines. shown: always. hover: while a pointer is over the tree or keyboard focus is in it, faded in and out. Decorative. On the web the bare attribute, and true, are shown. |
indent | number | null | How far each level begins further in than the one above it. null (auto, the default): a child's start stands where its parent's label begins, so the step follows what the parent's row holds before its label — the chevron at the start, the icon or the icon's room. A number: that many points (px, dp), the product's explicit choice; 0 sets no level in, and then no guide is drawn. A design system sets what auto rests on through the parts' tokens and the level's indent token. |
iconSpace | auto | always | never | "auto" | Whether a row without an icon keeps the icon's room, so that the labels of a level stand in one column. auto: it does while another item of the same level has an icon. always: every row keeps it. never: only a row with an icon has the room. |
iconGap | number | null | The room between the icon (or its kept room) and the label, in points. null (auto): comp.tree-view.icon.gap, which no density changes. A number is the product's explicit choice. |
highlight | indented | full | "indented" | How far a row's fill reaches — the selected fill and the hover and press layers, and the focus ring with them. indented: from where the row's level begins to the tree's end edge, so that the fill shows the level. full: from the tree's start edge to its end edge, as file browsers draw it. The hit area is the whole width either way. |
labelWrap | boolean | false | false: a label is one line, cut with an ellipsis where it does not fit, and the whole of it is shown in a tooltip on hover and on keyboard focus while it is cut. true: it wraps onto as many lines as it needs and is never cut. The accessible name is the whole label either way. |
supportingWrap | boolean | false | As labelWrap, for the supporting text: one line cut with an ellipsis (the whole in the tooltip), or wrapping. A supporting text beside the label (inline) shares the label's line and follows labelWrap. |
stickyParents | boolean | true | Keeps the rows of the open parents that the rows at the top of the scrolling box belong to pinned at its top while what they hold scrolls under them, as an editor's sticky scroll does, so that a person deep in a long tree sees where they are. The pinned rows are the rows themselves and work as they do. On by default in the tree view (off in the navigation drawer, which is short). A tree in no scrolling box pins nothing. |
stickyMax | number | 2 | The most rows pinned at once, one per level; where more parents are above what is shown, the innermost of them are the ones pinned. 0 pins none. |
editable | boolean | false | Lets people rename items in place: a double-click on a row's label, or F2 on a focused row, puts the label into a text field; Enter or leaving the field gives the new name, Escape gives it up. The tree changes no label itself: it emits the item's rename and the product sets the label. New items are named the same way whatever this says, in a row the product asks for (TRV-48). |
editLabel | string | "" | The accessible name of the field in which a name is edited or a new item named ("Name"). Tanrei ships no words of its own. Required where editable is true or a new item may be asked for. |
loadingLabel | string | "" | The words of a loading row ("Loading…"), drawn beside its loading indicator and used as the indicator's name. Tanrei ships no words of its own. Required where any item may be loading. |
filterable | boolean | false | Draws a filter field above the rows: what a person types there narrows the tree to the rows whose label holds it, opening for the time being the groups that hold them, and the tree comes back as it was when the field is emptied. Off by default: a product that filters on a server, ranks, or matches in another way leaves it off and gives the tree the items that match, as before. |
filterValue | string | "" | The filter text: what the filter field holds. The tree sets it when the person types and emits filterChange; the product may set it too (a filter given back when a page is rendered again). Empty, or only white space, applies no filter. |
filterLabel | string | "" | Names the filter field for assistive technology, in the product's language: what it narrows ("Filter navigation", "Filter files"). Required while filterable is true. It names this one field, so it is not one of the product's words set once. |
filterPlaceholder | string | "" | The hint the filter field shows while it is empty ("Filter"), in the product's language. Never a replacement for filterLabel. The product's: Tanrei has no words for it. Empty reads the product's words; with neither, the field shows no hint. |
filterEmptyText | string | "" | What the tree draws at the top of the results and says while a filter is applied and no row matches ("No matches"), in the product's language. The product's: Tanrei has no words for it. Empty reads the product's words; with neither, nothing is drawn in its place and it is reported. |
filterCountText | string | "" | What the tree draws quietly at the top of the results (TRV-65) and says to assistive technology once the result of a filter has settled (TRV-66), with {count} where the number of rows that match goes ("Matches: {count}"), in the product's language. filterCountHidden leaves it undrawn and still said. The product's: Tanrei has no words for it. Empty reads the product's words; with neither, no count is drawn or said and it is reported. |
filterCountHidden | boolean | false | Does not draw the count at the top of the results (TRV-65); it is still said to assistive technology (TRV-66). The words for a filter that matches nothing are drawn either way. A tree whose results are counted elsewhere on the screen, or where the rows left say enough, turns it on. |
filterClearLabel | string | "" | The accessible name of the filter field's clear button ("Clear"), in the product's language. Required while the field can hold text. Empty reads the product's words. |
filterShortcut | strings | / | The key that, pressed while focus is on a row, moves focus to the filter field, in the keyboard shortcut component's key names, one chord (["/"], the default). It is a key of the tree's rows, as F2 is: the tree listens for it nowhere else and draws no hint of it (TRV-62). A product that wants a key for the whole window binds it and asks the tree to focus its filter (TRV-63). An empty list binds nothing, and "/" is then typed as any other character (TRV-23). |
事件
| 事件 | 说明 |
|---|---|
change | The user changed the selection — by a press, a key, a range or select-all; carries value (single) or values (multiple) after the change, once per gesture. |
create | The user named the new item in the row the product asked for (TRV-48): carries its label (not empty, with no white space at its ends), whether it is a parent or a leaf, the item it goes under (none for the first level) and the item it follows (none where it is the first). Emitted exactly once; the tree takes its own row away as it emits it, and the product adds the item. |
filterChange | The person changed the filter text — typing, deleting, pasting, dictating, an input method committing text, the clear button or Escape; carries the new text. Not emitted when the product sets filterValue. |
条目
该组件重复排列的条目种类。测试样例按顺序列出条目;各平台以各自的惯用方式为每个种类命名。
item · 分组 — 包含条目
One thing in the hierarchy: a row with a label and, where it holds other items, a chevron that opens and closes them. An item that holds items, or says it will, is a parent; every other item is a leaf. The fixture's content is unused.
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string | "" | What identifies the item: the tree's value (single) or values (multiple) select the item whose value they hold, and change carries it. Unique within the tree. Empty, the item cannot be chosen: a press on its row opens or closes it where it is a parent and does nothing where it is a leaf. |
label | string | "" | Its name, drawn in its row and used as its accessible name. Plain text; the label slot takes its place where the product needs inline content. Required unless the label slot is filled. |
supportingText | string | "" | A few words about the item ("the source of truth, edited by hand"), drawn below the label or beside it as supportingPosition says, and read as the item's description. Empty draws the label alone. |
supportingPosition | below | inline | "below" | Where the supporting text stands: below the label, on a line of its own (the default), or inline, after the label on the label's line. |
trailingText | string | "" | A short value at the end of the row: a count, a size, the present value. One line; read after the supporting text as the item's description. |
icon | string | "" | The name of a glyph in the system's icon set, drawn before the label: what kind of thing the item is. The tree has no glyphs of its own for a folder or a file: the product names them. Decorative. |
openIcon | string | "" | The name of a second glyph, drawn in the icon's place while the parent is open (folder_open beside folder). Ignored on a leaf and while icon is empty. Where the tree's chevron is none, the two glyphs are what shows open and closed, and the glyph is what a press opens and closes. Decorative. |
open | boolean | false | Whether the items a parent holds are shown under its row. Set by the product for a parent that starts open; the web sets it itself when the user opens or closes the parent, and a controlled platform leaves it to the caller. Ignored on a leaf. |
expandable | boolean | false | Says that the item holds items the product has not given yet: it is drawn and exposed as a parent, with a chevron, and its toggle tells the product to give them. An item that holds items is a parent whatever this says. Read only while the tree's expandable is true. |
loading | boolean | false | Says that the product is fetching what the item holds: while the item is open a loading row is drawn under it, after any items it already holds, and the item is exposed as busy. Ignored on a leaf. |
disabled | boolean | false | Makes it unavailable: it keeps its place and its open, draws its disabled look, and cannot be chosen, opened or closed by the user. The arrow keys still reach it, and the items it shows keep their own disabled. |
| 插槽 | 说明 |
|---|---|
label | The product's own label content in place of the label prop: text with inline content (a code span, a part of the name marked as a match). The tree draws it in the label's style and color. No control. |
trailing | Content at the end of the row, before the trailing text and before an end chevron: a tag, a swatch, a mark. The tree places it and gives it no style. No control; it is part of the row and is read as part of the item's description where it has text. |
| 事件 | 说明 |
|---|---|
toggle | The parent opened or closed: by the user from its chevron, its row or the keyboard, or by the platform's search finding text under it; carries its new open state. Emitted exactly once per change, when the change begins. The moment to give the items of a parent that is expandable. |
rename | The user gave the item a new name in place (TRV-46): carries the new label — not empty, with no white space at its ends, and not the label it had — and the label it had. Emitted exactly once per name given. The tree draws the label it had until the product sets the new one; a product that refuses the name leaves the label as it is. |
| 规格 | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| 条目 | li | TanreiTreeItem | TanreiTreeItem | TanreiTreeItem | TreeItem |
value · 属性 | data-value | tag | value | value | value |
label · 属性 | (row text) | title | label | label | label |
supportingText · 属性 | data-supporting-text | supportingText | supportingText | supportingText | supportingText |
supportingPosition · 属性 | data-supporting-position | supportingPosition | supportingPosition | supportingPosition | supportingPosition |
trailingText · 属性 | data-trailing-text | trailingText | trailingText | trailingText | trailingText |
icon · 属性 | tanrei-icon | icon | icon | icon | icon |
openIcon · 属性 | data-open-icon | openIcon | openIcon | openIcon | openIcon |
open · 属性 | data-open | isExpanded | expanded | expanded | open |
expandable · 属性 | data-expandable | isExpandable | expandable | expandable | expandable |
loading · 属性 | data-loading | isLoading | loading | loading | loading |
disabled · 属性 | data-disabled | disabled | enabled | enabled | disabled |
toggle · 事件 | toggle | isExpanded | onExpandedChange | onExpansionChanged | onToggle |
rename · 事件 | rename | onRename | onRename | onRename | onRename |
| 备注 | A native <li> whose first child element is its row (a <span> or a <div>) and whose next child, where it is a parent, is a <ul> of its items: <li data-value="src" data-open><span><i class="tanrei-icon">folder</i>src <small data-supporting-text>what is published</small> <span data-trailing-text>12 files</span></span><ul>…</ul></li>. The row's text is the label (the label slot: text and inline content); inside the row, an element with data-supporting-text holds the supporting text, one with data-trailing the trailing slot's content and one with data-trailing-text the trailing text; a .tanrei-icon element first in the row is the icon. data-open, data-expandable, data-loading and data-disabled are boolean attributes of the <li>, which the element mirrors as aria-expanded, aria-busy and aria-disabled; the element sets and removes data-open itself when the user opens or closes the parent. toggle is dispatched on the <li> and bubbles, with detail.open. data-open-icon is an attribute of the .tanrei-icon element (<i class="tanrei-icon" data-open-icon="folder_open">folder</i>), so that the stylesheet draws the open glyph before the element is defined. rename is dispatched on the <li> and bubbles, with detail.label and detail.previous. | TanreiTreeItem(_ title: String, supportingText: String = "", trailingText: String = "", icon: String? = nil, isExpanded: Binding<Bool>, isExpandable: Bool = false, isLoading: Bool = false) { children }, tagged with .tag(value); a leaf has an initializer without the binding and the children. Toggling writes the binding, as TanreiNavigationDrawerGroup and TanreiAccordionItem do. A second initializer takes the label as a @ViewBuilder and trailing: as another (the slots). disabled follows .disabled(_:). | TanreiTreeItem(label, value = "", expanded = false, onExpandedChange = {}, supportingText = "", trailingText = "", icon = null, expandable = false, loading = false, enabled = true, labelContent = null, trailing = null) { children } in the tree's scope, with the names of TanreiNavigationDrawerGroup and TanreiAccordionItem. enabled is the inverse flag; labelContent and trailing are the slots. | TanreiTreeItem(label:, value: '', supportingText: '', trailingText: '', icon:, expanded: false, onExpansionChanged:, expandable: false, loading: false, enabled: true, labelWidget:, trailing:, children: const []), a description of an item and not a widget, as the list's items are; controlled, with the callback name of TanreiNavigationDrawerGroup. labelWidget and trailing are the slots. | <TreeItem label="src" value="src" open={…} onToggle={…}>…</TreeItem>, with the names of NavigationDrawerGroup and AccordionItem; controlled when open is given, and defaultOpen otherwise. The label slot is the labelContent prop and the trailing slot the trailing prop; its items are children. |
状态
行为
以 EARS 形式写成的需求。每个平台的实现都按这些 ID 逐条测试。需求原文取自规格文件,原样以英文呈现。
| ID | 需求 |
|---|---|
TRV-1 | THE tree view SHALL lay out the rows of its items in one column, in the order given and depth first: an item's row and then, WHILE the item is a parent and open, the rows of the items it holds, before the row of the item that follows it. It SHALL be as wide as the layout it is placed in gives it, with its start edge on the start edge of what is around it; SHALL bring no outer space, no fill and no edge of its own (PRS-15: the space between it and its neighbours is its container's); SHALL have no minimum width, no maximum width and no height of its own; and SHALL NOT scroll: it is as tall as the rows it shows, and the product places a long tree in a scrolling box of its own, at whose top the rows of the open parents above what is shown are pinned (TRV-53). Rows SHALL touch: there is no space and no line between two rows. |
TRV-2 | THE tree view SHALL draw each item's row as a container at least as high as its density gives (comp.tree-view.container.height.regular for regular and comp.tree-view.container.height.compact for compact) and growing with its text, with corners comp.tree-view.container.shape, padded at its start and end by comp.tree-view.container.padding-inline.regular for regular and comp.tree-view.container.padding-inline.compact for compact and above its first line and below its last by comp.tree-view.container.padding-block.regular for regular and comp.tree-view.container.padding-block.compact for compact. Inside it SHALL lay out, from the start: the chevron's box WHILE chevron is start (TRV-4); the icon, or its kept room (TRV-5, TRV-43); the text, which takes the remaining width and holds the label and the supporting text (TRV-6, TRV-7); the trailing slot's content, where there is any; the trailing text, where the item has one (TRV-7); and the chevron's box WHILE chevron is end (TRV-4). Between the icon (or its room) and the text it SHALL keep iconGap — comp.tree-view.icon.gap while iconGap is null, the same in every density — and between each other two of them comp.tree-view.container.gap.regular for regular and comp.tree-view.container.gap.compact for compact. THE tree view SHALL centre the chevron, the icon, the trailing content and the trailing text on the label's first line and keep them there however tall the text grows, and any height a row has beyond its text SHALL be shared equally above and below it. |
TRV-3 | THE tree view SHALL show an item's depth by where its row's content begins: the content of an item of the first level SHALL begin where the tree's start edge and its padding put it, and the content of every deeper item SHALL begin one step further from that edge than its parent's. WHILE indent is null (auto) the step SHALL be the length from the parent's content start to where the parent's label begins — the chevron's box and a gap WHILE chevron is start and the tree is expandable, plus the icon's box (or its kept room, TRV-43) and iconGap WHERE the parent's row has an icon or keeps its room — so that a child's start stands where its parent's label begins, and comp.tree-view.level.indent.regular for regular and comp.tree-view.level.indent.compact for compact where the parent's row holds nothing before its label; WHILE indent is a number the step SHALL be that number of points, and 0 SHALL set no level in. WHILE highlight is indented the row's container (its fill, its state layer and its focus indicator) SHALL begin where its content's level begins, its padding before the content; WHILE highlight is full every container SHALL begin at the tree's start edge (TRV-51). Every container SHALL end at the tree's end edge. WHILE the steps of an item's levels would begin its label past half of the tree's width THE tree view SHALL begin that row no further in than puts its label at half of the width, so that a deep tree in a narrow window keeps half of its width for names, SHALL still expose the item's true level (TRV-32), and in debug builds SHALL report the depth once. |
TRV-4 | WHILE the tree is expandable and chevron is start or end THE tree view SHALL keep a box of comp.tree-view.chevron.size.regular for regular and comp.tree-view.chevron.size.compact for compact in every row, at its start WHILE chevron is start and at its very end — after the trailing content and the trailing text — WHILE chevron is end. In the row of a parent — an item that holds items, or whose expandable is true — it SHALL draw in that box, in the chevron color of the item's condition (comp.tree-view.unselected.chevron.color, comp.tree-view.selected.chevron.color), with sys.icon.family.unselected and sys.icon.fill.unselected, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max: at the start, the glyph chevron_right as the icon helper draws it WHILE the parent is closed, pointing toward the end of the line, turned a quarter turn to point down WHILE it is open; at the end, the glyph expand_more WHILE closed, turned a half turn WHILE open. In the row of a leaf it SHALL draw nothing in the box and keep the box, so that the labels (at the start) or the trailing texts (at the end) of one level stand in one column. WHILE chevron is none, or the tree is not expandable, THE tree view SHALL keep no chevron's box. The chevron, and the icon of an item that names an open icon (TRV-5), SHALL be the only things about a row that show whether its item is open: the container, the label and the texts SHALL be the same open and closed. THE tree view SHALL keep the chevron from assistive technology, which is given the state in words (TRV-32), and a disabled parent SHALL keep its chevron. |
TRV-5 | WHILE an item's icon is not empty THE tree view SHALL draw that glyph of the system's icon set after the chevron's box (or first, where there is none), in a box of comp.tree-view.icon.size.regular for regular and comp.tree-view.icon.size.compact for compact, in the icon color of the item's condition (comp.tree-view.unselected.icon.color, comp.tree-view.selected.icon.color), with the family and fill of that condition (sys.icon.family.selected and sys.icon.fill.selected for a selected item, sys.icon.family.unselected and sys.icon.fill.unselected otherwise), sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, and SHALL keep it from assistive technology. WHILE icon is empty THE tree view SHALL draw no glyph and keep its room as TRV-43 says. THE tree view SHALL have no glyph of its own for any kind of item. WHILE an item is a parent, is open and its openIcon is not empty THE tree view SHALL draw the openIcon glyph instead of icon, in the same place, box, color, family and fill, and SHALL draw icon again WHEN the parent closes; the glyph SHALL change at once, WHEN the change of open begins, with no motion of its own. THE tree view SHALL ignore openIcon on a leaf and WHILE icon is empty, and SHALL draw icon alone WHILE openIcon is empty. |
TRV-6 | THE tree view SHALL draw an item's label in the text style of its density (comp.tree-view.label.typescale.regular for regular and comp.tree-view.label.typescale.compact for compact) and the label color of its condition (comp.tree-view.unselected.label.color, comp.tree-view.selected.label.color). WHILE labelWrap is false the label SHALL be one line, cut at the end of its own direction with an ellipsis where it does not fit, and shown whole in the tooltip of TRV-50 while it is cut; WHILE labelWrap is true it SHALL wrap and SHALL never be cut, and a name with no place to break (a path, an identifier) SHALL break where it must rather than widen the tree. The accessible name SHALL be the whole label either way. WHERE the item's label slot holds content THE tree view SHALL draw that content in the label's place, size and color instead of the label, on one line or wrapping as labelWrap says, and SHALL leave a code span in it its own typeface. The label slot and the trailing slot SHALL hold no control: IF either holds a link, a button or a field THE tree view SHALL still render it and in debug builds report that a row holds no control; a platform that cannot inspect its slot content SHALL state the rule in its README and docs page. |
TRV-7 | WHILE an item's supportingText is not empty THE tree view SHALL draw it in comp.tree-view.supporting-text.typescale and the supporting-text color of the item's condition (comp.tree-view.unselected.supporting-text.color, comp.tree-view.selected.supporting-text.color): WHILE supportingPosition is below, on a line of its own under the label, starting where the label starts — one line cut with an ellipsis (the whole in the tooltip of TRV-50) WHILE supportingWrap is false, wrapping and never cut WHILE it is true; WHILE supportingPosition is inline, after the label in the same block of text and comp.tree-view.container.gap.regular for regular and comp.tree-view.container.gap.compact for compact after the label's last word, on the label's baseline, sharing the label's line and its cut (TRV-6). WHILE an item's trailingText is not empty THE tree view SHALL draw it on one line, in comp.tree-view.trailing-text.typescale and the trailing-text color of the item's condition (comp.tree-view.unselected.trailing-text.color, comp.tree-view.selected.trailing-text.color), after the trailing slot's content and before an end chevron; it SHALL NOT wrap, and WHILE it would take more than half of the row's inner width it SHALL be cut at the end of its own direction with an ellipsis, so that the label always keeps at least half. THE tree view SHALL give the trailing slot's content no style, and SHALL apply a disabled item's opacity to it with the rest of the row. |
TRV-8 | WHILE guides is shown or hover, and the step of a level is not 0 (TRV-3), THE tree view SHALL draw, for each open parent that shows at least one row under it, one line comp.tree-view.guide.width wide in comp.tree-view.guide.color, from the lower edge of the parent's row to the lower edge of the last row shown under it (the rows of its open descendants and a loading row included), centred under the parent's first leading thing: its chevron WHILE chevron is start; else its icon, where it has one; else the start of its label. The line SHALL lie in the indent of the rows it runs beside, SHALL keep its color and its full opacity in every state and condition of the rows beside it (a disabled item's opacity does not reach it), SHALL grow and shrink with the rows it runs beside as they are revealed and hidden (TRV-11), SHALL pass under a pinned row (TRV-53), SHALL be hidden from assistive technology, and SHALL take no space: a tree is laid out the same with and without guides. WHILE guides is hover THE tree view SHALL draw the lines WHILE a pointer is anywhere over the tree or keyboard focus is in it, and not otherwise, fading them in and out over comp.tree-view.motion.state-duration with comp.tree-view.motion.state-easing, at once where motion is reduced. WHILE guides is none THE tree view SHALL draw no line. |
TRV-9 | THE tree view SHALL draw a parent's open as the state of that parent alone: WHILE open is true the rows of the items it holds are shown under its row, and WHILE open is false they are not drawn, take no space, cannot take focus and are not exposed to assistive technology, whatever their own open says; a closed parent SHALL keep its descendants' open, so that it opens again as it was. WHEN the tree first appears it SHALL draw every parent as its open says, in its first frame, without the motion of TRV-11 and without emitting toggle. THE tree view SHALL ignore the open of a leaf. On a platform that keeps what it does not draw (the web, where the items stay in the document) THE tree view SHALL NOT remove or rebuild a closed parent's items. |
TRV-10 | WHEN the user presses the chevron of a parent that is not disabled, by pointer or touch, THE tree view SHALL emit that item's toggle exactly once with its new open state, SHALL NOT change the selection, and on the web SHALL set the item's open itself (a controlled platform leaves it to the caller). WHEN the user presses the row of a parent that cannot be chosen (TRV-15) anywhere, or presses Enter or Space while focus is on it, THE tree view SHALL do the same: a row that has nothing to choose opens and closes. WHEN the user presses the row of an item that can be chosen outside its chevron THE tree view SHALL change the selection (TRV-16, TRV-17) and SHALL NOT open or close it: the chevron opens and the row chooses, and one press never does both. WHILE chevron is none the press rules of TRV-44 SHALL apply in place of this one. THE tree view SHALL NOT open a parent because the pointer rests on it, and SHALL NOT open or close anything for a second press that follows the first quickly: each press is answered by itself. |
TRV-11 | WHEN a parent's open changes THE tree view SHALL reveal or hide the rows under it from its row downward, the rows after them moving with their lower edge, and SHALL turn its chevron (a quarter turn at the start, a half turn at the end), over comp.tree-view.motion.disclosure-duration with comp.tree-view.motion.disclosure-easing, at once where motion is reduced. The rows SHALL be laid out once, at the size they have when shown, and the motion SHALL only change how much of them is seen: no label SHALL wrap again and nothing SHALL be measured again from frame to frame. Rows that are being hidden SHALL stay drawn until the motion ends and SHALL leave the arrow keys and assistive technology when it begins. WHEN open changes again before the motion ends THE tree view SHALL go on from where the motion is toward the new state, without a jump. toggle SHALL be emitted when the change begins, not when the motion ends. |
TRV-12 | WHEN a parent opens or closes THE tree view SHALL keep that parent's row where it is in the view, as far as the scrolling of what holds the tree allows, and move only what follows it; it SHALL NOT scroll for any other reason of its own, and the rows of a parent that opens MAY reach below the view's edge. WHEN a parent closes, or its items are taken away, while keyboard focus is on a row under it THE tree view SHALL move focus to that parent's row; WHEN the item that has focus is removed and no parent of it closed THE tree view SHALL move focus to the row that follows it or, where none does, the row before it. WHEN keyboard focus moves to a row that is not wholly in view — or lies under the pinned rows of TRV-53 — THE tree view SHALL have the scrolling box that holds it bring that row into view, below the pinned rows, by the least distance that shows it whole. |
TRV-13 | WHILE a parent is open and its loading is true THE tree view SHALL draw one loading row under it, after the rows of any items it already holds and one level further in than its own row: a container of that level and of the tree's density (TRV-2, TRV-3) holding, at the start of its content, a Loading indicator component of the small size named by loadingLabel, which the tree lays out and does not restyle, and after it loadingLabel in comp.tree-view.supporting-text.typescale and comp.tree-view.unselected.supporting-text.color. The loading row is not an item: it SHALL take no focus, answer no pointer and no key, SHALL NOT be counted among its parent's items, and SHALL be shown and hidden by the motion of TRV-11 with the rows beside it. THE tree view SHALL expose the parent as busy WHILE its loading is true, open or closed, and SHALL announce nothing itself when loading begins or ends. THE tree view SHALL fetch nothing: a parent whose expandable is true and that holds no items opens onto no rows, or onto the loading row, and what it holds is the product's to give, which it learns from toggle. WHILE an item may be loading and loadingLabel is empty THE tree view SHALL still draw the indicator, and in debug builds report that it has no name. |
TRV-14 | WHERE the platform can search the text of what is shown and reports a match that lies in rows it does not draw (on the web, the browser's find-in-page), WHEN the match is under a closed parent THE tree view SHALL open that parent and every closed parent above it, at once and without the motion of TRV-11, SHALL emit each one's toggle exactly once with true, and SHALL leave bringing the match into view to the platform; it SHALL do so for a disabled parent too, whose rows are still part of the page. On a platform that has no such search or cannot report such a match THE tree view SHALL NOT open anything for it. Rows that a filter hides are not part of what the platform searches (TRV-70). |
TRV-15 | THE tree view SHALL let an item be chosen exactly WHILE selection is single or multiple, the item's value is not empty and its disabled is false, a parent and a leaf alike. It SHALL render an item as selected, from the selected condition (comp.tree-view.selected.container.color and the selected tokens TRV-4 to TRV-7 name), WHILE selection is single and value equals the item's value, or selection is multiple and values holds it, and every other item from the unselected condition (comp.tree-view.unselected.container.color and the unselected tokens). WHEN value or values names no item THE tree view SHALL select nothing for that name, and WHEN two items carry the same value it SHALL still render them, both selected where that value is; in debug builds it SHALL report each of these. The selected fill SHALL be drawn on the item's own row and on no other: THE tree view SHALL NOT mark a parent because an item under it is selected, and SHALL NOT open a closed parent to show a selection; opening the parents of the item it selects is the product's, which knows the path. WHEN the tree first appears it SHALL draw every item in the condition value and values give it, in its first frame and without the motion of TRV-31: appearing is no change of selection. |
TRV-16 | WHEN the user activates — by a press on its row outside the chevron, or by Enter or Space while focus is on it — an item that can be chosen in a tree whose selection is single and whose value is not that item's value THE tree view SHALL set value to the item's value, so that the item that was selected becomes unselected, and emit change exactly once; WHEN the item is already selected THE tree view SHALL change nothing and SHALL NOT emit change, because a single choice is never cleared by its own item. |
TRV-17 | WHEN the user activates — by a press on its row outside the chevron, with no modifier or with Ctrl (Command on Apple platforms), or by Space or Enter while focus is on it — an item that can be chosen in a tree whose selection is multiple THE tree view SHALL add the item's value to values, or remove it when values holds it, make that item the anchor of TRV-52, and emit change exactly once. Each item SHALL be chosen by itself: choosing a parent SHALL NOT choose the items it holds, a parent SHALL NOT show a mixed state for them, and closing a parent SHALL NOT change values. |
TRV-18 | WHILE selection is none THE tree view SHALL render every item from the unselected condition, SHALL ignore value and values, and SHALL expose no selected state. A leaf — and, in a tree whose selection is single or multiple, a leaf that cannot be chosen — SHALL then answer nothing: THE tree view SHALL draw no hover and no press answer on its row, SHALL emit nothing for a press on it, and SHALL still let the arrow keys reach it and show its keyboard focus (TRV-24, TRV-25), so that a person who reads the tree with a keyboard or a screen reader meets every row. A parent SHALL answer hover, focus and press on its whole row, which opens and closes it (TRV-10). |
TRV-19 | THE tree view SHALL be one stop in the focus order however many items it shows: Tab SHALL move focus into it to one row — the row that had focus last, or else the first selected item that is shown, or else the first row — and Tab again SHALL leave it; no other row, and nothing inside a row, SHALL be a stop. Focus that enters the tree SHALL NOT change the selection and SHALL NOT open or close anything. WHILE the tree shows no row it SHALL NOT be a stop. The filter field (TRV-54) is not a row: WHILE it is drawn it is a stop of its own, before the tree, and TRV-68 says where focus enters while a filter hides the row that had it. |
TRV-20 | WHEN focus is on a row and the user presses Down Arrow or Up Arrow THE tree view SHALL move focus to the next or the previous row that is shown, in the order the rows are drawn, whatever its level and whether or not it is disabled, without wrapping at the first and the last; WHEN the user presses Home or End it SHALL move focus to the first or the last row that is shown. It SHALL open and close nothing for these keys and SHALL NOT change the selection: selection does not follow focus. THE tree view SHALL NOT take these keys from the platform WHILE focus is not on one of its rows. |
TRV-21 | WHEN focus is on a row and the user presses the arrow key toward the end of the line (Right in a left-to-right layout) THE tree view SHALL act by the row it is on — whatever chevron says, so that a tree with no chevron still opens by the keyboard (TRV-44); WHILE the tree is not expandable every parent counts as open and nothing opens or closes (TRV-49): on a closed parent that is not disabled, open it as TRV-10 says and keep focus on it; on an open parent that shows at least one item, move focus to the first of them; on a leaf, on a disabled closed parent and on an open parent that shows none, do nothing. WHEN the user presses the arrow key toward the start of the line it SHALL likewise act by the row: on an open parent that is not disabled, close it as TRV-10 says and keep focus on it; on any other row that has a parent, move focus to that parent's row; on a row of the first level, do nothing. |
TRV-22 | WHEN focus is on a row and the user presses Enter or Space THE tree view SHALL do what a press on that row outside its chevron does: choose the item where it can be chosen (TRV-16, TRV-17), open or close it where it is a parent that cannot be chosen (TRV-10), and nothing on a leaf that cannot be chosen or on a disabled item. WHEN the user presses the asterisk key THE tree view SHALL open every closed parent that is not disabled among the focused row's siblings, itself included, emitting each one's toggle exactly once with true, and SHALL keep focus where it is. THE tree view SHALL leave every key it does not name here or in TRV-20, TRV-21, TRV-23, TRV-45 and TRV-52, and every key pressed with a modifier that those rules do not name, to the platform; WHILE an edit field is open (TRV-46) every key typed in it SHALL be the field's. |
TRV-23 | WHEN the user types printable characters while focus is on a row THE tree view SHALL move focus to the next row that is shown, after the focused one and going on from the first row after the last, whose label begins with the characters typed within one second of each other, compared without regard to case as the platform's locale does; one character typed again SHALL go on to the next such row; IF no label matches THE tree view SHALL keep focus where it is. It SHALL NOT open a closed parent to find a match, SHALL NOT change the selection, and SHALL NOT take a space typed inside such a run as an activation. WHILE filterable is true the chord of filterShortcut SHALL NOT be taken as type-ahead (TRV-62). |
TRV-24 | WHILE a row is hovered, keyboard-focused or pressed THE tree view SHALL render its container, label, supporting text, trailing text, icon, chevron and opacity from the component tokens of its condition in the highest-precedence active state, with precedence pressed over focused over hovered; focus gained by pointer or touch SHALL NOT select the focused state. Hover and press over any part of the row's hit area (TRV-26) SHALL answer on the whole container, as far as highlight says (TRV-51), the chevron's included; a row that answers nothing (TRV-18) SHALL take the focused state alone. The guide lines and the rows beside it SHALL NOT change under a pointer, a press or focus. |
TRV-25 | WHEN a row receives keyboard focus THE tree view SHALL show the focus ring inside the row's container — indented or full as highlight says (TRV-51) — following comp.tree-view.container.shape, so that neither a neighbouring row nor a scrolling ancestor cuts it, without animation, and at full strength on a disabled row too (TRV-27); focus gained by pointer or touch SHALL NOT show it. |
TRV-26 | THE tree view SHALL make each row's hit area the tree's whole width, however far its content and its container are set in (TRV-3), and the row's whole height, with no gap between the hit areas of two rows. WHERE input is touch, or the platform cannot tell the input type, a row SHALL be at least sys.target.touch high, drawn so, in both densities, because rows that touch cannot extend a hit area over each other; for a fine pointer a row SHALL be at least sys.target.pointer high WHILE density is regular, and WHILE density is compact as high as its density gives, the one place where a row of the tree is lower than the pointer target. In the row of a parent that can be chosen, the chevron's own hit area SHALL be, WHILE chevron is start, the part of the row's hit area from its start edge to the middle of the gap after the chevron's box, and WHILE chevron is end, the part from the middle of the gap before the chevron's box to the row's end edge; WHILE chevron is none, the icon's hit area of TRV-44 is the part from the row's start edge to the middle of the gap after the icon. Each SHALL be at least sys.target.touch wide for touch and sys.target.pointer wide for a fine pointer, reaching over what stands beside the mark where the box gives less. |
TRV-27 | WHILE an item's disabled is true THE tree view SHALL render its container, label, supporting text, trailing text, icon, chevron and opacity from the disabled component tokens of its condition, and SHALL apply that opacity to the whole row, its trailing content included, and to nothing else: the focus ring of TRV-25 SHALL be drawn at full strength on a disabled row that has keyboard focus, because the arrow keys reach it and the ring says where focus is; SHALL NOT emit change or toggle for a press, a key or a touch on it; SHALL draw no hover and no press answer on it; and SHALL expose it as disabled. The arrow keys SHALL still reach it, so that people learn it exists. The item SHALL keep its place, its selection and its open: a disabled parent that is open still shows the items it holds, which keep their own disabled, and the product MAY still set its open and select it. A row that draws no fill at rest SHALL draw none while disabled. |
TRV-28 | WHEN an item's row is pressed by pointer, touch, Enter or Space THE tree view SHALL show its pressed state without delay and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner. |
TRV-29 | WHILE an item's row is pressed THE tree view SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole container at once; ripple grows the pressed state layer, clipped to the container (indented or full as highlight says), from the point of contact (from the center for keyboard activation); dim applies the pressed opacity to the whole row and draws no state layer; none draws no answer to the press. Under ripple and dim only the container color stays with the state under the press; label, supporting text, trailing text, icon, chevron and opacity still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.tree-view.motion.feedback-duration with comp.tree-view.motion.state-easing, fades over comp.tree-view.motion.state-duration with comp.tree-view.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced. |
TRV-30 | THE tree view SHALL ease its rows' container corners by sys.shape.corner-smoothing (0 is a quarter circle, 0.6 the iOS continuous corner; a platform with only the OS curve draws it for any amount above 0) and show a row's keyboard focus as sys.behavior.focus-style names (ring or glow); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README. |
TRV-31 | WHEN the colors of a row change between states, or between the unselected and the selected condition, THE tree view SHALL animate them over comp.tree-view.motion.state-duration with comp.tree-view.motion.state-easing, and SHALL change them at once where motion is reduced. The selected fill SHALL NOT slide from one row to another. |
TRV-32 | THE tree view SHALL expose itself as a tree named by label, and as one in which several items may be selected WHILE selection is multiple; each item as one tree item named by its label (the text of the label slot's content where the product gave one), described by its supporting text, then the text of its trailing content, then its trailing text, with its level, its place among the items of its parent and their number; a parent with its expanded or collapsed state WHILE the tree is expandable, and with none WHILE it is not (TRV-49), and the items it shows as a group that belongs to it in either case; WHILE selection is single or multiple, each item that can be chosen with its selected state, true or false, and an item that cannot with none; a disabled item as disabled and a loading parent as busy; and the chevron, the icon and the guide lines hidden. WHILE a name is being edited the edit field SHALL be exposed as a text field inside the row it stands in, named by editLabel, and keyboard focus SHALL be in it; a new row (TRV-48) SHALL be exposed as a tree item at its level holding that field. WHILE editLabel is empty and a field is open THE tree view SHALL still open the field, and in debug builds report that it has no name. On the web these are the tree, treeitem and group roles with aria-expanded, aria-selected, aria-multiselectable, aria-disabled and aria-busy, the level, the set size and the position coming from the nesting of the elements. WHERE a platform has no tree semantics THE tree view SHALL expose each row as one element in the order drawn, with that name and description, the expanded or collapsed state, the selected state and the disabled state as the platform says them, and its level by the platform's own means where it has one; it SHALL NOT invent words for a level, and the platform's README SHALL say what a person is not told. WHILE label is empty THE tree view SHALL still render, and in debug builds report that the tree has no accessible name; WHILE an item's label is empty and its label slot holds nothing it SHALL report that the item has none. |
TRV-33 | WHEN a parent opens or closes, and WHEN the selection changes, THE tree view SHALL let the changed state of the row be the only thing assistive technology is told: it SHALL announce nothing itself, SHALL have no live region other than the status of a filter (TRV-66), and SHALL NOT move keyboard or screen-reader focus, except as TRV-12 says for a row that goes away and TRV-45 to TRV-48 say for an edit field. |
TRV-34 | WHILE the text size is enlarged THE tree view SHALL grow its labels, its supporting texts and its trailing texts with it, keep its paddings, gaps and indents, and grow each row in height; a label and a supporting text on one line (TRV-6, TRV-7) SHALL be cut with an ellipsis and shown whole in the tooltip of TRV-50, and with labelWrap and supportingWrap they SHALL wrap and the rows grow; it SHALL keep the chevron's and the icon's boxes at their token sizes, both still centred on the label's first line (TRV-2), SHALL set no fixed height, SHALL lose no content when the reader raises line height, paragraph spacing, letter spacing or word spacing (a one-line text is cut, and is whole in its tooltip and its accessible name), and SHALL need no horizontal scrolling at a width of 320 points with text at 200 %, where TRV-3 keeps half of the width for names. |
TRV-35 | WHERE the platform draws with a person's own forced colors (the web's forced-colors mode, and a platform's equivalent where it has one) THE tree view SHALL draw its labels, texts, icons and chevrons in the platform's text color; the guide lines in that text color; a selected row in the platform's colors for a selected item, fill and text, and in addition with an edge in the text color inside its container, because the mode may remove a fill; a disabled row in the platform's color for disabled text; and a row's hover, press and focus in the platform's own colors, with the focus ring still inside the row. Open and closed SHALL still be told apart by the chevron's turn, which no color carries. |
TRV-36 | WHERE the layout direction is right-to-left THE tree view SHALL mirror a row and apply its paddings and its indent by logical start and end: the tree's start edge is its right edge, each level begins further to the left, the chevron's box at the start is at the right of its row with the icon, the text, the trailing content, the trailing text and an end chevron after it toward the left, and the guide lines stand under the parents' first leading things. A closed parent's start chevron SHALL point to the left, drawn so by the icon helper, which mirrors the glyph because the icon set marks chevron_right (TRV-38), and an open parent's SHALL point down. The arrow keys of TRV-21 SHALL follow the direction: Left opens and steps in, Right closes and steps out; Up, Down, Home and End SHALL be the same in both directions. A ripple SHALL still grow from the point of contact. |
TRV-37 | THE tree view SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the tree view is shown THE tree view SHALL draw itself for the new direction at once, without a motion of its own, keeping its selection, every parent's open and its focus. |
TRV-38 | WHERE the layout direction is right-to-left THE tree view SHALL draw an item's icon and a parent's chevron mirrored left to right exactly when the system's icon set marks that glyph's name as mirrored or the product forces mirroring for that use, and unmirrored when the product forbids it: the icon helper mirrors the glyph inside its own box, about the box's vertical centre line, changing neither its place, size, color and fill nor the order TRV-2 gives. THE tree view SHALL NOT mirror a glyph itself, and SHALL NOT mirror content the product gives as an image or a view of its own rather than by a glyph's name. The quarter turn of an open parent's chevron (TRV-4) is the tree's and SHALL end pointing down in both directions. |
TRV-39 | THE tree view SHALL lay out an item's label, its supporting text and its trailing text, and the words of a loading row, each as text of its own direction, isolated from the text around it: each takes the base direction of its first strong character, or the layout direction where it has none, so that a left-to-right file name, a path, a number with its unit and a version keep their own order in a right-to-left layout and reorder nothing beside them; THE tree view SHALL still place each and align its lines by the layout direction, not by the text's own, and SHALL cut a trailing text that does not fit (TRV-7) at the end of its own direction. |
TRV-40 | WHILE the system's icon set cannot yet draw a glyph of the tree view (its font is still loading) THE tree view SHALL draw nothing in that glyph's place, never the glyph's name as text, SHALL keep the glyph's box, and SHALL draw the glyph once the set can; a parent SHALL open and close all the same. |
TRV-41 | WHEN density names a value that the active design system does not have THE tree view SHALL render with the default density's tokens, and WHEN selection names a value the tree view does not define it SHALL render with none, WHEN chevron names one it SHALL render as start, WHEN guides names one as none, WHEN highlight names one as indented and WHEN iconSpace names one as auto; WHEN indent, iconGap or stickyMax is not a number of zero or more it SHALL render as its default; in debug builds it SHALL report the unknown value. WHEN selection changes while the tree is shown THE tree view SHALL draw the new form at once, keeping every parent's open and its focus, and SHALL emit no change for it. |
TRV-42 | IF the tree view has no items THE tree view SHALL draw nothing, take no space, expose nothing and be no stop in the focus order: the words for an empty hierarchy are the product's, beside the tree. IF an item holds no items and its expandable is false THE tree view SHALL treat it as a leaf whatever its open and loading say. WHEN items are added to or removed from a shown tree THE tree view SHALL draw them at once, without the motion of TRV-11, keeping the selection, every other parent's open and, as far as TRV-12 allows, its focus. |
TRV-43 | THE tree view SHALL keep the room of an icon (comp.tree-view.icon.size.regular for regular and comp.tree-view.icon.size.compact for compact, and iconGap after it) in the row of an item whose icon is empty WHILE iconSpace is always, and WHILE iconSpace is auto and another item of the same level — the items of the same parent, or of the first level — has an icon, so that the labels of a level stand in one column; WHILE iconSpace is never, and WHILE it is auto and no item of the level has an icon, it SHALL keep no such room. The kept room SHALL draw nothing and SHALL count as the icon's for the step of TRV-3 and for the guides of TRV-8 only as room: a guide stands under an icon that is drawn, and under the label's start where the room is empty. |
TRV-44 | WHILE chevron is none THE tree view SHALL draw no chevron and keep no chevron's box, and SHALL open and close a parent that is not disabled as follows. By the keyboard, the arrows of TRV-21 SHALL always open and close it, whether or not it can be chosen. By pointer or touch: WHERE the parent's row has an icon, a press on the icon SHALL open or close it, emit its toggle once and never choose, its hit area being TRV-26's; WHERE it has no icon and cannot be chosen, a press anywhere on its row SHALL open or close it (TRV-10); WHERE it has no icon and can be chosen, a press on its row SHALL choose it (TRV-16, TRV-17) and SHALL NOT open or close it — selection wins, and the keyboard still opens it. A parent that names an icon and an open icon shows open and closed by its glyph (TRV-5); a parent with neither shows it by the expanded state alone, which assistive technology is told (TRV-32), and in debug builds THE tree view SHALL report a parent that has no icon and no open icon once, because nothing on screen shows whether it is open. |
TRV-45 | WHILE editable is true THE tree view SHALL open an edit field on an item's name (TRV-46) WHEN the user double-clicks or double-taps the label of its row, WHEN the user presses F2 while keyboard focus is on its row, and WHEN the product asks the tree view to edit that item (the platform's method for it). It SHALL NOT open one on a disabled item, on the loading row, or on an item whose label is given by content of the label slot rather than by plain text, whose inline content is the product's to edit; and WHILE editable is false it SHALL open none, leaving F2 and a double-click to the platform and doing nothing when asked. The two presses of a double-click SHALL each be answered as TRV-10 to TRV-17 say, and the field SHALL open after the second. Only one field SHALL be open in a tree at a time: opening one, by any of these means or by TRV-48, SHALL first commit the one that is open. |
TRV-46 | WHILE an edit field is open on an item THE tree view SHALL draw an Embedded text input component of one line over the item's label, in the box of the label's first line — the same font, line height, start and width, so that opening and closing the field moves nothing in the row or beside it — in the label's text style and color, the label and a supporting text inline after it not drawn under it, holding the label, with keyboard focus in it and — for a leaf whose label holds a dot after its first character — the text before the last dot selected, and for any other item the whole label selected; and SHALL draw around the field's box the focus indicator of TRV-25 (sys.behavior.focus-style), inside the row, while the chevron or its box, the icon, the trailing content, the trailing text, the row's selection and the row's open stay as they were. The row SHALL keep at least its height, and a long name SHALL scroll inside the field rather than widen the tree. Every key typed in the field SHALL be the field's: WHEN the user presses Enter, or WHEN focus leaves the field by any other means (Tab, a press elsewhere, the window losing focus), THE tree view SHALL commit the field; WHEN the user presses Escape it SHALL close the field without emitting; WHILE an input method is composing Enter and Escape SHALL be the input method's. WHEN the field commits THE tree view SHALL close it and, WHERE its text with white space removed at both ends is not empty and differs from the label, emit the item's rename exactly once with that text and the label it had; otherwise it SHALL close the field without emitting. THE tree view SHALL NOT change the label itself: it SHALL draw the label it had until the product sets the new one. WHEN the field closes on Enter or Escape keyboard focus SHALL return to the item's row; WHEN it closes because focus left it, focus SHALL stay where it went. WHILE the field is open THE tree view SHALL NOT answer a press inside it as a press on the row (TRV-10), and WHEN the item is removed, becomes disabled, or is hidden because a parent above it closes, or editable becomes false, it SHALL close the field without emitting. |
TRV-47 | WHILE an edit field is open on an item THE tree view SHALL keep that item where it is and keep its row's state of selection, open, hover and press as they were; SHALL take none of the keys of TRV-20 to TRV-23 from the field; and SHALL change nothing about the selection or about any parent's open because a field opened, committed or closed. WHEN the product sets the item's label in answer to rename THE tree view SHALL draw it at once, without a motion of its own (the label of a row is the product's, TRV-6). |
TRV-48 | WHEN the product asks the tree view for a new item (the platform's method for it, saying whether the new item is a parent or a leaf and, where it likes, the glyph of its icon) THE tree view SHALL commit any field that is open and add a new row of its own at the level of the reference item, directly after the rows the reference item shows, holding an edit field (TRV-46) that is empty and has keyboard focus. The reference item SHALL be the first of these that is shown: the selected item (single: the item value names; multiple: the row that has or last had focus where it is selected, else the first selected item in the order drawn), the row that has or last had focus, and none, in which case the new row is the last row of the first level. The new row SHALL be drawn as a row of its level and of the tree's density: for a parent a closed chevron in the chevron's box where chevron is start or end, for a leaf the box as TRV-4 says, and the glyph the product named in the icon's place. WHEN its field commits with a text that, with white space removed at both ends, is not empty THE tree view SHALL take the new row away and emit create exactly once with that text, whether it is a parent, the item it goes under (none at the first level) and the item it follows (none where it is first); WHEN the product adds an item in the new row's place in answer, before the next frame is drawn, focus SHALL move to that item's row. WHEN the field closes on Escape or commits empty THE tree view SHALL take the new row away, emit nothing, and return focus to the row that had it. The new row is not an item: it SHALL NOT be chosen or opened, the arrow keys SHALL NOT reach it while its field is open, and the tree SHALL change no selection and no parent's open for it. A new item SHALL be asked for whatever editable says. |
TRV-49 | WHILE expandable is false THE tree view SHALL draw every parent as open whatever its open says, draw no chevron and keep no chevron's box, emit no toggle, open and close nothing for a press, a key or the platform's search, and expose no parent as expanded or collapsed: each item is a tree item at its level, a parent's items a group that belongs to it (the tree pattern's tree items without the expanded state are end nodes to a screen reader, and their groups still tell the level). The arrow toward the end of the line SHALL move focus to a parent's first item and the one toward the start to an item's parent (TRV-21), and the asterisk SHALL do nothing. A press on a row SHALL choose an item that can be chosen and do nothing on any other. An item's own expandable SHALL be read only WHILE the tree's expandable is true. WHEN expandable becomes true the tree SHALL draw each parent as its open says, at once. |
TRV-50 | WHILE a row's label, or its supporting text, is cut (TRV-6, TRV-7) THE tree view SHALL show the whole of it in a Tooltip component beside the row's text WHEN a fine pointer rests on the row for the tooltip's show delay and WHEN the row receives keyboard focus, and SHALL hide it WHEN the pointer leaves the row and the tooltip for the hide delay, WHEN focus leaves the row, WHEN the user presses Escape (which does nothing else), and WHEN the row is pressed; the tooltip SHALL stay while the pointer is over it (WCAG 1.4.13: dismissible, hoverable, persistent). It SHALL hold the label alone WHERE the item has no supporting text, and the label as the title and the supporting text as the text of a rich tooltip WHERE it has one. WHILE nothing of the row is cut THE tree view SHALL show no tooltip for it. The tooltip SHALL be hidden from assistive technology, which is given the whole label as the row's name and the whole supporting text as its description (TRV-32); no touch long-press opens it, because a long-press on a row is the platform's. |
TRV-51 | WHILE highlight is indented THE tree view SHALL draw a row's container — its fill (selected or of a state), its state layer and ripple, and its focus indicator — from where the row's content's level begins, less the row's padding, to the tree's end edge; WHILE highlight is full it SHALL draw them from the tree's start edge to its end edge whatever the row's level, the content still beginning one step further in for each level (TRV-3). The corners (comp.tree-view.container.shape) and the focus ring follow the container in both. |
TRV-52 | WHILE selection is multiple THE tree view SHALL answer, besides TRV-17, the keys and presses of the tree pattern's multi-select model, each emitting change exactly once where values changed and never opening or closing anything: Shift with Down Arrow or Up Arrow SHALL move focus to the next or previous row shown and select every item that can be chosen from the anchor to the focused row, deselecting the items of the range it selected before that are no longer in it, and leaving every other value as it was; Shift with Space SHALL do the same from the anchor to the focused row without moving focus; Ctrl (Command on Apple platforms) with Shift and Home or End SHALL select every item that can be chosen from the focused row to the first or the last row shown and move focus there; Ctrl (Command) with A SHALL select every item that can be chosen among the rows shown; a press with Shift SHALL select from the anchor to the pressed row as Shift with Space does; a press with Ctrl (Command) SHALL toggle the pressed item as a plain press does (TRV-17). The anchor SHALL be the item last chosen or unchosen by a plain press, a press with Ctrl (Command), Space or Enter, and where there is none the focused row. Items that cannot be chosen (no value, disabled) SHALL be passed over in a range and keep their state. WHILE selection is single or none these keys and modifiers SHALL be the platform's, except that a press with a modifier SHALL do what a plain press does. |
TRV-53 | WHILE stickyParents is true and stickyMax is more than 0, and the tree stands in a scrolling box, THE tree view SHALL pin at the top of that box the rows of the open parents that hold the topmost row shown below the pinned rows, one per level in the order of their levels, the innermost stickyMax of them where more parents are open above it, each held in place while what it holds scrolls under it and leaving with its last row; it SHALL pin the rows themselves — the same rows, with their own fill, state, selection, focus, press and the events of a press, never a copy in the accessibility tree — drawn over the rows that scroll under them with comp.tree-view.sticky.container.color under their own fill and a line comp.tree-view.sticky.divider.width wide in comp.tree-view.sticky.divider.color under the last of them. WHEN keyboard focus moves onto a row that lies under the pinned rows THE tree view SHALL have the scrolling box bring that row into view below them by the least distance. A guide line SHALL pass under a pinned row (TRV-8). Nothing about pinning SHALL move by itself: there is no motion to reduce. A tree that stands in no scrolling box of its own, or whose stickyParents is false, SHALL pin nothing. A platform whose scrolling list cannot hold a row in place SHALL draw the pinned rows as an overlay hidden from assistive technology that keeps focus on the real rows, and SHALL say so in its README. |
TRV-54 | WHILE filterable is true and the tree has items THE tree view SHALL draw a filter field above its first row, as wide as the tree, and what stands under it — the status line of TRV-65 while there is one, then its first row — comp.tree-view.filter.gap below it: a Text input component of type search in its default variant and the small size, showing the glyph search of the system's icon set in its leading-icon slot, filterPlaceholder as its placeholder and filterValue as its value, drawn and answering as that text input draws and answers (its own sentences hold, its tokens are its own). WHILE the tree stands in a scrolling box THE tree view SHALL keep the field, and the gap below it, pinned at the top of that box on comp.tree-view.sticky.container.color, with the rows TRV-53 pins directly under it and the edge of TRV-53 drawn under the last of them — under the gap below the field WHILE no row is pinned and rows scroll under it. |
TRV-55 | WHEN the person changes the filter field's text — by typing, deleting, pasting, cutting, dictating, an input method committing text, the clear button (TRV-64) or Escape (TRV-61, TRV-69) — THE tree view SHALL emit filterChange exactly once per change with the new text, setting filterValue itself first on the web (a controlled platform leaves filterValue to the caller), and SHALL apply the filter to the new text at once. WHEN the product sets filterValue THE tree view SHALL show that text in the field and apply it, and SHALL NOT emit filterChange. WHILE an input method is composing THE tree view SHALL filter by the text the field reports, and SHALL leave the composition, Enter, Escape and the arrow keys to the input method (TXI-4). |
TRV-56 | THE tree view SHALL apply a filter WHILE filterable is true and filterValue holds a character that is not white space, and SHALL read the filter text as words: the runs of characters between white space. An item SHALL match WHILE its label (the text of its label slot's content, where the product gave one) contains every word, each anywhere in it and in any order, compared without regard to case, to the width of a character or to the marks on a letter, as the label's language compares (as the combobox compares, CMB-25). THE tree view SHALL compare nothing else of an item — not its supporting text, its trailing text, the text of its trailing content or its value. A disabled item SHALL match as any other item and stay disabled. It SHALL match only the items it holds: the items of a parent that loads them when it opens (TRV-13) are not searched until they are given. THE tree view SHALL NOT fetch, wait, debounce, rank or reorder anything for a filter. |
TRV-57 | WHILE a filter is applied THE tree view SHALL show, in the order given: each row that matches; each parent's row that holds an item that matches, at any depth; and every item inside a parent whose own row matches, so that a parent found by its name is shown whole. Every other row SHALL be hidden — not drawn, taking no space, not focusable and not exposed to assistive technology — together with what it holds. A hidden item SHALL keep its open, its disabled state and its selection, and WHEN no filter is applied any more every row SHALL be shown as it was. |
TRV-59 | WHILE a filter is applied THE tree view SHALL draw open, for the filter, each closed parent that holds an item that matches and is shown, at any depth, so that every match is in view without the person opening anything, and SHALL draw every other parent as its open says. Opening a parent for the filter SHALL write nothing to the item's open, SHALL emit no toggle, and SHALL be drawn at once, without the motion of TRV-11; the row SHALL be exposed as expanded while it is drawn open. WHEN the person opens or closes a parent while a filter is applied — by its chevron, its row or an arrow key (TRV-10, TRV-21, TRV-44) — THE tree view SHALL answer as those sentences say, setting open itself on the web and emitting toggle exactly once where open changes, and SHALL stop opening that parent for the filter until the filter text changes. WHEN no filter is applied any more THE tree view SHALL draw every parent as its open says: the parents the filter opened close again, at once, and a parent the person opened or closed stays as the person left it. |
TRV-60 | WHILE a filter is applied THE tree view SHALL draw each row it shows as it draws that row without a filter, and SHALL NOT mark, in a label, the characters that a word of the filter text matched: no color, weight, fill or element of their own. What the filter leaves is the answer, as in a file tree's filter and a site's sidebar filter; a row SHALL be told from the others only by its own states (TRV-15, TRV-24) and as the active match (TRV-61). THE tree view SHALL leave the product's content as it is: where the label is the product's own text in the document (the web), it SHALL add, split and remove no node of it. |
TRV-61 | WHILE keyboard focus is in the filter field, a filter is applied and a row shown matches and is not disabled, THE tree view SHALL make the first such row, in the order the rows are drawn, the active match — as soon as there is one, and the new first one whenever the result changes — and SHALL draw it as it draws that row while the row has keyboard focus, its focused tokens and its focus ring (TRV-25), without animation, while focus stays in the field, so that typing goes on editing the filter text; THE tree view SHALL NOT tell assistive technology of it — no active descendant, no focus and no announcement — until the person moves to it, as a search's automatic choice is not read out; a pointer over a row SHALL NOT make it the active match, and WHILE focus is not in the field no row SHALL be drawn as one. WHILE no input method is composing, WHEN the person presses Enter THE tree view SHALL take the active match as Enter on that row takes it (TRV-22): choose it where it can be chosen, or open or close it where it is a parent that cannot be chosen; and SHALL keep focus in the field; WHEN the person presses Down Arrow THE tree view SHALL move focus to the active match itself, not past it, making it the tree's focused row (TRV-19), where it is read as any focused row is, and do nothing more. WHILE there is no active match the two keys SHALL do nothing of the tree's own. WHEN the person presses Escape WHILE the field holds text THE tree view SHALL empty it, emit filterChange once with the empty text, and keep focus in the field, as a native search field does; WHEN the person presses Escape WHILE the field is empty THE tree view SHALL return focus to the row that had it before the filter shortcut moved it to the field (TRV-62), and otherwise SHALL do nothing of its own and let the key pass on. Up Arrow, Home, End and the arrow keys along the line SHALL stay the field's. |
TRV-62 | WHILE filterable is true and filterShortcut holds exactly one chord, WHEN the person presses that chord while keyboard focus is on a row and no edit field is open (read as KBS-1 reads a chord; not a key repeating while held, and not during an input-method composition) THE tree view SHALL move focus to the filter field with its text selected, and SHALL NOT take the press as type-ahead (TRV-23). THE tree view SHALL listen for the chord nowhere else, SHALL draw no hint of it, and SHALL expose it as no keyboard shortcut of the field: it is a key of the tree's rows, as F2 is (TRV-45), and a hint would promise it from anywhere. WHILE filterShortcut is empty or holds more than one chord the tree view SHALL bind nothing, reporting more than one chord in debug builds. |
TRV-63 | WHEN the product asks the tree view to focus its filter (the platform's method for it) WHILE filterable is true and the field is drawn THE tree view SHALL move focus to the field with its text selected, whether or not filterShortcut is empty, so that a product can bind a key of its own for the whole window. |
TRV-64 | WHILE the filter field holds text THE tree view SHALL draw, in the field's trailing-icon slot, an icon-only Tanrei Button of the text variant and the small size showing the glyph close of the system's icon set, not mirrored, named by filterClearLabel. WHEN it is activated THE tree view SHALL empty the field, emit filterChange once with the empty text, and move focus to the field. WHILE filterClearLabel is empty and the product's words give none THE tree view SHALL still draw the button, and in debug builds report that it has no accessible name. |
TRV-65 | WHILE a filter is applied THE tree view SHALL draw a status line at the top of the results, where its first row would stand, the rows following it: filterCountText with its place {count} filled by the number of rows that match WHILE a row matches and filterCountHidden is false, and filterEmptyText WHILE no row matches, in place of the rows, all of them hidden, so that the tree shows no row and is no stop in the focus order (TRV-19). It SHALL draw the line in comp.tree-view.filter.status.typescale and comp.tree-view.filter.status.color, inset as a row's content is (comp.tree-view.container.padding-inline.regular for regular and comp.tree-view.container.padding-inline.compact for compact), wrapping and growing with enlarged text, as text of its own direction (TRV-39), taking no focus and no state, and hidden from assistive technology, which TRV-66 tells the same words. WHILE a row matches and filterCountHidden is true it SHALL draw no line. WHILE the words it would draw are empty and the product's words give none THE tree view SHALL draw nothing in their place and in debug builds report it. |
TRV-66 | WHEN the result of a filter changes — the filter text or the items change while a filter is applied — and has then stayed as it is for one second (the pause of TRV-23), THE tree view SHALL tell assistive technology once, politely and without moving focus, filterCountText with its place {count} filled by the number of rows that match, or filterEmptyText WHILE no row matches; WHEN no filter is applied any more it SHALL say nothing. It SHALL say them whether or not filterCountHidden is true, through one status of its own that is not drawn (the line of TRV-65 is what is drawn) — on the web an element with the status role and aria-atomic true, beside the tree's element and outside it — which is the only thing the tree view ever announces (TRV-33). WHILE filterCountText is empty and the product's words give none THE tree view SHALL say no count and in debug builds report it. |
TRV-67 | THE tree view SHALL change nothing but what it draws for a filter: value, values, every item's open (except where the person opens or closes one, TRV-59) and its labels SHALL stay as they were, and it SHALL keep filterValue no longer than its own lifetime and write it to no storage. A selected item that the filter hides SHALL stay selected, and change SHALL NOT be emitted for it; Shift with the arrows, Shift with Space and Ctrl (Command) with A SHALL act on the rows shown only (TRV-52). WHEN the filter text changes while a filter is applied THE tree view SHALL have the scrolling box that holds it bring the field and the first row shown into view; WHEN no filter is applied any more it SHALL, WHILE keyboard focus is on a row, keep that row where it is in the view, as TRV-12 keeps a parent's row, and SHALL NOT scroll for it otherwise. |
TRV-68 | WHEN a filter comes to hide the row that has keyboard focus (the product sets filterValue, or changes the items) THE tree view SHALL move focus to the filter field. WHEN focus enters the tree by Tab WHILE a filter is applied and the row that had focus last is hidden THE tree view SHALL give focus to the first row shown that matches and is not disabled (the active match of TRV-61), or else the first row shown, in place of TRV-19's first choice. WHILE an edit field is open on an item, and on the new row of TRV-48, THE tree view SHALL keep that row shown, whether or not it matches, until its field closes, and SHALL then apply the filter to it. |
TRV-69 | WHILE a filter is applied and keyboard focus is on a row, WHEN the person presses Escape (and no tooltip of TRV-50 is shown) THE tree view SHALL open, as the person would (open set and toggle emitted for each), the parents the filter had opened around that row, so that it stays shown, then empty the filter — setting filterValue to empty and emitting filterChange once — and keep focus on that row, which TRV-67 keeps where it is, so that a person who found an item by the filter sees it among its neighbours. |
TRV-70 | THE tree view SHALL draw every change a filter makes — rows hidden and shown, parents opened for the filter and closed again — at once, without the motion of TRV-11, whether or not motion is reduced; a parent the person opens or closes during a filter moves as TRV-11 says. Rows the filter hides SHALL NOT be found by the platform's own search of what is shown (on the web, find-in-page), and TRV-14 SHALL NOT open anything for them. |
TRV-71 | THE tree view SHALL expose the filter field as a search field — on the web an input of type search, which has the searchbox role — named by filterLabel, with filterPlaceholder as its placeholder, controlling the tree (aria-controls on the web), naming no active descendant (the active match of TRV-61 is read once the person moves to it), before the tree in reading order and a stop of its own in the focus order, outside the tree's one stop (TRV-19); each shown item with its level and its position among the shown items of its parent and their number; and the rows the filter hides not at all. WHILE filterable is true and filterLabel is empty THE tree view SHALL still draw the field, and in debug builds report that it has no accessible name. |
TRV-72 | WHERE the platform has ARIA attributes (the web), the host carries its tree role (TRV-32) itself — while no filter field stands in it; while one does, its first list carries the role and the label (TRV-54, TRV-71), and the host, which then carries no role, holds the product's name and writes none of its own — so what the product writes on it is not moved, but for a name: WHILE label is not empty THE tree view SHALL hold the product's aria-label and aria-labelledby on that host as their data-aria- twins, so that label is its whole name although the platform gives aria-labelledby the precedence, and WHEN label is emptied SHALL give them back as they were written; WHILE label is empty a name the product gives so — aria-label, or aria-labelledby naming an element — SHALL be its name and SHALL NOT be reported as having none in debug builds (TRV-32). WHEN the product removes one, or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaLabelledByElements) to null, THE tree view SHALL take it away. |
各平台名称对照
对等并不意味着 API 完全相同。同一份契约,由各平台以自己的惯用方式表达。
| 规格 | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| 组件 | tanrei-tree-view | TanreiTreeView | TanreiTreeView | TanreiTreeView | TreeView |
label · 属性 | label | label | label | semanticLabel | accessibilityLabel |
selection · 属性 | selection | selection | selection | selection | selection |
value · 属性 | value | selection | value | value | value |
values · 属性 | values | selection | values | values | values |
density · 属性 | density | density | density | density | density |
chevron · 属性 | chevron | chevron | chevron | chevron | chevron |
expandable · 属性 | expandable | isExpandable | expandable | expandable | expandable |
guides · 属性 | guides | guides | guides | guides | guides |
indent · 属性 | indent | indent | indent | indent | indent |
iconSpace · 属性 | icon-space | iconSpace | iconSpace | iconSpace | iconSpace |
iconGap · 属性 | icon-gap | iconGap | iconGap | iconGap | iconGap |
highlight · 属性 | highlight | highlight | highlight | highlight | highlight |
labelWrap · 属性 | label-wrap | labelWrap | labelWrap | labelWrap | labelWrap |
supportingWrap · 属性 | supporting-wrap | supportingWrap | supportingWrap | supportingWrap | supportingWrap |
stickyParents · 属性 | sticky-parents | stickyParents | stickyParents | stickyParents | stickyParents |
stickyMax · 属性 | sticky-max | stickyMax | stickyMax | stickyMax | stickyMax |
editable · 属性 | editable | isEditable | editable | editable | editable |
editLabel · 属性 | edit-label | editLabel | editLabel | editLabel | editLabel |
loadingLabel · 属性 | loading-label | loadingLabel | loadingLabel | loadingLabel | loadingLabel |
filterable · 属性 | filterable | isFilterable | filterable | filterable | filterable |
filterValue · 属性 | filter-value | filterText | filterValue | filterValue | filterValue |
filterLabel · 属性 | filter-label | filterLabel | filterLabel | filterLabel | filterLabel |
filterPlaceholder · 属性 | filter-placeholder | filterPrompt | filterPlaceholder | filterPlaceholder | filterPlaceholder |
filterEmptyText · 属性 | filter-empty-text | filterEmptyText | filterEmptyText | filterEmptyText | filterEmptyText |
filterCountText · 属性 | filter-count-text | filterCountText | filterCountText | filterCountText | filterCountText |
filterCountHidden · 属性 | filter-count-hidden | isFilterCountHidden | filterCountHidden | filterCountHidden | filterCountHidden |
filterClearLabel · 属性 | filter-clear-label | filterClearLabel | filterClearLabel | filterClearLabel | filterClearLabel |
filterShortcut · 属性 | filter-shortcut | filterShortcut | filterShortcut | filterShortcut | filterShortcut |
change · 事件 | change | selection | onValueChange | onChanged | onChange |
create · 事件 | create | onCreate | onCreate | onCreate | onCreate |
filterChange · 事件 | filterchange | filterText | onFilterValueChange | onFilterChanged | onFilterChange |
| 备注 | Form: <tanrei-tree-view> is a custom element with **no shadow root**, around the product's own nested lists in the light DOM — <tanrei-tree-view label="Project files" selection="single" value="src/index.ts"><ul><li data-value="src" data-open><span>src</span><ul><li data-value="src/index.ts"><span>index.ts</span></li></ul></li></ul></tanrei-tree-view> — the third component of this form after the table and the accordion. A hierarchy is a nested list: that is what a Markdown renderer emits, what a crawler and a reader mode keep, and what the page shows where script does not run. Everything drawn comes from one stylesheet, @tanrei/web/tree-view.css, whose selectors start at tanrei-tree-view and are wrapped in :where(); the element adopts the same sheet into the root it is connected to, so it also works inside another component's shadow root. Before the element is defined the stylesheet already draws the whole look: the indent from the nesting depth (a counter-free rule per nesting step through a custom property the nested ul raises), the chevron as the row's ::before in the icon font on an li that holds a ul or has data-expandable, turned under li[data-open], the rows under an li without data-open not shown, the guide line as the nested ul's ::before, the selected fill from aria-selected once the element has set it and from [value] matching only after definition (a selected row is the one thing drawn at upgrade: the product that renders on a server writes aria-selected="true" itself to have it in the first frame). Under @media (scripting: none) every level is shown and no chevron is drawn: a plain nested list in the tree's type and spacing. The element, once defined, adds what a stylesheet cannot: role="tree" and aria-label on itself (aria-multiselectable for multiple), role="none" on each <ul> directly inside it and role="group" on each nested one, role="treeitem", aria-expanded, aria-selected, aria-disabled and aria-busy on each <li> (level, set size and position come from the nesting; they are written as aria-level, aria-setsize and aria-posinset only where a product marks a list as partial), the roving tabindex (0 on one <li>, -1 on the others) with the focus ring drawn on the row by li:focus-visible > :first-child, the keys of TRV-20 to TRV-23, the pointer rules of TRV-10 (the chevron's hit area is the row's ::before box extended by the indent), change with detail.value or detail.values, the shared press primitive on the row (ripple and dim, TRV-29), the loading row (a <tanrei-loading-indicator size="small"> and the label in an element of its own after the nested list, aria-hidden apart from the indicator's name), opening for find-in-page where the browser reports it (closed lists are hidden with hidden="until-found" where the browser has it and answered on beforematch; elsewhere closed rows are not found, TRV-14), the motion of TRV-11 (the nested list's height from 0 to auto under interpolate-size: allow-keywords where the browser can, one measured Web Animations run elsewhere, at once where neither is available) and every debug report. A MutationObserver on the subtree keeps roles and the tab stop right when the product adds or removes items. value and values are properties and attributes (values as a space-separated list is not taken: a value may hold a space; the attribute is JSON, the property an array). Inside .tanrei-prose the element is an island by its tag name (PRS-16). Markdown: the product wraps a nested list in <tanrei-tree-view> at build time and wraps each item's own text in its row element (a step of a few lines, shown in the README); Tanrei does not transform content. React: TreeView and TreeItem render the same elements. indent and icon-gap take a number of pixels, or auto (the default). guides takes none, shown or hover; the bare attribute is shown. expandable and sticky-parents are on by default: expandable="false" and sticky-parents="false" turn them off. data-supporting-position="inline" on an <li> puts its supporting text after its label. A one-line label is the row's label element (<bdi>, <span>): a label written as the row's own text wraps, and is reported in debug builds. The full-text tooltip is a <tanrei-tooltip> the element puts in the row it shows it for. Pinned rows are the rows themselves, held by position: sticky (the element marks the innermost sticky-max parents above what is shown). Editing: edit(item) opens the edit field on an <li> (F2 and a double-click on the label do the same while editable); create({ parent, icon }) adds the new row and returns whether it did; create is dispatched on the tree with detail.label, detail.parent, detail.within and detail.after (the <li> elements, or null). The edit field is a <tanrei-embedded-text-input> the element puts in the row and takes away again; its own change does not leave the row. The new row is an <li data-tanrei-new> of the element's own, as the loading row is. Filter (TRV-54 to TRV-71): the element inserts, before its first list, a box of its own (data-tanrei-filter) holding a <tanrei-text-input type="search" size="small">, the status line (data-tanrei-filter-status, hidden from assistive technology) and a visually hidden status (role="status", aria-atomic="true"), and removes it when filterable becomes false; the input carries aria-controls (the tree), set through the element properties of its native input, and no aria-activedescendant (TRV-61). It hides a row with an attribute of its own on the <li> (data-tanrei-filtered, display: none in tree-view.css; never hidden="until-found", TRV-70) and opens a parent for the filter with another (data-tanrei-filter-open), writing neither data-open nor hidden, and draws the active match with data-tanrei-filter-active on its row. It marks no match and touches none of the product's text nodes (TRV-60). While the filter box is there the tree role is not on the element: a field and a status may not stand inside a tree, so role="tree", its aria-label and aria-multiselectable move to the first root list (the other root lists are owned through ariaOwnsElements), and back to the element when filterable becomes false; a product's own aria-labelledby on the element is not carried over. The field's aria-controls names that list. The filter box carries data-tanrei-sticky where the tree stands in a scrolling box and data-tanrei-stuck while rows scroll under it and none is pinned. filter-shortcut is a space-separated list of chords; filterchange carries detail.value; focusFilter() is the method of TRV-63. | TanreiTreeView(_ label: String, selection: Binding<Value?>, density:, showsGuides:, loadingLabel:) { TanreiTreeItem(…) { … } }, with an initializer taking Binding<Set<Value>> for multiple and one without a selection for none, as TanreiList spells its three forms; a data-driven initializer takes a collection and a key path to its children, as the platform's outline views do. Built on a lazy stack of rows, not on List or OutlineGroup, whose rows, disclosure mark and selection are the platform's own look. Not used for the same reason: DisclosureGroup. Semantics: no tree trait exists for custom views; each row is one element with the selected trait and a value of expanded or collapsed, in order; on macOS the row also carries its disclosure level through the AppKit accessibility protocol. On iOS the level is not told (TRV-32's fallback), which the README states. Keys (TRV-19 to TRV-23) apply with a hardware keyboard and on macOS; .focusable() on the tree with a focused-row state gives the single stop. Not yet built: the shared row model — chevron, expandable, guides none/shown/hover, indent auto or a number, iconSpace, iconGap, highlight, one-line labels and notes with their tooltip, supportingPosition, stickyParents and stickyMax, the multi-select keys (TRV-2 to TRV-8, TRV-43, TRV-44, TRV-49 to TRV-53) — and openIcon (TRV-5), and editing in place — the edit field, rename, the method that opens the field and the one that asks for a new row, create (TRV-45 to TRV-48) — are proposed spellings, decided when the platform is built. Filter (TRV-54 to TRV-71), not built yet: as the navigation drawer's on this platform (the field, the polite announcement, the method that focuses the field). | TanreiTreeView(label, selection, value, onValueChange, …) { TanreiTreeItem(…) { … } }, controlled; the multiple form takes values: Set<String> and onValuesChange. The scope flattens the shown items into a LazyColumn (the product's scrolling box where it gives none). Semantics: collectionInfo on the tree and collectionItemInfo on each row, selected, stateDescription left to the platform's expanded state through expand() and collapse() actions, disabled(); the platform's hierarchical collection flag where the toolkit exposes it, otherwise the level is not told (TRV-32's fallback), which the README states. Keys through onKeyEvent on the tree with a FocusRequester per shown row and one row in the focus order. Not yet built: the shared row model — chevron, expandable, guides none/shown/hover, indent auto or a number, iconSpace, iconGap, highlight, one-line labels and notes with their tooltip, supportingPosition, stickyParents and stickyMax, the multi-select keys (TRV-2 to TRV-8, TRV-43, TRV-44, TRV-49 to TRV-53) — and openIcon (TRV-5), and editing in place — the edit field, rename, the method that opens the field and the one that asks for a new row, create (TRV-45 to TRV-48) — are proposed spellings, decided when the platform is built. Filter (TRV-54 to TRV-71), not built yet: as the navigation drawer's on this platform (the field, the polite announcement, the method that focuses the field). | TanreiTreeView(semanticLabel:, selection:, value:, values:, onChanged:, density:, guides:, loadingLabel:, items: [TanreiTreeItem(…)]). Not built on the framework's TreeSliver or the Material library's ExpansionTile, whose rows and marks are their own look; it may read how the framework's tree lays out and reveals rows. Shrink-wrapped by default; a TanreiTreeView.sliver form builds rows lazily in the product's scroll view. Semantics: the tree and tree-item roles where the framework has them, with expanded, selected and enabled; otherwise one node per row in order with those flags and no level (TRV-32's fallback), stated in the README. Keys through Focus and Shortcuts on the tree with one focus node in the traversal order. Not yet built: the shared row model — chevron, expandable, guides none/shown/hover, indent auto or a number, iconSpace, iconGap, highlight, one-line labels and notes with their tooltip, supportingPosition, stickyParents and stickyMax, the multi-select keys (TRV-2 to TRV-8, TRV-43, TRV-44, TRV-49 to TRV-53) — and openIcon (TRV-5), and editing in place — the edit field, rename, the method that opens the field and the one that asks for a new row, create (TRV-45 to TRV-48) — are proposed spellings, decided when the platform is built. Filter (TRV-54 to TRV-71), not built yet: as the navigation drawer's on this platform (the field, the polite announcement, the method that focuses the field). | <TreeView accessibilityLabel="Project files" selection="single" value={…} onChange={…}> with TreeItem children; value is controlled when given, with defaultValue otherwise, and values likewise. Rows are views in a column; a TreeView.Flat form takes the shown rows as data for the product's FlatList. Semantics: accessibilityState={{ expanded, selected, disabled, busy }} on each row and the tree and tree item roles where the platform maps them; the level is not told on iOS and Android (TRV-32's fallback), stated in the README. Keys apply where a hardware keyboard is present (and on the web through React Native for Web). Follows the app's direction where an ancestor's is not known. Not yet built: the shared row model — chevron, expandable, guides none/shown/hover, indent auto or a number, iconSpace, iconGap, highlight, one-line labels and notes with their tooltip, supportingPosition, stickyParents and stickyMax, the multi-select keys (TRV-2 to TRV-8, TRV-43, TRV-44, TRV-49 to TRV-53) — and openIcon (TRV-5), and editing in place — the edit field, rename, the method that opens the field and the one that asks for a new row, create (TRV-45 to TRV-48) — are proposed spellings, decided when the platform is built. Filter (TRV-54 to TRV-71), not built yet: as the navigation drawer's on this platform (the field, the polite announcement, the method that focuses the field). |
令牌
| 令牌 | 引用 | 解析值(浅色) |
|---|---|---|
--tanrei-comp-tree-view-chevron-size-compact | sys.icon.extra-small | 16px |
--tanrei-comp-tree-view-chevron-size-regular | sys.icon.small | 20px |
--tanrei-comp-tree-view-container-gap-compact | sys.space.space100 | 8px |
--tanrei-comp-tree-view-container-gap-regular | sys.space.space150 | 12px |
--tanrei-comp-tree-view-container-height-compact | — | 32px |
--tanrei-comp-tree-view-container-height-regular | — | 48px |
--tanrei-comp-tree-view-container-padding-block-compact | — | 6px |
--tanrei-comp-tree-view-container-padding-block-regular | — | 12px |
--tanrei-comp-tree-view-container-padding-inline-compact | sys.space.space100 | 8px |
--tanrei-comp-tree-view-container-padding-inline-regular | sys.space.space150 | 12px |
--tanrei-comp-tree-view-container-shape | sys.shape.corner.small | 6px |
--tanrei-comp-tree-view-disabled-opacity | — | 0.38 |
--tanrei-comp-tree-view-filter-gap | sys.space.space100 | 8px |
--tanrei-comp-tree-view-filter-status-color | sys.color.on-surface-variant | |
--tanrei-comp-tree-view-focused-opacity | — | 1 |
--tanrei-comp-tree-view-guide-color | sys.color.outline-variant | |
--tanrei-comp-tree-view-guide-width | sys.border.hairline | 1px |
--tanrei-comp-tree-view-hovered-opacity | — | 1 |
--tanrei-comp-tree-view-icon-gap | sys.space.space150 | 12px |
--tanrei-comp-tree-view-icon-size-compact | sys.icon.extra-small | 16px |
--tanrei-comp-tree-view-icon-size-regular | sys.icon.small | 20px |
--tanrei-comp-tree-view-level-indent-compact | sys.space.space300 | 24px |
--tanrei-comp-tree-view-level-indent-regular | sys.space.space400 | 32px |
--tanrei-comp-tree-view-motion-disclosure-duration | sys.motion.role.disclosure | 400ms |
--tanrei-comp-tree-view-motion-disclosure-easing | sys.motion.easing.spatial | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-tree-view-motion-feedback-duration | sys.motion.role.feedback | 450ms |
--tanrei-comp-tree-view-motion-state-duration | sys.motion.role.state.large | 250ms |
--tanrei-comp-tree-view-motion-state-easing | sys.motion.easing.state | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-tree-view-pressed-opacity | — | 1 |
--tanrei-comp-tree-view-selected-chevron-color | sys.color.on-secondary-container | |
--tanrei-comp-tree-view-selected-container-color | sys.color.secondary-container | |
--tanrei-comp-tree-view-selected-disabled-chevron-color | — | |
--tanrei-comp-tree-view-selected-disabled-container-color | — | |
--tanrei-comp-tree-view-selected-disabled-icon-color | — | |
--tanrei-comp-tree-view-selected-disabled-label-color | — | |
--tanrei-comp-tree-view-selected-disabled-state-layer-color | — | |
--tanrei-comp-tree-view-selected-disabled-supporting-text-color | — | |
--tanrei-comp-tree-view-selected-disabled-trailing-text-color | — | |
--tanrei-comp-tree-view-selected-focused-chevron-color | — | |
--tanrei-comp-tree-view-selected-focused-container-color | — | |
--tanrei-comp-tree-view-selected-focused-icon-color | — | |
--tanrei-comp-tree-view-selected-focused-label-color | — | |
--tanrei-comp-tree-view-selected-focused-state-layer-color | — | |
--tanrei-comp-tree-view-selected-focused-supporting-text-color | — | |
--tanrei-comp-tree-view-selected-focused-trailing-text-color | — | |
--tanrei-comp-tree-view-selected-hovered-chevron-color | — | |
--tanrei-comp-tree-view-selected-hovered-container-color | — | |
--tanrei-comp-tree-view-selected-hovered-icon-color | — | |
--tanrei-comp-tree-view-selected-hovered-label-color | — | |
--tanrei-comp-tree-view-selected-hovered-state-layer-color | — | |
--tanrei-comp-tree-view-selected-hovered-supporting-text-color | — | |
--tanrei-comp-tree-view-selected-hovered-trailing-text-color | — | |
--tanrei-comp-tree-view-selected-icon-color | sys.color.on-secondary-container | |
--tanrei-comp-tree-view-selected-label-color | sys.color.on-secondary-container | |
--tanrei-comp-tree-view-selected-pressed-chevron-color | — | |
--tanrei-comp-tree-view-selected-pressed-container-color | — | |
--tanrei-comp-tree-view-selected-pressed-icon-color | — | |
--tanrei-comp-tree-view-selected-pressed-label-color | — | |
--tanrei-comp-tree-view-selected-pressed-state-layer-color | — | |
--tanrei-comp-tree-view-selected-pressed-supporting-text-color | — | |
--tanrei-comp-tree-view-selected-pressed-trailing-text-color | — | |
--tanrei-comp-tree-view-selected-state-layer-color | comp.tree-view.selected.label.color | |
--tanrei-comp-tree-view-selected-supporting-text-color | sys.color.on-secondary-container | |
--tanrei-comp-tree-view-selected-trailing-text-color | sys.color.on-secondary-container | |
--tanrei-comp-tree-view-sticky-container-color | sys.color.surface | |
--tanrei-comp-tree-view-sticky-divider-color | sys.color.outline-variant | |
--tanrei-comp-tree-view-sticky-divider-width | sys.border.hairline | 1px |
--tanrei-comp-tree-view-unselected-chevron-color | sys.color.on-surface-variant | |
--tanrei-comp-tree-view-unselected-container-color | — | |
--tanrei-comp-tree-view-unselected-disabled-chevron-color | — | |
--tanrei-comp-tree-view-unselected-disabled-container-color | — | |
--tanrei-comp-tree-view-unselected-disabled-icon-color | — | |
--tanrei-comp-tree-view-unselected-disabled-label-color | — | |
--tanrei-comp-tree-view-unselected-disabled-state-layer-color | — | |
--tanrei-comp-tree-view-unselected-disabled-supporting-text-color | — | |
--tanrei-comp-tree-view-unselected-disabled-trailing-text-color | — | |
--tanrei-comp-tree-view-unselected-focused-chevron-color | — | |
--tanrei-comp-tree-view-unselected-focused-container-color | — | |
--tanrei-comp-tree-view-unselected-focused-icon-color | — | |
--tanrei-comp-tree-view-unselected-focused-label-color | — | |
--tanrei-comp-tree-view-unselected-focused-state-layer-color | — | |
--tanrei-comp-tree-view-unselected-focused-supporting-text-color | — | |
--tanrei-comp-tree-view-unselected-focused-trailing-text-color | — | |
--tanrei-comp-tree-view-unselected-hovered-chevron-color | — | |
--tanrei-comp-tree-view-unselected-hovered-container-color | — | |
--tanrei-comp-tree-view-unselected-hovered-icon-color | — | |
--tanrei-comp-tree-view-unselected-hovered-label-color | — | |
--tanrei-comp-tree-view-unselected-hovered-state-layer-color | — | |
--tanrei-comp-tree-view-unselected-hovered-supporting-text-color | — | |
--tanrei-comp-tree-view-unselected-hovered-trailing-text-color | — | |
--tanrei-comp-tree-view-unselected-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-tree-view-unselected-label-color | sys.color.on-surface | |
--tanrei-comp-tree-view-unselected-pressed-chevron-color | — | |
--tanrei-comp-tree-view-unselected-pressed-container-color | — | |
--tanrei-comp-tree-view-unselected-pressed-icon-color | — | |
--tanrei-comp-tree-view-unselected-pressed-label-color | — | |
--tanrei-comp-tree-view-unselected-pressed-state-layer-color | — | |
--tanrei-comp-tree-view-unselected-pressed-supporting-text-color | — | |
--tanrei-comp-tree-view-unselected-pressed-trailing-text-color | — | |
--tanrei-comp-tree-view-unselected-state-layer-color | comp.tree-view.unselected.label.color | |
--tanrei-comp-tree-view-unselected-supporting-text-color | sys.color.on-surface-variant | |
--tanrei-comp-tree-view-unselected-trailing-text-color | sys.color.on-surface-variant | |
--tanrei-comp-tree-view-filter-status-typescale | sys.typescale.body-small | 400 13px/1.5385 |
--tanrei-comp-tree-view-label-typescale-compact | sys.typescale.body-small | 400 13px/1.5385 |
--tanrei-comp-tree-view-label-typescale-regular | sys.typescale.body | 400 14px/1.5714 |
--tanrei-comp-tree-view-supporting-text-typescale | sys.typescale.body-small | 400 13px/1.5385 |
--tanrei-comp-tree-view-trailing-text-typescale | sys.typescale.body-small | 400 13px/1.5385 |
测试样例
作为基准的渲染结果。所有平台都以相同的 ID 为每个样例截取快照,以便相互比较。
density: regular (39)
basic
label: Project files items: 4 all-closed
label: Project files items: 4 all-open
label: Project files items: 4 leaves-only
label: Root files items: 4 icons
label: Project files items: 4 guides
label: Project files guides: shown items: 4 chevron-end
label: Project files chevron: end items: 3 chevron-none
label: Project files selection: single value: src/index.ts chevron: none guides: shown items: 4 guides-label
label: Project files chevron: none guides: shown items: 4 indent-16
label: Project files guides: shown indent: 16 items: 4 indent-0
label: Project files guides: shown indent: 0 items: 4 icon-space-auto
label: Project files items: 3 icon-space-never
label: Project files iconSpace: never items: 3 highlight-full
label: Project files selection: single value: src/components/checkbox.ts highlight: full items: 4 not-expandable
label: Project files expandable: false guides: shown items: 4 supporting-inline
label: Where the tokens live items: 3 long-labels-wrap
label: Generated files selection: single value: swift labelWrap: true supportingWrap: true items: 1 single
label: Project files selection: single value: src/components/checkbox.ts items: 4 single-parent-selected
label: Project files selection: single value: src/components items: 4 single-none-selected
label: Project files selection: single items: 4 multiple
label: Project files selection: multiple values: ["src/components/button.ts","src/compone… items: 4 multiple-parent-and-child
label: Project files selection: multiple values: ["src/components","src/components/tree-v… guides: shown items: 4 supporting-text
label: Where the tokens live items: 3 trailing-text
label: Project files items: 3 all-parts
label: Project files selection: single value: src/index.ts guides: shown items: 2 disabled
label: Token sources selection: multiple values: ["themes/dark","generated"] items: 3 loading
label: Repository loadingLabel: Loading… items: 4 long-labels
label: Generated files selection: single value: swift items: 1 deep
label: Catalogue guides: shown items: 1 japanese
label: ドキュメント selection: single value: tokens items: 2 rtl
label: ملفات المشروع selection: single value: list direction: rtl items: 2 icons-rtl
label: الإعدادات direction: rtl items: 2 filter-empty-field
label: Project files filterable: true filterLabel: Filter files filterPlaceholder: Filter filterEmptyText: No matches filterCountText: Matches: {count} filterClearLabel: Clear items: 4 filter
label: Project files filterable: true filterValue: tree ts filterLabel: Filter files filterPlaceholder: Filter filterEmptyText: No matches filterCountText: Matches: {count} filterClearLabel: Clear items: 4 filter-count-hidden
label: Project files filterable: true filterValue: tree ts filterLabel: Filter files filterPlaceholder: Filter filterEmptyText: No matches filterCountText: Matches: {count} filterCountHidden: true filterClearLabel: Clear items: 4 filter-parent
label: Project files filterable: true filterValue: styles filterLabel: Filter files filterPlaceholder: Filter filterEmptyText: No matches filterCountText: Matches: {count} filterClearLabel: Clear items: 4 filter-opened
label: Project files filterable: true filterValue: test filterLabel: Filter files filterPlaceholder: Filter filterEmptyText: No matches filterCountText: Matches: {count} filterClearLabel: Clear items: 4 filter-none
label: Project files filterable: true filterValue: zzz filterLabel: Filter files filterPlaceholder: Filter filterEmptyText: No matches filterCountText: Matches: {count} filterClearLabel: Clear items: 4 filter-single
label: Project files selection: single value: src/components/checkbox.ts filterable: true filterValue: button filterLabel: Filter files filterPlaceholder: Filter filterEmptyText: No matches filterCountText: Matches: {count} filterClearLabel: Clear items: 4 density: compact (9)
compact
label: Project files density: compact items: 4 compact-guides
label: Project files density: compact guides: shown items: 4 chevron-none-compact
label: Project files density: compact chevron: none guides: shown items: 4 single-compact
label: Project files selection: single value: src/index.ts density: compact items: 4 all-parts-compact
label: Project files selection: single value: src/index.ts density: compact guides: shown items: 2 file-tree
label: Where the tokens live density: compact guides: shown items: 3 studio-tokens
label: Every token selection: single value: sys.color.secondary-container density: compact items: 4 rtl-guides-compact
label: ملفات المشروع density: compact guides: shown direction: rtl items: 2 ltr-labels-rtl
label: ملفات الرموز density: compact guides: shown labelWrap: true supportingWrap: true direction: rtl items: 3 生成自 specs/tree-view.spec.json, tokens/comp/tree-view.tokens.json, design/tokens/
| 你手上的是 | 使用 |
|---|---|
| 供人浏览或在其中做选择的结构 | 树视图 |
| 产品的各个去处,以链接的形式 | 导航抽屉 |
| 一个页面的各个标题 | 目录 |
| 折叠在几个标题行之下的内容 | 折叠面板 |
| 只有一级的项 | 列表 |
| 项 | 按它的行 | 按它的 V 形箭头 |
|---|---|---|
| 可以选择,是父项 | 选择它 | 展开或折叠它 |
| 可以选择,是叶子项 | 选择它 | — |
| 不能选择,是父项 | 展开或折叠它 | 展开或折叠它 |
| 不能选择,是叶子项 | 什么也不发生 | — |
一次按下绝不会做两件事。用户选择一个文件夹时,不会因为它同时折叠了而找不到自己原来的位置。
当 selection 为 single 或 multiple、项有 data-value 并且没有被禁用时,这个项可以被选择。单选时,再次按下已选中的项不会改变任何东西。多选时,每个项都是单独选择的:选择父项并不会选择它包含的项。
选择之后做什么由你决定。响应 change,在树旁边的窗格里显示选中的文件,或选中令牌的编辑器。行里不放任何控件:请把操作放在树的旁边。
树把选中填充画在项自己的行上。它不会因为父项下面有选中的项而标记父项,也不会为了显示选择而展开折叠着的父项。如果选中的项应当在视野之内,请在它上面的各级父项上设置 data-open:路径是你知道的。
density |
单行文字的行 | 用于 |
|---|---|---|
regular(默认) |
48 | 既用触摸、也用指针操作的产品。 |
compact |
32 | 用指针浏览的工具,以及文字页面里的一棵小树。 |
compact 的行比指针目标还矮。这是可以接受的,因为它是按名称明确要求的,从来不是默认值。对触摸而言,两种密度下行的高度都达到触摸目标。
chevron end:像菜单那样,把标记放在值之后
- src12
- components9
- button.ts4 KB
- index.ts1 KB
- components9
- package.json2 KB
chevron |
位置 | 用于 |
|---|---|---|
start(默认) |
在图标之前;展开时转动四分之一圈 | 文件树和树模式。 |
end |
在后置文本之后;expand_more,展开时转动半圈 |
设置等像菜单一样阅读的树:导航抽屉的位置。 |
none |
不画 | 用自己的字形表示状态的文件夹。 |
chevron none:文件夹字形表示展开和折叠,指针移上去时出现引导线
- src
- components
- button.ts
- index.ts
- components
- package.json
使用 chevron="none" 时,图标指定了展开字形(data-open-icon="folder_open")的父项用字形表示展开和折叠,按下字形即可展开或折叠。没有图标的父项通过按下它的行来展开或折叠;但如果按下行会选择它,则选择优先,由方向键来展开。方向键始终可以展开和折叠。
expandable="false" 会去掉展开和折叠本身:每个父项都显示为展开,没有箭头,也没有切换,没有哪一行带有展开或折叠的状态。用于始终完整显示的结构。
guides="shown"(或不带值的属性)从每个展开的父项向下画一道细线,位于它所包含的各行旁边。guides="hover" 只在指针位于树上方或焦点在树内时才画。在很深的树里使用它,因为那里单靠缩进已经看不出哪些行是一组的。线位于父项行中第一个前置元素的中心下方:开头的箭头;没有则为图标;再没有则为标签的起点。它不占空间;缩进为 0 时不画。
indent |
子项从哪里开始 |
|---|---|
auto(默认) |
从父项标签开始的位置:每一级的距离取决于父项行中标签之前的内容——开头的箭头、图标。标签之前什么都没有的行,按该级的缩进令牌把子项向里移。 |
| 数字 | 比父项多缩进这么多像素。0 不设层级缩进,也不画引导线。 |
icon-space="auto"(默认)在同一级有其他行带图标时,也为没有图标的行保留图标的位置,使同一级的标签对齐成一列。always 为每一行保留,never 只在有图标的行保留。icon-gap 以像素设定图标与标签之间的距离;保持 auto 时为令牌 comp.tree-view.icon.gap,两种密度相同。
highlight="indented"(默认)从该级开始的位置绘制行的填充、悬停与按下以及焦点环,让填充体现层级。highlight="full" 则像文件浏览器那样横跨整棵树绘制。两种情况下点击区域都是整个宽度。
标签和辅助文本各占一行,放不下的部分用省略号截断。被截断的文本在悬停和键盘聚焦时在工具提示中完整显示,按 Escape 可隐藏;无障碍名称始终是完整的标签。设置 label-wrap 和 supporting-wrap 后,它们改为换行。辅助文本位于标签下方;在 <li> 上加 data-supporting-position="inline" 会把它放在标签后面、同一行上。请把标签放进它自己的元素(<bdi>):直接写在行里的标签会换行。
在可滚动的框中,所显示的行之上那些展开的父项的行,会像编辑器的粘性滚动一样固定在框的顶部,它们的子项从下面滚过。最多固定 sticky-max 行(2),里层的父项优先。固定的就是这些行本身:按下固定的父项会将它折叠,键盘移到被它们遮住的行时,会滚动到固定行的下方。sticky-parents="false" 关闭此功能。不在自己的滚动框中的树不会固定任何行。
| 按键或操作 | 多选时 |
|---|---|
| Space、Enter、按下 | 选择或取消选择该项,并把它作为锚点。 |
| Shift+下、Shift+上 | 移动焦点,并选择从锚点到焦点行的范围;往回移动会缩小范围。 |
| Shift+Space、按住 Shift 按下 | 选择从锚点到该行的范围。 |
| Ctrl+Shift+Home、Ctrl+Shift+End | 选择从焦点行到所显示的第一行或最后一行。 |
| Ctrl+A | 选择所显示的、可以选择的所有行。 |
在 Apple 平台上用 Command 代替 Ctrl。每次操作只触发一次 change。
设置 editable 后,双击一行的标签,或在有焦点的行上按 F2,名字会进入一个输入框,并选中扩展名之前的部分。按 Enter 或离开输入框即确定新名字;按 Escape 则放弃。树自己从不更改标签:它触发 rename,如果你接受这个名字,就由你来设置标签。
tree.addEventListener('rename', (event) => {
const li = event.target;
if (renameFile(li.dataset.value, event.detail.label)) li.querySelector('bdi').textContent = event.detail.label;
});
newFileButton.addEventListener('click', () => tree.create({ icon: 'draft' }));
newFolderButton.addEventListener('click', () => tree.create({ parent: true, icon: 'folder' }));
tree.addEventListener('create', (event) => {
const li = makeItem(event.detail.label, event.detail.parent);
event.detail.after ? event.detail.after.after(li) : tree.querySelector('ul').append(li);
});create() 会在选中项旁边、同一层加一行树自己的行,带一个空输入框。在给出名字之前,你的数据里什么也不会加。create 带着这个名字,你据此加上的项会获得焦点。按 Escape 或名字为空时,什么也不会留下。用 edit-label(“名称”)或 setWords({ editLabel }) 给输入框起名。
树给它什么就显示什么,它不滚动,也不会只绘制视野之内的行。请把它放进你自己的滚动框里。
- 大的父项在展开时再给出它的子项:给它标上
data-expandable,并在toggle表明它已展开时加上它的<ul>。获取期间设置data-loading。 - 只有显示出来的行才有开销。折叠着的父项的子项在它展开之前不会被加样式、排版或朗读。
- 无论树有多大,一行的改动只会重新计算这一行的样式。
设置 filterable 后,树会在第一行上方画一个筛选输入框,它固定在你的滚动框顶部,被固定的父项排在它下面。用户键入的内容会把树缩小到标签中含有它的那些行;清空输入框,树就恢复原样。
- 什么算匹配。 标签中含有键入的每一个词的行算作匹配,每个词可以出现在标签中的任何位置,顺序不限,不区分大小写、字符的宽度(全角与半角)和字母上的附加符号。只比较标签,不比较辅助文本、后置文本和值。还没有给出子项的父项(
data-expandable)不会被搜索。 - 留下的内容。 不匹配的行被隐藏。包含匹配项的父项保留,因自身名称而匹配的父项连同内容整体保留。
- 父项临时展开。 包含匹配项的折叠父项会为了筛选而画成展开的样子。它的
data-open不变,也不会触发toggle,所以清空输入框后它会重新折叠。筛选期间用户自己展开或折叠的父项归用户所有:它保持用户留下的状态。 - 第一个匹配项随时可取。 焦点在输入框中时,第一个匹配的行会画成获得焦点的样子。输入时不会朗读它,按下方向键把焦点移到它上面时,辅助技术才会读出。按 Enter 会像在那一行上按 Enter 一样取用它:选中它,或者展开、折叠一个不能选择的父项。按下方向键会把焦点移到这一行本身,进入树中。Escape 清空输入框;在某一行上按 Escape 会清空筛选,让用户停在找到的项上,看到它周围的项。
- 在行上按“/” 会把焦点移到输入框;其他字母仍然像以前一样,按开头的字母跳到对应的行。树只在行上监听这个键,也不会画出提示。
filter-shortcut可以换成别的键,focusFilter()则让你绑定一个对整个窗口都有效的键。 - 数量。 筛选期间,结果顶部会用低调的一行说明有多少行匹配(
filter-count-text),一行都不匹配时则显示filter-empty-text。设置filter-count-hidden后不画数量,但仍会告诉辅助技术。 - 不标记匹配项。 筛选后留下的行就是结果,所以每一行看起来都和不筛选时一样,就像代码评审中的文件树或文档网站侧边栏的筛选那样。树从不改动你的标签文本。
如果你用别的方式判断匹配——在服务器上查找、排序,或者比较标签以外的内容——就不要设置 filterable,把符合条件的项交给树,在包含它们的父项上设置 data-open,并在你自己的状态文本里说明有多少项符合。
每一级都比上一级更靠里。如果这样会让某行的标签从树宽度的一半之后才开始,这一行最多只缩进到让标签正好从一半处开始,所以名称始终保有一半的宽度。辅助技术仍然会得知真正的层级。
除了选中填充之外,一切都在脚本加载之前画好;选中填充在元素定义时才出现。要让它出现在第一帧里,请自己在选中项的行上写 aria-selected="true"。
- 用你的产品的语言写
label;只要有项可能处于加载中,也要写loading-label;要重命名或给新项起名时,也要写edit-label。loading-label和edit-label也可以用setWords({ loadingLabel, editLabel })为整个产品只给一次。 - 字形由你自己指定:文件夹和文件是你的产品里的种类,不是树的。
- 让名称保持完整。标签会换行,绝不会被截断。
- 不要在行里放链接、按钮或输入框。树的任何按键都到不了它。
- 不要把树用于网站的页面。链接是通过浏览器和屏幕阅读器列出链接的那些方式被找到的。
- 不要靠移动焦点来选择。方向键负责移动;Enter 和 Space 负责选择。
- 不要在可筛选的树的行上把“/”另作他用。请把
filter-shortcut设为别的键,或者不设任何键。
- 角色
tree- 标注
- The tree is named by label. An item's label (or the text of its label slot) is its accessible name; its supporting text, the text of its trailing content and its trailing text are its description, in that order. A parent carries the expanded or collapsed state, a loading parent is busy, and in a selectable tree an item that can be chosen carries the selected state. The chevron, the icon and the guide lines are decorative. The words of a loading row are the product's (loadingLabel); Tanrei ships none. The edit field is named by editLabel, the product's words ("Name"); a new row is a tree item at its level holding that field. The filter field is a search field named by filterLabel, the product's words, controlling the tree; its clear button is named by filterClearLabel. The field names no active descendant: the active match is read once the person moves to it with Down Arrow. How many rows match is drawn at the top of the results, hidden from assistive technology, and said by a status beside the tree, in the product's filterCountText or filterEmptyText — the one thing the tree announces.
- 最小目标
- 即使外观尺寸更小,点击区域也满足最小目标尺寸。 minimum 32px · pointer 44px · spacing 8px · touch 48px
键盘操作
| 按键 | 动作 |
|---|---|
| Tab / Shift+Tab | Moves focus into the tree (to the row focused last, the first selected item that is shown, or the first row) and out of it. The tree is one stop; no row and nothing in a row is another. |
| Down Arrow / Up Arrow | Moves focus to the next or previous row that is shown, whatever its level, without wrapping. Opens nothing and does not change the selection. |
| Right Arrow (Left Arrow in right-to-left layouts) | On a closed parent, opens it; on an open parent, moves focus to the first item it shows; on a leaf, nothing. |
| Left Arrow (Right Arrow in right-to-left layouts) | On an open parent, closes it; on any other row below the first level, moves focus to its parent; on a row of the first level, nothing. |
| Home / End | Moves focus to the first or the last row that is shown. |
| Enter / Space | What a press on the row does: chooses the item where it can be chosen (single: selects it; multiple: adds it to or removes it from the selection); opens or closes a parent that cannot be chosen; nothing on a leaf that cannot be chosen. |
| * (asterisk) | Opens every closed parent among the focused row's siblings. Focus stays. |
| Printable characters | Moves focus to the next shown row whose label begins with the characters typed. |
| Shift+Down Arrow / Shift+Up Arrow (multiple) | Moves focus and selects from the anchor to the focused row; going back shrinks the range. |
| Shift+Space (multiple) | Selects from the anchor to the focused row. |
| Ctrl+Shift+Home / Ctrl+Shift+End (multiple; Command on Apple platforms) | Selects from the focused row to the first or the last row shown, and moves focus there. |
| Ctrl+A (multiple; Command on Apple platforms) | Selects every item that can be chosen among the rows shown. |
| Escape (over a tooltip) | Hides the tooltip of a cut label or note. |
| F2 | WHILE editable is true: opens the edit field on the focused row's name. Enter in the field gives the new name, Escape gives it up, and focus returns to the row. |
| Enter / Escape (in an edit field) | Enter gives the name typed (rename, or create for a new row); Escape gives it up. Leaving the field by Tab or a press elsewhere also gives the name. While an input method is composing, both are the input method's. |
| / (the filterShortcut chord, on a row) | WHILE filterable is true: moves focus to the filter field. Not taken as type-ahead. |
| Enter (in the filter field) | Takes the first row that matches — the active match, drawn as a focused row while focus stays in the field and not read out until the person moves to it — as Enter on that row would: chooses it, or opens or closes a parent that cannot be chosen. |
| Down Arrow (in the filter field) | Moves focus to the active match itself, where it is read as a focused row. |
| Escape (in the filter field) | With text: empties the field and keeps focus there. Empty: returns focus to the row it came from by "/", or passes on. |
| Escape (on a row, while a filter is applied) | Empties the filter and keeps focus on the row, which stays in place among its neighbours. |
备注
- A tree view is the tree pattern: one stop in the focus order, the arrow keys moving through the rows that are shown, the horizontal arrows opening, closing and stepping between levels. It is the opposite choice from the navigation drawer and the accordion, on purpose. A drawer navigates: its destinations are links and buttons, each its own stop, found by the ways a browser and a screen reader list links, and its groups are disclosures. An accordion folds content: each header is a button and its own stop, and the arrow keys stay the page's. A tree reveals or selects a hierarchy: it may hold hundreds of rows, which no person should have to Tab through, and what it holds are items of one structure, not controls. Use the drawer for the places of a product, the accordion for folded content, and the tree for a structure a person looks through or chooses in.
- A row holds no control (TRV-6): the tree role gives each row one name and one action, and a link, a button or a field inside it could be reached by no key of the pattern. An item is therefore not a link and has no destination: a hierarchy of pages is a navigation drawer or a table of contents, which keep the platform's links. What a press on an item does beyond choosing it — opening the chosen file in a pane, showing the chosen token's editor — is the product's answer to change.
- The chevron opens and the row chooses (TRV-10). Where an item cannot be chosen — every item of a tree whose selection is none, and an item without a value in any tree — the whole row opens and closes it, so that a tree that is only read has rows as easy to hit as an accordion's. One press never does two things: a person who chooses a folder does not lose their place because it also closed.
- Selection does not follow focus (TRV-20): moving through a tree with the arrow keys changes nothing, so that a person can read it without the pane beside it changing at every row. Enter or Space chooses.
- The selected state is drawn by the quiet fill, the on-fill color of the label and the glyphs, and the filled form of the icon, as a selected destination of a navigation drawer is; to assistive technology it is the selected state. Under forced colors the row takes the platform's selected colors and an edge (TRV-35). A product that needs each chosen item of a multiple selection to carry a mark of its own puts one in the trailing slot.
- WCAG 2.5.8 (target size): for touch every row is at least the touch target high in both densities (TRV-26). The compact density is lower than the pointer target for a fine pointer, by design: it is the density of a tool looked through with a mouse, it stays above the 24 by 24 the criterion asks, and its rows reach across the tree's whole width. A product that cannot accept that uses regular.
- WCAG 1.4.10 (reflow) and 1.4.4 (resize text): labels wrap and are never cut, rows grow, and the indent never takes more than half of the tree's width (TRV-3), so a deep tree stays readable at 320 wide and at 200 % text. Past that depth the indent no longer shows the level; assistive technology is still told it.
- WCAG 1.4.1 (use of color): open and closed are told by the chevron's turn and by the expanded state, never by color. Disabled is told by the faded row and by the disabled state.
- WCAG 1.4.3 and 1.4.11 (contrast) in the default system: the label is on-surface and the supporting text, trailing text, icon and chevron are on-surface-variant, on the surface the tree stands on; on the selected fill, a layer of that color over the surface, every part is on-secondary-container. Each pair is held to 4.5:1 by the token invariants, the selected text on its fill as it is drawn over each surface role, and the selected fill stands 1.1:1 at least from a hovered row. The guide lines are decorative: the hierarchy is told by the indent and by the structure.
- A loading parent is busy and nothing is announced when its items arrive (TRV-13, TRV-33): a person who opened it finds them with the next Down Arrow. A fetch that fails is the product's to say, beside the tree or as an item whose label says it.
- With filterable, the tree narrows itself to what a person types: the field is a search field before the tree and a stop of its own, so the tree stays one stop; as in a search, the first row that matches is active as soon as there is one while focus stays in the field — drawn as a focused row and, as the search's automatic choice, not read out until the person moves — Enter takes it as Enter on its row would, and Down Arrow moves focus to it, where it is read; the rows a filter hides are gone from what assistive technology is told, so every level, position and size a person hears is the filter's (WCAG 1.3.1); the parents that hold a match are opened for the time being and closed again when the field is emptied, so a filter never rearranges what a person opened; Escape on a row empties the filter and leaves the person on the item they found, among its neighbours. The count is a status message (WCAG 4.1.3), said politely once typing pauses, and drawn quietly at the top of the results unless the product hides it. The rows that match are not marked: what the filter leaves is the answer, and the row Enter takes is drawn as a focused row (TRV-60, TRV-61), so nothing is told by color alone (WCAG 1.4.1). A product that matches in another way — on a server, by a ranking, by more than the label — leaves filterable off, gives the tree the items that match and sets open on the parents that hold them, and says how many matched in a status of its own, as before.
- The tree does not scroll and does not draw only the rows in view: the product places it in its own scrolling box, and gives a parent's items when it opens (expandable, toggle) where the hierarchy is too large to give at once. A platform that builds rows lazily states each item's level, position and set size where the structure no longer gives them.
- Web: the items are the product's own nested lists in the document — a `<ul>` of `<li>` elements, each holding its row and, where it is a parent, a `<ul>` of its own — inside `<tanrei-tree-view>`. Without script the page shows the whole hierarchy as the nested list it is, every level shown; with the script the element gives the lists the tree's roles, states and keys. The stylesheet draws the whole look before the element is defined, so nothing moves when it is upgraded.
- How a state looks is a value decision of the active design system; components read the state's tokens and never compute the effect themselves.
- Renaming in place follows the file managers people already know: F2 opens the field (Windows Explorer, and VS Code on Windows and Linux), a double-click on the label opens it where the product makes the tree editable, the name without its extension is selected (Finder, Explorer, VS Code), Enter or leaving the field gives the name and Escape gives it up. Enter is not the key that opens the field, as it is in Finder: in the tree pattern Enter chooses (TRV-22), and a tree that renamed on Enter would take the one key that chooses. The tree never changes a label itself: the product owns the data, and the field only proposes a name (rename).
- A new item is named in a row of its own before it exists (create), as VS Code's explorer names a new file: nothing is added to the product's data until a name is given, and Escape leaves nothing behind. The new row is placed beside the selected item, at its level.
- A name and a note are one line by default (TRV-6, TRV-7, TRV-50): the whole text is the row's accessible name and description, and while it is cut it is shown in a tooltip on hover and on keyboard focus, which Escape dismisses and the pointer can move into (WCAG 1.4.13). A product whose names must always be read whole on screen sets labelWrap and supportingWrap (WCAG 1.4.10 at 320 wide reads best wrapped).
- Multiple selection follows the tree pattern: Space and a press toggle one item, Shift with the arrows, Space or a press selects a range from the anchor, Ctrl (Command) with Shift and Home or End to the first or last row, Ctrl (Command) with A everything shown; the tree is multiselectable and every item that can be chosen carries its selected state (TRV-52).
- Pinned parent rows (TRV-53) are the rows themselves, not copies: assistive technology meets each once, in its place, and a row the keyboard reaches under them is scrolled into view below them.
- With expandable false the tree is a structure always shown whole: no tree item carries an expanded state, and the groups still tell each level (TRV-49).
- With chevron none, open and closed are told by the glyph (folder, folder_open) and by the expanded state, never by color; the arrow keys always open and close, and a parent with no glyph is reported in debug builds because nothing on screen tells its state (WCAG 1.3.1, 1.4.1).
- "/" on a row moves to the filter, and every other printable character keeps moving by type-ahead (TRV-23), as the tree pattern recommends: the two answer different questions — "where is the next row that begins so" and "show me only what holds this".
生成自 specs/tree-view.spec.json, tokens/sys/measure.tokens.json
- 元素是一个以
label命名的tree。每一行是一个treeitem,每个嵌套列表是一个group,所以级别来自嵌套关系。 - 父项的行带有
aria-expanded。可以选择的行带有aria-selected,值为 true 或 false。禁用的行带有aria-disabled,正在加载的父项带有aria-busy。 - 标签是行的名称。辅助文本、后置内容的文本和后置文本依次构成它的描述。
- 树只占一个停靠点。Tab 进入时,落在上一次获得焦点的行上;没有的话,落在第一个显示出来的选中行上;再没有,就落在第一行上。
- 下方向键和上方向键在显示出来的行之间移动。右方向键展开父项,再按则进入其中。左方向键折叠它,再按则退到外面。在从右到左的布局中,左右方向键互换。
- Home 和 End 前往第一行和最后一行。键入字母会移到标签以这些字母开头的下一行。
*展开与获得焦点的行同级的所有折叠着的父项。 - Enter 和 Space 做的事与按下这一行相同。选择从不跟随焦点。
- 设置
editable时,F2 在有焦点的行的名字上打开输入框。Enter 确定名字,Escape 放弃,焦点回到这一行。输入法正在组字时,这两个键都归输入法。输入框的名称是edit-label,新行是它那一层里带有输入框的树项。 - 使用
chevron="none"时,展开和折叠由字形和aria-expanded告知,从不只靠颜色,方向键始终可以展开和折叠。使用expandable="false"时,没有哪一行带有aria-expanded;分组仍然告知层级。 - 多选时,Shift 加方向键或 Space 选择从锚点开始的范围,Ctrl(Command)加 Shift 加 Home 或 End 选择到第一行或最后一行,Ctrl(Command)加 A 选择所显示的全部。
- 固定的父项行就是这些行本身,所以辅助技术在原位置只会遇到每一行一次。
- 展开、折叠和选择都由行自己的状态来表明。不会朗读任何内容,焦点也不移动。
- 父项折叠时如果获得焦点的行在它里面,焦点就交给这个父项。获得焦点的行被移除时,焦点交给它后面的那一行。
- 焦点环画在行的内部,所以滚动框不会把它裁掉。在禁用的行上,焦点环仍以正常的浓度绘制:行变淡,焦点环不变淡。
- V 形箭头、图标和引导线对辅助技术隐藏。加载行也是隐藏的:由父项说明它正忙。
- 标签和辅助文本各占一行;被截断的文本在悬停和键盘聚焦时在工具提示中完整显示,按 Escape 可隐藏,指针可以移到工具提示上,无障碍名称始终是完整的。设置
label-wrap和supporting-wrap时,文本为 200% 时它们会换行,行会增高。 - 在强制颜色模式下,选中的行采用系统的选中颜色,并加上一道边线。
- 筛选输入框是一个以
filter-label为名称的搜索字段,位于树之前,是一个独立的停靠点,所以树仍然只占一个停靠点。它表明自己控制着这棵树,不指向任何活动后代:按下方向键把焦点移到第一个匹配项时才会读出它。它的清除按钮以filter-clear-label为名称。 - 被筛选隐藏的行对辅助技术来说也不存在了,所以用户听到的每一个层级、位置和数量都是筛选之后的。键入停顿一秒后,树旁边的一个状态会以礼貌(polite)的方式说明有多少行匹配,或者一行都不匹配,焦点不会移动:这是树唯一会朗读的内容。结果顶部画出的数量对辅助技术隐藏,辅助技术听到的是这个状态。
- 匹配的行不加标记,因此没有只靠颜色传达的信息,行的名称仍是完整的标签。
- 调试构建会报告:空的
label、没有标签的项、直接写在行里的标签、行里的控件、不对应任何项的值、两个值相同的项、未知的density、selection、chevron、guides、icon-space或highlight、不是数字的indent或icon-gap、在chevron="none"下没有图标的父项、没有edit-label的输入框、没有filter-label的筛选输入框、没有名称的清除按钮,以及包含不止一个组合键的filter-shortcut。
- 给每个可以选择的项一个在树内唯一的
data-value。 - 获取失败时,由你自己说明:写在树的旁边,或者作为一个用标签说明这一点的项。子项到达时,树不会朗读任何内容。
- 多选时,如果每个选中的项必须靠填充以外的东西来区分,请在后置内容里放一个你自己的标记。
- 把很长的树放进带有名称或标题的滚动框里,让用户知道框里是什么。
- 设置
filterable时,给输入框一个说明它筛选什么的filter-label(“筛选文件”),并为filter-count-text、filter-empty-text、filter-clear-label和filter-placeholder提供你的文字,可以写在树上,也可以用setWords()一次性给出。