@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
@@ -4,6 +4,7 @@ registerElementEvents({
4
4
  'helix-headless-announcement': ['helix-dismiss'],
5
5
  'helix-headless-app-header': ['helix-scrolled'],
6
6
  'helix-headless-avatar': ['error', 'load'],
7
+ 'helix-headless-callout': ['helix-dismiss'],
7
8
  'helix-headless-canvas': [
8
9
  'helix-before-move',
9
10
  'helix-before-reconnect',
@@ -19,9 +20,9 @@ registerElementEvents({
19
20
  'helix-remove',
20
21
  'helix-select',
21
22
  ],
22
- 'helix-headless-carousel': ['helix-select'],
23
- 'helix-headless-chat': ['helix-before-pin', 'helix-history', 'helix-send'],
24
- 'helix-headless-chat-composer': ['helix-send'],
23
+ 'helix-headless-carousel': ['helix-activate', 'helix-select'],
24
+ 'helix-headless-chat': ['helix-before-pin', 'helix-history', 'helix-pin', 'helix-send'],
25
+ 'helix-headless-chat-composer': ['helix-send', 'input'],
25
26
  'helix-headless-chat-message': ['helix-send'],
26
27
  'helix-headless-checkbox': ['change', 'helix-before-toggle', 'input'],
27
28
  'helix-headless-checkbox-group': ['change', 'helix-before-change', 'input'],
@@ -51,14 +52,25 @@ registerElementEvents({
51
52
  'input',
52
53
  'toggle',
53
54
  ],
54
- 'helix-headless-command': ['helix-before-close', 'helix-before-open', 'helix-select', 'toggle'],
55
- 'helix-headless-copy-button': ['helix-copy'],
55
+ 'helix-headless-command': [
56
+ 'helix-before-close',
57
+ 'helix-before-open',
58
+ 'helix-filter',
59
+ 'helix-select',
60
+ 'toggle',
61
+ ],
62
+ 'helix-headless-copy-button': ['helix-copy', 'helix-copy-error'],
56
63
  'helix-headless-data-table': [
64
+ 'helix-activate',
57
65
  'helix-filter',
66
+ 'helix-filtered',
58
67
  'helix-navigate',
59
68
  'helix-reorder',
69
+ 'helix-reordered',
60
70
  'helix-select',
71
+ 'helix-selected',
61
72
  'helix-sort',
73
+ 'helix-sorted',
62
74
  ],
63
75
  'helix-headless-date-input': [
64
76
  'change',
@@ -73,7 +85,7 @@ registerElementEvents({
73
85
  'helix-headless-drawer': ['close', 'helix-before-close', 'helix-before-open', 'toggle'],
74
86
  'helix-headless-drilldown': ['helix-select'],
75
87
  'helix-headless-drilldown-item': ['helix-select'],
76
- 'helix-headless-file-input': ['change', 'helix-before-change', 'input'],
88
+ 'helix-headless-file-input': ['change', 'helix-before-change', 'helix-reject', 'input'],
77
89
  'helix-headless-filter-chip': ['helix-dismiss'],
78
90
  'helix-headless-filter-input': [
79
91
  'change',
@@ -99,9 +111,11 @@ registerElementEvents({
99
111
  'helix-graph-zoom',
100
112
  ],
101
113
  'helix-headless-heatmap': ['helix-resize'],
114
+ 'helix-headless-icon': ['error', 'load'],
102
115
  'helix-headless-image': ['error', 'load'],
103
- 'helix-headless-input': ['change', 'helix-before-change', 'input'],
116
+ 'helix-headless-input': ['change', 'helix-before-change', 'helix-clear', 'input'],
104
117
  'helix-headless-kbd': ['helix-shortcut'],
118
+ 'helix-headless-legend': ['change', 'helix-before-change'],
105
119
  'helix-headless-lightbox': [
106
120
  'close',
107
121
  'helix-activate',
@@ -112,7 +126,10 @@ registerElementEvents({
112
126
  ],
113
127
  'helix-headless-listbox': ['change', 'helix-before-change', 'helix-filter', 'input'],
114
128
  'helix-headless-map': [
129
+ 'change',
115
130
  'helix-activate',
131
+ 'helix-before-change',
132
+ 'helix-before-toggle',
116
133
  'helix-map-cluster',
117
134
  'helix-map-pan',
118
135
  'helix-map-part-change',
@@ -133,26 +150,28 @@ registerElementEvents({
133
150
  'helix-headless-map-terrain': ['helix-map-part-change'],
134
151
  'helix-headless-megamenu': ['helix-before-close', 'helix-before-open', 'toggle'],
135
152
  'helix-headless-menu': ['helix-before-close', 'helix-before-open', 'helix-select', 'toggle'],
136
- 'helix-headless-menu-item': ['helix-select'],
153
+ 'helix-headless-menu-item': ['helix-before-toggle', 'helix-select'],
137
154
  'helix-headless-notification': ['helix-dismiss', 'helix-read'],
138
- 'helix-headless-notification-group': ['helix-before-close', 'helix-before-open'],
155
+ 'helix-headless-notification-group': ['helix-before-close', 'helix-before-open', 'toggle'],
139
156
  'helix-headless-notifications': ['helix-dismiss', 'helix-read'],
140
157
  'helix-headless-number-input': ['change', 'helix-before-change', 'input'],
141
158
  'helix-headless-object-inspector': ['helix-copy'],
142
159
  'helix-headless-option': ['helix-select'],
143
160
  'helix-headless-outline': ['helix-activate'],
144
- 'helix-headless-pagination': ['helix-navigate'],
145
- 'helix-headless-pagination-size': ['helix-before-change'],
161
+ 'helix-headless-pagination': ['helix-activate', 'helix-navigate'],
162
+ 'helix-headless-pagination-size': ['change', 'helix-before-change', 'input'],
146
163
  'helix-headless-pin-input': ['change', 'helix-before-change', 'helix-complete', 'input'],
147
164
  'helix-headless-popover': ['helix-before-close', 'helix-before-open', 'toggle'],
165
+ 'helix-headless-qr-code': ['error'],
148
166
  'helix-headless-radio': ['helix-select'],
149
167
  'helix-headless-radio-group': ['change', 'helix-before-change', 'helix-select', 'input'],
150
- 'helix-headless-rating': ['change', 'helix-before-change', 'input'],
168
+ 'helix-headless-rating': ['change', 'helix-before-change', 'helix-clear', 'input'],
151
169
  'helix-headless-select': [
152
170
  'change',
153
171
  'helix-before-change',
154
172
  'helix-before-close',
155
173
  'helix-before-open',
174
+ 'helix-filter',
156
175
  'input',
157
176
  'toggle',
158
177
  ],
@@ -164,14 +183,21 @@ registerElementEvents({
164
183
  'toggle',
165
184
  ],
166
185
  'helix-headless-speed-dial-item': ['helix-select'],
167
- 'helix-headless-splitter': ['helix-before-collapse', 'helix-before-resize'],
186
+ 'helix-headless-splitter': [
187
+ 'helix-before-collapse',
188
+ 'helix-before-resize',
189
+ 'helix-collapse',
190
+ 'helix-resized',
191
+ ],
168
192
  'helix-headless-step': ['helix-select'],
169
193
  'helix-headless-steps': ['helix-activate', 'helix-before-activate', 'helix-select'],
170
194
  'helix-headless-switch': ['change', 'helix-before-toggle', 'input'],
171
195
  'helix-headless-tab': ['helix-select'],
196
+ 'helix-headless-table': ['helix-sort'],
172
197
  'helix-headless-table-column': ['helix-before-resize'],
173
198
  'helix-headless-tabs': ['helix-activate', 'helix-before-activate', 'helix-select'],
174
199
  'helix-headless-tag': ['helix-dismiss'],
200
+ 'helix-headless-tag-group': ['change', 'helix-before-change', 'helix-dismiss'],
175
201
  'helix-headless-textarea': ['change', 'helix-before-change', 'input'],
176
202
  'helix-headless-time-input': [
177
203
  'change',
@@ -188,10 +214,13 @@ registerElementEvents({
188
214
  'helix-headless-toggle-group': ['change', 'helix-before-change', 'helix-select', 'input'],
189
215
  'helix-headless-tooltip': ['helix-before-close', 'helix-before-open', 'toggle'],
190
216
  'helix-headless-tour': [
217
+ 'helix-activate',
191
218
  'helix-before-activate',
192
219
  'helix-before-close',
220
+ 'helix-before-open',
193
221
  'helix-complete',
194
222
  'helix-tour-ask',
223
+ 'toggle',
195
224
  ],
196
225
  'helix-headless-tour-stop': ['helix-tour-ask'],
197
226
  'helix-headless-tree': ['helix-activate', 'helix-before-activate', 'helix-select'],
@@ -25,7 +25,7 @@ import { type TemplateResult } from 'lit';
25
25
  *
26
26
  * @slot - The message: why the region is empty, and what to do about it.
27
27
  * @slot actions - The action that would put something in the region.
28
- * @slot heading - The heading, which names what is missing.
28
+ * @slot heading - The heading, which names what is missing. Slot a heading element, such as an `<h3>`, so a reader can find it in the page's outline.
29
29
  * @slot icon - An icon above the heading.
30
30
  *
31
31
  * @csspart live - A copy of the heading and the message, in a live region that screen readers announce when the view appears. Visually hidden in the styled tier.
@@ -16,6 +16,12 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
16
16
  * visible trigger is a `<label for>`. The label is not focusable. The input is,
17
17
  * and Enter and Space on it open the picker.
18
18
  *
19
+ * Files arrive from the picker, a drop, or a paste onto the field, and each one
20
+ * is screened: `accept`, `max-file-size`, `max-files` and your own `validate`
21
+ * can refuse it. A refused file is left out, `helix-reject` lists each one with
22
+ * its reason, and a reader hears `rejected-text` with the names filled in.
23
+ * `directory` asks the picker for a folder.
24
+ *
19
25
  * @since 0.1.0
20
26
  * @status stable
21
27
  * @category forms
@@ -24,10 +30,12 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
24
30
  *
25
31
  * @slot feedback - The message that says what is wrong with the chosen files.
26
32
  * @slot hint - Help under the field.
33
+ * @slot label - The label, for content richer than the `label` text.
27
34
  * @slot trigger - The content that opens the picker when pressed.
28
35
  *
29
36
  * @event {Event} change - Fired after the value changes.
30
37
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired before the chosen files change, with their names, and cancelable. Canceling it keeps the files the field had, even when the browser has already written a new pick.
38
+ * @event {HelixRejectEvent} helix-reject - Fired when files arrive and some are left out. `detail.files` lists each with its `reason`: `type`, `size`, `count` or `invalid`, and your `validate` message.
31
39
  * @event {Event} input - Fired after the value changes.
32
40
  *
33
41
  * @csspart control - The file input itself, which carries the label and validity.
@@ -37,19 +45,24 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
37
45
  * @csspart file - One chosen file.
38
46
  * @csspart hint - Help under the field, while nothing is wrong.
39
47
  * @csspart kind - A file's type.
48
+ * @csspart kind-cell - The grid cell around the tag, rendered only when the tag sits in a `Tag.Group`.
40
49
  * @csspart kind-dismiss - The remove button, when `dismissible`.
41
50
  * @csspart kind-dismiss-mark - The glyph on the dismiss control, where none is slotted.
42
51
  * @csspart kind-label - The label, trimmed to its capitals and baseline.
52
+ * @csspart kind-link - The link around the label, when the tag has an `href`.
43
53
  * @csspart kind-start - The category before the label.
44
54
  * @csspart label - The label above the field.
45
55
  * @csspart list - The chosen files.
56
+ * @csspart live - The live region that says `rejected-text` when files are refused. It is rendered from the first refusal.
46
57
  * @csspart meta - The row that holds a file's size and type.
47
58
  * @csspart name - A file's name.
48
59
  * @csspart remove - The control that removes a file.
49
60
  * @csspart size - A file's size.
61
+ * @csspart size-cell - The grid cell around the tag, rendered only when the tag sits in a `Tag.Group`.
50
62
  * @csspart size-dismiss - The remove button, when `dismissible`.
51
63
  * @csspart size-dismiss-mark - The glyph on the dismiss control, where none is slotted.
52
64
  * @csspart size-label - The label, trimmed to its capitals and baseline.
65
+ * @csspart size-link - The link around the label, when the tag has an `href`.
53
66
  * @csspart size-start - The category before the label.
54
67
  * @csspart trigger - The label that opens the picker.
55
68
  *
@@ -58,23 +71,45 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
58
71
  export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base {
59
72
  protected readonly aria: AriaForwardController;
60
73
  /**
61
- * Which files the picker offers, as a native `accept` list.
74
+ * Which files the picker offers and a drop takes, as a native `accept` list.
75
+ *
76
+ * A dropped file the list does not name is left out, because the picker's
77
+ * filter is the only check the platform makes and a drop never meets it.
62
78
  */
63
79
  accept: string;
64
80
  /**
65
81
  * Which camera or microphone a mobile picker should offer, if any.
66
82
  */
67
83
  capture?: 'environment' | 'user';
84
+ /**
85
+ * Whether the picker chooses a folder, and the field takes every file in
86
+ * it. The platform's `webkitdirectory`.
87
+ */
88
+ directory: boolean;
68
89
  /**
69
90
  * Language tag the sizes are written in.
70
91
  *
71
92
  * Absent uses the runtime's own.
72
93
  */
73
94
  locale: string;
95
+ /**
96
+ * The most files the field holds, under `multiple`. Files past it are
97
+ * refused.
98
+ */
99
+ maxFiles?: number;
100
+ /**
101
+ * The most bytes one file may have. A larger one is refused.
102
+ */
103
+ maxFileSize?: number;
74
104
  /**
75
105
  * Whether more than one file can be chosen.
76
106
  */
77
107
  multiple: boolean;
108
+ /**
109
+ * What a reader hears when files are refused, with `{count}` and `{names}`
110
+ * filled in.
111
+ */
112
+ rejectedText: string;
78
113
  /**
79
114
  * The accessible name of the control that removes a file. `{name}` is
80
115
  * replaced with the file's name.
@@ -88,6 +123,22 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
88
123
  * The trigger's text when nothing is slotted into it.
89
124
  */
90
125
  triggerLabel: string;
126
+ /**
127
+ * Your own check on each arriving file, which returns why it is refused, or
128
+ * nothing to take it. A property, since a function cannot be an attribute.
129
+ */
130
+ validate?: (file: File) => string | null | undefined;
131
+ /** The live region, rendered from the first refusal. */
132
+ private readonly status;
133
+ /**
134
+ * Screens arriving files and commits what is taken, after the files the
135
+ * field keeps. Refused files are announced and said.
136
+ */
137
+ private take;
138
+ /** Sends `helix-reject` and says which files were left out. */
139
+ private refuse;
140
+ /** Takes files pasted onto the field, as a drop takes them. */
141
+ private readonly handlePaste;
91
142
  /**
92
143
  * Whether something is being dragged over the field.
93
144
  *
@@ -185,8 +236,21 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
185
236
  private readonly handleDragOver;
186
237
  private readonly handleDragLeave;
187
238
  private readonly handleDrop;
239
+ /**
240
+ * Removes a file and keeps focus in the field.
241
+ *
242
+ * The pressed button goes with its file, so focus moves to the file that took
243
+ * its place, or to the one before it, or to the input once the list is empty:
244
+ * left alone, it fell to the page's body.
245
+ */
188
246
  private readonly handleRemove;
189
247
  protected firstUpdated(changed: PropertyValues): void;
248
+ /**
249
+ * Reports again on every update, so a new `name` or `required` reaches the
250
+ * form. Only a pick and the first render reported, so the form kept the old
251
+ * name and the old verdict.
252
+ */
253
+ protected updated(changed: PropertyValues): void;
190
254
  /** Moves focus to the native file input, as a label pointing at it would. */
191
255
  focus(options?: FocusOptions): void;
192
256
  /**
@@ -4,11 +4,12 @@ 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, fillTemplate, HelixBeforeChangeEvent, HelixFormElement, MixinField, requestChange, restoreValue, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, drawIcon, drawStatus, fillTemplate, formatNumbers, HelixBeforeChangeEvent, HelixFormElement, HelixRejectEvent, MixinField, requestChange, restoreValue, 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
+ import { screenFiles } from '../screen.js';
12
13
  import '../../tag/index.js';
13
14
  const named = (label, name) => label.includes('{name}') ? fillTemplate(label, { name }) : `${label} ${name}`;
14
15
  const size = (bytes, locale) => {
@@ -26,29 +27,41 @@ const size = (bytes, locale) => {
26
27
  return `${written} ${unit}`;
27
28
  };
28
29
  const kind = (file) => {
29
- const subtype = file.type.split('/')[1];
30
- if (subtype) {
31
- return subtype.replace(/^x-/, '').split('+')[0]?.toUpperCase() ?? 'File';
30
+ const extension = /\.([a-z0-9]{1,6})$/i.exec(file.name)?.[1];
31
+ if (extension) {
32
+ return extension.toUpperCase();
32
33
  }
33
- const extension = file.name.includes('.') ? file.name.split('.').pop() : '';
34
- return extension ? extension.toUpperCase() : 'File';
34
+ const subtype = file.type.split('/')[1]?.replace(/^x-/, '').split('+')[0] ?? '';
35
+ return /^[a-z0-9]{1,6}$/i.test(subtype) ? subtype.toUpperCase() : 'File';
35
36
  };
36
37
  let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(HelixFormElement) {
37
38
  constructor() {
38
39
  super(...arguments);
39
40
  this.aria = new AriaForwardController(this, {
41
+ named: () => this.labeled,
40
42
  target: () => this.partNamed('control'),
41
43
  });
42
44
  this.accept = '';
45
+ this.directory = false;
43
46
  this.locale = '';
44
47
  this.multiple = false;
48
+ this.rejectedText = 'Not added: {names}';
45
49
  this.removeLabel = 'Remove {name}';
46
50
  this.triggerLabel = 'Choose files';
51
+ this.status = new StatusController(this);
52
+ this.handlePaste = (event) => {
53
+ const pasted = [...(event.clipboardData?.files ?? [])];
54
+ if (this.isDisabled || pasted.length === 0) {
55
+ return;
56
+ }
57
+ event.preventDefault();
58
+ this.take(pasted, this.multiple ? this.files : []);
59
+ };
47
60
  this.dropping = false;
48
61
  this.committed = [];
49
62
  this.handleChange = (event) => {
50
63
  event.stopPropagation();
51
- this.commit(this.files);
64
+ this.take(this.files, [], true);
52
65
  };
53
66
  this.handleDragOver = (event) => {
54
67
  if (this.isDisabled) {
@@ -71,15 +84,44 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
71
84
  if (dropped.length === 0) {
72
85
  return;
73
86
  }
74
- this.commit(this.multiple ? [...this.files, ...dropped] : dropped.slice(-1));
87
+ this.take(dropped, this.multiple ? this.files : []);
75
88
  };
76
89
  this.handleRemove = (event) => {
77
90
  const at = Number(event.currentTarget.dataset.at);
78
- if (Number.isFinite(at)) {
79
- this.removeAt(at);
91
+ if (!Number.isFinite(at)) {
92
+ return;
80
93
  }
94
+ this.removeAt(at);
95
+ this.updateComplete.then(() => {
96
+ const buttons = [...this.renderRoot.querySelectorAll('[part="remove"]')];
97
+ (buttons[Math.min(at, buttons.length - 1)] ?? this.control)?.focus();
98
+ });
81
99
  };
82
100
  }
101
+ take(arriving, keeping, picked = false) {
102
+ const { kept, refused } = screenFiles(arriving, keeping, {
103
+ accept: this.accept,
104
+ maxFileSize: this.maxFileSize,
105
+ maxFiles: this.maxFiles,
106
+ multiple: this.multiple || picked,
107
+ validate: this.validate,
108
+ });
109
+ const same = (one, two) => one.length === two.length && one.every((file, at) => file === two[at]);
110
+ if (picked || !(same(kept, this.committed) && same(kept, this.files))) {
111
+ this.commit(kept);
112
+ }
113
+ if (refused.length > 0) {
114
+ this.refuse(refused);
115
+ }
116
+ }
117
+ refuse(refused) {
118
+ this.dispatchEvent(new HelixRejectEvent({ files: refused }));
119
+ const words = fillTemplate(this.rejectedText, {
120
+ count: formatNumbers(this.lang)(refused.length),
121
+ names: new Intl.ListFormat(this.lang || undefined, { style: 'short', type: 'unit' }).format(refused.map((one) => one.file.name)),
122
+ });
123
+ this.status.say(words);
124
+ }
83
125
  get files() {
84
126
  return [...(this.control?.files ?? [])];
85
127
  }
@@ -152,6 +194,10 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
152
194
  this.committed = this.files;
153
195
  this.sync();
154
196
  }
197
+ updated(changed) {
198
+ super.updated(changed);
199
+ this.sync();
200
+ }
155
201
  focus(options) {
156
202
  this.control?.focus(options);
157
203
  }
@@ -164,10 +210,10 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
164
210
  }
165
211
  renderMeta(file) {
166
212
  return html `<${this.tag('tag')} part="size"
167
- exportparts="dismiss: size-dismiss, dismiss-mark: size-dismiss-mark, label: size-label, start: size-start">${size(file.size, this.locale)}</${this.tag('tag')}>
213
+ exportparts="cell: size-cell, dismiss: size-dismiss, dismiss-mark: size-dismiss-mark, label: size-label, link: size-link, start: size-start">${size(file.size, this.locale)}</${this.tag('tag')}>
168
214
  <${this.tag('tag')}
169
215
  part="kind"
170
- exportparts="dismiss: kind-dismiss, dismiss-mark: kind-dismiss-mark, label: kind-label, start: kind-start">${kind(file)}</${this.tag('tag')}>`;
216
+ 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')}>`;
171
217
  }
172
218
  render() {
173
219
  const files = this.files;
@@ -178,6 +224,7 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
178
224
  @dragleave=${this.handleDragLeave}
179
225
  @dragover=${this.handleDragOver}
180
226
  @drop=${this.handleDrop}
227
+ @paste=${this.handlePaste}
181
228
  >
182
229
  <input
183
230
  part="control"
@@ -191,6 +238,7 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
191
238
  ?disabled=${this.isDisabled}
192
239
  ?multiple=${this.multiple}
193
240
  ?required=${this.required}
241
+ ?webkitdirectory=${this.directory}
194
242
  @change=${this.handleChange}
195
243
  @input=${this.stopInner}
196
244
  />
@@ -202,6 +250,7 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
202
250
  one ordering decision this component makes differently from the way it
203
251
  reads. Reasoning above renderMeta. -->
204
252
  ${this.renderHint()}${this.renderFeedback()}
253
+ ${drawStatus(this.status)}
205
254
  ${files.length === 0
206
255
  ? nothing
207
256
  : html `<ul part="list">
@@ -232,18 +281,33 @@ __decorate([
232
281
  __decorate([
233
282
  property()
234
283
  ], HelixHeadlessFileInput.prototype, "capture", void 0);
284
+ __decorate([
285
+ property({ type: Boolean })
286
+ ], HelixHeadlessFileInput.prototype, "directory", void 0);
235
287
  __decorate([
236
288
  property()
237
289
  ], HelixHeadlessFileInput.prototype, "locale", void 0);
290
+ __decorate([
291
+ property({ attribute: 'max-files', type: Number })
292
+ ], HelixHeadlessFileInput.prototype, "maxFiles", void 0);
293
+ __decorate([
294
+ property({ attribute: 'max-file-size', type: Number })
295
+ ], HelixHeadlessFileInput.prototype, "maxFileSize", void 0);
238
296
  __decorate([
239
297
  property({ reflect: true, type: Boolean })
240
298
  ], HelixHeadlessFileInput.prototype, "multiple", void 0);
299
+ __decorate([
300
+ property({ attribute: 'rejected-text' })
301
+ ], HelixHeadlessFileInput.prototype, "rejectedText", void 0);
241
302
  __decorate([
242
303
  property({ attribute: 'remove-label' })
243
304
  ], HelixHeadlessFileInput.prototype, "removeLabel", void 0);
244
305
  __decorate([
245
306
  property({ attribute: 'trigger-label' })
246
307
  ], HelixHeadlessFileInput.prototype, "triggerLabel", void 0);
308
+ __decorate([
309
+ property({ attribute: false })
310
+ ], HelixHeadlessFileInput.prototype, "validate", void 0);
247
311
  __decorate([
248
312
  state()
249
313
  ], HelixHeadlessFileInput.prototype, "dropping", void 0);
@@ -0,0 +1,35 @@
1
+ import { type RefusedFile } from '@fusion.dev/helix-core';
2
+ /**
3
+ * Which of the files arriving at a file field it takes, and why it refuses the
4
+ * rest: the rules `accept`, `max-file-size`, `max-files` and `validate` set.
5
+ * Zag's file upload screens a drop the same way and reports each refused file
6
+ * with its reason.
7
+ */
8
+ export type { Refusal, RefusedFile } from '@fusion.dev/helix-core';
9
+ /** The rules a field screens arriving files by. */
10
+ export interface ScreenRules {
11
+ /** A native `accept` list. Empty takes every type. */
12
+ readonly accept: string;
13
+ /** The most bytes one file may have. */
14
+ readonly maxFileSize?: number | undefined;
15
+ /** The most files the field holds, counting the ones it has. */
16
+ readonly maxFiles?: number | undefined;
17
+ /** Whether the field holds more than one file. */
18
+ readonly multiple: boolean;
19
+ /** Your own check, which returns why a file is refused, or nothing. */
20
+ readonly validate?: ((file: File) => string | null | undefined) | undefined;
21
+ }
22
+ /**
23
+ * Whether a file is one `accept` lists: an extension, a type, or a family
24
+ * such as `image/*`, as the platform reads it.
25
+ */
26
+ export declare const accepts: (file: File, accept: string) => boolean;
27
+ /**
28
+ * Screens arriving files against the rules, given the files the field keeps.
29
+ * `kept` is what the field holds afterward: the files it keeps and the
30
+ * arrivals it took, or for a single-file field the last arrival it took.
31
+ */
32
+ export declare const screenFiles: (arriving: File[], keeping: File[], rules: ScreenRules) => {
33
+ kept: File[];
34
+ refused: RefusedFile[];
35
+ };
@@ -0,0 +1,49 @@
1
+ import { commaList } from '@fusion.dev/helix-core';
2
+ export const accepts = (file, accept) => {
3
+ const tokens = commaList(accept).map((token) => token.toLowerCase());
4
+ if (tokens.length === 0) {
5
+ return true;
6
+ }
7
+ const name = file.name.toLowerCase();
8
+ const type = file.type.toLowerCase();
9
+ return tokens.some((token) => token.startsWith('.')
10
+ ? name.endsWith(token)
11
+ : token.endsWith('/*')
12
+ ? type.startsWith(token.slice(0, -1))
13
+ : type === token);
14
+ };
15
+ const refusal = (file, rules) => {
16
+ if (!accepts(file, rules.accept)) {
17
+ return { file, reason: 'type' };
18
+ }
19
+ if (rules.maxFileSize !== undefined && file.size > rules.maxFileSize) {
20
+ return { file, reason: 'size' };
21
+ }
22
+ const message = rules.validate?.(file);
23
+ return message ? { file, message, reason: 'invalid' } : undefined;
24
+ };
25
+ export const screenFiles = (arriving, keeping, rules) => {
26
+ const refused = [];
27
+ const taken = [];
28
+ for (const file of arriving) {
29
+ const refusedFor = refusal(file, rules);
30
+ if (refusedFor) {
31
+ refused.push(refusedFor);
32
+ }
33
+ else {
34
+ taken.push(file);
35
+ }
36
+ }
37
+ if (!rules.multiple) {
38
+ const last = taken.at(-1);
39
+ for (const file of taken.slice(0, -1)) {
40
+ refused.push({ file, reason: 'count' });
41
+ }
42
+ return { kept: last ? [last] : keeping.slice(0, 1), refused };
43
+ }
44
+ const room = rules.maxFiles === undefined ? taken.length : Math.max(0, rules.maxFiles - keeping.length);
45
+ for (const file of taken.slice(room)) {
46
+ refused.push({ file, reason: 'count' });
47
+ }
48
+ return { kept: [...keeping, ...taken.slice(0, room)], refused };
49
+ };
@@ -151,6 +151,15 @@ export declare class HelixHeadlessFilterMenu extends HelixElement {
151
151
  * a filter is never left looking at a menu that says it took.
152
152
  */
153
153
  apply(field: string, values: readonly string[], operator?: string): boolean;
154
+ /**
155
+ * Keeps each choice row's tick where this element puts it.
156
+ *
157
+ * A choice row checks itself after `helix-before-toggle`, and this element
158
+ * already sets `checked` on every row from its own state. The request is
159
+ * refused, so a tick moves only when the choice is applied, and stopped, so
160
+ * it does not reach a page that never placed the row.
161
+ */
162
+ private readonly keepTick;
154
163
  /**
155
164
  * A row was chosen: a value to tick, or a comparison to use.
156
165
  *
@@ -17,6 +17,12 @@ let HelixHeadlessFilterMenu = class HelixHeadlessFilterMenu extends HelixElement
17
17
  this.label = 'Filter';
18
18
  this.picked = new Map();
19
19
  this.comparisons = new Map();
20
+ this.keepTick = (event) => {
21
+ if (event.target === event.currentTarget) {
22
+ event.preventDefault();
23
+ event.stopPropagation();
24
+ }
25
+ };
20
26
  this.handleSelect = (event) => {
21
27
  const row = event.composedPath()[0];
22
28
  if (!(row instanceof HTMLElement)) {
@@ -152,6 +158,7 @@ let HelixHeadlessFilterMenu = class HelixHeadlessFilterMenu extends HelixElement
152
158
  checkable
153
159
  exclusive
154
160
  ?checked=${operator.name === using?.name}
161
+ @helix-before-toggle=${this.keepTick}
155
162
  data-facet="operator"
156
163
  data-field=${name}
157
164
  data-value=${operator.name}
@@ -173,6 +180,7 @@ let HelixHeadlessFilterMenu = class HelixHeadlessFilterMenu extends HelixElement
173
180
  checkable
174
181
  ?exclusive=${using?.arity === 'one'}
175
182
  ?checked=${ticked.has(option.value)}
183
+ @helix-before-toggle=${this.keepTick}
176
184
  data-facet="value"
177
185
  data-field=${name}
178
186
  data-value=${option.value}