@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.
Files changed (194) hide show
  1. package/LICENSE +21 -0
  2. package/dist/ActionBar.d.ts +71 -0
  3. package/dist/ActionBar.d.ts.map +1 -0
  4. package/dist/ActionGroup.d.ts +74 -0
  5. package/dist/ActionGroup.d.ts.map +1 -0
  6. package/dist/Alert.d.ts +70 -0
  7. package/dist/Alert.d.ts.map +1 -0
  8. package/dist/Breadcrumbs.d.ts +10 -2
  9. package/dist/Breadcrumbs.d.ts.map +1 -1
  10. package/dist/Button.d.ts +4 -0
  11. package/dist/Button.d.ts.map +1 -1
  12. package/dist/Calendar.d.ts +13 -0
  13. package/dist/Calendar.d.ts.map +1 -1
  14. package/dist/Checkbox.d.ts +2 -2
  15. package/dist/Checkbox.d.ts.map +1 -1
  16. package/dist/Collection.d.ts +125 -0
  17. package/dist/Collection.d.ts.map +1 -0
  18. package/dist/Color.d.ts +114 -2
  19. package/dist/Color.d.ts.map +1 -1
  20. package/dist/ColorEditor.d.ts +42 -0
  21. package/dist/ColorEditor.d.ts.map +1 -0
  22. package/dist/ComboBox.d.ts +64 -0
  23. package/dist/ComboBox.d.ts.map +1 -1
  24. package/dist/ContextualHelpTrigger.d.ts +40 -0
  25. package/dist/ContextualHelpTrigger.d.ts.map +1 -0
  26. package/dist/DateField.d.ts +27 -2
  27. package/dist/DateField.d.ts.map +1 -1
  28. package/dist/DatePicker.d.ts +67 -2
  29. package/dist/DatePicker.d.ts.map +1 -1
  30. package/dist/Dialog.d.ts.map +1 -1
  31. package/dist/Disclosure.d.ts +2 -0
  32. package/dist/Disclosure.d.ts.map +1 -1
  33. package/dist/DragAndDrop.d.ts +80 -0
  34. package/dist/DragAndDrop.d.ts.map +1 -0
  35. package/dist/DragPreview.d.ts +14 -0
  36. package/dist/DragPreview.d.ts.map +1 -0
  37. package/dist/DropZone.d.ts +27 -0
  38. package/dist/DropZone.d.ts.map +1 -0
  39. package/dist/FieldError.d.ts +23 -0
  40. package/dist/FieldError.d.ts.map +1 -0
  41. package/dist/FileTrigger.d.ts +26 -0
  42. package/dist/FileTrigger.d.ts.map +1 -0
  43. package/dist/Focusable.d.ts +27 -0
  44. package/dist/Focusable.d.ts.map +1 -0
  45. package/dist/Form.d.ts +27 -0
  46. package/dist/Form.d.ts.map +1 -0
  47. package/dist/GridList.d.ts +40 -1
  48. package/dist/GridList.d.ts.map +1 -1
  49. package/dist/Icon.d.ts +57 -0
  50. package/dist/Icon.d.ts.map +1 -0
  51. package/dist/Keyboard.d.ts +13 -0
  52. package/dist/Keyboard.d.ts.map +1 -0
  53. package/dist/Link.d.ts.map +1 -1
  54. package/dist/ListBox.d.ts +43 -1
  55. package/dist/ListBox.d.ts.map +1 -1
  56. package/dist/ListDropTargetDelegate.d.ts +38 -0
  57. package/dist/ListDropTargetDelegate.d.ts.map +1 -0
  58. package/dist/Menu.d.ts +20 -2
  59. package/dist/Menu.d.ts.map +1 -1
  60. package/dist/Meter.d.ts +2 -2
  61. package/dist/Meter.d.ts.map +1 -1
  62. package/dist/Modal.d.ts +2 -0
  63. package/dist/Modal.d.ts.map +1 -1
  64. package/dist/NumberField.d.ts +2 -0
  65. package/dist/NumberField.d.ts.map +1 -1
  66. package/dist/Popover.d.ts +4 -2
  67. package/dist/Popover.d.ts.map +1 -1
  68. package/dist/Pressable.d.ts +27 -0
  69. package/dist/Pressable.d.ts.map +1 -0
  70. package/dist/ProgressBar.d.ts +2 -2
  71. package/dist/ProgressBar.d.ts.map +1 -1
  72. package/dist/RadioGroup.d.ts.map +1 -1
  73. package/dist/RangeCalendar.d.ts +5 -0
  74. package/dist/RangeCalendar.d.ts.map +1 -1
  75. package/dist/RouterProvider.d.ts +75 -0
  76. package/dist/RouterProvider.d.ts.map +1 -0
  77. package/dist/SearchField.d.ts +2 -3
  78. package/dist/SearchField.d.ts.map +1 -1
  79. package/dist/Select.d.ts +11 -0
  80. package/dist/Select.d.ts.map +1 -1
  81. package/dist/SelectionIndicator.d.ts +30 -0
  82. package/dist/SelectionIndicator.d.ts.map +1 -0
  83. package/dist/SharedElementTransition.d.ts +39 -0
  84. package/dist/SharedElementTransition.d.ts.map +1 -0
  85. package/dist/Slider.d.ts +6 -3
  86. package/dist/Slider.d.ts.map +1 -1
  87. package/dist/Table.d.ts +39 -0
  88. package/dist/Table.d.ts.map +1 -1
  89. package/dist/Tabs.d.ts +4 -3
  90. package/dist/Tabs.d.ts.map +1 -1
  91. package/dist/TagGroup.d.ts +12 -2
  92. package/dist/TagGroup.d.ts.map +1 -1
  93. package/dist/Text.d.ts +10 -0
  94. package/dist/Text.d.ts.map +1 -0
  95. package/dist/TextField.d.ts +4 -0
  96. package/dist/TextField.d.ts.map +1 -1
  97. package/dist/TimeField.d.ts +26 -1
  98. package/dist/TimeField.d.ts.map +1 -1
  99. package/dist/Toast.d.ts.map +1 -1
  100. package/dist/ToggleButton.d.ts +30 -0
  101. package/dist/ToggleButton.d.ts.map +1 -0
  102. package/dist/ToggleButtonGroup.d.ts +33 -0
  103. package/dist/ToggleButtonGroup.d.ts.map +1 -0
  104. package/dist/Toolbar.d.ts.map +1 -1
  105. package/dist/Tooltip.d.ts +9 -0
  106. package/dist/Tooltip.d.ts.map +1 -1
  107. package/dist/Tree.d.ts +44 -2
  108. package/dist/Tree.d.ts.map +1 -1
  109. package/dist/Virtualizer.d.ts +61 -0
  110. package/dist/Virtualizer.d.ts.map +1 -0
  111. package/dist/VirtualizerLayouts.d.ts +82 -0
  112. package/dist/VirtualizerLayouts.d.ts.map +1 -0
  113. package/dist/VisuallyHidden.d.ts +3 -1
  114. package/dist/VisuallyHidden.d.ts.map +1 -1
  115. package/dist/contexts.d.ts +1 -0
  116. package/dist/contexts.d.ts.map +1 -1
  117. package/dist/index.d.ts +57 -25
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +13961 -5946
  120. package/dist/index.js.map +1 -7
  121. package/dist/index.ssr.js +9612 -2401
  122. package/dist/index.ssr.js.map +1 -7
  123. package/dist/useDragAndDrop.d.ts +93 -0
  124. package/dist/useDragAndDrop.d.ts.map +1 -0
  125. package/dist/utils.d.ts +7 -1
  126. package/dist/utils.d.ts.map +1 -1
  127. package/dist/virtualizer/Layout.d.ts +79 -0
  128. package/dist/virtualizer/Layout.d.ts.map +1 -0
  129. package/package.json +8 -6
  130. package/src/ActionBar.tsx +248 -0
  131. package/src/ActionGroup.tsx +285 -0
  132. package/src/Alert.tsx +177 -0
  133. package/src/Autocomplete.tsx +1 -1
  134. package/src/Breadcrumbs.tsx +103 -17
  135. package/src/Button.tsx +65 -21
  136. package/src/Calendar.tsx +179 -53
  137. package/src/Checkbox.tsx +1 -2
  138. package/src/Collection.tsx +341 -0
  139. package/src/Color.tsx +652 -34
  140. package/src/ColorEditor.tsx +231 -0
  141. package/src/ComboBox.tsx +315 -81
  142. package/src/ContextualHelpTrigger.tsx +183 -0
  143. package/src/DateField.tsx +93 -19
  144. package/src/DatePicker.tsx +495 -25
  145. package/src/Dialog.tsx +40 -9
  146. package/src/Disclosure.tsx +33 -27
  147. package/src/DragAndDrop.tsx +334 -0
  148. package/src/DragPreview.tsx +45 -0
  149. package/src/DropZone.tsx +213 -0
  150. package/src/FieldError.tsx +67 -0
  151. package/src/FileTrigger.tsx +83 -0
  152. package/src/Focusable.tsx +106 -0
  153. package/src/Form.tsx +85 -0
  154. package/src/GridList.tsx +379 -41
  155. package/src/Icon.tsx +154 -0
  156. package/src/Keyboard.tsx +26 -0
  157. package/src/Link.tsx +14 -1
  158. package/src/ListBox.tsx +484 -33
  159. package/src/ListDropTargetDelegate.ts +282 -0
  160. package/src/Menu.tsx +388 -35
  161. package/src/Meter.tsx +7 -3
  162. package/src/Modal.tsx +32 -4
  163. package/src/NumberField.tsx +163 -43
  164. package/src/Popover.tsx +136 -180
  165. package/src/Pressable.tsx +108 -0
  166. package/src/ProgressBar.tsx +7 -3
  167. package/src/RadioGroup.tsx +35 -25
  168. package/src/RangeCalendar.tsx +100 -68
  169. package/src/RouterProvider.tsx +240 -0
  170. package/src/SearchField.tsx +142 -34
  171. package/src/Select.tsx +221 -73
  172. package/src/SelectionIndicator.tsx +105 -0
  173. package/src/SharedElementTransition.tsx +258 -0
  174. package/src/Slider.tsx +16 -6
  175. package/src/Table.tsx +417 -57
  176. package/src/Tabs.tsx +68 -35
  177. package/src/TagGroup.tsx +121 -36
  178. package/src/Text.tsx +18 -0
  179. package/src/TextField.tsx +25 -8
  180. package/src/TimeField.tsx +101 -151
  181. package/src/Toast.tsx +108 -14
  182. package/src/ToggleButton.tsx +159 -0
  183. package/src/ToggleButtonGroup.tsx +136 -0
  184. package/src/Toolbar.tsx +14 -8
  185. package/src/Tooltip.tsx +108 -19
  186. package/src/Tree.tsx +1143 -87
  187. package/src/Virtualizer.tsx +702 -0
  188. package/src/VirtualizerLayouts.ts +265 -0
  189. package/src/VisuallyHidden.tsx +15 -21
  190. package/src/contexts.ts +1 -0
  191. package/src/index.ts +1057 -620
  192. package/src/useDragAndDrop.ts +351 -0
  193. package/src/utils.tsx +37 -3
  194. 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: Color | string;
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: local.color,
1393
+ color: resolvedColor(),
1321
1394
  'aria-label': ariaProps['aria-label'],
1322
1395
  }));
1323
1396
 
1324
1397
  // Normalize color
1325
- const color = createMemo(() => normalizeColor(local.color));
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
+ }