@zag-js/color-picker 0.49.0 → 0.51.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,1387 +1,2 @@
1
- // src/color-picker.anatomy.ts
2
- import { createAnatomy } from "@zag-js/anatomy";
3
- var anatomy = createAnatomy("color-picker", [
4
- "root",
5
- "label",
6
- "control",
7
- "trigger",
8
- "positioner",
9
- "content",
10
- "area",
11
- "areaThumb",
12
- "areaBackground",
13
- "channelSlider",
14
- "channelSliderTrack",
15
- "channelSliderThumb",
16
- "channelInput",
17
- "transparencyGrid",
18
- "swatchGroup",
19
- "swatchTrigger",
20
- "swatchIndicator",
21
- "swatch",
22
- "eyeDropperTrigger",
23
- "formatTrigger",
24
- "formatSelect"
25
- ]);
26
- var parts = anatomy.build();
27
-
28
- // src/color-picker.connect.ts
29
- import { getColorAreaGradient, normalizeColor } from "@zag-js/color-utils";
30
- import {
31
- getEventKey,
32
- getEventPoint,
33
- getEventStep,
34
- getNativeEvent,
35
- isLeftClick,
36
- isModifierKey
37
- } from "@zag-js/dom-event";
38
- import { dataAttr, query, visuallyHiddenStyle } from "@zag-js/dom-query";
39
- import { getPlacementStyles } from "@zag-js/popper";
40
-
41
- // src/color-picker.dom.ts
42
- import { getRelativePoint } from "@zag-js/dom-event";
43
- import { createScope, queryAll } from "@zag-js/dom-query";
44
- import { getFirstFocusable } from "@zag-js/tabbable";
45
- import { runIfFn } from "@zag-js/utils";
46
- var dom = createScope({
47
- getRootId: (ctx) => ctx.ids?.root ?? `color-picker:${ctx.id}`,
48
- getLabelId: (ctx) => ctx.ids?.label ?? `color-picker:${ctx.id}:label`,
49
- getHiddenInputId: (ctx) => `color-picker:${ctx.id}:hidden-input`,
50
- getControlId: (ctx) => ctx.ids?.control ?? `color-picker:${ctx.id}:control`,
51
- getTriggerId: (ctx) => ctx.ids?.trigger ?? `color-picker:${ctx.id}:trigger`,
52
- getContentId: (ctx) => ctx.ids?.content ?? `color-picker:${ctx.id}:content`,
53
- getPositionerId: (ctx) => `color-picker:${ctx.id}:positioner`,
54
- getFormatSelectId: (ctx) => `color-picker:${ctx.id}:format-select`,
55
- getAreaId: (ctx) => ctx.ids?.area ?? `color-picker:${ctx.id}:area`,
56
- getAreaGradientId: (ctx) => ctx.ids?.areaGradient ?? `color-picker:${ctx.id}:area-gradient`,
57
- getAreaThumbId: (ctx) => ctx.ids?.areaThumb ?? `color-picker:${ctx.id}:area-thumb`,
58
- getChannelSliderId: (ctx, channel) => ctx.ids?.channelSliderTrack?.(channel) ?? `color-picker:${ctx.id}:slider-track:${channel}`,
59
- getChannelSliderThumbId: (ctx, channel) => ctx.ids?.channelSliderThumb?.(channel) ?? `color-picker:${ctx.id}:slider-thumb:${channel}`,
60
- getContentEl: (ctx) => dom.getById(ctx, dom.getContentId(ctx)),
61
- getAreaThumbEl: (ctx) => dom.getById(ctx, dom.getAreaThumbId(ctx)),
62
- getChannelSliderThumbEl: (ctx, channel) => dom.getById(ctx, dom.getChannelSliderThumbId(ctx, channel)),
63
- getChannelInputEl: (ctx, channel) => {
64
- return [
65
- ...queryAll(dom.getContentEl(ctx), `input[data-channel="${channel}"]`),
66
- ...queryAll(dom.getControlEl(ctx), `input[data-channel="${channel}"]`)
67
- ];
68
- },
69
- getFormatSelectEl: (ctx) => dom.getById(ctx, dom.getFormatSelectId(ctx)),
70
- getHiddenInputEl: (ctx) => dom.getById(ctx, dom.getHiddenInputId(ctx)),
71
- getAreaEl: (ctx) => dom.getById(ctx, dom.getAreaId(ctx)),
72
- getAreaValueFromPoint(ctx, point) {
73
- const areaEl = dom.getAreaEl(ctx);
74
- if (!areaEl)
75
- return;
76
- const { percent } = getRelativePoint(point, areaEl);
77
- return percent;
78
- },
79
- getControlEl: (ctx) => dom.getById(ctx, dom.getControlId(ctx)),
80
- getTriggerEl: (ctx) => dom.getById(ctx, dom.getTriggerId(ctx)),
81
- getPositionerEl: (ctx) => dom.getById(ctx, dom.getPositionerId(ctx)),
82
- getChannelSliderTrackEl: (ctx, channel) => {
83
- return dom.getById(ctx, dom.getChannelSliderId(ctx, channel));
84
- },
85
- getChannelSliderValueFromPoint(ctx, point, channel) {
86
- const trackEl = dom.getChannelSliderTrackEl(ctx, channel);
87
- if (!trackEl)
88
- return;
89
- const { percent } = getRelativePoint(point, trackEl);
90
- return percent;
91
- },
92
- getChannelInputEls: (ctx) => {
93
- return [
94
- ...queryAll(dom.getContentEl(ctx), "input[data-channel]"),
95
- ...queryAll(dom.getControlEl(ctx), "input[data-channel]")
96
- ];
97
- },
98
- getFirstFocusableEl: (ctx) => getFirstFocusable(dom.getContentEl(ctx), "if-empty"),
99
- getInitialFocusEl: (ctx) => {
100
- let el = runIfFn(ctx.initialFocusEl);
101
- el = dom.getFirstFocusableEl(ctx) ?? dom.getContentEl(ctx);
102
- return el;
103
- }
104
- });
105
-
106
- // src/utils/get-channel-display-color.ts
107
- import { parseColor } from "@zag-js/color-utils";
108
- function getChannelDisplayColor(color, channel) {
109
- switch (channel) {
110
- case "hue":
111
- return parseColor(`hsl(${color.getChannelValue("hue")}, 100%, 50%)`);
112
- case "lightness":
113
- case "brightness":
114
- case "saturation":
115
- case "red":
116
- case "green":
117
- case "blue":
118
- return color.withChannelValue("alpha", 1);
119
- case "alpha": {
120
- return color;
121
- }
122
- default:
123
- throw new Error("Unknown color channel: " + channel);
124
- }
125
- }
126
-
127
- // src/utils/get-channel-input-value.ts
128
- import { parseColor as parseColor2 } from "@zag-js/color-utils";
129
- function getChannelValue(color, channel) {
130
- if (channel == null)
131
- return "";
132
- if (channel === "hex") {
133
- return color.toString("hex");
134
- }
135
- if (channel === "css") {
136
- return color.toString("css");
137
- }
138
- if (channel in color) {
139
- return color.getChannelValue(channel).toString();
140
- }
141
- const isHSL = color.getFormat() === "hsla";
142
- switch (channel) {
143
- case "hue":
144
- return isHSL ? color.toFormat("hsla").getChannelValue("hue").toString() : color.toFormat("hsba").getChannelValue("hue").toString();
145
- case "saturation":
146
- return isHSL ? color.toFormat("hsla").getChannelValue("saturation").toString() : color.toFormat("hsba").getChannelValue("saturation").toString();
147
- case "lightness":
148
- return color.toFormat("hsla").getChannelValue("lightness").toString();
149
- case "brightness":
150
- return color.toFormat("hsba").getChannelValue("brightness").toString();
151
- case "red":
152
- case "green":
153
- case "blue":
154
- return color.toFormat("rgba").getChannelValue(channel).toString();
155
- default:
156
- return color.getChannelValue(channel).toString();
157
- }
158
- }
159
- function getChannelRange(color, channel) {
160
- switch (channel) {
161
- case "hex":
162
- const minColor = parseColor2("#000000");
163
- const maxColor = parseColor2("#FFFFFF");
164
- return {
165
- minValue: minColor.toHexInt(),
166
- maxValue: maxColor.toHexInt(),
167
- pageSize: 10,
168
- step: 1
169
- };
170
- case "css":
171
- return void 0;
172
- case "hue":
173
- case "saturation":
174
- case "lightness":
175
- return color.toFormat("hsla").getChannelRange(channel);
176
- case "brightness":
177
- return color.toFormat("hsba").getChannelRange(channel);
178
- case "red":
179
- case "green":
180
- case "blue":
181
- return color.toFormat("rgba").getChannelRange(channel);
182
- default:
183
- return color.getChannelRange(channel);
184
- }
185
- }
186
-
187
- // src/utils/get-slider-background.ts
188
- function getSliderBackgroundDirection(orientation, dir) {
189
- if (orientation === "vertical") {
190
- return "top";
191
- } else if (dir === "ltr") {
192
- return "right";
193
- } else {
194
- return "left";
195
- }
196
- }
197
- var getSliderBackground = (props) => {
198
- const { channel, value, dir } = props;
199
- const bgDirection = getSliderBackgroundDirection(props.orientation, dir);
200
- const { minValue, maxValue } = value.getChannelRange(channel);
201
- switch (channel) {
202
- case "hue":
203
- 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%)`;
204
- case "lightness": {
205
- let start = value.withChannelValue(channel, minValue).toString("css");
206
- let middle = value.withChannelValue(channel, (maxValue - minValue) / 2).toString("css");
207
- let end = value.withChannelValue(channel, maxValue).toString("css");
208
- return `linear-gradient(to ${bgDirection}, ${start}, ${middle}, ${end})`;
209
- }
210
- case "saturation":
211
- case "brightness":
212
- case "red":
213
- case "green":
214
- case "blue":
215
- case "alpha": {
216
- let start = value.withChannelValue(channel, minValue).toString("css");
217
- let end = value.withChannelValue(channel, maxValue).toString("css");
218
- return `linear-gradient(to ${bgDirection}, ${start}, ${end})`;
219
- }
220
- default:
221
- throw new Error("Unknown color channel: " + channel);
222
- }
223
- };
224
-
225
- // src/color-picker.connect.ts
226
- function connect(state, send, normalize) {
227
- const value = state.context.value;
228
- const areaValue = state.context.areaValue;
229
- const valueAsString = state.context.valueAsString;
230
- const disabled = state.context.isDisabled;
231
- const interactive = state.context.isInteractive;
232
- const dragging = state.hasTag("dragging");
233
- const open = state.hasTag("open");
234
- const focused = state.hasTag("focused");
235
- const getAreaChannels = (props) => {
236
- const channels = areaValue.getChannels();
237
- return {
238
- xChannel: props.xChannel ?? channels[1],
239
- yChannel: props.yChannel ?? channels[2]
240
- };
241
- };
242
- const currentPlacement = state.context.currentPlacement;
243
- const popperStyles = getPlacementStyles({
244
- ...state.context.positioning,
245
- placement: currentPlacement
246
- });
247
- function getSwatchTriggerState(props) {
248
- const color = normalizeColor(props.value).toFormat(state.context.format);
249
- return {
250
- value: color,
251
- valueAsString: color.toString("hex"),
252
- checked: color.isEqual(value),
253
- disabled: props.disabled || !interactive
254
- };
255
- }
256
- return {
257
- dragging,
258
- open,
259
- valueAsString,
260
- value,
261
- setOpen(_open) {
262
- if (_open === open)
263
- return;
264
- send({ type: _open ? "OPEN" : "CLOSE" });
265
- },
266
- setValue(value2) {
267
- send({ type: "VALUE.SET", value: normalizeColor(value2), src: "set-color" });
268
- },
269
- getChannelValue(channel) {
270
- return getChannelValue(value, channel);
271
- },
272
- setChannelValue(channel, channelValue) {
273
- const color = value.withChannelValue(channel, channelValue);
274
- send({ type: "VALUE.SET", value: color, src: "set-channel" });
275
- },
276
- format: state.context.format,
277
- setFormat(format) {
278
- const formatValue = value.toFormat(format);
279
- send({ type: "VALUE.SET", value: formatValue, src: "set-format" });
280
- },
281
- alpha: value.getChannelValue("alpha"),
282
- setAlpha(alphaValue) {
283
- const color = value.withChannelValue("alpha", alphaValue);
284
- send({ type: "VALUE.SET", value: color, src: "set-alpha" });
285
- },
286
- rootProps: normalize.element({
287
- ...parts.root.attrs,
288
- dir: state.context.dir,
289
- id: dom.getRootId(state.context),
290
- "data-disabled": dataAttr(disabled),
291
- "data-readonly": 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": dataAttr(disabled),
302
- "data-readonly": dataAttr(state.context.readOnly),
303
- "data-focus": dataAttr(focused),
304
- onClick(event) {
305
- event.preventDefault();
306
- const inputEl = query(dom.getControlEl(state.context), "[data-channel=hex]");
307
- inputEl?.focus({ preventScroll: true });
308
- }
309
- }),
310
- controlProps: normalize.element({
311
- ...parts.control.attrs,
312
- id: dom.getControlId(state.context),
313
- dir: state.context.dir,
314
- "data-disabled": dataAttr(disabled),
315
- "data-readonly": dataAttr(state.context.readOnly),
316
- "data-state": open ? "open" : "closed",
317
- "data-focus": dataAttr(focused)
318
- }),
319
- triggerProps: normalize.button({
320
- ...parts.trigger.attrs,
321
- id: dom.getTriggerId(state.context),
322
- dir: state.context.dir,
323
- disabled,
324
- "aria-label": `select color. current color is ${valueAsString}`,
325
- "aria-controls": dom.getContentId(state.context),
326
- "aria-labelledby": dom.getLabelId(state.context),
327
- "data-disabled": dataAttr(disabled),
328
- "data-readonly": dataAttr(state.context.readOnly),
329
- "data-placement": currentPlacement,
330
- "aria-expanded": dataAttr(open),
331
- "data-state": open ? "open" : "closed",
332
- "data-focus": dataAttr(focused),
333
- type: "button",
334
- onClick() {
335
- if (!interactive)
336
- return;
337
- send({ type: "TRIGGER.CLICK" });
338
- },
339
- onBlur() {
340
- if (!interactive)
341
- return;
342
- send({ type: "TRIGGER.BLUR" });
343
- },
344
- style: {
345
- position: "relative"
346
- }
347
- }),
348
- positionerProps: normalize.element({
349
- ...parts.positioner.attrs,
350
- id: dom.getPositionerId(state.context),
351
- dir: state.context.dir,
352
- style: popperStyles.floating
353
- }),
354
- contentProps: normalize.element({
355
- ...parts.content.attrs,
356
- id: dom.getContentId(state.context),
357
- dir: state.context.dir,
358
- "data-placement": currentPlacement,
359
- "data-state": open ? "open" : "closed",
360
- hidden: !open
361
- }),
362
- getAreaProps(props = {}) {
363
- const { xChannel, yChannel } = getAreaChannels(props);
364
- const { areaStyles } = getColorAreaGradient(areaValue, {
365
- xChannel,
366
- yChannel,
367
- dir: state.context.dir
368
- });
369
- return normalize.element({
370
- ...parts.area.attrs,
371
- id: dom.getAreaId(state.context),
372
- role: "group",
373
- onPointerDown(event) {
374
- if (!interactive)
375
- return;
376
- const evt = getNativeEvent(event);
377
- if (!isLeftClick(evt) || isModifierKey(evt))
378
- return;
379
- const point = getEventPoint(evt);
380
- const channel = { xChannel, yChannel };
381
- send({ type: "AREA.POINTER_DOWN", point, channel, id: "area" });
382
- event.preventDefault();
383
- },
384
- style: {
385
- position: "relative",
386
- touchAction: "none",
387
- forcedColorAdjust: "none",
388
- ...areaStyles
389
- }
390
- });
391
- },
392
- getAreaBackgroundProps(props = {}) {
393
- const { xChannel, yChannel } = getAreaChannels(props);
394
- const { areaGradientStyles } = getColorAreaGradient(areaValue, {
395
- xChannel,
396
- yChannel,
397
- dir: state.context.dir
398
- });
399
- return normalize.element({
400
- ...parts.areaBackground.attrs,
401
- id: dom.getAreaGradientId(state.context),
402
- style: {
403
- position: "relative",
404
- touchAction: "none",
405
- forcedColorAdjust: "none",
406
- ...areaGradientStyles
407
- }
408
- });
409
- },
410
- getAreaThumbProps(props = {}) {
411
- const { xChannel, yChannel } = getAreaChannels(props);
412
- const channel = { xChannel, yChannel };
413
- const xPercent = areaValue.getChannelValuePercent(xChannel);
414
- const yPercent = 1 - areaValue.getChannelValuePercent(yChannel);
415
- const xValue = areaValue.getChannelValue(xChannel);
416
- const yValue = areaValue.getChannelValue(yChannel);
417
- return normalize.element({
418
- ...parts.areaThumb.attrs,
419
- id: dom.getAreaThumbId(state.context),
420
- dir: state.context.dir,
421
- tabIndex: disabled ? void 0 : 0,
422
- "data-disabled": dataAttr(disabled),
423
- role: "slider",
424
- "aria-valuemin": 0,
425
- "aria-valuemax": 100,
426
- "aria-valuenow": xValue,
427
- "aria-label": `${xChannel} and ${yChannel}`,
428
- "aria-roledescription": "2d slider",
429
- "aria-valuetext": `${xChannel} ${xValue}, ${yChannel} ${yValue}`,
430
- style: {
431
- position: "absolute",
432
- left: `${xPercent * 100}%`,
433
- top: `${yPercent * 100}%`,
434
- transform: "translate(-50%, -50%)",
435
- touchAction: "none",
436
- forcedColorAdjust: "none",
437
- background: areaValue.withChannelValue("alpha", 1).toString("css")
438
- },
439
- onFocus() {
440
- if (!interactive)
441
- return;
442
- send({ type: "AREA.FOCUS", id: "area", channel });
443
- },
444
- onKeyDown(event) {
445
- if (event.defaultPrevented)
446
- return;
447
- if (!interactive)
448
- return;
449
- const step = getEventStep(event);
450
- const keyMap = {
451
- ArrowUp() {
452
- send({ type: "AREA.ARROW_UP", channel, step });
453
- },
454
- ArrowDown() {
455
- send({ type: "AREA.ARROW_DOWN", channel, step });
456
- },
457
- ArrowLeft() {
458
- send({ type: "AREA.ARROW_LEFT", channel, step });
459
- },
460
- ArrowRight() {
461
- send({ type: "AREA.ARROW_RIGHT", channel, step });
462
- },
463
- PageUp() {
464
- send({ type: "AREA.PAGE_UP", channel, step });
465
- },
466
- PageDown() {
467
- send({ type: "AREA.PAGE_DOWN", channel, step });
468
- },
469
- Escape(event2) {
470
- event2.stopPropagation();
471
- }
472
- };
473
- const exec = keyMap[getEventKey(event, state.context)];
474
- if (exec) {
475
- exec(event);
476
- event.preventDefault();
477
- }
478
- }
479
- });
480
- },
481
- getTransparencyGridProps(props = {}) {
482
- const { size = "12px" } = props;
483
- return normalize.element({
484
- ...parts.transparencyGrid.attrs,
485
- style: {
486
- "--size": size,
487
- width: "100%",
488
- height: "100%",
489
- position: "absolute",
490
- backgroundColor: "#fff",
491
- backgroundImage: "conic-gradient(#eeeeee 0 25%, transparent 0 50%, #eeeeee 0 75%, transparent 0)",
492
- backgroundSize: "var(--size) var(--size)",
493
- inset: "0px",
494
- zIndex: "auto",
495
- pointerEvents: "none"
496
- }
497
- });
498
- },
499
- getChannelSliderProps(props) {
500
- const { orientation = "horizontal", channel } = props;
501
- return normalize.element({
502
- ...parts.channelSlider.attrs,
503
- "data-channel": channel,
504
- "data-orientation": orientation,
505
- role: "presentation",
506
- onPointerDown(event) {
507
- if (!interactive)
508
- return;
509
- const evt = getNativeEvent(event);
510
- if (!isLeftClick(evt) || isModifierKey(evt))
511
- return;
512
- const point = getEventPoint(evt);
513
- send({ type: "CHANNEL_SLIDER.POINTER_DOWN", channel, point, id: channel, orientation });
514
- event.preventDefault();
515
- },
516
- style: {
517
- position: "relative",
518
- touchAction: "none"
519
- }
520
- });
521
- },
522
- getChannelSliderTrackProps(props) {
523
- const { orientation = "horizontal", channel } = props;
524
- return normalize.element({
525
- ...parts.channelSliderTrack.attrs,
526
- id: dom.getChannelSliderId(state.context, channel),
527
- role: "group",
528
- "data-channel": channel,
529
- "data-orientation": orientation,
530
- style: {
531
- position: "relative",
532
- forcedColorAdjust: "none",
533
- backgroundImage: getSliderBackground({
534
- orientation,
535
- channel,
536
- dir: state.context.dir,
537
- value: areaValue
538
- })
539
- }
540
- });
541
- },
542
- getChannelSliderThumbProps(props) {
543
- const { orientation = "horizontal", channel } = props;
544
- const { minValue, maxValue, step: stepValue } = areaValue.getChannelRange(channel);
545
- const channelValue = areaValue.getChannelValue(channel);
546
- const offset = (channelValue - minValue) / (maxValue - minValue);
547
- const placementStyles = orientation === "horizontal" ? { left: `${offset * 100}%`, top: "50%" } : { top: `${offset * 100}%`, left: "50%" };
548
- return normalize.element({
549
- ...parts.channelSliderThumb.attrs,
550
- id: dom.getChannelSliderThumbId(state.context, channel),
551
- role: "slider",
552
- "aria-label": channel,
553
- tabIndex: disabled ? void 0 : 0,
554
- "data-channel": channel,
555
- "data-disabled": dataAttr(disabled),
556
- "data-orientation": orientation,
557
- "aria-disabled": dataAttr(disabled),
558
- "aria-orientation": orientation,
559
- "aria-valuemax": maxValue,
560
- "aria-valuemin": minValue,
561
- "aria-valuenow": channelValue,
562
- "aria-valuetext": `${channel} ${channelValue}`,
563
- style: {
564
- forcedColorAdjust: "none",
565
- position: "absolute",
566
- background: getChannelDisplayColor(areaValue, channel).toString("css"),
567
- ...placementStyles
568
- },
569
- onFocus() {
570
- if (!interactive)
571
- return;
572
- send({ type: "CHANNEL_SLIDER.FOCUS", channel });
573
- },
574
- onKeyDown(event) {
575
- if (event.defaultPrevented)
576
- return;
577
- if (!interactive)
578
- return;
579
- const step = getEventStep(event) * stepValue;
580
- const keyMap = {
581
- ArrowUp() {
582
- send({ type: "CHANNEL_SLIDER.ARROW_UP", channel, step });
583
- },
584
- ArrowDown() {
585
- send({ type: "CHANNEL_SLIDER.ARROW_DOWN", channel, step });
586
- },
587
- ArrowLeft() {
588
- send({ type: "CHANNEL_SLIDER.ARROW_LEFT", channel, step });
589
- },
590
- ArrowRight() {
591
- send({ type: "CHANNEL_SLIDER.ARROW_RIGHT", channel, step });
592
- },
593
- PageUp() {
594
- send({ type: "CHANNEL_SLIDER.PAGE_UP", channel });
595
- },
596
- PageDown() {
597
- send({ type: "CHANNEL_SLIDER.PAGE_DOWN", channel });
598
- },
599
- Home() {
600
- send({ type: "CHANNEL_SLIDER.HOME", channel });
601
- },
602
- End() {
603
- send({ type: "CHANNEL_SLIDER.END", channel });
604
- },
605
- Escape(event2) {
606
- event2.stopPropagation();
607
- }
608
- };
609
- const exec = keyMap[getEventKey(event, state.context)];
610
- if (exec) {
611
- exec(event);
612
- event.preventDefault();
613
- }
614
- }
615
- });
616
- },
617
- getChannelInputProps(props) {
618
- const { channel } = props;
619
- const isTextField = channel === "hex" || channel === "css";
620
- const range = getChannelRange(value, channel);
621
- return normalize.input({
622
- ...parts.channelInput.attrs,
623
- dir: state.context.dir,
624
- type: isTextField ? "text" : "number",
625
- "data-channel": channel,
626
- "aria-label": channel,
627
- spellCheck: false,
628
- autoComplete: "off",
629
- disabled,
630
- "data-disabled": dataAttr(disabled),
631
- readOnly: state.context.readOnly,
632
- defaultValue: getChannelValue(value, channel),
633
- min: range?.minValue,
634
- max: range?.maxValue,
635
- step: range?.step,
636
- onBeforeInput(event) {
637
- if (isTextField || !interactive)
638
- return;
639
- const value2 = event.currentTarget.value;
640
- if (value2.match(/[^0-9.]/g)) {
641
- event.preventDefault();
642
- }
643
- },
644
- onFocus(event) {
645
- if (!interactive)
646
- return;
647
- send({ type: "CHANNEL_INPUT.FOCUS", channel });
648
- event.target.select();
649
- },
650
- onBlur(event) {
651
- if (!interactive)
652
- return;
653
- const value2 = isTextField ? event.currentTarget.value : event.currentTarget.valueAsNumber;
654
- send({ type: "CHANNEL_INPUT.BLUR", channel, value: value2, isTextField });
655
- },
656
- onKeyDown(event) {
657
- if (event.defaultPrevented)
658
- return;
659
- if (!interactive)
660
- return;
661
- if (event.key === "Enter") {
662
- const value2 = isTextField ? event.currentTarget.value : event.currentTarget.valueAsNumber;
663
- send({ type: "CHANNEL_INPUT.CHANGE", channel, value: value2, isTextField });
664
- event.preventDefault();
665
- }
666
- },
667
- style: {
668
- appearance: "none",
669
- WebkitAppearance: "none",
670
- MozAppearance: "textfield"
671
- }
672
- });
673
- },
674
- hiddenInputProps: normalize.input({
675
- type: "text",
676
- disabled,
677
- name: state.context.name,
678
- id: dom.getHiddenInputId(state.context),
679
- style: visuallyHiddenStyle,
680
- defaultValue: valueAsString
681
- }),
682
- eyeDropperTriggerProps: normalize.button({
683
- ...parts.eyeDropperTrigger.attrs,
684
- type: "button",
685
- dir: state.context.dir,
686
- disabled,
687
- "data-disabled": dataAttr(disabled),
688
- "aria-label": "Pick a color from the screen",
689
- onClick() {
690
- if (!interactive)
691
- return;
692
- send("EYEDROPPER.CLICK");
693
- }
694
- }),
695
- swatchGroupProps: normalize.element({
696
- ...parts.swatchGroup.attrs,
697
- role: "group"
698
- }),
699
- getSwatchTriggerState,
700
- getSwatchTriggerProps(props) {
701
- const triggerState = getSwatchTriggerState(props);
702
- return normalize.button({
703
- ...parts.swatchTrigger.attrs,
704
- disabled: triggerState.disabled,
705
- dir: state.context.dir,
706
- type: "button",
707
- "aria-label": `select ${triggerState.valueAsString} as the color`,
708
- "data-state": triggerState.checked ? "checked" : "unchecked",
709
- "data-value": triggerState.valueAsString,
710
- "data-disabled": dataAttr(triggerState.disabled),
711
- onClick() {
712
- if (triggerState.disabled)
713
- return;
714
- send({ type: "SWATCH_TRIGGER.CLICK", value: triggerState.value });
715
- },
716
- style: {
717
- position: "relative"
718
- }
719
- });
720
- },
721
- getSwatchIndicatorProps(props) {
722
- const triggerState = getSwatchTriggerState(props);
723
- return normalize.element({
724
- ...parts.swatchIndicator.attrs,
725
- dir: state.context.dir,
726
- hidden: !triggerState.checked
727
- });
728
- },
729
- getSwatchProps(props) {
730
- const { respectAlpha = true } = props;
731
- const triggerState = getSwatchTriggerState(props);
732
- return normalize.element({
733
- ...parts.swatch.attrs,
734
- dir: state.context.dir,
735
- "data-state": triggerState.checked ? "checked" : "unchecked",
736
- "data-value": triggerState.valueAsString,
737
- style: {
738
- position: "relative",
739
- background: triggerState.value.toString(respectAlpha ? "css" : "hex")
740
- }
741
- });
742
- },
743
- formatTriggerProps: normalize.button({
744
- ...parts.formatTrigger.attrs,
745
- dir: state.context.dir,
746
- type: "button",
747
- "aria-label": `change color format to ${getNextFormat(state.context.format)}`,
748
- onClick(event) {
749
- if (event.currentTarget.disabled)
750
- return;
751
- const nextFormat = getNextFormat(state.context.format);
752
- send({ type: "FORMAT.SET", format: nextFormat, src: "format-trigger" });
753
- }
754
- }),
755
- formatSelectProps: normalize.select({
756
- ...parts.formatSelect.attrs,
757
- "aria-label": "change color format",
758
- dir: state.context.dir,
759
- defaultValue: state.context.format,
760
- disabled,
761
- onChange(event) {
762
- const format = assertFormat(event.currentTarget.value);
763
- send({ type: "FORMAT.SET", format, src: "format-select" });
764
- }
765
- })
766
- };
767
- }
768
- var formats = ["hsba", "hsla", "rgba"];
769
- var formatRegex = new RegExp(`^(${formats.join("|")})$`);
770
- function getNextFormat(format) {
771
- const index = formats.indexOf(format);
772
- return formats[index + 1] ?? formats[0];
773
- }
774
- function assertFormat(format) {
775
- if (formatRegex.test(format))
776
- return format;
777
- throw new Error(`Unsupported color format: ${format}`);
778
- }
779
-
780
- // src/color-picker.machine.ts
781
- import { parseColor as parseColor4 } from "@zag-js/color-utils";
782
- import { createMachine, guards } from "@zag-js/core";
783
- import { trackDismissableElement } from "@zag-js/dismissable";
784
- import { trackPointerMove } from "@zag-js/dom-event";
785
- import { raf } from "@zag-js/dom-query";
786
- import { dispatchInputValueEvent, trackFormControl } from "@zag-js/form-utils";
787
- import { getPlacement } from "@zag-js/popper";
788
- import { disableTextSelection } from "@zag-js/text-selection";
789
- import { compact, tryCatch } from "@zag-js/utils";
790
-
791
- // src/color-picker.parse.ts
792
- import { parseColor as parseColor3 } from "@zag-js/color-utils";
793
- var parse = (colorString) => {
794
- return parseColor3(colorString);
795
- };
796
-
797
- // src/color-picker.machine.ts
798
- var { and } = guards;
799
- function machine(userContext) {
800
- const ctx = compact(userContext);
801
- return createMachine(
802
- {
803
- id: "color-picker",
804
- initial: ctx.open ? "open" : "idle",
805
- context: {
806
- dir: "ltr",
807
- value: parse("#000000"),
808
- format: "rgba",
809
- disabled: false,
810
- closeOnSelect: false,
811
- ...ctx,
812
- activeId: null,
813
- activeChannel: null,
814
- activeOrientation: null,
815
- fieldsetDisabled: false,
816
- restoreFocus: true,
817
- positioning: {
818
- ...ctx.positioning,
819
- placement: "bottom"
820
- }
821
- },
822
- computed: {
823
- isRtl: (ctx2) => ctx2.dir === "rtl",
824
- isDisabled: (ctx2) => !!ctx2.disabled || ctx2.fieldsetDisabled,
825
- isInteractive: (ctx2) => !(ctx2.isDisabled || ctx2.readOnly),
826
- valueAsString: (ctx2) => ctx2.value.toString(ctx2.format),
827
- areaValue: (ctx2) => {
828
- const format = ctx2.format.startsWith("hsl") ? "hsla" : "hsba";
829
- return ctx2.value.toFormat(format);
830
- }
831
- },
832
- activities: ["trackFormControl"],
833
- watch: {
834
- value: ["syncInputElements"],
835
- format: ["syncFormatSelectElement"],
836
- open: ["toggleVisibility"]
837
- },
838
- on: {
839
- "VALUE.SET": {
840
- actions: ["setValue"]
841
- },
842
- "FORMAT.SET": {
843
- actions: ["setFormat"]
844
- },
845
- "CHANNEL_INPUT.CHANGE": {
846
- actions: ["setChannelColorFromInput"]
847
- },
848
- "EYEDROPPER.CLICK": {
849
- actions: ["openEyeDropper"]
850
- }
851
- },
852
- states: {
853
- idle: {
854
- tags: ["closed"],
855
- on: {
856
- "CONTROLLED.OPEN": {
857
- target: "open",
858
- actions: ["setInitialFocus"]
859
- },
860
- OPEN: [
861
- {
862
- guard: "isOpenControlled",
863
- actions: ["invokeOnOpen"]
864
- },
865
- {
866
- target: "open",
867
- actions: ["invokeOnOpen", "setInitialFocus"]
868
- }
869
- ],
870
- "TRIGGER.CLICK": [
871
- {
872
- guard: "isOpenControlled",
873
- actions: ["invokeOnOpen"]
874
- },
875
- {
876
- target: "open",
877
- actions: ["invokeOnOpen", "setInitialFocus"]
878
- }
879
- ],
880
- "CHANNEL_INPUT.FOCUS": {
881
- target: "focused",
882
- actions: ["setActiveChannel"]
883
- }
884
- }
885
- },
886
- focused: {
887
- tags: ["closed", "focused"],
888
- on: {
889
- "CONTROLLED.OPEN": {
890
- target: "open",
891
- actions: ["setInitialFocus"]
892
- },
893
- OPEN: [
894
- {
895
- guard: "isOpenControlled",
896
- actions: ["invokeOnOpen"]
897
- },
898
- {
899
- target: "open",
900
- actions: ["invokeOnOpen", "setInitialFocus"]
901
- }
902
- ],
903
- "TRIGGER.CLICK": [
904
- {
905
- guard: "isOpenControlled",
906
- actions: ["invokeOnOpen"]
907
- },
908
- {
909
- target: "open",
910
- actions: ["invokeOnOpen", "setInitialFocus"]
911
- }
912
- ],
913
- "CHANNEL_INPUT.FOCUS": {
914
- actions: ["setActiveChannel"]
915
- },
916
- "CHANNEL_INPUT.BLUR": {
917
- target: "idle",
918
- actions: ["setChannelColorFromInput"]
919
- },
920
- "TRIGGER.BLUR": {
921
- target: "idle"
922
- }
923
- }
924
- },
925
- open: {
926
- tags: ["open"],
927
- activities: ["trackPositioning", "trackDismissableElement"],
928
- on: {
929
- "CONTROLLED.CLOSE": [
930
- {
931
- guard: "shouldRestoreFocus",
932
- target: "focused",
933
- actions: ["setReturnFocus"]
934
- },
935
- {
936
- target: "idle"
937
- }
938
- ],
939
- "TRIGGER.CLICK": [
940
- {
941
- guard: "isOpenControlled",
942
- actions: ["invokeOnClose"]
943
- },
944
- {
945
- target: "idle",
946
- actions: ["invokeOnClose"]
947
- }
948
- ],
949
- "AREA.POINTER_DOWN": {
950
- target: "open:dragging",
951
- actions: ["setActiveChannel", "setAreaColorFromPoint", "focusAreaThumb"]
952
- },
953
- "AREA.FOCUS": {
954
- actions: ["setActiveChannel"]
955
- },
956
- "CHANNEL_SLIDER.POINTER_DOWN": {
957
- target: "open:dragging",
958
- actions: ["setActiveChannel", "setChannelColorFromPoint", "focusChannelThumb"]
959
- },
960
- "CHANNEL_SLIDER.FOCUS": {
961
- actions: ["setActiveChannel"]
962
- },
963
- "AREA.ARROW_LEFT": {
964
- actions: ["decrementXChannel"]
965
- },
966
- "AREA.ARROW_RIGHT": {
967
- actions: ["incrementXChannel"]
968
- },
969
- "AREA.ARROW_UP": {
970
- actions: ["incrementYChannel"]
971
- },
972
- "AREA.ARROW_DOWN": {
973
- actions: ["decrementYChannel"]
974
- },
975
- "AREA.PAGE_UP": {
976
- actions: ["incrementXChannel"]
977
- },
978
- "AREA.PAGE_DOWN": {
979
- actions: ["decrementXChannel"]
980
- },
981
- "CHANNEL_SLIDER.ARROW_LEFT": {
982
- actions: ["decrementChannel"]
983
- },
984
- "CHANNEL_SLIDER.ARROW_RIGHT": {
985
- actions: ["incrementChannel"]
986
- },
987
- "CHANNEL_SLIDER.ARROW_UP": {
988
- actions: ["incrementChannel"]
989
- },
990
- "CHANNEL_SLIDER.ARROW_DOWN": {
991
- actions: ["decrementChannel"]
992
- },
993
- "CHANNEL_SLIDER.PAGE_UP": {
994
- actions: ["incrementChannel"]
995
- },
996
- "CHANNEL_SLIDER.PAGE_DOWN": {
997
- actions: ["decrementChannel"]
998
- },
999
- "CHANNEL_SLIDER.HOME": {
1000
- actions: ["setChannelToMin"]
1001
- },
1002
- "CHANNEL_SLIDER.END": {
1003
- actions: ["setChannelToMax"]
1004
- },
1005
- "CHANNEL_INPUT.BLUR": {
1006
- actions: ["setChannelColorFromInput"]
1007
- },
1008
- INTERACT_OUTSIDE: [
1009
- {
1010
- guard: "isOpenControlled",
1011
- actions: ["invokeOnClose"]
1012
- },
1013
- {
1014
- guard: "shouldRestoreFocus",
1015
- target: "focused",
1016
- actions: ["invokeOnClose", "setReturnFocus"]
1017
- },
1018
- {
1019
- target: "idle",
1020
- actions: ["invokeOnClose"]
1021
- }
1022
- ],
1023
- CLOSE: [
1024
- {
1025
- guard: "isOpenControlled",
1026
- actions: ["invokeOnClose"]
1027
- },
1028
- {
1029
- target: "idle",
1030
- actions: ["invokeOnClose"]
1031
- }
1032
- ],
1033
- "SWATCH_TRIGGER.CLICK": [
1034
- {
1035
- guard: and("isOpenControlled", "closeOnSelect"),
1036
- actions: ["setValue", "invokeOnClose"]
1037
- },
1038
- {
1039
- guard: "closeOnSelect",
1040
- target: "focused",
1041
- actions: ["setValue", "invokeOnClose", "setReturnFocus"]
1042
- },
1043
- {
1044
- actions: ["setValue"]
1045
- }
1046
- ]
1047
- }
1048
- },
1049
- "open:dragging": {
1050
- tags: ["open"],
1051
- exit: ["clearActiveChannel"],
1052
- activities: ["trackPointerMove", "disableTextSelection", "trackPositioning", "trackDismissableElement"],
1053
- on: {
1054
- "CONTROLLED.CLOSE": [
1055
- {
1056
- guard: "shouldRestoreFocus",
1057
- target: "focused",
1058
- actions: ["setReturnFocus"]
1059
- },
1060
- {
1061
- target: "idle"
1062
- }
1063
- ],
1064
- "AREA.POINTER_MOVE": {
1065
- actions: ["setAreaColorFromPoint", "focusAreaThumb"]
1066
- },
1067
- "AREA.POINTER_UP": {
1068
- target: "open",
1069
- actions: ["invokeOnChangeEnd"]
1070
- },
1071
- "CHANNEL_SLIDER.POINTER_MOVE": {
1072
- actions: ["setChannelColorFromPoint", "focusChannelThumb"]
1073
- },
1074
- "CHANNEL_SLIDER.POINTER_UP": {
1075
- target: "open",
1076
- actions: ["invokeOnChangeEnd"]
1077
- },
1078
- INTERACT_OUTSIDE: [
1079
- {
1080
- guard: "isOpenControlled",
1081
- actions: ["invokeOnClose"]
1082
- },
1083
- {
1084
- guard: "shouldRestoreFocus",
1085
- target: "focused",
1086
- actions: ["invokeOnClose", "setReturnFocus"]
1087
- },
1088
- {
1089
- target: "idle",
1090
- actions: ["invokeOnClose"]
1091
- }
1092
- ],
1093
- CLOSE: [
1094
- {
1095
- guard: "isOpenControlled",
1096
- actions: ["invokeOnClose"]
1097
- },
1098
- {
1099
- target: "idle",
1100
- actions: ["invokeOnClose"]
1101
- }
1102
- ]
1103
- }
1104
- }
1105
- }
1106
- },
1107
- {
1108
- guards: {
1109
- closeOnSelect: (ctx2) => !!ctx2.closeOnSelect,
1110
- isOpenControlled: (ctx2) => !!ctx2["open.controlled"],
1111
- shouldRestoreFocus: (ctx2) => !!ctx2.restoreFocus
1112
- },
1113
- activities: {
1114
- trackPositioning(ctx2) {
1115
- ctx2.currentPlacement = ctx2.positioning.placement;
1116
- const anchorEl = dom.getTriggerEl(ctx2);
1117
- const getPositionerEl = () => dom.getPositionerEl(ctx2);
1118
- return getPlacement(anchorEl, getPositionerEl, {
1119
- ...ctx2.positioning,
1120
- defer: true,
1121
- onComplete(data) {
1122
- ctx2.currentPlacement = data.placement;
1123
- }
1124
- });
1125
- },
1126
- trackDismissableElement(ctx2, _evt, { send }) {
1127
- const getContentEl = () => dom.getContentEl(ctx2);
1128
- return trackDismissableElement(getContentEl, {
1129
- exclude: dom.getTriggerEl(ctx2),
1130
- defer: true,
1131
- onInteractOutside(event) {
1132
- ctx2.onInteractOutside?.(event);
1133
- if (event.defaultPrevented)
1134
- return;
1135
- ctx2.restoreFocus = !(event.detail.focusable || event.detail.contextmenu);
1136
- },
1137
- onPointerDownOutside: ctx2.onPointerDownOutside,
1138
- onFocusOutside: ctx2.onFocusOutside,
1139
- onDismiss() {
1140
- send({ type: "INTERACT_OUTSIDE" });
1141
- }
1142
- });
1143
- },
1144
- trackFormControl(ctx2, _evt, { send, initialContext }) {
1145
- const inputEl = dom.getHiddenInputEl(ctx2);
1146
- return trackFormControl(inputEl, {
1147
- onFieldsetDisabledChange(disabled) {
1148
- ctx2.fieldsetDisabled = disabled;
1149
- },
1150
- onFormReset() {
1151
- send({ type: "VALUE.SET", value: initialContext.value, src: "form.reset" });
1152
- }
1153
- });
1154
- },
1155
- trackPointerMove(ctx2, _evt, { send }) {
1156
- return trackPointerMove(dom.getDoc(ctx2), {
1157
- onPointerMove({ point }) {
1158
- const type = ctx2.activeId === "area" ? "AREA.POINTER_MOVE" : "CHANNEL_SLIDER.POINTER_MOVE";
1159
- send({ type, point });
1160
- },
1161
- onPointerUp() {
1162
- const type = ctx2.activeId === "area" ? "AREA.POINTER_UP" : "CHANNEL_SLIDER.POINTER_UP";
1163
- send({ type });
1164
- }
1165
- });
1166
- },
1167
- disableTextSelection(ctx2) {
1168
- return disableTextSelection({ doc: dom.getDoc(ctx2), target: dom.getContentEl(ctx2) });
1169
- }
1170
- },
1171
- actions: {
1172
- openEyeDropper(ctx2) {
1173
- const isSupported = "EyeDropper" in dom.getWin(ctx2);
1174
- if (!isSupported)
1175
- return;
1176
- const win = dom.getWin(ctx2);
1177
- const picker = new win.EyeDropper();
1178
- picker.open().then(({ sRGBHex }) => {
1179
- const format = ctx2.value.getFormat();
1180
- const color = parseColor4(sRGBHex).toFormat(format);
1181
- set.value(ctx2, color);
1182
- ctx2.onValueChangeEnd?.({ value: ctx2.value, valueAsString: ctx2.valueAsString });
1183
- }).catch(() => void 0);
1184
- },
1185
- setActiveChannel(ctx2, evt) {
1186
- ctx2.activeId = evt.id;
1187
- if (evt.channel)
1188
- ctx2.activeChannel = evt.channel;
1189
- if (evt.orientation)
1190
- ctx2.activeOrientation = evt.orientation;
1191
- },
1192
- clearActiveChannel(ctx2) {
1193
- ctx2.activeChannel = null;
1194
- ctx2.activeId = null;
1195
- ctx2.activeOrientation = null;
1196
- },
1197
- setAreaColorFromPoint(ctx2, evt) {
1198
- const { xChannel, yChannel } = evt.channel || ctx2.activeChannel;
1199
- const percent = dom.getAreaValueFromPoint(ctx2, evt.point);
1200
- if (!percent)
1201
- return;
1202
- const xValue = ctx2.areaValue.getChannelPercentValue(xChannel, percent.x);
1203
- const yValue = ctx2.areaValue.getChannelPercentValue(yChannel, 1 - percent.y);
1204
- const color = ctx2.areaValue.withChannelValue(xChannel, xValue).withChannelValue(yChannel, yValue);
1205
- set.value(ctx2, color);
1206
- },
1207
- setChannelColorFromPoint(ctx2, evt) {
1208
- const channel = evt.channel || ctx2.activeId;
1209
- const percent = dom.getChannelSliderValueFromPoint(ctx2, evt.point, channel);
1210
- if (!percent)
1211
- return;
1212
- const orientation = ctx2.activeOrientation || "horizontal";
1213
- const channelPercent = orientation === "horizontal" ? percent.x : percent.y;
1214
- const value = ctx2.areaValue.getChannelPercentValue(channel, channelPercent);
1215
- const color = ctx2.areaValue.withChannelValue(channel, value);
1216
- set.value(ctx2, color);
1217
- },
1218
- setValue(ctx2, evt) {
1219
- set.value(ctx2, evt.value);
1220
- },
1221
- setFormat(ctx2, evt) {
1222
- set.format(ctx2, evt.format);
1223
- },
1224
- syncInputElements(ctx2) {
1225
- sync.inputs(ctx2);
1226
- },
1227
- invokeOnChangeEnd(ctx2) {
1228
- invoke.changeEnd(ctx2);
1229
- },
1230
- setChannelColorFromInput(ctx2, evt) {
1231
- const { channel, isTextField, value } = evt;
1232
- const currentAlpha = ctx2.value.getChannelValue("alpha");
1233
- let color;
1234
- if (channel === "alpha") {
1235
- let valueAsNumber = parseFloat(value);
1236
- valueAsNumber = Number.isNaN(valueAsNumber) ? currentAlpha : valueAsNumber;
1237
- color = ctx2.value.withChannelValue("alpha", valueAsNumber);
1238
- } else if (isTextField) {
1239
- color = tryCatch(
1240
- () => parse(value).withChannelValue("alpha", currentAlpha),
1241
- () => ctx2.value
1242
- );
1243
- } else {
1244
- const current = ctx2.value.toFormat(ctx2.format);
1245
- const valueAsNumber = Number.isNaN(value) ? current.getChannelValue(channel) : value;
1246
- color = current.withChannelValue(channel, valueAsNumber);
1247
- }
1248
- sync.inputs(ctx2, color);
1249
- set.value(ctx2, color);
1250
- },
1251
- incrementChannel(ctx2, evt) {
1252
- const color = ctx2.value.incrementChannel(evt.channel, evt.step);
1253
- set.value(ctx2, color);
1254
- },
1255
- decrementChannel(ctx2, evt) {
1256
- const color = ctx2.value.decrementChannel(evt.channel, evt.step);
1257
- set.value(ctx2, color);
1258
- },
1259
- incrementXChannel(ctx2, evt) {
1260
- const { xChannel } = evt.channel;
1261
- const color = ctx2.areaValue.incrementChannel(xChannel, evt.step);
1262
- set.value(ctx2, color);
1263
- },
1264
- decrementXChannel(ctx2, evt) {
1265
- const { xChannel } = evt.channel;
1266
- const color = ctx2.areaValue.decrementChannel(xChannel, evt.step);
1267
- set.value(ctx2, color);
1268
- },
1269
- incrementYChannel(ctx2, evt) {
1270
- const { yChannel } = evt.channel;
1271
- const color = ctx2.areaValue.incrementChannel(yChannel, evt.step);
1272
- set.value(ctx2, color);
1273
- },
1274
- decrementYChannel(ctx2, evt) {
1275
- const { yChannel } = evt.channel;
1276
- const color = ctx2.areaValue.decrementChannel(yChannel, evt.step);
1277
- set.value(ctx2, color);
1278
- },
1279
- setChannelToMax(ctx2, evt) {
1280
- const range = ctx2.value.getChannelRange(evt.channel);
1281
- const color = ctx2.value.withChannelValue(evt.channel, range.maxValue);
1282
- set.value(ctx2, color);
1283
- },
1284
- setChannelToMin(ctx2, evt) {
1285
- const range = ctx2.value.getChannelRange(evt.channel);
1286
- const color = ctx2.value.withChannelValue(evt.channel, range.minValue);
1287
- set.value(ctx2, color);
1288
- },
1289
- focusAreaThumb(ctx2) {
1290
- raf(() => {
1291
- dom.getAreaThumbEl(ctx2)?.focus({ preventScroll: true });
1292
- });
1293
- },
1294
- focusChannelThumb(ctx2, evt) {
1295
- raf(() => {
1296
- dom.getChannelSliderThumbEl(ctx2, evt.channel)?.focus({ preventScroll: true });
1297
- });
1298
- },
1299
- setInitialFocus(ctx2) {
1300
- raf(() => {
1301
- dom.getInitialFocusEl(ctx2)?.focus({ preventScroll: true });
1302
- });
1303
- },
1304
- setReturnFocus(ctx2) {
1305
- raf(() => {
1306
- dom.getTriggerEl(ctx2)?.focus({ preventScroll: true });
1307
- });
1308
- },
1309
- syncFormatSelectElement(ctx2) {
1310
- sync.formatSelect(ctx2);
1311
- },
1312
- invokeOnOpen(ctx2) {
1313
- ctx2.onOpenChange?.({ open: true });
1314
- },
1315
- invokeOnClose(ctx2) {
1316
- ctx2.onOpenChange?.({ open: false });
1317
- },
1318
- toggleVisibility(ctx2, evt, { send }) {
1319
- send({ type: ctx2.open ? "CONTROLLED.OPEN" : "CONTROLLED.CLOSE", previousEvent: evt });
1320
- }
1321
- },
1322
- compareFns: {
1323
- value: (a, b) => a.isEqual(b)
1324
- }
1325
- }
1326
- );
1327
- }
1328
- var sync = {
1329
- // sync channel inputs
1330
- inputs(ctx, color) {
1331
- const channelInputs = dom.getChannelInputEls(ctx);
1332
- raf(() => {
1333
- channelInputs.forEach((inputEl) => {
1334
- const channel = inputEl.dataset.channel;
1335
- dom.setValue(inputEl, getChannelValue(color || ctx.value, channel));
1336
- });
1337
- });
1338
- },
1339
- // sync format select
1340
- formatSelect(ctx) {
1341
- const selectEl = dom.getFormatSelectEl(ctx);
1342
- raf(() => {
1343
- dom.setValue(selectEl, ctx.format);
1344
- });
1345
- }
1346
- };
1347
- var invoke = {
1348
- changeEnd(ctx) {
1349
- const value = ctx.value.toFormat(ctx.format);
1350
- ctx.onValueChangeEnd?.({
1351
- value,
1352
- valueAsString: ctx.valueAsString
1353
- });
1354
- },
1355
- change(ctx) {
1356
- const value = ctx.value.toFormat(ctx.format);
1357
- ctx.onValueChange?.({
1358
- value,
1359
- valueAsString: ctx.valueAsString
1360
- });
1361
- dispatchInputValueEvent(dom.getHiddenInputEl(ctx), { value: ctx.valueAsString });
1362
- },
1363
- formatChange(ctx) {
1364
- ctx.onFormatChange?.({ format: ctx.format });
1365
- }
1366
- };
1367
- var set = {
1368
- value(ctx, color) {
1369
- if (!color || ctx.value.isEqual(color))
1370
- return;
1371
- ctx.value = color;
1372
- invoke.change(ctx);
1373
- },
1374
- format(ctx, format) {
1375
- if (ctx.format === format)
1376
- return;
1377
- ctx.format = format;
1378
- invoke.formatChange(ctx);
1379
- }
1380
- };
1381
- export {
1382
- anatomy,
1383
- connect,
1384
- machine,
1385
- parse
1386
- };
1
+ import{createAnatomy}from"@zag-js/anatomy";var anatomy=createAnatomy("color-picker",["root","label","control","trigger","positioner","content","area","areaThumb","areaBackground","channelSlider","channelSliderTrack","channelSliderThumb","channelInput","transparencyGrid","swatchGroup","swatchTrigger","swatchIndicator","swatch","eyeDropperTrigger","formatTrigger","formatSelect"]);var parts=anatomy.build();import{getColorAreaGradient,normalizeColor}from"@zag-js/color-utils";import{getEventKey,getEventPoint,getEventStep,getNativeEvent,isLeftClick,isModifierKey}from"@zag-js/dom-event";import{dataAttr,query,visuallyHiddenStyle}from"@zag-js/dom-query";import{getPlacementStyles}from"@zag-js/popper";import{getRelativePoint}from"@zag-js/dom-event";import{createScope,queryAll}from"@zag-js/dom-query";var dom=createScope({getRootId:ctx=>ctx.ids?.root??`color-picker:${ctx.id}`,getLabelId:ctx=>ctx.ids?.label??`color-picker:${ctx.id}:label`,getHiddenInputId:ctx=>`color-picker:${ctx.id}:hidden-input`,getControlId:ctx=>ctx.ids?.control??`color-picker:${ctx.id}:control`,getTriggerId:ctx=>ctx.ids?.trigger??`color-picker:${ctx.id}:trigger`,getContentId:ctx=>ctx.ids?.content??`color-picker:${ctx.id}:content`,getPositionerId:ctx=>`color-picker:${ctx.id}:positioner`,getFormatSelectId:ctx=>`color-picker:${ctx.id}:format-select`,getAreaId:ctx=>ctx.ids?.area??`color-picker:${ctx.id}:area`,getAreaGradientId:ctx=>ctx.ids?.areaGradient??`color-picker:${ctx.id}:area-gradient`,getAreaThumbId:ctx=>ctx.ids?.areaThumb??`color-picker:${ctx.id}:area-thumb`,getChannelSliderId:(ctx,channel)=>ctx.ids?.channelSliderTrack?.(channel)??`color-picker:${ctx.id}:slider-track:${channel}`,getChannelSliderThumbId:(ctx,channel)=>ctx.ids?.channelSliderThumb?.(channel)??`color-picker:${ctx.id}:slider-thumb:${channel}`,getContentEl:ctx=>dom.getById(ctx,dom.getContentId(ctx)),getAreaThumbEl:ctx=>dom.getById(ctx,dom.getAreaThumbId(ctx)),getChannelSliderThumbEl:(ctx,channel)=>dom.getById(ctx,dom.getChannelSliderThumbId(ctx,channel)),getChannelInputEl:(ctx,channel)=>{return[...queryAll(dom.getContentEl(ctx),`input[data-channel="${channel}"]`),...queryAll(dom.getControlEl(ctx),`input[data-channel="${channel}"]`)]},getFormatSelectEl:ctx=>dom.getById(ctx,dom.getFormatSelectId(ctx)),getHiddenInputEl:ctx=>dom.getById(ctx,dom.getHiddenInputId(ctx)),getAreaEl:ctx=>dom.getById(ctx,dom.getAreaId(ctx)),getAreaValueFromPoint(ctx,point){const areaEl=dom.getAreaEl(ctx);if(!areaEl)return;const{percent}=getRelativePoint(point,areaEl);return percent},getControlEl:ctx=>dom.getById(ctx,dom.getControlId(ctx)),getTriggerEl:ctx=>dom.getById(ctx,dom.getTriggerId(ctx)),getPositionerEl:ctx=>dom.getById(ctx,dom.getPositionerId(ctx)),getChannelSliderTrackEl:(ctx,channel)=>{return dom.getById(ctx,dom.getChannelSliderId(ctx,channel))},getChannelSliderValueFromPoint(ctx,point,channel){const trackEl=dom.getChannelSliderTrackEl(ctx,channel);if(!trackEl)return;const{percent}=getRelativePoint(point,trackEl);return percent},getChannelInputEls:ctx=>{return[...queryAll(dom.getContentEl(ctx),"input[data-channel]"),...queryAll(dom.getControlEl(ctx),"input[data-channel]")]}});import{parseColor}from"@zag-js/color-utils";function getChannelDisplayColor(color,channel){switch(channel){case"hue":return parseColor(`hsl(${color.getChannelValue("hue")}, 100%, 50%)`);case"lightness":case"brightness":case"saturation":case"red":case"green":case"blue":return color.withChannelValue("alpha",1);case"alpha":{return color}default:throw new Error("Unknown color channel: "+channel)}}import{parseColor as parseColor2}from"@zag-js/color-utils";function getChannelValue(color,channel){if(channel==null)return"";if(channel==="hex"){return color.toString("hex")}if(channel==="css"){return color.toString("css")}if(channel in color){return color.getChannelValue(channel).toString()}const isHSL=color.getFormat()==="hsla";switch(channel){case"hue":return isHSL?color.toFormat("hsla").getChannelValue("hue").toString():color.toFormat("hsba").getChannelValue("hue").toString();case"saturation":return isHSL?color.toFormat("hsla").getChannelValue("saturation").toString():color.toFormat("hsba").getChannelValue("saturation").toString();case"lightness":return color.toFormat("hsla").getChannelValue("lightness").toString();case"brightness":return color.toFormat("hsba").getChannelValue("brightness").toString();case"red":case"green":case"blue":return color.toFormat("rgba").getChannelValue(channel).toString();default:return color.getChannelValue(channel).toString()}}function getChannelRange(color,channel){switch(channel){case"hex":const minColor=parseColor2("#000000");const maxColor=parseColor2("#FFFFFF");return{minValue:minColor.toHexInt(),maxValue:maxColor.toHexInt(),pageSize:10,step:1};case"css":return void 0;case"hue":case"saturation":case"lightness":return color.toFormat("hsla").getChannelRange(channel);case"brightness":return color.toFormat("hsba").getChannelRange(channel);case"red":case"green":case"blue":return color.toFormat("rgba").getChannelRange(channel);default:return color.getChannelRange(channel)}}function getSliderBackgroundDirection(orientation,dir){if(orientation==="vertical"){return"top"}else if(dir==="ltr"){return"right"}else{return"left"}}var getSliderBackground=props=>{const{channel,value,dir}=props;const bgDirection=getSliderBackgroundDirection(props.orientation,dir);const{minValue,maxValue}=value.getChannelRange(channel);switch(channel){case"hue":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%)`;case"lightness":{let start=value.withChannelValue(channel,minValue).toString("css");let middle=value.withChannelValue(channel,(maxValue-minValue)/2).toString("css");let end=value.withChannelValue(channel,maxValue).toString("css");return`linear-gradient(to ${bgDirection}, ${start}, ${middle}, ${end})`}case"saturation":case"brightness":case"red":case"green":case"blue":case"alpha":{let start=value.withChannelValue(channel,minValue).toString("css");let end=value.withChannelValue(channel,maxValue).toString("css");return`linear-gradient(to ${bgDirection}, ${start}, ${end})`}default:throw new Error("Unknown color channel: "+channel)}};function connect(state,send,normalize){const value=state.context.value;const areaValue=state.context.areaValue;const valueAsString=state.context.valueAsString;const disabled=state.context.isDisabled;const interactive=state.context.isInteractive;const dragging=state.hasTag("dragging");const open=state.hasTag("open");const focused=state.hasTag("focused");const getAreaChannels=props=>{const channels=areaValue.getChannels();return{xChannel:props.xChannel??channels[1],yChannel:props.yChannel??channels[2]}};const currentPlacement=state.context.currentPlacement;const popperStyles=getPlacementStyles({...state.context.positioning,placement:currentPlacement});function getSwatchTriggerState(props){const color=normalizeColor(props.value).toFormat(state.context.format);return{value:color,valueAsString:color.toString("hex"),checked:color.isEqual(value),disabled:props.disabled||!interactive}}return{dragging,open,valueAsString,value,setOpen(nextOpen){if(nextOpen===open)return;send({type:nextOpen?"OPEN":"CLOSE"})},setValue(value2){send({type:"VALUE.SET",value:normalizeColor(value2),src:"set-color"})},getChannelValue(channel){return getChannelValue(value,channel)},setChannelValue(channel,channelValue){const color=value.withChannelValue(channel,channelValue);send({type:"VALUE.SET",value:color,src:"set-channel"})},format:state.context.format,setFormat(format){const formatValue=value.toFormat(format);send({type:"VALUE.SET",value:formatValue,src:"set-format"})},alpha:value.getChannelValue("alpha"),setAlpha(alphaValue){const color=value.withChannelValue("alpha",alphaValue);send({type:"VALUE.SET",value:color,src:"set-alpha"})},rootProps:normalize.element({...parts.root.attrs,dir:state.context.dir,id:dom.getRootId(state.context),"data-disabled":dataAttr(disabled),"data-readonly":dataAttr(state.context.readOnly),style:{"--value":value.toString("css")}}),labelProps:normalize.element({...parts.label.attrs,dir:state.context.dir,id:dom.getLabelId(state.context),htmlFor:dom.getHiddenInputId(state.context),"data-disabled":dataAttr(disabled),"data-readonly":dataAttr(state.context.readOnly),"data-focus":dataAttr(focused),onClick(event){event.preventDefault();const inputEl=query(dom.getControlEl(state.context),"[data-channel=hex]");inputEl?.focus({preventScroll:true})}}),controlProps:normalize.element({...parts.control.attrs,id:dom.getControlId(state.context),dir:state.context.dir,"data-disabled":dataAttr(disabled),"data-readonly":dataAttr(state.context.readOnly),"data-state":open?"open":"closed","data-focus":dataAttr(focused)}),triggerProps:normalize.button({...parts.trigger.attrs,id:dom.getTriggerId(state.context),dir:state.context.dir,disabled,"aria-label":`select color. current color is ${valueAsString}`,"aria-controls":dom.getContentId(state.context),"aria-labelledby":dom.getLabelId(state.context),"data-disabled":dataAttr(disabled),"data-readonly":dataAttr(state.context.readOnly),"data-placement":currentPlacement,"aria-expanded":dataAttr(open),"data-state":open?"open":"closed","data-focus":dataAttr(focused),type:"button",onClick(){if(!interactive)return;send({type:"TRIGGER.CLICK"})},onBlur(){if(!interactive)return;send({type:"TRIGGER.BLUR"})},style:{position:"relative"}}),positionerProps:normalize.element({...parts.positioner.attrs,id:dom.getPositionerId(state.context),dir:state.context.dir,style:popperStyles.floating}),contentProps:normalize.element({...parts.content.attrs,id:dom.getContentId(state.context),dir:state.context.dir,"data-placement":currentPlacement,"data-state":open?"open":"closed",hidden:!open}),getAreaProps(props={}){const{xChannel,yChannel}=getAreaChannels(props);const{areaStyles}=getColorAreaGradient(areaValue,{xChannel,yChannel,dir:state.context.dir});return normalize.element({...parts.area.attrs,id:dom.getAreaId(state.context),role:"group",onPointerDown(event){if(!interactive)return;const evt=getNativeEvent(event);if(!isLeftClick(evt)||isModifierKey(evt))return;const point=getEventPoint(evt);const channel={xChannel,yChannel};send({type:"AREA.POINTER_DOWN",point,channel,id:"area"});event.preventDefault()},style:{position:"relative",touchAction:"none",forcedColorAdjust:"none",...areaStyles}})},getAreaBackgroundProps(props={}){const{xChannel,yChannel}=getAreaChannels(props);const{areaGradientStyles}=getColorAreaGradient(areaValue,{xChannel,yChannel,dir:state.context.dir});return normalize.element({...parts.areaBackground.attrs,id:dom.getAreaGradientId(state.context),style:{position:"relative",touchAction:"none",forcedColorAdjust:"none",...areaGradientStyles}})},getAreaThumbProps(props={}){const{xChannel,yChannel}=getAreaChannels(props);const channel={xChannel,yChannel};const xPercent=areaValue.getChannelValuePercent(xChannel);const yPercent=1-areaValue.getChannelValuePercent(yChannel);const xValue=areaValue.getChannelValue(xChannel);const yValue=areaValue.getChannelValue(yChannel);return normalize.element({...parts.areaThumb.attrs,id:dom.getAreaThumbId(state.context),dir:state.context.dir,tabIndex:disabled?void 0:0,"data-disabled":dataAttr(disabled),role:"slider","aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":xValue,"aria-label":`${xChannel} and ${yChannel}`,"aria-roledescription":"2d slider","aria-valuetext":`${xChannel} ${xValue}, ${yChannel} ${yValue}`,style:{position:"absolute",left:`${xPercent*100}%`,top:`${yPercent*100}%`,transform:"translate(-50%, -50%)",touchAction:"none",forcedColorAdjust:"none",background:areaValue.withChannelValue("alpha",1).toString("css")},onFocus(){if(!interactive)return;send({type:"AREA.FOCUS",id:"area",channel})},onKeyDown(event){if(event.defaultPrevented)return;if(!interactive)return;const step=getEventStep(event);const keyMap={ArrowUp(){send({type:"AREA.ARROW_UP",channel,step})},ArrowDown(){send({type:"AREA.ARROW_DOWN",channel,step})},ArrowLeft(){send({type:"AREA.ARROW_LEFT",channel,step})},ArrowRight(){send({type:"AREA.ARROW_RIGHT",channel,step})},PageUp(){send({type:"AREA.PAGE_UP",channel,step})},PageDown(){send({type:"AREA.PAGE_DOWN",channel,step})},Escape(event2){event2.stopPropagation()}};const exec=keyMap[getEventKey(event,state.context)];if(exec){exec(event);event.preventDefault()}}})},getTransparencyGridProps(props={}){const{size="12px"}=props;return normalize.element({...parts.transparencyGrid.attrs,style:{"--size":size,width:"100%",height:"100%",position:"absolute",backgroundColor:"#fff",backgroundImage:"conic-gradient(#eeeeee 0 25%, transparent 0 50%, #eeeeee 0 75%, transparent 0)",backgroundSize:"var(--size) var(--size)",inset:"0px",zIndex:"auto",pointerEvents:"none"}})},getChannelSliderProps(props){const{orientation="horizontal",channel}=props;return normalize.element({...parts.channelSlider.attrs,"data-channel":channel,"data-orientation":orientation,role:"presentation",onPointerDown(event){if(!interactive)return;const evt=getNativeEvent(event);if(!isLeftClick(evt)||isModifierKey(evt))return;const point=getEventPoint(evt);send({type:"CHANNEL_SLIDER.POINTER_DOWN",channel,point,id:channel,orientation});event.preventDefault()},style:{position:"relative",touchAction:"none"}})},getChannelSliderTrackProps(props){const{orientation="horizontal",channel}=props;return normalize.element({...parts.channelSliderTrack.attrs,id:dom.getChannelSliderId(state.context,channel),role:"group","data-channel":channel,"data-orientation":orientation,style:{position:"relative",forcedColorAdjust:"none",backgroundImage:getSliderBackground({orientation,channel,dir:state.context.dir,value:areaValue})}})},getChannelSliderThumbProps(props){const{orientation="horizontal",channel}=props;const{minValue,maxValue,step:stepValue}=areaValue.getChannelRange(channel);const channelValue=areaValue.getChannelValue(channel);const offset=(channelValue-minValue)/(maxValue-minValue);const placementStyles=orientation==="horizontal"?{left:`${offset*100}%`,top:"50%"}:{top:`${offset*100}%`,left:"50%"};return normalize.element({...parts.channelSliderThumb.attrs,id:dom.getChannelSliderThumbId(state.context,channel),role:"slider","aria-label":channel,tabIndex:disabled?void 0:0,"data-channel":channel,"data-disabled":dataAttr(disabled),"data-orientation":orientation,"aria-disabled":dataAttr(disabled),"aria-orientation":orientation,"aria-valuemax":maxValue,"aria-valuemin":minValue,"aria-valuenow":channelValue,"aria-valuetext":`${channel} ${channelValue}`,style:{forcedColorAdjust:"none",position:"absolute",background:getChannelDisplayColor(areaValue,channel).toString("css"),...placementStyles},onFocus(){if(!interactive)return;send({type:"CHANNEL_SLIDER.FOCUS",channel})},onKeyDown(event){if(event.defaultPrevented)return;if(!interactive)return;const step=getEventStep(event)*stepValue;const keyMap={ArrowUp(){send({type:"CHANNEL_SLIDER.ARROW_UP",channel,step})},ArrowDown(){send({type:"CHANNEL_SLIDER.ARROW_DOWN",channel,step})},ArrowLeft(){send({type:"CHANNEL_SLIDER.ARROW_LEFT",channel,step})},ArrowRight(){send({type:"CHANNEL_SLIDER.ARROW_RIGHT",channel,step})},PageUp(){send({type:"CHANNEL_SLIDER.PAGE_UP",channel})},PageDown(){send({type:"CHANNEL_SLIDER.PAGE_DOWN",channel})},Home(){send({type:"CHANNEL_SLIDER.HOME",channel})},End(){send({type:"CHANNEL_SLIDER.END",channel})},Escape(event2){event2.stopPropagation()}};const exec=keyMap[getEventKey(event,state.context)];if(exec){exec(event);event.preventDefault()}}})},getChannelInputProps(props){const{channel}=props;const isTextField=channel==="hex"||channel==="css";const range=getChannelRange(value,channel);return normalize.input({...parts.channelInput.attrs,dir:state.context.dir,type:isTextField?"text":"number","data-channel":channel,"aria-label":channel,spellCheck:false,autoComplete:"off",disabled,"data-disabled":dataAttr(disabled),readOnly:state.context.readOnly,defaultValue:getChannelValue(value,channel),min:range?.minValue,max:range?.maxValue,step:range?.step,onBeforeInput(event){if(isTextField||!interactive)return;const value2=event.currentTarget.value;if(value2.match(/[^0-9.]/g)){event.preventDefault()}},onFocus(event){if(!interactive)return;send({type:"CHANNEL_INPUT.FOCUS",channel});event.target.select()},onBlur(event){if(!interactive)return;const value2=isTextField?event.currentTarget.value:event.currentTarget.valueAsNumber;send({type:"CHANNEL_INPUT.BLUR",channel,value:value2,isTextField})},onKeyDown(event){if(event.defaultPrevented)return;if(!interactive)return;if(event.key==="Enter"){const value2=isTextField?event.currentTarget.value:event.currentTarget.valueAsNumber;send({type:"CHANNEL_INPUT.CHANGE",channel,value:value2,isTextField});event.preventDefault()}},style:{appearance:"none",WebkitAppearance:"none",MozAppearance:"textfield"}})},hiddenInputProps:normalize.input({type:"text",disabled,name:state.context.name,id:dom.getHiddenInputId(state.context),style:visuallyHiddenStyle,defaultValue:valueAsString}),eyeDropperTriggerProps:normalize.button({...parts.eyeDropperTrigger.attrs,type:"button",dir:state.context.dir,disabled,"data-disabled":dataAttr(disabled),"aria-label":"Pick a color from the screen",onClick(){if(!interactive)return;send("EYEDROPPER.CLICK")}}),swatchGroupProps:normalize.element({...parts.swatchGroup.attrs,role:"group"}),getSwatchTriggerState,getSwatchTriggerProps(props){const triggerState=getSwatchTriggerState(props);return normalize.button({...parts.swatchTrigger.attrs,disabled:triggerState.disabled,dir:state.context.dir,type:"button","aria-label":`select ${triggerState.valueAsString} as the color`,"data-state":triggerState.checked?"checked":"unchecked","data-value":triggerState.valueAsString,"data-disabled":dataAttr(triggerState.disabled),onClick(){if(triggerState.disabled)return;send({type:"SWATCH_TRIGGER.CLICK",value:triggerState.value})},style:{position:"relative"}})},getSwatchIndicatorProps(props){const triggerState=getSwatchTriggerState(props);return normalize.element({...parts.swatchIndicator.attrs,dir:state.context.dir,hidden:!triggerState.checked})},getSwatchProps(props){const{respectAlpha=true}=props;const triggerState=getSwatchTriggerState(props);return normalize.element({...parts.swatch.attrs,dir:state.context.dir,"data-state":triggerState.checked?"checked":"unchecked","data-value":triggerState.valueAsString,style:{position:"relative",background:triggerState.value.toString(respectAlpha?"css":"hex")}})},formatTriggerProps:normalize.button({...parts.formatTrigger.attrs,dir:state.context.dir,type:"button","aria-label":`change color format to ${getNextFormat(state.context.format)}`,onClick(event){if(event.currentTarget.disabled)return;const nextFormat=getNextFormat(state.context.format);send({type:"FORMAT.SET",format:nextFormat,src:"format-trigger"})}}),formatSelectProps:normalize.select({...parts.formatSelect.attrs,"aria-label":"change color format",dir:state.context.dir,defaultValue:state.context.format,disabled,onChange(event){const format=assertFormat(event.currentTarget.value);send({type:"FORMAT.SET",format,src:"format-select"})}})}}var formats=["hsba","hsla","rgba"];var formatRegex=new RegExp(`^(${formats.join("|")})$`);function getNextFormat(format){const index=formats.indexOf(format);return formats[index+1]??formats[0]}function assertFormat(format){if(formatRegex.test(format))return format;throw new Error(`Unsupported color format: ${format}`)}import{parseColor as parseColor4}from"@zag-js/color-utils";import{createMachine,guards}from"@zag-js/core";import{trackDismissableElement}from"@zag-js/dismissable";import{trackPointerMove}from"@zag-js/dom-event";import{getInitialFocus,raf}from"@zag-js/dom-query";import{dispatchInputValueEvent,trackFormControl}from"@zag-js/form-utils";import{getPlacement}from"@zag-js/popper";import{disableTextSelection}from"@zag-js/text-selection";import{compact,tryCatch}from"@zag-js/utils";import{parseColor as parseColor3}from"@zag-js/color-utils";var parse=colorString=>{return parseColor3(colorString)};var{and}=guards;function machine(userContext){const ctx=compact(userContext);return createMachine({id:"color-picker",initial:ctx.open?"open":"idle",context:{dir:"ltr",value:parse("#000000"),format:"rgba",disabled:false,closeOnSelect:false,...ctx,activeId:null,activeChannel:null,activeOrientation:null,fieldsetDisabled:false,restoreFocus:true,positioning:{...ctx.positioning,placement:"bottom"}},computed:{isRtl:ctx2=>ctx2.dir==="rtl",isDisabled:ctx2=>!!ctx2.disabled||ctx2.fieldsetDisabled,isInteractive:ctx2=>!(ctx2.isDisabled||ctx2.readOnly),valueAsString:ctx2=>ctx2.value.toString(ctx2.format),areaValue:ctx2=>{const format=ctx2.format.startsWith("hsl")?"hsla":"hsba";return ctx2.value.toFormat(format)}},activities:["trackFormControl"],watch:{value:["syncInputElements"],format:["syncFormatSelectElement"],open:["toggleVisibility"]},on:{"VALUE.SET":{actions:["setValue"]},"FORMAT.SET":{actions:["setFormat"]},"CHANNEL_INPUT.CHANGE":{actions:["setChannelColorFromInput"]},"EYEDROPPER.CLICK":{actions:["openEyeDropper"]}},states:{idle:{tags:["closed"],on:{"CONTROLLED.OPEN":{target:"open",actions:["setInitialFocus"]},OPEN:[{guard:"isOpenControlled",actions:["invokeOnOpen"]},{target:"open",actions:["invokeOnOpen","setInitialFocus"]}],"TRIGGER.CLICK":[{guard:"isOpenControlled",actions:["invokeOnOpen"]},{target:"open",actions:["invokeOnOpen","setInitialFocus"]}],"CHANNEL_INPUT.FOCUS":{target:"focused",actions:["setActiveChannel"]}}},focused:{tags:["closed","focused"],on:{"CONTROLLED.OPEN":{target:"open",actions:["setInitialFocus"]},OPEN:[{guard:"isOpenControlled",actions:["invokeOnOpen"]},{target:"open",actions:["invokeOnOpen","setInitialFocus"]}],"TRIGGER.CLICK":[{guard:"isOpenControlled",actions:["invokeOnOpen"]},{target:"open",actions:["invokeOnOpen","setInitialFocus"]}],"CHANNEL_INPUT.FOCUS":{actions:["setActiveChannel"]},"CHANNEL_INPUT.BLUR":{target:"idle",actions:["setChannelColorFromInput"]},"TRIGGER.BLUR":{target:"idle"}}},open:{tags:["open"],activities:["trackPositioning","trackDismissableElement"],on:{"CONTROLLED.CLOSE":[{guard:"shouldRestoreFocus",target:"focused",actions:["setReturnFocus"]},{target:"idle"}],"TRIGGER.CLICK":[{guard:"isOpenControlled",actions:["invokeOnClose"]},{target:"idle",actions:["invokeOnClose"]}],"AREA.POINTER_DOWN":{target:"open:dragging",actions:["setActiveChannel","setAreaColorFromPoint","focusAreaThumb"]},"AREA.FOCUS":{actions:["setActiveChannel"]},"CHANNEL_SLIDER.POINTER_DOWN":{target:"open:dragging",actions:["setActiveChannel","setChannelColorFromPoint","focusChannelThumb"]},"CHANNEL_SLIDER.FOCUS":{actions:["setActiveChannel"]},"AREA.ARROW_LEFT":{actions:["decrementXChannel"]},"AREA.ARROW_RIGHT":{actions:["incrementXChannel"]},"AREA.ARROW_UP":{actions:["incrementYChannel"]},"AREA.ARROW_DOWN":{actions:["decrementYChannel"]},"AREA.PAGE_UP":{actions:["incrementXChannel"]},"AREA.PAGE_DOWN":{actions:["decrementXChannel"]},"CHANNEL_SLIDER.ARROW_LEFT":{actions:["decrementChannel"]},"CHANNEL_SLIDER.ARROW_RIGHT":{actions:["incrementChannel"]},"CHANNEL_SLIDER.ARROW_UP":{actions:["incrementChannel"]},"CHANNEL_SLIDER.ARROW_DOWN":{actions:["decrementChannel"]},"CHANNEL_SLIDER.PAGE_UP":{actions:["incrementChannel"]},"CHANNEL_SLIDER.PAGE_DOWN":{actions:["decrementChannel"]},"CHANNEL_SLIDER.HOME":{actions:["setChannelToMin"]},"CHANNEL_SLIDER.END":{actions:["setChannelToMax"]},"CHANNEL_INPUT.BLUR":{actions:["setChannelColorFromInput"]},INTERACT_OUTSIDE:[{guard:"isOpenControlled",actions:["invokeOnClose"]},{guard:"shouldRestoreFocus",target:"focused",actions:["invokeOnClose","setReturnFocus"]},{target:"idle",actions:["invokeOnClose"]}],CLOSE:[{guard:"isOpenControlled",actions:["invokeOnClose"]},{target:"idle",actions:["invokeOnClose"]}],"SWATCH_TRIGGER.CLICK":[{guard:and("isOpenControlled","closeOnSelect"),actions:["setValue","invokeOnClose"]},{guard:"closeOnSelect",target:"focused",actions:["setValue","invokeOnClose","setReturnFocus"]},{actions:["setValue"]}]}},"open:dragging":{tags:["open"],exit:["clearActiveChannel"],activities:["trackPointerMove","disableTextSelection","trackPositioning","trackDismissableElement"],on:{"CONTROLLED.CLOSE":[{guard:"shouldRestoreFocus",target:"focused",actions:["setReturnFocus"]},{target:"idle"}],"AREA.POINTER_MOVE":{actions:["setAreaColorFromPoint","focusAreaThumb"]},"AREA.POINTER_UP":{target:"open",actions:["invokeOnChangeEnd"]},"CHANNEL_SLIDER.POINTER_MOVE":{actions:["setChannelColorFromPoint","focusChannelThumb"]},"CHANNEL_SLIDER.POINTER_UP":{target:"open",actions:["invokeOnChangeEnd"]},INTERACT_OUTSIDE:[{guard:"isOpenControlled",actions:["invokeOnClose"]},{guard:"shouldRestoreFocus",target:"focused",actions:["invokeOnClose","setReturnFocus"]},{target:"idle",actions:["invokeOnClose"]}],CLOSE:[{guard:"isOpenControlled",actions:["invokeOnClose"]},{target:"idle",actions:["invokeOnClose"]}]}}}},{guards:{closeOnSelect:ctx2=>!!ctx2.closeOnSelect,isOpenControlled:ctx2=>!!ctx2["open.controlled"],shouldRestoreFocus:ctx2=>!!ctx2.restoreFocus},activities:{trackPositioning(ctx2){ctx2.currentPlacement=ctx2.positioning.placement;const anchorEl=dom.getTriggerEl(ctx2);const getPositionerEl=()=>dom.getPositionerEl(ctx2);return getPlacement(anchorEl,getPositionerEl,{...ctx2.positioning,defer:true,onComplete(data){ctx2.currentPlacement=data.placement}})},trackDismissableElement(ctx2,_evt,{send}){const getContentEl=()=>dom.getContentEl(ctx2);return trackDismissableElement(getContentEl,{exclude:dom.getTriggerEl(ctx2),defer:true,onInteractOutside(event){ctx2.onInteractOutside?.(event);if(event.defaultPrevented)return;ctx2.restoreFocus=!(event.detail.focusable||event.detail.contextmenu)},onPointerDownOutside:ctx2.onPointerDownOutside,onFocusOutside:ctx2.onFocusOutside,onDismiss(){send({type:"INTERACT_OUTSIDE"})}})},trackFormControl(ctx2,_evt,{send,initialContext}){const inputEl=dom.getHiddenInputEl(ctx2);return trackFormControl(inputEl,{onFieldsetDisabledChange(disabled){ctx2.fieldsetDisabled=disabled},onFormReset(){send({type:"VALUE.SET",value:initialContext.value,src:"form.reset"})}})},trackPointerMove(ctx2,_evt,{send}){return trackPointerMove(dom.getDoc(ctx2),{onPointerMove({point}){const type=ctx2.activeId==="area"?"AREA.POINTER_MOVE":"CHANNEL_SLIDER.POINTER_MOVE";send({type,point})},onPointerUp(){const type=ctx2.activeId==="area"?"AREA.POINTER_UP":"CHANNEL_SLIDER.POINTER_UP";send({type})}})},disableTextSelection(ctx2){return disableTextSelection({doc:dom.getDoc(ctx2),target:dom.getContentEl(ctx2)})}},actions:{openEyeDropper(ctx2){const isSupported="EyeDropper"in dom.getWin(ctx2);if(!isSupported)return;const win=dom.getWin(ctx2);const picker=new win.EyeDropper;picker.open().then(({sRGBHex})=>{const format=ctx2.value.getFormat();const color=parseColor4(sRGBHex).toFormat(format);set.value(ctx2,color);ctx2.onValueChangeEnd?.({value:ctx2.value,valueAsString:ctx2.valueAsString})}).catch(()=>void 0)},setActiveChannel(ctx2,evt){ctx2.activeId=evt.id;if(evt.channel)ctx2.activeChannel=evt.channel;if(evt.orientation)ctx2.activeOrientation=evt.orientation},clearActiveChannel(ctx2){ctx2.activeChannel=null;ctx2.activeId=null;ctx2.activeOrientation=null},setAreaColorFromPoint(ctx2,evt){const{xChannel,yChannel}=evt.channel||ctx2.activeChannel;const percent=dom.getAreaValueFromPoint(ctx2,evt.point);if(!percent)return;const xValue=ctx2.areaValue.getChannelPercentValue(xChannel,percent.x);const yValue=ctx2.areaValue.getChannelPercentValue(yChannel,1-percent.y);const color=ctx2.areaValue.withChannelValue(xChannel,xValue).withChannelValue(yChannel,yValue);set.value(ctx2,color)},setChannelColorFromPoint(ctx2,evt){const channel=evt.channel||ctx2.activeId;const percent=dom.getChannelSliderValueFromPoint(ctx2,evt.point,channel);if(!percent)return;const orientation=ctx2.activeOrientation||"horizontal";const channelPercent=orientation==="horizontal"?percent.x:percent.y;const value=ctx2.areaValue.getChannelPercentValue(channel,channelPercent);const color=ctx2.areaValue.withChannelValue(channel,value);set.value(ctx2,color)},setValue(ctx2,evt){set.value(ctx2,evt.value)},setFormat(ctx2,evt){set.format(ctx2,evt.format)},syncInputElements(ctx2){sync.inputs(ctx2)},invokeOnChangeEnd(ctx2){invoke.changeEnd(ctx2)},setChannelColorFromInput(ctx2,evt){const{channel,isTextField,value}=evt;const currentAlpha=ctx2.value.getChannelValue("alpha");let color;if(channel==="alpha"){let valueAsNumber=parseFloat(value);valueAsNumber=Number.isNaN(valueAsNumber)?currentAlpha:valueAsNumber;color=ctx2.value.withChannelValue("alpha",valueAsNumber)}else if(isTextField){color=tryCatch(()=>parse(value).withChannelValue("alpha",currentAlpha),()=>ctx2.value)}else{const current=ctx2.value.toFormat(ctx2.format);const valueAsNumber=Number.isNaN(value)?current.getChannelValue(channel):value;color=current.withChannelValue(channel,valueAsNumber)}sync.inputs(ctx2,color);set.value(ctx2,color)},incrementChannel(ctx2,evt){const color=ctx2.value.incrementChannel(evt.channel,evt.step);set.value(ctx2,color)},decrementChannel(ctx2,evt){const color=ctx2.value.decrementChannel(evt.channel,evt.step);set.value(ctx2,color)},incrementXChannel(ctx2,evt){const{xChannel}=evt.channel;const color=ctx2.areaValue.incrementChannel(xChannel,evt.step);set.value(ctx2,color)},decrementXChannel(ctx2,evt){const{xChannel}=evt.channel;const color=ctx2.areaValue.decrementChannel(xChannel,evt.step);set.value(ctx2,color)},incrementYChannel(ctx2,evt){const{yChannel}=evt.channel;const color=ctx2.areaValue.incrementChannel(yChannel,evt.step);set.value(ctx2,color)},decrementYChannel(ctx2,evt){const{yChannel}=evt.channel;const color=ctx2.areaValue.decrementChannel(yChannel,evt.step);set.value(ctx2,color)},setChannelToMax(ctx2,evt){const range=ctx2.value.getChannelRange(evt.channel);const color=ctx2.value.withChannelValue(evt.channel,range.maxValue);set.value(ctx2,color)},setChannelToMin(ctx2,evt){const range=ctx2.value.getChannelRange(evt.channel);const color=ctx2.value.withChannelValue(evt.channel,range.minValue);set.value(ctx2,color)},focusAreaThumb(ctx2){raf(()=>{dom.getAreaThumbEl(ctx2)?.focus({preventScroll:true})})},focusChannelThumb(ctx2,evt){raf(()=>{dom.getChannelSliderThumbEl(ctx2,evt.channel)?.focus({preventScroll:true})})},setInitialFocus(ctx2){raf(()=>{const element=getInitialFocus(dom.getContentEl(ctx2),ctx2.initialFocusEl);element?.focus({preventScroll:true})})},setReturnFocus(ctx2){raf(()=>{dom.getTriggerEl(ctx2)?.focus({preventScroll:true})})},syncFormatSelectElement(ctx2){sync.formatSelect(ctx2)},invokeOnOpen(ctx2){ctx2.onOpenChange?.({open:true})},invokeOnClose(ctx2){ctx2.onOpenChange?.({open:false})},toggleVisibility(ctx2,evt,{send}){send({type:ctx2.open?"CONTROLLED.OPEN":"CONTROLLED.CLOSE",previousEvent:evt})}},compareFns:{value:(a,b)=>a.isEqual(b)}})}var sync={inputs(ctx,color){const channelInputs=dom.getChannelInputEls(ctx);raf(()=>{channelInputs.forEach(inputEl=>{const channel=inputEl.dataset.channel;dom.setValue(inputEl,getChannelValue(color||ctx.value,channel))})})},formatSelect(ctx){const selectEl=dom.getFormatSelectEl(ctx);raf(()=>{dom.setValue(selectEl,ctx.format)})}};var invoke={changeEnd(ctx){const value=ctx.value.toFormat(ctx.format);ctx.onValueChangeEnd?.({value,valueAsString:ctx.valueAsString})},change(ctx){const value=ctx.value.toFormat(ctx.format);ctx.onValueChange?.({value,valueAsString:ctx.valueAsString});dispatchInputValueEvent(dom.getHiddenInputEl(ctx),{value:ctx.valueAsString})},formatChange(ctx){ctx.onFormatChange?.({format:ctx.format})}};var set={value(ctx,color){if(!color||ctx.value.isEqual(color))return;ctx.value=color;invoke.change(ctx)},format(ctx,format){if(ctx.format===format)return;ctx.format=format;invoke.formatChange(ctx)}};export{anatomy,connect,machine,parse};
1387
2
  //# sourceMappingURL=index.mjs.map