@fusion.dev/helix-headless 0.147.0 → 0.149.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 (296) hide show
  1. package/custom-elements.json +11222 -2332
  2. package/dist/accordion/item/accordion-item.d.ts +5 -2
  3. package/dist/accordion/item/accordion-item.js +11 -7
  4. package/dist/accordion/root/accordion-root.js +4 -6
  5. package/dist/announcement/root/announcement-root.d.ts +61 -5
  6. package/dist/announcement/root/announcement-root.js +126 -20
  7. package/dist/area-chart/root/area-chart-root.d.ts +11 -0
  8. package/dist/area-chart/root/area-chart-root.js +21 -9
  9. package/dist/avatar/group/avatar-group.d.ts +10 -2
  10. package/dist/avatar/group/avatar-group.js +10 -5
  11. package/dist/badge/count.d.ts +19 -0
  12. package/dist/badge/count.js +20 -0
  13. package/dist/badge/root/badge-root.d.ts +6 -2
  14. package/dist/badge/root/badge-root.js +8 -22
  15. package/dist/bar-chart/root/bar-chart-root.d.ts +13 -1
  16. package/dist/bar-chart/root/bar-chart-root.js +17 -4
  17. package/dist/blockquote/root/blockquote-root.d.ts +66 -8
  18. package/dist/blockquote/root/blockquote-root.js +85 -8
  19. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +2 -2
  20. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +8 -1
  21. package/dist/breadcrumbs/root/breadcrumbs-root.js +5 -2
  22. package/dist/button/root/button-root.d.ts +10 -1
  23. package/dist/button/root/button-root.js +3 -2
  24. package/dist/button-group/arrows.d.ts +14 -0
  25. package/dist/button-group/arrows.js +10 -0
  26. package/dist/button-group/root/button-group-root.d.ts +144 -3
  27. package/dist/button-group/root/button-group-root.js +142 -2
  28. package/dist/callout/root/callout-root.d.ts +16 -2
  29. package/dist/callout/root/callout-root.js +16 -4
  30. package/dist/canvas/describe.d.ts +10 -0
  31. package/dist/canvas/describe.js +9 -0
  32. package/dist/canvas/edge-pan.d.ts +61 -0
  33. package/dist/canvas/edge-pan.js +63 -0
  34. package/dist/canvas/neighbor.d.ts +17 -0
  35. package/dist/canvas/neighbor.js +1 -0
  36. package/dist/canvas/node/canvas-node.d.ts +12 -2
  37. package/dist/canvas/node/canvas-node.js +8 -3
  38. package/dist/canvas/root/canvas-document.d.ts +10 -0
  39. package/dist/canvas/root/canvas-document.js +1 -0
  40. package/dist/canvas/root/canvas-root.d.ts +168 -9
  41. package/dist/canvas/root/canvas-root.js +239 -69
  42. package/dist/canvas/snap.d.ts +42 -0
  43. package/dist/canvas/snap.js +32 -0
  44. package/dist/carousel/root/carousel-root.d.ts +9 -3
  45. package/dist/carousel/root/carousel-root.js +17 -10
  46. package/dist/chat/composer/chat-composer.d.ts +5 -0
  47. package/dist/chat/composer/chat-composer.js +4 -1
  48. package/dist/chat/message/chat-message.d.ts +9 -5
  49. package/dist/chat/message/chat-message.js +8 -4
  50. package/dist/chat/naming.js +2 -2
  51. package/dist/chat/root/chat-root.d.ts +40 -4
  52. package/dist/chat/root/chat-root.js +38 -14
  53. package/dist/checkbox/root/checkbox-root.d.ts +11 -0
  54. package/dist/checkbox/root/checkbox-root.js +8 -2
  55. package/dist/checkbox-group/root/checkbox-group-root.d.ts +24 -0
  56. package/dist/checkbox-group/root/checkbox-group-root.js +28 -12
  57. package/dist/checkbox-group/select-all.d.ts +11 -0
  58. package/dist/checkbox-group/select-all.js +5 -0
  59. package/dist/code-block/root/code-block-root.d.ts +20 -8
  60. package/dist/code-block/root/code-block-root.js +16 -5
  61. package/dist/color-input/root/color-input-root.d.ts +13 -0
  62. package/dist/color-input/root/color-input-root.js +18 -5
  63. package/dist/color-picker/root/color-picker-root.d.ts +19 -3
  64. package/dist/color-picker/root/color-picker-root.js +52 -24
  65. package/dist/color-picker/wheel.d.ts +11 -0
  66. package/dist/color-picker/wheel.js +9 -0
  67. package/dist/combobox/root/combobox-root.d.ts +106 -18
  68. package/dist/combobox/root/combobox-root.js +101 -26
  69. package/dist/command/root/command-root.d.ts +19 -9
  70. package/dist/command/root/command-root.js +9 -4
  71. package/dist/copy-button/root/copy-button-root.d.ts +5 -0
  72. package/dist/copy-button/root/copy-button-root.js +7 -2
  73. package/dist/data-table/root/data-table-root.d.ts +171 -29
  74. package/dist/data-table/root/data-table-root.js +284 -161
  75. package/dist/date-input/root/date-input-root.d.ts +32 -9
  76. package/dist/date-input/root/date-input-root.js +66 -44
  77. package/dist/date-picker/root/date-picker-root.d.ts +38 -15
  78. package/dist/date-picker/root/date-picker-root.js +52 -33
  79. package/dist/description-list/item/description-list-item.d.ts +4 -0
  80. package/dist/description-list/item/description-list-item.js +7 -1
  81. package/dist/dialog/root/dialog-root.d.ts +12 -1
  82. package/dist/dialog/root/dialog-root.js +11 -4
  83. package/dist/divider/root/divider-root.d.ts +45 -16
  84. package/dist/divider/root/divider-root.js +28 -4
  85. package/dist/donut-chart/root/donut-chart-root.d.ts +5 -0
  86. package/dist/donut-chart/root/donut-chart-root.js +8 -5
  87. package/dist/drawer/root/drawer-root.d.ts +12 -1
  88. package/dist/drawer/root/drawer-root.js +9 -2
  89. package/dist/drilldown/item/drilldown-item.js +3 -4
  90. package/dist/drilldown/root/drilldown-root.d.ts +5 -0
  91. package/dist/drilldown/root/drilldown-root.js +4 -1
  92. package/dist/element-events.generated.js +26 -3
  93. package/dist/empty/root/empty-root.d.ts +17 -3
  94. package/dist/empty/root/empty-root.js +29 -5
  95. package/dist/file-input/root/file-input-root.d.ts +18 -3
  96. package/dist/file-input/root/file-input-root.js +34 -18
  97. package/dist/file-input/status.js +2 -1
  98. package/dist/filter-input/chip/filter-chip.d.ts +13 -0
  99. package/dist/filter-input/chip/filter-chip.js +7 -3
  100. package/dist/filter-input/menu/filter-menu.d.ts +5 -0
  101. package/dist/filter-input/menu/filter-menu.js +3 -1
  102. package/dist/filter-input/root/filter-input-root.d.ts +20 -3
  103. package/dist/filter-input/root/filter-input-root.js +8 -4
  104. package/dist/format-date/fields.d.ts +20 -0
  105. package/dist/format-date/fields.js +14 -0
  106. package/dist/format-date/options.d.ts +2 -1
  107. package/dist/format-date/options.js +9 -38
  108. package/dist/format-date/root/format-date-root.d.ts +55 -5
  109. package/dist/format-date/root/format-date-root.js +29 -34
  110. package/dist/graph/root/graph-model.d.ts +11 -0
  111. package/dist/graph/root/graph-model.js +4 -0
  112. package/dist/graph/root/graph-root.d.ts +101 -14
  113. package/dist/graph/root/graph-root.js +140 -46
  114. package/dist/heatmap/root/heatmap-root.d.ts +37 -4
  115. package/dist/heatmap/root/heatmap-root.js +51 -16
  116. package/dist/icon-button/root/icon-button-root.d.ts +5 -0
  117. package/dist/icon-button/root/icon-button-root.js +1 -0
  118. package/dist/image/root/image-root.d.ts +11 -0
  119. package/dist/input/root/input-root.d.ts +5 -0
  120. package/dist/input/root/input-root.js +4 -1
  121. package/dist/kbd/root/kbd-root.js +1 -2
  122. package/dist/legend/root/legend-root.js +4 -17
  123. package/dist/lightbox/root/lightbox-root.d.ts +14 -4
  124. package/dist/lightbox/root/lightbox-root.js +25 -7
  125. package/dist/line-chart/root/line-chart-root.d.ts +5 -0
  126. package/dist/line-chart/root/line-chart-root.js +4 -2
  127. package/dist/list/item/list-item.d.ts +32 -0
  128. package/dist/list/item/list-item.js +34 -7
  129. package/dist/list/root/list-root.d.ts +87 -0
  130. package/dist/list/root/list-root.js +119 -23
  131. package/dist/listbox/reorder/index.d.ts +1 -0
  132. package/dist/listbox/reorder/index.js +4 -0
  133. package/dist/listbox/reorder/listbox-reorder.d.ts +83 -0
  134. package/dist/listbox/reorder/listbox-reorder.js +151 -0
  135. package/dist/listbox/root/listbox-root.d.ts +185 -33
  136. package/dist/listbox/root/listbox-root.js +166 -136
  137. package/dist/loader/root/loader-root.d.ts +5 -0
  138. package/dist/loader/root/loader-root.js +3 -1
  139. package/dist/map/area/map-area.d.ts +5 -0
  140. package/dist/map/area/map-area.js +3 -1
  141. package/dist/map/buildings/map-buildings.d.ts +5 -0
  142. package/dist/map/buildings/map-buildings.js +3 -1
  143. package/dist/map/contours/map-contours.d.ts +5 -0
  144. package/dist/map/contours/map-contours.js +3 -1
  145. package/dist/map/landcover/map-landcover.d.ts +5 -0
  146. package/dist/map/landcover/map-landcover.js +3 -1
  147. package/dist/map/marker/map-marker.d.ts +18 -2
  148. package/dist/map/marker/map-marker.js +13 -5
  149. package/dist/map/points/map-points.d.ts +5 -0
  150. package/dist/map/points/map-points.js +6 -4
  151. package/dist/map/position/map-position.d.ts +5 -0
  152. package/dist/map/position/map-position.js +3 -1
  153. package/dist/map/raster/map-raster.d.ts +5 -0
  154. package/dist/map/raster/map-raster.js +3 -1
  155. package/dist/map/root/map-root.d.ts +73 -16
  156. package/dist/map/root/map-root.js +122 -44
  157. package/dist/map/route/map-route.d.ts +5 -0
  158. package/dist/map/route/map-route.js +3 -1
  159. package/dist/map/terrain/map-terrain.d.ts +5 -0
  160. package/dist/map/terrain/map-terrain.js +3 -1
  161. package/dist/megamenu/root/megamenu-root.js +4 -11
  162. package/dist/menu/root/menu-root.d.ts +7 -2
  163. package/dist/menu/root/menu-root.js +3 -1
  164. package/dist/meter/root/meter-root.d.ts +16 -3
  165. package/dist/meter/root/meter-root.js +12 -5
  166. package/dist/navigation/root/navigation-root.d.ts +5 -0
  167. package/dist/navigation/root/navigation-root.js +7 -8
  168. package/dist/notifications/group/notification-group.d.ts +8 -1
  169. package/dist/notifications/group/notification-group.js +12 -6
  170. package/dist/notifications/notification/notification.d.ts +17 -1
  171. package/dist/notifications/notification/notification.js +6 -2
  172. package/dist/notifications/root/notifications-root.d.ts +49 -5
  173. package/dist/notifications/root/notifications-root.js +36 -8
  174. package/dist/number-input/root/number-input-root.d.ts +21 -8
  175. package/dist/number-input/root/number-input-root.js +26 -11
  176. package/dist/object-inspector/root/object-inspector-root.d.ts +56 -13
  177. package/dist/object-inspector/root/object-inspector-root.js +41 -30
  178. package/dist/outline/root/outline-root.d.ts +5 -0
  179. package/dist/outline/root/outline-root.js +3 -1
  180. package/dist/pagination/root/pagination-root.d.ts +7 -1
  181. package/dist/pagination/root/pagination-root.js +16 -7
  182. package/dist/pagination/size/pagination-size.d.ts +8 -3
  183. package/dist/pagination/size/pagination-size.js +5 -3
  184. package/dist/pagination/summary/pagination-summary.d.ts +8 -6
  185. package/dist/pagination/summary/pagination-summary.js +4 -9
  186. package/dist/pin-input/code.d.ts +23 -0
  187. package/dist/pin-input/code.js +21 -0
  188. package/dist/pin-input/root/pin-input-root.d.ts +9 -3
  189. package/dist/pin-input/root/pin-input-root.js +13 -23
  190. package/dist/popover/root/popover-root.d.ts +12 -2
  191. package/dist/popover/root/popover-root.js +10 -4
  192. package/dist/progress/root/progress-root.d.ts +7 -2
  193. package/dist/progress/root/progress-root.js +5 -2
  194. package/dist/qr-code/name.d.ts +3 -2
  195. package/dist/qr-code/name.js +2 -2
  196. package/dist/qr-code/root/qr-code-root.d.ts +26 -5
  197. package/dist/qr-code/root/qr-code-root.js +27 -6
  198. package/dist/radio/group/radio-group.d.ts +11 -0
  199. package/dist/radio/group/radio-group.js +8 -2
  200. package/dist/rating/root/rating-root.d.ts +12 -1
  201. package/dist/rating/root/rating-root.js +10 -3
  202. package/dist/scatter-chart/measures.d.ts +22 -0
  203. package/dist/scatter-chart/measures.js +11 -0
  204. package/dist/scatter-chart/root/scatter-chart-root.d.ts +22 -8
  205. package/dist/scatter-chart/root/scatter-chart-root.js +25 -23
  206. package/dist/select/root/select-root.d.ts +67 -8
  207. package/dist/select/root/select-root.js +45 -11
  208. package/dist/skip-link/root/skip-link-root.d.ts +5 -0
  209. package/dist/skip-link/root/skip-link-root.js +3 -1
  210. package/dist/slider/root/slider-root.d.ts +7 -2
  211. package/dist/slider/root/slider-root.js +11 -5
  212. package/dist/sparkline/readings.d.ts +15 -5
  213. package/dist/sparkline/readings.js +3 -3
  214. package/dist/sparkline/root/sparkline-root.d.ts +9 -4
  215. package/dist/sparkline/root/sparkline-root.js +9 -3
  216. package/dist/speed-dial/group/speed-dial-group.d.ts +48 -0
  217. package/dist/speed-dial/group/speed-dial-group.js +43 -0
  218. package/dist/speed-dial/index.d.ts +1 -0
  219. package/dist/speed-dial/index.js +1 -0
  220. package/dist/speed-dial/item/speed-dial-item.d.ts +26 -1
  221. package/dist/speed-dial/item/speed-dial-item.js +41 -3
  222. package/dist/speed-dial/long-press.d.ts +50 -0
  223. package/dist/speed-dial/long-press.js +48 -0
  224. package/dist/speed-dial/root/speed-dial-root.d.ts +85 -1
  225. package/dist/speed-dial/root/speed-dial-root.js +117 -5
  226. package/dist/splitter/root/splitter-root.d.ts +12 -3
  227. package/dist/splitter/root/splitter-root.js +22 -4
  228. package/dist/steps/root/steps-root.d.ts +9 -2
  229. package/dist/steps/root/steps-root.js +6 -3
  230. package/dist/steps/step/step.d.ts +12 -0
  231. package/dist/steps/step/step.js +11 -2
  232. package/dist/switch/root/switch-root.d.ts +11 -0
  233. package/dist/switch/root/switch-root.js +8 -2
  234. package/dist/table/cell/table-cell.d.ts +8 -1
  235. package/dist/table/cell/table-cell.js +6 -2
  236. package/dist/table/column/table-column.d.ts +11 -4
  237. package/dist/table/column/table-column.js +15 -10
  238. package/dist/table/grid-controller.d.ts +122 -0
  239. package/dist/table/grid-controller.js +299 -0
  240. package/dist/table/root/table-root.d.ts +154 -8
  241. package/dist/table/root/table-root.js +197 -38
  242. package/dist/table/row/table-row.d.ts +55 -1
  243. package/dist/table/row/table-row.js +60 -7
  244. package/dist/tabs/root/tabs-root.d.ts +5 -0
  245. package/dist/tabs/root/tabs-root.js +3 -1
  246. package/dist/tag/group/tag-group.d.ts +5 -0
  247. package/dist/tag/group/tag-group.js +8 -15
  248. package/dist/tag/root/tag-root.d.ts +14 -1
  249. package/dist/tag/root/tag-root.js +11 -8
  250. package/dist/textarea/root/textarea-root.d.ts +5 -0
  251. package/dist/textarea/root/textarea-root.js +3 -1
  252. package/dist/time-input/root/time-input-root.d.ts +14 -7
  253. package/dist/time-input/root/time-input-root.js +43 -32
  254. package/dist/time-picker/root/time-picker-root.d.ts +16 -7
  255. package/dist/time-picker/root/time-picker-root.js +28 -12
  256. package/dist/toast/item/toast-item.d.ts +13 -3
  257. package/dist/toast/item/toast-item.js +2 -0
  258. package/dist/toast/root/toast-root.d.ts +22 -2
  259. package/dist/toast/root/toast-root.js +24 -17
  260. package/dist/toggle/group/toggle-group.js +3 -5
  261. package/dist/toolbar/root/toolbar-root.d.ts +36 -0
  262. package/dist/toolbar/root/toolbar-root.js +43 -9
  263. package/dist/tooltip/root/tooltip-root.d.ts +27 -17
  264. package/dist/tooltip/root/tooltip-root.js +1 -2
  265. package/dist/tour/root/tour-root.d.ts +8 -2
  266. package/dist/tour/root/tour-root.js +4 -2
  267. package/dist/tour/stop/tour-stop.d.ts +5 -0
  268. package/dist/tour/stop/tour-stop.js +6 -1
  269. package/dist/tree/grip.d.ts +5 -0
  270. package/dist/tree/grip.js +1 -0
  271. package/dist/tree/item/tree-item.d.ts +24 -0
  272. package/dist/tree/item/tree-item.js +27 -5
  273. package/dist/tree/root/tree-root.d.ts +66 -1
  274. package/dist/tree/root/tree-root.js +77 -5
  275. package/dist/trend/root/trend-root.d.ts +20 -4
  276. package/dist/trend/root/trend-root.js +20 -5
  277. package/dist/video/root/video-root.d.ts +23 -3
  278. package/dist/video/root/video-root.js +56 -15
  279. package/dist/video-playlist/menu/video-playlist-menu.d.ts +5 -0
  280. package/dist/video-playlist/menu/video-playlist-menu.js +3 -1
  281. package/dist/video-playlist/root/video-playlist-root.d.ts +5 -0
  282. package/dist/video-playlist/root/video-playlist-root.js +3 -1
  283. package/dist/virtual-list/root/virtual-list-root.d.ts +19 -3
  284. package/dist/virtual-list/root/virtual-list-root.js +10 -19
  285. package/lint.json +1 -1
  286. package/package.json +7 -6
  287. package/types/jsx.d.ts +6005 -2114
  288. package/types/svelte.d.ts +1 -0
  289. package/types/vue.d.ts +1594 -676
  290. package/types/words.d.ts +3280 -0
  291. package/dist/kbd/keys.d.ts +0 -29
  292. package/dist/kbd/keys.js +0 -52
  293. package/dist/toast/hotkey.d.ts +0 -22
  294. package/dist/toast/hotkey.js +0 -23
  295. package/dist/tooltip/focus-target.d.ts +0 -6
  296. package/dist/tooltip/focus-target.js +0 -9
@@ -0,0 +1,151 @@
1
+ import { ReorderController } from '@fusion.dev/helix-core';
2
+ const WORDS = {
3
+ canceledLabel: ['canceled-label', 'Canceled'],
4
+ droppedLabel: ['dropped-label', 'Dropped'],
5
+ movedLabel: ['moved-label', '{name} moved to position {position} of {count}'],
6
+ pickedUpLabel: ['picked-up-label', 'Picked up {name}'],
7
+ };
8
+ const MOVES = ['dragging', 'drop-after', 'drop-before'];
9
+ const keyOfItem = (item) => String(typeof item === 'object' ? item.value : item);
10
+ export class ListboxReorderController {
11
+ constructor(host, sheet, listSheet) {
12
+ this.marked = new Set();
13
+ this.host = host;
14
+ this.sheet = sheet;
15
+ this.listSheet = listSheet;
16
+ host.addController(this);
17
+ this.reorder = new ReorderController(host, {
18
+ axis: 'block',
19
+ box: (key) => this.optionFor(key)?.getBoundingClientRect(),
20
+ current: () => {
21
+ const active = this.drawn.find((option) => option.active);
22
+ return active && !active.disabled ? active.value : undefined;
23
+ },
24
+ enabled: () => host.reorderable && host.filter === '' && host.layout !== 'grid' && !host.isDisabled,
25
+ grip: (event) => {
26
+ const option = this.optionOf(event);
27
+ return option && !option.disabled ? { element: option, key: option.value } : undefined;
28
+ },
29
+ holds: false,
30
+ keys: (key) => this.keysAround(key),
31
+ name: (key) => this.optionFor(key)?.text || key,
32
+ preview: (key) => this.optionFor(key),
33
+ scroller: () => host.renderRoot?.querySelector('[part="items"]') ?? null,
34
+ });
35
+ }
36
+ hostUpdated() {
37
+ queueMicrotask(() => this.mark());
38
+ }
39
+ drawPreview(on) {
40
+ const root = this.host.renderRoot;
41
+ const drawn = root?.querySelector('[part="drag-preview"]');
42
+ if (on && root && !drawn) {
43
+ const preview = document.createElement('div');
44
+ for (const [name, value] of [
45
+ ['part', 'drag-preview'],
46
+ ['popover', 'manual'],
47
+ ['aria-hidden', 'true'],
48
+ ['inert', ''],
49
+ ]) {
50
+ preview.setAttribute(name, value);
51
+ }
52
+ root.append(preview);
53
+ }
54
+ else if (!on) {
55
+ drawn?.remove();
56
+ }
57
+ }
58
+ get drawn() {
59
+ const host = this.host;
60
+ return (host.items
61
+ ? [...(host.renderRoot?.querySelector('[data-rows]')?.children ?? [])]
62
+ : host.options);
63
+ }
64
+ optionOf(event) {
65
+ const drawn = this.drawn;
66
+ return event.composedPath().find((node) => drawn.includes(node));
67
+ }
68
+ optionFor(key) {
69
+ return this.drawn.find((option) => option.value === key);
70
+ }
71
+ keysAround(key) {
72
+ const { items, options } = this.host;
73
+ if (items) {
74
+ return items.map(keyOfItem);
75
+ }
76
+ const parent = options.find((option) => option.value === key)?.parentElement;
77
+ return options.filter((option) => option.parentElement === parent).map((one) => one.value);
78
+ }
79
+ mark() {
80
+ const on = this.host.reorderable;
81
+ adopt(this.host, this.listSheet, on);
82
+ this.drawPreview(on);
83
+ const drawn = this.drawn;
84
+ const marked = new Set();
85
+ for (const option of drawn) {
86
+ const move = on ? this.reorder.moveOf(option.value) : undefined;
87
+ if (move || this.marked.has(option)) {
88
+ for (const state of MOVES) {
89
+ option.toggleState(state, state === move);
90
+ }
91
+ }
92
+ if (move) {
93
+ marked.add(option);
94
+ }
95
+ adopt(option, this.sheet, on);
96
+ }
97
+ for (const option of this.marked) {
98
+ if (!drawn.includes(option)) {
99
+ for (const state of MOVES) {
100
+ option.toggleState(state, false);
101
+ }
102
+ }
103
+ }
104
+ this.marked = marked;
105
+ }
106
+ }
107
+ const adopt = (element, given, on) => {
108
+ const root = element.shadowRoot;
109
+ const sheet = given?.styleSheet;
110
+ if (!root || !sheet || root.adoptedStyleSheets.includes(sheet) === on) {
111
+ return;
112
+ }
113
+ root.adoptedStyleSheets = on
114
+ ? [...root.adoptedStyleSheets, sheet]
115
+ : root.adoptedStyleSheets.filter((one) => one !== sheet);
116
+ };
117
+ const given = new WeakMap();
118
+ const attach = (list, sheet, listSheet) => {
119
+ const controller = given.get(list) ?? new ListboxReorderController(list);
120
+ given.set(list, controller);
121
+ controller.sheet ??= sheet;
122
+ controller.listSheet ??= listSheet;
123
+ };
124
+ const onPage = (of, root = document) => [...root.querySelectorAll('*')].flatMap((node) => [
125
+ ...(node instanceof of ? [node] : []),
126
+ ...(node.shadowRoot ? onPage(of, node.shadowRoot) : []),
127
+ ]);
128
+ export const reorderListbox = (element, sheet, listSheet) => {
129
+ element.addInitializer((host) => attach(host, sheet, listSheet));
130
+ if (typeof document !== 'undefined') {
131
+ for (const list of onPage(element)) {
132
+ attach(list, sheet, listSheet);
133
+ list.requestUpdate();
134
+ }
135
+ }
136
+ const proto = element.prototype;
137
+ for (const [name, [attribute, fallback]] of Object.entries(WORDS)) {
138
+ if (name in proto) {
139
+ continue;
140
+ }
141
+ Object.defineProperty(proto, name, {
142
+ configurable: true,
143
+ get() {
144
+ return this.getAttribute(attribute) ?? fallback;
145
+ },
146
+ set(next) {
147
+ this.setAttribute(attribute, next);
148
+ },
149
+ });
150
+ }
151
+ };
@@ -1,4 +1,4 @@
1
- import { AriaForwardController, DisabledCascadeController, HelixFormElement } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, type DataOptionItem, DisabledCascadeController, HelixFormElement } from '@fusion.dev/helix-core';
2
2
  import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
3
  import { HelixHeadlessOption } from '../../option/root/option-root.js';
4
4
  declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusion.dev/helix-core").CollectionElement) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
@@ -20,16 +20,32 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
20
20
  * active option is where the keyboard is: the arrow keys move it, and
21
21
  * `aria-activedescendant` points to it while focus stays on the list.
22
22
  *
23
- * The arrow keys, Home, End, PageUp and PageDown move the highlight, and typed
24
- * letters move it to the next option whose words start with them. In a
25
- * single-choice list the highlight is the choice unless `manual` is set; then
26
- * Enter, Space or a click chooses. With `multiple`, Space toggles the
27
- * highlighted option, Shift with an arrow adds the option it moves to, Shift
28
- * with Home or End adds every option from the highlight to that end, and
29
- * Control+A or Command+A chooses every option, or none where all are chosen.
30
- * In a filter field, Space, Home, End and letters edit the text, and with
31
- * `multiple`, Control with Shift and Home or End adds every option from the
32
- * highlight to that end.
23
+ * **Keyboard.** The list is one tab stop, or its filter field is where there
24
+ * is one. In a single-choice list the highlight is the choice unless `manual`
25
+ * is set; then Enter, Space or a click chooses.
26
+ *
27
+ * | Key | What it does |
28
+ * |---|---|
29
+ * | Down, Up | Moves the highlight to the next or the previous option, round from the last to the first. With `layout="grid"`, to the option below or above, stopping at the edge. |
30
+ * | Right, Left | With `layout="grid"`, moves to the next or the previous option, mirrored right to left. In a filter field, moves the caret. |
31
+ * | Home, End | Moves to the first or the last option. In a filter field, moves the caret. |
32
+ * | Page Down, Page Up | Moves by as many options as the list shows at once. |
33
+ * | A letter or a word | Moves to the next option whose words start with what you typed. In a filter field, types it. |
34
+ * | Enter | Chooses the highlighted option, or follows it where it is a link. |
35
+ * | Space | Chooses the highlighted option, or with `multiple` toggles it. In a filter field, types a space. |
36
+ * | Shift with Down or Up | With `multiple`, adds the option the highlight moves to. In a filter field, moves the highlight and leaves the value. |
37
+ * | Shift with Space | With `multiple`, adds every option from the one last chosen to the highlighted one. In a filter field, types a space. |
38
+ * | Shift with Home or End | With `multiple`, adds every option from the highlight to that end. In a filter field, hold Control as well. |
39
+ * | Control or Command with A | With `multiple`, chooses every option, or none where all are chosen. In a filter field, selects the text. |
40
+ * | Escape | In a filter field, clears the filter. |
41
+ * | Alt with Up or Down | In a `reorderable` list, moves the highlighted option a place. Alt with Home or End moves it to either end. |
42
+ *
43
+ * **ARIA.** The `items` part is the `listbox`, named by the label and
44
+ * described by the hint and the message, with `aria-multiselectable` under
45
+ * `multiple`, `aria-required`, `aria-invalid`, and `aria-busy` while loading.
46
+ * Each option is an `option` with `aria-selected`. The list, or its filter
47
+ * field, points `aria-activedescendant` at the highlighted option; the field
48
+ * is `aria-autocomplete="list"` and controls the list.
33
49
  *
34
50
  * An option with `href` is a link: Enter or a press follows it and selects
35
51
  * nothing, and moving onto it with the arrows does not follow it.
@@ -38,6 +54,26 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
38
54
  * left, such as "3 results", from `result-label` and `results-label`. A filter
39
55
  * that leaves none says `empty-text`.
40
56
  *
57
+ * **Moving options** is a separate import, `@fusion.dev/helix/listbox/reorder`,
58
+ * so a list that never moves options carries none of its code. With it
59
+ * imported, `reorderable` lets a reader move options:
60
+ *
61
+ * - Alt with Up or Down moves the highlighted option a place, and Alt with
62
+ * Home or End moves it to either end, as the APG's rearrangeable listbox
63
+ * does. The highlight stays on it.
64
+ * - A pointer drags an option, with a drop indicator, and the list scrolls near
65
+ * an edge. A press that does not travel still chooses the option.
66
+ * - A drop sends `helix-before-reorder`, which you can refuse, and then
67
+ * `helix-reorder`, whose `keys` hold every value in the new order. **You
68
+ * move the options**, or set `items` in the new order: the list moves no
69
+ * element of yours.
70
+ * - The list says each move in its `live` region. The words are the
71
+ * `moved-label`, `canceled-label`, `dropped-label`, and `picked-up-label`
72
+ * attributes, which the import reads.
73
+ * - An option in a group moves within its group. Options do not move while a
74
+ * filter narrows the list, under `layout="grid"`, or while the list is
75
+ * disabled.
76
+ *
41
77
  * @since 0.1.0
42
78
  * @status stable
43
79
  * @category forms
@@ -56,8 +92,10 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
56
92
  *
57
93
  * @event {Event} change - Fired after the value changes.
58
94
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired when the value is about to change, and cancelable.
95
+ * @event {HelixBeforeReorderEvent} helix-before-reorder - Fired when an option is dropped at a new place, in a `reorderable` list with `@fusion.dev/helix/listbox/reorder` imported, with every value in the new order. Cancelable: cancel it to keep the option where it is.
59
96
  * @event {HelixFilterEvent} helix-filter - Fired before the list is filtered, and cancelable.
60
97
  * @event {HelixLoadMoreEvent} helix-load-more - Fired once the list is scrolled within a screen of its end, once for each number of options, and not while `loading` is set. Add the next page of options and set `loading` while you fetch it.
98
+ * @event {HelixReorderEvent} helix-reorder - Fired after `helix-before-reorder` when nobody canceled it. Move your options, or your `items`, to the order in `keys`. Cancelable: cancel it to say the move did not happen.
61
99
  * @event {Event} input - Fired after the value changes.
62
100
  *
63
101
  * @csspart empty - The text shown when the list has no options to show, or the filter hides every one. `:state(no-results)` is set for the second.
@@ -68,7 +106,7 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
68
106
  * @csspart hint - Help under the list.
69
107
  * @csspart items - The list of options.
70
108
  * @csspart label - The label above the list.
71
- * @csspart live - A copy of the empty and loading text, and how many options a filter left, in a region that screen readers announce when it changes. Visually hidden in the styled tier.
109
+ * @csspart live - A copy of the empty and loading text, how many options a filter left, and each move in a `reorderable` list, in a region that screen readers announce when it changes. Visually hidden in the styled tier.
72
110
  * @csspart loading - The text shown while the options load.
73
111
  * @csspart search - The region that holds the filter field.
74
112
  * @csspart search-field - The filter field.
@@ -80,14 +118,23 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
80
118
  * @cssstate no-results - A filter hid every option of a list that has some.
81
119
  */
82
120
  export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
121
+ /**
122
+ * The key this family's words are registered under, its styled tag:
123
+ * `registerWords('de', { 'helix-listbox': { filterLabel: 'Filtern' } })`.
124
+ */
125
+ static words: string;
83
126
  protected readonly aria: AriaForwardController;
84
127
  /**
85
- * What the browser may autofill in the text field, as a platform token.
128
+ * What a browser's autofill may choose, as a platform token such as
129
+ * `country` or `cc-exp-month`, as a native `<select>` takes it.
130
+ *
131
+ * Set, the list draws a hidden native `<select>` with the same options for
132
+ * the browser to fill, as React Aria's `HiddenSelect` does, and a fill
133
+ * chooses the option. `off`, the default, draws none.
86
134
  *
87
- * **Defaults to `off`, which is not the platform's default and is the
88
- * point.** The field filters a list rather than collecting a value, so a
89
- * browser offering to fill it with a saved address is competing for the same
90
- * keystrokes as the listbox. A consumer who wants autofill can still ask.
135
+ * **The filter field is never filled.** It narrows the list rather than
136
+ * collecting a value, so a browser offering it a saved address competes
137
+ * with the reader for the same keystrokes: it is always `off`.
91
138
  */
92
139
  autocomplete: string;
93
140
  /**
@@ -138,14 +185,49 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
138
185
  * Placeholder text for the filter field.
139
186
  */
140
187
  filterLabel: string;
188
+ /**
189
+ * The options as data, for a list too long to draw whole: each a value,
190
+ * which is also its words, or a record with a `value`, a `label` and
191
+ * `disabled`. Set, the list draws only the options in sight, and the ones
192
+ * you write as children are not read.
193
+ *
194
+ * **Every option is still reachable.** The keyboard, the typeahead, the
195
+ * filter and the value work over the whole set, and the option the keyboard
196
+ * is on is drawn wherever it is, so a screen reader is told about it. Each
197
+ * drawn option says its place in the set (`aria-posinset`, `aria-setsize`).
198
+ * React Aria virtualizes a `ListBox` with its `Virtualizer`; this is core's
199
+ * `VirtualizeController`.
200
+ *
201
+ * **The list needs a height to scroll in**: the styled tier's
202
+ * `--listbox-max-block-size`, or a `max-block-size` on `::part(items)` in the
203
+ * headless tier. Import the Option family as well, whose elements are drawn.
204
+ * A server sends the list empty, and the options are drawn once it hydrates.
205
+ */
206
+ items: readonly DataOptionItem[] | undefined;
207
+ /**
208
+ * How the options are laid out, which the arrow keys follow: `stack`, one
209
+ * under another, or `grid`, in rows and columns, as React Aria's `ListBox`
210
+ * lays out cards or photos. With `grid`, Up and Down move within a column,
211
+ * and Left and Right along the list, mirrored right to left.
212
+ *
213
+ * The columns are the stylesheet's: the styled tier draws them at
214
+ * `--listbox-column-min-inline-size` or wider, and a headless list lays out
215
+ * `::part(items)` as it likes, since the keys read where each option is
216
+ * drawn. A list of `items` stays a stack. Left out, the list is a stack,
217
+ * and the host carries no attribute for it.
218
+ */
219
+ layout?: 'grid' | 'stack';
220
+ /** Draws `items` a window at a time. See core's `OptionWindowController`. */
221
+ private readonly data;
141
222
  /**
142
223
  * What the list says when its options could not be loaded, such as after a
143
- * request to a server fails. Empty is no error.
224
+ * request to a server fails. Empty, null or no attribute is no error, so
225
+ * removing the attribute clears it.
144
226
  *
145
227
  * Set, it shows under any options there are, in place of `empty-text` and
146
228
  * `loading-text`, and a screen reader hears it. Clear it when you ask again.
147
229
  */
148
- loadError: string;
230
+ loadError: string | null;
149
231
  /**
150
232
  * Stops the arrow keys from choosing each option they move to. Enter, Space
151
233
  * or a click chooses the highlighted option.
@@ -157,6 +239,9 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
157
239
  manual: boolean;
158
240
  /**
159
241
  * Whether more than one option can be chosen.
242
+ *
243
+ * A press then toggles the option pressed, and Shift with a press adds every
244
+ * option from the one last chosen to it, as React Aria's list does.
160
245
  */
161
246
  multiple: boolean;
162
247
  /**
@@ -167,19 +252,40 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
167
252
  */
168
253
  noResultsText?: string;
169
254
  /**
170
- * What a screen reader hears when a filter leaves one option. `{count}` is
171
- * the count.
255
+ * Lets a reader move options, once `@fusion.dev/helix/listbox/reorder` (or
256
+ * `@fusion.dev/helix-headless/listbox/reorder`) is imported: Alt with an
257
+ * arrow moves the highlighted option, and a pointer drags one. The list
258
+ * moves no option itself: you move your options, or your `items`, on
259
+ * `helix-reorder`. Without that import it does nothing, so a list that never
260
+ * moves options carries none of the code.
172
261
  */
173
- resultLabel: string;
262
+ reorderable: boolean;
174
263
  /**
175
- * What a screen reader hears when a filter leaves any other number of
176
- * options. `{count}` is the count.
264
+ * What a screen reader hears when a filter leaves one option, where
265
+ * `results-label` should not say it. `{count}` is the count.
177
266
  *
178
- * **Two sentences, chosen by `Intl.PluralRules`**, which is Data Table's
179
- * pair: a language with more forms than two picks the one for its `one` and
180
- * writes the rest here.
267
+ * @deprecated Write every form in `results-label`, as `{count, plural, one
268
+ * {# result} other {# results}}`. Removed in the next major version.
269
+ */
270
+ resultLabel?: string;
271
+ /**
272
+ * What a screen reader hears when a filter narrows the list. `{count}` is
273
+ * how many options are left, written in the locale, and each language
274
+ * writes the forms it has: `{count, plural, one {# wynik} few {# wyniki}
275
+ * many {# wyników} other {# wyniku}}`.
181
276
  */
182
277
  resultsLabel: string;
278
+ /**
279
+ * What VoiceOver hears as the highlight reaches a chosen option from the
280
+ * filter field, on Apple devices. `{name}` is the option's words.
281
+ */
282
+ selectedLabel: string;
283
+ /**
284
+ * The message reported when the field is required and no option is chosen.
285
+ * Absent, or empty, is this sentence, or the words registered for the page's
286
+ * locale.
287
+ */
288
+ valueMissingMessage: string;
183
289
  /**
184
290
  * Which option the keyboard is on, which is not what is chosen.
185
291
  */
@@ -189,6 +295,16 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
189
295
  * it back on the first match.
190
296
  */
191
297
  private parked;
298
+ /**
299
+ * The option last chosen, which Shift with Space or with a press extends
300
+ * from.
301
+ */
302
+ private anchor;
303
+ /**
304
+ * Whether Shift was held for the press being handled, read off the click on
305
+ * its way down to the option, which is where React Aria extends a range.
306
+ */
307
+ private shifted;
192
308
  /** The letters typed at the list, which move the highlight to a match. */
193
309
  private readonly typeahead;
194
310
  /**
@@ -212,6 +328,9 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
212
328
  * markup, so the hiding is done to them in `willUpdate` and the render reads
213
329
  * the answer it left here. Counted during render instead, a filter matching
214
330
  * nothing left an empty box with no message in it.
331
+ *
332
+ * Plain rather than reactive: `place` writes it inside an update, which
333
+ * draws it, and a slot change asks for that update.
215
334
  */
216
335
  private hasMatches;
217
336
  /**
@@ -221,7 +340,7 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
221
340
  * count: without this the list shipped "Nothing to show" over markup that was
222
341
  * about to have options in it, which is the one thing an empty state must
223
342
  * never do. `place` does not run on the server, so this is false exactly
224
- * when the answer is unknown.
343
+ * when the answer is unknown. Plain, for the reason `hasMatches` is.
225
344
  */
226
345
  private counted;
227
346
  protected readonly cascade: DisabledCascadeController;
@@ -260,6 +379,17 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
260
379
  * which the region says in place of this.
261
380
  */
262
381
  private tell;
382
+ /**
383
+ * Says the option the arrows reached from the filter field, on Apple devices.
384
+ *
385
+ * **VoiceOver does not read the option `aria-activedescendant` points to**
386
+ * while focus is in a text field, so a filterable list moved its highlight
387
+ * in silence there. React Aria's combobox says the option on Apple devices,
388
+ * with "selected" where it is chosen; the field here is the same pattern.
389
+ * Only while the field has focus, so a highlight placed as the list opens
390
+ * or seeded from the value says nothing until the reader moves it.
391
+ */
392
+ private tellActive;
263
393
  /**
264
394
  * @internal Moves the highlight to the option whose words start with the
265
395
  * letters typed, and returns its value, or nothing where none matches.
@@ -269,6 +399,13 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
269
399
  * `<select>` does.
270
400
  */
271
401
  typeaheadTo(key: string): string | undefined;
402
+ /**
403
+ * @internal Called when the option already chosen is chosen again, which
404
+ * changes nothing here and answers the question for the control that opened
405
+ * the list. Select and Combobox close their panel on it, as a native
406
+ * `<select>` closes when its own answer is pressed.
407
+ */
408
+ chosenAgain: (() => void) | undefined;
272
409
  /** @internal Whether letters typed in the last moment are still one search. */
273
410
  get typing(): boolean;
274
411
  /**
@@ -298,8 +435,16 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
298
435
  * component comes up empty.
299
436
  */
300
437
  private among;
301
- /** The groups, found the same way and for the same reason. */
438
+ /**
439
+ * The groups, found the same way and for the same reason, and by their
440
+ * name rather than their class, as the rules are: a list that holds no group
441
+ * does not carry the group's code, and either tier's tag ends the same way.
442
+ */
302
443
  private get groups();
444
+ /** Every option the list reads: the records of `items`, or the elements. */
445
+ private get entries();
446
+ /** The option elements on the page: the ones drawn for `items`, or the children. */
447
+ private get elements();
303
448
  /** The options a filter has left showing. */
304
449
  private get shown();
305
450
  /** The chosen values, as an array whether or not `multiple` is set. */
@@ -312,6 +457,12 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
312
457
  */
313
458
  private choose;
314
459
  private readonly handleSelection;
460
+ /**
461
+ * Adds every option a reader can reach from the one last chosen to `to`,
462
+ * APG's Shift with Space and React Aria's Shift with a press. With none
463
+ * chosen yet, it adds `to` alone and the next range starts there.
464
+ */
465
+ private extend;
315
466
  /**
316
467
  * Moves the keyboard, and chooses with it only where that is the pattern.
317
468
  *
@@ -326,6 +477,7 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
326
477
  * a combobox or a command palette chose the highlighted option and closed the
327
478
  * panel, so a query of two words could not be typed.
328
479
  */
480
+ private readonly handleClick;
329
481
  private readonly handleKeydown;
330
482
  /**
331
483
  * Puts the highlight on an option, and chooses it where moving is choosing.
@@ -380,10 +532,8 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
380
532
  * read as a dial rather than scanned as a list, which is what Time Picker's
381
533
  * wheels do with three of these.
382
534
  *
383
- * A delta from the two boxes rather than `offsetTop`. An option is a slotted
384
- * light DOM child, so its `offsetParent` is whatever is positioned above it,
385
- * which is not this list and is not guaranteed to be anything: measuring both
386
- * rects and moving by the difference needs no such assumption.
535
+ * Measured by core's `centerInView`, or, for a list of `items`, by the
536
+ * arithmetic, since the chosen option may not be drawn.
387
537
  */
388
538
  reveal(): void;
389
539
  /** Hides what the filter excludes, and writes the rest onto the options. */
@@ -523,6 +673,8 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
523
673
  * different thing to say from a list that has none.
524
674
  */
525
675
  private get noResults();
676
+ /** Whether `load-error` holds words, which a removed attribute does not. */
677
+ private get failed();
526
678
  /** The error, or what a filter that hid everything says, before the shared words. */
527
679
  protected renderLive(hasContent: boolean): TemplateResult;
528
680
  protected render(): TemplateResult;