@zag-js/color-picker 0.56.0 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -11,8 +11,10 @@ var anatomy = createAnatomy("color-picker", [
11
11
  "areaThumb",
12
12
  "areaBackground",
13
13
  "channelSlider",
14
+ "channelSliderLabel",
14
15
  "channelSliderTrack",
15
16
  "channelSliderThumb",
17
+ "channelSliderValueText",
16
18
  "channelInput",
17
19
  "transparencyGrid",
18
20
  "swatchGroup",
@@ -68,8 +70,7 @@ var dom = createScope({
68
70
  getAreaEl: (ctx) => dom.getById(ctx, dom.getAreaId(ctx)),
69
71
  getAreaValueFromPoint(ctx, point) {
70
72
  const areaEl = dom.getAreaEl(ctx);
71
- if (!areaEl)
72
- return;
73
+ if (!areaEl) return;
73
74
  const { percent } = getRelativePoint(point, areaEl);
74
75
  return percent;
75
76
  },
@@ -81,8 +82,7 @@ var dom = createScope({
81
82
  },
82
83
  getChannelSliderValueFromPoint(ctx, point, channel) {
83
84
  const trackEl = dom.getChannelSliderTrackEl(ctx, channel);
84
- if (!trackEl)
85
- return;
85
+ if (!trackEl) return;
86
86
  const { percent } = getRelativePoint(point, trackEl);
87
87
  return percent;
88
88
  },
@@ -118,8 +118,7 @@ function getChannelDisplayColor(color, channel) {
118
118
  // src/utils/get-channel-input-value.ts
119
119
  import { parseColor as parseColor2 } from "@zag-js/color-utils";
120
120
  function getChannelValue(color, channel) {
121
- if (channel == null)
122
- return "";
121
+ if (channel == null) return "";
123
122
  if (channel === "hex") {
124
123
  return color.toString("hex");
125
124
  }
@@ -250,8 +249,7 @@ function connect(state, send, normalize) {
250
249
  valueAsString,
251
250
  value,
252
251
  setOpen(nextOpen) {
253
- if (nextOpen === open)
254
- return;
252
+ if (nextOpen === open) return;
255
253
  send({ type: nextOpen ? "OPEN" : "CLOSE" });
256
254
  },
257
255
  setValue(value2) {
@@ -260,6 +258,9 @@ function connect(state, send, normalize) {
260
258
  getChannelValue(channel) {
261
259
  return getChannelValue(value, channel);
262
260
  },
261
+ getChannelValueText(channel, locale) {
262
+ return value.formatChannelValue(channel, locale);
263
+ },
263
264
  setChannelValue(channel, channelValue) {
264
265
  const color = value.withChannelValue(channel, channelValue);
265
266
  send({ type: "VALUE.SET", value: color, src: "set-channel" });
@@ -330,13 +331,11 @@ function connect(state, send, normalize) {
330
331
  "data-focus": dataAttr(focused),
331
332
  type: "button",
332
333
  onClick() {
333
- if (!interactive)
334
- return;
334
+ if (!interactive) return;
335
335
  send({ type: "TRIGGER.CLICK" });
336
336
  },
337
337
  onBlur() {
338
- if (!interactive)
339
- return;
338
+ if (!interactive) return;
340
339
  send({ type: "TRIGGER.BLUR" });
341
340
  },
342
341
  style: {
@@ -374,12 +373,9 @@ function connect(state, send, normalize) {
374
373
  id: dom.getAreaId(state.context),
375
374
  role: "group",
376
375
  onPointerDown(event) {
377
- if (!interactive)
378
- return;
379
- if (!isLeftClick(event))
380
- return;
381
- if (isModifierKey(event))
382
- return;
376
+ if (!interactive) return;
377
+ if (!isLeftClick(event)) return;
378
+ if (isModifierKey(event)) return;
383
379
  const point = getEventPoint(event);
384
380
  const channel = { xChannel, yChannel };
385
381
  send({ type: "AREA.POINTER_DOWN", point, channel, id: "area" });
@@ -441,15 +437,12 @@ function connect(state, send, normalize) {
441
437
  background: areaValue.withChannelValue("alpha", 1).toString("css")
442
438
  },
443
439
  onFocus() {
444
- if (!interactive)
445
- return;
440
+ if (!interactive) return;
446
441
  send({ type: "AREA.FOCUS", id: "area", channel });
447
442
  },
448
443
  onKeyDown(event) {
449
- if (event.defaultPrevented)
450
- return;
451
- if (!interactive)
452
- return;
444
+ if (event.defaultPrevented) return;
445
+ if (!interactive) return;
453
446
  const step = getEventStep(event);
454
447
  const keyMap = {
455
448
  ArrowUp() {
@@ -501,21 +494,18 @@ function connect(state, send, normalize) {
501
494
  });
502
495
  },
503
496
  getChannelSliderProps(props) {
504
- const { orientation = "horizontal", channel } = props;
497
+ const { orientation = "horizontal", channel, format } = props;
505
498
  return normalize.element({
506
499
  ...parts.channelSlider.attrs,
507
500
  "data-channel": channel,
508
501
  "data-orientation": orientation,
509
502
  role: "presentation",
510
503
  onPointerDown(event) {
511
- if (!interactive)
512
- return;
513
- if (!isLeftClick(event))
514
- return;
515
- if (isModifierKey(event))
516
- return;
504
+ if (!interactive) return;
505
+ if (!isLeftClick(event)) return;
506
+ if (isModifierKey(event)) return;
517
507
  const point = getEventPoint(event);
518
- send({ type: "CHANNEL_SLIDER.POINTER_DOWN", channel, point, id: channel, orientation });
508
+ send({ type: "CHANNEL_SLIDER.POINTER_DOWN", channel, format, point, id: channel, orientation });
519
509
  event.preventDefault();
520
510
  },
521
511
  style: {
@@ -525,7 +515,8 @@ function connect(state, send, normalize) {
525
515
  });
526
516
  },
527
517
  getChannelSliderTrackProps(props) {
528
- const { orientation = "horizontal", channel } = props;
518
+ const { orientation = "horizontal", channel, format } = props;
519
+ const normalizedValue = format ? value.toFormat(format) : areaValue;
529
520
  return normalize.element({
530
521
  ...parts.channelSliderTrack.attrs,
531
522
  id: dom.getChannelSliderId(state.context, channel),
@@ -539,16 +530,40 @@ function connect(state, send, normalize) {
539
530
  orientation,
540
531
  channel,
541
532
  dir: state.context.dir,
542
- value: areaValue
533
+ value: normalizedValue
543
534
  })
544
535
  }
545
536
  });
546
537
  },
538
+ getChannelSliderLabelProps(props) {
539
+ const { channel } = props;
540
+ return normalize.element({
541
+ ...parts.channelSliderLabel.attrs,
542
+ "data-channel": channel,
543
+ onClick(event) {
544
+ if (!interactive) return;
545
+ event.preventDefault();
546
+ const thumbId = dom.getChannelSliderThumbId(state.context, channel);
547
+ dom.getById(state.context, thumbId)?.focus({ preventScroll: true });
548
+ },
549
+ style: {
550
+ userSelect: "none",
551
+ WebkitUserSelect: "none"
552
+ }
553
+ });
554
+ },
555
+ getChannelSliderValueTextProps(props) {
556
+ return normalize.element({
557
+ ...parts.channelSliderValueText.attrs,
558
+ "data-channel": props.channel
559
+ });
560
+ },
547
561
  getChannelSliderThumbProps(props) {
548
- const { orientation = "horizontal", channel } = props;
549
- const { minValue, maxValue, step: stepValue } = areaValue.getChannelRange(channel);
550
- const channelValue = areaValue.getChannelValue(channel);
551
- const offset = (channelValue - minValue) / (maxValue - minValue);
562
+ const { orientation = "horizontal", channel, format } = props;
563
+ const normalizedValue = format ? value.toFormat(format) : areaValue;
564
+ const channelRange = normalizedValue.getChannelRange(channel);
565
+ const channelValue = normalizedValue.getChannelValue(channel);
566
+ const offset = (channelValue - channelRange.minValue) / (channelRange.maxValue - channelRange.minValue);
552
567
  const placementStyles = orientation === "horizontal" ? { left: `${offset * 100}%`, top: "50%" } : { top: `${offset * 100}%`, left: "50%" };
553
568
  return normalize.element({
554
569
  ...parts.channelSliderThumb.attrs,
@@ -561,8 +576,8 @@ function connect(state, send, normalize) {
561
576
  "data-orientation": orientation,
562
577
  "aria-disabled": dataAttr(disabled),
563
578
  "aria-orientation": orientation,
564
- "aria-valuemax": maxValue,
565
- "aria-valuemin": minValue,
579
+ "aria-valuemax": channelRange.maxValue,
580
+ "aria-valuemin": channelRange.minValue,
566
581
  "aria-valuenow": channelValue,
567
582
  "aria-valuetext": `${channel} ${channelValue}`,
568
583
  style: {
@@ -572,16 +587,13 @@ function connect(state, send, normalize) {
572
587
  ...placementStyles
573
588
  },
574
589
  onFocus() {
575
- if (!interactive)
576
- return;
590
+ if (!interactive) return;
577
591
  send({ type: "CHANNEL_SLIDER.FOCUS", channel });
578
592
  },
579
593
  onKeyDown(event) {
580
- if (event.defaultPrevented)
581
- return;
582
- if (!interactive)
583
- return;
584
- const step = getEventStep(event) * stepValue;
594
+ if (event.defaultPrevented) return;
595
+ if (!interactive) return;
596
+ const step = getEventStep(event) * channelRange.step;
585
597
  const keyMap = {
586
598
  ArrowUp() {
587
599
  send({ type: "CHANNEL_SLIDER.ARROW_UP", channel, step });
@@ -622,7 +634,7 @@ function connect(state, send, normalize) {
622
634
  getChannelInputProps(props) {
623
635
  const { channel } = props;
624
636
  const isTextField = channel === "hex" || channel === "css";
625
- const range = getChannelRange(value, channel);
637
+ const channelRange = getChannelRange(value, channel);
626
638
  return normalize.input({
627
639
  ...parts.channelInput.attrs,
628
640
  dir: state.context.dir,
@@ -635,34 +647,29 @@ function connect(state, send, normalize) {
635
647
  "data-disabled": dataAttr(disabled),
636
648
  readOnly: state.context.readOnly,
637
649
  defaultValue: getChannelValue(value, channel),
638
- min: range?.minValue,
639
- max: range?.maxValue,
640
- step: range?.step,
650
+ min: channelRange?.minValue,
651
+ max: channelRange?.maxValue,
652
+ step: channelRange?.step,
641
653
  onBeforeInput(event) {
642
- if (isTextField || !interactive)
643
- return;
654
+ if (isTextField || !interactive) return;
644
655
  const value2 = event.currentTarget.value;
645
656
  if (value2.match(/[^0-9.]/g)) {
646
657
  event.preventDefault();
647
658
  }
648
659
  },
649
660
  onFocus(event) {
650
- if (!interactive)
651
- return;
661
+ if (!interactive) return;
652
662
  send({ type: "CHANNEL_INPUT.FOCUS", channel });
653
663
  event.target.select();
654
664
  },
655
665
  onBlur(event) {
656
- if (!interactive)
657
- return;
666
+ if (!interactive) return;
658
667
  const value2 = isTextField ? event.currentTarget.value : event.currentTarget.valueAsNumber;
659
668
  send({ type: "CHANNEL_INPUT.BLUR", channel, value: value2, isTextField });
660
669
  },
661
670
  onKeyDown(event) {
662
- if (event.defaultPrevented)
663
- return;
664
- if (!interactive)
665
- return;
671
+ if (event.defaultPrevented) return;
672
+ if (!interactive) return;
666
673
  if (event.key === "Enter") {
667
674
  const value2 = isTextField ? event.currentTarget.value : event.currentTarget.valueAsNumber;
668
675
  send({ type: "CHANNEL_INPUT.CHANGE", channel, value: value2, isTextField });
@@ -695,8 +702,7 @@ function connect(state, send, normalize) {
695
702
  "data-disabled": dataAttr(disabled),
696
703
  "aria-label": "Pick a color from the screen",
697
704
  onClick() {
698
- if (!interactive)
699
- return;
705
+ if (!interactive) return;
700
706
  send("EYEDROPPER.CLICK");
701
707
  }
702
708
  });
@@ -720,8 +726,7 @@ function connect(state, send, normalize) {
720
726
  "data-value": swatchState.valueAsString,
721
727
  "data-disabled": dataAttr(swatchState.disabled),
722
728
  onClick() {
723
- if (swatchState.disabled)
724
- return;
729
+ if (swatchState.disabled) return;
725
730
  send({ type: "SWATCH_TRIGGER.CLICK", value: swatchState.value });
726
731
  },
727
732
  style: {
@@ -758,8 +763,7 @@ function connect(state, send, normalize) {
758
763
  type: "button",
759
764
  "aria-label": `change color format to ${getNextFormat(state.context.format)}`,
760
765
  onClick(event) {
761
- if (event.currentTarget.disabled)
762
- return;
766
+ if (event.currentTarget.disabled) return;
763
767
  const nextFormat = getNextFormat(state.context.format);
764
768
  send({ type: "FORMAT.SET", format: nextFormat, src: "format-trigger" });
765
769
  }
@@ -787,8 +791,7 @@ function getNextFormat(format) {
787
791
  return formats[index + 1] ?? formats[0];
788
792
  }
789
793
  function assertFormat(format) {
790
- if (formatRegex.test(format))
791
- return format;
794
+ if (formatRegex.test(format)) return format;
792
795
  throw new Error(`Unsupported color format: ${format}`);
793
796
  }
794
797
 
@@ -976,22 +979,22 @@ function machine(userContext) {
976
979
  actions: ["setActiveChannel"]
977
980
  },
978
981
  "AREA.ARROW_LEFT": {
979
- actions: ["decrementXChannel"]
982
+ actions: ["decrementAreaXChannel"]
980
983
  },
981
984
  "AREA.ARROW_RIGHT": {
982
- actions: ["incrementXChannel"]
985
+ actions: ["incrementAreaXChannel"]
983
986
  },
984
987
  "AREA.ARROW_UP": {
985
- actions: ["incrementYChannel"]
988
+ actions: ["incrementAreaYChannel"]
986
989
  },
987
990
  "AREA.ARROW_DOWN": {
988
- actions: ["decrementYChannel"]
991
+ actions: ["decrementAreaYChannel"]
989
992
  },
990
993
  "AREA.PAGE_UP": {
991
- actions: ["incrementXChannel"]
994
+ actions: ["incrementAreaXChannel"]
992
995
  },
993
996
  "AREA.PAGE_DOWN": {
994
- actions: ["decrementXChannel"]
997
+ actions: ["decrementAreaXChannel"]
995
998
  },
996
999
  "CHANNEL_SLIDER.ARROW_LEFT": {
997
1000
  actions: ["decrementChannel"]
@@ -1145,8 +1148,7 @@ function machine(userContext) {
1145
1148
  defer: true,
1146
1149
  onInteractOutside(event) {
1147
1150
  ctx2.onInteractOutside?.(event);
1148
- if (event.defaultPrevented)
1149
- return;
1151
+ if (event.defaultPrevented) return;
1150
1152
  ctx2.restoreFocus = !(event.detail.focusable || event.detail.contextmenu);
1151
1153
  },
1152
1154
  onPointerDownOutside: ctx2.onPointerDownOutside,
@@ -1167,11 +1169,11 @@ function machine(userContext) {
1167
1169
  }
1168
1170
  });
1169
1171
  },
1170
- trackPointerMove(ctx2, _evt, { send }) {
1172
+ trackPointerMove(ctx2, evt, { send }) {
1171
1173
  return trackPointerMove(dom.getDoc(ctx2), {
1172
1174
  onPointerMove({ point }) {
1173
1175
  const type = ctx2.activeId === "area" ? "AREA.POINTER_MOVE" : "CHANNEL_SLIDER.POINTER_MOVE";
1174
- send({ type, point });
1176
+ send({ type, point, format: evt.format });
1175
1177
  },
1176
1178
  onPointerUp() {
1177
1179
  const type = ctx2.activeId === "area" ? "AREA.POINTER_UP" : "CHANNEL_SLIDER.POINTER_UP";
@@ -1186,8 +1188,7 @@ function machine(userContext) {
1186
1188
  actions: {
1187
1189
  openEyeDropper(ctx2) {
1188
1190
  const isSupported = "EyeDropper" in dom.getWin(ctx2);
1189
- if (!isSupported)
1190
- return;
1191
+ if (!isSupported) return;
1191
1192
  const win = dom.getWin(ctx2);
1192
1193
  const picker = new win.EyeDropper();
1193
1194
  picker.open().then(({ sRGBHex }) => {
@@ -1199,10 +1200,8 @@ function machine(userContext) {
1199
1200
  },
1200
1201
  setActiveChannel(ctx2, evt) {
1201
1202
  ctx2.activeId = evt.id;
1202
- if (evt.channel)
1203
- ctx2.activeChannel = evt.channel;
1204
- if (evt.orientation)
1205
- ctx2.activeOrientation = evt.orientation;
1203
+ if (evt.channel) ctx2.activeChannel = evt.channel;
1204
+ if (evt.orientation) ctx2.activeOrientation = evt.orientation;
1206
1205
  },
1207
1206
  clearActiveChannel(ctx2) {
1208
1207
  ctx2.activeChannel = null;
@@ -1210,24 +1209,24 @@ function machine(userContext) {
1210
1209
  ctx2.activeOrientation = null;
1211
1210
  },
1212
1211
  setAreaColorFromPoint(ctx2, evt) {
1212
+ const normalizedValue = evt.format ? ctx2.value.toFormat(evt.format) : ctx2.areaValue;
1213
1213
  const { xChannel, yChannel } = evt.channel || ctx2.activeChannel;
1214
1214
  const percent = dom.getAreaValueFromPoint(ctx2, evt.point);
1215
- if (!percent)
1216
- return;
1217
- const xValue = ctx2.areaValue.getChannelPercentValue(xChannel, percent.x);
1218
- const yValue = ctx2.areaValue.getChannelPercentValue(yChannel, 1 - percent.y);
1219
- const color = ctx2.areaValue.withChannelValue(xChannel, xValue).withChannelValue(yChannel, yValue);
1215
+ if (!percent) return;
1216
+ const xValue = normalizedValue.getChannelPercentValue(xChannel, percent.x);
1217
+ const yValue = normalizedValue.getChannelPercentValue(yChannel, 1 - percent.y);
1218
+ const color = normalizedValue.withChannelValue(xChannel, xValue).withChannelValue(yChannel, yValue);
1220
1219
  set.value(ctx2, color);
1221
1220
  },
1222
1221
  setChannelColorFromPoint(ctx2, evt) {
1223
1222
  const channel = evt.channel || ctx2.activeId;
1223
+ const normalizedValue = evt.format ? ctx2.value.toFormat(evt.format) : ctx2.areaValue;
1224
1224
  const percent = dom.getChannelSliderValueFromPoint(ctx2, evt.point, channel);
1225
- if (!percent)
1226
- return;
1225
+ if (!percent) return;
1227
1226
  const orientation = ctx2.activeOrientation || "horizontal";
1228
1227
  const channelPercent = orientation === "horizontal" ? percent.x : percent.y;
1229
- const value = ctx2.areaValue.getChannelPercentValue(channel, channelPercent);
1230
- const color = ctx2.areaValue.withChannelValue(channel, value);
1228
+ const value = normalizedValue.getChannelPercentValue(channel, channelPercent);
1229
+ const color = normalizedValue.withChannelValue(channel, value);
1231
1230
  set.value(ctx2, color);
1232
1231
  },
1233
1232
  setValue(ctx2, evt) {
@@ -1271,22 +1270,22 @@ function machine(userContext) {
1271
1270
  const color = ctx2.value.decrementChannel(evt.channel, evt.step);
1272
1271
  set.value(ctx2, color);
1273
1272
  },
1274
- incrementXChannel(ctx2, evt) {
1273
+ incrementAreaXChannel(ctx2, evt) {
1275
1274
  const { xChannel } = evt.channel;
1276
1275
  const color = ctx2.areaValue.incrementChannel(xChannel, evt.step);
1277
1276
  set.value(ctx2, color);
1278
1277
  },
1279
- decrementXChannel(ctx2, evt) {
1278
+ decrementAreaXChannel(ctx2, evt) {
1280
1279
  const { xChannel } = evt.channel;
1281
1280
  const color = ctx2.areaValue.decrementChannel(xChannel, evt.step);
1282
1281
  set.value(ctx2, color);
1283
1282
  },
1284
- incrementYChannel(ctx2, evt) {
1283
+ incrementAreaYChannel(ctx2, evt) {
1285
1284
  const { yChannel } = evt.channel;
1286
1285
  const color = ctx2.areaValue.incrementChannel(yChannel, evt.step);
1287
1286
  set.value(ctx2, color);
1288
1287
  },
1289
- decrementYChannel(ctx2, evt) {
1288
+ decrementAreaYChannel(ctx2, evt) {
1290
1289
  const { yChannel } = evt.channel;
1291
1290
  const color = ctx2.areaValue.decrementChannel(yChannel, evt.step);
1292
1291
  set.value(ctx2, color);
@@ -1385,14 +1384,12 @@ var invoke = {
1385
1384
  };
1386
1385
  var set = {
1387
1386
  value(ctx, color) {
1388
- if (!color || ctx.value.isEqual(color))
1389
- return;
1387
+ if (!color || ctx.value.isEqual(color)) return;
1390
1388
  ctx.value = color;
1391
1389
  invoke.change(ctx);
1392
1390
  },
1393
1391
  format(ctx, format) {
1394
- if (ctx.format === format)
1395
- return;
1392
+ if (ctx.format === format) return;
1396
1393
  ctx.format = format;
1397
1394
  invoke.formatChange(ctx);
1398
1395
  }