@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
@@ -23,12 +23,20 @@ declare const HelixHeadlessRadioGroup_base: (new (...args: any[]) => import("@fu
23
23
  * the value until something is chosen, and where a reset returns. The two work
24
24
  * like `checked` and `defaultChecked`.
25
25
  *
26
+ * A choice sends its events in this order: `helix-select` from the radio, then,
27
+ * a microtask later and only if nobody refused it, `helix-before-change` from
28
+ * the group, and once the value has moved, `input` and `change`. The microtask
29
+ * is what lets a listener you add to the group after it connected refuse the
30
+ * `helix-select`. A native radio sends `input` and `change` in the same task as
31
+ * the click.
32
+ *
26
33
  * @since 0.1.0
27
34
  * @status stable
28
35
  * @aliases RadioButtons, Choice group
29
36
  * @documentation https://fusion.dev/helix/components/radio
30
37
  *
31
38
  * @slot - The radios.
39
+ * @slot feedback - The error message, for content richer than a string. It shows while there is a message.
32
40
  * @slot hint - The help text under the group, for content richer than a string.
33
41
  * @slot label - The group's label, for a question that needs more than a string.
34
42
  *
@@ -45,6 +53,18 @@ declare const HelixHeadlessRadioGroup_base: (new (...args: any[]) => import("@fu
45
53
  * @cssstate invalid - The group has an error to show, from the browser's validation or from your `feedback`.
46
54
  */
47
55
  export declare class HelixHeadlessRadioGroup extends HelixHeadlessRadioGroup_base {
56
+ /**
57
+ * Whether the group holds a tab stop of its own.
58
+ *
59
+ * A toolbar is one tab stop, so a toolbar the group sits in sets this to
60
+ * `false` while its stop is on another control, and every radio, the chosen
61
+ * one too, is then out of the tab order. The arrow keys inside the group
62
+ * work as before once focus is in it.
63
+ *
64
+ * No attribute: it is coordination between elements, and a consumer has no
65
+ * reason to set it.
66
+ */
67
+ focusable: boolean;
48
68
  /**
49
69
  * The direction the choices run. `vertical` puts one choice on each line.
50
70
  *
@@ -56,6 +76,22 @@ export declare class HelixHeadlessRadioGroup extends HelixHeadlessRadioGroup_bas
56
76
  * only `horizontal` has to be named.
57
77
  */
58
78
  orientation?: 'horizontal' | 'vertical';
79
+ /**
80
+ * Whether a reader can see and focus the answer and cannot change it. A
81
+ * read-only group still submits its value.
82
+ *
83
+ * React Aria's `isReadOnly`. A press on a radio is refused, and the arrow
84
+ * keys, Home and End leave focus on the answer, so Tab still reaches it and
85
+ * a reader hears it with the group's name. The group says `aria-readonly`.
86
+ * A script can still set `value`.
87
+ */
88
+ readOnly: boolean;
89
+ /**
90
+ * Keeps the keys that would move the answer from the keyboard controller
91
+ * while the group is read-only. A capture listener on the host runs ahead of
92
+ * the controller's own on the same target.
93
+ */
94
+ private readonly holdKeys;
59
95
  protected readonly keys: KeyboardNavigationController;
60
96
  protected readonly cascade: DisabledCascadeController;
61
97
  /** The radios, in light-DOM order. Client only: the server has no tree. */
@@ -76,8 +112,36 @@ export declare class HelixHeadlessRadioGroup extends HelixHeadlessRadioGroup_bas
76
112
  * the item's is the one that was doing nothing.
77
113
  */
78
114
  private readonly handleSelection;
115
+ /**
116
+ * The radio holding the answer, or nothing.
117
+ *
118
+ * Nothing where `value` names no radio here: a chosen radio that was removed
119
+ * is no longer an answer, as a native group drops it from the form. `value`
120
+ * keeps the name, so a value written before its radios arrive takes once they
121
+ * do.
122
+ */
123
+ private get answer();
124
+ /** Whether the group holds an answer the form can submit. */
125
+ private get answered();
126
+ /**
127
+ * Takes the answer from a radio whose markup says `selected`, where nothing
128
+ * else declared one.
129
+ *
130
+ * As `Toggle.Group` does, so `<helix-radio selected>` preselects as
131
+ * `<option selected>` and `<helix-toggle selected>` do, and a reset returns
132
+ * to it. Client only: the server's element shim has no children to read.
133
+ */
134
+ private adopt;
79
135
  /** Writes the answer onto the items, and nothing onto the host. */
80
136
  private place;
137
+ /** The radio that holds the group's tab stop: the answer, or the first that can be chosen. */
138
+ private entry;
139
+ /**
140
+ * Focus goes to the radio that holds the stop, and to the radio that would
141
+ * hold it while a toolbar holds the stop instead, so a label press or a
142
+ * refused submission still lands in the group.
143
+ */
144
+ protected get focusTarget(): HTMLElement | null;
81
145
  /** Reports the answer and the verdict on it. */
82
146
  private sync;
83
147
  connectedCallback(): void;
@@ -8,55 +8,103 @@ import { customElement, DisabledCascadeController, HelixFormElement, hostAria, K
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessRadio } from '../root/radio-root.js';
11
+ const MOVING_KEYS = new Set(['ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'End', 'Home']);
11
12
  let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControllerValue(MixinField(HelixFormElement)) {
12
13
  constructor() {
13
14
  super(...arguments);
15
+ this.focusable = true;
16
+ this.readOnly = false;
17
+ this.holdKeys = (event) => {
18
+ if (this.readOnly && MOVING_KEYS.has(event.key)) {
19
+ event.stopImmediatePropagation();
20
+ event.preventDefault();
21
+ }
22
+ };
14
23
  this.keys = new KeyboardNavigationController(this, { items: () => this.items });
15
24
  this.cascade = new DisabledCascadeController(this, {
16
25
  items: () => this.items,
17
26
  });
18
27
  this.handleSelection = (event) => {
19
28
  event.stopPropagation();
20
- if (event.defaultPrevented) {
21
- return;
22
- }
23
- const next = event.detail.value;
24
- if (next === this.value) {
29
+ if (this.readOnly) {
30
+ event.preventDefault();
25
31
  return;
26
32
  }
27
- this.requestValue(next);
33
+ queueMicrotask(() => {
34
+ if (event.defaultPrevented) {
35
+ return;
36
+ }
37
+ const next = event.detail.value;
38
+ if (next === this.value) {
39
+ return;
40
+ }
41
+ this.requestValue(next);
42
+ });
28
43
  };
29
44
  }
30
45
  get items() {
31
- return [...this.children].filter((child) => child instanceof HelixHeadlessRadio);
46
+ return [...(this.children ?? [])].filter((child) => child instanceof HelixHeadlessRadio);
47
+ }
48
+ get answer() {
49
+ return this.items.find((item) => item.value === this.value);
50
+ }
51
+ get answered() {
52
+ if (this.value === '') {
53
+ return false;
54
+ }
55
+ const items = this.items;
56
+ return items.length === 0 || items.some((item) => item.value === this.value);
57
+ }
58
+ adopt() {
59
+ if (this.value !== '' || this.children === undefined) {
60
+ return;
61
+ }
62
+ const declared = this.items.find((item) => item.defaultSelected);
63
+ if (declared) {
64
+ this.defaultValue = declared.value;
65
+ }
32
66
  }
33
67
  place() {
34
68
  const items = this.items;
35
- const answer = items.find((item) => item.value === this.value);
69
+ const answer = this.answer;
36
70
  for (const item of items) {
37
71
  item.selected = item === answer;
38
72
  }
39
- const entry = answer ? undefined : items.find((item) => !item.disabled);
73
+ const entry = this.focusable ? this.entry() : undefined;
40
74
  for (const item of items) {
41
75
  item.focusable = item === entry;
42
76
  }
43
77
  }
78
+ entry() {
79
+ const answer = this.answer;
80
+ return answer && !answer.disabled ? answer : this.items.find((item) => !item.disabled);
81
+ }
82
+ get focusTarget() {
83
+ return (super.focusTarget ??
84
+ this.entry()?.shadowRoot?.querySelector('[part="item"]') ??
85
+ null);
86
+ }
44
87
  sync() {
45
- this.internals.setFormValue(this.value === '' ? null : this.value);
46
- this.reportRequired(this.value === '', 'Choose one to continue.', 'items');
88
+ const answered = this.answered;
89
+ this.internals.setFormValue(answered ? this.value : null);
90
+ this.reportRequired(!answered, 'Choose one to continue.', 'items');
47
91
  }
48
92
  connectedCallback() {
49
93
  super.connectedCallback();
50
94
  this.addEventListener('helix-select', this.handleSelection);
95
+ this.addEventListener('keydown', this.holdKeys, { capture: true });
51
96
  }
52
97
  disconnectedCallback() {
53
98
  super.disconnectedCallback();
54
99
  this.removeEventListener('helix-select', this.handleSelection);
100
+ this.removeEventListener('keydown', this.holdKeys, { capture: true });
55
101
  }
56
102
  willUpdate(changed) {
57
103
  super.willUpdate(changed);
104
+ this.adopt();
58
105
  hostAria(this.internals, { label: this.label, role: 'radiogroup' });
59
106
  this.internals.ariaRequired = this.required ? 'true' : null;
107
+ this.internals.ariaReadOnly = this.readOnly ? 'true' : null;
60
108
  }
61
109
  updated(changed) {
62
110
  super.updated(changed);
@@ -67,14 +115,20 @@ let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControl
67
115
  render() {
68
116
  return html `${this.renderGroupLabel()}
69
117
  <div part="items">
70
- <slot @slotchange=${() => this.place()}></slot>
118
+ <slot @slotchange=${() => this.requestUpdate()}></slot>
71
119
  </div>
72
120
  ${this.renderHint()}${this.renderFeedback()}`;
73
121
  }
74
122
  };
123
+ __decorate([
124
+ property({ attribute: false })
125
+ ], HelixHeadlessRadioGroup.prototype, "focusable", void 0);
75
126
  __decorate([
76
127
  property({ reflect: true })
77
128
  ], HelixHeadlessRadioGroup.prototype, "orientation", void 0);
129
+ __decorate([
130
+ property({ attribute: 'readonly', reflect: true, type: Boolean })
131
+ ], HelixHeadlessRadioGroup.prototype, "readOnly", void 0);
78
132
  HelixHeadlessRadioGroup = __decorate([
79
133
  customElement('helix-headless-radio-group')
80
134
  ], HelixHeadlessRadioGroup);
@@ -73,11 +73,14 @@ export declare class HelixHeadlessRadio extends HelixHeadlessRadio_base {
73
73
  get defaultSelected(): boolean;
74
74
  set defaultSelected(next: boolean);
75
75
  /**
76
- * Whether the radio holds the group's tab stop while nothing is chosen.
76
+ * Whether the radio holds the group's tab stop. Unset, the chosen radio
77
+ * holds it.
77
78
  *
78
79
  * The stop rides on the selection, and an unanswered group has no selection,
79
80
  * so without this every choice is `tabindex="-1"` and the keyboard cannot
80
- * enter the set at all.
81
+ * enter the set at all. The group writes `true` for the chosen radio, or for
82
+ * the first that can be chosen while nothing is, and `false` for every radio,
83
+ * the chosen one too, while a toolbar the group sits in holds the stop.
81
84
  *
82
85
  * **Written by the group, with no attribute, and it reaches the control
83
86
  * rather than the host.** A `tabindex` on the host is two faults at once: it
@@ -88,15 +91,15 @@ export declare class HelixHeadlessRadio extends HelixHeadlessRadio_base {
88
91
  * them an unchecked radio. `Toggle.Root` carries the same property for the
89
92
  * same reason.
90
93
  */
91
- focusable: boolean;
94
+ focusable?: boolean;
92
95
  /**
93
96
  * Asks for the render that follows a hydration, so the stop stops being every
94
97
  * choice's.
95
98
  *
96
99
  * `tabindex` reads `childrenKnown` and a hydrating render is one where that is
97
100
  * false, which is what keeps the hydrating render the server's. Nothing else
98
- * would ask: the group writes an already-false `focusable` onto every choice
99
- * but one, which changes nothing and so re-renders nothing.
101
+ * is sure to ask: a write from the group that leaves `focusable` as it was
102
+ * re-renders nothing.
100
103
  */
101
104
  protected firstUpdated(changed: PropertyValues): void;
102
105
  protected render(): TemplateResult;
@@ -11,7 +11,6 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends MixinSelectableItem(He
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.clicks = new HostClickController(this, () => this.partNamed('item'));
14
- this.focusable = false;
15
14
  }
16
15
  get defaultSelected() {
17
16
  return this.hasAttribute('selected');
@@ -30,7 +29,7 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends MixinSelectableItem(He
30
29
  role="radio"
31
30
  aria-checked=${this.selected ? 'true' : 'false'}
32
31
  ?disabled=${this.disabled}
33
- tabindex=${this.selected || this.focusable || !this.childrenKnown ? '0' : '-1'}
32
+ tabindex=${(this.focusable ?? this.selected) || !this.childrenKnown ? '0' : '-1'}
34
33
  @click=${() => this.select()}
35
34
  >
36
35
  <span part="box"></span>
@@ -1,4 +1,4 @@
1
- import { HelixFormElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, HelixFormElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessRating_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
4
4
  /**
@@ -15,14 +15,23 @@ declare const HelixHeadlessRating_base: (new (...args: any[]) => import("@fusion
15
15
  * A read-only rating displays a score, which is the most common use. It is
16
16
  * announced as a displayed value and does not look disabled.
17
17
  *
18
+ * With `clearable`, pressing the chosen score again, or Backspace or Delete on
19
+ * the marks, takes the rating back to unrated and sends `helix-clear`.
20
+ *
18
21
  * @since 0.1.0
19
22
  * @status stable
20
23
  * @category forms
21
24
  * @aliases Stars, Score, Review score
22
25
  * @documentation https://fusion.dev/helix/components/rating
23
26
  *
27
+ * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
28
+ * @slot hint - Help under the control, for content richer than the `hint` text.
29
+ * @slot label - The label, for content richer than the `label` text.
30
+ * @slot mark - The shape of a mark, copied into every position.
31
+ *
24
32
  * @event {Event} change - The score was chosen. Sent together with `input`, because each choice commits the score.
25
33
  * @event {HelixBeforeChangeEvent} helix-before-change - The score is about to change. Cancelable: cancel it to keep the current score.
34
+ * @event {Event} helix-clear - The rating went back to unrated, under `clearable`. Sent after `input` and `change`.
26
35
  * @event {Event} input - The score changed.
27
36
  *
28
37
  * @csspart feedback - The message that says what is wrong.
@@ -45,6 +54,12 @@ declare const HelixHeadlessRating_base: (new (...args: any[]) => import("@fusion
45
54
  */
46
55
  export declare class HelixHeadlessRating extends HelixHeadlessRating_base {
47
56
  protected readonly announcesInput = false;
57
+ /**
58
+ * Whether a reader can take the rating back to unrated: by pressing the
59
+ * chosen score again, or with Backspace or Delete. Zag's rating group
60
+ * clears the same way.
61
+ */
62
+ clearable: boolean;
48
63
  /**
49
64
  * How many marks there are.
50
65
  */
@@ -92,6 +107,14 @@ export declare class HelixHeadlessRating extends HelixHeadlessRating_base {
92
107
  * nonsense is the fault this pays off, so neither case is quietly ignored.
93
108
  */
94
109
  min: number;
110
+ /**
111
+ * The name of each mark a reader can choose, with `{score}` and `{max}`
112
+ * filled in. Set it to translate "3 out of 5".
113
+ *
114
+ * Also what a read-only rating says for its score where `value-text` is
115
+ * empty.
116
+ */
117
+ optionLabel: string;
95
118
  /**
96
119
  * Shows the score without letting users change it.
97
120
  *
@@ -128,6 +151,9 @@ export declare class HelixHeadlessRating extends HelixHeadlessRating_base {
128
151
  * and each of its options by the score that option is.
129
152
  */
130
153
  valueText: string;
154
+ protected readonly aria: AriaForwardController;
155
+ /** @internal The element a missing name is checked on: the set of stars, so the name of one star does not count as the name of the rating. */
156
+ protected get namedControl(): HTMLElement | null;
131
157
  /**
132
158
  * The shape a consumer slotted, if they slotted one.
133
159
  *
@@ -222,8 +248,17 @@ export declare class HelixHeadlessRating extends HelixHeadlessRating_base {
222
248
  * or off the element entirely, has asked for nothing.
223
249
  */
224
250
  private readonly handleDragStart;
251
+ /** The score a press began on, so releasing on the chosen one can clear it. */
252
+ private pressedOn;
253
+ /**
254
+ * Whether a drag has just chosen, so the click a browser may send after it
255
+ * does not choose again. Cleared a task later, since the click may not come.
256
+ */
257
+ private dragChose;
225
258
  private readonly handleDragMove;
226
259
  private readonly handleDragEnd;
260
+ /** Whether a press on `score` takes a clearable rating back to unrated. */
261
+ private clearsAt;
227
262
  private readonly handleDragCancel;
228
263
  /**
229
264
  * Reports the value, and whether the form may go without one.
@@ -246,6 +281,10 @@ export declare class HelixHeadlessRating extends HelixHeadlessRating_base {
246
281
  */
247
282
  private sync;
248
283
  private readonly handleOptionClick;
284
+ /** Backspace and Delete take a clearable rating back to unrated. */
285
+ private readonly handleClearKey;
286
+ /** Back to unrated, through the same cancelable request as a score, and said. */
287
+ private clear;
249
288
  private readonly handleOptionEnter;
250
289
  private readonly handleLeave;
251
290
  connectedCallback(): void;
@@ -255,6 +294,15 @@ export declare class HelixHeadlessRating extends HelixHeadlessRating_base {
255
294
  protected updated(changed: PropertyValues): void;
256
295
  protected willUpdate(changed: PropertyValues): void;
257
296
  protected firstUpdated(changed: PropertyValues): void;
297
+ /** The name of one score, as `optionLabel` writes it. */
298
+ private nameFor;
299
+ /**
300
+ * The option holding the tab stop: the chosen one, or for a score between
301
+ * steps, such as an average of 3.5 on whole marks, the step below it, and
302
+ * the first while nothing is chosen. Between steps no option matched, and
303
+ * the rating left the tab order.
304
+ */
305
+ private stopAt;
258
306
  protected render(): TemplateResult;
259
307
  /**
260
308
  * The value as a number, or `NaN` while the control is empty.
@@ -4,19 +4,24 @@ 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 { valueAsNumber as asNumber, customElement, numberAsValue as fromNumber, HelixFormElement, KeyboardNavigationController, MixinControllerValue, MixinField, reportRuntime, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, valueAsNumber as asNumber, customElement, fillTemplate, numberAsValue as fromNumber, HelixFormElement, KeyboardNavigationController, MixinControllerValue, MixinField, reportRuntime, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue(MixinField(HelixFormElement)) {
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.announcesInput = false;
14
+ this.clearable = false;
14
15
  this.max = 5;
15
16
  this.min = 0;
17
+ this.optionLabel = '{score} out of {max}';
16
18
  this.readOnly = false;
17
19
  this.showValue = false;
18
20
  this.step = 1;
19
21
  this.valueText = '';
22
+ this.aria = new AriaForwardController(this, {
23
+ target: () => this.partNamed('marks'),
24
+ });
20
25
  this.markSource = undefined;
21
26
  this.markRead = false;
22
27
  this.previewing = undefined;
@@ -46,8 +51,11 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
46
51
  catch {
47
52
  }
48
53
  this.dragging = event.pointerId;
54
+ this.pressedOn = score;
49
55
  this.previewing = Number.parseFloat(score);
50
56
  };
57
+ this.pressedOn = undefined;
58
+ this.dragChose = false;
51
59
  this.handleDragMove = (event) => {
52
60
  if (this.dragging !== event.pointerId) {
53
61
  return;
@@ -64,9 +72,18 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
64
72
  this.dragging = undefined;
65
73
  this.previewing = undefined;
66
74
  const score = this.scoreAt(event.clientX);
67
- if (score !== undefined) {
68
- this.requestValue(score);
75
+ if (score === undefined) {
76
+ return;
77
+ }
78
+ this.dragChose = true;
79
+ setTimeout(() => {
80
+ this.dragChose = false;
81
+ }, 0);
82
+ if (this.clearsAt(score) && score === this.pressedOn) {
83
+ this.clear();
84
+ return;
69
85
  }
86
+ this.requestValue(score);
70
87
  };
71
88
  this.handleDragCancel = (event) => {
72
89
  if (this.dragging !== event.pointerId) {
@@ -76,8 +93,22 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
76
93
  this.previewing = undefined;
77
94
  };
78
95
  this.handleOptionClick = (event) => {
96
+ if (this.dragChose) {
97
+ return;
98
+ }
79
99
  const option = event.currentTarget;
80
- this.requestValue(option.dataset.score ?? '');
100
+ const score = option.dataset.score ?? '';
101
+ if (this.clearsAt(score)) {
102
+ this.clear();
103
+ return;
104
+ }
105
+ this.requestValue(score);
106
+ };
107
+ this.handleClearKey = (event) => {
108
+ if (this.clearable && (event.key === 'Backspace' || event.key === 'Delete')) {
109
+ event.preventDefault();
110
+ this.clear();
111
+ }
81
112
  };
82
113
  this.handleOptionEnter = (event) => {
83
114
  if (this.isDisabled) {
@@ -91,6 +122,9 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
91
122
  };
92
123
  this.warned = false;
93
124
  }
125
+ get namedControl() {
126
+ return this.partNamed('marks');
127
+ }
94
128
  get floor() {
95
129
  if (!Number.isFinite(this.min)) {
96
130
  return 0;
@@ -152,10 +186,18 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
152
186
  }
153
187
  return nearest;
154
188
  }
189
+ clearsAt(score) {
190
+ return this.clearable && this.value !== '' && Number(score) === Number(this.value);
191
+ }
155
192
  sync() {
156
193
  this.internals.setFormValue(this.value === '' ? null : this.value);
157
194
  this.reportRequired(this.value === '', 'Choose a rating to continue.', 'marks');
158
195
  }
196
+ clear() {
197
+ if (this.value !== '' && !this.readOnly && !this.isDisabled && this.requestValue('')) {
198
+ this.dispatchEvent(new Event('helix-clear', { bubbles: true, composed: true }));
199
+ }
200
+ }
159
201
  connectedCallback() {
160
202
  super.connectedCallback();
161
203
  this.addEventListener('pointerleave', this.handleLeave);
@@ -190,11 +232,23 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
190
232
  this.finishHydration();
191
233
  this.sync();
192
234
  }
235
+ nameFor(score) {
236
+ return fillTemplate(this.optionLabel, { max: this.max, score });
237
+ }
238
+ stopAt(answered, first) {
239
+ if (!answered) {
240
+ return first;
241
+ }
242
+ const below = this.steps.filter((score) => score <= this.score);
243
+ return below.at(-1) ?? first;
244
+ }
193
245
  render() {
194
246
  const showing = this.previewing ?? this.score;
195
- const said = this.valueText || `${this.score} out of ${this.max}`;
247
+ const said = this.valueText || this.nameFor(this.score);
196
248
  const count = Math.max(0, Math.round(this.max));
197
249
  const first = this.steps[0];
250
+ const answered = this.value !== '' && Number.isFinite(Number.parseFloat(this.value));
251
+ const stop = this.stopAt(answered, first);
198
252
  const mark = (layer, index) => html `<span part="mark">${this.markFor(layer, index)}</span>`;
199
253
  const marksIn = (layer) => Array.from({ length: count }, (_, index) => mark(layer, index));
200
254
  return html `${this.renderGroupLabel()}
@@ -210,6 +264,7 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
210
264
  data-slotted=${this.markSource ? '' : nothing}
211
265
  style=${`--rating-full: ${Math.floor(showing)}; --rating-part: ${showing - Math.floor(showing)}; --rating-steps: ${this.positions.length}`}
212
266
  @pointercancel=${this.handleDragCancel}
267
+ @keydown=${this.handleClearKey}
213
268
  @pointerdown=${this.handleDragStart}
214
269
  @pointermove=${this.handleDragMove}
215
270
  @pointerup=${this.handleDragEnd}
@@ -240,14 +295,12 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
240
295
  part="option"
241
296
  role="radio"
242
297
  type="button"
243
- aria-checked=${this.score === score ? 'true' : 'false'}
244
- aria-label=${`${score} out of ${this.max}`}
298
+ aria-checked=${answered && this.score === score ? 'true' : 'false'}
299
+ aria-label=${this.nameFor(score)}
245
300
  data-score=${String(score)}
246
301
  style=${`--rating-step: ${index + 1}`}
247
302
  ?disabled=${this.isDisabled}
248
- tabindex=${this.score === score || (this.score === this.floor && score === first)
249
- ? '0'
250
- : '-1'}
303
+ tabindex=${score === stop ? '0' : '-1'}
251
304
  @click=${this.handleOptionClick}
252
305
  @pointerenter=${this.handleOptionEnter}
253
306
  ></button>`)}
@@ -269,12 +322,18 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
269
322
  this.value = fromNumber(count);
270
323
  }
271
324
  };
325
+ __decorate([
326
+ property({ type: Boolean })
327
+ ], HelixHeadlessRating.prototype, "clearable", void 0);
272
328
  __decorate([
273
329
  property({ type: Number })
274
330
  ], HelixHeadlessRating.prototype, "max", void 0);
275
331
  __decorate([
276
332
  property({ type: Number })
277
333
  ], HelixHeadlessRating.prototype, "min", void 0);
334
+ __decorate([
335
+ property({ attribute: 'option-label' })
336
+ ], HelixHeadlessRating.prototype, "optionLabel", void 0);
278
337
  __decorate([
279
338
  property({ attribute: 'readonly', reflect: true, type: Boolean })
280
339
  ], HelixHeadlessRating.prototype, "readOnly", void 0);
@@ -33,7 +33,14 @@ declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...arg
33
33
  * round numbers, and the range extends to them.
34
34
  *
35
35
  * The summary gives each series' correlation coefficient and its number of
36
- * points. The table has every number, and the drawing is `aria-hidden`.
36
+ * points. The table has every number, and the drawing is hidden from screen
37
+ * readers.
38
+ *
39
+ * **The plot is read by key.** It takes focus, named by `label` or the caption.
40
+ * The left and right arrows move from row to row in the table's order, Home
41
+ * and End go to the first and the last, and the up and down arrows move among
42
+ * the series of a row. The tooltip sits on that point, the `live` part says the
43
+ * row in words, and Escape puts the tooltip away.
37
44
  *
38
45
  * Each point is a `<span>` placed by percentage over the drawing, as
39
46
  * `LineChart` places its end points, so points stay round when the drawing
@@ -52,9 +59,10 @@ declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...arg
52
59
  * @documentation https://fusion.dev/helix/components/scatter-chart
53
60
  *
54
61
  * @csspart gridline - One line across the plot, drawn when `gridlines` is set.
62
+ * @csspart live - The readout: a polite live region that says the row the keys moved to. Hide it visually.
55
63
  * @csspart mark - The invisible point the tooltip is anchored to.
56
64
  * @csspart plane - The grid that holds the plot, both axes, and their names. It runs left to right on every page.
57
- * @csspart plot - The drawing.
65
+ * @csspart plot - The drawing, which takes focus to be read by key.
58
66
  * @csspart point - One row, as a point.
59
67
  * @csspart point-active - A point in the row under the pointer. Set alongside `point`.
60
68
  * @csspart summary - The text that says how strongly the measures relate, for people who cannot see the points.
@@ -68,6 +76,7 @@ declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...arg
68
76
  * @csspart tooltip-arrow-top - The point while the bubble is above the reading, on its bottom edge. Set alongside `tooltip-arrow`.
69
77
  * @csspart tooltip-bottom - The bubble while it is below the reading. Set alongside `tooltip`.
70
78
  * @csspart tooltip-cell - One column's name and value inside the tooltip.
79
+ * @csspart tooltip-cell-current - The cell the up and down arrows rest on, while they rest there. Set alongside `tooltip-cell`.
71
80
  * @csspart tooltip-label - The row's own name inside the tooltip.
72
81
  * @csspart tooltip-left - The bubble while it is left of the reading. Set alongside `tooltip`.
73
82
  * @csspart tooltip-name - A column's name inside the tooltip.
@@ -149,7 +158,12 @@ export declare class HelixHeadlessScatterChart extends HelixHeadlessScatterChart
149
158
  * absent where there are fewer than three points or no spread to measure.
150
159
  */
151
160
  protected renderSummary(field: ScatterField): TemplateResult;
152
- /** The row a pointer is resting on, over the point it is nearest. */
161
+ /**
162
+ * The row a pointer is resting on, over the point it is nearest.
163
+ *
164
+ * A row reached by key has no nearest point, so the bubble sits on the
165
+ * point of the series the keys rest on, or on the row's first point.
166
+ */
153
167
  protected renderTooltip(): TemplateResult | typeof nothing;
154
168
  /**
155
169
  * The point a pointer is nearest, within reach.