@zag-js/color-picker 1.41.0 → 2.0.0-next.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 (47) hide show
  1. package/dist/color-picker.anatomy.d.mts +2 -2
  2. package/dist/color-picker.anatomy.d.ts +2 -2
  3. package/dist/color-picker.anatomy.js +1 -0
  4. package/dist/color-picker.anatomy.mjs +1 -0
  5. package/dist/color-picker.connect.d.mts +1 -0
  6. package/dist/color-picker.connect.d.ts +1 -0
  7. package/dist/color-picker.connect.js +78 -42
  8. package/dist/color-picker.connect.mjs +79 -43
  9. package/dist/color-picker.dom.js +23 -22
  10. package/dist/color-picker.dom.mjs +23 -22
  11. package/dist/color-picker.machine.d.mts +1 -0
  12. package/dist/color-picker.machine.d.ts +1 -0
  13. package/dist/color-picker.machine.js +40 -17
  14. package/dist/color-picker.machine.mjs +40 -17
  15. package/dist/color-picker.props.d.mts +1 -0
  16. package/dist/color-picker.props.d.ts +1 -0
  17. package/dist/color-picker.types.d.mts +32 -1
  18. package/dist/color-picker.types.d.ts +32 -1
  19. package/dist/index.d.mts +2 -1
  20. package/dist/index.d.ts +2 -1
  21. package/dist/utils/get-area-format.d.mts +6 -0
  22. package/dist/utils/get-area-format.d.ts +6 -0
  23. package/dist/utils/get-area-format.js +37 -0
  24. package/dist/utils/get-area-format.mjs +12 -0
  25. package/dist/utils/get-channel-display-color.js +7 -0
  26. package/dist/utils/get-channel-display-color.mjs +7 -0
  27. package/dist/utils/get-channel-input-value.d.mts +1 -0
  28. package/dist/utils/get-channel-input-value.d.ts +1 -0
  29. package/dist/utils/get-channel-input-value.js +39 -8
  30. package/dist/utils/get-channel-input-value.mjs +39 -8
  31. package/dist/utils/get-gamut-overlay-path.d.mts +33 -0
  32. package/dist/utils/get-gamut-overlay-path.d.ts +33 -0
  33. package/dist/utils/get-gamut-overlay-path.js +81 -0
  34. package/dist/utils/get-gamut-overlay-path.mjs +55 -0
  35. package/dist/utils/get-gamut-overlay-path.test.d.mts +2 -0
  36. package/dist/utils/get-gamut-overlay-path.test.d.ts +2 -0
  37. package/dist/utils/get-gamut-overlay-path.test.js +35 -0
  38. package/dist/utils/get-gamut-overlay-path.test.mjs +33 -0
  39. package/dist/utils/get-slider-background.d.mts +1 -0
  40. package/dist/utils/get-slider-background.d.ts +1 -0
  41. package/dist/utils/get-slider-background.js +16 -1
  42. package/dist/utils/get-slider-background.mjs +16 -1
  43. package/dist/utils/is-srgb-gamut.d.mts +6 -0
  44. package/dist/utils/is-srgb-gamut.d.ts +6 -0
  45. package/dist/utils/is-srgb-gamut.js +33 -0
  46. package/dist/utils/is-srgb-gamut.mjs +8 -0
  47. package/package.json +9 -9
@@ -1,5 +1,5 @@
1
1
  // src/color-picker.connect.ts
2
- import { getColorAreaGradient, normalizeColor } from "@zag-js/color-utils";
2
+ import { getColorAreaGradient, isInSrgbGamut, normalizeColor } from "@zag-js/color-utils";
3
3
  import { getEventKey, getEventPoint, getEventStep, isLeftClick, isModifierKey } from "@zag-js/dom-query";
4
4
  import { dataAttr, query, visuallyHiddenStyle } from "@zag-js/dom-query";
5
5
  import { getPlacementSide, getPlacementStyles } from "@zag-js/popper";
@@ -7,12 +7,14 @@ import { parts } from "./color-picker.anatomy.mjs";
7
7
  import * as dom from "./color-picker.dom.mjs";
8
8
  import { getChannelDisplayColor } from "./utils/get-channel-display-color.mjs";
9
9
  import { getChannelRange, getChannelValue } from "./utils/get-channel-input-value.mjs";
10
+ import { getGamutOverlayData } from "./utils/get-gamut-overlay-path.mjs";
10
11
  import { getSliderBackground } from "./utils/get-slider-background.mjs";
11
12
  function connect(service, normalize) {
12
13
  const { context, send, prop, computed, state, scope } = service;
13
14
  const value = context.get("value");
14
15
  const format = context.get("format");
15
16
  const areaValue = computed("areaValue");
17
+ const stableAreaValue = context.get("areaBaseColor") ?? areaValue;
16
18
  const valueAsString = computed("valueAsString");
17
19
  const disabled = computed("disabled");
18
20
  const readOnly = !!prop("readOnly");
@@ -29,11 +31,21 @@ function connect(service, normalize) {
29
31
  yChannel: props.yChannel ?? channels[2]
30
32
  };
31
33
  };
34
+ function getDefaultDevicePixelRatio() {
35
+ if (typeof globalThis === "undefined") return 1;
36
+ const dpr = globalThis.devicePixelRatio;
37
+ return typeof dpr === "number" && Number.isFinite(dpr) ? dpr : 1;
38
+ }
39
+ function resolveGamutOverlay(props = {}) {
40
+ const pixelRatio = props.pixelRatio ?? getDefaultDevicePixelRatio();
41
+ return getGamutOverlayData(stableAreaValue, getAreaChannels(props), format, { pixelRatio });
42
+ }
32
43
  const currentPlacement = context.get("currentPlacement");
33
44
  const currentPlacementSide = currentPlacement ? getPlacementSide(currentPlacement) : void 0;
34
45
  const popperStyles = getPlacementStyles({
35
46
  ...prop("positioning"),
36
- placement: currentPlacement
47
+ placement: currentPlacement,
48
+ positioned: context.get("positioned")
37
49
  });
38
50
  function getSwatchTriggerState(props) {
39
51
  const color = normalizeColor(props.value).toFormat(context.get("format"));
@@ -49,6 +61,10 @@ function connect(service, normalize) {
49
61
  open,
50
62
  valueAsString,
51
63
  value,
64
+ isInSrgbGamut: isInSrgbGamut(value),
65
+ getGamutOverlay(props = {}) {
66
+ return resolveGamutOverlay(props);
67
+ },
52
68
  inline: !!prop("inline"),
53
69
  setOpen(nextOpen) {
54
70
  if (prop("inline")) return;
@@ -70,9 +86,8 @@ function connect(service, normalize) {
70
86
  send({ type: "VALUE.SET", value: color, src: "set-channel" });
71
87
  },
72
88
  format: context.get("format"),
73
- setFormat(format2) {
74
- const formatValue = value.toFormat(format2);
75
- send({ type: "VALUE.SET", value: formatValue, src: "set-format" });
89
+ setFormat(nextFormat) {
90
+ send({ type: "FORMAT.SET", format: nextFormat, src: "set-format" });
76
91
  },
77
92
  alpha: value.getChannelValue("alpha"),
78
93
  setAlpha(alphaValue) {
@@ -81,9 +96,8 @@ function connect(service, normalize) {
81
96
  },
82
97
  getRootProps() {
83
98
  return normalize.element({
84
- ...parts.root.attrs,
99
+ ...parts.root.attrs(scope.id),
85
100
  dir: prop("dir"),
86
- id: dom.getRootId(scope),
87
101
  "data-disabled": dataAttr(disabled),
88
102
  "data-readonly": dataAttr(readOnly),
89
103
  "data-invalid": dataAttr(invalid),
@@ -94,7 +108,7 @@ function connect(service, normalize) {
94
108
  },
95
109
  getLabelProps() {
96
110
  return normalize.element({
97
- ...parts.label.attrs,
111
+ ...parts.label.attrs(scope.id),
98
112
  dir: prop("dir"),
99
113
  id: dom.getLabelId(scope),
100
114
  htmlFor: dom.getHiddenInputId(scope),
@@ -112,8 +126,7 @@ function connect(service, normalize) {
112
126
  },
113
127
  getControlProps() {
114
128
  return normalize.element({
115
- ...parts.control.attrs,
116
- id: dom.getControlId(scope),
129
+ ...parts.control.attrs(scope.id),
117
130
  dir: prop("dir"),
118
131
  "data-disabled": dataAttr(disabled),
119
132
  "data-readonly": dataAttr(readOnly),
@@ -124,7 +137,7 @@ function connect(service, normalize) {
124
137
  },
125
138
  getTriggerProps() {
126
139
  return normalize.button({
127
- ...parts.trigger.attrs,
140
+ ...parts.trigger.attrs(scope.id),
128
141
  id: dom.getTriggerId(scope),
129
142
  dir: prop("dir"),
130
143
  disabled,
@@ -156,15 +169,14 @@ function connect(service, normalize) {
156
169
  },
157
170
  getPositionerProps() {
158
171
  return normalize.element({
159
- ...parts.positioner.attrs,
160
- id: dom.getPositionerId(scope),
172
+ ...parts.positioner.attrs(scope.id),
161
173
  dir: prop("dir"),
162
174
  style: popperStyles.floating
163
175
  });
164
176
  },
165
177
  getContentProps() {
166
178
  return normalize.element({
167
- ...parts.content.attrs,
179
+ ...parts.content.attrs(scope.id),
168
180
  id: dom.getContentId(scope),
169
181
  dir: prop("dir"),
170
182
  role: prop("inline") ? void 0 : "dialog",
@@ -177,7 +189,7 @@ function connect(service, normalize) {
177
189
  },
178
190
  getValueTextProps() {
179
191
  return normalize.element({
180
- ...parts.valueText.attrs,
192
+ ...parts.valueText.attrs(scope.id),
181
193
  dir: prop("dir"),
182
194
  "data-disabled": dataAttr(disabled),
183
195
  "data-focus": dataAttr(focused)
@@ -185,14 +197,13 @@ function connect(service, normalize) {
185
197
  },
186
198
  getAreaProps(props = {}) {
187
199
  const { xChannel, yChannel } = getAreaChannels(props);
188
- const { areaStyles } = getColorAreaGradient(areaValue, {
200
+ const { areaStyles } = getColorAreaGradient(stableAreaValue, {
189
201
  xChannel,
190
202
  yChannel,
191
203
  dir: prop("dir")
192
204
  });
193
205
  return normalize.element({
194
- ...parts.area.attrs,
195
- id: dom.getAreaId(scope),
206
+ ...parts.area.attrs(scope.id),
196
207
  role: "group",
197
208
  "data-invalid": dataAttr(invalid),
198
209
  "data-disabled": dataAttr(disabled),
@@ -216,14 +227,13 @@ function connect(service, normalize) {
216
227
  },
217
228
  getAreaBackgroundProps(props = {}) {
218
229
  const { xChannel, yChannel } = getAreaChannels(props);
219
- const { areaGradientStyles } = getColorAreaGradient(areaValue, {
230
+ const { areaGradientStyles } = getColorAreaGradient(stableAreaValue, {
220
231
  xChannel,
221
232
  yChannel,
222
233
  dir: prop("dir")
223
234
  });
224
235
  return normalize.element({
225
- ...parts.areaBackground.attrs,
226
- id: dom.getAreaGradientId(scope),
236
+ ...parts.areaBackground.attrs(scope.id),
227
237
  "data-invalid": dataAttr(invalid),
228
238
  "data-disabled": dataAttr(disabled),
229
239
  "data-readonly": dataAttr(readOnly),
@@ -235,18 +245,45 @@ function connect(service, normalize) {
235
245
  }
236
246
  });
237
247
  },
248
+ getGamutOverlayProps(props = {}) {
249
+ const data = resolveGamutOverlay(props);
250
+ return normalize.element({
251
+ ...parts.gamutOverlay.attrs(scope.id),
252
+ viewBox: "0 0 100 100",
253
+ preserveAspectRatio: "none",
254
+ xmlns: "http://www.w3.org/2000/svg",
255
+ "aria-hidden": true,
256
+ "data-state": data ? "visible" : "hidden",
257
+ style: {
258
+ position: "absolute",
259
+ inset: "0",
260
+ width: "100%",
261
+ height: "100%",
262
+ overflow: "hidden",
263
+ pointerEvents: "none"
264
+ }
265
+ });
266
+ },
238
267
  getAreaThumbProps(props = {}) {
239
268
  const { xChannel, yChannel } = getAreaChannels(props);
240
269
  const channel = { xChannel, yChannel };
241
- const xPercent = areaValue.getChannelValuePercent(xChannel);
242
- const yPercent = 1 - areaValue.getChannelValuePercent(yChannel);
270
+ const dragPos = context.get("areaDragPosition");
243
271
  const isRtl = prop("dir") === "rtl";
272
+ let xPercent;
273
+ let yPercent;
274
+ if (dragPos) {
275
+ xPercent = dragPos.x;
276
+ yPercent = dragPos.y;
277
+ } else {
278
+ xPercent = areaValue.getChannelValuePercent(xChannel);
279
+ yPercent = 1 - areaValue.getChannelValuePercent(yChannel);
280
+ }
244
281
  const finalXPercent = isRtl ? 1 - xPercent : xPercent;
245
282
  const xValue = areaValue.getChannelValue(xChannel);
246
283
  const yValue = areaValue.getChannelValue(yChannel);
247
284
  const color = areaValue.withChannelValue("alpha", 1).toString("css");
248
285
  return normalize.element({
249
- ...parts.areaThumb.attrs,
286
+ ...parts.areaThumb.attrs(scope.id),
250
287
  id: dom.getAreaThumbId(scope),
251
288
  dir: prop("dir"),
252
289
  tabIndex: disabled ? void 0 : 0,
@@ -314,7 +351,7 @@ function connect(service, normalize) {
314
351
  getTransparencyGridProps(props = {}) {
315
352
  const { size = "12px" } = props;
316
353
  return normalize.element({
317
- ...parts.transparencyGrid.attrs,
354
+ ...parts.transparencyGrid.attrs(scope.id),
318
355
  style: {
319
356
  "--size": size,
320
357
  width: "100%",
@@ -332,7 +369,7 @@ function connect(service, normalize) {
332
369
  getChannelSliderProps(props) {
333
370
  const { orientation = "horizontal", channel, format: format2 } = props;
334
371
  return normalize.element({
335
- ...parts.channelSlider.attrs,
372
+ ...parts.channelSlider.attrs(scope.id),
336
373
  "data-channel": channel,
337
374
  "data-orientation": orientation,
338
375
  role: "presentation",
@@ -352,10 +389,9 @@ function connect(service, normalize) {
352
389
  },
353
390
  getChannelSliderTrackProps(props) {
354
391
  const { orientation = "horizontal", channel, format: format2 } = props;
355
- const normalizedValue = format2 ? value.toFormat(format2) : areaValue;
392
+ const normalizedValue = format2 ? value.toFormat(format2) : stableAreaValue;
356
393
  return normalize.element({
357
- ...parts.channelSliderTrack.attrs,
358
- id: dom.getChannelSliderTrackId(scope, channel),
394
+ ...parts.channelSliderTrack.attrs(scope.id),
359
395
  role: "group",
360
396
  "data-channel": channel,
361
397
  "data-orientation": orientation,
@@ -374,7 +410,7 @@ function connect(service, normalize) {
374
410
  getChannelSliderLabelProps(props) {
375
411
  const { channel } = props;
376
412
  return normalize.element({
377
- ...parts.channelSliderLabel.attrs,
413
+ ...parts.channelSliderLabel.attrs(scope.id),
378
414
  "data-channel": channel,
379
415
  onClick(event) {
380
416
  if (!interactive) return;
@@ -390,13 +426,13 @@ function connect(service, normalize) {
390
426
  },
391
427
  getChannelSliderValueTextProps(props) {
392
428
  return normalize.element({
393
- ...parts.channelSliderValueText.attrs,
429
+ ...parts.channelSliderValueText.attrs(scope.id),
394
430
  "data-channel": props.channel
395
431
  });
396
432
  },
397
433
  getChannelSliderThumbProps(props) {
398
434
  const { orientation = "horizontal", channel, format: format2 } = props;
399
- const normalizedValue = format2 ? value.toFormat(format2) : areaValue;
435
+ const normalizedValue = format2 ? value.toFormat(format2) : stableAreaValue;
400
436
  const channelRange = normalizedValue.getChannelRange(channel);
401
437
  const channelValue = normalizedValue.getChannelValue(channel);
402
438
  const offset = (channelValue - channelRange.minValue) / (channelRange.maxValue - channelRange.minValue);
@@ -404,7 +440,7 @@ function connect(service, normalize) {
404
440
  const finalOffset = orientation === "horizontal" && isRtl ? 1 - offset : offset;
405
441
  const placementStyles = orientation === "horizontal" ? { left: `${finalOffset * 100}%`, top: "50%" } : { top: `${offset * 100}%`, left: "50%" };
406
442
  return normalize.element({
407
- ...parts.channelSliderThumb.attrs,
443
+ ...parts.channelSliderThumb.attrs(scope.id),
408
444
  id: dom.getChannelSliderThumbId(scope, channel),
409
445
  role: "slider",
410
446
  "aria-label": channel,
@@ -421,7 +457,7 @@ function connect(service, normalize) {
421
457
  style: {
422
458
  forcedColorAdjust: "none",
423
459
  position: "absolute",
424
- background: getChannelDisplayColor(areaValue, channel).toString("css"),
460
+ background: getChannelDisplayColor(stableAreaValue, channel).toString("css"),
425
461
  ...placementStyles
426
462
  },
427
463
  onFocus() {
@@ -476,7 +512,7 @@ function connect(service, normalize) {
476
512
  const isTextField = channel === "hex" || channel === "css";
477
513
  const channelRange = getChannelRange(value, channel);
478
514
  return normalize.input({
479
- ...parts.channelInput.attrs,
515
+ ...parts.channelInput.attrs(scope.id),
480
516
  dir: prop("dir"),
481
517
  type: isTextField ? "text" : "number",
482
518
  "data-channel": channel,
@@ -540,7 +576,7 @@ function connect(service, normalize) {
540
576
  },
541
577
  getEyeDropperTriggerProps() {
542
578
  return normalize.button({
543
- ...parts.eyeDropperTrigger.attrs,
579
+ ...parts.eyeDropperTrigger.attrs(scope.id),
544
580
  type: "button",
545
581
  dir: prop("dir"),
546
582
  disabled,
@@ -556,7 +592,7 @@ function connect(service, normalize) {
556
592
  },
557
593
  getSwatchGroupProps() {
558
594
  return normalize.element({
559
- ...parts.swatchGroup.attrs,
595
+ ...parts.swatchGroup.attrs(scope.id),
560
596
  role: "group"
561
597
  });
562
598
  },
@@ -564,7 +600,7 @@ function connect(service, normalize) {
564
600
  getSwatchTriggerProps(props) {
565
601
  const swatchState = getSwatchTriggerState(props);
566
602
  return normalize.button({
567
- ...parts.swatchTrigger.attrs,
603
+ ...parts.swatchTrigger.attrs(scope.id),
568
604
  disabled: swatchState.disabled,
569
605
  dir: prop("dir"),
570
606
  type: "button",
@@ -585,7 +621,7 @@ function connect(service, normalize) {
585
621
  getSwatchIndicatorProps(props) {
586
622
  const swatchState = getSwatchTriggerState(props);
587
623
  return normalize.element({
588
- ...parts.swatchIndicator.attrs,
624
+ ...parts.swatchIndicator.attrs(scope.id),
589
625
  dir: prop("dir"),
590
626
  hidden: !swatchState.checked
591
627
  });
@@ -595,7 +631,7 @@ function connect(service, normalize) {
595
631
  const swatchState = getSwatchTriggerState(props);
596
632
  const color = swatchState.value.toString(respectAlpha ? "css" : "hex");
597
633
  return normalize.element({
598
- ...parts.swatch.attrs,
634
+ ...parts.swatch.attrs(scope.id),
599
635
  dir: prop("dir"),
600
636
  "data-state": swatchState.checked ? "checked" : "unchecked",
601
637
  "data-value": swatchState.valueAsString,
@@ -608,7 +644,7 @@ function connect(service, normalize) {
608
644
  },
609
645
  getFormatTriggerProps() {
610
646
  return normalize.button({
611
- ...parts.formatTrigger.attrs,
647
+ ...parts.formatTrigger.attrs(scope.id),
612
648
  dir: prop("dir"),
613
649
  type: "button",
614
650
  "aria-label": `change color format to ${getNextFormat(format)}`,
@@ -621,7 +657,7 @@ function connect(service, normalize) {
621
657
  },
622
658
  getFormatSelectProps() {
623
659
  return normalize.select({
624
- ...parts.formatSelect.attrs,
660
+ ...parts.formatSelect.attrs(scope.id),
625
661
  "aria-label": "change color format",
626
662
  dir: prop("dir"),
627
663
  defaultValue: prop("format"),
@@ -634,7 +670,7 @@ function connect(service, normalize) {
634
670
  }
635
671
  };
636
672
  }
637
- var formats = ["hsba", "hsla", "rgba"];
673
+ var formats = ["hsba", "hsla", "rgba", "oklab", "oklch"];
638
674
  var formatRegex = new RegExp(`^(${formats.join("|")})$`);
639
675
  function getNextFormat(format) {
640
676
  const index = formats.indexOf(format);
@@ -50,22 +50,23 @@ __export(color_picker_dom_exports, {
50
50
  });
51
51
  module.exports = __toCommonJS(color_picker_dom_exports);
52
52
  var import_dom_query = require("@zag-js/dom-query");
53
- var getRootId = (ctx) => ctx.ids?.root ?? `color-picker:${ctx.id}`;
54
- var getLabelId = (ctx) => ctx.ids?.label ?? `color-picker:${ctx.id}:label`;
55
- var getHiddenInputId = (ctx) => ctx.ids?.hiddenInput ?? `color-picker:${ctx.id}:hidden-input`;
56
- var getControlId = (ctx) => ctx.ids?.control ?? `color-picker:${ctx.id}:control`;
57
- var getTriggerId = (ctx) => ctx.ids?.trigger ?? `color-picker:${ctx.id}:trigger`;
58
- var getContentId = (ctx) => ctx.ids?.content ?? `color-picker:${ctx.id}:content`;
59
- var getPositionerId = (ctx) => ctx.ids?.positioner ?? `color-picker:${ctx.id}:positioner`;
60
- var getFormatSelectId = (ctx) => ctx.ids?.formatSelect ?? `color-picker:${ctx.id}:format-select`;
61
- var getAreaId = (ctx) => ctx.ids?.area ?? `color-picker:${ctx.id}:area`;
62
- var getAreaGradientId = (ctx) => ctx.ids?.areaGradient ?? `color-picker:${ctx.id}:area-gradient`;
63
- var getAreaThumbId = (ctx) => ctx.ids?.areaThumb ?? `color-picker:${ctx.id}:area-thumb`;
64
- var getChannelSliderTrackId = (ctx, channel) => ctx.ids?.channelSliderTrack?.(channel) ?? `color-picker:${ctx.id}:slider-track:${channel}`;
65
- var getChannelSliderThumbId = (ctx, channel) => ctx.ids?.channelSliderThumb?.(channel) ?? `color-picker:${ctx.id}:slider-thumb:${channel}`;
66
- var getContentEl = (ctx) => ctx.getById(getContentId(ctx));
67
- var getAreaThumbEl = (ctx) => ctx.getById(getAreaThumbId(ctx));
68
- var getChannelSliderThumbEl = (ctx, channel) => ctx.getById(getChannelSliderThumbId(ctx, channel));
53
+ var import_color_picker = require("./color-picker.anatomy.js");
54
+ var getRootId = (ctx) => ctx.ids?.root ?? `${ctx.id}`;
55
+ var getLabelId = (ctx) => ctx.ids?.label ?? `${ctx.id}:label`;
56
+ var getHiddenInputId = (ctx) => ctx.ids?.hiddenInput ?? `${ctx.id}:hidden-input`;
57
+ var getControlId = (ctx) => ctx.ids?.control ?? `${ctx.id}:control`;
58
+ var getTriggerId = (ctx) => ctx.ids?.trigger ?? `${ctx.id}:trigger`;
59
+ var getContentId = (ctx) => ctx.ids?.content ?? `${ctx.id}:content`;
60
+ var getPositionerId = (ctx) => ctx.ids?.positioner ?? `${ctx.id}:positioner`;
61
+ var getFormatSelectId = (ctx) => ctx.ids?.formatSelect ?? `${ctx.id}:format-select`;
62
+ var getAreaId = (ctx) => ctx.ids?.area ?? `${ctx.id}:area`;
63
+ var getAreaGradientId = (ctx) => ctx.ids?.areaGradient ?? `${ctx.id}:area-gradient`;
64
+ var getAreaThumbId = (ctx) => ctx.ids?.areaThumb ?? `${ctx.id}:area-thumb`;
65
+ var getChannelSliderTrackId = (ctx, channel) => ctx.ids?.channelSliderTrack?.(channel) ?? `${ctx.id}:slider-track:${channel}`;
66
+ var getChannelSliderThumbId = (ctx, channel) => ctx.ids?.channelSliderThumb?.(channel) ?? `${ctx.id}:slider-thumb:${channel}`;
67
+ var getContentEl = (ctx) => ctx.query(ctx.selector(import_color_picker.parts.content));
68
+ var getAreaThumbEl = (ctx) => ctx.query(ctx.selector(import_color_picker.parts.areaThumb));
69
+ var getChannelSliderThumbEl = (ctx, channel) => ctx.query(`${ctx.selector(import_color_picker.parts.channelSliderThumb)}[data-channel="${channel}"]`);
69
70
  var getChannelInputEl = (ctx, channel) => {
70
71
  const selector = `input[data-channel="${channel}"]`;
71
72
  return [
@@ -73,9 +74,9 @@ var getChannelInputEl = (ctx, channel) => {
73
74
  ...(0, import_dom_query.queryAll)(getControlEl(ctx), selector)
74
75
  ];
75
76
  };
76
- var getFormatSelectEl = (ctx) => ctx.getById(getFormatSelectId(ctx));
77
+ var getFormatSelectEl = (ctx) => ctx.query(ctx.selector(import_color_picker.parts.formatSelect));
77
78
  var getHiddenInputEl = (ctx) => ctx.getById(getHiddenInputId(ctx));
78
- var getAreaEl = (ctx) => ctx.getById(getAreaId(ctx));
79
+ var getAreaEl = (ctx) => ctx.query(ctx.selector(import_color_picker.parts.area));
79
80
  var getAreaValueFromPoint = (ctx, point, dir) => {
80
81
  const areaEl = getAreaEl(ctx);
81
82
  if (!areaEl) return;
@@ -85,10 +86,10 @@ var getAreaValueFromPoint = (ctx, point, dir) => {
85
86
  y: getPercentValue({ orientation: "vertical" })
86
87
  };
87
88
  };
88
- var getControlEl = (ctx) => ctx.getById(getControlId(ctx));
89
- var getTriggerEl = (ctx) => ctx.getById(getTriggerId(ctx));
90
- var getPositionerEl = (ctx) => ctx.getById(getPositionerId(ctx));
91
- var getChannelSliderTrackEl = (ctx, channel) => ctx.getById(getChannelSliderTrackId(ctx, channel));
89
+ var getControlEl = (ctx) => ctx.query(ctx.selector(import_color_picker.parts.control));
90
+ var getTriggerEl = (ctx) => ctx.query(ctx.selector(import_color_picker.parts.trigger));
91
+ var getPositionerEl = (ctx) => ctx.query(ctx.selector(import_color_picker.parts.positioner));
92
+ var getChannelSliderTrackEl = (ctx, channel) => ctx.query(`${ctx.selector(import_color_picker.parts.channelSliderTrack)}[data-channel="${channel}"]`);
92
93
  var getChannelSliderValueFromPoint = (ctx, point, channel, dir) => {
93
94
  const trackEl = getChannelSliderTrackEl(ctx, channel);
94
95
  if (!trackEl) return;
@@ -1,21 +1,22 @@
1
1
  // src/color-picker.dom.ts
2
2
  import { getRelativePoint, queryAll } from "@zag-js/dom-query";
3
- var getRootId = (ctx) => ctx.ids?.root ?? `color-picker:${ctx.id}`;
4
- var getLabelId = (ctx) => ctx.ids?.label ?? `color-picker:${ctx.id}:label`;
5
- var getHiddenInputId = (ctx) => ctx.ids?.hiddenInput ?? `color-picker:${ctx.id}:hidden-input`;
6
- var getControlId = (ctx) => ctx.ids?.control ?? `color-picker:${ctx.id}:control`;
7
- var getTriggerId = (ctx) => ctx.ids?.trigger ?? `color-picker:${ctx.id}:trigger`;
8
- var getContentId = (ctx) => ctx.ids?.content ?? `color-picker:${ctx.id}:content`;
9
- var getPositionerId = (ctx) => ctx.ids?.positioner ?? `color-picker:${ctx.id}:positioner`;
10
- var getFormatSelectId = (ctx) => ctx.ids?.formatSelect ?? `color-picker:${ctx.id}:format-select`;
11
- var getAreaId = (ctx) => ctx.ids?.area ?? `color-picker:${ctx.id}:area`;
12
- var getAreaGradientId = (ctx) => ctx.ids?.areaGradient ?? `color-picker:${ctx.id}:area-gradient`;
13
- var getAreaThumbId = (ctx) => ctx.ids?.areaThumb ?? `color-picker:${ctx.id}:area-thumb`;
14
- var getChannelSliderTrackId = (ctx, channel) => ctx.ids?.channelSliderTrack?.(channel) ?? `color-picker:${ctx.id}:slider-track:${channel}`;
15
- var getChannelSliderThumbId = (ctx, channel) => ctx.ids?.channelSliderThumb?.(channel) ?? `color-picker:${ctx.id}:slider-thumb:${channel}`;
16
- var getContentEl = (ctx) => ctx.getById(getContentId(ctx));
17
- var getAreaThumbEl = (ctx) => ctx.getById(getAreaThumbId(ctx));
18
- var getChannelSliderThumbEl = (ctx, channel) => ctx.getById(getChannelSliderThumbId(ctx, channel));
3
+ import { parts } from "./color-picker.anatomy.mjs";
4
+ var getRootId = (ctx) => ctx.ids?.root ?? `${ctx.id}`;
5
+ var getLabelId = (ctx) => ctx.ids?.label ?? `${ctx.id}:label`;
6
+ var getHiddenInputId = (ctx) => ctx.ids?.hiddenInput ?? `${ctx.id}:hidden-input`;
7
+ var getControlId = (ctx) => ctx.ids?.control ?? `${ctx.id}:control`;
8
+ var getTriggerId = (ctx) => ctx.ids?.trigger ?? `${ctx.id}:trigger`;
9
+ var getContentId = (ctx) => ctx.ids?.content ?? `${ctx.id}:content`;
10
+ var getPositionerId = (ctx) => ctx.ids?.positioner ?? `${ctx.id}:positioner`;
11
+ var getFormatSelectId = (ctx) => ctx.ids?.formatSelect ?? `${ctx.id}:format-select`;
12
+ var getAreaId = (ctx) => ctx.ids?.area ?? `${ctx.id}:area`;
13
+ var getAreaGradientId = (ctx) => ctx.ids?.areaGradient ?? `${ctx.id}:area-gradient`;
14
+ var getAreaThumbId = (ctx) => ctx.ids?.areaThumb ?? `${ctx.id}:area-thumb`;
15
+ var getChannelSliderTrackId = (ctx, channel) => ctx.ids?.channelSliderTrack?.(channel) ?? `${ctx.id}:slider-track:${channel}`;
16
+ var getChannelSliderThumbId = (ctx, channel) => ctx.ids?.channelSliderThumb?.(channel) ?? `${ctx.id}:slider-thumb:${channel}`;
17
+ var getContentEl = (ctx) => ctx.query(ctx.selector(parts.content));
18
+ var getAreaThumbEl = (ctx) => ctx.query(ctx.selector(parts.areaThumb));
19
+ var getChannelSliderThumbEl = (ctx, channel) => ctx.query(`${ctx.selector(parts.channelSliderThumb)}[data-channel="${channel}"]`);
19
20
  var getChannelInputEl = (ctx, channel) => {
20
21
  const selector = `input[data-channel="${channel}"]`;
21
22
  return [
@@ -23,9 +24,9 @@ var getChannelInputEl = (ctx, channel) => {
23
24
  ...queryAll(getControlEl(ctx), selector)
24
25
  ];
25
26
  };
26
- var getFormatSelectEl = (ctx) => ctx.getById(getFormatSelectId(ctx));
27
+ var getFormatSelectEl = (ctx) => ctx.query(ctx.selector(parts.formatSelect));
27
28
  var getHiddenInputEl = (ctx) => ctx.getById(getHiddenInputId(ctx));
28
- var getAreaEl = (ctx) => ctx.getById(getAreaId(ctx));
29
+ var getAreaEl = (ctx) => ctx.query(ctx.selector(parts.area));
29
30
  var getAreaValueFromPoint = (ctx, point, dir) => {
30
31
  const areaEl = getAreaEl(ctx);
31
32
  if (!areaEl) return;
@@ -35,10 +36,10 @@ var getAreaValueFromPoint = (ctx, point, dir) => {
35
36
  y: getPercentValue({ orientation: "vertical" })
36
37
  };
37
38
  };
38
- var getControlEl = (ctx) => ctx.getById(getControlId(ctx));
39
- var getTriggerEl = (ctx) => ctx.getById(getTriggerId(ctx));
40
- var getPositionerEl = (ctx) => ctx.getById(getPositionerId(ctx));
41
- var getChannelSliderTrackEl = (ctx, channel) => ctx.getById(getChannelSliderTrackId(ctx, channel));
39
+ var getControlEl = (ctx) => ctx.query(ctx.selector(parts.control));
40
+ var getTriggerEl = (ctx) => ctx.query(ctx.selector(parts.trigger));
41
+ var getPositionerEl = (ctx) => ctx.query(ctx.selector(parts.positioner));
42
+ var getChannelSliderTrackEl = (ctx, channel) => ctx.query(`${ctx.selector(parts.channelSliderTrack)}[data-channel="${channel}"]`);
42
43
  var getChannelSliderValueFromPoint = (ctx, point, channel, dir) => {
43
44
  const trackEl = getChannelSliderTrackEl(ctx, channel);
44
45
  if (!trackEl) return;
@@ -1,6 +1,7 @@
1
1
  import * as _zag_js_core from '@zag-js/core';
2
2
  import { ColorPickerSchema } from './color-picker.types.mjs';
3
3
  import '@zag-js/color-utils';
4
+ import './utils/get-gamut-overlay-path.mjs';
4
5
  import '@zag-js/dismissable';
5
6
  import '@zag-js/popper';
6
7
  import '@zag-js/types';
@@ -1,6 +1,7 @@
1
1
  import * as _zag_js_core from '@zag-js/core';
2
2
  import { ColorPickerSchema } from './color-picker.types.js';
3
3
  import '@zag-js/color-utils';
4
+ import './utils/get-gamut-overlay-path.js';
4
5
  import '@zag-js/dismissable';
5
6
  import '@zag-js/popper';
6
7
  import '@zag-js/types';
@@ -41,6 +41,7 @@ var import_popper = require("@zag-js/popper");
41
41
  var import_utils = require("@zag-js/utils");
42
42
  var dom = __toESM(require("./color-picker.dom.js"));
43
43
  var import_color_picker = require("./color-picker.parse.js");
44
+ var import_get_area_format = require("./utils/get-area-format.js");
44
45
  var import_get_channel_input_value = require("./utils/get-channel-input-value.js");
45
46
  var import_is_valid_hex = require("./utils/is-valid-hex.js");
46
47
  var { and } = (0, import_core.createGuards)();
@@ -96,11 +97,14 @@ var machine = (0, import_core.createMachine)({
96
97
  activeId: bindable(() => ({ defaultValue: null })),
97
98
  activeChannel: bindable(() => ({ defaultValue: null })),
98
99
  activeOrientation: bindable(() => ({ defaultValue: null })),
100
+ areaBaseColor: bindable(() => ({ defaultValue: null })),
101
+ areaDragPosition: bindable(() => ({ defaultValue: null })),
99
102
  fieldsetDisabled: bindable(() => ({ defaultValue: false })),
100
103
  restoreFocus: bindable(() => ({ defaultValue: true })),
101
104
  currentPlacement: bindable(() => ({
102
105
  defaultValue: void 0
103
- }))
106
+ })),
107
+ positioned: bindable(() => ({ defaultValue: false }))
104
108
  };
105
109
  },
106
110
  computed: {
@@ -109,8 +113,7 @@ var machine = (0, import_core.createMachine)({
109
113
  interactive: ({ prop }) => !(prop("disabled") || prop("readOnly")),
110
114
  valueAsString: ({ context }) => context.get("value").toString(context.get("format")),
111
115
  areaValue: ({ context }) => {
112
- const format = context.get("format").startsWith("hsl") ? "hsla" : "hsba";
113
- return context.get("value").toFormat(format);
116
+ return context.get("value").toFormat((0, import_get_area_format.getAreaFormat)(context.get("format")));
114
117
  }
115
118
  },
116
119
  effects: ["trackFormControl"],
@@ -388,6 +391,7 @@ var machine = (0, import_core.createMachine)({
388
391
  defer: true,
389
392
  onComplete(data) {
390
393
  context.set("currentPlacement", data.placement);
394
+ context.set("positioned", true);
391
395
  }
392
396
  });
393
397
  },
@@ -458,36 +462,47 @@ var machine = (0, import_core.createMachine)({
458
462
  });
459
463
  }).catch(() => void 0);
460
464
  },
461
- setActiveChannel({ context, event }) {
465
+ setActiveChannel({ context, event, computed }) {
462
466
  context.set("activeId", event.id);
463
467
  if (event.channel) context.set("activeChannel", event.channel);
464
468
  if (event.orientation) context.set("activeOrientation", event.orientation);
469
+ const areaFormat = (0, import_get_area_format.getAreaFormat)(context.get("format"));
470
+ if (areaFormat !== context.get("format")) {
471
+ context.set("areaBaseColor", computed("areaValue"));
472
+ }
465
473
  },
466
474
  clearActiveChannel({ context }) {
467
475
  context.set("activeChannel", null);
468
476
  context.set("activeId", null);
469
477
  context.set("activeOrientation", null);
478
+ context.set("areaBaseColor", null);
479
+ context.set("areaDragPosition", null);
470
480
  },
471
481
  setAreaColorFromPoint({ context, event, computed, scope, prop }) {
472
- const v = event.format ? context.get("value").toFormat(event.format) : computed("areaValue");
482
+ const storeFormat = context.get("format");
483
+ const v = event.format ? context.get("value").toFormat(event.format) : context.get("areaBaseColor") ?? computed("areaValue");
473
484
  const { xChannel, yChannel } = event.channel || context.get("activeChannel");
474
485
  const percent = dom.getAreaValueFromPoint(scope, event.point, prop("dir"));
475
486
  if (!percent) return;
487
+ context.set("areaDragPosition", { x: percent.x, y: percent.y });
476
488
  const xValue = v.getChannelPercentValue(xChannel, percent.x);
477
489
  const yValue = v.getChannelPercentValue(yChannel, 1 - percent.y);
478
- const color = v.withChannelValue(xChannel, xValue).withChannelValue(yChannel, yValue);
490
+ const color = v.withChannelValue(xChannel, xValue).withChannelValue(yChannel, yValue).toFormat(storeFormat);
479
491
  context.set("value", color);
480
492
  },
481
493
  setChannelColorFromPoint({ context, event, computed, scope, prop }) {
494
+ const storeFormat = context.get("format");
482
495
  const channel = event.channel || context.get("activeId");
483
- const normalizedValue = event.format ? context.get("value").toFormat(event.format) : computed("areaValue");
496
+ const baseColor = context.get("areaBaseColor");
497
+ const normalizedValue = event.format ? context.get("value").toFormat(event.format) : baseColor ?? computed("areaValue");
484
498
  const percent = dom.getChannelSliderValueFromPoint(scope, event.point, channel, prop("dir"));
485
499
  if (!percent) return;
486
500
  const orientation = event.orientation || context.get("activeOrientation") || "horizontal";
487
501
  const channelPercent = orientation === "horizontal" ? percent.x : percent.y;
488
502
  const value = normalizedValue.getChannelPercentValue(channel, channelPercent);
489
- const color = normalizedValue.withChannelValue(channel, value);
490
- context.set("value", color);
503
+ const updatedBase = normalizedValue.withChannelValue(channel, value);
504
+ context.set("areaBaseColor", updatedBase);
505
+ context.set("value", updatedBase.toFormat(storeFormat));
491
506
  },
492
507
  setValue({ context, event }) {
493
508
  const format = context.get("format");
@@ -544,24 +559,32 @@ var machine = (0, import_core.createMachine)({
544
559
  const color = context.get("value").decrementChannel(event.channel, event.step);
545
560
  context.set("value", color);
546
561
  },
547
- incrementAreaXChannel({ context, event, computed }) {
562
+ incrementAreaXChannel({ context, event }) {
548
563
  const { xChannel } = event.channel;
549
- const color = computed("areaValue").incrementChannel(xChannel, event.step);
564
+ const storeFormat = context.get("format");
565
+ const v = context.get("value").toFormat((0, import_get_area_format.getAreaFormat)(storeFormat));
566
+ const color = v.incrementChannel(xChannel, event.step).toFormat(storeFormat);
550
567
  context.set("value", color);
551
568
  },
552
- decrementAreaXChannel({ context, event, computed }) {
569
+ decrementAreaXChannel({ context, event }) {
553
570
  const { xChannel } = event.channel;
554
- const color = computed("areaValue").decrementChannel(xChannel, event.step);
571
+ const storeFormat = context.get("format");
572
+ const v = context.get("value").toFormat((0, import_get_area_format.getAreaFormat)(storeFormat));
573
+ const color = v.decrementChannel(xChannel, event.step).toFormat(storeFormat);
555
574
  context.set("value", color);
556
575
  },
557
- incrementAreaYChannel({ context, event, computed }) {
576
+ incrementAreaYChannel({ context, event }) {
558
577
  const { yChannel } = event.channel;
559
- const color = computed("areaValue").incrementChannel(yChannel, event.step);
578
+ const storeFormat = context.get("format");
579
+ const v = context.get("value").toFormat((0, import_get_area_format.getAreaFormat)(storeFormat));
580
+ const color = v.incrementChannel(yChannel, event.step).toFormat(storeFormat);
560
581
  context.set("value", color);
561
582
  },
562
- decrementAreaYChannel({ context, event, computed }) {
583
+ decrementAreaYChannel({ context, event }) {
563
584
  const { yChannel } = event.channel;
564
- const color = computed("areaValue").decrementChannel(yChannel, event.step);
585
+ const storeFormat = context.get("format");
586
+ const v = context.get("value").toFormat((0, import_get_area_format.getAreaFormat)(storeFormat));
587
+ const color = v.decrementChannel(yChannel, event.step).toFormat(storeFormat);
565
588
  context.set("value", color);
566
589
  },
567
590
  setChannelToMax({ context, event }) {