@zag-js/color-picker 0.23.0 → 0.25.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.d.mts +101 -32
- package/dist/index.d.ts +101 -32
- package/dist/index.js +516 -300
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +515 -303
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -12
- package/src/color-picker.anatomy.ts +11 -5
- package/src/color-picker.connect.ts +206 -124
- package/src/color-picker.dom.ts +26 -4
- package/src/color-picker.machine.ts +240 -128
- package/src/color-picker.parse.ts +14 -0
- package/src/color-picker.types.ts +110 -30
- package/src/index.ts +9 -6
- package/src/utils/get-channel-input-value.ts +45 -11
- package/src/utils/get-slider-background.ts +14 -12
- package/src/utils/get-channel-details.ts +0 -60
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getColorAreaGradient,
|
|
3
|
-
normalizeColor,
|
|
4
|
-
type Color,
|
|
5
|
-
type ColorChannel,
|
|
6
|
-
type ColorFormat,
|
|
7
|
-
} from "@zag-js/color-utils"
|
|
1
|
+
import { getColorAreaGradient, normalizeColor } from "@zag-js/color-utils"
|
|
8
2
|
import {
|
|
9
3
|
getEventKey,
|
|
10
4
|
getEventPoint,
|
|
@@ -14,69 +8,138 @@ import {
|
|
|
14
8
|
isModifiedEvent,
|
|
15
9
|
type EventKeyMap,
|
|
16
10
|
} from "@zag-js/dom-event"
|
|
17
|
-
import { dataAttr,
|
|
11
|
+
import { dataAttr, query } from "@zag-js/dom-query"
|
|
12
|
+
import { getPlacementStyles } from "@zag-js/popper"
|
|
18
13
|
import type { NormalizeProps, PropTypes } from "@zag-js/types"
|
|
19
14
|
import { visuallyHiddenStyle } from "@zag-js/visually-hidden"
|
|
20
15
|
import { parts } from "./color-picker.anatomy"
|
|
21
16
|
import { dom } from "./color-picker.dom"
|
|
22
|
-
import type {
|
|
23
|
-
ColorAreaProps,
|
|
24
|
-
ColorChannelInputProps,
|
|
25
|
-
ColorChannelProps,
|
|
26
|
-
ColorSwatchProps,
|
|
27
|
-
MachineApi,
|
|
28
|
-
Send,
|
|
29
|
-
State,
|
|
30
|
-
} from "./color-picker.types"
|
|
31
|
-
import { getChannelDetails } from "./utils/get-channel-details"
|
|
17
|
+
import type { AreaProps, MachineApi, Send, State } from "./color-picker.types"
|
|
32
18
|
import { getChannelDisplayColor } from "./utils/get-channel-display-color"
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
19
|
+
import { getChannelRange, getChannelValue } from "./utils/get-channel-input-value"
|
|
20
|
+
import { getSliderBackground } from "./utils/get-slider-background"
|
|
35
21
|
|
|
36
22
|
export function connect<T extends PropTypes>(state: State, send: Send, normalize: NormalizeProps<T>): MachineApi<T> {
|
|
37
|
-
const valueAsColor = state.context.valueAsColor
|
|
38
23
|
const value = state.context.value
|
|
24
|
+
const valueAsString = state.context.valueAsString
|
|
25
|
+
|
|
39
26
|
const isDisabled = state.context.isDisabled
|
|
40
27
|
const isInteractive = state.context.isInteractive
|
|
41
|
-
const isDragging = state.matches("dragging")
|
|
42
28
|
|
|
43
|
-
const
|
|
29
|
+
const isDragging = state.hasTag("dragging")
|
|
30
|
+
const isOpen = state.hasTag("open")
|
|
31
|
+
|
|
32
|
+
const channels = value.getChannels()
|
|
33
|
+
|
|
34
|
+
const getAreaChannels = (props: AreaProps) => {
|
|
35
|
+
const channels = value.getChannels()
|
|
36
|
+
return {
|
|
37
|
+
xChannel: props.xChannel ?? channels[1],
|
|
38
|
+
yChannel: props.yChannel ?? channels[2],
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const currentPlacement = state.context.currentPlacement
|
|
43
|
+
const popperStyles = getPlacementStyles({
|
|
44
|
+
...state.context.positioning,
|
|
45
|
+
placement: currentPlacement,
|
|
46
|
+
})
|
|
44
47
|
|
|
45
48
|
return {
|
|
46
49
|
isDragging,
|
|
50
|
+
isOpen,
|
|
51
|
+
valueAsString,
|
|
47
52
|
value,
|
|
48
|
-
valueAsColor,
|
|
49
|
-
alpha: valueAsColor.getChannelValue("alpha"),
|
|
50
53
|
channels,
|
|
51
|
-
|
|
52
|
-
setColor(value: string | Color) {
|
|
54
|
+
setColor(value) {
|
|
53
55
|
send({ type: "VALUE.SET", value: normalizeColor(value), src: "set-color" })
|
|
54
56
|
},
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
getChannelValue(channel) {
|
|
58
|
+
return getChannelValue(value, channel)
|
|
59
|
+
},
|
|
60
|
+
setChannelValue(channel, channelValue) {
|
|
61
|
+
const color = value.withChannelValue(channel, channelValue)
|
|
58
62
|
send({ type: "VALUE.SET", value: color, src: "set-channel" })
|
|
59
63
|
},
|
|
60
|
-
|
|
61
|
-
setFormat(format
|
|
62
|
-
const
|
|
63
|
-
send({ type: "VALUE.SET", value, src: "set-format" })
|
|
64
|
+
format: value.outputFormat ?? value.getFormat(),
|
|
65
|
+
setFormat(format) {
|
|
66
|
+
const formatValue = value.toFormat(format)
|
|
67
|
+
send({ type: "VALUE.SET", value: formatValue, src: "set-format" })
|
|
64
68
|
},
|
|
65
|
-
|
|
66
|
-
setAlpha(
|
|
67
|
-
const color =
|
|
69
|
+
alpha: value.getChannelValue("alpha"),
|
|
70
|
+
setAlpha(alphaValue) {
|
|
71
|
+
const color = value.withChannelValue("alpha", alphaValue)
|
|
68
72
|
send({ type: "VALUE.SET", value: color, src: "set-alpha" })
|
|
69
73
|
},
|
|
70
74
|
|
|
75
|
+
rootProps: normalize.element({
|
|
76
|
+
...parts.root.attrs,
|
|
77
|
+
dir: state.context.dir,
|
|
78
|
+
id: dom.getRootId(state.context),
|
|
79
|
+
"data-disabled": dataAttr(isDisabled),
|
|
80
|
+
"data-readonly": dataAttr(state.context.readOnly),
|
|
81
|
+
style: {
|
|
82
|
+
"--value": value.toString("css"),
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
|
|
86
|
+
labelProps: normalize.element({
|
|
87
|
+
...parts.label.attrs,
|
|
88
|
+
dir: state.context.dir,
|
|
89
|
+
id: dom.getLabelId(state.context),
|
|
90
|
+
htmlFor: dom.getHiddenInputId(state.context),
|
|
91
|
+
"data-disabled": dataAttr(isDisabled),
|
|
92
|
+
"data-readonly": dataAttr(state.context.readOnly),
|
|
93
|
+
onClick(event) {
|
|
94
|
+
event.preventDefault()
|
|
95
|
+
const inputEl = query(dom.getControlEl(state.context), "[data-channel=hex]")
|
|
96
|
+
inputEl?.focus({ preventScroll: true })
|
|
97
|
+
},
|
|
98
|
+
}),
|
|
99
|
+
|
|
100
|
+
controlProps: normalize.element({
|
|
101
|
+
...parts.control.attrs,
|
|
102
|
+
id: dom.getControlId(state.context),
|
|
103
|
+
dir: state.context.dir,
|
|
104
|
+
"data-disabled": dataAttr(isDisabled),
|
|
105
|
+
"data-readonly": dataAttr(state.context.readOnly),
|
|
106
|
+
}),
|
|
107
|
+
|
|
108
|
+
triggerProps: normalize.button({
|
|
109
|
+
...parts.trigger.attrs,
|
|
110
|
+
id: dom.getTriggerId(state.context),
|
|
111
|
+
dir: state.context.dir,
|
|
112
|
+
"aria-labelledby": dom.getLabelId(state.context),
|
|
113
|
+
"data-disabled": dataAttr(isDisabled),
|
|
114
|
+
"data-readonly": dataAttr(state.context.readOnly),
|
|
115
|
+
"data-placement": currentPlacement,
|
|
116
|
+
type: "button",
|
|
117
|
+
onClick() {
|
|
118
|
+
send({ type: "TRIGGER.CLICK" })
|
|
119
|
+
},
|
|
120
|
+
style: {
|
|
121
|
+
position: "relative",
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
|
|
125
|
+
positionerProps: normalize.element({
|
|
126
|
+
...parts.positioner.attrs,
|
|
127
|
+
id: dom.getPositionerId(state.context),
|
|
128
|
+
dir: state.context.dir,
|
|
129
|
+
style: popperStyles.floating,
|
|
130
|
+
}),
|
|
131
|
+
|
|
71
132
|
contentProps: normalize.element({
|
|
72
133
|
...parts.content.attrs,
|
|
73
134
|
id: dom.getContentId(state.context),
|
|
74
135
|
dir: state.context.dir,
|
|
136
|
+
"data-placement": currentPlacement,
|
|
137
|
+
hidden: !isOpen,
|
|
75
138
|
}),
|
|
76
139
|
|
|
77
|
-
getAreaProps(props
|
|
78
|
-
const { xChannel, yChannel } = props
|
|
79
|
-
const { areaStyles } = getColorAreaGradient(
|
|
140
|
+
getAreaProps(props = {}) {
|
|
141
|
+
const { xChannel, yChannel } = getAreaChannels(props)
|
|
142
|
+
const { areaStyles } = getColorAreaGradient(value, {
|
|
80
143
|
xChannel,
|
|
81
144
|
yChannel,
|
|
82
145
|
dir: state.context.dir,
|
|
@@ -96,6 +159,7 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
96
159
|
const channel = { xChannel, yChannel }
|
|
97
160
|
|
|
98
161
|
send({ type: "AREA.POINTER_DOWN", point, channel, id: "area" })
|
|
162
|
+
event.preventDefault()
|
|
99
163
|
},
|
|
100
164
|
style: {
|
|
101
165
|
position: "relative",
|
|
@@ -106,16 +170,16 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
106
170
|
})
|
|
107
171
|
},
|
|
108
172
|
|
|
109
|
-
|
|
110
|
-
const { xChannel, yChannel } = props
|
|
111
|
-
const { areaGradientStyles } = getColorAreaGradient(
|
|
173
|
+
getAreaBackgroundProps(props = {}) {
|
|
174
|
+
const { xChannel, yChannel } = getAreaChannels(props)
|
|
175
|
+
const { areaGradientStyles } = getColorAreaGradient(value, {
|
|
112
176
|
xChannel,
|
|
113
177
|
yChannel,
|
|
114
178
|
dir: state.context.dir,
|
|
115
179
|
})
|
|
116
180
|
|
|
117
181
|
return normalize.element({
|
|
118
|
-
...parts.
|
|
182
|
+
...parts.areaBackground.attrs,
|
|
119
183
|
id: dom.getAreaGradientId(state.context),
|
|
120
184
|
style: {
|
|
121
185
|
position: "relative",
|
|
@@ -126,16 +190,17 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
126
190
|
})
|
|
127
191
|
},
|
|
128
192
|
|
|
129
|
-
getAreaThumbProps(props
|
|
130
|
-
const { xChannel, yChannel } = props
|
|
131
|
-
const { getThumbPosition } = getChannelDetails(valueAsColor, xChannel, yChannel)
|
|
132
|
-
const { x, y } = getThumbPosition()
|
|
133
|
-
|
|
193
|
+
getAreaThumbProps(props = {}) {
|
|
194
|
+
const { xChannel, yChannel } = getAreaChannels(props)
|
|
134
195
|
const channel = { xChannel, yChannel }
|
|
135
196
|
|
|
197
|
+
const x = value.getChannelValuePercent(xChannel)
|
|
198
|
+
const y = 1 - value.getChannelValuePercent(yChannel)
|
|
199
|
+
|
|
136
200
|
return normalize.element({
|
|
137
201
|
...parts.areaThumb.attrs,
|
|
138
202
|
id: dom.getAreaThumbId(state.context),
|
|
203
|
+
dir: state.context.dir,
|
|
139
204
|
tabIndex: isDisabled ? undefined : 0,
|
|
140
205
|
"data-disabled": dataAttr(isDisabled),
|
|
141
206
|
role: "presentation",
|
|
@@ -146,13 +211,10 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
146
211
|
transform: "translate(-50%, -50%)",
|
|
147
212
|
touchAction: "none",
|
|
148
213
|
forcedColorAdjust: "none",
|
|
149
|
-
background:
|
|
150
|
-
},
|
|
151
|
-
onBlur() {
|
|
152
|
-
send("AREA.BLUR")
|
|
214
|
+
background: value.withChannelValue("alpha", 1).toString("css"),
|
|
153
215
|
},
|
|
154
216
|
onFocus() {
|
|
155
|
-
send({ type: "AREA.FOCUS", id: "area" })
|
|
217
|
+
send({ type: "AREA.FOCUS", id: "area", channel })
|
|
156
218
|
},
|
|
157
219
|
onKeyDown(event) {
|
|
158
220
|
if (!isInteractive) return
|
|
@@ -178,6 +240,9 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
178
240
|
PageDown() {
|
|
179
241
|
send({ type: "AREA.PAGE_DOWN", channel, step })
|
|
180
242
|
},
|
|
243
|
+
Escape(event) {
|
|
244
|
+
event.stopPropagation()
|
|
245
|
+
},
|
|
181
246
|
}
|
|
182
247
|
|
|
183
248
|
const exec = keyMap[getEventKey(event, state.context)]
|
|
@@ -190,15 +255,32 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
190
255
|
})
|
|
191
256
|
},
|
|
192
257
|
|
|
193
|
-
|
|
194
|
-
const {
|
|
258
|
+
getTransparencyGridProps(props = {}) {
|
|
259
|
+
const { size = "12px" } = props
|
|
260
|
+
return normalize.element({
|
|
261
|
+
...parts.transparencyGrid.attrs,
|
|
262
|
+
style: {
|
|
263
|
+
"--size": size,
|
|
264
|
+
width: "100%",
|
|
265
|
+
height: "100%",
|
|
266
|
+
position: "absolute",
|
|
267
|
+
backgroundColor: "#fff",
|
|
268
|
+
backgroundImage: "conic-gradient(#eeeeee 0 25%, transparent 0 50%, #eeeeee 0 75%, transparent 0)",
|
|
269
|
+
backgroundSize: "var(--size) var(--size)",
|
|
270
|
+
inset: "0px",
|
|
271
|
+
zIndex: "auto",
|
|
272
|
+
pointerEvents: "none",
|
|
273
|
+
},
|
|
274
|
+
})
|
|
275
|
+
},
|
|
195
276
|
|
|
277
|
+
getChannelSliderProps(props) {
|
|
278
|
+
const { orientation = "horizontal", channel } = props
|
|
196
279
|
return normalize.element({
|
|
197
|
-
...parts.
|
|
198
|
-
id: dom.getChannelSliderTrackId(state.context, channel),
|
|
199
|
-
role: "group",
|
|
280
|
+
...parts.channelSlider.attrs,
|
|
200
281
|
"data-channel": channel,
|
|
201
282
|
"data-orientation": orientation,
|
|
283
|
+
role: "presentation",
|
|
202
284
|
onPointerDown(event) {
|
|
203
285
|
if (!isInteractive) return
|
|
204
286
|
|
|
@@ -207,43 +289,42 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
207
289
|
|
|
208
290
|
const point = getEventPoint(evt)
|
|
209
291
|
send({ type: "CHANNEL_SLIDER.POINTER_DOWN", channel, point, id: channel, orientation })
|
|
292
|
+
|
|
293
|
+
event.preventDefault()
|
|
210
294
|
},
|
|
211
295
|
style: {
|
|
212
296
|
position: "relative",
|
|
213
297
|
touchAction: "none",
|
|
214
|
-
forcedColorAdjust: "none",
|
|
215
|
-
backgroundImage: getSliderBgImage(state.context, { orientation, channel }),
|
|
216
298
|
},
|
|
217
299
|
})
|
|
218
300
|
},
|
|
219
301
|
|
|
220
|
-
|
|
302
|
+
getChannelSliderTrackProps(props) {
|
|
221
303
|
const { orientation = "horizontal", channel } = props
|
|
304
|
+
|
|
222
305
|
return normalize.element({
|
|
223
|
-
...parts.
|
|
224
|
-
|
|
306
|
+
...parts.channelSliderTrack.attrs,
|
|
307
|
+
id: dom.getChannelSliderId(state.context, channel),
|
|
308
|
+
role: "group",
|
|
225
309
|
"data-channel": channel,
|
|
310
|
+
"data-orientation": orientation,
|
|
226
311
|
style: {
|
|
227
|
-
position: "
|
|
228
|
-
|
|
229
|
-
backgroundImage:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
backgroundSize: "16px 16px",
|
|
236
|
-
backgroundPosition: "-2px -2px,-2px 6px,6px -10px,-10px -2px",
|
|
237
|
-
inset: 0,
|
|
238
|
-
zIndex: -1,
|
|
312
|
+
position: "relative",
|
|
313
|
+
forcedColorAdjust: "none",
|
|
314
|
+
backgroundImage: getSliderBackground({
|
|
315
|
+
orientation,
|
|
316
|
+
channel,
|
|
317
|
+
dir: state.context.dir,
|
|
318
|
+
value: value,
|
|
319
|
+
}),
|
|
239
320
|
},
|
|
240
321
|
})
|
|
241
322
|
},
|
|
242
323
|
|
|
243
|
-
getChannelSliderThumbProps(props
|
|
324
|
+
getChannelSliderThumbProps(props) {
|
|
244
325
|
const { orientation = "horizontal", channel } = props
|
|
245
|
-
const { minValue, maxValue, step: stepValue } =
|
|
246
|
-
const channelValue =
|
|
326
|
+
const { minValue, maxValue, step: stepValue } = value.getChannelRange(channel)
|
|
327
|
+
const channelValue = value.getChannelValue(channel)
|
|
247
328
|
|
|
248
329
|
const offset = (channelValue - minValue) / (maxValue - minValue)
|
|
249
330
|
|
|
@@ -269,17 +350,13 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
269
350
|
style: {
|
|
270
351
|
forcedColorAdjust: "none",
|
|
271
352
|
position: "absolute",
|
|
272
|
-
background: getChannelDisplayColor(
|
|
353
|
+
background: getChannelDisplayColor(value, channel).toString("css"),
|
|
273
354
|
...placementStyles,
|
|
274
355
|
},
|
|
275
356
|
onFocus() {
|
|
276
357
|
if (!isInteractive) return
|
|
277
358
|
send({ type: "CHANNEL_SLIDER.FOCUS", channel })
|
|
278
359
|
},
|
|
279
|
-
onBlur() {
|
|
280
|
-
if (!isInteractive) return
|
|
281
|
-
send({ type: "CHANNEL_SLIDER.BLUR", channel })
|
|
282
|
-
},
|
|
283
360
|
onKeyDown(event) {
|
|
284
361
|
if (!isInteractive) return
|
|
285
362
|
const step = getEventStep(event) * stepValue
|
|
@@ -309,6 +386,9 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
309
386
|
End() {
|
|
310
387
|
send({ type: "CHANNEL_SLIDER.END", channel })
|
|
311
388
|
},
|
|
389
|
+
Escape(event) {
|
|
390
|
+
event.stopPropagation()
|
|
391
|
+
},
|
|
312
392
|
}
|
|
313
393
|
|
|
314
394
|
const exec = keyMap[getEventKey(event, state.context)]
|
|
@@ -321,10 +401,10 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
321
401
|
})
|
|
322
402
|
},
|
|
323
403
|
|
|
324
|
-
getChannelInputProps(props
|
|
404
|
+
getChannelInputProps(props) {
|
|
325
405
|
const { channel } = props
|
|
326
406
|
const isTextField = channel === "hex" || channel === "css"
|
|
327
|
-
const range =
|
|
407
|
+
const range = getChannelRange(value, channel)
|
|
328
408
|
|
|
329
409
|
return normalize.input({
|
|
330
410
|
...parts.channelInput.attrs,
|
|
@@ -335,22 +415,30 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
335
415
|
"data-disabled": dataAttr(isDisabled),
|
|
336
416
|
readOnly: state.context.readOnly,
|
|
337
417
|
id: dom.getChannelInputId(state.context, channel),
|
|
338
|
-
defaultValue:
|
|
418
|
+
defaultValue: getChannelValue(value, channel),
|
|
339
419
|
min: range?.minValue,
|
|
340
420
|
max: range?.maxValue,
|
|
341
421
|
step: range?.step,
|
|
422
|
+
onBeforeInput(event) {
|
|
423
|
+
if (isTextField) return
|
|
424
|
+
const value = event.currentTarget.value
|
|
425
|
+
if (value.match(/[^0-9.]/g)) {
|
|
426
|
+
event.preventDefault()
|
|
427
|
+
}
|
|
428
|
+
},
|
|
342
429
|
onFocus(event) {
|
|
343
430
|
send({ type: "CHANNEL_INPUT.FOCUS", channel })
|
|
344
|
-
|
|
431
|
+
event.target.select()
|
|
345
432
|
},
|
|
346
433
|
onBlur(event) {
|
|
347
|
-
const value = event.currentTarget.value
|
|
348
|
-
send({ type: "CHANNEL_INPUT.
|
|
434
|
+
const value = isTextField ? event.currentTarget.value : event.currentTarget.valueAsNumber
|
|
435
|
+
send({ type: "CHANNEL_INPUT.BLUR", channel, value, isTextField })
|
|
349
436
|
},
|
|
350
437
|
onKeyDown(event) {
|
|
351
438
|
if (event.key === "Enter") {
|
|
352
|
-
const value = event.currentTarget.value
|
|
439
|
+
const value = isTextField ? event.currentTarget.value : event.currentTarget.valueAsNumber
|
|
353
440
|
send({ type: "CHANNEL_INPUT.CHANGE", channel, value, isTextField })
|
|
441
|
+
event.preventDefault()
|
|
354
442
|
}
|
|
355
443
|
},
|
|
356
444
|
style: {
|
|
@@ -367,15 +455,12 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
367
455
|
name: state.context.name,
|
|
368
456
|
id: dom.getHiddenInputId(state.context),
|
|
369
457
|
style: visuallyHiddenStyle,
|
|
370
|
-
defaultValue:
|
|
371
|
-
onChange(event) {
|
|
372
|
-
const value = event.currentTarget.value
|
|
373
|
-
send({ type: "VALUE.SET", value, src: "input.change" })
|
|
374
|
-
},
|
|
458
|
+
defaultValue: valueAsString,
|
|
375
459
|
}),
|
|
376
460
|
|
|
377
461
|
eyeDropperTriggerProps: normalize.button({
|
|
378
462
|
...parts.eyeDropperTrigger.attrs,
|
|
463
|
+
type: "button",
|
|
379
464
|
disabled: isDisabled,
|
|
380
465
|
"data-disabled": dataAttr(isDisabled),
|
|
381
466
|
"aria-label": "Pick a color from the screen",
|
|
@@ -385,43 +470,40 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
|
|
|
385
470
|
},
|
|
386
471
|
}),
|
|
387
472
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
473
|
+
swatchGroupProps: normalize.element({
|
|
474
|
+
...parts.swatchGroup.attrs,
|
|
475
|
+
role: "group",
|
|
476
|
+
}),
|
|
477
|
+
|
|
478
|
+
getSwatchTriggerProps(props) {
|
|
479
|
+
const { value: valueProp } = props
|
|
480
|
+
const color = normalizeColor(valueProp).toFormat(value.getFormat())
|
|
481
|
+
return normalize.button({
|
|
482
|
+
...parts.swatchTrigger.attrs,
|
|
483
|
+
disabled: isDisabled,
|
|
484
|
+
type: "button",
|
|
485
|
+
"data-value": color.toString("hex"),
|
|
486
|
+
onClick() {
|
|
487
|
+
if (!isInteractive) return
|
|
488
|
+
send({ type: "VALUE.SET", value: color })
|
|
489
|
+
},
|
|
394
490
|
style: {
|
|
395
|
-
|
|
396
|
-
height: "100%",
|
|
397
|
-
background: "#fff",
|
|
398
|
-
backgroundImage: [
|
|
399
|
-
"linear-gradient(-45deg,#0000 75.5%,#bcbcbc 75.5%)",
|
|
400
|
-
"linear-gradient(45deg,#0000 75.5%,#bcbcbc 75.5%)",
|
|
401
|
-
"linear-gradient(-45deg,#bcbcbc 25.5%,#0000 25.5%)",
|
|
402
|
-
"linear-gradient(45deg,#bcbcbc 25.5%,#0000 25.5%)",
|
|
403
|
-
].join(","),
|
|
404
|
-
backgroundPosition: "-2px -2px,-2px 6px,6px -10px,-10px -2px",
|
|
405
|
-
backgroundSize: "16px 16px",
|
|
406
|
-
position: "absolute",
|
|
407
|
-
inset: "0px",
|
|
408
|
-
zIndex: -1,
|
|
491
|
+
position: "relative",
|
|
409
492
|
},
|
|
410
493
|
})
|
|
411
494
|
},
|
|
412
495
|
|
|
413
|
-
getSwatchProps(props
|
|
414
|
-
const { value,
|
|
415
|
-
const
|
|
496
|
+
getSwatchProps(props) {
|
|
497
|
+
const { value: valueProp, respectAlpha = true } = props
|
|
498
|
+
const colorValue = normalizeColor(valueProp)
|
|
499
|
+
const color = colorValue.toFormat(value.getFormat())
|
|
416
500
|
return normalize.element({
|
|
417
501
|
...parts.swatch.attrs,
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
send({ type: "VALUE.SET", value: color })
|
|
421
|
-
},
|
|
502
|
+
"data-state": colorValue.isEqual(value) ? "selected" : "unselected",
|
|
503
|
+
"data-value": color.toString("hex"),
|
|
422
504
|
style: {
|
|
423
505
|
position: "relative",
|
|
424
|
-
background: color.toString("css"),
|
|
506
|
+
background: color.toString(respectAlpha ? "css" : "hex"),
|
|
425
507
|
},
|
|
426
508
|
})
|
|
427
509
|
},
|
package/src/color-picker.dom.ts
CHANGED
|
@@ -2,19 +2,28 @@ import type { ColorChannel } from "@zag-js/color-utils"
|
|
|
2
2
|
import { getRelativePoint, type Point } from "@zag-js/dom-event"
|
|
3
3
|
import { createScope, queryAll } from "@zag-js/dom-query"
|
|
4
4
|
import type { MachineContext as Ctx } from "./color-picker.types"
|
|
5
|
+
import { getFirstFocusable } from "@zag-js/tabbable"
|
|
6
|
+
import { runIfFn } from "@zag-js/utils"
|
|
5
7
|
|
|
6
8
|
export const dom = createScope({
|
|
9
|
+
getRootId: (ctx: Ctx) => ctx.ids?.root ?? `color-picker:${ctx.id}`,
|
|
10
|
+
getLabelId: (ctx: Ctx) => ctx.ids?.label ?? `color-picker:${ctx.id}:label`,
|
|
11
|
+
getHiddenInputId: (ctx: Ctx) => `color-picker:${ctx.id}:hidden-input`,
|
|
12
|
+
getControlId: (ctx: Ctx) => ctx.ids?.control ?? `color-picker:${ctx.id}:control`,
|
|
13
|
+
getTriggerId: (ctx: Ctx) => ctx.ids?.trigger ?? `color-picker:${ctx.id}:trigger`,
|
|
7
14
|
getContentId: (ctx: Ctx) => ctx.ids?.content ?? `color-picker:${ctx.id}:content`,
|
|
15
|
+
getPositionerId: (ctx: Ctx) => `color-picker:${ctx.id}:positioner`,
|
|
16
|
+
|
|
8
17
|
getAreaId: (ctx: Ctx) => ctx.ids?.area ?? `color-picker:${ctx.id}:area`,
|
|
9
18
|
getAreaGradientId: (ctx: Ctx) => ctx.ids?.areaGradient ?? `color-picker:${ctx.id}:area-gradient`,
|
|
10
19
|
getAreaThumbId: (ctx: Ctx) => ctx.ids?.areaThumb ?? `color-picker:${ctx.id}:area-thumb`,
|
|
11
|
-
|
|
20
|
+
|
|
21
|
+
getChannelSliderId: (ctx: Ctx, channel: ColorChannel) =>
|
|
12
22
|
ctx.ids?.channelSliderTrack?.(channel) ?? `color-picker:${ctx.id}:slider-track:${channel}`,
|
|
13
23
|
getChannelInputId: (ctx: Ctx, channel: string) =>
|
|
14
24
|
ctx.ids?.channelInput?.(channel) ?? `color-picker:${ctx.id}:input:${channel}`,
|
|
15
25
|
getChannelSliderThumbId: (ctx: Ctx, channel: ColorChannel) =>
|
|
16
26
|
ctx.ids?.channelSliderThumb?.(channel) ?? `color-picker:${ctx.id}:slider-thumb:${channel}`,
|
|
17
|
-
getHiddenInputId: (ctx: Ctx) => `color-picker:${ctx.id}:hidden-input`,
|
|
18
27
|
|
|
19
28
|
getContentEl: (ctx: Ctx) => dom.getById(ctx, dom.getContentId(ctx)),
|
|
20
29
|
getAreaThumbEl: (ctx: Ctx) => dom.getById(ctx, dom.getAreaThumbId(ctx)),
|
|
@@ -32,8 +41,11 @@ export const dom = createScope({
|
|
|
32
41
|
return percent
|
|
33
42
|
},
|
|
34
43
|
|
|
44
|
+
getControlEl: (ctx: Ctx) => dom.getById(ctx, dom.getControlId(ctx)),
|
|
45
|
+
getTriggerEl: (ctx: Ctx) => dom.getById(ctx, dom.getTriggerId(ctx)),
|
|
46
|
+
getPositionerEl: (ctx: Ctx) => dom.getById(ctx, dom.getPositionerId(ctx)),
|
|
35
47
|
getChannelSliderTrackEl: (ctx: Ctx, channel: ColorChannel) => {
|
|
36
|
-
return dom.getById(ctx, dom.
|
|
48
|
+
return dom.getById(ctx, dom.getChannelSliderId(ctx, channel))
|
|
37
49
|
},
|
|
38
50
|
getChannelSliderValueFromPoint(ctx: Ctx, point: Point, channel: ColorChannel) {
|
|
39
51
|
const trackEl = dom.getChannelSliderTrackEl(ctx, channel)
|
|
@@ -42,6 +54,16 @@ export const dom = createScope({
|
|
|
42
54
|
return percent
|
|
43
55
|
},
|
|
44
56
|
getChannelInputEls: (ctx: Ctx) => {
|
|
45
|
-
return
|
|
57
|
+
return [
|
|
58
|
+
...queryAll<HTMLInputElement>(dom.getContentEl(ctx), "input[data-channel]"),
|
|
59
|
+
...queryAll<HTMLInputElement>(dom.getControlEl(ctx), "input[data-channel]"),
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
getFirstFocusableEl: (ctx: Ctx) => getFirstFocusable(dom.getContentEl(ctx), "if-empty"),
|
|
63
|
+
getInitialFocusEl: (ctx: Ctx): HTMLElement | undefined => {
|
|
64
|
+
let el: any = runIfFn(ctx.initialFocusEl)
|
|
65
|
+
if (!el && ctx.autoFocus) el = dom.getFirstFocusableEl(ctx)
|
|
66
|
+
if (!el) el = dom.getContentEl(ctx)
|
|
67
|
+
return el
|
|
46
68
|
},
|
|
47
69
|
})
|