@fusion.dev/helix-headless 0.142.1 → 0.144.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/custom-elements.json +25381 -1202
  2. package/dist/accordion/item/accordion-item.d.ts +38 -2
  3. package/dist/accordion/item/accordion-item.js +38 -6
  4. package/dist/accordion/root/accordion-root.d.ts +23 -1
  5. package/dist/accordion/root/accordion-root.js +39 -3
  6. package/dist/announcement/root/announcement-root.d.ts +11 -7
  7. package/dist/announcement/root/announcement-root.js +1 -1
  8. package/dist/app-header/root/app-header-root.d.ts +21 -0
  9. package/dist/app-header/root/app-header-root.js +31 -5
  10. package/dist/area-chart/root/area-chart-root.d.ts +12 -4
  11. package/dist/area-chart/root/area-chart-root.js +39 -11
  12. package/dist/avatar/group/avatar-group.d.ts +24 -3
  13. package/dist/avatar/group/avatar-group.js +34 -2
  14. package/dist/avatar/root/avatar-root.d.ts +17 -1
  15. package/dist/avatar/root/avatar-root.js +31 -10
  16. package/dist/badge/root/badge-root.d.ts +12 -0
  17. package/dist/badge/root/badge-root.js +1 -1
  18. package/dist/bar-chart/root/bar-chart-root.d.ts +30 -5
  19. package/dist/bar-chart/root/bar-chart-root.js +58 -21
  20. package/dist/blockquote/root/blockquote-root.d.ts +12 -7
  21. package/dist/blockquote/root/blockquote-root.js +6 -4
  22. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -3
  23. package/dist/breadcrumbs/root/breadcrumbs-root.js +9 -1
  24. package/dist/button/root/button-root.d.ts +84 -1
  25. package/dist/button/root/button-root.js +81 -3
  26. package/dist/callout/root/callout-root.d.ts +32 -6
  27. package/dist/callout/root/callout-root.js +26 -7
  28. package/dist/canvas/root/canvas-root.d.ts +1 -0
  29. package/dist/canvas/root/canvas-root.js +10 -1
  30. package/dist/card/root/card-root.d.ts +30 -1
  31. package/dist/card/root/card-root.js +38 -5
  32. package/dist/carousel/root/carousel-root.d.ts +47 -5
  33. package/dist/carousel/root/carousel-root.js +53 -21
  34. package/dist/chat/composer/chat-composer.d.ts +13 -11
  35. package/dist/chat/composer/chat-composer.js +6 -12
  36. package/dist/chat/message/chat-message.js +8 -1
  37. package/dist/chat/root/chat-root.d.ts +1 -0
  38. package/dist/chat/root/chat-root.js +2 -1
  39. package/dist/checkbox/root/checkbox-root.d.ts +7 -0
  40. package/dist/checkbox/root/checkbox-root.js +9 -3
  41. package/dist/checkbox-group/root/checkbox-group-root.d.ts +116 -33
  42. package/dist/checkbox-group/root/checkbox-group-root.js +141 -44
  43. package/dist/code/root/code-root.d.ts +1 -1
  44. package/dist/code-block/lines.d.ts +13 -0
  45. package/dist/code-block/lines.js +2 -0
  46. package/dist/code-block/root/code-block-root.d.ts +49 -0
  47. package/dist/code-block/root/code-block-root.js +44 -3
  48. package/dist/color-input/root/color-input-root.d.ts +60 -2
  49. package/dist/color-input/root/color-input-root.js +55 -7
  50. package/dist/color-picker/channels.d.ts +26 -0
  51. package/dist/color-picker/channels.js +84 -0
  52. package/dist/color-picker/root/color-picker-root.d.ts +118 -12
  53. package/dist/color-picker/root/color-picker-root.js +183 -13
  54. package/dist/combobox/root/combobox-root.d.ts +126 -3
  55. package/dist/combobox/root/combobox-root.js +224 -21
  56. package/dist/command/root/command-root.d.ts +47 -10
  57. package/dist/command/root/command-root.js +45 -4
  58. package/dist/copy-button/from.d.ts +22 -0
  59. package/dist/copy-button/from.js +28 -0
  60. package/dist/copy-button/root/copy-button-root.d.ts +48 -7
  61. package/dist/copy-button/root/copy-button-root.js +54 -8
  62. package/dist/data-table/root/data-table-root.d.ts +164 -10
  63. package/dist/data-table/root/data-table-root.js +488 -57
  64. package/dist/date-input/root/date-input-root.d.ts +123 -9
  65. package/dist/date-input/root/date-input-root.js +147 -45
  66. package/dist/date-picker/root/date-picker-root.d.ts +229 -18
  67. package/dist/date-picker/root/date-picker-root.js +342 -46
  68. package/dist/dialog/description.d.ts +9 -0
  69. package/dist/dialog/description.js +14 -0
  70. package/dist/dialog/root/dialog-root.d.ts +101 -10
  71. package/dist/dialog/root/dialog-root.js +83 -4
  72. package/dist/donut-chart/root/donut-chart-root.d.ts +61 -16
  73. package/dist/donut-chart/root/donut-chart-root.js +48 -13
  74. package/dist/drawer/root/drawer-root.d.ts +93 -7
  75. package/dist/drawer/root/drawer-root.js +126 -5
  76. package/dist/drawer/swipe-start.d.ts +9 -0
  77. package/dist/drawer/swipe-start.js +12 -0
  78. package/dist/drilldown/item/drilldown-item.d.ts +16 -13
  79. package/dist/drilldown/item/drilldown-item.js +6 -2
  80. package/dist/drilldown/root/drilldown-root.d.ts +21 -5
  81. package/dist/drilldown/root/drilldown-root.js +26 -10
  82. package/dist/element-events.generated.js +42 -13
  83. package/dist/empty/root/empty-root.d.ts +1 -1
  84. package/dist/file-input/root/file-input-root.d.ts +65 -1
  85. package/dist/file-input/root/file-input-root.js +76 -12
  86. package/dist/file-input/screen.d.ts +35 -0
  87. package/dist/file-input/screen.js +49 -0
  88. package/dist/filter-input/menu/filter-menu.d.ts +9 -0
  89. package/dist/filter-input/menu/filter-menu.js +8 -0
  90. package/dist/filter-input/root/filter-input-root.d.ts +43 -6
  91. package/dist/filter-input/root/filter-input-root.js +89 -10
  92. package/dist/filter-input/suggest.d.ts +17 -0
  93. package/dist/filter-input/suggest.js +23 -0
  94. package/dist/format-date/options.d.ts +33 -0
  95. package/dist/format-date/options.js +42 -0
  96. package/dist/format-date/root/format-date-root.d.ts +62 -3
  97. package/dist/format-date/root/format-date-root.js +78 -37
  98. package/dist/graph/root/graph-root.d.ts +24 -3
  99. package/dist/graph/root/graph-root.js +66 -12
  100. package/dist/heatmap/root/buckets.d.ts +2 -1
  101. package/dist/heatmap/root/buckets.js +5 -1
  102. package/dist/heatmap/root/heatmap-root.d.ts +47 -9
  103. package/dist/heatmap/root/heatmap-root.js +46 -18
  104. package/dist/icon/index.d.ts +2 -0
  105. package/dist/icon/index.js +2 -0
  106. package/dist/icon/registry.d.ts +37 -0
  107. package/dist/icon/registry.js +83 -0
  108. package/dist/icon/root/icon-root.d.ts +53 -6
  109. package/dist/icon/root/icon-root.js +72 -4
  110. package/dist/icon/root/index.d.ts +2 -0
  111. package/dist/icon/root/index.js +2 -0
  112. package/dist/icon/set.d.ts +1 -0
  113. package/dist/icon/set.js +3 -0
  114. package/dist/icon-button/root/icon-button-root.d.ts +1 -1
  115. package/dist/image/root/image-root.d.ts +23 -2
  116. package/dist/image/root/image-root.js +23 -4
  117. package/dist/input/root/input-root.d.ts +62 -9
  118. package/dist/input/root/input-root.js +61 -5
  119. package/dist/kbd/root/kbd-root.d.ts +26 -11
  120. package/dist/kbd/root/kbd-root.js +35 -38
  121. package/dist/kbd/shortcut.d.ts +18 -0
  122. package/dist/kbd/shortcut.js +35 -0
  123. package/dist/legend/hidden-series.d.ts +8 -0
  124. package/dist/legend/hidden-series.js +13 -0
  125. package/dist/legend/root/legend-root.d.ts +63 -0
  126. package/dist/legend/root/legend-root.js +61 -7
  127. package/dist/lightbox/item/lightbox-item.d.ts +17 -1
  128. package/dist/lightbox/item/lightbox-item.js +10 -1
  129. package/dist/lightbox/root/lightbox-root.d.ts +77 -6
  130. package/dist/lightbox/root/lightbox-root.js +116 -5
  131. package/dist/line-chart/root/line-chart-root.d.ts +29 -4
  132. package/dist/line-chart/root/line-chart-root.js +58 -17
  133. package/dist/list/item/list-item.d.ts +11 -5
  134. package/dist/list/item/list-item.js +5 -6
  135. package/dist/listbox/range.d.ts +17 -0
  136. package/dist/listbox/range.js +5 -0
  137. package/dist/listbox/root/listbox-root.d.ts +184 -4
  138. package/dist/listbox/root/listbox-root.js +229 -23
  139. package/dist/loader/busy.d.ts +17 -0
  140. package/dist/loader/busy.js +24 -0
  141. package/dist/loader/root/loader-root.d.ts +37 -0
  142. package/dist/loader/root/loader-root.js +52 -3
  143. package/dist/map/root/map-root.d.ts +19 -4
  144. package/dist/map/root/map-root.js +36 -7
  145. package/dist/megamenu/root/megamenu-root.d.ts +10 -1
  146. package/dist/megamenu/root/megamenu-root.js +10 -1
  147. package/dist/menu/item/menu-item.d.ts +41 -12
  148. package/dist/menu/item/menu-item.js +46 -3
  149. package/dist/menu/root/menu-root.d.ts +32 -2
  150. package/dist/menu/root/menu-root.js +48 -8
  151. package/dist/meter/root/meter-root.d.ts +13 -0
  152. package/dist/meter/root/meter-root.js +24 -5
  153. package/dist/navigation/root/navigation-root.d.ts +10 -2
  154. package/dist/navigation/root/navigation-root.js +13 -2
  155. package/dist/notifications/feed-keys.d.ts +10 -0
  156. package/dist/notifications/feed-keys.js +14 -0
  157. package/dist/notifications/group/notification-group.d.ts +1 -0
  158. package/dist/notifications/notification/notification.d.ts +29 -0
  159. package/dist/notifications/notification/notification.js +51 -3
  160. package/dist/notifications/root/notifications-root.d.ts +49 -0
  161. package/dist/notifications/root/notifications-root.js +76 -2
  162. package/dist/number-input/root/number-input-root.d.ts +109 -7
  163. package/dist/number-input/root/number-input-root.js +109 -21
  164. package/dist/object-inspector/root/object-inspector-root.d.ts +49 -5
  165. package/dist/object-inspector/root/object-inspector-root.js +92 -12
  166. package/dist/option/root/option-root.d.ts +58 -6
  167. package/dist/option/root/option-root.js +35 -5
  168. package/dist/outline/root/outline-root.d.ts +13 -2
  169. package/dist/outline/root/outline-root.js +4 -2
  170. package/dist/pagination/root/pagination-root.d.ts +19 -1
  171. package/dist/pagination/root/pagination-root.js +49 -9
  172. package/dist/pagination/size/pagination-size.d.ts +13 -3
  173. package/dist/pagination/size/pagination-size.js +7 -0
  174. package/dist/pin-input/format.d.ts +7 -0
  175. package/dist/pin-input/format.js +16 -0
  176. package/dist/pin-input/root/pin-input-root.d.ts +37 -0
  177. package/dist/pin-input/root/pin-input-root.js +48 -7
  178. package/dist/popover/root/popover-root.d.ts +78 -3
  179. package/dist/popover/root/popover-root.js +74 -5
  180. package/dist/progress/root/progress-root.d.ts +23 -1
  181. package/dist/progress/root/progress-root.js +40 -10
  182. package/dist/qr-code/export.d.ts +31 -0
  183. package/dist/qr-code/export.js +10 -0
  184. package/dist/qr-code/root/qr-code-root.d.ts +45 -0
  185. package/dist/qr-code/root/qr-code-root.js +78 -3
  186. package/dist/radio/group/radio-group.d.ts +64 -0
  187. package/dist/radio/group/radio-group.js +66 -12
  188. package/dist/radio/root/radio-root.d.ts +8 -5
  189. package/dist/radio/root/radio-root.js +1 -2
  190. package/dist/rating/root/rating-root.d.ts +49 -1
  191. package/dist/rating/root/rating-root.js +69 -10
  192. package/dist/scatter-chart/root/scatter-chart-root.d.ts +17 -3
  193. package/dist/scatter-chart/root/scatter-chart-root.js +40 -14
  194. package/dist/select/root/select-root.d.ts +93 -3
  195. package/dist/select/root/select-root.js +111 -13
  196. package/dist/skeleton/root/skeleton-root.d.ts +39 -3
  197. package/dist/skeleton/root/skeleton-root.js +33 -4
  198. package/dist/skip-link/root/skip-link-root.d.ts +10 -0
  199. package/dist/skip-link/root/skip-link-root.js +15 -1
  200. package/dist/slider/root/slider-root.d.ts +59 -2
  201. package/dist/slider/root/slider-root.js +82 -8
  202. package/dist/sparkline/root/sparkline-root.d.ts +9 -10
  203. package/dist/speed-dial/item/speed-dial-item.d.ts +11 -5
  204. package/dist/speed-dial/item/speed-dial-item.js +7 -2
  205. package/dist/speed-dial/root/speed-dial-root.d.ts +15 -4
  206. package/dist/speed-dial/root/speed-dial-root.js +19 -5
  207. package/dist/splitter/root/splitter-root.d.ts +28 -6
  208. package/dist/splitter/root/splitter-root.js +35 -1
  209. package/dist/stat/root/stat-root.d.ts +9 -1
  210. package/dist/stat/root/stat-root.js +15 -2
  211. package/dist/steps/root/steps-root.js +1 -1
  212. package/dist/steps/step/step.d.ts +1 -0
  213. package/dist/steps/step/step.js +1 -1
  214. package/dist/switch/root/switch-root.d.ts +14 -2
  215. package/dist/switch/root/switch-root.js +7 -1
  216. package/dist/table/cell/table-cell.d.ts +24 -4
  217. package/dist/table/cell/table-cell.js +21 -1
  218. package/dist/table/column/table-column.d.ts +32 -6
  219. package/dist/table/column/table-column.js +36 -6
  220. package/dist/table/root/table-root.d.ts +94 -3
  221. package/dist/table/root/table-root.js +128 -5
  222. package/dist/table/row/table-row.d.ts +7 -0
  223. package/dist/table/row/table-row.js +9 -1
  224. package/dist/tabs/root/tabs-root.d.ts +6 -1
  225. package/dist/tabs/root/tabs-root.js +6 -2
  226. package/dist/tag/group/tag-group.d.ts +107 -0
  227. package/dist/tag/group/tag-group.js +157 -0
  228. package/dist/tag/index.d.ts +1 -0
  229. package/dist/tag/index.js +1 -0
  230. package/dist/tag/root/tag-root.d.ts +83 -1
  231. package/dist/tag/root/tag-root.js +87 -17
  232. package/dist/tag/selection.d.ts +10 -0
  233. package/dist/tag/selection.js +12 -0
  234. package/dist/textarea/grow.d.ts +22 -0
  235. package/dist/textarea/grow.js +20 -0
  236. package/dist/textarea/root/textarea-root.d.ts +13 -0
  237. package/dist/textarea/root/textarea-root.js +10 -2
  238. package/dist/time-input/root/time-input-root.d.ts +71 -6
  239. package/dist/time-input/root/time-input-root.js +110 -44
  240. package/dist/time-picker/root/time-picker-root.d.ts +155 -26
  241. package/dist/time-picker/root/time-picker-root.js +192 -63
  242. package/dist/toast/hotkey.d.ts +22 -0
  243. package/dist/toast/hotkey.js +23 -0
  244. package/dist/toast/item/toast-item.d.ts +16 -1
  245. package/dist/toast/item/toast-item.js +41 -13
  246. package/dist/toast/root/toast-root.d.ts +105 -8
  247. package/dist/toast/root/toast-root.js +209 -4
  248. package/dist/toggle/group/toggle-group.d.ts +95 -16
  249. package/dist/toggle/group/toggle-group.js +99 -13
  250. package/dist/toggle/root/toggle-root.d.ts +26 -11
  251. package/dist/toggle/root/toggle-root.js +14 -6
  252. package/dist/toolbar/root/toolbar-root.d.ts +55 -15
  253. package/dist/toolbar/root/toolbar-root.js +99 -16
  254. package/dist/tooltip/focus-target.d.ts +6 -0
  255. package/dist/tooltip/focus-target.js +9 -0
  256. package/dist/tooltip/root/tooltip-root.d.ts +73 -15
  257. package/dist/tooltip/root/tooltip-root.js +96 -7
  258. package/dist/tour/naming.d.ts +30 -0
  259. package/dist/tour/naming.js +10 -0
  260. package/dist/tour/root/tour-root.d.ts +67 -3
  261. package/dist/tour/root/tour-root.js +105 -17
  262. package/dist/tour/stop/tour-stop.d.ts +65 -3
  263. package/dist/tour/stop/tour-stop.js +61 -11
  264. package/dist/tour/wait.d.ts +11 -0
  265. package/dist/tour/wait.js +19 -0
  266. package/dist/tree/item/tree-item.d.ts +21 -5
  267. package/dist/tree/item/tree-item.js +14 -4
  268. package/dist/tree/root/tree-root.d.ts +29 -5
  269. package/dist/tree/root/tree-root.js +64 -8
  270. package/dist/trend/root/trend-root.d.ts +36 -1
  271. package/dist/trend/root/trend-root.js +41 -15
  272. package/dist/video/root/keys.d.ts +5 -0
  273. package/dist/video/root/keys.js +13 -1
  274. package/dist/video/root/video-root.d.ts +19 -0
  275. package/dist/video/root/video-root.js +48 -4
  276. package/dist/video-playlist/menu/video-playlist-menu.d.ts +20 -2
  277. package/dist/video-playlist/menu/video-playlist-menu.js +27 -5
  278. package/dist/video-playlist/root/video-playlist-root.js +6 -3
  279. package/dist/virtual-list/root/virtual-list-root.d.ts +52 -4
  280. package/dist/virtual-list/root/virtual-list-root.js +103 -21
  281. package/lint.json +1 -1
  282. package/package.json +5 -5
  283. package/types/jsx.d.ts +9140 -1149
  284. package/types/svelte.d.ts +1 -0
  285. package/types/vue.d.ts +2089 -267
@@ -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;
@@ -21,10 +21,31 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
21
21
  this.autocomplete = '';
22
22
  this.decrementLabel = 'Decrease';
23
23
  this.incrementLabel = 'Increase';
24
+ this.locale = '';
24
25
  this.placeholder = '';
25
26
  this.readOnly = false;
27
+ this.roleDescription = 'number field';
26
28
  this.scrub = false;
27
29
  this.step = 1;
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);
28
49
  this.scrubFrom = 0;
29
50
  this.scrubValue = 0;
30
51
  this.scrubStart = '';
@@ -75,16 +96,14 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
75
96
  restoreValue(control, this.value);
76
97
  };
77
98
  this.handleBlur = () => {
78
- const control = this.control;
79
- const parsed = this.numeric;
80
- if (!control || parsed === null) {
81
- return;
99
+ this.editing = false;
100
+ if (this.settle()) {
101
+ this.announceChange();
82
102
  }
83
- this.requestValue(String(this.clamp(parsed)));
84
- restoreValue(control, this.value);
85
103
  };
86
104
  this.handleChange = (event) => {
87
105
  event.stopPropagation();
106
+ this.settle();
88
107
  this.announceChange();
89
108
  };
90
109
  this.handleKeydown = (event) => {
@@ -117,20 +136,34 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
117
136
  }
118
137
  event.preventDefault();
119
138
  };
139
+ this.handleKeypress = (event) => {
140
+ if (event.key === 'Enter' && !event.defaultPrevented) {
141
+ this.submitImplicitly();
142
+ }
143
+ };
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);
120
151
  }
121
- static { this.shadowRootOptions = {
122
- ...HelixFormElement.shadowRootOptions,
123
- delegatesFocus: true,
124
- }; }
125
152
  get control() {
126
153
  return this.partNamed('control');
127
154
  }
128
155
  get numeric() {
129
- if (this.value.trim() === '') {
156
+ const text = this.value.trim();
157
+ if (text === '') {
130
158
  return null;
131
159
  }
132
- const parsed = Number(this.value);
133
- return Number.isFinite(parsed) ? parsed : null;
160
+ return parseNumber(text, this.locale, this.formatOptions);
161
+ }
162
+ onStep(value) {
163
+ const step = Math.abs(this.step) || 1;
164
+ const anchor = this.min ?? 0;
165
+ const steps = (value - anchor) / step;
166
+ return Math.abs(steps - Math.round(steps)) < 1e-9;
134
167
  }
135
168
  clamp(value) {
136
169
  const step = Math.abs(this.step) || 1;
@@ -168,13 +201,10 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
168
201
  this.nudge(large ? -(this.largeStep ?? 10) : -1);
169
202
  }
170
203
  renderLabel() {
171
- if (!this.scrub) {
204
+ if (!this.scrub || !this.labeled) {
172
205
  return super.renderLabel();
173
206
  }
174
207
  const hidden = this.label === '' ? this.getAttribute('aria-label') : null;
175
- if (this.label === '' && !hidden) {
176
- return nothing;
177
- }
178
208
  return html `<label
179
209
  part="label"
180
210
  for=${this.controlId}
@@ -186,6 +216,17 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
186
216
  ><slot name="label">${this.label || hidden}</slot></label
187
217
  >`;
188
218
  }
219
+ settle() {
220
+ const control = this.control;
221
+ const parsed = this.numeric;
222
+ if (!control || parsed === null) {
223
+ return false;
224
+ }
225
+ const before = this.value;
226
+ this.requestValue(String(this.clamp(parsed)));
227
+ restoreValue(control, this.value);
228
+ return this.value !== before;
229
+ }
189
230
  sync() {
190
231
  const control = this.control;
191
232
  this.internals.setFormValue(this.value);
@@ -212,6 +253,14 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
212
253
  max: this.max,
213
254
  }), control);
214
255
  }
256
+ else if (parsed !== null && !this.onStep(parsed)) {
257
+ const step = Math.abs(this.step) || 1;
258
+ const anchor = this.min ?? 0;
259
+ const below = this.clamp(anchor + Math.floor((parsed - anchor) / step) * step);
260
+ const above = this.clamp(anchor + Math.ceil((parsed - anchor) / step) * step);
261
+ this.internals.setValidity({ stepMismatch: true }, fillTemplate(this.stepMismatchMessage ||
262
+ 'Please enter a valid value. The two nearest valid values are {below} and {above}.', { above, below }), control);
263
+ }
215
264
  else {
216
265
  this.internals.setValidity({}, '', control);
217
266
  }
@@ -239,6 +288,9 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
239
288
  this.releaseCaret = undefined;
240
289
  this.sync();
241
290
  }
291
+ get blocksImplicitSubmission() {
292
+ return true;
293
+ }
242
294
  render() {
243
295
  const parsed = this.numeric;
244
296
  const atMin = parsed !== null && this.min !== undefined && parsed <= this.min;
@@ -262,9 +314,10 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
262
314
  <input
263
315
  part="control"
264
316
  id=${this.controlId}
265
- .value=${this.value}
317
+ .value=${this.shown}
266
318
  type="text"
267
- role="spinbutton"
319
+ role=${this.touchApple ? nothing : 'spinbutton'}
320
+ aria-roledescription=${this.touchApple ? this.roleDescription : nothing}
268
321
  inputmode="decimal"
269
322
  autocomplete=${this.autocomplete || nothing}
270
323
  name=${this.name || nothing}
@@ -274,13 +327,27 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
274
327
  aria-valuemin=${this.min ?? nothing}
275
328
  aria-valuemax=${this.max ?? nothing}
276
329
  aria-valuenow=${parsed ?? 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}
277
341
  ?disabled=${this.isDisabled}
278
342
  ?readonly=${this.readOnly}
279
343
  ?required=${this.required}
280
344
  @blur=${this.handleBlur}
281
345
  @change=${this.handleChange}
346
+ @focus=${this.handleFocus}
282
347
  @input=${this.handleInput}
283
348
  @keydown=${this.handleKeydown}
349
+ @keypress=${this.handleKeypress}
350
+ @wheel=${this.handleWheel}
284
351
  />
285
352
  <slot name="end"></slot>
286
353
  <button
@@ -314,12 +381,18 @@ __decorate([
314
381
  __decorate([
315
382
  property({ attribute: 'decrement-label' })
316
383
  ], HelixHeadlessNumberInput.prototype, "decrementLabel", void 0);
384
+ __decorate([
385
+ property({ attribute: 'format-options', type: Object })
386
+ ], HelixHeadlessNumberInput.prototype, "formatOptions", void 0);
317
387
  __decorate([
318
388
  property({ attribute: 'increment-label' })
319
389
  ], HelixHeadlessNumberInput.prototype, "incrementLabel", void 0);
320
390
  __decorate([
321
391
  property({ attribute: 'large-step', type: Number })
322
392
  ], HelixHeadlessNumberInput.prototype, "largeStep", void 0);
393
+ __decorate([
394
+ property()
395
+ ], HelixHeadlessNumberInput.prototype, "locale", void 0);
323
396
  __decorate([
324
397
  property({ type: Number })
325
398
  ], HelixHeadlessNumberInput.prototype, "max", void 0);
@@ -338,12 +411,27 @@ __decorate([
338
411
  __decorate([
339
412
  property({ attribute: 'readonly', reflect: true, type: Boolean })
340
413
  ], HelixHeadlessNumberInput.prototype, "readOnly", void 0);
414
+ __decorate([
415
+ property({ attribute: 'role-description' })
416
+ ], HelixHeadlessNumberInput.prototype, "roleDescription", void 0);
341
417
  __decorate([
342
418
  property({ reflect: true, type: Boolean })
343
419
  ], HelixHeadlessNumberInput.prototype, "scrub", void 0);
344
420
  __decorate([
345
421
  property({ type: Number })
346
422
  ], HelixHeadlessNumberInput.prototype, "step", void 0);
423
+ __decorate([
424
+ property({ attribute: 'step-mismatch-message' })
425
+ ], HelixHeadlessNumberInput.prototype, "stepMismatchMessage", void 0);
426
+ __decorate([
427
+ property({ attribute: 'value-text' })
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);
347
435
  HelixHeadlessNumberInput = __decorate([
348
436
  customElement('helix-headless-number-input')
349
437
  ], HelixHeadlessNumberInput);
@@ -1,4 +1,4 @@
1
- import { HelixElement } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, HelixElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import '../../copy-button/index.js';
4
4
  /** One visible line of the tree: a value, where it sits, and whether it is open. */
@@ -28,9 +28,15 @@ interface Row {
28
28
  *
29
29
  * The tree is one tab stop. Up and Down move between rows, Right opens a branch
30
30
  * or moves into it, Left closes it or moves to its parent, and Home and End
31
- * move to the first and last rows. Focus stays on the tree, and
32
- * `aria-activedescendant` marks the active row. Each row carries its real
33
- * level, position, and set size, including in a windowed tree.
31
+ * move to the first and last rows. Typing moves to the next row whose key
32
+ * starts with what was typed, and `*` opens every branch beside the active
33
+ * row. Focus stays on the tree, and `aria-activedescendant` marks the active
34
+ * row. Each row carries its real level, position, and set size, including in
35
+ * a windowed tree.
36
+ *
37
+ * A new `value` keeps the branches a reader opened or closed, and the active
38
+ * row, where the new value still has them. A branch the old value did not have
39
+ * opens to `default-depth`.
34
40
  *
35
41
  * The copy button copies the active row, a branch as JSON and a string as its
36
42
  * text. So do Command+C and Control+C while the tree has focus and no text is
@@ -54,6 +60,7 @@ interface Row {
54
60
  * @csspart boolean - A `true` or `false`.
55
61
  * @csspart copy - The copy button, which copies the active row.
56
62
  * @csspart copy-button - The button inside the copy control.
63
+ * @csspart copy-live - The live region inside the copy button, which says its `pending-label` when the copy becomes pending under focus.
57
64
  * @csspart copy-mark - The clipboard icon, and the confirmation icon that replaces it.
58
65
  * @csspart copy-text - The words on that button, trimmed to their capitals and baseline.
59
66
  * @csspart copy-tooltip - The tooltip, forwarded from the copy button's tooltip.
@@ -110,6 +117,14 @@ export declare class HelixHeadlessObjectInspector extends HelixElement {
110
117
  * page's markup should not take the page down over a comma.
111
118
  */
112
119
  for: string;
120
+ /** How an array of one entry counts it. `{count}` is replaced with the count. */
121
+ itemLabel: string;
122
+ /** How an array of any other number of entries counts them. `{count}` is replaced with the count. */
123
+ itemsLabel: string;
124
+ /** How an object of one key counts it. `{count}` is replaced with the count. */
125
+ keyLabel: string;
126
+ /** How an object of any other number of keys counts them. `{count}` is replaced with the count. */
127
+ keysLabel: string;
113
128
  /**
114
129
  * Accessible name for the tree, and the text of the header where there is one.
115
130
  */
@@ -128,6 +143,11 @@ export declare class HelixHeadlessObjectInspector extends HelixElement {
128
143
  * reach a value.
129
144
  */
130
145
  noCopy: boolean;
146
+ /**
147
+ * The tree's accessible name when `label` is empty and the host names it
148
+ * with neither `aria-label` nor `aria-labelledby`.
149
+ */
150
+ treeLabel: string;
131
151
  /**
132
152
  * The value to show, when your code already holds it as an object.
133
153
  *
@@ -152,6 +172,12 @@ export declare class HelixHeadlessObjectInspector extends HelixElement {
152
172
  private active;
153
173
  /** Set by a keyboard move, so the next render brings the active row into view. */
154
174
  private revealing;
175
+ /** What has been typed toward a key, and when the last letter came. */
176
+ private typed;
177
+ /** The branches of the last value seen, by path, so a new value can tell old ones from new. */
178
+ private branches;
179
+ /** Carries a host `aria-label` and `aria-labelledby` to the tree, which is what takes focus. */
180
+ protected readonly aria: AriaForwardController;
155
181
  /**
156
182
  * How tall a row is taken to be before it has been drawn, in pixels.
157
183
  *
@@ -169,7 +195,15 @@ export declare class HelixHeadlessObjectInspector extends HelixElement {
169
195
  private readonly copier;
170
196
  /** Every visible row, in order, for the tree as it stands. */
171
197
  private flatten;
172
- /** Opens every branch shallower than `defaultDepth`. */
198
+ /**
199
+ * Opens every branch shallower than `defaultDepth`.
200
+ *
201
+ * `keep` is for a new value: a branch the last value also had keeps whether
202
+ * a reader opened or closed it, and only a new one takes the default.
203
+ * **Assigning a value used to close everything**, so an inspector showing
204
+ * live data, a polled response or a debug panel, threw away the reader's
205
+ * place on every update.
206
+ */
173
207
  private seed;
174
208
  protected willUpdate(changed: PropertyValues): void;
175
209
  protected firstUpdated(changed: PropertyValues): void;
@@ -193,6 +227,16 @@ export declare class HelixHeadlessObjectInspector extends HelixElement {
193
227
  private toggle;
194
228
  private move;
195
229
  private readonly handleKeydown;
230
+ /** Opens every branch at the active row's level under the same parent, as `*` asks. */
231
+ private openSiblings;
232
+ /**
233
+ * Moves to the next row whose key starts with what was typed.
234
+ *
235
+ * Letters typed within half a second of each other run together, so `me`
236
+ * finds `meta` past `message`. A repeated single letter cycles through the
237
+ * keys that start with it.
238
+ */
239
+ private typeahead;
196
240
  private handleRowClick;
197
241
  private renderValue;
198
242
  /**
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  var HelixHeadlessObjectInspector_1;
8
- import { CopyController, customElement, elementById, fillTemplate, HelixElement, inlineStep, scrollItemIntoView, searchableById, VirtualizeController, } from '@fusion.dev/helix-core';
8
+ import { AriaForwardController, CopyController, customElement, elementById, fillTemplate, formatNumbers, HelixElement, inlineStep, scrollItemIntoView, searchableById, VirtualizeController, } from '@fusion.dev/helix-core';
9
9
  import { caretDown, caretRight } from '@fusion.dev/helix-icons';
10
10
  import { html, nothing } from 'lit';
11
11
  import { property, state } from 'lit/decorators.js';
@@ -22,14 +22,25 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
22
22
  this.copyValueLabel = 'Copy value';
23
23
  this.defaultDepth = 1;
24
24
  this.for = '';
25
+ this.itemLabel = '{count} item';
26
+ this.itemsLabel = '{count} items';
27
+ this.keyLabel = '{count} key';
28
+ this.keysLabel = '{count} keys';
25
29
  this.label = '';
26
30
  this.noCopy = false;
31
+ this.treeLabel = 'Value';
27
32
  this.value = undefined;
28
33
  this.windowed = false;
29
34
  this.read = undefined;
30
35
  this.opened = new Set();
31
36
  this.active = 0;
32
37
  this.revealing = false;
38
+ this.typed = { at: 0, text: '' };
39
+ this.branches = new Set();
40
+ this.aria = new AriaForwardController(this, {
41
+ label: true,
42
+ target: () => this.partNamed('tree'),
43
+ });
33
44
  this.virtualize = new VirtualizeController(this, {
34
45
  container: () => (this.windowed ? this.partNamed('tree') : null),
35
46
  count: () => this.rows.length,
@@ -92,8 +103,14 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
92
103
  this.toggle(row);
93
104
  }
94
105
  break;
106
+ case '*':
107
+ this.openSiblings(row);
108
+ break;
95
109
  default:
96
- return;
110
+ if (event.key.length !== 1 || event.altKey || event.ctrlKey || event.metaKey) {
111
+ return;
112
+ }
113
+ this.typeahead(event.key);
97
114
  }
98
115
  event.preventDefault();
99
116
  };
@@ -131,20 +148,28 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
131
148
  visit(this.root, '', 0, 1, 1);
132
149
  return rows;
133
150
  }
134
- seed() {
151
+ seed(keep) {
135
152
  const opened = new Set();
153
+ const branches = new Set();
136
154
  const visit = (value, path, depth) => {
137
- if (!branchy(value) || depth >= this.defaultDepth) {
155
+ if (!branchy(value)) {
138
156
  return;
139
157
  }
140
- opened.add(path);
158
+ branches.add(path);
159
+ const known = keep && this.branches.has(path);
160
+ if (known ? this.opened.has(path) : depth < this.defaultDepth) {
161
+ opened.add(path);
162
+ }
141
163
  for (const [name, item] of entries(value)) {
142
164
  visit(item, child(path, name), depth + 1);
143
165
  }
144
166
  };
145
167
  visit(this.root, '', 0);
168
+ this.branches = branches;
146
169
  this.opened = opened;
147
- this.active = 0;
170
+ if (!keep) {
171
+ this.active = 0;
172
+ }
148
173
  }
149
174
  willUpdate(changed) {
150
175
  super.willUpdate(changed);
@@ -152,15 +177,17 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
152
177
  this.loadedFor = this.for;
153
178
  this.load();
154
179
  }
180
+ const was = this.rows[this.active]?.path;
155
181
  if (changed.has('value') || changed.has('read') || changed.has('defaultDepth')) {
156
- this.seed();
182
+ this.seed(!changed.has('defaultDepth'));
157
183
  }
158
184
  if (changed.has('value') ||
159
185
  changed.has('read') ||
160
186
  changed.has('opened') ||
161
187
  changed.has('defaultDepth')) {
162
188
  this.rows = this.flatten();
163
- this.active = Math.min(this.active, Math.max(0, this.rows.length - 1));
189
+ const found = was === undefined ? -1 : this.rows.findIndex((row) => row.path === was);
190
+ this.active = found >= 0 ? found : Math.min(this.active, Math.max(0, this.rows.length - 1));
164
191
  }
165
192
  }
166
193
  firstUpdated(changed) {
@@ -229,6 +256,35 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
229
256
  this.active = Math.max(0, Math.min(to, last));
230
257
  this.revealing = true;
231
258
  }
259
+ openSiblings(row) {
260
+ const parent = row.path.slice(0, row.path.lastIndexOf('/'));
261
+ const opened = new Set(this.opened);
262
+ for (const one of this.rows) {
263
+ if (one.branch &&
264
+ one.depth === row.depth &&
265
+ one.path.slice(0, one.path.lastIndexOf('/')) === parent) {
266
+ opened.add(one.path);
267
+ }
268
+ }
269
+ this.opened = opened;
270
+ }
271
+ typeahead(letter) {
272
+ const now = Date.now();
273
+ const text = now - this.typed.at < 500 ? this.typed.text + letter : letter;
274
+ this.typed = { at: now, text };
275
+ const wanted = text.toLocaleLowerCase();
276
+ const repeated = [...wanted].every((one) => one === wanted[0]);
277
+ const rows = this.rows;
278
+ const start = wanted.length === 1 || repeated ? this.active + 1 : this.active;
279
+ for (let step = 0; step < rows.length; step += 1) {
280
+ const at = (start + step) % rows.length;
281
+ const key = rows[at]?.key?.toLocaleLowerCase();
282
+ if (key?.startsWith(repeated ? (wanted[0] ?? '') : wanted)) {
283
+ this.move(at);
284
+ return;
285
+ }
286
+ }
287
+ }
232
288
  handleRowClick(index, row) {
233
289
  this.active = index;
234
290
  if (row.branch) {
@@ -240,9 +296,18 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
240
296
  if (row.branch) {
241
297
  const count = entries(value).length;
242
298
  const array = Array.isArray(value);
243
- const noun = array ? (count === 1 ? 'item' : 'items') : count === 1 ? 'key' : 'keys';
299
+ const one = new Intl.PluralRules(this.lang || undefined).select(count) === 'one';
300
+ const words = array
301
+ ? one
302
+ ? this.itemLabel
303
+ : this.itemsLabel
304
+ : one
305
+ ? this.keyLabel
306
+ : this.keysLabel;
244
307
  return html `<span part="summary">${array ? '[…]' : '{…}'}</span>
245
- <span part="count" dir="auto">${`${count} ${noun}`}</span>`;
308
+ <span part="count" dir="auto"
309
+ >${fillTemplate(words, { count: formatNumbers(this.lang)(count) })}</span
310
+ >`;
246
311
  }
247
312
  if (typeof value === 'string') {
248
313
  return html `<span part="string" dir="ltr">${JSON.stringify(value)}</span>`;
@@ -288,7 +353,7 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
288
353
  renderCopyButton(row) {
289
354
  return staticHtml `<${this.tag('copy-button')}
290
355
  part="copy"
291
- exportparts="button: copy-button, text: copy-text, mark: copy-mark, tooltip: copy-tooltip,
356
+ exportparts="button: copy-button, live: copy-live, text: copy-text, mark: copy-mark, tooltip: copy-tooltip,
292
357
  tooltip-arrow: copy-tooltip-arrow, tooltip-arrow-bottom: copy-tooltip-arrow-bottom,
293
358
  tooltip-arrow-left: copy-tooltip-arrow-left, tooltip-arrow-right: copy-tooltip-arrow-right,
294
359
  tooltip-arrow-top: copy-tooltip-arrow-top, tooltip-bottom: copy-tooltip-bottom,
@@ -310,7 +375,7 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
310
375
  role="tree"
311
376
  tabindex="0"
312
377
  ?data-scroller=${this.windowed}
313
- aria-label=${this.label || 'Value'}
378
+ aria-label=${this.label || this.treeLabel}
314
379
  aria-activedescendant=${rows.length > 0 ? `row-${this.active}` : nothing}
315
380
  @keydown=${this.handleKeydown}
316
381
  @scroll=${() => this.virtualize.measure()}
@@ -335,12 +400,27 @@ __decorate([
335
400
  __decorate([
336
401
  property()
337
402
  ], HelixHeadlessObjectInspector.prototype, "for", void 0);
403
+ __decorate([
404
+ property({ attribute: 'item-label' })
405
+ ], HelixHeadlessObjectInspector.prototype, "itemLabel", void 0);
406
+ __decorate([
407
+ property({ attribute: 'items-label' })
408
+ ], HelixHeadlessObjectInspector.prototype, "itemsLabel", void 0);
409
+ __decorate([
410
+ property({ attribute: 'key-label' })
411
+ ], HelixHeadlessObjectInspector.prototype, "keyLabel", void 0);
412
+ __decorate([
413
+ property({ attribute: 'keys-label' })
414
+ ], HelixHeadlessObjectInspector.prototype, "keysLabel", void 0);
338
415
  __decorate([
339
416
  property()
340
417
  ], HelixHeadlessObjectInspector.prototype, "label", void 0);
341
418
  __decorate([
342
419
  property({ attribute: 'no-copy', type: Boolean })
343
420
  ], HelixHeadlessObjectInspector.prototype, "noCopy", void 0);
421
+ __decorate([
422
+ property({ attribute: 'tree-label' })
423
+ ], HelixHeadlessObjectInspector.prototype, "treeLabel", void 0);
344
424
  __decorate([
345
425
  property({ attribute: false })
346
426
  ], HelixHeadlessObjectInspector.prototype, "value", void 0);
@@ -1,4 +1,4 @@
1
- import { HelixElement } from '@fusion.dev/helix-core';
1
+ import { HelixElement, type LinkModifiers } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * The options among some elements, reaching one level into a group.
@@ -34,6 +34,9 @@ export declare const optionsAmong: (elements: readonly Element[]) => HelixHeadle
34
34
  * Filtering belongs to the owner, which hides an option with `hidden`. What
35
35
  * counts as a match differs between a select and a command palette.
36
36
  *
37
+ * An option with `href` is a link: choosing it follows the link and selects
38
+ * nothing, as an entry in a command palette that goes to a page does.
39
+ *
37
40
  * @since 0.1.0
38
41
  * @status stable
39
42
  * @category forms
@@ -44,7 +47,7 @@ export declare const optionsAmong: (elements: readonly Element[]) => HelixHeadle
44
47
  * @slot end - Content after the label, such as a shortcut or a count.
45
48
  * @slot start - Content before the label, such as an icon or an avatar.
46
49
  *
47
- * @event {HelixSelectEvent} helix-select - The option was chosen. Cancelable: canceling it leaves the selection as it was.
50
+ * @event {HelixSelectEvent} helix-select - The option was chosen. Cancelable: canceling it leaves the selection as it was, and keeps the browser from following an option's `href`.
48
51
  *
49
52
  * @csspart description - The description under the label, when there is one.
50
53
  * @csspart item - The row.
@@ -53,13 +56,16 @@ export declare const optionsAmong: (elements: readonly Element[]) => HelixHeadle
53
56
  * @csspart label - The option's label.
54
57
  * @csspart text - The label and the description under it, as one column.
55
58
  *
59
+ * @cssstate active - While the keyboard is on the option, which is the option `aria-activedescendant` names.
56
60
  * @cssstate selected - While the option is chosen.
57
61
  */
58
62
  export declare class HelixHeadlessOption extends HelixElement {
59
63
  /**
60
64
  * Whether this is the option the keyboard is on.
61
65
  *
62
- * Written by the listbox.
66
+ * Written by the listbox, which writes it once a reader moves the highlight.
67
+ * It shows as `:state(active)`, and as a `data-active` attribute for the
68
+ * stylesheets written before the state existed.
63
69
  */
64
70
  active: boolean;
65
71
  /**
@@ -145,6 +151,28 @@ export declare class HelixHeadlessOption extends HelixElement {
145
151
  * Prevents the option from being chosen.
146
152
  */
147
153
  disabled: boolean;
154
+ /**
155
+ * Whether following the link downloads what it names. Only meaningful
156
+ * beside an `href`.
157
+ */
158
+ download: boolean;
159
+ /**
160
+ * The URL the option links to. Setting it makes the option a link: choosing
161
+ * it, with a press, Enter or a click, follows the link and selects nothing,
162
+ * as React Aria's items with `href` do. Command, Control and Shift open it
163
+ * where they open a link, in a new tab or a new window.
164
+ *
165
+ * `helix-select` is said first with the `href` in it, and canceling it keeps
166
+ * the browser from following, so a client-side router follows it instead.
167
+ *
168
+ * **The option stays an option**, and an anchor is never drawn inside it:
169
+ * `role="option"` sits on the host, where a listbox's
170
+ * `aria-activedescendant` can name it, and an `<a>` inside would be exposed
171
+ * as a link within the option and take its name (Menu Item's `href` records
172
+ * the measurement). The link is followed through an anchor made for the
173
+ * moment, which `followLink` describes.
174
+ */
175
+ href: string;
148
176
  /**
149
177
  * A shorter name for this choice, for where the row does not fit.
150
178
  *
@@ -158,12 +186,30 @@ export declare class HelixHeadlessOption extends HelixElement {
158
186
  * nothing for every choice.
159
187
  */
160
188
  label: string;
189
+ /**
190
+ * The link's `rel`. Set it whenever you set `target="_blank"`.
191
+ */
192
+ rel: string;
161
193
  /**
162
194
  * Whether this choice is currently chosen.
163
195
  *
164
196
  * Written by the listbox.
165
197
  */
166
198
  selected: boolean;
199
+ /**
200
+ * Where the link opens, such as `_blank`, for an option with an `href`.
201
+ */
202
+ target: string;
203
+ /**
204
+ * The words a reader types to reach this choice, where the row's own words
205
+ * are not them: typeahead, a filter and a combobox match against it, and a
206
+ * select's trigger shows it.
207
+ *
208
+ * For a row whose content is a picture, an avatar beside a name and a role,
209
+ * or a name written in another script, as React Aria's `textValue`. Empty
210
+ * reads the row's words, then `label`.
211
+ */
212
+ textValue: string;
167
213
  /**
168
214
  * The value that identifies the option, and that a form submits.
169
215
  */
@@ -207,11 +253,17 @@ export declare class HelixHeadlessOption extends HelixElement {
207
253
  * it is in every comparison too.
208
254
  *
209
255
  * **The `label` where the slot holds nothing**, which is what the row shows
210
- * then, so the six read the words a reader sees.
256
+ * then, so the six read the words a reader sees. `text-value` wins over both,
257
+ * being the consumer saying outright what the words are.
211
258
  */
212
259
  get text(): string;
213
- /** Sends `helix-select` for this option. Does nothing when it is disabled. */
214
- select(): void;
260
+ /**
261
+ * Sends `helix-select` for this option. Does nothing when it is disabled.
262
+ *
263
+ * An option with an `href` then follows its link, unless the event was
264
+ * canceled, with the keys `modifiers` names held.
265
+ */
266
+ select(modifiers?: LinkModifiers): void;
215
267
  /**
216
268
  * Mirrors the chosen state where a stylesheet can select on it.
217
269
  *