@fusion.dev/helix-headless 0.143.0 → 0.145.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 (314) hide show
  1. package/custom-elements.json +16990 -1383
  2. package/dist/accordion/item/accordion-item.d.ts +48 -12
  3. package/dist/accordion/item/accordion-item.js +26 -5
  4. package/dist/accordion/root/accordion-root.d.ts +6 -0
  5. package/dist/accordion/root/accordion-root.js +7 -1
  6. package/dist/announcement/root/announcement-root.d.ts +21 -1
  7. package/dist/announcement/root/announcement-root.js +36 -1
  8. package/dist/app-header/root/app-header-root.d.ts +7 -0
  9. package/dist/app-header/root/app-header-root.js +5 -1
  10. package/dist/app-header/sectioning.d.ts +7 -0
  11. package/dist/app-header/sectioning.js +26 -0
  12. package/dist/area-chart/root/area-chart-root.d.ts +12 -4
  13. package/dist/area-chart/root/area-chart-root.js +24 -7
  14. package/dist/avatar/root/avatar-root.d.ts +14 -0
  15. package/dist/avatar/root/avatar-root.js +23 -9
  16. package/dist/badge/root/badge-root.d.ts +11 -7
  17. package/dist/badge/root/badge-root.js +1 -1
  18. package/dist/bar-chart/root/bar-chart-root.d.ts +12 -3
  19. package/dist/bar-chart/root/bar-chart-root.js +17 -10
  20. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +32 -1
  21. package/dist/breadcrumbs/root/breadcrumbs-root.js +25 -2
  22. package/dist/button/root/button-root.d.ts +47 -1
  23. package/dist/button/root/button-root.js +38 -4
  24. package/dist/callout/root/callout-root.d.ts +18 -1
  25. package/dist/callout/root/callout-root.js +15 -4
  26. package/dist/canvas/root/canvas-root.d.ts +38 -3
  27. package/dist/canvas/root/canvas-root.js +121 -40
  28. package/dist/card/root/card-root.d.ts +30 -1
  29. package/dist/card/root/card-root.js +38 -5
  30. package/dist/carousel/root/carousel-root.d.ts +90 -0
  31. package/dist/carousel/root/carousel-root.js +134 -12
  32. package/dist/chat/composer/chat-composer.d.ts +38 -6
  33. package/dist/chat/composer/chat-composer.js +42 -9
  34. package/dist/chat/message/chat-message.d.ts +32 -1
  35. package/dist/chat/message/chat-message.js +25 -9
  36. package/dist/chat/naming.d.ts +22 -0
  37. package/dist/chat/naming.js +12 -0
  38. package/dist/chat/relative-clock.d.ts +39 -0
  39. package/dist/chat/relative-clock.js +31 -0
  40. package/dist/chat/root/chat-root.d.ts +36 -0
  41. package/dist/chat/root/chat-root.js +24 -1
  42. package/dist/checkbox/root/checkbox-root.js +2 -0
  43. package/dist/checkbox-group/root/checkbox-group-root.d.ts +53 -3
  44. package/dist/checkbox-group/root/checkbox-group-root.js +71 -22
  45. package/dist/code-block/lines.d.ts +13 -0
  46. package/dist/code-block/lines.js +2 -0
  47. package/dist/code-block/root/code-block-root.d.ts +38 -1
  48. package/dist/code-block/root/code-block-root.js +28 -7
  49. package/dist/color-input/root/color-input-root.d.ts +34 -1
  50. package/dist/color-input/root/color-input-root.js +23 -4
  51. package/dist/color-picker/channels.d.ts +26 -0
  52. package/dist/color-picker/channels.js +84 -0
  53. package/dist/color-picker/root/color-picker-root.d.ts +81 -5
  54. package/dist/color-picker/root/color-picker-root.js +103 -10
  55. package/dist/combobox/root/combobox-root.d.ts +38 -3
  56. package/dist/combobox/root/combobox-root.js +64 -2
  57. package/dist/command/root/command-root.d.ts +30 -4
  58. package/dist/command/root/command-root.js +33 -3
  59. package/dist/copy-button/from.d.ts +22 -0
  60. package/dist/copy-button/from.js +28 -0
  61. package/dist/copy-button/root/copy-button-root.d.ts +47 -1
  62. package/dist/copy-button/root/copy-button-root.js +44 -8
  63. package/dist/data-table/root/data-table-root.d.ts +150 -2
  64. package/dist/data-table/root/data-table-root.js +303 -29
  65. package/dist/data-table/tree.d.ts +40 -0
  66. package/dist/data-table/tree.js +55 -0
  67. package/dist/date-input/root/date-input-root.d.ts +73 -4
  68. package/dist/date-input/root/date-input-root.js +93 -43
  69. package/dist/date-picker/root/date-picker-root.d.ts +145 -1
  70. package/dist/date-picker/root/date-picker-root.js +239 -48
  71. package/dist/description-list/item/description-list-item.d.ts +17 -2
  72. package/dist/description-list/item/description-list-item.js +15 -2
  73. package/dist/description-list/root/description-list-root.d.ts +27 -1
  74. package/dist/description-list/root/description-list-root.js +26 -1
  75. package/dist/dialog/description.d.ts +9 -0
  76. package/dist/dialog/description.js +14 -0
  77. package/dist/dialog/root/dialog-root.d.ts +64 -1
  78. package/dist/dialog/root/dialog-root.js +49 -23
  79. package/dist/donut-chart/root/donut-chart-root.d.ts +25 -4
  80. package/dist/donut-chart/root/donut-chart-root.js +21 -6
  81. package/dist/drawer/root/drawer-root.d.ts +63 -2
  82. package/dist/drawer/root/drawer-root.js +95 -19
  83. package/dist/drawer/swipe-start.d.ts +9 -0
  84. package/dist/drawer/swipe-start.js +12 -0
  85. package/dist/drilldown/item/drilldown-item.d.ts +11 -6
  86. package/dist/drilldown/item/drilldown-item.js +8 -5
  87. package/dist/drilldown/root/drilldown-root.d.ts +11 -0
  88. package/dist/drilldown/root/drilldown-root.js +8 -3
  89. package/dist/element-events.generated.js +61 -13
  90. package/dist/empty/root/empty-root.d.ts +1 -1
  91. package/dist/file-input/root/file-input-root.d.ts +47 -0
  92. package/dist/file-input/root/file-input-root.js +60 -22
  93. package/dist/file-input/screen.d.ts +35 -0
  94. package/dist/file-input/screen.js +49 -0
  95. package/dist/filter-input/menu/filter-menu.d.ts +3 -0
  96. package/dist/filter-input/menu/filter-menu.js +8 -4
  97. package/dist/filter-input/root/filter-input-root.d.ts +38 -4
  98. package/dist/filter-input/root/filter-input-root.js +67 -3
  99. package/dist/filter-input/suggest.d.ts +17 -0
  100. package/dist/filter-input/suggest.js +23 -0
  101. package/dist/format-date/options.d.ts +33 -0
  102. package/dist/format-date/options.js +42 -0
  103. package/dist/format-date/root/format-date-root.d.ts +51 -0
  104. package/dist/format-date/root/format-date-root.js +58 -38
  105. package/dist/heatmap/root/heatmap-root.d.ts +23 -6
  106. package/dist/heatmap/root/heatmap-root.js +24 -13
  107. package/dist/icon/index.d.ts +2 -0
  108. package/dist/icon/index.js +2 -0
  109. package/dist/icon/registry.d.ts +37 -0
  110. package/dist/icon/registry.js +83 -0
  111. package/dist/icon/root/icon-root.d.ts +30 -4
  112. package/dist/icon/root/icon-root.js +41 -6
  113. package/dist/icon/root/index.d.ts +2 -0
  114. package/dist/icon/root/index.js +2 -0
  115. package/dist/icon/set.d.ts +1 -0
  116. package/dist/icon/set.js +3 -0
  117. package/dist/image/root/image-root.d.ts +14 -0
  118. package/dist/image/root/image-root.js +23 -2
  119. package/dist/input/root/input-root.d.ts +11 -0
  120. package/dist/input/root/input-root.js +16 -1
  121. package/dist/kbd/root/kbd-root.d.ts +20 -13
  122. package/dist/kbd/root/kbd-root.js +35 -37
  123. package/dist/kbd/shortcut.d.ts +18 -0
  124. package/dist/kbd/shortcut.js +35 -0
  125. package/dist/legend/hidden-series.d.ts +8 -0
  126. package/dist/legend/hidden-series.js +13 -0
  127. package/dist/legend/root/legend-root.d.ts +56 -0
  128. package/dist/legend/root/legend-root.js +54 -5
  129. package/dist/lightbox/item/lightbox-item.d.ts +15 -0
  130. package/dist/lightbox/item/lightbox-item.js +10 -1
  131. package/dist/lightbox/root/lightbox-root.d.ts +61 -1
  132. package/dist/lightbox/root/lightbox-root.js +94 -4
  133. package/dist/line-chart/root/line-chart-root.d.ts +11 -3
  134. package/dist/line-chart/root/line-chart-root.js +19 -5
  135. package/dist/list/item/list-item.d.ts +68 -1
  136. package/dist/list/item/list-item.js +66 -4
  137. package/dist/list/numbering.d.ts +14 -0
  138. package/dist/list/numbering.js +8 -0
  139. package/dist/list/root/list-root.d.ts +106 -9
  140. package/dist/list/root/list-root.js +189 -4
  141. package/dist/list/selection.d.ts +19 -0
  142. package/dist/list/selection.js +21 -0
  143. package/dist/listbox/root/listbox-root.d.ts +74 -3
  144. package/dist/listbox/root/listbox-root.js +86 -9
  145. package/dist/loader/busy.d.ts +17 -0
  146. package/dist/loader/busy.js +24 -0
  147. package/dist/loader/root/loader-root.d.ts +37 -0
  148. package/dist/loader/root/loader-root.js +52 -3
  149. package/dist/map/root/map-root.d.ts +6 -2
  150. package/dist/map/root/map-root.js +5 -2
  151. package/dist/megamenu/root/megamenu-root.d.ts +21 -0
  152. package/dist/megamenu/root/megamenu-root.js +16 -2
  153. package/dist/menu/index.d.ts +1 -0
  154. package/dist/menu/index.js +1 -0
  155. package/dist/menu/item/menu-item.d.ts +38 -11
  156. package/dist/menu/item/menu-item.js +33 -1
  157. package/dist/menu/root/menu-root.d.ts +25 -1
  158. package/dist/menu/root/menu-root.js +63 -6
  159. package/dist/menu/section/menu-section.d.ts +55 -0
  160. package/dist/menu/section/menu-section.js +60 -0
  161. package/dist/meter/root/meter-root.d.ts +7 -0
  162. package/dist/meter/root/meter-root.js +6 -3
  163. package/dist/navigation/root/navigation-root.d.ts +16 -1
  164. package/dist/navigation/root/navigation-root.js +27 -1
  165. package/dist/notifications/feed-keys.d.ts +10 -0
  166. package/dist/notifications/feed-keys.js +14 -0
  167. package/dist/notifications/notification/notification.d.ts +17 -0
  168. package/dist/notifications/notification/notification.js +21 -2
  169. package/dist/notifications/root/notifications-root.d.ts +60 -0
  170. package/dist/notifications/root/notifications-root.js +76 -1
  171. package/dist/number-input/root/number-input-root.d.ts +47 -0
  172. package/dist/number-input/root/number-input-root.js +58 -22
  173. package/dist/object-inspector/root/object-inspector-root.d.ts +98 -16
  174. package/dist/object-inspector/root/object-inspector-root.js +180 -80
  175. package/dist/object-inspector/rows.d.ts +57 -0
  176. package/dist/object-inspector/rows.js +86 -0
  177. package/dist/option/root/option-root.d.ts +54 -5
  178. package/dist/option/root/option-root.js +34 -5
  179. package/dist/outline/levels.d.ts +31 -0
  180. package/dist/outline/levels.js +23 -0
  181. package/dist/outline/root/outline-root.d.ts +68 -3
  182. package/dist/outline/root/outline-root.js +73 -8
  183. package/dist/pagination/size/pagination-size.d.ts +11 -2
  184. package/dist/pagination/size/pagination-size.js +19 -4
  185. package/dist/pin-input/format.d.ts +7 -0
  186. package/dist/pin-input/format.js +16 -0
  187. package/dist/pin-input/root/pin-input-root.d.ts +21 -0
  188. package/dist/pin-input/root/pin-input-root.js +29 -2
  189. package/dist/popover/root/popover-root.d.ts +62 -4
  190. package/dist/popover/root/popover-root.js +53 -5
  191. package/dist/progress/root/progress-root.d.ts +7 -0
  192. package/dist/progress/root/progress-root.js +9 -3
  193. package/dist/qr-code/export.d.ts +31 -0
  194. package/dist/qr-code/export.js +10 -0
  195. package/dist/qr-code/root/qr-code-root.d.ts +26 -0
  196. package/dist/qr-code/root/qr-code-root.js +49 -2
  197. package/dist/radio/group/radio-group.d.ts +17 -0
  198. package/dist/radio/group/radio-group.js +14 -1
  199. package/dist/rating/root/rating-root.d.ts +25 -0
  200. package/dist/rating/root/rating-root.js +46 -4
  201. package/dist/scatter-chart/root/scatter-chart-root.d.ts +17 -3
  202. package/dist/scatter-chart/root/scatter-chart-root.js +25 -7
  203. package/dist/scroll-area/root/scroll-area-root.d.ts +113 -0
  204. package/dist/scroll-area/root/scroll-area-root.js +218 -3
  205. package/dist/scroll-area/scrollbar.d.ts +65 -0
  206. package/dist/scroll-area/scrollbar.js +24 -0
  207. package/dist/select/root/select-root.d.ts +24 -0
  208. package/dist/select/root/select-root.js +21 -7
  209. package/dist/skeleton/root/skeleton-root.d.ts +25 -0
  210. package/dist/skeleton/root/skeleton-root.js +17 -2
  211. package/dist/slider/root/slider-root.d.ts +39 -0
  212. package/dist/slider/root/slider-root.js +45 -9
  213. package/dist/splitter/root/splitter-root.d.ts +13 -0
  214. package/dist/splitter/root/splitter-root.js +20 -2
  215. package/dist/splitter/snap.d.ts +9 -0
  216. package/dist/splitter/snap.js +12 -0
  217. package/dist/stat/root/stat-root.d.ts +2 -0
  218. package/dist/stat/root/stat-root.js +1 -1
  219. package/dist/steps/reach.d.ts +15 -0
  220. package/dist/steps/reach.js +12 -0
  221. package/dist/steps/root/steps-root.d.ts +30 -0
  222. package/dist/steps/root/steps-root.js +53 -0
  223. package/dist/steps/step/step.d.ts +39 -2
  224. package/dist/steps/step/step.js +29 -7
  225. package/dist/switch/root/switch-root.d.ts +12 -2
  226. package/dist/switch/root/switch-root.js +6 -1
  227. package/dist/table/column/table-column.d.ts +11 -0
  228. package/dist/table/column/table-column.js +6 -1
  229. package/dist/table/root/table-root.d.ts +101 -0
  230. package/dist/table/root/table-root.js +166 -5
  231. package/dist/table/row/table-row.d.ts +47 -1
  232. package/dist/table/row/table-row.js +39 -2
  233. package/dist/tabs/panel/tab-panel.d.ts +9 -5
  234. package/dist/tabs/panel/tab-panel.js +3 -4
  235. package/dist/tabs/root/tabs-root.d.ts +25 -1
  236. package/dist/tabs/root/tabs-root.js +21 -3
  237. package/dist/tabs/tab/tab.d.ts +36 -4
  238. package/dist/tabs/tab/tab.js +46 -3
  239. package/dist/tag/group/tag-group.d.ts +107 -0
  240. package/dist/tag/group/tag-group.js +157 -0
  241. package/dist/tag/index.d.ts +1 -0
  242. package/dist/tag/index.js +1 -0
  243. package/dist/tag/root/tag-root.d.ts +60 -0
  244. package/dist/tag/root/tag-root.js +74 -18
  245. package/dist/tag/selection.d.ts +10 -0
  246. package/dist/tag/selection.js +12 -0
  247. package/dist/textarea/grow.d.ts +22 -0
  248. package/dist/textarea/grow.js +20 -0
  249. package/dist/textarea/root/textarea-root.d.ts +9 -0
  250. package/dist/textarea/root/textarea-root.js +9 -1
  251. package/dist/time-input/root/time-input-root.d.ts +44 -5
  252. package/dist/time-input/root/time-input-root.js +69 -39
  253. package/dist/time-picker/root/time-picker-root.d.ts +54 -8
  254. package/dist/time-picker/root/time-picker-root.js +121 -91
  255. package/dist/timeline/group/timeline-group.d.ts +46 -0
  256. package/dist/timeline/group/timeline-group.js +37 -0
  257. package/dist/timeline/index.d.ts +1 -0
  258. package/dist/timeline/index.js +1 -0
  259. package/dist/timeline/root/timeline-root.d.ts +10 -3
  260. package/dist/toast/hotkey.d.ts +22 -0
  261. package/dist/toast/hotkey.js +23 -0
  262. package/dist/toast/item/toast-item.d.ts +26 -18
  263. package/dist/toast/item/toast-item.js +55 -48
  264. package/dist/toast/root/toast-root.d.ts +42 -8
  265. package/dist/toast/root/toast-root.js +64 -4
  266. package/dist/toggle/group/toggle-group.d.ts +57 -15
  267. package/dist/toggle/group/toggle-group.js +69 -9
  268. package/dist/toggle/root/toggle-root.d.ts +12 -1
  269. package/dist/toggle/root/toggle-root.js +11 -3
  270. package/dist/toolbar/root/toolbar-root.d.ts +13 -0
  271. package/dist/toolbar/root/toolbar-root.js +15 -5
  272. package/dist/tooltip/focus-target.d.ts +6 -0
  273. package/dist/tooltip/focus-target.js +9 -0
  274. package/dist/tooltip/root/tooltip-root.d.ts +42 -17
  275. package/dist/tooltip/root/tooltip-root.js +50 -9
  276. package/dist/tour/naming.d.ts +30 -0
  277. package/dist/tour/naming.js +10 -0
  278. package/dist/tour/root/tour-root.d.ts +35 -0
  279. package/dist/tour/root/tour-root.js +56 -5
  280. package/dist/tour/stop/tour-stop.d.ts +53 -8
  281. package/dist/tour/stop/tour-stop.js +45 -15
  282. package/dist/tour/wait.d.ts +11 -0
  283. package/dist/tour/wait.js +19 -0
  284. package/dist/tree/item/tree-item.d.ts +54 -1
  285. package/dist/tree/item/tree-item.js +89 -20
  286. package/dist/tree/root/tree-root.d.ts +37 -0
  287. package/dist/tree/root/tree-root.js +51 -6
  288. package/dist/tree/selection.d.ts +24 -0
  289. package/dist/tree/selection.js +26 -0
  290. package/dist/trend/root/trend-root.d.ts +13 -0
  291. package/dist/trend/root/trend-root.js +14 -9
  292. package/dist/video/media-events.d.ts +49 -0
  293. package/dist/video/media-events.js +39 -0
  294. package/dist/video/root/video-root.d.ts +99 -13
  295. package/dist/video/root/video-root.js +84 -47
  296. package/dist/video-playlist/menu/video-playlist-menu.d.ts +3 -0
  297. package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
  298. package/dist/video-playlist/order.d.ts +41 -0
  299. package/dist/video-playlist/order.js +34 -0
  300. package/dist/video-playlist/root/video-playlist-root.d.ts +70 -2
  301. package/dist/video-playlist/root/video-playlist-root.js +60 -16
  302. package/dist/virtual-list/controls.d.ts +4 -0
  303. package/dist/virtual-list/controls.js +30 -0
  304. package/dist/virtual-list/fill.d.ts +30 -0
  305. package/dist/virtual-list/fill.js +39 -0
  306. package/dist/virtual-list/keys.d.ts +15 -0
  307. package/dist/virtual-list/keys.js +24 -0
  308. package/dist/virtual-list/root/virtual-list-root.d.ts +155 -23
  309. package/dist/virtual-list/root/virtual-list-root.js +175 -34
  310. package/lint.json +1 -1
  311. package/package.json +5 -5
  312. package/types/jsx.d.ts +8871 -1286
  313. package/types/svelte.d.ts +3 -0
  314. package/types/vue.d.ts +1918 -269
@@ -4,9 +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 { AriaForwardController, customElement, HelixElement, IndicatorController, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, focusableElements, HelixElement, IndicatorController, KeyboardNavigationController, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
+ const FOCUSABLE = 'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])';
11
+ const navigable = (item) => {
12
+ const control = () => item.navigationControl ??
13
+ (item.matches(FOCUSABLE) ? item : focusableElements(item)[0]);
14
+ return {
15
+ contains: (node) => {
16
+ const target = control();
17
+ return target !== undefined && node !== null && (node === target || target.contains(node));
18
+ },
19
+ disabled: false,
20
+ focusItem: () => control()?.focus(),
21
+ select: () => { },
22
+ selected: false,
23
+ };
24
+ };
10
25
  let HelixHeadlessNavigation = class HelixHeadlessNavigation extends HelixElement {
11
26
  constructor() {
12
27
  super(...arguments);
@@ -23,7 +38,15 @@ let HelixHeadlessNavigation = class HelixHeadlessNavigation extends HelixElement
23
38
  observe: () => this.items,
24
39
  surface: () => this.partNamed('nav'),
25
40
  });
41
+ this.arrows = false;
26
42
  this.label = 'Main';
43
+ this.keys = new KeyboardNavigationController(this, {
44
+ items: () => (this.arrows ? this.items.map(navigable) : []),
45
+ origin: 'focus',
46
+ reserved: () => this.orientation === 'vertical' ? ['ArrowLeft', 'ArrowRight'] : ['ArrowDown', 'ArrowUp'],
47
+ selects: () => false,
48
+ wrap: false,
49
+ });
27
50
  this.aria = new AriaForwardController(this, {
28
51
  label: true,
29
52
  target: () => this.partNamed('nav'),
@@ -62,6 +85,9 @@ let HelixHeadlessNavigation = class HelixHeadlessNavigation extends HelixElement
62
85
  </nav>`;
63
86
  }
64
87
  };
88
+ __decorate([
89
+ property({ type: Boolean })
90
+ ], HelixHeadlessNavigation.prototype, "arrows", void 0);
65
91
  __decorate([
66
92
  property()
67
93
  ], HelixHeadlessNavigation.prototype, "label", void 0);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Where a key moves focus in a feed of `count` rows, from the row at `at`, or
3
+ * `undefined` for a key the feed does not take.
4
+ *
5
+ * `onRow` says whether focus is on the row's own box. The arrows and `Home` and
6
+ * `End` move only from there, so a control inside a row keeps its keys.
7
+ * `PageDown` and `PageUp` move from anywhere in a row, which is how the APG feed
8
+ * pattern moves on from inside an article. The answer stays inside the feed.
9
+ */
10
+ export declare const feedStep: (key: string, at: number, count: number, onRow: boolean) => number | undefined;
@@ -0,0 +1,14 @@
1
+ export const feedStep = (key, at, count, onRow) => {
2
+ if (count <= 0) {
3
+ return undefined;
4
+ }
5
+ const anywhere = { PageDown: at + 1, PageUp: at - 1 };
6
+ const fromRow = {
7
+ ArrowDown: at + 1,
8
+ ArrowUp: at - 1,
9
+ End: count - 1,
10
+ Home: 0,
11
+ };
12
+ const to = anywhere[key] ?? (onRow ? fromRow[key] : undefined);
13
+ return to === undefined ? undefined : Math.max(0, Math.min(to, count - 1));
14
+ };
@@ -79,6 +79,12 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
79
79
  * Empty is the runtime's own.
80
80
  */
81
81
  locale: string;
82
+ /**
83
+ * @internal Set by the feed: this row's place in it, counting from one, read
84
+ * as `aria-posinset`. Zero leaves the place to the browser, which counts the
85
+ * rows it can see.
86
+ */
87
+ position: number;
82
88
  /**
83
89
  * Whether the notification has been read.
84
90
  *
@@ -96,6 +102,17 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
96
102
  */
97
103
  get read(): boolean;
98
104
  set read(next: boolean);
105
+ /**
106
+ * @internal Set by the feed: how many rows the feed holds in all, read as
107
+ * `aria-setsize`, and `-1` when nobody knows. Zero leaves it to the browser.
108
+ */
109
+ setSize: number;
110
+ /**
111
+ * @internal Set by the feed: whether `Tab` stops on this row. Every row is a
112
+ * stop until the feed has chosen one, which is also what a server sends, so
113
+ * the first render on the client is the server's.
114
+ */
115
+ stop: boolean;
99
116
  /**
100
117
  * What a screen reader hears before an unread notification's text.
101
118
  *
@@ -17,6 +17,9 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
17
17
  this.at = '';
18
18
  this.defaultRead = false;
19
19
  this.locale = '';
20
+ this.position = 0;
21
+ this.setSize = 0;
22
+ this.stop = true;
20
23
  this.unreadLabel = 'Unread';
21
24
  this.isNotification = true;
22
25
  this.readState = undefined;
@@ -58,6 +61,8 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
58
61
  willUpdate(changed) {
59
62
  super.willUpdate(changed);
60
63
  hostAria(this.internals, { role: 'listitem' });
64
+ this.internals.ariaPosInSet = this.position > 0 ? String(this.position) : null;
65
+ this.internals.ariaSetSize = this.setSize === 0 ? null : String(this.setSize);
61
66
  this.toggleState('read', this.read);
62
67
  }
63
68
  updated(changed) {
@@ -105,10 +110,15 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
105
110
  ></${stamp}>`;
106
111
  }
107
112
  render() {
108
- return html `<div part="item">
113
+ return html `<div
114
+ part="item"
115
+ role="group"
116
+ aria-labelledby="label"
117
+ tabindex=${this.stop ? 0 : -1}
118
+ >
109
119
  <span part="media"><slot name="media"></slot></span>
110
120
  <div part="body">
111
- <div part="label">${this.read ? nothing : html `<span role="img" aria-label=${this.unreadLabel}></span>`}<slot></slot></div>
121
+ <div id="label" part="label">${this.read ? nothing : html `<span role="img" aria-label=${this.unreadLabel}></span>`}<slot></slot></div>
112
122
  <slot name="description"></slot>
113
123
  <slot name="detail"></slot>
114
124
  <span part="actions"><slot name="actions" @slotchange=${this.handleActions}></slot></span>
@@ -152,9 +162,18 @@ __decorate([
152
162
  __decorate([
153
163
  property()
154
164
  ], HelixHeadlessNotification.prototype, "locale", void 0);
165
+ __decorate([
166
+ property({ attribute: false })
167
+ ], HelixHeadlessNotification.prototype, "position", void 0);
155
168
  __decorate([
156
169
  property({ type: Boolean })
157
170
  ], HelixHeadlessNotification.prototype, "read", null);
171
+ __decorate([
172
+ property({ attribute: false })
173
+ ], HelixHeadlessNotification.prototype, "setSize", void 0);
174
+ __decorate([
175
+ property({ attribute: false })
176
+ ], HelixHeadlessNotification.prototype, "stop", void 0);
158
177
  __decorate([
159
178
  property({ attribute: 'unread-label' })
160
179
  ], HelixHeadlessNotification.prototype, "unreadLabel", void 0);
@@ -21,6 +21,19 @@ declare const HelixHeadlessNotifications_base: (new (...args: any[]) => import("
21
21
  * the items and written to an attribute, so a stylesheet and a badge can both
22
22
  * use it without counting.
23
23
  *
24
+ * **The feed is one `Tab` stop for its rows.** Each row takes focus itself and
25
+ * is read by its label. `ArrowDown` and `ArrowUp` move to the next and the
26
+ * previous row, `Home` and `End` to the first and the last, and `PageDown` and
27
+ * `PageUp` move a row on or back from anywhere inside one, as in the APG feed
28
+ * pattern. The controls inside a row stay in the `Tab` order. Set `total` when
29
+ * the feed holds one page of a longer list, and each row says its place in the
30
+ * whole of it.
31
+ *
32
+ * **`load-more` adds a button after the list** that sends `helix-load-more`,
33
+ * for a feed that holds one page of a longer list. Append the next page, set
34
+ * `loading` while it is on its way, and take `load-more` off once there is
35
+ * nothing more. The button is hidden while the feed is loading.
36
+ *
24
37
  * @since 0.1.0
25
38
  * @status stable
26
39
  * @category feedback
@@ -32,6 +45,7 @@ declare const HelixHeadlessNotifications_base: (new (...args: any[]) => import("
32
45
  * @slot header - Above the list, for a title and the mark-all control.
33
46
  *
34
47
  * @event {HelixDismissEvent} helix-dismiss - Fired when the user dismisses a row. The row dispatches it, and you can listen for it on the feed. Cancelable: cancel it to keep the row.
48
+ * @event {HelixLoadMoreEvent} helix-load-more - Fired when a reader presses the `load-more` button, asking for the next page of the feed.
35
49
  * @event {HelixReadEvent} helix-read - Fired by an item when it is marked read or unread, and by the feed when everything is marked read. Cancelable.
36
50
  *
37
51
  * @csspart empty - Shown when there is nothing in the feed.
@@ -41,6 +55,7 @@ declare const HelixHeadlessNotifications_base: (new (...args: any[]) => import("
41
55
  * @csspart live - A copy of the empty and loading messages, in a live region that screen readers announce when they change. Visually hidden in the styled tier.
42
56
  * @csspart loading - Shown while the feed is loading.
43
57
  * @csspart mark-all - The control that marks everything read.
58
+ * @csspart more - The button after the list that asks for more, while `load-more` is set and the feed is not loading.
44
59
  *
45
60
  * @cssstate media - While a notification in the list holds something in its media slot, so every row keeps that column.
46
61
  */
@@ -59,6 +74,15 @@ export declare class HelixHeadlessNotifications extends HelixHeadlessNotificatio
59
74
  * The feed's name, read by screen readers.
60
75
  */
61
76
  label: string;
77
+ /**
78
+ * Whether to draw a button after the list that asks for the next page, by
79
+ * sending `helix-load-more`. Take it off once there is nothing more.
80
+ */
81
+ loadMore: boolean;
82
+ /**
83
+ * The text of the `load-more` button.
84
+ */
85
+ loadMoreLabel: string;
62
86
  /**
63
87
  * Whether to draw a control that marks everything read.
64
88
  */
@@ -67,6 +91,14 @@ export declare class HelixHeadlessNotifications extends HelixHeadlessNotificatio
67
91
  * Label for the mark-all control.
68
92
  */
69
93
  markAllLabel: string;
94
+ /**
95
+ * How many notifications there are in all, where the feed holds only some of
96
+ * them, read as each row's `aria-setsize`. `-1` says the count is unknown.
97
+ *
98
+ * Unset, the browser counts the rows it can see, which is right for a feed
99
+ * that holds everything.
100
+ */
101
+ total?: number;
70
102
  /**
71
103
  * How many items are unread, counted from the items themselves.
72
104
  *
@@ -158,6 +190,32 @@ export declare class HelixHeadlessNotifications extends HelixHeadlessNotificatio
158
190
  */
159
191
  private readonly handleRead;
160
192
  private assign;
193
+ /** The row `Tab` stops on, once a reader has been to one. */
194
+ private stopRow;
195
+ /**
196
+ * The rows a reader can reach with the keys: every row in the feed, in order,
197
+ * less the ones a folded group renders nowhere.
198
+ */
199
+ private get reachable();
200
+ /** The row whose own box is the element focus is on, if it is one. */
201
+ private rowFocused;
202
+ /** A row taking focus becomes the feed's stop, so `Tab` comes back to it. */
203
+ private readonly handleFocusIn;
204
+ /**
205
+ * Moves between rows.
206
+ *
207
+ * The arrows and `Home` and `End` only from a row's own box, so a control in
208
+ * a row keeps its keys. `PageDown` and `PageUp` from anywhere in a row, which
209
+ * is how the APG feed pattern moves on from inside an article.
210
+ */
211
+ private readonly handleKeydown;
212
+ /**
213
+ * Tells each row whether it is the stop, and where it sits in the feed.
214
+ *
215
+ * Through each row's own properties, written once it has hydrated, so a
216
+ * server's markup stays the server's: every row is a stop until this runs.
217
+ */
218
+ private rove;
161
219
  connectedCallback(): void;
162
220
  disconnectedCallback(): void;
163
221
  protected willUpdate(changed: PropertyValues): void;
@@ -202,6 +260,8 @@ export declare class HelixHeadlessNotifications extends HelixHeadlessNotificatio
202
260
  private markEmpty;
203
261
  protected updated(changed: PropertyValues): void;
204
262
  protected render(): TemplateResult;
263
+ /** Asks for the next page, for the `load-more` button. */
264
+ private readonly askForMore;
205
265
  /**
206
266
  * Marks everything read, and keeps focus in the feed.
207
267
  *
@@ -4,13 +4,16 @@ 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, focusableElements, HelixElement, HelixReadEvent, hostAria, MixinCollection, markRegion, } from '@fusion.dev/helix-core';
7
+ import { customElement, deepContains, focusableElements, HelixElement, HelixLoadMoreEvent, HelixReadEvent, hostAria, MixinCollection, markRegion, rovingStop, whenHydrated, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
+ import { feedStep } from '../feed-keys.js';
10
11
  let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinCollection(HelixElement) {
11
12
  constructor() {
12
13
  super(...arguments);
13
14
  this.label = '';
15
+ this.loadMore = false;
16
+ this.loadMoreLabel = 'Load more';
14
17
  this.markAll = false;
15
18
  this.markAllLabel = 'Mark all as read';
16
19
  this.handleSlotChange = () => {
@@ -20,6 +23,39 @@ let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinC
20
23
  this.handleRead = () => {
21
24
  queueMicrotask(() => this.requestUpdate());
22
25
  };
26
+ this.handleFocusIn = (event) => {
27
+ const row = this.rowFocused(event.composedPath()[0]);
28
+ if (row && row !== this.stopRow) {
29
+ this.stopRow = row;
30
+ this.rove();
31
+ }
32
+ };
33
+ this.handleKeydown = (event) => {
34
+ if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) {
35
+ return;
36
+ }
37
+ const rows = this.reachable;
38
+ const from = event.composedPath()[0];
39
+ const own = this.rowFocused(from);
40
+ const inside = own ?? rows.find((row) => from instanceof Node && deepContains(row, from));
41
+ if (!inside) {
42
+ return;
43
+ }
44
+ const to = feedStep(event.key, rows.indexOf(inside), rows.length, own !== undefined);
45
+ if (to === undefined) {
46
+ return;
47
+ }
48
+ event.preventDefault();
49
+ const next = rows[to];
50
+ if (next) {
51
+ this.stopRow = next;
52
+ this.rove();
53
+ rowBox(next)?.focus();
54
+ }
55
+ };
56
+ this.askForMore = () => {
57
+ this.dispatchEvent(new HelixLoadMoreEvent());
58
+ };
23
59
  this.handleMarkAll = (event) => {
24
60
  const button = event.currentTarget;
25
61
  const focused = this.shadowRoot?.activeElement === button;
@@ -106,13 +142,36 @@ let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinC
106
142
  slot?.assign?.(...[...this.children].filter((child) => child.getAttribute('slot') === name));
107
143
  }
108
144
  }
145
+ get reachable() {
146
+ return this.items.filter((row) => !('assignedSlot' in row) || row.assignedSlot !== null);
147
+ }
148
+ rowFocused(target) {
149
+ return this.reachable.find((row) => rowBox(row) === target);
150
+ }
151
+ rove() {
152
+ const rows = this.reachable;
153
+ const stop = rovingStop(rows, this.stopRow);
154
+ const direct = this.rows;
155
+ for (const row of this.items) {
156
+ const position = direct.indexOf(row) + 1;
157
+ whenHydrated(row, () => {
158
+ row.stop = row === stop;
159
+ row.position = this.total === undefined ? 0 : position;
160
+ row.setSize = this.total ?? 0;
161
+ });
162
+ }
163
+ }
109
164
  connectedCallback() {
110
165
  super.connectedCallback();
111
166
  this.addEventListener('helix-read', this.handleRead);
167
+ this.addEventListener('focusin', this.handleFocusIn);
168
+ this.addEventListener('keydown', this.handleKeydown);
112
169
  }
113
170
  disconnectedCallback() {
114
171
  super.disconnectedCallback();
115
172
  this.removeEventListener('helix-read', this.handleRead);
173
+ this.removeEventListener('focusin', this.handleFocusIn);
174
+ this.removeEventListener('keydown', this.handleKeydown);
116
175
  }
117
176
  willUpdate(changed) {
118
177
  super.willUpdate(changed);
@@ -130,6 +189,7 @@ let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinC
130
189
  this.assign();
131
190
  this.markEmpty();
132
191
  this.markMedia();
192
+ this.rove();
133
193
  const unread = this.unread > 0 ? String(this.unread) : null;
134
194
  if (this.getAttribute('unread') !== unread) {
135
195
  if (unread === null) {
@@ -155,19 +215,34 @@ let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinC
155
215
  ${this.renderStatus(hasItems)}
156
216
  </div>
157
217
  ${this.renderLive(hasItems)}
218
+ ${this.loadMore && !this.loading
219
+ ? html `<button part="more" type="button" @click=${this.askForMore}>
220
+ ${this.loadMoreLabel}
221
+ </button>`
222
+ : nothing}
158
223
  <div part="footer"><slot name="footer"></slot></div>`;
159
224
  }
160
225
  };
161
226
  __decorate([
162
227
  property()
163
228
  ], HelixHeadlessNotifications.prototype, "label", void 0);
229
+ __decorate([
230
+ property({ attribute: 'load-more', type: Boolean })
231
+ ], HelixHeadlessNotifications.prototype, "loadMore", void 0);
232
+ __decorate([
233
+ property({ attribute: 'load-more-label' })
234
+ ], HelixHeadlessNotifications.prototype, "loadMoreLabel", void 0);
164
235
  __decorate([
165
236
  property({ attribute: 'mark-all', type: Boolean })
166
237
  ], HelixHeadlessNotifications.prototype, "markAll", void 0);
167
238
  __decorate([
168
239
  property({ attribute: 'mark-all-label' })
169
240
  ], HelixHeadlessNotifications.prototype, "markAllLabel", void 0);
241
+ __decorate([
242
+ property({ type: Number })
243
+ ], HelixHeadlessNotifications.prototype, "total", void 0);
170
244
  HelixHeadlessNotifications = __decorate([
171
245
  customElement('helix-headless-notifications')
172
246
  ], HelixHeadlessNotifications);
173
247
  export { HelixHeadlessNotifications };
248
+ const rowBox = (row) => row.shadowRoot?.querySelector('[part="item"]') ?? null;
@@ -18,6 +18,17 @@ declare const HelixHeadlessNumberInput_base: (new (...args: any[]) => import("@f
18
18
  *
19
19
  * `inputmode="decimal"` keeps the numeric keypad on phones.
20
20
  *
21
+ * `format-options` shows the number with `Intl.NumberFormat` in `locale`, as a
22
+ * currency, a percent or a unit, while the field is not being edited. Focus
23
+ * shows the number itself, and a typed `$1,200` or `25%` is read back with the
24
+ * same options. The value stays the plain number: `1200`, or `0.25` for 25%.
25
+ * The locale's group separators are read in every field, so `1,234` is a
26
+ * number in `en-US`.
27
+ *
28
+ * On iOS and iPadOS the field is a text field described as a number field,
29
+ * with no `role="spinbutton"`, which VoiceOver reads as a slider you cannot
30
+ * type in. React Aria's NumberField does the same.
31
+ *
21
32
  * Validity follows the platform's rules: `min`, `max`, and `step` are checked
22
33
  * and reported through the host's own `setValidity`, under the platform's flag
23
34
  * names.
@@ -80,6 +91,16 @@ export declare class HelixHeadlessNumberInput extends HelixHeadlessNumberInput_b
80
91
  * The name of the step down button, read by screen readers.
81
92
  */
82
93
  decrementLabel: string;
94
+ /**
95
+ * `Intl.NumberFormat` options the number is shown with while the field is
96
+ * not being edited: a currency, a percent, a unit, or a number of decimals.
97
+ * Written as JSON in the attribute.
98
+ *
99
+ * ```html
100
+ * <helix-number-input format-options='{"style":"currency","currency":"EUR"}'></helix-number-input>
101
+ * ```
102
+ */
103
+ formatOptions?: Intl.NumberFormatOptions;
83
104
  /**
84
105
  * The name of the step up button, read by screen readers.
85
106
  */
@@ -133,6 +154,11 @@ export declare class HelixHeadlessNumberInput extends HelixHeadlessNumberInput_b
133
154
  * Prevents editing. The value is still submitted with the form.
134
155
  */
135
156
  readOnly: boolean;
157
+ /**
158
+ * What the field is described as on iOS and iPadOS, where it carries no
159
+ * spinbutton role. See the class description.
160
+ */
161
+ roleDescription: string;
136
162
  /**
137
163
  * Makes the label a grip that changes the value when it is dragged.
138
164
  *
@@ -163,6 +189,27 @@ export declare class HelixHeadlessNumberInput extends HelixHeadlessNumberInput_b
163
189
  * number alone.
164
190
  */
165
191
  valueText: string;
192
+ /**
193
+ * Whether the mouse wheel steps the number while the field has focus, as
194
+ * React Aria's NumberField does unless told not to. Off, the wheel scrolls
195
+ * the page, which is what a reader passing over a form expects.
196
+ */
197
+ wheel: boolean;
198
+ /** Whether the field has focus, so it shows the number itself and not its format. */
199
+ private editing;
200
+ /**
201
+ * What the field shows: the value as written while it is edited, and with
202
+ * `format-options` once it is not.
203
+ */
204
+ private get shown();
205
+ /** Steps on the wheel, where asked, while the field has focus. */
206
+ private readonly handleWheel;
207
+ private readonly handleFocus;
208
+ /**
209
+ * Whether this is an iPhone or an iPad, read once on the client. A server
210
+ * has no navigator and renders the spinbutton.
211
+ */
212
+ private readonly touchApple;
166
213
  private get control();
167
214
  /** The value as a number, or `null` where the field holds nothing usable. */
168
215
  private get numeric();
@@ -4,10 +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 { AriaForwardController, valueAsNumber as asNumber, customElement, fillTemplate, numberAsValue as fromNumber, HelixFormElement, holdCaret, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, appleTouch, valueAsNumber as asNumber, customElement, fillTemplate, formatNumber, numberAsValue as fromNumber, HelixFormElement, holdCaret, MixinControllerValue, MixinField, parseNumber, restoreValue, } from '@fusion.dev/helix-core';
8
8
  import { minus, plus } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
- import { property } from 'lit/decorators.js';
10
+ import { property, state } from 'lit/decorators.js';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
12
  const SCRUB_PITCH = 4;
13
13
  const SCRUB_FINE = 16;
@@ -24,9 +24,28 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
24
24
  this.locale = '';
25
25
  this.placeholder = '';
26
26
  this.readOnly = false;
27
+ this.roleDescription = 'number field';
27
28
  this.scrub = false;
28
29
  this.step = 1;
29
30
  this.valueText = '';
31
+ this.wheel = false;
32
+ this.editing = false;
33
+ this.handleWheel = (event) => {
34
+ if (!this.wheel || !this.editing || event.deltaY === 0) {
35
+ return;
36
+ }
37
+ event.preventDefault();
38
+ if (event.deltaY < 0) {
39
+ this.stepUp();
40
+ }
41
+ else {
42
+ this.stepDown();
43
+ }
44
+ };
45
+ this.handleFocus = () => {
46
+ this.editing = true;
47
+ };
48
+ this.touchApple = appleTouch(typeof navigator === 'undefined' ? undefined : navigator);
30
49
  this.scrubFrom = 0;
31
50
  this.scrubValue = 0;
32
51
  this.scrubStart = '';
@@ -77,6 +96,7 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
77
96
  restoreValue(control, this.value);
78
97
  };
79
98
  this.handleBlur = () => {
99
+ this.editing = false;
80
100
  if (this.settle()) {
81
101
  this.announceChange();
82
102
  }
@@ -122,6 +142,13 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
122
142
  }
123
143
  };
124
144
  }
145
+ get shown() {
146
+ const parsed = this.numeric;
147
+ if (this.editing || this.formatOptions === undefined || parsed === null) {
148
+ return this.value;
149
+ }
150
+ return formatNumber(parsed, this.locale, this.formatOptions);
151
+ }
125
152
  get control() {
126
153
  return this.partNamed('control');
127
154
  }
@@ -130,11 +157,7 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
130
157
  if (text === '') {
131
158
  return null;
132
159
  }
133
- const read = decimalComma(this.locale) && text.includes(',') && !text.includes('.')
134
- ? text.replace(',', '.')
135
- : text;
136
- const parsed = read.includes(',') ? Number.NaN : Number(read);
137
- return Number.isFinite(parsed) ? parsed : null;
160
+ return parseNumber(text, this.locale, this.formatOptions);
138
161
  }
139
162
  onStep(value) {
140
163
  const step = Math.abs(this.step) || 1;
@@ -291,9 +314,10 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
291
314
  <input
292
315
  part="control"
293
316
  id=${this.controlId}
294
- .value=${this.value}
317
+ .value=${this.shown}
295
318
  type="text"
296
- role="spinbutton"
319
+ role=${this.touchApple ? nothing : 'spinbutton'}
320
+ aria-roledescription=${this.touchApple ? this.roleDescription : nothing}
297
321
  inputmode="decimal"
298
322
  autocomplete=${this.autocomplete || nothing}
299
323
  name=${this.name || nothing}
@@ -303,17 +327,27 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
303
327
  aria-valuemin=${this.min ?? nothing}
304
328
  aria-valuemax=${this.max ?? nothing}
305
329
  aria-valuenow=${parsed ?? nothing}
306
- aria-valuetext=${this.valueText && parsed !== null
307
- ? fillTemplate(this.valueText, { value: this.value })
308
- : nothing}
330
+ aria-valuetext=${parsed === null
331
+ ? nothing
332
+ : this.valueText
333
+ ? fillTemplate(this.valueText, {
334
+ value: this.formatOptions
335
+ ? formatNumber(parsed, this.locale, this.formatOptions)
336
+ : this.value,
337
+ })
338
+ : this.formatOptions
339
+ ? formatNumber(parsed, this.locale, this.formatOptions)
340
+ : nothing}
309
341
  ?disabled=${this.isDisabled}
310
342
  ?readonly=${this.readOnly}
311
343
  ?required=${this.required}
312
344
  @blur=${this.handleBlur}
313
345
  @change=${this.handleChange}
346
+ @focus=${this.handleFocus}
314
347
  @input=${this.handleInput}
315
348
  @keydown=${this.handleKeydown}
316
349
  @keypress=${this.handleKeypress}
350
+ @wheel=${this.handleWheel}
317
351
  />
318
352
  <slot name="end"></slot>
319
353
  <button
@@ -347,6 +381,9 @@ __decorate([
347
381
  __decorate([
348
382
  property({ attribute: 'decrement-label' })
349
383
  ], HelixHeadlessNumberInput.prototype, "decrementLabel", void 0);
384
+ __decorate([
385
+ property({ attribute: 'format-options', type: Object })
386
+ ], HelixHeadlessNumberInput.prototype, "formatOptions", void 0);
350
387
  __decorate([
351
388
  property({ attribute: 'increment-label' })
352
389
  ], HelixHeadlessNumberInput.prototype, "incrementLabel", void 0);
@@ -374,6 +411,9 @@ __decorate([
374
411
  __decorate([
375
412
  property({ attribute: 'readonly', reflect: true, type: Boolean })
376
413
  ], HelixHeadlessNumberInput.prototype, "readOnly", void 0);
414
+ __decorate([
415
+ property({ attribute: 'role-description' })
416
+ ], HelixHeadlessNumberInput.prototype, "roleDescription", void 0);
377
417
  __decorate([
378
418
  property({ reflect: true, type: Boolean })
379
419
  ], HelixHeadlessNumberInput.prototype, "scrub", void 0);
@@ -386,20 +426,16 @@ __decorate([
386
426
  __decorate([
387
427
  property({ attribute: 'value-text' })
388
428
  ], HelixHeadlessNumberInput.prototype, "valueText", void 0);
429
+ __decorate([
430
+ property({ type: Boolean })
431
+ ], HelixHeadlessNumberInput.prototype, "wheel", void 0);
432
+ __decorate([
433
+ state()
434
+ ], HelixHeadlessNumberInput.prototype, "editing", void 0);
389
435
  HelixHeadlessNumberInput = __decorate([
390
436
  customElement('helix-headless-number-input')
391
437
  ], HelixHeadlessNumberInput);
392
438
  export { HelixHeadlessNumberInput };
393
- const commas = new Map();
394
- const decimalComma = (locale) => {
395
- let comma = commas.get(locale);
396
- if (comma === undefined) {
397
- const parts = new Intl.NumberFormat(locale || undefined).formatToParts(1.5);
398
- comma = parts.find((part) => part.type === 'decimal')?.value === ',';
399
- commas.set(locale, comma);
400
- }
401
- return comma;
402
- };
403
439
  const decimals = (value) => {
404
440
  const text = String(value);
405
441
  const exponent = /e-(\d+)$/.exec(text);