@zeyos/zx 4.3.4 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/CHANGELOG.md +438 -0
  2. package/README.md +49 -10
  3. package/dist/types/components/activity-item/activity-item.d.ts +351 -0
  4. package/dist/types/components/activity-list/activity-list.d.ts +217 -0
  5. package/dist/types/components/app-sidebar/app-sidebar.d.ts +33 -3
  6. package/dist/types/components/avatar/avatar.d.ts +46 -7
  7. package/dist/types/components/badge/badge.d.ts +21 -1
  8. package/dist/types/components/button/button.d.ts +23 -0
  9. package/dist/types/components/calendar/calendar.d.ts +19 -0
  10. package/dist/types/components/card/card.d.ts +7 -0
  11. package/dist/types/components/card-view/card-view.d.ts +11 -3
  12. package/dist/types/components/card-view/record-card.d.ts +7 -1
  13. package/dist/types/components/chart/chart.d.ts +10 -0
  14. package/dist/types/components/checklist/checklist.d.ts +9 -0
  15. package/dist/types/components/code-editor/code-editor.d.ts +149 -0
  16. package/dist/types/components/code-editor/field-adapter.d.ts +5 -0
  17. package/dist/types/components/context-menu/context-menu.d.ts +18 -2
  18. package/dist/types/components/date-picker/date-picker.d.ts +8 -0
  19. package/dist/types/components/date-picker/time-picker.d.ts +7 -0
  20. package/dist/types/components/datebox/date-range-box.d.ts +5 -1
  21. package/dist/types/components/datebox/datebox.d.ts +7 -2
  22. package/dist/types/components/dialog/dialog.d.ts +75 -6
  23. package/dist/types/components/dropdown/dropdown.d.ts +2 -2
  24. package/dist/types/components/entity-ref/entity-ref.d.ts +331 -0
  25. package/dist/types/components/field/field.d.ts +24 -2
  26. package/dist/types/components/fieldset/fieldset.d.ts +19 -1
  27. package/dist/types/components/file-item/file-item.d.ts +262 -0
  28. package/dist/types/components/file-list/file-list.d.ts +171 -0
  29. package/dist/types/components/filter/filter.d.ts +91 -36
  30. package/dist/types/components/filter-panel/filter-panel.d.ts +449 -0
  31. package/dist/types/components/form/form.d.ts +12 -1
  32. package/dist/types/components/kanban-view/kanban-card.d.ts +146 -0
  33. package/dist/types/components/kanban-view/kanban-view.d.ts +170 -4
  34. package/dist/types/components/launcher/launcher.d.ts +278 -26
  35. package/dist/types/components/list-toolbar/list-toolbar.d.ts +599 -0
  36. package/dist/types/components/menu-button/menu-button.d.ts +37 -42
  37. package/dist/types/components/message/message.d.ts +126 -17
  38. package/dist/types/components/modal/modal.d.ts +61 -2
  39. package/dist/types/components/navigation-bar/navigation-bar.d.ts +105 -21
  40. package/dist/types/components/panel/panel.d.ts +7 -0
  41. package/dist/types/components/search/search.d.ts +7 -0
  42. package/dist/types/components/select/color.d.ts +12 -0
  43. package/dist/types/components/select/glyphs.d.ts +124 -0
  44. package/dist/types/components/select/select.d.ts +15 -3
  45. package/dist/types/components/sort-control/sort-control.d.ts +105 -0
  46. package/dist/types/components/stat-tile/stat-tile.d.ts +288 -0
  47. package/dist/types/components/tabbox/tabbox.d.ts +16 -0
  48. package/dist/types/components/table/table.d.ts +190 -1
  49. package/dist/types/components/table-view/table-view.d.ts +11 -5
  50. package/dist/types/components/tag-picker/tag-picker.d.ts +21 -4
  51. package/dist/types/components/timebox/timebox.d.ts +7 -0
  52. package/dist/types/components/tree/tree.d.ts +13 -0
  53. package/dist/types/components/value-list/value-list.d.ts +8 -0
  54. package/dist/types/components/view/record-view.d.ts +96 -5
  55. package/dist/types/core/breakpoint.d.ts +1 -1
  56. package/dist/types/core/icons.d.ts +37 -22
  57. package/dist/types/core/motion.d.ts +59 -0
  58. package/dist/types/core/overlay-host.d.ts +15 -1
  59. package/dist/types/index.d.ts +16 -3
  60. package/dist/types/internal/app-rail.d.ts +10 -0
  61. package/dist/types/internal/menu-items.d.ts +134 -0
  62. package/dist/types/zeyos/connect.d.ts +6 -0
  63. package/dist/types/zeyos/form.d.ts +50 -9
  64. package/dist/types/zeyos/index.d.ts +1 -1
  65. package/dist/types/zeyos/query.d.ts +79 -0
  66. package/dist/types/zeyos/schema.d.ts +24 -1
  67. package/dist/types/zeyos/table.d.ts +100 -14
  68. package/dist/types/zeyos/view.d.ts +10 -0
  69. package/dist/zx-zeyos.esm.js +2206 -489
  70. package/dist/zx-zeyos.esm.min.js +56 -1
  71. package/dist/zx.css +3322 -724
  72. package/dist/zx.esm.js +12244 -5809
  73. package/dist/zx.esm.min.js +58 -3
  74. package/dist/zx.global.js +12246 -5811
  75. package/dist/zx.global.min.js +58 -3
  76. package/dist/zx.min.css +1 -1
  77. package/docs/api.json +1683 -146
  78. package/docs/llms.md +777 -110
  79. package/docs/llms.txt +14 -8
  80. package/package.json +1 -1
  81. package/src/components/account-menu/account-menu.css +35 -10
  82. package/src/components/activity-item/activity-item.css +137 -0
  83. package/src/components/activity-item/activity-item.js +602 -0
  84. package/src/components/activity-list/activity-list.css +99 -0
  85. package/src/components/activity-list/activity-list.js +409 -0
  86. package/src/components/app-icon/app-icon.css +1 -1
  87. package/src/components/app-rail/app-rail.css +191 -22
  88. package/src/components/app-sidebar/app-sidebar.css +349 -24
  89. package/src/components/app-sidebar/app-sidebar.js +86 -11
  90. package/src/components/aurora/aurora.css +5 -3
  91. package/src/components/avatar/avatar.css +32 -7
  92. package/src/components/avatar/avatar.js +72 -8
  93. package/src/components/badge/badge.css +54 -2
  94. package/src/components/badge/badge.js +29 -2
  95. package/src/components/button/button.css +74 -8
  96. package/src/components/button/button.js +44 -1
  97. package/src/components/calendar/calendar.css +46 -46
  98. package/src/components/calendar/calendar.js +33 -3
  99. package/src/components/card/card.css +2 -2
  100. package/src/components/card/card.js +12 -1
  101. package/src/components/card-view/card-view.css +10 -7
  102. package/src/components/card-view/card-view.js +25 -9
  103. package/src/components/card-view/record-card.js +7 -3
  104. package/src/components/chart/chart.css +4 -1
  105. package/src/components/chart/chart.js +12 -0
  106. package/src/components/check-button/check-button.css +10 -2
  107. package/src/components/checklist/checklist.css +9 -2
  108. package/src/components/checklist/checklist.js +11 -5
  109. package/src/components/checklist/field-adapter.js +3 -1
  110. package/src/components/code-editor/code-editor.css +68 -0
  111. package/src/components/code-editor/code-editor.js +306 -0
  112. package/src/components/code-editor/field-adapter.js +34 -0
  113. package/src/components/context-menu/context-menu.css +87 -5
  114. package/src/components/context-menu/context-menu.js +22 -30
  115. package/src/components/copy/copy-button.css +6 -0
  116. package/src/components/copy/copy-input.css +8 -3
  117. package/src/components/data-filter/data-filter.css +5 -4
  118. package/src/components/date-picker/date-picker.css +6 -6
  119. package/src/components/date-picker/date-picker.js +22 -8
  120. package/src/components/date-picker/date-range-picker.css +3 -3
  121. package/src/components/date-picker/month-picker.js +3 -3
  122. package/src/components/date-picker/time-picker.css +1 -1
  123. package/src/components/date-picker/time-picker.js +20 -8
  124. package/src/components/datebox/date-range-box.css +79 -2
  125. package/src/components/datebox/date-range-box.js +13 -2
  126. package/src/components/datebox/datebox.css +79 -2
  127. package/src/components/datebox/datebox.js +13 -2
  128. package/src/components/dialog/dialog.css +26 -0
  129. package/src/components/dialog/dialog.js +174 -70
  130. package/src/components/dropdown/dropdown.css +1 -1
  131. package/src/components/dropdown/dropdown.js +7 -5
  132. package/src/components/empty-state/empty-state.css +2 -2
  133. package/src/components/entity-ref/entity-ref.css +182 -0
  134. package/src/components/entity-ref/entity-ref.js +496 -0
  135. package/src/components/field/field.css +28 -9
  136. package/src/components/field/field.js +68 -26
  137. package/src/components/field-upload/field-upload.css +1 -1
  138. package/src/components/fieldset/fieldset.css +5 -3
  139. package/src/components/fieldset/fieldset.js +28 -5
  140. package/src/components/file-item/file-item.css +88 -0
  141. package/src/components/file-item/file-item.js +505 -0
  142. package/src/components/file-list/file-list.css +51 -0
  143. package/src/components/file-list/file-list.js +320 -0
  144. package/src/components/filter/filter.css +27 -1
  145. package/src/components/filter/filter.js +205 -47
  146. package/src/components/filter-panel/filter-panel.css +104 -0
  147. package/src/components/filter-panel/filter-panel.js +964 -0
  148. package/src/components/finder/finder.css +15 -13
  149. package/src/components/form/form.css +5 -4
  150. package/src/components/form/form.js +11 -2
  151. package/src/components/grid/grid.css +1 -1
  152. package/src/components/groupbox/groupbox.css +1 -1
  153. package/src/components/kanban-view/kanban-card.js +247 -0
  154. package/src/components/kanban-view/kanban-view.css +226 -8
  155. package/src/components/kanban-view/kanban-view.js +255 -12
  156. package/src/components/launcher/launcher.css +107 -25
  157. package/src/components/launcher/launcher.js +557 -63
  158. package/src/components/list-toolbar/list-toolbar.css +194 -0
  159. package/src/components/list-toolbar/list-toolbar.js +851 -0
  160. package/src/components/master-panel/master-panel.css +2 -0
  161. package/src/components/menu-button/menu-button.css +87 -5
  162. package/src/components/menu-button/menu-button.js +86 -46
  163. package/src/components/message/message.css +38 -5
  164. package/src/components/message/message.js +230 -17
  165. package/src/components/modal/modal.css +15 -0
  166. package/src/components/modal/modal.js +159 -3
  167. package/src/components/navigation-bar/navigation-bar.css +95 -11
  168. package/src/components/navigation-bar/navigation-bar.js +171 -4
  169. package/src/components/number-field/field-adapter.js +1 -0
  170. package/src/components/number-field/number-field.css +70 -0
  171. package/src/components/number-field/number-field.js +2 -0
  172. package/src/components/pagination/pagination.css +8 -6
  173. package/src/components/panel/panel.css +1 -1
  174. package/src/components/panel/panel.js +13 -2
  175. package/src/components/rating/rating.css +7 -0
  176. package/src/components/search/search.js +17 -5
  177. package/src/components/select/color.js +22 -0
  178. package/src/components/select/field-adapter.js +6 -1
  179. package/src/components/select/glyphs.js +165 -0
  180. package/src/components/select/select.css +138 -45
  181. package/src/components/select/select.js +86 -47
  182. package/src/components/sheet/sheet.css +11 -8
  183. package/src/components/slider/slider.css +16 -2
  184. package/src/components/slider/slider.js +1 -1
  185. package/src/components/sort-control/sort-control.css +20 -0
  186. package/src/components/sort-control/sort-control.js +140 -0
  187. package/src/components/stat-tile/stat-tile.css +208 -0
  188. package/src/components/stat-tile/stat-tile.js +601 -0
  189. package/src/components/tabbox/tabbox.css +32 -27
  190. package/src/components/tabbox/tabbox.js +17 -2
  191. package/src/components/table/table.css +155 -31
  192. package/src/components/table/table.js +386 -33
  193. package/src/components/table-view/table-view.css +8 -2
  194. package/src/components/table-view/table-view.js +21 -6
  195. package/src/components/tag-picker/tag-picker.css +86 -6
  196. package/src/components/tag-picker/tag-picker.js +25 -8
  197. package/src/components/timebox/timebox.css +22 -2
  198. package/src/components/timebox/timebox.js +18 -5
  199. package/src/components/toggle/toggle.css +12 -1
  200. package/src/components/toolbar/toolbar.js +2 -2
  201. package/src/components/tooltip/tooltip.css +4 -0
  202. package/src/components/tooltip/tooltip.js +10 -3
  203. package/src/components/tree/tree.css +25 -16
  204. package/src/components/tree/tree.js +31 -0
  205. package/src/components/value-list/value-list.css +3 -3
  206. package/src/components/value-list/value-list.js +22 -6
  207. package/src/components/view/record-view.css +5 -0
  208. package/src/components/view/record-view.js +100 -7
  209. package/src/core/breakpoint.js +1 -1
  210. package/src/core/icons.js +259 -57
  211. package/src/core/motion.js +147 -0
  212. package/src/core/overlay-host.js +21 -2
  213. package/src/index.js +30 -3
  214. package/src/internal/app-rail.js +18 -1
  215. package/src/internal/menu-items.js +142 -0
  216. package/src/zeyos/connect.js +6 -2
  217. package/src/zeyos/form.js +67 -27
  218. package/src/zeyos/index.js +1 -1
  219. package/src/zeyos/query.js +153 -0
  220. package/src/zeyos/schema.js +144 -27
  221. package/src/zeyos/table.js +68 -5
  222. package/src/zeyos/view.js +6 -0
  223. package/styles/base.css +56 -0
  224. package/styles/icons.css +11 -1
  225. package/styles/print.css +192 -0
  226. package/styles/tokens/dark.css +113 -37
  227. package/styles/tokens/global.css +121 -27
  228. package/styles/tokens/semantic.css +100 -31
  229. package/styles/tokens/themes.css +7 -6
  230. package/styles/zx.css +11 -1
  231. package/styles/tokens/density.css +0 -9
@@ -6,13 +6,19 @@
6
6
  min-inline-size: 0;
7
7
  }
8
8
 
9
- .zx-table-view[data-loading="true"] .zx-table__column-controls {
9
+ .zx-table-view__toolbar {
10
+ min-inline-size: 0;
11
+ }
12
+
13
+ .zx-table-view[data-loading="true"] .zx-table__column-controls,
14
+ .zx-table-view[data-loading="true"] .zx-record-view__field-controls {
10
15
  pointer-events: none;
11
16
  opacity: .62;
12
17
  }
13
18
 
14
19
  @media (prefers-reduced-motion: no-preference) {
15
- .zx-table-view .zx-table__column-controls {
20
+ .zx-table-view .zx-table__column-controls,
21
+ .zx-table-view .zx-record-view__field-controls {
16
22
  transition: opacity var(--zx-dur-fast) var(--zx-ease);
17
23
  }
18
24
  }
@@ -1,7 +1,7 @@
1
1
  // @ts-check
2
2
  import { h, restoreTarget, snapshotTarget } from '../../core/dom.js';
3
3
  import { formatTableCell, Table } from '../table/table.js';
4
- import { RecordView, readViewField } from '../view/record-view.js';
4
+ import { normalizeViewFieldControls, RecordView, readViewField } from '../view/record-view.js';
5
5
 
6
6
  /** @typedef {Record<string, any>} TableViewRecord */
7
7
  /** @typedef {import('../view/record-view.js').ViewField} ViewField */
@@ -20,7 +20,10 @@ import { RecordView, readViewField } from '../view/record-view.js';
20
20
  * @property {unknown[]} [selection=[]] Initially selected record ids.
21
21
  * @property {string[]} [fieldOrder=[]] Preferred stable field order.
22
22
  * @property {string[]} [hiddenFields=[]] Initially hidden field ids.
23
- * @property {boolean} [fieldControls=true] Show the accessible visibility and reorder disclosure.
23
+ * @property {boolean|import('../view/record-view.js').ViewFieldControlsConfig} [fieldControls=true]
24
+ * Show the accessible visibility and reorder disclosure. `true` keeps the composed Table's own
25
+ * column chooser and `false` omits it; an object renders the shared record-view chooser instead,
26
+ * with a translated `label` and, with `target`, mounted into a host element.
24
27
  * @property {string|Node|(()=>string|Node)|null} [emptyText=null] Empty-result content.
25
28
  * @property {TableOptions} [table={}] Advanced low-level Table options. Shared options win where
26
29
  * their contracts overlap; all other options and callbacks are forwarded unchanged.
@@ -74,9 +77,15 @@ export class TableView extends RecordView {
74
77
  this._tableViewDestroyed = false;
75
78
  this._tableHost = h('div', { class: 'zx-table-view__table' });
76
79
  this._initRecordView(root);
80
+ // Only a `fieldControls` object replaces Table's own chooser, and only an unresolved target
81
+ // keeps that replacement inside the view — so `true` and `false` render exactly as before.
82
+ const fieldControls = normalizeViewFieldControls(this.options.fieldControls);
83
+ this._tableViewToolbar = fieldControls.custom && !this._viewFieldControlsTarget
84
+ ? h('div', { class: 'zx-record-view__toolbar zx-table-view__toolbar' })
85
+ : null;
77
86
 
78
87
  try {
79
- root.replaceChildren(this._tableHost);
88
+ root.replaceChildren(...[this._tableViewToolbar, this._tableHost].filter(Boolean));
80
89
  const viewOptions = /** @type {Readonly<TableViewOptions>} */ (this.options);
81
90
  const options = tableOptionsForView(viewOptions.table, {
82
91
  fields: this.getFields(),
@@ -93,9 +102,11 @@ export class TableView extends RecordView {
93
102
  // Seed selection before bridging events so construction stays silent like RecordView itself.
94
103
  this.table.setSelection(this.getSelectionIds());
95
104
  this._bridgeTableEvents();
105
+ if (fieldControls.custom) this._mountViewFieldControls(this._tableViewToolbar ?? root);
96
106
  return root;
97
107
  } catch (error) {
98
108
  this.table?.destroy();
109
+ this._removeViewFieldControls();
99
110
  if (!created) restoreTarget(root, this._tableViewOriginal);
100
111
  throw error;
101
112
  }
@@ -302,10 +313,14 @@ export function fieldsToTableColumns(fields) {
302
313
  * Builds the composed Table configuration. Shared options deliberately overwrite overlapping
303
314
  * low-level options; every advanced option remains untouched.
304
315
  * @param {TableOptions|undefined} table Advanced Table options.
305
- * @param {{fields:ViewField[],data:TableViewRecord[],recordId:string|((record:TableViewRecord)=>unknown),sort:ViewSort|null,sortMode:'local'|'server',selectable:false|'single'|'multi',hiddenFields:string[],fieldControls:boolean,emptyText:string|Node|(()=>string|Node)|null}} shared Shared state.
316
+ * @param {{fields:ViewField[],data:TableViewRecord[],recordId:string|((record:TableViewRecord)=>unknown),sort:ViewSort|null,sortMode:'local'|'server',selectable:false|'single'|'multi',hiddenFields:string[],fieldControls:boolean|import('../view/record-view.js').ViewFieldControlsConfig,emptyText:string|Node|(()=>string|Node)|null}} shared Shared state.
306
317
  * @returns {TableOptions} Table configuration.
307
318
  */
308
319
  export function tableOptionsForView(table, shared) {
320
+ // A customised chooser is rendered by TableView from the shared field state, so Table's own one
321
+ // stands down; `true`/`false` keep Table's chooser exactly as it has always been.
322
+ const controls = normalizeViewFieldControls(shared.fieldControls);
323
+ const tableChooser = controls.enabled && !controls.custom;
309
324
  return {
310
325
  ...(table && typeof table === 'object' ? table : {}),
311
326
  columns: fieldsToTableColumns(shared.fields),
@@ -315,8 +330,8 @@ export function tableOptionsForView(table, shared) {
315
330
  sortMode: shared.sortMode,
316
331
  selectable: shared.selectable,
317
332
  hiddenColumns: [...shared.hiddenFields],
318
- columnVisibility: Boolean(shared.fieldControls),
319
- columnReorder: Boolean(shared.fieldControls),
333
+ columnVisibility: tableChooser,
334
+ columnReorder: tableChooser,
320
335
  emptyText: shared.emptyText
321
336
  };
322
337
  }
@@ -10,7 +10,9 @@
10
10
  align-items: center;
11
11
  gap: var(--zx-space-1);
12
12
  min-block-size: var(--zx-control-height);
13
- padding: 3px 3px 3px var(--zx-space-2);
13
+ padding: 3px;
14
+ /* The tags sit further from the field's leading edge than from its other three. */
15
+ padding-inline-start: var(--zx-space-2);
14
16
  border: 1px solid var(--zx-color-border-control);
15
17
  border-radius: var(--zx-control-radius);
16
18
  background: var(--zx-color-bg-control);
@@ -30,6 +32,12 @@
30
32
  cursor: default;
31
33
  }
32
34
 
35
+ /* Read-only, the picker is its tags: nothing to type into and no list to open, so no input and no
36
+ chevron — and a full row of tags does not wrap them onto an empty second line. */
37
+ .zx-tag-picker[data-readonly] :is(.zx-tag-picker__input, .zx-tag-picker__toggle) {
38
+ display: none;
39
+ }
40
+
33
41
  .zx-tag-picker__tags {
34
42
  display: contents;
35
43
  }
@@ -63,6 +71,38 @@
63
71
  color: var(--zx-tag-color, var(--zx-color-accent));
64
72
  }
65
73
 
74
+ /* A categorical tone: the tag takes the Badge tone palette — the tone's tint as fill, its ink as
75
+ text, no border — and an icon tile the same pair, so a label keeps one colour everywhere. */
76
+ .zx-tag-picker__tag[data-tone] {
77
+ border-color: transparent;
78
+ background: var(--zx-tag-tone-bg);
79
+ color: var(--zx-tag-tone-fg);
80
+ }
81
+
82
+ .zx-tag-picker__visual[data-tone] {
83
+ background: var(--zx-tag-tone-bg);
84
+ color: var(--zx-tag-tone-fg);
85
+ }
86
+
87
+ .zx-tag-picker .zx-tag-picker__tag[data-tone] .zx-tag-picker__remove {
88
+ color: inherit;
89
+ }
90
+
91
+ .zx-tag-picker .zx-tag-picker__tag[data-tone] .zx-tag-picker__remove:hover {
92
+ background: color-mix(in srgb, currentColor 14%, transparent);
93
+ color: inherit;
94
+ }
95
+
96
+ :is(.zx-tag-picker__tag, .zx-tag-picker__visual)[data-tone="blue"] { --zx-tag-tone-bg: var(--zx-color-tone-blue-bg); --zx-tag-tone-fg: var(--zx-color-tone-blue-fg); }
97
+ :is(.zx-tag-picker__tag, .zx-tag-picker__visual)[data-tone="teal"] { --zx-tag-tone-bg: var(--zx-color-tone-teal-bg); --zx-tag-tone-fg: var(--zx-color-tone-teal-fg); }
98
+ :is(.zx-tag-picker__tag, .zx-tag-picker__visual)[data-tone="green"] { --zx-tag-tone-bg: var(--zx-color-tone-green-bg); --zx-tag-tone-fg: var(--zx-color-tone-green-fg); }
99
+ :is(.zx-tag-picker__tag, .zx-tag-picker__visual)[data-tone="amber"] { --zx-tag-tone-bg: var(--zx-color-tone-amber-bg); --zx-tag-tone-fg: var(--zx-color-tone-amber-fg); }
100
+ :is(.zx-tag-picker__tag, .zx-tag-picker__visual)[data-tone="orange"] { --zx-tag-tone-bg: var(--zx-color-tone-orange-bg); --zx-tag-tone-fg: var(--zx-color-tone-orange-fg); }
101
+ :is(.zx-tag-picker__tag, .zx-tag-picker__visual)[data-tone="red"] { --zx-tag-tone-bg: var(--zx-color-tone-red-bg); --zx-tag-tone-fg: var(--zx-color-tone-red-fg); }
102
+ :is(.zx-tag-picker__tag, .zx-tag-picker__visual)[data-tone="pink"] { --zx-tag-tone-bg: var(--zx-color-tone-pink-bg); --zx-tag-tone-fg: var(--zx-color-tone-pink-fg); }
103
+ :is(.zx-tag-picker__tag, .zx-tag-picker__visual)[data-tone="violet"] { --zx-tag-tone-bg: var(--zx-color-tone-violet-bg); --zx-tag-tone-fg: var(--zx-color-tone-violet-fg); }
104
+ :is(.zx-tag-picker__tag, .zx-tag-picker__visual)[data-tone="slate"] { --zx-tag-tone-bg: var(--zx-color-tone-slate-bg); --zx-tag-tone-fg: var(--zx-color-tone-slate-fg); }
105
+
66
106
  .zx-tag-picker__tag:not(:has(.zx-tag-picker__remove)) {
67
107
  padding-inline-end: var(--zx-space-2);
68
108
  }
@@ -123,7 +163,7 @@
123
163
  max-inline-size: calc(100vw - var(--zx-space-4));
124
164
  overflow: hidden;
125
165
  border: 1px solid var(--zx-color-overlay-border);
126
- border-radius: var(--zx-radius-md);
166
+ border-radius: var(--zx-radius-lg);
127
167
  background-color: var(--zx-color-overlay-surface);
128
168
  background-image: var(--zx-overlay-surface-image);
129
169
  background-clip: padding-box;
@@ -154,8 +194,11 @@
154
194
  display: flex;
155
195
  align-items: center;
156
196
  gap: var(--zx-space-2);
157
- padding: var(--zx-space-2) var(--zx-space-2);
158
- border-radius: var(--zx-radius-md);
197
+ /* The same row as a Select option and a menu item. */
198
+ min-block-size: calc(var(--zx-control-height) - 2px);
199
+ padding: var(--zx-space-1) var(--zx-space-2);
200
+ border-radius: var(--zx-radius-sm);
201
+ line-height: 1.35;
159
202
  cursor: pointer;
160
203
  }
161
204
 
@@ -163,9 +206,9 @@
163
206
  background: var(--zx-color-overlay-hover);
164
207
  }
165
208
 
209
+ /* A chosen tag keeps its check mark; the row itself stays in the text colour. */
166
210
  .zx-tag-picker__option[aria-selected="true"] {
167
- color: var(--zx-color-accent);
168
- font-weight: 550;
211
+ color: var(--zx-color-text);
169
212
  }
170
213
 
171
214
  .zx-tag-picker__option[aria-disabled="true"] {
@@ -200,3 +243,40 @@
200
243
  font-size: var(--zx-text-sm);
201
244
  text-align: center;
202
245
  }
246
+
247
+ /*
248
+ * Quiet fields. Inside `[data-zx-fields="quiet"]` the tags read as the value: no border, no fill,
249
+ * no chevron. Hover brings the hover fill and the chevron back; focus or an open list brings back
250
+ * the border, the control fill and the focus ring. Disabled and read-only pickers never light up.
251
+ */
252
+ [data-zx-fields="quiet"] .zx-tag-picker__control {
253
+ border-color: transparent;
254
+ background: transparent;
255
+ }
256
+
257
+ [data-zx-fields="quiet"] .zx-tag-picker__toggle {
258
+ opacity: 0;
259
+ }
260
+
261
+ [data-zx-fields="quiet"] .zx-tag-picker:not([data-disabled], [data-readonly]):hover .zx-tag-picker__control {
262
+ background: var(--zx-color-bg-hover);
263
+ }
264
+
265
+ [data-zx-fields="quiet"] .zx-tag-picker:not([data-disabled], [data-readonly]):is(:hover, [data-focus], [data-state="open"]) .zx-tag-picker__toggle {
266
+ opacity: 1;
267
+ }
268
+
269
+ [data-zx-fields="quiet"] .zx-tag-picker:not([data-disabled], [data-readonly]):is([data-focus], [data-state="open"]) .zx-tag-picker__control {
270
+ border-color: var(--zx-color-border-control);
271
+ background: var(--zx-color-bg-control);
272
+ }
273
+
274
+ @media (prefers-reduced-motion: no-preference) {
275
+ [data-zx-fields="quiet"] .zx-tag-picker__control {
276
+ transition: background-color var(--zx-dur-fast) var(--zx-ease), border-color var(--zx-dur-fast) var(--zx-ease);
277
+ }
278
+
279
+ [data-zx-fields="quiet"] .zx-tag-picker__toggle {
280
+ transition: opacity var(--zx-dur-fast) var(--zx-ease);
281
+ }
282
+ }
@@ -2,7 +2,9 @@ import { Component } from '../../core/component.js';
2
2
  import { h, restoreTarget, snapshotTarget } from '../../core/dom.js';
3
3
  import { icon } from '../../core/icons.js';
4
4
  import { position } from '../../core/position.js';
5
- import { isCssColor, uid } from '../../core/util.js';
5
+ import { uid } from '../../core/util.js';
6
+ import { normalizeBadgeTone } from '../badge/badge.js';
7
+ import { itemColor } from '../select/color.js';
6
8
  import { matchItems } from '../select/filter.js';
7
9
 
8
10
  /** @typedef {Record<string, any>|string|number} TagItem */
@@ -15,7 +17,12 @@ import { matchItems } from '../select/filter.js';
15
17
  * @property {TagValueReader} [valueKey='ID'] Item ID property or reader.
16
18
  * @property {TagLabelReader} [labelKey='name'] Item label property or reader.
17
19
  * @property {TagValueReader} [iconKey='icon'] Optional item icon property or reader.
18
- * @property {TagValueReader} [colorKey='color'] Optional item colour property or reader.
20
+ * @property {TagValueReader} [colorKey='color'] Optional item colour property or reader: a CSS colour
21
+ * or a token reference such as `var(--zx-color-tone-blue-fg)`.
22
+ * @property {TagValueReader} [toneKey='tone'] Optional item tone property or reader: a categorical
23
+ * tone name from the Badge palette (`blue`, `teal`, `green`, `amber`, `orange`, `red`, `pink`,
24
+ * `violet`, `slate`). A toned tag takes the palette's fill and ink, as a Badge with that `tone`
25
+ * does; a tone wins over a colour, and unknown names are ignored.
19
26
  * @property {Array<TagValueReader>|null} [searchKeys=null] Fields searched by local filtering.
20
27
  * @property {'local'|((query: string) => Promise<TagItem[]>|TagItem[])} [filter='local'] Filtering mode.
21
28
  * @property {number} [minQuery=0] Minimum async query length.
@@ -26,7 +33,8 @@ import { matchItems } from '../select/filter.js';
26
33
  * @property {string} [placeholder=''] Empty-state input text.
27
34
  * @property {number} [listHeight=260] Maximum list height in pixels.
28
35
  * @property {boolean} [disabled=false] Whether interaction is disabled.
29
- * @property {boolean} [readonly=false] Whether tags may be added or removed.
36
+ * @property {boolean} [readonly=false] When true the picker shows only its tags — no input, chevron, or
37
+ * remove buttons.
30
38
  * @property {((item: TagItem) => Node|string)|null} [renderItem=null] Option renderer.
31
39
  * @property {((item: TagItem) => Node|string)|null} [renderTag=null] Tag-label renderer.
32
40
  * @property {Record<string, string>|Record<string, Record<string, string>>} [msg] Localized messages.
@@ -69,6 +77,7 @@ export class TagPicker extends Component {
69
77
  labelKey: 'name',
70
78
  iconKey: 'icon',
71
79
  colorKey: 'color',
80
+ toneKey: 'tone',
72
81
  searchKeys: null,
73
82
  filter: 'local',
74
83
  minQuery: 0,
@@ -415,12 +424,13 @@ export class TagPicker extends Component {
415
424
  _renderTags() {
416
425
  const tags = this._selected.map((item) => {
417
426
  const label = this._labelOf(item);
418
- const color = this._colorOf(item);
427
+ const tone = this._toneOf(item);
428
+ const color = tone ? null : this._colorOf(item);
419
429
  const content = this.options.renderTag ? this.options.renderTag(item)
420
430
  : [this._iconOf(item), h('span', { class: 'zx-tag-picker__tag-label' }, label)];
421
431
  const tag = h('span', {
422
432
  class: 'zx-tag-picker__tag',
423
- dataset: { colored: String(Boolean(color)) },
433
+ dataset: { colored: String(Boolean(color)), tone: tone ?? undefined },
424
434
  style: color ? { '--zx-tag-color': color } : null
425
435
  }, content);
426
436
  if (!this._readonly && !this._disabled) {
@@ -790,17 +800,24 @@ export class TagPicker extends Component {
790
800
  ? /** @type {Node} */ (value).cloneNode(true)
791
801
  : typeof value === 'string' && value.trim() ? icon(value, { size: 12 }) : null;
792
802
  if (!visual) return null;
803
+ const tone = this._toneOf(item);
804
+ const color = tone ? null : this._colorOf(item);
793
805
  return h('span', {
794
806
  class: 'zx-tag-picker__visual',
795
- style: this._colorOf(item) ? { '--zx-tag-color': this._colorOf(item) } : null,
807
+ dataset: { tone: tone ?? undefined },
808
+ style: color ? { '--zx-tag-color': color } : null,
796
809
  ariaHidden: 'true'
797
810
  }, visual);
798
811
  }
799
812
 
800
813
  /** @param {unknown} item @returns {string|null} */
801
814
  _colorOf(item) {
802
- const value = this._readItem(item, this.options.colorKey);
803
- return isCssColor(value) ? value : null;
815
+ return itemColor(this._readItem(item, this.options.colorKey));
816
+ }
817
+
818
+ /** @param {unknown} item @returns {string|null} */
819
+ _toneOf(item) {
820
+ return normalizeBadgeTone(this._readItem(item, this.options.toneKey));
804
821
  }
805
822
 
806
823
  /** @param {unknown} item @param {unknown} reader @returns {unknown} */
@@ -28,14 +28,14 @@
28
28
 
29
29
  .zx-timebox__separator {
30
30
  color: var(--zx-color-text-muted);
31
- font-weight: 700;
31
+ font-weight: var(--zx-weight-medium);
32
32
  }
33
33
 
34
34
  .zx-timebox__sign {
35
35
  min-inline-size: var(--zx-control-height);
36
36
  padding: 0;
37
37
  border-color: var(--zx-color-border-control);
38
- font-weight: 750;
38
+ font-weight: var(--zx-weight-semibold);
39
39
  }
40
40
 
41
41
  .zx-timebox__sign[data-state="positive"] {
@@ -49,3 +49,23 @@
49
49
  background: var(--zx-color-danger-bg);
50
50
  color: var(--zx-color-danger);
51
51
  }
52
+
53
+ /*
54
+ * Quiet fields. The hour and minute segments are native inputs, which the base quiet-fields rule
55
+ * already quiets. The sign button follows them: no border and no fill until hover or focus, its
56
+ * glyph keeping the colour that says which way the duration runs.
57
+ */
58
+ [data-zx-fields="quiet"] .zx-timebox__sign,
59
+ [data-zx-fields="quiet"] .zx-timebox__sign[data-state] {
60
+ border-color: transparent;
61
+ background: transparent;
62
+ }
63
+
64
+ [data-zx-fields="quiet"] .zx-timebox:not([aria-disabled="true"]) .zx-timebox__sign:hover {
65
+ background: var(--zx-color-bg-hover);
66
+ }
67
+
68
+ [data-zx-fields="quiet"] .zx-timebox__sign:focus-visible {
69
+ border-color: var(--zx-color-border-control);
70
+ background: var(--zx-color-bg-control);
71
+ }
@@ -75,7 +75,9 @@ export class Timebox extends Component {
75
75
  const content = h('div', {
76
76
  class: 'zx-timebox__content',
77
77
  role: 'group',
78
- ariaLabel: this.options.seconds ? 'Duration in hours, minutes, and seconds' : 'Duration in hours and minutes'
78
+ ariaLabel: this.options.seconds
79
+ ? this._message('timebox.durationWithSeconds', 'Duration in hours, minutes, and seconds')
80
+ : this._message('timebox.duration', 'Duration in hours and minutes')
79
81
  });
80
82
  this._content = content;
81
83
 
@@ -84,7 +86,7 @@ export class Timebox extends Component {
84
86
  ref: 'sign',
85
87
  class: 'zx-timebox__sign',
86
88
  type: 'button',
87
- ariaLabel: 'Toggle duration sign',
89
+ ariaLabel: this._message('timebox.toggleSign', 'Toggle duration sign'),
88
90
  ariaPressed: 'false'
89
91
  }, '+'));
90
92
  this.listen(this.refs.sign, 'click', () => {
@@ -95,12 +97,12 @@ export class Timebox extends Component {
95
97
  });
96
98
  }
97
99
 
98
- this._addSegment(content, 'hours', 'Hours', false);
100
+ this._addSegment(content, 'hours', this._message('timebox.hours', 'Hours'), false);
99
101
  content.append(h('span', { class: 'zx-timebox__separator', ariaHidden: 'true' }, ':'));
100
- this._addSegment(content, 'minutes', 'Minutes', true);
102
+ this._addSegment(content, 'minutes', this._message('timebox.minutes', 'Minutes'), true);
101
103
  if (this.options.seconds) {
102
104
  content.append(h('span', { class: 'zx-timebox__separator', ariaHidden: 'true' }, ':'));
103
- this._addSegment(content, 'seconds', 'Seconds', true);
105
+ this._addSegment(content, 'seconds', this._message('timebox.seconds', 'Seconds'), true);
104
106
  }
105
107
  root.append(content);
106
108
  this.set(this.options.value, this.options.unit, { silent: true });
@@ -237,6 +239,17 @@ export class Timebox extends Component {
237
239
  _emitChange() {
238
240
  this.emit('change', { value: this.get(this.options.unit) });
239
241
  }
242
+
243
+ /**
244
+ * Resolves a message through the host translator, falling back to the built-in English text.
245
+ * @param {string} key Message key.
246
+ * @param {string} fallback English default.
247
+ * @returns {string}
248
+ */
249
+ _message(key, fallback) {
250
+ const message = this.msg(key);
251
+ return message === key ? fallback : message;
252
+ }
240
253
  }
241
254
 
242
255
  /** @event Timebox#change @type {CustomEvent<{value: number}>} */
@@ -1,7 +1,11 @@
1
+ /* A label reads like text beside the switch: start-aligned, wrapping within the available width,
2
+ with the track level with its first line. A one-line toggle is still a control's height tall,
3
+ its track and its text centred in it. */
1
4
  .zx-toggle {
2
5
  display: inline-flex;
3
- align-items: center;
6
+ align-items: flex-start;
4
7
  gap: var(--zx-space-2);
8
+ max-inline-size: 100%;
5
9
  min-block-size: var(--zx-control-height);
6
10
  border: 0;
7
11
  background: none;
@@ -9,6 +13,7 @@
9
13
  padding: 0;
10
14
  font: inherit;
11
15
  font-size: var(--zx-control-font-size);
16
+ text-align: start;
12
17
  cursor: pointer;
13
18
  }
14
19
 
@@ -31,12 +36,18 @@
31
36
  flex: none;
32
37
  inline-size: 34px;
33
38
  block-size: 20px;
39
+ margin-block-start: max(0px, (var(--zx-control-height) - 20px) / 2);
34
40
  border: 1px solid var(--zx-color-border-strong);
35
41
  border-radius: var(--zx-radius-full);
36
42
  background: var(--zx-color-bg-control);
37
43
  padding: 2px;
38
44
  }
39
45
 
46
+ .zx-toggle__label {
47
+ min-inline-size: 0;
48
+ padding-block: max(0px, (var(--zx-control-height) - 1lh) / 2);
49
+ }
50
+
40
51
  .zx-toggle__thumb {
41
52
  display: block;
42
53
  inline-size: 14px;
@@ -80,8 +80,8 @@ export class Toolbar extends Component {
80
80
  /** Inline size of the last completed measure pass; `-1` forces the next one. */
81
81
  #lastWidth = -1;
82
82
  /**
83
- * Block size of the last completed measure pass. Switching density resizes the controls without
84
- * changing the row width, so the height belongs in the cache key.
83
+ * Block size of the last completed measure pass. A theme change (control height, spacing unit)
84
+ * resizes the controls without changing the row width, so the height belongs in the cache key.
85
85
  */
86
86
  #lastHeight = -1;
87
87
  /** Handle of the frame a pending observer-driven measure pass is scheduled in; `0` if none. */
@@ -5,6 +5,10 @@
5
5
  */
6
6
 
7
7
  .zx-tooltip {
8
+ /* As wide as its text up to the maximum, never narrower: squeezed into too little room beside its
9
+ anchor, the bubble overflows instead of wrapping word by word, which is what makes the
10
+ position fall back to the other side, or above or below. */
11
+ inline-size: max-content;
8
12
  max-inline-size: var(--zx-tooltip-max-inline-size, 260px);
9
13
  border: 1px solid var(--zx-color-overlay-border);
10
14
  border-radius: var(--zx-radius-md);
@@ -1,6 +1,6 @@
1
1
  import { Component } from '../../core/component.js';
2
2
  import { h, resolveElement } from '../../core/dom.js';
3
- import { overlayHost } from '../../core/overlay-host.js';
3
+ import { connectedOverlayHost, overlayHost } from '../../core/overlay-host.js';
4
4
  import { position } from '../../core/position.js';
5
5
  import { uid } from '../../core/util.js';
6
6
 
@@ -96,7 +96,8 @@ export class Tooltip extends Component {
96
96
  super(null, options);
97
97
 
98
98
  this.#anchor = resolved;
99
- overlayHost(resolved).append(this.el);
99
+ // A detached anchor has no theme scope yet: the bubble waits and finds its host in show().
100
+ connectedOverlayHost(resolved)?.append(this.el);
100
101
  this.#content = this.options.content;
101
102
  this.#disabled = Boolean(this.options.disabled);
102
103
  this.#describedBy = resolved.getAttribute('aria-describedby');
@@ -174,11 +175,17 @@ export class Tooltip extends Component {
174
175
  if (node === null) return this;
175
176
 
176
177
  this.el.replaceChildren(node);
178
+ const placement = PLACEMENTS.has(this.options.placement) ? this.options.placement : 'top';
177
179
  this.#position = position(this.#anchor, /** @type {HTMLElement} */ (this.el), {
178
- placement: PLACEMENTS.has(this.options.placement) ? this.options.placement : 'top',
180
+ placement,
179
181
  offset: Number(this.options.offset),
180
182
  flip: true
181
183
  });
184
+ // A bubble beside its anchor that fits on neither side goes above or below it instead. Only
185
+ // the anchor-positioned path sets fallbacks; the script fallback clamps into the viewport.
186
+ if (/^(left|right)/.test(placement) && this.el.style.getPropertyValue('position-try-fallbacks')) {
187
+ this.el.style.setProperty('position-try-fallbacks', 'flip-inline, top span-all, bottom span-all');
188
+ }
182
189
  this.el.dataset.state = 'open';
183
190
  this.#anchor.setAttribute('aria-describedby', appendToken(this.#describedBy, this.el.id));
184
191
  this.emit('open');
@@ -1,5 +1,7 @@
1
1
  .zx-tree {
2
- --zx-tree-indent: 18px;
2
+ /* One step per level: enough to read the nesting, not so much that a deep branch walks off the
3
+ edge of a sidebar. */
4
+ --zx-tree-indent: var(--zx-space-3);
3
5
  display: block;
4
6
  min-inline-size: 0;
5
7
  color: var(--zx-color-text);
@@ -10,10 +12,12 @@
10
12
  display: flex;
11
13
  align-items: center;
12
14
  gap: var(--zx-space-2);
13
- min-block-size: calc(var(--zx-control-height) - 6px);
14
- padding-inline: var(--zx-space-2);
15
+ /* The same row as a Finder column: a space-1 margin round one line of text. */
16
+ min-block-size: calc(var(--zx-control-height) - var(--zx-space-1));
17
+ padding: var(--zx-space-1) var(--zx-space-2);
15
18
  padding-inline-start: calc(var(--zx-space-2) + var(--zx-tree-level, 0) * var(--zx-tree-indent));
16
- border-radius: var(--zx-radius-md);
19
+ border-radius: var(--zx-radius-sm);
20
+ line-height: 1.25;
17
21
  cursor: pointer;
18
22
  }
19
23
 
@@ -21,10 +25,18 @@
21
25
  background: var(--zx-color-bg-hover);
22
26
  }
23
27
 
28
+ /* The selected node of a single-selection tree is where you are, not something you picked: the
29
+ neutral current fill, the full text colour and a heavier label. A multi-selection is a set of
30
+ picks and keeps the accent tint, so the two never read as the same thing. */
24
31
  .zx-tree__row[aria-selected="true"] {
32
+ background: var(--zx-color-bg-current);
33
+ color: var(--zx-color-text);
34
+ font-weight: var(--zx-weight-semibold);
35
+ }
36
+
37
+ .zx-tree[aria-multiselectable="true"] .zx-tree__row[aria-selected="true"] {
25
38
  background: var(--zx-color-bg-selected);
26
- color: var(--zx-color-accent);
27
- font-weight: 550;
39
+ font-weight: inherit;
28
40
  }
29
41
 
30
42
  .zx-tree__row:focus-visible {
@@ -38,7 +50,7 @@
38
50
  inline-size: 14px;
39
51
  block-size: 14px;
40
52
  flex: none;
41
- color: var(--zx-color-text-muted);
53
+ color: var(--zx-color-text-subtle);
42
54
  }
43
55
 
44
56
  .zx-tree__row[aria-expanded="true"] .zx-tree__twisty {
@@ -60,19 +72,17 @@
60
72
  margin: 0;
61
73
  }
62
74
 
75
+ /* Icons are quieter than labels, folders included: a branch is told apart by its glyph and its
76
+ twisty, not by borrowing a status colour. The current row's icon takes the text colour. */
63
77
  .zx-tree__icon {
64
78
  display: inline-grid;
65
79
  place-items: center;
66
80
  flex: none;
67
- color: var(--zx-color-text-muted);
68
- }
69
-
70
- .zx-tree__row[data-branch] .zx-tree__icon {
71
- color: var(--zx-color-warning);
81
+ color: var(--zx-color-text-subtle);
72
82
  }
73
83
 
74
84
  .zx-tree__row[aria-selected="true"] .zx-tree__icon {
75
- color: inherit;
85
+ color: var(--zx-color-text);
76
86
  }
77
87
 
78
88
  .zx-tree__label {
@@ -83,11 +93,10 @@
83
93
 
84
94
  .zx-tree__badge {
85
95
  margin-inline-start: auto;
86
- padding-inline: var(--zx-space-2);
87
- border-radius: var(--zx-radius-full);
88
- background: var(--zx-color-bg-muted);
96
+ padding-inline: var(--zx-space-1);
89
97
  color: var(--zx-color-text-muted);
90
98
  font-size: var(--zx-text-xs);
99
+ font-weight: var(--zx-weight-medium);
91
100
  font-variant-numeric: tabular-nums;
92
101
  }
93
102
 
@@ -611,6 +611,11 @@ export class TreeView extends Component {
611
611
  this._expandSiblings(row);
612
612
  return;
613
613
  default:
614
+ // A printable key typed on a row belongs to the type-ahead: claimed, so the browser's
615
+ // find-as-you-type and the page's single-key shortcuts do not act on it as well. Typing
616
+ // into a control that `renderLabel` placed in a row stays that control's.
617
+ if (!isTreeTypeaheadKey(event) || isTextEntry(event.target)) return;
618
+ event.preventDefault();
614
619
  this._handleTypeahead(event);
615
620
  }
616
621
  }
@@ -782,3 +787,29 @@ function cloneNodes(nodes) {
782
787
  function toIterable(value) {
783
788
  return Array.isArray(value) ? value : [];
784
789
  }
790
+
791
+ /**
792
+ * Whether a key press is one the type-ahead consumes: a single printable character, not a space
793
+ * (Space selects), with no Ctrl, Meta, or Alt (those are shortcuts). Shift is allowed, since it
794
+ * only changes the letter's case.
795
+ * @param {{key?: string, ctrlKey?: boolean, metaKey?: boolean, altKey?: boolean}} event Key event.
796
+ * @returns {boolean}
797
+ */
798
+ export function isTreeTypeaheadKey(event) {
799
+ if (!event || event.ctrlKey || event.metaKey || event.altKey) return false;
800
+ const key = typeof event.key === 'string' ? event.key : '';
801
+ // The same test the shared type-ahead applies, so a key is claimed exactly when it is used.
802
+ return key.length === 1 && !/\s/.test(key);
803
+ }
804
+
805
+ /** @param {EventTarget|null} target @returns {boolean} Whether typing into the target edits text. */
806
+ function isTextEntry(target) {
807
+ const element = /** @type {HTMLElement|null} */ (target && /** @type {Node} */ (target).nodeType === 1
808
+ ? target : null);
809
+ if (!element) return false;
810
+ if (element.isContentEditable) return true;
811
+ if (element.localName === 'textarea' || element.localName === 'select') return true;
812
+ if (element.localName !== 'input') return false;
813
+ return !['checkbox', 'radio', 'button', 'submit', 'reset', 'image', 'range', 'color', 'file']
814
+ .includes(/** @type {HTMLInputElement} */ (element).type);
815
+ }