@fusion.dev/helix-headless 0.143.0 → 0.145.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 (314) hide show
  1. package/custom-elements.json +16990 -1383
  2. package/dist/accordion/item/accordion-item.d.ts +48 -12
  3. package/dist/accordion/item/accordion-item.js +26 -5
  4. package/dist/accordion/root/accordion-root.d.ts +6 -0
  5. package/dist/accordion/root/accordion-root.js +7 -1
  6. package/dist/announcement/root/announcement-root.d.ts +21 -1
  7. package/dist/announcement/root/announcement-root.js +36 -1
  8. package/dist/app-header/root/app-header-root.d.ts +7 -0
  9. package/dist/app-header/root/app-header-root.js +5 -1
  10. package/dist/app-header/sectioning.d.ts +7 -0
  11. package/dist/app-header/sectioning.js +26 -0
  12. package/dist/area-chart/root/area-chart-root.d.ts +12 -4
  13. package/dist/area-chart/root/area-chart-root.js +24 -7
  14. package/dist/avatar/root/avatar-root.d.ts +14 -0
  15. package/dist/avatar/root/avatar-root.js +23 -9
  16. package/dist/badge/root/badge-root.d.ts +11 -7
  17. package/dist/badge/root/badge-root.js +1 -1
  18. package/dist/bar-chart/root/bar-chart-root.d.ts +12 -3
  19. package/dist/bar-chart/root/bar-chart-root.js +17 -10
  20. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +32 -1
  21. package/dist/breadcrumbs/root/breadcrumbs-root.js +25 -2
  22. package/dist/button/root/button-root.d.ts +47 -1
  23. package/dist/button/root/button-root.js +38 -4
  24. package/dist/callout/root/callout-root.d.ts +18 -1
  25. package/dist/callout/root/callout-root.js +15 -4
  26. package/dist/canvas/root/canvas-root.d.ts +38 -3
  27. package/dist/canvas/root/canvas-root.js +121 -40
  28. package/dist/card/root/card-root.d.ts +30 -1
  29. package/dist/card/root/card-root.js +38 -5
  30. package/dist/carousel/root/carousel-root.d.ts +90 -0
  31. package/dist/carousel/root/carousel-root.js +134 -12
  32. package/dist/chat/composer/chat-composer.d.ts +38 -6
  33. package/dist/chat/composer/chat-composer.js +42 -9
  34. package/dist/chat/message/chat-message.d.ts +32 -1
  35. package/dist/chat/message/chat-message.js +25 -9
  36. package/dist/chat/naming.d.ts +22 -0
  37. package/dist/chat/naming.js +12 -0
  38. package/dist/chat/relative-clock.d.ts +39 -0
  39. package/dist/chat/relative-clock.js +31 -0
  40. package/dist/chat/root/chat-root.d.ts +36 -0
  41. package/dist/chat/root/chat-root.js +24 -1
  42. package/dist/checkbox/root/checkbox-root.js +2 -0
  43. package/dist/checkbox-group/root/checkbox-group-root.d.ts +53 -3
  44. package/dist/checkbox-group/root/checkbox-group-root.js +71 -22
  45. package/dist/code-block/lines.d.ts +13 -0
  46. package/dist/code-block/lines.js +2 -0
  47. package/dist/code-block/root/code-block-root.d.ts +38 -1
  48. package/dist/code-block/root/code-block-root.js +28 -7
  49. package/dist/color-input/root/color-input-root.d.ts +34 -1
  50. package/dist/color-input/root/color-input-root.js +23 -4
  51. package/dist/color-picker/channels.d.ts +26 -0
  52. package/dist/color-picker/channels.js +84 -0
  53. package/dist/color-picker/root/color-picker-root.d.ts +81 -5
  54. package/dist/color-picker/root/color-picker-root.js +103 -10
  55. package/dist/combobox/root/combobox-root.d.ts +38 -3
  56. package/dist/combobox/root/combobox-root.js +64 -2
  57. package/dist/command/root/command-root.d.ts +30 -4
  58. package/dist/command/root/command-root.js +33 -3
  59. package/dist/copy-button/from.d.ts +22 -0
  60. package/dist/copy-button/from.js +28 -0
  61. package/dist/copy-button/root/copy-button-root.d.ts +47 -1
  62. package/dist/copy-button/root/copy-button-root.js +44 -8
  63. package/dist/data-table/root/data-table-root.d.ts +150 -2
  64. package/dist/data-table/root/data-table-root.js +303 -29
  65. package/dist/data-table/tree.d.ts +40 -0
  66. package/dist/data-table/tree.js +55 -0
  67. package/dist/date-input/root/date-input-root.d.ts +73 -4
  68. package/dist/date-input/root/date-input-root.js +93 -43
  69. package/dist/date-picker/root/date-picker-root.d.ts +145 -1
  70. package/dist/date-picker/root/date-picker-root.js +239 -48
  71. package/dist/description-list/item/description-list-item.d.ts +17 -2
  72. package/dist/description-list/item/description-list-item.js +15 -2
  73. package/dist/description-list/root/description-list-root.d.ts +27 -1
  74. package/dist/description-list/root/description-list-root.js +26 -1
  75. package/dist/dialog/description.d.ts +9 -0
  76. package/dist/dialog/description.js +14 -0
  77. package/dist/dialog/root/dialog-root.d.ts +64 -1
  78. package/dist/dialog/root/dialog-root.js +49 -23
  79. package/dist/donut-chart/root/donut-chart-root.d.ts +25 -4
  80. package/dist/donut-chart/root/donut-chart-root.js +21 -6
  81. package/dist/drawer/root/drawer-root.d.ts +63 -2
  82. package/dist/drawer/root/drawer-root.js +95 -19
  83. package/dist/drawer/swipe-start.d.ts +9 -0
  84. package/dist/drawer/swipe-start.js +12 -0
  85. package/dist/drilldown/item/drilldown-item.d.ts +11 -6
  86. package/dist/drilldown/item/drilldown-item.js +8 -5
  87. package/dist/drilldown/root/drilldown-root.d.ts +11 -0
  88. package/dist/drilldown/root/drilldown-root.js +8 -3
  89. package/dist/element-events.generated.js +61 -13
  90. package/dist/empty/root/empty-root.d.ts +1 -1
  91. package/dist/file-input/root/file-input-root.d.ts +47 -0
  92. package/dist/file-input/root/file-input-root.js +60 -22
  93. package/dist/file-input/screen.d.ts +35 -0
  94. package/dist/file-input/screen.js +49 -0
  95. package/dist/filter-input/menu/filter-menu.d.ts +3 -0
  96. package/dist/filter-input/menu/filter-menu.js +8 -4
  97. package/dist/filter-input/root/filter-input-root.d.ts +38 -4
  98. package/dist/filter-input/root/filter-input-root.js +67 -3
  99. package/dist/filter-input/suggest.d.ts +17 -0
  100. package/dist/filter-input/suggest.js +23 -0
  101. package/dist/format-date/options.d.ts +33 -0
  102. package/dist/format-date/options.js +42 -0
  103. package/dist/format-date/root/format-date-root.d.ts +51 -0
  104. package/dist/format-date/root/format-date-root.js +58 -38
  105. package/dist/heatmap/root/heatmap-root.d.ts +23 -6
  106. package/dist/heatmap/root/heatmap-root.js +24 -13
  107. package/dist/icon/index.d.ts +2 -0
  108. package/dist/icon/index.js +2 -0
  109. package/dist/icon/registry.d.ts +37 -0
  110. package/dist/icon/registry.js +83 -0
  111. package/dist/icon/root/icon-root.d.ts +30 -4
  112. package/dist/icon/root/icon-root.js +41 -6
  113. package/dist/icon/root/index.d.ts +2 -0
  114. package/dist/icon/root/index.js +2 -0
  115. package/dist/icon/set.d.ts +1 -0
  116. package/dist/icon/set.js +3 -0
  117. package/dist/image/root/image-root.d.ts +14 -0
  118. package/dist/image/root/image-root.js +23 -2
  119. package/dist/input/root/input-root.d.ts +11 -0
  120. package/dist/input/root/input-root.js +16 -1
  121. package/dist/kbd/root/kbd-root.d.ts +20 -13
  122. package/dist/kbd/root/kbd-root.js +35 -37
  123. package/dist/kbd/shortcut.d.ts +18 -0
  124. package/dist/kbd/shortcut.js +35 -0
  125. package/dist/legend/hidden-series.d.ts +8 -0
  126. package/dist/legend/hidden-series.js +13 -0
  127. package/dist/legend/root/legend-root.d.ts +56 -0
  128. package/dist/legend/root/legend-root.js +54 -5
  129. package/dist/lightbox/item/lightbox-item.d.ts +15 -0
  130. package/dist/lightbox/item/lightbox-item.js +10 -1
  131. package/dist/lightbox/root/lightbox-root.d.ts +61 -1
  132. package/dist/lightbox/root/lightbox-root.js +94 -4
  133. package/dist/line-chart/root/line-chart-root.d.ts +11 -3
  134. package/dist/line-chart/root/line-chart-root.js +19 -5
  135. package/dist/list/item/list-item.d.ts +68 -1
  136. package/dist/list/item/list-item.js +66 -4
  137. package/dist/list/numbering.d.ts +14 -0
  138. package/dist/list/numbering.js +8 -0
  139. package/dist/list/root/list-root.d.ts +106 -9
  140. package/dist/list/root/list-root.js +189 -4
  141. package/dist/list/selection.d.ts +19 -0
  142. package/dist/list/selection.js +21 -0
  143. package/dist/listbox/root/listbox-root.d.ts +74 -3
  144. package/dist/listbox/root/listbox-root.js +86 -9
  145. package/dist/loader/busy.d.ts +17 -0
  146. package/dist/loader/busy.js +24 -0
  147. package/dist/loader/root/loader-root.d.ts +37 -0
  148. package/dist/loader/root/loader-root.js +52 -3
  149. package/dist/map/root/map-root.d.ts +6 -2
  150. package/dist/map/root/map-root.js +5 -2
  151. package/dist/megamenu/root/megamenu-root.d.ts +21 -0
  152. package/dist/megamenu/root/megamenu-root.js +16 -2
  153. package/dist/menu/index.d.ts +1 -0
  154. package/dist/menu/index.js +1 -0
  155. package/dist/menu/item/menu-item.d.ts +38 -11
  156. package/dist/menu/item/menu-item.js +33 -1
  157. package/dist/menu/root/menu-root.d.ts +25 -1
  158. package/dist/menu/root/menu-root.js +63 -6
  159. package/dist/menu/section/menu-section.d.ts +55 -0
  160. package/dist/menu/section/menu-section.js +60 -0
  161. package/dist/meter/root/meter-root.d.ts +7 -0
  162. package/dist/meter/root/meter-root.js +6 -3
  163. package/dist/navigation/root/navigation-root.d.ts +16 -1
  164. package/dist/navigation/root/navigation-root.js +27 -1
  165. package/dist/notifications/feed-keys.d.ts +10 -0
  166. package/dist/notifications/feed-keys.js +14 -0
  167. package/dist/notifications/notification/notification.d.ts +17 -0
  168. package/dist/notifications/notification/notification.js +21 -2
  169. package/dist/notifications/root/notifications-root.d.ts +60 -0
  170. package/dist/notifications/root/notifications-root.js +76 -1
  171. package/dist/number-input/root/number-input-root.d.ts +47 -0
  172. package/dist/number-input/root/number-input-root.js +58 -22
  173. package/dist/object-inspector/root/object-inspector-root.d.ts +98 -16
  174. package/dist/object-inspector/root/object-inspector-root.js +180 -80
  175. package/dist/object-inspector/rows.d.ts +57 -0
  176. package/dist/object-inspector/rows.js +86 -0
  177. package/dist/option/root/option-root.d.ts +54 -5
  178. package/dist/option/root/option-root.js +34 -5
  179. package/dist/outline/levels.d.ts +31 -0
  180. package/dist/outline/levels.js +23 -0
  181. package/dist/outline/root/outline-root.d.ts +68 -3
  182. package/dist/outline/root/outline-root.js +73 -8
  183. package/dist/pagination/size/pagination-size.d.ts +11 -2
  184. package/dist/pagination/size/pagination-size.js +19 -4
  185. package/dist/pin-input/format.d.ts +7 -0
  186. package/dist/pin-input/format.js +16 -0
  187. package/dist/pin-input/root/pin-input-root.d.ts +21 -0
  188. package/dist/pin-input/root/pin-input-root.js +29 -2
  189. package/dist/popover/root/popover-root.d.ts +62 -4
  190. package/dist/popover/root/popover-root.js +53 -5
  191. package/dist/progress/root/progress-root.d.ts +7 -0
  192. package/dist/progress/root/progress-root.js +9 -3
  193. package/dist/qr-code/export.d.ts +31 -0
  194. package/dist/qr-code/export.js +10 -0
  195. package/dist/qr-code/root/qr-code-root.d.ts +26 -0
  196. package/dist/qr-code/root/qr-code-root.js +49 -2
  197. package/dist/radio/group/radio-group.d.ts +17 -0
  198. package/dist/radio/group/radio-group.js +14 -1
  199. package/dist/rating/root/rating-root.d.ts +25 -0
  200. package/dist/rating/root/rating-root.js +46 -4
  201. package/dist/scatter-chart/root/scatter-chart-root.d.ts +17 -3
  202. package/dist/scatter-chart/root/scatter-chart-root.js +25 -7
  203. package/dist/scroll-area/root/scroll-area-root.d.ts +113 -0
  204. package/dist/scroll-area/root/scroll-area-root.js +218 -3
  205. package/dist/scroll-area/scrollbar.d.ts +65 -0
  206. package/dist/scroll-area/scrollbar.js +24 -0
  207. package/dist/select/root/select-root.d.ts +24 -0
  208. package/dist/select/root/select-root.js +21 -7
  209. package/dist/skeleton/root/skeleton-root.d.ts +25 -0
  210. package/dist/skeleton/root/skeleton-root.js +17 -2
  211. package/dist/slider/root/slider-root.d.ts +39 -0
  212. package/dist/slider/root/slider-root.js +45 -9
  213. package/dist/splitter/root/splitter-root.d.ts +13 -0
  214. package/dist/splitter/root/splitter-root.js +20 -2
  215. package/dist/splitter/snap.d.ts +9 -0
  216. package/dist/splitter/snap.js +12 -0
  217. package/dist/stat/root/stat-root.d.ts +2 -0
  218. package/dist/stat/root/stat-root.js +1 -1
  219. package/dist/steps/reach.d.ts +15 -0
  220. package/dist/steps/reach.js +12 -0
  221. package/dist/steps/root/steps-root.d.ts +30 -0
  222. package/dist/steps/root/steps-root.js +53 -0
  223. package/dist/steps/step/step.d.ts +39 -2
  224. package/dist/steps/step/step.js +29 -7
  225. package/dist/switch/root/switch-root.d.ts +12 -2
  226. package/dist/switch/root/switch-root.js +6 -1
  227. package/dist/table/column/table-column.d.ts +11 -0
  228. package/dist/table/column/table-column.js +6 -1
  229. package/dist/table/root/table-root.d.ts +101 -0
  230. package/dist/table/root/table-root.js +166 -5
  231. package/dist/table/row/table-row.d.ts +47 -1
  232. package/dist/table/row/table-row.js +39 -2
  233. package/dist/tabs/panel/tab-panel.d.ts +9 -5
  234. package/dist/tabs/panel/tab-panel.js +3 -4
  235. package/dist/tabs/root/tabs-root.d.ts +25 -1
  236. package/dist/tabs/root/tabs-root.js +21 -3
  237. package/dist/tabs/tab/tab.d.ts +36 -4
  238. package/dist/tabs/tab/tab.js +46 -3
  239. package/dist/tag/group/tag-group.d.ts +107 -0
  240. package/dist/tag/group/tag-group.js +157 -0
  241. package/dist/tag/index.d.ts +1 -0
  242. package/dist/tag/index.js +1 -0
  243. package/dist/tag/root/tag-root.d.ts +60 -0
  244. package/dist/tag/root/tag-root.js +74 -18
  245. package/dist/tag/selection.d.ts +10 -0
  246. package/dist/tag/selection.js +12 -0
  247. package/dist/textarea/grow.d.ts +22 -0
  248. package/dist/textarea/grow.js +20 -0
  249. package/dist/textarea/root/textarea-root.d.ts +9 -0
  250. package/dist/textarea/root/textarea-root.js +9 -1
  251. package/dist/time-input/root/time-input-root.d.ts +44 -5
  252. package/dist/time-input/root/time-input-root.js +69 -39
  253. package/dist/time-picker/root/time-picker-root.d.ts +54 -8
  254. package/dist/time-picker/root/time-picker-root.js +121 -91
  255. package/dist/timeline/group/timeline-group.d.ts +46 -0
  256. package/dist/timeline/group/timeline-group.js +37 -0
  257. package/dist/timeline/index.d.ts +1 -0
  258. package/dist/timeline/index.js +1 -0
  259. package/dist/timeline/root/timeline-root.d.ts +10 -3
  260. package/dist/toast/hotkey.d.ts +22 -0
  261. package/dist/toast/hotkey.js +23 -0
  262. package/dist/toast/item/toast-item.d.ts +26 -18
  263. package/dist/toast/item/toast-item.js +55 -48
  264. package/dist/toast/root/toast-root.d.ts +42 -8
  265. package/dist/toast/root/toast-root.js +64 -4
  266. package/dist/toggle/group/toggle-group.d.ts +57 -15
  267. package/dist/toggle/group/toggle-group.js +69 -9
  268. package/dist/toggle/root/toggle-root.d.ts +12 -1
  269. package/dist/toggle/root/toggle-root.js +11 -3
  270. package/dist/toolbar/root/toolbar-root.d.ts +13 -0
  271. package/dist/toolbar/root/toolbar-root.js +15 -5
  272. package/dist/tooltip/focus-target.d.ts +6 -0
  273. package/dist/tooltip/focus-target.js +9 -0
  274. package/dist/tooltip/root/tooltip-root.d.ts +42 -17
  275. package/dist/tooltip/root/tooltip-root.js +50 -9
  276. package/dist/tour/naming.d.ts +30 -0
  277. package/dist/tour/naming.js +10 -0
  278. package/dist/tour/root/tour-root.d.ts +35 -0
  279. package/dist/tour/root/tour-root.js +56 -5
  280. package/dist/tour/stop/tour-stop.d.ts +53 -8
  281. package/dist/tour/stop/tour-stop.js +45 -15
  282. package/dist/tour/wait.d.ts +11 -0
  283. package/dist/tour/wait.js +19 -0
  284. package/dist/tree/item/tree-item.d.ts +54 -1
  285. package/dist/tree/item/tree-item.js +89 -20
  286. package/dist/tree/root/tree-root.d.ts +37 -0
  287. package/dist/tree/root/tree-root.js +51 -6
  288. package/dist/tree/selection.d.ts +24 -0
  289. package/dist/tree/selection.js +26 -0
  290. package/dist/trend/root/trend-root.d.ts +13 -0
  291. package/dist/trend/root/trend-root.js +14 -9
  292. package/dist/video/media-events.d.ts +49 -0
  293. package/dist/video/media-events.js +39 -0
  294. package/dist/video/root/video-root.d.ts +99 -13
  295. package/dist/video/root/video-root.js +84 -47
  296. package/dist/video-playlist/menu/video-playlist-menu.d.ts +3 -0
  297. package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
  298. package/dist/video-playlist/order.d.ts +41 -0
  299. package/dist/video-playlist/order.js +34 -0
  300. package/dist/video-playlist/root/video-playlist-root.d.ts +70 -2
  301. package/dist/video-playlist/root/video-playlist-root.js +60 -16
  302. package/dist/virtual-list/controls.d.ts +4 -0
  303. package/dist/virtual-list/controls.js +30 -0
  304. package/dist/virtual-list/fill.d.ts +30 -0
  305. package/dist/virtual-list/fill.js +39 -0
  306. package/dist/virtual-list/keys.d.ts +15 -0
  307. package/dist/virtual-list/keys.js +24 -0
  308. package/dist/virtual-list/root/virtual-list-root.d.ts +155 -23
  309. package/dist/virtual-list/root/virtual-list-root.js +175 -34
  310. package/lint.json +1 -1
  311. package/package.json +5 -5
  312. package/types/jsx.d.ts +8871 -1286
  313. package/types/svelte.d.ts +3 -0
  314. package/types/vue.d.ts +1918 -269
@@ -0,0 +1,57 @@
1
+ /** One visible line of the tree: a value, where it sits, and whether it is open. */
2
+ export interface Row {
3
+ branch: boolean;
4
+ depth: number;
5
+ /** The property name or array index this value is under; absent for the root. */
6
+ key?: string;
7
+ /** Whether its key or its value holds what the tree is filtered by. */
8
+ match: boolean;
9
+ open: boolean;
10
+ path: string;
11
+ position: number;
12
+ siblings: number;
13
+ value: unknown;
14
+ }
15
+ /** Whether a value has entries of its own, an object or an array. */
16
+ export declare const branchy: (value: unknown) => value is object;
17
+ /** A branch's entries, an array's under its indexes. */
18
+ export declare const entries: (value: object) => [string, unknown][];
19
+ /** A child's path under its parent's, escaped the way a JSON Pointer escapes one. */
20
+ export declare const child: (path: string, key: string) => string;
21
+ /**
22
+ * Every branch a path sits inside, from the root down, and not the path itself.
23
+ *
24
+ * The root's path is the empty string, so every other path has it first. A
25
+ * key's own `/` is escaped as `~1`, so cutting at each `/` cuts between keys.
26
+ */
27
+ export declare const ancestorsOf: (path: string) => string[];
28
+ /**
29
+ * The paths of every entry whose key or value holds `query`, ignoring case,
30
+ * across the whole value and not only the open branches.
31
+ *
32
+ * A branch is matched by its key alone: its brackets and its count are drawn,
33
+ * and they are not what a reader is looking for.
34
+ */
35
+ export declare const search: (root: unknown, query: string) => Set<string>;
36
+ /**
37
+ * Every visible row, in order, for a value with `opened` branches open.
38
+ *
39
+ * With `found`, the paths a filter matched, a branch shows only the entries
40
+ * that matched or lead to one, and a matched entry shows everything under it.
41
+ * Position and set size count the entries shown, which is the set a reader
42
+ * hears.
43
+ */
44
+ export declare const flatten: (root: unknown, opened: Set<string>, found?: Set<string>) => Row[];
45
+ /**
46
+ * Which branches are open for a value, by path.
47
+ *
48
+ * Every branch shallower than `depth` opens. A branch in `known`, the
49
+ * branches of the last value, keeps whether it was in `opened`, so a new value
50
+ * keeps the branches a reader opened or closed and only a new one takes the
51
+ * default. Returns the open branches and every branch, for the next value to
52
+ * be told apart from this one.
53
+ */
54
+ export declare const seed: (root: unknown, depth: number, known?: Set<string>, opened?: Set<string>) => {
55
+ branches: Set<string>;
56
+ opened: Set<string>;
57
+ };
@@ -0,0 +1,86 @@
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
+ };
@@ -1,4 +1,4 @@
1
- import { HelixElement } from '@fusion.dev/helix-core';
1
+ import { HelixElement, type LinkModifiers } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * The options among some elements, reaching one level into a group.
@@ -34,6 +34,9 @@ export declare const optionsAmong: (elements: readonly Element[]) => HelixHeadle
34
34
  * Filtering belongs to the owner, which hides an option with `hidden`. What
35
35
  * counts as a match differs between a select and a command palette.
36
36
  *
37
+ * An option with `href` is a link: choosing it follows the link and selects
38
+ * nothing, as an entry in a command palette that goes to a page does.
39
+ *
37
40
  * @since 0.1.0
38
41
  * @status stable
39
42
  * @category forms
@@ -44,7 +47,7 @@ export declare const optionsAmong: (elements: readonly Element[]) => HelixHeadle
44
47
  * @slot end - Content after the label, such as a shortcut or a count.
45
48
  * @slot start - Content before the label, such as an icon or an avatar.
46
49
  *
47
- * @event {HelixSelectEvent} helix-select - The option was chosen. Cancelable: canceling it leaves the selection as it was.
50
+ * @event {HelixSelectEvent} helix-select - The option was chosen. Cancelable: canceling it leaves the selection as it was, and keeps the browser from following an option's `href`.
48
51
  *
49
52
  * @csspart description - The description under the label, when there is one.
50
53
  * @csspart item - The row.
@@ -148,6 +151,28 @@ export declare class HelixHeadlessOption extends HelixElement {
148
151
  * Prevents the option from being chosen.
149
152
  */
150
153
  disabled: boolean;
154
+ /**
155
+ * Whether following the link downloads what it names. Only meaningful
156
+ * beside an `href`.
157
+ */
158
+ download: boolean;
159
+ /**
160
+ * The URL the option links to. Setting it makes the option a link: choosing
161
+ * it, with a press, Enter or a click, follows the link and selects nothing,
162
+ * as React Aria's items with `href` do. Command, Control and Shift open it
163
+ * where they open a link, in a new tab or a new window.
164
+ *
165
+ * `helix-select` is said first with the `href` in it, and canceling it keeps
166
+ * the browser from following, so a client-side router follows it instead.
167
+ *
168
+ * **The option stays an option**, and an anchor is never drawn inside it:
169
+ * `role="option"` sits on the host, where a listbox's
170
+ * `aria-activedescendant` can name it, and an `<a>` inside would be exposed
171
+ * as a link within the option and take its name (Menu Item's `href` records
172
+ * the measurement). The link is followed through an anchor made for the
173
+ * moment, which `followLink` describes.
174
+ */
175
+ href: string;
151
176
  /**
152
177
  * A shorter name for this choice, for where the row does not fit.
153
178
  *
@@ -161,12 +186,30 @@ export declare class HelixHeadlessOption extends HelixElement {
161
186
  * nothing for every choice.
162
187
  */
163
188
  label: string;
189
+ /**
190
+ * The link's `rel`. Set it whenever you set `target="_blank"`.
191
+ */
192
+ rel: string;
164
193
  /**
165
194
  * Whether this choice is currently chosen.
166
195
  *
167
196
  * Written by the listbox.
168
197
  */
169
198
  selected: boolean;
199
+ /**
200
+ * Where the link opens, such as `_blank`, for an option with an `href`.
201
+ */
202
+ target: string;
203
+ /**
204
+ * The words a reader types to reach this choice, where the row's own words
205
+ * are not them: typeahead, a filter and a combobox match against it, and a
206
+ * select's trigger shows it.
207
+ *
208
+ * For a row whose content is a picture, an avatar beside a name and a role,
209
+ * or a name written in another script, as React Aria's `textValue`. Empty
210
+ * reads the row's words, then `label`.
211
+ */
212
+ textValue: string;
170
213
  /**
171
214
  * The value that identifies the option, and that a form submits.
172
215
  */
@@ -210,11 +253,17 @@ export declare class HelixHeadlessOption extends HelixElement {
210
253
  * it is in every comparison too.
211
254
  *
212
255
  * **The `label` where the slot holds nothing**, which is what the row shows
213
- * then, so the six read the words a reader sees.
256
+ * then, so the six read the words a reader sees. `text-value` wins over both,
257
+ * being the consumer saying outright what the words are.
214
258
  */
215
259
  get text(): string;
216
- /** Sends `helix-select` for this option. Does nothing when it is disabled. */
217
- select(): void;
260
+ /**
261
+ * Sends `helix-select` for this option. Does nothing when it is disabled.
262
+ *
263
+ * An option with an `href` then follows its link, unless the event was
264
+ * canceled, with the keys `modifiers` names held.
265
+ */
266
+ select(modifiers?: LinkModifiers): void;
218
267
  /**
219
268
  * Mirrors the chosen state where a stylesheet can select on it.
220
269
  *
@@ -4,7 +4,7 @@ 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, HelixElement, HelixSelectEvent } from '@fusion.dev/helix-core';
7
+ import { customElement, followLink, HelixElement, HelixSelectEvent, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  export const optionsAmong = (elements) => {
@@ -18,8 +18,13 @@ let HelixHeadlessOption = class HelixHeadlessOption extends HelixElement {
18
18
  this.alternate = '';
19
19
  this.alternateLabel = '';
20
20
  this.disabled = false;
21
+ this.download = false;
22
+ this.href = '';
21
23
  this.label = '';
24
+ this.rel = '';
22
25
  this.selected = false;
26
+ this.target = '';
27
+ this.textValue = '';
23
28
  this.value = '';
24
29
  }
25
30
  get defaultSelected() {
@@ -29,17 +34,26 @@ let HelixHeadlessOption = class HelixHeadlessOption extends HelixElement {
29
34
  this.toggleAttribute('selected', next);
30
35
  }
31
36
  get text() {
37
+ if (this.textValue !== '') {
38
+ return this.textValue;
39
+ }
32
40
  return ([...this.childNodes]
33
41
  .filter((node) => node.nodeType === Node.TEXT_NODE || (node instanceof Element && node.slot === ''))
34
42
  .map((node) => node.textContent ?? '')
35
43
  .join('')
36
44
  .trim() || this.label);
37
45
  }
38
- select() {
46
+ select(modifiers) {
39
47
  if (this.disabled) {
40
48
  return;
41
49
  }
42
- this.dispatchEvent(new HelixSelectEvent({ value: this.value }));
50
+ if (this.href === '') {
51
+ this.dispatchEvent(new HelixSelectEvent({ value: this.value }));
52
+ return;
53
+ }
54
+ if (this.dispatchEvent(new HelixSelectEvent({ href: this.href, value: this.value }))) {
55
+ followLink(this.ownerDocument, this, modifiers);
56
+ }
43
57
  }
44
58
  willUpdate(changed) {
45
59
  super.willUpdate(changed);
@@ -48,13 +62,13 @@ let HelixHeadlessOption = class HelixHeadlessOption extends HelixElement {
48
62
  }
49
63
  render() {
50
64
  this.internals.role = 'option';
51
- this.internals.ariaSelected = String(this.selected);
65
+ this.internals.ariaSelected = this.href === '' ? String(this.selected) : null;
52
66
  this.internals.ariaDisabled = this.disabled ? 'true' : null;
53
67
  this.internals.ariaDescription =
54
68
  this.alternate !== '' && this.alternateLabel !== '' ? this.alternateLabel : null;
55
69
  return html `<div
56
70
  part="item${this.selected ? ' item-selected' : ''}${this.active ? ' item-active' : ''}"
57
- @click=${() => this.select()}
71
+ @click=${(event) => this.select(event)}
58
72
  >
59
73
  <slot name="start"></slot>
60
74
  <span part="text">
@@ -80,12 +94,27 @@ __decorate([
80
94
  __decorate([
81
95
  property({ reflect: true, type: Boolean })
82
96
  ], HelixHeadlessOption.prototype, "disabled", void 0);
97
+ __decorate([
98
+ property({ type: Boolean })
99
+ ], HelixHeadlessOption.prototype, "download", void 0);
100
+ __decorate([
101
+ property()
102
+ ], HelixHeadlessOption.prototype, "href", void 0);
83
103
  __decorate([
84
104
  property()
85
105
  ], HelixHeadlessOption.prototype, "label", void 0);
106
+ __decorate([
107
+ property()
108
+ ], HelixHeadlessOption.prototype, "rel", void 0);
86
109
  __decorate([
87
110
  property({ type: Boolean })
88
111
  ], HelixHeadlessOption.prototype, "selected", void 0);
112
+ __decorate([
113
+ property()
114
+ ], HelixHeadlessOption.prototype, "target", void 0);
115
+ __decorate([
116
+ property({ attribute: 'text-value' })
117
+ ], HelixHeadlessOption.prototype, "textValue", void 0);
89
118
  __decorate([
90
119
  property()
91
120
  ], HelixHeadlessOption.prototype, "value", void 0);
@@ -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,12 @@ 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`.
17
22
  *
18
23
  * The current section is decided by the headings' positions. An
19
24
  * `IntersectionObserver` with a band near the top fails both ways: when
@@ -27,13 +32,24 @@ import { type PropertyValues, type TemplateResult } from 'lit';
27
32
  * @documentation https://fusion.dev/helix/components/outline
28
33
  *
29
34
  * @slot - The links, as anchors with fragment hrefs, or lists holding them.
35
+ * @slot heading - A heading above the links, such as "On this page". It names the navigation.
30
36
  *
31
37
  * @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
38
  *
39
+ * @csspart heading - The box holding the `heading` slot, above the navigation.
33
40
  * @csspart indicator - The mark that moves to the section in view.
34
41
  * @csspart nav - The navigation landmark.
35
42
  */
36
43
  export declare class HelixHeadlessOutline extends HelixElement {
44
+ /**
45
+ * Manual, so each link in a flat run can be put in a list item of its own.
46
+ *
47
+ * The list items are slots, which lay out as nothing at all, so each link is
48
+ * still laid out as a child of the navigation exactly where it was, and the
49
+ * consumer's markup is not touched. A run holding anything but links, a
50
+ * `<ul>` among them, goes to one plain slot as before.
51
+ */
52
+ static shadowRootOptions: ShadowRootInit;
37
53
  private readonly indicator;
38
54
  /**
39
55
  * How the page scrolls to a section when its link is clicked.
@@ -58,7 +74,8 @@ export declare class HelixHeadlessOutline extends HelixElement {
58
74
  */
59
75
  behavior: ScrollBehavior;
60
76
  /**
61
- * The navigation's name, read by screen readers.
77
+ * The navigation's name, read by screen readers. A heading in the `heading`
78
+ * slot names it in place of this.
62
79
  */
63
80
  label: string;
64
81
  /**
@@ -92,6 +109,11 @@ export declare class HelixHeadlessOutline extends HelixElement {
92
109
  */
93
110
  private reading;
94
111
  private observer;
112
+ /**
113
+ * Watches the children, which a manual root is not told about: a link
114
+ * arriving, leaving or changing its level changes the lists drawn for them.
115
+ */
116
+ private childWatch;
95
117
  /**
96
118
  * The links, in document order. Empty on the server, which has no tree.
97
119
  *
@@ -101,6 +123,18 @@ export declare class HelixHeadlessOutline extends HelixElement {
101
123
  * nothing was announced, and nothing said why.
102
124
  */
103
125
  private get links();
126
+ /** The children that are the content, which is all of them but the heading. */
127
+ private get content();
128
+ /**
129
+ * The links of a flat run, which the render puts into lists, or `undefined`
130
+ * where the content is anything else.
131
+ *
132
+ * Anything else is a list a consumer wrote, which already has its levels, a
133
+ * slot forwarded from a component around this one, or a server's render and
134
+ * the hydration of one, which cannot know the children. A root a server sent
135
+ * routes by name, so it keeps the plain slot after hydration too.
136
+ */
137
+ private get run();
104
138
  /**
105
139
  * What each link points at, paired with the link, skipping anything missing.
106
140
  *
@@ -250,8 +284,39 @@ export declare class HelixHeadlessOutline extends HelixElement {
250
284
  private unland;
251
285
  connectedCallback(): void;
252
286
  protected firstUpdated(changed: PropertyValues): void;
287
+ /**
288
+ * Puts each child in its slot, on a root this element made.
289
+ *
290
+ * A link of a flat run goes to the slot inside its list item, and anything
291
+ * else, with the text between, to the plain slot. A root a server sent
292
+ * routes by name and needs none of it.
293
+ */
294
+ private assign;
295
+ /** Whether the heading is what names the navigation. */
296
+ private namedByHeading;
297
+ /**
298
+ * Names the navigation by what is in the `heading` slot, in place of `label`.
299
+ *
300
+ * An element reference, because the heading is in the light DOM and the
301
+ * `<nav>` in this shadow root, which an IDREF cannot cross. A browser without
302
+ * element reflection keeps `label`.
303
+ */
304
+ private readonly nameByHeading;
253
305
  protected updated(changed: PropertyValues): void;
254
306
  protected render(): TemplateResult;
307
+ /**
308
+ * A list for a run of links, with a list item for each, nested by level.
309
+ *
310
+ * **Slots, because a slot lays out as nothing.** A list and its items drawn
311
+ * as boxes would put each link inside an item, out of the navigation's own
312
+ * flex column, and every outline styled against that column would move. A
313
+ * slot is `display: contents`, keeps a role it is given, and shows its
314
+ * children while nothing is assigned to it, so the items nest in the
315
+ * accessibility tree and nowhere else. Measured in Chromium: every link at the
316
+ * same box with and without the lists, and each item at the level its
317
+ * nesting gives it.
318
+ */
319
+ protected renderList(branches: Branch[]): TemplateResult;
255
320
  }
256
321
  declare global {
257
322
  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, reflectElements, } 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);
@@ -72,16 +74,38 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
72
74
  this.watch();
73
75
  this.indicator.place();
74
76
  };
77
+ this.namedByHeading = false;
78
+ this.nameByHeading = () => {
79
+ const slot = this.renderRoot?.querySelector?.('slot[name="heading"]');
80
+ this.namedByHeading = reflectElements(this.partNamed('nav'), 'ariaLabelledByElements', slot?.assignedElements({ flatten: true }) ?? [], this.namedByHeading);
81
+ };
75
82
  }
83
+ static { this.shadowRootOptions = {
84
+ ...HelixElement.shadowRootOptions,
85
+ slotAssignment: 'manual',
86
+ }; }
76
87
  get current() {
77
88
  return this.reading;
78
89
  }
79
90
  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)
91
+ const slots = [
92
+ ...(this.renderRoot?.querySelectorAll?.('slot[data-link], nav > slot:not([role])') ?? []),
93
+ ];
94
+ return slots
95
+ .flatMap((slot) => slot.assignedElements({ flatten: true }))
96
+ .flatMap((element) => isFragment(element)
83
97
  ? [element]
84
- : [...element.querySelectorAll('a[href^="#"]')].filter(fragment));
98
+ : [...element.querySelectorAll('a[href^="#"]')].filter(isFragment));
99
+ }
100
+ get content() {
101
+ return this.knownChildren.filter((child) => child.slot !== 'heading');
102
+ }
103
+ get run() {
104
+ const content = this.content;
105
+ if (this.assignsByName || content.length === 0 || !content.every(isFragment)) {
106
+ return undefined;
107
+ }
108
+ return content;
85
109
  }
86
110
  get sections() {
87
111
  return this.links.flatMap((link) => {
@@ -163,6 +187,8 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
163
187
  }
164
188
  disconnectedCallback() {
165
189
  super.disconnectedCallback();
190
+ this.childWatch?.disconnect();
191
+ this.childWatch = undefined;
166
192
  this.removeEventListener('click', this.handleClick);
167
193
  document.removeEventListener('scroll', this.handleScroll, true);
168
194
  if (this.frame !== 0) {
@@ -183,6 +209,12 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
183
209
  }
184
210
  connectedCallback() {
185
211
  super.connectedCallback();
212
+ this.childWatch = new MutationObserver(() => this.requestUpdate());
213
+ this.childWatch.observe(this, {
214
+ attributeFilter: ['data-level', 'href', 'slot'],
215
+ childList: true,
216
+ subtree: true,
217
+ });
186
218
  this.addEventListener('click', this.handleClick);
187
219
  document.addEventListener('scroll', this.handleScroll, {
188
220
  capture: true,
@@ -195,9 +227,32 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
195
227
  firstUpdated(changed) {
196
228
  super.firstUpdated(changed);
197
229
  this.watch();
230
+ this.finishHydration();
231
+ }
232
+ assign() {
233
+ if (this.assignsByName) {
234
+ return;
235
+ }
236
+ const root = this.renderRoot;
237
+ const heading = root?.querySelector?.('slot[name="heading"]');
238
+ heading?.assign?.(...this.knownChildren.filter((child) => child.slot === 'heading'));
239
+ const run = this.run;
240
+ if (run) {
241
+ for (const slot of root?.querySelectorAll?.('slot[data-link]') ?? []) {
242
+ const link = run[Number(slot.dataset.link)];
243
+ if (link) {
244
+ slot.assign(link);
245
+ }
246
+ }
247
+ return;
248
+ }
249
+ const plain = root?.querySelector?.('nav > slot:not([role])');
250
+ plain?.assign?.(...[...this.childNodes].filter((node) => node instanceof Element ? node.slot !== 'heading' : node instanceof Text));
198
251
  }
199
252
  updated(changed) {
200
253
  super.updated(changed);
254
+ this.assign();
255
+ this.nameByHeading();
201
256
  if (changed.has('offset')) {
202
257
  this.reach();
203
258
  this.mark();
@@ -208,11 +263,21 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
208
263
  this.indicator.place();
209
264
  }
210
265
  render() {
211
- return html `<nav part="nav" aria-label=${this.label}>
212
- <slot @slotchange=${this.handleSlotChange}></slot>
266
+ const run = this.run;
267
+ return html `<div part="heading"><slot name="heading" @slotchange=${this.nameByHeading}></slot></div>
268
+ <nav part="nav" aria-label=${this.label} @slotchange=${this.handleSlotChange}>
269
+ ${run ? this.renderList(nest(run.map((link) => levelOf(link.getAttribute('data-level'))))) : html `<slot></slot>`}
213
270
  <span part="indicator" aria-hidden="true"></span>
214
271
  </nav>`;
215
272
  }
273
+ renderList(branches) {
274
+ return html `<slot role="list"
275
+ >${branches.map((branch) => html `<slot role="listitem"
276
+ ><slot data-link=${branch.index}></slot
277
+ >${branch.children.length > 0 ? this.renderList(branch.children) : nothing}</slot
278
+ >`)}</slot
279
+ >`;
280
+ }
216
281
  };
217
282
  __decorate([
218
283
  property()