@fusion.dev/helix-headless 0.142.1 → 0.144.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 (285) hide show
  1. package/custom-elements.json +25381 -1202
  2. package/dist/accordion/item/accordion-item.d.ts +38 -2
  3. package/dist/accordion/item/accordion-item.js +38 -6
  4. package/dist/accordion/root/accordion-root.d.ts +23 -1
  5. package/dist/accordion/root/accordion-root.js +39 -3
  6. package/dist/announcement/root/announcement-root.d.ts +11 -7
  7. package/dist/announcement/root/announcement-root.js +1 -1
  8. package/dist/app-header/root/app-header-root.d.ts +21 -0
  9. package/dist/app-header/root/app-header-root.js +31 -5
  10. package/dist/area-chart/root/area-chart-root.d.ts +12 -4
  11. package/dist/area-chart/root/area-chart-root.js +39 -11
  12. package/dist/avatar/group/avatar-group.d.ts +24 -3
  13. package/dist/avatar/group/avatar-group.js +34 -2
  14. package/dist/avatar/root/avatar-root.d.ts +17 -1
  15. package/dist/avatar/root/avatar-root.js +31 -10
  16. package/dist/badge/root/badge-root.d.ts +12 -0
  17. package/dist/badge/root/badge-root.js +1 -1
  18. package/dist/bar-chart/root/bar-chart-root.d.ts +30 -5
  19. package/dist/bar-chart/root/bar-chart-root.js +58 -21
  20. package/dist/blockquote/root/blockquote-root.d.ts +12 -7
  21. package/dist/blockquote/root/blockquote-root.js +6 -4
  22. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -3
  23. package/dist/breadcrumbs/root/breadcrumbs-root.js +9 -1
  24. package/dist/button/root/button-root.d.ts +84 -1
  25. package/dist/button/root/button-root.js +81 -3
  26. package/dist/callout/root/callout-root.d.ts +32 -6
  27. package/dist/callout/root/callout-root.js +26 -7
  28. package/dist/canvas/root/canvas-root.d.ts +1 -0
  29. package/dist/canvas/root/canvas-root.js +10 -1
  30. package/dist/card/root/card-root.d.ts +30 -1
  31. package/dist/card/root/card-root.js +38 -5
  32. package/dist/carousel/root/carousel-root.d.ts +47 -5
  33. package/dist/carousel/root/carousel-root.js +53 -21
  34. package/dist/chat/composer/chat-composer.d.ts +13 -11
  35. package/dist/chat/composer/chat-composer.js +6 -12
  36. package/dist/chat/message/chat-message.js +8 -1
  37. package/dist/chat/root/chat-root.d.ts +1 -0
  38. package/dist/chat/root/chat-root.js +2 -1
  39. package/dist/checkbox/root/checkbox-root.d.ts +7 -0
  40. package/dist/checkbox/root/checkbox-root.js +9 -3
  41. package/dist/checkbox-group/root/checkbox-group-root.d.ts +116 -33
  42. package/dist/checkbox-group/root/checkbox-group-root.js +141 -44
  43. package/dist/code/root/code-root.d.ts +1 -1
  44. package/dist/code-block/lines.d.ts +13 -0
  45. package/dist/code-block/lines.js +2 -0
  46. package/dist/code-block/root/code-block-root.d.ts +49 -0
  47. package/dist/code-block/root/code-block-root.js +44 -3
  48. package/dist/color-input/root/color-input-root.d.ts +60 -2
  49. package/dist/color-input/root/color-input-root.js +55 -7
  50. package/dist/color-picker/channels.d.ts +26 -0
  51. package/dist/color-picker/channels.js +84 -0
  52. package/dist/color-picker/root/color-picker-root.d.ts +118 -12
  53. package/dist/color-picker/root/color-picker-root.js +183 -13
  54. package/dist/combobox/root/combobox-root.d.ts +126 -3
  55. package/dist/combobox/root/combobox-root.js +224 -21
  56. package/dist/command/root/command-root.d.ts +47 -10
  57. package/dist/command/root/command-root.js +45 -4
  58. package/dist/copy-button/from.d.ts +22 -0
  59. package/dist/copy-button/from.js +28 -0
  60. package/dist/copy-button/root/copy-button-root.d.ts +48 -7
  61. package/dist/copy-button/root/copy-button-root.js +54 -8
  62. package/dist/data-table/root/data-table-root.d.ts +164 -10
  63. package/dist/data-table/root/data-table-root.js +488 -57
  64. package/dist/date-input/root/date-input-root.d.ts +123 -9
  65. package/dist/date-input/root/date-input-root.js +147 -45
  66. package/dist/date-picker/root/date-picker-root.d.ts +229 -18
  67. package/dist/date-picker/root/date-picker-root.js +342 -46
  68. package/dist/dialog/description.d.ts +9 -0
  69. package/dist/dialog/description.js +14 -0
  70. package/dist/dialog/root/dialog-root.d.ts +101 -10
  71. package/dist/dialog/root/dialog-root.js +83 -4
  72. package/dist/donut-chart/root/donut-chart-root.d.ts +61 -16
  73. package/dist/donut-chart/root/donut-chart-root.js +48 -13
  74. package/dist/drawer/root/drawer-root.d.ts +93 -7
  75. package/dist/drawer/root/drawer-root.js +126 -5
  76. package/dist/drawer/swipe-start.d.ts +9 -0
  77. package/dist/drawer/swipe-start.js +12 -0
  78. package/dist/drilldown/item/drilldown-item.d.ts +16 -13
  79. package/dist/drilldown/item/drilldown-item.js +6 -2
  80. package/dist/drilldown/root/drilldown-root.d.ts +21 -5
  81. package/dist/drilldown/root/drilldown-root.js +26 -10
  82. package/dist/element-events.generated.js +42 -13
  83. package/dist/empty/root/empty-root.d.ts +1 -1
  84. package/dist/file-input/root/file-input-root.d.ts +65 -1
  85. package/dist/file-input/root/file-input-root.js +76 -12
  86. package/dist/file-input/screen.d.ts +35 -0
  87. package/dist/file-input/screen.js +49 -0
  88. package/dist/filter-input/menu/filter-menu.d.ts +9 -0
  89. package/dist/filter-input/menu/filter-menu.js +8 -0
  90. package/dist/filter-input/root/filter-input-root.d.ts +43 -6
  91. package/dist/filter-input/root/filter-input-root.js +89 -10
  92. package/dist/filter-input/suggest.d.ts +17 -0
  93. package/dist/filter-input/suggest.js +23 -0
  94. package/dist/format-date/options.d.ts +33 -0
  95. package/dist/format-date/options.js +42 -0
  96. package/dist/format-date/root/format-date-root.d.ts +62 -3
  97. package/dist/format-date/root/format-date-root.js +78 -37
  98. package/dist/graph/root/graph-root.d.ts +24 -3
  99. package/dist/graph/root/graph-root.js +66 -12
  100. package/dist/heatmap/root/buckets.d.ts +2 -1
  101. package/dist/heatmap/root/buckets.js +5 -1
  102. package/dist/heatmap/root/heatmap-root.d.ts +47 -9
  103. package/dist/heatmap/root/heatmap-root.js +46 -18
  104. package/dist/icon/index.d.ts +2 -0
  105. package/dist/icon/index.js +2 -0
  106. package/dist/icon/registry.d.ts +37 -0
  107. package/dist/icon/registry.js +83 -0
  108. package/dist/icon/root/icon-root.d.ts +53 -6
  109. package/dist/icon/root/icon-root.js +72 -4
  110. package/dist/icon/root/index.d.ts +2 -0
  111. package/dist/icon/root/index.js +2 -0
  112. package/dist/icon/set.d.ts +1 -0
  113. package/dist/icon/set.js +3 -0
  114. package/dist/icon-button/root/icon-button-root.d.ts +1 -1
  115. package/dist/image/root/image-root.d.ts +23 -2
  116. package/dist/image/root/image-root.js +23 -4
  117. package/dist/input/root/input-root.d.ts +62 -9
  118. package/dist/input/root/input-root.js +61 -5
  119. package/dist/kbd/root/kbd-root.d.ts +26 -11
  120. package/dist/kbd/root/kbd-root.js +35 -38
  121. package/dist/kbd/shortcut.d.ts +18 -0
  122. package/dist/kbd/shortcut.js +35 -0
  123. package/dist/legend/hidden-series.d.ts +8 -0
  124. package/dist/legend/hidden-series.js +13 -0
  125. package/dist/legend/root/legend-root.d.ts +63 -0
  126. package/dist/legend/root/legend-root.js +61 -7
  127. package/dist/lightbox/item/lightbox-item.d.ts +17 -1
  128. package/dist/lightbox/item/lightbox-item.js +10 -1
  129. package/dist/lightbox/root/lightbox-root.d.ts +77 -6
  130. package/dist/lightbox/root/lightbox-root.js +116 -5
  131. package/dist/line-chart/root/line-chart-root.d.ts +29 -4
  132. package/dist/line-chart/root/line-chart-root.js +58 -17
  133. package/dist/list/item/list-item.d.ts +11 -5
  134. package/dist/list/item/list-item.js +5 -6
  135. package/dist/listbox/range.d.ts +17 -0
  136. package/dist/listbox/range.js +5 -0
  137. package/dist/listbox/root/listbox-root.d.ts +184 -4
  138. package/dist/listbox/root/listbox-root.js +229 -23
  139. package/dist/loader/busy.d.ts +17 -0
  140. package/dist/loader/busy.js +24 -0
  141. package/dist/loader/root/loader-root.d.ts +37 -0
  142. package/dist/loader/root/loader-root.js +52 -3
  143. package/dist/map/root/map-root.d.ts +19 -4
  144. package/dist/map/root/map-root.js +36 -7
  145. package/dist/megamenu/root/megamenu-root.d.ts +10 -1
  146. package/dist/megamenu/root/megamenu-root.js +10 -1
  147. package/dist/menu/item/menu-item.d.ts +41 -12
  148. package/dist/menu/item/menu-item.js +46 -3
  149. package/dist/menu/root/menu-root.d.ts +32 -2
  150. package/dist/menu/root/menu-root.js +48 -8
  151. package/dist/meter/root/meter-root.d.ts +13 -0
  152. package/dist/meter/root/meter-root.js +24 -5
  153. package/dist/navigation/root/navigation-root.d.ts +10 -2
  154. package/dist/navigation/root/navigation-root.js +13 -2
  155. package/dist/notifications/feed-keys.d.ts +10 -0
  156. package/dist/notifications/feed-keys.js +14 -0
  157. package/dist/notifications/group/notification-group.d.ts +1 -0
  158. package/dist/notifications/notification/notification.d.ts +29 -0
  159. package/dist/notifications/notification/notification.js +51 -3
  160. package/dist/notifications/root/notifications-root.d.ts +49 -0
  161. package/dist/notifications/root/notifications-root.js +76 -2
  162. package/dist/number-input/root/number-input-root.d.ts +109 -7
  163. package/dist/number-input/root/number-input-root.js +109 -21
  164. package/dist/object-inspector/root/object-inspector-root.d.ts +49 -5
  165. package/dist/object-inspector/root/object-inspector-root.js +92 -12
  166. package/dist/option/root/option-root.d.ts +58 -6
  167. package/dist/option/root/option-root.js +35 -5
  168. package/dist/outline/root/outline-root.d.ts +13 -2
  169. package/dist/outline/root/outline-root.js +4 -2
  170. package/dist/pagination/root/pagination-root.d.ts +19 -1
  171. package/dist/pagination/root/pagination-root.js +49 -9
  172. package/dist/pagination/size/pagination-size.d.ts +13 -3
  173. package/dist/pagination/size/pagination-size.js +7 -0
  174. package/dist/pin-input/format.d.ts +7 -0
  175. package/dist/pin-input/format.js +16 -0
  176. package/dist/pin-input/root/pin-input-root.d.ts +37 -0
  177. package/dist/pin-input/root/pin-input-root.js +48 -7
  178. package/dist/popover/root/popover-root.d.ts +78 -3
  179. package/dist/popover/root/popover-root.js +74 -5
  180. package/dist/progress/root/progress-root.d.ts +23 -1
  181. package/dist/progress/root/progress-root.js +40 -10
  182. package/dist/qr-code/export.d.ts +31 -0
  183. package/dist/qr-code/export.js +10 -0
  184. package/dist/qr-code/root/qr-code-root.d.ts +45 -0
  185. package/dist/qr-code/root/qr-code-root.js +78 -3
  186. package/dist/radio/group/radio-group.d.ts +64 -0
  187. package/dist/radio/group/radio-group.js +66 -12
  188. package/dist/radio/root/radio-root.d.ts +8 -5
  189. package/dist/radio/root/radio-root.js +1 -2
  190. package/dist/rating/root/rating-root.d.ts +49 -1
  191. package/dist/rating/root/rating-root.js +69 -10
  192. package/dist/scatter-chart/root/scatter-chart-root.d.ts +17 -3
  193. package/dist/scatter-chart/root/scatter-chart-root.js +40 -14
  194. package/dist/select/root/select-root.d.ts +93 -3
  195. package/dist/select/root/select-root.js +111 -13
  196. package/dist/skeleton/root/skeleton-root.d.ts +39 -3
  197. package/dist/skeleton/root/skeleton-root.js +33 -4
  198. package/dist/skip-link/root/skip-link-root.d.ts +10 -0
  199. package/dist/skip-link/root/skip-link-root.js +15 -1
  200. package/dist/slider/root/slider-root.d.ts +59 -2
  201. package/dist/slider/root/slider-root.js +82 -8
  202. package/dist/sparkline/root/sparkline-root.d.ts +9 -10
  203. package/dist/speed-dial/item/speed-dial-item.d.ts +11 -5
  204. package/dist/speed-dial/item/speed-dial-item.js +7 -2
  205. package/dist/speed-dial/root/speed-dial-root.d.ts +15 -4
  206. package/dist/speed-dial/root/speed-dial-root.js +19 -5
  207. package/dist/splitter/root/splitter-root.d.ts +28 -6
  208. package/dist/splitter/root/splitter-root.js +35 -1
  209. package/dist/stat/root/stat-root.d.ts +9 -1
  210. package/dist/stat/root/stat-root.js +15 -2
  211. package/dist/steps/root/steps-root.js +1 -1
  212. package/dist/steps/step/step.d.ts +1 -0
  213. package/dist/steps/step/step.js +1 -1
  214. package/dist/switch/root/switch-root.d.ts +14 -2
  215. package/dist/switch/root/switch-root.js +7 -1
  216. package/dist/table/cell/table-cell.d.ts +24 -4
  217. package/dist/table/cell/table-cell.js +21 -1
  218. package/dist/table/column/table-column.d.ts +32 -6
  219. package/dist/table/column/table-column.js +36 -6
  220. package/dist/table/root/table-root.d.ts +94 -3
  221. package/dist/table/root/table-root.js +128 -5
  222. package/dist/table/row/table-row.d.ts +7 -0
  223. package/dist/table/row/table-row.js +9 -1
  224. package/dist/tabs/root/tabs-root.d.ts +6 -1
  225. package/dist/tabs/root/tabs-root.js +6 -2
  226. package/dist/tag/group/tag-group.d.ts +107 -0
  227. package/dist/tag/group/tag-group.js +157 -0
  228. package/dist/tag/index.d.ts +1 -0
  229. package/dist/tag/index.js +1 -0
  230. package/dist/tag/root/tag-root.d.ts +83 -1
  231. package/dist/tag/root/tag-root.js +87 -17
  232. package/dist/tag/selection.d.ts +10 -0
  233. package/dist/tag/selection.js +12 -0
  234. package/dist/textarea/grow.d.ts +22 -0
  235. package/dist/textarea/grow.js +20 -0
  236. package/dist/textarea/root/textarea-root.d.ts +13 -0
  237. package/dist/textarea/root/textarea-root.js +10 -2
  238. package/dist/time-input/root/time-input-root.d.ts +71 -6
  239. package/dist/time-input/root/time-input-root.js +110 -44
  240. package/dist/time-picker/root/time-picker-root.d.ts +155 -26
  241. package/dist/time-picker/root/time-picker-root.js +192 -63
  242. package/dist/toast/hotkey.d.ts +22 -0
  243. package/dist/toast/hotkey.js +23 -0
  244. package/dist/toast/item/toast-item.d.ts +16 -1
  245. package/dist/toast/item/toast-item.js +41 -13
  246. package/dist/toast/root/toast-root.d.ts +105 -8
  247. package/dist/toast/root/toast-root.js +209 -4
  248. package/dist/toggle/group/toggle-group.d.ts +95 -16
  249. package/dist/toggle/group/toggle-group.js +99 -13
  250. package/dist/toggle/root/toggle-root.d.ts +26 -11
  251. package/dist/toggle/root/toggle-root.js +14 -6
  252. package/dist/toolbar/root/toolbar-root.d.ts +55 -15
  253. package/dist/toolbar/root/toolbar-root.js +99 -16
  254. package/dist/tooltip/focus-target.d.ts +6 -0
  255. package/dist/tooltip/focus-target.js +9 -0
  256. package/dist/tooltip/root/tooltip-root.d.ts +73 -15
  257. package/dist/tooltip/root/tooltip-root.js +96 -7
  258. package/dist/tour/naming.d.ts +30 -0
  259. package/dist/tour/naming.js +10 -0
  260. package/dist/tour/root/tour-root.d.ts +67 -3
  261. package/dist/tour/root/tour-root.js +105 -17
  262. package/dist/tour/stop/tour-stop.d.ts +65 -3
  263. package/dist/tour/stop/tour-stop.js +61 -11
  264. package/dist/tour/wait.d.ts +11 -0
  265. package/dist/tour/wait.js +19 -0
  266. package/dist/tree/item/tree-item.d.ts +21 -5
  267. package/dist/tree/item/tree-item.js +14 -4
  268. package/dist/tree/root/tree-root.d.ts +29 -5
  269. package/dist/tree/root/tree-root.js +64 -8
  270. package/dist/trend/root/trend-root.d.ts +36 -1
  271. package/dist/trend/root/trend-root.js +41 -15
  272. package/dist/video/root/keys.d.ts +5 -0
  273. package/dist/video/root/keys.js +13 -1
  274. package/dist/video/root/video-root.d.ts +19 -0
  275. package/dist/video/root/video-root.js +48 -4
  276. package/dist/video-playlist/menu/video-playlist-menu.d.ts +20 -2
  277. package/dist/video-playlist/menu/video-playlist-menu.js +27 -5
  278. package/dist/video-playlist/root/video-playlist-root.js +6 -3
  279. package/dist/virtual-list/root/virtual-list-root.d.ts +52 -4
  280. package/dist/virtual-list/root/virtual-list-root.js +103 -21
  281. package/lint.json +1 -1
  282. package/package.json +5 -5
  283. package/types/jsx.d.ts +9140 -1149
  284. package/types/svelte.d.ts +1 -0
  285. package/types/vue.d.ts +2089 -267
@@ -1,6 +1,6 @@
1
1
  import { AriaForwardController, HelixFormElement, PopoverController } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
- import type { HelixHeadlessFilterField } from '../field/filter-field.js';
3
+ import { HelixHeadlessFilterField } from '../field/filter-field.js';
4
4
  declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinTokensInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
5
5
  /**
6
6
  * @summary A text field for filters, with the active filters shown in it as tokens.
@@ -13,8 +13,10 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
13
13
  * its tokens show a selection it owns, drawn from the options it was given.
14
14
  *
15
15
  * `value` is the typed text. Every keystroke fires `input` with the text on the
16
- * element, so a table can narrow as the user types. Tokens are not reported,
17
- * because they are markup you already hold. Backspace in an empty field fires
16
+ * element, so a table can narrow as the user types. A form receives the text
17
+ * under `name`, and then one more entry under `name` for each token: its
18
+ * `value` attribute, or for a `FilterChip`, its field and values written
19
+ * `field:values` as the field reads them typed. Backspace in an empty field fires
18
20
  * `helix-dismiss` on the last token, the same event its remove control fires,
19
21
  * so one handler covers the pointer and the keyboard.
20
22
  *
@@ -38,8 +40,11 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
38
40
  * moves through them with the arrow keys, and presses one on Enter. The option
39
41
  * fires its own `helix-select`, and you decide what choosing it means. Keys
40
42
  * before a colon and values after it use the same panel with different
41
- * contents. `readFilterText` is exported so you can narrow the panel against
42
- * partly typed text. The panel opens when it has options and closes when it has
43
+ * contents. `filter-suggestions` narrows the panel to the options whose words
44
+ * hold the term at the caret, after its colon, ignoring case and accents, and
45
+ * hides the rest. `readFilterText` is exported so you can narrow it yourself
46
+ * against partly typed text. While the panel is open, a reader hears how many
47
+ * suggestions it offers. The panel opens when it has options and closes when it has
43
48
  * none, and a choice does not close it, so after picking a key you see its
44
49
  * values next. Use `selectionStart` and `setSelectionRange` to write a
45
50
  * completion back and put the caret after it, or the next parse reads the wrong
@@ -69,6 +74,9 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
69
74
  * @documentation https://fusion.dev/helix/components/filter-input
70
75
  *
71
76
  * @slot end - Content after the text, such as a count or a control.
77
+ * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
78
+ * @slot hint - Help under the control, for content richer than the `hint` text.
79
+ * @slot label - The label, for content richer than the `label` text.
72
80
  * @slot start - Content before the text, such as a search icon.
73
81
  * @slot suggestions - Suggestions for what to type next, as `Option`s and `Option.Group`s.
74
82
  * @slot tags - The active filters, as `FilterChip`s or `Tag`s.
@@ -81,7 +89,7 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
81
89
  * @event {HelixFacetEvent} helix-facet - Fired when a filter changes, whether it was typed, picked, or removed. Cancelable: call `preventDefault()` to leave the text and the chip as they were.
82
90
  * @event {HelixSelectEvent} helix-select - Fired by an option in `suggestions` when it is chosen, and bubbles out of the field. Cancelable: call `preventDefault()` to stop other listeners acting on it. The field takes no action on a choice; you decide what it does.
83
91
  * @event {Event} input - Fired when the text changes.
84
- * @event {Event} toggle - Fired when the suggestions panel opens or closes, with `newState` and `oldState`.
92
+ * @event {ToggleEvent} toggle - Fired when the suggestions panel opens or closes, with `newState` and `oldState`.
85
93
  *
86
94
  * @csspart clear - The button that empties the field.
87
95
  * @csspart control - The input itself.
@@ -90,6 +98,7 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
90
98
  * @csspart hint - The help text under the field, shown while there is no error.
91
99
  * @csspart label - The label above the field.
92
100
  * @csspart lead - The box before the text that holds the `start` slot.
101
+ * @csspart live - The live region that says how many suggestions the open panel offers. It is rendered from the first time the panel opens with suggestions.
93
102
  * @csspart rest - The count of tokens that did not fit. It is a `<button>` with `expandable` and a `<span>` without it.
94
103
  * @csspart surface - The floating panel the suggestions sit in.
95
104
  * @csspart tags - The row the tokens sit in.
@@ -115,12 +124,40 @@ export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_b
115
124
  * an icon.
116
125
  */
117
126
  clearLabel: string;
127
+ /**
128
+ * Whether the panel narrows itself to the suggestions that hold the term
129
+ * being typed, as React Aria's Autocomplete filters. Off, the panel shows
130
+ * what you slot and you narrow it.
131
+ */
132
+ filterSuggestions: boolean;
118
133
  /**
119
134
  * The text shown while the field is empty.
120
135
  *
121
136
  * Not a label: it goes when typing starts.
122
137
  */
123
138
  placeholder: string;
139
+ /** What a reader hears for one suggestion, with `{count}` filled in. */
140
+ suggestionLabel: string;
141
+ /** What a reader hears for any other number of suggestions, with `{count}` filled in. */
142
+ suggestionsLabel: string;
143
+ /** The live region, rendered from the first count said. */
144
+ private readonly status;
145
+ /** The count last said, so the same panel is said once. */
146
+ private saidCount;
147
+ /** The options this element hid while narrowing, so only those are shown again. */
148
+ private readonly narrowed;
149
+ /**
150
+ * Hides the slotted options whose words do not hold the term at the caret,
151
+ * under `filter-suggestions`, and shows again the ones it hid before.
152
+ */
153
+ private narrow;
154
+ /** Says how many suggestions the open panel offers, when that changes. */
155
+ private sayCount;
156
+ /**
157
+ * What the form receives: the text, then one entry for each token that
158
+ * says what it filters. See the class description.
159
+ */
160
+ private formValue;
124
161
  /** How many tokens did not fit, which is what the row's count renders. */
125
162
  private over;
126
163
  /**
@@ -4,11 +4,14 @@ 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 { AriaForwardController, customElement, drawIcon, HelixFacetEvent, HelixFormElement, holdCaret, MixinControllerOpen, MixinControllerValue, MixinField, MixinTokens, PopoverController, restoreValue, scrollItemIntoView, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, countLabel, customElement, drawIcon, drawStatus, HelixFacetEvent, HelixFormElement, holdCaret, MixinControllerOpen, MixinControllerValue, MixinField, MixinTokens, PopoverController, restoreValue, StatusController, scrollItemIntoView, } from '@fusion.dev/helix-core';
8
8
  import { x } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
+ import { HelixHeadlessFilterField } from '../field/filter-field.js';
11
12
  import { readFilterText } from '../grammar.js';
13
+ import { HelixHeadlessFilterMenu } from '../menu/filter-menu.js';
14
+ import { currentTerm, suggests, tokenEntry } from '../suggest.js';
12
15
  const asksToGo = (node) => typeof node?.dismiss === 'function';
13
16
  const isOption = (node) => 'active' in node && 'value' in node;
14
17
  let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinControllerOpen(MixinTokens(MixinControllerValue(MixinField(HelixFormElement)))) {
@@ -20,7 +23,13 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
20
23
  this.announcesInput = true;
21
24
  this.clearable = false;
22
25
  this.clearLabel = 'Clear';
26
+ this.filterSuggestions = false;
23
27
  this.placeholder = '';
28
+ this.suggestionLabel = '{count} suggestion';
29
+ this.suggestionsLabel = '{count} suggestions';
30
+ this.status = new StatusController(this);
31
+ this.saidCount = -1;
32
+ this.narrowed = new Set();
24
33
  this.over = 0;
25
34
  this.activeAt = -1;
26
35
  this.suggesting = false;
@@ -66,7 +75,9 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
66
75
  menu.apply(field, []);
67
76
  };
68
77
  this.handleClear = () => {
69
- this.requestValue('');
78
+ if (this.value !== '' && this.requestValue('')) {
79
+ this.announceChange();
80
+ }
70
81
  const control = this.control;
71
82
  if (control) {
72
83
  restoreValue(control, this.value);
@@ -150,6 +161,51 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
150
161
  this.over = this.hideOverflow(this.tokens);
151
162
  };
152
163
  }
164
+ narrow() {
165
+ const slot = this.renderRoot?.querySelector('slot[name="suggestions"]');
166
+ const term = this.filterSuggestions
167
+ ? currentTerm(this.value, this.control?.selectionStart ?? this.value.length)
168
+ : '';
169
+ for (const node of slot?.assignedElements({ flatten: true }) ?? []) {
170
+ const options = isOption(node) ? [node] : [...node.children].filter(isOption);
171
+ for (const option of options) {
172
+ const hide = !suggests(option.textContent ?? '', term);
173
+ if (hide && !option.hidden) {
174
+ option.hidden = true;
175
+ this.narrowed.add(option);
176
+ }
177
+ else if (!hide && this.narrowed.has(option)) {
178
+ option.hidden = false;
179
+ this.narrowed.delete(option);
180
+ }
181
+ }
182
+ }
183
+ }
184
+ sayCount() {
185
+ const count = this.open && this.suggesting ? this.suggestions.length : -1;
186
+ if (count === this.saidCount) {
187
+ return;
188
+ }
189
+ this.saidCount = count;
190
+ if (count < 0) {
191
+ return;
192
+ }
193
+ this.status.sayNow(countLabel(count, this.suggestionLabel, this.suggestionsLabel, this.lang));
194
+ }
195
+ formValue() {
196
+ const entries = (this.tokens?.assignedElements({ flatten: true }) ?? [])
197
+ .map(tokenEntry)
198
+ .filter((entry) => entry !== null);
199
+ if (this.name === '' || entries.length === 0) {
200
+ return this.value;
201
+ }
202
+ const data = new FormData();
203
+ data.append(this.name, this.value);
204
+ for (const entry of entries) {
205
+ data.append(this.name, entry);
206
+ }
207
+ return data;
208
+ }
153
209
  static { this.shadowRootOptions = {
154
210
  ...HelixFormElement.shadowRootOptions,
155
211
  delegatesFocus: true,
@@ -177,7 +233,7 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
177
233
  }
178
234
  }
179
235
  }
180
- return found.filter((option) => !option.disabled);
236
+ return found.filter((option) => !option.disabled && !option.hidden);
181
237
  }
182
238
  get selectionDirection() {
183
239
  return this.control?.selectionDirection ?? 'none';
@@ -192,12 +248,13 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
192
248
  this.control?.setSelectionRange(start, end, direction);
193
249
  }
194
250
  get fields() {
195
- return [
196
- ...this.querySelectorAll('helix-headless-filter-field, helix-filter-field'),
197
- ];
251
+ return [...this.querySelectorAll('*')].filter((element) => element instanceof HelixHeadlessFilterField);
198
252
  }
199
253
  menuFor(field) {
200
- for (const menu of this.querySelectorAll('helix-headless-filter-menu, helix-filter-menu')) {
254
+ for (const menu of this.querySelectorAll('*')) {
255
+ if (!(menu instanceof HelixHeadlessFilterMenu)) {
256
+ continue;
257
+ }
201
258
  if (menu.fields.some((declared) => (declared.name ?? '') === field)) {
202
259
  return menu;
203
260
  }
@@ -259,12 +316,16 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
259
316
  from < 0 ? (by > 0 ? 0 : options.length - 1) : (from + by + options.length) % options.length;
260
317
  }
261
318
  refreshSuggesting() {
319
+ this.narrow();
262
320
  const options = this.suggestions;
263
321
  this.suggesting = options.length > 0;
264
322
  this.activeAt = this.activeAt < options.length ? this.activeAt : -1;
265
323
  }
266
324
  removeLastToken() {
267
- const last = this.tokens?.assignedElements({ flatten: true }).at(-1);
325
+ const last = this.tokens
326
+ ?.assignedElements({ flatten: true })
327
+ .filter((token) => !token.hidden)
328
+ .at(-1);
268
329
  if (asksToGo(last)) {
269
330
  last.dismiss();
270
331
  }
@@ -303,7 +364,8 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
303
364
  super.updated(changed);
304
365
  this.releaseCaret?.();
305
366
  this.releaseCaret = undefined;
306
- this.internals.setFormValue(this.value);
367
+ this.internals.setFormValue(this.formValue());
368
+ this.sayCount();
307
369
  const control = this.control;
308
370
  if (control) {
309
371
  this.copyValidity(control);
@@ -373,9 +435,17 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
373
435
  built only once there were options could never learn that there are
374
436
  any. A manual popover puts it in the top layer, so it is not clipped by
375
437
  a scrolling table or an overflow on an ancestor. -->
376
- <div part="surface" id="suggestions" role="listbox" popover="manual" ?hidden=${!this.open}>
438
+ <div
439
+ part="surface"
440
+ id="suggestions"
441
+ role="listbox"
442
+ aria-labelledby=${this.labeled ? this.labelId : nothing}
443
+ popover="manual"
444
+ ?hidden=${!this.open}
445
+ >
377
446
  <slot name="suggestions" @slotchange=${this.handleSuggestions}></slot>
378
447
  </div>
448
+ ${drawStatus(this.status)}
379
449
  ${this.renderHint()}${this.renderFeedback()}`;
380
450
  }
381
451
  };
@@ -385,9 +455,18 @@ __decorate([
385
455
  __decorate([
386
456
  property({ attribute: 'clear-label' })
387
457
  ], HelixHeadlessFilterInput.prototype, "clearLabel", void 0);
458
+ __decorate([
459
+ property({ attribute: 'filter-suggestions', type: Boolean })
460
+ ], HelixHeadlessFilterInput.prototype, "filterSuggestions", void 0);
388
461
  __decorate([
389
462
  property()
390
463
  ], HelixHeadlessFilterInput.prototype, "placeholder", void 0);
464
+ __decorate([
465
+ property({ attribute: 'suggestion-label' })
466
+ ], HelixHeadlessFilterInput.prototype, "suggestionLabel", void 0);
467
+ __decorate([
468
+ property({ attribute: 'suggestions-label' })
469
+ ], HelixHeadlessFilterInput.prototype, "suggestionsLabel", void 0);
391
470
  __decorate([
392
471
  state()
393
472
  ], HelixHeadlessFilterInput.prototype, "over", void 0);
@@ -0,0 +1,17 @@
1
+ /**
2
+ * What a filter field narrows its suggestions by, and what it sends a form,
3
+ * kept apart from the element so each can be read and tested alone.
4
+ */
5
+ /**
6
+ * The term being typed at the caret: the run of text since the last space,
7
+ * and after its colon where it has one, so `status:ope` narrows by `ope`.
8
+ */
9
+ export declare const currentTerm: (text: string, caret?: number) => string;
10
+ /** Whether a suggestion's words hold the term, ignoring case and accents. */
11
+ export declare const suggests: (words: string, term: string) => boolean;
12
+ /**
13
+ * What a token sends a form: its `value` attribute, or for a token naming a
14
+ * field, the field and the values it shows, written `field:values` the way the
15
+ * field reads them typed. Nothing for a token that says neither.
16
+ */
17
+ export declare const tokenEntry: (token: Element) => string | null;
@@ -0,0 +1,23 @@
1
+ export const currentTerm = (text, caret = text.length) => {
2
+ const before = text.slice(0, caret);
3
+ const word = before.slice(before.search(/\S*$/u));
4
+ const colon = word.indexOf(':');
5
+ return (colon === -1 ? word : word.slice(colon + 1)).replace(/^-/u, '');
6
+ };
7
+ export const suggests = (words, term) => {
8
+ const plain = (text) => text.normalize('NFD').replace(/\p{M}/gu, '').toLowerCase();
9
+ return term === '' || plain(words).includes(plain(term));
10
+ };
11
+ export const tokenEntry = (token) => {
12
+ const written = token.getAttribute('value');
13
+ if (written !== null) {
14
+ return written;
15
+ }
16
+ const field = token.getAttribute('name');
17
+ const values = (token.textContent ?? '').replace(/\s+/gu, ' ').trim();
18
+ if (!field || values === '') {
19
+ return null;
20
+ }
21
+ const negated = token.getAttribute('operator')?.startsWith('not') ? '-' : '';
22
+ return `${negated}${field}:${values.replace(/\s*,\s*/gu, ',')}`;
23
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * What a Format Date was asked for, in its own spelling: the `Intl` options a
3
+ * consumer set, and whether the timestamp is a day with no time of day.
4
+ */
5
+ export interface Asked {
6
+ dateOnly: boolean;
7
+ dateStyle?: 'full' | 'long' | 'medium' | 'short' | undefined;
8
+ day?: 'numeric' | '2-digit' | undefined;
9
+ era?: 'long' | 'narrow' | 'short' | undefined;
10
+ hour?: 'numeric' | '2-digit' | undefined;
11
+ hourCycle?: 'h11' | 'h12' | 'h23' | 'h24' | undefined;
12
+ minute?: 'numeric' | '2-digit' | undefined;
13
+ month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow' | undefined;
14
+ second?: 'numeric' | '2-digit' | undefined;
15
+ timeStyle?: 'full' | 'long' | 'medium' | 'short' | undefined;
16
+ timeZone?: string | undefined;
17
+ timeZoneName?: 'long' | 'short' | undefined;
18
+ weekday?: 'long' | 'short' | 'narrow' | undefined;
19
+ year?: 'numeric' | '2-digit' | undefined;
20
+ }
21
+ /**
22
+ * The `Intl.DateTimeFormat` options for what was asked, and none that was not.
23
+ *
24
+ * **A style wins over the fields.** `dateStyle` and `timeStyle` are each
25
+ * locale's own whole pattern, and `Intl` throws a `TypeError` when one is
26
+ * given beside `month` or `hour`, so the fields are left out wherever a style
27
+ * is set. `hourCycle` and `timeZone` go with either.
28
+ *
29
+ * **A day with no time is read in UTC** unless a zone is given. Parsed, it is
30
+ * midnight UTC, and read in a zone west of it that is the day before: measured
31
+ * before this, `2026-08-04` read "August 3, 2026" in Los Angeles.
32
+ */
33
+ export declare const formatOptions: (asked: Asked) => Intl.DateTimeFormatOptions;
@@ -0,0 +1,42 @@
1
+ const FIELDS = [
2
+ 'day',
3
+ 'era',
4
+ 'hour',
5
+ 'minute',
6
+ 'month',
7
+ 'second',
8
+ 'timeZoneName',
9
+ 'weekday',
10
+ 'year',
11
+ ];
12
+ export const formatOptions = (asked) => {
13
+ const options = {};
14
+ const styled = asked.dateStyle !== undefined || asked.timeStyle !== undefined;
15
+ if (styled) {
16
+ if (asked.dateStyle) {
17
+ options.dateStyle = asked.dateStyle;
18
+ }
19
+ if (asked.timeStyle) {
20
+ options.timeStyle = asked.timeStyle;
21
+ }
22
+ }
23
+ else {
24
+ for (const field of FIELDS) {
25
+ const value = asked[field];
26
+ if (value) {
27
+ ;
28
+ options[field] = value;
29
+ }
30
+ }
31
+ }
32
+ if (asked.hourCycle) {
33
+ options.hourCycle = asked.hourCycle;
34
+ }
35
+ if (asked.timeZone) {
36
+ options.timeZone = asked.timeZone;
37
+ }
38
+ else if (asked.dateOnly) {
39
+ options.timeZone = 'UTC';
40
+ }
41
+ return options;
42
+ };
@@ -12,13 +12,29 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
12
12
  * renders both `minute` and `month` as `m`, so a compact "3m" could mean three
13
13
  * minutes or three months. `elapsedTime` in `helix/core` does the measurement.
14
14
  *
15
+ * **`relative` shows the same reading in words**, such as "3 hours ago" at
16
+ * `long`, "3 hr. ago" at `short` and "3h ago" at `narrow`, the styles
17
+ * `sl-relative-time` offers. `numeric="always"` says "1 day ago" where the
18
+ * default says "yesterday".
19
+ *
20
+ * **`date-style` and `time-style` take each locale's own pattern**, such as
21
+ * "Tuesday, August 4, 2026" at `full`, and stand in for the separate fields,
22
+ * which `Intl` refuses beside them. `hour-cycle` sets a twelve or twenty-four
23
+ * hour clock either way.
24
+ *
15
25
  * **The text refreshes on one timer shared by the page.** Each element
16
26
  * schedules an update for when its current reading expires, and an element
17
27
  * showing a date schedules none, so a long feed left open costs almost nothing.
18
28
  *
19
- * **The output is always `<time datetime>`**, so screen readers, copy and
20
- * paste, and scripts parsing the page get the exact instant, whatever the
21
- * visible text says.
29
+ * **The output is always `<time datetime>`**, so copy and paste and scripts
30
+ * parsing the page get the exact instant, whatever the visible text says. A
31
+ * screen reader reads the text, so an `elapsed` reading is named in full, such
32
+ * as "3 hours ago", and carries the whole date as its title.
33
+ *
34
+ * **A date with no time, such as `2026-08-04`, is that day everywhere.** It is
35
+ * formatted in UTC unless `time-zone` says otherwise, as Date Input and Date
36
+ * Picker format their dates, so a reader west of UTC sees August 4 and not
37
+ * August 3.
22
38
  *
23
39
  * @since 0.1.0
24
40
  * @status stable
@@ -47,6 +63,11 @@ export declare class HelixHeadlessFormatDate extends HelixElement {
47
63
  * browser may use either.
48
64
  */
49
65
  date: string;
66
+ /**
67
+ * The locale's own pattern for the date: `full`, `long`, `medium`, or
68
+ * `short`. Set, it stands in for `weekday`, `day`, `month`, `year` and `era`.
69
+ */
70
+ dateStyle?: 'full' | 'long' | 'medium' | 'short';
50
71
  /**
51
72
  * How the day of the month is written, where a date is shown.
52
73
  */
@@ -60,10 +81,22 @@ export declare class HelixHeadlessFormatDate extends HelixElement {
60
81
  * few characters and a date is not.
61
82
  */
62
83
  elapsed: boolean;
84
+ /**
85
+ * How the era is written, such as AD, where a date is shown.
86
+ */
87
+ era?: 'long' | 'narrow' | 'short';
63
88
  /**
64
89
  * How the hour is written, where a time of day is shown.
65
90
  */
66
91
  hour?: 'numeric' | '2-digit';
92
+ /**
93
+ * The clock a time of day is written on: `h12` or `h11` for twelve hours,
94
+ * `h23` or `h24` for twenty-four.
95
+ *
96
+ * Absent is the locale's own, which is right unless your product shows every
97
+ * reader the same clock whatever their language.
98
+ */
99
+ hourCycle?: 'h11' | 'h12' | 'h23' | 'h24';
67
100
  /**
68
101
  * The locale to format the timestamp in.
69
102
  *
@@ -78,10 +111,30 @@ export declare class HelixHeadlessFormatDate extends HelixElement {
78
111
  * How the month is written, where a date is shown.
79
112
  */
80
113
  month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow';
114
+ /**
115
+ * Whether a `relative` reading may use a word for a number: `auto` says
116
+ * "yesterday" and "now", and `always` says "1 day ago" and "in 0 seconds".
117
+ */
118
+ numeric?: 'always' | 'auto';
119
+ /**
120
+ * Shows how long ago the timestamp was, or how long until it, in words, in
121
+ * place of the date: `long` ("3 hours ago"), `short` ("3 hr. ago") or
122
+ * `narrow` ("3h ago").
123
+ *
124
+ * The reading refreshes as it goes stale, as `elapsed` does, and the whole
125
+ * date is its title. `elapsed` wins where both are set.
126
+ */
127
+ relative?: 'long' | 'narrow' | 'short';
81
128
  /**
82
129
  * How the second is written, where a time of day is shown.
83
130
  */
84
131
  second?: 'numeric' | '2-digit';
132
+ /**
133
+ * The locale's own pattern for the time of day: `full`, `long`, `medium`, or
134
+ * `short`. Set, it stands in for `hour`, `minute`, `second` and
135
+ * `time-zone-name`.
136
+ */
137
+ timeStyle?: 'full' | 'long' | 'medium' | 'short';
85
138
  /**
86
139
  * The time zone to read the timestamp in.
87
140
  *
@@ -114,10 +167,16 @@ export declare class HelixHeadlessFormatDate extends HelixElement {
114
167
  private written;
115
168
  private stopTicking;
116
169
  private watching;
170
+ /** Whether the timestamp is a day with no time, which is formatted in UTC. */
171
+ private get dateOnly();
117
172
  /** The timestamp, or nothing where there is not one to read. */
118
173
  private get moment();
119
174
  /** Every `Intl.DateTimeFormat` option a consumer set, and none they did not. */
120
175
  private get options();
176
+ /** Whether the reading is how long ago rather than the date. */
177
+ private get reading();
178
+ /** What the reading says now, and when it stops being true. */
179
+ private readingAt;
121
180
  connectedCallback(): void;
122
181
  disconnectedCallback(): void;
123
182
  protected firstUpdated(changed: PropertyValues): void;