@fusion.dev/helix-headless 0.147.0 → 0.149.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 (296) hide show
  1. package/custom-elements.json +11222 -2332
  2. package/dist/accordion/item/accordion-item.d.ts +5 -2
  3. package/dist/accordion/item/accordion-item.js +11 -7
  4. package/dist/accordion/root/accordion-root.js +4 -6
  5. package/dist/announcement/root/announcement-root.d.ts +61 -5
  6. package/dist/announcement/root/announcement-root.js +126 -20
  7. package/dist/area-chart/root/area-chart-root.d.ts +11 -0
  8. package/dist/area-chart/root/area-chart-root.js +21 -9
  9. package/dist/avatar/group/avatar-group.d.ts +10 -2
  10. package/dist/avatar/group/avatar-group.js +10 -5
  11. package/dist/badge/count.d.ts +19 -0
  12. package/dist/badge/count.js +20 -0
  13. package/dist/badge/root/badge-root.d.ts +6 -2
  14. package/dist/badge/root/badge-root.js +8 -22
  15. package/dist/bar-chart/root/bar-chart-root.d.ts +13 -1
  16. package/dist/bar-chart/root/bar-chart-root.js +17 -4
  17. package/dist/blockquote/root/blockquote-root.d.ts +66 -8
  18. package/dist/blockquote/root/blockquote-root.js +85 -8
  19. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +2 -2
  20. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +8 -1
  21. package/dist/breadcrumbs/root/breadcrumbs-root.js +5 -2
  22. package/dist/button/root/button-root.d.ts +10 -1
  23. package/dist/button/root/button-root.js +3 -2
  24. package/dist/button-group/arrows.d.ts +14 -0
  25. package/dist/button-group/arrows.js +10 -0
  26. package/dist/button-group/root/button-group-root.d.ts +144 -3
  27. package/dist/button-group/root/button-group-root.js +142 -2
  28. package/dist/callout/root/callout-root.d.ts +16 -2
  29. package/dist/callout/root/callout-root.js +16 -4
  30. package/dist/canvas/describe.d.ts +10 -0
  31. package/dist/canvas/describe.js +9 -0
  32. package/dist/canvas/edge-pan.d.ts +61 -0
  33. package/dist/canvas/edge-pan.js +63 -0
  34. package/dist/canvas/neighbor.d.ts +17 -0
  35. package/dist/canvas/neighbor.js +1 -0
  36. package/dist/canvas/node/canvas-node.d.ts +12 -2
  37. package/dist/canvas/node/canvas-node.js +8 -3
  38. package/dist/canvas/root/canvas-document.d.ts +10 -0
  39. package/dist/canvas/root/canvas-document.js +1 -0
  40. package/dist/canvas/root/canvas-root.d.ts +168 -9
  41. package/dist/canvas/root/canvas-root.js +239 -69
  42. package/dist/canvas/snap.d.ts +42 -0
  43. package/dist/canvas/snap.js +32 -0
  44. package/dist/carousel/root/carousel-root.d.ts +9 -3
  45. package/dist/carousel/root/carousel-root.js +17 -10
  46. package/dist/chat/composer/chat-composer.d.ts +5 -0
  47. package/dist/chat/composer/chat-composer.js +4 -1
  48. package/dist/chat/message/chat-message.d.ts +9 -5
  49. package/dist/chat/message/chat-message.js +8 -4
  50. package/dist/chat/naming.js +2 -2
  51. package/dist/chat/root/chat-root.d.ts +40 -4
  52. package/dist/chat/root/chat-root.js +38 -14
  53. package/dist/checkbox/root/checkbox-root.d.ts +11 -0
  54. package/dist/checkbox/root/checkbox-root.js +8 -2
  55. package/dist/checkbox-group/root/checkbox-group-root.d.ts +24 -0
  56. package/dist/checkbox-group/root/checkbox-group-root.js +28 -12
  57. package/dist/checkbox-group/select-all.d.ts +11 -0
  58. package/dist/checkbox-group/select-all.js +5 -0
  59. package/dist/code-block/root/code-block-root.d.ts +20 -8
  60. package/dist/code-block/root/code-block-root.js +16 -5
  61. package/dist/color-input/root/color-input-root.d.ts +13 -0
  62. package/dist/color-input/root/color-input-root.js +18 -5
  63. package/dist/color-picker/root/color-picker-root.d.ts +19 -3
  64. package/dist/color-picker/root/color-picker-root.js +52 -24
  65. package/dist/color-picker/wheel.d.ts +11 -0
  66. package/dist/color-picker/wheel.js +9 -0
  67. package/dist/combobox/root/combobox-root.d.ts +106 -18
  68. package/dist/combobox/root/combobox-root.js +101 -26
  69. package/dist/command/root/command-root.d.ts +19 -9
  70. package/dist/command/root/command-root.js +9 -4
  71. package/dist/copy-button/root/copy-button-root.d.ts +5 -0
  72. package/dist/copy-button/root/copy-button-root.js +7 -2
  73. package/dist/data-table/root/data-table-root.d.ts +171 -29
  74. package/dist/data-table/root/data-table-root.js +284 -161
  75. package/dist/date-input/root/date-input-root.d.ts +32 -9
  76. package/dist/date-input/root/date-input-root.js +66 -44
  77. package/dist/date-picker/root/date-picker-root.d.ts +38 -15
  78. package/dist/date-picker/root/date-picker-root.js +52 -33
  79. package/dist/description-list/item/description-list-item.d.ts +4 -0
  80. package/dist/description-list/item/description-list-item.js +7 -1
  81. package/dist/dialog/root/dialog-root.d.ts +12 -1
  82. package/dist/dialog/root/dialog-root.js +11 -4
  83. package/dist/divider/root/divider-root.d.ts +45 -16
  84. package/dist/divider/root/divider-root.js +28 -4
  85. package/dist/donut-chart/root/donut-chart-root.d.ts +5 -0
  86. package/dist/donut-chart/root/donut-chart-root.js +8 -5
  87. package/dist/drawer/root/drawer-root.d.ts +12 -1
  88. package/dist/drawer/root/drawer-root.js +9 -2
  89. package/dist/drilldown/item/drilldown-item.js +3 -4
  90. package/dist/drilldown/root/drilldown-root.d.ts +5 -0
  91. package/dist/drilldown/root/drilldown-root.js +4 -1
  92. package/dist/element-events.generated.js +26 -3
  93. package/dist/empty/root/empty-root.d.ts +17 -3
  94. package/dist/empty/root/empty-root.js +29 -5
  95. package/dist/file-input/root/file-input-root.d.ts +18 -3
  96. package/dist/file-input/root/file-input-root.js +34 -18
  97. package/dist/file-input/status.js +2 -1
  98. package/dist/filter-input/chip/filter-chip.d.ts +13 -0
  99. package/dist/filter-input/chip/filter-chip.js +7 -3
  100. package/dist/filter-input/menu/filter-menu.d.ts +5 -0
  101. package/dist/filter-input/menu/filter-menu.js +3 -1
  102. package/dist/filter-input/root/filter-input-root.d.ts +20 -3
  103. package/dist/filter-input/root/filter-input-root.js +8 -4
  104. package/dist/format-date/fields.d.ts +20 -0
  105. package/dist/format-date/fields.js +14 -0
  106. package/dist/format-date/options.d.ts +2 -1
  107. package/dist/format-date/options.js +9 -38
  108. package/dist/format-date/root/format-date-root.d.ts +55 -5
  109. package/dist/format-date/root/format-date-root.js +29 -34
  110. package/dist/graph/root/graph-model.d.ts +11 -0
  111. package/dist/graph/root/graph-model.js +4 -0
  112. package/dist/graph/root/graph-root.d.ts +101 -14
  113. package/dist/graph/root/graph-root.js +140 -46
  114. package/dist/heatmap/root/heatmap-root.d.ts +37 -4
  115. package/dist/heatmap/root/heatmap-root.js +51 -16
  116. package/dist/icon-button/root/icon-button-root.d.ts +5 -0
  117. package/dist/icon-button/root/icon-button-root.js +1 -0
  118. package/dist/image/root/image-root.d.ts +11 -0
  119. package/dist/input/root/input-root.d.ts +5 -0
  120. package/dist/input/root/input-root.js +4 -1
  121. package/dist/kbd/root/kbd-root.js +1 -2
  122. package/dist/legend/root/legend-root.js +4 -17
  123. package/dist/lightbox/root/lightbox-root.d.ts +14 -4
  124. package/dist/lightbox/root/lightbox-root.js +25 -7
  125. package/dist/line-chart/root/line-chart-root.d.ts +5 -0
  126. package/dist/line-chart/root/line-chart-root.js +4 -2
  127. package/dist/list/item/list-item.d.ts +32 -0
  128. package/dist/list/item/list-item.js +34 -7
  129. package/dist/list/root/list-root.d.ts +87 -0
  130. package/dist/list/root/list-root.js +119 -23
  131. package/dist/listbox/reorder/index.d.ts +1 -0
  132. package/dist/listbox/reorder/index.js +4 -0
  133. package/dist/listbox/reorder/listbox-reorder.d.ts +83 -0
  134. package/dist/listbox/reorder/listbox-reorder.js +151 -0
  135. package/dist/listbox/root/listbox-root.d.ts +185 -33
  136. package/dist/listbox/root/listbox-root.js +166 -136
  137. package/dist/loader/root/loader-root.d.ts +5 -0
  138. package/dist/loader/root/loader-root.js +3 -1
  139. package/dist/map/area/map-area.d.ts +5 -0
  140. package/dist/map/area/map-area.js +3 -1
  141. package/dist/map/buildings/map-buildings.d.ts +5 -0
  142. package/dist/map/buildings/map-buildings.js +3 -1
  143. package/dist/map/contours/map-contours.d.ts +5 -0
  144. package/dist/map/contours/map-contours.js +3 -1
  145. package/dist/map/landcover/map-landcover.d.ts +5 -0
  146. package/dist/map/landcover/map-landcover.js +3 -1
  147. package/dist/map/marker/map-marker.d.ts +18 -2
  148. package/dist/map/marker/map-marker.js +13 -5
  149. package/dist/map/points/map-points.d.ts +5 -0
  150. package/dist/map/points/map-points.js +6 -4
  151. package/dist/map/position/map-position.d.ts +5 -0
  152. package/dist/map/position/map-position.js +3 -1
  153. package/dist/map/raster/map-raster.d.ts +5 -0
  154. package/dist/map/raster/map-raster.js +3 -1
  155. package/dist/map/root/map-root.d.ts +73 -16
  156. package/dist/map/root/map-root.js +122 -44
  157. package/dist/map/route/map-route.d.ts +5 -0
  158. package/dist/map/route/map-route.js +3 -1
  159. package/dist/map/terrain/map-terrain.d.ts +5 -0
  160. package/dist/map/terrain/map-terrain.js +3 -1
  161. package/dist/megamenu/root/megamenu-root.js +4 -11
  162. package/dist/menu/root/menu-root.d.ts +7 -2
  163. package/dist/menu/root/menu-root.js +3 -1
  164. package/dist/meter/root/meter-root.d.ts +16 -3
  165. package/dist/meter/root/meter-root.js +12 -5
  166. package/dist/navigation/root/navigation-root.d.ts +5 -0
  167. package/dist/navigation/root/navigation-root.js +7 -8
  168. package/dist/notifications/group/notification-group.d.ts +8 -1
  169. package/dist/notifications/group/notification-group.js +12 -6
  170. package/dist/notifications/notification/notification.d.ts +17 -1
  171. package/dist/notifications/notification/notification.js +6 -2
  172. package/dist/notifications/root/notifications-root.d.ts +49 -5
  173. package/dist/notifications/root/notifications-root.js +36 -8
  174. package/dist/number-input/root/number-input-root.d.ts +21 -8
  175. package/dist/number-input/root/number-input-root.js +26 -11
  176. package/dist/object-inspector/root/object-inspector-root.d.ts +56 -13
  177. package/dist/object-inspector/root/object-inspector-root.js +41 -30
  178. package/dist/outline/root/outline-root.d.ts +5 -0
  179. package/dist/outline/root/outline-root.js +3 -1
  180. package/dist/pagination/root/pagination-root.d.ts +7 -1
  181. package/dist/pagination/root/pagination-root.js +16 -7
  182. package/dist/pagination/size/pagination-size.d.ts +8 -3
  183. package/dist/pagination/size/pagination-size.js +5 -3
  184. package/dist/pagination/summary/pagination-summary.d.ts +8 -6
  185. package/dist/pagination/summary/pagination-summary.js +4 -9
  186. package/dist/pin-input/code.d.ts +23 -0
  187. package/dist/pin-input/code.js +21 -0
  188. package/dist/pin-input/root/pin-input-root.d.ts +9 -3
  189. package/dist/pin-input/root/pin-input-root.js +13 -23
  190. package/dist/popover/root/popover-root.d.ts +12 -2
  191. package/dist/popover/root/popover-root.js +10 -4
  192. package/dist/progress/root/progress-root.d.ts +7 -2
  193. package/dist/progress/root/progress-root.js +5 -2
  194. package/dist/qr-code/name.d.ts +3 -2
  195. package/dist/qr-code/name.js +2 -2
  196. package/dist/qr-code/root/qr-code-root.d.ts +26 -5
  197. package/dist/qr-code/root/qr-code-root.js +27 -6
  198. package/dist/radio/group/radio-group.d.ts +11 -0
  199. package/dist/radio/group/radio-group.js +8 -2
  200. package/dist/rating/root/rating-root.d.ts +12 -1
  201. package/dist/rating/root/rating-root.js +10 -3
  202. package/dist/scatter-chart/measures.d.ts +22 -0
  203. package/dist/scatter-chart/measures.js +11 -0
  204. package/dist/scatter-chart/root/scatter-chart-root.d.ts +22 -8
  205. package/dist/scatter-chart/root/scatter-chart-root.js +25 -23
  206. package/dist/select/root/select-root.d.ts +67 -8
  207. package/dist/select/root/select-root.js +45 -11
  208. package/dist/skip-link/root/skip-link-root.d.ts +5 -0
  209. package/dist/skip-link/root/skip-link-root.js +3 -1
  210. package/dist/slider/root/slider-root.d.ts +7 -2
  211. package/dist/slider/root/slider-root.js +11 -5
  212. package/dist/sparkline/readings.d.ts +15 -5
  213. package/dist/sparkline/readings.js +3 -3
  214. package/dist/sparkline/root/sparkline-root.d.ts +9 -4
  215. package/dist/sparkline/root/sparkline-root.js +9 -3
  216. package/dist/speed-dial/group/speed-dial-group.d.ts +48 -0
  217. package/dist/speed-dial/group/speed-dial-group.js +43 -0
  218. package/dist/speed-dial/index.d.ts +1 -0
  219. package/dist/speed-dial/index.js +1 -0
  220. package/dist/speed-dial/item/speed-dial-item.d.ts +26 -1
  221. package/dist/speed-dial/item/speed-dial-item.js +41 -3
  222. package/dist/speed-dial/long-press.d.ts +50 -0
  223. package/dist/speed-dial/long-press.js +48 -0
  224. package/dist/speed-dial/root/speed-dial-root.d.ts +85 -1
  225. package/dist/speed-dial/root/speed-dial-root.js +117 -5
  226. package/dist/splitter/root/splitter-root.d.ts +12 -3
  227. package/dist/splitter/root/splitter-root.js +22 -4
  228. package/dist/steps/root/steps-root.d.ts +9 -2
  229. package/dist/steps/root/steps-root.js +6 -3
  230. package/dist/steps/step/step.d.ts +12 -0
  231. package/dist/steps/step/step.js +11 -2
  232. package/dist/switch/root/switch-root.d.ts +11 -0
  233. package/dist/switch/root/switch-root.js +8 -2
  234. package/dist/table/cell/table-cell.d.ts +8 -1
  235. package/dist/table/cell/table-cell.js +6 -2
  236. package/dist/table/column/table-column.d.ts +11 -4
  237. package/dist/table/column/table-column.js +15 -10
  238. package/dist/table/grid-controller.d.ts +122 -0
  239. package/dist/table/grid-controller.js +299 -0
  240. package/dist/table/root/table-root.d.ts +154 -8
  241. package/dist/table/root/table-root.js +197 -38
  242. package/dist/table/row/table-row.d.ts +55 -1
  243. package/dist/table/row/table-row.js +60 -7
  244. package/dist/tabs/root/tabs-root.d.ts +5 -0
  245. package/dist/tabs/root/tabs-root.js +3 -1
  246. package/dist/tag/group/tag-group.d.ts +5 -0
  247. package/dist/tag/group/tag-group.js +8 -15
  248. package/dist/tag/root/tag-root.d.ts +14 -1
  249. package/dist/tag/root/tag-root.js +11 -8
  250. package/dist/textarea/root/textarea-root.d.ts +5 -0
  251. package/dist/textarea/root/textarea-root.js +3 -1
  252. package/dist/time-input/root/time-input-root.d.ts +14 -7
  253. package/dist/time-input/root/time-input-root.js +43 -32
  254. package/dist/time-picker/root/time-picker-root.d.ts +16 -7
  255. package/dist/time-picker/root/time-picker-root.js +28 -12
  256. package/dist/toast/item/toast-item.d.ts +13 -3
  257. package/dist/toast/item/toast-item.js +2 -0
  258. package/dist/toast/root/toast-root.d.ts +22 -2
  259. package/dist/toast/root/toast-root.js +24 -17
  260. package/dist/toggle/group/toggle-group.js +3 -5
  261. package/dist/toolbar/root/toolbar-root.d.ts +36 -0
  262. package/dist/toolbar/root/toolbar-root.js +43 -9
  263. package/dist/tooltip/root/tooltip-root.d.ts +27 -17
  264. package/dist/tooltip/root/tooltip-root.js +1 -2
  265. package/dist/tour/root/tour-root.d.ts +8 -2
  266. package/dist/tour/root/tour-root.js +4 -2
  267. package/dist/tour/stop/tour-stop.d.ts +5 -0
  268. package/dist/tour/stop/tour-stop.js +6 -1
  269. package/dist/tree/grip.d.ts +5 -0
  270. package/dist/tree/grip.js +1 -0
  271. package/dist/tree/item/tree-item.d.ts +24 -0
  272. package/dist/tree/item/tree-item.js +27 -5
  273. package/dist/tree/root/tree-root.d.ts +66 -1
  274. package/dist/tree/root/tree-root.js +77 -5
  275. package/dist/trend/root/trend-root.d.ts +20 -4
  276. package/dist/trend/root/trend-root.js +20 -5
  277. package/dist/video/root/video-root.d.ts +23 -3
  278. package/dist/video/root/video-root.js +56 -15
  279. package/dist/video-playlist/menu/video-playlist-menu.d.ts +5 -0
  280. package/dist/video-playlist/menu/video-playlist-menu.js +3 -1
  281. package/dist/video-playlist/root/video-playlist-root.d.ts +5 -0
  282. package/dist/video-playlist/root/video-playlist-root.js +3 -1
  283. package/dist/virtual-list/root/virtual-list-root.d.ts +19 -3
  284. package/dist/virtual-list/root/virtual-list-root.js +10 -19
  285. package/lint.json +1 -1
  286. package/package.json +7 -6
  287. package/types/jsx.d.ts +6005 -2114
  288. package/types/svelte.d.ts +1 -0
  289. package/types/vue.d.ts +1594 -676
  290. package/types/words.d.ts +3280 -0
  291. package/dist/kbd/keys.d.ts +0 -29
  292. package/dist/kbd/keys.js +0 -52
  293. package/dist/toast/hotkey.d.ts +0 -22
  294. package/dist/toast/hotkey.js +0 -23
  295. package/dist/tooltip/focus-target.d.ts +0 -6
  296. package/dist/tooltip/focus-target.js +0 -9
@@ -4,9 +4,10 @@ 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, fillTemplate, formatNumber, HelixElement } from '@fusion.dev/helix-core';
7
+ import { customElement, focusTarget, HelixElement, wordsLocale } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
+ import { badgeBlank, badgeCapped, badgeText, badgeWords } from '../count.js';
10
11
  let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
11
12
  constructor() {
12
13
  super(...arguments);
@@ -21,28 +22,16 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
21
22
  return String(this.value ?? '');
22
23
  }
23
24
  get blank() {
24
- return this.given.trim() !== '' && Number(this.given) === 0;
25
+ return badgeBlank(this.given);
25
26
  }
26
27
  get capped() {
27
- const count = Number(this.given);
28
- return this.max !== undefined && Number.isFinite(count) && count > this.max;
28
+ return badgeCapped(this.given, this.max);
29
29
  }
30
30
  get text() {
31
- const write = (count) => this.formatOptions ? formatNumber(count, this.locale, this.formatOptions) : String(count);
32
- if (this.capped) {
33
- return `${write(this.max)}+`;
34
- }
35
- const count = Number(this.given);
36
- return this.formatOptions && this.given.trim() !== '' && Number.isFinite(count)
37
- ? write(count)
38
- : this.given;
31
+ return badgeText(this.given, this.max, wordsLocale(this), this.formatOptions);
39
32
  }
40
33
  get words() {
41
- const text = this.text;
42
- if (this.blank || (text === '' && this.label === '')) {
43
- return '';
44
- }
45
- return fillTemplate(this.label, { count: text }) || text;
34
+ return badgeWords(this.text, this.label, this.blank, wordsLocale(this));
46
35
  }
47
36
  wrapped() {
48
37
  const found = this.renderRoot
@@ -50,10 +39,7 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
50
39
  ?.assignedElements({ flatten: true })
51
40
  .find((element) => element instanceof HTMLElement &&
52
41
  (element.tabIndex >= 0 || element.shadowRoot?.delegatesFocus === true));
53
- const inner = found?.shadowRoot?.delegatesFocus
54
- ? found.shadowRoot.querySelector('a[href], button, input, select, textarea')
55
- : null;
56
- return inner ?? found;
42
+ return found && focusTarget(found);
57
43
  }
58
44
  firstUpdated(changed) {
59
45
  super.firstUpdated(changed);
@@ -113,7 +99,7 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
113
99
  data-blank=${blank ? '' : nothing}
114
100
  data-single=${text.length <= 1 ? '' : nothing}
115
101
  role="status"
116
- aria-label=${announced ? fillTemplate(this.label, { count: text }) || text : nothing}
102
+ aria-label=${announced ? this.words : nothing}
117
103
  >${blank ? html `<span aria-hidden="true">${text}</span>` : text}</span
118
104
  >`;
119
105
  }
@@ -78,6 +78,11 @@ declare const HelixHeadlessBarChart_base: typeof HelixElement & (new (...args: a
78
78
  * `wash="gradient"` is set.
79
79
  */
80
80
  export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
81
+ /**
82
+ * The key this family's words are registered under, its styled tag:
83
+ * `registerWords('de', { 'helix-bar-chart': { highestLabel: 'Höchster Wert' } })`.
84
+ */
85
+ static words: string;
81
86
  /**
82
87
  * Which gridlines are drawn across the plot, if any.
83
88
  *
@@ -100,7 +105,8 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
100
105
  */
101
106
  gridlines?: 'both' | 'category' | 'value';
102
107
  /**
103
- * The word the summary puts before each series' highest reading.
108
+ * The name the summary gives each series' highest reading, `{highestLabel}`
109
+ * in `summary-label`.
104
110
  */
105
111
  highestLabel: string;
106
112
  /**
@@ -187,6 +193,12 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
187
193
  * second scale stack among themselves.
188
194
  */
189
195
  stacked: boolean;
196
+ /**
197
+ * The summary's sentence. `{highestLabel}` is `highest-label`, and
198
+ * `{readings}` each series' highest reading: the series, the category and
199
+ * the reading as the table writes it.
200
+ */
201
+ summaryLabel: string;
190
202
  /** The series names on the second scale, lowercased and trimmed. */
191
203
  private get scaled();
192
204
  /** The series names drawn as a line, lowercased and trimmed for matching. */
@@ -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 { byRow, CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, seriesNames, ticks, unbroken, } from '@fusion.dev/helix-core';
7
+ import { byRow, CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, formatWords, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, seriesNames, ticks, unbroken, words, wordsLocale, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { stackEnds, stackedReadings, stackSpans } from '../stack.js';
@@ -14,7 +14,9 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
14
14
  super(...arguments);
15
15
  this.highestLabel = 'Highest';
16
16
  this.stacked = false;
17
+ this.summaryLabel = '{highestLabel}: {readings}.';
17
18
  }
19
+ static { this.words = 'helix-bar-chart'; }
18
20
  get scaled() {
19
21
  return new Set(seriesNames(this.secondary));
20
22
  }
@@ -58,9 +60,10 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
58
60
  const [second, top] = extent(scaled.size > 0 ? on(own) : [0]);
59
61
  const other = [Math.min(0, second), Math.max(0, top)];
60
62
  const z = scaled.size > 0 ? linear(other, range) : undefined;
63
+ const format = formatNumbers(wordsLocale(this));
61
64
  const widest = z === undefined
62
65
  ? 0
63
- : Math.max(...[...ticks(domain), ...ticks(other)].map((level) => formatNumbers(this.locale)(level).length));
66
+ : Math.max(...[...ticks(domain), ...ticks(other)].map((level) => format(level).length));
64
67
  const lone = series.filter(own);
65
68
  const tint = lone.length === 1 && lone[0] !== undefined
66
69
  ? `--bar-chart-secondary: var(--chart-series-${seriesIndex(series.indexOf(lone[0]))})`
@@ -79,7 +82,11 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
79
82
  return [`${one.name} ${top.label} ${text}`];
80
83
  });
81
84
  const named = this.named ? `${this.named}. ` : '';
82
- return html `<p id="helix-chart-summary" part="summary">${named}${this.highestLabel}: ${highest.join('; ')}.</p>`;
85
+ const said = formatWords(this, 'summaryLabel', {
86
+ highestLabel: this.highestLabel,
87
+ readings: highest.join('; '),
88
+ });
89
+ return html `<p id="helix-chart-summary" part="summary">${named}${said}</p>`;
83
90
  }
84
91
  renderPlot(series, categories, y, domain, floor, z) {
85
92
  const sideways = this.orientation === 'horizontal';
@@ -188,6 +195,7 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
188
195
  }
189
196
  renderAxis(scale, domain, which, sideways, widest, tint) {
190
197
  const sizing = sideways ? undefined : `--bar-chart-axis: ${widest}ch`;
198
+ const format = formatNumbers(wordsLocale(this));
191
199
  return html `<div
192
200
  aria-hidden="true"
193
201
  data-axis=${which}
@@ -197,7 +205,7 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
197
205
  ${ticks(domain).map((level) => html `<span
198
206
  part="tick"
199
207
  style=${sideways ? `inset-inline-start: ${scale(level)}%` : `inset-block-start: ${scale(level)}%`}
200
- >${formatNumbers(this.locale)(level)}</span
208
+ >${format(level)}</span
201
209
  >`)}
202
210
  </div>`;
203
211
  }
@@ -282,6 +290,7 @@ __decorate([
282
290
  property()
283
291
  ], HelixHeadlessBarChart.prototype, "gridlines", void 0);
284
292
  __decorate([
293
+ words,
285
294
  property({ attribute: 'highest-label' })
286
295
  ], HelixHeadlessBarChart.prototype, "highestLabel", void 0);
287
296
  __decorate([
@@ -296,6 +305,10 @@ __decorate([
296
305
  __decorate([
297
306
  property({ reflect: true, type: Boolean })
298
307
  ], HelixHeadlessBarChart.prototype, "stacked", void 0);
308
+ __decorate([
309
+ words,
310
+ property({ attribute: 'summary-label' })
311
+ ], HelixHeadlessBarChart.prototype, "summaryLabel", void 0);
299
312
  HelixHeadlessBarChart = __decorate([
300
313
  customElement('helix-headless-bar-chart')
301
314
  ], HelixHeadlessBarChart);
@@ -1,16 +1,36 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
- import { type PropertyValues, type TemplateResult } from 'lit';
2
+ import { type CSSResultGroup, type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * @summary A passage set off from the prose around it.
5
5
  *
6
6
  * @description
7
- * Renders a `<figure>` holding the `<blockquote>` and a `<figcaption>` for the
7
+ * Renders a figure holding the `<blockquote>` and a caption for the
8
8
  * attribution, the structure the HTML spec describes. The attribution sits
9
9
  * outside the quotation because it is about the quotation, and it names the
10
10
  * figure, so a screen reader reads the source as the figure's caption.
11
11
  *
12
- * The figure keeps the browser's default margins on this tier. Set
13
- * `::part(figure) { margin: 0 }` where they are unwanted.
12
+ * **The figure is a `<div role="figure">`**, so the headless tier draws no
13
+ * space around the quotation. A `<figure>` takes a 40px indent and a line of
14
+ * space above and below from the browser's own sheet, which every page had to
15
+ * reset. The `<blockquote>` keeps its own margins, as a bare one does. A
16
+ * screen reader meets the same figure, named by its caption.
17
+ *
18
+ * **A figure only while there is an attribution.** With nothing in the caption
19
+ * the figure has no name and nothing to say, so the role and its name are
20
+ * dropped and a reader meets the quotation alone. A server cannot see what is
21
+ * slotted, so it serves the figure, and the client drops it after hydration;
22
+ * an `attribution` written as words is served exactly.
23
+ *
24
+ * **Quotation marks follow the language**, under `marks`. The opening mark is
25
+ * the one the browser sets for a `<q>` in the element's language, „ under
26
+ * `lang="de"` and « under `lang="fr"`, and a `Quote` inside the passage takes
27
+ * the inner marks. The mark is drawn, not read: a screen reader hears the
28
+ * passage. A passage under `marks` that starts with a mark typed into the text
29
+ * would show two, and warns once, with the runtime kind `typed-marks`.
30
+ *
31
+ * **`cite` is the source's URL**, which the browser keeps and does not show.
32
+ * Words in it, such as the author's name, warn once, with the runtime kind
33
+ * `cite-not-url`.
14
34
  *
15
35
  * For a quotation inside a sentence, use `Quote`.
16
36
  *
@@ -21,17 +41,37 @@ import { type PropertyValues, type TemplateResult } from 'lit';
21
41
  * @documentation https://fusion.dev/helix/components/blockquote
22
42
  *
23
43
  * @slot - The quoted passage.
24
- * @slot attribution - Who said it, and where.
44
+ * @slot attribution - Who said it, and where. Replaces the words of `attribution`.
25
45
  *
26
- * @csspart attribution - The `<figcaption>` carrying the source.
46
+ * @csspart attribution - The caption carrying the source.
27
47
  * @csspart blockquote - The quotation.
28
- * @csspart figure - The `<figure>` holding the quotation and its attribution.
48
+ * @csspart figure - The figure holding the quotation and its attribution, a `<div>` with `role="figure"` while the attribution has words.
29
49
  */
30
50
  export declare class HelixHeadlessBlockquote extends HelixElement {
51
+ static styles: CSSResultGroup;
31
52
  /**
32
- * The URL of the source the passage came from.
53
+ * Who said it, and where, as words, such as "Maria Lopez, operations
54
+ * manager". The `attribution` slot replaces them, for markup such as a link.
55
+ *
56
+ * Words are served with the figure they name, where a server cannot see a
57
+ * slotted attribution.
58
+ */
59
+ attribution: string;
60
+ /**
61
+ * The URL of the source the passage came from. Who said it goes in
62
+ * `attribution`: a `cite` with words in it warns once, with the runtime kind
63
+ * `cite-not-url`.
33
64
  */
34
65
  cite: string;
66
+ /**
67
+ * Whether to draw an opening quotation mark before the passage, in the
68
+ * element's language. Leave the marks out of the text.
69
+ */
70
+ marks: boolean;
71
+ /** Whether this blockquote has said its passage starts with a typed mark, so it says so once. */
72
+ private warned;
73
+ /** Whether this blockquote has said its `cite` holds words, so it says so once. */
74
+ private citeWarned;
35
75
  /**
36
76
  * Says on the caption whether there is an attribution in it.
37
77
  *
@@ -44,8 +84,26 @@ export declare class HelixHeadlessBlockquote extends HelixElement {
44
84
  * rendered, which React reports on hydration.
45
85
  */
46
86
  private markEmpty;
87
+ /**
88
+ * Reads the passage for a mark typed at its start, which `marks` would
89
+ * double. Only the start: the mark drawn is an opening one.
90
+ */
91
+ private readonly readPassage;
92
+ /**
93
+ * Reads `cite` for words where the source's URL belongs, the common mistake
94
+ * of naming the author there. A URL holds no spaces, so words with a space
95
+ * between them are enough to tell.
96
+ */
97
+ private readCite;
98
+ protected firstUpdated(changed: PropertyValues): void;
47
99
  protected updated(changed: PropertyValues): void;
48
100
  protected render(): TemplateResult;
101
+ /**
102
+ * The caption, with the words of `attribution` as its slot's fallback,
103
+ * behind a seam a tier can move. The words are in an element, so the region
104
+ * counts them as its own.
105
+ */
106
+ protected renderAttribution(): TemplateResult;
49
107
  }
50
108
  declare global {
51
109
  interface HTMLElementTagNameMap {
@@ -4,33 +4,110 @@ 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, markRegion } from '@fusion.dev/helix-core';
8
- import { html, nothing } from 'lit';
7
+ import { customElement, HelixElement, markRegion, reportRuntime } from '@fusion.dev/helix-core';
8
+ import { css, html, nothing, } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
+ import { typedMarks } from '../../quote/marks.js';
10
11
  let HelixHeadlessBlockquote = class HelixHeadlessBlockquote extends HelixElement {
11
12
  constructor() {
12
13
  super(...arguments);
14
+ this.attribution = '';
13
15
  this.cite = '';
16
+ this.marks = false;
17
+ this.warned = false;
18
+ this.citeWarned = false;
19
+ this.readPassage = () => {
20
+ if (!this.marks || this.warned) {
21
+ return;
22
+ }
23
+ const text = [...this.childNodes]
24
+ .filter((node) => !(node instanceof Element) || !node.slot)
25
+ .map((node) => node.textContent ?? '')
26
+ .join('')
27
+ .trim();
28
+ if (!typedMarks(text.charAt(0))) {
29
+ return;
30
+ }
31
+ this.warned = true;
32
+ reportRuntime({
33
+ kind: 'typed-marks',
34
+ message: `<${this.localName} marks> starts with a typed quotation mark, and the element draws its own, so the mark shows twice. Take the typed mark out.`,
35
+ tag: this.localName,
36
+ });
37
+ };
14
38
  }
39
+ static { this.styles = css `
40
+ :host([marks]) [part='blockquote']::before {
41
+ content: open-quote / '';
42
+ }
43
+
44
+ :host([marks]) [part='blockquote']::after {
45
+ content: no-close-quote;
46
+ }
47
+ `; }
15
48
  markEmpty() {
16
- markRegion(this.renderRoot, 'attribution', ['attribution']);
49
+ markRegion(this.renderRoot, 'attribution', ['attribution'], {
50
+ occupied: 'slot[name="attribution"] > *',
51
+ });
52
+ const named = !this.partNamed('attribution')?.hasAttribute('data-empty');
53
+ const figure = this.partNamed('figure');
54
+ if (named) {
55
+ figure?.setAttribute('role', 'figure');
56
+ figure?.setAttribute('aria-labelledby', 'attribution');
57
+ }
58
+ else {
59
+ figure?.removeAttribute('role');
60
+ figure?.removeAttribute('aria-labelledby');
61
+ }
62
+ }
63
+ readCite() {
64
+ if (this.citeWarned || !/\S\s+\S/.test(this.cite || '')) {
65
+ return;
66
+ }
67
+ this.citeWarned = true;
68
+ reportRuntime({
69
+ kind: 'cite-not-url',
70
+ message: `<${this.localName} cite="${this.cite}"> holds words, and \`cite\` takes the URL of the source. Put who said it in \`attribution\`.`,
71
+ tag: this.localName,
72
+ });
73
+ }
74
+ firstUpdated(changed) {
75
+ super.firstUpdated(changed);
76
+ this.renderRoot.addEventListener('slotchange', this.readPassage);
17
77
  }
18
78
  updated(changed) {
19
79
  super.updated(changed);
20
80
  this.markEmpty();
81
+ if (changed.has('marks')) {
82
+ this.readPassage();
83
+ }
84
+ if (changed.has('cite')) {
85
+ this.readCite();
86
+ }
21
87
  }
22
88
  render() {
23
- return html `<figure aria-labelledby="attribution" part="figure">
89
+ return html `<div aria-labelledby="attribution" part="figure" role="figure">
24
90
  <blockquote part="blockquote" cite=${this.cite || nothing}><slot></slot></blockquote>
25
- <figcaption id="attribution" part="attribution"
26
- ><slot name="attribution" @slotchange=${() => this.markEmpty()}></slot
27
- ></figcaption>
28
- </figure>`;
91
+ ${this.renderAttribution()}
92
+ </div>`;
93
+ }
94
+ renderAttribution() {
95
+ return html `<div id="attribution" part="attribution"
96
+ ><slot name="attribution" @slotchange=${() => this.markEmpty()}
97
+ >${this.attribution ? html `<span>${this.attribution}</span>` : nothing}</slot
98
+ ></div
99
+ >`;
29
100
  }
30
101
  };
102
+ __decorate([
103
+ property()
104
+ ], HelixHeadlessBlockquote.prototype, "attribution", void 0);
31
105
  __decorate([
32
106
  property()
33
107
  ], HelixHeadlessBlockquote.prototype, "cite", void 0);
108
+ __decorate([
109
+ property({ reflect: true, type: Boolean })
110
+ ], HelixHeadlessBlockquote.prototype, "marks", void 0);
34
111
  HelixHeadlessBlockquote = __decorate([
35
112
  customElement('helix-headless-blockquote')
36
113
  ], HelixHeadlessBlockquote);
@@ -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, hostAria, slotFilled } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, hostAria, slotFilled, slotNamed, } from '@fusion.dev/helix-core';
8
8
  import { caretRight } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
@@ -29,7 +29,7 @@ let HelixHeadlessBreadcrumb = class HelixHeadlessBreadcrumb extends HelixElement
29
29
  this.markEnd();
30
30
  }
31
31
  markEnd() {
32
- this.toggleState('end', slotFilled(this.renderRoot?.querySelector('slot[name="end"]')));
32
+ this.toggleState('end', slotFilled(slotNamed(this.renderRoot, 'end')));
33
33
  }
34
34
  renderSeparator() {
35
35
  return html `<slot name="separator"
@@ -60,9 +60,16 @@ export interface BreadcrumbItem {
60
60
  * @csspart list - The ordered list.
61
61
  */
62
62
  export declare class HelixHeadlessBreadcrumbs extends HelixElement {
63
+ /**
64
+ * The key this family's words are registered under, its styled tag:
65
+ * `registerWords('de', { 'helix-breadcrumbs': { label: 'Brotkrümelpfad' } })`.
66
+ */
67
+ static words: string;
63
68
  /**
64
69
  * The name of the button that shows a folded trail's hidden crumbs. `{count}`
65
- * is replaced by how many it shows. Translate it with the page.
70
+ * is how many it shows, written in the locale, and a language with plural
71
+ * forms writes them: `{count, plural, one {# weiteren zeigen} other {#
72
+ * weitere zeigen}}`.
66
73
  */
67
74
  expandLabel: string;
68
75
  /**
@@ -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, fillTemplate, HelixElement, hostAria } from '@fusion.dev/helix-core';
7
+ import { customElement, formatWords, HelixElement, hostAria, words } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { repeat } from 'lit/directives/repeat.js';
@@ -25,6 +25,7 @@ let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixEleme
25
25
  });
26
26
  };
27
27
  }
28
+ static { this.words = 'helix-breadcrumbs'; }
28
29
  willUpdate(changed) {
29
30
  super.willUpdate(changed);
30
31
  hostAria(this.internals, { label: this.label, role: 'navigation' });
@@ -55,7 +56,7 @@ let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixEleme
55
56
  ><button
56
57
  part="expand"
57
58
  type="button"
58
- aria-label=${fillTemplate(this.expandLabel, { count: String(folded) })}
59
+ aria-label=${formatWords(this, 'expandLabel', { count: folded })}
59
60
  @click=${this.expand}
60
61
  >
61
62
  …
@@ -81,12 +82,14 @@ let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixEleme
81
82
  }
82
83
  };
83
84
  __decorate([
85
+ words,
84
86
  property({ attribute: 'expand-label' })
85
87
  ], HelixHeadlessBreadcrumbs.prototype, "expandLabel", void 0);
86
88
  __decorate([
87
89
  property({ attribute: false })
88
90
  ], HelixHeadlessBreadcrumbs.prototype, "items", void 0);
89
91
  __decorate([
92
+ words,
90
93
  property()
91
94
  ], HelixHeadlessBreadcrumbs.prototype, "label", void 0);
92
95
  __decorate([
@@ -59,6 +59,11 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
59
59
  * @cssstate pending - The button is pending: `pending` is set.
60
60
  */
61
61
  export declare class HelixHeadlessButton extends HelixFormElement {
62
+ /**
63
+ * The key this family's words are registered under, its styled tag:
64
+ * `registerWords('de', { 'helix-button': { pendingLabel: 'Wird geladen' } })`.
65
+ */
66
+ static words: string;
62
67
  protected readonly aria: AriaForwardController;
63
68
  /**
64
69
  * @internal Whether a host `aria-label` names the inner button. A subclass
@@ -167,8 +172,12 @@ export declare class HelixHeadlessButton extends HelixFormElement {
167
172
  * Lives here rather than only on `IconButton` so the two share one render.
168
173
  * An icon-only button without it is a control a screen reader announces as
169
174
  * "button" and nothing else.
175
+ *
176
+ * Undefaulted, so a subclass's own default, such as Copy Button's `Copy`, is
177
+ * the first value its element receives and so the English its words fall
178
+ * back to.
170
179
  */
171
- label: string;
180
+ label?: string;
172
181
  /**
173
182
  * Whether the action the button started is still running.
174
183
  *
@@ -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 { AriaForwardController, customElement, defaultButton, drawStatus, elementById, HelixFallbackCommandEvent, HelixFormElement, HelixPressEvent, pressType, StatusController, submitsOnEnter, supportsInvokers, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, defaultButton, drawStatus, elementById, HelixFallbackCommandEvent, HelixFormElement, HelixPressEvent, pressType, StatusController, submitsOnEnter, supportsInvokers, words, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { submitAs } from '../submitter.js';
@@ -24,7 +24,6 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
24
24
  this.formMethod = '';
25
25
  this.formNoValidate = false;
26
26
  this.formTarget = '';
27
- this.label = '';
28
27
  this.pending = false;
29
28
  this.pendingLabel = 'Loading';
30
29
  this.status = new StatusController(this);
@@ -64,6 +63,7 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
64
63
  }
65
64
  };
66
65
  }
66
+ static { this.words = 'helix-button'; }
67
67
  get forwardsLabel() {
68
68
  return true;
69
69
  }
@@ -229,6 +229,7 @@ __decorate([
229
229
  property({ type: Boolean })
230
230
  ], HelixHeadlessButton.prototype, "pending", void 0);
231
231
  __decorate([
232
+ words,
232
233
  property({ attribute: 'pending-label' })
233
234
  ], HelixHeadlessButton.prototype, "pendingLabel", void 0);
234
235
  __decorate([
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The index a key moves focus to, or `-1` for a key that moves nothing.
3
+ *
4
+ * Down and Up step forward and back, and Left and Right step by `step`, which
5
+ * core's `inlineStep` turns on a right-to-left page. Home and End go to the
6
+ * ends. A step wraps from the last button round to the first, as a toolbar's
7
+ * does.
8
+ *
9
+ * @param key The key pressed.
10
+ * @param step `1` or `-1` for Left and Right, from `inlineStep`, and `0` otherwise.
11
+ * @param from The index focus is on, or `-1` where it is on none of them.
12
+ * @param count How many buttons focus can move between.
13
+ */
14
+ export declare const arrowTarget: (key: string, step: number, from: number, count: number) => number;
@@ -0,0 +1,10 @@
1
+ export const arrowTarget = (key, step, from, count) => {
2
+ const by = key === 'ArrowDown' ? 1 : key === 'ArrowUp' ? -1 : step;
3
+ if (from < 0 || count === 0) {
4
+ return -1;
5
+ }
6
+ if (key === 'Home' || key === 'End') {
7
+ return key === 'Home' ? 0 : count - 1;
8
+ }
9
+ return by === 0 ? -1 : (from + by + count) % count;
10
+ };