@fusion.dev/helix-headless 0.154.0 → 0.156.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 (104) hide show
  1. package/custom-elements.json +3213 -179
  2. package/dist/accordion/item/accordion-item.d.ts +15 -0
  3. package/dist/accordion/item/accordion-item.js +12 -1
  4. package/dist/accordion/root/accordion-root.d.ts +2 -1
  5. package/dist/accordion/root/accordion-root.js +1 -0
  6. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +36 -3
  7. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +42 -2
  8. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +15 -5
  9. package/dist/breadcrumbs/root/breadcrumbs-root.js +29 -4
  10. package/dist/button-group/root/button-group-root.d.ts +2 -0
  11. package/dist/button-group/root/button-group-root.js +1 -0
  12. package/dist/canvas/node/canvas-node.js +1 -1
  13. package/dist/canvas/root/canvas-root.d.ts +17 -2
  14. package/dist/canvas/root/canvas-root.js +23 -6
  15. package/dist/chat/composer/chat-composer.js +1 -1
  16. package/dist/chat/root/chat-root.d.ts +14 -1
  17. package/dist/chat/root/chat-root.js +14 -4
  18. package/dist/color-input/root/color-input-root.d.ts +9 -3
  19. package/dist/color-input/root/color-input-root.js +3 -1
  20. package/dist/color-picker/channels.d.ts +5 -3
  21. package/dist/color-picker/channels.js +79 -21
  22. package/dist/color-picker/root/color-picker-root.d.ts +58 -5
  23. package/dist/color-picker/root/color-picker-root.js +75 -13
  24. package/dist/combobox/root/combobox-root.d.ts +10 -0
  25. package/dist/combobox/root/combobox-root.js +18 -3
  26. package/dist/command/root/command-root.d.ts +116 -1
  27. package/dist/command/root/command-root.js +124 -15
  28. package/dist/data-table/root/data-table-root.d.ts +25 -0
  29. package/dist/data-table/root/data-table-root.js +13 -3
  30. package/dist/date-input/root/date-input-root.d.ts +33 -1
  31. package/dist/date-input/root/date-input-root.js +9 -2
  32. package/dist/date-picker/root/date-picker-root.d.ts +43 -13
  33. package/dist/date-picker/root/date-picker-root.js +54 -31
  34. package/dist/element-events.generated.js +3 -1
  35. package/dist/graph/root/graph-root.d.ts +13 -0
  36. package/dist/graph/root/graph-root.js +16 -0
  37. package/dist/list/root/list-root.d.ts +74 -1
  38. package/dist/list/root/list-root.js +92 -4
  39. package/dist/list/selection.js +3 -3
  40. package/dist/map/root/map-root.d.ts +6 -0
  41. package/dist/map/root/map-root.js +8 -1
  42. package/dist/navigation/root/navigation-root.d.ts +26 -0
  43. package/dist/navigation/root/navigation-root.js +58 -1
  44. package/dist/number-input/root/number-input-root.d.ts +63 -6
  45. package/dist/number-input/root/number-input-root.js +96 -11
  46. package/dist/object-inspector/root/object-inspector-root.d.ts +36 -1
  47. package/dist/object-inspector/root/object-inspector-root.js +26 -2
  48. package/dist/option/root/option-root.d.ts +32 -0
  49. package/dist/option/root/option-root.js +44 -5
  50. package/dist/outline/root/outline-root.d.ts +6 -2
  51. package/dist/outline/root/outline-root.js +7 -3
  52. package/dist/select/root/select-root.d.ts +29 -3
  53. package/dist/select/root/select-root.js +34 -9
  54. package/dist/slider/root/slider-root.d.ts +71 -9
  55. package/dist/slider/root/slider-root.js +129 -42
  56. package/dist/speed-dial/item/speed-dial-item.d.ts +29 -1
  57. package/dist/speed-dial/item/speed-dial-item.js +48 -7
  58. package/dist/speed-dial/root/speed-dial-root.d.ts +32 -6
  59. package/dist/speed-dial/root/speed-dial-root.js +50 -5
  60. package/dist/splitter/root/splitter-root.d.ts +22 -0
  61. package/dist/splitter/root/splitter-root.js +36 -7
  62. package/dist/steps/reach.d.ts +5 -1
  63. package/dist/steps/reach.js +2 -2
  64. package/dist/steps/root/steps-root.d.ts +37 -2
  65. package/dist/steps/root/steps-root.js +49 -7
  66. package/dist/steps/step/step.d.ts +6 -0
  67. package/dist/steps/step/step.js +4 -0
  68. package/dist/table/root/table-root.d.ts +20 -0
  69. package/dist/table/root/table-root.js +13 -3
  70. package/dist/tabs/root/tabs-root.d.ts +37 -0
  71. package/dist/tabs/root/tabs-root.js +92 -2
  72. package/dist/tabs/tab/tab.d.ts +38 -2
  73. package/dist/tabs/tab/tab.js +45 -4
  74. package/dist/tag/group/tag-group.d.ts +29 -3
  75. package/dist/tag/group/tag-group.js +42 -11
  76. package/dist/time-input/root/time-input-root.d.ts +32 -1
  77. package/dist/time-input/root/time-input-root.js +9 -2
  78. package/dist/time-picker/root/time-picker-root.d.ts +63 -1
  79. package/dist/time-picker/root/time-picker-root.js +58 -7
  80. package/dist/timeline/item/timeline-item.d.ts +25 -0
  81. package/dist/timeline/item/timeline-item.js +27 -2
  82. package/dist/toggle/root/toggle-root.d.ts +2 -0
  83. package/dist/toggle/root/toggle-root.js +1 -1
  84. package/dist/tree/item/tree-item.d.ts +10 -0
  85. package/dist/tree/item/tree-item.js +12 -0
  86. package/dist/tree/root/tree-root.d.ts +40 -1
  87. package/dist/tree/root/tree-root.js +120 -4
  88. package/dist/tree/selection.d.ts +6 -0
  89. package/dist/tree/selection.js +11 -0
  90. package/dist/video/root/keys.d.ts +6 -2
  91. package/dist/video/root/keys.js +18 -1
  92. package/dist/video/root/video-root.d.ts +60 -1
  93. package/dist/video/root/video-root.js +70 -2
  94. package/dist/video-playlist/item/video-playlist-item.d.ts +15 -1
  95. package/dist/video-playlist/item/video-playlist-item.js +7 -0
  96. package/dist/video-playlist/root/video-playlist-root.d.ts +43 -4
  97. package/dist/video-playlist/root/video-playlist-root.js +69 -2
  98. package/dist/virtual-list/root/virtual-list-root.d.ts +8 -0
  99. package/dist/virtual-list/root/virtual-list-root.js +3 -0
  100. package/lint.json +1 -1
  101. package/package.json +5 -5
  102. package/types/jsx.d.ts +3885 -383
  103. package/types/vue.d.ts +1045 -107
  104. package/types/words.d.ts +146 -1
@@ -11,7 +11,7 @@ import { property, state } from 'lit/decorators.js';
11
11
  import { keyed } from 'lit/directives/keyed.js';
12
12
  import { repeat } from 'lit/directives/repeat.js';
13
13
  import { html } from 'lit/static-html.js';
14
- import { TURN_WORDS } from '../naming.js';
14
+ import { authorWords, TURN_WORDS } from '../naming.js';
15
15
  const REGIONS = ['header', 'actions', 'resume', 'status', 'composer'];
16
16
  const SECOND_NAMES = {
17
17
  author: {
@@ -40,6 +40,7 @@ const drawnInFull = ({ element }) => element.firstElementChild?.hasUpdated !== f
40
40
  let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixElement) {
41
41
  constructor() {
42
42
  super(...arguments);
43
+ this.arrivalLabel = '{author}: {text}';
43
44
  this.authorStyles = {};
44
45
  this.defaultPinned = true;
45
46
  this.failedLabel = 'Not sent';
@@ -180,6 +181,7 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
180
181
  if (this.fed) {
181
182
  return this.turns.map((turn) => ({
182
183
  author: turn.author,
184
+ by: authorWords(turn.author, turn.authorLabel ?? '', this.words),
183
185
  failed: this.failedLabel,
184
186
  id: this.identity(turn),
185
187
  state: turn.state,
@@ -188,6 +190,7 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
188
190
  }
189
191
  return this.messages.map((message) => ({
190
192
  author: message.author,
193
+ by: authorWords(message.author, message.authorLabel, this.words),
191
194
  failed: message.failedLabel,
192
195
  id: message,
193
196
  state: message.state,
@@ -349,9 +352,12 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
349
352
  return at < 0 ? [] : turns.slice(at + 1);
350
353
  }
351
354
  phrase(turn) {
352
- return turn.state === 'failed'
353
- ? formatWords(this, 'failedTurnLabel', { failed: turn.failed, text: turn.text })
354
- : turn.text;
355
+ if (turn.state === 'failed') {
356
+ return formatWords(this, 'failedTurnLabel', { failed: turn.failed, text: turn.text });
357
+ }
358
+ return turn.by === ''
359
+ ? turn.text
360
+ : formatWords(this, 'arrivalLabel', { author: turn.by, text: turn.text });
355
361
  }
356
362
  watchSizes() {
357
363
  if (typeof ResizeObserver === 'undefined') {
@@ -509,6 +515,10 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
509
515
  </div>`;
510
516
  }
511
517
  };
518
+ __decorate([
519
+ words,
520
+ property({ attribute: 'arrival-label' })
521
+ ], HelixHeadlessChat.prototype, "arrivalLabel", void 0);
512
522
  __decorate([
513
523
  property({ attribute: 'author-styles', converter: wordsConverter })
514
524
  ], HelixHeadlessChat.prototype, "authorStyles", void 0);
@@ -17,8 +17,9 @@ declare const HelixHeadlessColorInput_base: (new (...args: any[]) => import("@fu
17
17
  * style the panel to match your interface.
18
18
  *
19
19
  * The field reads every color syntax, including `#f00`, `#ff0000`,
20
- * `rgb(255 0 0)`, `rgba(255,0,0,.5)`, `rgb(255 0 0 / 50%)`, and
21
- * `hsl(0 100% 50%)`. It writes colors in the syntax that `format` names. The
20
+ * `rgb(255 0 0)`, `rgba(255,0,0,.5)`, `rgb(255 0 0 / 50%)`,
21
+ * `hsl(0 100% 50%)`, `oklch(62.8% 0.258 29.2)`, and `hsb(0, 100%, 100%)`. It
22
+ * writes colors in the syntax that `format` names. The
22
23
  * field shows the value without reformatting it, because half-typed input does
23
24
  * not survive a round trip through a formatter. Date Input does the same with
24
25
  * dates.
@@ -75,6 +76,8 @@ declare const HelixHeadlessColorInput_base: (new (...args: any[]) => import("@fu
75
76
  * @csspart picker-channel-marker-label - The value a mark stands at, when `marker-labels` is set.
76
77
  * @csspart picker-channel-markers - The row of marks along the track.
77
78
  * @csspart picker-channel-output - The current value, shown beside the track.
79
+ * @csspart picker-channel-thumb - A ramp's thumb box, which holds no content in a picker.
80
+ * @csspart picker-channel-thumbs - The layer over a ramp's track that holds its thumb box.
78
81
  * @csspart picker-channel-tooltip - A ramp's value in a bubble, where a ramp shows one.
79
82
  * @csspart picker-channel-tooltips - The layer over a ramp's track that holds its bubble.
80
83
  * @csspart picker-channels - The channel sliders beside the sample.
@@ -99,7 +102,9 @@ declare const HelixHeadlessColorInput_base: (new (...args: any[]) => import("@fu
99
102
  * @csspart picker-grid-brightness - The range input for the square's vertical axis.
100
103
  * @csspart picker-grid-handle - The mark showing where in the square the color sits.
101
104
  * @csspart picker-grid-saturation - The range input for the square's horizontal axis.
105
+ * @csspart picker-hint - The picker's own help, which the field's `hint` stands in for here, so it stays hidden.
102
106
  * @csspart picker-icon - An icon drawn by the picker.
107
+ * @csspart picker-label - The picker's own name, which the field's `label` stands in for here, so it stays hidden.
103
108
  * @csspart picker-sample - A chip of the current color. Pressing it copies the value.
104
109
  * @csspart picker-swatch - One preset.
105
110
  * @csspart picker-swatches - The row of presets.
@@ -150,7 +155,8 @@ export declare class HelixHeadlessColorInput extends HelixHeadlessColorInput_bas
150
155
  */
151
156
  colorWords: Partial<ColorWords>;
152
157
  /**
153
- * The syntax the value is written in: `hex`, `rgb`, or `hsl`.
158
+ * The syntax the value is written in: `hex`, `rgb`, `hsl`, `oklch`, or
159
+ * `hsb`, as Color Picker's `format`.
154
160
  *
155
161
  * **Undefaulted, because it reflects.** The two together stamp an attribute
156
162
  * onto every host anybody renders, including the ones nobody configured: the
@@ -126,6 +126,7 @@ let HelixHeadlessColorInput = class HelixHeadlessColorInput extends MixinPickerF
126
126
  channel-hint: picker-channel-hint, channel-label: picker-channel-label,
127
127
  channel-marker: picker-channel-marker, channel-marker-label: picker-channel-marker-label,
128
128
  channel-markers: picker-channel-markers, channel-output: picker-channel-output,
129
+ channel-thumb: picker-channel-thumb, channel-thumbs: picker-channel-thumbs,
129
130
  channel-tooltip: picker-channel-tooltip, channel-tooltips: picker-channel-tooltips,
130
131
  channels: picker-channels, controls: picker-controls, eyedropper: picker-eyedropper,
131
132
  eyedropper-button: picker-eyedropper-button, eyedropper-live: picker-eyedropper-live, eyedropper-spinner: picker-eyedropper-spinner, eyedropper-text: picker-eyedropper-text,
@@ -133,7 +134,8 @@ let HelixHeadlessColorInput = class HelixHeadlessColorInput extends MixinPickerF
133
134
  fields: picker-fields, footer: picker-footer, format: picker-format,
134
135
  format-button: picker-format-button, format-live: picker-format-live, format-spinner: picker-format-spinner, format-text: picker-format-text, grid: picker-grid,
135
136
  grid-brightness: picker-grid-brightness, grid-handle: picker-grid-handle,
136
- grid-saturation: picker-grid-saturation, icon: picker-icon, sample: picker-sample,
137
+ grid-saturation: picker-grid-saturation, hint: picker-hint, icon: picker-icon,
138
+ label: picker-label, sample: picker-sample,
137
139
  swatch: picker-swatch, swatches: picker-swatches, value: picker-value,
138
140
  wheel: picker-wheel, wheel-hue: picker-wheel-hue"
139
141
  ?invalid=${this.resolvedFeedback !== ''}
@@ -1,17 +1,19 @@
1
1
  import { type Color, type ColorFormat } from '@fusion.dev/helix-core';
2
2
  /** One number a color is written with, in the syntax the picker writes. */
3
- export type ChannelName = 'alpha' | 'blue' | 'green' | 'hue' | 'lightness' | 'red' | 'saturation';
4
- /** A channel as a field shows it: its number and the range it may take. */
3
+ export type ChannelName = 'alpha' | 'blue' | 'brightness' | 'chroma' | 'green' | 'hue' | 'lightness' | 'red' | 'saturation';
4
+ /** A channel as a field shows it: its number, the range it may take, and its step. */
5
5
  export interface ChannelField {
6
6
  channel: ChannelName;
7
7
  max: number;
8
8
  min: number;
9
+ step: number;
9
10
  value: number;
10
11
  }
11
12
  /**
12
13
  * The numbers a color is written with in `format`, for a field each, as React
13
14
  * Aria's ColorField takes a `channel`: red, green and blue for hex and rgb,
14
- * hue, saturation and lightness for hsl, and the opacity where the picker
15
+ * hue, saturation and lightness for hsl, hue, saturation and brightness for
16
+ * hsb, lightness, chroma and hue for oklch, and the opacity where the picker
15
17
  * carries one.
16
18
  *
17
19
  * Hex is written as bytes, so its fields are the bytes: a field of two hex
@@ -1,4 +1,16 @@
1
- import { clamp, toHsl, toRgb } from '@fusion.dev/helix-core';
1
+ import { clamp, toHsl, toOklch, toRgb } from '@fusion.dev/helix-core';
2
+ const onStep = (value, step) => {
3
+ const decimals = String(step).split('.')[1]?.length ?? 0;
4
+ return Number((Math.round(value / step) * step).toFixed(decimals));
5
+ };
6
+ const FAMILY = {
7
+ hex: /^rgba?$/,
8
+ hsb: /^hsba?$/,
9
+ hsl: /^hsla?$/,
10
+ oklch: /^oklch$/,
11
+ rgb: /^rgba?$/,
12
+ };
13
+ const OKLCH_STEPS = [0.1, 0.001, 0.1];
2
14
  const percent = (token) => {
3
15
  if (token === undefined) {
4
16
  return 100;
@@ -8,7 +20,8 @@ const percent = (token) => {
8
20
  };
9
21
  const readWritten = (value, format) => {
10
22
  const text = value.trim().toLowerCase();
11
- if (format !== 'hsl' && /^#([\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/.test(text)) {
23
+ if ((format === 'hex' || format === 'rgb') &&
24
+ /^#([\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/.test(text)) {
12
25
  const digits = text.slice(1);
13
26
  const width = digits.length <= 4 ? 1 : 2;
14
27
  const byte = (index) => {
@@ -18,23 +31,46 @@ const readWritten = (value, format) => {
18
31
  const count = digits.length / width;
19
32
  return [byte(0), byte(1), byte(2), count === 4 ? Math.round((byte(3) / 255) * 100) : 100];
20
33
  }
21
- const call = /^(rgba?|hsla?)\(([^)]*)\)$/.exec(text);
22
- if (!call?.[1] || call[2] === undefined) {
23
- return null;
24
- }
25
- if (call[1].startsWith('hsl') !== (format === 'hsl')) {
34
+ const call = /^(rgba?|hsla?|hsba?|oklch)\(([^)]*)\)$/.exec(text);
35
+ if (!call?.[1] || call[2] === undefined || !FAMILY[format].test(call[1])) {
26
36
  return null;
27
37
  }
28
38
  const [first, second, third, fourth] = call[2].replace(/[,/]/g, ' ').split(/\s+/).filter(Boolean);
29
- const numbers = [first, second, third].map((one) => Number.parseFloat(one ?? ''));
39
+ const numbers = [first, second, third].map((one) => one === 'none' ? 0 : Number.parseFloat(one ?? ''));
30
40
  if (numbers.some(Number.isNaN)) {
31
41
  return null;
32
42
  }
43
+ if (format === 'oklch') {
44
+ const [lightness = 0, chroma = 0, hue = 0] = numbers;
45
+ return [
46
+ onStep(first?.endsWith('%') ? lightness : lightness * 100, OKLCH_STEPS[0]),
47
+ onStep(second?.endsWith('%') ? (chroma / 100) * 0.4 : chroma, OKLCH_STEPS[1]),
48
+ onStep(hue, OKLCH_STEPS[2]),
49
+ Math.round(percent(fourth)),
50
+ ];
51
+ }
33
52
  return [...numbers, percent(fourth)].map((one) => Math.round(one));
34
53
  };
35
54
  export const channelFields = (value, color, format, alpha) => {
36
55
  const written = readWritten(value, format);
37
56
  const fromModel = () => {
57
+ if (format === 'hsb') {
58
+ return [
59
+ Math.round(color.hue),
60
+ Math.round(color.saturation),
61
+ Math.round(color.brightness),
62
+ Math.round(color.alpha),
63
+ ];
64
+ }
65
+ if (format === 'oklch') {
66
+ const { chroma, hue, lightness } = toOklch(color);
67
+ return [
68
+ onStep(lightness * 100, OKLCH_STEPS[0]),
69
+ onStep(chroma, OKLCH_STEPS[1]),
70
+ onStep(hue, OKLCH_STEPS[2]),
71
+ Math.round(color.alpha),
72
+ ];
73
+ }
38
74
  if (format === 'hsl') {
39
75
  const { hue, lightness, saturation } = toHsl(color);
40
76
  return [Math.round(hue), saturation, lightness, Math.round(color.alpha)];
@@ -43,18 +79,32 @@ export const channelFields = (value, color, format, alpha) => {
43
79
  return [red, green, blue, Math.round(color.alpha)];
44
80
  };
45
81
  const [one = 0, two = 0, three = 0, four = 100] = written ?? fromModel();
46
- const fields = format === 'hsl'
47
- ? [
48
- { channel: 'hue', max: 360, min: 0, value: one },
49
- { channel: 'saturation', max: 100, min: 0, value: two },
50
- { channel: 'lightness', max: 100, min: 0, value: three },
51
- ]
52
- : [
53
- { channel: 'red', max: 255, min: 0, value: one },
54
- { channel: 'green', max: 255, min: 0, value: two },
55
- { channel: 'blue', max: 255, min: 0, value: three },
56
- ];
57
- return alpha ? [...fields, { channel: 'alpha', max: 100, min: 0, value: four }] : fields;
82
+ const byFormat = {
83
+ hex: [],
84
+ hsb: [
85
+ { channel: 'hue', max: 360, min: 0, step: 1, value: one },
86
+ { channel: 'saturation', max: 100, min: 0, step: 1, value: two },
87
+ { channel: 'brightness', max: 100, min: 0, step: 1, value: three },
88
+ ],
89
+ hsl: [
90
+ { channel: 'hue', max: 360, min: 0, step: 1, value: one },
91
+ { channel: 'saturation', max: 100, min: 0, step: 1, value: two },
92
+ { channel: 'lightness', max: 100, min: 0, step: 1, value: three },
93
+ ],
94
+ oklch: [
95
+ { channel: 'lightness', max: 100, min: 0, step: OKLCH_STEPS[0], value: one },
96
+ { channel: 'chroma', max: 0.4, min: 0, step: OKLCH_STEPS[1], value: two },
97
+ { channel: 'hue', max: 360, min: 0, step: OKLCH_STEPS[2], value: three },
98
+ ],
99
+ rgb: [],
100
+ };
101
+ const bytes = [
102
+ { channel: 'red', max: 255, min: 0, step: 1, value: one },
103
+ { channel: 'green', max: 255, min: 0, step: 1, value: two },
104
+ { channel: 'blue', max: 255, min: 0, step: 1, value: three },
105
+ ];
106
+ const fields = byFormat[format].length > 0 ? byFormat[format] : bytes;
107
+ return alpha ? [...fields, { channel: 'alpha', max: 100, min: 0, step: 1, value: four }] : fields;
58
108
  };
59
109
  export const writeChannel = (fields, format, alpha, channel, value) => {
60
110
  if (Number.isNaN(value)) {
@@ -65,7 +115,7 @@ export const writeChannel = (fields, format, alpha, channel, value) => {
65
115
  if (field === undefined) {
66
116
  return null;
67
117
  }
68
- numbers.set(channel, clamp(Math.round(value), field.min, field.max));
118
+ numbers.set(channel, clamp(onStep(value, field.step), field.min, field.max));
69
119
  const get = (name, fallback = 0) => numbers.get(name) ?? fallback;
70
120
  const opacity = get('alpha', 100);
71
121
  const fraction = Number((opacity / 100).toFixed(2));
@@ -73,6 +123,14 @@ export const writeChannel = (fields, format, alpha, channel, value) => {
73
123
  const body = `${get('hue')}, ${get('saturation')}%, ${get('lightness')}%`;
74
124
  return alpha ? `hsla(${body}, ${fraction})` : `hsl(${body})`;
75
125
  }
126
+ if (format === 'hsb') {
127
+ const body = `${get('hue')}, ${get('saturation')}%, ${get('brightness')}%`;
128
+ return alpha ? `hsba(${body}, ${fraction})` : `hsb(${body})`;
129
+ }
130
+ if (format === 'oklch') {
131
+ const body = `${get('lightness')}% ${get('chroma')} ${get('hue')}`;
132
+ return alpha ? `oklch(${body} / ${fraction})` : `oklch(${body})`;
133
+ }
76
134
  if (format === 'rgb') {
77
135
  const body = `${get('red')}, ${get('green')}, ${get('blue')}`;
78
136
  return alpha ? `rgba(${body}, ${fraction})` : `rgb(${body})`;
@@ -32,6 +32,11 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
32
32
  * is named by its color. The opacity ramp is heard as a percentage. Set
33
33
  * `color-words` to speak the names in another language.
34
34
  *
35
+ * `label` names the picker, which is then a group with that name, and the
36
+ * square's ramps say it before their own, as "Brand, Saturation". `hint`
37
+ * writes help under the picker, which the square, the ramps and the value
38
+ * field are described by. Inside Color Input the field carries both.
39
+ *
35
40
  * The presets are a listbox with the current color selected, and one tab
36
41
  * stop. The arrow keys move between them, Home and End go to the ends, and
37
42
  * Enter, Space or a press chooses one.
@@ -43,6 +48,8 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
43
48
  * @documentation https://fusion.dev/helix/components/color-picker
44
49
  *
45
50
  * @slot eyedropper-icon - The icon on the eyedropper button, usually a dropper.
51
+ * @slot hint - Help under the picker, for content richer than the `hint` text.
52
+ * @slot label - The name above the picker, for content richer than the `label` text.
46
53
  *
47
54
  * @event {Event} change - Fired after the value changes.
48
55
  * @event {HelixBeforeChangeEvent} helix-before-change - Fired before the color changes. Cancelable: call `preventDefault()` to keep the current color.
@@ -62,6 +69,8 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
62
69
  * @csspart channel-marker-label - The value a mark stands at, when `marker-labels` is set.
63
70
  * @csspart channel-markers - The row of marks along the track.
64
71
  * @csspart channel-output - The current value, shown beside the track.
72
+ * @csspart channel-thumb - A ramp's thumb box, which holds no content in a picker.
73
+ * @csspart channel-thumbs - The layer over a ramp's track that holds its thumb box.
65
74
  * @csspart channel-tooltip - A ramp's value in a bubble, where a ramp shows one.
66
75
  * @csspart channel-tooltips - The layer over a ramp's track that holds its bubble.
67
76
  * @csspart channels - The ramps beside the sample.
@@ -88,6 +97,8 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
88
97
  * `--color-picker-brightness`, and this tier ships no CSS.
89
98
  * @csspart grid-brightness - The range input for the square's vertical axis.
90
99
  * @csspart grid-saturation - The range input for the square's horizontal axis.
100
+ * @csspart hint - The help under the picker, while `hint` is set or the `hint` slot has content.
101
+ * @csspart label - The name above the picker, while `label` is set or the `label` slot has content.
91
102
  * @csspart sample - A chip of the current color. Pressing it copies the value.
92
103
  * @csspart swatch - One preset, an option in the `swatches` listbox.
93
104
  * @csspart swatches - The row of presets, a listbox.
@@ -100,7 +111,10 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
100
111
  * @cssproperty --color-picker-saturation - The chosen saturation, 0 to 100, as a percentage.
101
112
  * @cssproperty --color-picker-swatch - The color a chip shows: the current color on the sample, and the preset's color on each preset.
102
113
  *
114
+ * @cssstate copied - The moment after the sample copies the value, while it says `copied-label`.
115
+ * @cssstate dragging - While a pointer drags the square's handle.
103
116
  * @cssstate invalid - While `invalid` is set or `feedback` says what is wrong.
117
+ * @cssstate sampling - While the eyedropper is open over the screen.
104
118
  */
105
119
  export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_base {
106
120
  /**
@@ -142,14 +156,20 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
142
156
  /**
143
157
  * Whether the picker shows a number field for each channel of the color, as
144
158
  * React Aria's ColorField does with `channel`: red, green and blue for hex
145
- * and rgb, hue, saturation and lightness for hsl, and the opacity with
146
- * `alpha`.
159
+ * and rgb, hue, saturation and lightness for hsl, hue, saturation and
160
+ * brightness for hsb, lightness, chroma and hue for oklch, and the opacity
161
+ * with `alpha`.
147
162
  *
148
163
  * For a reader who has the numbers from a brand guide, and for a keyboard,
149
164
  * where typing 128 is quicker than holding an arrow key. The fields follow
150
165
  * the syntax the format toggle sets.
151
166
  */
152
167
  channelFields: boolean;
168
+ /**
169
+ * What the chroma channel's field is called, in `channel-fields` with
170
+ * oklch.
171
+ */
172
+ chromaLabel: string;
153
173
  /**
154
174
  * The words a color's name is spoken in, as JSON keyed by their English,
155
175
  * such as `{"red": "rot", "vibrant": "kräftiges"}`.
@@ -179,10 +199,16 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
179
199
  */
180
200
  eyedropperLabel: string;
181
201
  /**
182
- * The syntax the value is written in: `hex`, `rgb`, or `hsl`.
202
+ * The syntax the value is written in: `hex`, `rgb`, `hsl`, `oklch`, or
203
+ * `hsb`.
183
204
  *
184
205
  * Only what is written. Everything is read whatever this says, so a picker on
185
- * `hex` still takes an `rgb()` handed to it and stores the hex for it.
206
+ * `hex` still takes an `rgb()` or an `oklch()` handed to it and stores the
207
+ * hex for it. `oklch` writes the sRGB color the picker holds, such as
208
+ * `oklch(62.8% 0.258 29.2)`; a color outside sRGB that is handed in is
209
+ * clipped to it. `hsb` writes the picker's own model, `hsb(0, 100%, 100%)`,
210
+ * as React Aria's and Zag's color strings do. It is not CSS, so the toggle
211
+ * walks the other four.
186
212
  */
187
213
  format?: ColorFormat;
188
214
  /**
@@ -193,13 +219,23 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
193
219
  * What the green channel's field is called, in `channel-fields`.
194
220
  */
195
221
  greenLabel: string;
222
+ /**
223
+ * Help under the picker, such as what the color is used for. The `hint`
224
+ * slot takes richer content.
225
+ */
226
+ hint: string;
196
227
  /**
197
228
  * What the hue channel is called.
198
229
  */
199
230
  hueLabel: string;
231
+ /**
232
+ * The name above the picker, such as "Brand color". The picker is then a
233
+ * group with this name. The `label` slot takes richer content.
234
+ */
235
+ label: string;
200
236
  /**
201
237
  * What the lightness channel's field is called, in `channel-fields` with
202
- * hsl.
238
+ * hsl and oklch.
203
239
  */
204
240
  lightnessLabel: string;
205
241
  /**
@@ -353,6 +389,23 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
353
389
  private sampling;
354
390
  /** The preset the reader was last on, which holds the row's one tab stop. */
355
391
  private swatchAt;
392
+ /**
393
+ * The children in a slot, read off the children rather than heard from
394
+ * `slotchange`: a declarative shadow root fires that before anything
395
+ * listens, so a served picker kept its label hidden.
396
+ */
397
+ private slottedIn;
398
+ private readonly handleNamingSlot;
399
+ /** The picker's own name, from `label` or the `label` slot's words. */
400
+ private get named();
401
+ /** Whether help is shown under the picker. */
402
+ private get hinted();
403
+ /** The ids a control in the picker is described by: the help, then the message. */
404
+ private get describedBy();
405
+ /** Whether a pointer is dragging the square's handle, said as `:state(dragging)`. */
406
+ private dragging;
407
+ /** Whether the eyedropper is open, said as `:state(sampling)`. */
408
+ private picking;
356
409
  /**
357
410
  * Whether the value on this element was produced here.
358
411
  *