@fusion.dev/helix-headless 0.144.0 → 0.146.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 (319) hide show
  1. package/custom-elements.json +15195 -4240
  2. package/dist/accordion/item/accordion-item.d.ts +62 -12
  3. package/dist/accordion/item/accordion-item.js +41 -10
  4. package/dist/accordion/root/accordion-root.d.ts +14 -5
  5. package/dist/accordion/root/accordion-root.js +7 -1
  6. package/dist/announcement/root/announcement-root.d.ts +24 -3
  7. package/dist/announcement/root/announcement-root.js +39 -2
  8. package/dist/app-header/root/app-header-root.d.ts +12 -0
  9. package/dist/app-header/root/app-header-root.js +13 -1
  10. package/dist/app-header/sectioning.d.ts +9 -0
  11. package/dist/app-header/sectioning.js +27 -0
  12. package/dist/avatar/group/avatar-group.d.ts +20 -14
  13. package/dist/avatar/group/avatar-group.js +17 -19
  14. package/dist/badge/root/badge-root.d.ts +3 -1
  15. package/dist/badge/root/badge-root.js +2 -2
  16. package/dist/bar-chart/root/bar-chart-root.d.ts +18 -1
  17. package/dist/bar-chart/root/bar-chart-root.js +52 -15
  18. package/dist/bar-chart/stack.d.ts +15 -0
  19. package/dist/bar-chart/stack.js +19 -0
  20. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +57 -1
  21. package/dist/breadcrumbs/root/breadcrumbs-root.js +73 -3
  22. package/dist/button/root/button-root.d.ts +26 -0
  23. package/dist/button/root/button-root.js +38 -18
  24. package/dist/button-group/root/button-group-root.d.ts +2 -1
  25. package/dist/button-group/root/button-group-root.js +5 -1
  26. package/dist/canvas/node/canvas-node.d.ts +4 -3
  27. package/dist/canvas/node/canvas-node.js +3 -2
  28. package/dist/canvas/root/canvas-root.d.ts +38 -3
  29. package/dist/canvas/root/canvas-root.js +123 -42
  30. package/dist/card/root/card-root.d.ts +24 -3
  31. package/dist/card/root/card-root.js +20 -8
  32. package/dist/carousel/root/carousel-root.d.ts +133 -4
  33. package/dist/carousel/root/carousel-root.js +174 -24
  34. package/dist/carousel/stops.d.ts +20 -0
  35. package/dist/carousel/stops.js +30 -0
  36. package/dist/chat/composer/chat-composer.d.ts +38 -6
  37. package/dist/chat/composer/chat-composer.js +42 -9
  38. package/dist/chat/message/chat-message.d.ts +32 -1
  39. package/dist/chat/message/chat-message.js +27 -10
  40. package/dist/chat/naming.d.ts +26 -0
  41. package/dist/chat/naming.js +14 -0
  42. package/dist/chat/root/chat-root.d.ts +58 -0
  43. package/dist/chat/root/chat-root.js +43 -3
  44. package/dist/checkbox/root/checkbox-root.d.ts +20 -0
  45. package/dist/checkbox/root/checkbox-root.js +14 -1
  46. package/dist/checkbox-group/root/checkbox-group-root.d.ts +8 -2
  47. package/dist/checkbox-group/root/checkbox-group-root.js +12 -10
  48. package/dist/checkbox-group/select-all.d.ts +27 -0
  49. package/dist/checkbox-group/select-all.js +12 -0
  50. package/dist/code-block/highlight.d.ts +15 -0
  51. package/dist/code-block/highlight.js +4 -0
  52. package/dist/code-block/root/code-block-root.d.ts +57 -2
  53. package/dist/code-block/root/code-block-root.js +63 -8
  54. package/dist/color-input/root/color-input-root.d.ts +3 -0
  55. package/dist/color-input/root/color-input-root.js +6 -3
  56. package/dist/color-picker/root/color-picker-root.d.ts +33 -4
  57. package/dist/color-picker/root/color-picker-root.js +80 -21
  58. package/dist/color-picker/swatches.d.ts +12 -0
  59. package/dist/color-picker/swatches.js +26 -0
  60. package/dist/combobox/root/combobox-root.d.ts +43 -0
  61. package/dist/combobox/root/combobox-root.js +79 -15
  62. package/dist/command/root/command-root.d.ts +16 -1
  63. package/dist/command/root/command-root.js +22 -4
  64. package/dist/copy-button/root/copy-button-root.d.ts +26 -1
  65. package/dist/copy-button/root/copy-button-root.js +12 -5
  66. package/dist/data-table/index.d.ts +1 -1
  67. package/dist/data-table/index.js +1 -1
  68. package/dist/data-table/keys.d.ts +24 -0
  69. package/dist/data-table/keys.js +24 -0
  70. package/dist/data-table/root/data-table-root.d.ts +170 -13
  71. package/dist/data-table/root/data-table-root.js +294 -52
  72. package/dist/data-table/tree.d.ts +55 -0
  73. package/dist/data-table/tree.js +54 -0
  74. package/dist/date-input/root/date-input-root.d.ts +3 -1
  75. package/dist/date-input/root/date-input-root.js +18 -7
  76. package/dist/date-picker/root/date-picker-root.js +12 -6
  77. package/dist/description-list/item/description-list-item.d.ts +17 -2
  78. package/dist/description-list/item/description-list-item.js +15 -2
  79. package/dist/description-list/root/description-list-root.d.ts +27 -1
  80. package/dist/description-list/root/description-list-root.js +26 -1
  81. package/dist/dialog/description.js +15 -1
  82. package/dist/dialog/root/dialog-root.d.ts +8 -2
  83. package/dist/dialog/root/dialog-root.js +16 -1
  84. package/dist/divider/root/divider-root.d.ts +3 -0
  85. package/dist/divider/root/divider-root.js +2 -1
  86. package/dist/donut-chart/root/donut-chart-root.d.ts +31 -0
  87. package/dist/donut-chart/root/donut-chart-root.js +48 -1
  88. package/dist/drawer/reveal.d.ts +8 -0
  89. package/dist/drawer/reveal.js +1 -0
  90. package/dist/drawer/root/drawer-root.d.ts +59 -0
  91. package/dist/drawer/root/drawer-root.js +63 -4
  92. package/dist/drilldown/item/drilldown-item.d.ts +11 -6
  93. package/dist/drilldown/item/drilldown-item.js +8 -5
  94. package/dist/drilldown/root/drilldown-root.d.ts +29 -0
  95. package/dist/drilldown/root/drilldown-root.js +28 -5
  96. package/dist/element-events.generated.js +83 -12
  97. package/dist/empty/root/empty-root.d.ts +3 -1
  98. package/dist/empty/root/empty-root.js +3 -2
  99. package/dist/file-input/root/file-input-root.d.ts +69 -6
  100. package/dist/file-input/root/file-input-root.js +82 -19
  101. package/dist/file-input/screen.d.ts +2 -1
  102. package/dist/filter-input/menu/filter-menu.d.ts +5 -0
  103. package/dist/filter-input/menu/filter-menu.js +16 -4
  104. package/dist/filter-input/root/filter-input-root.d.ts +30 -6
  105. package/dist/filter-input/root/filter-input-root.js +1 -1
  106. package/dist/filter-input/suggest.d.ts +28 -3
  107. package/dist/filter-input/suggest.js +18 -8
  108. package/dist/format-date/root/format-date-root.d.ts +9 -16
  109. package/dist/format-date/root/format-date-root.js +10 -27
  110. package/dist/heatmap/root/heatmap-root.d.ts +24 -13
  111. package/dist/heatmap/root/heatmap-root.js +34 -12
  112. package/dist/icon/root/icon-root.d.ts +12 -1
  113. package/dist/icon/root/icon-root.js +21 -10
  114. package/dist/icon/settle.d.ts +7 -0
  115. package/dist/icon/settle.js +10 -0
  116. package/dist/icon-button/root/icon-button-root.d.ts +2 -0
  117. package/dist/image/root/image-root.d.ts +5 -1
  118. package/dist/image/root/image-root.js +10 -2
  119. package/dist/input/root/input-root.d.ts +13 -3
  120. package/dist/input/root/input-root.js +12 -3
  121. package/dist/kbd/root/kbd-root.d.ts +35 -1
  122. package/dist/kbd/root/kbd-root.js +23 -4
  123. package/dist/legend/root/legend-root.d.ts +55 -8
  124. package/dist/legend/root/legend-root.js +55 -13
  125. package/dist/lightbox/root/lightbox-root.d.ts +49 -6
  126. package/dist/lightbox/root/lightbox-root.js +86 -19
  127. package/dist/lightbox/zoom-controller.d.ts +59 -0
  128. package/dist/lightbox/zoom-controller.js +128 -0
  129. package/dist/lightbox/zoom.d.ts +22 -0
  130. package/dist/lightbox/zoom.js +16 -0
  131. package/dist/line-chart/root/line-chart-root.d.ts +22 -1
  132. package/dist/line-chart/root/line-chart-root.js +21 -1
  133. package/dist/list/item/list-item.d.ts +70 -1
  134. package/dist/list/item/list-item.js +77 -6
  135. package/dist/list/numbering.d.ts +14 -0
  136. package/dist/list/numbering.js +8 -0
  137. package/dist/list/root/list-root.d.ts +155 -9
  138. package/dist/list/root/list-root.js +246 -4
  139. package/dist/list/selection.d.ts +49 -0
  140. package/dist/list/selection.js +30 -0
  141. package/dist/listbox/root/listbox-root.d.ts +37 -1
  142. package/dist/listbox/root/listbox-root.js +49 -4
  143. package/dist/loader/busy.d.ts +10 -0
  144. package/dist/loader/busy.js +11 -0
  145. package/dist/loader/root/loader-root.d.ts +9 -2
  146. package/dist/loader/root/loader-root.js +23 -3
  147. package/dist/map/root/map-root.d.ts +6 -2
  148. package/dist/map/root/map-root.js +5 -2
  149. package/dist/megamenu/root/megamenu-root.d.ts +62 -0
  150. package/dist/megamenu/root/megamenu-root.js +90 -5
  151. package/dist/menu/index.d.ts +1 -0
  152. package/dist/menu/index.js +1 -0
  153. package/dist/menu/item/menu-item.d.ts +87 -11
  154. package/dist/menu/item/menu-item.js +78 -22
  155. package/dist/menu/root/menu-root.d.ts +39 -1
  156. package/dist/menu/root/menu-root.js +65 -8
  157. package/dist/menu/section/menu-section.d.ts +55 -0
  158. package/dist/menu/section/menu-section.js +60 -0
  159. package/dist/meter/root/meter-root.d.ts +20 -1
  160. package/dist/meter/root/meter-root.js +11 -15
  161. package/dist/navigation/root/navigation-root.d.ts +18 -2
  162. package/dist/navigation/root/navigation-root.js +29 -3
  163. package/dist/notifications/root/notifications-root.d.ts +25 -0
  164. package/dist/notifications/root/notifications-root.js +33 -5
  165. package/dist/number-input/root/number-input-root.d.ts +17 -8
  166. package/dist/number-input/root/number-input-root.js +2 -7
  167. package/dist/object-inspector/root/object-inspector-root.d.ts +161 -32
  168. package/dist/object-inspector/root/object-inspector-root.js +264 -123
  169. package/dist/object-inspector/rows.d.ts +75 -0
  170. package/dist/object-inspector/rows.js +119 -0
  171. package/dist/option/root/option-root.d.ts +5 -3
  172. package/dist/outline/levels.d.ts +31 -0
  173. package/dist/outline/levels.js +23 -0
  174. package/dist/outline/root/outline-root.d.ts +86 -3
  175. package/dist/outline/root/outline-root.js +95 -12
  176. package/dist/pagination/size/pagination-size.d.ts +12 -2
  177. package/dist/pagination/size/pagination-size.js +20 -5
  178. package/dist/pin-input/code.d.ts +10 -0
  179. package/dist/pin-input/code.js +13 -0
  180. package/dist/pin-input/root/pin-input-root.d.ts +25 -1
  181. package/dist/pin-input/root/pin-input-root.js +10 -11
  182. package/dist/popover/hide-outside.d.ts +14 -0
  183. package/dist/popover/hide-outside.js +41 -0
  184. package/dist/popover/root/popover-root.d.ts +57 -7
  185. package/dist/popover/root/popover-root.js +58 -3
  186. package/dist/progress/root/progress-root.d.ts +20 -0
  187. package/dist/progress/root/progress-root.js +15 -15
  188. package/dist/qr-code/export.d.ts +26 -1
  189. package/dist/qr-code/export.js +14 -0
  190. package/dist/qr-code/root/qr-code-root.d.ts +19 -5
  191. package/dist/qr-code/root/qr-code-root.js +58 -9
  192. package/dist/quote/marks.d.ts +8 -0
  193. package/dist/quote/marks.js +5 -0
  194. package/dist/quote/root/quote-root.d.ts +14 -2
  195. package/dist/quote/root/quote-root.js +23 -1
  196. package/dist/radio/group/radio-group.d.ts +4 -10
  197. package/dist/radio/group/radio-group.js +5 -10
  198. package/dist/radio/root/radio-root.d.ts +6 -2
  199. package/dist/radio/root/radio-root.js +6 -1
  200. package/dist/rating/root/rating-root.d.ts +10 -5
  201. package/dist/rating/root/rating-root.js +32 -8
  202. package/dist/rating/steps.d.ts +7 -0
  203. package/dist/rating/steps.js +9 -0
  204. package/dist/scatter-chart/fit.d.ts +15 -0
  205. package/dist/scatter-chart/fit.js +35 -0
  206. package/dist/scatter-chart/root/scatter-chart-root.d.ts +21 -1
  207. package/dist/scatter-chart/root/scatter-chart-root.js +30 -17
  208. package/dist/scroll-area/root/scroll-area-root.d.ts +84 -0
  209. package/dist/scroll-area/root/scroll-area-root.js +106 -7
  210. package/dist/scroll-area/scrollbar-controller.d.ts +111 -0
  211. package/dist/scroll-area/scrollbar-controller.js +172 -0
  212. package/dist/scroll-area/scrollbar.d.ts +92 -0
  213. package/dist/scroll-area/scrollbar.js +33 -0
  214. package/dist/select/root/select-root.d.ts +15 -0
  215. package/dist/select/root/select-root.js +18 -6
  216. package/dist/skeleton/root/skeleton-root.d.ts +3 -0
  217. package/dist/skeleton/root/skeleton-root.js +4 -2
  218. package/dist/skip-link/root/skip-link-root.js +2 -2
  219. package/dist/slider/root/slider-root.d.ts +41 -4
  220. package/dist/slider/root/slider-root.js +23 -3
  221. package/dist/sparkline/readings.d.ts +18 -0
  222. package/dist/sparkline/readings.js +19 -0
  223. package/dist/sparkline/root/sparkline-root.d.ts +78 -7
  224. package/dist/sparkline/root/sparkline-root.js +118 -23
  225. package/dist/speed-dial/root/speed-dial-root.js +2 -2
  226. package/dist/splitter/root/splitter-root.d.ts +20 -12
  227. package/dist/splitter/root/splitter-root.js +226 -203
  228. package/dist/splitter/snap.d.ts +9 -0
  229. package/dist/splitter/snap.js +12 -0
  230. package/dist/stat/root/stat-root.d.ts +14 -5
  231. package/dist/stat/root/stat-root.js +21 -5
  232. package/dist/steps/reach.d.ts +20 -0
  233. package/dist/steps/reach.js +15 -0
  234. package/dist/steps/root/steps-root.d.ts +76 -0
  235. package/dist/steps/root/steps-root.js +93 -1
  236. package/dist/steps/step/step.d.ts +45 -2
  237. package/dist/steps/step/step.js +33 -6
  238. package/dist/switch/root/switch-root.d.ts +10 -0
  239. package/dist/table/column/table-column.d.ts +11 -10
  240. package/dist/table/column/table-column.js +8 -19
  241. package/dist/table/root/table-root.d.ts +126 -4
  242. package/dist/table/root/table-root.js +156 -6
  243. package/dist/table/row/table-row.d.ts +47 -1
  244. package/dist/table/row/table-row.js +39 -2
  245. package/dist/tabs/panel/tab-panel.d.ts +22 -5
  246. package/dist/tabs/panel/tab-panel.js +34 -6
  247. package/dist/tabs/root/tabs-root.d.ts +37 -1
  248. package/dist/tabs/root/tabs-root.js +22 -4
  249. package/dist/tabs/tab/tab.d.ts +41 -4
  250. package/dist/tabs/tab/tab.js +49 -3
  251. package/dist/tag/group/tag-group.d.ts +31 -4
  252. package/dist/tag/group/tag-group.js +46 -7
  253. package/dist/tag/root/tag-root.js +9 -2
  254. package/dist/textarea/root/textarea-root.d.ts +28 -1
  255. package/dist/textarea/root/textarea-root.js +40 -1
  256. package/dist/time-input/root/time-input-root.d.ts +5 -1
  257. package/dist/time-input/root/time-input-root.js +16 -8
  258. package/dist/time-picker/root/time-picker-root.d.ts +18 -4
  259. package/dist/time-picker/root/time-picker-root.js +7 -10
  260. package/dist/time-picker/wheel-name.d.ts +9 -0
  261. package/dist/time-picker/wheel-name.js +15 -0
  262. package/dist/timeline/group/timeline-group.d.ts +46 -0
  263. package/dist/timeline/group/timeline-group.js +37 -0
  264. package/dist/timeline/index.d.ts +1 -0
  265. package/dist/timeline/index.js +1 -0
  266. package/dist/timeline/root/timeline-root.d.ts +10 -3
  267. package/dist/toast/item/toast-item.d.ts +24 -18
  268. package/dist/toast/item/toast-item.js +36 -40
  269. package/dist/toast/root/toast-root.d.ts +6 -0
  270. package/dist/toast/root/toast-root.js +16 -1
  271. package/dist/toggle/group/toggle-group.d.ts +24 -3
  272. package/dist/toggle/group/toggle-group.js +34 -5
  273. package/dist/toolbar/root/toolbar-root.d.ts +13 -0
  274. package/dist/toolbar/root/toolbar-root.js +15 -5
  275. package/dist/tooltip/root/tooltip-root.d.ts +13 -2
  276. package/dist/tooltip/root/tooltip-root.js +9 -1
  277. package/dist/tour/back.d.ts +10 -0
  278. package/dist/tour/back.js +8 -0
  279. package/dist/tour/root/tour-root.d.ts +30 -2
  280. package/dist/tour/root/tour-root.js +34 -8
  281. package/dist/tree/item/tree-item.d.ts +61 -4
  282. package/dist/tree/item/tree-item.js +92 -20
  283. package/dist/tree/root/tree-root.d.ts +58 -0
  284. package/dist/tree/root/tree-root.js +80 -16
  285. package/dist/tree/selection.d.ts +24 -0
  286. package/dist/tree/selection.js +13 -0
  287. package/dist/trend/root/trend-root.d.ts +43 -18
  288. package/dist/trend/root/trend-root.js +31 -6
  289. package/dist/video/media-events.d.ts +44 -0
  290. package/dist/video/media-events.js +35 -0
  291. package/dist/video/root/video-root.d.ts +117 -18
  292. package/dist/video/root/video-root.js +93 -47
  293. package/dist/video-playlist/menu/video-playlist-menu.d.ts +5 -0
  294. package/dist/video-playlist/menu/video-playlist-menu.js +3 -1
  295. package/dist/video-playlist/order.d.ts +41 -0
  296. package/dist/video-playlist/order.js +34 -0
  297. package/dist/video-playlist/root/video-playlist-root.d.ts +77 -3
  298. package/dist/video-playlist/root/video-playlist-root.js +60 -16
  299. package/dist/virtual-list/controls.d.ts +4 -0
  300. package/dist/virtual-list/controls.js +30 -0
  301. package/dist/virtual-list/fill.d.ts +22 -0
  302. package/dist/virtual-list/fill.js +23 -0
  303. package/dist/virtual-list/keys.d.ts +15 -0
  304. package/dist/virtual-list/keys.js +24 -0
  305. package/dist/virtual-list/plan.d.ts +49 -0
  306. package/dist/virtual-list/plan.js +16 -0
  307. package/dist/virtual-list/root/virtual-list-root.d.ts +259 -26
  308. package/dist/virtual-list/root/virtual-list-root.js +333 -49
  309. package/lint.json +1 -1
  310. package/package.json +8 -8
  311. package/types/jsx.d.ts +8113 -1394
  312. package/types/svelte.d.ts +2 -0
  313. package/types/vue.d.ts +1678 -353
  314. package/dist/data-table/root/order.d.ts +0 -141
  315. package/dist/data-table/root/order.js +0 -76
  316. package/dist/listbox/range.d.ts +0 -17
  317. package/dist/listbox/range.js +0 -5
  318. package/dist/tag/selection.d.ts +0 -10
  319. package/dist/tag/selection.js +0 -12
@@ -0,0 +1,119 @@
1
+ export const branchy = (value) => value !== null && typeof value === 'object';
2
+ export const entries = (value) => Array.isArray(value) ? value.map((item, at) => [String(at), item]) : Object.entries(value);
3
+ export const child = (path, key) => `${path}/${key.replaceAll('~', '~0').replaceAll('/', '~1')}`;
4
+ export const ancestorsOf = (path) => {
5
+ const cuts = [];
6
+ for (let at = path.indexOf('/'); at !== -1; at = path.indexOf('/', at + 1)) {
7
+ cuts.push(path.slice(0, at));
8
+ }
9
+ return cuts;
10
+ };
11
+ const words = (value) => (branchy(value) ? '' : String(value));
12
+ export const search = (root, query) => {
13
+ const found = new Set();
14
+ const wanted = query.trim().toLocaleLowerCase();
15
+ if (wanted === '' || root === undefined) {
16
+ return found;
17
+ }
18
+ const visit = (value, path, key) => {
19
+ if (key?.toLocaleLowerCase().includes(wanted) ||
20
+ words(value).toLocaleLowerCase().includes(wanted)) {
21
+ found.add(path);
22
+ }
23
+ if (branchy(value)) {
24
+ for (const [name, item] of entries(value)) {
25
+ visit(item, child(path, name), name);
26
+ }
27
+ }
28
+ };
29
+ visit(root, '');
30
+ return found;
31
+ };
32
+ export const flatten = (root, opened, found) => {
33
+ const rows = [];
34
+ if (root === undefined) {
35
+ return rows;
36
+ }
37
+ const trail = new Set([...(found ?? [])].flatMap(ancestorsOf));
38
+ const visit = (value, path, depth, position, siblings, under, key) => {
39
+ const branch = branchy(value);
40
+ const open = branch && opened.has(path);
41
+ const match = found?.has(path) === true;
42
+ rows.push({
43
+ branch,
44
+ depth,
45
+ match,
46
+ open,
47
+ path,
48
+ position,
49
+ siblings,
50
+ value,
51
+ ...(key === undefined ? {} : { key }),
52
+ });
53
+ if (!open) {
54
+ return;
55
+ }
56
+ const inside = under || match || !found;
57
+ const children = entries(value)
58
+ .map(([name, item]) => [name, item, child(path, name)])
59
+ .filter(([, , at]) => inside || found?.has(at) || trail.has(at));
60
+ children.forEach(([name, item, at], index) => {
61
+ visit(item, at, depth + 1, index + 1, children.length, inside && !!found, name);
62
+ });
63
+ };
64
+ if (!found || found.has('') || trail.has('')) {
65
+ visit(root, '', 0, 1, 1, false);
66
+ }
67
+ return rows;
68
+ };
69
+ export const seed = (root, depth, known = new Set(), opened = new Set()) => {
70
+ const open = new Set();
71
+ const branches = new Set();
72
+ const visit = (value, path, level) => {
73
+ if (!branchy(value)) {
74
+ return;
75
+ }
76
+ branches.add(path);
77
+ if (known.has(path) ? opened.has(path) : level < depth) {
78
+ open.add(path);
79
+ }
80
+ for (const [name, item] of entries(value)) {
81
+ visit(item, child(path, name), level + 1);
82
+ }
83
+ };
84
+ visit(root, '', 0);
85
+ return { branches, opened: open };
86
+ };
87
+ export const cursorFor = (rows, path, cursor, filtered) => {
88
+ const at = (one) => rows.findIndex((row) => row.path === one);
89
+ const own = path === undefined ? -1 : at(path);
90
+ if (own >= 0 || path === undefined) {
91
+ return own >= 0 ? own : Math.min(cursor, Math.max(0, rows.length - 1));
92
+ }
93
+ const match = filtered ? rows.findIndex((row) => row.match) : -1;
94
+ const around = ancestorsOf(path)
95
+ .reverse()
96
+ .map(at)
97
+ .find((one) => one >= 0);
98
+ return match >= 0 ? match : (around ?? 0);
99
+ };
100
+ export const withAncestors = (opened, branches, paths) => {
101
+ const next = new Set(opened);
102
+ for (const path of paths) {
103
+ for (const above of ancestorsOf(path)) {
104
+ if (branches.has(above)) {
105
+ next.add(above);
106
+ }
107
+ }
108
+ }
109
+ return next;
110
+ };
111
+ export const siblingBranches = (rows, row) => {
112
+ const parent = row.path.slice(0, row.path.lastIndexOf('/'));
113
+ return rows
114
+ .filter((one) => one.branch &&
115
+ !one.open &&
116
+ one.depth === row.depth &&
117
+ one.path.slice(0, one.path.lastIndexOf('/')) === parent)
118
+ .map((one) => one.path);
119
+ };
@@ -63,9 +63,11 @@ export declare class HelixHeadlessOption extends HelixElement {
63
63
  /**
64
64
  * Whether this is the option the keyboard is on.
65
65
  *
66
- * Written by the listbox, which writes it once a reader moves the highlight.
67
- * It shows as `:state(active)`, and as a `data-active` attribute for the
68
- * stylesheets written before the state existed.
66
+ * Written by the listbox once a reader moves the highlight. Style it with
67
+ * `:state(active)`, or `::part(item-active)` for an option inside another
68
+ * component's shadow root. The `data-active` attribute sets it from markup;
69
+ * it is also written back for stylesheets from before the state, and no
70
+ * stylesheet of the library's own selects it.
69
71
  */
70
72
  active: boolean;
71
73
  /**
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The levels of a flat run of links, as the nested lists they describe.
3
+ *
4
+ * A contents list written flat, `<a data-level="2">`, says its depth in an
5
+ * attribute that only a stylesheet reads. The outline turns the run into lists
6
+ * inside list items in its shadow root, so a screen reader hears the levels a
7
+ * nested `<ul>` would have given it, with the links left where they are.
8
+ */
9
+ /** One link in the tree: its place in the run, and the links nested under it. */
10
+ export interface Branch {
11
+ /** The link's position in the flat run, counted from zero. */
12
+ index: number;
13
+ /** The links one level or more deeper that follow it, before the next at its level. */
14
+ children: Branch[];
15
+ }
16
+ /**
17
+ * The level a link's `data-level` asks for, a whole number from 1.
18
+ *
19
+ * Absent, empty, or not a number is the top level, which is how the outline
20
+ * has always read a link without one.
21
+ */
22
+ export declare const levelOf: (value: string | null) => number;
23
+ /**
24
+ * Nests a run of levels.
25
+ *
26
+ * Each link goes under the nearest link before it with a lower level, and to
27
+ * the top where there is none. A jump of two levels nests one list deeper and
28
+ * not two, since a list item with no link of its own would be read as an empty
29
+ * entry. A run that starts deeper than 1 starts at the top all the same.
30
+ */
31
+ export declare const nest: (levels: readonly number[]) => Branch[];
@@ -0,0 +1,23 @@
1
+ export const levelOf = (value) => {
2
+ const level = Number.parseInt(value ?? '', 10);
3
+ return Number.isFinite(level) && level >= 1 ? level : 1;
4
+ };
5
+ export const nest = (levels) => {
6
+ const top = [];
7
+ const open = [];
8
+ levels.forEach((level, index) => {
9
+ const branch = { children: [], index };
10
+ while (open.length > 0 && (open.at(-1)?.level ?? 0) >= level) {
11
+ open.pop();
12
+ }
13
+ const parent = open.at(-1);
14
+ if (parent) {
15
+ parent.branch.children.push(branch);
16
+ }
17
+ else {
18
+ top.push(branch);
19
+ }
20
+ open.push({ branch, level });
21
+ });
22
+ return top;
23
+ };
@@ -1,5 +1,6 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
+ import { type Branch } from '../levels.js';
3
4
  /**
4
5
  * @summary Links to the headings on the page, with the section in view marked.
5
6
  *
@@ -12,8 +13,15 @@ import { type PropertyValues, type TemplateResult } from 'lit';
12
13
  * the same page.
13
14
  *
14
15
  * Write the anchors flat, with `data-level` for their depth, or nested in
15
- * lists, `<ul><li><a>`. A screen reader hears a nested list's levels; it hears
16
- * `data-level` as a flat run of links.
16
+ * lists, `<ul><li><a>`. A screen reader hears the levels either way: the
17
+ * outline puts a flat run into lists inside list items of its own, nested by
18
+ * `data-level`, and leaves the links where they are. A link without
19
+ * `data-level` is at the top level.
20
+ *
21
+ * A heading slotted into `heading` names the navigation, in place of `label`.
22
+ *
23
+ * In a contents rail with a scrollbar of its own, `follow` keeps the link of
24
+ * the section in view showing as the reader scrolls the page.
17
25
  *
18
26
  * The current section is decided by the headings' positions. An
19
27
  * `IntersectionObserver` with a band near the top fails both ways: when
@@ -27,13 +35,24 @@ import { type PropertyValues, type TemplateResult } from 'lit';
27
35
  * @documentation https://fusion.dev/helix/components/outline
28
36
  *
29
37
  * @slot - The links, as anchors with fragment hrefs, or lists holding them.
38
+ * @slot heading - A heading above the links, such as "On this page". It names the navigation.
30
39
  *
31
40
  * @event {HelixActivateEvent} helix-activate - Fired when the section in view changes, with its id as the value. It reports the scroll position and cannot be prevented.
32
41
  *
42
+ * @csspart heading - The box holding the `heading` slot, above the navigation.
33
43
  * @csspart indicator - The mark that moves to the section in view.
34
44
  * @csspart nav - The navigation landmark.
35
45
  */
36
46
  export declare class HelixHeadlessOutline extends HelixElement {
47
+ /**
48
+ * Manual, so each link in a flat run can be put in a list item of its own.
49
+ *
50
+ * The list items are slots, which lay out as nothing at all, so each link is
51
+ * still laid out as a child of the navigation exactly where it was, and the
52
+ * consumer's markup is not touched. A run holding anything but links, a
53
+ * `<ul>` among them, goes to one plain slot as before.
54
+ */
55
+ static shadowRootOptions: ShadowRootInit;
37
56
  private readonly indicator;
38
57
  /**
39
58
  * How the page scrolls to a section when its link is clicked.
@@ -58,7 +77,15 @@ export declare class HelixHeadlessOutline extends HelixElement {
58
77
  */
59
78
  behavior: ScrollBehavior;
60
79
  /**
61
- * The navigation's name, read by screen readers.
80
+ * Keeps the link of the section in view in view itself, in an outline
81
+ * longer than the box it scrolls in, such as a contents rail with a
82
+ * scrollbar of its own. Only that box moves: never the page, and never the
83
+ * box the sections scroll in.
84
+ */
85
+ follow: boolean;
86
+ /**
87
+ * The navigation's name, read by screen readers. A heading in the `heading`
88
+ * slot names it in place of this.
62
89
  */
63
90
  label: string;
64
91
  /**
@@ -92,6 +119,11 @@ export declare class HelixHeadlessOutline extends HelixElement {
92
119
  */
93
120
  private reading;
94
121
  private observer;
122
+ /**
123
+ * Watches the children, which a manual root is not told about: a link
124
+ * arriving, leaving or changing its level changes the lists drawn for them.
125
+ */
126
+ private childWatch;
95
127
  /**
96
128
  * The links, in document order. Empty on the server, which has no tree.
97
129
  *
@@ -101,6 +133,18 @@ export declare class HelixHeadlessOutline extends HelixElement {
101
133
  * nothing was announced, and nothing said why.
102
134
  */
103
135
  private get links();
136
+ /** The children that are the content, which is all of them but the heading. */
137
+ private get content();
138
+ /**
139
+ * The links of a flat run, which the render puts into lists, or `undefined`
140
+ * where the content is anything else.
141
+ *
142
+ * Anything else is a list a consumer wrote, which already has its levels, a
143
+ * slot forwarded from a component around this one, or a server's render and
144
+ * the hydration of one, which cannot know the children. A root a server sent
145
+ * routes by name, so it keeps the plain slot after hydration too.
146
+ */
147
+ private get run();
104
148
  /**
105
149
  * What each link points at, paired with the link, skipping anything missing.
106
150
  *
@@ -120,6 +164,14 @@ export declare class HelixHeadlessOutline extends HelixElement {
120
164
  private mark;
121
165
  /** Where the destination is set, whether measured or asked for. */
122
166
  private markTo;
167
+ /**
168
+ * Scrolls the box the links scroll in to show `link`, under `follow`.
169
+ *
170
+ * The nearest box above the link that scrolls, and only where it is the
171
+ * rail's own: the page, the box the sections scroll in, or a box holding it
172
+ * would move the reading under the reader, which is the scroll this marks.
173
+ */
174
+ private reveal;
123
175
  /**
124
176
  * Whether the page has no more scroll left.
125
177
  *
@@ -250,8 +302,39 @@ export declare class HelixHeadlessOutline extends HelixElement {
250
302
  private unland;
251
303
  connectedCallback(): void;
252
304
  protected firstUpdated(changed: PropertyValues): void;
305
+ /**
306
+ * Puts each child in its slot, on a root this element made.
307
+ *
308
+ * A link of a flat run goes to the slot inside its list item, and anything
309
+ * else, with the text between, to the plain slot. A root a server sent
310
+ * routes by name and needs none of it.
311
+ */
312
+ private assign;
313
+ /** Whether the heading is what names the navigation. */
314
+ private namedByHeading;
315
+ /**
316
+ * Names the navigation by what is in the `heading` slot, in place of `label`.
317
+ *
318
+ * An element reference, because the heading is in the light DOM and the
319
+ * `<nav>` in this shadow root, which an IDREF cannot cross. A browser without
320
+ * element reflection keeps `label`.
321
+ */
322
+ private readonly nameByHeading;
253
323
  protected updated(changed: PropertyValues): void;
254
324
  protected render(): TemplateResult;
325
+ /**
326
+ * A list for a run of links, with a list item for each, nested by level.
327
+ *
328
+ * **Slots, because a slot lays out as nothing.** A list and its items drawn
329
+ * as boxes would put each link inside an item, out of the navigation's own
330
+ * flex column, and every outline styled against that column would move. A
331
+ * slot is `display: contents`, keeps a role it is given, and shows its
332
+ * children while nothing is assigned to it, so the items nest in the
333
+ * accessibility tree and nowhere else. Measured in Chromium: every link at the
334
+ * same box with and without the lists, and each item at the level its
335
+ * nesting gives it.
336
+ */
337
+ protected renderList(branches: Branch[]): TemplateResult;
255
338
  }
256
339
  declare global {
257
340
  interface HTMLElementTagNameMap {
@@ -4,9 +4,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, elementById, HelixActivateEvent, HelixElement, IndicatorController, } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
7
+ import { customElement, elementById, HelixActivateEvent, HelixElement, IndicatorController, plainClick, reflectElements, scrollItemIntoView, } from '@fusion.dev/helix-core';
8
+ import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
+ import { levelOf, nest } from '../levels.js';
11
+ const isFragment = (element) => element instanceof HTMLAnchorElement && element.getAttribute('href')?.startsWith('#') === true;
10
12
  let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
11
13
  constructor() {
12
14
  super(...arguments);
@@ -16,6 +18,7 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
16
18
  surface: () => this.partNamed('nav'),
17
19
  });
18
20
  this.behavior = 'auto';
21
+ this.follow = false;
19
22
  this.label = 'On this page';
20
23
  this.offset = 0;
21
24
  this.reading = '';
@@ -30,10 +33,7 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
30
33
  });
31
34
  };
32
35
  this.handleClick = (event) => {
33
- if (event.defaultPrevented || event.button !== 0) {
34
- return;
35
- }
36
- if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
36
+ if (event.defaultPrevented || !plainClick(event)) {
37
37
  return;
38
38
  }
39
39
  const link = event.target?.closest?.('a[href^="#"]');
@@ -72,16 +72,38 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
72
72
  this.watch();
73
73
  this.indicator.place();
74
74
  };
75
+ this.namedByHeading = false;
76
+ this.nameByHeading = () => {
77
+ const slot = this.renderRoot?.querySelector?.('slot[name="heading"]');
78
+ this.namedByHeading = reflectElements(this.partNamed('nav'), 'ariaLabelledByElements', slot?.assignedElements({ flatten: true }) ?? [], this.namedByHeading);
79
+ };
75
80
  }
81
+ static { this.shadowRootOptions = {
82
+ ...HelixElement.shadowRootOptions,
83
+ slotAssignment: 'manual',
84
+ }; }
76
85
  get current() {
77
86
  return this.reading;
78
87
  }
79
88
  get links() {
80
- const slot = this.renderRoot?.querySelector('slot');
81
- const fragment = (element) => element instanceof HTMLAnchorElement && element.getAttribute('href')?.startsWith('#') === true;
82
- return (slot?.assignedElements({ flatten: true }) ?? []).flatMap((element) => fragment(element)
89
+ const slots = [
90
+ ...(this.renderRoot?.querySelectorAll?.('slot[data-link], nav > slot:not([role])') ?? []),
91
+ ];
92
+ return slots
93
+ .flatMap((slot) => slot.assignedElements({ flatten: true }))
94
+ .flatMap((element) => isFragment(element)
83
95
  ? [element]
84
- : [...element.querySelectorAll('a[href^="#"]')].filter(fragment));
96
+ : [...element.querySelectorAll('a[href^="#"]')].filter(isFragment));
97
+ }
98
+ get content() {
99
+ return this.knownChildren.filter((child) => child.slot !== 'heading');
100
+ }
101
+ get run() {
102
+ const content = this.content;
103
+ if (this.assignsByName || content.length === 0 || !content.every(isFragment)) {
104
+ return undefined;
105
+ }
106
+ return content;
85
107
  }
86
108
  get sections() {
87
109
  return this.links.flatMap((link) => {
@@ -118,9 +140,26 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
118
140
  if (next !== this.reading) {
119
141
  this.reading = next;
120
142
  this.indicator.place();
143
+ if (this.follow && active) {
144
+ this.reveal(active.link);
145
+ }
121
146
  this.dispatchEvent(new HelixActivateEvent({ value: next }));
122
147
  }
123
148
  }
149
+ reveal(link) {
150
+ const sections = this.scroller;
151
+ const page = this.page;
152
+ for (let node = link.parentElement; node && node !== page; node = node.parentElement) {
153
+ const { overflowY } = getComputedStyle(node);
154
+ if (!/auto|scroll|overlay/.test(overflowY) || node.scrollHeight <= node.clientHeight) {
155
+ continue;
156
+ }
157
+ if (node !== sections && !node.contains(sections)) {
158
+ scrollItemIntoView(link, { scroller: node });
159
+ }
160
+ return;
161
+ }
162
+ }
124
163
  get atEnd() {
125
164
  const doc = this.scroller;
126
165
  return Math.ceil(doc.scrollTop + doc.clientHeight) >= doc.scrollHeight - 1;
@@ -163,6 +202,8 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
163
202
  }
164
203
  disconnectedCallback() {
165
204
  super.disconnectedCallback();
205
+ this.childWatch?.disconnect();
206
+ this.childWatch = undefined;
166
207
  this.removeEventListener('click', this.handleClick);
167
208
  document.removeEventListener('scroll', this.handleScroll, true);
168
209
  if (this.frame !== 0) {
@@ -183,6 +224,12 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
183
224
  }
184
225
  connectedCallback() {
185
226
  super.connectedCallback();
227
+ this.childWatch = new MutationObserver(() => this.requestUpdate());
228
+ this.childWatch.observe(this, {
229
+ attributeFilter: ['data-level', 'href', 'slot'],
230
+ childList: true,
231
+ subtree: true,
232
+ });
186
233
  this.addEventListener('click', this.handleClick);
187
234
  document.addEventListener('scroll', this.handleScroll, {
188
235
  capture: true,
@@ -195,9 +242,32 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
195
242
  firstUpdated(changed) {
196
243
  super.firstUpdated(changed);
197
244
  this.watch();
245
+ this.finishHydration();
246
+ }
247
+ assign() {
248
+ if (this.assignsByName) {
249
+ return;
250
+ }
251
+ const root = this.renderRoot;
252
+ const heading = root?.querySelector?.('slot[name="heading"]');
253
+ heading?.assign?.(...this.knownChildren.filter((child) => child.slot === 'heading'));
254
+ const run = this.run;
255
+ if (run) {
256
+ for (const slot of root?.querySelectorAll?.('slot[data-link]') ?? []) {
257
+ const link = run[Number(slot.dataset.link)];
258
+ if (link) {
259
+ slot.assign(link);
260
+ }
261
+ }
262
+ return;
263
+ }
264
+ const plain = root?.querySelector?.('nav > slot:not([role])');
265
+ plain?.assign?.(...[...this.childNodes].filter((node) => node instanceof Element ? node.slot !== 'heading' : node instanceof Text));
198
266
  }
199
267
  updated(changed) {
200
268
  super.updated(changed);
269
+ this.assign();
270
+ this.nameByHeading();
201
271
  if (changed.has('offset')) {
202
272
  this.reach();
203
273
  this.mark();
@@ -208,15 +278,28 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
208
278
  this.indicator.place();
209
279
  }
210
280
  render() {
211
- return html `<nav part="nav" aria-label=${this.label}>
212
- <slot @slotchange=${this.handleSlotChange}></slot>
281
+ const run = this.run;
282
+ return html `<div part="heading"><slot name="heading" @slotchange=${this.nameByHeading}></slot></div>
283
+ <nav part="nav" aria-label=${this.label} @slotchange=${this.handleSlotChange}>
284
+ ${run ? this.renderList(nest(run.map((link) => levelOf(link.getAttribute('data-level'))))) : html `<slot></slot>`}
213
285
  <span part="indicator" aria-hidden="true"></span>
214
286
  </nav>`;
215
287
  }
288
+ renderList(branches) {
289
+ return html `<slot role="list"
290
+ >${branches.map((branch) => html `<slot role="listitem"
291
+ ><slot data-link=${branch.index}></slot
292
+ >${branch.children.length > 0 ? this.renderList(branch.children) : nothing}</slot
293
+ >`)}</slot
294
+ >`;
295
+ }
216
296
  };
217
297
  __decorate([
218
298
  property()
219
299
  ], HelixHeadlessOutline.prototype, "behavior", void 0);
300
+ __decorate([
301
+ property({ type: Boolean })
302
+ ], HelixHeadlessOutline.prototype, "follow", void 0);
220
303
  __decorate([
221
304
  property()
222
305
  ], HelixHeadlessOutline.prototype, "label", void 0);
@@ -1,4 +1,4 @@
1
- import { HelixElement } from '@fusion.dev/helix-core';
1
+ import { HelixFormElement } from '@fusion.dev/helix-core';
2
2
  import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
3
  import '../../option/index.js';
4
4
  import '../../select/index.js';
@@ -28,6 +28,11 @@ import '../../select/index.js';
28
28
  * Re-querying and returning to page one is up to you, because only you know
29
29
  * whether the current position should be kept.
30
30
  *
31
+ * It takes part in a form. Give it a `name` and the size in force is submitted
32
+ * under that name, so a `GET` form carries it in the address beside the page.
33
+ * A form reset returns it to `default-value`, and `disabled`, or a disabled
34
+ * `<fieldset>` around it, turns the select off.
35
+ *
31
36
  * @since 0.1.0
32
37
  * @status stable
33
38
  * @aliases Page size, Rows per page
@@ -44,6 +49,7 @@ import '../../select/index.js';
44
49
  * @csspart control-label - The label beside the number.
45
50
  * @csspart control-listbox - The list inside the panel.
46
51
  * @csspart control-listbox-empty - The text shown when the filter hides every option.
52
+ * @csspart control-listbox-error - The inner list's `error` part, shown while its `load-error` is set.
47
53
  * @csspart control-listbox-feedback - The inner list's message that says what is wrong with the value.
48
54
  * @csspart control-listbox-filter-icon - The icon beside the filter field.
49
55
  * @csspart control-listbox-filter-mark - The default icon beside the filter field, shown when none is slotted.
@@ -67,7 +73,7 @@ import '../../select/index.js';
67
73
  *
68
74
  * @cssstate empty - While there are no sizes to offer.
69
75
  */
70
- export declare class HelixHeadlessPaginationSize extends HelixElement {
76
+ export declare class HelixHeadlessPaginationSize extends HelixFormElement {
71
77
  /**
72
78
  * The size to start on when `value` is not set.
73
79
  */
@@ -115,6 +121,10 @@ export declare class HelixHeadlessPaginationSize extends HelixElement {
115
121
  * after it, and a refused size still sent it.
116
122
  */
117
123
  private readonly stopNative;
124
+ /** Returns to the size the markup declared, as a native `<select>` does on a reset. */
125
+ formResetCallback(): void;
126
+ /** Takes back the size a browser restores with the form, or the declared one. */
127
+ formStateRestoreCallback(state: string | File | FormData | null): void;
118
128
  protected willUpdate(changed: PropertyValues): void;
119
129
  /**
120
130
  * The control itself, which is the whole of this part, behind a seam the styled tier can move.
@@ -4,13 +4,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, formatNumbers, HelixBeforeChangeEvent, HelixElement, requestChange, } from '@fusion.dev/helix-core';
7
+ import { customElement, formatNumbers, HelixBeforeChangeEvent, HelixFormElement, requestChange, } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { html } from 'lit/static-html.js';
11
11
  import '../../option/index.js';
12
12
  import '../../select/index.js';
13
- let HelixHeadlessPaginationSize = class HelixHeadlessPaginationSize extends HelixElement {
13
+ let HelixHeadlessPaginationSize = class HelixHeadlessPaginationSize extends HelixFormElement {
14
14
  constructor() {
15
15
  super(...arguments);
16
16
  this.defaultValue = 0;
@@ -29,8 +29,8 @@ let HelixHeadlessPaginationSize = class HelixHeadlessPaginationSize extends Heli
29
29
  const previous = this.value;
30
30
  this.valueState = size;
31
31
  this.requestUpdate('value', previous);
32
- this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
33
- this.dispatchEvent(new Event('change', { bubbles: true, composed: false }));
32
+ this.announceInput();
33
+ this.announceChange();
34
34
  },
35
35
  event: (size) => new HelixBeforeChangeEvent({ value: String(size) }),
36
36
  host: this,
@@ -68,9 +68,23 @@ let HelixHeadlessPaginationSize = class HelixHeadlessPaginationSize extends Heli
68
68
  const options = this.options;
69
69
  return options.includes(this.value) ? this.value : (options[0] ?? 0);
70
70
  }
71
+ formResetCallback() {
72
+ super.formResetCallback();
73
+ const previous = this.value;
74
+ this.valueState = undefined;
75
+ this.requestUpdate('value', previous);
76
+ }
77
+ formStateRestoreCallback(state) {
78
+ const size = typeof state === 'string' ? Number.parseInt(state, 10) : Number.NaN;
79
+ const previous = this.value;
80
+ this.valueState = Number.isFinite(size) && size > 0 ? size : undefined;
81
+ this.requestUpdate('value', previous);
82
+ }
71
83
  willUpdate(changed) {
72
84
  super.willUpdate(changed);
73
85
  this.toggleState('empty', this.options.length === 0);
86
+ const current = this.current;
87
+ this.internals.setFormValue(current > 0 ? String(current) : null);
74
88
  }
75
89
  renderSelect() {
76
90
  const select = this.tag('select');
@@ -79,13 +93,14 @@ let HelixHeadlessPaginationSize = class HelixHeadlessPaginationSize extends Heli
79
93
  part="control"
80
94
  exportparts="feedback: control-feedback, hint: control-hint, indicator: control-indicator,
81
95
  label: control-label, listbox: control-listbox, listbox-empty: control-listbox-empty,
82
- listbox-feedback: control-listbox-feedback, listbox-filter-icon: control-listbox-filter-icon,
96
+ listbox-error: control-listbox-error, listbox-feedback: control-listbox-feedback, listbox-filter-icon: control-listbox-filter-icon,
83
97
  listbox-filter-mark: control-listbox-filter-mark, listbox-hint: control-listbox-hint,
84
98
  listbox-items: control-listbox-items, listbox-label: control-listbox-label,
85
99
  listbox-live: control-listbox-live, listbox-loading: control-listbox-loading, listbox-search: control-listbox-search,
86
100
  listbox-search-field: control-listbox-search-field, surface: control-surface,
87
101
  trigger: control-trigger, value: control-value"
88
102
  label=${this.label}
103
+ ?disabled=${this.isDisabled}
89
104
  .value=${String(this.current)}
90
105
  @helix-before-change=${this.handleChange}
91
106
  @input=${this.stopNative}
@@ -0,0 +1,10 @@
1
+ import type { PinCase, PinType } from './root/pin-input-root.js';
2
+ /**
3
+ * Typed or pasted text as the code's characters: trimmed, with what `type`
4
+ * refuses dropped, and its letters cased as `letterCase` asks. Web Awesome's
5
+ * OTP input takes the same `case`.
6
+ *
7
+ * `numeric` keeps digits, `alphanumeric` keeps ASCII letters and digits, and
8
+ * `text` keeps everything.
9
+ */
10
+ export declare const cleanCode: (text: string, type: PinType, letterCase?: PinCase) => string;
@@ -0,0 +1,13 @@
1
+ export const cleanCode = (text, type, letterCase = 'preserve') => {
2
+ const trimmed = text.trim();
3
+ const kept = type === 'numeric'
4
+ ? trimmed.replace(/\D/g, '')
5
+ : type === 'alphanumeric'
6
+ ? trimmed.replace(/[^0-9A-Za-z]/g, '')
7
+ : trimmed;
8
+ return letterCase === 'upper'
9
+ ? kept.toUpperCase()
10
+ : letterCase === 'lower'
11
+ ? kept.toLowerCase()
12
+ : kept;
13
+ };