@zag-js/color-picker 0.28.1 → 0.30.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.d.mts +47 -10
- package/dist/index.d.ts +47 -10
- package/dist/index.js +220 -89
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +220 -89
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -14
- package/src/color-picker.anatomy.ts +3 -0
- package/src/color-picker.connect.ts +134 -28
- package/src/color-picker.dom.ts +3 -2
- package/src/color-picker.machine.ts +90 -49
- package/src/color-picker.parse.ts +2 -10
- package/src/color-picker.types.ts +49 -8
- package/src/index.ts +5 -1
- package/src/utils/get-channel-input-value.ts +5 -5
package/dist/index.mjs
CHANGED
|
@@ -17,8 +17,11 @@ var anatomy = createAnatomy("color-picker", [
|
|
|
17
17
|
"transparencyGrid",
|
|
18
18
|
"swatchGroup",
|
|
19
19
|
"swatchTrigger",
|
|
20
|
+
"swatchIndicator",
|
|
20
21
|
"swatch",
|
|
21
|
-
"eyeDropperTrigger"
|
|
22
|
+
"eyeDropperTrigger",
|
|
23
|
+
"formatTrigger",
|
|
24
|
+
"formatSelect"
|
|
22
25
|
]);
|
|
23
26
|
var parts = anatomy.build();
|
|
24
27
|
|
|
@@ -49,6 +52,7 @@ var dom = createScope({
|
|
|
49
52
|
getTriggerId: (ctx) => ctx.ids?.trigger ?? `color-picker:${ctx.id}:trigger`,
|
|
50
53
|
getContentId: (ctx) => ctx.ids?.content ?? `color-picker:${ctx.id}:content`,
|
|
51
54
|
getPositionerId: (ctx) => `color-picker:${ctx.id}:positioner`,
|
|
55
|
+
getFormatSelectId: (ctx) => `color-picker:${ctx.id}:format-select`,
|
|
52
56
|
getAreaId: (ctx) => ctx.ids?.area ?? `color-picker:${ctx.id}:area`,
|
|
53
57
|
getAreaGradientId: (ctx) => ctx.ids?.areaGradient ?? `color-picker:${ctx.id}:area-gradient`,
|
|
54
58
|
getAreaThumbId: (ctx) => ctx.ids?.areaThumb ?? `color-picker:${ctx.id}:area-thumb`,
|
|
@@ -63,6 +67,7 @@ var dom = createScope({
|
|
|
63
67
|
...queryAll(dom.getControlEl(ctx), `input[data-channel="${channel}"]`)
|
|
64
68
|
];
|
|
65
69
|
},
|
|
70
|
+
getFormatSelectEl: (ctx) => dom.getById(ctx, dom.getFormatSelectId(ctx)),
|
|
66
71
|
getHiddenInputEl: (ctx) => dom.getById(ctx, dom.getHiddenInputId(ctx)),
|
|
67
72
|
getAreaEl: (ctx) => dom.getById(ctx, dom.getAreaId(ctx)),
|
|
68
73
|
getAreaValueFromPoint(ctx, point) {
|
|
@@ -94,10 +99,7 @@ var dom = createScope({
|
|
|
94
99
|
getFirstFocusableEl: (ctx) => getFirstFocusable(dom.getContentEl(ctx), "if-empty"),
|
|
95
100
|
getInitialFocusEl: (ctx) => {
|
|
96
101
|
let el = runIfFn(ctx.initialFocusEl);
|
|
97
|
-
|
|
98
|
-
el = dom.getFirstFocusableEl(ctx);
|
|
99
|
-
if (!el)
|
|
100
|
-
el = dom.getContentEl(ctx);
|
|
102
|
+
el = dom.getFirstFocusableEl(ctx) ?? dom.getContentEl(ctx);
|
|
101
103
|
return el;
|
|
102
104
|
}
|
|
103
105
|
});
|
|
@@ -142,7 +144,7 @@ function getChannelValue(color, channel) {
|
|
|
142
144
|
case "hue":
|
|
143
145
|
return isHSL ? color.toFormat("hsla").getChannelValue("hue").toString() : color.toFormat("hsba").getChannelValue("hue").toString();
|
|
144
146
|
case "saturation":
|
|
145
|
-
return isHSL ? color.toFormat("
|
|
147
|
+
return isHSL ? color.toFormat("hsla").getChannelValue("saturation").toString() : color.toFormat("hsba").getChannelValue("saturation").toString();
|
|
146
148
|
case "lightness":
|
|
147
149
|
return color.toFormat("hsla").getChannelValue("lightness").toString();
|
|
148
150
|
case "brightness":
|
|
@@ -171,13 +173,13 @@ function getChannelRange(color, channel) {
|
|
|
171
173
|
case "hue":
|
|
172
174
|
case "saturation":
|
|
173
175
|
case "lightness":
|
|
174
|
-
return color.toFormat("
|
|
176
|
+
return color.toFormat("hsla").getChannelRange(channel);
|
|
175
177
|
case "brightness":
|
|
176
|
-
return color.toFormat("
|
|
178
|
+
return color.toFormat("hsba").getChannelRange(channel);
|
|
177
179
|
case "red":
|
|
178
180
|
case "green":
|
|
179
181
|
case "blue":
|
|
180
|
-
return color.toFormat("
|
|
182
|
+
return color.toFormat("rgba").getChannelRange(channel);
|
|
181
183
|
default:
|
|
182
184
|
return color.getChannelRange(channel);
|
|
183
185
|
}
|
|
@@ -224,13 +226,15 @@ var getSliderBackground = (props) => {
|
|
|
224
226
|
// src/color-picker.connect.ts
|
|
225
227
|
function connect(state, send, normalize) {
|
|
226
228
|
const value = state.context.value;
|
|
229
|
+
const areaValue = state.context.areaValue;
|
|
227
230
|
const valueAsString = state.context.valueAsString;
|
|
228
231
|
const isDisabled = state.context.isDisabled;
|
|
229
232
|
const isInteractive = state.context.isInteractive;
|
|
230
233
|
const isDragging = state.hasTag("dragging");
|
|
231
234
|
const isOpen = state.hasTag("open");
|
|
235
|
+
const isFocused = state.hasTag("focused");
|
|
232
236
|
const getAreaChannels = (props) => {
|
|
233
|
-
const channels =
|
|
237
|
+
const channels = areaValue.getChannels();
|
|
234
238
|
return {
|
|
235
239
|
xChannel: props.xChannel ?? channels[1],
|
|
236
240
|
yChannel: props.yChannel ?? channels[2]
|
|
@@ -241,11 +245,26 @@ function connect(state, send, normalize) {
|
|
|
241
245
|
...state.context.positioning,
|
|
242
246
|
placement: currentPlacement
|
|
243
247
|
});
|
|
248
|
+
function getSwatchTriggerState(props) {
|
|
249
|
+
const color = normalizeColor(props.value).toFormat(state.context.format);
|
|
250
|
+
return {
|
|
251
|
+
value: color,
|
|
252
|
+
valueAsString: color.toString("hex"),
|
|
253
|
+
isChecked: color.isEqual(value),
|
|
254
|
+
isDisabled: props.disabled || !isInteractive
|
|
255
|
+
};
|
|
256
|
+
}
|
|
244
257
|
return {
|
|
245
258
|
isDragging,
|
|
246
259
|
isOpen,
|
|
247
260
|
valueAsString,
|
|
248
261
|
value,
|
|
262
|
+
open() {
|
|
263
|
+
send({ type: "OPEN" });
|
|
264
|
+
},
|
|
265
|
+
close() {
|
|
266
|
+
send({ type: "CLOSE" });
|
|
267
|
+
},
|
|
249
268
|
setValue(value2) {
|
|
250
269
|
send({ type: "VALUE.SET", value: normalizeColor(value2), src: "set-color" });
|
|
251
270
|
},
|
|
@@ -256,7 +275,7 @@ function connect(state, send, normalize) {
|
|
|
256
275
|
const color = value.withChannelValue(channel, channelValue);
|
|
257
276
|
send({ type: "VALUE.SET", value: color, src: "set-channel" });
|
|
258
277
|
},
|
|
259
|
-
format:
|
|
278
|
+
format: state.context.format,
|
|
260
279
|
setFormat(format) {
|
|
261
280
|
const formatValue = value.toFormat(format);
|
|
262
281
|
send({ type: "VALUE.SET", value: formatValue, src: "set-format" });
|
|
@@ -283,6 +302,7 @@ function connect(state, send, normalize) {
|
|
|
283
302
|
htmlFor: dom.getHiddenInputId(state.context),
|
|
284
303
|
"data-disabled": dataAttr(isDisabled),
|
|
285
304
|
"data-readonly": dataAttr(state.context.readOnly),
|
|
305
|
+
"data-focus": dataAttr(isFocused),
|
|
286
306
|
onClick(event) {
|
|
287
307
|
event.preventDefault();
|
|
288
308
|
const inputEl = query(dom.getControlEl(state.context), "[data-channel=hex]");
|
|
@@ -294,21 +314,35 @@ function connect(state, send, normalize) {
|
|
|
294
314
|
id: dom.getControlId(state.context),
|
|
295
315
|
dir: state.context.dir,
|
|
296
316
|
"data-disabled": dataAttr(isDisabled),
|
|
297
|
-
"data-readonly": dataAttr(state.context.readOnly)
|
|
317
|
+
"data-readonly": dataAttr(state.context.readOnly),
|
|
318
|
+
"data-state": isOpen ? "open" : "closed",
|
|
319
|
+
"data-focus": dataAttr(isFocused)
|
|
298
320
|
}),
|
|
299
321
|
triggerProps: normalize.button({
|
|
300
322
|
...parts.trigger.attrs,
|
|
301
323
|
id: dom.getTriggerId(state.context),
|
|
302
324
|
dir: state.context.dir,
|
|
325
|
+
disabled: isDisabled,
|
|
326
|
+
"aria-label": `select color. current color is ${valueAsString}`,
|
|
303
327
|
"aria-controls": dom.getContentId(state.context),
|
|
304
328
|
"aria-labelledby": dom.getLabelId(state.context),
|
|
305
329
|
"data-disabled": dataAttr(isDisabled),
|
|
306
330
|
"data-readonly": dataAttr(state.context.readOnly),
|
|
307
331
|
"data-placement": currentPlacement,
|
|
332
|
+
"aria-expanded": dataAttr(isOpen),
|
|
333
|
+
"data-state": isOpen ? "open" : "closed",
|
|
334
|
+
"data-focus": dataAttr(isFocused),
|
|
308
335
|
type: "button",
|
|
309
336
|
onClick() {
|
|
337
|
+
if (!isInteractive)
|
|
338
|
+
return;
|
|
310
339
|
send({ type: "TRIGGER.CLICK" });
|
|
311
340
|
},
|
|
341
|
+
onBlur() {
|
|
342
|
+
if (!isInteractive)
|
|
343
|
+
return;
|
|
344
|
+
send({ type: "TRIGGER.BLUR" });
|
|
345
|
+
},
|
|
312
346
|
style: {
|
|
313
347
|
position: "relative"
|
|
314
348
|
}
|
|
@@ -324,11 +358,12 @@ function connect(state, send, normalize) {
|
|
|
324
358
|
id: dom.getContentId(state.context),
|
|
325
359
|
dir: state.context.dir,
|
|
326
360
|
"data-placement": currentPlacement,
|
|
361
|
+
"data-state": isOpen ? "open" : "closed",
|
|
327
362
|
hidden: !isOpen
|
|
328
363
|
}),
|
|
329
364
|
getAreaProps(props = {}) {
|
|
330
365
|
const { xChannel, yChannel } = getAreaChannels(props);
|
|
331
|
-
const { areaStyles } = getColorAreaGradient(
|
|
366
|
+
const { areaStyles } = getColorAreaGradient(areaValue, {
|
|
332
367
|
xChannel,
|
|
333
368
|
yChannel,
|
|
334
369
|
dir: state.context.dir
|
|
@@ -358,7 +393,7 @@ function connect(state, send, normalize) {
|
|
|
358
393
|
},
|
|
359
394
|
getAreaBackgroundProps(props = {}) {
|
|
360
395
|
const { xChannel, yChannel } = getAreaChannels(props);
|
|
361
|
-
const { areaGradientStyles } = getColorAreaGradient(
|
|
396
|
+
const { areaGradientStyles } = getColorAreaGradient(areaValue, {
|
|
362
397
|
xChannel,
|
|
363
398
|
yChannel,
|
|
364
399
|
dir: state.context.dir
|
|
@@ -377,25 +412,35 @@ function connect(state, send, normalize) {
|
|
|
377
412
|
getAreaThumbProps(props = {}) {
|
|
378
413
|
const { xChannel, yChannel } = getAreaChannels(props);
|
|
379
414
|
const channel = { xChannel, yChannel };
|
|
380
|
-
const
|
|
381
|
-
const
|
|
415
|
+
const xPercent = areaValue.getChannelValuePercent(xChannel);
|
|
416
|
+
const yPercent = 1 - areaValue.getChannelValuePercent(yChannel);
|
|
417
|
+
const xValue = areaValue.getChannelValue(xChannel);
|
|
418
|
+
const yValue = areaValue.getChannelValue(yChannel);
|
|
382
419
|
return normalize.element({
|
|
383
420
|
...parts.areaThumb.attrs,
|
|
384
421
|
id: dom.getAreaThumbId(state.context),
|
|
385
422
|
dir: state.context.dir,
|
|
386
423
|
tabIndex: isDisabled ? void 0 : 0,
|
|
387
424
|
"data-disabled": dataAttr(isDisabled),
|
|
388
|
-
role: "
|
|
425
|
+
role: "slider",
|
|
426
|
+
"aria-valuemin": 0,
|
|
427
|
+
"aria-valuemax": 100,
|
|
428
|
+
"aria-valuenow": xValue,
|
|
429
|
+
"aria-label": `${xChannel} and ${yChannel}`,
|
|
430
|
+
"aria-roledescription": "2d slider",
|
|
431
|
+
"aria-valuetext": `${xChannel} ${xValue}, ${yChannel} ${yValue}`,
|
|
389
432
|
style: {
|
|
390
433
|
position: "absolute",
|
|
391
|
-
left: `${
|
|
392
|
-
top: `${
|
|
434
|
+
left: `${xPercent * 100}%`,
|
|
435
|
+
top: `${yPercent * 100}%`,
|
|
393
436
|
transform: "translate(-50%, -50%)",
|
|
394
437
|
touchAction: "none",
|
|
395
438
|
forcedColorAdjust: "none",
|
|
396
|
-
background:
|
|
439
|
+
background: areaValue.withChannelValue("alpha", 1).toString("css")
|
|
397
440
|
},
|
|
398
441
|
onFocus() {
|
|
442
|
+
if (!isInteractive)
|
|
443
|
+
return;
|
|
399
444
|
send({ type: "AREA.FOCUS", id: "area", channel });
|
|
400
445
|
},
|
|
401
446
|
onKeyDown(event) {
|
|
@@ -489,15 +534,15 @@ function connect(state, send, normalize) {
|
|
|
489
534
|
orientation,
|
|
490
535
|
channel,
|
|
491
536
|
dir: state.context.dir,
|
|
492
|
-
value
|
|
537
|
+
value: areaValue
|
|
493
538
|
})
|
|
494
539
|
}
|
|
495
540
|
});
|
|
496
541
|
},
|
|
497
542
|
getChannelSliderThumbProps(props) {
|
|
498
543
|
const { orientation = "horizontal", channel } = props;
|
|
499
|
-
const { minValue, maxValue, step: stepValue } =
|
|
500
|
-
const channelValue =
|
|
544
|
+
const { minValue, maxValue, step: stepValue } = areaValue.getChannelRange(channel);
|
|
545
|
+
const channelValue = areaValue.getChannelValue(channel);
|
|
501
546
|
const offset = (channelValue - minValue) / (maxValue - minValue);
|
|
502
547
|
const placementStyles = orientation === "horizontal" ? { left: `${offset * 100}%`, top: "50%" } : { top: `${offset * 100}%`, left: "50%" };
|
|
503
548
|
return normalize.element({
|
|
@@ -514,10 +559,11 @@ function connect(state, send, normalize) {
|
|
|
514
559
|
"aria-valuemax": maxValue,
|
|
515
560
|
"aria-valuemin": minValue,
|
|
516
561
|
"aria-valuenow": channelValue,
|
|
562
|
+
"aria-valuetext": `${channel} ${channelValue}`,
|
|
517
563
|
style: {
|
|
518
564
|
forcedColorAdjust: "none",
|
|
519
565
|
position: "absolute",
|
|
520
|
-
background: getChannelDisplayColor(
|
|
566
|
+
background: getChannelDisplayColor(areaValue, channel).toString("css"),
|
|
521
567
|
...placementStyles
|
|
522
568
|
},
|
|
523
569
|
onFocus() {
|
|
@@ -576,6 +622,8 @@ function connect(state, send, normalize) {
|
|
|
576
622
|
type: isTextField ? "text" : "number",
|
|
577
623
|
"data-channel": channel,
|
|
578
624
|
"aria-label": channel,
|
|
625
|
+
spellCheck: false,
|
|
626
|
+
autoComplete: "off",
|
|
579
627
|
disabled: isDisabled,
|
|
580
628
|
"data-disabled": dataAttr(isDisabled),
|
|
581
629
|
readOnly: state.context.readOnly,
|
|
@@ -584,7 +632,7 @@ function connect(state, send, normalize) {
|
|
|
584
632
|
max: range?.maxValue,
|
|
585
633
|
step: range?.step,
|
|
586
634
|
onBeforeInput(event) {
|
|
587
|
-
if (isTextField)
|
|
635
|
+
if (isTextField || !isInteractive)
|
|
588
636
|
return;
|
|
589
637
|
const value2 = event.currentTarget.value;
|
|
590
638
|
if (value2.match(/[^0-9.]/g)) {
|
|
@@ -592,14 +640,20 @@ function connect(state, send, normalize) {
|
|
|
592
640
|
}
|
|
593
641
|
},
|
|
594
642
|
onFocus(event) {
|
|
643
|
+
if (!isInteractive)
|
|
644
|
+
return;
|
|
595
645
|
send({ type: "CHANNEL_INPUT.FOCUS", channel });
|
|
596
646
|
event.target.select();
|
|
597
647
|
},
|
|
598
648
|
onBlur(event) {
|
|
649
|
+
if (!isInteractive)
|
|
650
|
+
return;
|
|
599
651
|
const value2 = isTextField ? event.currentTarget.value : event.currentTarget.valueAsNumber;
|
|
600
652
|
send({ type: "CHANNEL_INPUT.BLUR", channel, value: value2, isTextField });
|
|
601
653
|
},
|
|
602
654
|
onKeyDown(event) {
|
|
655
|
+
if (!isInteractive)
|
|
656
|
+
return;
|
|
603
657
|
if (event.key === "Enter") {
|
|
604
658
|
const value2 = isTextField ? event.currentTarget.value : event.currentTarget.valueAsNumber;
|
|
605
659
|
send({ type: "CHANNEL_INPUT.CHANGE", channel, value: value2, isTextField });
|
|
@@ -638,42 +692,87 @@ function connect(state, send, normalize) {
|
|
|
638
692
|
...parts.swatchGroup.attrs,
|
|
639
693
|
role: "group"
|
|
640
694
|
}),
|
|
695
|
+
getSwatchTriggerState,
|
|
641
696
|
getSwatchTriggerProps(props) {
|
|
642
|
-
const
|
|
643
|
-
const color = normalizeColor(valueProp).toFormat(value.getFormat());
|
|
697
|
+
const triggerState = getSwatchTriggerState(props);
|
|
644
698
|
return normalize.button({
|
|
645
699
|
...parts.swatchTrigger.attrs,
|
|
646
|
-
disabled: isDisabled,
|
|
700
|
+
disabled: triggerState.isDisabled,
|
|
647
701
|
dir: state.context.dir,
|
|
648
702
|
type: "button",
|
|
649
|
-
"
|
|
703
|
+
"aria-label": `select ${triggerState.valueAsString} as the color`,
|
|
704
|
+
"data-state": triggerState.isChecked ? "checked" : "unchecked",
|
|
705
|
+
"data-value": triggerState.valueAsString,
|
|
706
|
+
"data-disabled": dataAttr(triggerState.isDisabled),
|
|
650
707
|
onClick() {
|
|
651
|
-
if (
|
|
708
|
+
if (triggerState.isDisabled)
|
|
652
709
|
return;
|
|
653
|
-
send({ type: "
|
|
710
|
+
send({ type: "SWATCH_TRIGGER.CLICK", value: triggerState.value });
|
|
654
711
|
},
|
|
655
712
|
style: {
|
|
656
713
|
position: "relative"
|
|
657
714
|
}
|
|
658
715
|
});
|
|
659
716
|
},
|
|
717
|
+
getSwatchIndicatorProps(props) {
|
|
718
|
+
const triggerState = getSwatchTriggerState(props);
|
|
719
|
+
return normalize.element({
|
|
720
|
+
...parts.swatchIndicator.attrs,
|
|
721
|
+
dir: state.context.dir,
|
|
722
|
+
"aria-hidden": true,
|
|
723
|
+
"data-state": triggerState.isChecked ? "open" : "closed"
|
|
724
|
+
});
|
|
725
|
+
},
|
|
660
726
|
getSwatchProps(props) {
|
|
661
|
-
const {
|
|
662
|
-
const
|
|
663
|
-
const color = colorValue.toFormat(value.getFormat());
|
|
727
|
+
const { respectAlpha = true } = props;
|
|
728
|
+
const triggerState = getSwatchTriggerState(props);
|
|
664
729
|
return normalize.element({
|
|
665
730
|
...parts.swatch.attrs,
|
|
666
731
|
dir: state.context.dir,
|
|
667
|
-
"data-state":
|
|
668
|
-
"data-value":
|
|
732
|
+
"data-state": triggerState.isChecked ? "checked" : "unchecked",
|
|
733
|
+
"data-value": triggerState.valueAsString,
|
|
669
734
|
style: {
|
|
670
735
|
position: "relative",
|
|
671
|
-
background:
|
|
736
|
+
background: triggerState.value.toString(respectAlpha ? "css" : "hex")
|
|
672
737
|
}
|
|
673
738
|
});
|
|
674
|
-
}
|
|
739
|
+
},
|
|
740
|
+
formatTriggerProps: normalize.button({
|
|
741
|
+
...parts.formatTrigger.attrs,
|
|
742
|
+
dir: state.context.dir,
|
|
743
|
+
type: "button",
|
|
744
|
+
"aria-label": `change color format to ${getNextFormat(state.context.format)}`,
|
|
745
|
+
onClick(event) {
|
|
746
|
+
if (event.currentTarget.disabled)
|
|
747
|
+
return;
|
|
748
|
+
const nextFormat = getNextFormat(state.context.format);
|
|
749
|
+
send({ type: "FORMAT.SET", format: nextFormat, src: "format-trigger" });
|
|
750
|
+
}
|
|
751
|
+
}),
|
|
752
|
+
formatSelectProps: normalize.select({
|
|
753
|
+
...parts.formatSelect.attrs,
|
|
754
|
+
"aria-label": "change color format",
|
|
755
|
+
dir: state.context.dir,
|
|
756
|
+
defaultValue: state.context.format,
|
|
757
|
+
disabled: isDisabled,
|
|
758
|
+
onChange(event) {
|
|
759
|
+
const format = assertFormat(event.currentTarget.value);
|
|
760
|
+
send({ type: "FORMAT.SET", format, src: "format-select" });
|
|
761
|
+
}
|
|
762
|
+
})
|
|
675
763
|
};
|
|
676
764
|
}
|
|
765
|
+
var formats = ["hsba", "hsla", "rgba"];
|
|
766
|
+
var formatRegex = new RegExp(`^(${formats.join("|")})$`);
|
|
767
|
+
function getNextFormat(format) {
|
|
768
|
+
const index = formats.indexOf(format);
|
|
769
|
+
return formats[index + 1] ?? formats[0];
|
|
770
|
+
}
|
|
771
|
+
function assertFormat(format) {
|
|
772
|
+
if (formatRegex.test(format))
|
|
773
|
+
return format;
|
|
774
|
+
throw new Error(`Unsupported color format: ${format}`);
|
|
775
|
+
}
|
|
677
776
|
|
|
678
777
|
// src/color-picker.machine.ts
|
|
679
778
|
import { parseColor as parseColor4 } from "@zag-js/color-utils";
|
|
@@ -688,13 +787,8 @@ import { compact, tryCatch } from "@zag-js/utils";
|
|
|
688
787
|
|
|
689
788
|
// src/color-picker.parse.ts
|
|
690
789
|
import { parseColor as parseColor3 } from "@zag-js/color-utils";
|
|
691
|
-
var parse = (
|
|
692
|
-
|
|
693
|
-
const colorFormat = value.getFormat();
|
|
694
|
-
const lockedFormat = colorFormat === "hsla" ? "hsla" : "hsba";
|
|
695
|
-
const result = value.toFormat(lockedFormat);
|
|
696
|
-
result.outputFormat = colorFormat;
|
|
697
|
-
return result;
|
|
790
|
+
var parse = (colorString) => {
|
|
791
|
+
return parseColor3(colorString);
|
|
698
792
|
};
|
|
699
793
|
|
|
700
794
|
// src/color-picker.machine.ts
|
|
@@ -707,13 +801,14 @@ function machine(userContext) {
|
|
|
707
801
|
context: {
|
|
708
802
|
dir: "ltr",
|
|
709
803
|
value: parse("#000000"),
|
|
804
|
+
format: "rgba",
|
|
710
805
|
disabled: false,
|
|
806
|
+
closeOnSelect: false,
|
|
711
807
|
...ctx,
|
|
712
808
|
activeId: null,
|
|
713
809
|
activeChannel: null,
|
|
714
810
|
activeOrientation: null,
|
|
715
811
|
fieldsetDisabled: false,
|
|
716
|
-
autoFocus: true,
|
|
717
812
|
positioning: {
|
|
718
813
|
...ctx.positioning,
|
|
719
814
|
placement: "bottom"
|
|
@@ -723,22 +818,30 @@ function machine(userContext) {
|
|
|
723
818
|
isRtl: (ctx2) => ctx2.dir === "rtl",
|
|
724
819
|
isDisabled: (ctx2) => !!ctx2.disabled || ctx2.fieldsetDisabled,
|
|
725
820
|
isInteractive: (ctx2) => !(ctx2.isDisabled || ctx2.readOnly),
|
|
726
|
-
valueAsString: (ctx2) =>
|
|
727
|
-
|
|
728
|
-
|
|
821
|
+
valueAsString: (ctx2) => ctx2.value.toString(ctx2.format),
|
|
822
|
+
areaValue: (ctx2) => {
|
|
823
|
+
const format = ctx2.format.startsWith("hsl") ? "hsla" : "hsba";
|
|
824
|
+
return ctx2.value.toFormat(format);
|
|
729
825
|
}
|
|
730
826
|
},
|
|
731
827
|
activities: ["trackFormControl"],
|
|
732
828
|
watch: {
|
|
733
829
|
value: ["syncInputElements"],
|
|
830
|
+
format: ["syncFormatSelectElement"],
|
|
734
831
|
open: ["toggleVisibility"]
|
|
735
832
|
},
|
|
736
833
|
on: {
|
|
737
834
|
"VALUE.SET": {
|
|
738
835
|
actions: ["setValue"]
|
|
739
836
|
},
|
|
837
|
+
"FORMAT.SET": {
|
|
838
|
+
actions: ["setFormat"]
|
|
839
|
+
},
|
|
740
840
|
"CHANNEL_INPUT.CHANGE": {
|
|
741
841
|
actions: ["setChannelColorFromInput"]
|
|
842
|
+
},
|
|
843
|
+
"EYEDROPPER.CLICK": {
|
|
844
|
+
actions: ["openEyeDropper"]
|
|
742
845
|
}
|
|
743
846
|
},
|
|
744
847
|
states: {
|
|
@@ -776,6 +879,9 @@ function machine(userContext) {
|
|
|
776
879
|
"CHANNEL_INPUT.BLUR": {
|
|
777
880
|
target: "idle",
|
|
778
881
|
actions: ["setChannelColorFromInput"]
|
|
882
|
+
},
|
|
883
|
+
"TRIGGER.BLUR": {
|
|
884
|
+
target: "idle"
|
|
779
885
|
}
|
|
780
886
|
}
|
|
781
887
|
},
|
|
@@ -787,9 +893,6 @@ function machine(userContext) {
|
|
|
787
893
|
target: "idle",
|
|
788
894
|
actions: ["invokeOnClose"]
|
|
789
895
|
},
|
|
790
|
-
"EYEDROPPER.CLICK": {
|
|
791
|
-
actions: ["openEyeDropper"]
|
|
792
|
-
},
|
|
793
896
|
"AREA.POINTER_DOWN": {
|
|
794
897
|
target: "open:dragging",
|
|
795
898
|
actions: ["setActiveChannel", "setAreaColorFromPoint", "focusAreaThumb"]
|
|
@@ -863,7 +966,17 @@ function machine(userContext) {
|
|
|
863
966
|
CLOSE: {
|
|
864
967
|
target: "idle",
|
|
865
968
|
actions: ["invokeOnClose"]
|
|
866
|
-
}
|
|
969
|
+
},
|
|
970
|
+
"SWATCH_TRIGGER.CLICK": [
|
|
971
|
+
{
|
|
972
|
+
guard: "closeOnSelect",
|
|
973
|
+
target: "focused",
|
|
974
|
+
actions: ["setValue", "setReturnFocus", "invokeOnClose"]
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
actions: ["setValue"]
|
|
978
|
+
}
|
|
979
|
+
]
|
|
867
980
|
}
|
|
868
981
|
},
|
|
869
982
|
"open:dragging": {
|
|
@@ -906,7 +1019,8 @@ function machine(userContext) {
|
|
|
906
1019
|
},
|
|
907
1020
|
{
|
|
908
1021
|
guards: {
|
|
909
|
-
isTargetFocusable: (_ctx, evt) => evt.restoreFocus
|
|
1022
|
+
isTargetFocusable: (_ctx, evt) => evt.restoreFocus,
|
|
1023
|
+
closeOnSelect: (ctx2) => !!ctx2.closeOnSelect
|
|
910
1024
|
},
|
|
911
1025
|
activities: {
|
|
912
1026
|
trackPositioning(ctx2) {
|
|
@@ -1001,9 +1115,9 @@ function machine(userContext) {
|
|
|
1001
1115
|
const percent = dom.getAreaValueFromPoint(ctx2, evt.point);
|
|
1002
1116
|
if (!percent)
|
|
1003
1117
|
return;
|
|
1004
|
-
const xValue = ctx2.
|
|
1005
|
-
const yValue = ctx2.
|
|
1006
|
-
const color = ctx2.
|
|
1118
|
+
const xValue = ctx2.areaValue.getChannelPercentValue(xChannel, percent.x);
|
|
1119
|
+
const yValue = ctx2.areaValue.getChannelPercentValue(yChannel, 1 - percent.y);
|
|
1120
|
+
const color = ctx2.areaValue.withChannelValue(xChannel, xValue).withChannelValue(yChannel, yValue);
|
|
1007
1121
|
set.value(ctx2, color);
|
|
1008
1122
|
},
|
|
1009
1123
|
setChannelColorFromPoint(ctx2, evt) {
|
|
@@ -1013,13 +1127,16 @@ function machine(userContext) {
|
|
|
1013
1127
|
return;
|
|
1014
1128
|
const orientation = ctx2.activeOrientation || "horizontal";
|
|
1015
1129
|
const channelPercent = orientation === "horizontal" ? percent.x : percent.y;
|
|
1016
|
-
const value = ctx2.
|
|
1017
|
-
const color = ctx2.
|
|
1130
|
+
const value = ctx2.areaValue.getChannelPercentValue(channel, channelPercent);
|
|
1131
|
+
const color = ctx2.areaValue.withChannelValue(channel, value);
|
|
1018
1132
|
set.value(ctx2, color);
|
|
1019
1133
|
},
|
|
1020
1134
|
setValue(ctx2, evt) {
|
|
1021
1135
|
set.value(ctx2, evt.value);
|
|
1022
1136
|
},
|
|
1137
|
+
setFormat(ctx2, evt) {
|
|
1138
|
+
set.format(ctx2, evt.format);
|
|
1139
|
+
},
|
|
1023
1140
|
syncInputElements(ctx2) {
|
|
1024
1141
|
sync.inputs(ctx2);
|
|
1025
1142
|
},
|
|
@@ -1029,26 +1146,22 @@ function machine(userContext) {
|
|
|
1029
1146
|
setChannelColorFromInput(ctx2, evt) {
|
|
1030
1147
|
const { channel, isTextField, value } = evt;
|
|
1031
1148
|
const currentAlpha = ctx2.value.getChannelValue("alpha");
|
|
1149
|
+
let color;
|
|
1032
1150
|
if (channel === "alpha") {
|
|
1033
1151
|
let valueAsNumber = parseFloat(value);
|
|
1034
1152
|
valueAsNumber = Number.isNaN(valueAsNumber) ? currentAlpha : valueAsNumber;
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
}
|
|
1039
|
-
if (isTextField) {
|
|
1040
|
-
const color2 = tryCatch(
|
|
1153
|
+
color = ctx2.value.withChannelValue("alpha", valueAsNumber);
|
|
1154
|
+
} else if (isTextField) {
|
|
1155
|
+
color = tryCatch(
|
|
1041
1156
|
() => parse(value).withChannelValue("alpha", currentAlpha),
|
|
1042
1157
|
() => ctx2.value
|
|
1043
1158
|
);
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
set.value(ctx2, color2);
|
|
1049
|
-
return;
|
|
1159
|
+
} else {
|
|
1160
|
+
const current = ctx2.value.toFormat(ctx2.format);
|
|
1161
|
+
const valueAsNumber = Number.isNaN(value) ? current.getChannelValue(channel) : value;
|
|
1162
|
+
color = current.withChannelValue(channel, valueAsNumber);
|
|
1050
1163
|
}
|
|
1051
|
-
|
|
1164
|
+
sync.inputs(ctx2, color);
|
|
1052
1165
|
set.value(ctx2, color);
|
|
1053
1166
|
},
|
|
1054
1167
|
incrementChannel(ctx2, evt) {
|
|
@@ -1061,22 +1174,22 @@ function machine(userContext) {
|
|
|
1061
1174
|
},
|
|
1062
1175
|
incrementXChannel(ctx2, evt) {
|
|
1063
1176
|
const { xChannel } = evt.channel;
|
|
1064
|
-
const color = ctx2.
|
|
1177
|
+
const color = ctx2.areaValue.incrementChannel(xChannel, evt.step);
|
|
1065
1178
|
set.value(ctx2, color);
|
|
1066
1179
|
},
|
|
1067
1180
|
decrementXChannel(ctx2, evt) {
|
|
1068
1181
|
const { xChannel } = evt.channel;
|
|
1069
|
-
const color = ctx2.
|
|
1182
|
+
const color = ctx2.areaValue.decrementChannel(xChannel, evt.step);
|
|
1070
1183
|
set.value(ctx2, color);
|
|
1071
1184
|
},
|
|
1072
1185
|
incrementYChannel(ctx2, evt) {
|
|
1073
1186
|
const { yChannel } = evt.channel;
|
|
1074
|
-
const color = ctx2.
|
|
1187
|
+
const color = ctx2.areaValue.incrementChannel(yChannel, evt.step);
|
|
1075
1188
|
set.value(ctx2, color);
|
|
1076
1189
|
},
|
|
1077
1190
|
decrementYChannel(ctx2, evt) {
|
|
1078
1191
|
const { yChannel } = evt.channel;
|
|
1079
|
-
const color = ctx2.
|
|
1192
|
+
const color = ctx2.areaValue.decrementChannel(yChannel, evt.step);
|
|
1080
1193
|
set.value(ctx2, color);
|
|
1081
1194
|
},
|
|
1082
1195
|
setChannelToMax(ctx2, evt) {
|
|
@@ -1109,6 +1222,9 @@ function machine(userContext) {
|
|
|
1109
1222
|
dom?.focus(ctx2, dom.getTriggerEl(ctx2));
|
|
1110
1223
|
});
|
|
1111
1224
|
},
|
|
1225
|
+
syncFormatSelectElement(ctx2) {
|
|
1226
|
+
sync.formatSelect(ctx2);
|
|
1227
|
+
},
|
|
1112
1228
|
invokeOnOpen(ctx2) {
|
|
1113
1229
|
ctx2.onOpenChange?.({ open: true });
|
|
1114
1230
|
},
|
|
@@ -1126,41 +1242,56 @@ function machine(userContext) {
|
|
|
1126
1242
|
);
|
|
1127
1243
|
}
|
|
1128
1244
|
var sync = {
|
|
1129
|
-
inputs
|
|
1245
|
+
// sync channel inputs
|
|
1246
|
+
inputs(ctx, color) {
|
|
1130
1247
|
const channelInputs = dom.getChannelInputEls(ctx);
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1248
|
+
raf(() => {
|
|
1249
|
+
channelInputs.forEach((inputEl) => {
|
|
1250
|
+
const channel = inputEl.dataset.channel;
|
|
1251
|
+
dom.setValue(inputEl, getChannelValue(color || ctx.value, channel));
|
|
1252
|
+
});
|
|
1253
|
+
});
|
|
1254
|
+
},
|
|
1255
|
+
// sync format select
|
|
1256
|
+
formatSelect(ctx) {
|
|
1257
|
+
const selectEl = dom.getFormatSelectEl(ctx);
|
|
1258
|
+
raf(() => {
|
|
1259
|
+
dom.setValue(selectEl, ctx.format);
|
|
1134
1260
|
});
|
|
1135
1261
|
}
|
|
1136
1262
|
};
|
|
1137
1263
|
var invoke = {
|
|
1138
1264
|
changeEnd(ctx) {
|
|
1265
|
+
const value = ctx.value.toFormat(ctx.format);
|
|
1139
1266
|
ctx.onValueChangeEnd?.({
|
|
1140
|
-
value
|
|
1267
|
+
value,
|
|
1141
1268
|
valueAsString: ctx.valueAsString
|
|
1142
1269
|
});
|
|
1143
1270
|
},
|
|
1144
1271
|
change(ctx) {
|
|
1272
|
+
const value = ctx.value.toFormat(ctx.format);
|
|
1145
1273
|
ctx.onValueChange?.({
|
|
1146
|
-
value
|
|
1274
|
+
value,
|
|
1147
1275
|
valueAsString: ctx.valueAsString
|
|
1148
1276
|
});
|
|
1149
1277
|
dispatchInputValueEvent(dom.getHiddenInputEl(ctx), { value: ctx.valueAsString });
|
|
1278
|
+
},
|
|
1279
|
+
formatChange(ctx) {
|
|
1280
|
+
ctx.onFormatChange?.({ format: ctx.format });
|
|
1150
1281
|
}
|
|
1151
1282
|
};
|
|
1152
1283
|
var set = {
|
|
1153
1284
|
value(ctx, color) {
|
|
1154
1285
|
if (!color || ctx.value.isEqual(color))
|
|
1155
1286
|
return;
|
|
1156
|
-
|
|
1157
|
-
const outputColor = color.toFormat(currentFormat);
|
|
1158
|
-
try {
|
|
1159
|
-
outputColor.outputFormat = ctx.value.outputFormat;
|
|
1160
|
-
} catch {
|
|
1161
|
-
}
|
|
1162
|
-
ctx.value = outputColor;
|
|
1287
|
+
ctx.value = color;
|
|
1163
1288
|
invoke.change(ctx);
|
|
1289
|
+
},
|
|
1290
|
+
format(ctx, format) {
|
|
1291
|
+
if (ctx.format === format)
|
|
1292
|
+
return;
|
|
1293
|
+
ctx.format = format;
|
|
1294
|
+
invoke.formatChange(ctx);
|
|
1164
1295
|
}
|
|
1165
1296
|
};
|
|
1166
1297
|
export {
|