@zag-js/color-picker 0.25.0 → 0.27.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.
@@ -29,8 +29,6 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
29
29
  const isDragging = state.hasTag("dragging")
30
30
  const isOpen = state.hasTag("open")
31
31
 
32
- const channels = value.getChannels()
33
-
34
32
  const getAreaChannels = (props: AreaProps) => {
35
33
  const channels = value.getChannels()
36
34
  return {
@@ -50,8 +48,7 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
50
48
  isOpen,
51
49
  valueAsString,
52
50
  value,
53
- channels,
54
- setColor(value) {
51
+ setValue(value) {
55
52
  send({ type: "VALUE.SET", value: normalizeColor(value), src: "set-color" })
56
53
  },
57
54
  getChannelValue(channel) {
@@ -408,13 +405,13 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
408
405
 
409
406
  return normalize.input({
410
407
  ...parts.channelInput.attrs,
408
+ dir: state.context.dir,
411
409
  type: isTextField ? "text" : "number",
412
410
  "data-channel": channel,
413
411
  "aria-label": channel,
414
412
  disabled: isDisabled,
415
413
  "data-disabled": dataAttr(isDisabled),
416
414
  readOnly: state.context.readOnly,
417
- id: dom.getChannelInputId(state.context, channel),
418
415
  defaultValue: getChannelValue(value, channel),
419
416
  min: range?.minValue,
420
417
  max: range?.maxValue,
@@ -461,6 +458,7 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
461
458
  eyeDropperTriggerProps: normalize.button({
462
459
  ...parts.eyeDropperTrigger.attrs,
463
460
  type: "button",
461
+ dir: state.context.dir,
464
462
  disabled: isDisabled,
465
463
  "data-disabled": dataAttr(isDisabled),
466
464
  "aria-label": "Pick a color from the screen",
@@ -481,6 +479,7 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
481
479
  return normalize.button({
482
480
  ...parts.swatchTrigger.attrs,
483
481
  disabled: isDisabled,
482
+ dir: state.context.dir,
484
483
  type: "button",
485
484
  "data-value": color.toString("hex"),
486
485
  onClick() {
@@ -499,6 +498,7 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
499
498
  const color = colorValue.toFormat(value.getFormat())
500
499
  return normalize.element({
501
500
  ...parts.swatch.attrs,
501
+ dir: state.context.dir,
502
502
  "data-state": colorValue.isEqual(value) ? "selected" : "unselected",
503
503
  "data-value": color.toString("hex"),
504
504
  style: {
@@ -20,8 +20,6 @@ export const dom = createScope({
20
20
 
21
21
  getChannelSliderId: (ctx: Ctx, channel: ColorChannel) =>
22
22
  ctx.ids?.channelSliderTrack?.(channel) ?? `color-picker:${ctx.id}:slider-track:${channel}`,
23
- getChannelInputId: (ctx: Ctx, channel: string) =>
24
- ctx.ids?.channelInput?.(channel) ?? `color-picker:${ctx.id}:input:${channel}`,
25
23
  getChannelSliderThumbId: (ctx: Ctx, channel: ColorChannel) =>
26
24
  ctx.ids?.channelSliderThumb?.(channel) ?? `color-picker:${ctx.id}:slider-thumb:${channel}`,
27
25
 
@@ -29,8 +27,12 @@ export const dom = createScope({
29
27
  getAreaThumbEl: (ctx: Ctx) => dom.getById(ctx, dom.getAreaThumbId(ctx)),
30
28
  getChannelSliderThumbEl: (ctx: Ctx, channel: ColorChannel) =>
31
29
  dom.getById(ctx, dom.getChannelSliderThumbId(ctx, channel)),
32
- getChannelInputEl: (ctx: Ctx, channel: string) =>
33
- dom.getById<HTMLInputElement>(ctx, dom.getChannelInputId(ctx, channel)),
30
+ getChannelInputEl: (ctx: Ctx, channel: string): HTMLInputElement[] => {
31
+ return [
32
+ ...queryAll<HTMLInputElement>(dom.getContentEl(ctx), `input[data-channel="${channel}"]`),
33
+ ...queryAll<HTMLInputElement>(dom.getControlEl(ctx), `input[data-channel="${channel}"]`),
34
+ ]
35
+ },
34
36
 
35
37
  getHiddenInputEl: (ctx: Ctx) => dom.getById<HTMLInputElement>(ctx, dom.getHiddenInputId(ctx)),
36
38
  getAreaEl: (ctx: Ctx) => dom.getById(ctx, dom.getAreaId(ctx)),
@@ -1,5 +1,5 @@
1
1
  import { parseColor, type Color } from "@zag-js/color-utils"
2
- import { createMachine, guards, ref } from "@zag-js/core"
2
+ import { createMachine } from "@zag-js/core"
3
3
  import { trackDismissableElement } from "@zag-js/dismissable"
4
4
  import { trackPointerMove } from "@zag-js/dom-event"
5
5
  import { raf } from "@zag-js/dom-query"
@@ -18,8 +18,6 @@ import type {
18
18
  } from "./color-picker.types"
19
19
  import { getChannelValue } from "./utils/get-channel-input-value"
20
20
 
21
- const { stateIn } = guards
22
-
23
21
  export function machine(userContext: UserDefinedContext) {
24
22
  const ctx = compact(userContext)
25
23
  return createMachine<MachineContext, MachineState>(
@@ -55,7 +53,7 @@ export function machine(userContext: UserDefinedContext) {
55
53
  activities: ["trackFormControl"],
56
54
 
57
55
  watch: {
58
- valueAsString: ["syncInputElements"],
56
+ value: ["syncInputElements"],
59
57
  open: ["toggleVisibility"],
60
58
  },
61
59
 
@@ -63,26 +61,6 @@ export function machine(userContext: UserDefinedContext) {
63
61
  "VALUE.SET": {
64
62
  actions: ["setValue"],
65
63
  },
66
- "CHANNEL_INPUT.FOCUS": [
67
- {
68
- guard: stateIn("idle"),
69
- target: "focused",
70
- actions: ["setActiveChannel"],
71
- },
72
- {
73
- actions: ["setActiveChannel"],
74
- },
75
- ],
76
- "CHANNEL_INPUT.BLUR": [
77
- {
78
- guard: stateIn("focused"),
79
- target: "idle",
80
- actions: ["setChannelColorFromInput"],
81
- },
82
- {
83
- actions: ["setChannelColorFromInput"],
84
- },
85
- ],
86
64
  "CHANNEL_INPUT.CHANGE": {
87
65
  actions: ["setChannelColorFromInput"],
88
66
  },
@@ -100,6 +78,10 @@ export function machine(userContext: UserDefinedContext) {
100
78
  target: "open",
101
79
  actions: ["setInitialFocus", "invokeOnOpen"],
102
80
  },
81
+ "CHANNEL_INPUT.FOCUS": {
82
+ target: "focused",
83
+ actions: ["setActiveChannel"],
84
+ },
103
85
  },
104
86
  },
105
87
 
@@ -114,6 +96,13 @@ export function machine(userContext: UserDefinedContext) {
114
96
  target: "open",
115
97
  actions: ["setInitialFocus", "invokeOnOpen"],
116
98
  },
99
+ "CHANNEL_INPUT.FOCUS": {
100
+ actions: ["setActiveChannel"],
101
+ },
102
+ "CHANNEL_INPUT.BLUR": {
103
+ target: "idle",
104
+ actions: ["setChannelColorFromInput"],
105
+ },
117
106
  },
118
107
  },
119
108
 
@@ -184,6 +173,9 @@ export function machine(userContext: UserDefinedContext) {
184
173
  "CHANNEL_SLIDER.END": {
185
174
  actions: ["setChannelToMax"],
186
175
  },
176
+ "CHANNEL_INPUT.BLUR": {
177
+ actions: ["setChannelColorFromInput"],
178
+ },
187
179
  INTERACT_OUTSIDE: [
188
180
  {
189
181
  guard: "shouldRestoreFocus",
@@ -367,26 +359,29 @@ export function machine(userContext: UserDefinedContext) {
367
359
  },
368
360
  setChannelColorFromInput(ctx, evt) {
369
361
  const { channel, isTextField, value } = evt
362
+ const currentAlpha = ctx.value.getChannelValue("alpha")
370
363
 
371
364
  // handle alpha channel
372
365
  if (channel === "alpha") {
373
- const newColor = ctx.value.withChannelValue("alpha", parseFloat(value))
366
+ let valueAsNumber = parseFloat(value)
367
+ valueAsNumber = Number.isNaN(valueAsNumber) ? currentAlpha : valueAsNumber
368
+ const newColor = ctx.value.withChannelValue("alpha", valueAsNumber)
374
369
  set.value(ctx, newColor)
375
370
  return
376
371
  }
377
372
 
378
373
  // handle other text channels
379
374
  if (isTextField) {
380
- const currentAlpha = ctx.value.getChannelValue("alpha")
381
-
382
375
  const color = tryCatch(
383
376
  () => parse(value).withChannelValue("alpha", currentAlpha),
384
377
  () => ctx.value,
385
378
  )
386
379
 
387
380
  // set channel input value immediately (in event user types native css color, we need to convert it to the current channel format)
388
- const inputEl = dom.getChannelInputEl(ctx, channel)
389
- dom.setValue(inputEl, getChannelValue(color, channel))
381
+ const inputEls = dom.getChannelInputEl(ctx, channel)
382
+ inputEls.forEach((inputEl) => {
383
+ dom.setValue(inputEl, getChannelValue(color, channel))
384
+ })
390
385
 
391
386
  set.value(ctx, color)
392
387
  return
@@ -501,7 +496,6 @@ const invoke = {
501
496
 
502
497
  const set = {
503
498
  value(ctx: MachineContext, color: Color | ColorType | undefined) {
504
- sync.inputs(ctx)
505
499
  if (!color || ctx.value.isEqual(color)) return
506
500
  const currentFormat = ctx.value.getFormat()
507
501
  const outputColor = color.toFormat(currentFormat)
@@ -509,7 +503,7 @@ const set = {
509
503
  outputColor.outputFormat = ctx.value.outputFormat
510
504
  } catch {}
511
505
 
512
- ctx.value = ref(outputColor) as Color
506
+ ctx.value = outputColor
513
507
  invoke.change(ctx)
514
508
  },
515
509
  }
@@ -1,14 +1,13 @@
1
1
  import { parseColor, type Color } from "@zag-js/color-utils"
2
- import { ref } from "@zag-js/core"
3
2
 
4
3
  export const parse = (color: string): Color => {
5
4
  const value = parseColor(color)
6
5
 
7
6
  const colorFormat = value.getFormat()
8
- const lockedFormat = colorFormat.startsWith("hsl") ? "hsla" : "hsba"
7
+ const lockedFormat = colorFormat === "hsla" ? "hsla" : "hsba"
9
8
 
10
9
  const result = value.toFormat(lockedFormat)
11
10
  result.outputFormat = colorFormat
12
11
 
13
- return ref(result)
12
+ return result
14
13
  }
@@ -217,14 +217,10 @@ export interface MachineApi<T extends PropTypes = PropTypes> {
217
217
  * The current color value (as a Color object)
218
218
  */
219
219
  valueAsString: string
220
- /**
221
- * The current color channels of the color
222
- */
223
- channels: [ColorChannel, ColorChannel, ColorChannel]
224
220
  /**
225
221
  * Function to set the color value
226
222
  */
227
- setColor(value: string | Color): void
223
+ setValue(value: string | Color): void
228
224
  /**
229
225
  * Function to set the color value
230
226
  */
@@ -16,7 +16,7 @@ export function getChannelValue(color: Color, channel: ExtendedColorChannel | nu
16
16
  return color.getChannelValue(channel).toString()
17
17
  }
18
18
 
19
- const isHSL = color.getFormat() === "hsl"
19
+ const isHSL = color.getFormat() === "hsla"
20
20
 
21
21
  switch (channel) {
22
22
  case "hue":