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