@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,11 +4,11 @@ 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, byRow, CHART_PLOT, customElement, drawTooltip, formatNumbers, HelixElement, HelixResizeEvent, MixinChartSource, markRegion, searchableById, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, byRow, CHART_PLOT, commaList, customElement, drawTooltip, fillTemplate, formatNumbers, HelixElement, HelixResizeEvent, MixinChartSource, markRegion, pressedValue, rangeAdd, requestValue, StatusController, searchableById, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import '../../tooltip/index.js';
11
- import { fits, toneOf } from './buckets.js';
11
+ import { fits, monthPage, overviewOf, toneOf } from './buckets.js';
12
12
  let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(HelixElement) {
13
13
  constructor() {
14
14
  super(...arguments);
@@ -17,9 +17,12 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
17
17
  this.gap = 2;
18
18
  this.minCell = 8;
19
19
  this.rows = 1;
20
+ this.selectedLabel = 'selected';
21
+ this.selectionLabel = '{count} selected';
20
22
  this.thresholds = '';
21
23
  this.tones = '';
22
24
  this.totalLabel = 'Total';
25
+ this.value = '';
23
26
  this.width = undefined;
24
27
  this.metrics = undefined;
25
28
  this.aria = new AriaForwardController(this, {
@@ -27,6 +30,35 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
27
30
  });
28
31
  this.observer = undefined;
29
32
  this.announced = undefined;
33
+ this.anchor = -1;
34
+ this.status = new StatusController(this);
35
+ this.onKeydown = (event) => {
36
+ const at = this.pointer.at;
37
+ if (!this.selectionMode || at < 0 || event.altKey || event.ctrlKey || event.metaKey) {
38
+ return;
39
+ }
40
+ if (event.key === ' ' || event.key === 'Enter') {
41
+ event.preventDefault();
42
+ this.press(at);
43
+ return;
44
+ }
45
+ const moves = /^(Arrow|Page|Home$|End$)/.test(event.key);
46
+ if (!event.shiftKey || !moves || this.selectionMode !== 'multiple') {
47
+ return;
48
+ }
49
+ const keys = this.keysOf(this.drawn);
50
+ const added = rangeAdd(commaList(this.value), keys, this.anchor < 0 ? at : this.anchor, at);
51
+ if (added) {
52
+ this.choose(added.join(', '));
53
+ }
54
+ };
55
+ this.onClick = (event) => {
56
+ const cell = event.target?.closest?.('[part~="cell"]');
57
+ const index = Number(cell?.getAttribute('data-index') ?? -1);
58
+ if (cell && Number.isInteger(index) && index >= 0) {
59
+ this.press(index);
60
+ }
61
+ };
30
62
  this.onPointerOver = (event) => {
31
63
  if (!this.pointer.accepts(event)) {
32
64
  return;
@@ -44,6 +76,29 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
44
76
  get visible() {
45
77
  return Math.min(this.points.length, this.room);
46
78
  }
79
+ keysOf(cells) {
80
+ const offset = this.points.length - cells.length;
81
+ const rows = this.sourceRows;
82
+ return cells.map((_, index) => rows[offset + index]?.label ?? '');
83
+ }
84
+ choose(next) {
85
+ if (next === this.value) {
86
+ return;
87
+ }
88
+ requestValue(this, next, (value) => {
89
+ this.value = value;
90
+ this.status.say(fillTemplate(this.selectionLabel, { count: commaList(value).length }));
91
+ });
92
+ }
93
+ press(index) {
94
+ const mode = this.selectionMode;
95
+ const key = this.keysOf(this.drawn)[index];
96
+ if (!mode || key === undefined || key === '') {
97
+ return;
98
+ }
99
+ this.anchor = index;
100
+ this.choose(pressedValue(mode, this.value, key));
101
+ }
47
102
  get points() {
48
103
  const series = this.series[0];
49
104
  if (!series) {
@@ -99,8 +154,13 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
99
154
  super.willUpdate(changed);
100
155
  this.pointer.grid = this.rowCount;
101
156
  }
157
+ pageFrom(at, forward) {
158
+ const offset = this.points.length - this.drawn.length;
159
+ return monthPage(this.sourceRows.slice(offset).map((row) => row.when), at, forward);
160
+ }
102
161
  connectedCallback() {
103
162
  super.connectedCallback();
163
+ this.addEventListener('keydown', this.onKeydown);
104
164
  if (typeof ResizeObserver !== 'undefined') {
105
165
  this.observer = new ResizeObserver((entries) => {
106
166
  const measured = entries[0]?.contentRect.width;
@@ -113,6 +173,7 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
113
173
  }
114
174
  disconnectedCallback() {
115
175
  super.disconnectedCallback();
176
+ this.removeEventListener('keydown', this.onKeydown);
116
177
  this.observer?.disconnect();
117
178
  this.observer = undefined;
118
179
  }
@@ -143,6 +204,8 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
143
204
  const palette = writtenList(this.tones);
144
205
  const cells = this.drawn;
145
206
  const readable = cells.length > 0;
207
+ const keys = this.selectionMode ? this.keysOf(cells) : [];
208
+ const chosen = new Set(this.selectionMode ? commaList(this.value) : []);
146
209
  return html `<div
147
210
  aria-describedby=${readable ? 'helix-chart-summary' : nothing}
148
211
  aria-label=${this.named || 'Heatmap'}
@@ -151,6 +214,7 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
151
214
  role=${readable ? 'application' : 'img'}
152
215
  style="--heatmap-rows: ${this.rowCount}"
153
216
  tabindex=${readable ? '0' : nothing}
217
+ @click=${this.onClick}
154
218
  @pointerover=${this.onPointerOver}
155
219
  @pointerleave=${this.onPointerLeave}
156
220
  >
@@ -159,13 +223,14 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
159
223
  : cells.map((reading, index) => {
160
224
  const tone = toneOf(reading, bounds, palette);
161
225
  const active = this.pointer.at === index;
226
+ const picked = chosen.has(keys[index] ?? '');
162
227
  return tone === undefined
163
228
  ? html `<span
164
- part="cell${active ? ' cell-active' : ''}"
229
+ part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
165
230
  data-index=${index}
166
231
  ></span>`
167
232
  : html `<span
168
- part="cell${active ? ' cell-active' : ''}"
233
+ part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
169
234
  data-index=${index}
170
235
  data-tone=${tone}
171
236
  style="--heatmap-tone: var(--heatmap-tone-${tone})"
@@ -178,27 +243,18 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
178
243
  <span part="start"><slot name="start"></slot></span>
179
244
  <span part="summary"><slot name="summary"></slot></span>
180
245
  <span part="end"><slot name="end"></slot></span>
181
- </div>${readable ? this.renderOverview(cells) : nothing}`;
246
+ </div>${readable ? this.renderOverview(cells) : nothing}${this.status.spoken
247
+ ? html `<span part="selection" role="status">${this.status.said}</span>`
248
+ : nothing}`;
182
249
  }
183
250
  renderOverview(cells) {
184
251
  const rows = this.sourceRows;
185
252
  const offset = this.points.length - cells.length;
186
253
  const column = this.plottedColumns[0] ?? -1;
187
- let busiest = -1;
188
- let total = 0;
189
- cells.forEach((cell, index) => {
190
- if (cell.value === undefined) {
191
- return;
192
- }
193
- total += cell.value;
194
- if (busiest < 0 || cell.value > (cells[busiest]?.value ?? 0)) {
195
- busiest = index;
196
- }
197
- });
254
+ const { busiest, total } = overviewOf(cells);
198
255
  const row = rows[offset + busiest];
199
- const most = row
200
- ? `${this.busiestLabel}: ${row.label} ${row.cells[column]?.text ?? ''}`.trim()
201
- : '';
256
+ const text = row?.cells[column]?.text ?? '';
257
+ const most = row ? `${this.busiestLabel}: ${row.label}${text === '' ? '' : `, ${text}`}` : '';
202
258
  const sum = `${this.totalLabel} ${formatNumbers(this.locale)(total)}`;
203
259
  return html `<p id="helix-chart-summary" part="overview">${most ? `${most}; ${sum}` : sum}.</p>`;
204
260
  }
@@ -216,7 +272,24 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
216
272
  placement: 'top',
217
273
  x: `${column * (cell + gap) + cell / 2}px`,
218
274
  y: `${row * (cell + gap)}px`,
219
- }, undefined, this.pointer);
275
+ }, undefined, this.reading(at));
276
+ }
277
+ reading(at) {
278
+ const pointer = this.pointer;
279
+ const key = this.keysOf(this.drawn)[at] ?? '';
280
+ if (!this.selectionMode || key === '' || this.selectedLabel === '') {
281
+ return pointer;
282
+ }
283
+ if (!commaList(this.value).includes(key)) {
284
+ return pointer;
285
+ }
286
+ return {
287
+ column: pointer.column,
288
+ keyed: pointer.keyed,
289
+ position: pointer.position,
290
+ shows: (cell) => pointer.shows(cell),
291
+ state: this.selectedLabel,
292
+ };
220
293
  }
221
294
  };
222
295
  __decorate([
@@ -234,6 +307,15 @@ __decorate([
234
307
  __decorate([
235
308
  property({ type: Number })
236
309
  ], HelixHeadlessHeatmap.prototype, "rows", void 0);
310
+ __decorate([
311
+ property({ attribute: 'selected-label' })
312
+ ], HelixHeadlessHeatmap.prototype, "selectedLabel", void 0);
313
+ __decorate([
314
+ property({ attribute: 'selection-label' })
315
+ ], HelixHeadlessHeatmap.prototype, "selectionLabel", void 0);
316
+ __decorate([
317
+ property({ attribute: 'selection-mode' })
318
+ ], HelixHeadlessHeatmap.prototype, "selectionMode", void 0);
237
319
  __decorate([
238
320
  property()
239
321
  ], HelixHeadlessHeatmap.prototype, "thresholds", void 0);
@@ -243,6 +325,9 @@ __decorate([
243
325
  __decorate([
244
326
  property({ attribute: 'total-label' })
245
327
  ], HelixHeadlessHeatmap.prototype, "totalLabel", void 0);
328
+ __decorate([
329
+ property()
330
+ ], HelixHeadlessHeatmap.prototype, "value", void 0);
246
331
  __decorate([
247
332
  state()
248
333
  ], HelixHeadlessHeatmap.prototype, "width", void 0);
@@ -1,3 +1,3 @@
1
1
  import './set.js';
2
- export { type IconResolver, registerIconLibrary, registerIcons, resolveIcon } from './registry.js';
2
+ export { type IconLibraryOptions, type IconResolver, registerIconLibrary, registerIcons, resolveIcon, } from './registry.js';
3
3
  export * from './root/icon-root.js';
@@ -1,3 +1,3 @@
1
1
  import './set.js';
2
- export { registerIconLibrary, registerIcons, resolveIcon } from './registry.js';
2
+ export { registerIconLibrary, registerIcons, resolveIcon, } from './registry.js';
3
3
  export * from './root/icon-root.js';
@@ -1,6 +1,15 @@
1
1
  import type { HelixIcon } from '@fusion.dev/helix-icons';
2
2
  /** What a library gives for a name: a glyph, a glyph later, or nothing. */
3
3
  export type IconResolver = (name: string) => HelixIcon | Promise<HelixIcon | undefined> | undefined;
4
+ /** How a library's glyphs are drawn, beside the names it resolves. */
5
+ export interface IconLibraryOptions {
6
+ /**
7
+ * Changes each drawn `<svg>` of the library's glyphs, as Web Awesome's
8
+ * `mutator` does: a stroke width, a fill, an attribute the set leaves out.
9
+ * Called after every render that draws one of them.
10
+ */
11
+ mutator?: (svg: SVGSVGElement) => void;
12
+ }
4
13
  /**
5
14
  * Makes glyphs resolvable by name: a record of names to glyphs, such as the
6
15
  * whole set, or a list of glyphs, each under its own `name`.
@@ -15,14 +24,30 @@ export declare const registerIcons: (glyphs: Record<string, HelixIcon> | readonl
15
24
  * The resolver may answer at once or with a promise; an element waiting on a
16
25
  * promise draws the glyph when it arrives and fires `load`. Each written name
17
26
  * is asked once, and a prefix registered again is asked afresh.
27
+ *
28
+ * A sprite sheet is a library whose glyphs point into it, so the sheet is
29
+ * fetched once and cached:
30
+ *
31
+ * ```js
32
+ * registerIconLibrary('sprite', (name) => ({
33
+ * content: `<use href="/icons.svg#${name}"></use>`,
34
+ * name,
35
+ * viewBox: '0 0 24 24',
36
+ * }))
37
+ * ```
38
+ *
39
+ * `mutator` changes each drawn `<svg>` of the library, such as
40
+ * `(svg) => svg.setAttribute('stroke-width', '1.5')`.
18
41
  */
19
- export declare const registerIconLibrary: (prefix: string, resolve: IconResolver) => void;
42
+ export declare const registerIconLibrary: (prefix: string, resolve: IconResolver, options?: IconLibraryOptions) => void;
20
43
  /**
21
44
  * Calls `watcher` whenever a glyph or a library is registered, so an element
22
45
  * drawn before its glyph was registered can look again. Returns the function
23
46
  * that stops it.
24
47
  */
25
48
  export declare const watchIcons: (watcher: () => void) => (() => void);
49
+ /** The mutator of the library a written name asks, or `undefined`. */
50
+ export declare const iconMutator: (name: string) => ((svg: SVGSVGElement) => void) | undefined;
26
51
  /** The registered glyph of this name, or `undefined`. */
27
52
  export declare const registeredIcon: (name: string) => HelixIcon | undefined;
28
53
  /**
@@ -24,9 +24,16 @@ export const registerIcons = (glyphs) => {
24
24
  }
25
25
  changed(held);
26
26
  };
27
- export const registerIconLibrary = (prefix, resolve) => {
27
+ export const registerIconLibrary = (prefix, resolve, options = {}) => {
28
28
  const held = registry();
29
29
  held.libraries.set(prefix, resolve);
30
+ held.mutators ??= new Map();
31
+ if (options.mutator) {
32
+ held.mutators.set(prefix, options.mutator);
33
+ }
34
+ else {
35
+ held.mutators.delete(prefix);
36
+ }
30
37
  for (const name of held.answers.keys()) {
31
38
  if (name.startsWith(`${prefix}:`)) {
32
39
  held.answers.delete(name);
@@ -41,6 +48,10 @@ export const watchIcons = (watcher) => {
41
48
  held.watchers.delete(watcher);
42
49
  };
43
50
  };
51
+ export const iconMutator = (name) => {
52
+ const split = name.indexOf(':');
53
+ return split > 0 ? registry().mutators?.get(name.slice(0, split)) : undefined;
54
+ };
44
55
  export const registeredIcon = (name) => registry().glyphs.get(name);
45
56
  export const resolveIcon = (name) => {
46
57
  const registered = registeredIcon(name);
@@ -36,6 +36,11 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
36
36
  * button with no text. An `aria-label` on the element works too: the element
37
37
  * is then an image with that name.
38
38
  *
39
+ * **A decorative icon that is all a control holds is reported.** In a
40
+ * development build, a button or a link whose only content is an icon with no
41
+ * `label`, and which has no name of its own, warns `missing-name` once,
42
+ * through the runtime channel every Helix warning uses.
43
+ *
39
44
  * @since 0.1.0
40
45
  * @status stable
41
46
  * @category media
@@ -121,6 +126,14 @@ export declare class HelixHeadlessIcon extends HelixElement {
121
126
  private lookup;
122
127
  protected willUpdate(changed: PropertyValues): void;
123
128
  protected updated(changed: PropertyValues): void;
129
+ /** Hands the drawn `<svg>` to the mutator of the library `name` asks, where it has one. */
130
+ private mutate;
131
+ /**
132
+ * Asks the button or link a decorative icon sits in whether it has a name.
133
+ * Its content is this icon alone where it has none, and the audit says so
134
+ * once, in a development build only. A Helix button reports its own name.
135
+ */
136
+ private auditControl;
124
137
  /** The name a report is waiting on the page to settle for, so it is asked once. */
125
138
  private pending;
126
139
  /**
@@ -4,11 +4,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixElement, hostAria, reportRuntime } from '@fusion.dev/helix-core';
7
+ import { auditName, customElement, HelixElement, hostAria, reportRuntime, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
11
- import { resolveIcon, watchIcons } from '../registry.js';
11
+ import { iconMutator, resolveIcon, watchIcons } from '../registry.js';
12
12
  import { afterPageSettles } from '../settle.js';
13
13
  let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
14
14
  constructor() {
@@ -66,6 +66,10 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
66
66
  }
67
67
  updated(changed) {
68
68
  super.updated(changed);
69
+ if (this.drawn !== undefined) {
70
+ this.mutate();
71
+ this.auditControl();
72
+ }
69
73
  if (this.arrived === this.name && this.drawn !== undefined) {
70
74
  this.dispatchEvent(new Event('load', { bubbles: false, cancelable: false, composed: false }));
71
75
  }
@@ -80,6 +84,23 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
80
84
  this.missed = '';
81
85
  }
82
86
  }
87
+ mutate() {
88
+ const svg = this.renderRoot.querySelector('svg');
89
+ const mutator = this.icon === undefined ? iconMutator(this.name) : undefined;
90
+ if (svg && mutator) {
91
+ mutator(svg);
92
+ }
93
+ }
94
+ auditControl() {
95
+ if (this.label !== '' || this.hasAttribute('aria-label')) {
96
+ return;
97
+ }
98
+ const control = this.closest('a[href], button, summary, [role="button"], [role="link"]');
99
+ if (control) {
100
+ auditName(control, () => null, `Its only content is <${this.localName}>, which is decorative: give the icon a \`label\`, or the control an \`aria-label\`.`, () => (control.textContent ?? '').trim() !== '' ||
101
+ control.querySelector('[alt]:not([alt=""]), [aria-label]:not([aria-label=""])') !== null);
102
+ }
103
+ }
83
104
  reportMissing() {
84
105
  const name = this.pending;
85
106
  this.pending = '';
@@ -1,2 +1,2 @@
1
- export { type IconResolver, registerIconLibrary, registerIcons, resolveIcon } from '../registry.js';
1
+ export { type IconLibraryOptions, type IconResolver, registerIconLibrary, registerIcons, resolveIcon, } from '../registry.js';
2
2
  export * from './icon-root.js';
@@ -1,2 +1,2 @@
1
- export { registerIconLibrary, registerIcons, resolveIcon } from '../registry.js';
1
+ export { registerIconLibrary, registerIcons, resolveIcon, } from '../registry.js';
2
2
  export * from './icon-root.js';
@@ -16,6 +16,17 @@ import { type PropertyValues, type TemplateResult } from 'lit';
16
16
  * its own. The fallback is read once the picture fails, after the picture's
17
17
  * `alt`, so a reader hears that it did not load, as a sighted reader sees.
18
18
  *
19
+ * **An image with no `alt` says so.** At its first render it reports a
20
+ * `missing-alt` warning, printed in the console and counted by
21
+ * `@fusion.dev/helix-diagnostics`, which names the fix: describe the picture
22
+ * in `alt`, or write `alt=""` for one that is decoration. An image hidden with
23
+ * `aria-hidden="true"` needs neither and is not reported.
24
+ *
25
+ * A sheet of yours reads the state as `:state(loading)`, `:state(loaded)`,
26
+ * `:state(error)`, and `:state(waiting)`, the last while `placeholder-delay`
27
+ * holds the placeholder back. It can also read `data-status` on the host once
28
+ * the picture has loaded or failed.
29
+ *
19
30
  * @since 0.1.0
20
31
  * @status stable
21
32
  * @category media
@@ -34,6 +45,8 @@ import { type PropertyValues, type TemplateResult } from 'lit';
34
45
  *
35
46
  * @cssstate error - The image failed to load, and the fallback shows.
36
47
  * @cssstate loaded - The image loaded.
48
+ * @cssstate loading - The image is on its way, so neither `loaded` nor `error` is true yet.
49
+ * @cssstate waiting - The image is on its way and `placeholder-delay` still holds the placeholder back.
37
50
  */
38
51
  export declare class HelixHeadlessImage extends HelixElement {
39
52
  /**
@@ -121,6 +134,11 @@ export declare class HelixHeadlessImage extends HelixElement {
121
134
  *
122
135
  * `loading` writes nothing, because it is where every image starts and the
123
136
  * resting state of a mirror is the attribute's absence.
137
+ *
138
+ * **Kept beside the custom states, and what the styled tier draws from.** An
139
+ * engine without `CustomStateSet`, Safari before 17.4 and Firefox before
140
+ * 126, records no state, and a picture faded in by `:state(loaded)` alone
141
+ * would stay transparent there for good. A sheet of your own can read either.
124
142
  */
125
143
  status: 'error' | 'loaded' | 'loading';
126
144
  /**
@@ -194,6 +212,8 @@ export declare class HelixHeadlessImage extends HelixElement {
194
212
  private settle;
195
213
  protected willUpdate(changed: PropertyValues): void;
196
214
  protected updated(changed: PropertyValues): void;
215
+ /** Whether the picture is on its way and the wait still holds the placeholder back. */
216
+ private get waiting();
197
217
  private startWait;
198
218
  protected firstUpdated(changed: PropertyValues): void;
199
219
  /**
@@ -92,7 +92,12 @@ let HelixHeadlessImage = class HelixHeadlessImage extends HelixElement {
92
92
  }
93
93
  this.toggleState('loaded', this.status === 'loaded');
94
94
  this.toggleState('error', this.status === 'error');
95
+ this.toggleState('loading', this.status === 'loading');
95
96
  }
97
+ this.toggleState('waiting', this.waiting);
98
+ }
99
+ get waiting() {
100
+ return this.status === 'loading' && this.placeholderDelay > 0 && !this.wait.elapsed;
96
101
  }
97
102
  startWait() {
98
103
  this.wait.start(this.placeholderDelay > 0 ? this.placeholderDelay : undefined);
@@ -142,7 +147,7 @@ let HelixHeadlessImage = class HelixHeadlessImage extends HelixElement {
142
147
  <span
143
148
  part="placeholder"
144
149
  aria-hidden="true"
145
- ?hidden=${this.status !== 'loading' || (this.placeholderDelay > 0 && !this.wait.elapsed)}
150
+ ?hidden=${this.status !== 'loading' || this.waiting}
146
151
  ><slot name="placeholder"></slot
147
152
  ></span>
148
153
  <span
@@ -1,4 +1,4 @@
1
- import { AriaForwardController, HelixFormElement } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, HelixFormElement, type OwnRule } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  /** What the control accepts, which is text of one kind or another. */
4
4
  export type InputType = 'email' | 'number' | 'password' | 'search' | 'tel' | 'text' | 'url';
@@ -25,16 +25,30 @@ declare const HelixHeadlessInput_base: (new (...args: any[]) => import("@fusion.
25
25
  * Aria's TextField `validate` does. What it returns is the message the field
26
26
  * shows once the user has edited it, and the message a submit raises.
27
27
  *
28
+ * **Keyboard.** The field's own keys are the platform's. Enter submits the
29
+ * form through its default button, as in a native field. The clear button
30
+ * under `clearable` and the reveal button under `reveal` are tab stops of
31
+ * their own after the field, so a keyboard reaches them.
32
+ *
33
+ * The inner input is named by `label`, or by a host `aria-label`,
34
+ * `aria-labelledby` or `<label for>`, and is described by the hint and the
35
+ * message, with `aria-invalid` while a message shows. The clear button is
36
+ * named `clear-label`, and the reveal button `reveal-label` with
37
+ * `aria-pressed` while the password shows.
38
+ *
28
39
  * @since 0.1.0
29
40
  * @status stable
30
41
  * @category forms
31
42
  * @aliases TextField, Field, TextInput
32
43
  * @documentation https://fusion.dev/helix/components/input
33
44
  *
45
+ * @slot clear-icon - Replaces the cross on the clear button, under `clearable`.
46
+ * @slot conceal-icon - Replaces the crossed-out eye on the reveal button while the password shows, under `reveal`.
34
47
  * @slot end - After the text, for an icon or a unit.
35
48
  * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
36
49
  * @slot hint - Help under the control, for content richer than the `hint` text.
37
50
  * @slot label - The label, for content richer than the `label` text.
51
+ * @slot reveal-icon - Replaces the eye on the reveal button while the password is hidden, under `reveal`.
38
52
  * @slot start - Before the text, for an icon or a prefix.
39
53
  *
40
54
  * @event {Event} change - Sent when the user finishes changing the value. Relayed from the native input.
@@ -290,7 +304,14 @@ export declare class HelixHeadlessInput extends HelixHeadlessInput_base {
290
304
  * nothing wrong. Return a message, or a list of them, to mark the field
291
305
  * invalid, and `true`, `null`, or nothing to accept the value.
292
306
  */
293
- validate?: (value: string) => string | string[] | true | null | undefined;
307
+ validate?: OwnRule;
308
+ /** The cross on the clear button, behind a seam a tier can move. */
309
+ protected renderClearIcon(): TemplateResult;
310
+ /**
311
+ * The eye on the reveal button, behind a seam a tier can move: open while
312
+ * the password is hidden, and crossed out while it shows.
313
+ */
314
+ protected renderRevealIcon(): TemplateResult;
294
315
  protected render(): TemplateResult;
295
316
  }
296
317
  declare global {
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { AriaForwardController, customElement, drawIcon, HelixClearEvent, HelixFormElement, holdCaret, MixinControllerValue, MixinField, restoreValue, submitImplicitly, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, applyOwnRule, customElement, drawIcon, HelixClearEvent, HelixFormElement, holdCaret, MixinControllerValue, MixinField, restoreValue, submitImplicitly, } from '@fusion.dev/helix-core';
8
8
  import { eye, eyeSlash, x } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -112,11 +112,7 @@ let HelixHeadlessInput = class HelixHeadlessInput extends MixinControllerValue(M
112
112
  return;
113
113
  }
114
114
  this.copyValidity(control);
115
- const own = control.validity.valid ? this.validate?.(this.value) : undefined;
116
- const message = Array.isArray(own) ? own.join(' ') : typeof own === 'string' ? own : '';
117
- if (message !== '') {
118
- this.internals.setValidity({ customError: true }, message, control);
119
- }
115
+ applyOwnRule(this.internals, control, this.validate, this.value);
120
116
  this.refreshFeedback();
121
117
  }
122
118
  focus(options) {
@@ -156,6 +152,14 @@ let HelixHeadlessInput = class HelixHeadlessInput extends MixinControllerValue(M
156
152
  this.releaseCaret = undefined;
157
153
  this.sync();
158
154
  }
155
+ renderClearIcon() {
156
+ return html `<slot name="clear-icon">${drawIcon(x)}</slot>`;
157
+ }
158
+ renderRevealIcon() {
159
+ return this.revealed
160
+ ? html `<slot name="conceal-icon">${drawIcon(eyeSlash)}</slot>`
161
+ : html `<slot name="reveal-icon">${drawIcon(eye)}</slot>`;
162
+ }
159
163
  render() {
160
164
  return html `${this.renderLabel()}
161
165
  <div part="field">
@@ -199,7 +203,7 @@ let HelixHeadlessInput = class HelixHeadlessInput extends MixinControllerValue(M
199
203
  aria-label=${this.clearLabel}
200
204
  @click=${this.handleClear}
201
205
  >
202
- ${drawIcon(x)}
206
+ ${this.renderClearIcon()}
203
207
  </button>`
204
208
  : nothing}
205
209
  ${this.reveal && this.type === 'password'
@@ -213,7 +217,7 @@ let HelixHeadlessInput = class HelixHeadlessInput extends MixinControllerValue(M
213
217
  this.revealed = !this.revealed;
214
218
  }}
215
219
  >
216
- ${drawIcon(this.revealed ? eyeSlash : eye)}
220
+ ${this.renderRevealIcon()}
217
221
  </button>`
218
222
  : nothing}
219
223
  </div>
@@ -26,7 +26,13 @@ import { type PropertyValues, type TemplateResult } from 'lit';
26
26
  * `caps` draws a chord as a key per key: each is a `key` part and the `+`
27
27
  * between two a `separator` part, so a sheet can draw a cap for each.
28
28
  * `key-labels` renames any key, on every platform, such as
29
- * `key-labels='{"mod":"Command"}'`.
29
+ * `key-labels='{"mod":"Command"}'`. On an Apple keyboard a word joins the
30
+ * next key with a hyphen, as in `Command-K`.
31
+ *
32
+ * **`activate` makes the shortcut work the control `for` names**, with no
33
+ * script: `activate="focus"` moves focus to it, as `/` does to a search
34
+ * field, and `activate="click"` presses it. It acts once `helix-shortcut` has
35
+ * gone out and no listener canceled it, and only where `within` listens.
30
36
  *
31
37
  * @since 0.1.0
32
38
  * @status stable
@@ -40,9 +46,18 @@ import { type PropertyValues, type TemplateResult } from 'lit';
40
46
  *
41
47
  * @csspart kbd - The key, or the chord under `keys`.
42
48
  * @csspart key - One key of a chord under `keys` and `caps`, such as `Ctrl` in `Ctrl+K`.
43
- * @csspart separator - The `+` between two keys of a chord under `keys` and `caps`, drawn where the platform writes one: on Windows and Linux. An Apple keyboard writes none.
49
+ * @csspart separator - The `+` between two keys of a chord under `keys` and `caps`, drawn where the platform writes one: on Windows and Linux. An Apple keyboard writes none between glyphs, and a hyphen between words from `key-labels`.
44
50
  */
45
51
  export declare class HelixHeadlessKbd extends HelixElement {
52
+ /**
53
+ * What the chord does to the control `for` names once it is pressed:
54
+ * `focus` moves focus to the control and `click` presses it.
55
+ *
56
+ * Unset, the chord sends `helix-shortcut` and does nothing else, for a page
57
+ * that acts on the event itself. It acts only where `within` listens, after
58
+ * `helix-shortcut`, and not when a listener cancels that event.
59
+ */
60
+ activate?: 'click' | 'focus';
46
61
  /**
47
62
  * Draws a chord under `keys` as one key per key, each a `key` part with a
48
63
  * `separator` part between two where the platform writes a `+`, as GitHub
@@ -72,7 +87,7 @@ export declare class HelixHeadlessKbd extends HelixElement {
72
87
  * changes: the chord written onto `aria-keyshortcuts` keeps the names a
73
88
  * screen reader knows.
74
89
  */
75
- keyLabels?: Record<string, string>;
90
+ keyLabels: Record<string, string>;
76
91
  /**
77
92
  * The key or chord to show, such as `mod+k`, `?`, or `/`.
78
93
  *
@@ -119,6 +134,8 @@ export declare class HelixHeadlessKbd extends HelixElement {
119
134
  private listen;
120
135
  private deafen;
121
136
  private readonly handleKeydown;
137
+ /** Focuses or presses the control `for` names, as `activate` says. */
138
+ private act;
122
139
  protected firstUpdated(changed: PropertyValues): void;
123
140
  protected render(): TemplateResult;
124
141
  /**