@proyecto-viviana/solidaria-components 0.2.5 → 0.3.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/LICENSE +21 -0
- package/README.md +39 -272
- package/dist/ActionBar.d.ts +79 -0
- package/dist/ActionBar.d.ts.map +1 -0
- package/dist/ActionGroup.d.ts +74 -0
- package/dist/ActionGroup.d.ts.map +1 -0
- package/dist/Alert.d.ts +70 -0
- package/dist/Alert.d.ts.map +1 -0
- package/dist/Autocomplete.d.ts +5 -5
- package/dist/Autocomplete.d.ts.map +1 -1
- package/dist/Breadcrumbs.d.ts +27 -8
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Button.d.ts +28 -5
- package/dist/Button.d.ts.map +1 -1
- package/dist/Calendar.d.ts +51 -7
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +33 -8
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/Collection.d.ts +130 -0
- package/dist/Collection.d.ts.map +1 -0
- package/dist/Color.d.ts +210 -9
- package/dist/Color.d.ts.map +1 -1
- package/dist/ColorEditor.d.ts +42 -0
- package/dist/ColorEditor.d.ts.map +1 -0
- package/dist/ComboBox.d.ts +146 -16
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/ContextualHelpTrigger.d.ts +40 -0
- package/dist/ContextualHelpTrigger.d.ts.map +1 -0
- package/dist/DateField.d.ts +35 -8
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DatePicker.d.ts +101 -5
- package/dist/DatePicker.d.ts.map +1 -1
- package/dist/DateRangePickerContext.d.ts +30 -0
- package/dist/DateRangePickerContext.d.ts.map +1 -0
- package/dist/Dialog.d.ts +5 -5
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Disclosure.d.ts +25 -5
- package/dist/Disclosure.d.ts.map +1 -1
- package/dist/DragAndDrop.d.ts +80 -0
- package/dist/DragAndDrop.d.ts.map +1 -0
- package/dist/DragPreview.d.ts +14 -0
- package/dist/DragPreview.d.ts.map +1 -0
- package/dist/DropZone.d.ts +27 -0
- package/dist/DropZone.d.ts.map +1 -0
- package/dist/FieldError.d.ts +27 -0
- package/dist/FieldError.d.ts.map +1 -0
- package/dist/FileTrigger.d.ts +26 -0
- package/dist/FileTrigger.d.ts.map +1 -0
- package/dist/Focusable.d.ts +27 -0
- package/dist/Focusable.d.ts.map +1 -0
- package/dist/Form.d.ts +41 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/GridList.d.ts +69 -10
- package/dist/GridList.d.ts.map +1 -1
- package/dist/HiddenDateInput.d.ts +26 -0
- package/dist/HiddenDateInput.d.ts.map +1 -0
- package/dist/HiddenTimeInput.d.ts +25 -0
- package/dist/HiddenTimeInput.d.ts.map +1 -0
- package/dist/Icon.d.ts +57 -0
- package/dist/Icon.d.ts.map +1 -0
- package/dist/Keyboard.d.ts +13 -0
- package/dist/Keyboard.d.ts.map +1 -0
- package/dist/Landmark.d.ts +3 -3
- package/dist/Landmark.d.ts.map +1 -1
- package/dist/Link.d.ts +10 -4
- package/dist/Link.d.ts.map +1 -1
- package/dist/ListBox.d.ts +73 -11
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/ListDropTargetDelegate.d.ts +38 -0
- package/dist/ListDropTargetDelegate.d.ts.map +1 -0
- package/dist/Menu.d.ts +79 -10
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Meter.d.ts +4 -4
- package/dist/Meter.d.ts.map +1 -1
- package/dist/Modal.d.ts +6 -4
- package/dist/Modal.d.ts.map +1 -1
- package/dist/NumberField.d.ts +10 -12
- package/dist/NumberField.d.ts.map +1 -1
- package/dist/Popover.d.ts +32 -7
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Pressable.d.ts +27 -0
- package/dist/Pressable.d.ts.map +1 -0
- package/dist/ProgressBar.d.ts +6 -4
- package/dist/ProgressBar.d.ts.map +1 -1
- package/dist/RadioGroup.d.ts +43 -9
- package/dist/RadioGroup.d.ts.map +1 -1
- package/dist/RangeCalendar.d.ts +39 -7
- package/dist/RangeCalendar.d.ts.map +1 -1
- package/dist/RouterProvider.d.ts +75 -0
- package/dist/RouterProvider.d.ts.map +1 -0
- package/dist/SearchField.d.ts +23 -21
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/Select.d.ts +48 -7
- package/dist/Select.d.ts.map +1 -1
- package/dist/SelectionIndicator.d.ts +30 -0
- package/dist/SelectionIndicator.d.ts.map +1 -0
- package/dist/Separator.d.ts +9 -3
- package/dist/Separator.d.ts.map +1 -1
- package/dist/SharedElementTransition.d.ts +41 -0
- package/dist/SharedElementTransition.d.ts.map +1 -0
- package/dist/Slider.d.ts +15 -8
- package/dist/Slider.d.ts.map +1 -1
- package/dist/StepList.d.ts +90 -0
- package/dist/StepList.d.ts.map +1 -0
- package/dist/Switch.d.ts +11 -5
- package/dist/Switch.d.ts.map +1 -1
- package/dist/Table.d.ts +222 -19
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +47 -10
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/TagGroup.d.ts +22 -10
- package/dist/TagGroup.d.ts.map +1 -1
- package/dist/Text.d.ts +10 -0
- package/dist/Text.d.ts.map +1 -0
- package/dist/TextField.d.ts +19 -11
- package/dist/TextField.d.ts.map +1 -1
- package/dist/TimeField.d.ts +32 -7
- package/dist/TimeField.d.ts.map +1 -1
- package/dist/Toast.d.ts +29 -14
- package/dist/Toast.d.ts.map +1 -1
- package/dist/ToggleButton.d.ts +36 -0
- package/dist/ToggleButton.d.ts.map +1 -0
- package/dist/ToggleButtonGroup.d.ts +33 -0
- package/dist/ToggleButtonGroup.d.ts.map +1 -0
- package/dist/Toolbar.d.ts +7 -3
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/Tooltip.d.ts +58 -7
- package/dist/Tooltip.d.ts.map +1 -1
- package/dist/Tree.d.ts +102 -11
- package/dist/Tree.d.ts.map +1 -1
- package/dist/Virtualizer.d.ts +61 -0
- package/dist/Virtualizer.d.ts.map +1 -0
- package/dist/VirtualizerLayouts.d.ts +82 -0
- package/dist/VirtualizerLayouts.d.ts.map +1 -0
- package/dist/VisuallyHidden.d.ts +4 -2
- package/dist/VisuallyHidden.d.ts.map +1 -1
- package/dist/contexts.d.ts +6 -1
- package/dist/contexts.d.ts.map +1 -1
- package/dist/index.d.ts +73 -39
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23342 -10644
- package/dist/index.js.map +1 -7
- package/dist/index.jsx +18110 -0
- package/dist/index.jsx.map +1 -0
- package/dist/useDragAndDrop.d.ts +93 -0
- package/dist/useDragAndDrop.d.ts.map +1 -0
- package/dist/utils.d.ts +8 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/virtualizer/Layout.d.ts +79 -0
- package/dist/virtualizer/Layout.d.ts.map +1 -0
- package/package.json +33 -32
- package/src/ActionBar.tsx +251 -0
- package/src/ActionGroup.tsx +277 -0
- package/src/Alert.tsx +152 -0
- package/src/Autocomplete.tsx +39 -44
- package/src/Breadcrumbs.tsx +227 -72
- package/src/Button.tsx +315 -74
- package/src/Calendar.tsx +347 -141
- package/src/Checkbox.tsx +414 -123
- package/src/Collection.tsx +350 -0
- package/src/Color.tsx +1325 -284
- package/src/ColorEditor.tsx +213 -0
- package/src/ComboBox.tsx +644 -245
- package/src/ContextualHelpTrigger.tsx +195 -0
- package/src/DateField.tsx +274 -106
- package/src/DatePicker.tsx +892 -111
- package/src/DateRangePickerContext.tsx +44 -0
- package/src/Dialog.tsx +173 -104
- package/src/Disclosure.tsx +158 -105
- package/src/DragAndDrop.tsx +340 -0
- package/src/DragPreview.tsx +47 -0
- package/src/DropZone.tsx +233 -0
- package/src/FieldError.tsx +89 -0
- package/src/FileTrigger.tsx +83 -0
- package/src/Focusable.tsx +103 -0
- package/src/Form.tsx +140 -0
- package/src/GridList.tsx +542 -128
- package/src/HiddenDateInput.tsx +153 -0
- package/src/HiddenTimeInput.tsx +133 -0
- package/src/Icon.tsx +133 -0
- package/src/Keyboard.tsx +26 -0
- package/src/Landmark.tsx +37 -63
- package/src/Link.tsx +132 -69
- package/src/ListBox.tsx +656 -106
- package/src/ListDropTargetDelegate.ts +283 -0
- package/src/Menu.tsx +1234 -132
- package/src/Meter.tsx +44 -58
- package/src/Modal.tsx +262 -166
- package/src/NumberField.tsx +267 -151
- package/src/Popover.tsx +452 -343
- package/src/Pressable.tsx +108 -0
- package/src/ProgressBar.tsx +54 -59
- package/src/RadioGroup.tsx +533 -121
- package/src/RangeCalendar.tsx +249 -150
- package/src/RouterProvider.tsx +223 -0
- package/src/SearchField.tsx +460 -133
- package/src/Select.tsx +804 -233
- package/src/SelectionIndicator.tsx +108 -0
- package/src/Separator.tsx +47 -49
- package/src/SharedElementTransition.tsx +264 -0
- package/src/Slider.tsx +148 -98
- package/src/StepList.tsx +272 -0
- package/src/Switch.tsx +93 -46
- package/src/Table.tsx +1551 -225
- package/src/Tabs.tsx +377 -123
- package/src/TagGroup.tsx +233 -135
- package/src/Text.tsx +18 -0
- package/src/TextField.tsx +413 -86
- package/src/TimeField.tsx +232 -222
- package/src/Toast.tsx +306 -160
- package/src/ToggleButton.tsx +169 -0
- package/src/ToggleButtonGroup.tsx +141 -0
- package/src/Toolbar.tsx +61 -70
- package/src/Tooltip.tsx +473 -116
- package/src/Tree.tsx +1514 -175
- package/src/Virtualizer.tsx +730 -0
- package/src/VirtualizerLayouts.ts +280 -0
- package/src/VisuallyHidden.tsx +32 -38
- package/src/contexts.ts +29 -36
- package/src/index.ts +972 -620
- package/src/useDragAndDrop.ts +367 -0
- package/src/utils.tsx +69 -50
- package/src/virtualizer/Layout.ts +192 -0
- package/dist/index.ssr.js +0 -9785
- package/dist/index.ssr.js.map +0 -7
package/src/Color.tsx
CHANGED
|
@@ -8,25 +8,33 @@
|
|
|
8
8
|
import {
|
|
9
9
|
type JSX,
|
|
10
10
|
createContext,
|
|
11
|
+
createEffect,
|
|
11
12
|
createMemo,
|
|
13
|
+
createSignal,
|
|
14
|
+
onCleanup,
|
|
12
15
|
splitProps,
|
|
16
|
+
untrack,
|
|
13
17
|
useContext,
|
|
14
18
|
Show,
|
|
15
|
-
} from
|
|
19
|
+
} from "solid-js";
|
|
16
20
|
import {
|
|
17
21
|
createColorSlider,
|
|
18
22
|
createColorArea,
|
|
19
23
|
createColorWheel,
|
|
20
24
|
createColorField,
|
|
21
25
|
createColorSwatch,
|
|
26
|
+
createListBox,
|
|
27
|
+
createOption,
|
|
22
28
|
createFocusRing,
|
|
23
29
|
createHover,
|
|
30
|
+
mergeProps,
|
|
24
31
|
type AriaColorSliderOptions,
|
|
25
32
|
type AriaColorAreaOptions,
|
|
26
33
|
type AriaColorWheelOptions,
|
|
27
34
|
type AriaColorFieldOptions,
|
|
28
|
-
} from
|
|
35
|
+
} from "@proyecto-viviana/solidaria";
|
|
29
36
|
import {
|
|
37
|
+
createListState,
|
|
30
38
|
createColorSliderState,
|
|
31
39
|
createColorAreaState,
|
|
32
40
|
createColorWheelState,
|
|
@@ -35,11 +43,14 @@ import {
|
|
|
35
43
|
type Color,
|
|
36
44
|
type ColorChannel,
|
|
37
45
|
type ColorFormat,
|
|
46
|
+
type ColorSpace,
|
|
38
47
|
type ColorSliderState,
|
|
39
48
|
type ColorAreaState,
|
|
40
49
|
type ColorWheelState,
|
|
41
50
|
type ColorFieldState,
|
|
42
|
-
|
|
51
|
+
type ListState,
|
|
52
|
+
type Key,
|
|
53
|
+
} from "@proyecto-viviana/solid-stately";
|
|
43
54
|
import {
|
|
44
55
|
type RenderChildren,
|
|
45
56
|
type ClassNameOrFunction,
|
|
@@ -47,11 +58,35 @@ import {
|
|
|
47
58
|
type SlotProps,
|
|
48
59
|
useRenderProps,
|
|
49
60
|
filterDOMProps,
|
|
50
|
-
} from
|
|
61
|
+
} from "./utils";
|
|
51
62
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
interface ColorPickerChannelContextValue {
|
|
64
|
+
value?: Color | string;
|
|
65
|
+
onChange?: (color: Color) => void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface ColorPickerStateContextValue {
|
|
69
|
+
color: () => Color;
|
|
70
|
+
setColor: (color: Color) => void;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface ColorSwatchPickerItemData {
|
|
74
|
+
key: string;
|
|
75
|
+
color: Color;
|
|
76
|
+
textValue: string;
|
|
77
|
+
isDisabled?: boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface ColorSwatchPickerContextValue {
|
|
81
|
+
state: ListState<ColorSwatchPickerItemData>;
|
|
82
|
+
registerItem: (item: ColorSwatchPickerItemData) => void;
|
|
83
|
+
unregisterItem: (key: string) => void;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const ColorPickerContextInternal = createContext<ColorPickerChannelContextValue | null>(null);
|
|
87
|
+
const ColorPickerStateContextInternal = createContext<ColorPickerStateContextValue | null>(null);
|
|
88
|
+
const ColorSwatchContextInternal = createContext<{ color?: Color | string } | null>(null);
|
|
89
|
+
const ColorSwatchPickerContextInternal = createContext<ColorSwatchPickerContextValue | null>(null);
|
|
55
90
|
|
|
56
91
|
export interface ColorSliderRenderProps {
|
|
57
92
|
/** Whether the slider is disabled. */
|
|
@@ -60,13 +95,19 @@ export interface ColorSliderRenderProps {
|
|
|
60
95
|
isDragging: boolean;
|
|
61
96
|
/** The color channel being controlled. */
|
|
62
97
|
channel: ColorChannel;
|
|
98
|
+
/** The slider orientation. */
|
|
99
|
+
orientation: "horizontal" | "vertical";
|
|
63
100
|
/** The current value. */
|
|
64
101
|
value: number;
|
|
102
|
+
/** The formatted current value. */
|
|
103
|
+
valueLabel: string;
|
|
65
104
|
/** The current color. */
|
|
66
105
|
color: Color;
|
|
106
|
+
/** The default inline styles applied by the color slider hook. */
|
|
107
|
+
defaultStyle: JSX.CSSProperties;
|
|
67
108
|
}
|
|
68
109
|
|
|
69
|
-
export interface ColorSliderProps extends Omit<AriaColorSliderOptions,
|
|
110
|
+
export interface ColorSliderProps extends Omit<AriaColorSliderOptions, "channel">, SlotProps {
|
|
70
111
|
/** The current color value (controlled). */
|
|
71
112
|
value?: Color | string;
|
|
72
113
|
/** The default color value (uncontrolled). */
|
|
@@ -75,6 +116,8 @@ export interface ColorSliderProps extends Omit<AriaColorSliderOptions, 'channel'
|
|
|
75
116
|
onChange?: (color: Color) => void;
|
|
76
117
|
/** Handler called when dragging ends. */
|
|
77
118
|
onChangeEnd?: (color: Color) => void;
|
|
119
|
+
/** Color space used for channel values. */
|
|
120
|
+
colorSpace?: ColorSpace;
|
|
78
121
|
/** The color channel to control. */
|
|
79
122
|
channel: ColorChannel;
|
|
80
123
|
/** A visible label for the slider. */
|
|
@@ -92,6 +135,10 @@ export interface ColorSliderTrackRenderProps {
|
|
|
92
135
|
isDisabled: boolean;
|
|
93
136
|
/** Whether the slider is being dragged. */
|
|
94
137
|
isDragging: boolean;
|
|
138
|
+
/** The slider orientation. */
|
|
139
|
+
orientation: "horizontal" | "vertical";
|
|
140
|
+
/** The default inline styles applied by the color slider hook. */
|
|
141
|
+
defaultStyle: JSX.CSSProperties;
|
|
95
142
|
}
|
|
96
143
|
|
|
97
144
|
export interface ColorSliderTrackProps extends SlotProps {
|
|
@@ -103,6 +150,24 @@ export interface ColorSliderTrackProps extends SlotProps {
|
|
|
103
150
|
style?: StyleOrFunction<ColorSliderTrackRenderProps>;
|
|
104
151
|
}
|
|
105
152
|
|
|
153
|
+
export interface ColorSliderLabelProps extends SlotProps {
|
|
154
|
+
/** The label contents. */
|
|
155
|
+
children?: JSX.Element;
|
|
156
|
+
/** The CSS className for the element. */
|
|
157
|
+
class?: string;
|
|
158
|
+
/** The inline style for the element. */
|
|
159
|
+
style?: JSX.CSSProperties;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface ColorSliderOutputProps extends SlotProps {
|
|
163
|
+
/** The output contents. Defaults to the formatted slider value. */
|
|
164
|
+
children?: JSX.Element | ((renderProps: ColorSliderRenderProps) => JSX.Element);
|
|
165
|
+
/** The CSS className for the element. */
|
|
166
|
+
class?: string;
|
|
167
|
+
/** The inline style for the element. */
|
|
168
|
+
style?: JSX.CSSProperties;
|
|
169
|
+
}
|
|
170
|
+
|
|
106
171
|
export interface ColorSliderThumbRenderProps {
|
|
107
172
|
/** Whether the slider is disabled. */
|
|
108
173
|
isDisabled: boolean;
|
|
@@ -114,6 +179,10 @@ export interface ColorSliderThumbRenderProps {
|
|
|
114
179
|
isFocusVisible: boolean;
|
|
115
180
|
/** Whether the thumb is hovered. */
|
|
116
181
|
isHovered: boolean;
|
|
182
|
+
/** The current display color. */
|
|
183
|
+
color: Color;
|
|
184
|
+
/** The default inline styles applied by the color slider hook. */
|
|
185
|
+
defaultStyle: JSX.CSSProperties;
|
|
117
186
|
}
|
|
118
187
|
|
|
119
188
|
export interface ColorSliderThumbProps extends SlotProps {
|
|
@@ -123,16 +192,20 @@ export interface ColorSliderThumbProps extends SlotProps {
|
|
|
123
192
|
class?: ClassNameOrFunction<ColorSliderThumbRenderProps>;
|
|
124
193
|
/** The inline style for the element. */
|
|
125
194
|
style?: StyleOrFunction<ColorSliderThumbRenderProps>;
|
|
195
|
+
/** Ref callback for the thumb element. */
|
|
196
|
+
ref?: (element: HTMLDivElement) => void;
|
|
126
197
|
}
|
|
127
198
|
|
|
128
|
-
// Context
|
|
129
199
|
interface ColorSliderContextValue {
|
|
130
200
|
state: ColorSliderState;
|
|
131
201
|
trackProps: JSX.HTMLAttributes<HTMLDivElement>;
|
|
132
202
|
thumbProps: JSX.HTMLAttributes<HTMLDivElement>;
|
|
133
203
|
inputProps: JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
204
|
+
outputProps: JSX.HTMLAttributes<HTMLOutputElement>;
|
|
205
|
+
labelProps: JSX.LabelHTMLAttributes<HTMLLabelElement>;
|
|
134
206
|
trackRef: HTMLDivElement | undefined;
|
|
135
207
|
setTrackRef: (el: HTMLDivElement) => void;
|
|
208
|
+
setInputRef: (el: HTMLInputElement) => void;
|
|
136
209
|
}
|
|
137
210
|
|
|
138
211
|
export const ColorSliderContext = createContext<ColorSliderContextValue | null>(null);
|
|
@@ -141,80 +214,116 @@ export const ColorSliderContext = createContext<ColorSliderContextValue | null>(
|
|
|
141
214
|
* A color slider allows users to adjust a single color channel.
|
|
142
215
|
*/
|
|
143
216
|
export function ColorSlider(props: ColorSliderProps): JSX.Element {
|
|
217
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
144
218
|
const [local, stateProps, ariaProps, rest] = splitProps(
|
|
145
219
|
props,
|
|
146
|
-
[
|
|
147
|
-
[
|
|
148
|
-
[
|
|
220
|
+
["children", "class", "style", "slot", "label"],
|
|
221
|
+
["value", "defaultValue", "onChange", "onChangeEnd", "channel", "colorSpace"],
|
|
222
|
+
[
|
|
223
|
+
"id",
|
|
224
|
+
"aria-label",
|
|
225
|
+
"aria-labelledby",
|
|
226
|
+
"aria-describedby",
|
|
227
|
+
"aria-details",
|
|
228
|
+
"isDisabled",
|
|
229
|
+
"name",
|
|
230
|
+
"form",
|
|
231
|
+
"orientation",
|
|
232
|
+
"channelName",
|
|
233
|
+
],
|
|
149
234
|
);
|
|
150
235
|
|
|
151
236
|
// Create color slider state
|
|
152
237
|
const state = createColorSliderState(() => ({
|
|
153
|
-
value: stateProps.value,
|
|
238
|
+
value: stateProps.value ?? pickerContext?.value,
|
|
154
239
|
defaultValue: stateProps.defaultValue,
|
|
155
|
-
onChange: stateProps.onChange,
|
|
240
|
+
onChange: stateProps.onChange ?? pickerContext?.onChange,
|
|
156
241
|
onChangeEnd: stateProps.onChangeEnd,
|
|
157
242
|
channel: stateProps.channel,
|
|
243
|
+
colorSpace: stateProps.colorSpace,
|
|
244
|
+
orientation: ariaProps.orientation,
|
|
158
245
|
isDisabled: ariaProps.isDisabled,
|
|
159
246
|
}));
|
|
160
247
|
|
|
161
|
-
// Track ref
|
|
162
248
|
let trackRef: HTMLDivElement | undefined;
|
|
163
249
|
const setTrackRef = (el: HTMLDivElement) => {
|
|
164
250
|
trackRef = el;
|
|
165
251
|
};
|
|
252
|
+
let inputRef: HTMLInputElement | undefined;
|
|
253
|
+
const setInputRef = (el: HTMLInputElement) => {
|
|
254
|
+
inputRef = el;
|
|
255
|
+
};
|
|
166
256
|
|
|
167
257
|
// Create color slider aria props
|
|
168
|
-
const
|
|
169
|
-
trackProps,
|
|
170
|
-
thumbProps,
|
|
171
|
-
inputProps,
|
|
172
|
-
labelProps,
|
|
173
|
-
} = createColorSlider(
|
|
258
|
+
const colorSliderAria = createColorSlider(
|
|
174
259
|
() => ({
|
|
260
|
+
id: ariaProps.id,
|
|
175
261
|
channel: stateProps.channel,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
262
|
+
label: local.label,
|
|
263
|
+
"aria-label": ariaProps["aria-label"],
|
|
264
|
+
"aria-labelledby": ariaProps["aria-labelledby"],
|
|
265
|
+
"aria-describedby": ariaProps["aria-describedby"],
|
|
266
|
+
"aria-details": ariaProps["aria-details"],
|
|
179
267
|
isDisabled: ariaProps.isDisabled,
|
|
268
|
+
name: ariaProps.name,
|
|
269
|
+
form: ariaProps.form,
|
|
270
|
+
orientation: ariaProps.orientation,
|
|
180
271
|
channelName: ariaProps.channelName,
|
|
181
272
|
}),
|
|
182
273
|
() => state,
|
|
183
|
-
() => trackRef ?? null
|
|
274
|
+
() => trackRef ?? null,
|
|
275
|
+
() => inputRef ?? null,
|
|
184
276
|
);
|
|
185
277
|
|
|
186
|
-
// Render props values
|
|
187
278
|
const renderValues = createMemo<ColorSliderRenderProps>(() => ({
|
|
188
279
|
isDisabled: state.isDisabled,
|
|
189
280
|
isDragging: state.isDragging,
|
|
190
281
|
channel: state.channel,
|
|
282
|
+
orientation: state.orientation,
|
|
191
283
|
value: state.getThumbValue(),
|
|
284
|
+
valueLabel: state.getThumbValueLabel(),
|
|
192
285
|
color: state.value,
|
|
286
|
+
defaultStyle: (colorSliderAria.trackProps as { style?: JSX.CSSProperties }).style ?? {},
|
|
193
287
|
}));
|
|
194
288
|
|
|
195
|
-
// Resolve render props
|
|
196
289
|
const renderProps = useRenderProps(
|
|
197
290
|
{
|
|
198
291
|
children: props.children,
|
|
199
292
|
class: local.class,
|
|
200
293
|
style: local.style,
|
|
201
|
-
defaultClassName:
|
|
294
|
+
defaultClassName: "solidaria-ColorSlider",
|
|
202
295
|
},
|
|
203
|
-
renderValues
|
|
296
|
+
renderValues,
|
|
204
297
|
);
|
|
205
298
|
|
|
206
|
-
|
|
207
|
-
|
|
299
|
+
const domProps = createMemo(() =>
|
|
300
|
+
filterDOMProps(rest as Record<string, unknown>, { global: true }),
|
|
301
|
+
);
|
|
208
302
|
|
|
209
303
|
return (
|
|
210
304
|
<ColorSliderContext.Provider
|
|
211
305
|
value={{
|
|
212
306
|
state,
|
|
213
|
-
trackProps
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
307
|
+
get trackProps() {
|
|
308
|
+
return colorSliderAria.trackProps;
|
|
309
|
+
},
|
|
310
|
+
get thumbProps() {
|
|
311
|
+
return colorSliderAria.thumbProps;
|
|
312
|
+
},
|
|
313
|
+
get inputProps() {
|
|
314
|
+
return colorSliderAria.inputProps;
|
|
315
|
+
},
|
|
316
|
+
get outputProps() {
|
|
317
|
+
return colorSliderAria.outputProps;
|
|
318
|
+
},
|
|
319
|
+
get labelProps() {
|
|
320
|
+
return colorSliderAria.labelProps;
|
|
321
|
+
},
|
|
322
|
+
get trackRef() {
|
|
323
|
+
return trackRef;
|
|
324
|
+
},
|
|
217
325
|
setTrackRef,
|
|
326
|
+
setInputRef,
|
|
218
327
|
}}
|
|
219
328
|
>
|
|
220
329
|
<div
|
|
@@ -223,73 +332,129 @@ export function ColorSlider(props: ColorSliderProps): JSX.Element {
|
|
|
223
332
|
style={renderProps.style()}
|
|
224
333
|
data-disabled={state.isDisabled || undefined}
|
|
225
334
|
data-dragging={state.isDragging || undefined}
|
|
226
|
-
data-
|
|
335
|
+
data-orientation={state.orientation}
|
|
336
|
+
slot={local.slot || undefined}
|
|
227
337
|
>
|
|
228
|
-
{/* Label */}
|
|
229
|
-
<Show when={local.label}>
|
|
230
|
-
<label {...labelProps}>{local.label}</label>
|
|
231
|
-
</Show>
|
|
232
|
-
|
|
233
338
|
{renderProps.renderChildren()}
|
|
234
|
-
|
|
235
|
-
{/* Hidden input for accessibility */}
|
|
236
|
-
<input {...inputProps} />
|
|
237
339
|
</div>
|
|
238
340
|
</ColorSliderContext.Provider>
|
|
239
341
|
);
|
|
240
342
|
}
|
|
241
343
|
|
|
344
|
+
/**
|
|
345
|
+
* The label element of a color slider.
|
|
346
|
+
*/
|
|
347
|
+
export function ColorSliderLabel(props: ColorSliderLabelProps): JSX.Element {
|
|
348
|
+
const [local, domProps] = splitProps(props, ["class", "slot", "children"]);
|
|
349
|
+
|
|
350
|
+
const context = useContext(ColorSliderContext);
|
|
351
|
+
if (!context) {
|
|
352
|
+
throw new Error("ColorSliderLabel must be used within a ColorSlider");
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const labelProps = () => {
|
|
356
|
+
const { ref: _ref, ...rest } = context.labelProps as Record<string, unknown>;
|
|
357
|
+
return rest;
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
return (
|
|
361
|
+
<label {...domProps} {...labelProps()} class={local.class}>
|
|
362
|
+
{local.children}
|
|
363
|
+
</label>
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* The output element of a color slider.
|
|
369
|
+
*/
|
|
370
|
+
export function ColorSliderOutput(props: ColorSliderOutputProps): JSX.Element {
|
|
371
|
+
const [local, domProps] = splitProps(props, ["class", "slot", "children"]);
|
|
372
|
+
|
|
373
|
+
const context = useContext(ColorSliderContext);
|
|
374
|
+
if (!context) {
|
|
375
|
+
throw new Error("ColorSliderOutput must be used within a ColorSlider");
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const state = context.state;
|
|
379
|
+
|
|
380
|
+
const renderValues = createMemo<ColorSliderRenderProps>(() => ({
|
|
381
|
+
isDisabled: state.isDisabled,
|
|
382
|
+
isDragging: state.isDragging,
|
|
383
|
+
channel: state.channel,
|
|
384
|
+
orientation: state.orientation,
|
|
385
|
+
value: state.getThumbValue(),
|
|
386
|
+
valueLabel: state.getThumbValueLabel(),
|
|
387
|
+
color: state.value,
|
|
388
|
+
defaultStyle: (context.trackProps as { style?: JSX.CSSProperties }).style ?? {},
|
|
389
|
+
}));
|
|
390
|
+
|
|
391
|
+
const children = () =>
|
|
392
|
+
typeof local.children === "function"
|
|
393
|
+
? local.children(renderValues())
|
|
394
|
+
: (local.children ?? renderValues().valueLabel);
|
|
395
|
+
|
|
396
|
+
return (
|
|
397
|
+
<output {...domProps} {...context.outputProps} class={local.class}>
|
|
398
|
+
{children()}
|
|
399
|
+
</output>
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
242
403
|
/**
|
|
243
404
|
* The track element of a color slider.
|
|
244
405
|
*/
|
|
245
406
|
export function ColorSliderTrack(props: ColorSliderTrackProps): JSX.Element {
|
|
246
|
-
const [local] = splitProps(props, [
|
|
407
|
+
const [local, domProps] = splitProps(props, ["class", "style", "slot", "children"]);
|
|
247
408
|
|
|
248
409
|
const context = useContext(ColorSliderContext);
|
|
249
410
|
if (!context) {
|
|
250
|
-
throw new Error(
|
|
411
|
+
throw new Error("ColorSliderTrack must be used within a ColorSlider");
|
|
251
412
|
}
|
|
252
413
|
|
|
253
|
-
const
|
|
414
|
+
const state = context.state;
|
|
254
415
|
|
|
255
|
-
// Render props values
|
|
256
416
|
const renderValues = createMemo<ColorSliderTrackRenderProps>(() => ({
|
|
257
417
|
isDisabled: state.isDisabled,
|
|
258
418
|
isDragging: state.isDragging,
|
|
419
|
+
orientation: state.orientation,
|
|
420
|
+
defaultStyle: (context.trackProps as { style?: JSX.CSSProperties }).style ?? {},
|
|
259
421
|
}));
|
|
260
422
|
|
|
261
|
-
// Resolve render props
|
|
262
423
|
const renderProps = useRenderProps(
|
|
263
424
|
{
|
|
264
425
|
children: props.children,
|
|
265
426
|
class: local.class,
|
|
266
427
|
style: local.style,
|
|
267
|
-
defaultClassName:
|
|
428
|
+
defaultClassName: "solidaria-ColorSlider-track",
|
|
268
429
|
},
|
|
269
|
-
renderValues
|
|
430
|
+
renderValues,
|
|
270
431
|
);
|
|
271
432
|
|
|
272
|
-
// Clean props
|
|
273
433
|
const cleanTrackProps = () => {
|
|
274
|
-
const {
|
|
434
|
+
const {
|
|
435
|
+
ref: _ref,
|
|
436
|
+
style: _trackStyle,
|
|
437
|
+
...rest
|
|
438
|
+
} = context.trackProps as Record<string, unknown>;
|
|
275
439
|
return rest;
|
|
276
440
|
};
|
|
277
441
|
|
|
278
|
-
// Merge styles
|
|
279
442
|
const mergedStyle = () => {
|
|
280
|
-
const trackStyle = (trackProps as { style?: Record<string, string> }).style || {};
|
|
443
|
+
const trackStyle = (context.trackProps as { style?: Record<string, string> }).style || {};
|
|
281
444
|
const renderStyle = renderProps.style() || {};
|
|
282
445
|
return { ...trackStyle, ...renderStyle };
|
|
283
446
|
};
|
|
284
447
|
|
|
285
448
|
return (
|
|
286
449
|
<div
|
|
287
|
-
|
|
450
|
+
{...domProps}
|
|
451
|
+
ref={context.setTrackRef}
|
|
288
452
|
{...cleanTrackProps()}
|
|
289
453
|
class={renderProps.class()}
|
|
290
454
|
style={mergedStyle()}
|
|
291
455
|
data-disabled={state.isDisabled || undefined}
|
|
292
456
|
data-dragging={state.isDragging || undefined}
|
|
457
|
+
data-orientation={state.orientation}
|
|
293
458
|
>
|
|
294
459
|
{renderProps.renderChildren()}
|
|
295
460
|
</div>
|
|
@@ -300,48 +465,49 @@ export function ColorSliderTrack(props: ColorSliderTrackProps): JSX.Element {
|
|
|
300
465
|
* The thumb element of a color slider.
|
|
301
466
|
*/
|
|
302
467
|
export function ColorSliderThumb(props: ColorSliderThumbProps): JSX.Element {
|
|
303
|
-
const [local] = splitProps(props, [
|
|
468
|
+
const [local, domProps] = splitProps(props, ["class", "style", "slot", "children", "ref"]);
|
|
304
469
|
|
|
305
470
|
const context = useContext(ColorSliderContext);
|
|
306
471
|
if (!context) {
|
|
307
|
-
throw new Error(
|
|
472
|
+
throw new Error("ColorSliderThumb must be used within a ColorSlider");
|
|
308
473
|
}
|
|
309
474
|
|
|
310
|
-
const
|
|
475
|
+
const state = context.state;
|
|
311
476
|
|
|
312
|
-
// Create focus ring
|
|
313
477
|
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
314
478
|
|
|
315
|
-
// Create hover
|
|
316
479
|
const { isHovered, hoverProps } = createHover({
|
|
317
480
|
get isDisabled() {
|
|
318
481
|
return state.isDisabled;
|
|
319
482
|
},
|
|
320
483
|
});
|
|
321
484
|
|
|
322
|
-
// Render props values
|
|
323
485
|
const renderValues = createMemo<ColorSliderThumbRenderProps>(() => ({
|
|
324
486
|
isDisabled: state.isDisabled,
|
|
325
487
|
isDragging: state.isDragging,
|
|
326
488
|
isFocused: isFocused(),
|
|
327
489
|
isFocusVisible: isFocusVisible(),
|
|
328
490
|
isHovered: isHovered(),
|
|
491
|
+
color: state.getDisplayColor(),
|
|
492
|
+
defaultStyle: (context.thumbProps as { style?: JSX.CSSProperties }).style ?? {},
|
|
329
493
|
}));
|
|
330
494
|
|
|
331
|
-
// Resolve render props
|
|
332
495
|
const renderProps = useRenderProps(
|
|
333
496
|
{
|
|
334
497
|
children: props.children,
|
|
335
498
|
class: local.class,
|
|
336
499
|
style: local.style,
|
|
337
|
-
defaultClassName:
|
|
500
|
+
defaultClassName: "solidaria-ColorSlider-thumb",
|
|
338
501
|
},
|
|
339
|
-
renderValues
|
|
502
|
+
renderValues,
|
|
340
503
|
);
|
|
341
504
|
|
|
342
|
-
// Clean props
|
|
343
505
|
const cleanThumbProps = () => {
|
|
344
|
-
const {
|
|
506
|
+
const {
|
|
507
|
+
ref: _ref,
|
|
508
|
+
style: _thumbStyle,
|
|
509
|
+
...rest
|
|
510
|
+
} = context.thumbProps as Record<string, unknown>;
|
|
345
511
|
return rest;
|
|
346
512
|
};
|
|
347
513
|
const cleanFocusProps = () => {
|
|
@@ -352,19 +518,25 @@ export function ColorSliderThumb(props: ColorSliderThumbProps): JSX.Element {
|
|
|
352
518
|
const { ref: _ref, ...rest } = hoverProps as Record<string, unknown>;
|
|
353
519
|
return rest;
|
|
354
520
|
};
|
|
521
|
+
const mergedInputProps = () => {
|
|
522
|
+
return mergeProps(
|
|
523
|
+
context.inputProps as Record<string, unknown>,
|
|
524
|
+
cleanFocusProps(),
|
|
525
|
+
) as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
526
|
+
};
|
|
355
527
|
|
|
356
|
-
// Merge styles
|
|
357
528
|
const mergedStyle = () => {
|
|
358
|
-
const thumbStyle = (thumbProps as { style?: Record<string, string> }).style || {};
|
|
529
|
+
const thumbStyle = (context.thumbProps as { style?: Record<string, string> }).style || {};
|
|
359
530
|
const renderStyle = renderProps.style() || {};
|
|
360
531
|
return { ...thumbStyle, ...renderStyle };
|
|
361
532
|
};
|
|
362
533
|
|
|
363
534
|
return (
|
|
364
535
|
<div
|
|
536
|
+
{...domProps}
|
|
365
537
|
{...cleanThumbProps()}
|
|
366
|
-
{...cleanFocusProps()}
|
|
367
538
|
{...cleanHoverProps()}
|
|
539
|
+
ref={local.ref}
|
|
368
540
|
class={renderProps.class()}
|
|
369
541
|
style={mergedStyle()}
|
|
370
542
|
data-disabled={state.isDisabled || undefined}
|
|
@@ -373,18 +545,16 @@ export function ColorSliderThumb(props: ColorSliderThumbProps): JSX.Element {
|
|
|
373
545
|
data-focus-visible={isFocusVisible() || undefined}
|
|
374
546
|
data-hovered={isHovered() || undefined}
|
|
375
547
|
>
|
|
548
|
+
<input ref={context.setInputRef} {...mergedInputProps()} />
|
|
376
549
|
{renderProps.renderChildren()}
|
|
377
550
|
</div>
|
|
378
551
|
);
|
|
379
552
|
}
|
|
380
553
|
|
|
381
|
-
// Attach sub-components
|
|
382
554
|
ColorSlider.Track = ColorSliderTrack;
|
|
383
555
|
ColorSlider.Thumb = ColorSliderThumb;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
// COLOR AREA
|
|
387
|
-
// ============================================
|
|
556
|
+
ColorSlider.Label = ColorSliderLabel;
|
|
557
|
+
ColorSlider.Output = ColorSliderOutput;
|
|
388
558
|
|
|
389
559
|
export interface ColorAreaRenderProps {
|
|
390
560
|
/** Whether the area is disabled. */
|
|
@@ -397,6 +567,8 @@ export interface ColorAreaRenderProps {
|
|
|
397
567
|
yChannel: ColorChannel;
|
|
398
568
|
/** The current color. */
|
|
399
569
|
color: Color;
|
|
570
|
+
/** The default inline styles applied by the color area hook. */
|
|
571
|
+
defaultStyle: JSX.CSSProperties;
|
|
400
572
|
}
|
|
401
573
|
|
|
402
574
|
export interface ColorAreaProps extends AriaColorAreaOptions, SlotProps {
|
|
@@ -439,6 +611,8 @@ export interface ColorAreaThumbRenderProps {
|
|
|
439
611
|
isDisabled: boolean;
|
|
440
612
|
/** Whether the thumb is being dragged. */
|
|
441
613
|
isDragging: boolean;
|
|
614
|
+
/** The current display color. */
|
|
615
|
+
color: Color;
|
|
442
616
|
/** Whether the thumb is focused. */
|
|
443
617
|
isFocused: boolean;
|
|
444
618
|
/** Whether the thumb has keyboard focus. */
|
|
@@ -454,9 +628,10 @@ export interface ColorAreaThumbProps extends SlotProps {
|
|
|
454
628
|
class?: ClassNameOrFunction<ColorAreaThumbRenderProps>;
|
|
455
629
|
/** The inline style for the element. */
|
|
456
630
|
style?: StyleOrFunction<ColorAreaThumbRenderProps>;
|
|
631
|
+
/** Ref callback for the thumb element. */
|
|
632
|
+
ref?: (element: HTMLDivElement) => void;
|
|
457
633
|
}
|
|
458
634
|
|
|
459
|
-
// Context
|
|
460
635
|
interface ColorAreaContextValue {
|
|
461
636
|
state: ColorAreaState;
|
|
462
637
|
colorAreaProps: JSX.HTMLAttributes<HTMLDivElement>;
|
|
@@ -474,21 +649,33 @@ export const ColorAreaContext = createContext<ColorAreaContextValue | null>(null
|
|
|
474
649
|
* A color area allows users to select a color using a 2D gradient.
|
|
475
650
|
*/
|
|
476
651
|
export function ColorArea(props: ColorAreaProps): JSX.Element {
|
|
652
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
477
653
|
const [local, stateProps, ariaProps, rest] = splitProps(
|
|
478
654
|
props,
|
|
479
|
-
[
|
|
480
|
-
[
|
|
481
|
-
[
|
|
655
|
+
["children", "class", "style", "slot"],
|
|
656
|
+
["value", "defaultValue", "onChange", "onChangeEnd", "xChannel", "yChannel", "colorSpace"],
|
|
657
|
+
[
|
|
658
|
+
"id",
|
|
659
|
+
"aria-label",
|
|
660
|
+
"aria-labelledby",
|
|
661
|
+
"aria-describedby",
|
|
662
|
+
"aria-details",
|
|
663
|
+
"isDisabled",
|
|
664
|
+
"xName",
|
|
665
|
+
"yName",
|
|
666
|
+
"form",
|
|
667
|
+
],
|
|
482
668
|
);
|
|
483
669
|
|
|
484
670
|
// Create color area state
|
|
485
671
|
const state = createColorAreaState(() => ({
|
|
486
|
-
value: stateProps.value,
|
|
672
|
+
value: stateProps.value ?? pickerContext?.value,
|
|
487
673
|
defaultValue: stateProps.defaultValue,
|
|
488
|
-
onChange: stateProps.onChange,
|
|
674
|
+
onChange: stateProps.onChange ?? pickerContext?.onChange,
|
|
489
675
|
onChangeEnd: stateProps.onChangeEnd,
|
|
490
676
|
xChannel: stateProps.xChannel,
|
|
491
677
|
yChannel: stateProps.yChannel,
|
|
678
|
+
colorSpace: stateProps.colorSpace,
|
|
492
679
|
isDisabled: ariaProps.isDisabled,
|
|
493
680
|
}));
|
|
494
681
|
|
|
@@ -499,55 +686,83 @@ export function ColorArea(props: ColorAreaProps): JSX.Element {
|
|
|
499
686
|
};
|
|
500
687
|
|
|
501
688
|
// Create color area aria props
|
|
502
|
-
const
|
|
503
|
-
colorAreaProps,
|
|
504
|
-
gradientProps,
|
|
505
|
-
thumbProps,
|
|
506
|
-
xInputProps,
|
|
507
|
-
yInputProps,
|
|
508
|
-
} = createColorArea(
|
|
689
|
+
const colorAreaAria = createColorArea(
|
|
509
690
|
() => ({
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
691
|
+
id: ariaProps.id,
|
|
692
|
+
"aria-label": ariaProps["aria-label"],
|
|
693
|
+
"aria-labelledby": ariaProps["aria-labelledby"],
|
|
694
|
+
"aria-describedby": ariaProps["aria-describedby"],
|
|
695
|
+
"aria-details": ariaProps["aria-details"],
|
|
513
696
|
isDisabled: ariaProps.isDisabled,
|
|
697
|
+
xName: ariaProps.xName,
|
|
698
|
+
yName: ariaProps.yName,
|
|
699
|
+
form: ariaProps.form,
|
|
514
700
|
}),
|
|
515
701
|
() => state,
|
|
516
|
-
() => areaRef ?? null
|
|
702
|
+
() => areaRef ?? null,
|
|
517
703
|
);
|
|
518
704
|
|
|
519
|
-
// Render props values
|
|
520
705
|
const renderValues = createMemo<ColorAreaRenderProps>(() => ({
|
|
521
706
|
isDisabled: state.isDisabled,
|
|
522
707
|
isDragging: state.isDragging,
|
|
523
708
|
xChannel: state.xChannel,
|
|
524
709
|
yChannel: state.yChannel,
|
|
525
710
|
color: state.value,
|
|
711
|
+
defaultStyle: (colorAreaAria.colorAreaProps as { style?: JSX.CSSProperties }).style ?? {},
|
|
526
712
|
}));
|
|
527
713
|
|
|
528
|
-
|
|
714
|
+
const childRenderValues: ColorAreaRenderProps = {
|
|
715
|
+
get isDisabled() {
|
|
716
|
+
return state.isDisabled;
|
|
717
|
+
},
|
|
718
|
+
get isDragging() {
|
|
719
|
+
return state.isDragging;
|
|
720
|
+
},
|
|
721
|
+
get xChannel() {
|
|
722
|
+
return state.xChannel;
|
|
723
|
+
},
|
|
724
|
+
get yChannel() {
|
|
725
|
+
return state.yChannel;
|
|
726
|
+
},
|
|
727
|
+
get color() {
|
|
728
|
+
return state.value;
|
|
729
|
+
},
|
|
730
|
+
get defaultStyle() {
|
|
731
|
+
return (colorAreaAria.colorAreaProps as { style?: JSX.CSSProperties }).style ?? {};
|
|
732
|
+
},
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
const colorAreaChildren = () => {
|
|
736
|
+
const children = props.children;
|
|
737
|
+
return typeof children === "function" ? children(childRenderValues) : children;
|
|
738
|
+
};
|
|
739
|
+
|
|
529
740
|
const renderProps = useRenderProps(
|
|
530
741
|
{
|
|
531
742
|
children: props.children,
|
|
532
743
|
class: local.class,
|
|
533
744
|
style: local.style,
|
|
534
|
-
defaultClassName:
|
|
745
|
+
defaultClassName: "solidaria-ColorArea",
|
|
535
746
|
},
|
|
536
|
-
renderValues
|
|
747
|
+
renderValues,
|
|
537
748
|
);
|
|
538
749
|
|
|
539
|
-
|
|
540
|
-
|
|
750
|
+
const domProps = createMemo(() =>
|
|
751
|
+
filterDOMProps(rest as Record<string, unknown>, { global: true }),
|
|
752
|
+
);
|
|
541
753
|
|
|
542
|
-
// Clean props
|
|
543
754
|
const cleanColorAreaProps = () => {
|
|
544
|
-
const {
|
|
755
|
+
const {
|
|
756
|
+
ref: _ref,
|
|
757
|
+
style: _areaStyle,
|
|
758
|
+
...rest
|
|
759
|
+
} = colorAreaAria.colorAreaProps as Record<string, unknown>;
|
|
545
760
|
return rest;
|
|
546
761
|
};
|
|
547
762
|
|
|
548
|
-
// Merge styles
|
|
549
763
|
const mergedStyle = () => {
|
|
550
|
-
const areaStyle =
|
|
764
|
+
const areaStyle =
|
|
765
|
+
(colorAreaAria.colorAreaProps as { style?: Record<string, string> }).style || {};
|
|
551
766
|
const renderStyle = renderProps.style() || {};
|
|
552
767
|
return { ...areaStyle, ...renderStyle };
|
|
553
768
|
};
|
|
@@ -556,11 +771,21 @@ export function ColorArea(props: ColorAreaProps): JSX.Element {
|
|
|
556
771
|
<ColorAreaContext.Provider
|
|
557
772
|
value={{
|
|
558
773
|
state,
|
|
559
|
-
colorAreaProps
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
774
|
+
get colorAreaProps() {
|
|
775
|
+
return colorAreaAria.colorAreaProps;
|
|
776
|
+
},
|
|
777
|
+
get gradientProps() {
|
|
778
|
+
return colorAreaAria.gradientProps;
|
|
779
|
+
},
|
|
780
|
+
get thumbProps() {
|
|
781
|
+
return colorAreaAria.thumbProps;
|
|
782
|
+
},
|
|
783
|
+
get xInputProps() {
|
|
784
|
+
return colorAreaAria.xInputProps;
|
|
785
|
+
},
|
|
786
|
+
get yInputProps() {
|
|
787
|
+
return colorAreaAria.yInputProps;
|
|
788
|
+
},
|
|
564
789
|
areaRef,
|
|
565
790
|
setAreaRef,
|
|
566
791
|
}}
|
|
@@ -571,14 +796,11 @@ export function ColorArea(props: ColorAreaProps): JSX.Element {
|
|
|
571
796
|
{...cleanColorAreaProps()}
|
|
572
797
|
class={renderProps.class()}
|
|
573
798
|
style={mergedStyle()}
|
|
799
|
+
slot={local.slot ?? undefined}
|
|
574
800
|
data-disabled={state.isDisabled || undefined}
|
|
575
801
|
data-dragging={state.isDragging || undefined}
|
|
576
802
|
>
|
|
577
|
-
{
|
|
578
|
-
|
|
579
|
-
{/* Hidden inputs for accessibility */}
|
|
580
|
-
<input {...xInputProps} />
|
|
581
|
-
<input {...yInputProps} />
|
|
803
|
+
{colorAreaChildren()}
|
|
582
804
|
</div>
|
|
583
805
|
</ColorAreaContext.Provider>
|
|
584
806
|
);
|
|
@@ -588,46 +810,47 @@ export function ColorArea(props: ColorAreaProps): JSX.Element {
|
|
|
588
810
|
* The gradient background of a color area.
|
|
589
811
|
*/
|
|
590
812
|
export function ColorAreaGradient(props: ColorAreaGradientProps): JSX.Element {
|
|
591
|
-
const [local] = splitProps(props, [
|
|
813
|
+
const [local, domProps] = splitProps(props, ["class", "style", "slot", "children"]);
|
|
592
814
|
|
|
593
815
|
const context = useContext(ColorAreaContext);
|
|
594
816
|
if (!context) {
|
|
595
|
-
throw new Error(
|
|
817
|
+
throw new Error("ColorAreaGradient must be used within a ColorArea");
|
|
596
818
|
}
|
|
597
819
|
|
|
598
|
-
const { state
|
|
820
|
+
const { state } = context;
|
|
599
821
|
|
|
600
|
-
// Render props values
|
|
601
822
|
const renderValues = createMemo<ColorAreaGradientRenderProps>(() => ({
|
|
602
823
|
isDisabled: state.isDisabled,
|
|
603
824
|
}));
|
|
604
825
|
|
|
605
|
-
// Resolve render props
|
|
606
826
|
const renderProps = useRenderProps(
|
|
607
827
|
{
|
|
608
828
|
children: props.children,
|
|
609
829
|
class: local.class,
|
|
610
830
|
style: local.style,
|
|
611
|
-
defaultClassName:
|
|
831
|
+
defaultClassName: "solidaria-ColorArea-gradient",
|
|
612
832
|
},
|
|
613
|
-
renderValues
|
|
833
|
+
renderValues,
|
|
614
834
|
);
|
|
615
835
|
|
|
616
|
-
// Clean props
|
|
617
836
|
const cleanGradientProps = () => {
|
|
618
|
-
const {
|
|
837
|
+
const {
|
|
838
|
+
ref: _ref,
|
|
839
|
+
style: _gradStyle,
|
|
840
|
+
...rest
|
|
841
|
+
} = context.gradientProps as Record<string, unknown>;
|
|
619
842
|
return rest;
|
|
620
843
|
};
|
|
621
844
|
|
|
622
|
-
// Merge styles
|
|
623
845
|
const mergedStyle = () => {
|
|
624
|
-
const gradStyle = (gradientProps as { style?: Record<string, string> }).style || {};
|
|
846
|
+
const gradStyle = (context.gradientProps as { style?: Record<string, string> }).style || {};
|
|
625
847
|
const renderStyle = renderProps.style() || {};
|
|
626
848
|
return { ...gradStyle, ...renderStyle };
|
|
627
849
|
};
|
|
628
850
|
|
|
629
851
|
return (
|
|
630
852
|
<div
|
|
853
|
+
{...domProps}
|
|
631
854
|
{...cleanGradientProps()}
|
|
632
855
|
class={renderProps.class()}
|
|
633
856
|
style={mergedStyle()}
|
|
@@ -642,48 +865,50 @@ export function ColorAreaGradient(props: ColorAreaGradientProps): JSX.Element {
|
|
|
642
865
|
* The thumb element of a color area.
|
|
643
866
|
*/
|
|
644
867
|
export function ColorAreaThumb(props: ColorAreaThumbProps): JSX.Element {
|
|
645
|
-
const [local] = splitProps(props, [
|
|
868
|
+
const [local, domProps] = splitProps(props, ["class", "style", "slot", "children", "ref"]);
|
|
646
869
|
|
|
647
870
|
const context = useContext(ColorAreaContext);
|
|
648
871
|
if (!context) {
|
|
649
|
-
throw new Error(
|
|
872
|
+
throw new Error("ColorAreaThumb must be used within a ColorArea");
|
|
650
873
|
}
|
|
651
874
|
|
|
652
|
-
const { state
|
|
875
|
+
const { state } = context;
|
|
653
876
|
|
|
654
|
-
// Create focus ring
|
|
655
877
|
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
878
|
+
let xInputRef: HTMLInputElement | undefined;
|
|
879
|
+
let yInputRef: HTMLInputElement | undefined;
|
|
656
880
|
|
|
657
|
-
// Create hover
|
|
658
881
|
const { isHovered, hoverProps } = createHover({
|
|
659
882
|
get isDisabled() {
|
|
660
883
|
return state.isDisabled;
|
|
661
884
|
},
|
|
662
885
|
});
|
|
663
886
|
|
|
664
|
-
// Render props values
|
|
665
887
|
const renderValues = createMemo<ColorAreaThumbRenderProps>(() => ({
|
|
666
888
|
isDisabled: state.isDisabled,
|
|
667
889
|
isDragging: state.isDragging,
|
|
890
|
+
color: state.getDisplayColor(),
|
|
668
891
|
isFocused: isFocused(),
|
|
669
892
|
isFocusVisible: isFocusVisible(),
|
|
670
893
|
isHovered: isHovered(),
|
|
671
894
|
}));
|
|
672
895
|
|
|
673
|
-
// Resolve render props
|
|
674
896
|
const renderProps = useRenderProps(
|
|
675
897
|
{
|
|
676
898
|
children: props.children,
|
|
677
899
|
class: local.class,
|
|
678
900
|
style: local.style,
|
|
679
|
-
defaultClassName:
|
|
901
|
+
defaultClassName: "solidaria-ColorArea-thumb",
|
|
680
902
|
},
|
|
681
|
-
renderValues
|
|
903
|
+
renderValues,
|
|
682
904
|
);
|
|
683
905
|
|
|
684
|
-
// Clean props
|
|
685
906
|
const cleanThumbProps = () => {
|
|
686
|
-
const {
|
|
907
|
+
const {
|
|
908
|
+
ref: _ref,
|
|
909
|
+
style: _thumbStyle,
|
|
910
|
+
...rest
|
|
911
|
+
} = context.thumbProps as Record<string, unknown>;
|
|
687
912
|
return rest;
|
|
688
913
|
};
|
|
689
914
|
const cleanFocusProps = () => {
|
|
@@ -694,18 +919,46 @@ export function ColorAreaThumb(props: ColorAreaThumbProps): JSX.Element {
|
|
|
694
919
|
const { ref: _ref, ...rest } = hoverProps as Record<string, unknown>;
|
|
695
920
|
return rest;
|
|
696
921
|
};
|
|
922
|
+
const mergedXInputProps = () => {
|
|
923
|
+
const { value: _value, ...inputProps } = context.xInputProps as Record<string, unknown>;
|
|
924
|
+
return mergeProps(inputProps, cleanFocusProps()) as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
925
|
+
};
|
|
926
|
+
const mergedYInputProps = () => {
|
|
927
|
+
const { value: _value, ...inputProps } = context.yInputProps as Record<string, unknown>;
|
|
928
|
+
return mergeProps(inputProps, cleanFocusProps()) as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
929
|
+
};
|
|
697
930
|
|
|
698
|
-
// Merge styles
|
|
699
931
|
const mergedStyle = () => {
|
|
700
|
-
const thumbStyle = (thumbProps as { style?: Record<string, string> }).style || {};
|
|
932
|
+
const thumbStyle = (context.thumbProps as { style?: Record<string, string> }).style || {};
|
|
701
933
|
const renderStyle = renderProps.style() || {};
|
|
702
934
|
return { ...thumbStyle, ...renderStyle };
|
|
703
935
|
};
|
|
704
936
|
|
|
937
|
+
const syncInputValue = (input: HTMLInputElement | undefined, value: number) => {
|
|
938
|
+
const nextValue = String(value);
|
|
939
|
+
const update = () => {
|
|
940
|
+
if (input && input.value !== nextValue) {
|
|
941
|
+
input.value = nextValue;
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
update();
|
|
946
|
+
queueMicrotask(update);
|
|
947
|
+
};
|
|
948
|
+
|
|
949
|
+
createEffect(() => {
|
|
950
|
+
syncInputValue(xInputRef, state.getXValue());
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
createEffect(() => {
|
|
954
|
+
syncInputValue(yInputRef, state.getYValue());
|
|
955
|
+
});
|
|
956
|
+
|
|
705
957
|
return (
|
|
706
958
|
<div
|
|
959
|
+
{...domProps}
|
|
960
|
+
ref={local.ref}
|
|
707
961
|
{...cleanThumbProps()}
|
|
708
|
-
{...cleanFocusProps()}
|
|
709
962
|
{...cleanHoverProps()}
|
|
710
963
|
class={renderProps.class()}
|
|
711
964
|
style={mergedStyle()}
|
|
@@ -715,19 +968,28 @@ export function ColorAreaThumb(props: ColorAreaThumbProps): JSX.Element {
|
|
|
715
968
|
data-focus-visible={isFocusVisible() || undefined}
|
|
716
969
|
data-hovered={isHovered() || undefined}
|
|
717
970
|
>
|
|
971
|
+
<input
|
|
972
|
+
{...mergedXInputProps()}
|
|
973
|
+
ref={(el) => {
|
|
974
|
+
xInputRef = el;
|
|
975
|
+
syncInputValue(el, state.getXValue());
|
|
976
|
+
}}
|
|
977
|
+
/>
|
|
978
|
+
<input
|
|
979
|
+
{...mergedYInputProps()}
|
|
980
|
+
ref={(el) => {
|
|
981
|
+
yInputRef = el;
|
|
982
|
+
syncInputValue(el, state.getYValue());
|
|
983
|
+
}}
|
|
984
|
+
/>
|
|
718
985
|
{renderProps.renderChildren()}
|
|
719
986
|
</div>
|
|
720
987
|
);
|
|
721
988
|
}
|
|
722
989
|
|
|
723
|
-
// Attach sub-components
|
|
724
990
|
ColorArea.Gradient = ColorAreaGradient;
|
|
725
991
|
ColorArea.Thumb = ColorAreaThumb;
|
|
726
992
|
|
|
727
|
-
// ============================================
|
|
728
|
-
// COLOR WHEEL
|
|
729
|
-
// ============================================
|
|
730
|
-
|
|
731
993
|
export interface ColorWheelRenderProps {
|
|
732
994
|
/** Whether the wheel is disabled. */
|
|
733
995
|
isDisabled: boolean;
|
|
@@ -737,6 +999,8 @@ export interface ColorWheelRenderProps {
|
|
|
737
999
|
hue: number;
|
|
738
1000
|
/** The current color. */
|
|
739
1001
|
color: Color;
|
|
1002
|
+
/** The default inline styles applied by the color wheel hook. */
|
|
1003
|
+
defaultStyle: JSX.CSSProperties;
|
|
740
1004
|
}
|
|
741
1005
|
|
|
742
1006
|
export interface ColorWheelProps extends AriaColorWheelOptions, SlotProps {
|
|
@@ -761,6 +1025,8 @@ export interface ColorWheelTrackRenderProps {
|
|
|
761
1025
|
isDisabled: boolean;
|
|
762
1026
|
/** Whether the wheel is being dragged. */
|
|
763
1027
|
isDragging: boolean;
|
|
1028
|
+
/** The default inline styles applied by the color wheel hook. */
|
|
1029
|
+
defaultStyle: JSX.CSSProperties;
|
|
764
1030
|
}
|
|
765
1031
|
|
|
766
1032
|
export interface ColorWheelTrackProps extends SlotProps {
|
|
@@ -777,12 +1043,16 @@ export interface ColorWheelThumbRenderProps {
|
|
|
777
1043
|
isDisabled: boolean;
|
|
778
1044
|
/** Whether the thumb is being dragged. */
|
|
779
1045
|
isDragging: boolean;
|
|
1046
|
+
/** The current display color. */
|
|
1047
|
+
color: Color;
|
|
780
1048
|
/** Whether the thumb is focused. */
|
|
781
1049
|
isFocused: boolean;
|
|
782
1050
|
/** Whether the thumb has keyboard focus. */
|
|
783
1051
|
isFocusVisible: boolean;
|
|
784
1052
|
/** Whether the thumb is hovered. */
|
|
785
1053
|
isHovered: boolean;
|
|
1054
|
+
/** The default inline styles applied by the color wheel hook. */
|
|
1055
|
+
defaultStyle: JSX.CSSProperties;
|
|
786
1056
|
}
|
|
787
1057
|
|
|
788
1058
|
export interface ColorWheelThumbProps extends SlotProps {
|
|
@@ -792,9 +1062,10 @@ export interface ColorWheelThumbProps extends SlotProps {
|
|
|
792
1062
|
class?: ClassNameOrFunction<ColorWheelThumbRenderProps>;
|
|
793
1063
|
/** The inline style for the element. */
|
|
794
1064
|
style?: StyleOrFunction<ColorWheelThumbRenderProps>;
|
|
1065
|
+
/** Ref callback for the thumb element. */
|
|
1066
|
+
ref?: (element: HTMLDivElement) => void;
|
|
795
1067
|
}
|
|
796
1068
|
|
|
797
|
-
// Context
|
|
798
1069
|
interface ColorWheelContextValue {
|
|
799
1070
|
state: ColorWheelState;
|
|
800
1071
|
trackProps: JSX.HTMLAttributes<HTMLDivElement>;
|
|
@@ -810,88 +1081,110 @@ export const ColorWheelContext = createContext<ColorWheelContextValue | null>(nu
|
|
|
810
1081
|
* A color wheel allows users to select a hue using a circular control.
|
|
811
1082
|
*/
|
|
812
1083
|
export function ColorWheel(props: ColorWheelProps): JSX.Element {
|
|
1084
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
813
1085
|
const [local, stateProps, ariaProps, rest] = splitProps(
|
|
814
1086
|
props,
|
|
815
|
-
[
|
|
816
|
-
[
|
|
817
|
-
[
|
|
1087
|
+
["children", "class", "style", "slot"],
|
|
1088
|
+
["value", "defaultValue", "onChange", "onChangeEnd"],
|
|
1089
|
+
[
|
|
1090
|
+
"id",
|
|
1091
|
+
"aria-label",
|
|
1092
|
+
"aria-labelledby",
|
|
1093
|
+
"aria-describedby",
|
|
1094
|
+
"aria-details",
|
|
1095
|
+
"aria-errormessage",
|
|
1096
|
+
"isDisabled",
|
|
1097
|
+
"name",
|
|
1098
|
+
"form",
|
|
1099
|
+
"outerRadius",
|
|
1100
|
+
"innerRadius",
|
|
1101
|
+
],
|
|
818
1102
|
);
|
|
819
1103
|
|
|
820
1104
|
// Create color wheel state
|
|
821
1105
|
const state = createColorWheelState(() => ({
|
|
822
|
-
value: stateProps.value,
|
|
1106
|
+
value: stateProps.value ?? pickerContext?.value,
|
|
823
1107
|
defaultValue: stateProps.defaultValue,
|
|
824
|
-
onChange: stateProps.onChange,
|
|
1108
|
+
onChange: stateProps.onChange ?? pickerContext?.onChange,
|
|
825
1109
|
onChangeEnd: stateProps.onChangeEnd,
|
|
826
1110
|
isDisabled: ariaProps.isDisabled,
|
|
827
1111
|
}));
|
|
828
1112
|
|
|
829
|
-
// Wheel ref
|
|
830
1113
|
let wheelRef: HTMLDivElement | undefined;
|
|
831
1114
|
const setWheelRef = (el: HTMLDivElement) => {
|
|
832
1115
|
wheelRef = el;
|
|
833
1116
|
};
|
|
834
1117
|
|
|
835
1118
|
// Create color wheel aria props
|
|
836
|
-
const
|
|
837
|
-
trackProps,
|
|
838
|
-
thumbProps,
|
|
839
|
-
inputProps,
|
|
840
|
-
} = createColorWheel(
|
|
1119
|
+
const colorWheelAria = createColorWheel(
|
|
841
1120
|
() => ({
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1121
|
+
id: ariaProps.id,
|
|
1122
|
+
"aria-label": ariaProps["aria-label"],
|
|
1123
|
+
"aria-labelledby": ariaProps["aria-labelledby"],
|
|
1124
|
+
"aria-describedby": ariaProps["aria-describedby"],
|
|
1125
|
+
"aria-details": ariaProps["aria-details"],
|
|
1126
|
+
"aria-errormessage": ariaProps["aria-errormessage"],
|
|
845
1127
|
isDisabled: ariaProps.isDisabled,
|
|
1128
|
+
name: ariaProps.name,
|
|
1129
|
+
form: ariaProps.form,
|
|
1130
|
+
outerRadius: ariaProps.outerRadius,
|
|
1131
|
+
innerRadius: ariaProps.innerRadius,
|
|
846
1132
|
}),
|
|
847
1133
|
() => state,
|
|
848
|
-
() => wheelRef ?? null
|
|
1134
|
+
() => wheelRef ?? null,
|
|
849
1135
|
);
|
|
850
1136
|
|
|
851
|
-
// Render props values
|
|
852
1137
|
const renderValues = createMemo<ColorWheelRenderProps>(() => ({
|
|
853
1138
|
isDisabled: state.isDisabled,
|
|
854
1139
|
isDragging: state.isDragging,
|
|
855
1140
|
hue: state.getHue(),
|
|
856
1141
|
color: state.value,
|
|
1142
|
+
defaultStyle: { position: "relative" },
|
|
857
1143
|
}));
|
|
858
1144
|
|
|
859
|
-
// Resolve render props
|
|
860
1145
|
const renderProps = useRenderProps(
|
|
861
1146
|
{
|
|
862
1147
|
children: props.children,
|
|
863
1148
|
class: local.class,
|
|
864
1149
|
style: local.style,
|
|
865
|
-
defaultClassName:
|
|
1150
|
+
defaultClassName: "solidaria-ColorWheel",
|
|
866
1151
|
},
|
|
867
|
-
renderValues
|
|
1152
|
+
renderValues,
|
|
868
1153
|
);
|
|
869
1154
|
|
|
870
|
-
|
|
871
|
-
|
|
1155
|
+
const domProps = createMemo(() =>
|
|
1156
|
+
filterDOMProps(rest as Record<string, unknown>, { global: true }),
|
|
1157
|
+
);
|
|
872
1158
|
|
|
873
1159
|
return (
|
|
874
1160
|
<ColorWheelContext.Provider
|
|
875
1161
|
value={{
|
|
876
1162
|
state,
|
|
877
|
-
trackProps
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1163
|
+
get trackProps() {
|
|
1164
|
+
return colorWheelAria.trackProps;
|
|
1165
|
+
},
|
|
1166
|
+
get thumbProps() {
|
|
1167
|
+
return colorWheelAria.thumbProps;
|
|
1168
|
+
},
|
|
1169
|
+
get inputProps() {
|
|
1170
|
+
return colorWheelAria.inputProps;
|
|
1171
|
+
},
|
|
1172
|
+
get wheelRef() {
|
|
1173
|
+
return wheelRef;
|
|
1174
|
+
},
|
|
881
1175
|
setWheelRef,
|
|
882
1176
|
}}
|
|
883
1177
|
>
|
|
884
1178
|
<div
|
|
1179
|
+
ref={setWheelRef}
|
|
885
1180
|
{...domProps()}
|
|
886
1181
|
class={renderProps.class()}
|
|
887
1182
|
style={renderProps.style()}
|
|
1183
|
+
slot={local.slot || undefined}
|
|
888
1184
|
data-disabled={state.isDisabled || undefined}
|
|
889
1185
|
data-dragging={state.isDragging || undefined}
|
|
890
1186
|
>
|
|
891
1187
|
{renderProps.renderChildren()}
|
|
892
|
-
|
|
893
|
-
{/* Hidden input for accessibility */}
|
|
894
|
-
<input {...inputProps} />
|
|
895
1188
|
</div>
|
|
896
1189
|
</ColorWheelContext.Provider>
|
|
897
1190
|
);
|
|
@@ -901,48 +1194,49 @@ export function ColorWheel(props: ColorWheelProps): JSX.Element {
|
|
|
901
1194
|
* The track element of a color wheel.
|
|
902
1195
|
*/
|
|
903
1196
|
export function ColorWheelTrack(props: ColorWheelTrackProps): JSX.Element {
|
|
904
|
-
const [local] = splitProps(props, [
|
|
1197
|
+
const [local, domProps] = splitProps(props, ["class", "style", "slot", "children"]);
|
|
905
1198
|
|
|
906
1199
|
const context = useContext(ColorWheelContext);
|
|
907
1200
|
if (!context) {
|
|
908
|
-
throw new Error(
|
|
1201
|
+
throw new Error("ColorWheelTrack must be used within a ColorWheel");
|
|
909
1202
|
}
|
|
910
1203
|
|
|
911
|
-
const
|
|
1204
|
+
const state = context.state;
|
|
912
1205
|
|
|
913
|
-
// Render props values
|
|
914
1206
|
const renderValues = createMemo<ColorWheelTrackRenderProps>(() => ({
|
|
915
1207
|
isDisabled: state.isDisabled,
|
|
916
1208
|
isDragging: state.isDragging,
|
|
1209
|
+
defaultStyle: (context.trackProps as { style?: JSX.CSSProperties }).style ?? {},
|
|
917
1210
|
}));
|
|
918
1211
|
|
|
919
|
-
// Resolve render props
|
|
920
1212
|
const renderProps = useRenderProps(
|
|
921
1213
|
{
|
|
922
1214
|
children: props.children,
|
|
923
1215
|
class: local.class,
|
|
924
1216
|
style: local.style,
|
|
925
|
-
defaultClassName:
|
|
1217
|
+
defaultClassName: "solidaria-ColorWheel-track",
|
|
926
1218
|
},
|
|
927
|
-
renderValues
|
|
1219
|
+
renderValues,
|
|
928
1220
|
);
|
|
929
1221
|
|
|
930
|
-
// Clean props
|
|
931
1222
|
const cleanTrackProps = () => {
|
|
932
|
-
const {
|
|
1223
|
+
const {
|
|
1224
|
+
ref: _ref,
|
|
1225
|
+
style: _trackStyle,
|
|
1226
|
+
...rest
|
|
1227
|
+
} = context.trackProps as Record<string, unknown>;
|
|
933
1228
|
return rest;
|
|
934
1229
|
};
|
|
935
1230
|
|
|
936
|
-
// Merge styles
|
|
937
1231
|
const mergedStyle = () => {
|
|
938
|
-
const trackStyle = (trackProps as { style?: Record<string, string> }).style || {};
|
|
1232
|
+
const trackStyle = (context.trackProps as { style?: Record<string, string> }).style || {};
|
|
939
1233
|
const renderStyle = renderProps.style() || {};
|
|
940
1234
|
return { ...trackStyle, ...renderStyle };
|
|
941
1235
|
};
|
|
942
1236
|
|
|
943
1237
|
return (
|
|
944
1238
|
<div
|
|
945
|
-
|
|
1239
|
+
{...domProps}
|
|
946
1240
|
{...cleanTrackProps()}
|
|
947
1241
|
class={renderProps.class()}
|
|
948
1242
|
style={mergedStyle()}
|
|
@@ -958,48 +1252,49 @@ export function ColorWheelTrack(props: ColorWheelTrackProps): JSX.Element {
|
|
|
958
1252
|
* The thumb element of a color wheel.
|
|
959
1253
|
*/
|
|
960
1254
|
export function ColorWheelThumb(props: ColorWheelThumbProps): JSX.Element {
|
|
961
|
-
const [local] = splitProps(props, [
|
|
1255
|
+
const [local, domProps] = splitProps(props, ["class", "style", "slot", "children", "ref"]);
|
|
962
1256
|
|
|
963
1257
|
const context = useContext(ColorWheelContext);
|
|
964
1258
|
if (!context) {
|
|
965
|
-
throw new Error(
|
|
1259
|
+
throw new Error("ColorWheelThumb must be used within a ColorWheel");
|
|
966
1260
|
}
|
|
967
1261
|
|
|
968
|
-
const
|
|
1262
|
+
const state = context.state;
|
|
969
1263
|
|
|
970
|
-
// Create focus ring
|
|
971
1264
|
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
972
1265
|
|
|
973
|
-
// Create hover
|
|
974
1266
|
const { isHovered, hoverProps } = createHover({
|
|
975
1267
|
get isDisabled() {
|
|
976
1268
|
return state.isDisabled;
|
|
977
1269
|
},
|
|
978
1270
|
});
|
|
979
1271
|
|
|
980
|
-
// Render props values
|
|
981
1272
|
const renderValues = createMemo<ColorWheelThumbRenderProps>(() => ({
|
|
982
1273
|
isDisabled: state.isDisabled,
|
|
983
1274
|
isDragging: state.isDragging,
|
|
1275
|
+
color: state.getDisplayColor(),
|
|
984
1276
|
isFocused: isFocused(),
|
|
985
1277
|
isFocusVisible: isFocusVisible(),
|
|
986
1278
|
isHovered: isHovered(),
|
|
1279
|
+
defaultStyle: (context.thumbProps as { style?: JSX.CSSProperties }).style ?? {},
|
|
987
1280
|
}));
|
|
988
1281
|
|
|
989
|
-
// Resolve render props
|
|
990
1282
|
const renderProps = useRenderProps(
|
|
991
1283
|
{
|
|
992
1284
|
children: props.children,
|
|
993
1285
|
class: local.class,
|
|
994
1286
|
style: local.style,
|
|
995
|
-
defaultClassName:
|
|
1287
|
+
defaultClassName: "solidaria-ColorWheel-thumb",
|
|
996
1288
|
},
|
|
997
|
-
renderValues
|
|
1289
|
+
renderValues,
|
|
998
1290
|
);
|
|
999
1291
|
|
|
1000
|
-
// Clean props
|
|
1001
1292
|
const cleanThumbProps = () => {
|
|
1002
|
-
const {
|
|
1293
|
+
const {
|
|
1294
|
+
ref: _ref,
|
|
1295
|
+
style: _thumbStyle,
|
|
1296
|
+
...rest
|
|
1297
|
+
} = context.thumbProps as Record<string, unknown>;
|
|
1003
1298
|
return rest;
|
|
1004
1299
|
};
|
|
1005
1300
|
const cleanFocusProps = () => {
|
|
@@ -1010,18 +1305,24 @@ export function ColorWheelThumb(props: ColorWheelThumbProps): JSX.Element {
|
|
|
1010
1305
|
const { ref: _ref, ...rest } = hoverProps as Record<string, unknown>;
|
|
1011
1306
|
return rest;
|
|
1012
1307
|
};
|
|
1308
|
+
const mergedInputProps = () => {
|
|
1309
|
+
return mergeProps(
|
|
1310
|
+
context.inputProps as Record<string, unknown>,
|
|
1311
|
+
cleanFocusProps(),
|
|
1312
|
+
) as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
1313
|
+
};
|
|
1013
1314
|
|
|
1014
|
-
// Merge styles
|
|
1015
1315
|
const mergedStyle = () => {
|
|
1016
|
-
const thumbStyle = (thumbProps as { style?: Record<string, string> }).style || {};
|
|
1316
|
+
const thumbStyle = (context.thumbProps as { style?: Record<string, string> }).style || {};
|
|
1017
1317
|
const renderStyle = renderProps.style() || {};
|
|
1018
1318
|
return { ...thumbStyle, ...renderStyle };
|
|
1019
1319
|
};
|
|
1020
1320
|
|
|
1021
1321
|
return (
|
|
1022
1322
|
<div
|
|
1323
|
+
{...domProps}
|
|
1324
|
+
ref={local.ref}
|
|
1023
1325
|
{...cleanThumbProps()}
|
|
1024
|
-
{...cleanFocusProps()}
|
|
1025
1326
|
{...cleanHoverProps()}
|
|
1026
1327
|
class={renderProps.class()}
|
|
1027
1328
|
style={mergedStyle()}
|
|
@@ -1031,30 +1332,28 @@ export function ColorWheelThumb(props: ColorWheelThumbProps): JSX.Element {
|
|
|
1031
1332
|
data-focus-visible={isFocusVisible() || undefined}
|
|
1032
1333
|
data-hovered={isHovered() || undefined}
|
|
1033
1334
|
>
|
|
1335
|
+
<input {...mergedInputProps()} />
|
|
1034
1336
|
{renderProps.renderChildren()}
|
|
1035
1337
|
</div>
|
|
1036
1338
|
);
|
|
1037
1339
|
}
|
|
1038
1340
|
|
|
1039
|
-
// Attach sub-components
|
|
1040
1341
|
ColorWheel.Track = ColorWheelTrack;
|
|
1041
1342
|
ColorWheel.Thumb = ColorWheelThumb;
|
|
1042
1343
|
|
|
1043
|
-
// ============================================
|
|
1044
|
-
// COLOR FIELD
|
|
1045
|
-
// ============================================
|
|
1046
|
-
|
|
1047
1344
|
export interface ColorFieldRenderProps {
|
|
1048
1345
|
/** Whether the field is disabled. */
|
|
1049
1346
|
isDisabled: boolean;
|
|
1050
1347
|
/** Whether the field is read-only. */
|
|
1051
1348
|
isReadOnly: boolean;
|
|
1349
|
+
/** Whether the field is required. */
|
|
1350
|
+
isRequired: boolean;
|
|
1052
1351
|
/** Whether the input value is invalid. */
|
|
1053
1352
|
isInvalid: boolean;
|
|
1054
1353
|
/** The current color value (null if invalid). */
|
|
1055
1354
|
color: Color | null;
|
|
1056
|
-
/** The color channel being edited
|
|
1057
|
-
channel: ColorChannel |
|
|
1355
|
+
/** The color channel being edited, or "hex" for full color mode. */
|
|
1356
|
+
channel: ColorChannel | "hex";
|
|
1058
1357
|
}
|
|
1059
1358
|
|
|
1060
1359
|
export interface ColorFieldProps extends AriaColorFieldOptions, SlotProps {
|
|
@@ -1066,10 +1365,16 @@ export interface ColorFieldProps extends AriaColorFieldOptions, SlotProps {
|
|
|
1066
1365
|
onChange?: (color: Color | null) => void;
|
|
1067
1366
|
/** The color channel to edit (for single channel mode). */
|
|
1068
1367
|
channel?: ColorChannel;
|
|
1368
|
+
/** The color space to use for channel mode. */
|
|
1369
|
+
colorSpace?: ColorSpace;
|
|
1069
1370
|
/** The color format for parsing/displaying. */
|
|
1070
1371
|
colorFormat?: ColorFormat;
|
|
1071
1372
|
/** A visible label for the field. */
|
|
1072
1373
|
label?: JSX.Element;
|
|
1374
|
+
/** Description text for the field. */
|
|
1375
|
+
description?: JSX.Element;
|
|
1376
|
+
/** Error message for the field. */
|
|
1377
|
+
errorMessage?: JSX.Element;
|
|
1073
1378
|
/** The children of the component. */
|
|
1074
1379
|
children?: RenderChildren<ColorFieldRenderProps>;
|
|
1075
1380
|
/** The CSS className for the element. */
|
|
@@ -1100,13 +1405,18 @@ export interface ColorFieldInputProps extends SlotProps {
|
|
|
1100
1405
|
class?: ClassNameOrFunction<ColorFieldInputRenderProps>;
|
|
1101
1406
|
/** The inline style for the element. */
|
|
1102
1407
|
style?: StyleOrFunction<ColorFieldInputRenderProps>;
|
|
1408
|
+
/** Ref callback for the input element. */
|
|
1409
|
+
ref?: (element: HTMLInputElement) => void;
|
|
1103
1410
|
}
|
|
1104
1411
|
|
|
1105
|
-
// Context
|
|
1106
1412
|
interface ColorFieldContextValue {
|
|
1107
1413
|
state: ColorFieldState;
|
|
1108
1414
|
inputProps: JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
1109
1415
|
labelProps: JSX.LabelHTMLAttributes<HTMLLabelElement>;
|
|
1416
|
+
descriptionProps: JSX.HTMLAttributes<HTMLElement>;
|
|
1417
|
+
errorMessageProps: JSX.HTMLAttributes<HTMLElement>;
|
|
1418
|
+
setInputRef: (el: HTMLInputElement) => void;
|
|
1419
|
+
setLabelElement: (isPresent: boolean) => void;
|
|
1110
1420
|
}
|
|
1111
1421
|
|
|
1112
1422
|
export const ColorFieldContext = createContext<ColorFieldContextValue | null>(null);
|
|
@@ -1115,90 +1425,221 @@ export const ColorFieldContext = createContext<ColorFieldContextValue | null>(nu
|
|
|
1115
1425
|
* A color field allows users to enter a color value as text.
|
|
1116
1426
|
*/
|
|
1117
1427
|
export function ColorField(props: ColorFieldProps): JSX.Element {
|
|
1428
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
1118
1429
|
const [local, stateProps, ariaProps, rest] = splitProps(
|
|
1119
1430
|
props,
|
|
1120
|
-
[
|
|
1121
|
-
[
|
|
1122
|
-
[
|
|
1431
|
+
["children", "class", "style", "slot", "label", "description", "errorMessage"],
|
|
1432
|
+
["value", "defaultValue", "onChange", "channel", "colorSpace", "colorFormat"],
|
|
1433
|
+
[
|
|
1434
|
+
"id",
|
|
1435
|
+
"aria-label",
|
|
1436
|
+
"aria-labelledby",
|
|
1437
|
+
"aria-describedby",
|
|
1438
|
+
"aria-details",
|
|
1439
|
+
"aria-errormessage",
|
|
1440
|
+
"name",
|
|
1441
|
+
"form",
|
|
1442
|
+
"isWheelDisabled",
|
|
1443
|
+
"isDisabled",
|
|
1444
|
+
"isReadOnly",
|
|
1445
|
+
"isRequired",
|
|
1446
|
+
"isInvalid",
|
|
1447
|
+
"validationBehavior",
|
|
1448
|
+
"autoFocus",
|
|
1449
|
+
"excludeFromTabOrder",
|
|
1450
|
+
"placeholder",
|
|
1451
|
+
],
|
|
1123
1452
|
);
|
|
1453
|
+
const [hasRegisteredLabelElement, setHasRegisteredLabelElement] = createSignal(false);
|
|
1124
1454
|
|
|
1125
1455
|
// Create color field state
|
|
1126
1456
|
const state = createColorFieldState(() => ({
|
|
1127
|
-
value: stateProps.value,
|
|
1457
|
+
value: stateProps.value ?? pickerContext?.value,
|
|
1128
1458
|
defaultValue: stateProps.defaultValue,
|
|
1129
|
-
onChange:
|
|
1459
|
+
onChange:
|
|
1460
|
+
stateProps.onChange ??
|
|
1461
|
+
((color) => {
|
|
1462
|
+
if (color) {
|
|
1463
|
+
pickerContext?.onChange?.(color);
|
|
1464
|
+
}
|
|
1465
|
+
}),
|
|
1130
1466
|
channel: stateProps.channel,
|
|
1467
|
+
colorSpace: stateProps.colorSpace,
|
|
1131
1468
|
colorFormat: stateProps.colorFormat,
|
|
1132
1469
|
isDisabled: ariaProps.isDisabled,
|
|
1133
1470
|
isReadOnly: ariaProps.isReadOnly,
|
|
1471
|
+
isInvalid: ariaProps.isInvalid,
|
|
1472
|
+
isRequired: ariaProps.isRequired,
|
|
1134
1473
|
}));
|
|
1135
1474
|
|
|
1136
1475
|
// Input ref
|
|
1137
1476
|
let inputRef: HTMLInputElement | undefined;
|
|
1477
|
+
const setInputRef = (el: HTMLInputElement) => {
|
|
1478
|
+
inputRef = el;
|
|
1479
|
+
};
|
|
1138
1480
|
|
|
1139
1481
|
// Create color field aria props
|
|
1140
|
-
const
|
|
1141
|
-
inputProps,
|
|
1142
|
-
labelProps,
|
|
1143
|
-
} = createColorField(
|
|
1482
|
+
const colorFieldAria = createColorField(
|
|
1144
1483
|
() => ({
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1484
|
+
id: ariaProps.id,
|
|
1485
|
+
"aria-label": ariaProps["aria-label"],
|
|
1486
|
+
"aria-labelledby": ariaProps["aria-labelledby"],
|
|
1487
|
+
"aria-describedby": ariaProps["aria-describedby"],
|
|
1488
|
+
"aria-details": ariaProps["aria-details"],
|
|
1489
|
+
"aria-errormessage": ariaProps["aria-errormessage"],
|
|
1490
|
+
name: ariaProps.name,
|
|
1491
|
+
form: ariaProps.form,
|
|
1492
|
+
isWheelDisabled: ariaProps.isWheelDisabled,
|
|
1148
1493
|
isDisabled: ariaProps.isDisabled,
|
|
1149
1494
|
isReadOnly: ariaProps.isReadOnly,
|
|
1495
|
+
isRequired: ariaProps.isRequired,
|
|
1496
|
+
isInvalid: ariaProps.isInvalid,
|
|
1497
|
+
validationBehavior: ariaProps.validationBehavior,
|
|
1498
|
+
autoFocus: ariaProps.autoFocus,
|
|
1499
|
+
excludeFromTabOrder: ariaProps.excludeFromTabOrder,
|
|
1500
|
+
placeholder: ariaProps.placeholder,
|
|
1150
1501
|
channel: stateProps.channel,
|
|
1502
|
+
colorSpace: stateProps.colorSpace,
|
|
1151
1503
|
}),
|
|
1152
1504
|
() => state,
|
|
1153
|
-
() => inputRef ?? null
|
|
1505
|
+
() => inputRef ?? null,
|
|
1506
|
+
);
|
|
1507
|
+
|
|
1508
|
+
const describedBy = () => {
|
|
1509
|
+
const invalid = ariaProps.isInvalid || state.isInvalid;
|
|
1510
|
+
const ids = [
|
|
1511
|
+
ariaProps["aria-describedby"],
|
|
1512
|
+
local.description && !invalid ? colorFieldAria.descriptionProps.id : undefined,
|
|
1513
|
+
invalid && local.errorMessage ? colorFieldAria.errorMessageProps.id : undefined,
|
|
1514
|
+
];
|
|
1515
|
+
return ids.filter(Boolean).join(" ") || undefined;
|
|
1516
|
+
};
|
|
1517
|
+
|
|
1518
|
+
const fieldInputProps = () => {
|
|
1519
|
+
const labelledBy =
|
|
1520
|
+
ariaProps["aria-labelledby"] ??
|
|
1521
|
+
(!ariaProps["aria-label"] && (local.label || hasRegisteredLabelElement())
|
|
1522
|
+
? colorFieldAria.labelProps.id
|
|
1523
|
+
: undefined);
|
|
1524
|
+
return {
|
|
1525
|
+
...colorFieldAria.inputProps,
|
|
1526
|
+
"aria-label": labelledBy ? undefined : colorFieldAria.inputProps["aria-label"],
|
|
1527
|
+
"aria-labelledby": labelledBy,
|
|
1528
|
+
"aria-describedby": describedBy(),
|
|
1529
|
+
"aria-errormessage":
|
|
1530
|
+
(ariaProps.isInvalid || state.isInvalid) && local.errorMessage
|
|
1531
|
+
? colorFieldAria.errorMessageProps.id
|
|
1532
|
+
: colorFieldAria.inputProps["aria-errormessage"],
|
|
1533
|
+
} as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
1534
|
+
};
|
|
1535
|
+
|
|
1536
|
+
const hiddenInputValue = createMemo(() =>
|
|
1537
|
+
Number.isNaN(state.numberValue) ? "" : String(state.numberValue),
|
|
1154
1538
|
);
|
|
1155
1539
|
|
|
1156
|
-
// Render props values
|
|
1157
1540
|
const renderValues = createMemo<ColorFieldRenderProps>(() => ({
|
|
1158
1541
|
isDisabled: state.isDisabled,
|
|
1159
1542
|
isReadOnly: state.isReadOnly,
|
|
1543
|
+
isRequired: state.isRequired,
|
|
1160
1544
|
isInvalid: state.isInvalid,
|
|
1161
1545
|
color: state.value,
|
|
1162
|
-
channel: state.channel,
|
|
1546
|
+
channel: state.channel ?? "hex",
|
|
1163
1547
|
}));
|
|
1164
1548
|
|
|
1165
|
-
// Resolve render props
|
|
1166
1549
|
const renderProps = useRenderProps(
|
|
1167
1550
|
{
|
|
1168
1551
|
children: props.children,
|
|
1169
1552
|
class: local.class,
|
|
1170
1553
|
style: local.style,
|
|
1171
|
-
defaultClassName:
|
|
1554
|
+
defaultClassName: "solidaria-ColorField",
|
|
1172
1555
|
},
|
|
1173
|
-
renderValues
|
|
1556
|
+
renderValues,
|
|
1174
1557
|
);
|
|
1175
1558
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1559
|
+
const childRenderValues: ColorFieldRenderProps = {
|
|
1560
|
+
get isDisabled() {
|
|
1561
|
+
return state.isDisabled;
|
|
1562
|
+
},
|
|
1563
|
+
get isReadOnly() {
|
|
1564
|
+
return state.isReadOnly;
|
|
1565
|
+
},
|
|
1566
|
+
get isRequired() {
|
|
1567
|
+
return state.isRequired;
|
|
1568
|
+
},
|
|
1569
|
+
get isInvalid() {
|
|
1570
|
+
return state.isInvalid;
|
|
1571
|
+
},
|
|
1572
|
+
get color() {
|
|
1573
|
+
return state.value;
|
|
1574
|
+
},
|
|
1575
|
+
get channel() {
|
|
1576
|
+
return state.channel ?? "hex";
|
|
1577
|
+
},
|
|
1578
|
+
};
|
|
1579
|
+
|
|
1580
|
+
let hasRenderedChildren = false;
|
|
1581
|
+
let renderedChildren: JSX.Element;
|
|
1582
|
+
const renderChildren = () => {
|
|
1583
|
+
if (!hasRenderedChildren) {
|
|
1584
|
+
const children = local.children;
|
|
1585
|
+
renderedChildren =
|
|
1586
|
+
typeof children === "function" ? untrack(() => children(childRenderValues)) : children;
|
|
1587
|
+
hasRenderedChildren = true;
|
|
1588
|
+
}
|
|
1589
|
+
return renderedChildren;
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
const domProps = createMemo(() =>
|
|
1593
|
+
filterDOMProps(rest as Record<string, unknown>, { global: true }),
|
|
1594
|
+
);
|
|
1178
1595
|
|
|
1179
1596
|
return (
|
|
1180
1597
|
<ColorFieldContext.Provider
|
|
1181
1598
|
value={{
|
|
1182
1599
|
state,
|
|
1183
|
-
inputProps
|
|
1184
|
-
|
|
1600
|
+
get inputProps() {
|
|
1601
|
+
return fieldInputProps();
|
|
1602
|
+
},
|
|
1603
|
+
get labelProps() {
|
|
1604
|
+
return colorFieldAria.labelProps;
|
|
1605
|
+
},
|
|
1606
|
+
get descriptionProps() {
|
|
1607
|
+
return colorFieldAria.descriptionProps;
|
|
1608
|
+
},
|
|
1609
|
+
get errorMessageProps() {
|
|
1610
|
+
return colorFieldAria.errorMessageProps;
|
|
1611
|
+
},
|
|
1612
|
+
setInputRef,
|
|
1613
|
+
setLabelElement: setHasRegisteredLabelElement,
|
|
1185
1614
|
}}
|
|
1186
1615
|
>
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1616
|
+
<>
|
|
1617
|
+
<div
|
|
1618
|
+
{...domProps()}
|
|
1619
|
+
class={renderProps.class()}
|
|
1620
|
+
style={renderProps.style()}
|
|
1621
|
+
slot={local.slot ?? undefined}
|
|
1622
|
+
data-disabled={state.isDisabled || undefined}
|
|
1623
|
+
data-readonly={state.isReadOnly || undefined}
|
|
1624
|
+
data-invalid={state.isInvalid || undefined}
|
|
1625
|
+
data-required={state.isRequired || undefined}
|
|
1626
|
+
data-channel={state.channel ?? "hex"}
|
|
1627
|
+
>
|
|
1628
|
+
<Show when={local.label}>
|
|
1629
|
+
<label {...colorFieldAria.labelProps}>{local.label}</label>
|
|
1630
|
+
</Show>
|
|
1631
|
+
|
|
1632
|
+
{renderChildren()}
|
|
1633
|
+
</div>
|
|
1634
|
+
<Show when={state.channel && ariaProps.name}>
|
|
1635
|
+
<input
|
|
1636
|
+
type="hidden"
|
|
1637
|
+
name={ariaProps.name}
|
|
1638
|
+
form={ariaProps.form}
|
|
1639
|
+
value={hiddenInputValue()}
|
|
1640
|
+
/>
|
|
1198
1641
|
</Show>
|
|
1199
|
-
|
|
1200
|
-
{renderProps.renderChildren()}
|
|
1201
|
-
</div>
|
|
1642
|
+
</>
|
|
1202
1643
|
</ColorFieldContext.Provider>
|
|
1203
1644
|
);
|
|
1204
1645
|
}
|
|
@@ -1207,26 +1648,24 @@ export function ColorField(props: ColorFieldProps): JSX.Element {
|
|
|
1207
1648
|
* The input element of a color field.
|
|
1208
1649
|
*/
|
|
1209
1650
|
export function ColorFieldInput(props: ColorFieldInputProps): JSX.Element {
|
|
1210
|
-
const [local] = splitProps(props, [
|
|
1651
|
+
const [local, domProps] = splitProps(props, ["class", "style", "slot", "children", "ref"]);
|
|
1211
1652
|
|
|
1212
1653
|
const context = useContext(ColorFieldContext);
|
|
1213
1654
|
if (!context) {
|
|
1214
|
-
throw new Error(
|
|
1655
|
+
throw new Error("ColorFieldInput must be used within a ColorField");
|
|
1215
1656
|
}
|
|
1216
1657
|
|
|
1217
|
-
const
|
|
1658
|
+
const state = context.state;
|
|
1659
|
+
const inputValue = createMemo(() => state.inputValue);
|
|
1218
1660
|
|
|
1219
|
-
// Create focus ring
|
|
1220
1661
|
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
1221
1662
|
|
|
1222
|
-
// Create hover
|
|
1223
1663
|
const { isHovered, hoverProps } = createHover({
|
|
1224
1664
|
get isDisabled() {
|
|
1225
1665
|
return state.isDisabled;
|
|
1226
1666
|
},
|
|
1227
1667
|
});
|
|
1228
1668
|
|
|
1229
|
-
// Render props values
|
|
1230
1669
|
const renderValues = createMemo<ColorFieldInputRenderProps>(() => ({
|
|
1231
1670
|
isDisabled: state.isDisabled,
|
|
1232
1671
|
isReadOnly: state.isReadOnly,
|
|
@@ -1236,20 +1675,23 @@ export function ColorFieldInput(props: ColorFieldInputProps): JSX.Element {
|
|
|
1236
1675
|
isHovered: isHovered(),
|
|
1237
1676
|
}));
|
|
1238
1677
|
|
|
1239
|
-
// Resolve render props
|
|
1240
1678
|
const renderProps = useRenderProps(
|
|
1241
1679
|
{
|
|
1242
1680
|
children: props.children,
|
|
1243
1681
|
class: local.class,
|
|
1244
1682
|
style: local.style,
|
|
1245
|
-
defaultClassName:
|
|
1683
|
+
defaultClassName: "solidaria-ColorField-input",
|
|
1246
1684
|
},
|
|
1247
|
-
renderValues
|
|
1685
|
+
renderValues,
|
|
1248
1686
|
);
|
|
1249
1687
|
|
|
1250
|
-
// Clean props
|
|
1251
1688
|
const cleanInputProps = () => {
|
|
1252
|
-
const {
|
|
1689
|
+
const {
|
|
1690
|
+
ref: _ref,
|
|
1691
|
+
style: _inputStyle,
|
|
1692
|
+
value: _value,
|
|
1693
|
+
...rest
|
|
1694
|
+
} = context.inputProps as Record<string, unknown>;
|
|
1253
1695
|
return rest;
|
|
1254
1696
|
};
|
|
1255
1697
|
const cleanFocusProps = () => {
|
|
@@ -1263,11 +1705,17 @@ export function ColorFieldInput(props: ColorFieldInputProps): JSX.Element {
|
|
|
1263
1705
|
|
|
1264
1706
|
return (
|
|
1265
1707
|
<input
|
|
1708
|
+
{...domProps}
|
|
1266
1709
|
{...cleanInputProps()}
|
|
1267
1710
|
{...cleanFocusProps()}
|
|
1268
1711
|
{...cleanHoverProps()}
|
|
1712
|
+
ref={(el) => {
|
|
1713
|
+
context.setInputRef(el);
|
|
1714
|
+
local.ref?.(el);
|
|
1715
|
+
}}
|
|
1269
1716
|
class={renderProps.class()}
|
|
1270
1717
|
style={renderProps.style()}
|
|
1718
|
+
value={inputValue()}
|
|
1271
1719
|
data-disabled={state.isDisabled || undefined}
|
|
1272
1720
|
data-readonly={state.isReadOnly || undefined}
|
|
1273
1721
|
data-invalid={state.isInvalid || undefined}
|
|
@@ -1278,13 +1726,8 @@ export function ColorFieldInput(props: ColorFieldInputProps): JSX.Element {
|
|
|
1278
1726
|
);
|
|
1279
1727
|
}
|
|
1280
1728
|
|
|
1281
|
-
// Attach sub-components
|
|
1282
1729
|
ColorField.Input = ColorFieldInput;
|
|
1283
1730
|
|
|
1284
|
-
// ============================================
|
|
1285
|
-
// COLOR SWATCH
|
|
1286
|
-
// ============================================
|
|
1287
|
-
|
|
1288
1731
|
export interface ColorSwatchRenderProps {
|
|
1289
1732
|
/** The color being displayed. */
|
|
1290
1733
|
color: Color;
|
|
@@ -1294,9 +1737,17 @@ export interface ColorSwatchRenderProps {
|
|
|
1294
1737
|
|
|
1295
1738
|
export interface ColorSwatchProps extends SlotProps {
|
|
1296
1739
|
/** The color to display. */
|
|
1297
|
-
color
|
|
1740
|
+
color?: Color | string;
|
|
1741
|
+
/** Localized color name override. */
|
|
1742
|
+
colorName?: string;
|
|
1298
1743
|
/** Accessible label for the swatch. */
|
|
1299
|
-
|
|
1744
|
+
"aria-label"?: string;
|
|
1745
|
+
/** ID of element that labels the swatch. */
|
|
1746
|
+
"aria-labelledby"?: string;
|
|
1747
|
+
/** ID of element that describes the swatch. */
|
|
1748
|
+
"aria-describedby"?: string;
|
|
1749
|
+
/** ID of element that provides detailed information about the swatch. */
|
|
1750
|
+
"aria-details"?: string;
|
|
1300
1751
|
/** The children of the component. */
|
|
1301
1752
|
children?: RenderChildren<ColorSwatchRenderProps>;
|
|
1302
1753
|
/** The CSS className for the element. */
|
|
@@ -1309,62 +1760,652 @@ export interface ColorSwatchProps extends SlotProps {
|
|
|
1309
1760
|
* A color swatch displays a preview of a color.
|
|
1310
1761
|
*/
|
|
1311
1762
|
export function ColorSwatch(props: ColorSwatchProps): JSX.Element {
|
|
1763
|
+
const swatchContext = useContext(ColorSwatchContextInternal);
|
|
1764
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
1312
1765
|
const [local, ariaProps, rest] = splitProps(
|
|
1313
1766
|
props,
|
|
1314
|
-
[
|
|
1315
|
-
[
|
|
1767
|
+
["children", "class", "style", "slot", "color", "colorName"],
|
|
1768
|
+
["aria-label", "aria-labelledby", "aria-describedby", "aria-details"],
|
|
1316
1769
|
);
|
|
1317
1770
|
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
'aria-label': ariaProps['aria-label'],
|
|
1322
|
-
}));
|
|
1771
|
+
const resolvedColor = createMemo<Color | string>(() => {
|
|
1772
|
+
return local.color ?? swatchContext?.color ?? pickerContext?.value ?? "#fff0";
|
|
1773
|
+
});
|
|
1323
1774
|
|
|
1324
|
-
|
|
1325
|
-
|
|
1775
|
+
const swatchAria = createColorSwatch(() => ({
|
|
1776
|
+
id: (rest as Record<string, unknown>).id as string | undefined,
|
|
1777
|
+
slot: local.slot,
|
|
1778
|
+
color: resolvedColor(),
|
|
1779
|
+
colorName: local.colorName,
|
|
1780
|
+
"aria-label": ariaProps["aria-label"],
|
|
1781
|
+
"aria-labelledby": ariaProps["aria-labelledby"],
|
|
1782
|
+
"aria-describedby": ariaProps["aria-describedby"],
|
|
1783
|
+
"aria-details": ariaProps["aria-details"],
|
|
1784
|
+
}));
|
|
1326
1785
|
|
|
1327
|
-
// Render props values
|
|
1328
1786
|
const renderValues = createMemo<ColorSwatchRenderProps>(() => ({
|
|
1329
|
-
color: color
|
|
1330
|
-
colorValue: color
|
|
1787
|
+
color: swatchAria.color,
|
|
1788
|
+
colorValue: swatchAria.color.toString("css"),
|
|
1331
1789
|
}));
|
|
1332
1790
|
|
|
1333
|
-
// Resolve render props
|
|
1334
1791
|
const renderProps = useRenderProps(
|
|
1335
1792
|
{
|
|
1336
1793
|
children: props.children,
|
|
1337
1794
|
class: local.class,
|
|
1338
1795
|
style: local.style,
|
|
1339
|
-
defaultClassName:
|
|
1796
|
+
defaultClassName: "solidaria-ColorSwatch",
|
|
1340
1797
|
},
|
|
1341
|
-
renderValues
|
|
1798
|
+
renderValues,
|
|
1342
1799
|
);
|
|
1343
1800
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1801
|
+
const domProps = createMemo(() =>
|
|
1802
|
+
filterDOMProps(rest as Record<string, unknown>, { global: true }),
|
|
1803
|
+
);
|
|
1346
1804
|
|
|
1347
|
-
// Clean props
|
|
1348
1805
|
const cleanSwatchProps = () => {
|
|
1349
|
-
const {
|
|
1806
|
+
const {
|
|
1807
|
+
ref: _ref,
|
|
1808
|
+
style: _swatchStyle,
|
|
1809
|
+
...rest
|
|
1810
|
+
} = swatchAria.swatchProps as Record<string, unknown>;
|
|
1350
1811
|
return rest;
|
|
1351
1812
|
};
|
|
1352
1813
|
|
|
1353
|
-
// Merge styles
|
|
1354
1814
|
const mergedStyle = () => {
|
|
1355
|
-
const swatchStyle = (swatchProps as { style?: Record<string, string> }).style || {};
|
|
1815
|
+
const swatchStyle = (swatchAria.swatchProps as { style?: Record<string, string> }).style || {};
|
|
1356
1816
|
const renderStyle = renderProps.style() || {};
|
|
1357
1817
|
return { ...swatchStyle, ...renderStyle };
|
|
1358
1818
|
};
|
|
1359
1819
|
|
|
1820
|
+
return (
|
|
1821
|
+
<div {...domProps()} {...cleanSwatchProps()} class={renderProps.class()} style={mergedStyle()}>
|
|
1822
|
+
{renderProps.renderChildren()}
|
|
1823
|
+
</div>
|
|
1824
|
+
);
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
export const ColorSliderStateContext = ColorSliderContext;
|
|
1828
|
+
export const ColorAreaStateContext = ColorAreaContext;
|
|
1829
|
+
export const ColorWheelStateContext = ColorWheelContext;
|
|
1830
|
+
export const ColorWheelTrackContext = ColorWheelContext;
|
|
1831
|
+
export const ColorFieldStateContext = ColorFieldContext;
|
|
1832
|
+
export const ColorSwatchContext = ColorSwatchContextInternal;
|
|
1833
|
+
export const ColorPickerContext = ColorPickerContextInternal;
|
|
1834
|
+
export const ColorPickerStateContext = ColorPickerStateContextInternal;
|
|
1835
|
+
export const ColorSwatchPickerContext = ColorSwatchPickerContextInternal;
|
|
1836
|
+
export const ColorThumb = ColorSliderThumb;
|
|
1837
|
+
|
|
1838
|
+
export interface ColorPickerRenderProps {
|
|
1839
|
+
/** The currently selected color. */
|
|
1840
|
+
color: Color;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
export interface ColorPickerProps extends SlotProps {
|
|
1844
|
+
/** The current color value (controlled). */
|
|
1845
|
+
value?: Color | string;
|
|
1846
|
+
/** The default color value (uncontrolled). */
|
|
1847
|
+
defaultValue?: Color | string;
|
|
1848
|
+
/** Handler called when the color changes. */
|
|
1849
|
+
onChange?: (color: Color) => void;
|
|
1850
|
+
/** The children of the color picker. */
|
|
1851
|
+
children?: RenderChildren<ColorPickerRenderProps>;
|
|
1852
|
+
/** The CSS className for the element. */
|
|
1853
|
+
class?: ClassNameOrFunction<ColorPickerRenderProps>;
|
|
1854
|
+
/** The inline style for the element. */
|
|
1855
|
+
style?: StyleOrFunction<ColorPickerRenderProps>;
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
export interface ColorSwatchPickerRenderProps {
|
|
1859
|
+
/** Whether the swatch picker has focus. */
|
|
1860
|
+
isFocused: boolean;
|
|
1861
|
+
/** Whether the swatch picker has keyboard focus. */
|
|
1862
|
+
isFocusVisible: boolean;
|
|
1863
|
+
/** The currently selected color. */
|
|
1864
|
+
selectedColor: Color;
|
|
1865
|
+
/** Item arrangement mode. */
|
|
1866
|
+
layout: "grid" | "stack";
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
export interface ColorSwatchPickerProps extends SlotProps {
|
|
1870
|
+
/** The element's unique identifier. */
|
|
1871
|
+
id?: string;
|
|
1872
|
+
/** The current color value (controlled). */
|
|
1873
|
+
value?: Color | string;
|
|
1874
|
+
/** The default color value (uncontrolled). */
|
|
1875
|
+
defaultValue?: Color | string;
|
|
1876
|
+
/** Handler called when the selected color changes. */
|
|
1877
|
+
onChange?: (color: Color) => void;
|
|
1878
|
+
/** Accessible label for the swatch picker. */
|
|
1879
|
+
"aria-label"?: string;
|
|
1880
|
+
/** ID of element that labels the swatch picker. */
|
|
1881
|
+
"aria-labelledby"?: string;
|
|
1882
|
+
/** ID of element that describes the swatch picker. */
|
|
1883
|
+
"aria-describedby"?: string;
|
|
1884
|
+
/** ID of element that provides detailed information about the swatch picker. */
|
|
1885
|
+
"aria-details"?: string;
|
|
1886
|
+
/** Whether swatches are arranged as a grid or stack. */
|
|
1887
|
+
layout?: "grid" | "stack";
|
|
1888
|
+
/** The children (ColorSwatchPickerItem elements). */
|
|
1889
|
+
children?: JSX.Element;
|
|
1890
|
+
/** The CSS className for the element. */
|
|
1891
|
+
class?: ClassNameOrFunction<ColorSwatchPickerRenderProps>;
|
|
1892
|
+
/** The inline style for the element. */
|
|
1893
|
+
style?: StyleOrFunction<ColorSwatchPickerRenderProps>;
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
export interface ColorSwatchPickerItemRenderProps {
|
|
1897
|
+
/** Whether the item is selected. */
|
|
1898
|
+
isSelected: boolean;
|
|
1899
|
+
/** Whether the item is focused. */
|
|
1900
|
+
isFocused: boolean;
|
|
1901
|
+
/** Whether the item has keyboard focus. */
|
|
1902
|
+
isFocusVisible: boolean;
|
|
1903
|
+
/** Whether the item is pressed. */
|
|
1904
|
+
isPressed: boolean;
|
|
1905
|
+
/** Whether the item is disabled. */
|
|
1906
|
+
isDisabled: boolean;
|
|
1907
|
+
/** The color represented by the item. */
|
|
1908
|
+
color: Color;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
export interface ColorSwatchPickerItemProps extends SlotProps {
|
|
1912
|
+
/** The color represented by this swatch item. */
|
|
1913
|
+
color: Color | string;
|
|
1914
|
+
/** Whether this item is disabled. */
|
|
1915
|
+
isDisabled?: boolean;
|
|
1916
|
+
/** Accessible label for this item. */
|
|
1917
|
+
"aria-label"?: string;
|
|
1918
|
+
/** The children of the swatch item. */
|
|
1919
|
+
children?: RenderChildren<ColorSwatchPickerItemRenderProps>;
|
|
1920
|
+
/** The CSS className for the element. */
|
|
1921
|
+
class?: ClassNameOrFunction<ColorSwatchPickerItemRenderProps>;
|
|
1922
|
+
/** The inline style for the element. */
|
|
1923
|
+
style?: StyleOrFunction<ColorSwatchPickerItemRenderProps>;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
export function ColorPicker(props: ColorPickerProps): JSX.Element {
|
|
1927
|
+
const [local] = splitProps(props, [
|
|
1928
|
+
"value",
|
|
1929
|
+
"defaultValue",
|
|
1930
|
+
"onChange",
|
|
1931
|
+
"children",
|
|
1932
|
+
"class",
|
|
1933
|
+
"style",
|
|
1934
|
+
"slot",
|
|
1935
|
+
]);
|
|
1936
|
+
|
|
1937
|
+
const [internalColor, setInternalColor] = createSignal<Color>(
|
|
1938
|
+
normalizeColor(local.defaultValue ?? "#ff0000"),
|
|
1939
|
+
);
|
|
1940
|
+
|
|
1941
|
+
const color = createMemo<Color>(() => {
|
|
1942
|
+
if (local.value !== undefined) {
|
|
1943
|
+
return normalizeColor(local.value);
|
|
1944
|
+
}
|
|
1945
|
+
return internalColor();
|
|
1946
|
+
});
|
|
1947
|
+
|
|
1948
|
+
const setColor = (nextColor: Color) => {
|
|
1949
|
+
if (local.value === undefined) {
|
|
1950
|
+
setInternalColor(nextColor);
|
|
1951
|
+
}
|
|
1952
|
+
local.onChange?.(nextColor);
|
|
1953
|
+
};
|
|
1954
|
+
|
|
1955
|
+
const renderValues = createMemo<ColorPickerRenderProps>(() => ({
|
|
1956
|
+
color: color(),
|
|
1957
|
+
}));
|
|
1958
|
+
|
|
1959
|
+
const renderProps = useRenderProps(
|
|
1960
|
+
{
|
|
1961
|
+
children: local.children,
|
|
1962
|
+
class: local.class,
|
|
1963
|
+
style: local.style,
|
|
1964
|
+
defaultClassName: "solidaria-ColorPicker",
|
|
1965
|
+
},
|
|
1966
|
+
renderValues,
|
|
1967
|
+
);
|
|
1968
|
+
|
|
1969
|
+
return (
|
|
1970
|
+
<ColorPickerStateContextInternal.Provider
|
|
1971
|
+
value={{
|
|
1972
|
+
color: () => color(),
|
|
1973
|
+
setColor,
|
|
1974
|
+
}}
|
|
1975
|
+
>
|
|
1976
|
+
<ColorPickerContextInternal.Provider
|
|
1977
|
+
value={{
|
|
1978
|
+
get value() {
|
|
1979
|
+
return color();
|
|
1980
|
+
},
|
|
1981
|
+
onChange: setColor,
|
|
1982
|
+
}}
|
|
1983
|
+
>
|
|
1984
|
+
<div class={renderProps.class()} style={renderProps.style()}>
|
|
1985
|
+
{renderProps.renderChildren()}
|
|
1986
|
+
</div>
|
|
1987
|
+
</ColorPickerContextInternal.Provider>
|
|
1988
|
+
</ColorPickerStateContextInternal.Provider>
|
|
1989
|
+
);
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
export function ColorSwatchPicker(props: ColorSwatchPickerProps): JSX.Element {
|
|
1993
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
1994
|
+
const [local, rest] = splitProps(props, [
|
|
1995
|
+
"value",
|
|
1996
|
+
"defaultValue",
|
|
1997
|
+
"onChange",
|
|
1998
|
+
"id",
|
|
1999
|
+
"aria-label",
|
|
2000
|
+
"aria-labelledby",
|
|
2001
|
+
"aria-describedby",
|
|
2002
|
+
"aria-details",
|
|
2003
|
+
"layout",
|
|
2004
|
+
"children",
|
|
2005
|
+
"class",
|
|
2006
|
+
"style",
|
|
2007
|
+
"slot",
|
|
2008
|
+
]);
|
|
2009
|
+
|
|
2010
|
+
const [itemMap, setItemMap] = createSignal<Map<string, ColorSwatchPickerItemData>>(new Map());
|
|
2011
|
+
const [itemOrder, setItemOrder] = createSignal<string[]>([]);
|
|
2012
|
+
const [internalColor, setInternalColor] = createSignal<Color>(
|
|
2013
|
+
normalizeColor(local.defaultValue ?? pickerContext?.value ?? "#ff0000"),
|
|
2014
|
+
);
|
|
2015
|
+
|
|
2016
|
+
const selectedColor = createMemo<Color>(() => {
|
|
2017
|
+
if (local.value !== undefined) {
|
|
2018
|
+
return normalizeColor(local.value);
|
|
2019
|
+
}
|
|
2020
|
+
if (pickerContext?.value !== undefined) {
|
|
2021
|
+
return normalizeColor(pickerContext.value);
|
|
2022
|
+
}
|
|
2023
|
+
return internalColor();
|
|
2024
|
+
});
|
|
2025
|
+
|
|
2026
|
+
const selectedKey = createMemo(() => selectedColor().toString("hexa"));
|
|
2027
|
+
const isControlled = createMemo(
|
|
2028
|
+
() => local.value !== undefined || pickerContext?.value !== undefined,
|
|
2029
|
+
);
|
|
2030
|
+
|
|
2031
|
+
const registerItem = (item: ColorSwatchPickerItemData) => {
|
|
2032
|
+
setItemMap((prev) => {
|
|
2033
|
+
const next = new Map(prev);
|
|
2034
|
+
next.set(item.key, item);
|
|
2035
|
+
return next;
|
|
2036
|
+
});
|
|
2037
|
+
setItemOrder((prev) => (prev.includes(item.key) ? prev : [...prev, item.key]));
|
|
2038
|
+
};
|
|
2039
|
+
|
|
2040
|
+
const unregisterItem = (key: string) => {
|
|
2041
|
+
setItemMap((prev) => {
|
|
2042
|
+
if (!prev.has(key)) return prev;
|
|
2043
|
+
const next = new Map(prev);
|
|
2044
|
+
next.delete(key);
|
|
2045
|
+
return next;
|
|
2046
|
+
});
|
|
2047
|
+
setItemOrder((prev) => prev.filter((itemKey) => itemKey !== key));
|
|
2048
|
+
};
|
|
2049
|
+
|
|
2050
|
+
const items = createMemo(() => {
|
|
2051
|
+
const map = itemMap();
|
|
2052
|
+
return itemOrder()
|
|
2053
|
+
.map((key) => map.get(key))
|
|
2054
|
+
.filter((item): item is ColorSwatchPickerItemData => item != null);
|
|
2055
|
+
});
|
|
2056
|
+
|
|
2057
|
+
const state = createListState<ColorSwatchPickerItemData>({
|
|
2058
|
+
get items() {
|
|
2059
|
+
return items();
|
|
2060
|
+
},
|
|
2061
|
+
get getKey() {
|
|
2062
|
+
return (item: ColorSwatchPickerItemData) => item.key;
|
|
2063
|
+
},
|
|
2064
|
+
get getTextValue() {
|
|
2065
|
+
return (item: ColorSwatchPickerItemData) => item.textValue;
|
|
2066
|
+
},
|
|
2067
|
+
get getDisabled() {
|
|
2068
|
+
return (item: ColorSwatchPickerItemData) => !!item.isDisabled;
|
|
2069
|
+
},
|
|
2070
|
+
selectionMode: "single",
|
|
2071
|
+
disallowEmptySelection: true,
|
|
2072
|
+
get selectedKeys() {
|
|
2073
|
+
return [selectedKey()];
|
|
2074
|
+
},
|
|
2075
|
+
onSelectionChange(keys) {
|
|
2076
|
+
if (keys === "all") return;
|
|
2077
|
+
const key = keys.values().next().value as string | undefined;
|
|
2078
|
+
if (!key) return;
|
|
2079
|
+
const item = itemMap().get(key);
|
|
2080
|
+
if (!item) return;
|
|
2081
|
+
if (!isControlled()) {
|
|
2082
|
+
setInternalColor(item.color);
|
|
2083
|
+
}
|
|
2084
|
+
(local.onChange ?? pickerContext?.onChange)?.(item.color);
|
|
2085
|
+
},
|
|
2086
|
+
});
|
|
2087
|
+
|
|
2088
|
+
const listBoxAria = createListBox(
|
|
2089
|
+
() => ({
|
|
2090
|
+
id: local.id,
|
|
2091
|
+
"aria-label":
|
|
2092
|
+
local["aria-label"] ?? (!local["aria-labelledby"] ? "Color swatch picker" : undefined),
|
|
2093
|
+
"aria-labelledby": local["aria-labelledby"],
|
|
2094
|
+
"aria-describedby": local["aria-describedby"],
|
|
2095
|
+
"aria-details": local["aria-details"],
|
|
2096
|
+
shouldFocusWrap: true,
|
|
2097
|
+
}),
|
|
2098
|
+
state,
|
|
2099
|
+
);
|
|
2100
|
+
|
|
2101
|
+
const resolveDirection = (): "ltr" | "rtl" => {
|
|
2102
|
+
if (typeof document === "undefined") return "ltr";
|
|
2103
|
+
const rootDir = document.dir;
|
|
2104
|
+
return rootDir === "rtl" ? "rtl" : "ltr";
|
|
2105
|
+
};
|
|
2106
|
+
|
|
2107
|
+
const findNextEnabledKey = (from: Key | null, direction: "next" | "prev") => {
|
|
2108
|
+
const collection = state.collection();
|
|
2109
|
+
const getAdjacent =
|
|
2110
|
+
direction === "next"
|
|
2111
|
+
? (key: Key) => collection.getKeyAfter(key)
|
|
2112
|
+
: (key: Key) => collection.getKeyBefore(key);
|
|
2113
|
+
const getBoundary =
|
|
2114
|
+
direction === "next" ? () => collection.getFirstKey() : () => collection.getLastKey();
|
|
2115
|
+
|
|
2116
|
+
let key = from != null ? getAdjacent(from) : getBoundary();
|
|
2117
|
+
while (key != null && state.isDisabled(key)) {
|
|
2118
|
+
key = getAdjacent(key);
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
return key;
|
|
2122
|
+
};
|
|
2123
|
+
|
|
2124
|
+
const getBoundaryEnabledKey = (direction: "next" | "prev") => {
|
|
2125
|
+
const collection = state.collection();
|
|
2126
|
+
const getAdjacent =
|
|
2127
|
+
direction === "next"
|
|
2128
|
+
? (key: Key) => collection.getKeyAfter(key)
|
|
2129
|
+
: (key: Key) => collection.getKeyBefore(key);
|
|
2130
|
+
const getBoundary =
|
|
2131
|
+
direction === "next" ? () => collection.getFirstKey() : () => collection.getLastKey();
|
|
2132
|
+
|
|
2133
|
+
let key = getBoundary();
|
|
2134
|
+
while (key != null && state.isDisabled(key)) {
|
|
2135
|
+
key = getAdjacent(key);
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
return key;
|
|
2139
|
+
};
|
|
2140
|
+
|
|
2141
|
+
const getOptionElementForKey = (
|
|
2142
|
+
listbox: HTMLElement | null,
|
|
2143
|
+
key: Key | null,
|
|
2144
|
+
): HTMLElement | null => {
|
|
2145
|
+
if (!listbox || key == null) return null;
|
|
2146
|
+
const keyString = String(key);
|
|
2147
|
+
for (const optionElement of listbox.querySelectorAll<HTMLElement>('[role="option"]')) {
|
|
2148
|
+
if (optionElement.id === keyString) {
|
|
2149
|
+
return optionElement;
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
return null;
|
|
2153
|
+
};
|
|
2154
|
+
|
|
2155
|
+
const findGridKey = (
|
|
2156
|
+
listbox: HTMLElement | null,
|
|
2157
|
+
key: Key,
|
|
2158
|
+
nextKey: (current: Key) => Key | null,
|
|
2159
|
+
shouldSkip: (prevRect: DOMRect, itemRect: DOMRect) => boolean,
|
|
2160
|
+
): Key | null => {
|
|
2161
|
+
let candidate: Key | null = key;
|
|
2162
|
+
const previousRect = getOptionElementForKey(listbox, candidate)?.getBoundingClientRect();
|
|
2163
|
+
if (!previousRect) {
|
|
2164
|
+
return null;
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
while (candidate != null) {
|
|
2168
|
+
candidate = nextKey(candidate);
|
|
2169
|
+
if (candidate == null) {
|
|
2170
|
+
return null;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
const itemRect = getOptionElementForKey(listbox, candidate)?.getBoundingClientRect();
|
|
2174
|
+
if (!itemRect) {
|
|
2175
|
+
return null;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
if (!shouldSkip(previousRect, itemRect)) {
|
|
2179
|
+
return candidate;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
return null;
|
|
2184
|
+
};
|
|
2185
|
+
|
|
2186
|
+
const isSameRow = (prevRect: DOMRect, itemRect: DOMRect) =>
|
|
2187
|
+
prevRect.y === itemRect.y || prevRect.x !== itemRect.x;
|
|
2188
|
+
const getGridKeyBelow = (listbox: HTMLElement | null, key: Key) =>
|
|
2189
|
+
findGridKey(listbox, key, (current) => findNextEnabledKey(current, "next"), isSameRow);
|
|
2190
|
+
const getGridKeyAbove = (listbox: HTMLElement | null, key: Key) =>
|
|
2191
|
+
findGridKey(listbox, key, (current) => findNextEnabledKey(current, "prev"), isSameRow);
|
|
2192
|
+
const getGridKeyRightOf = (key: Key) =>
|
|
2193
|
+
resolveDirection() === "rtl"
|
|
2194
|
+
? findNextEnabledKey(key, "prev")
|
|
2195
|
+
: findNextEnabledKey(key, "next");
|
|
2196
|
+
const getGridKeyLeftOf = (key: Key) =>
|
|
2197
|
+
resolveDirection() === "rtl"
|
|
2198
|
+
? findNextEnabledKey(key, "next")
|
|
2199
|
+
: findNextEnabledKey(key, "prev");
|
|
2200
|
+
|
|
2201
|
+
const handleGridKeyDown = (e: KeyboardEvent): boolean => {
|
|
2202
|
+
if ((local.layout ?? "grid") !== "grid") return false;
|
|
2203
|
+
if (
|
|
2204
|
+
e.key !== "ArrowRight" &&
|
|
2205
|
+
e.key !== "ArrowLeft" &&
|
|
2206
|
+
e.key !== "ArrowDown" &&
|
|
2207
|
+
e.key !== "ArrowUp"
|
|
2208
|
+
) {
|
|
2209
|
+
return false;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
const listbox = e.currentTarget as HTMLElement | null;
|
|
2213
|
+
const focusedKey = state.focusedKey();
|
|
2214
|
+
const initialKey =
|
|
2215
|
+
focusedKey ??
|
|
2216
|
+
(e.key === "ArrowUp" || e.key === "ArrowLeft"
|
|
2217
|
+
? getBoundaryEnabledKey("prev")
|
|
2218
|
+
: getBoundaryEnabledKey("next"));
|
|
2219
|
+
if (initialKey == null) return false;
|
|
2220
|
+
|
|
2221
|
+
let nextKey: Key | null = null;
|
|
2222
|
+
switch (e.key) {
|
|
2223
|
+
case "ArrowDown":
|
|
2224
|
+
nextKey = getGridKeyBelow(listbox, initialKey) ?? getBoundaryEnabledKey("next");
|
|
2225
|
+
break;
|
|
2226
|
+
case "ArrowUp":
|
|
2227
|
+
nextKey = getGridKeyAbove(listbox, initialKey) ?? getBoundaryEnabledKey("prev");
|
|
2228
|
+
break;
|
|
2229
|
+
case "ArrowRight":
|
|
2230
|
+
nextKey =
|
|
2231
|
+
getGridKeyRightOf(initialKey) ??
|
|
2232
|
+
(resolveDirection() === "rtl"
|
|
2233
|
+
? getBoundaryEnabledKey("prev")
|
|
2234
|
+
: getBoundaryEnabledKey("next"));
|
|
2235
|
+
break;
|
|
2236
|
+
case "ArrowLeft":
|
|
2237
|
+
nextKey =
|
|
2238
|
+
getGridKeyLeftOf(initialKey) ??
|
|
2239
|
+
(resolveDirection() === "rtl"
|
|
2240
|
+
? getBoundaryEnabledKey("next")
|
|
2241
|
+
: getBoundaryEnabledKey("prev"));
|
|
2242
|
+
break;
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
if (nextKey == null) return false;
|
|
2246
|
+
|
|
2247
|
+
state.setFocusedKey(nextKey);
|
|
2248
|
+
if (state.selectionMode() === "single") {
|
|
2249
|
+
state.replaceSelection(nextKey);
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
e.preventDefault();
|
|
2253
|
+
e.stopPropagation();
|
|
2254
|
+
return true;
|
|
2255
|
+
};
|
|
2256
|
+
|
|
2257
|
+
const getListBoxKeyDown = () => {
|
|
2258
|
+
const props = listBoxAria.listBoxProps as Record<string, unknown>;
|
|
2259
|
+
return props.onKeyDown as JSX.EventHandler<HTMLDivElement, KeyboardEvent> | undefined;
|
|
2260
|
+
};
|
|
2261
|
+
|
|
2262
|
+
const onColorSwatchPickerKeyDown: JSX.EventHandler<HTMLDivElement, KeyboardEvent> = (e) => {
|
|
2263
|
+
if (handleGridKeyDown(e)) {
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
2266
|
+
getListBoxKeyDown()?.(e);
|
|
2267
|
+
};
|
|
2268
|
+
|
|
2269
|
+
createEffect(() => {
|
|
2270
|
+
const key = selectedKey();
|
|
2271
|
+
if (key) {
|
|
2272
|
+
state.setFocusedKey(key);
|
|
2273
|
+
}
|
|
2274
|
+
});
|
|
2275
|
+
|
|
2276
|
+
const { isFocused, isFocusVisible, focusProps } = createFocusRing({ within: true });
|
|
2277
|
+
const domProps = createMemo(() =>
|
|
2278
|
+
filterDOMProps(rest as Record<string, unknown>, { global: true }),
|
|
2279
|
+
);
|
|
2280
|
+
const renderValues = createMemo<ColorSwatchPickerRenderProps>(() => ({
|
|
2281
|
+
isFocused: state.isFocused() || isFocused(),
|
|
2282
|
+
isFocusVisible: isFocusVisible(),
|
|
2283
|
+
selectedColor: selectedColor(),
|
|
2284
|
+
layout: local.layout ?? "grid",
|
|
2285
|
+
}));
|
|
2286
|
+
|
|
2287
|
+
const renderProps = useRenderProps(
|
|
2288
|
+
{
|
|
2289
|
+
class: local.class,
|
|
2290
|
+
style: local.style,
|
|
2291
|
+
defaultClassName: "solidaria-ColorSwatchPicker",
|
|
2292
|
+
},
|
|
2293
|
+
renderValues,
|
|
2294
|
+
);
|
|
2295
|
+
|
|
2296
|
+
const cleanListBoxProps = () => {
|
|
2297
|
+
const {
|
|
2298
|
+
ref: _ref,
|
|
2299
|
+
onKeyDown: _onKeyDown,
|
|
2300
|
+
...restListBoxProps
|
|
2301
|
+
} = listBoxAria.listBoxProps as Record<string, unknown>;
|
|
2302
|
+
return restListBoxProps;
|
|
2303
|
+
};
|
|
2304
|
+
const cleanFocusProps = () => {
|
|
2305
|
+
const { ref: _ref, ...restFocusProps } = focusProps as Record<string, unknown>;
|
|
2306
|
+
return restFocusProps;
|
|
2307
|
+
};
|
|
2308
|
+
|
|
2309
|
+
return (
|
|
2310
|
+
<ColorSwatchPickerContextInternal.Provider
|
|
2311
|
+
value={{
|
|
2312
|
+
state,
|
|
2313
|
+
registerItem,
|
|
2314
|
+
unregisterItem,
|
|
2315
|
+
}}
|
|
2316
|
+
>
|
|
2317
|
+
<div
|
|
2318
|
+
{...mergeProps(domProps(), cleanListBoxProps(), cleanFocusProps(), {
|
|
2319
|
+
onKeyDown: onColorSwatchPickerKeyDown,
|
|
2320
|
+
})}
|
|
2321
|
+
class={renderProps.class()}
|
|
2322
|
+
style={renderProps.style()}
|
|
2323
|
+
slot={local.slot ?? undefined}
|
|
2324
|
+
data-focused={state.isFocused() || undefined}
|
|
2325
|
+
data-focus-visible={isFocusVisible() || undefined}
|
|
2326
|
+
data-layout={local.layout ?? "grid"}
|
|
2327
|
+
>
|
|
2328
|
+
{local.children}
|
|
2329
|
+
</div>
|
|
2330
|
+
</ColorSwatchPickerContextInternal.Provider>
|
|
2331
|
+
);
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
export function ColorSwatchPickerItem(props: ColorSwatchPickerItemProps): JSX.Element {
|
|
2335
|
+
const context = useContext(ColorSwatchPickerContextInternal);
|
|
2336
|
+
if (!context) {
|
|
2337
|
+
throw new Error("ColorSwatchPickerItem must be used within a ColorSwatchPicker");
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
const [local, ariaProps, rest] = splitProps(
|
|
2341
|
+
props,
|
|
2342
|
+
["children", "class", "style", "slot", "color"],
|
|
2343
|
+
["isDisabled", "aria-label"],
|
|
2344
|
+
);
|
|
2345
|
+
|
|
2346
|
+
const color = createMemo(() => normalizeColor(local.color));
|
|
2347
|
+
const key = createMemo(() => color().toString("hexa"));
|
|
2348
|
+
const textValue = createMemo(() => {
|
|
2349
|
+
const locale = globalThis.navigator?.language ?? "en-US";
|
|
2350
|
+
return color().getColorName(locale);
|
|
2351
|
+
});
|
|
2352
|
+
|
|
2353
|
+
createEffect(() => {
|
|
2354
|
+
const itemKey = key();
|
|
2355
|
+
context.registerItem({
|
|
2356
|
+
key: itemKey,
|
|
2357
|
+
color: color(),
|
|
2358
|
+
textValue: textValue(),
|
|
2359
|
+
isDisabled: ariaProps.isDisabled,
|
|
2360
|
+
});
|
|
2361
|
+
onCleanup(() => context.unregisterItem(itemKey));
|
|
2362
|
+
});
|
|
2363
|
+
|
|
2364
|
+
const optionAria = createOption(
|
|
2365
|
+
() => ({
|
|
2366
|
+
key: key(),
|
|
2367
|
+
isDisabled: ariaProps.isDisabled,
|
|
2368
|
+
"aria-label": ariaProps["aria-label"] ?? textValue(),
|
|
2369
|
+
}),
|
|
2370
|
+
context.state,
|
|
2371
|
+
);
|
|
2372
|
+
|
|
2373
|
+
const renderValues = createMemo<ColorSwatchPickerItemRenderProps>(() => ({
|
|
2374
|
+
isSelected: optionAria.isSelected(),
|
|
2375
|
+
isFocused: optionAria.isFocused(),
|
|
2376
|
+
isFocusVisible: optionAria.isFocusVisible(),
|
|
2377
|
+
isPressed: optionAria.isPressed(),
|
|
2378
|
+
isDisabled: optionAria.isDisabled(),
|
|
2379
|
+
color: color(),
|
|
2380
|
+
}));
|
|
2381
|
+
|
|
2382
|
+
const renderProps = useRenderProps(
|
|
2383
|
+
{
|
|
2384
|
+
children: local.children,
|
|
2385
|
+
class: local.class,
|
|
2386
|
+
style: local.style,
|
|
2387
|
+
defaultClassName: "solidaria-ColorSwatchPickerItem",
|
|
2388
|
+
},
|
|
2389
|
+
renderValues,
|
|
2390
|
+
);
|
|
2391
|
+
|
|
2392
|
+
const domProps = createMemo(() =>
|
|
2393
|
+
filterDOMProps(rest as Record<string, unknown>, { global: true }),
|
|
2394
|
+
);
|
|
2395
|
+
const cleanOptionProps = () => {
|
|
2396
|
+
const { ref: _ref, ...restOptionProps } = optionAria.optionProps as Record<string, unknown>;
|
|
2397
|
+
return restOptionProps;
|
|
2398
|
+
};
|
|
2399
|
+
|
|
1360
2400
|
return (
|
|
1361
2401
|
<div
|
|
1362
|
-
{...domProps()}
|
|
1363
|
-
{...cleanSwatchProps()}
|
|
2402
|
+
{...mergeProps(domProps(), cleanOptionProps())}
|
|
1364
2403
|
class={renderProps.class()}
|
|
1365
|
-
style={
|
|
2404
|
+
style={renderProps.style()}
|
|
1366
2405
|
>
|
|
1367
|
-
{
|
|
2406
|
+
<ColorSwatchContextInternal.Provider value={{ color: color() }}>
|
|
2407
|
+
{renderProps.children ? renderProps.renderChildren() : <ColorSwatch />}
|
|
2408
|
+
</ColorSwatchContextInternal.Provider>
|
|
1368
2409
|
</div>
|
|
1369
2410
|
);
|
|
1370
2411
|
}
|