@fusion.dev/helix-headless 0.146.0 → 0.148.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 (288) hide show
  1. package/custom-elements.json +9287 -1084
  2. package/dist/accordion/item/accordion-item.d.ts +43 -21
  3. package/dist/accordion/item/accordion-item.js +37 -11
  4. package/dist/accordion/root/accordion-root.d.ts +8 -2
  5. package/dist/accordion/root/accordion-root.js +6 -8
  6. package/dist/announcement/root/announcement-root.d.ts +66 -6
  7. package/dist/announcement/root/announcement-root.js +144 -13
  8. package/dist/area-chart/root/area-chart-root.d.ts +18 -1
  9. package/dist/area-chart/root/area-chart-root.js +14 -7
  10. package/dist/avatar/root/avatar-root.d.ts +31 -1
  11. package/dist/avatar/root/avatar-root.js +11 -1
  12. package/dist/badge/count.d.ts +14 -0
  13. package/dist/badge/count.js +20 -0
  14. package/dist/badge/root/badge-root.d.ts +57 -6
  15. package/dist/badge/root/badge-root.js +77 -8
  16. package/dist/bar-chart/root/bar-chart-root.js +11 -20
  17. package/dist/bar-chart/stack.d.ts +10 -0
  18. package/dist/bar-chart/stack.js +8 -0
  19. package/dist/blockquote/root/blockquote-root.d.ts +66 -8
  20. package/dist/blockquote/root/blockquote-root.js +85 -8
  21. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +14 -0
  22. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +12 -2
  23. package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -2
  24. package/dist/button/root/button-root.d.ts +24 -38
  25. package/dist/button/root/button-root.js +6 -37
  26. package/dist/button/submitter.d.ts +27 -0
  27. package/dist/button/submitter.js +25 -0
  28. package/dist/button-group/arrows.d.ts +14 -0
  29. package/dist/button-group/arrows.js +10 -0
  30. package/dist/button-group/root/button-group-root.d.ts +141 -3
  31. package/dist/button-group/root/button-group-root.js +142 -2
  32. package/dist/canvas/edge-pan.d.ts +61 -0
  33. package/dist/canvas/edge-pan.js +63 -0
  34. package/dist/canvas/node/canvas-node.d.ts +12 -2
  35. package/dist/canvas/node/canvas-node.js +6 -2
  36. package/dist/canvas/root/canvas-document.d.ts +10 -0
  37. package/dist/canvas/root/canvas-document.js +1 -0
  38. package/dist/canvas/root/canvas-root.d.ts +107 -5
  39. package/dist/canvas/root/canvas-root.js +140 -36
  40. package/dist/canvas/snap.d.ts +42 -0
  41. package/dist/canvas/snap.js +32 -0
  42. package/dist/card/root/card-root.d.ts +12 -1
  43. package/dist/card/root/card-root.js +11 -4
  44. package/dist/carousel/root/carousel-root.d.ts +4 -3
  45. package/dist/carousel/root/carousel-root.js +2 -6
  46. package/dist/chat/root/chat-root.d.ts +28 -4
  47. package/dist/chat/root/chat-root.js +12 -7
  48. package/dist/checkbox/root/checkbox-root.js +5 -7
  49. package/dist/checkbox-group/root/checkbox-group-root.d.ts +13 -0
  50. package/dist/checkbox-group/root/checkbox-group-root.js +16 -7
  51. package/dist/checkbox-group/select-all.d.ts +9 -4
  52. package/dist/checkbox-group/select-all.js +1 -1
  53. package/dist/code-block/root/code-block-root.d.ts +41 -18
  54. package/dist/code-block/root/code-block-root.js +21 -6
  55. package/dist/color-input/root/color-input-root.d.ts +11 -0
  56. package/dist/color-input/root/color-input-root.js +12 -5
  57. package/dist/color-picker/channels.js +1 -2
  58. package/dist/color-picker/root/color-picker-root.d.ts +32 -24
  59. package/dist/color-picker/root/color-picker-root.js +99 -55
  60. package/dist/color-picker/wheel.d.ts +28 -0
  61. package/dist/color-picker/wheel.js +15 -0
  62. package/dist/combobox/root/combobox-root.d.ts +83 -11
  63. package/dist/combobox/root/combobox-root.js +81 -18
  64. package/dist/command/root/command-root.d.ts +7 -4
  65. package/dist/command/root/command-root.js +2 -19
  66. package/dist/copy-button/root/copy-button-root.d.ts +16 -1
  67. package/dist/copy-button/root/copy-button-root.js +7 -2
  68. package/dist/data-table/keys.d.ts +0 -18
  69. package/dist/data-table/keys.js +1 -11
  70. package/dist/data-table/root/data-table-root.js +29 -50
  71. package/dist/date-input/root/date-input-root.d.ts +8 -1
  72. package/dist/date-input/root/date-input-root.js +8 -5
  73. package/dist/date-picker/root/date-picker-root.d.ts +14 -9
  74. package/dist/date-picker/root/date-picker-root.js +21 -16
  75. package/dist/description-list/item/description-list-item.d.ts +22 -4
  76. package/dist/description-list/item/description-list-item.js +14 -4
  77. package/dist/divider/root/divider-root.d.ts +45 -16
  78. package/dist/divider/root/divider-root.js +28 -4
  79. package/dist/donut-chart/root/donut-chart-root.js +13 -6
  80. package/dist/donut-chart/slice-label.d.ts +35 -0
  81. package/dist/donut-chart/slice-label.js +11 -0
  82. package/dist/drawer/root/drawer-root.d.ts +4 -2
  83. package/dist/drawer/root/drawer-root.js +15 -3
  84. package/dist/drilldown/item/drilldown-item.d.ts +21 -1
  85. package/dist/drilldown/item/drilldown-item.js +38 -4
  86. package/dist/drilldown/root/drilldown-root.js +3 -2
  87. package/dist/element-events.generated.js +5 -1
  88. package/dist/empty/root/empty-root.d.ts +46 -2
  89. package/dist/empty/root/empty-root.js +50 -5
  90. package/dist/file-input/root/file-input-root.d.ts +34 -6
  91. package/dist/file-input/root/file-input-root.js +55 -21
  92. package/dist/file-input/status.d.ts +24 -0
  93. package/dist/file-input/status.js +11 -0
  94. package/dist/filter-input/chip/filter-chip.d.ts +23 -0
  95. package/dist/filter-input/chip/filter-chip.js +10 -2
  96. package/dist/filter-input/root/filter-input-root.d.ts +8 -0
  97. package/dist/filter-input/root/filter-input-root.js +17 -27
  98. package/dist/format-date/fields.d.ts +20 -0
  99. package/dist/format-date/fields.js +14 -0
  100. package/dist/format-date/options.d.ts +5 -5
  101. package/dist/format-date/options.js +10 -39
  102. package/dist/format-date/root/format-date-root.d.ts +53 -4
  103. package/dist/format-date/root/format-date-root.js +38 -50
  104. package/dist/graph/root/graph-root.d.ts +23 -5
  105. package/dist/graph/root/graph-root.js +17 -20
  106. package/dist/heatmap/root/buckets.d.ts +21 -0
  107. package/dist/heatmap/root/buckets.js +34 -0
  108. package/dist/heatmap/root/heatmap-root.d.ts +76 -3
  109. package/dist/heatmap/root/heatmap-root.js +105 -20
  110. package/dist/icon/index.d.ts +1 -1
  111. package/dist/icon/index.js +1 -1
  112. package/dist/icon/registry.d.ts +26 -1
  113. package/dist/icon/registry.js +12 -1
  114. package/dist/icon/root/icon-root.d.ts +13 -0
  115. package/dist/icon/root/icon-root.js +23 -2
  116. package/dist/icon/root/index.d.ts +1 -1
  117. package/dist/icon/root/index.js +1 -1
  118. package/dist/image/root/image-root.d.ts +20 -0
  119. package/dist/image/root/image-root.js +6 -1
  120. package/dist/input/root/input-root.d.ts +23 -2
  121. package/dist/input/root/input-root.js +12 -8
  122. package/dist/kbd/root/kbd-root.d.ts +20 -3
  123. package/dist/kbd/root/kbd-root.js +23 -50
  124. package/dist/legend/hidden-series.d.ts +10 -2
  125. package/dist/legend/hidden-series.js +5 -2
  126. package/dist/legend/root/legend-root.d.ts +25 -7
  127. package/dist/legend/root/legend-root.js +25 -26
  128. package/dist/lightbox/root/lightbox-root.d.ts +50 -5
  129. package/dist/lightbox/root/lightbox-root.js +76 -8
  130. package/dist/lightbox/zoom.d.ts +2 -0
  131. package/dist/lightbox/zoom.js +5 -3
  132. package/dist/line-chart/root/line-chart-root.d.ts +16 -0
  133. package/dist/line-chart/root/line-chart-root.js +10 -2
  134. package/dist/list/root/list-root.d.ts +10 -2
  135. package/dist/list/root/list-root.js +31 -24
  136. package/dist/list/selection.d.ts +17 -0
  137. package/dist/list/selection.js +16 -0
  138. package/dist/listbox/root/listbox-root.d.ts +125 -26
  139. package/dist/listbox/root/listbox-root.js +156 -166
  140. package/dist/map/marker/map-marker.d.ts +2 -1
  141. package/dist/map/root/map-root.d.ts +40 -2
  142. package/dist/map/root/map-root.js +64 -23
  143. package/dist/megamenu/hand-over.d.ts +75 -0
  144. package/dist/megamenu/hand-over.js +67 -0
  145. package/dist/megamenu/root/megamenu-root.d.ts +38 -14
  146. package/dist/megamenu/root/megamenu-root.js +29 -50
  147. package/dist/menu/item/menu-item.js +4 -4
  148. package/dist/menu/root/menu-root.d.ts +5 -2
  149. package/dist/meter/root/meter-root.d.ts +42 -0
  150. package/dist/meter/root/meter-root.js +33 -7
  151. package/dist/navigation/root/navigation-root.js +5 -8
  152. package/dist/notifications/group/notification-group.d.ts +8 -1
  153. package/dist/notifications/group/notification-group.js +12 -6
  154. package/dist/notifications/notification/notification.d.ts +12 -1
  155. package/dist/notifications/notification/notification.js +3 -1
  156. package/dist/notifications/root/notifications-root.d.ts +48 -3
  157. package/dist/notifications/root/notifications-root.js +36 -6
  158. package/dist/number-input/grain.d.ts +28 -0
  159. package/dist/number-input/grain.js +14 -0
  160. package/dist/number-input/root/number-input-root.d.ts +17 -1
  161. package/dist/number-input/root/number-input-root.js +29 -18
  162. package/dist/object-inspector/root/object-inspector-root.d.ts +17 -6
  163. package/dist/object-inspector/root/object-inspector-root.js +31 -20
  164. package/dist/option/index.d.ts +1 -0
  165. package/dist/option/index.js +1 -0
  166. package/dist/option/separator/option-separator.d.ts +30 -0
  167. package/dist/option/separator/option-separator.js +21 -0
  168. package/dist/outline/root/outline-root.d.ts +12 -0
  169. package/dist/outline/root/outline-root.js +5 -19
  170. package/dist/pagination/size/pagination-size.d.ts +6 -0
  171. package/dist/pagination/size/pagination-size.js +5 -3
  172. package/dist/pin-input/code.d.ts +51 -1
  173. package/dist/pin-input/code.js +37 -0
  174. package/dist/pin-input/root/pin-input-root.d.ts +98 -121
  175. package/dist/pin-input/root/pin-input-root.js +151 -156
  176. package/dist/popover/hide-outside.js +2 -9
  177. package/dist/popover/root/popover-root.d.ts +7 -1
  178. package/dist/popover/root/popover-root.js +9 -1
  179. package/dist/progress/root/progress-root.d.ts +27 -0
  180. package/dist/progress/root/progress-root.js +25 -7
  181. package/dist/qr-code/export.d.ts +19 -0
  182. package/dist/qr-code/export.js +14 -0
  183. package/dist/qr-code/name.d.ts +11 -0
  184. package/dist/qr-code/name.js +7 -0
  185. package/dist/qr-code/root/qr-code-root.d.ts +31 -9
  186. package/dist/qr-code/root/qr-code-root.js +29 -17
  187. package/dist/radio/group/radio-group.d.ts +15 -0
  188. package/dist/radio/root/radio-root.js +2 -2
  189. package/dist/rating/root/rating-root.d.ts +22 -9
  190. package/dist/rating/root/rating-root.js +15 -39
  191. package/dist/scatter-chart/measures.d.ts +22 -0
  192. package/dist/scatter-chart/measures.js +11 -0
  193. package/dist/scatter-chart/root/scatter-chart-root.d.ts +26 -0
  194. package/dist/scatter-chart/root/scatter-chart-root.js +21 -7
  195. package/dist/scroll-area/scrollbar.d.ts +0 -8
  196. package/dist/scroll-area/scrollbar.js +1 -1
  197. package/dist/select/align.d.ts +47 -0
  198. package/dist/select/align.js +7 -0
  199. package/dist/select/root/select-root.d.ts +115 -11
  200. package/dist/select/root/select-root.js +104 -20
  201. package/dist/slider/root/slider-root.d.ts +18 -1
  202. package/dist/slider/root/slider-root.js +22 -0
  203. package/dist/sparkline/readings.d.ts +30 -12
  204. package/dist/sparkline/readings.js +32 -15
  205. package/dist/sparkline/root/sparkline-root.d.ts +46 -3
  206. package/dist/sparkline/root/sparkline-root.js +60 -21
  207. package/dist/speed-dial/group/speed-dial-group.d.ts +48 -0
  208. package/dist/speed-dial/group/speed-dial-group.js +43 -0
  209. package/dist/speed-dial/index.d.ts +1 -0
  210. package/dist/speed-dial/index.js +1 -0
  211. package/dist/speed-dial/item/speed-dial-item.d.ts +27 -2
  212. package/dist/speed-dial/item/speed-dial-item.js +43 -5
  213. package/dist/speed-dial/long-press.d.ts +50 -0
  214. package/dist/speed-dial/long-press.js +48 -0
  215. package/dist/speed-dial/root/speed-dial-root.d.ts +86 -1
  216. package/dist/speed-dial/root/speed-dial-root.js +123 -6
  217. package/dist/splitter/root/splitter-root.d.ts +9 -2
  218. package/dist/splitter/root/splitter-root.js +18 -3
  219. package/dist/steps/root/steps-root.d.ts +8 -1
  220. package/dist/steps/root/steps-root.js +6 -1
  221. package/dist/steps/step/step.d.ts +1 -0
  222. package/dist/steps/step/step.js +1 -1
  223. package/dist/table/cell/table-cell.d.ts +8 -1
  224. package/dist/table/cell/table-cell.js +6 -2
  225. package/dist/table/column/table-column.d.ts +6 -0
  226. package/dist/table/column/table-column.js +5 -1
  227. package/dist/table/grid-controller.d.ts +117 -0
  228. package/dist/table/grid-controller.js +293 -0
  229. package/dist/table/root/table-root.d.ts +47 -3
  230. package/dist/table/root/table-root.js +93 -29
  231. package/dist/table/row/table-row.d.ts +36 -0
  232. package/dist/table/row/table-row.js +52 -2
  233. package/dist/tabs/root/tabs-root.d.ts +5 -3
  234. package/dist/tag/group/tag-group.d.ts +16 -4
  235. package/dist/tag/group/tag-group.js +23 -20
  236. package/dist/textarea/root/textarea-root.d.ts +14 -2
  237. package/dist/textarea/root/textarea-root.js +6 -7
  238. package/dist/time-input/root/time-input-root.d.ts +5 -1
  239. package/dist/time-input/root/time-input-root.js +10 -7
  240. package/dist/time-picker/root/time-picker-root.d.ts +12 -14
  241. package/dist/time-picker/root/time-picker-root.js +18 -23
  242. package/dist/toast/item/toast-item.d.ts +9 -6
  243. package/dist/toast/item/toast-item.js +1 -1
  244. package/dist/toast/root/toast-root.d.ts +40 -4
  245. package/dist/toast/root/toast-root.js +68 -16
  246. package/dist/toast/words.d.ts +12 -0
  247. package/dist/toast/words.js +25 -0
  248. package/dist/toggle/group/toggle-group.d.ts +29 -5
  249. package/dist/toggle/group/toggle-group.js +26 -43
  250. package/dist/toggle/root/toggle-root.js +2 -4
  251. package/dist/toolbar/root/toolbar-root.d.ts +52 -5
  252. package/dist/toolbar/root/toolbar-root.js +88 -13
  253. package/dist/tooltip/root/tooltip-root.d.ts +7 -1
  254. package/dist/tooltip/root/tooltip-root.js +10 -2
  255. package/dist/tour/root/tour-root.d.ts +15 -0
  256. package/dist/tour/root/tour-root.js +20 -1
  257. package/dist/tour/stop/tour-stop.d.ts +3 -0
  258. package/dist/tour/stop/tour-stop.js +3 -3
  259. package/dist/tree/root/tree-root.js +2 -2
  260. package/dist/trend/figure.d.ts +16 -0
  261. package/dist/trend/figure.js +8 -0
  262. package/dist/trend/root/trend-root.d.ts +41 -2
  263. package/dist/trend/root/trend-root.js +29 -3
  264. package/dist/video/root/extra-controls.d.ts +12 -0
  265. package/dist/video/root/extra-controls.js +6 -2
  266. package/dist/video/root/video-root.d.ts +16 -1
  267. package/dist/video/root/video-root.js +15 -1
  268. package/dist/video-playlist/root/video-playlist-root.d.ts +11 -1
  269. package/dist/video-playlist/root/video-playlist-root.js +3 -0
  270. package/dist/virtual-list/fill.d.ts +11 -0
  271. package/dist/virtual-list/fill.js +9 -0
  272. package/dist/virtual-list/keys.d.ts +16 -7
  273. package/dist/virtual-list/keys.js +17 -6
  274. package/dist/virtual-list/plan.d.ts +17 -1
  275. package/dist/virtual-list/plan.js +13 -2
  276. package/dist/virtual-list/root/virtual-list-root.d.ts +25 -3
  277. package/dist/virtual-list/root/virtual-list-root.js +53 -41
  278. package/lint.json +1 -1
  279. package/package.json +5 -5
  280. package/types/jsx.d.ts +4934 -954
  281. package/types/svelte.d.ts +2 -0
  282. package/types/vue.d.ts +946 -253
  283. package/dist/color-picker/swatches.d.ts +0 -12
  284. package/dist/color-picker/swatches.js +0 -26
  285. package/dist/rating/steps.d.ts +0 -7
  286. package/dist/rating/steps.js +0 -9
  287. package/dist/tooltip/focus-target.d.ts +0 -6
  288. package/dist/tooltip/focus-target.js +0 -9
@@ -1,5 +1,7 @@
1
1
  import { AriaForwardController, HelixFormElement } from '@fusion.dev/helix-core';
2
- import { type PropertyValues, type TemplateResult } from 'lit';
2
+ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
+ import { type FileStatus } from '../status.js';
4
+ export type { FileStatus } from '../status.js';
3
5
  import '../../tag/index.js';
4
6
  declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
5
7
  /**
@@ -59,6 +61,9 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
59
61
  * @csspart feedback - The message that says what is wrong.
60
62
  * @csspart field - The box you press or drop files on.
61
63
  * @csspart file - One chosen file.
64
+ * @csspart file-done - A file whose upload `setFileStatus` says is done. Set together with `file`.
65
+ * @csspart file-error - A file whose upload `setFileStatus` says failed. Set together with `file`.
66
+ * @csspart file-uploading - A file whose upload `setFileStatus` says is under way. Set together with `file`.
62
67
  * @csspart hint - Help under the field, while nothing is wrong.
63
68
  * @csspart kind - A file's type.
64
69
  * @csspart kind-cell - The grid cell around the tag, rendered only when the tag sits in a `Tag.Group`.
@@ -72,6 +77,7 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
72
77
  * @csspart live - The live region that says `added-text`, `rejected-text`, and `removed-text`. It is rendered from the first time it has something to say.
73
78
  * @csspart meta - The row that holds a file's size and type.
74
79
  * @csspart name - A file's name.
80
+ * @csspart progress - A file's upload bar, a `<progress>`, while `setFileStatus` gives it a measure or says it is under way.
75
81
  * @csspart remove - The control that removes a file.
76
82
  * @csspart size - A file's size.
77
83
  * @csspart size-cell - The grid cell around the tag, rendered only when the tag sits in a `Tag.Group`.
@@ -80,6 +86,8 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
80
86
  * @csspart size-label - The label, trimmed to its capitals and baseline.
81
87
  * @csspart size-link - The link around the label, when the tag has an `href`.
82
88
  * @csspart size-start - The category before the label.
89
+ * @csspart status - How a file's upload is going, from `setFileStatus`: the bar and the words.
90
+ * @csspart status-text - The words `setFileStatus` gives a file, such as the reason it failed.
83
91
  * @csspart trigger - The label that opens the picker.
84
92
  *
85
93
  * @cssstate dropping - Files are being dragged over the field.
@@ -128,6 +136,11 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
128
136
  * Whether more than one file can be chosen.
129
137
  */
130
138
  multiple: boolean;
139
+ /**
140
+ * The name of a file's progress bar, read by screen readers, with `{name}`
141
+ * filled in. A label with no `{name}` has it added after it.
142
+ */
143
+ progressLabel: string;
131
144
  /**
132
145
  * What a reader hears when files are refused, with `{count}` and `{names}`
133
146
  * filled in.
@@ -170,6 +183,19 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
170
183
  validate?: (file: File) => string | null | undefined;
171
184
  /** The live region, rendered from the first time it has something to say. */
172
185
  private readonly status;
186
+ /** What the page has said of each file's upload, by file. */
187
+ private readonly statuses;
188
+ /**
189
+ * Shows how a file's upload is going, in its row: a bar for `progress`,
190
+ * from 0 to 1, or a bar without a measure while `state` is `uploading`, and
191
+ * `text` beside it. `state` names the row `file-uploading`, `file-done` or
192
+ * `file-error` beside `file`, and a reader hears `text` when the state
193
+ * becomes `done` or `error`. `null` clears it.
194
+ *
195
+ * The field uploads nothing, as Zag's file upload does not: your page sends
196
+ * the file and says here how it is going.
197
+ */
198
+ setFileStatus(file: File, status: FileStatus | null): void;
173
199
  /**
174
200
  * Screens arriving files and commits what is taken, after the files the
175
201
  * field keeps, through `transformFiles` where it is set.
@@ -193,10 +219,8 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
193
219
  * count is written as the language writes a number.
194
220
  */
195
221
  private listed;
196
- /** The element slotted into `remove-icon`, which each remove button copies. */
197
- private removeIcon;
198
- /** One copy of `removeIcon` per row, kept so a render does not replace it. */
199
- private readonly removeIcons;
222
+ /** The element slotted into `remove-icon`, one copy per row's remove button. */
223
+ private readonly removeIcon;
200
224
  private readonly handleRemoveIcon;
201
225
  /** The icon on one row's remove button: a copy of `remove-icon`, or the bin. */
202
226
  private removeMark;
@@ -342,6 +366,11 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
342
366
  * renders the styled one from the same template.
343
367
  */
344
368
  protected renderMeta(file: File): TemplateResult;
369
+ /**
370
+ * How a file's upload is going, from `setFileStatus`, behind a seam a tier
371
+ * can move: a `<progress>` named by `progress-label` and the status words.
372
+ */
373
+ protected renderFileStatus(file: File): TemplateResult | typeof nothing;
345
374
  protected render(): TemplateResult;
346
375
  }
347
376
  declare global {
@@ -349,4 +378,3 @@ declare global {
349
378
  'helix-headless-file-input': HelixHeadlessFileInput;
350
379
  }
351
380
  }
352
- export {};
@@ -4,14 +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, drawStatus, fillTemplate, formatNumbers, HelixBeforeChangeEvent, HelixFormElement, HelixRejectEvent, MixinField, requestChange, restoreValue, StatusController, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, drawIcon, drawStatus, fillTemplate, formatNumbers, HelixBeforeChangeEvent, HelixFormElement, HelixRejectEvent, labelWith, MixinField, requestChange, restoreValue, SlottedGlyph, StatusController, } from '@fusion.dev/helix-core';
8
8
  import { trash } from '@fusion.dev/helix-icons';
9
9
  import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { html } from 'lit/static-html.js';
12
12
  import { screenFiles } from '../screen.js';
13
+ import { barOf, saysStatus } from '../status.js';
13
14
  import '../../tag/index.js';
14
- const named = (label, name) => label.includes('{name}') ? fillTemplate(label, { name }) : `${label} ${name}`;
15
15
  const size = (bytes, locale) => {
16
16
  const units = ['bytes', 'KB', 'MB', 'GB', 'TB'];
17
17
  let value = bytes;
@@ -46,16 +46,17 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
46
46
  this.directory = false;
47
47
  this.locale = '';
48
48
  this.multiple = false;
49
+ this.progressLabel = 'Uploading {name}';
49
50
  this.rejectedText = 'Not added: {names}';
50
51
  this.removedText = 'Removed: {name}';
51
52
  this.removeLabel = 'Remove {name}';
52
53
  this.triggerLabel = 'Choose files';
53
54
  this.status = new StatusController(this);
54
- this.removeIcon = undefined;
55
- this.removeIcons = new Map();
55
+ this.statuses = new WeakMap();
56
+ this.removeIcon = new SlottedGlyph();
56
57
  this.handleRemoveIcon = (event) => {
57
- this.removeIcons.clear();
58
- this.removeIcon = event.target.assignedElements({ flatten: true })[0];
58
+ this.removeIcon.read(event.target);
59
+ this.requestUpdate();
59
60
  };
60
61
  this.handlePaste = (event) => {
61
62
  const pasted = [...(event.clipboardData?.files ?? [])];
@@ -106,6 +107,19 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
106
107
  });
107
108
  };
108
109
  }
110
+ setFileStatus(file, status) {
111
+ const was = this.statuses.get(file);
112
+ if (status) {
113
+ this.statuses.set(file, status);
114
+ if (saysStatus(status, was)) {
115
+ this.status.say(status.text ?? '');
116
+ }
117
+ }
118
+ else {
119
+ this.statuses.delete(file);
120
+ }
121
+ this.requestUpdate();
122
+ }
109
123
  take(arriving, keeping, picked = false) {
110
124
  const { kept, refused } = screenFiles(arriving, keeping, {
111
125
  accept: this.accept,
@@ -152,12 +166,7 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
152
166
  });
153
167
  }
154
168
  removeMark(at) {
155
- if (!this.removeIcon) {
156
- return drawIcon(trash);
157
- }
158
- const held = this.removeIcons.get(at) ?? this.removeIcon.cloneNode(true);
159
- this.removeIcons.set(at, held);
160
- return held;
169
+ return this.removeIcon.copyFor(at) ?? drawIcon(trash);
161
170
  }
162
171
  get files() {
163
172
  return [...(this.control?.files ?? [])];
@@ -258,6 +267,23 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
258
267
  part="kind"
259
268
  exportparts="cell: kind-cell, dismiss: kind-dismiss, dismiss-mark: kind-dismiss-mark, label: kind-label, link: kind-link, start: kind-start">${kind(file)}</${this.tag('tag')}>`;
260
269
  }
270
+ renderFileStatus(file) {
271
+ const status = this.statuses.get(file);
272
+ if (!status) {
273
+ return nothing;
274
+ }
275
+ const bar = barOf(status);
276
+ return html `<span part="status"
277
+ >${bar === undefined
278
+ ? nothing
279
+ : html `<progress
280
+ part="progress"
281
+ max="1"
282
+ value=${bar === 'unmeasured' ? nothing : bar}
283
+ aria-label=${labelWith(this.progressLabel, 'name', file.name)}
284
+ ></progress>`}${status.text ? html `<span part="status-text">${status.text}</span>` : nothing}</span
285
+ >`;
286
+ }
261
287
  render() {
262
288
  const files = this.files;
263
289
  return html `${this.renderLabel()}
@@ -300,26 +326,34 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
300
326
  ${files.length === 0
301
327
  ? nothing
302
328
  : html `<ul part="list">
303
- ${files.map((file, at) => html `<li part="file">
329
+ ${files.map((file, at) => {
330
+ const state = this.statuses.get(file)?.state;
331
+ const done = state === 'done';
332
+ const failed = state === 'error';
333
+ const sending = state === 'uploading';
334
+ return html `<li
335
+ part="file${done ? ' file-done' : ''}${failed ? ' file-error' : ''}${sending ? ' file-uploading' : ''}"
336
+ >
304
337
  <span part="detail">
305
338
  ${this.renderFile
306
- ? this.renderFile(file, at)
307
- : html `<span part="name">${file.name}</span>
339
+ ? this.renderFile(file, at)
340
+ : html `<span part="name">${file.name}</span>
308
341
  <span part="meta">
309
342
  ${this.renderMeta(file)}
310
- </span>`}
343
+ </span>`}${this.renderFileStatus(file)}
311
344
  </span>
312
345
  <button
313
346
  part="remove"
314
347
  type="button"
315
- aria-label=${named(this.removeLabel, file.name)}
348
+ aria-label=${labelWith(this.removeLabel, 'name', file.name)}
316
349
  data-at=${at}
317
350
  ?disabled=${this.isDisabled}
318
351
  @click=${this.handleRemove}
319
352
  >
320
353
  ${this.removeMark(at)}
321
354
  </button>
322
- </li>`)}
355
+ </li>`;
356
+ })}
323
357
  </ul>`}`;
324
358
  }
325
359
  };
@@ -347,6 +381,9 @@ __decorate([
347
381
  __decorate([
348
382
  property({ reflect: true, type: Boolean })
349
383
  ], HelixHeadlessFileInput.prototype, "multiple", void 0);
384
+ __decorate([
385
+ property({ attribute: 'progress-label' })
386
+ ], HelixHeadlessFileInput.prototype, "progressLabel", void 0);
350
387
  __decorate([
351
388
  property({ attribute: 'rejected-text' })
352
389
  ], HelixHeadlessFileInput.prototype, "rejectedText", void 0);
@@ -368,9 +405,6 @@ __decorate([
368
405
  __decorate([
369
406
  property({ attribute: false })
370
407
  ], HelixHeadlessFileInput.prototype, "validate", void 0);
371
- __decorate([
372
- state()
373
- ], HelixHeadlessFileInput.prototype, "removeIcon", void 0);
374
408
  __decorate([
375
409
  state()
376
410
  ], HelixHeadlessFileInput.prototype, "dropping", void 0);
@@ -0,0 +1,24 @@
1
+ /**
2
+ * How one file's upload is going, as a page reports it to File Input. The
3
+ * field does not upload anything: the page does, and says how it is going.
4
+ */
5
+ export interface FileStatus {
6
+ /** How much is done, from 0 to 1. Left out, the bar moves without a measure while `state` is `uploading`. */
7
+ readonly progress?: number | undefined;
8
+ /** Where the upload is: under way, finished, or failed. */
9
+ readonly state?: 'done' | 'error' | 'uploading' | undefined;
10
+ /** Words beside the bar, such as "Uploaded" or the reason it failed. */
11
+ readonly text?: string | undefined;
12
+ }
13
+ /**
14
+ * The bar's value, held between 0 and 1, or nothing for a bar without a
15
+ * measure, and no bar at all for a status that is neither measured nor under
16
+ * way.
17
+ */
18
+ export declare const barOf: (status: FileStatus) => number | 'unmeasured' | undefined;
19
+ /**
20
+ * Whether a new status is worth saying: a finish or a failure with words, the
21
+ * first time the file reaches it. Progress is shown and not said, since a
22
+ * reader hearing every percent hears nothing else.
23
+ */
24
+ export declare const saysStatus: (next: FileStatus, was: FileStatus | undefined) => boolean;
@@ -0,0 +1,11 @@
1
+ import { clamp } from '@fusion.dev/helix-core';
2
+ export const barOf = (status) => {
3
+ const { progress, state } = status;
4
+ if (typeof progress === 'number' && Number.isFinite(progress)) {
5
+ return clamp(progress, 0, 1);
6
+ }
7
+ return state === 'uploading' ? 'unmeasured' : undefined;
8
+ };
9
+ export const saysStatus = (next, was) => Boolean(next.text) &&
10
+ (next.state === 'done' || next.state === 'error') &&
11
+ next.state !== was?.state;
@@ -27,6 +27,15 @@ declare const HelixHeadlessFilterChip_base: (new (...args: any[]) => import("@fu
27
27
  * dismissal, which is also how you take control of the interaction for one
28
28
  * press. `requestChange` documents the pattern.
29
29
  *
30
+ * **Keyboard.** The remove button is a tab stop of its own, and Enter or Space
31
+ * on it asks to remove the filter. In a `FilterInput`, Backspace in the empty
32
+ * field asks to remove the last chip.
33
+ *
34
+ * The remove button is named `dismiss-label` followed by the chip's words,
35
+ * such as "Remove filter Status is not open", so a row of chips says which
36
+ * filter each button removes. Write `{label}` in `dismiss-label` to put the
37
+ * words elsewhere.
38
+ *
30
39
  * To let the user change the operator, slot a menu trigger into `operator`.
31
40
  * With the text fallback, the operator is a label. The chip does not interpret
32
41
  * operators: their meaning belongs to your domain, as `FilterOperator`
@@ -100,6 +109,20 @@ export declare class HelixHeadlessFilterChip extends HelixHeadlessFilterChip_bas
100
109
  * makes an unusual filter look unusual.
101
110
  */
102
111
  operatorLabel: string;
112
+ /**
113
+ * The remove button's name: `dismissLabel` with the chip's words, its field,
114
+ * its comparison and its values, such as "Remove filter Status is not open".
115
+ *
116
+ * **Named for its chip**, as File Input names each remove button for its
117
+ * file: a row of buttons each called "Remove filter" says nothing about
118
+ * which filter each one takes away. `{label}` in `dismiss-label` says where
119
+ * the words go, and without it they follow the label.
120
+ *
121
+ * Each word is read from what is slotted and otherwise from `label` and
122
+ * `operator-label`, which is all a server and a hydrating render can read,
123
+ * so the values join the update after.
124
+ */
125
+ private get removeName();
103
126
  protected firstUpdated(changed: PropertyValues): void;
104
127
  protected render(): TemplateResult;
105
128
  }
@@ -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, MixinDismissible } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, labelWith, MixinDismissible, slottedWords, } 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 } from 'lit/decorators.js';
@@ -16,8 +16,16 @@ let HelixHeadlessFilterChip = class HelixHeadlessFilterChip extends MixinDismiss
16
16
  this.label = '';
17
17
  this.operatorLabel = '';
18
18
  }
19
+ get removeName() {
20
+ const read = (slot, fallback) => (this.childrenKnown && slottedWords(this, [slot])) || fallback;
21
+ const words = [read('field', this.label), read('operator', this.operatorLabel), read('', '')]
22
+ .filter(Boolean)
23
+ .join(' ');
24
+ return labelWith(this.dismissLabel, 'label', words);
25
+ }
19
26
  firstUpdated(changed) {
20
27
  super.firstUpdated(changed);
28
+ this.renderRoot.addEventListener('slotchange', () => this.requestUpdate());
21
29
  this.finishHydration();
22
30
  }
23
31
  render() {
@@ -33,7 +41,7 @@ let HelixHeadlessFilterChip = class HelixHeadlessFilterChip extends MixinDismiss
33
41
  ? html `<button
34
42
  part="dismiss"
35
43
  type="button"
36
- aria-label=${this.dismissLabel}
44
+ aria-label=${this.removeName}
37
45
  @click=${() => this.dismiss()}
38
46
  >
39
47
  <svg part="dismiss-mark" fill="currentColor" viewBox=${x.viewBox} aria-hidden="true">
@@ -97,6 +97,7 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
97
97
  * @aliases SearchField, FacetInput, TokenInput
98
98
  * @documentation https://fusion.dev/helix/components/filter-input
99
99
  *
100
+ * @slot clear-icon - Replaces the cross on the clear button, under `clearable`.
100
101
  * @slot end - Content after the text, such as a count or a control.
101
102
  * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
102
103
  * @slot hint - Help under the control, for content richer than the `hint` text.
@@ -256,6 +257,11 @@ export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_b
256
257
  * and one that pretends it was never there.
257
258
  */
258
259
  private get suggestions();
260
+ /**
261
+ * Every option slotted into `suggestions`, and every option one level inside
262
+ * a group slotted there, hidden or not.
263
+ */
264
+ private get options();
259
265
  /**
260
266
  * The direction of the selection, named as on `<input>`.
261
267
  *
@@ -437,6 +443,8 @@ export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_b
437
443
  private markActive;
438
444
  /** Puts the caret in the text field. */
439
445
  focus(options?: FocusOptions): void;
446
+ /** The cross on the clear button, behind a seam a tier can move. */
447
+ protected renderClearIcon(): TemplateResult;
440
448
  protected render(): TemplateResult;
441
449
  }
442
450
  declare global {
@@ -162,22 +162,18 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
162
162
  };
163
163
  }
164
164
  narrow() {
165
- const slot = this.renderRoot?.querySelector('slot[name="suggestions"]');
166
165
  const term = this.filterSuggestions
167
166
  ? currentTerm(this.value, this.control?.selectionStart ?? this.value.length)
168
167
  : '';
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
- }
168
+ for (const option of this.options) {
169
+ const hide = !suggests(option.textContent ?? '', term);
170
+ if (hide && !option.hidden) {
171
+ option.hidden = true;
172
+ this.narrowed.add(option);
173
+ }
174
+ else if (!hide && this.narrowed.has(option)) {
175
+ option.hidden = false;
176
+ this.narrowed.delete(option);
181
177
  }
182
178
  }
183
179
  }
@@ -220,20 +216,11 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
220
216
  return this.clearable && this.value !== '' && !this.isDisabled;
221
217
  }
222
218
  get suggestions() {
219
+ return this.options.filter((option) => !option.disabled && !option.hidden);
220
+ }
221
+ get options() {
223
222
  const slot = this.renderRoot?.querySelector('slot[name="suggestions"]');
224
- const found = [];
225
- for (const node of slot?.assignedElements({ flatten: true }) ?? []) {
226
- if (isOption(node)) {
227
- found.push(node);
228
- continue;
229
- }
230
- for (const child of node.children) {
231
- if (isOption(child)) {
232
- found.push(child);
233
- }
234
- }
235
- }
236
- return found.filter((option) => !option.disabled && !option.hidden);
223
+ return (slot?.assignedElements({ flatten: true }) ?? []).flatMap((node) => isOption(node) ? [node] : [...node.children].filter(isOption));
237
224
  }
238
225
  get selectionDirection() {
239
226
  return this.control?.selectionDirection ?? 'none';
@@ -392,6 +379,9 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
392
379
  focus(options) {
393
380
  this.control?.focus(options);
394
381
  }
382
+ renderClearIcon() {
383
+ return html `<slot name="clear-icon">${drawIcon(x)}</slot>`;
384
+ }
395
385
  render() {
396
386
  return html `${this.renderLabel()}
397
387
  <div part="field" @focusin=${this.handleFocusIn} @focusout=${this.handleFocusOut}>
@@ -426,7 +416,7 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
426
416
  aria-label=${this.clearLabel}
427
417
  @click=${this.handleClear}
428
418
  >
429
- ${drawIcon(x)}
419
+ ${this.renderClearIcon()}
430
420
  </button>`
431
421
  : nothing}
432
422
  </div>
@@ -0,0 +1,20 @@
1
+ import { type TemplateResult } from 'lit';
2
+ /**
3
+ * Each date field `Intl` names, drawn as a part of its own.
4
+ *
5
+ * Keyed by the `type` of `Intl.DateTimeFormat#formatToParts`. Written out one
6
+ * template to a field rather than bound through one `part` binding, because a
7
+ * part is read from the source as a literal: a bound name is a part no
8
+ * stylesheet check can find. A type left out, the punctuation between the
9
+ * fields among them, is drawn as plain text.
10
+ */
11
+ export declare const FIELD: Record<string, (value: string) => TemplateResult>;
12
+ /**
13
+ * A formatted date, each field in a part of its own and the text between them
14
+ * as it is.
15
+ *
16
+ * The words are the ones `format` would give, in the same order, so the reading
17
+ * a screen reader hears and the text a reader copies do not change: an inline
18
+ * span with no style of its own changes nothing on the page either.
19
+ */
20
+ export declare const drawFields: (parts: readonly Intl.DateTimeFormatPart[]) => unknown[];
@@ -0,0 +1,14 @@
1
+ import { html } from 'lit';
2
+ export const FIELD = {
3
+ day: (value) => html `<span part="day">${value}</span>`,
4
+ dayPeriod: (value) => html `<span part="day-period">${value}</span>`,
5
+ era: (value) => html `<span part="era">${value}</span>`,
6
+ hour: (value) => html `<span part="hour">${value}</span>`,
7
+ minute: (value) => html `<span part="minute">${value}</span>`,
8
+ month: (value) => html `<span part="month">${value}</span>`,
9
+ second: (value) => html `<span part="second">${value}</span>`,
10
+ timeZoneName: (value) => html `<span part="time-zone-name">${value}</span>`,
11
+ weekday: (value) => html `<span part="weekday">${value}</span>`,
12
+ year: (value) => html `<span part="year">${value}</span>`,
13
+ };
14
+ export const drawFields = (parts) => parts.map(({ type, value }) => FIELD[type]?.(value) ?? value);
@@ -1,9 +1,8 @@
1
1
  /**
2
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.
3
+ * consumer set. The element itself is one, so it passes itself.
4
4
  */
5
5
  export interface Asked {
6
- dateOnly: boolean;
7
6
  dateStyle?: 'full' | 'long' | 'medium' | 'short' | undefined;
8
7
  day?: 'numeric' | '2-digit' | undefined;
9
8
  era?: 'long' | 'narrow' | 'short' | undefined;
@@ -19,15 +18,16 @@ export interface Asked {
19
18
  year?: 'numeric' | '2-digit' | undefined;
20
19
  }
21
20
  /**
22
- * The `Intl.DateTimeFormat` options for what was asked, and none that was not.
21
+ * The `Intl.DateTimeFormat` options for what was asked, and none that was not:
22
+ * an option not asked for is `undefined`, which `Intl` reads as absent.
23
23
  *
24
24
  * **A style wins over the fields.** `dateStyle` and `timeStyle` are each
25
25
  * locale's own whole pattern, and `Intl` throws a `TypeError` when one is
26
26
  * given beside `month` or `hour`, so the fields are left out wherever a style
27
27
  * is set. `hourCycle` and `timeZone` go with either.
28
28
  *
29
- * **A day with no time is read in UTC** unless a zone is given. Parsed, it is
29
+ * **A day with no time, `dateOnly`, is read in UTC** unless a zone is given. Parsed, it is
30
30
  * midnight UTC, and read in a zone west of it that is the day before: measured
31
31
  * before this, `2026-08-04` read "August 3, 2026" in Los Angeles.
32
32
  */
33
- export declare const formatOptions: (asked: Asked) => Intl.DateTimeFormatOptions;
33
+ export declare const formatOptions: (asked: Asked, dateOnly?: boolean) => Intl.DateTimeFormatOptions;
@@ -1,42 +1,13 @@
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';
1
+ import { FIELD } from './fields.js';
2
+ const FIELDS = Object.keys(FIELD);
3
+ export const formatOptions = (asked, dateOnly = false) => {
4
+ const options = {
5
+ hourCycle: asked.hourCycle,
6
+ timeZone: asked.timeZone || (dateOnly ? 'UTC' : undefined),
7
+ };
8
+ const styled = asked.dateStyle || asked.timeStyle;
9
+ for (const field of styled ? ['dateStyle', 'timeStyle'] : FIELDS) {
10
+ options[field] = asked[field];
40
11
  }
41
12
  return options;
42
13
  };