@zag-js/color-picker 0.23.0 → 0.25.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.
package/dist/index.mjs CHANGED
@@ -1,26 +1,29 @@
1
1
  // src/color-picker.anatomy.ts
2
2
  import { createAnatomy } from "@zag-js/anatomy";
3
3
  var anatomy = createAnatomy("color-picker", [
4
+ "root",
5
+ "label",
6
+ "control",
7
+ "trigger",
8
+ "positioner",
9
+ "content",
4
10
  "area",
5
11
  "areaThumb",
6
- "areaGradient",
12
+ "areaBackground",
13
+ "channelSlider",
7
14
  "channelSliderTrack",
8
- "channelSliderTrackBackground",
9
15
  "channelSliderThumb",
10
16
  "channelInput",
17
+ "transparencyGrid",
18
+ "swatchGroup",
19
+ "swatchTrigger",
11
20
  "swatch",
12
- "swatchBackground",
13
- "content",
14
- "label",
15
21
  "eyeDropperTrigger"
16
22
  ]);
17
23
  var parts = anatomy.build();
18
24
 
19
25
  // src/color-picker.connect.ts
20
- import {
21
- getColorAreaGradient,
22
- normalizeColor
23
- } from "@zag-js/color-utils";
26
+ import { getColorAreaGradient, normalizeColor } from "@zag-js/color-utils";
24
27
  import {
25
28
  getEventKey,
26
29
  getEventPoint,
@@ -29,21 +32,29 @@ import {
29
32
  isLeftClick,
30
33
  isModifiedEvent
31
34
  } from "@zag-js/dom-event";
32
- import { dataAttr, raf } from "@zag-js/dom-query";
35
+ import { dataAttr, query } from "@zag-js/dom-query";
36
+ import { getPlacementStyles } from "@zag-js/popper";
33
37
  import { visuallyHiddenStyle } from "@zag-js/visually-hidden";
34
38
 
35
39
  // src/color-picker.dom.ts
36
40
  import { getRelativePoint } from "@zag-js/dom-event";
37
41
  import { createScope, queryAll } from "@zag-js/dom-query";
42
+ import { getFirstFocusable } from "@zag-js/tabbable";
43
+ import { runIfFn } from "@zag-js/utils";
38
44
  var dom = createScope({
45
+ getRootId: (ctx) => ctx.ids?.root ?? `color-picker:${ctx.id}`,
46
+ getLabelId: (ctx) => ctx.ids?.label ?? `color-picker:${ctx.id}:label`,
47
+ getHiddenInputId: (ctx) => `color-picker:${ctx.id}:hidden-input`,
48
+ getControlId: (ctx) => ctx.ids?.control ?? `color-picker:${ctx.id}:control`,
49
+ getTriggerId: (ctx) => ctx.ids?.trigger ?? `color-picker:${ctx.id}:trigger`,
39
50
  getContentId: (ctx) => ctx.ids?.content ?? `color-picker:${ctx.id}:content`,
51
+ getPositionerId: (ctx) => `color-picker:${ctx.id}:positioner`,
40
52
  getAreaId: (ctx) => ctx.ids?.area ?? `color-picker:${ctx.id}:area`,
41
53
  getAreaGradientId: (ctx) => ctx.ids?.areaGradient ?? `color-picker:${ctx.id}:area-gradient`,
42
54
  getAreaThumbId: (ctx) => ctx.ids?.areaThumb ?? `color-picker:${ctx.id}:area-thumb`,
43
- getChannelSliderTrackId: (ctx, channel) => ctx.ids?.channelSliderTrack?.(channel) ?? `color-picker:${ctx.id}:slider-track:${channel}`,
55
+ getChannelSliderId: (ctx, channel) => ctx.ids?.channelSliderTrack?.(channel) ?? `color-picker:${ctx.id}:slider-track:${channel}`,
44
56
  getChannelInputId: (ctx, channel) => ctx.ids?.channelInput?.(channel) ?? `color-picker:${ctx.id}:input:${channel}`,
45
57
  getChannelSliderThumbId: (ctx, channel) => ctx.ids?.channelSliderThumb?.(channel) ?? `color-picker:${ctx.id}:slider-thumb:${channel}`,
46
- getHiddenInputId: (ctx) => `color-picker:${ctx.id}:hidden-input`,
47
58
  getContentEl: (ctx) => dom.getById(ctx, dom.getContentId(ctx)),
48
59
  getAreaThumbEl: (ctx) => dom.getById(ctx, dom.getAreaThumbId(ctx)),
49
60
  getChannelSliderThumbEl: (ctx, channel) => dom.getById(ctx, dom.getChannelSliderThumbId(ctx, channel)),
@@ -57,8 +68,11 @@ var dom = createScope({
57
68
  const { percent } = getRelativePoint(point, areaEl);
58
69
  return percent;
59
70
  },
71
+ getControlEl: (ctx) => dom.getById(ctx, dom.getControlId(ctx)),
72
+ getTriggerEl: (ctx) => dom.getById(ctx, dom.getTriggerId(ctx)),
73
+ getPositionerEl: (ctx) => dom.getById(ctx, dom.getPositionerId(ctx)),
60
74
  getChannelSliderTrackEl: (ctx, channel) => {
61
- return dom.getById(ctx, dom.getChannelSliderTrackId(ctx, channel));
75
+ return dom.getById(ctx, dom.getChannelSliderId(ctx, channel));
62
76
  },
63
77
  getChannelSliderValueFromPoint(ctx, point, channel) {
64
78
  const trackEl = dom.getChannelSliderTrackEl(ctx, channel);
@@ -68,62 +82,22 @@ var dom = createScope({
68
82
  return percent;
69
83
  },
70
84
  getChannelInputEls: (ctx) => {
71
- return queryAll(dom.getContentEl(ctx), "input[data-channel]");
85
+ return [
86
+ ...queryAll(dom.getContentEl(ctx), "input[data-channel]"),
87
+ ...queryAll(dom.getControlEl(ctx), "input[data-channel]")
88
+ ];
89
+ },
90
+ getFirstFocusableEl: (ctx) => getFirstFocusable(dom.getContentEl(ctx), "if-empty"),
91
+ getInitialFocusEl: (ctx) => {
92
+ let el = runIfFn(ctx.initialFocusEl);
93
+ if (!el && ctx.autoFocus)
94
+ el = dom.getFirstFocusableEl(ctx);
95
+ if (!el)
96
+ el = dom.getContentEl(ctx);
97
+ return el;
72
98
  }
73
99
  });
74
100
 
75
- // src/utils/get-channel-details.ts
76
- import { getPercentValue, snapValueToStep } from "@zag-js/numeric-range";
77
- function getChannelDetails(color, xChannel, yChannel) {
78
- const channels = color.getColorSpaceAxes({ xChannel, yChannel });
79
- const xChannelRange = color.getChannelRange(channels.xChannel);
80
- const yChannelRange = color.getChannelRange(channels.yChannel);
81
- const { minValue: minValueX, maxValue: maxValueX, step: stepX, pageSize: pageSizeX } = xChannelRange;
82
- const { minValue: minValueY, maxValue: maxValueY, step: stepY, pageSize: pageSizeY } = yChannelRange;
83
- const xValue = color.getChannelValue(channels.xChannel);
84
- const yValue = color.getChannelValue(channels.yChannel);
85
- return {
86
- channels,
87
- xChannelStep: stepX,
88
- yChannelStep: stepY,
89
- xChannelPageStep: pageSizeX,
90
- yChannelPageStep: pageSizeY,
91
- xValue,
92
- yValue,
93
- getThumbPosition() {
94
- let x = (xValue - minValueX) / (maxValueX - minValueX);
95
- let y = 1 - (yValue - minValueY) / (maxValueY - minValueY);
96
- return { x, y };
97
- },
98
- incrementX(stepSize) {
99
- return xValue + stepSize > maxValueX ? maxValueX : snapValueToStep(xValue + stepSize, minValueX, maxValueX, stepX);
100
- },
101
- incrementY(stepSize) {
102
- return yValue + stepSize > maxValueY ? maxValueY : snapValueToStep(yValue + stepSize, minValueY, maxValueY, stepY);
103
- },
104
- decrementX(stepSize) {
105
- return snapValueToStep(xValue - stepSize, minValueX, maxValueX, stepX);
106
- },
107
- decrementY(stepSize) {
108
- return snapValueToStep(yValue - stepSize, minValueY, maxValueY, stepY);
109
- },
110
- getColorFromPoint(x, y) {
111
- let newXValue = getPercentValue(x, minValueX, maxValueX, stepX);
112
- let newYValue = getPercentValue(1 - y, minValueY, maxValueY, stepY);
113
- let newColor;
114
- if (newXValue !== xValue) {
115
- newXValue = snapValueToStep(newXValue, minValueX, maxValueX, stepX);
116
- newColor = color.withChannelValue(channels.xChannel, newXValue);
117
- }
118
- if (newYValue !== yValue) {
119
- newYValue = snapValueToStep(newYValue, minValueY, maxValueY, stepY);
120
- newColor = (newColor || color).withChannelValue(channels.yChannel, newYValue);
121
- }
122
- return newColor;
123
- }
124
- };
125
- }
126
-
127
101
  // src/utils/get-channel-display-color.ts
128
102
  import { parseColor } from "@zag-js/color-utils";
129
103
  function getChannelDisplayColor(color, channel) {
@@ -146,31 +120,48 @@ function getChannelDisplayColor(color, channel) {
146
120
  }
147
121
 
148
122
  // src/utils/get-channel-input-value.ts
149
- function getChannelInputValue(color, channel) {
123
+ import { parseColor as parseColor2 } from "@zag-js/color-utils";
124
+ function getChannelValue(color, channel) {
150
125
  if (channel == null)
151
- return;
126
+ return "";
127
+ if (channel === "hex") {
128
+ return color.toString("hex");
129
+ }
130
+ if (channel === "css") {
131
+ return color.toString("css");
132
+ }
133
+ if (channel in color) {
134
+ return color.getChannelValue(channel).toString();
135
+ }
136
+ const isHSL = color.getFormat() === "hsl";
152
137
  switch (channel) {
153
- case "hex":
154
- return color.toString("hex");
155
- case "css":
156
- return color.toString("css");
157
138
  case "hue":
139
+ return isHSL ? color.toFormat("hsla").getChannelValue("hue").toString() : color.toFormat("hsba").getChannelValue("hue").toString();
158
140
  case "saturation":
141
+ return isHSL ? color.toFormat("hsl").getChannelValue("saturation").toString() : color.toFormat("hsb").getChannelValue("saturation").toString();
159
142
  case "lightness":
160
- return color.toFormat("hsl").getChannelValue("lightness").toString();
143
+ return color.toFormat("hsla").getChannelValue("lightness").toString();
161
144
  case "brightness":
162
- return color.toFormat("hsb").getChannelValue("brightness").toString();
145
+ return color.toFormat("hsba").getChannelValue("brightness").toString();
163
146
  case "red":
164
147
  case "green":
165
148
  case "blue":
166
- return color.toFormat("rgb").getChannelValue(channel).toString();
149
+ return color.toFormat("rgba").getChannelValue(channel).toString();
167
150
  default:
168
151
  return color.getChannelValue(channel).toString();
169
152
  }
170
153
  }
171
- function getChannelInputRange(color, channel) {
154
+ function getChannelRange(color, channel) {
172
155
  switch (channel) {
173
156
  case "hex":
157
+ const minColor = parseColor2("#000000");
158
+ const maxColor = parseColor2("#FFFFFF");
159
+ return {
160
+ minValue: minColor.toHexInt(),
161
+ maxValue: maxColor.toHexInt(),
162
+ pageSize: 10,
163
+ step: 1
164
+ };
174
165
  case "css":
175
166
  return void 0;
176
167
  case "hue":
@@ -189,7 +180,7 @@ function getChannelInputRange(color, channel) {
189
180
  }
190
181
 
191
182
  // src/utils/get-slider-background.ts
192
- function getSliderBgDirection(orientation, dir) {
183
+ function getSliderBackgroundDirection(orientation, dir) {
193
184
  if (orientation === "vertical") {
194
185
  return "top";
195
186
  } else if (dir === "ltr") {
@@ -198,19 +189,18 @@ function getSliderBgDirection(orientation, dir) {
198
189
  return "left";
199
190
  }
200
191
  }
201
- var getSliderBgImage = (ctx, props) => {
202
- const { channel } = props;
203
- const dir = getSliderBgDirection(props.orientation, ctx.dir);
204
- const value = ctx.valueAsColor;
205
- const { minValue, maxValue } = ctx.valueAsColor.getChannelRange(channel);
192
+ var getSliderBackground = (props) => {
193
+ const { channel, value, dir } = props;
194
+ const bgDirection = getSliderBackgroundDirection(props.orientation, dir);
195
+ const { minValue, maxValue } = value.getChannelRange(channel);
206
196
  switch (channel) {
207
197
  case "hue":
208
- return `linear-gradient(to ${dir}, rgb(255, 0, 0) 0%, rgb(255, 255, 0) 17%, rgb(0, 255, 0) 33%, rgb(0, 255, 255) 50%, rgb(0, 0, 255) 67%, rgb(255, 0, 255) 83%, rgb(255, 0, 0) 100%)`;
198
+ return `linear-gradient(to ${bgDirection}, rgb(255, 0, 0) 0%, rgb(255, 255, 0) 17%, rgb(0, 255, 0) 33%, rgb(0, 255, 255) 50%, rgb(0, 0, 255) 67%, rgb(255, 0, 255) 83%, rgb(255, 0, 0) 100%)`;
209
199
  case "lightness": {
210
- let start = ctx.valueAsColor.withChannelValue(channel, minValue).toString("css");
200
+ let start = value.withChannelValue(channel, minValue).toString("css");
211
201
  let middle = value.withChannelValue(channel, (maxValue - minValue) / 2).toString("css");
212
202
  let end = value.withChannelValue(channel, maxValue).toString("css");
213
- return `linear-gradient(to ${dir}, ${start}, ${middle}, ${end})`;
203
+ return `linear-gradient(to ${bgDirection}, ${start}, ${middle}, ${end})`;
214
204
  }
215
205
  case "saturation":
216
206
  case "brightness":
@@ -220,7 +210,7 @@ var getSliderBgImage = (ctx, props) => {
220
210
  case "alpha": {
221
211
  let start = value.withChannelValue(channel, minValue).toString("css");
222
212
  let end = value.withChannelValue(channel, maxValue).toString("css");
223
- return `linear-gradient(to ${dir}, ${start}, ${end})`;
213
+ return `linear-gradient(to ${bgDirection}, ${start}, ${end})`;
224
214
  }
225
215
  default:
226
216
  throw new Error("Unknown color channel: " + channel);
@@ -229,41 +219,113 @@ var getSliderBgImage = (ctx, props) => {
229
219
 
230
220
  // src/color-picker.connect.ts
231
221
  function connect(state, send, normalize) {
232
- const valueAsColor = state.context.valueAsColor;
233
222
  const value = state.context.value;
223
+ const valueAsString = state.context.valueAsString;
234
224
  const isDisabled = state.context.isDisabled;
235
225
  const isInteractive = state.context.isInteractive;
236
- const isDragging = state.matches("dragging");
237
- const channels = valueAsColor.getColorChannels();
226
+ const isDragging = state.hasTag("dragging");
227
+ const isOpen = state.hasTag("open");
228
+ const channels = value.getChannels();
229
+ const getAreaChannels = (props) => {
230
+ const channels2 = value.getChannels();
231
+ return {
232
+ xChannel: props.xChannel ?? channels2[1],
233
+ yChannel: props.yChannel ?? channels2[2]
234
+ };
235
+ };
236
+ const currentPlacement = state.context.currentPlacement;
237
+ const popperStyles = getPlacementStyles({
238
+ ...state.context.positioning,
239
+ placement: currentPlacement
240
+ });
238
241
  return {
239
242
  isDragging,
243
+ isOpen,
244
+ valueAsString,
240
245
  value,
241
- valueAsColor,
242
- alpha: valueAsColor.getChannelValue("alpha"),
243
246
  channels,
244
247
  setColor(value2) {
245
248
  send({ type: "VALUE.SET", value: normalizeColor(value2), src: "set-color" });
246
249
  },
247
- setChannelValue(channel, value2) {
248
- const color = valueAsColor.withChannelValue(channel, value2);
250
+ getChannelValue(channel) {
251
+ return getChannelValue(value, channel);
252
+ },
253
+ setChannelValue(channel, channelValue) {
254
+ const color = value.withChannelValue(channel, channelValue);
249
255
  send({ type: "VALUE.SET", value: color, src: "set-channel" });
250
256
  },
257
+ format: value.outputFormat ?? value.getFormat(),
251
258
  setFormat(format) {
252
- const value2 = valueAsColor.toFormat(format);
253
- send({ type: "VALUE.SET", value: value2, src: "set-format" });
259
+ const formatValue = value.toFormat(format);
260
+ send({ type: "VALUE.SET", value: formatValue, src: "set-format" });
254
261
  },
255
- setAlpha(value2) {
256
- const color = valueAsColor.withChannelValue("alpha", value2);
262
+ alpha: value.getChannelValue("alpha"),
263
+ setAlpha(alphaValue) {
264
+ const color = value.withChannelValue("alpha", alphaValue);
257
265
  send({ type: "VALUE.SET", value: color, src: "set-alpha" });
258
266
  },
267
+ rootProps: normalize.element({
268
+ ...parts.root.attrs,
269
+ dir: state.context.dir,
270
+ id: dom.getRootId(state.context),
271
+ "data-disabled": dataAttr(isDisabled),
272
+ "data-readonly": dataAttr(state.context.readOnly),
273
+ style: {
274
+ "--value": value.toString("css")
275
+ }
276
+ }),
277
+ labelProps: normalize.element({
278
+ ...parts.label.attrs,
279
+ dir: state.context.dir,
280
+ id: dom.getLabelId(state.context),
281
+ htmlFor: dom.getHiddenInputId(state.context),
282
+ "data-disabled": dataAttr(isDisabled),
283
+ "data-readonly": dataAttr(state.context.readOnly),
284
+ onClick(event) {
285
+ event.preventDefault();
286
+ const inputEl = query(dom.getControlEl(state.context), "[data-channel=hex]");
287
+ inputEl?.focus({ preventScroll: true });
288
+ }
289
+ }),
290
+ controlProps: normalize.element({
291
+ ...parts.control.attrs,
292
+ id: dom.getControlId(state.context),
293
+ dir: state.context.dir,
294
+ "data-disabled": dataAttr(isDisabled),
295
+ "data-readonly": dataAttr(state.context.readOnly)
296
+ }),
297
+ triggerProps: normalize.button({
298
+ ...parts.trigger.attrs,
299
+ id: dom.getTriggerId(state.context),
300
+ dir: state.context.dir,
301
+ "aria-labelledby": dom.getLabelId(state.context),
302
+ "data-disabled": dataAttr(isDisabled),
303
+ "data-readonly": dataAttr(state.context.readOnly),
304
+ "data-placement": currentPlacement,
305
+ type: "button",
306
+ onClick() {
307
+ send({ type: "TRIGGER.CLICK" });
308
+ },
309
+ style: {
310
+ position: "relative"
311
+ }
312
+ }),
313
+ positionerProps: normalize.element({
314
+ ...parts.positioner.attrs,
315
+ id: dom.getPositionerId(state.context),
316
+ dir: state.context.dir,
317
+ style: popperStyles.floating
318
+ }),
259
319
  contentProps: normalize.element({
260
320
  ...parts.content.attrs,
261
321
  id: dom.getContentId(state.context),
262
- dir: state.context.dir
322
+ dir: state.context.dir,
323
+ "data-placement": currentPlacement,
324
+ hidden: !isOpen
263
325
  }),
264
- getAreaProps(props) {
265
- const { xChannel, yChannel } = props;
266
- const { areaStyles } = getColorAreaGradient(state.context.valueAsColor, {
326
+ getAreaProps(props = {}) {
327
+ const { xChannel, yChannel } = getAreaChannels(props);
328
+ const { areaStyles } = getColorAreaGradient(value, {
267
329
  xChannel,
268
330
  yChannel,
269
331
  dir: state.context.dir
@@ -281,6 +343,7 @@ function connect(state, send, normalize) {
281
343
  const point = getEventPoint(evt);
282
344
  const channel = { xChannel, yChannel };
283
345
  send({ type: "AREA.POINTER_DOWN", point, channel, id: "area" });
346
+ event.preventDefault();
284
347
  },
285
348
  style: {
286
349
  position: "relative",
@@ -290,15 +353,15 @@ function connect(state, send, normalize) {
290
353
  }
291
354
  });
292
355
  },
293
- getAreaGradientProps(props) {
294
- const { xChannel, yChannel } = props;
295
- const { areaGradientStyles } = getColorAreaGradient(valueAsColor, {
356
+ getAreaBackgroundProps(props = {}) {
357
+ const { xChannel, yChannel } = getAreaChannels(props);
358
+ const { areaGradientStyles } = getColorAreaGradient(value, {
296
359
  xChannel,
297
360
  yChannel,
298
361
  dir: state.context.dir
299
362
  });
300
363
  return normalize.element({
301
- ...parts.areaGradient.attrs,
364
+ ...parts.areaBackground.attrs,
302
365
  id: dom.getAreaGradientId(state.context),
303
366
  style: {
304
367
  position: "relative",
@@ -308,14 +371,15 @@ function connect(state, send, normalize) {
308
371
  }
309
372
  });
310
373
  },
311
- getAreaThumbProps(props) {
312
- const { xChannel, yChannel } = props;
313
- const { getThumbPosition } = getChannelDetails(valueAsColor, xChannel, yChannel);
314
- const { x, y } = getThumbPosition();
374
+ getAreaThumbProps(props = {}) {
375
+ const { xChannel, yChannel } = getAreaChannels(props);
315
376
  const channel = { xChannel, yChannel };
377
+ const x = value.getChannelValuePercent(xChannel);
378
+ const y = 1 - value.getChannelValuePercent(yChannel);
316
379
  return normalize.element({
317
380
  ...parts.areaThumb.attrs,
318
381
  id: dom.getAreaThumbId(state.context),
382
+ dir: state.context.dir,
319
383
  tabIndex: isDisabled ? void 0 : 0,
320
384
  "data-disabled": dataAttr(isDisabled),
321
385
  role: "presentation",
@@ -326,13 +390,10 @@ function connect(state, send, normalize) {
326
390
  transform: "translate(-50%, -50%)",
327
391
  touchAction: "none",
328
392
  forcedColorAdjust: "none",
329
- background: valueAsColor.withChannelValue("alpha", 1).toString("css")
330
- },
331
- onBlur() {
332
- send("AREA.BLUR");
393
+ background: value.withChannelValue("alpha", 1).toString("css")
333
394
  },
334
395
  onFocus() {
335
- send({ type: "AREA.FOCUS", id: "area" });
396
+ send({ type: "AREA.FOCUS", id: "area", channel });
336
397
  },
337
398
  onKeyDown(event) {
338
399
  if (!isInteractive)
@@ -356,6 +417,9 @@ function connect(state, send, normalize) {
356
417
  },
357
418
  PageDown() {
358
419
  send({ type: "AREA.PAGE_DOWN", channel, step });
420
+ },
421
+ Escape(event2) {
422
+ event2.stopPropagation();
359
423
  }
360
424
  };
361
425
  const exec = keyMap[getEventKey(event, state.context)];
@@ -366,14 +430,31 @@ function connect(state, send, normalize) {
366
430
  }
367
431
  });
368
432
  },
369
- getChannelSliderTrackProps(props) {
433
+ getTransparencyGridProps(props = {}) {
434
+ const { size = "12px" } = props;
435
+ return normalize.element({
436
+ ...parts.transparencyGrid.attrs,
437
+ style: {
438
+ "--size": size,
439
+ width: "100%",
440
+ height: "100%",
441
+ position: "absolute",
442
+ backgroundColor: "#fff",
443
+ backgroundImage: "conic-gradient(#eeeeee 0 25%, transparent 0 50%, #eeeeee 0 75%, transparent 0)",
444
+ backgroundSize: "var(--size) var(--size)",
445
+ inset: "0px",
446
+ zIndex: "auto",
447
+ pointerEvents: "none"
448
+ }
449
+ });
450
+ },
451
+ getChannelSliderProps(props) {
370
452
  const { orientation = "horizontal", channel } = props;
371
453
  return normalize.element({
372
- ...parts.channelSliderTrack.attrs,
373
- id: dom.getChannelSliderTrackId(state.context, channel),
374
- role: "group",
454
+ ...parts.channelSlider.attrs,
375
455
  "data-channel": channel,
376
456
  "data-orientation": orientation,
457
+ role: "presentation",
377
458
  onPointerDown(event) {
378
459
  if (!isInteractive)
379
460
  return;
@@ -382,41 +463,38 @@ function connect(state, send, normalize) {
382
463
  return;
383
464
  const point = getEventPoint(evt);
384
465
  send({ type: "CHANNEL_SLIDER.POINTER_DOWN", channel, point, id: channel, orientation });
466
+ event.preventDefault();
385
467
  },
386
468
  style: {
387
469
  position: "relative",
388
- touchAction: "none",
389
- forcedColorAdjust: "none",
390
- backgroundImage: getSliderBgImage(state.context, { orientation, channel })
470
+ touchAction: "none"
391
471
  }
392
472
  });
393
473
  },
394
- getChannelSliderBackgroundProps(props) {
474
+ getChannelSliderTrackProps(props) {
395
475
  const { orientation = "horizontal", channel } = props;
396
476
  return normalize.element({
397
- ...parts.channelSliderTrackBackground.attrs,
398
- "data-orientation": orientation,
477
+ ...parts.channelSliderTrack.attrs,
478
+ id: dom.getChannelSliderId(state.context, channel),
479
+ role: "group",
399
480
  "data-channel": channel,
481
+ "data-orientation": orientation,
400
482
  style: {
401
- position: "absolute",
402
- backgroundColor: "#fff",
403
- backgroundImage: [
404
- "linear-gradient(-45deg,#0000 75.5%,#bcbcbc 75.5%)",
405
- "linear-gradient(45deg,#0000 75.5%,#bcbcbc 75.5%)",
406
- "linear-gradient(-45deg,#bcbcbc 25.5%,#0000 25.5%)",
407
- "linear-gradient(45deg,#bcbcbc 25.5%,#0000 25.5%)"
408
- ].join(","),
409
- backgroundSize: "16px 16px",
410
- backgroundPosition: "-2px -2px,-2px 6px,6px -10px,-10px -2px",
411
- inset: 0,
412
- zIndex: -1
483
+ position: "relative",
484
+ forcedColorAdjust: "none",
485
+ backgroundImage: getSliderBackground({
486
+ orientation,
487
+ channel,
488
+ dir: state.context.dir,
489
+ value
490
+ })
413
491
  }
414
492
  });
415
493
  },
416
494
  getChannelSliderThumbProps(props) {
417
495
  const { orientation = "horizontal", channel } = props;
418
- const { minValue, maxValue, step: stepValue } = valueAsColor.getChannelRange(channel);
419
- const channelValue = valueAsColor.getChannelValue(channel);
496
+ const { minValue, maxValue, step: stepValue } = value.getChannelRange(channel);
497
+ const channelValue = value.getChannelValue(channel);
420
498
  const offset = (channelValue - minValue) / (maxValue - minValue);
421
499
  const placementStyles = orientation === "horizontal" ? { left: `${offset * 100}%`, top: "50%" } : { top: `${offset * 100}%`, left: "50%" };
422
500
  return normalize.element({
@@ -436,7 +514,7 @@ function connect(state, send, normalize) {
436
514
  style: {
437
515
  forcedColorAdjust: "none",
438
516
  position: "absolute",
439
- background: getChannelDisplayColor(valueAsColor, channel).toString("css"),
517
+ background: getChannelDisplayColor(value, channel).toString("css"),
440
518
  ...placementStyles
441
519
  },
442
520
  onFocus() {
@@ -444,11 +522,6 @@ function connect(state, send, normalize) {
444
522
  return;
445
523
  send({ type: "CHANNEL_SLIDER.FOCUS", channel });
446
524
  },
447
- onBlur() {
448
- if (!isInteractive)
449
- return;
450
- send({ type: "CHANNEL_SLIDER.BLUR", channel });
451
- },
452
525
  onKeyDown(event) {
453
526
  if (!isInteractive)
454
527
  return;
@@ -477,6 +550,9 @@ function connect(state, send, normalize) {
477
550
  },
478
551
  End() {
479
552
  send({ type: "CHANNEL_SLIDER.END", channel });
553
+ },
554
+ Escape(event2) {
555
+ event2.stopPropagation();
480
556
  }
481
557
  };
482
558
  const exec = keyMap[getEventKey(event, state.context)];
@@ -490,7 +566,7 @@ function connect(state, send, normalize) {
490
566
  getChannelInputProps(props) {
491
567
  const { channel } = props;
492
568
  const isTextField = channel === "hex" || channel === "css";
493
- const range = getChannelInputRange(valueAsColor, channel);
569
+ const range = getChannelRange(value, channel);
494
570
  return normalize.input({
495
571
  ...parts.channelInput.attrs,
496
572
  type: isTextField ? "text" : "number",
@@ -500,22 +576,31 @@ function connect(state, send, normalize) {
500
576
  "data-disabled": dataAttr(isDisabled),
501
577
  readOnly: state.context.readOnly,
502
578
  id: dom.getChannelInputId(state.context, channel),
503
- defaultValue: getChannelInputValue(valueAsColor, channel),
579
+ defaultValue: getChannelValue(value, channel),
504
580
  min: range?.minValue,
505
581
  max: range?.maxValue,
506
582
  step: range?.step,
583
+ onBeforeInput(event) {
584
+ if (isTextField)
585
+ return;
586
+ const value2 = event.currentTarget.value;
587
+ if (value2.match(/[^0-9.]/g)) {
588
+ event.preventDefault();
589
+ }
590
+ },
507
591
  onFocus(event) {
508
592
  send({ type: "CHANNEL_INPUT.FOCUS", channel });
509
- raf(() => event.target.select());
593
+ event.target.select();
510
594
  },
511
595
  onBlur(event) {
512
- const value2 = event.currentTarget.value;
513
- send({ type: "CHANNEL_INPUT.CHANGE", channel, value: value2, isTextField });
596
+ const value2 = isTextField ? event.currentTarget.value : event.currentTarget.valueAsNumber;
597
+ send({ type: "CHANNEL_INPUT.BLUR", channel, value: value2, isTextField });
514
598
  },
515
599
  onKeyDown(event) {
516
600
  if (event.key === "Enter") {
517
- const value2 = event.currentTarget.value;
601
+ const value2 = isTextField ? event.currentTarget.value : event.currentTarget.valueAsNumber;
518
602
  send({ type: "CHANNEL_INPUT.CHANGE", channel, value: value2, isTextField });
603
+ event.preventDefault();
519
604
  }
520
605
  },
521
606
  style: {
@@ -531,14 +616,11 @@ function connect(state, send, normalize) {
531
616
  name: state.context.name,
532
617
  id: dom.getHiddenInputId(state.context),
533
618
  style: visuallyHiddenStyle,
534
- defaultValue: value,
535
- onChange(event) {
536
- const value2 = event.currentTarget.value;
537
- send({ type: "VALUE.SET", value: value2, src: "input.change" });
538
- }
619
+ defaultValue: valueAsString
539
620
  }),
540
621
  eyeDropperTriggerProps: normalize.button({
541
622
  ...parts.eyeDropperTrigger.attrs,
623
+ type: "button",
542
624
  disabled: isDisabled,
543
625
  "data-disabled": dataAttr(isDisabled),
544
626
  "aria-label": "Pick a color from the screen",
@@ -548,43 +630,39 @@ function connect(state, send, normalize) {
548
630
  send("EYEDROPPER.CLICK");
549
631
  }
550
632
  }),
551
- getSwatchBackgroundProps(props) {
552
- const { value: value2 } = props;
553
- const alpha = normalizeColor(value2).getChannelValue("alpha");
554
- return normalize.element({
555
- ...parts.swatchBackground.attrs,
556
- "data-alpha": alpha,
633
+ swatchGroupProps: normalize.element({
634
+ ...parts.swatchGroup.attrs,
635
+ role: "group"
636
+ }),
637
+ getSwatchTriggerProps(props) {
638
+ const { value: valueProp } = props;
639
+ const color = normalizeColor(valueProp).toFormat(value.getFormat());
640
+ return normalize.button({
641
+ ...parts.swatchTrigger.attrs,
642
+ disabled: isDisabled,
643
+ type: "button",
644
+ "data-value": color.toString("hex"),
645
+ onClick() {
646
+ if (!isInteractive)
647
+ return;
648
+ send({ type: "VALUE.SET", value: color });
649
+ },
557
650
  style: {
558
- width: "100%",
559
- height: "100%",
560
- background: "#fff",
561
- backgroundImage: [
562
- "linear-gradient(-45deg,#0000 75.5%,#bcbcbc 75.5%)",
563
- "linear-gradient(45deg,#0000 75.5%,#bcbcbc 75.5%)",
564
- "linear-gradient(-45deg,#bcbcbc 25.5%,#0000 25.5%)",
565
- "linear-gradient(45deg,#bcbcbc 25.5%,#0000 25.5%)"
566
- ].join(","),
567
- backgroundPosition: "-2px -2px,-2px 6px,6px -10px,-10px -2px",
568
- backgroundSize: "16px 16px",
569
- position: "absolute",
570
- inset: "0px",
571
- zIndex: -1
651
+ position: "relative"
572
652
  }
573
653
  });
574
654
  },
575
655
  getSwatchProps(props) {
576
- const { value: value2, readOnly } = props;
577
- const color = normalizeColor(value2).toFormat(valueAsColor.getColorFormat());
656
+ const { value: valueProp, respectAlpha = true } = props;
657
+ const colorValue = normalizeColor(valueProp);
658
+ const color = colorValue.toFormat(value.getFormat());
578
659
  return normalize.element({
579
660
  ...parts.swatch.attrs,
580
- onClick() {
581
- if (readOnly || !isInteractive)
582
- return;
583
- send({ type: "VALUE.SET", value: color });
584
- },
661
+ "data-state": colorValue.isEqual(value) ? "selected" : "unselected",
662
+ "data-value": color.toString("hex"),
585
663
  style: {
586
664
  position: "relative",
587
- background: color.toString("css")
665
+ background: color.toString(respectAlpha ? "css" : "hex")
588
666
  }
589
667
  });
590
668
  }
@@ -592,86 +670,145 @@ function connect(state, send, normalize) {
592
670
  }
593
671
 
594
672
  // src/color-picker.machine.ts
595
- import { parseColor as parseColor2 } from "@zag-js/color-utils";
596
- import { createMachine } from "@zag-js/core";
673
+ import { parseColor as parseColor4 } from "@zag-js/color-utils";
674
+ import { createMachine, guards, ref as ref2 } from "@zag-js/core";
675
+ import { trackDismissableElement } from "@zag-js/dismissable";
597
676
  import { trackPointerMove } from "@zag-js/dom-event";
598
- import { raf as raf2 } from "@zag-js/dom-query";
599
- import { trackFormControl } from "@zag-js/form-utils";
600
- import { clampValue, getPercentValue as getPercentValue2, snapValueToStep as snapValueToStep2 } from "@zag-js/numeric-range";
677
+ import { raf } from "@zag-js/dom-query";
678
+ import { dispatchInputValueEvent, trackFormControl } from "@zag-js/form-utils";
679
+ import { getPlacement } from "@zag-js/popper";
601
680
  import { disableTextSelection } from "@zag-js/text-selection";
602
681
  import { compact, tryCatch } from "@zag-js/utils";
682
+
683
+ // src/color-picker.parse.ts
684
+ import { parseColor as parseColor3 } from "@zag-js/color-utils";
685
+ import { ref } from "@zag-js/core";
686
+ var parse = (color) => {
687
+ const value = parseColor3(color);
688
+ const colorFormat = value.getFormat();
689
+ const lockedFormat = colorFormat.startsWith("hsl") ? "hsla" : "hsba";
690
+ const result = value.toFormat(lockedFormat);
691
+ result.outputFormat = colorFormat;
692
+ return ref(result);
693
+ };
694
+
695
+ // src/color-picker.machine.ts
696
+ var { stateIn } = guards;
603
697
  function machine(userContext) {
604
698
  const ctx = compact(userContext);
605
699
  return createMachine(
606
700
  {
607
701
  id: "color-picker",
608
- initial: "idle",
702
+ initial: ctx.open ? "open" : "idle",
609
703
  context: {
610
704
  dir: "ltr",
611
- value: "#D9D9D9",
705
+ value: parse("#000000"),
612
706
  disabled: false,
613
707
  ...ctx,
614
708
  activeId: null,
615
709
  activeChannel: null,
616
710
  activeOrientation: null,
617
- fieldsetDisabled: false
711
+ fieldsetDisabled: false,
712
+ autoFocus: true,
713
+ positioning: {
714
+ ...ctx.positioning,
715
+ placement: "bottom"
716
+ }
618
717
  },
619
718
  computed: {
620
719
  isRtl: (ctx2) => ctx2.dir === "rtl",
621
720
  isDisabled: (ctx2) => !!ctx2.disabled || ctx2.fieldsetDisabled,
622
721
  isInteractive: (ctx2) => !(ctx2.isDisabled || ctx2.readOnly),
623
- valueAsColor: (ctx2) => parseColor2(ctx2.value)
722
+ valueAsString: (ctx2) => {
723
+ const color = ctx2.value.outputFormat ? ctx2.value.toFormat(ctx2.value.outputFormat) : ctx2.value;
724
+ return color.toString("css");
725
+ }
726
+ },
727
+ activities: ["trackFormControl"],
728
+ watch: {
729
+ valueAsString: ["syncInputElements"],
730
+ open: ["toggleVisibility"]
624
731
  },
625
732
  on: {
626
733
  "VALUE.SET": {
627
734
  actions: ["setValue"]
735
+ },
736
+ "CHANNEL_INPUT.FOCUS": [
737
+ {
738
+ guard: stateIn("idle"),
739
+ target: "focused",
740
+ actions: ["setActiveChannel"]
741
+ },
742
+ {
743
+ actions: ["setActiveChannel"]
744
+ }
745
+ ],
746
+ "CHANNEL_INPUT.BLUR": [
747
+ {
748
+ guard: stateIn("focused"),
749
+ target: "idle",
750
+ actions: ["setChannelColorFromInput"]
751
+ },
752
+ {
753
+ actions: ["setChannelColorFromInput"]
754
+ }
755
+ ],
756
+ "CHANNEL_INPUT.CHANGE": {
757
+ actions: ["setChannelColorFromInput"]
628
758
  }
629
759
  },
630
- activities: ["trackFormControl"],
631
- watch: {
632
- value: ["syncInputElements"]
633
- },
634
760
  states: {
635
761
  idle: {
762
+ tags: ["closed"],
636
763
  on: {
764
+ OPEN: {
765
+ target: "open",
766
+ actions: ["setInitialFocus", "invokeOnOpen"]
767
+ },
768
+ "TRIGGER.CLICK": {
769
+ target: "open",
770
+ actions: ["setInitialFocus", "invokeOnOpen"]
771
+ }
772
+ }
773
+ },
774
+ focused: {
775
+ tags: ["closed", "focused"],
776
+ on: {
777
+ OPEN: {
778
+ target: "open",
779
+ actions: ["setInitialFocus", "invokeOnOpen"]
780
+ },
781
+ "TRIGGER.CLICK": {
782
+ target: "open",
783
+ actions: ["setInitialFocus", "invokeOnOpen"]
784
+ }
785
+ }
786
+ },
787
+ open: {
788
+ tags: ["open"],
789
+ activities: ["trackPositioning", "trackDismissableElement"],
790
+ on: {
791
+ "TRIGGER.CLICK": {
792
+ target: "idle",
793
+ actions: ["invokeOnClose"]
794
+ },
637
795
  "EYEDROPPER.CLICK": {
638
796
  actions: ["openEyeDropper"]
639
797
  },
640
798
  "AREA.POINTER_DOWN": {
641
- target: "dragging",
799
+ target: "open:dragging",
642
800
  actions: ["setActiveChannel", "setAreaColorFromPoint", "focusAreaThumb"]
643
801
  },
644
802
  "AREA.FOCUS": {
645
- target: "focused",
646
803
  actions: ["setActiveChannel"]
647
804
  },
648
805
  "CHANNEL_SLIDER.POINTER_DOWN": {
649
- target: "dragging",
806
+ target: "open:dragging",
650
807
  actions: ["setActiveChannel", "setChannelColorFromPoint", "focusChannelThumb"]
651
808
  },
652
809
  "CHANNEL_SLIDER.FOCUS": {
653
- target: "focused",
654
- actions: ["setActiveChannel"]
655
- },
656
- "CHANNEL_INPUT.FOCUS": {
657
- target: "focused",
658
810
  actions: ["setActiveChannel"]
659
811
  },
660
- "CHANNEL_INPUT.CHANGE": {
661
- actions: ["setChannelColorFromInput"]
662
- }
663
- }
664
- },
665
- focused: {
666
- on: {
667
- "AREA.POINTER_DOWN": {
668
- target: "dragging",
669
- actions: ["setActiveChannel", "setAreaColorFromPoint", "focusAreaThumb"]
670
- },
671
- "CHANNEL_SLIDER.POINTER_DOWN": {
672
- target: "dragging",
673
- actions: ["setActiveChannel", "setChannelColorFromPoint", "focusChannelThumb"]
674
- },
675
812
  "AREA.ARROW_LEFT": {
676
813
  actions: ["decrementXChannel"]
677
814
  },
@@ -714,44 +851,100 @@ function machine(userContext) {
714
851
  "CHANNEL_SLIDER.END": {
715
852
  actions: ["setChannelToMax"]
716
853
  },
717
- "CHANNEL_INPUT.FOCUS": {
718
- actions: ["setActiveChannel"]
719
- },
720
- "CHANNEL_INPUT.CHANGE": {
721
- actions: ["setChannelColorFromInput"]
722
- },
723
- "CHANNEL_SLIDER.BLUR": {
724
- target: "idle"
725
- },
726
- "AREA.BLUR": {
727
- target: "idle"
854
+ INTERACT_OUTSIDE: [
855
+ {
856
+ guard: "shouldRestoreFocus",
857
+ target: "focused",
858
+ actions: ["setReturnFocus", "invokeOnClose"]
859
+ },
860
+ {
861
+ target: "idle",
862
+ actions: ["invokeOnClose"]
863
+ }
864
+ ],
865
+ CLOSE: {
866
+ target: "idle",
867
+ actions: ["invokeOnClose"]
728
868
  }
729
869
  }
730
870
  },
731
- dragging: {
871
+ "open:dragging": {
872
+ tags: ["open"],
732
873
  exit: ["clearActiveChannel"],
733
- activities: ["trackPointerMove", "disableTextSelection"],
874
+ activities: ["trackPointerMove", "disableTextSelection", "trackPositioning", "trackDismissableElement"],
734
875
  on: {
735
876
  "AREA.POINTER_MOVE": {
736
- actions: ["setAreaColorFromPoint"]
877
+ actions: ["setAreaColorFromPoint", "focusAreaThumb"]
737
878
  },
738
879
  "AREA.POINTER_UP": {
739
- target: "focused",
880
+ target: "open",
740
881
  actions: ["invokeOnChangeEnd"]
741
882
  },
742
883
  "CHANNEL_SLIDER.POINTER_MOVE": {
743
- actions: ["setChannelColorFromPoint"]
884
+ actions: ["setChannelColorFromPoint", "focusChannelThumb"]
744
885
  },
745
886
  "CHANNEL_SLIDER.POINTER_UP": {
746
- target: "focused",
887
+ target: "open",
747
888
  actions: ["invokeOnChangeEnd"]
889
+ },
890
+ INTERACT_OUTSIDE: [
891
+ {
892
+ guard: "shouldRestoreFocus",
893
+ target: "focused",
894
+ actions: ["setReturnFocus", "invokeOnClose"]
895
+ },
896
+ {
897
+ target: "idle",
898
+ actions: ["invokeOnClose"]
899
+ }
900
+ ],
901
+ CLOSE: {
902
+ target: "idle",
903
+ actions: ["invokeOnClose"]
748
904
  }
749
905
  }
750
906
  }
751
907
  }
752
908
  },
753
909
  {
910
+ guards: {
911
+ isTargetFocusable: (_ctx, evt) => evt.restoreFocus
912
+ },
754
913
  activities: {
914
+ trackPositioning(ctx2) {
915
+ ctx2.currentPlacement = ctx2.positioning.placement;
916
+ const anchorEl = dom.getTriggerEl(ctx2);
917
+ const getPositionerEl = () => dom.getPositionerEl(ctx2);
918
+ return getPlacement(anchorEl, getPositionerEl, {
919
+ ...ctx2.positioning,
920
+ defer: true,
921
+ onComplete(data) {
922
+ ctx2.currentPlacement = data.placement;
923
+ },
924
+ onCleanup() {
925
+ ctx2.currentPlacement = void 0;
926
+ }
927
+ });
928
+ },
929
+ trackDismissableElement(ctx2, _evt, { send }) {
930
+ const getContentEl = () => dom.getContentEl(ctx2);
931
+ let restoreFocus = true;
932
+ return trackDismissableElement(getContentEl, {
933
+ exclude: dom.getTriggerEl(ctx2),
934
+ defer: true,
935
+ onInteractOutside(event) {
936
+ ctx2.onInteractOutside?.(event);
937
+ if (event.defaultPrevented)
938
+ return;
939
+ restoreFocus = !(event.detail.focusable || event.detail.contextmenu);
940
+ },
941
+ onPointerDownOutside: ctx2.onPointerDownOutside,
942
+ onFocusOutside: ctx2.onFocusOutside,
943
+ onDismiss() {
944
+ send({ type: "INTERACT_OUTSIDE", restoreFocus });
945
+ }
946
+ });
947
+ },
755
948
  trackFormControl(ctx2, _evt, { send, initialContext }) {
756
949
  const inputEl = dom.getHiddenInputEl(ctx2);
757
950
  return trackFormControl(inputEl, {
@@ -787,20 +980,18 @@ function machine(userContext) {
787
980
  const win = dom.getWin(ctx2);
788
981
  const picker = new win.EyeDropper();
789
982
  picker.open().then(({ sRGBHex }) => {
790
- const format = ctx2.valueAsColor.getColorFormat();
791
- const color = parseColor2(sRGBHex).toFormat(format);
983
+ const format = ctx2.value.getFormat();
984
+ const color = parseColor4(sRGBHex).toFormat(format);
792
985
  set.value(ctx2, color);
793
- ctx2.onValueChangeEnd?.({ value: ctx2.value, valueAsColor: color });
986
+ ctx2.onValueChangeEnd?.({ value: ctx2.value, valueAsString: ctx2.valueAsString });
794
987
  }).catch(() => void 0);
795
988
  },
796
989
  setActiveChannel(ctx2, evt) {
797
990
  ctx2.activeId = evt.id;
798
- if (evt.channel) {
991
+ if (evt.channel)
799
992
  ctx2.activeChannel = evt.channel;
800
- }
801
- if (evt.orientation) {
993
+ if (evt.orientation)
802
994
  ctx2.activeOrientation = evt.orientation;
803
- }
804
995
  },
805
996
  clearActiveChannel(ctx2) {
806
997
  ctx2.activeChannel = null;
@@ -812,10 +1003,9 @@ function machine(userContext) {
812
1003
  const percent = dom.getAreaValueFromPoint(ctx2, evt.point);
813
1004
  if (!percent)
814
1005
  return;
815
- const { getColorFromPoint } = getChannelDetails(ctx2.valueAsColor, xChannel, yChannel);
816
- const color = getColorFromPoint(percent.x, percent.y);
817
- if (!color)
818
- return;
1006
+ const xValue = ctx2.value.getChannelPercentValue(xChannel, percent.x);
1007
+ const yValue = ctx2.value.getChannelPercentValue(yChannel, 1 - percent.y);
1008
+ const color = ctx2.value.withChannelValue(xChannel, xValue).withChannelValue(yChannel, yValue);
819
1009
  set.value(ctx2, color);
820
1010
  },
821
1011
  setChannelColorFromPoint(ctx2, evt) {
@@ -823,24 +1013,17 @@ function machine(userContext) {
823
1013
  const percent = dom.getChannelSliderValueFromPoint(ctx2, evt.point, channel);
824
1014
  if (!percent)
825
1015
  return;
826
- const { minValue, maxValue, step } = ctx2.valueAsColor.getChannelRange(channel);
827
1016
  const orientation = ctx2.activeOrientation || "horizontal";
828
- const point = orientation === "horizontal" ? percent.x : percent.y;
829
- const channelValue = getPercentValue2(point, minValue, maxValue, step);
830
- const value = snapValueToStep2(channelValue - step, minValue, maxValue, step);
831
- const newColor = ctx2.valueAsColor.withChannelValue(channel, value);
832
- set.value(ctx2, newColor);
1017
+ const channelPercent = orientation === "horizontal" ? percent.x : percent.y;
1018
+ const value = ctx2.value.getChannelPercentValue(channel, channelPercent);
1019
+ const color = ctx2.value.withChannelValue(channel, value);
1020
+ set.value(ctx2, color);
833
1021
  },
834
1022
  setValue(ctx2, evt) {
835
1023
  set.value(ctx2, evt.value);
836
1024
  },
837
1025
  syncInputElements(ctx2) {
838
- const inputs = dom.getChannelInputEls(ctx2);
839
- inputs.forEach((input) => {
840
- const channel = input.dataset.channel;
841
- dom.setValue(input, getChannelInputValue(ctx2.valueAsColor, channel));
842
- });
843
- dom.setValue(dom.getHiddenInputEl(ctx2), ctx2.value);
1026
+ sync.inputs(ctx2);
844
1027
  },
845
1028
  invokeOnChangeEnd(ctx2) {
846
1029
  invoke.changeEnd(ctx2);
@@ -848,112 +1031,141 @@ function machine(userContext) {
848
1031
  setChannelColorFromInput(ctx2, evt) {
849
1032
  const { channel, isTextField, value } = evt;
850
1033
  if (channel === "alpha") {
851
- const newColor2 = ctx2.valueAsColor.withChannelValue("alpha", value);
852
- set.value(ctx2, newColor2);
1034
+ const newColor = ctx2.value.withChannelValue("alpha", parseFloat(value));
1035
+ set.value(ctx2, newColor);
853
1036
  return;
854
1037
  }
855
1038
  if (isTextField) {
856
- const format = "hsl";
857
- const currentAlpha = ctx2.valueAsColor.getChannelValue("alpha");
858
- const newColor2 = tryCatch(
859
- () => parseColor2(value).toFormat(format).withChannelValue("alpha", currentAlpha),
860
- () => ctx2.valueAsColor
1039
+ const currentAlpha = ctx2.value.getChannelValue("alpha");
1040
+ const color2 = tryCatch(
1041
+ () => parse(value).withChannelValue("alpha", currentAlpha),
1042
+ () => ctx2.value
861
1043
  );
862
1044
  const inputEl = dom.getChannelInputEl(ctx2, channel);
863
- dom.setValue(inputEl, getChannelInputValue(ctx2.valueAsColor, channel));
864
- set.value(ctx2, newColor2);
1045
+ dom.setValue(inputEl, getChannelValue(color2, channel));
1046
+ set.value(ctx2, color2);
865
1047
  return;
866
1048
  }
867
- const newColor = ctx2.valueAsColor.withChannelValue(channel, value);
868
- set.value(ctx2, newColor);
1049
+ const color = ctx2.value.toFormat(ctx2.value.outputFormat).withChannelValue(channel, value);
1050
+ set.value(ctx2, color);
869
1051
  },
870
1052
  incrementChannel(ctx2, evt) {
871
- const { minValue, maxValue, step } = ctx2.valueAsColor.getChannelRange(evt.channel);
872
- const channelValue = ctx2.valueAsColor.getChannelValue(evt.channel);
873
- const value = snapValueToStep2(channelValue + evt.step, minValue, maxValue, step);
874
- const color = ctx2.valueAsColor.withChannelValue(evt.channel, clampValue(value, minValue, maxValue));
1053
+ const color = ctx2.value.incrementChannel(evt.channel, evt.step);
875
1054
  set.value(ctx2, color);
876
1055
  },
877
1056
  decrementChannel(ctx2, evt) {
878
- const { minValue, maxValue, step } = ctx2.valueAsColor.getChannelRange(evt.channel);
879
- const channelValue = ctx2.valueAsColor.getChannelValue(evt.channel);
880
- const value = snapValueToStep2(channelValue - evt.step, minValue, maxValue, step);
881
- const color = ctx2.valueAsColor.withChannelValue(evt.channel, clampValue(value, minValue, maxValue));
1057
+ const color = ctx2.value.decrementChannel(evt.channel, evt.step);
882
1058
  set.value(ctx2, color);
883
1059
  },
884
1060
  incrementXChannel(ctx2, evt) {
885
- const { xChannel, yChannel } = evt.channel;
886
- const { incrementX } = getChannelDetails(ctx2.valueAsColor, xChannel, yChannel);
887
- const color = ctx2.valueAsColor.withChannelValue(xChannel, incrementX(evt.step));
1061
+ const { xChannel } = evt.channel;
1062
+ const color = ctx2.value.incrementChannel(xChannel, evt.step);
888
1063
  set.value(ctx2, color);
889
1064
  },
890
1065
  decrementXChannel(ctx2, evt) {
891
- const { xChannel, yChannel } = evt.channel;
892
- const { decrementX } = getChannelDetails(ctx2.valueAsColor, xChannel, yChannel);
893
- const color = ctx2.valueAsColor.withChannelValue(xChannel, decrementX(evt.step));
1066
+ const { xChannel } = evt.channel;
1067
+ const color = ctx2.value.decrementChannel(xChannel, evt.step);
894
1068
  set.value(ctx2, color);
895
1069
  },
896
1070
  incrementYChannel(ctx2, evt) {
897
- const { xChannel, yChannel } = evt.channel;
898
- const { incrementY } = getChannelDetails(ctx2.valueAsColor, xChannel, yChannel);
899
- const color = ctx2.valueAsColor.withChannelValue(yChannel, incrementY(evt.step));
1071
+ const { yChannel } = evt.channel;
1072
+ const color = ctx2.value.incrementChannel(yChannel, evt.step);
900
1073
  set.value(ctx2, color);
901
1074
  },
902
1075
  decrementYChannel(ctx2, evt) {
903
- const { xChannel, yChannel } = evt.channel;
904
- const { decrementY } = getChannelDetails(ctx2.valueAsColor, xChannel, yChannel);
905
- const color = ctx2.valueAsColor.withChannelValue(yChannel, decrementY(evt.step));
1076
+ const { yChannel } = evt.channel;
1077
+ const color = ctx2.value.decrementChannel(yChannel, evt.step);
906
1078
  set.value(ctx2, color);
907
1079
  },
908
1080
  setChannelToMax(ctx2, evt) {
909
- const { maxValue } = ctx2.valueAsColor.getChannelRange(evt.channel);
910
- const color = ctx2.valueAsColor.withChannelValue(evt.channel, maxValue);
1081
+ const range = ctx2.value.getChannelRange(evt.channel);
1082
+ const color = ctx2.value.withChannelValue(evt.channel, range.maxValue);
911
1083
  set.value(ctx2, color);
912
1084
  },
913
1085
  setChannelToMin(ctx2, evt) {
914
- const { minValue } = ctx2.valueAsColor.getChannelRange(evt.channel);
915
- const color = ctx2.valueAsColor.withChannelValue(evt.channel, minValue);
1086
+ const range = ctx2.value.getChannelRange(evt.channel);
1087
+ const color = ctx2.value.withChannelValue(evt.channel, range.minValue);
916
1088
  set.value(ctx2, color);
917
1089
  },
918
1090
  focusAreaThumb(ctx2) {
919
- raf2(() => {
920
- dom.getAreaThumbEl(ctx2)?.focus({ preventScroll: true });
1091
+ raf(() => {
1092
+ dom?.focus(ctx2, dom.getAreaThumbEl(ctx2));
921
1093
  });
922
1094
  },
923
1095
  focusChannelThumb(ctx2, evt) {
924
- raf2(() => {
925
- dom.getChannelSliderThumbEl(ctx2, evt.channel)?.focus({ preventScroll: true });
1096
+ raf(() => {
1097
+ dom?.focus(ctx2, dom.getChannelSliderThumbEl(ctx2, evt.channel));
1098
+ });
1099
+ },
1100
+ setInitialFocus(ctx2) {
1101
+ raf(() => {
1102
+ dom.getInitialFocusEl(ctx2)?.focus({ preventScroll: true });
926
1103
  });
1104
+ },
1105
+ setReturnFocus(ctx2) {
1106
+ raf(() => {
1107
+ dom?.focus(ctx2, dom.getTriggerEl(ctx2));
1108
+ });
1109
+ },
1110
+ invokeOnOpen(ctx2) {
1111
+ ctx2.onOpenChange?.({ open: true });
1112
+ },
1113
+ invokeOnClose(ctx2) {
1114
+ ctx2.onOpenChange?.({ open: false });
1115
+ },
1116
+ toggleVisibility(ctx2, _evt, { send }) {
1117
+ send({ type: ctx2.open ? "OPEN" : "CLOSE", src: "controlled" });
927
1118
  }
1119
+ },
1120
+ compareFns: {
1121
+ value: (a, b) => a.isEqual(b)
928
1122
  }
929
1123
  }
930
1124
  );
931
1125
  }
1126
+ var sync = {
1127
+ inputs(ctx) {
1128
+ const channelInputs = dom.getChannelInputEls(ctx);
1129
+ channelInputs.forEach((inputEl) => {
1130
+ const channel = inputEl.dataset.channel;
1131
+ dom.setValue(inputEl, getChannelValue(ctx.value, channel));
1132
+ });
1133
+ }
1134
+ };
932
1135
  var invoke = {
933
1136
  changeEnd(ctx) {
934
1137
  ctx.onValueChangeEnd?.({
935
1138
  value: ctx.value,
936
- valueAsColor: ctx.valueAsColor
1139
+ valueAsString: ctx.valueAsString
937
1140
  });
938
1141
  },
939
1142
  change(ctx) {
940
1143
  ctx.onValueChange?.({
941
1144
  value: ctx.value,
942
- valueAsColor: ctx.valueAsColor
1145
+ valueAsString: ctx.valueAsString
943
1146
  });
1147
+ dispatchInputValueEvent(dom.getHiddenInputEl(ctx), { value: ctx.valueAsString });
944
1148
  }
945
1149
  };
946
1150
  var set = {
947
1151
  value(ctx, color) {
948
- if (ctx.valueAsColor.isEqual(color))
1152
+ sync.inputs(ctx);
1153
+ if (!color || ctx.value.isEqual(color))
949
1154
  return;
950
- ctx.value = color.toString("css");
1155
+ const currentFormat = ctx.value.getFormat();
1156
+ const outputColor = color.toFormat(currentFormat);
1157
+ try {
1158
+ outputColor.outputFormat = ctx.value.outputFormat;
1159
+ } catch {
1160
+ }
1161
+ ctx.value = ref2(outputColor);
951
1162
  invoke.change(ctx);
952
1163
  }
953
1164
  };
954
1165
  export {
955
1166
  anatomy,
956
1167
  connect,
957
- machine
1168
+ machine,
1169
+ parse
958
1170
  };
959
1171
  //# sourceMappingURL=index.mjs.map