@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,16 +4,18 @@ 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, fillTemplate, HelixCompleteEvent, HelixFormElement, MixinControllerValue, MixinField, submitImplicitly, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, HelixCompleteEvent, HelixFormElement, MixinControllerValue, MixinField, restoreValue, submitImplicitly, verdictFlags, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
- import { property } from 'lit/decorators.js';
10
- import { cleanCode } from '../code.js';
9
+ import { property, state } from 'lit/decorators.js';
10
+ import { caretTo, cleanCode, codeVerdict, overwriteCode, readTyped, } from '../code.js';
11
11
  import { separatorsAfter } from '../format.js';
12
+ const UNSEEN = 'position:absolute;width:1px;height:1px;opacity:0;pointer-events:none;font-size:16px';
13
+ const keepFocus = (event) => event.preventDefault();
12
14
  let HelixHeadlessPinInput = class HelixHeadlessPinInput extends MixinControllerValue(MixinField(HelixFormElement)) {
13
15
  constructor() {
14
16
  super(...arguments);
15
17
  this.aria = new AriaForwardController(this, {
16
- target: () => this.partNamed('field'),
18
+ target: () => this.control,
17
19
  });
18
20
  this.announcesInput = true;
19
21
  this.autocomplete = 'one-time-code';
@@ -23,95 +25,77 @@ let HelixHeadlessPinInput = class HelixHeadlessPinInput extends MixinControllerV
23
25
  this.format = '';
24
26
  this.length = 6;
25
27
  this.mask = false;
28
+ this.maskCharacter = '•';
26
29
  this.placeholder = '';
27
30
  this.readOnly = false;
28
31
  this.type = 'numeric';
32
+ this.active = -1;
33
+ this.pressedAt = undefined;
29
34
  this.handleInput = (event) => {
30
35
  event.stopPropagation();
31
- const cell = event.target;
32
- const at = this.cells.indexOf(cell);
33
- if (at === -1) {
34
- return;
35
- }
36
- if (cell.value === '') {
37
- const characters = this.characters;
38
- characters[at] = '';
39
- this.requestValue(characters.join(''));
40
- this.updateComplete.then(() => {
41
- this.paint();
42
- this.cells[at]?.focus();
43
- });
44
- return;
45
- }
46
- this.write(at, cell.value);
47
- this.updateComplete.then(() => this.paint());
36
+ const control = event.target;
37
+ this.take(readTyped(control.value, control.selectionStart ?? control.value.length, this.type, this.case, this.count));
48
38
  };
49
39
  this.handlePaste = (event) => {
50
- const cell = event.target;
51
- const at = this.cells.indexOf(cell);
52
- if (at === -1 || this.isDisabled || this.readOnly) {
40
+ event.preventDefault();
41
+ const control = this.control;
42
+ if (!control || this.isDisabled || this.readOnly) {
53
43
  return;
54
44
  }
55
- event.preventDefault();
56
- this.write(at, event.clipboardData?.getData('text') ?? '');
45
+ const text = cleanCode(event.clipboardData?.getData('text') ?? '', this.type, this.case);
46
+ if (text === '') {
47
+ return;
48
+ }
49
+ const start = control.selectionStart ?? this.end;
50
+ const end = control.selectionEnd ?? start;
51
+ const kept = end - start > 1 ? this.value.slice(0, start) + this.value.slice(end) : this.value;
52
+ this.take(overwriteCode(kept, start, text, this.count));
57
53
  };
58
54
  this.handleKeydown = (event) => {
59
- const cell = event.target;
60
- const at = this.cells.indexOf(cell);
61
- if (at === -1 || this.isDisabled || this.readOnly) {
55
+ if (event.isComposing || this.isDisabled) {
62
56
  return;
63
57
  }
64
- const step = event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0;
65
- const to = event.key === 'Home'
66
- ? 0
67
- : event.key === 'End'
68
- ? Math.min(this.value.length, this.count - 1)
69
- : at + step;
70
- if (event.key === 'Delete' && at < this.value.length) {
71
- const characters = this.characters;
72
- characters.splice(at, 1);
73
- this.requestValue(characters.join('').replace(/\s+$/, ''));
74
- this.updateComplete.then(() => {
75
- this.paint();
76
- this.cells[at]?.focus();
77
- });
78
- }
79
- else if (event.key === 'Backspace' && cell.value === '' && at > 0) {
80
- const characters = this.characters;
81
- characters[at - 1] = '';
82
- this.requestValue(characters.join('').replace(/\s+$/, ''));
83
- this.updateComplete.then(() => {
84
- this.paint();
85
- this.cells[at - 1]?.focus();
86
- });
87
- }
88
- else if (to !== at && to >= 0 && to < this.count) {
89
- this.cells[to]?.focus();
90
- this.cells[to]?.select();
58
+ if (this.readOnly) {
59
+ if (event.key === 'Backspace' || event.key === 'Delete') {
60
+ event.preventDefault();
61
+ }
62
+ return;
91
63
  }
92
- else {
64
+ const to = caretTo(event.key, this.caretBox, this.end);
65
+ if (to === undefined) {
93
66
  return;
94
67
  }
95
68
  event.preventDefault();
69
+ this.active = to;
70
+ this.placeCaret(true);
96
71
  };
97
- this.committed = '';
98
- this.handleFocus = (event) => {
99
- ;
100
- event.target.select();
101
- };
102
- this.within = false;
103
- this.handleFocusIn = () => {
104
- if (this.within) {
72
+ this.handlePress = (event) => {
73
+ const control = this.control;
74
+ if (!control || this.isDisabled) {
105
75
  return;
106
76
  }
107
- this.within = true;
108
- this.committed = this.value;
109
- };
110
- this.handleFocusOut = (event) => {
111
- if (this.cells.includes(event.relatedTarget)) {
77
+ const boxes = [...this.renderRoot.querySelectorAll('[part~="cell"]')];
78
+ const box = event.target.closest('[part~="cell"]');
79
+ const at = box ? boxes.indexOf(box) : this.end;
80
+ if (this.shadowRoot?.activeElement === control) {
81
+ if (!this.readOnly) {
82
+ this.active = Math.min(at, this.end);
83
+ this.placeCaret(true);
84
+ }
112
85
  return;
113
86
  }
114
- this.within = false;
87
+ this.pressedAt = at;
88
+ control.focus();
89
+ };
90
+ this.committed = '';
91
+ this.handleFocus = () => {
92
+ this.committed = this.value;
93
+ this.active = this.readOnly ? -1 : Math.min(this.pressedAt ?? this.end, this.end);
94
+ this.pressedAt = undefined;
95
+ this.placeCaret(true);
96
+ };
97
+ this.handleBlur = () => {
98
+ this.active = -1;
115
99
  this.commit();
116
100
  };
117
101
  this.handleKeypress = (event) => {
@@ -120,9 +104,6 @@ let HelixHeadlessPinInput = class HelixHeadlessPinInput extends MixinControllerV
120
104
  }
121
105
  };
122
106
  }
123
- get namedControl() {
124
- return this.partNamed('field');
125
- }
126
107
  static { this.shadowRootOptions = {
127
108
  ...HelixFormElement.shadowRootOptions,
128
109
  delegatesFocus: true,
@@ -130,49 +111,45 @@ let HelixHeadlessPinInput = class HelixHeadlessPinInput extends MixinControllerV
130
111
  get count() {
131
112
  return Math.max(1, Math.floor(this.length) || 1);
132
113
  }
133
- get cells() {
134
- return [...this.renderRoot.querySelectorAll('[part="cell"]')];
114
+ get control() {
115
+ return this.partNamed('control');
135
116
  }
136
- get characters() {
137
- return Array.from({ length: this.count }, (_, at) => this.value[at] ?? '');
117
+ get end() {
118
+ return Math.min(this.value.length, this.count - 1);
138
119
  }
139
- clean(text) {
140
- return cleanCode(text, this.type, this.case);
120
+ get caretBox() {
121
+ return this.active < 0 ? -1 : Math.min(this.active, this.end);
141
122
  }
142
- write(from, text) {
143
- const cleaned = this.clean(text);
144
- if (cleaned === '') {
145
- return;
123
+ take({ caret, code }) {
124
+ const applied = this.requestValue(code);
125
+ const control = this.control;
126
+ if (control) {
127
+ restoreValue(control, this.value);
146
128
  }
147
- const start = Math.min(from, this.value.length);
148
- const characters = this.characters;
149
- for (let at = 0; at < cleaned.length && start + at < this.count; at += 1) {
150
- characters[start + at] = cleaned[at];
129
+ if (this.active >= 0 && applied) {
130
+ this.active = caret;
151
131
  }
152
- const next = characters.join('');
153
- const filled = Math.min(this.count - 1, start + cleaned.length);
154
- const applied = this.requestValue(next);
155
- this.updateComplete.then(() => {
156
- this.paint();
157
- this.cells[filled]?.focus();
158
- this.cells[filled]?.select();
159
- if (applied && this.value === next && next.length === this.count) {
160
- this.dispatchEvent(new HelixCompleteEvent({ value: next }));
161
- this.commit();
162
- if (this.autosubmit) {
163
- submitImplicitly(this.form);
164
- }
132
+ this.placeCaret(true);
133
+ if (applied && this.value === code && code.length === this.count) {
134
+ this.dispatchEvent(new HelixCompleteEvent({ value: code }));
135
+ this.commit();
136
+ if (this.autosubmit) {
137
+ submitImplicitly(this.form);
165
138
  }
166
- });
139
+ }
167
140
  }
168
- paint() {
169
- const characters = this.characters;
170
- for (const [at, cell] of this.cells.entries()) {
171
- const want = characters[at] ?? '';
172
- if (cell.value !== want) {
173
- cell.value = want;
174
- }
141
+ placeCaret(force = false) {
142
+ const control = this.control;
143
+ const at = this.caretBox;
144
+ if (!control || at < 0) {
145
+ return;
175
146
  }
147
+ const start = control.selectionStart ?? 0;
148
+ const end = control.selectionEnd ?? start;
149
+ if (!force && end - start > 1) {
150
+ return;
151
+ }
152
+ control.setSelectionRange(at, at < this.value.length ? at + 1 : at);
176
153
  }
177
154
  commit() {
178
155
  if (this.value === this.committed) {
@@ -183,34 +160,28 @@ let HelixHeadlessPinInput = class HelixHeadlessPinInput extends MixinControllerV
183
160
  }
184
161
  sync() {
185
162
  this.internals.setFormValue(this.value);
186
- const first = this.cells[0];
187
- if (!first) {
163
+ const control = this.control;
164
+ if (!control) {
188
165
  return;
189
166
  }
190
- if (this.required && this.value === '') {
191
- this.internals.setValidity({ valueMissing: true }, this.valueMissingMessage || 'Please fill in this field.', first);
192
- }
193
- else if (this.value !== '' && this.value.length < this.count) {
194
- this.internals.setValidity({ tooShort: true }, fillTemplate(this.tooShortMessage || 'Please enter all {length} characters.', {
195
- length: this.count,
196
- }), first);
197
- }
198
- else {
199
- this.internals.setValidity({}, '', first);
200
- }
167
+ const found = verdictFlags(codeVerdict(this.value, this.count, this.required, {
168
+ missing: this.valueMissingMessage,
169
+ short: this.tooShortMessage,
170
+ }));
171
+ this.internals.setValidity(found?.[0] ?? {}, found?.[1] ?? '', control);
201
172
  this.refreshFeedback();
202
173
  }
203
174
  focus(options) {
204
- const cells = this.cells;
205
- const at = Math.min(this.value.length, cells.length - 1);
206
- cells[at]?.focus(options);
175
+ this.control?.focus(options);
207
176
  }
208
177
  clear() {
209
178
  this.requestValue('');
210
- this.updateComplete.then(() => {
211
- this.paint();
212
- this.cells[0]?.focus();
213
- });
179
+ const control = this.control;
180
+ if (control) {
181
+ restoreValue(control, this.value);
182
+ this.pressedAt = 0;
183
+ control.focus();
184
+ }
214
185
  }
215
186
  firstUpdated(changed) {
216
187
  super.firstUpdated(changed);
@@ -219,44 +190,62 @@ let HelixHeadlessPinInput = class HelixHeadlessPinInput extends MixinControllerV
219
190
  updated(changed) {
220
191
  super.updated(changed);
221
192
  this.toggleState('complete', this.value.length >= this.count);
222
- this.paint();
223
193
  this.sync();
194
+ this.placeCaret();
224
195
  }
225
196
  get blocksImplicitSubmission() {
226
197
  return true;
227
198
  }
199
+ renderBox(character, at) {
200
+ const caret = at === this.caretBox;
201
+ const content = character !== ''
202
+ ? this.mask
203
+ ? this.maskCharacter
204
+ : character
205
+ : caret
206
+ ? html `<span part="caret"></span>`
207
+ : this.placeholder
208
+ ? html `<span part="placeholder">${this.placeholder}</span>`
209
+ : nothing;
210
+ return html `<div
211
+ part="cell${caret ? ' cell-active' : ''}${character === '' ? '' : ' cell-filled'}"
212
+ aria-hidden="true"
213
+ >${content}</div>`;
214
+ }
228
215
  render() {
229
- const characters = this.characters;
230
216
  const separators = separatorsAfter(this.format, this.count);
231
- return html `${this.renderGroupLabel()}
217
+ return html `${this.renderLabel()}
232
218
  <div
233
219
  dir="ltr"
234
220
  part="field"
235
- role="group"
236
- aria-describedby=${this.describedBy ?? nothing}
237
- aria-labelledby=${this.labeled ? this.labelId : nothing}
238
- @focusin=${this.handleFocusIn}
239
- @focusout=${this.handleFocusOut}
221
+ @click=${this.handlePress}
222
+ @mousedown=${keepFocus}
240
223
  >
241
- ${characters.map((character, at) => html `<input
242
- part="cell"
243
- .value=${character}
244
- type=${this.mask ? 'password' : 'text'}
245
- placeholder=${this.placeholder || nothing}
246
- inputmode=${this.type === 'numeric' ? 'numeric' : 'text'}
247
- autocomplete=${at === 0 ? this.autocomplete || nothing : 'off'}
248
- aria-label=${fillTemplate(this.cellLabel, { index: at + 1, length: this.count })}
249
- aria-invalid=${this.invalidity ?? nothing}
250
- ?disabled=${this.isDisabled}
251
- ?readonly=${this.readOnly}
252
- @focus=${this.handleFocus}
253
- @input=${this.handleInput}
254
- @keydown=${this.handleKeydown}
255
- @keypress=${this.handleKeypress}
256
- @paste=${this.handlePaste}
257
- />${separators[at]
258
- ? html `<span part="separator" aria-hidden="true">${separators[at]}</span>`
259
- : nothing}`)}
224
+ <input
225
+ part="control"
226
+ id=${this.controlId}
227
+ style=${UNSEEN}
228
+ .value=${this.value}
229
+ type=${this.mask ? 'password' : 'text'}
230
+ inputmode=${this.type === 'numeric' ? 'numeric' : 'text'}
231
+ autocomplete=${this.autocomplete || nothing}
232
+ aria-describedby=${this.describedBy ?? nothing}
233
+ aria-invalid=${this.invalidity ?? nothing}
234
+ aria-required=${this.required ? 'true' : nothing}
235
+ ?disabled=${this.isDisabled}
236
+ ?readonly=${this.readOnly}
237
+ @blur=${this.handleBlur}
238
+ @focus=${this.handleFocus}
239
+ @input=${this.handleInput}
240
+ @keydown=${this.handleKeydown}
241
+ @keypress=${this.handleKeypress}
242
+ @paste=${this.handlePaste}
243
+ />${Array.from({ length: this.count }, (_, at) => {
244
+ const separator = separators[at];
245
+ return html `${this.renderBox(this.value[at] ?? '', at)}${separator
246
+ ? html `<span part="separator" aria-hidden="true">${separator}</span>`
247
+ : nothing}`;
248
+ })}
260
249
  </div>
261
250
  ${this.renderHint()}${this.renderFeedback()}`;
262
251
  }
@@ -282,6 +271,9 @@ __decorate([
282
271
  __decorate([
283
272
  property({ reflect: true, type: Boolean })
284
273
  ], HelixHeadlessPinInput.prototype, "mask", void 0);
274
+ __decorate([
275
+ property({ attribute: 'mask-character' })
276
+ ], HelixHeadlessPinInput.prototype, "maskCharacter", void 0);
285
277
  __decorate([
286
278
  property()
287
279
  ], HelixHeadlessPinInput.prototype, "placeholder", void 0);
@@ -294,6 +286,9 @@ __decorate([
294
286
  __decorate([
295
287
  property()
296
288
  ], HelixHeadlessPinInput.prototype, "type", void 0);
289
+ __decorate([
290
+ state()
291
+ ], HelixHeadlessPinInput.prototype, "active", void 0);
297
292
  HelixHeadlessPinInput = __decorate([
298
293
  customElement('helix-headless-pin-input')
299
294
  ], HelixHeadlessPinInput);
@@ -1,7 +1,8 @@
1
+ import { deepContains } from '@fusion.dev/helix-core';
1
2
  const KEEP = 'script, style, template, link, meta, [aria-live], [role="alert"], [role="log"], [role="status"]';
2
3
  export const hideOutside = (kept) => {
3
4
  const hidden = [];
4
- const holds = (element) => kept.some((one) => composedContains(element, one));
5
+ const holds = (element) => kept.some((one) => deepContains(element, one));
5
6
  const seen = new Set();
6
7
  for (const start of kept) {
7
8
  let node = start;
@@ -31,11 +32,3 @@ export const hideOutside = (kept) => {
31
32
  hidden.length = 0;
32
33
  };
33
34
  };
34
- const composedContains = (outer, inner) => {
35
- for (let node = inner; node; node = node instanceof ShadowRoot ? node.host : node.parentNode) {
36
- if (node === outer) {
37
- return true;
38
- }
39
- }
40
- return false;
41
- };
@@ -1,4 +1,4 @@
1
- import { AriaForwardController, CommandController, HelixElement, type Placement } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, CommandController, HelixElement, OpenPhaseController, type Placement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  declare const HelixHeadlessPopover_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & typeof HelixElement;
4
4
  /**
@@ -69,6 +69,11 @@ declare const HelixHeadlessPopover_base: (new (...args: any[]) => import("@fusio
69
69
  * @csspart content - The scrolling region inside the panel.
70
70
  * @csspart popover - The panel.
71
71
  *
72
+ * @cssproperty --floating-anchor-inline-size - The trigger's width, written on the panel while it is open, as in `min-inline-size: var(--floating-anchor-inline-size)`. Read it; a value set here is written over.
73
+ * @cssproperty --floating-available-block-size - The room between the panel and the edge of the screen it opens toward, written on the panel while it is open. Read it; a value set here is written over.
74
+ *
75
+ * @cssstate entering - From the moment the panel shows until its entrance has finished.
76
+ * @cssstate exiting - From the moment the panel hides until its exit has finished.
72
77
  * @cssstate open - While the popover is open.
73
78
  * @cssstate placed-bottom - While the panel sits below its trigger.
74
79
  * @cssstate placed-left - While the panel sits to the left of its trigger.
@@ -105,6 +110,7 @@ export declare class HelixHeadlessPopover extends HelixHeadlessPopover_base {
105
110
  * It moves if that side does not fit.
106
111
  */
107
112
  placement: Placement;
113
+ protected readonly phases: OpenPhaseController;
108
114
  private readonly panel;
109
115
  /**
110
116
  * The side of its trigger the panel last landed on, which names the arrow's
@@ -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, anchorElement, arrowReach, CommandController, customElement, deepActiveElement, FocusScopeController, HelixElement, headingIn, MixinControllerOpen, PopoverController, reflectElements, sideOf, tellDisclosure, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, anchorElement, arrowReach, CommandController, customElement, deepActiveElement, FocusScopeController, HelixElement, headingIn, MixinControllerOpen, OpenPhaseController, PopoverController, reflectElements, sideOf, tellDisclosure, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { hideOutside } from '../hide-outside.js';
@@ -19,6 +19,14 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
19
19
  });
20
20
  this.offset = 4;
21
21
  this.placement = 'bottom-start';
22
+ this.phases = new OpenPhaseController(this, {
23
+ mark: {
24
+ entering: (on) => this.toggleState('entering', on),
25
+ exiting: (on) => this.toggleState('exiting', on),
26
+ },
27
+ open: () => this.open,
28
+ surface: () => this.partNamed('popover'),
29
+ });
22
30
  this.panel = new PopoverController(this, {
23
31
  anchor: () => this.trigger,
24
32
  arrow: () => this.partNamed('arrow'),
@@ -15,6 +15,13 @@ import { type PropertyValues, type TemplateResult } from 'lit';
15
15
  *
16
16
  * The fill is a fraction of the track, written as a custom property.
17
17
  *
18
+ * **Reaching `max` is said and sent.** When the value arrives at `max` from
19
+ * below, the bar says `complete-label`, "Complete" by default, in a polite
20
+ * `live` region and sends `helix-complete`, so a reader hears that the work is
21
+ * done and a page can move on. A bar that starts full says nothing, and an
22
+ * empty `complete-label` keeps it quiet. The `complete` custom state is set
23
+ * while the bar is full.
24
+ *
18
25
  * @since 0.1.0
19
26
  * @status stable
20
27
  * @category feedback
@@ -23,14 +30,27 @@ import { type PropertyValues, type TemplateResult } from 'lit';
23
30
  *
24
31
  * @slot label - A visible label. Its words name the progress bar in place of `label`.
25
32
  *
33
+ * @event {HelixCompleteEvent} helix-complete - The value reached `max` from below. Its `value` is `max`. Not sent for a bar that starts full.
34
+ *
26
35
  * @csspart groove - The unfilled circle under the ring's filled arc.
27
36
  * @csspart indicator - The filled part that shows the work done.
37
+ * @csspart live - A polite region that says `complete-label` when the work is done, rendered from that moment. Hide it visually.
28
38
  * @csspart track - The full length the work is measured against.
29
39
  * @csspart value - The value shown beside the bar when `show-value` is set.
30
40
  *
31
41
  * @cssproperty --progress-fraction - The value as a fraction of the range, 0 to 1. Pinned to 1 while indeterminate.
42
+ *
43
+ * @cssstate complete - The value is at `max` and the bar is not indeterminate.
32
44
  */
33
45
  export declare class HelixHeadlessProgress extends HelixElement {
46
+ /**
47
+ * The words a screen reader hears when the value reaches `max`, such as
48
+ * "Upload complete".
49
+ *
50
+ * Said once, politely, as the work finishes. Empty says nothing, for a bar
51
+ * whose page announces the result itself.
52
+ */
53
+ completeLabel: string;
34
54
  /**
35
55
  * `Intl.NumberFormat` options the reading is written with, as React Aria's
36
56
  * Meter and ProgressBar take them: a unit, a currency, or a number of
@@ -128,11 +148,18 @@ export declare class HelixHeadlessProgress extends HelixElement {
128
148
  * report progress.
129
149
  */
130
150
  valueText: string;
151
+ /** The region the bar says `complete-label` in, rendered from the first time it does. */
152
+ private readonly status;
153
+ /** Whether the bar was full at the last update, or unknown before the first. */
154
+ private wasComplete;
155
+ /** Whether the work is done: determinate and at `max`. */
156
+ private get complete();
131
157
  /** The range the value is measured in, with a top above its bottom. */
132
158
  private get range();
133
159
  /** What is done, as a fraction of the whole, between 0 and 1. */
134
160
  get fraction(): number;
135
161
  protected willUpdate(changed: PropertyValues): void;
162
+ protected updated(changed: PropertyValues): void;
136
163
  /**
137
164
  * What the bar says: the consumer's words, or the reading in
138
165
  * `format-options`, a share of the range for a percent and the value for
@@ -4,13 +4,13 @@ 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, formatNumber, HelixElement, hostAria, slottedWords, } from '@fusion.dev/helix-core';
7
+ import { customElement, drawStatus, HelixCompleteEvent, HelixElement, hostAria, readingText, StatusController, slottedWords, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
- const WHOLE_PERCENT = { maximumFractionDigits: 0, style: 'percent' };
11
10
  let HelixHeadlessProgress = class HelixHeadlessProgress extends HelixElement {
12
11
  constructor() {
13
12
  super(...arguments);
13
+ this.completeLabel = 'Complete';
14
14
  this.indeterminate = false;
15
15
  this.label = 'Progress';
16
16
  this.locale = '';
@@ -19,10 +19,14 @@ let HelixHeadlessProgress = class HelixHeadlessProgress extends HelixElement {
19
19
  this.showValue = false;
20
20
  this.value = 0;
21
21
  this.valueText = '';
22
+ this.status = new StatusController(this);
22
23
  this.renamed = () => {
23
24
  this.requestUpdate();
24
25
  };
25
26
  }
27
+ get complete() {
28
+ return !this.indeterminate && this.fraction >= 1;
29
+ }
26
30
  get range() {
27
31
  const min = Number.isFinite(this.min) ? this.min : 0;
28
32
  return { max: this.max > min ? this.max : min + 100, min };
@@ -45,13 +49,24 @@ let HelixHeadlessProgress = class HelixHeadlessProgress extends HelixElement {
45
49
  : String(Math.min(Math.max(this.value || min, min), max));
46
50
  this.internals.ariaValueText =
47
51
  this.valueText || (this.formatOptions !== undefined && !this.indeterminate ? this.text : null);
52
+ this.toggleState('complete', this.complete);
53
+ }
54
+ updated(changed) {
55
+ super.updated(changed);
56
+ const complete = this.complete;
57
+ const arrived = complete && this.wasComplete === false;
58
+ this.wasComplete = complete;
59
+ if (!arrived) {
60
+ return;
61
+ }
62
+ if (this.completeLabel !== '') {
63
+ this.status.say(this.completeLabel);
64
+ }
65
+ this.dispatchEvent(new HelixCompleteEvent({ value: String(this.range.max) }));
48
66
  }
49
67
  get text() {
50
- const options = this.formatOptions;
51
68
  return (this.valueText ||
52
- (options && options.style !== 'percent'
53
- ? formatNumber(Math.min(Math.max(this.value, this.range.min), this.range.max), this.locale, options)
54
- : formatNumber(this.fraction, this.locale, options ?? WHOLE_PERCENT)));
69
+ readingText(Math.min(Math.max(this.value, this.range.min), this.range.max), this.fraction, this.locale, this.formatOptions));
55
70
  }
56
71
  render() {
57
72
  const fraction = this.indeterminate ? 1 : this.fraction;
@@ -68,9 +83,12 @@ let HelixHeadlessProgress = class HelixHeadlessProgress extends HelixElement {
68
83
  ? html `<span part="value" aria-hidden="true"
69
84
  >${this.text}</span
70
85
  >`
71
- : ''}`;
86
+ : ''}${drawStatus(this.status)}`;
72
87
  }
73
88
  };
89
+ __decorate([
90
+ property({ attribute: 'complete-label' })
91
+ ], HelixHeadlessProgress.prototype, "completeLabel", void 0);
74
92
  __decorate([
75
93
  property({ attribute: 'format-options', type: Object })
76
94
  ], HelixHeadlessProgress.prototype, "formatOptions", void 0);
@@ -39,6 +39,25 @@ export interface QrLogo {
39
39
  /** An inline SVG's markup, written as a nested `<svg>` at the mark's place. */
40
40
  svg?: string;
41
41
  }
42
+ /** A rectangle the page measured, as `getBoundingClientRect` gives one. */
43
+ export interface QrRect {
44
+ readonly height: number;
45
+ readonly left: number;
46
+ readonly top: number;
47
+ readonly width: number;
48
+ }
49
+ /**
50
+ * A rectangle measured on the page as a place in the drawing: `area` is the
51
+ * drawn code's own rectangle and `box` the side of its `viewBox`, so a logo
52
+ * set over the code by a stylesheet lands where it was drawn.
53
+ */
54
+ export declare const boxIn: (rect: QrRect, area: QrRect, box: number) => QrBox;
55
+ /**
56
+ * The logo's box in the drawing, or nothing where any of it falls outside the
57
+ * code, such as a logo the headless tier sets beside the code rather than
58
+ * over it, which a file of the code alone has no place for.
59
+ */
60
+ export declare const logoBox: (rect: QrRect, area: QrRect, box: number) => QrBox | undefined;
42
61
  /**
43
62
  * The logo as the file writes it: its box, then the mark, as an `<image>` or as
44
63
  * the slotted SVG moved to the mark's place. Nothing for a mark that is