@destyler/color-picker 0.0.7-beta.1

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