@fusion.dev/helix-headless 0.146.0 → 0.148.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 (288) hide show
  1. package/custom-elements.json +9287 -1084
  2. package/dist/accordion/item/accordion-item.d.ts +43 -21
  3. package/dist/accordion/item/accordion-item.js +37 -11
  4. package/dist/accordion/root/accordion-root.d.ts +8 -2
  5. package/dist/accordion/root/accordion-root.js +6 -8
  6. package/dist/announcement/root/announcement-root.d.ts +66 -6
  7. package/dist/announcement/root/announcement-root.js +144 -13
  8. package/dist/area-chart/root/area-chart-root.d.ts +18 -1
  9. package/dist/area-chart/root/area-chart-root.js +14 -7
  10. package/dist/avatar/root/avatar-root.d.ts +31 -1
  11. package/dist/avatar/root/avatar-root.js +11 -1
  12. package/dist/badge/count.d.ts +14 -0
  13. package/dist/badge/count.js +20 -0
  14. package/dist/badge/root/badge-root.d.ts +57 -6
  15. package/dist/badge/root/badge-root.js +77 -8
  16. package/dist/bar-chart/root/bar-chart-root.js +11 -20
  17. package/dist/bar-chart/stack.d.ts +10 -0
  18. package/dist/bar-chart/stack.js +8 -0
  19. package/dist/blockquote/root/blockquote-root.d.ts +66 -8
  20. package/dist/blockquote/root/blockquote-root.js +85 -8
  21. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +14 -0
  22. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +12 -2
  23. package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -2
  24. package/dist/button/root/button-root.d.ts +24 -38
  25. package/dist/button/root/button-root.js +6 -37
  26. package/dist/button/submitter.d.ts +27 -0
  27. package/dist/button/submitter.js +25 -0
  28. package/dist/button-group/arrows.d.ts +14 -0
  29. package/dist/button-group/arrows.js +10 -0
  30. package/dist/button-group/root/button-group-root.d.ts +141 -3
  31. package/dist/button-group/root/button-group-root.js +142 -2
  32. package/dist/canvas/edge-pan.d.ts +61 -0
  33. package/dist/canvas/edge-pan.js +63 -0
  34. package/dist/canvas/node/canvas-node.d.ts +12 -2
  35. package/dist/canvas/node/canvas-node.js +6 -2
  36. package/dist/canvas/root/canvas-document.d.ts +10 -0
  37. package/dist/canvas/root/canvas-document.js +1 -0
  38. package/dist/canvas/root/canvas-root.d.ts +107 -5
  39. package/dist/canvas/root/canvas-root.js +140 -36
  40. package/dist/canvas/snap.d.ts +42 -0
  41. package/dist/canvas/snap.js +32 -0
  42. package/dist/card/root/card-root.d.ts +12 -1
  43. package/dist/card/root/card-root.js +11 -4
  44. package/dist/carousel/root/carousel-root.d.ts +4 -3
  45. package/dist/carousel/root/carousel-root.js +2 -6
  46. package/dist/chat/root/chat-root.d.ts +28 -4
  47. package/dist/chat/root/chat-root.js +12 -7
  48. package/dist/checkbox/root/checkbox-root.js +5 -7
  49. package/dist/checkbox-group/root/checkbox-group-root.d.ts +13 -0
  50. package/dist/checkbox-group/root/checkbox-group-root.js +16 -7
  51. package/dist/checkbox-group/select-all.d.ts +9 -4
  52. package/dist/checkbox-group/select-all.js +1 -1
  53. package/dist/code-block/root/code-block-root.d.ts +41 -18
  54. package/dist/code-block/root/code-block-root.js +21 -6
  55. package/dist/color-input/root/color-input-root.d.ts +11 -0
  56. package/dist/color-input/root/color-input-root.js +12 -5
  57. package/dist/color-picker/channels.js +1 -2
  58. package/dist/color-picker/root/color-picker-root.d.ts +32 -24
  59. package/dist/color-picker/root/color-picker-root.js +99 -55
  60. package/dist/color-picker/wheel.d.ts +28 -0
  61. package/dist/color-picker/wheel.js +15 -0
  62. package/dist/combobox/root/combobox-root.d.ts +83 -11
  63. package/dist/combobox/root/combobox-root.js +81 -18
  64. package/dist/command/root/command-root.d.ts +7 -4
  65. package/dist/command/root/command-root.js +2 -19
  66. package/dist/copy-button/root/copy-button-root.d.ts +16 -1
  67. package/dist/copy-button/root/copy-button-root.js +7 -2
  68. package/dist/data-table/keys.d.ts +0 -18
  69. package/dist/data-table/keys.js +1 -11
  70. package/dist/data-table/root/data-table-root.js +29 -50
  71. package/dist/date-input/root/date-input-root.d.ts +8 -1
  72. package/dist/date-input/root/date-input-root.js +8 -5
  73. package/dist/date-picker/root/date-picker-root.d.ts +14 -9
  74. package/dist/date-picker/root/date-picker-root.js +21 -16
  75. package/dist/description-list/item/description-list-item.d.ts +22 -4
  76. package/dist/description-list/item/description-list-item.js +14 -4
  77. package/dist/divider/root/divider-root.d.ts +45 -16
  78. package/dist/divider/root/divider-root.js +28 -4
  79. package/dist/donut-chart/root/donut-chart-root.js +13 -6
  80. package/dist/donut-chart/slice-label.d.ts +35 -0
  81. package/dist/donut-chart/slice-label.js +11 -0
  82. package/dist/drawer/root/drawer-root.d.ts +4 -2
  83. package/dist/drawer/root/drawer-root.js +15 -3
  84. package/dist/drilldown/item/drilldown-item.d.ts +21 -1
  85. package/dist/drilldown/item/drilldown-item.js +38 -4
  86. package/dist/drilldown/root/drilldown-root.js +3 -2
  87. package/dist/element-events.generated.js +5 -1
  88. package/dist/empty/root/empty-root.d.ts +46 -2
  89. package/dist/empty/root/empty-root.js +50 -5
  90. package/dist/file-input/root/file-input-root.d.ts +34 -6
  91. package/dist/file-input/root/file-input-root.js +55 -21
  92. package/dist/file-input/status.d.ts +24 -0
  93. package/dist/file-input/status.js +11 -0
  94. package/dist/filter-input/chip/filter-chip.d.ts +23 -0
  95. package/dist/filter-input/chip/filter-chip.js +10 -2
  96. package/dist/filter-input/root/filter-input-root.d.ts +8 -0
  97. package/dist/filter-input/root/filter-input-root.js +17 -27
  98. package/dist/format-date/fields.d.ts +20 -0
  99. package/dist/format-date/fields.js +14 -0
  100. package/dist/format-date/options.d.ts +5 -5
  101. package/dist/format-date/options.js +10 -39
  102. package/dist/format-date/root/format-date-root.d.ts +53 -4
  103. package/dist/format-date/root/format-date-root.js +38 -50
  104. package/dist/graph/root/graph-root.d.ts +23 -5
  105. package/dist/graph/root/graph-root.js +17 -20
  106. package/dist/heatmap/root/buckets.d.ts +21 -0
  107. package/dist/heatmap/root/buckets.js +34 -0
  108. package/dist/heatmap/root/heatmap-root.d.ts +76 -3
  109. package/dist/heatmap/root/heatmap-root.js +105 -20
  110. package/dist/icon/index.d.ts +1 -1
  111. package/dist/icon/index.js +1 -1
  112. package/dist/icon/registry.d.ts +26 -1
  113. package/dist/icon/registry.js +12 -1
  114. package/dist/icon/root/icon-root.d.ts +13 -0
  115. package/dist/icon/root/icon-root.js +23 -2
  116. package/dist/icon/root/index.d.ts +1 -1
  117. package/dist/icon/root/index.js +1 -1
  118. package/dist/image/root/image-root.d.ts +20 -0
  119. package/dist/image/root/image-root.js +6 -1
  120. package/dist/input/root/input-root.d.ts +23 -2
  121. package/dist/input/root/input-root.js +12 -8
  122. package/dist/kbd/root/kbd-root.d.ts +20 -3
  123. package/dist/kbd/root/kbd-root.js +23 -50
  124. package/dist/legend/hidden-series.d.ts +10 -2
  125. package/dist/legend/hidden-series.js +5 -2
  126. package/dist/legend/root/legend-root.d.ts +25 -7
  127. package/dist/legend/root/legend-root.js +25 -26
  128. package/dist/lightbox/root/lightbox-root.d.ts +50 -5
  129. package/dist/lightbox/root/lightbox-root.js +76 -8
  130. package/dist/lightbox/zoom.d.ts +2 -0
  131. package/dist/lightbox/zoom.js +5 -3
  132. package/dist/line-chart/root/line-chart-root.d.ts +16 -0
  133. package/dist/line-chart/root/line-chart-root.js +10 -2
  134. package/dist/list/root/list-root.d.ts +10 -2
  135. package/dist/list/root/list-root.js +31 -24
  136. package/dist/list/selection.d.ts +17 -0
  137. package/dist/list/selection.js +16 -0
  138. package/dist/listbox/root/listbox-root.d.ts +125 -26
  139. package/dist/listbox/root/listbox-root.js +156 -166
  140. package/dist/map/marker/map-marker.d.ts +2 -1
  141. package/dist/map/root/map-root.d.ts +40 -2
  142. package/dist/map/root/map-root.js +64 -23
  143. package/dist/megamenu/hand-over.d.ts +75 -0
  144. package/dist/megamenu/hand-over.js +67 -0
  145. package/dist/megamenu/root/megamenu-root.d.ts +38 -14
  146. package/dist/megamenu/root/megamenu-root.js +29 -50
  147. package/dist/menu/item/menu-item.js +4 -4
  148. package/dist/menu/root/menu-root.d.ts +5 -2
  149. package/dist/meter/root/meter-root.d.ts +42 -0
  150. package/dist/meter/root/meter-root.js +33 -7
  151. package/dist/navigation/root/navigation-root.js +5 -8
  152. package/dist/notifications/group/notification-group.d.ts +8 -1
  153. package/dist/notifications/group/notification-group.js +12 -6
  154. package/dist/notifications/notification/notification.d.ts +12 -1
  155. package/dist/notifications/notification/notification.js +3 -1
  156. package/dist/notifications/root/notifications-root.d.ts +48 -3
  157. package/dist/notifications/root/notifications-root.js +36 -6
  158. package/dist/number-input/grain.d.ts +28 -0
  159. package/dist/number-input/grain.js +14 -0
  160. package/dist/number-input/root/number-input-root.d.ts +17 -1
  161. package/dist/number-input/root/number-input-root.js +29 -18
  162. package/dist/object-inspector/root/object-inspector-root.d.ts +17 -6
  163. package/dist/object-inspector/root/object-inspector-root.js +31 -20
  164. package/dist/option/index.d.ts +1 -0
  165. package/dist/option/index.js +1 -0
  166. package/dist/option/separator/option-separator.d.ts +30 -0
  167. package/dist/option/separator/option-separator.js +21 -0
  168. package/dist/outline/root/outline-root.d.ts +12 -0
  169. package/dist/outline/root/outline-root.js +5 -19
  170. package/dist/pagination/size/pagination-size.d.ts +6 -0
  171. package/dist/pagination/size/pagination-size.js +5 -3
  172. package/dist/pin-input/code.d.ts +51 -1
  173. package/dist/pin-input/code.js +37 -0
  174. package/dist/pin-input/root/pin-input-root.d.ts +98 -121
  175. package/dist/pin-input/root/pin-input-root.js +151 -156
  176. package/dist/popover/hide-outside.js +2 -9
  177. package/dist/popover/root/popover-root.d.ts +7 -1
  178. package/dist/popover/root/popover-root.js +9 -1
  179. package/dist/progress/root/progress-root.d.ts +27 -0
  180. package/dist/progress/root/progress-root.js +25 -7
  181. package/dist/qr-code/export.d.ts +19 -0
  182. package/dist/qr-code/export.js +14 -0
  183. package/dist/qr-code/name.d.ts +11 -0
  184. package/dist/qr-code/name.js +7 -0
  185. package/dist/qr-code/root/qr-code-root.d.ts +31 -9
  186. package/dist/qr-code/root/qr-code-root.js +29 -17
  187. package/dist/radio/group/radio-group.d.ts +15 -0
  188. package/dist/radio/root/radio-root.js +2 -2
  189. package/dist/rating/root/rating-root.d.ts +22 -9
  190. package/dist/rating/root/rating-root.js +15 -39
  191. package/dist/scatter-chart/measures.d.ts +22 -0
  192. package/dist/scatter-chart/measures.js +11 -0
  193. package/dist/scatter-chart/root/scatter-chart-root.d.ts +26 -0
  194. package/dist/scatter-chart/root/scatter-chart-root.js +21 -7
  195. package/dist/scroll-area/scrollbar.d.ts +0 -8
  196. package/dist/scroll-area/scrollbar.js +1 -1
  197. package/dist/select/align.d.ts +47 -0
  198. package/dist/select/align.js +7 -0
  199. package/dist/select/root/select-root.d.ts +115 -11
  200. package/dist/select/root/select-root.js +104 -20
  201. package/dist/slider/root/slider-root.d.ts +18 -1
  202. package/dist/slider/root/slider-root.js +22 -0
  203. package/dist/sparkline/readings.d.ts +30 -12
  204. package/dist/sparkline/readings.js +32 -15
  205. package/dist/sparkline/root/sparkline-root.d.ts +46 -3
  206. package/dist/sparkline/root/sparkline-root.js +60 -21
  207. package/dist/speed-dial/group/speed-dial-group.d.ts +48 -0
  208. package/dist/speed-dial/group/speed-dial-group.js +43 -0
  209. package/dist/speed-dial/index.d.ts +1 -0
  210. package/dist/speed-dial/index.js +1 -0
  211. package/dist/speed-dial/item/speed-dial-item.d.ts +27 -2
  212. package/dist/speed-dial/item/speed-dial-item.js +43 -5
  213. package/dist/speed-dial/long-press.d.ts +50 -0
  214. package/dist/speed-dial/long-press.js +48 -0
  215. package/dist/speed-dial/root/speed-dial-root.d.ts +86 -1
  216. package/dist/speed-dial/root/speed-dial-root.js +123 -6
  217. package/dist/splitter/root/splitter-root.d.ts +9 -2
  218. package/dist/splitter/root/splitter-root.js +18 -3
  219. package/dist/steps/root/steps-root.d.ts +8 -1
  220. package/dist/steps/root/steps-root.js +6 -1
  221. package/dist/steps/step/step.d.ts +1 -0
  222. package/dist/steps/step/step.js +1 -1
  223. package/dist/table/cell/table-cell.d.ts +8 -1
  224. package/dist/table/cell/table-cell.js +6 -2
  225. package/dist/table/column/table-column.d.ts +6 -0
  226. package/dist/table/column/table-column.js +5 -1
  227. package/dist/table/grid-controller.d.ts +117 -0
  228. package/dist/table/grid-controller.js +293 -0
  229. package/dist/table/root/table-root.d.ts +47 -3
  230. package/dist/table/root/table-root.js +93 -29
  231. package/dist/table/row/table-row.d.ts +36 -0
  232. package/dist/table/row/table-row.js +52 -2
  233. package/dist/tabs/root/tabs-root.d.ts +5 -3
  234. package/dist/tag/group/tag-group.d.ts +16 -4
  235. package/dist/tag/group/tag-group.js +23 -20
  236. package/dist/textarea/root/textarea-root.d.ts +14 -2
  237. package/dist/textarea/root/textarea-root.js +6 -7
  238. package/dist/time-input/root/time-input-root.d.ts +5 -1
  239. package/dist/time-input/root/time-input-root.js +10 -7
  240. package/dist/time-picker/root/time-picker-root.d.ts +12 -14
  241. package/dist/time-picker/root/time-picker-root.js +18 -23
  242. package/dist/toast/item/toast-item.d.ts +9 -6
  243. package/dist/toast/item/toast-item.js +1 -1
  244. package/dist/toast/root/toast-root.d.ts +40 -4
  245. package/dist/toast/root/toast-root.js +68 -16
  246. package/dist/toast/words.d.ts +12 -0
  247. package/dist/toast/words.js +25 -0
  248. package/dist/toggle/group/toggle-group.d.ts +29 -5
  249. package/dist/toggle/group/toggle-group.js +26 -43
  250. package/dist/toggle/root/toggle-root.js +2 -4
  251. package/dist/toolbar/root/toolbar-root.d.ts +52 -5
  252. package/dist/toolbar/root/toolbar-root.js +88 -13
  253. package/dist/tooltip/root/tooltip-root.d.ts +7 -1
  254. package/dist/tooltip/root/tooltip-root.js +10 -2
  255. package/dist/tour/root/tour-root.d.ts +15 -0
  256. package/dist/tour/root/tour-root.js +20 -1
  257. package/dist/tour/stop/tour-stop.d.ts +3 -0
  258. package/dist/tour/stop/tour-stop.js +3 -3
  259. package/dist/tree/root/tree-root.js +2 -2
  260. package/dist/trend/figure.d.ts +16 -0
  261. package/dist/trend/figure.js +8 -0
  262. package/dist/trend/root/trend-root.d.ts +41 -2
  263. package/dist/trend/root/trend-root.js +29 -3
  264. package/dist/video/root/extra-controls.d.ts +12 -0
  265. package/dist/video/root/extra-controls.js +6 -2
  266. package/dist/video/root/video-root.d.ts +16 -1
  267. package/dist/video/root/video-root.js +15 -1
  268. package/dist/video-playlist/root/video-playlist-root.d.ts +11 -1
  269. package/dist/video-playlist/root/video-playlist-root.js +3 -0
  270. package/dist/virtual-list/fill.d.ts +11 -0
  271. package/dist/virtual-list/fill.js +9 -0
  272. package/dist/virtual-list/keys.d.ts +16 -7
  273. package/dist/virtual-list/keys.js +17 -6
  274. package/dist/virtual-list/plan.d.ts +17 -1
  275. package/dist/virtual-list/plan.js +13 -2
  276. package/dist/virtual-list/root/virtual-list-root.d.ts +25 -3
  277. package/dist/virtual-list/root/virtual-list-root.js +53 -41
  278. package/lint.json +1 -1
  279. package/package.json +5 -5
  280. package/types/jsx.d.ts +4934 -954
  281. package/types/svelte.d.ts +2 -0
  282. package/types/vue.d.ts +946 -253
  283. package/dist/color-picker/swatches.d.ts +0 -12
  284. package/dist/color-picker/swatches.js +0 -26
  285. package/dist/rating/steps.d.ts +0 -7
  286. package/dist/rating/steps.js +0 -9
  287. package/dist/tooltip/focus-target.d.ts +0 -6
  288. package/dist/tooltip/focus-target.js +0 -9
@@ -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, formatNumbers, HelixBeforeChangeEvent, HelixFormElement, requestChange, } from '@fusion.dev/helix-core';
7
+ import { customElement, formatNumbers, HelixBeforeChangeEvent, HelixFormElement, hostWords, requestChange, } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { html } from 'lit/static-html.js';
@@ -89,6 +89,7 @@ let HelixHeadlessPaginationSize = class HelixHeadlessPaginationSize extends Heli
89
89
  renderSelect() {
90
90
  const select = this.tag('select');
91
91
  const option = this.tag('option');
92
+ const named = this.isConnected ? hostWords(this) : '';
92
93
  return html `<${select}
93
94
  part="control"
94
95
  exportparts="feedback: control-feedback, hint: control-hint, indicator: control-indicator,
@@ -97,9 +98,10 @@ let HelixHeadlessPaginationSize = class HelixHeadlessPaginationSize extends Heli
97
98
  listbox-filter-mark: control-listbox-filter-mark, listbox-hint: control-listbox-hint,
98
99
  listbox-items: control-listbox-items, listbox-label: control-listbox-label,
99
100
  listbox-live: control-listbox-live, listbox-loading: control-listbox-loading, listbox-search: control-listbox-search,
100
- listbox-search-field: control-listbox-search-field, surface: control-surface,
101
+ listbox-search-field: control-listbox-search-field, live: control-live, surface: control-surface,
101
102
  trigger: control-trigger, value: control-value"
102
- label=${this.label}
103
+ label=${named ? '' : this.label}
104
+ aria-label=${named || nothing}
103
105
  ?disabled=${this.isDisabled}
104
106
  .value=${String(this.current)}
105
107
  @helix-before-change=${this.handleChange}
@@ -1,4 +1,8 @@
1
- import type { PinCase, PinType } from './root/pin-input-root.js';
1
+ import { type PickerVerdict } from '@fusion.dev/helix-core';
2
+ /** What a box accepts, which decides the keyboard a phone offers. */
3
+ export type PinType = 'alphanumeric' | 'numeric' | 'text';
4
+ /** How typed letters are cased: as typed, or turned upper or lower case. */
5
+ export type PinCase = 'lower' | 'preserve' | 'upper';
2
6
  /**
3
7
  * Typed or pasted text as the code's characters: trimmed, with what `type`
4
8
  * refuses dropped, and its letters cased as `letterCase` asks. Web Awesome's
@@ -8,3 +12,49 @@ import type { PinCase, PinType } from './root/pin-input-root.js';
8
12
  * `text` keeps everything.
9
13
  */
10
14
  export declare const cleanCode: (text: string, type: PinType, letterCase?: PinCase) => string;
15
+ /** A code after an edit, and the box the caret is in after it. */
16
+ export interface CodeEdit {
17
+ /** The box the caret is in, from 0, never past the last box. */
18
+ caret: number;
19
+ /** The code's characters, as many as there are boxes at most. */
20
+ code: string;
21
+ }
22
+ /**
23
+ * What the one field holds after the platform edited it, as a code: `raw` is
24
+ * the field's text with the caret at `caret`, cleaned and cut at `count`, and
25
+ * the caret is moved past only the characters the code kept before it.
26
+ */
27
+ export declare const readTyped: (raw: string, caret: number, type: PinType, letterCase: PinCase, count: number) => CodeEdit;
28
+ /**
29
+ * A run of characters written over `code` from box `at` onward, which is what a
30
+ * paste does: the boxes it reaches take its characters and the rest keep
31
+ * theirs, and anything past the last box is dropped.
32
+ *
33
+ * **A code has no holes in it**, so the run starts no later than the end of
34
+ * the code: a whole code pasted into an empty field fills from the first box
35
+ * wherever the caret was, and two characters pasted into box three of a field
36
+ * holding two fill from box three.
37
+ */
38
+ export declare const overwriteCode: (code: string, at: number, text: string, count: number) => CodeEdit;
39
+ /** The messages a code's verdict is written with, where the page set its own. */
40
+ export interface CodeMessages {
41
+ /** For a code with no characters in a required field. */
42
+ missing?: string | undefined;
43
+ /** For a code with fewer characters than boxes; `{length}` is how many. */
44
+ short?: string | undefined;
45
+ }
46
+ /**
47
+ * What is wrong with a code, as a picker field reports it: a required field
48
+ * with nothing in it is missing, a code with fewer characters than boxes is
49
+ * too short, and anything else is fine (`''`). The platform's own words where
50
+ * the page set none.
51
+ */
52
+ export declare const codeVerdict: (code: string, count: number, required: boolean, messages: CodeMessages) => PickerVerdict;
53
+ /**
54
+ * The box a caret key moves to from `caret`, kept within the boxes up to
55
+ * `end`, or `undefined` for a key that moves nothing.
56
+ *
57
+ * Not `inlineStep`: the row is left to right whatever the page, so the key
58
+ * pointing right is the one that moves on in every script.
59
+ */
60
+ export declare const caretTo: (key: string, caret: number, end: number) => number | undefined;
@@ -1,3 +1,4 @@
1
+ import { fillTemplate } from '@fusion.dev/helix-core';
1
2
  export const cleanCode = (text, type, letterCase = 'preserve') => {
2
3
  const trimmed = text.trim();
3
4
  const kept = type === 'numeric'
@@ -11,3 +12,39 @@ export const cleanCode = (text, type, letterCase = 'preserve') => {
11
12
  ? kept.toLowerCase()
12
13
  : kept;
13
14
  };
15
+ export const readTyped = (raw, caret, type, letterCase, count) => ({
16
+ caret: Math.min(cleanCode(raw.slice(0, caret), type, letterCase).length, count - 1),
17
+ code: cleanCode(raw, type, letterCase).slice(0, count),
18
+ });
19
+ export const overwriteCode = (code, at, text, count) => {
20
+ const start = Math.min(at, code.length);
21
+ const characters = [...code.slice(0, count)];
22
+ for (let offset = 0; offset < text.length && start + offset < count; offset += 1) {
23
+ characters[start + offset] = text[offset];
24
+ }
25
+ return { caret: Math.min(count - 1, start + text.length), code: characters.join('') };
26
+ };
27
+ export const codeVerdict = (code, count, required, messages) => {
28
+ if (required && code === '') {
29
+ return ['valueMissing', messages.missing || 'Please fill in this field.'];
30
+ }
31
+ if (code !== '' && code.length < count) {
32
+ return [
33
+ 'tooShort',
34
+ fillTemplate(messages.short || 'Please enter all {length} characters.', { length: count }),
35
+ ];
36
+ }
37
+ return '';
38
+ };
39
+ export const caretTo = (key, caret, end) => {
40
+ const to = key === 'ArrowRight'
41
+ ? caret + 1
42
+ : key === 'ArrowLeft'
43
+ ? caret - 1
44
+ : key === 'Home'
45
+ ? 0
46
+ : key === 'End'
47
+ ? end
48
+ : undefined;
49
+ return to === undefined ? undefined : Math.max(0, Math.min(to, end));
50
+ };
@@ -1,53 +1,56 @@
1
1
  import { AriaForwardController, HelixFormElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
- /** What a cell accepts, which decides the keyboard a phone offers. */
4
- export type PinType = 'alphanumeric' | 'numeric' | 'text';
5
- /** How typed letters are cased: as typed, or turned upper or lower case. */
6
- export type PinCase = 'lower' | 'preserve' | 'upper';
3
+ import { type PinCase, type PinType } from '../code.js';
4
+ export type { PinCase, PinType } from '../code.js';
7
5
  declare const HelixHeadlessPinInput_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
8
6
  /**
9
7
  * @summary A field for a short code, with one box for each character.
10
8
  *
11
9
  * @description
12
- * Each cell is its own `<input>`, so every box has a real focus ring, caret,
13
- * and tab stop.
10
+ * One real `<input>` holds the code, and the boxes are drawn from it, as Web
11
+ * Awesome's OTP input does. A screen reader meets one field, named by the
12
+ * label, and the code never sits in a row of separate fields. The boxes are
13
+ * hidden from it.
14
14
  *
15
- * Input longer than one character is treated as a paste, in any cell. This
16
- * covers pasting, a browser autofilling an SMS code, and a password manager,
17
- * which all put the whole code into the focused box. A six-character code that
18
- * arrives in cell three fills from cell three.
15
+ * The caret is drawn in the box it is in, and the box is marked with
16
+ * `cell-active` while the field has focus. A filled box selects its character,
17
+ * so typing replaces it and moves on.
19
18
  *
20
- * The code is one value to a form. The cells are in a shadow root and submit
21
- * nothing. The host is form-associated and submits the joined string, which
22
- * also carries validity and responds to a form reset.
19
+ * Input longer than one character is treated as a paste. This covers pasting,
20
+ * a browser autofilling an SMS code, and a password manager. A six-character
21
+ * code pasted into an empty field fills from the first box.
23
22
  *
24
- * Only the first cell has `autocomplete="one-time-code"`, because the hint
25
- * applies to the whole field.
23
+ * The code is one value to a form. The field is in a shadow root and submits
24
+ * nothing. The host is form-associated and submits the code, which also
25
+ * carries validity and responds to a form reset. The field carries
26
+ * `autocomplete="one-time-code"`, which lets a phone offer the code from a
27
+ * message.
26
28
  *
27
29
  * `autosubmit` submits the form once the last box is filled, through the form's
28
30
  * default button where there is one. `format` draws separators between the
29
- * boxes, such as the dash in `###-###`, as text a screen reader skips. Delete
30
- * removes the character in the focused box and pulls the rest of the code
31
- * back, as Backspace removes the one before it.
31
+ * boxes, such as the dash in `###-###`, as text a screen reader skips. `mask`
32
+ * makes the field a password field and draws `mask-character` in each filled
33
+ * box, so the code is never drawn as text.
32
34
  *
33
- * The row of cells always runs left to right, with `dir="ltr"`, including in
35
+ * The row of boxes always runs left to right, with `dir="ltr"`, including in
34
36
  * Arabic and Hebrew. The first box is the leftmost, a paste fills to the right,
35
37
  * and the right arrow moves to the next box. The label, hint, and feedback
36
38
  * follow the page's direction.
37
39
  *
38
- * **Keyboard.** Each box is a tab stop of its own, as each is a real input.
40
+ * **Keyboard.** The field is one tab stop.
39
41
  *
40
42
  * | Key | What it does |
41
43
  * |---|---|
42
44
  * | A character | Fills the box and moves to the next one. A longer run is read as a paste. |
43
45
  * | Left, Right | Moves to the box before or after. |
44
46
  * | Home, End | Moves to the first box, or to the box after the last character. |
45
- * | Backspace | In an empty box, clears the box before it and moves there. |
47
+ * | Backspace | Removes the character in the box, or in an empty box the one before it, and moves there. |
46
48
  * | Delete | Removes the character in the box and pulls the rest of the code back. |
47
49
  * | Enter | Submits the form. |
48
50
  *
49
- * The row has `role="group"` and the field's name, and each box is named by
50
- * `cell-label`, such as "Character 2 of 6". `helix-complete` is a
51
+ * The field is a text field, or a password field under `mask`, named by the
52
+ * label and described by the hint and the message, with `aria-invalid` while
53
+ * a message shows and `aria-required` under `required`. `helix-complete` is a
51
54
  * `HelixCompleteEvent` sent once the last box is filled.
52
55
  *
53
56
  * @since 0.1.0
@@ -65,11 +68,16 @@ declare const HelixHeadlessPinInput_base: (new (...args: any[]) => import("@fusi
65
68
  * @event {HelixCompleteEvent} helix-complete - The last box was filled.
66
69
  * @event {Event} input - The code changed.
67
70
  *
71
+ * @csspart caret - The caret, drawn in the empty box it is in while the field has focus.
68
72
  * @csspart cell - One box.
73
+ * @csspart cell-active - The box the caret is in, while the field has focus. Set together with `cell`.
74
+ * @csspart cell-filled - A box that holds a character. Set together with `cell`.
75
+ * @csspart control - The one field that holds the code, out of sight: the boxes are drawn from it.
69
76
  * @csspart feedback - The message that says what is wrong with the code.
70
77
  * @csspart field - The row of boxes, ordered left to right in every language.
71
78
  * @csspart hint - The help under the boxes, shown while there is no feedback.
72
79
  * @csspart label - The label above the boxes.
80
+ * @csspart placeholder - The `placeholder` text in an empty box.
73
81
  * @csspart separator - The text `format` draws between two boxes.
74
82
  *
75
83
  * @cssstate complete - Every box holds a character.
@@ -77,17 +85,12 @@ declare const HelixHeadlessPinInput_base: (new (...args: any[]) => import("@fusi
77
85
  */
78
86
  export declare class HelixHeadlessPinInput extends HelixHeadlessPinInput_base {
79
87
  protected readonly aria: AriaForwardController;
80
- /** @internal The element a missing name is checked on: the row of cells, so the name of one cell does not count as the name of the field. */
81
- protected get namedControl(): HTMLElement | null;
82
- /** Typed or dragged, so every edit is announced. See `announcesInput`. */
88
+ /** Typed or pasted, so every edit is announced. See `announcesInput`. */
83
89
  protected readonly announcesInput = true;
84
90
  /**
85
- * The autofill hint for the browser, set on the first box only.
86
- *
87
- * `one-time-code` is why this defaults to anything at all: it is the token
88
- * that lets a phone offer the code out of a message. Later cells stay `off`
89
- * for the reason in the class comment above, that six cells each offering
90
- * the same code is one offer made six times.
91
+ * The autofill hint for the browser. `one-time-code` is why this defaults to
92
+ * anything at all: it is the token that lets a phone offer the code out of a
93
+ * message.
91
94
  */
92
95
  autocomplete: string;
93
96
  /**
@@ -103,7 +106,11 @@ export declare class HelixHeadlessPinInput extends HelixHeadlessPinInput_base {
103
106
  */
104
107
  case: PinCase;
105
108
  /**
106
- * Accessible name for one box, with `{index}` and `{length}` filled in.
109
+ * The name each box once had, with `{index}` and `{length}` filled in.
110
+ *
111
+ * @deprecated No longer read. The boxes are drawn from one field, which is
112
+ * named by `label`, and a screen reader does not meet the boxes. Removed in
113
+ * the next breaking release.
107
114
  */
108
115
  cellLabel: string;
109
116
  /**
@@ -117,23 +124,23 @@ export declare class HelixHeadlessPinInput extends HelixHeadlessPinInput_base {
117
124
  */
118
125
  length: number;
119
126
  /**
120
- * Whether the characters are hidden, the way a password is.
127
+ * Whether the characters are hidden, the way a password is. The field is a
128
+ * password field, and each filled box draws `mask-character`.
121
129
  */
122
130
  mask: boolean;
123
131
  /**
124
- * What an empty box shows, usually a single dot.
125
- *
126
- * The platform's own placeholder, on each cell, rather than a character
127
- * drawn behind the field: it clears the moment anything is typed, it is
128
- * already excluded from the accessible value, and it works the same under
129
- * `mask`, where the cell is a password input and a hand-drawn glyph would
130
- * have to know that.
131
- *
132
- * Empty by default, because a row of boxes already reads as a row of boxes.
132
+ * What a filled box draws under `mask`, in place of its character, as Web
133
+ * Awesome's `--mask-char`.
134
+ */
135
+ maskCharacter: string;
136
+ /**
137
+ * What an empty box shows, usually a single dot. It is not shown in the box
138
+ * the caret is in. Empty by default, because a row of boxes already reads as
139
+ * a row of boxes.
133
140
  */
134
141
  placeholder: string;
135
142
  /**
136
- * Whether the boxes block input. A read-only code still submits its value.
143
+ * Whether the field blocks input. A read-only code still submits its value.
137
144
  */
138
145
  readOnly: boolean;
139
146
  /**
@@ -159,51 +166,60 @@ export declare class HelixHeadlessPinInput extends HelixHeadlessPinInput_base {
159
166
  *
160
167
  * **Not reflected**: nothing selects on it, so reflecting only writes
161
168
  * `type="numeric"` onto every `<helix-headless-pin-input>` ever rendered, an
162
- * attribute no consumer's markup had. The area rules carry the sentence
163
- * "reflect only what CSS selects on". The real inputs take it as a binding,
169
+ * attribute no consumer's markup had. The field takes it as `inputmode`,
164
170
  * which is what makes the keyboard follow.
165
171
  */
166
172
  type: PinType;
167
173
  /** Focus is delegated, so the host is never the focus target. */
168
174
  static shadowRootOptions: ShadowRootInit;
175
+ /**
176
+ * The box the caret is in while the field has focus, and -1 while it does
177
+ * not or while the field is read-only, where there is nothing to type into.
178
+ */
179
+ private active;
180
+ /** The box a press asked for, taken when the focus it starts arrives. */
181
+ private pressedAt;
169
182
  /** How many boxes there are, clamped once so every reader agrees. */
170
183
  private get count();
171
- private get cells();
172
- /** The code as characters, padded to the row so every box has one to show. */
173
- private get characters();
174
- /** Everything this box would accept, with the rest dropped and cased. */
175
- private clean;
184
+ private get control();
185
+ /** The furthest box the caret can be in: after the code, and the last box at most. */
186
+ private get end();
187
+ /** The box the caret is drawn in, kept within the code a script may have shortened. */
188
+ private get caretBox();
176
189
  /**
177
- * Writes a run of characters from one box onward.
190
+ * Takes a code the field was edited to, and moves the caret to `caret`.
178
191
  *
179
192
  * The single path for typing, pasting and autofill alike, because they are
180
- * the same act at different lengths and splitting them is how the three
181
- * disagree about trimming, about the cursor, and about what happens at the
182
- * end.
193
+ * the same act at different lengths. Through `requestValue`, so a consumer
194
+ * who owns the code can refuse it, and a refused edit puts the field back.
183
195
  */
184
- private write;
196
+ private take;
185
197
  /**
186
- * Puts the value back into the boxes.
198
+ * Puts the field's selection on the box the caret is in: the character
199
+ * there, so typing replaces it, or the empty place after the code.
187
200
  *
188
- * The cells are uncontrolled between renders, because Lit will not rewrite an
189
- * input's `.value` when the bound string has not changed, and a refused
190
- * keystroke leaves the box holding a character the element rejected. This is
191
- * the same put-back `Input` does with `restoreValue`, once per box.
201
+ * Left alone while the reader holds a wider selection of their own, such as
202
+ * everything after Control+A, unless `force` says the caret moved.
192
203
  */
193
- private paint;
204
+ private placeCaret;
194
205
  private readonly handleInput;
206
+ /**
207
+ * A paste writes over the boxes from the caret on, rather than into the
208
+ * field's text, so a code pasted into box three of a full code replaces the
209
+ * characters there instead of pushing them along.
210
+ */
195
211
  private readonly handlePaste;
196
212
  /**
197
- * The keys that move between boxes, and the one that clears backwards.
198
- *
199
- * Backspace in an empty box goes back and clears the one it lands in, which
200
- * is what makes a code correctable without the reader hunting for the box
201
- * they meant. In a full box it does what it always does and is left alone.
213
+ * The keys that move between boxes. Backspace and Delete are the field's
214
+ * own: the selection on a filled box makes them remove its character, and
215
+ * the empty place after the code makes Backspace remove the one before.
202
216
  *
203
217
  * Home goes to the first box and End to the end of the code, the box after
204
218
  * its last character, which is where the caret goes in a single text field.
205
219
  */
206
220
  private readonly handleKeydown;
221
+ /** A press on a box puts the caret there, or after the code where the box is past it. */
222
+ private readonly handlePress;
207
223
  /**
208
224
  * The code as it stood when focus arrived, so a visit that changed nothing
209
225
  * says nothing.
@@ -220,72 +236,37 @@ export declare class HelixHeadlessPinInput extends HelixHeadlessPinInput_base {
220
236
  * | types a character and deletes it | silent | silent | silent |
221
237
  *
222
238
  * One row differed and it is the one that matters: a consumer who assigns
223
- * `value` and then has a reader tab through the row was told the reader had
239
+ * `value` and then has a reader tab through the field was told the reader had
224
240
  * entered a code. `change` on a field means "the reader had a go at this",
225
241
  * `MixinField` treats it as exactly that, and announcing a value the consumer
226
242
  * set themselves is a false positive rather than the safe half of a trade.
227
243
  *
228
- * **The narrower divergence that is left** is a script assigning while the row
229
- * is focused, where the platform stays silent because no user edit happened
230
- * and this commits on the way out. Closing it needs a dirty flag set from real
231
- * cell input rather than a baseline, which is more machinery than the case has
232
- * earned: a consumer writing to a field the reader is inside is already in a
233
- * fight with them.
234
- *
235
244
  * Seeded in `firstUpdated`, a code in the markup being committed by whoever
236
- * wrote it, and re-taken on every arrival from outside the row.
245
+ * wrote it, and re-taken on every arrival.
237
246
  */
238
247
  private committed;
239
- /** Selecting on focus, so typing replaces rather than appending. */
248
+ /** Takes the baseline, and puts the caret in the box pressed or after the code. */
240
249
  private readonly handleFocus;
241
250
  /**
242
- * Whether focus is currently somewhere inside the row.
243
- *
244
- * **The arrival is a transition, not a `relatedTarget` reading**, which is
245
- * what the exit still does. `relatedTarget` does not work on the way in for
246
- * two reasons and the second decides it. Every character moves focus to the
247
- * next box, so an unguarded `focusin` re-baselines on each keystroke and the
248
- * row can never report anything: measured, the three commit specs went silent
249
- * at once. And `focusin` carries `relatedTarget` in a browser and not in
250
- * happy-dom, so a guard written on it holds in production and not in the
251
- * specs, which is the worse half. This flag needs neither: entering is
252
- * `focusin` while it is `false`, and nothing else is.
253
- */
254
- private within;
255
- /**
256
- * Takes the baseline when focus arrives from outside the row.
257
- *
258
- * The value at arrival is what the platform compares against, and the table
259
- * on `committed` is why.
260
- */
261
- private readonly handleFocusIn;
262
- /**
263
- * Commits when focus leaves the row.
251
+ * Commits when focus leaves the field.
264
252
  *
265
253
  * `change` is one of the two events `MixinField` reads as "the reader has had
266
254
  * a go at this", so without this a code that was never completed is never
267
255
  * `touched` and its own `tooShort` message cannot show: two boxes of four
268
- * filled and tabbed away from is answered with nothing at all. `Input` gets
269
- * this for free by relaying its control's own `change`; here the cells each
270
- * fire one, none of them means the code is entered, and neither does any
271
- * single one of them mean the reader has left.
272
- *
273
- * **Focus leaving the row, and not a cell.** Every character moves focus to
274
- * the next box, so a per-cell blur would commit on every keystroke, which is
275
- * the fault this exists to fix pointed the other way.
256
+ * filled and tabbed away from is answered with nothing at all.
276
257
  */
277
- private readonly handleFocusOut;
258
+ private readonly handleBlur;
278
259
  /**
279
260
  * Announces the code as entered, from whichever moment reached it first.
280
261
  *
281
262
  * **One writer, because the fact has two moments and only one meaning.** A
282
- * filled code and a row being left are both "the reader is done with this".
263
+ * filled code and a field being left are both "the reader is done with this".
283
264
  * Dispatching from either site directly is how a moment gets added that fires
284
265
  * without baselining.
285
266
  *
286
267
  * Silent when nothing moved since the baseline, which is what makes it safe
287
268
  * to call from both: completing a code that is already the committed one, or
288
- * leaving a row nobody edited, says nothing.
269
+ * leaving a field nobody edited, says nothing.
289
270
  *
290
271
  * `composed: false`, like the native event this stands in for. It is
291
272
  * dispatched on the host, so a consumer hears it.
@@ -307,20 +288,15 @@ export declare class HelixHeadlessPinInput extends HelixHeadlessPinInput_base {
307
288
  * | typed `1234` | valid | valid |
308
289
  *
309
290
  * So `required` decides whether *nothing* is acceptable, and the length rule
310
- * applies to anything that is not nothing. That is the right split for a code
311
- * as well: two of four characters is not an optional code omitted, it is a
312
- * code got wrong, and submitting it would send a fragment. The gate on the
313
- * message is `touched` rather than `required`, which is what stops a reader
314
- * being told off before they have had a go.
315
- *
316
- * `execCommand('insertText')` is not enough to measure this: `tooShort`
317
- * applies only to a value the user edited, and the first run of the probe
318
- * reported every case valid because nothing had set that flag.
291
+ * applies to anything that is not nothing. Two of four characters is not an
292
+ * optional code omitted, it is a code got wrong, and submitting it would send
293
+ * a fragment. The gate on the message is `touched` rather than `required`,
294
+ * which is what stops a reader being told off before they have had a go.
319
295
  */
320
296
  private sync;
321
297
  /** Puts the caret in the first empty box. */
322
298
  focus(options?: FocusOptions): void;
323
- /** Empties every box and moves focus to the first. */
299
+ /** Empties every box and puts the caret in the first. */
324
300
  clear(): void;
325
301
  protected firstUpdated(changed: PropertyValues): void;
326
302
  protected updated(changed: PropertyValues): void;
@@ -333,6 +309,8 @@ export declare class HelixHeadlessPinInput extends HelixHeadlessPinInput_base {
333
309
  private readonly handleKeypress;
334
310
  /** @internal A single line of text counts against implicit submission. */
335
311
  protected get blocksImplicitSubmission(): boolean;
312
+ /** What one box draws: its character, the mask, the caret, or the placeholder. */
313
+ private renderBox;
336
314
  protected render(): TemplateResult;
337
315
  }
338
316
  declare global {
@@ -340,4 +318,3 @@ declare global {
340
318
  'helix-headless-pin-input': HelixHeadlessPinInput;
341
319
  }
342
320
  }
343
- export {};