@fusion.dev/helix-headless 0.145.0 → 0.147.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 (343) hide show
  1. package/custom-elements.json +12286 -4281
  2. package/dist/accordion/item/accordion-item.d.ts +51 -18
  3. package/dist/accordion/item/accordion-item.js +45 -13
  4. package/dist/accordion/root/accordion-root.d.ts +16 -7
  5. package/dist/accordion/root/accordion-root.js +3 -3
  6. package/dist/announcement/root/announcement-root.d.ts +16 -6
  7. package/dist/announcement/root/announcement-root.js +37 -7
  8. package/dist/app-header/root/app-header-root.d.ts +7 -2
  9. package/dist/app-header/root/app-header-root.js +9 -1
  10. package/dist/app-header/sectioning.d.ts +3 -1
  11. package/dist/app-header/sectioning.js +3 -2
  12. package/dist/area-chart/root/area-chart-root.d.ts +18 -1
  13. package/dist/area-chart/root/area-chart-root.js +11 -3
  14. package/dist/avatar/group/avatar-group.d.ts +20 -14
  15. package/dist/avatar/group/avatar-group.js +17 -19
  16. package/dist/avatar/root/avatar-root.d.ts +31 -1
  17. package/dist/avatar/root/avatar-root.js +11 -1
  18. package/dist/badge/root/badge-root.d.ts +60 -7
  19. package/dist/badge/root/badge-root.js +89 -6
  20. package/dist/bar-chart/root/bar-chart-root.d.ts +18 -1
  21. package/dist/bar-chart/root/bar-chart-root.js +48 -20
  22. package/dist/bar-chart/stack.d.ts +25 -0
  23. package/dist/bar-chart/stack.js +27 -0
  24. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +14 -0
  25. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +12 -2
  26. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +25 -0
  27. package/dist/breadcrumbs/root/breadcrumbs-root.js +51 -4
  28. package/dist/button/root/button-root.d.ts +50 -38
  29. package/dist/button/root/button-root.js +42 -53
  30. package/dist/button/submitter.d.ts +27 -0
  31. package/dist/button/submitter.js +25 -0
  32. package/dist/button-group/root/button-group-root.d.ts +2 -1
  33. package/dist/button-group/root/button-group-root.js +5 -1
  34. package/dist/canvas/node/canvas-node.d.ts +4 -3
  35. package/dist/canvas/node/canvas-node.js +3 -2
  36. package/dist/canvas/root/canvas-root.js +2 -2
  37. package/dist/card/root/card-root.d.ts +36 -4
  38. package/dist/card/root/card-root.js +29 -10
  39. package/dist/carousel/root/carousel-root.d.ts +43 -4
  40. package/dist/carousel/root/carousel-root.js +40 -12
  41. package/dist/carousel/stops.d.ts +20 -0
  42. package/dist/carousel/stops.js +30 -0
  43. package/dist/chat/message/chat-message.js +5 -4
  44. package/dist/chat/naming.d.ts +5 -1
  45. package/dist/chat/naming.js +4 -2
  46. package/dist/chat/root/chat-root.d.ts +40 -4
  47. package/dist/chat/root/chat-root.js +32 -6
  48. package/dist/checkbox/root/checkbox-root.d.ts +20 -0
  49. package/dist/checkbox/root/checkbox-root.js +18 -7
  50. package/dist/checkbox-group/root/checkbox-group-root.d.ts +8 -2
  51. package/dist/checkbox-group/root/checkbox-group-root.js +14 -11
  52. package/dist/checkbox-group/select-all.d.ts +21 -0
  53. package/dist/checkbox-group/select-all.js +7 -0
  54. package/dist/code-block/highlight.d.ts +15 -0
  55. package/dist/code-block/highlight.js +4 -0
  56. package/dist/code-block/root/code-block-root.d.ts +79 -10
  57. package/dist/code-block/root/code-block-root.js +74 -5
  58. package/dist/color-input/root/color-input-root.d.ts +14 -0
  59. package/dist/color-input/root/color-input-root.js +17 -7
  60. package/dist/color-picker/channels.js +1 -2
  61. package/dist/color-picker/root/color-picker-root.d.ts +53 -16
  62. package/dist/color-picker/root/color-picker-root.js +155 -46
  63. package/dist/color-picker/wheel.d.ts +17 -0
  64. package/dist/color-picker/wheel.js +6 -0
  65. package/dist/combobox/root/combobox-root.d.ts +56 -0
  66. package/dist/combobox/root/combobox-root.js +92 -15
  67. package/dist/command/root/command-root.d.ts +19 -2
  68. package/dist/command/root/command-root.js +24 -23
  69. package/dist/copy-button/root/copy-button-root.d.ts +21 -1
  70. package/dist/copy-button/root/copy-button-root.js +16 -4
  71. package/dist/data-table/index.d.ts +1 -1
  72. package/dist/data-table/index.js +1 -1
  73. package/dist/data-table/keys.d.ts +6 -0
  74. package/dist/data-table/keys.js +14 -0
  75. package/dist/data-table/root/data-table-root.d.ts +27 -18
  76. package/dist/data-table/root/data-table-root.js +31 -57
  77. package/dist/data-table/tree.d.ts +16 -1
  78. package/dist/data-table/tree.js +2 -3
  79. package/dist/date-input/root/date-input-root.d.ts +11 -2
  80. package/dist/date-input/root/date-input-root.js +26 -12
  81. package/dist/date-picker/root/date-picker-root.d.ts +14 -9
  82. package/dist/date-picker/root/date-picker-root.js +33 -22
  83. package/dist/description-list/item/description-list-item.d.ts +18 -4
  84. package/dist/description-list/item/description-list-item.js +8 -4
  85. package/dist/dialog/description.js +15 -1
  86. package/dist/dialog/root/dialog-root.d.ts +8 -2
  87. package/dist/dialog/root/dialog-root.js +16 -1
  88. package/dist/divider/root/divider-root.d.ts +3 -0
  89. package/dist/divider/root/divider-root.js +2 -1
  90. package/dist/donut-chart/root/donut-chart-root.d.ts +31 -0
  91. package/dist/donut-chart/root/donut-chart-root.js +55 -1
  92. package/dist/donut-chart/slice-label.d.ts +35 -0
  93. package/dist/donut-chart/slice-label.js +11 -0
  94. package/dist/drawer/reveal.d.ts +8 -0
  95. package/dist/drawer/reveal.js +1 -0
  96. package/dist/drawer/root/drawer-root.d.ts +63 -2
  97. package/dist/drawer/root/drawer-root.js +75 -4
  98. package/dist/drilldown/item/drilldown-item.d.ts +21 -1
  99. package/dist/drilldown/item/drilldown-item.js +40 -5
  100. package/dist/drilldown/root/drilldown-root.d.ts +21 -3
  101. package/dist/drilldown/root/drilldown-root.js +22 -3
  102. package/dist/element-events.generated.js +35 -6
  103. package/dist/empty/root/empty-root.d.ts +34 -2
  104. package/dist/empty/root/empty-root.js +26 -4
  105. package/dist/file-input/root/file-input-root.d.ts +99 -8
  106. package/dist/file-input/root/file-input-root.js +120 -22
  107. package/dist/file-input/screen.d.ts +2 -1
  108. package/dist/file-input/status.d.ts +24 -0
  109. package/dist/file-input/status.js +10 -0
  110. package/dist/filter-input/chip/filter-chip.d.ts +23 -0
  111. package/dist/filter-input/chip/filter-chip.js +11 -2
  112. package/dist/filter-input/menu/filter-menu.d.ts +2 -0
  113. package/dist/filter-input/menu/filter-menu.js +12 -4
  114. package/dist/filter-input/root/filter-input-root.d.ts +38 -6
  115. package/dist/filter-input/root/filter-input-root.js +18 -28
  116. package/dist/filter-input/suggest.d.ts +28 -3
  117. package/dist/filter-input/suggest.js +18 -8
  118. package/dist/format-date/options.d.ts +3 -4
  119. package/dist/format-date/options.js +2 -2
  120. package/dist/format-date/root/format-date-root.d.ts +27 -17
  121. package/dist/format-date/root/format-date-root.js +27 -48
  122. package/dist/heatmap/root/buckets.d.ts +21 -0
  123. package/dist/heatmap/root/buckets.js +34 -0
  124. package/dist/heatmap/root/heatmap-root.d.ts +83 -11
  125. package/dist/heatmap/root/heatmap-root.js +104 -12
  126. package/dist/icon/index.d.ts +1 -1
  127. package/dist/icon/index.js +1 -1
  128. package/dist/icon/registry.d.ts +26 -1
  129. package/dist/icon/registry.js +12 -1
  130. package/dist/icon/root/icon-root.d.ts +25 -1
  131. package/dist/icon/root/icon-root.js +44 -12
  132. package/dist/icon/root/index.d.ts +1 -1
  133. package/dist/icon/root/index.js +1 -1
  134. package/dist/icon/settle.d.ts +7 -0
  135. package/dist/icon/settle.js +10 -0
  136. package/dist/icon-button/root/icon-button-root.d.ts +2 -0
  137. package/dist/image/root/image-root.d.ts +14 -1
  138. package/dist/image/root/image-root.js +7 -2
  139. package/dist/input/root/input-root.d.ts +34 -3
  140. package/dist/input/root/input-root.js +17 -4
  141. package/dist/kbd/keys.d.ts +29 -0
  142. package/dist/kbd/keys.js +52 -0
  143. package/dist/kbd/root/kbd-root.d.ts +52 -1
  144. package/dist/kbd/root/kbd-root.js +40 -47
  145. package/dist/legend/hidden-series.d.ts +10 -2
  146. package/dist/legend/hidden-series.js +5 -2
  147. package/dist/legend/root/legend-root.d.ts +77 -12
  148. package/dist/legend/root/legend-root.js +71 -17
  149. package/dist/lightbox/root/lightbox-root.d.ts +98 -10
  150. package/dist/lightbox/root/lightbox-root.js +153 -18
  151. package/dist/lightbox/zoom-controller.d.ts +59 -0
  152. package/dist/lightbox/zoom-controller.js +128 -0
  153. package/dist/lightbox/zoom.d.ts +24 -0
  154. package/dist/lightbox/zoom.js +18 -0
  155. package/dist/line-chart/root/line-chart-root.d.ts +38 -1
  156. package/dist/line-chart/root/line-chart-root.js +31 -3
  157. package/dist/list/item/list-item.d.ts +2 -0
  158. package/dist/list/item/list-item.js +11 -2
  159. package/dist/list/root/list-root.d.ts +70 -13
  160. package/dist/list/root/list-root.js +91 -13
  161. package/dist/list/selection.d.ts +54 -7
  162. package/dist/list/selection.js +32 -7
  163. package/dist/listbox/root/listbox-root.d.ts +44 -4
  164. package/dist/listbox/root/listbox-root.js +72 -34
  165. package/dist/loader/busy.d.ts +10 -0
  166. package/dist/loader/busy.js +11 -0
  167. package/dist/loader/root/loader-root.d.ts +9 -2
  168. package/dist/loader/root/loader-root.js +23 -3
  169. package/dist/megamenu/hand-over.d.ts +75 -0
  170. package/dist/megamenu/hand-over.js +67 -0
  171. package/dist/megamenu/root/megamenu-root.d.ts +80 -15
  172. package/dist/megamenu/root/megamenu-root.js +76 -19
  173. package/dist/menu/item/menu-item.d.ts +51 -2
  174. package/dist/menu/item/menu-item.js +50 -26
  175. package/dist/menu/root/menu-root.d.ts +17 -0
  176. package/dist/menu/root/menu-root.js +2 -2
  177. package/dist/meter/root/meter-root.d.ts +62 -1
  178. package/dist/meter/root/meter-root.js +38 -16
  179. package/dist/navigation/root/navigation-root.d.ts +2 -1
  180. package/dist/navigation/root/navigation-root.js +2 -2
  181. package/dist/notifications/root/notifications-root.d.ts +20 -4
  182. package/dist/notifications/root/notifications-root.js +30 -8
  183. package/dist/number-input/grain.d.ts +28 -0
  184. package/dist/number-input/grain.js +14 -0
  185. package/dist/number-input/root/number-input-root.d.ts +34 -9
  186. package/dist/number-input/root/number-input-root.js +31 -25
  187. package/dist/object-inspector/root/object-inspector-root.d.ts +88 -33
  188. package/dist/object-inspector/root/object-inspector-root.js +134 -92
  189. package/dist/object-inspector/rows.d.ts +18 -0
  190. package/dist/object-inspector/rows.js +33 -0
  191. package/dist/option/index.d.ts +1 -0
  192. package/dist/option/index.js +1 -0
  193. package/dist/option/root/option-root.d.ts +5 -3
  194. package/dist/option/separator/option-separator.d.ts +30 -0
  195. package/dist/option/separator/option-separator.js +21 -0
  196. package/dist/outline/root/outline-root.d.ts +30 -0
  197. package/dist/outline/root/outline-root.js +17 -13
  198. package/dist/pagination/size/pagination-size.d.ts +6 -0
  199. package/dist/pagination/size/pagination-size.js +5 -3
  200. package/dist/pin-input/code.d.ts +37 -0
  201. package/dist/pin-input/code.js +25 -0
  202. package/dist/pin-input/root/pin-input-root.d.ts +116 -115
  203. package/dist/pin-input/root/pin-input-root.js +163 -155
  204. package/dist/popover/hide-outside.d.ts +14 -0
  205. package/dist/popover/hide-outside.js +34 -0
  206. package/dist/popover/root/popover-root.d.ts +64 -8
  207. package/dist/popover/root/popover-root.js +67 -4
  208. package/dist/progress/root/progress-root.d.ts +47 -0
  209. package/dist/progress/root/progress-root.js +34 -16
  210. package/dist/qr-code/export.d.ts +45 -1
  211. package/dist/qr-code/export.js +28 -0
  212. package/dist/qr-code/name.d.ts +11 -0
  213. package/dist/qr-code/name.js +7 -0
  214. package/dist/qr-code/root/qr-code-root.d.ts +36 -11
  215. package/dist/qr-code/root/qr-code-root.js +58 -11
  216. package/dist/quote/marks.d.ts +8 -0
  217. package/dist/quote/marks.js +5 -0
  218. package/dist/quote/root/quote-root.d.ts +14 -2
  219. package/dist/quote/root/quote-root.js +23 -1
  220. package/dist/radio/group/radio-group.d.ts +15 -0
  221. package/dist/radio/root/radio-root.d.ts +6 -2
  222. package/dist/radio/root/radio-root.js +7 -2
  223. package/dist/rating/root/rating-root.d.ts +30 -12
  224. package/dist/rating/root/rating-root.js +27 -27
  225. package/dist/scatter-chart/fit.d.ts +15 -0
  226. package/dist/scatter-chart/fit.js +35 -0
  227. package/dist/scatter-chart/root/scatter-chart-root.d.ts +51 -1
  228. package/dist/scatter-chart/root/scatter-chart-root.js +64 -24
  229. package/dist/scroll-area/root/scroll-area-root.d.ts +22 -51
  230. package/dist/scroll-area/root/scroll-area-root.js +57 -173
  231. package/dist/scroll-area/scrollbar-controller.d.ts +111 -0
  232. package/dist/scroll-area/scrollbar-controller.js +172 -0
  233. package/dist/scroll-area/scrollbar.d.ts +27 -8
  234. package/dist/scroll-area/scrollbar.js +10 -1
  235. package/dist/select/align.d.ts +47 -0
  236. package/dist/select/align.js +7 -0
  237. package/dist/select/root/select-root.d.ts +71 -0
  238. package/dist/select/root/select-root.js +78 -6
  239. package/dist/skeleton/root/skeleton-root.d.ts +3 -0
  240. package/dist/skeleton/root/skeleton-root.js +4 -2
  241. package/dist/skip-link/root/skip-link-root.js +2 -2
  242. package/dist/slider/root/slider-root.d.ts +59 -5
  243. package/dist/slider/root/slider-root.js +45 -3
  244. package/dist/sparkline/readings.d.ts +36 -0
  245. package/dist/sparkline/readings.js +36 -0
  246. package/dist/sparkline/root/sparkline-root.d.ts +121 -7
  247. package/dist/sparkline/root/sparkline-root.js +160 -26
  248. package/dist/speed-dial/item/speed-dial-item.d.ts +1 -1
  249. package/dist/speed-dial/item/speed-dial-item.js +2 -2
  250. package/dist/speed-dial/root/speed-dial-root.d.ts +6 -0
  251. package/dist/speed-dial/root/speed-dial-root.js +11 -3
  252. package/dist/splitter/root/splitter-root.d.ts +10 -12
  253. package/dist/splitter/root/splitter-root.js +211 -204
  254. package/dist/stat/root/stat-root.d.ts +14 -5
  255. package/dist/stat/root/stat-root.js +21 -5
  256. package/dist/steps/reach.d.ts +5 -0
  257. package/dist/steps/reach.js +3 -0
  258. package/dist/steps/root/steps-root.d.ts +56 -3
  259. package/dist/steps/root/steps-root.js +50 -6
  260. package/dist/steps/step/step.d.ts +8 -0
  261. package/dist/steps/step/step.js +8 -3
  262. package/dist/switch/root/switch-root.d.ts +10 -0
  263. package/dist/table/column/table-column.d.ts +6 -10
  264. package/dist/table/column/table-column.js +8 -20
  265. package/dist/table/root/table-root.d.ts +31 -10
  266. package/dist/table/root/table-root.js +14 -18
  267. package/dist/table/row/table-row.d.ts +24 -0
  268. package/dist/table/row/table-row.js +37 -1
  269. package/dist/tabs/panel/tab-panel.d.ts +16 -3
  270. package/dist/tabs/panel/tab-panel.js +34 -5
  271. package/dist/tabs/root/tabs-root.d.ts +17 -3
  272. package/dist/tabs/root/tabs-root.js +1 -1
  273. package/dist/tabs/tab/tab.d.ts +5 -0
  274. package/dist/tabs/tab/tab.js +5 -2
  275. package/dist/tag/group/tag-group.d.ts +44 -5
  276. package/dist/tag/group/tag-group.js +60 -9
  277. package/dist/tag/root/tag-root.js +9 -2
  278. package/dist/textarea/root/textarea-root.d.ts +41 -2
  279. package/dist/textarea/root/textarea-root.js +39 -1
  280. package/dist/time-input/root/time-input-root.d.ts +10 -2
  281. package/dist/time-input/root/time-input-root.js +24 -13
  282. package/dist/time-picker/root/time-picker-root.d.ts +28 -17
  283. package/dist/time-picker/root/time-picker-root.js +18 -29
  284. package/dist/time-picker/wheel-name.d.ts +9 -0
  285. package/dist/time-picker/wheel-name.js +15 -0
  286. package/dist/timeline/group/timeline-group.js +2 -2
  287. package/dist/toast/item/toast-item.d.ts +16 -3
  288. package/dist/toast/item/toast-item.js +22 -6
  289. package/dist/toast/root/toast-root.d.ts +34 -4
  290. package/dist/toast/root/toast-root.js +74 -13
  291. package/dist/toast/words.d.ts +12 -0
  292. package/dist/toast/words.js +25 -0
  293. package/dist/toggle/group/toggle-group.d.ts +52 -7
  294. package/dist/toggle/group/toggle-group.js +58 -44
  295. package/dist/toggle/root/toggle-root.js +2 -4
  296. package/dist/toolbar/root/toolbar-root.d.ts +21 -5
  297. package/dist/toolbar/root/toolbar-root.js +52 -8
  298. package/dist/tooltip/root/tooltip-root.d.ts +20 -3
  299. package/dist/tooltip/root/tooltip-root.js +18 -1
  300. package/dist/tour/back.d.ts +10 -0
  301. package/dist/tour/back.js +8 -0
  302. package/dist/tour/root/tour-root.d.ts +45 -2
  303. package/dist/tour/root/tour-root.js +53 -8
  304. package/dist/tour/stop/tour-stop.d.ts +3 -0
  305. package/dist/tour/stop/tour-stop.js +3 -3
  306. package/dist/tree/item/tree-item.d.ts +7 -3
  307. package/dist/tree/item/tree-item.js +3 -0
  308. package/dist/tree/root/tree-root.d.ts +24 -3
  309. package/dist/tree/root/tree-root.js +32 -13
  310. package/dist/tree/selection.js +2 -15
  311. package/dist/trend/figure.d.ts +16 -0
  312. package/dist/trend/figure.js +8 -0
  313. package/dist/trend/root/trend-root.d.ts +83 -19
  314. package/dist/trend/root/trend-root.js +59 -8
  315. package/dist/video/media-events.d.ts +13 -18
  316. package/dist/video/media-events.js +1 -5
  317. package/dist/video/root/extra-controls.d.ts +12 -0
  318. package/dist/video/root/extra-controls.js +6 -2
  319. package/dist/video/root/video-root.d.ts +23 -6
  320. package/dist/video/root/video-root.js +19 -10
  321. package/dist/video-playlist/menu/video-playlist-menu.d.ts +2 -0
  322. package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
  323. package/dist/video-playlist/root/video-playlist-root.d.ts +17 -1
  324. package/dist/video-playlist/root/video-playlist-root.js +3 -0
  325. package/dist/virtual-list/fill.d.ts +11 -8
  326. package/dist/virtual-list/fill.js +10 -17
  327. package/dist/virtual-list/keys.d.ts +16 -7
  328. package/dist/virtual-list/keys.js +17 -6
  329. package/dist/virtual-list/plan.d.ts +65 -0
  330. package/dist/virtual-list/plan.js +27 -0
  331. package/dist/virtual-list/root/virtual-list-root.d.ts +136 -13
  332. package/dist/virtual-list/root/virtual-list-root.js +218 -51
  333. package/lint.json +1 -1
  334. package/package.json +8 -8
  335. package/types/jsx.d.ts +7441 -1458
  336. package/types/svelte.d.ts +1 -0
  337. package/types/vue.d.ts +1501 -326
  338. package/dist/chat/relative-clock.d.ts +0 -39
  339. package/dist/chat/relative-clock.js +0 -31
  340. package/dist/data-table/root/order.d.ts +0 -141
  341. package/dist/data-table/root/order.js +0 -76
  342. package/dist/tag/selection.d.ts +0 -10
  343. package/dist/tag/selection.js +0 -12
@@ -4,15 +4,41 @@ 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, hostAria } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
9
- import { property } from 'lit/decorators.js';
7
+ import { customElement, focusableElements, HelixElement, hostAria } from '@fusion.dev/helix-core';
8
+ import { html, nothing } from 'lit';
9
+ import { property, state } from 'lit/decorators.js';
10
10
  let HelixHeadlessTabPanel = class HelixHeadlessTabPanel extends HelixElement {
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.label = '';
14
14
  this.selected = false;
15
15
  this.value = '';
16
+ this.holdsStop = false;
17
+ this.watcher = typeof MutationObserver === 'undefined'
18
+ ? undefined
19
+ : new MutationObserver(() => this.readStops());
20
+ this.readStops = () => {
21
+ this.holdsStop = this.selected && focusableElements(this).length > 0;
22
+ };
23
+ }
24
+ connectedCallback() {
25
+ super.connectedCallback();
26
+ this.watcher?.observe(this, {
27
+ attributeFilter: ['contenteditable', 'disabled', 'hidden', 'href', 'tabindex'],
28
+ attributes: true,
29
+ childList: true,
30
+ subtree: true,
31
+ });
32
+ }
33
+ disconnectedCallback() {
34
+ super.disconnectedCallback();
35
+ this.watcher?.disconnect();
36
+ }
37
+ updated(changed) {
38
+ super.updated(changed);
39
+ if (changed.has('selected')) {
40
+ this.readStops();
41
+ }
16
42
  }
17
43
  willUpdate(changed) {
18
44
  super.willUpdate(changed);
@@ -23,9 +49,9 @@ let HelixHeadlessTabPanel = class HelixHeadlessTabPanel extends HelixElement {
23
49
  return html `<div
24
50
  part="panel"
25
51
  ?hidden=${!this.selected}
26
- tabindex="0"
52
+ tabindex=${this.holdsStop ? nothing : '0'}
27
53
  >
28
- <slot></slot>
54
+ <slot @slotchange=${this.readStops}></slot>
29
55
  </div>`;
30
56
  }
31
57
  };
@@ -38,6 +64,9 @@ __decorate([
38
64
  __decorate([
39
65
  property()
40
66
  ], HelixHeadlessTabPanel.prototype, "value", void 0);
67
+ __decorate([
68
+ state()
69
+ ], HelixHeadlessTabPanel.prototype, "holdsStop", void 0);
41
70
  HelixHeadlessTabPanel = __decorate([
42
71
  customElement('helix-headless-tab-panel')
43
72
  ], HelixHeadlessTabPanel);
@@ -21,13 +21,26 @@ declare const HelixHeadlessTabs_base: (new (...args: any[]) => import("@fusion.d
21
21
  * between tabs either way.
22
22
  *
23
23
  * A tab with an `href` is a link, for tabs that are pages of a site. Pressing
24
- * it follows the link, and the arrow keys move focus without choosing, so
25
- * arrowing past a tab never leaves the page. Mark the tab of the page you are
26
- * on with `selected`, or set `active` to its value.
24
+ * it follows the link. Where any tab is a link, the arrow keys move focus
25
+ * without choosing on every tab of the list, as under `manual`, so arrowing
26
+ * past a tab never leaves the page and the list answers the keys one way.
27
+ * Mark the tab of the page you are on with `selected`, or set `active` to its
28
+ * value.
27
29
  *
28
30
  * Each tab says which panel it controls through `aria-controls`, set as an
29
31
  * element reference to the panel's host, where the engine has reflection.
30
32
  *
33
+ * Keys, on the tab list: Left and Right Arrow move between tabs, and so do Up
34
+ * and Down; Home and End go to the first and the last. The horizontal arrows
35
+ * follow the reading direction, so on a right-to-left page Left moves on.
36
+ * Disabled tabs are passed over. Tab leaves the list for the selected panel,
37
+ * which is a tab stop only while it holds nothing focusable.
38
+ *
39
+ * The `indicator` part moves to the selected tab and carries its place and
40
+ * size, so a mark of your own can be drawn there: style the part, or slot an
41
+ * element into `indicator`, which sits inside the part and reads the same
42
+ * properties.
43
+ *
31
44
  * @since 0.1.0
32
45
  * @status stable
33
46
  * @category navigation
@@ -35,6 +48,7 @@ declare const HelixHeadlessTabs_base: (new (...args: any[]) => import("@fusion.d
35
48
  * @documentation https://fusion.dev/helix/components/tabs
36
49
  *
37
50
  * @slot - The tabs, as direct children of this element.
51
+ * @slot indicator - A mark of your own, drawn inside the `indicator` part, where `--indicator-x`, `--indicator-y`, `--indicator-inline-size` and `--indicator-block-size` reach it.
38
52
  * @slot panels - The panels, as direct children of this element. Panels inside a wrapper are not paired with tabs, because pairing reads only the direct children.
39
53
  *
40
54
  * @event {HelixActivateEvent} helix-activate - Fired after the active tab changes, so the element already holds the new value.
@@ -105,7 +105,7 @@ let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(He
105
105
  aria-orientation=${this.orientation === 'vertical' ? 'vertical' : nothing}
106
106
  >
107
107
  <slot @slotchange=${this.handleSlotChange}></slot>
108
- <span part="indicator" aria-hidden="true"></span>
108
+ <span part="indicator" aria-hidden="true"><slot name="indicator"></slot></span>
109
109
  </div>
110
110
  <div part="panels"><slot name="panels" @slotchange=${this.handleSlotChange}></slot></div>`;
111
111
  }
@@ -47,6 +47,11 @@ export declare class HelixHeadlessTab extends HelixHeadlessTab_base {
47
47
  * Where the link opens, such as `_blank`, for a tab with an `href`.
48
48
  */
49
49
  target?: string;
50
+ /**
51
+ * Moves focus to the tab's control, the button or the link inside it, so a
52
+ * script can focus the tab the way it focuses a native one.
53
+ */
54
+ focus(options?: FocusOptions): void;
50
55
  /**
51
56
  * Chooses the tab before the browser follows its link, and keeps the browser
52
57
  * from following where the choice was refused. A press with a modifier, or
@@ -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, MixinSelectableItem, reflectElements, } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, MixinSelectableItem, plainClick, reflectElements, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessTab = class HelixHeadlessTab extends MixinSelectableItem(HelixElement) {
@@ -13,7 +13,7 @@ let HelixHeadlessTab = class HelixHeadlessTab extends MixinSelectableItem(HelixE
13
13
  this.itemPart = 'tab';
14
14
  this.controls = undefined;
15
15
  this.handleLinkClick = (event) => {
16
- if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
16
+ if (!plainClick(event)) {
17
17
  return;
18
18
  }
19
19
  if (!this.select()) {
@@ -21,6 +21,9 @@ let HelixHeadlessTab = class HelixHeadlessTab extends MixinSelectableItem(HelixE
21
21
  }
22
22
  };
23
23
  }
24
+ focus(options) {
25
+ this.partNamed('tab')?.focus(options);
26
+ }
24
27
  updated(changed) {
25
28
  super.updated(changed);
26
29
  const control = this.renderRoot.querySelector('[part="tab"]');
@@ -1,7 +1,8 @@
1
- import { HelixElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
1
+ import { HelixElement, KeyboardNavigationController, type SelectionMode } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import { HelixHeadlessTag } from '../root/tag-root.js';
4
- import { type TagSelectionMode } from '../selection.js';
4
+ /** How many tags a group lets a reader select: core's `SelectionMode`. */
5
+ export type TagSelectionMode = SelectionMode;
5
6
  /**
6
7
  * @summary A set of tags that the arrow keys move through, and that can be removed and selected from the keyboard.
7
8
  *
@@ -17,9 +18,18 @@ import { type TagSelectionMode } from '../selection.js';
17
18
  * `single` selects one at a time and `multiple` any number. `value` holds the
18
19
  * selected tags' values, separated by commas, and a change asks with
19
20
  * `helix-before-change` and tells with `change`. Enter on a tag with an
20
- * `href` follows its link. A `disabled` tag is passed over.
21
+ * `href` follows its link. A `disabled` tag is passed over. A selected tag
22
+ * that is removed leaves `value`, which then sends `change`.
21
23
  *
22
- * Name the set with `label`.
24
+ * Name the set with `label`. Each tag is named by its words alone, without
25
+ * its remove button. `value` takes an array of values on the property as
26
+ * well, as React sets `value={['Apple']}`; a press writes it back as a
27
+ * string.
28
+ *
29
+ * **Removing the last tag keeps focus in the group.** With no tags left the
30
+ * set is a `group`, and focus moves to its `empty` part,
31
+ * which holds the `empty` slot: put words there such as "No filters" for a
32
+ * reader to hear. With nothing slotted it says `empty-label`, "No tags".
23
33
  *
24
34
  * @since 0.1.0
25
35
  * @status stable
@@ -27,12 +37,20 @@ import { type TagSelectionMode } from '../selection.js';
27
37
  * @documentation https://fusion.dev/helix/components/tag
28
38
  *
29
39
  * @slot - The tags, in the order they are read.
40
+ * @slot empty - Shown in place of the tags while there are none, such as "No filters". Its fallback is `empty-label`.
30
41
  *
31
42
  * @event {Event} change - Fired once a press has changed which tags are selected.
32
43
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired before a press changes which tags are selected, with the `value` it would leave. Cancelable.
33
44
  * @event {HelixDismissEvent} helix-dismiss - A tag's remove button was pressed, or Delete or Backspace on the focused tag. Bubbles from that tag, which is its `target`; remove the tag in response. Cancelable.
45
+ *
46
+ * @csspart empty - Holds the `empty` slot while the group has no tags, rendered only then. Takes focus when the last tag is removed.
34
47
  */
35
48
  export declare class HelixHeadlessTagGroup extends HelixElement {
49
+ /**
50
+ * The words the group shows and says once its last tag is removed, where
51
+ * nothing is in the `empty` slot.
52
+ */
53
+ emptyLabel: string;
36
54
  /**
37
55
  * The name of the set, such as "Filters", which a screen reader announces
38
56
  * before its tags.
@@ -51,7 +69,7 @@ export declare class HelixHeadlessTagGroup extends HelixElement {
51
69
  * Set it to start with tags selected. A tag's value is its `value`, or its
52
70
  * words where it has none.
53
71
  */
54
- value: string;
72
+ value: string | readonly string[];
55
73
  /** The tag the reader was last on, which keeps the tab stop. */
56
74
  private last;
57
75
  /**
@@ -61,6 +79,11 @@ export declare class HelixHeadlessTagGroup extends HelixElement {
61
79
  * a list of tags has a first and a last.
62
80
  */
63
81
  protected readonly keys: KeyboardNavigationController;
82
+ /**
83
+ * `value` as one written list, separated by commas, however it was set:
84
+ * an array of values is joined.
85
+ */
86
+ private get written();
64
87
  /** The tags the group holds, in order. */
65
88
  get tags(): HelixHeadlessTag[];
66
89
  /** The tags a key can land on, which are the ones that are not disabled. */
@@ -93,9 +116,25 @@ export declare class HelixHeadlessTagGroup extends HelixElement {
93
116
  protected press(tag: HelixHeadlessTag): void;
94
117
  private readonly handleKeydown;
95
118
  private readonly handleClick;
119
+ /**
120
+ * What happens once a tag has asked to be removed and the page has had its
121
+ * turn: a selected tag that left takes its value out of `value`, and focus
122
+ * that the last tag took with it comes to the group's `empty` part.
123
+ *
124
+ * Asked after the event and again a frame later, as `MixinDismissible`
125
+ * asks, because a framework removes the tag on its next render, after the
126
+ * handler. A canceled dismissal changes nothing.
127
+ */
128
+ private readonly handleDismiss;
129
+ private readonly handleSlotchange;
96
130
  private readonly handleFocusin;
97
131
  connectedCallback(): void;
98
132
  disconnectedCallback(): void;
133
+ /**
134
+ * Whether the group is known to hold no tags, which a server and a
135
+ * hydrating render cannot know: there it holds whatever it was given.
136
+ */
137
+ private get empty();
99
138
  protected willUpdate(changed: PropertyValues<this>): void;
100
139
  protected updated(changed: PropertyValues<this>): void;
101
140
  protected render(): TemplateResult;
@@ -4,21 +4,21 @@ 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 { commaList, customElement, HelixBeforeChangeEvent, HelixElement, hostAria, KeyboardNavigationController, requestChange, rovingStop, slottedWords, whenHydrated, } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
7
+ import { commaList, customElement, deepActiveElement, deepContains, HelixBeforeChangeEvent, HelixElement, hostAria, KeyboardNavigationController, pressedValue, prunedValue, requestChange, rovingStop, slottedWords, typeaheadByWords, whenHydrated, } from '@fusion.dev/helix-core';
8
+ import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessTag } from '../root/tag-root.js';
11
- import { pressedValue } from '../selection.js';
12
11
  let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
13
12
  constructor() {
14
13
  super(...arguments);
14
+ this.emptyLabel = 'No tags';
15
15
  this.label = '';
16
16
  this.value = '';
17
17
  this.keys = new KeyboardNavigationController(this, {
18
18
  items: () => this.reachable.map((tag) => this.navigable(tag)),
19
19
  origin: 'focus',
20
20
  selects: () => false,
21
- typeahead: (item) => item.words,
21
+ typeahead: typeaheadByWords(),
22
22
  wrap: false,
23
23
  });
24
24
  this.handleKeydown = (event) => {
@@ -53,6 +53,37 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
53
53
  this.press(tag);
54
54
  }
55
55
  };
56
+ this.handleDismiss = (event) => {
57
+ const tag = this.tagOf(event);
58
+ if (!tag) {
59
+ return;
60
+ }
61
+ const had = deepContains(this, deepActiveElement());
62
+ const settle = async () => {
63
+ if (event.defaultPrevented || tag.isConnected) {
64
+ return;
65
+ }
66
+ const left = this.tags.map((one) => this.valueFor(one));
67
+ const pruned = prunedValue(this.written, this.valueFor(tag), left);
68
+ if (pruned !== this.written) {
69
+ this.value = pruned;
70
+ this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
71
+ }
72
+ const active = document.activeElement;
73
+ if (!had || this.tags.length > 0 || (active !== null && active !== document.body)) {
74
+ return;
75
+ }
76
+ this.requestUpdate();
77
+ await this.updateComplete;
78
+ this.renderRoot.querySelector('[part~="empty"]')?.focus();
79
+ };
80
+ queueMicrotask(() => void settle());
81
+ requestAnimationFrame(() => void settle());
82
+ };
83
+ this.handleSlotchange = () => {
84
+ this.sync();
85
+ this.requestUpdate();
86
+ };
56
87
  this.handleFocusin = (event) => {
57
88
  const tag = this.tagOf(event);
58
89
  if (tag && tag !== this.last) {
@@ -61,6 +92,14 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
61
92
  }
62
93
  };
63
94
  }
95
+ get written() {
96
+ return typeof this.value === 'string'
97
+ ? this.value
98
+ : this.value
99
+ .map((one) => String(one).trim())
100
+ .filter((one) => one !== '')
101
+ .join(', ');
102
+ }
64
103
  get tags() {
65
104
  return [...(this.children ?? [])].filter((child) => child instanceof HelixHeadlessTag);
66
105
  }
@@ -82,7 +121,7 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
82
121
  }
83
122
  sync() {
84
123
  const mode = this.selectionMode ?? 'none';
85
- const chosen = new Set(commaList(this.value));
124
+ const chosen = new Set(commaList(this.written));
86
125
  const tags = this.tags;
87
126
  const items = tags.map((tag) => ({
88
127
  disabled: tag.disabled,
@@ -114,7 +153,7 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
114
153
  },
115
154
  event: (value) => new HelixBeforeChangeEvent({ value }),
116
155
  host: this,
117
- next: pressedValue(mode, this.value, this.valueFor(tag)),
156
+ next: pressedValue(mode, this.written, this.valueFor(tag)),
118
157
  });
119
158
  }
120
159
  connectedCallback() {
@@ -122,26 +161,38 @@ let HelixHeadlessTagGroup = class HelixHeadlessTagGroup extends HelixElement {
122
161
  this.addEventListener('keydown', this.handleKeydown);
123
162
  this.addEventListener('click', this.handleClick);
124
163
  this.addEventListener('focusin', this.handleFocusin);
164
+ this.addEventListener('helix-dismiss', this.handleDismiss);
125
165
  }
126
166
  disconnectedCallback() {
127
167
  super.disconnectedCallback();
128
168
  this.removeEventListener('keydown', this.handleKeydown);
129
169
  this.removeEventListener('click', this.handleClick);
130
170
  this.removeEventListener('focusin', this.handleFocusin);
171
+ this.removeEventListener('helix-dismiss', this.handleDismiss);
172
+ }
173
+ get empty() {
174
+ return this.childrenKnown && this.tags.length === 0;
131
175
  }
132
176
  willUpdate(changed) {
133
177
  super.willUpdate(changed);
134
- hostAria(this.internals, { label: this.label, role: 'grid' });
135
- this.internals.ariaMultiSelectable = this.selectionMode === 'multiple' ? 'true' : null;
178
+ const empty = this.empty;
179
+ hostAria(this.internals, { label: this.label, role: empty ? 'group' : 'grid' });
180
+ this.internals.ariaMultiSelectable = this.selectionMode === 'multiple' && !empty ? 'true' : null;
136
181
  }
137
182
  updated(changed) {
138
183
  super.updated(changed);
139
184
  this.sync();
140
185
  }
141
186
  render() {
142
- return html `<slot @slotchange=${() => this.sync()}></slot>`;
187
+ const empty = this.empty
188
+ ? html `<div part="empty" tabindex="-1"><slot name="empty">${this.emptyLabel}</slot></div>`
189
+ : nothing;
190
+ return html `<slot @slotchange=${this.handleSlotchange}></slot>${empty}`;
143
191
  }
144
192
  };
193
+ __decorate([
194
+ property({ attribute: 'empty-label' })
195
+ ], HelixHeadlessTagGroup.prototype, "emptyLabel", void 0);
145
196
  __decorate([
146
197
  property()
147
198
  ], HelixHeadlessTagGroup.prototype, "label", void 0);
@@ -36,7 +36,10 @@ let HelixHeadlessTag = class HelixHeadlessTag extends MixinDismissible(HelixElem
36
36
  }
37
37
  willUpdate(changed) {
38
38
  super.willUpdate(changed);
39
- hostAria(this.internals, { role: this.grouped ? 'row' : '' });
39
+ hostAria(this.internals, {
40
+ label: this.grouped ? this.words : '',
41
+ role: this.grouped ? 'row' : '',
42
+ });
40
43
  this.internals.ariaSelected = this.selected === undefined ? null : String(this.selected);
41
44
  this.internals.ariaDisabled = this.disabled ? 'true' : null;
42
45
  this.toggleState('selected', this.selected === true);
@@ -58,7 +61,11 @@ let HelixHeadlessTag = class HelixHeadlessTag extends MixinDismissible(HelixElem
58
61
  render() {
59
62
  const content = html `<slot name="start" part="start"></slot>${this.renderLabel()}${this.dismissible ? this.renderDismiss() : nothing}`;
60
63
  return this.grouped
61
- ? html `<span part="cell" role="gridcell" tabindex=${this.focusable ? '0' : '-1'}
64
+ ? html `<span
65
+ aria-label=${this.words || nothing}
66
+ part="cell"
67
+ role="gridcell"
68
+ tabindex=${this.focusable ? '0' : '-1'}
62
69
  >${content}</span
63
70
  >`
64
71
  : content;
@@ -1,4 +1,4 @@
1
- import { AriaForwardController, HelixFormElement } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, HelixFormElement, type OwnRule } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessTextarea_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
4
4
  /**
@@ -17,12 +17,27 @@ declare const HelixHeadlessTextarea_base: (new (...args: any[]) => import("@fusi
17
17
  * `max-rows` stops the growth at that many lines, and the field scrolls past
18
18
  * them.
19
19
  *
20
+ * `clearable` draws a button that empties the field while it holds text, and
21
+ * sends `helix-clear`, as `Input` does. `validate` adds your own rule, run on
22
+ * every change once the browser finds nothing wrong, as React Aria's
23
+ * TextField `validate` does.
24
+ *
25
+ * **Keyboard.** The field's own keys are the platform's, and Enter starts a
26
+ * new line. The clear button under `clearable` is a tab stop of its own after
27
+ * the field.
28
+ *
29
+ * The inner textarea is named by `label`, or by a host `aria-label`,
30
+ * `aria-labelledby` or `<label for>`, and is described by the hint and the
31
+ * message, with `aria-invalid` while a message shows. The clear button is
32
+ * named `clear-label`.
33
+ *
20
34
  * @since 0.1.0
21
35
  * @status stable
22
36
  * @category forms
23
37
  * @aliases Multiline, TextArea
24
38
  * @documentation https://fusion.dev/helix/components/textarea
25
39
  *
40
+ * @slot clear-icon - Replaces the cross on the clear button, under `clearable`.
26
41
  * @slot end - After the text, for an icon or an action beside it.
27
42
  * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
28
43
  * @slot hint - Help under the control, for content richer than the `hint` text.
@@ -31,15 +46,18 @@ declare const HelixHeadlessTextarea_base: (new (...args: any[]) => import("@fusi
31
46
  *
32
47
  * @event {Event} change - Sent from the textarea when the user finishes an edit.
33
48
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired as the text changes, and cancelable.
49
+ * @event {HelixClearEvent} helix-clear - Fired after the clear button has emptied the field, after `input` and `change`.
34
50
  * @event {Event} input - Fired after the value changes.
35
51
  *
52
+ * @csspart clear - The button that empties the field, under `clearable`.
36
53
  * @csspart control - The textarea itself.
37
54
  * @csspart feedback - The message that says what is wrong.
38
55
  * @csspart field - The box that holds the textarea.
39
56
  * @csspart hint - Help under the field, while nothing is wrong.
40
57
  * @csspart label - The label above the field.
41
58
  *
42
- * @cssstate invalid - The control has an error to show, from the browser's validation or from your `feedback`.
59
+ * @cssstate blank - The field holds no text.
60
+ * @cssstate invalid - The control has an error to show, from the browser's validation, `validate`, or your `feedback`.
43
61
  */
44
62
  export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
45
63
  protected readonly aria: AriaForwardController;
@@ -57,6 +75,14 @@ export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
57
75
  * Grows the box to fit its content, so it does not scroll.
58
76
  */
59
77
  autoGrow: boolean;
78
+ /**
79
+ * Draws a button that empties the field while it holds text.
80
+ */
81
+ clearable: boolean;
82
+ /**
83
+ * The accessible name of the clear button, which shows only an icon.
84
+ */
85
+ clearLabel: string;
60
86
  /**
61
87
  * How many characters wide the box is, when the layout does not set its
62
88
  * width.
@@ -194,6 +220,11 @@ export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
194
220
  focus(options?: FocusOptions): void;
195
221
  /** Reports the value and the validity the host stands behind. */
196
222
  private sync;
223
+ /**
224
+ * Empties the field and puts the caret back in it, through `requestValue`,
225
+ * so a consumer who owns the value can refuse. `Input` clears the same way.
226
+ */
227
+ private readonly handleClear;
197
228
  connectedCallback(): void;
198
229
  disconnectedCallback(): void;
199
230
  protected firstUpdated(changed: PropertyValues): void;
@@ -212,11 +243,19 @@ export declare class HelixHeadlessTextarea extends HelixHeadlessTextarea_base {
212
243
  * How many lines the box is before anything is typed.
213
244
  */
214
245
  rows: number;
246
+ /**
247
+ * Your own rule for the value, run on every change once the browser finds
248
+ * nothing wrong. Return a message, or a list of them, to mark the field
249
+ * invalid, and `true`, `null`, or nothing to accept the value.
250
+ */
251
+ validate?: OwnRule;
215
252
  /**
216
253
  * How the browser wraps the text, and whether the breaks are submitted with
217
254
  * it.
218
255
  */
219
256
  wrap: 'hard' | 'off' | 'soft';
257
+ /** The cross on the clear button, behind a seam a tier can move. */
258
+ protected renderClearIcon(): TemplateResult;
220
259
  protected render(): TemplateResult;
221
260
  }
222
261
  declare global {
@@ -4,7 +4,8 @@ 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, HelixFormElement, holdCaret, isRtl, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, applyOwnRule, customElement, drawIcon, HelixClearEvent, HelixFormElement, holdCaret, isRtl, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
8
+ import { x } from '@fusion.dev/helix-icons';
8
9
  import { html, nothing } from 'lit';
9
10
  import { property } from 'lit/decorators.js';
10
11
  import { grownHeight, lineMetrics } from '../grow.js';
@@ -17,6 +18,8 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
17
18
  this.announcesInput = true;
18
19
  this.autocomplete = '';
19
20
  this.autoGrow = false;
21
+ this.clearable = false;
22
+ this.clearLabel = 'Clear';
20
23
  this.dirName = '';
21
24
  this.placeholder = '';
22
25
  this.readOnly = false;
@@ -32,6 +35,17 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
32
35
  event.stopPropagation();
33
36
  this.announceChange();
34
37
  };
38
+ this.handleClear = () => {
39
+ if (this.value !== '' && this.requestValue('')) {
40
+ this.announceChange();
41
+ this.dispatchEvent(new HelixClearEvent());
42
+ }
43
+ const control = this.control;
44
+ if (control) {
45
+ restoreValue(control, this.value);
46
+ control.focus();
47
+ }
48
+ };
35
49
  this.rows = 3;
36
50
  this.wrap = 'soft';
37
51
  }
@@ -112,6 +126,7 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
112
126
  return;
113
127
  }
114
128
  this.copyValidity(control);
129
+ applyOwnRule(this.internals, control, this.validate, this.value);
115
130
  this.refreshFeedback();
116
131
  }
117
132
  connectedCallback() {
@@ -140,6 +155,7 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
140
155
  }
141
156
  willUpdate(changed) {
142
157
  super.willUpdate(changed);
158
+ this.toggleState('blank', this.value === '');
143
159
  if (!this.hasUpdated) {
144
160
  return;
145
161
  }
@@ -155,6 +171,9 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
155
171
  this.sync();
156
172
  this.grow();
157
173
  }
174
+ renderClearIcon() {
175
+ return html `<slot name="clear-icon">${drawIcon(x)}</slot>`;
176
+ }
158
177
  render() {
159
178
  return html `${this.renderLabel()}
160
179
  <div part="field">
@@ -180,6 +199,16 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
180
199
  @change=${this.handleChange}
181
200
  ></textarea>
182
201
  <slot name="end"></slot>
202
+ ${this.clearable && this.value !== '' && !this.isDisabled && !this.readOnly
203
+ ? html `<button
204
+ part="clear"
205
+ type="button"
206
+ aria-label=${this.clearLabel}
207
+ @click=${this.handleClear}
208
+ >
209
+ ${this.renderClearIcon()}
210
+ </button>`
211
+ : nothing}
183
212
  </div>
184
213
  ${this.renderHint()}${this.renderFeedback()}`;
185
214
  }
@@ -190,6 +219,12 @@ __decorate([
190
219
  __decorate([
191
220
  property({ attribute: 'auto-grow', reflect: true, type: Boolean })
192
221
  ], HelixHeadlessTextarea.prototype, "autoGrow", void 0);
222
+ __decorate([
223
+ property({ type: Boolean })
224
+ ], HelixHeadlessTextarea.prototype, "clearable", void 0);
225
+ __decorate([
226
+ property({ attribute: 'clear-label' })
227
+ ], HelixHeadlessTextarea.prototype, "clearLabel", void 0);
193
228
  __decorate([
194
229
  property({ type: Number })
195
230
  ], HelixHeadlessTextarea.prototype, "cols", void 0);
@@ -214,6 +249,9 @@ __decorate([
214
249
  __decorate([
215
250
  property({ type: Number })
216
251
  ], HelixHeadlessTextarea.prototype, "rows", void 0);
252
+ __decorate([
253
+ property({ attribute: false })
254
+ ], HelixHeadlessTextarea.prototype, "validate", void 0);
217
255
  __decorate([
218
256
  property()
219
257
  ], HelixHeadlessTextarea.prototype, "wrap", void 0);