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