@destyler/color-picker 0.0.7-beta.7 → 0.1.0

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