@fusion.dev/helix-headless 0.146.0 → 0.147.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 (239) hide show
  1. package/custom-elements.json +5266 -785
  2. package/dist/accordion/item/accordion-item.d.ts +38 -19
  3. package/dist/accordion/item/accordion-item.js +31 -9
  4. package/dist/accordion/root/accordion-root.d.ts +8 -2
  5. package/dist/accordion/root/accordion-root.js +3 -3
  6. package/dist/announcement/root/announcement-root.d.ts +13 -4
  7. package/dist/announcement/root/announcement-root.js +34 -6
  8. package/dist/area-chart/root/area-chart-root.d.ts +18 -1
  9. package/dist/area-chart/root/area-chart-root.js +11 -3
  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/root/badge-root.d.ts +57 -6
  13. package/dist/badge/root/badge-root.js +88 -5
  14. package/dist/bar-chart/root/bar-chart-root.js +11 -20
  15. package/dist/bar-chart/stack.d.ts +10 -0
  16. package/dist/bar-chart/stack.js +8 -0
  17. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +14 -0
  18. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +12 -2
  19. package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -2
  20. package/dist/button/root/button-root.d.ts +24 -38
  21. package/dist/button/root/button-root.js +6 -37
  22. package/dist/button/submitter.d.ts +27 -0
  23. package/dist/button/submitter.js +25 -0
  24. package/dist/card/root/card-root.d.ts +12 -1
  25. package/dist/card/root/card-root.js +11 -4
  26. package/dist/chat/root/chat-root.d.ts +15 -1
  27. package/dist/chat/root/chat-root.js +11 -2
  28. package/dist/checkbox/root/checkbox-root.js +5 -7
  29. package/dist/checkbox-group/root/checkbox-group-root.js +4 -3
  30. package/dist/checkbox-group/select-all.d.ts +0 -6
  31. package/dist/checkbox-group/select-all.js +0 -5
  32. package/dist/code-block/root/code-block-root.d.ts +35 -14
  33. package/dist/code-block/root/code-block-root.js +17 -3
  34. package/dist/color-input/root/color-input-root.d.ts +11 -0
  35. package/dist/color-input/root/color-input-root.js +12 -5
  36. package/dist/color-picker/channels.js +1 -2
  37. package/dist/color-picker/root/color-picker-root.d.ts +32 -24
  38. package/dist/color-picker/root/color-picker-root.js +101 -51
  39. package/dist/color-picker/wheel.d.ts +17 -0
  40. package/dist/color-picker/wheel.js +6 -0
  41. package/dist/combobox/root/combobox-root.d.ts +13 -0
  42. package/dist/combobox/root/combobox-root.js +14 -1
  43. package/dist/command/root/command-root.d.ts +3 -1
  44. package/dist/command/root/command-root.js +2 -19
  45. package/dist/copy-button/root/copy-button-root.d.ts +16 -1
  46. package/dist/copy-button/root/copy-button-root.js +7 -2
  47. package/dist/data-table/keys.d.ts +0 -18
  48. package/dist/data-table/keys.js +1 -11
  49. package/dist/data-table/root/data-table-root.js +13 -7
  50. package/dist/date-input/root/date-input-root.d.ts +8 -1
  51. package/dist/date-input/root/date-input-root.js +8 -5
  52. package/dist/date-picker/root/date-picker-root.d.ts +14 -9
  53. package/dist/date-picker/root/date-picker-root.js +21 -16
  54. package/dist/description-list/item/description-list-item.d.ts +18 -4
  55. package/dist/description-list/item/description-list-item.js +8 -4
  56. package/dist/donut-chart/root/donut-chart-root.js +13 -6
  57. package/dist/donut-chart/slice-label.d.ts +35 -0
  58. package/dist/donut-chart/slice-label.js +11 -0
  59. package/dist/drawer/root/drawer-root.d.ts +4 -2
  60. package/dist/drawer/root/drawer-root.js +15 -3
  61. package/dist/drilldown/item/drilldown-item.d.ts +21 -1
  62. package/dist/drilldown/item/drilldown-item.js +39 -4
  63. package/dist/drilldown/root/drilldown-root.js +3 -2
  64. package/dist/element-events.generated.js +4 -1
  65. package/dist/empty/root/empty-root.d.ts +31 -1
  66. package/dist/empty/root/empty-root.js +26 -5
  67. package/dist/file-input/root/file-input-root.d.ts +34 -6
  68. package/dist/file-input/root/file-input-root.js +54 -19
  69. package/dist/file-input/status.d.ts +24 -0
  70. package/dist/file-input/status.js +10 -0
  71. package/dist/filter-input/chip/filter-chip.d.ts +23 -0
  72. package/dist/filter-input/chip/filter-chip.js +11 -2
  73. package/dist/filter-input/root/filter-input-root.d.ts +8 -0
  74. package/dist/filter-input/root/filter-input-root.js +17 -27
  75. package/dist/format-date/options.d.ts +3 -4
  76. package/dist/format-date/options.js +2 -2
  77. package/dist/format-date/root/format-date-root.d.ts +18 -1
  78. package/dist/format-date/root/format-date-root.js +19 -23
  79. package/dist/heatmap/root/buckets.d.ts +21 -0
  80. package/dist/heatmap/root/buckets.js +34 -0
  81. package/dist/heatmap/root/heatmap-root.d.ts +64 -3
  82. package/dist/heatmap/root/heatmap-root.js +89 -19
  83. package/dist/icon/index.d.ts +1 -1
  84. package/dist/icon/index.js +1 -1
  85. package/dist/icon/registry.d.ts +26 -1
  86. package/dist/icon/registry.js +12 -1
  87. package/dist/icon/root/icon-root.d.ts +13 -0
  88. package/dist/icon/root/icon-root.js +23 -2
  89. package/dist/icon/root/index.d.ts +1 -1
  90. package/dist/icon/root/index.js +1 -1
  91. package/dist/image/root/image-root.d.ts +9 -0
  92. package/dist/image/root/image-root.js +6 -1
  93. package/dist/input/root/input-root.d.ts +23 -2
  94. package/dist/input/root/input-root.js +12 -8
  95. package/dist/kbd/keys.d.ts +29 -0
  96. package/dist/kbd/keys.js +52 -0
  97. package/dist/kbd/root/kbd-root.d.ts +20 -3
  98. package/dist/kbd/root/kbd-root.js +24 -50
  99. package/dist/legend/hidden-series.d.ts +10 -2
  100. package/dist/legend/hidden-series.js +5 -2
  101. package/dist/legend/root/legend-root.d.ts +25 -7
  102. package/dist/legend/root/legend-root.js +22 -10
  103. package/dist/lightbox/root/lightbox-root.d.ts +50 -5
  104. package/dist/lightbox/root/lightbox-root.js +76 -8
  105. package/dist/lightbox/zoom.d.ts +2 -0
  106. package/dist/lightbox/zoom.js +5 -3
  107. package/dist/line-chart/root/line-chart-root.d.ts +16 -0
  108. package/dist/line-chart/root/line-chart-root.js +10 -2
  109. package/dist/list/root/list-root.d.ts +10 -2
  110. package/dist/list/root/list-root.js +26 -5
  111. package/dist/list/selection.d.ts +17 -0
  112. package/dist/list/selection.js +16 -0
  113. package/dist/listbox/root/listbox-root.d.ts +9 -5
  114. package/dist/listbox/root/listbox-root.js +29 -37
  115. package/dist/megamenu/hand-over.d.ts +75 -0
  116. package/dist/megamenu/hand-over.js +67 -0
  117. package/dist/megamenu/root/megamenu-root.d.ts +38 -14
  118. package/dist/megamenu/root/megamenu-root.js +36 -50
  119. package/dist/menu/item/menu-item.js +4 -4
  120. package/dist/menu/root/menu-root.d.ts +3 -0
  121. package/dist/meter/root/meter-root.d.ts +42 -0
  122. package/dist/meter/root/meter-root.js +33 -7
  123. package/dist/notifications/root/notifications-root.d.ts +10 -1
  124. package/dist/notifications/root/notifications-root.js +11 -1
  125. package/dist/number-input/grain.d.ts +28 -0
  126. package/dist/number-input/grain.js +14 -0
  127. package/dist/number-input/root/number-input-root.d.ts +17 -1
  128. package/dist/number-input/root/number-input-root.js +29 -18
  129. package/dist/object-inspector/root/object-inspector-root.d.ts +13 -6
  130. package/dist/object-inspector/root/object-inspector-root.js +18 -17
  131. package/dist/option/index.d.ts +1 -0
  132. package/dist/option/index.js +1 -0
  133. package/dist/option/separator/option-separator.d.ts +30 -0
  134. package/dist/option/separator/option-separator.js +21 -0
  135. package/dist/outline/root/outline-root.d.ts +12 -0
  136. package/dist/outline/root/outline-root.js +5 -19
  137. package/dist/pagination/size/pagination-size.d.ts +5 -0
  138. package/dist/pagination/size/pagination-size.js +4 -2
  139. package/dist/pin-input/code.d.ts +28 -1
  140. package/dist/pin-input/code.js +12 -0
  141. package/dist/pin-input/root/pin-input-root.d.ts +98 -121
  142. package/dist/pin-input/root/pin-input-root.js +157 -148
  143. package/dist/popover/hide-outside.js +2 -9
  144. package/dist/popover/root/popover-root.d.ts +7 -1
  145. package/dist/popover/root/popover-root.js +9 -1
  146. package/dist/progress/root/progress-root.d.ts +27 -0
  147. package/dist/progress/root/progress-root.js +25 -7
  148. package/dist/qr-code/export.d.ts +19 -0
  149. package/dist/qr-code/export.js +14 -0
  150. package/dist/qr-code/name.d.ts +11 -0
  151. package/dist/qr-code/name.js +7 -0
  152. package/dist/qr-code/root/qr-code-root.d.ts +17 -6
  153. package/dist/qr-code/root/qr-code-root.js +10 -12
  154. package/dist/radio/group/radio-group.d.ts +15 -0
  155. package/dist/radio/root/radio-root.js +2 -2
  156. package/dist/rating/root/rating-root.d.ts +22 -9
  157. package/dist/rating/root/rating-root.js +15 -39
  158. package/dist/scatter-chart/root/scatter-chart-root.d.ts +30 -0
  159. package/dist/scatter-chart/root/scatter-chart-root.js +34 -7
  160. package/dist/scroll-area/scrollbar.d.ts +0 -8
  161. package/dist/scroll-area/scrollbar.js +1 -1
  162. package/dist/select/align.d.ts +47 -0
  163. package/dist/select/align.js +7 -0
  164. package/dist/select/root/select-root.d.ts +59 -3
  165. package/dist/select/root/select-root.js +72 -12
  166. package/dist/slider/root/slider-root.d.ts +18 -1
  167. package/dist/slider/root/slider-root.js +22 -0
  168. package/dist/sparkline/readings.d.ts +30 -12
  169. package/dist/sparkline/readings.js +32 -15
  170. package/dist/sparkline/root/sparkline-root.d.ts +46 -3
  171. package/dist/sparkline/root/sparkline-root.js +60 -21
  172. package/dist/speed-dial/item/speed-dial-item.d.ts +1 -1
  173. package/dist/speed-dial/item/speed-dial-item.js +2 -2
  174. package/dist/speed-dial/root/speed-dial-root.d.ts +6 -0
  175. package/dist/speed-dial/root/speed-dial-root.js +9 -1
  176. package/dist/splitter/root/splitter-root.d.ts +3 -0
  177. package/dist/splitter/root/splitter-root.js +2 -0
  178. package/dist/steps/root/steps-root.d.ts +8 -1
  179. package/dist/steps/root/steps-root.js +6 -1
  180. package/dist/steps/step/step.d.ts +1 -0
  181. package/dist/steps/step/step.js +1 -1
  182. package/dist/table/column/table-column.d.ts +6 -0
  183. package/dist/table/column/table-column.js +5 -1
  184. package/dist/table/root/table-root.js +13 -6
  185. package/dist/table/row/table-row.d.ts +24 -0
  186. package/dist/table/row/table-row.js +37 -1
  187. package/dist/tabs/root/tabs-root.d.ts +5 -3
  188. package/dist/tag/group/tag-group.d.ts +16 -4
  189. package/dist/tag/group/tag-group.js +19 -7
  190. package/dist/textarea/root/textarea-root.d.ts +14 -2
  191. package/dist/textarea/root/textarea-root.js +6 -7
  192. package/dist/time-input/root/time-input-root.d.ts +5 -1
  193. package/dist/time-input/root/time-input-root.js +10 -7
  194. package/dist/time-picker/root/time-picker-root.d.ts +11 -14
  195. package/dist/time-picker/root/time-picker-root.js +15 -23
  196. package/dist/toast/item/toast-item.d.ts +3 -3
  197. package/dist/toast/item/toast-item.js +0 -1
  198. package/dist/toast/root/toast-root.d.ts +28 -4
  199. package/dist/toast/root/toast-root.js +58 -12
  200. package/dist/toast/words.d.ts +12 -0
  201. package/dist/toast/words.js +25 -0
  202. package/dist/toggle/group/toggle-group.d.ts +29 -5
  203. package/dist/toggle/group/toggle-group.js +26 -41
  204. package/dist/toggle/root/toggle-root.js +2 -4
  205. package/dist/toolbar/root/toolbar-root.d.ts +21 -5
  206. package/dist/toolbar/root/toolbar-root.js +52 -8
  207. package/dist/tooltip/root/tooltip-root.d.ts +7 -1
  208. package/dist/tooltip/root/tooltip-root.js +10 -1
  209. package/dist/tour/root/tour-root.d.ts +15 -0
  210. package/dist/tour/root/tour-root.js +20 -1
  211. package/dist/tour/stop/tour-stop.d.ts +3 -0
  212. package/dist/tour/stop/tour-stop.js +3 -3
  213. package/dist/tree/root/tree-root.js +2 -2
  214. package/dist/trend/figure.d.ts +16 -0
  215. package/dist/trend/figure.js +8 -0
  216. package/dist/trend/root/trend-root.d.ts +41 -2
  217. package/dist/trend/root/trend-root.js +29 -3
  218. package/dist/video/root/extra-controls.d.ts +12 -0
  219. package/dist/video/root/extra-controls.js +6 -2
  220. package/dist/video/root/video-root.d.ts +5 -1
  221. package/dist/video-playlist/root/video-playlist-root.d.ts +11 -1
  222. package/dist/video-playlist/root/video-playlist-root.js +3 -0
  223. package/dist/virtual-list/fill.d.ts +11 -0
  224. package/dist/virtual-list/fill.js +9 -0
  225. package/dist/virtual-list/keys.d.ts +16 -7
  226. package/dist/virtual-list/keys.js +17 -6
  227. package/dist/virtual-list/plan.d.ts +17 -1
  228. package/dist/virtual-list/plan.js +13 -2
  229. package/dist/virtual-list/root/virtual-list-root.d.ts +25 -3
  230. package/dist/virtual-list/root/virtual-list-root.js +48 -24
  231. package/lint.json +1 -1
  232. package/package.json +5 -5
  233. package/types/jsx.d.ts +2979 -738
  234. package/types/svelte.d.ts +1 -0
  235. package/types/vue.d.ts +619 -202
  236. package/dist/color-picker/swatches.d.ts +0 -12
  237. package/dist/color-picker/swatches.js +0 -26
  238. package/dist/rating/steps.d.ts +0 -7
  239. package/dist/rating/steps.js +0 -9
@@ -4,20 +4,20 @@ 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 { CopyController, capturePointer, colorName, customElement, drawIcon, fillTemplate, formatColor, HelixFormElement, HelixSampleEndEvent, HelixSampleStartEvent, hostAria, hueName, inlineStep, isRtl, MixinControllerValue, parseColor, rovingStop, splitValues, toCss, wordsConverter, } from '@fusion.dev/helix-core';
7
+ import { CopyController, capturePointer, clamp, colorName, customElement, drawIcon, fillTemplate, formatColor, formatNumber, HelixFormElement, HelixSampleEndEvent, HelixSampleStartEvent, hostAria, hueName, isRtl, KeyboardNavigationController, localeOf, MixinControllerValue, MixinPickerFeedback, parseColor, rovingStop, splitValues, toCss, wordsConverter, } from '@fusion.dev/helix-core';
8
8
  import { eyedropper } from '@fusion.dev/helix-icons';
9
9
  import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { live } from 'lit/directives/live.js';
12
12
  import { html } from 'lit/static-html.js';
13
13
  import { channelFields, writeChannel } from '../channels.js';
14
- import { swatchMove } from '../swatches.js';
14
+ import { hueAt } from '../wheel.js';
15
15
  import '../../button/index.js';
16
16
  import '../../button-group/index.js';
17
17
  import '../../slider/index.js';
18
18
  const FORMATS = ['hex', 'rgb', 'hsl'];
19
19
  const START = { alpha: 100, brightness: 100, hue: 0, saturation: 100 };
20
- let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinControllerValue(HelixFormElement) {
20
+ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinPickerFeedback(MixinControllerValue(HelixFormElement)) {
21
21
  constructor() {
22
22
  super(...arguments);
23
23
  this.alpha = false;
@@ -29,11 +29,9 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
29
29
  this.copiedLabel = 'Copied';
30
30
  this.copyLabel = 'Copy {value}';
31
31
  this.eyedropperLabel = 'Sample a color from the screen';
32
- this.feedback = '';
33
32
  this.formatLabel = 'Syntax: {format}. Change it.';
34
33
  this.greenLabel = 'Green';
35
34
  this.hueLabel = 'Hue';
36
- this.invalid = false;
37
35
  this.lightnessLabel = 'Lightness';
38
36
  this.noEyedropper = false;
39
37
  this.noFormat = false;
@@ -47,6 +45,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
47
45
  this.swatches = '';
48
46
  this.toolsLabel = 'Color tools';
49
47
  this.valueLabel = 'Color';
48
+ this.wheel = false;
50
49
  this.sampling = false;
51
50
  this.owned = false;
52
51
  this.announcesInput = true;
@@ -96,19 +95,24 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
96
95
  }
97
96
  this.settle();
98
97
  };
99
- this.handleSwatchKeydown = (event) => {
100
- const row = [
101
- ...(this.renderRoot.querySelectorAll('[part="swatches"] [part="swatch"]') ?? []),
102
- ];
103
- const at = row.indexOf(event.target);
104
- const next = swatchMove(event.key, inlineStep(this, event.key), at, row.length);
105
- if (at < 0 || next === null) {
106
- return;
107
- }
108
- event.preventDefault();
109
- this.swatchAt = next;
110
- row[next]?.focus();
111
- };
98
+ this.swatchKeys = new KeyboardNavigationController(this, {
99
+ items: () => [
100
+ ...(this.renderRoot?.querySelectorAll('[part="swatches"] [part="swatch"]') ??
101
+ []),
102
+ ].map((swatch, index) => ({
103
+ contains: (node) => swatch === node,
104
+ disabled: false,
105
+ focusItem: () => {
106
+ this.swatchAt = index;
107
+ swatch.focus();
108
+ },
109
+ select: () => { },
110
+ selected: false,
111
+ })),
112
+ origin: 'focus',
113
+ selects: () => false,
114
+ wrap: false,
115
+ });
112
116
  this.handleFormat = () => {
113
117
  if (this.readOnly) {
114
118
  return;
@@ -166,20 +170,45 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
166
170
  const down = Math.min(1, Math.max(0, (event.clientY - box.top) / box.height));
167
171
  this.move({ brightness: Math.round((1 - down) * 100), saturation: Math.round(across * 100) });
168
172
  };
173
+ this.handleWheelPointer = (event) => {
174
+ const wheel = event.currentTarget;
175
+ const grid = this.partNamed('grid');
176
+ if (this.isDisabled || (grid && event.composedPath().includes(grid))) {
177
+ return;
178
+ }
179
+ if (event.type === 'pointerup' || event.type === 'pointercancel') {
180
+ if (wheel.hasPointerCapture(event.pointerId)) {
181
+ this.settle();
182
+ }
183
+ return;
184
+ }
185
+ if (event.type === 'pointerdown') {
186
+ capturePointer(wheel, event.pointerId);
187
+ this.partNamed('wheel-hue')?.focus();
188
+ }
189
+ else if (!wheel.hasPointerCapture(event.pointerId)) {
190
+ return;
191
+ }
192
+ event.preventDefault();
193
+ const box = wheel.getBoundingClientRect();
194
+ if (box.width > 0 && box.height > 0) {
195
+ this.move({ hue: hueAt(event.clientX, event.clientY, box) });
196
+ }
197
+ };
169
198
  this.handleAreaKeydown = (event) => {
170
199
  if (this.isDisabled || this.readOnly) {
171
200
  return;
172
201
  }
173
202
  const color = this.color;
174
203
  const along = isRtl(this) ? -1 : 1;
175
- const clamp = (at) => Math.min(100, Math.max(0, at));
204
+ const held = (at) => clamp(at, 0, 100);
176
205
  const moves = {
177
- ArrowDown: { brightness: clamp(color.brightness - 1) },
178
- ArrowLeft: { saturation: clamp(color.saturation - along) },
179
- ArrowRight: { saturation: clamp(color.saturation + along) },
180
- ArrowUp: { brightness: clamp(color.brightness + 1) },
181
- PageDown: { brightness: clamp(color.brightness - 10) },
182
- PageUp: { brightness: clamp(color.brightness + 10) },
206
+ ArrowDown: { brightness: held(color.brightness - 1) },
207
+ ArrowLeft: { saturation: held(color.saturation - along) },
208
+ ArrowRight: { saturation: held(color.saturation + along) },
209
+ ArrowUp: { brightness: held(color.brightness + 1) },
210
+ PageDown: { brightness: held(color.brightness - 10) },
211
+ PageUp: { brightness: held(color.brightness + 10) },
183
212
  };
184
213
  const next = moves[event.key];
185
214
  if (!next) {
@@ -239,9 +268,6 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
239
268
  this.internals.setFormValue(this.value === '' ? null : this.value);
240
269
  this.reportRequired();
241
270
  }
242
- get validityFeedback() {
243
- return this.feedback;
244
- }
245
271
  reportRequired() {
246
272
  if (this.required && this.value === '') {
247
273
  this.internals.setValidity({ valueMissing: true }, this.valueMissingMessage || 'Choose a color.', this.partNamed('grid') ?? undefined);
@@ -325,9 +351,10 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
325
351
  part="channel"
326
352
  exportparts="control: channel-control, feedback: channel-feedback, field: channel-field,
327
353
  hint: channel-hint, label: channel-label, marker: channel-marker,
328
- marker-label: channel-marker-label, markers: channel-markers, output: channel-output"
354
+ marker-label: channel-marker-label, markers: channel-markers, output: channel-output,
355
+ tooltip: channel-tooltip, tooltips: channel-tooltips"
329
356
  data-channel=${channel}
330
- aria-invalid=${this.invalid ? 'true' : nothing}
357
+ aria-invalid=${this.marked ? 'true' : nothing}
331
358
  label=${label}
332
359
  max=${max}
333
360
  min="0"
@@ -365,7 +392,6 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
365
392
  }
366
393
  willUpdate(changed) {
367
394
  super.willUpdate(changed);
368
- this.internals.ariaInvalid = this.invalid ? 'true' : null;
369
395
  const outside = [...(this.internals.labels ?? [])];
370
396
  hostAria(this.internals, { labelledBy: outside, role: outside.length > 0 ? 'group' : '' });
371
397
  if (!this.hydrating) {
@@ -377,7 +403,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
377
403
  ${this.sampling && !this.noEyedropper
378
404
  ? html `<${this.tag('button')}
379
405
  part="eyedropper"
380
- exportparts="button: eyedropper-button, live: eyedropper-live, text: eyedropper-text"
406
+ exportparts="button: eyedropper-button, live: eyedropper-live, spinner: eyedropper-spinner, text: eyedropper-text"
381
407
  label=${this.eyedropperLabel}
382
408
  ?disabled=${this.isDisabled}
383
409
  @click=${this.handleEyeDropper}
@@ -389,7 +415,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
389
415
  ? nothing
390
416
  : html `<${this.tag('button')}
391
417
  part="format"
392
- exportparts="button: format-button, live: format-live, text: format-text"
418
+ exportparts="button: format-button, live: format-live, spinner: format-spinner, text: format-text"
393
419
  label=${fillTemplate(this.formatLabel, { format: this.format ?? 'hex' })}
394
420
  ?disabled=${this.isDisabled}
395
421
  @click=${this.handleFormat}
@@ -411,7 +437,6 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
411
437
  role="listbox"
412
438
  aria-label=${this.presetsLabel}
413
439
  aria-orientation="horizontal"
414
- @keydown=${this.handleSwatchKeydown}
415
440
  >
416
441
  ${presets.map((preset, index) => {
417
442
  const parsed = parseColor(preset);
@@ -420,7 +445,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
420
445
  type="button"
421
446
  role="option"
422
447
  tabindex=${index === stop ? '0' : '-1'}
423
- aria-label=${parsed ? colorName(parsed, this.colorWords, this.lang) : preset}
448
+ aria-label=${parsed ? colorName(parsed, this.colorWords, localeOf(this)) : preset}
424
449
  aria-selected=${chosen[index] ? 'true' : 'false'}
425
450
  style="--color-picker-swatch: ${preset}"
426
451
  ?disabled=${this.isDisabled}
@@ -435,13 +460,13 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
435
460
  render() {
436
461
  const color = this.color;
437
462
  const painted = toCss(color);
438
- const named = colorName(color, this.colorWords, this.lang);
463
+ const named = colorName(color, this.colorWords, localeOf(this));
439
464
  const both = `${this.saturationLabel} ${color.saturation}%, ${this.brightnessLabel} ${color.brightness}%, ${named}`;
440
465
  const reversed = `${this.brightnessLabel} ${color.brightness}%, ${this.saturationLabel} ${color.saturation}%, ${named}`;
441
- const hue = hueName({ alpha: 100, brightness: 100, hue: color.hue, saturation: 100 }, this.colorWords, this.lang);
466
+ const hue = hueName({ alpha: 100, brightness: 100, hue: color.hue, saturation: 100 }, this.colorWords, localeOf(this));
442
467
  const owner = this.getAttribute('aria-label')?.trim();
443
468
  const axisName = (channel) => (owner ? `${owner}, ${channel}` : channel);
444
- return html `<div
469
+ const area = html `<div
445
470
  part="grid"
446
471
  style="--color-picker-hue: ${color.hue}; --color-picker-saturation: ${color.saturation}%; --color-picker-brightness: ${color.brightness}%"
447
472
  @pointerdown=${this.handleGridPointer}
@@ -458,7 +483,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
458
483
  aria-label=${axisName(this.saturationLabel)}
459
484
  aria-describedby=${this.feedback === '' ? nothing : 'feedback'}
460
485
  aria-valuetext=${both}
461
- aria-invalid=${this.invalid ? 'true' : nothing}
486
+ aria-invalid=${this.marked ? 'true' : nothing}
462
487
  .value=${String(color.saturation)}
463
488
  ?disabled=${this.isDisabled}
464
489
  @input=${(event) => this.takeArea('saturation', event)}
@@ -474,13 +499,41 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
474
499
  aria-label=${axisName(this.brightnessLabel)}
475
500
  aria-describedby=${this.feedback === '' ? nothing : 'feedback'}
476
501
  aria-valuetext=${reversed}
477
- aria-invalid=${this.invalid ? 'true' : nothing}
502
+ aria-invalid=${this.marked ? 'true' : nothing}
478
503
  .value=${String(color.brightness)}
479
504
  ?disabled=${this.isDisabled}
480
505
  @input=${(event) => this.takeArea('brightness', event)}
481
506
  @change=${() => this.settle()}
482
507
  />
483
- </div>
508
+ </div>`;
509
+ return html `${this.wheel
510
+ ? html `<div
511
+ part="wheel"
512
+ style="--color-picker-hue: ${color.hue}"
513
+ @pointerdown=${this.handleWheelPointer}
514
+ @pointermove=${this.handleWheelPointer}
515
+ @pointerup=${this.handleWheelPointer}
516
+ @pointercancel=${this.handleWheelPointer}
517
+ >
518
+ ${''}
519
+ <input
520
+ part="wheel-hue"
521
+ type="range"
522
+ min="0"
523
+ max="360"
524
+ dir="ltr"
525
+ aria-label=${axisName(this.hueLabel)}
526
+ aria-describedby=${this.feedback === '' ? nothing : 'feedback'}
527
+ aria-valuetext=${`${color.hue}, ${hue}`}
528
+ aria-invalid=${this.marked ? 'true' : nothing}
529
+ .value=${String(color.hue)}
530
+ ?disabled=${this.isDisabled}
531
+ @input=${(event) => this.takeArea('hue', event)}
532
+ @change=${() => this.settle()}
533
+ />
534
+ ${area}
535
+ </div>`
536
+ : area}
484
537
 
485
538
  <div part="controls" style="--color-picker-hue: ${color.hue}; --color-picker-swatch: ${painted}">
486
539
  <button
@@ -494,9 +547,9 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
494
547
  @click=${() => this.copier.copy(this.value)}
495
548
  ></button>
496
549
  <div part="channels">
497
- ${this.renderChannel('hue', this.hueLabel, 360, color.hue, `{value}, ${hue}`)}
550
+ ${this.wheel ? nothing : this.renderChannel('hue', this.hueLabel, 360, color.hue, `{value}, ${hue}`)}
498
551
  ${this.alpha
499
- ? this.renderChannel('alpha', this.opacityLabel, 100, color.alpha, new Intl.NumberFormat(this.lang || undefined, { style: 'percent' }).format(color.alpha / 100))
552
+ ? this.renderChannel('alpha', this.opacityLabel, 100, color.alpha, formatNumber(color.alpha / 100, localeOf(this), { style: 'percent' }))
500
553
  : nothing}
501
554
  </div>
502
555
  </div>
@@ -514,7 +567,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
514
567
  type="text"
515
568
  aria-label=${this.valueLabel}
516
569
  aria-describedby=${this.feedback === '' ? nothing : 'feedback'}
517
- aria-invalid=${this.invalid ? 'true' : nothing}
570
+ aria-invalid=${this.marked ? 'true' : nothing}
518
571
  autocomplete=${this.autocomplete || nothing}
519
572
  spellcheck="false"
520
573
  .value=${this.value}
@@ -530,7 +583,7 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
530
583
  </div>
531
584
 
532
585
  ${this.presets.length === 0 ? nothing : this.renderSwatches(painted)}
533
- ${this.feedback === '' ? nothing : html `<p part="feedback" id="feedback">${this.feedback}</p>`}`;
586
+ ${this.renderPickerFeedback()}`;
534
587
  }
535
588
  };
536
589
  __decorate([
@@ -563,9 +616,6 @@ __decorate([
563
616
  __decorate([
564
617
  property({ attribute: 'eyedropper-label' })
565
618
  ], HelixHeadlessColorPicker.prototype, "eyedropperLabel", void 0);
566
- __decorate([
567
- property()
568
- ], HelixHeadlessColorPicker.prototype, "feedback", void 0);
569
619
  __decorate([
570
620
  property({ reflect: true })
571
621
  ], HelixHeadlessColorPicker.prototype, "format", void 0);
@@ -578,9 +628,6 @@ __decorate([
578
628
  __decorate([
579
629
  property({ attribute: 'hue-label' })
580
630
  ], HelixHeadlessColorPicker.prototype, "hueLabel", void 0);
581
- __decorate([
582
- property({ reflect: true, type: Boolean })
583
- ], HelixHeadlessColorPicker.prototype, "invalid", void 0);
584
631
  __decorate([
585
632
  property({ attribute: 'lightness-label' })
586
633
  ], HelixHeadlessColorPicker.prototype, "lightnessLabel", void 0);
@@ -623,6 +670,9 @@ __decorate([
623
670
  __decorate([
624
671
  property({ attribute: 'value-missing-message' })
625
672
  ], HelixHeadlessColorPicker.prototype, "valueMissingMessage", void 0);
673
+ __decorate([
674
+ property({ reflect: true, type: Boolean })
675
+ ], HelixHeadlessColorPicker.prototype, "wheel", void 0);
626
676
  __decorate([
627
677
  state()
628
678
  ], HelixHeadlessColorPicker.prototype, "moved", void 0);
@@ -0,0 +1,17 @@
1
+ /** A box on the screen, as `getBoundingClientRect` gives one. */
2
+ export interface WheelBox {
3
+ height: number;
4
+ left: number;
5
+ top: number;
6
+ width: number;
7
+ }
8
+ /**
9
+ * The hue at a point on a hue wheel: degrees clockwise from the top, 0 to
10
+ * 359, the way React Aria's ColorWheel and every paint program lay hue out.
11
+ *
12
+ * Measured from the box's center, so a press anywhere in the wheel answers,
13
+ * not only one on the ring: the ring is the target a reader aims at, and a
14
+ * press a little inside it still means the hue in that direction. Clockwise
15
+ * whatever the writing direction, since a circle has no line of text.
16
+ */
17
+ export declare const hueAt: (x: number, y: number, box: WheelBox) => number;
@@ -0,0 +1,6 @@
1
+ export const hueAt = (x, y, box) => {
2
+ const across = x - (box.left + box.width / 2);
3
+ const down = y - (box.top + box.height / 2);
4
+ const degrees = Math.round((Math.atan2(across, -down) * 180) / Math.PI);
5
+ return (degrees + 360) % 360;
6
+ };
@@ -94,7 +94,9 @@ declare const HelixHeadlessCombobox_base: (new (...args: any[]) => import("@fusi
94
94
  * @csspart tag-start - The category before the label.
95
95
  * @csspart tags - The row of chosen options, when `multiple` is set.
96
96
  *
97
+ * @cssstate error - While `load-error` is set.
97
98
  * @cssstate invalid - The control has an error to show, from the browser's validation or from your `feedback`.
99
+ * @cssstate no-results - What was typed hid every option of a list that has some.
98
100
  * @cssstate open - While the list is open.
99
101
  */
100
102
  export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
@@ -334,6 +336,17 @@ export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
334
336
  * is a thing a consumer may do.
335
337
  */
336
338
  private get showing();
339
+ /**
340
+ * The list's `error` and `no-results` states on the host too, so a page
341
+ * styles the whole control from them, as WA's combobox and its status slots
342
+ * allow. The list keeps its own, which only a stylesheet inside this shadow
343
+ * root could reach.
344
+ *
345
+ * Read once the list has drawn the filter, since `hidden` is what the
346
+ * filter writes onto an option: a filter that hid every option of a list
347
+ * that has some, with nothing loading.
348
+ */
349
+ private markStatus;
337
350
  /**
338
351
  * What the create row would add, or nothing where there is nothing to add.
339
352
  *
@@ -245,6 +245,15 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
245
245
  get showing() {
246
246
  return (this.listbox?.options ?? []).some((option) => !option.hidden);
247
247
  }
248
+ markStatus() {
249
+ const list = this.listbox;
250
+ this.toggleState('error', this.loadError !== '');
251
+ this.toggleState('no-results', list !== null &&
252
+ !this.loading &&
253
+ list.filter !== '' &&
254
+ list.options.length > 0 &&
255
+ !this.showing);
256
+ }
248
257
  get creation() {
249
258
  const typed = this.typed.trim();
250
259
  if (!this.creatable || typed === '') {
@@ -353,7 +362,10 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
353
362
  const narrowing = new HelixFilterEvent({ value: typed });
354
363
  if (this.dispatchEvent(narrowing)) {
355
364
  this.listbox?.requestFilter(typed);
356
- void this.listbox?.updateComplete.then(() => this.linkActive());
365
+ void this.listbox?.updateComplete.then(() => {
366
+ this.linkActive();
367
+ this.markStatus();
368
+ });
357
369
  }
358
370
  this.suggestion = this.suggest && inserted ? this.completionFor(typed) : '';
359
371
  }
@@ -434,6 +446,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
434
446
  super.updated?.(changed);
435
447
  this.sync();
436
448
  this.linkActive();
449
+ void (this.listbox?.updateComplete ?? Promise.resolve()).then(() => this.markStatus());
437
450
  const control = this.partNamed('control');
438
451
  const wanted = this.fieldText;
439
452
  if (control && control.value !== wanted) {
@@ -81,7 +81,9 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
81
81
  */
82
82
  filterDelay: number;
83
83
  /**
84
- * The key that opens the palette, written as `mod+k`, `shift+p`, or `k`.
84
+ * The key that opens the palette, written as `mod+k`, `shift+p`, or `k`,
85
+ * in Kbd's chord spelling: `mod` is Command or Control, `ctrl`, `cmd`,
86
+ * `alt` and `shift` the keys they name.
85
87
  *
86
88
  * Empty turns it off, for a consumer who opens the palette from a button or
87
89
  * from their own shortcut handler. There is no list of defaults to fight
@@ -4,21 +4,12 @@ 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, HelixSelectEvent, MixinControllerOpen, } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, HelixSelectEvent, MixinControllerOpen, matchesChord, } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { html } from 'lit/static-html.js';
11
11
  import '../../dialog/index.js';
12
12
  import '../../listbox/index.js';
13
- const parse = (hotkey) => {
14
- const parts = hotkey.toLowerCase().split('+');
15
- return {
16
- alt: parts.includes('alt'),
17
- key: parts.at(-1) ?? '',
18
- mod: parts.includes('mod'),
19
- shift: parts.includes('shift'),
20
- };
21
- };
22
13
  let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpen(HelixElement) {
23
14
  constructor() {
24
15
  super(...arguments);
@@ -34,15 +25,7 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
34
25
  this.resultsLabel = '{count} commands';
35
26
  this.footed = false;
36
27
  this.handleHotkey = (event) => {
37
- if (this.hotkey === '') {
38
- return;
39
- }
40
- const wanted = parse(this.hotkey);
41
- const mod = event.metaKey || event.ctrlKey;
42
- if (event.key.toLowerCase() !== wanted.key ||
43
- mod !== wanted.mod ||
44
- event.shiftKey !== wanted.shift ||
45
- event.altKey !== wanted.alt) {
28
+ if (!matchesChord(event, this.hotkey, true) && !matchesChord(event, this.hotkey, false)) {
46
29
  return;
47
30
  }
48
31
  event.preventDefault();
@@ -1,4 +1,4 @@
1
- import { type TemplateResult } from 'lit';
1
+ import { type PropertyValues, type TemplateResult } from 'lit';
2
2
  import { HelixHeadlessIconButton } from '../../icon-button/root/icon-button-root.js';
3
3
  import '../../tooltip/index.js';
4
4
  /**
@@ -26,6 +26,12 @@ import '../../tooltip/index.js';
26
26
  * rendered. An `aria-label` on the element names it before a copy, in place of
27
27
  * `label`.
28
28
  *
29
+ * **Keyboard.** Enter and Space copy, as they press a native button.
30
+ *
31
+ * The button is named `label`, or the host's `aria-label`, and its name
32
+ * changes to `copied-label` or `error-label` for the moment after a press,
33
+ * which is what a screen reader on it hears. The tooltip shows the same words.
34
+ *
29
35
  * @since 0.1.0
30
36
  * @status stable
31
37
  * @category actions
@@ -42,6 +48,7 @@ import '../../tooltip/index.js';
42
48
  * @csspart button - The inner button.
43
49
  * @csspart live - The live region that says `pending-label` when the button becomes pending under focus. It is rendered from the first time the button is pending.
44
50
  * @csspart mark - The clipboard icon, and the confirmation icon that replaces it.
51
+ * @csspart spinner - The ring a pending button turns, an empty element the styled button draws over the icon. It is rendered only while the button is pending.
45
52
  * @csspart tooltip - The tooltip bubble, forwarded from the tooltip inside the button.
46
53
  * @csspart tooltip-arrow - The tooltip's arrow, which points at the button.
47
54
  * @csspart tooltip-arrow-bottom - The arrow while the bubble is below the button, on the bubble's top edge. Set together with `tooltip-arrow`.
@@ -52,6 +59,13 @@ import '../../tooltip/index.js';
52
59
  * @csspart tooltip-left - The bubble while it is left of the button. Set together with `tooltip`.
53
60
  * @csspart tooltip-right - The bubble while it is right of the button. Set together with `tooltip`.
54
61
  * @csspart tooltip-top - The bubble while it is above the button. Set together with `tooltip`.
62
+ *
63
+ * @cssstate copied - The moment after a successful copy, while `copied-label` names the button.
64
+ * @cssstate failed - The moment after a press that put nothing on the clipboard, while `error-label` names the button.
65
+ *
66
+ * @withheld csspart text - Button's words, which this does not render: the label is the icon's name.
67
+ * @withheld slot end - Button's, which this does not render.
68
+ * @withheld slot start - Button's, which this does not render.
55
69
  */
56
70
  export declare class HelixHeadlessCopyButton extends HelixHeadlessIconButton {
57
71
  /**
@@ -135,6 +149,7 @@ export declare class HelixHeadlessCopyButton extends HelixHeadlessIconButton {
135
149
  * cover the confirmation.
136
150
  */
137
151
  protected get forwardsLabel(): boolean;
152
+ protected willUpdate(changed: PropertyValues): void;
138
153
  /** @internal A copy button never submits, so it is no form's default button. */
139
154
  protected get submitsForm(): boolean;
140
155
  /**
@@ -24,7 +24,7 @@ let HelixHeadlessCopyButton = HelixHeadlessCopyButton_1 = class HelixHeadlessCop
24
24
  this.label = 'Copy';
25
25
  this.value = '';
26
26
  this.namedBy = null;
27
- this.clipboard = new CopyController(this, Object.defineProperty({}, 'after', { get: () => this.feedbackDuration }));
27
+ this.clipboard = new CopyController(this, { after: () => this.feedbackDuration });
28
28
  this.handleCopy = async (event) => {
29
29
  if (this.pending) {
30
30
  event.preventDefault();
@@ -51,6 +51,11 @@ let HelixHeadlessCopyButton = HelixHeadlessCopyButton_1 = class HelixHeadlessCop
51
51
  get forwardsLabel() {
52
52
  return false;
53
53
  }
54
+ willUpdate(changed) {
55
+ super.willUpdate(changed);
56
+ this.toggleState('copied', this.copied);
57
+ this.toggleState('failed', this.failed);
58
+ }
54
59
  get submitsForm() {
55
60
  return false;
56
61
  }
@@ -83,7 +88,7 @@ let HelixHeadlessCopyButton = HelixHeadlessCopyButton_1 = class HelixHeadlessCop
83
88
  @keydown=${this.notePress}
84
89
  @pointerdown=${this.notePress}
85
90
  >
86
- ${this.renderCopiedIcon()} ${this.renderFailedIcon()} ${this.renderCopyIcon()}
91
+ ${this.renderCopiedIcon()} ${this.renderFailedIcon()} ${this.renderCopyIcon()}${this.renderSpinner()}
87
92
  </button>`)}${this.renderLive()}`;
88
93
  }
89
94
  renderCopyIcon() {
@@ -1,24 +1,6 @@
1
- /**
2
- * `keys` in the order the rows are, which is `order`: the keys of the whole
3
- * result. A key the result does not hold, a selected row a search has hidden
4
- * say, keeps its place after them in the order it was given.
5
- *
6
- * So `selected` and `expanded`, and the events that carry them, read in row
7
- * order whatever order the rows were chosen in.
8
- */
9
- export declare const inRowOrder: (keys: Iterable<string>, order: readonly string[]) => string[];
10
1
  /**
11
2
  * The `expanded` a toggle of row `key` leaves, in row order, or `undefined`
12
3
  * where the row is open or closed already. Keys are split on commas only, so a
13
4
  * key may hold a space.
14
5
  */
15
6
  export declare const toggledOpen: (expanded: string, key: string, open: boolean, order: readonly string[]) => string | undefined;
16
- /**
17
- * Where a Shift range runs from, as an index into the page's `keys`: the row
18
- * last chosen while it is on the page, and otherwise the row the reader is on,
19
- * `from`, which becomes the one to run from.
20
- */
21
- export declare const anchorAt: (keys: readonly string[], anchorKey: string | undefined, from: number) => {
22
- anchor: number;
23
- anchorKey: string | undefined;
24
- };
@@ -1,10 +1,4 @@
1
- import { commaList } from '@fusion.dev/helix-core';
2
- export const inRowOrder = (keys, order) => {
3
- const wanted = new Set(keys);
4
- const placed = order.filter((key) => wanted.has(key));
5
- const seen = new Set(placed);
6
- return [...placed, ...[...wanted].filter((key) => !seen.has(key))];
7
- };
1
+ import { commaList, inRowOrder } from '@fusion.dev/helix-core';
8
2
  export const toggledOpen = (expanded, key, open, order) => {
9
3
  const keys = new Set(commaList(expanded));
10
4
  if (keys.has(key) === open) {
@@ -18,7 +12,3 @@ export const toggledOpen = (expanded, key, open, order) => {
18
12
  }
19
13
  return inRowOrder(keys, order).join(',');
20
14
  };
21
- export const anchorAt = (keys, anchorKey, from) => {
22
- const anchor = anchorKey === undefined ? -1 : keys.indexOf(anchorKey);
23
- return anchor < 0 ? { anchor: from, anchorKey: keys[from] } : { anchor, anchorKey };
24
- };