@zag-js/color-picker 0.20.0 → 0.22.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
@@ -17,7 +17,10 @@ var anatomy = createAnatomy("color-picker", [
17
17
  var parts = anatomy.build();
18
18
 
19
19
  // src/color-picker.connect.ts
20
- import { normalizeColor } from "@zag-js/color-utils";
20
+ import {
21
+ getColorAreaGradient,
22
+ normalizeColor
23
+ } from "@zag-js/color-utils";
21
24
  import {
22
25
  getEventKey,
23
26
  getEventPoint,
@@ -26,7 +29,7 @@ import {
26
29
  isLeftClick,
27
30
  isModifiedEvent
28
31
  } from "@zag-js/dom-event";
29
- import { dataAttr } from "@zag-js/dom-query";
32
+ import { dataAttr, raf } from "@zag-js/dom-query";
30
33
  import { visuallyHiddenStyle } from "@zag-js/visually-hidden";
31
34
 
32
35
  // src/color-picker.dom.ts
@@ -151,6 +154,16 @@ function getChannelInputValue(color, channel) {
151
154
  return color.toString("hex");
152
155
  case "css":
153
156
  return color.toString("css");
157
+ case "hue":
158
+ case "saturation":
159
+ case "lightness":
160
+ return color.toFormat("hsl").getChannelValue("lightness").toString();
161
+ case "brightness":
162
+ return color.toFormat("hsb").getChannelValue("brightness").toString();
163
+ case "red":
164
+ case "green":
165
+ case "blue":
166
+ return color.toFormat("rgb").getChannelValue(channel).toString();
154
167
  default:
155
168
  return color.getChannelValue(channel).toString();
156
169
  }
@@ -160,191 +173,21 @@ function getChannelInputRange(color, channel) {
160
173
  case "hex":
161
174
  case "css":
162
175
  return void 0;
176
+ case "hue":
177
+ case "saturation":
178
+ case "lightness":
179
+ return color.toFormat("hsl").getChannelRange(channel);
180
+ case "brightness":
181
+ return color.toFormat("hsb").getChannelRange(channel);
182
+ case "red":
183
+ case "green":
184
+ case "blue":
185
+ return color.toFormat("rgb").getChannelRange(channel);
163
186
  default:
164
187
  return color.getChannelRange(channel);
165
188
  }
166
189
  }
167
190
 
168
- // src/utils/generate-format-background.ts
169
- var generateRGB_R = (orientation, dir, zValue) => {
170
- const maskImage = `linear-gradient(to ${orientation[Number(!dir)]}, transparent, #000)`;
171
- const result = {
172
- areaStyles: {
173
- backgroundImage: `linear-gradient(to ${orientation[Number(dir)]},rgb(${zValue},0,0),rgb(${zValue},255,0))`
174
- },
175
- areaGradientStyles: {
176
- backgroundImage: `linear-gradient(to ${orientation[Number(dir)]},rgb(${zValue},0,255),rgb(${zValue},255,255))`,
177
- WebkitMaskImage: maskImage,
178
- maskImage
179
- }
180
- };
181
- return result;
182
- };
183
- var generateRGB_G = (orientation, dir, zValue) => {
184
- const maskImage = `linear-gradient(to ${orientation[Number(!dir)]}, transparent, #000)`;
185
- const result = {
186
- areaStyles: {
187
- backgroundImage: `linear-gradient(to ${orientation[Number(dir)]},rgb(0,${zValue},0),rgb(255,${zValue},0))`
188
- },
189
- areaGradientStyles: {
190
- backgroundImage: `linear-gradient(to ${orientation[Number(dir)]},rgb(0,${zValue},255),rgb(255,${zValue},255))`,
191
- WebkitMaskImage: maskImage,
192
- maskImage
193
- }
194
- };
195
- return result;
196
- };
197
- var generateRGB_B = (orientation, dir, zValue) => {
198
- const maskImage = `linear-gradient(to ${orientation[Number(!dir)]}, transparent, #000)`;
199
- const result = {
200
- areaStyles: {
201
- backgroundImage: `linear-gradient(to ${orientation[Number(dir)]},rgb(0,0,${zValue}),rgb(255,0,${zValue}))`
202
- },
203
- areaGradientStyles: {
204
- backgroundImage: `linear-gradient(to ${orientation[Number(dir)]},rgb(0,255,${zValue}),rgb(255,255,${zValue}))`,
205
- WebkitMaskImage: maskImage,
206
- maskImage
207
- }
208
- };
209
- return result;
210
- };
211
- var generateHSL_H = (orientation, dir, zValue) => {
212
- const result = {
213
- areaStyles: {},
214
- areaGradientStyles: {
215
- background: [
216
- `linear-gradient(to ${orientation[Number(dir)]}, hsla(0,0%,0%,1) 0%, hsla(0,0%,0%,0) 50%, hsla(0,0%,100%,0) 50%, hsla(0,0%,100%,1) 100%)`,
217
- `linear-gradient(to ${orientation[Number(!dir)]},hsl(0,0%,50%),hsla(0,0%,50%,0))`,
218
- `hsl(${zValue}, 100%, 50%)`
219
- ].join(",")
220
- }
221
- };
222
- return result;
223
- };
224
- var generateHSL_S = (orientation, dir, alphaValue) => {
225
- const result = {
226
- areaStyles: {},
227
- areaGradientStyles: {
228
- background: [
229
- `linear-gradient(to ${orientation[Number(!dir)]}, hsla(0,0%,0%,${alphaValue}) 0%, hsla(0,0%,0%,0) 50%, hsla(0,0%,100%,0) 50%, hsla(0,0%,100%,${alphaValue}) 100%)`,
230
- `linear-gradient(to ${orientation[Number(dir)]},hsla(0,100%,50%,${alphaValue}),hsla(60,100%,50%,${alphaValue}),hsla(120,100%,50%,${alphaValue}),hsla(180,100%,50%,${alphaValue}),hsla(240,100%,50%,${alphaValue}),hsla(300,100%,50%,${alphaValue}),hsla(359,100%,50%,${alphaValue}))`,
231
- "hsl(0, 0%, 50%)"
232
- ].join(",")
233
- }
234
- };
235
- return result;
236
- };
237
- var generateHSL_L = (orientation, dir, zValue) => {
238
- const result = {
239
- areaStyles: {},
240
- areaGradientStyles: {
241
- backgroundImage: [
242
- `linear-gradient(to ${orientation[Number(!dir)]},hsl(0,0%,${zValue}%),hsla(0,0%,${zValue}%,0))`,
243
- `linear-gradient(to ${orientation[Number(dir)]},hsl(0,100%,${zValue}%),hsl(60,100%,${zValue}%),hsl(120,100%,${zValue}%),hsl(180,100%,${zValue}%),hsl(240,100%,${zValue}%),hsl(300,100%,${zValue}%),hsl(360,100%,${zValue}%))`
244
- ].join(",")
245
- }
246
- };
247
- return result;
248
- };
249
- var generateHSB_H = (orientation, dir, zValue) => {
250
- const result = {
251
- areaStyles: {},
252
- areaGradientStyles: {
253
- background: [
254
- `linear-gradient(to ${orientation[Number(dir)]},hsl(0,0%,0%),hsla(0,0%,0%,0))`,
255
- `linear-gradient(to ${orientation[Number(!dir)]},hsl(0,0%,100%),hsla(0,0%,100%,0))`,
256
- `hsl(${zValue}, 100%, 50%)`
257
- ].join(",")
258
- }
259
- };
260
- return result;
261
- };
262
- var generateHSB_S = (orientation, dir, alphaValue) => {
263
- const result = {
264
- areaStyles: {},
265
- areaGradientStyles: {
266
- background: [
267
- `linear-gradient(to ${orientation[Number(!dir)]},hsla(0,0%,0%,${alphaValue}),hsla(0,0%,0%,0))`,
268
- `linear-gradient(to ${orientation[Number(dir)]},hsla(0,100%,50%,${alphaValue}),hsla(60,100%,50%,${alphaValue}),hsla(120,100%,50%,${alphaValue}),hsla(180,100%,50%,${alphaValue}),hsla(240,100%,50%,${alphaValue}),hsla(300,100%,50%,${alphaValue}),hsla(359,100%,50%,${alphaValue}))`,
269
- `linear-gradient(to ${orientation[Number(!dir)]},hsl(0,0%,0%),hsl(0,0%,100%))`
270
- ].join(",")
271
- }
272
- };
273
- return result;
274
- };
275
- var generateHSB_B = (orientation, dir, alphaValue) => {
276
- const result = {
277
- areaStyles: {},
278
- areaGradientStyles: {
279
- background: [
280
- `linear-gradient(to ${orientation[Number(!dir)]},hsla(0,0%,100%,${alphaValue}),hsla(0,0%,100%,0))`,
281
- `linear-gradient(to ${orientation[Number(dir)]},hsla(0,100%,50%,${alphaValue}),hsla(60,100%,50%,${alphaValue}),hsla(120,100%,50%,${alphaValue}),hsla(180,100%,50%,${alphaValue}),hsla(240,100%,50%,${alphaValue}),hsla(300,100%,50%,${alphaValue}),hsla(359,100%,50%,${alphaValue}))`,
282
- "#000"
283
- ].join(",")
284
- }
285
- };
286
- return result;
287
- };
288
-
289
- // src/utils/get-color-area-gradient.ts
290
- function getColorAreaGradient(ctx, xChannel, yChannel) {
291
- const value = ctx.valueAsColor;
292
- const { zChannel } = value.getColorSpaceAxes({ xChannel, yChannel });
293
- const zValue = value.getChannelValue(zChannel);
294
- const { minValue: zMin, maxValue: zMax } = value.getChannelRange(zChannel);
295
- const orientation = ["top", ctx.dir === "rtl" ? "left" : "right"];
296
- let dir = false;
297
- let background = { areaStyles: {}, areaGradientStyles: {} };
298
- let alphaValue = (zValue - zMin) / (zMax - zMin);
299
- let isHSL = value.getColorSpace() === "hsl";
300
- switch (zChannel) {
301
- case "red": {
302
- dir = xChannel === "green";
303
- background = generateRGB_R(orientation, dir, zValue);
304
- break;
305
- }
306
- case "green": {
307
- dir = xChannel === "red";
308
- background = generateRGB_G(orientation, dir, zValue);
309
- break;
310
- }
311
- case "blue": {
312
- dir = xChannel === "red";
313
- background = generateRGB_B(orientation, dir, zValue);
314
- break;
315
- }
316
- case "hue": {
317
- dir = xChannel !== "saturation";
318
- if (isHSL) {
319
- background = generateHSL_H(orientation, dir, zValue);
320
- } else {
321
- background = generateHSB_H(orientation, dir, zValue);
322
- }
323
- break;
324
- }
325
- case "saturation": {
326
- dir = xChannel === "hue";
327
- if (isHSL) {
328
- background = generateHSL_S(orientation, dir, alphaValue);
329
- } else {
330
- background = generateHSB_S(orientation, dir, alphaValue);
331
- }
332
- break;
333
- }
334
- case "brightness": {
335
- dir = xChannel === "hue";
336
- background = generateHSB_B(orientation, dir, alphaValue);
337
- break;
338
- }
339
- case "lightness": {
340
- dir = xChannel === "hue";
341
- background = generateHSL_L(orientation, dir, zValue);
342
- break;
343
- }
344
- }
345
- return background;
346
- }
347
-
348
191
  // src/utils/get-slider-background.ts
349
192
  function getSliderBgDirection(orientation, dir) {
350
193
  if (orientation === "vertical") {
@@ -415,11 +258,16 @@ function connect(state, send, normalize) {
415
258
  },
416
259
  contentProps: normalize.element({
417
260
  ...parts.content.attrs,
418
- id: dom.getContentId(state.context)
261
+ id: dom.getContentId(state.context),
262
+ dir: state.context.dir
419
263
  }),
420
264
  getAreaProps(props) {
421
265
  const { xChannel, yChannel } = props;
422
- const { areaStyles } = getColorAreaGradient(state.context, xChannel, yChannel);
266
+ const { areaStyles } = getColorAreaGradient(state.context.valueAsColor, {
267
+ xChannel,
268
+ yChannel,
269
+ dir: state.context.dir
270
+ });
423
271
  return normalize.element({
424
272
  ...parts.area.attrs,
425
273
  id: dom.getAreaId(state.context),
@@ -444,7 +292,11 @@ function connect(state, send, normalize) {
444
292
  },
445
293
  getAreaGradientProps(props) {
446
294
  const { xChannel, yChannel } = props;
447
- const { areaGradientStyles } = getColorAreaGradient(state.context, xChannel, yChannel);
295
+ const { areaGradientStyles } = getColorAreaGradient(valueAsColor, {
296
+ xChannel,
297
+ yChannel,
298
+ dir: state.context.dir
299
+ });
448
300
  return normalize.element({
449
301
  ...parts.areaGradient.attrs,
450
302
  id: dom.getAreaGradientId(state.context),
@@ -652,22 +504,15 @@ function connect(state, send, normalize) {
652
504
  min: range?.minValue,
653
505
  max: range?.maxValue,
654
506
  step: range?.step,
655
- onFocus() {
507
+ onFocus(event) {
656
508
  send({ type: "CHANNEL_INPUT.FOCUS", channel });
657
- },
658
- onChange(event) {
659
- if (isTextField)
660
- return;
661
- const value2 = event.currentTarget.value;
662
- send({ type: "CHANNEL_INPUT.CHANGE", channel, value: value2, isTextField });
509
+ raf(() => event.target.select());
663
510
  },
664
511
  onBlur(event) {
665
512
  const value2 = event.currentTarget.value;
666
- send({ type: "CHANNEL_INPUT.BLUR", channel, value: value2, isTextField });
513
+ send({ type: "CHANNEL_INPUT.CHANGE", channel, value: value2, isTextField });
667
514
  },
668
515
  onKeyDown(event) {
669
- if (!isTextField)
670
- return;
671
516
  if (event.key === "Enter") {
672
517
  const value2 = event.currentTarget.value;
673
518
  send({ type: "CHANNEL_INPUT.CHANGE", channel, value: value2, isTextField });
@@ -729,7 +574,7 @@ function connect(state, send, normalize) {
729
574
  },
730
575
  getSwatchProps(props) {
731
576
  const { value: value2, readOnly } = props;
732
- const color = normalizeColor(value2).toFormat(valueAsColor.getColorSpace());
577
+ const color = normalizeColor(value2).toFormat(valueAsColor.getColorFormat());
733
578
  return normalize.element({
734
579
  ...parts.swatch.attrs,
735
580
  onClick() {
@@ -750,11 +595,11 @@ function connect(state, send, normalize) {
750
595
  import { parseColor as parseColor2 } from "@zag-js/color-utils";
751
596
  import { createMachine } from "@zag-js/core";
752
597
  import { trackPointerMove } from "@zag-js/dom-event";
753
- import { raf } from "@zag-js/dom-query";
598
+ import { raf as raf2 } from "@zag-js/dom-query";
754
599
  import { trackFormControl } from "@zag-js/form-utils";
755
600
  import { clampValue, getPercentValue as getPercentValue2, snapValueToStep as snapValueToStep2 } from "@zag-js/numeric-range";
756
601
  import { disableTextSelection } from "@zag-js/text-selection";
757
- import { compact } from "@zag-js/utils";
602
+ import { compact, tryCatch } from "@zag-js/utils";
758
603
  function machine(userContext) {
759
604
  const ctx = compact(userContext);
760
605
  return createMachine(
@@ -875,14 +720,6 @@ function machine(userContext) {
875
720
  "CHANNEL_INPUT.CHANGE": {
876
721
  actions: ["setChannelColorFromInput"]
877
722
  },
878
- "CHANNEL_INPUT.BLUR": [
879
- {
880
- guard: "isTextField",
881
- target: "idle",
882
- actions: ["setChannelColorFromInput"]
883
- },
884
- { target: "idle" }
885
- ],
886
723
  "CHANNEL_SLIDER.BLUR": {
887
724
  target: "idle"
888
725
  },
@@ -914,9 +751,6 @@ function machine(userContext) {
914
751
  }
915
752
  },
916
753
  {
917
- guards: {
918
- isTextField: (_ctx, evt) => !!evt.isTextField
919
- },
920
754
  activities: {
921
755
  trackFormControl(ctx2, _evt, { send, initialContext }) {
922
756
  const inputEl = dom.getHiddenInputEl(ctx2);
@@ -953,7 +787,7 @@ function machine(userContext) {
953
787
  const win = dom.getWin(ctx2);
954
788
  const picker = new win.EyeDropper();
955
789
  picker.open().then(({ sRGBHex }) => {
956
- const format = ctx2.valueAsColor.getColorSpace();
790
+ const format = ctx2.valueAsColor.getColorFormat();
957
791
  const color = parseColor2(sRGBHex).toFormat(format);
958
792
  set.value(ctx2, color);
959
793
  ctx2.onValueChangeEnd?.({ value: ctx2.value, valueAsColor: color });
@@ -1013,14 +847,25 @@ function machine(userContext) {
1013
847
  },
1014
848
  setChannelColorFromInput(ctx2, evt) {
1015
849
  const { channel, isTextField, value } = evt;
1016
- try {
1017
- const format = ctx2.valueAsColor.getColorSpace();
1018
- const newColor = isTextField ? parseColor2(value).toFormat(format) : ctx2.valueAsColor.withChannelValue(channel, value);
1019
- set.value(ctx2, newColor);
1020
- } catch {
850
+ if (channel === "alpha") {
851
+ const newColor2 = ctx2.valueAsColor.withChannelValue("alpha", value);
852
+ set.value(ctx2, newColor2);
853
+ return;
854
+ }
855
+ if (isTextField) {
856
+ const format = "hsl";
857
+ const currentAlpha = ctx2.valueAsColor.getChannelValue("alpha");
858
+ const newColor2 = tryCatch(
859
+ () => parseColor2(value).toFormat(format).withChannelValue("alpha", currentAlpha),
860
+ () => ctx2.valueAsColor
861
+ );
1021
862
  const inputEl = dom.getChannelInputEl(ctx2, channel);
1022
863
  dom.setValue(inputEl, getChannelInputValue(ctx2.valueAsColor, channel));
864
+ set.value(ctx2, newColor2);
865
+ return;
1023
866
  }
867
+ const newColor = ctx2.valueAsColor.withChannelValue(channel, value);
868
+ set.value(ctx2, newColor);
1024
869
  },
1025
870
  incrementChannel(ctx2, evt) {
1026
871
  const { minValue, maxValue, step } = ctx2.valueAsColor.getChannelRange(evt.channel);
@@ -1071,12 +916,12 @@ function machine(userContext) {
1071
916
  set.value(ctx2, color);
1072
917
  },
1073
918
  focusAreaThumb(ctx2) {
1074
- raf(() => {
919
+ raf2(() => {
1075
920
  dom.getAreaThumbEl(ctx2)?.focus({ preventScroll: true });
1076
921
  });
1077
922
  },
1078
923
  focusChannelThumb(ctx2, evt) {
1079
- raf(() => {
924
+ raf2(() => {
1080
925
  dom.getChannelSliderThumbEl(ctx2, evt.channel)?.focus({ preventScroll: true });
1081
926
  });
1082
927
  }