@proyecto-viviana/solidaria-components 0.2.5 → 0.2.9
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/dist/ActionBar.d.ts +71 -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/Breadcrumbs.d.ts +10 -2
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Button.d.ts +4 -0
- package/dist/Button.d.ts.map +1 -1
- package/dist/Calendar.d.ts +13 -0
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +2 -2
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/Collection.d.ts +125 -0
- package/dist/Collection.d.ts.map +1 -0
- package/dist/Color.d.ts +114 -2
- 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 +64 -0
- 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 +27 -2
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DatePicker.d.ts +67 -2
- package/dist/DatePicker.d.ts.map +1 -1
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Disclosure.d.ts +2 -0
- 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 +23 -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 +27 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/GridList.d.ts +40 -1
- package/dist/GridList.d.ts.map +1 -1
- 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/Link.d.ts.map +1 -1
- package/dist/ListBox.d.ts +43 -1
- 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 +20 -2
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Meter.d.ts +2 -2
- package/dist/Meter.d.ts.map +1 -1
- package/dist/Modal.d.ts +2 -0
- package/dist/Modal.d.ts.map +1 -1
- package/dist/NumberField.d.ts +2 -0
- package/dist/NumberField.d.ts.map +1 -1
- package/dist/Popover.d.ts +4 -2
- 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 +2 -2
- package/dist/ProgressBar.d.ts.map +1 -1
- package/dist/RadioGroup.d.ts.map +1 -1
- package/dist/RangeCalendar.d.ts +5 -0
- 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 +2 -3
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/Select.d.ts +11 -0
- 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/SharedElementTransition.d.ts +39 -0
- package/dist/SharedElementTransition.d.ts.map +1 -0
- package/dist/Slider.d.ts +6 -3
- package/dist/Slider.d.ts.map +1 -1
- package/dist/Table.d.ts +39 -0
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +4 -3
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/TagGroup.d.ts +12 -2
- 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 +4 -0
- package/dist/TextField.d.ts.map +1 -1
- package/dist/TimeField.d.ts +26 -1
- package/dist/TimeField.d.ts.map +1 -1
- package/dist/Toast.d.ts.map +1 -1
- package/dist/ToggleButton.d.ts +30 -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.map +1 -1
- package/dist/Tooltip.d.ts +9 -0
- package/dist/Tooltip.d.ts.map +1 -1
- package/dist/Tree.d.ts +44 -2
- 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 +3 -1
- package/dist/VisuallyHidden.d.ts.map +1 -1
- package/dist/contexts.d.ts +1 -0
- package/dist/contexts.d.ts.map +1 -1
- package/dist/index.d.ts +57 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13961 -5946
- package/dist/index.js.map +1 -7
- package/dist/index.ssr.js +9612 -2401
- package/dist/index.ssr.js.map +1 -7
- package/dist/useDragAndDrop.d.ts +93 -0
- package/dist/useDragAndDrop.d.ts.map +1 -0
- package/dist/utils.d.ts +7 -1
- 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 +8 -6
- package/src/ActionBar.tsx +248 -0
- package/src/ActionGroup.tsx +285 -0
- package/src/Alert.tsx +177 -0
- package/src/Autocomplete.tsx +1 -1
- package/src/Breadcrumbs.tsx +103 -17
- package/src/Button.tsx +65 -21
- package/src/Calendar.tsx +179 -53
- package/src/Checkbox.tsx +1 -2
- package/src/Collection.tsx +341 -0
- package/src/Color.tsx +652 -34
- package/src/ColorEditor.tsx +231 -0
- package/src/ComboBox.tsx +315 -81
- package/src/ContextualHelpTrigger.tsx +183 -0
- package/src/DateField.tsx +93 -19
- package/src/DatePicker.tsx +495 -25
- package/src/Dialog.tsx +40 -9
- package/src/Disclosure.tsx +33 -27
- package/src/DragAndDrop.tsx +334 -0
- package/src/DragPreview.tsx +45 -0
- package/src/DropZone.tsx +213 -0
- package/src/FieldError.tsx +67 -0
- package/src/FileTrigger.tsx +83 -0
- package/src/Focusable.tsx +106 -0
- package/src/Form.tsx +85 -0
- package/src/GridList.tsx +379 -41
- package/src/Icon.tsx +154 -0
- package/src/Keyboard.tsx +26 -0
- package/src/Link.tsx +14 -1
- package/src/ListBox.tsx +484 -33
- package/src/ListDropTargetDelegate.ts +282 -0
- package/src/Menu.tsx +388 -35
- package/src/Meter.tsx +7 -3
- package/src/Modal.tsx +32 -4
- package/src/NumberField.tsx +163 -43
- package/src/Popover.tsx +136 -180
- package/src/Pressable.tsx +108 -0
- package/src/ProgressBar.tsx +7 -3
- package/src/RadioGroup.tsx +35 -25
- package/src/RangeCalendar.tsx +100 -68
- package/src/RouterProvider.tsx +240 -0
- package/src/SearchField.tsx +142 -34
- package/src/Select.tsx +221 -73
- package/src/SelectionIndicator.tsx +105 -0
- package/src/SharedElementTransition.tsx +258 -0
- package/src/Slider.tsx +16 -6
- package/src/Table.tsx +417 -57
- package/src/Tabs.tsx +68 -35
- package/src/TagGroup.tsx +121 -36
- package/src/Text.tsx +18 -0
- package/src/TextField.tsx +25 -8
- package/src/TimeField.tsx +101 -151
- package/src/Toast.tsx +108 -14
- package/src/ToggleButton.tsx +159 -0
- package/src/ToggleButtonGroup.tsx +136 -0
- package/src/Toolbar.tsx +14 -8
- package/src/Tooltip.tsx +108 -19
- package/src/Tree.tsx +1143 -87
- package/src/Virtualizer.tsx +702 -0
- package/src/VirtualizerLayouts.ts +265 -0
- package/src/VisuallyHidden.tsx +15 -21
- package/src/contexts.ts +1 -0
- package/src/index.ts +1057 -620
- package/src/useDragAndDrop.ts +351 -0
- package/src/utils.tsx +37 -3
- package/src/virtualizer/Layout.ts +200 -0
package/src/Color.tsx
CHANGED
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
import {
|
|
9
9
|
type JSX,
|
|
10
10
|
createContext,
|
|
11
|
+
createEffect,
|
|
11
12
|
createMemo,
|
|
13
|
+
createSignal,
|
|
14
|
+
onCleanup,
|
|
12
15
|
splitProps,
|
|
13
16
|
useContext,
|
|
14
17
|
Show,
|
|
@@ -19,14 +22,18 @@ import {
|
|
|
19
22
|
createColorWheel,
|
|
20
23
|
createColorField,
|
|
21
24
|
createColorSwatch,
|
|
25
|
+
createListBox,
|
|
26
|
+
createOption,
|
|
22
27
|
createFocusRing,
|
|
23
28
|
createHover,
|
|
29
|
+
mergeProps,
|
|
24
30
|
type AriaColorSliderOptions,
|
|
25
31
|
type AriaColorAreaOptions,
|
|
26
32
|
type AriaColorWheelOptions,
|
|
27
33
|
type AriaColorFieldOptions,
|
|
28
34
|
} from '@proyecto-viviana/solidaria';
|
|
29
35
|
import {
|
|
36
|
+
createListState,
|
|
30
37
|
createColorSliderState,
|
|
31
38
|
createColorAreaState,
|
|
32
39
|
createColorWheelState,
|
|
@@ -39,6 +46,8 @@ import {
|
|
|
39
46
|
type ColorAreaState,
|
|
40
47
|
type ColorWheelState,
|
|
41
48
|
type ColorFieldState,
|
|
49
|
+
type ListState,
|
|
50
|
+
type Key,
|
|
42
51
|
} from '@proyecto-viviana/solid-stately';
|
|
43
52
|
import {
|
|
44
53
|
type RenderChildren,
|
|
@@ -49,6 +58,34 @@ import {
|
|
|
49
58
|
filterDOMProps,
|
|
50
59
|
} from './utils';
|
|
51
60
|
|
|
61
|
+
interface ColorPickerChannelContextValue {
|
|
62
|
+
value?: Color | string;
|
|
63
|
+
onChange?: (color: Color) => void;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface ColorPickerStateContextValue {
|
|
67
|
+
color: () => Color;
|
|
68
|
+
setColor: (color: Color) => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface ColorSwatchPickerItemData {
|
|
72
|
+
key: string;
|
|
73
|
+
color: Color;
|
|
74
|
+
textValue: string;
|
|
75
|
+
isDisabled?: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface ColorSwatchPickerContextValue {
|
|
79
|
+
state: ListState<ColorSwatchPickerItemData>;
|
|
80
|
+
registerItem: (item: ColorSwatchPickerItemData) => void;
|
|
81
|
+
unregisterItem: (key: string) => void;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const ColorPickerContextInternal = createContext<ColorPickerChannelContextValue | null>(null);
|
|
85
|
+
const ColorPickerStateContextInternal = createContext<ColorPickerStateContextValue | null>(null);
|
|
86
|
+
const ColorSwatchContextInternal = createContext<{ color?: Color | string } | null>(null);
|
|
87
|
+
const ColorSwatchPickerContextInternal = createContext<ColorSwatchPickerContextValue | null>(null);
|
|
88
|
+
|
|
52
89
|
// ============================================
|
|
53
90
|
// COLOR SLIDER
|
|
54
91
|
// ============================================
|
|
@@ -141,6 +178,7 @@ export const ColorSliderContext = createContext<ColorSliderContextValue | null>(
|
|
|
141
178
|
* A color slider allows users to adjust a single color channel.
|
|
142
179
|
*/
|
|
143
180
|
export function ColorSlider(props: ColorSliderProps): JSX.Element {
|
|
181
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
144
182
|
const [local, stateProps, ariaProps, rest] = splitProps(
|
|
145
183
|
props,
|
|
146
184
|
['children', 'class', 'style', 'slot', 'label'],
|
|
@@ -150,9 +188,9 @@ export function ColorSlider(props: ColorSliderProps): JSX.Element {
|
|
|
150
188
|
|
|
151
189
|
// Create color slider state
|
|
152
190
|
const state = createColorSliderState(() => ({
|
|
153
|
-
value: stateProps.value,
|
|
191
|
+
value: stateProps.value ?? pickerContext?.value,
|
|
154
192
|
defaultValue: stateProps.defaultValue,
|
|
155
|
-
onChange: stateProps.onChange,
|
|
193
|
+
onChange: stateProps.onChange ?? pickerContext?.onChange,
|
|
156
194
|
onChangeEnd: stateProps.onChangeEnd,
|
|
157
195
|
channel: stateProps.channel,
|
|
158
196
|
isDisabled: ariaProps.isDisabled,
|
|
@@ -231,9 +269,6 @@ export function ColorSlider(props: ColorSliderProps): JSX.Element {
|
|
|
231
269
|
</Show>
|
|
232
270
|
|
|
233
271
|
{renderProps.renderChildren()}
|
|
234
|
-
|
|
235
|
-
{/* Hidden input for accessibility */}
|
|
236
|
-
<input {...inputProps} />
|
|
237
272
|
</div>
|
|
238
273
|
</ColorSliderContext.Provider>
|
|
239
274
|
);
|
|
@@ -243,7 +278,7 @@ export function ColorSlider(props: ColorSliderProps): JSX.Element {
|
|
|
243
278
|
* The track element of a color slider.
|
|
244
279
|
*/
|
|
245
280
|
export function ColorSliderTrack(props: ColorSliderTrackProps): JSX.Element {
|
|
246
|
-
const [local] = splitProps(props, ['class', 'style', 'slot']);
|
|
281
|
+
const [local, domProps] = splitProps(props, ['class', 'style', 'slot', 'children']);
|
|
247
282
|
|
|
248
283
|
const context = useContext(ColorSliderContext);
|
|
249
284
|
if (!context) {
|
|
@@ -284,6 +319,7 @@ export function ColorSliderTrack(props: ColorSliderTrackProps): JSX.Element {
|
|
|
284
319
|
|
|
285
320
|
return (
|
|
286
321
|
<div
|
|
322
|
+
{...domProps}
|
|
287
323
|
ref={setTrackRef}
|
|
288
324
|
{...cleanTrackProps()}
|
|
289
325
|
class={renderProps.class()}
|
|
@@ -300,14 +336,14 @@ export function ColorSliderTrack(props: ColorSliderTrackProps): JSX.Element {
|
|
|
300
336
|
* The thumb element of a color slider.
|
|
301
337
|
*/
|
|
302
338
|
export function ColorSliderThumb(props: ColorSliderThumbProps): JSX.Element {
|
|
303
|
-
const [local] = splitProps(props, ['class', 'style', 'slot']);
|
|
339
|
+
const [local, domProps] = splitProps(props, ['class', 'style', 'slot', 'children']);
|
|
304
340
|
|
|
305
341
|
const context = useContext(ColorSliderContext);
|
|
306
342
|
if (!context) {
|
|
307
343
|
throw new Error('ColorSliderThumb must be used within a ColorSlider');
|
|
308
344
|
}
|
|
309
345
|
|
|
310
|
-
const { state, thumbProps } = context;
|
|
346
|
+
const { state, thumbProps, inputProps } = context;
|
|
311
347
|
|
|
312
348
|
// Create focus ring
|
|
313
349
|
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
@@ -352,6 +388,12 @@ export function ColorSliderThumb(props: ColorSliderThumbProps): JSX.Element {
|
|
|
352
388
|
const { ref: _ref, ...rest } = hoverProps as Record<string, unknown>;
|
|
353
389
|
return rest;
|
|
354
390
|
};
|
|
391
|
+
const mergedInputProps = () => {
|
|
392
|
+
return mergeProps(
|
|
393
|
+
inputProps as Record<string, unknown>,
|
|
394
|
+
cleanFocusProps()
|
|
395
|
+
) as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
396
|
+
};
|
|
355
397
|
|
|
356
398
|
// Merge styles
|
|
357
399
|
const mergedStyle = () => {
|
|
@@ -362,8 +404,8 @@ export function ColorSliderThumb(props: ColorSliderThumbProps): JSX.Element {
|
|
|
362
404
|
|
|
363
405
|
return (
|
|
364
406
|
<div
|
|
407
|
+
{...domProps}
|
|
365
408
|
{...cleanThumbProps()}
|
|
366
|
-
{...cleanFocusProps()}
|
|
367
409
|
{...cleanHoverProps()}
|
|
368
410
|
class={renderProps.class()}
|
|
369
411
|
style={mergedStyle()}
|
|
@@ -373,6 +415,7 @@ export function ColorSliderThumb(props: ColorSliderThumbProps): JSX.Element {
|
|
|
373
415
|
data-focus-visible={isFocusVisible() || undefined}
|
|
374
416
|
data-hovered={isHovered() || undefined}
|
|
375
417
|
>
|
|
418
|
+
<input {...mergedInputProps()} />
|
|
376
419
|
{renderProps.renderChildren()}
|
|
377
420
|
</div>
|
|
378
421
|
);
|
|
@@ -474,6 +517,7 @@ export const ColorAreaContext = createContext<ColorAreaContextValue | null>(null
|
|
|
474
517
|
* A color area allows users to select a color using a 2D gradient.
|
|
475
518
|
*/
|
|
476
519
|
export function ColorArea(props: ColorAreaProps): JSX.Element {
|
|
520
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
477
521
|
const [local, stateProps, ariaProps, rest] = splitProps(
|
|
478
522
|
props,
|
|
479
523
|
['children', 'class', 'style', 'slot'],
|
|
@@ -483,9 +527,9 @@ export function ColorArea(props: ColorAreaProps): JSX.Element {
|
|
|
483
527
|
|
|
484
528
|
// Create color area state
|
|
485
529
|
const state = createColorAreaState(() => ({
|
|
486
|
-
value: stateProps.value,
|
|
530
|
+
value: stateProps.value ?? pickerContext?.value,
|
|
487
531
|
defaultValue: stateProps.defaultValue,
|
|
488
|
-
onChange: stateProps.onChange,
|
|
532
|
+
onChange: stateProps.onChange ?? pickerContext?.onChange,
|
|
489
533
|
onChangeEnd: stateProps.onChangeEnd,
|
|
490
534
|
xChannel: stateProps.xChannel,
|
|
491
535
|
yChannel: stateProps.yChannel,
|
|
@@ -575,10 +619,6 @@ export function ColorArea(props: ColorAreaProps): JSX.Element {
|
|
|
575
619
|
data-dragging={state.isDragging || undefined}
|
|
576
620
|
>
|
|
577
621
|
{renderProps.renderChildren()}
|
|
578
|
-
|
|
579
|
-
{/* Hidden inputs for accessibility */}
|
|
580
|
-
<input {...xInputProps} />
|
|
581
|
-
<input {...yInputProps} />
|
|
582
622
|
</div>
|
|
583
623
|
</ColorAreaContext.Provider>
|
|
584
624
|
);
|
|
@@ -588,7 +628,7 @@ export function ColorArea(props: ColorAreaProps): JSX.Element {
|
|
|
588
628
|
* The gradient background of a color area.
|
|
589
629
|
*/
|
|
590
630
|
export function ColorAreaGradient(props: ColorAreaGradientProps): JSX.Element {
|
|
591
|
-
const [local] = splitProps(props, ['class', 'style', 'slot']);
|
|
631
|
+
const [local, domProps] = splitProps(props, ['class', 'style', 'slot', 'children']);
|
|
592
632
|
|
|
593
633
|
const context = useContext(ColorAreaContext);
|
|
594
634
|
if (!context) {
|
|
@@ -628,6 +668,7 @@ export function ColorAreaGradient(props: ColorAreaGradientProps): JSX.Element {
|
|
|
628
668
|
|
|
629
669
|
return (
|
|
630
670
|
<div
|
|
671
|
+
{...domProps}
|
|
631
672
|
{...cleanGradientProps()}
|
|
632
673
|
class={renderProps.class()}
|
|
633
674
|
style={mergedStyle()}
|
|
@@ -642,14 +683,14 @@ export function ColorAreaGradient(props: ColorAreaGradientProps): JSX.Element {
|
|
|
642
683
|
* The thumb element of a color area.
|
|
643
684
|
*/
|
|
644
685
|
export function ColorAreaThumb(props: ColorAreaThumbProps): JSX.Element {
|
|
645
|
-
const [local] = splitProps(props, ['class', 'style', 'slot']);
|
|
686
|
+
const [local, domProps] = splitProps(props, ['class', 'style', 'slot', 'children']);
|
|
646
687
|
|
|
647
688
|
const context = useContext(ColorAreaContext);
|
|
648
689
|
if (!context) {
|
|
649
690
|
throw new Error('ColorAreaThumb must be used within a ColorArea');
|
|
650
691
|
}
|
|
651
692
|
|
|
652
|
-
const { state, thumbProps } = context;
|
|
693
|
+
const { state, thumbProps, xInputProps, yInputProps } = context;
|
|
653
694
|
|
|
654
695
|
// Create focus ring
|
|
655
696
|
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
@@ -694,6 +735,18 @@ export function ColorAreaThumb(props: ColorAreaThumbProps): JSX.Element {
|
|
|
694
735
|
const { ref: _ref, ...rest } = hoverProps as Record<string, unknown>;
|
|
695
736
|
return rest;
|
|
696
737
|
};
|
|
738
|
+
const mergedXInputProps = () => {
|
|
739
|
+
return mergeProps(
|
|
740
|
+
xInputProps as Record<string, unknown>,
|
|
741
|
+
cleanFocusProps()
|
|
742
|
+
) as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
743
|
+
};
|
|
744
|
+
const mergedYInputProps = () => {
|
|
745
|
+
return mergeProps(
|
|
746
|
+
yInputProps as Record<string, unknown>,
|
|
747
|
+
cleanFocusProps()
|
|
748
|
+
) as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
749
|
+
};
|
|
697
750
|
|
|
698
751
|
// Merge styles
|
|
699
752
|
const mergedStyle = () => {
|
|
@@ -704,8 +757,8 @@ export function ColorAreaThumb(props: ColorAreaThumbProps): JSX.Element {
|
|
|
704
757
|
|
|
705
758
|
return (
|
|
706
759
|
<div
|
|
760
|
+
{...domProps}
|
|
707
761
|
{...cleanThumbProps()}
|
|
708
|
-
{...cleanFocusProps()}
|
|
709
762
|
{...cleanHoverProps()}
|
|
710
763
|
class={renderProps.class()}
|
|
711
764
|
style={mergedStyle()}
|
|
@@ -715,6 +768,8 @@ export function ColorAreaThumb(props: ColorAreaThumbProps): JSX.Element {
|
|
|
715
768
|
data-focus-visible={isFocusVisible() || undefined}
|
|
716
769
|
data-hovered={isHovered() || undefined}
|
|
717
770
|
>
|
|
771
|
+
<input {...mergedXInputProps()} />
|
|
772
|
+
<input {...mergedYInputProps()} />
|
|
718
773
|
{renderProps.renderChildren()}
|
|
719
774
|
</div>
|
|
720
775
|
);
|
|
@@ -810,6 +865,7 @@ export const ColorWheelContext = createContext<ColorWheelContextValue | null>(nu
|
|
|
810
865
|
* A color wheel allows users to select a hue using a circular control.
|
|
811
866
|
*/
|
|
812
867
|
export function ColorWheel(props: ColorWheelProps): JSX.Element {
|
|
868
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
813
869
|
const [local, stateProps, ariaProps, rest] = splitProps(
|
|
814
870
|
props,
|
|
815
871
|
['children', 'class', 'style', 'slot'],
|
|
@@ -819,9 +875,9 @@ export function ColorWheel(props: ColorWheelProps): JSX.Element {
|
|
|
819
875
|
|
|
820
876
|
// Create color wheel state
|
|
821
877
|
const state = createColorWheelState(() => ({
|
|
822
|
-
value: stateProps.value,
|
|
878
|
+
value: stateProps.value ?? pickerContext?.value,
|
|
823
879
|
defaultValue: stateProps.defaultValue,
|
|
824
|
-
onChange: stateProps.onChange,
|
|
880
|
+
onChange: stateProps.onChange ?? pickerContext?.onChange,
|
|
825
881
|
onChangeEnd: stateProps.onChangeEnd,
|
|
826
882
|
isDisabled: ariaProps.isDisabled,
|
|
827
883
|
}));
|
|
@@ -889,9 +945,6 @@ export function ColorWheel(props: ColorWheelProps): JSX.Element {
|
|
|
889
945
|
data-dragging={state.isDragging || undefined}
|
|
890
946
|
>
|
|
891
947
|
{renderProps.renderChildren()}
|
|
892
|
-
|
|
893
|
-
{/* Hidden input for accessibility */}
|
|
894
|
-
<input {...inputProps} />
|
|
895
948
|
</div>
|
|
896
949
|
</ColorWheelContext.Provider>
|
|
897
950
|
);
|
|
@@ -901,7 +954,7 @@ export function ColorWheel(props: ColorWheelProps): JSX.Element {
|
|
|
901
954
|
* The track element of a color wheel.
|
|
902
955
|
*/
|
|
903
956
|
export function ColorWheelTrack(props: ColorWheelTrackProps): JSX.Element {
|
|
904
|
-
const [local] = splitProps(props, ['class', 'style', 'slot']);
|
|
957
|
+
const [local, domProps] = splitProps(props, ['class', 'style', 'slot', 'children']);
|
|
905
958
|
|
|
906
959
|
const context = useContext(ColorWheelContext);
|
|
907
960
|
if (!context) {
|
|
@@ -942,6 +995,7 @@ export function ColorWheelTrack(props: ColorWheelTrackProps): JSX.Element {
|
|
|
942
995
|
|
|
943
996
|
return (
|
|
944
997
|
<div
|
|
998
|
+
{...domProps}
|
|
945
999
|
ref={setWheelRef}
|
|
946
1000
|
{...cleanTrackProps()}
|
|
947
1001
|
class={renderProps.class()}
|
|
@@ -958,14 +1012,14 @@ export function ColorWheelTrack(props: ColorWheelTrackProps): JSX.Element {
|
|
|
958
1012
|
* The thumb element of a color wheel.
|
|
959
1013
|
*/
|
|
960
1014
|
export function ColorWheelThumb(props: ColorWheelThumbProps): JSX.Element {
|
|
961
|
-
const [local] = splitProps(props, ['class', 'style', 'slot']);
|
|
1015
|
+
const [local, domProps] = splitProps(props, ['class', 'style', 'slot', 'children']);
|
|
962
1016
|
|
|
963
1017
|
const context = useContext(ColorWheelContext);
|
|
964
1018
|
if (!context) {
|
|
965
1019
|
throw new Error('ColorWheelThumb must be used within a ColorWheel');
|
|
966
1020
|
}
|
|
967
1021
|
|
|
968
|
-
const { state, thumbProps } = context;
|
|
1022
|
+
const { state, thumbProps, inputProps } = context;
|
|
969
1023
|
|
|
970
1024
|
// Create focus ring
|
|
971
1025
|
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
@@ -1010,6 +1064,12 @@ export function ColorWheelThumb(props: ColorWheelThumbProps): JSX.Element {
|
|
|
1010
1064
|
const { ref: _ref, ...rest } = hoverProps as Record<string, unknown>;
|
|
1011
1065
|
return rest;
|
|
1012
1066
|
};
|
|
1067
|
+
const mergedInputProps = () => {
|
|
1068
|
+
return mergeProps(
|
|
1069
|
+
inputProps as Record<string, unknown>,
|
|
1070
|
+
cleanFocusProps()
|
|
1071
|
+
) as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
1072
|
+
};
|
|
1013
1073
|
|
|
1014
1074
|
// Merge styles
|
|
1015
1075
|
const mergedStyle = () => {
|
|
@@ -1020,8 +1080,8 @@ export function ColorWheelThumb(props: ColorWheelThumbProps): JSX.Element {
|
|
|
1020
1080
|
|
|
1021
1081
|
return (
|
|
1022
1082
|
<div
|
|
1083
|
+
{...domProps}
|
|
1023
1084
|
{...cleanThumbProps()}
|
|
1024
|
-
{...cleanFocusProps()}
|
|
1025
1085
|
{...cleanHoverProps()}
|
|
1026
1086
|
class={renderProps.class()}
|
|
1027
1087
|
style={mergedStyle()}
|
|
@@ -1031,6 +1091,7 @@ export function ColorWheelThumb(props: ColorWheelThumbProps): JSX.Element {
|
|
|
1031
1091
|
data-focus-visible={isFocusVisible() || undefined}
|
|
1032
1092
|
data-hovered={isHovered() || undefined}
|
|
1033
1093
|
>
|
|
1094
|
+
<input {...mergedInputProps()} />
|
|
1034
1095
|
{renderProps.renderChildren()}
|
|
1035
1096
|
</div>
|
|
1036
1097
|
);
|
|
@@ -1115,6 +1176,7 @@ export const ColorFieldContext = createContext<ColorFieldContextValue | null>(nu
|
|
|
1115
1176
|
* A color field allows users to enter a color value as text.
|
|
1116
1177
|
*/
|
|
1117
1178
|
export function ColorField(props: ColorFieldProps): JSX.Element {
|
|
1179
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
1118
1180
|
const [local, stateProps, ariaProps, rest] = splitProps(
|
|
1119
1181
|
props,
|
|
1120
1182
|
['children', 'class', 'style', 'slot', 'label'],
|
|
@@ -1124,9 +1186,13 @@ export function ColorField(props: ColorFieldProps): JSX.Element {
|
|
|
1124
1186
|
|
|
1125
1187
|
// Create color field state
|
|
1126
1188
|
const state = createColorFieldState(() => ({
|
|
1127
|
-
value: stateProps.value,
|
|
1189
|
+
value: stateProps.value ?? pickerContext?.value,
|
|
1128
1190
|
defaultValue: stateProps.defaultValue,
|
|
1129
|
-
onChange: stateProps.onChange
|
|
1191
|
+
onChange: stateProps.onChange ?? ((color) => {
|
|
1192
|
+
if (color) {
|
|
1193
|
+
pickerContext?.onChange?.(color);
|
|
1194
|
+
}
|
|
1195
|
+
}),
|
|
1130
1196
|
channel: stateProps.channel,
|
|
1131
1197
|
colorFormat: stateProps.colorFormat,
|
|
1132
1198
|
isDisabled: ariaProps.isDisabled,
|
|
@@ -1207,7 +1273,7 @@ export function ColorField(props: ColorFieldProps): JSX.Element {
|
|
|
1207
1273
|
* The input element of a color field.
|
|
1208
1274
|
*/
|
|
1209
1275
|
export function ColorFieldInput(props: ColorFieldInputProps): JSX.Element {
|
|
1210
|
-
const [local] = splitProps(props, ['class', 'style', 'slot']);
|
|
1276
|
+
const [local, domProps] = splitProps(props, ['class', 'style', 'slot', 'children']);
|
|
1211
1277
|
|
|
1212
1278
|
const context = useContext(ColorFieldContext);
|
|
1213
1279
|
if (!context) {
|
|
@@ -1263,6 +1329,7 @@ export function ColorFieldInput(props: ColorFieldInputProps): JSX.Element {
|
|
|
1263
1329
|
|
|
1264
1330
|
return (
|
|
1265
1331
|
<input
|
|
1332
|
+
{...domProps}
|
|
1266
1333
|
{...cleanInputProps()}
|
|
1267
1334
|
{...cleanFocusProps()}
|
|
1268
1335
|
{...cleanHoverProps()}
|
|
@@ -1294,7 +1361,7 @@ export interface ColorSwatchRenderProps {
|
|
|
1294
1361
|
|
|
1295
1362
|
export interface ColorSwatchProps extends SlotProps {
|
|
1296
1363
|
/** The color to display. */
|
|
1297
|
-
color
|
|
1364
|
+
color?: Color | string;
|
|
1298
1365
|
/** Accessible label for the swatch. */
|
|
1299
1366
|
'aria-label'?: string;
|
|
1300
1367
|
/** The children of the component. */
|
|
@@ -1309,20 +1376,26 @@ export interface ColorSwatchProps extends SlotProps {
|
|
|
1309
1376
|
* A color swatch displays a preview of a color.
|
|
1310
1377
|
*/
|
|
1311
1378
|
export function ColorSwatch(props: ColorSwatchProps): JSX.Element {
|
|
1379
|
+
const swatchContext = useContext(ColorSwatchContextInternal);
|
|
1380
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
1312
1381
|
const [local, ariaProps, rest] = splitProps(
|
|
1313
1382
|
props,
|
|
1314
1383
|
['children', 'class', 'style', 'slot', 'color'],
|
|
1315
1384
|
['aria-label']
|
|
1316
1385
|
);
|
|
1317
1386
|
|
|
1387
|
+
const resolvedColor = createMemo<Color | string>(() => {
|
|
1388
|
+
return local.color ?? swatchContext?.color ?? pickerContext?.value ?? '#0000';
|
|
1389
|
+
});
|
|
1390
|
+
|
|
1318
1391
|
// Create color swatch aria props
|
|
1319
1392
|
const { swatchProps } = createColorSwatch(() => ({
|
|
1320
|
-
color:
|
|
1393
|
+
color: resolvedColor(),
|
|
1321
1394
|
'aria-label': ariaProps['aria-label'],
|
|
1322
1395
|
}));
|
|
1323
1396
|
|
|
1324
1397
|
// Normalize color
|
|
1325
|
-
const color = createMemo(() => normalizeColor(
|
|
1398
|
+
const color = createMemo(() => normalizeColor(resolvedColor()));
|
|
1326
1399
|
|
|
1327
1400
|
// Render props values
|
|
1328
1401
|
const renderValues = createMemo<ColorSwatchRenderProps>(() => ({
|
|
@@ -1368,3 +1441,548 @@ export function ColorSwatch(props: ColorSwatchProps): JSX.Element {
|
|
|
1368
1441
|
</div>
|
|
1369
1442
|
);
|
|
1370
1443
|
}
|
|
1444
|
+
|
|
1445
|
+
export const ColorSliderStateContext = ColorSliderContext;
|
|
1446
|
+
export const ColorAreaStateContext = ColorAreaContext;
|
|
1447
|
+
export const ColorWheelStateContext = ColorWheelContext;
|
|
1448
|
+
export const ColorWheelTrackContext = ColorWheelContext;
|
|
1449
|
+
export const ColorFieldStateContext = ColorFieldContext;
|
|
1450
|
+
export const ColorSwatchContext = ColorSwatchContextInternal;
|
|
1451
|
+
export const ColorPickerContext = ColorPickerContextInternal;
|
|
1452
|
+
export const ColorPickerStateContext = ColorPickerStateContextInternal;
|
|
1453
|
+
export const ColorSwatchPickerContext = ColorSwatchPickerContextInternal;
|
|
1454
|
+
export const ColorThumb = ColorSliderThumb;
|
|
1455
|
+
|
|
1456
|
+
export interface ColorPickerRenderProps {
|
|
1457
|
+
/** The currently selected color. */
|
|
1458
|
+
color: Color;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
export interface ColorPickerProps extends SlotProps {
|
|
1462
|
+
/** The current color value (controlled). */
|
|
1463
|
+
value?: Color | string;
|
|
1464
|
+
/** The default color value (uncontrolled). */
|
|
1465
|
+
defaultValue?: Color | string;
|
|
1466
|
+
/** Handler called when the color changes. */
|
|
1467
|
+
onChange?: (color: Color) => void;
|
|
1468
|
+
/** The children of the color picker. */
|
|
1469
|
+
children?: RenderChildren<ColorPickerRenderProps>;
|
|
1470
|
+
/** The CSS className for the element. */
|
|
1471
|
+
class?: ClassNameOrFunction<ColorPickerRenderProps>;
|
|
1472
|
+
/** The inline style for the element. */
|
|
1473
|
+
style?: StyleOrFunction<ColorPickerRenderProps>;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
export interface ColorSwatchPickerRenderProps {
|
|
1477
|
+
/** Whether the swatch picker has focus. */
|
|
1478
|
+
isFocused: boolean;
|
|
1479
|
+
/** Whether the swatch picker has keyboard focus. */
|
|
1480
|
+
isFocusVisible: boolean;
|
|
1481
|
+
/** The currently selected color. */
|
|
1482
|
+
selectedColor: Color;
|
|
1483
|
+
/** Item arrangement mode. */
|
|
1484
|
+
layout: 'grid' | 'stack';
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
export interface ColorSwatchPickerProps extends SlotProps {
|
|
1488
|
+
/** The current color value (controlled). */
|
|
1489
|
+
value?: Color | string;
|
|
1490
|
+
/** The default color value (uncontrolled). */
|
|
1491
|
+
defaultValue?: Color | string;
|
|
1492
|
+
/** Handler called when the selected color changes. */
|
|
1493
|
+
onChange?: (color: Color) => void;
|
|
1494
|
+
/** Accessible label for the swatch picker. */
|
|
1495
|
+
'aria-label'?: string;
|
|
1496
|
+
/** ID of element that labels the swatch picker. */
|
|
1497
|
+
'aria-labelledby'?: string;
|
|
1498
|
+
/** ID of element that describes the swatch picker. */
|
|
1499
|
+
'aria-describedby'?: string;
|
|
1500
|
+
/** Whether swatches are arranged as a grid or stack. */
|
|
1501
|
+
layout?: 'grid' | 'stack';
|
|
1502
|
+
/** The children (ColorSwatchPickerItem elements). */
|
|
1503
|
+
children?: JSX.Element;
|
|
1504
|
+
/** The CSS className for the element. */
|
|
1505
|
+
class?: ClassNameOrFunction<ColorSwatchPickerRenderProps>;
|
|
1506
|
+
/** The inline style for the element. */
|
|
1507
|
+
style?: StyleOrFunction<ColorSwatchPickerRenderProps>;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
export interface ColorSwatchPickerItemRenderProps {
|
|
1511
|
+
/** Whether the item is selected. */
|
|
1512
|
+
isSelected: boolean;
|
|
1513
|
+
/** Whether the item is focused. */
|
|
1514
|
+
isFocused: boolean;
|
|
1515
|
+
/** Whether the item has keyboard focus. */
|
|
1516
|
+
isFocusVisible: boolean;
|
|
1517
|
+
/** Whether the item is pressed. */
|
|
1518
|
+
isPressed: boolean;
|
|
1519
|
+
/** Whether the item is disabled. */
|
|
1520
|
+
isDisabled: boolean;
|
|
1521
|
+
/** The color represented by the item. */
|
|
1522
|
+
color: Color;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
export interface ColorSwatchPickerItemProps extends SlotProps {
|
|
1526
|
+
/** The color represented by this swatch item. */
|
|
1527
|
+
color: Color | string;
|
|
1528
|
+
/** Whether this item is disabled. */
|
|
1529
|
+
isDisabled?: boolean;
|
|
1530
|
+
/** Accessible label for this item. */
|
|
1531
|
+
'aria-label'?: string;
|
|
1532
|
+
/** The children of the swatch item. */
|
|
1533
|
+
children?: RenderChildren<ColorSwatchPickerItemRenderProps>;
|
|
1534
|
+
/** The CSS className for the element. */
|
|
1535
|
+
class?: ClassNameOrFunction<ColorSwatchPickerItemRenderProps>;
|
|
1536
|
+
/** The inline style for the element. */
|
|
1537
|
+
style?: StyleOrFunction<ColorSwatchPickerItemRenderProps>;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
export function ColorPicker(props: ColorPickerProps): JSX.Element {
|
|
1541
|
+
const [local] = splitProps(props, [
|
|
1542
|
+
'value',
|
|
1543
|
+
'defaultValue',
|
|
1544
|
+
'onChange',
|
|
1545
|
+
'children',
|
|
1546
|
+
'class',
|
|
1547
|
+
'style',
|
|
1548
|
+
'slot',
|
|
1549
|
+
]);
|
|
1550
|
+
|
|
1551
|
+
const [internalColor, setInternalColor] = createSignal<Color>(
|
|
1552
|
+
normalizeColor(local.defaultValue ?? '#ff0000')
|
|
1553
|
+
);
|
|
1554
|
+
|
|
1555
|
+
const color = createMemo<Color>(() => {
|
|
1556
|
+
if (local.value !== undefined) {
|
|
1557
|
+
return normalizeColor(local.value);
|
|
1558
|
+
}
|
|
1559
|
+
return internalColor();
|
|
1560
|
+
});
|
|
1561
|
+
|
|
1562
|
+
const setColor = (nextColor: Color) => {
|
|
1563
|
+
if (local.value === undefined) {
|
|
1564
|
+
setInternalColor(nextColor);
|
|
1565
|
+
}
|
|
1566
|
+
local.onChange?.(nextColor);
|
|
1567
|
+
};
|
|
1568
|
+
|
|
1569
|
+
const renderValues = createMemo<ColorPickerRenderProps>(() => ({
|
|
1570
|
+
color: color(),
|
|
1571
|
+
}));
|
|
1572
|
+
|
|
1573
|
+
const renderProps = useRenderProps(
|
|
1574
|
+
{
|
|
1575
|
+
children: local.children,
|
|
1576
|
+
class: local.class,
|
|
1577
|
+
style: local.style,
|
|
1578
|
+
defaultClassName: 'solidaria-ColorPicker',
|
|
1579
|
+
},
|
|
1580
|
+
renderValues
|
|
1581
|
+
);
|
|
1582
|
+
|
|
1583
|
+
return (
|
|
1584
|
+
<ColorPickerStateContextInternal.Provider
|
|
1585
|
+
value={{
|
|
1586
|
+
color: () => color(),
|
|
1587
|
+
setColor,
|
|
1588
|
+
}}
|
|
1589
|
+
>
|
|
1590
|
+
<ColorPickerContextInternal.Provider
|
|
1591
|
+
value={{
|
|
1592
|
+
get value() {
|
|
1593
|
+
return color();
|
|
1594
|
+
},
|
|
1595
|
+
onChange: setColor,
|
|
1596
|
+
}}
|
|
1597
|
+
>
|
|
1598
|
+
<div class={renderProps.class()} style={renderProps.style()}>
|
|
1599
|
+
{renderProps.renderChildren()}
|
|
1600
|
+
</div>
|
|
1601
|
+
</ColorPickerContextInternal.Provider>
|
|
1602
|
+
</ColorPickerStateContextInternal.Provider>
|
|
1603
|
+
);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
export function ColorSwatchPicker(props: ColorSwatchPickerProps): JSX.Element {
|
|
1607
|
+
const pickerContext = useContext(ColorPickerContextInternal);
|
|
1608
|
+
const [local, rest] = splitProps(props, [
|
|
1609
|
+
'value',
|
|
1610
|
+
'defaultValue',
|
|
1611
|
+
'onChange',
|
|
1612
|
+
'aria-label',
|
|
1613
|
+
'aria-labelledby',
|
|
1614
|
+
'aria-describedby',
|
|
1615
|
+
'layout',
|
|
1616
|
+
'children',
|
|
1617
|
+
'class',
|
|
1618
|
+
'style',
|
|
1619
|
+
'slot',
|
|
1620
|
+
]);
|
|
1621
|
+
|
|
1622
|
+
const [itemMap, setItemMap] = createSignal<Map<string, ColorSwatchPickerItemData>>(new Map());
|
|
1623
|
+
const [itemOrder, setItemOrder] = createSignal<string[]>([]);
|
|
1624
|
+
const [internalColor, setInternalColor] = createSignal<Color>(
|
|
1625
|
+
normalizeColor(local.defaultValue ?? pickerContext?.value ?? '#ff0000')
|
|
1626
|
+
);
|
|
1627
|
+
|
|
1628
|
+
const selectedColor = createMemo<Color>(() => {
|
|
1629
|
+
if (local.value !== undefined) {
|
|
1630
|
+
return normalizeColor(local.value);
|
|
1631
|
+
}
|
|
1632
|
+
if (pickerContext?.value !== undefined) {
|
|
1633
|
+
return normalizeColor(pickerContext.value);
|
|
1634
|
+
}
|
|
1635
|
+
return internalColor();
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
const selectedKey = createMemo(() => selectedColor().toString('hexa'));
|
|
1639
|
+
const isControlled = createMemo(() => local.value !== undefined || pickerContext?.value !== undefined);
|
|
1640
|
+
|
|
1641
|
+
const registerItem = (item: ColorSwatchPickerItemData) => {
|
|
1642
|
+
setItemMap((prev) => {
|
|
1643
|
+
const next = new Map(prev);
|
|
1644
|
+
next.set(item.key, item);
|
|
1645
|
+
return next;
|
|
1646
|
+
});
|
|
1647
|
+
setItemOrder((prev) => (prev.includes(item.key) ? prev : [...prev, item.key]));
|
|
1648
|
+
};
|
|
1649
|
+
|
|
1650
|
+
const unregisterItem = (key: string) => {
|
|
1651
|
+
setItemMap((prev) => {
|
|
1652
|
+
if (!prev.has(key)) return prev;
|
|
1653
|
+
const next = new Map(prev);
|
|
1654
|
+
next.delete(key);
|
|
1655
|
+
return next;
|
|
1656
|
+
});
|
|
1657
|
+
setItemOrder((prev) => prev.filter((itemKey) => itemKey !== key));
|
|
1658
|
+
};
|
|
1659
|
+
|
|
1660
|
+
const items = createMemo(() => {
|
|
1661
|
+
const map = itemMap();
|
|
1662
|
+
return itemOrder()
|
|
1663
|
+
.map((key) => map.get(key))
|
|
1664
|
+
.filter((item): item is ColorSwatchPickerItemData => item != null);
|
|
1665
|
+
});
|
|
1666
|
+
|
|
1667
|
+
const state = createListState<ColorSwatchPickerItemData>({
|
|
1668
|
+
get items() {
|
|
1669
|
+
return items();
|
|
1670
|
+
},
|
|
1671
|
+
get getKey() {
|
|
1672
|
+
return (item: ColorSwatchPickerItemData) => item.key;
|
|
1673
|
+
},
|
|
1674
|
+
get getTextValue() {
|
|
1675
|
+
return (item: ColorSwatchPickerItemData) => item.textValue;
|
|
1676
|
+
},
|
|
1677
|
+
get getDisabled() {
|
|
1678
|
+
return (item: ColorSwatchPickerItemData) => !!item.isDisabled;
|
|
1679
|
+
},
|
|
1680
|
+
selectionMode: 'single',
|
|
1681
|
+
disallowEmptySelection: true,
|
|
1682
|
+
get selectedKeys() {
|
|
1683
|
+
return [selectedKey()];
|
|
1684
|
+
},
|
|
1685
|
+
onSelectionChange(keys) {
|
|
1686
|
+
if (keys === 'all') return;
|
|
1687
|
+
const key = keys.values().next().value as string | undefined;
|
|
1688
|
+
if (!key) return;
|
|
1689
|
+
const item = itemMap().get(key);
|
|
1690
|
+
if (!item) return;
|
|
1691
|
+
if (!isControlled()) {
|
|
1692
|
+
setInternalColor(item.color);
|
|
1693
|
+
}
|
|
1694
|
+
(local.onChange ?? pickerContext?.onChange)?.(item.color);
|
|
1695
|
+
},
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1698
|
+
const listBoxAria = createListBox(
|
|
1699
|
+
() => ({
|
|
1700
|
+
'aria-label': local['aria-label'] ?? (!local['aria-labelledby'] ? 'Color swatch picker' : undefined),
|
|
1701
|
+
'aria-labelledby': local['aria-labelledby'],
|
|
1702
|
+
'aria-describedby': local['aria-describedby'],
|
|
1703
|
+
shouldFocusWrap: true,
|
|
1704
|
+
}),
|
|
1705
|
+
state
|
|
1706
|
+
);
|
|
1707
|
+
|
|
1708
|
+
const resolveDirection = (): 'ltr' | 'rtl' => {
|
|
1709
|
+
if (typeof document === 'undefined') return 'ltr';
|
|
1710
|
+
const rootDir = document.dir;
|
|
1711
|
+
return rootDir === 'rtl' ? 'rtl' : 'ltr';
|
|
1712
|
+
};
|
|
1713
|
+
|
|
1714
|
+
const findNextEnabledKey = (from: Key | null, direction: 'next' | 'prev') => {
|
|
1715
|
+
const collection = state.collection();
|
|
1716
|
+
const getAdjacent = direction === 'next'
|
|
1717
|
+
? (key: Key) => collection.getKeyAfter(key)
|
|
1718
|
+
: (key: Key) => collection.getKeyBefore(key);
|
|
1719
|
+
const getBoundary = direction === 'next'
|
|
1720
|
+
? () => collection.getFirstKey()
|
|
1721
|
+
: () => collection.getLastKey();
|
|
1722
|
+
|
|
1723
|
+
let key = from != null ? getAdjacent(from) : getBoundary();
|
|
1724
|
+
while (key != null && state.isDisabled(key)) {
|
|
1725
|
+
key = getAdjacent(key);
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
return key;
|
|
1729
|
+
};
|
|
1730
|
+
|
|
1731
|
+
const getBoundaryEnabledKey = (direction: 'next' | 'prev') => {
|
|
1732
|
+
const collection = state.collection();
|
|
1733
|
+
const getAdjacent = direction === 'next'
|
|
1734
|
+
? (key: Key) => collection.getKeyAfter(key)
|
|
1735
|
+
: (key: Key) => collection.getKeyBefore(key);
|
|
1736
|
+
const getBoundary = direction === 'next'
|
|
1737
|
+
? () => collection.getFirstKey()
|
|
1738
|
+
: () => collection.getLastKey();
|
|
1739
|
+
|
|
1740
|
+
let key = getBoundary();
|
|
1741
|
+
while (key != null && state.isDisabled(key)) {
|
|
1742
|
+
key = getAdjacent(key);
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
return key;
|
|
1746
|
+
};
|
|
1747
|
+
|
|
1748
|
+
const getOptionElementForKey = (listbox: HTMLElement | null, key: Key | null): HTMLElement | null => {
|
|
1749
|
+
if (!listbox || key == null) return null;
|
|
1750
|
+
const keyString = String(key);
|
|
1751
|
+
for (const optionElement of listbox.querySelectorAll<HTMLElement>('[role="option"]')) {
|
|
1752
|
+
if (optionElement.id === keyString) {
|
|
1753
|
+
return optionElement;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
return null;
|
|
1757
|
+
};
|
|
1758
|
+
|
|
1759
|
+
const findGridKey = (
|
|
1760
|
+
listbox: HTMLElement | null,
|
|
1761
|
+
key: Key,
|
|
1762
|
+
nextKey: (current: Key) => Key | null,
|
|
1763
|
+
shouldSkip: (prevRect: DOMRect, itemRect: DOMRect) => boolean
|
|
1764
|
+
): Key | null => {
|
|
1765
|
+
let candidate: Key | null = key;
|
|
1766
|
+
const previousRect = getOptionElementForKey(listbox, candidate)?.getBoundingClientRect();
|
|
1767
|
+
if (!previousRect) {
|
|
1768
|
+
return null;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
while (candidate != null) {
|
|
1772
|
+
candidate = nextKey(candidate);
|
|
1773
|
+
if (candidate == null) {
|
|
1774
|
+
return null;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
const itemRect = getOptionElementForKey(listbox, candidate)?.getBoundingClientRect();
|
|
1778
|
+
if (!itemRect) {
|
|
1779
|
+
return null;
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
if (!shouldSkip(previousRect, itemRect)) {
|
|
1783
|
+
return candidate;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
return null;
|
|
1788
|
+
};
|
|
1789
|
+
|
|
1790
|
+
const isSameRow = (prevRect: DOMRect, itemRect: DOMRect) => prevRect.y === itemRect.y || prevRect.x !== itemRect.x;
|
|
1791
|
+
const getGridKeyBelow = (listbox: HTMLElement | null, key: Key) =>
|
|
1792
|
+
findGridKey(listbox, key, (current) => findNextEnabledKey(current, 'next'), isSameRow);
|
|
1793
|
+
const getGridKeyAbove = (listbox: HTMLElement | null, key: Key) =>
|
|
1794
|
+
findGridKey(listbox, key, (current) => findNextEnabledKey(current, 'prev'), isSameRow);
|
|
1795
|
+
const getGridKeyRightOf = (key: Key) =>
|
|
1796
|
+
resolveDirection() === 'rtl' ? findNextEnabledKey(key, 'prev') : findNextEnabledKey(key, 'next');
|
|
1797
|
+
const getGridKeyLeftOf = (key: Key) =>
|
|
1798
|
+
resolveDirection() === 'rtl' ? findNextEnabledKey(key, 'next') : findNextEnabledKey(key, 'prev');
|
|
1799
|
+
|
|
1800
|
+
const handleGridKeyDown = (e: KeyboardEvent): boolean => {
|
|
1801
|
+
if ((local.layout ?? 'grid') !== 'grid') return false;
|
|
1802
|
+
if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft' && e.key !== 'ArrowDown' && e.key !== 'ArrowUp') {
|
|
1803
|
+
return false;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
const listbox = e.currentTarget as HTMLElement | null;
|
|
1807
|
+
const focusedKey = state.focusedKey();
|
|
1808
|
+
const initialKey = focusedKey ?? (e.key === 'ArrowUp' || e.key === 'ArrowLeft'
|
|
1809
|
+
? getBoundaryEnabledKey('prev')
|
|
1810
|
+
: getBoundaryEnabledKey('next'));
|
|
1811
|
+
if (initialKey == null) return false;
|
|
1812
|
+
|
|
1813
|
+
let nextKey: Key | null = null;
|
|
1814
|
+
switch (e.key) {
|
|
1815
|
+
case 'ArrowDown':
|
|
1816
|
+
nextKey = getGridKeyBelow(listbox, initialKey) ?? getBoundaryEnabledKey('next');
|
|
1817
|
+
break;
|
|
1818
|
+
case 'ArrowUp':
|
|
1819
|
+
nextKey = getGridKeyAbove(listbox, initialKey) ?? getBoundaryEnabledKey('prev');
|
|
1820
|
+
break;
|
|
1821
|
+
case 'ArrowRight':
|
|
1822
|
+
nextKey = getGridKeyRightOf(initialKey) ?? (
|
|
1823
|
+
resolveDirection() === 'rtl' ? getBoundaryEnabledKey('prev') : getBoundaryEnabledKey('next')
|
|
1824
|
+
);
|
|
1825
|
+
break;
|
|
1826
|
+
case 'ArrowLeft':
|
|
1827
|
+
nextKey = getGridKeyLeftOf(initialKey) ?? (
|
|
1828
|
+
resolveDirection() === 'rtl' ? getBoundaryEnabledKey('next') : getBoundaryEnabledKey('prev')
|
|
1829
|
+
);
|
|
1830
|
+
break;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
if (nextKey == null) return false;
|
|
1834
|
+
|
|
1835
|
+
state.setFocusedKey(nextKey);
|
|
1836
|
+
if (state.selectionMode() === 'single') {
|
|
1837
|
+
state.replaceSelection(nextKey);
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
e.preventDefault();
|
|
1841
|
+
e.stopPropagation();
|
|
1842
|
+
return true;
|
|
1843
|
+
};
|
|
1844
|
+
|
|
1845
|
+
const getListBoxKeyDown = () => {
|
|
1846
|
+
const props = listBoxAria.listBoxProps as Record<string, unknown>;
|
|
1847
|
+
return props.onKeyDown as JSX.EventHandler<HTMLDivElement, KeyboardEvent> | undefined;
|
|
1848
|
+
};
|
|
1849
|
+
|
|
1850
|
+
const onColorSwatchPickerKeyDown: JSX.EventHandler<HTMLDivElement, KeyboardEvent> = (e) => {
|
|
1851
|
+
if (handleGridKeyDown(e)) {
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
getListBoxKeyDown()?.(e);
|
|
1855
|
+
};
|
|
1856
|
+
|
|
1857
|
+
createEffect(() => {
|
|
1858
|
+
const key = selectedKey();
|
|
1859
|
+
if (key) {
|
|
1860
|
+
state.setFocusedKey(key);
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1863
|
+
|
|
1864
|
+
const { isFocused, isFocusVisible, focusProps } = createFocusRing({ within: true });
|
|
1865
|
+
const domProps = createMemo(() => filterDOMProps(rest as Record<string, unknown>, { global: true }));
|
|
1866
|
+
const renderValues = createMemo<ColorSwatchPickerRenderProps>(() => ({
|
|
1867
|
+
isFocused: state.isFocused() || isFocused(),
|
|
1868
|
+
isFocusVisible: isFocusVisible(),
|
|
1869
|
+
selectedColor: selectedColor(),
|
|
1870
|
+
layout: local.layout ?? 'grid',
|
|
1871
|
+
}));
|
|
1872
|
+
|
|
1873
|
+
const renderProps = useRenderProps(
|
|
1874
|
+
{
|
|
1875
|
+
class: local.class,
|
|
1876
|
+
style: local.style,
|
|
1877
|
+
defaultClassName: 'solidaria-ColorSwatchPicker',
|
|
1878
|
+
},
|
|
1879
|
+
renderValues
|
|
1880
|
+
);
|
|
1881
|
+
|
|
1882
|
+
const cleanListBoxProps = () => {
|
|
1883
|
+
const { ref: _ref, onKeyDown: _onKeyDown, ...restListBoxProps } = listBoxAria.listBoxProps as Record<string, unknown>;
|
|
1884
|
+
return restListBoxProps;
|
|
1885
|
+
};
|
|
1886
|
+
const cleanFocusProps = () => {
|
|
1887
|
+
const { ref: _ref, ...restFocusProps } = focusProps as Record<string, unknown>;
|
|
1888
|
+
return restFocusProps;
|
|
1889
|
+
};
|
|
1890
|
+
|
|
1891
|
+
return (
|
|
1892
|
+
<ColorSwatchPickerContextInternal.Provider
|
|
1893
|
+
value={{
|
|
1894
|
+
state,
|
|
1895
|
+
registerItem,
|
|
1896
|
+
unregisterItem,
|
|
1897
|
+
}}
|
|
1898
|
+
>
|
|
1899
|
+
<div
|
|
1900
|
+
{...mergeProps(domProps(), cleanListBoxProps(), cleanFocusProps(), { onKeyDown: onColorSwatchPickerKeyDown })}
|
|
1901
|
+
class={renderProps.class()}
|
|
1902
|
+
style={renderProps.style()}
|
|
1903
|
+
data-focused={state.isFocused() || undefined}
|
|
1904
|
+
data-focus-visible={isFocusVisible() || undefined}
|
|
1905
|
+
data-layout={local.layout ?? 'grid'}
|
|
1906
|
+
>
|
|
1907
|
+
{local.children}
|
|
1908
|
+
</div>
|
|
1909
|
+
</ColorSwatchPickerContextInternal.Provider>
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
export function ColorSwatchPickerItem(props: ColorSwatchPickerItemProps): JSX.Element {
|
|
1914
|
+
const context = useContext(ColorSwatchPickerContextInternal);
|
|
1915
|
+
if (!context) {
|
|
1916
|
+
throw new Error('ColorSwatchPickerItem must be used within a ColorSwatchPicker');
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
const [local, ariaProps, rest] = splitProps(
|
|
1920
|
+
props,
|
|
1921
|
+
['children', 'class', 'style', 'slot', 'color'],
|
|
1922
|
+
['isDisabled', 'aria-label']
|
|
1923
|
+
);
|
|
1924
|
+
|
|
1925
|
+
const color = createMemo(() => normalizeColor(local.color));
|
|
1926
|
+
const key = createMemo(() => color().toString('hexa'));
|
|
1927
|
+
const textValue = createMemo(() => {
|
|
1928
|
+
const locale = globalThis.navigator?.language ?? 'en-US';
|
|
1929
|
+
return color().getColorName(locale);
|
|
1930
|
+
});
|
|
1931
|
+
|
|
1932
|
+
createEffect(() => {
|
|
1933
|
+
const itemKey = key();
|
|
1934
|
+
context.registerItem({
|
|
1935
|
+
key: itemKey,
|
|
1936
|
+
color: color(),
|
|
1937
|
+
textValue: textValue(),
|
|
1938
|
+
isDisabled: ariaProps.isDisabled,
|
|
1939
|
+
});
|
|
1940
|
+
onCleanup(() => context.unregisterItem(itemKey));
|
|
1941
|
+
});
|
|
1942
|
+
|
|
1943
|
+
const optionAria = createOption(
|
|
1944
|
+
() => ({
|
|
1945
|
+
key: key(),
|
|
1946
|
+
isDisabled: ariaProps.isDisabled,
|
|
1947
|
+
'aria-label': ariaProps['aria-label'] ?? textValue(),
|
|
1948
|
+
}),
|
|
1949
|
+
context.state
|
|
1950
|
+
);
|
|
1951
|
+
|
|
1952
|
+
const renderValues = createMemo<ColorSwatchPickerItemRenderProps>(() => ({
|
|
1953
|
+
isSelected: optionAria.isSelected(),
|
|
1954
|
+
isFocused: optionAria.isFocused(),
|
|
1955
|
+
isFocusVisible: optionAria.isFocusVisible(),
|
|
1956
|
+
isPressed: optionAria.isPressed(),
|
|
1957
|
+
isDisabled: optionAria.isDisabled(),
|
|
1958
|
+
color: color(),
|
|
1959
|
+
}));
|
|
1960
|
+
|
|
1961
|
+
const renderProps = useRenderProps(
|
|
1962
|
+
{
|
|
1963
|
+
children: local.children,
|
|
1964
|
+
class: local.class,
|
|
1965
|
+
style: local.style,
|
|
1966
|
+
defaultClassName: 'solidaria-ColorSwatchPickerItem',
|
|
1967
|
+
},
|
|
1968
|
+
renderValues
|
|
1969
|
+
);
|
|
1970
|
+
|
|
1971
|
+
const domProps = createMemo(() => filterDOMProps(rest as Record<string, unknown>, { global: true }));
|
|
1972
|
+
const cleanOptionProps = () => {
|
|
1973
|
+
const { ref: _ref, ...restOptionProps } = optionAria.optionProps as Record<string, unknown>;
|
|
1974
|
+
return restOptionProps;
|
|
1975
|
+
};
|
|
1976
|
+
|
|
1977
|
+
return (
|
|
1978
|
+
<div
|
|
1979
|
+
{...mergeProps(domProps(), cleanOptionProps())}
|
|
1980
|
+
class={renderProps.class()}
|
|
1981
|
+
style={renderProps.style()}
|
|
1982
|
+
>
|
|
1983
|
+
<ColorSwatchContextInternal.Provider value={{ color: color() }}>
|
|
1984
|
+
{renderProps.children ? renderProps.renderChildren() : <ColorSwatch />}
|
|
1985
|
+
</ColorSwatchContextInternal.Provider>
|
|
1986
|
+
</div>
|
|
1987
|
+
);
|
|
1988
|
+
}
|