@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
@@ -8,25 +8,36 @@
8
8
  import {
9
9
  type JSX,
10
10
  createContext,
11
+ createEffect,
11
12
  createMemo,
12
13
  createSignal,
13
14
  splitProps,
14
15
  useContext,
15
16
  Show,
16
17
  } from 'solid-js';
18
+ import { Portal } from 'solid-js/web';
17
19
  import {
18
20
  createDatePicker,
21
+ createDateRangePicker,
22
+ createPopover,
23
+ FocusScope,
24
+ useUNSAFE_PortalContext,
19
25
  type AriaDatePickerProps,
26
+ type AriaDateRangePickerProps,
20
27
  type DatePickerState as AriaDatePickerState,
21
28
  } from '@proyecto-viviana/solidaria';
22
29
  import {
23
30
  createDateFieldState,
24
31
  createCalendarState,
32
+ createRangeCalendarState,
25
33
  type DateFieldState,
26
34
  type CalendarState,
35
+ type RangeCalendarState,
27
36
  type DateFieldStateProps,
28
37
  type CalendarDate,
29
38
  type DateValue,
39
+ type RangeCalendarStateProps,
40
+ type RangeValue,
30
41
  } from '@proyecto-viviana/solid-stately';
31
42
  import {
32
43
  type RenderChildren,
@@ -38,6 +49,8 @@ import {
38
49
  useIsHydrated,
39
50
  } from './utils';
40
51
  import { DateFieldContext } from './DateField';
52
+ import { CalendarContext } from './Calendar';
53
+ import { RangeCalendarContext } from './RangeCalendar';
41
54
 
42
55
  // ============================================
43
56
  // TYPES
@@ -56,6 +69,11 @@ export interface DatePickerRenderProps {
56
69
  isOpen: boolean;
57
70
  }
58
71
 
72
+ export interface DateRangePickerRenderProps
73
+ extends Omit<DatePickerRenderProps, 'isInvalid'> {
74
+ isInvalid: boolean;
75
+ }
76
+
59
77
  export interface DatePickerContextValue {
60
78
  fieldState: DateFieldState<DateValue>;
61
79
  calendarState: CalendarState<DateValue>;
@@ -65,9 +83,24 @@ export interface DatePickerContextValue {
65
83
  close: () => void;
66
84
  toggle: () => void;
67
85
  };
86
+ triggerRef: () => HTMLElement | null;
87
+ setTriggerRef: (element: HTMLElement | null) => void;
68
88
  pickerAria: ReturnType<typeof createDatePicker>;
69
89
  }
70
90
 
91
+ export interface DateRangePickerContextValue {
92
+ calendarState: RangeCalendarState<DateValue>;
93
+ overlayState: {
94
+ isOpen: boolean;
95
+ open: () => void;
96
+ close: () => void;
97
+ toggle: () => void;
98
+ };
99
+ triggerRef: () => HTMLElement | null;
100
+ setTriggerRef: (element: HTMLElement | null) => void;
101
+ pickerAria: ReturnType<typeof createDateRangePicker>;
102
+ }
103
+
71
104
  export interface DatePickerProps<T extends DateValue = DateValue>
72
105
  extends Omit<AriaDatePickerProps, 'id' | 'isDisabled' | 'isReadOnly' | 'isRequired'>,
73
106
  Omit<DateFieldStateProps<T>, 'locale'>,
@@ -84,6 +117,17 @@ export interface DatePickerProps<T extends DateValue = DateValue>
84
117
  shouldCloseOnSelect?: boolean;
85
118
  }
86
119
 
120
+ export interface DateRangePickerProps<T extends DateValue = DateValue>
121
+ extends Omit<AriaDateRangePickerProps, 'id' | 'isDisabled' | 'isReadOnly'>,
122
+ Omit<RangeCalendarStateProps<T>, 'locale'>,
123
+ SlotProps {
124
+ children?: JSX.Element;
125
+ class?: ClassNameOrFunction<DateRangePickerRenderProps>;
126
+ style?: StyleOrFunction<DateRangePickerRenderProps>;
127
+ locale?: string;
128
+ shouldCloseOnSelect?: boolean;
129
+ }
130
+
87
131
  export interface DatePickerButtonRenderProps {
88
132
  /** Whether the button is disabled. */
89
133
  isDisabled: boolean;
@@ -102,11 +146,16 @@ export interface DatePickerButtonProps extends SlotProps {
102
146
  isDisabled?: boolean;
103
147
  }
104
148
 
149
+ export interface DateRangePickerButtonProps extends DatePickerButtonProps {}
150
+
105
151
  // ============================================
106
152
  // CONTEXT
107
153
  // ============================================
108
154
 
109
155
  export const DatePickerContext = createContext<DatePickerContextValue | null>(null);
156
+ export const DateRangePickerContext = createContext<DateRangePickerContextValue | null>(null);
157
+ export const DatePickerStateContext = createContext<DateFieldState<DateValue> | null>(null);
158
+ export const DateRangePickerStateContext = createContext<RangeCalendarState<DateValue> | null>(null);
110
159
 
111
160
  export function useDatePickerContext(): DatePickerContextValue {
112
161
  const context = useContext(DatePickerContext);
@@ -116,6 +165,14 @@ export function useDatePickerContext(): DatePickerContextValue {
116
165
  return context;
117
166
  }
118
167
 
168
+ export function useDateRangePickerContext(): DateRangePickerContextValue {
169
+ const context = useContext(DateRangePickerContext);
170
+ if (!context) {
171
+ throw new Error('DateRangePicker components must be used within a DateRangePicker');
172
+ }
173
+ return context;
174
+ }
175
+
119
176
  // ============================================
120
177
  // DATE PICKER COMPONENT
121
178
  // ============================================
@@ -192,6 +249,7 @@ function DatePickerInner<T extends DateValue = CalendarDate>(
192
249
 
193
250
  // Create overlay trigger state
194
251
  const [isOpen, setIsOpen] = createSignal(false);
252
+ let triggerRef: HTMLElement | null = null;
195
253
 
196
254
  const overlayState = {
197
255
  get isOpen() { return isOpen(); },
@@ -229,7 +287,11 @@ function DatePickerInner<T extends DateValue = CalendarDate>(
229
287
 
230
288
  // Create date picker ARIA props
231
289
  const pickerAria = createDatePicker(
232
- rest,
290
+ () => ({
291
+ ...(rest as Record<string, unknown>),
292
+ description: stateProps.description,
293
+ errorMessage: stateProps.errorMessage,
294
+ }),
233
295
  fieldState as unknown as DateFieldState<DateValue>,
234
296
  overlayState as AriaDatePickerState,
235
297
  calendarState as unknown as CalendarState<DateValue>
@@ -240,15 +302,26 @@ function DatePickerInner<T extends DateValue = CalendarDate>(
240
302
  fieldState: fieldState as unknown as DateFieldState<DateValue>,
241
303
  calendarState: calendarState as unknown as CalendarState<DateValue>,
242
304
  overlayState,
305
+ triggerRef: () => triggerRef,
306
+ setTriggerRef: (element) => {
307
+ if (!element) return;
308
+ if (!triggerRef || !triggerRef.isConnected) {
309
+ triggerRef = element;
310
+ }
311
+ },
243
312
  pickerAria,
244
313
  };
245
314
 
246
315
  // Render props values
316
+ const isInvalid = createMemo(
317
+ () => fieldState.isInvalid() || Boolean((rest as { isInvalid?: boolean }).isInvalid)
318
+ );
319
+
247
320
  const renderValues = createMemo<DatePickerRenderProps>(() => ({
248
321
  isDisabled: fieldState.isDisabled(),
249
322
  isReadOnly: fieldState.isReadOnly(),
250
323
  isRequired: fieldState.isRequired(),
251
- isInvalid: fieldState.isInvalid(),
324
+ isInvalid: isInvalid(),
252
325
  isOpen: overlayState.isOpen,
253
326
  }));
254
327
 
@@ -263,23 +336,163 @@ function DatePickerInner<T extends DateValue = CalendarDate>(
263
336
  );
264
337
 
265
338
  return (
266
- <DatePickerContext.Provider value={contextValue}>
267
- {/* Also provide DateFieldContext so DateInput/DateSegment work inside DatePicker */}
268
- <DateFieldContext.Provider value={fieldState as unknown as DateFieldState<DateValue>}>
269
- <div
270
- {...pickerAria.groupProps}
271
- class={renderProps.class()}
272
- style={renderProps.style()}
273
- data-disabled={dataAttr(fieldState.isDisabled())}
274
- data-readonly={dataAttr(fieldState.isReadOnly())}
275
- data-required={dataAttr(fieldState.isRequired())}
276
- data-invalid={dataAttr(fieldState.isInvalid())}
277
- data-open={dataAttr(overlayState.isOpen)}
339
+ <DatePickerStateContext.Provider value={fieldState as unknown as DateFieldState<DateValue>}>
340
+ <DatePickerContext.Provider value={contextValue}>
341
+ {/* Also provide DateFieldContext so DateInput/DateSegment work inside DatePicker */}
342
+ <DateFieldContext.Provider
343
+ value={{
344
+ state: fieldState as unknown as DateFieldState<DateValue>,
345
+ aria: {
346
+ labelProps: pickerAria.labelProps,
347
+ inputProps: pickerAria.fieldProps,
348
+ descriptionProps: pickerAria.descriptionProps,
349
+ errorMessageProps: pickerAria.errorMessageProps,
350
+ },
351
+ }}
278
352
  >
279
- {props.children}
280
- </div>
281
- </DateFieldContext.Provider>
282
- </DatePickerContext.Provider>
353
+ <CalendarContext.Provider value={calendarState as unknown as CalendarState<DateValue>}>
354
+ <div
355
+ {...pickerAria.groupProps}
356
+ class={renderProps.class()}
357
+ style={renderProps.style()}
358
+ data-disabled={dataAttr(fieldState.isDisabled())}
359
+ data-readonly={dataAttr(fieldState.isReadOnly())}
360
+ data-required={dataAttr(fieldState.isRequired())}
361
+ data-invalid={dataAttr(isInvalid())}
362
+ data-open={dataAttr(overlayState.isOpen)}
363
+ >
364
+ {props.children}
365
+ </div>
366
+ </CalendarContext.Provider>
367
+ </DateFieldContext.Provider>
368
+ </DatePickerContext.Provider>
369
+ </DatePickerStateContext.Provider>
370
+ );
371
+ }
372
+
373
+ export function DateRangePicker<T extends DateValue = CalendarDate>(
374
+ props: DateRangePickerProps<T>
375
+ ): JSX.Element {
376
+ const isHydrated = useIsHydrated();
377
+ return (
378
+ <Show
379
+ when={isHydrated()}
380
+ fallback={<div class="solidaria-DateRangePicker solidaria-DateRangePicker--placeholder" aria-hidden="true" />}
381
+ >
382
+ <DateRangePickerInner {...props} />
383
+ </Show>
384
+ );
385
+ }
386
+
387
+ function DateRangePickerInner<T extends DateValue = CalendarDate>(
388
+ props: DateRangePickerProps<T>
389
+ ): JSX.Element {
390
+ const [local, stateProps, rest] = splitProps(
391
+ props,
392
+ ['children', 'class', 'style', 'slot', 'shouldCloseOnSelect'],
393
+ [
394
+ 'value',
395
+ 'defaultValue',
396
+ 'onChange',
397
+ 'minValue',
398
+ 'maxValue',
399
+ 'isDisabled',
400
+ 'isReadOnly',
401
+ 'focusedValue',
402
+ 'defaultFocusedValue',
403
+ 'onFocusChange',
404
+ 'locale',
405
+ 'isDateUnavailable',
406
+ 'visibleMonths',
407
+ 'isDateDisabled',
408
+ 'validationState',
409
+ 'allowsNonContiguousRanges',
410
+ 'firstDayOfWeek',
411
+ ]
412
+ );
413
+
414
+ const [isOpen, setIsOpen] = createSignal(false);
415
+ let triggerRef: HTMLElement | null = null;
416
+ const overlayState = {
417
+ get isOpen() { return isOpen(); },
418
+ open: () => setIsOpen(true),
419
+ close: () => setIsOpen(false),
420
+ toggle: () => setIsOpen((prev) => !prev),
421
+ };
422
+
423
+ const calendarState = createRangeCalendarState({
424
+ ...stateProps,
425
+ onChange: (value) => {
426
+ stateProps.onChange?.(value);
427
+ if (local.shouldCloseOnSelect !== false && value?.start && value?.end) {
428
+ overlayState.close();
429
+ }
430
+ },
431
+ });
432
+ const pickerAria = createDateRangePicker(
433
+ () => ({
434
+ ...(rest as Record<string, unknown>),
435
+ description: (props as { description?: string }).description,
436
+ errorMessage: (props as { errorMessage?: string }).errorMessage,
437
+ }),
438
+ calendarState as unknown as RangeCalendarState<DateValue>,
439
+ overlayState as AriaDatePickerState
440
+ );
441
+
442
+ const isInvalid = createMemo(
443
+ () => Boolean((rest as { isInvalid?: boolean }).isInvalid) || calendarState.validationState() === 'invalid'
444
+ );
445
+ const isRequired = createMemo(
446
+ () => Boolean((rest as { isRequired?: boolean }).isRequired)
447
+ );
448
+
449
+ const contextValue: DateRangePickerContextValue = {
450
+ calendarState: calendarState as unknown as RangeCalendarState<DateValue>,
451
+ overlayState,
452
+ triggerRef: () => triggerRef,
453
+ setTriggerRef: (element) => {
454
+ if (!element) return;
455
+ if (!triggerRef || !triggerRef.isConnected) triggerRef = element;
456
+ },
457
+ pickerAria,
458
+ };
459
+
460
+ const renderValues = createMemo<DateRangePickerRenderProps>(() => ({
461
+ isDisabled: calendarState.isDisabled(),
462
+ isReadOnly: calendarState.isReadOnly(),
463
+ isRequired: isRequired(),
464
+ isInvalid: isInvalid(),
465
+ isOpen: overlayState.isOpen,
466
+ }));
467
+
468
+ const renderProps = useRenderProps(
469
+ {
470
+ class: local.class,
471
+ style: local.style,
472
+ defaultClassName: 'solidaria-DateRangePicker',
473
+ },
474
+ renderValues
475
+ );
476
+
477
+ return (
478
+ <DateRangePickerStateContext.Provider value={calendarState as unknown as RangeCalendarState<DateValue>}>
479
+ <DateRangePickerContext.Provider value={contextValue}>
480
+ <RangeCalendarContext.Provider value={calendarState as unknown as RangeCalendarState<DateValue>}>
481
+ <div
482
+ {...pickerAria.groupProps}
483
+ class={renderProps.class()}
484
+ style={renderProps.style()}
485
+ data-disabled={dataAttr(calendarState.isDisabled())}
486
+ data-readonly={dataAttr(calendarState.isReadOnly())}
487
+ data-required={dataAttr(isRequired())}
488
+ data-invalid={dataAttr(isInvalid())}
489
+ data-open={dataAttr(overlayState.isOpen)}
490
+ >
491
+ {props.children}
492
+ </div>
493
+ </RangeCalendarContext.Provider>
494
+ </DateRangePickerContext.Provider>
495
+ </DateRangePickerStateContext.Provider>
283
496
  );
284
497
  }
285
498
 
@@ -292,6 +505,7 @@ function DatePickerInner<T extends DateValue = CalendarDate>(
292
505
  */
293
506
  export function DatePickerButton(props: DatePickerButtonProps): JSX.Element {
294
507
  const context = useDatePickerContext();
508
+ let buttonRef: HTMLButtonElement | undefined;
295
509
 
296
510
  // Render props values
297
511
  const renderValues = createMemo<DatePickerButtonRenderProps>(() => ({
@@ -320,6 +534,10 @@ export function DatePickerButton(props: DatePickerButtonProps): JSX.Element {
320
534
 
321
535
  return (
322
536
  <button
537
+ ref={(el) => {
538
+ buttonRef = el;
539
+ context.setTriggerRef(el);
540
+ }}
323
541
  {...context.pickerAria.buttonProps}
324
542
  class={renderProps.class()}
325
543
  style={renderProps.style()}
@@ -332,6 +550,46 @@ export function DatePickerButton(props: DatePickerButtonProps): JSX.Element {
332
550
  );
333
551
  }
334
552
 
553
+ export function DateRangePickerButton(props: DateRangePickerButtonProps): JSX.Element {
554
+ const context = useDateRangePickerContext();
555
+
556
+ const renderValues = createMemo<DatePickerButtonRenderProps>(() => ({
557
+ isDisabled: context.calendarState.isDisabled() || (props.isDisabled ?? false),
558
+ isOpen: context.overlayState.isOpen,
559
+ }));
560
+
561
+ const renderProps = useRenderProps(
562
+ {
563
+ children: props.children,
564
+ class: props.class,
565
+ style: props.style,
566
+ defaultClassName: 'solidaria-DateRangePickerButton',
567
+ },
568
+ renderValues
569
+ );
570
+
571
+ const getChildren = () => {
572
+ if (typeof props.children === 'function') {
573
+ return renderProps.renderChildren();
574
+ }
575
+ return props.children ?? '📅';
576
+ };
577
+
578
+ return (
579
+ <button
580
+ ref={(el) => context.setTriggerRef(el)}
581
+ {...context.pickerAria.buttonProps}
582
+ class={renderProps.class()}
583
+ style={renderProps.style()}
584
+ disabled={context.calendarState.isDisabled() || props.isDisabled}
585
+ data-disabled={dataAttr(context.calendarState.isDisabled() || props.isDisabled)}
586
+ data-open={dataAttr(context.overlayState.isOpen)}
587
+ >
588
+ {getChildren()}
589
+ </button>
590
+ );
591
+ }
592
+
335
593
  // ============================================
336
594
  // DATE PICKER CONTENT COMPONENT
337
595
  // ============================================
@@ -345,21 +603,233 @@ export interface DatePickerContentProps extends SlotProps {
345
603
  style?: JSX.CSSProperties;
346
604
  }
347
605
 
606
+ export interface DateRangePickerContentProps extends DatePickerContentProps {}
607
+
608
+ export interface DatePickerLabelProps {
609
+ children?: JSX.Element;
610
+ class?: string;
611
+ }
612
+
613
+ export function DatePickerLabel(props: DatePickerLabelProps): JSX.Element {
614
+ const context = useDatePickerContext();
615
+ return (
616
+ <span {...context.pickerAria.labelProps} class={props.class}>
617
+ {props.children}
618
+ </span>
619
+ );
620
+ }
621
+
622
+ export interface DatePickerDescriptionProps {
623
+ children?: JSX.Element;
624
+ class?: string;
625
+ }
626
+
627
+ export function DatePickerDescription(props: DatePickerDescriptionProps): JSX.Element {
628
+ const context = useDatePickerContext();
629
+ return (
630
+ <p {...context.pickerAria.descriptionProps} class={props.class}>
631
+ {props.children}
632
+ </p>
633
+ );
634
+ }
635
+
636
+ export interface DatePickerErrorMessageProps {
637
+ children?: JSX.Element;
638
+ class?: string;
639
+ }
640
+
641
+ export function DatePickerErrorMessage(props: DatePickerErrorMessageProps): JSX.Element {
642
+ const context = useDatePickerContext();
643
+ return (
644
+ <p {...context.pickerAria.errorMessageProps} class={props.class}>
645
+ {props.children}
646
+ </p>
647
+ );
648
+ }
649
+
650
+ export interface DateRangePickerLabelProps {
651
+ children?: JSX.Element;
652
+ class?: string;
653
+ }
654
+
655
+ export function DateRangePickerLabel(props: DateRangePickerLabelProps): JSX.Element {
656
+ const context = useDateRangePickerContext();
657
+ return (
658
+ <span {...context.pickerAria.labelProps} class={props.class}>
659
+ {props.children}
660
+ </span>
661
+ );
662
+ }
663
+
664
+ export interface DateRangePickerDescriptionProps {
665
+ children?: JSX.Element;
666
+ class?: string;
667
+ }
668
+
669
+ export function DateRangePickerDescription(props: DateRangePickerDescriptionProps): JSX.Element {
670
+ const context = useDateRangePickerContext();
671
+ return (
672
+ <p {...context.pickerAria.descriptionProps} class={props.class}>
673
+ {props.children}
674
+ </p>
675
+ );
676
+ }
677
+
678
+ export interface DateRangePickerErrorMessageProps {
679
+ children?: JSX.Element;
680
+ class?: string;
681
+ }
682
+
683
+ export function DateRangePickerErrorMessage(props: DateRangePickerErrorMessageProps): JSX.Element {
684
+ const context = useDateRangePickerContext();
685
+ return (
686
+ <p {...context.pickerAria.errorMessageProps} class={props.class}>
687
+ {props.children}
688
+ </p>
689
+ );
690
+ }
691
+
348
692
  /**
349
693
  * The content area of the date picker (typically contains a Calendar).
350
694
  */
351
695
  export function DatePickerContent(props: DatePickerContentProps): JSX.Element {
352
696
  const context = useDatePickerContext();
697
+ const portalContext = useUNSAFE_PortalContext();
698
+ let contentRef: HTMLDivElement | undefined;
699
+ const portalContainer = () => portalContext.getContainer?.() ?? undefined;
700
+
701
+ const popoverAria = createPopover(
702
+ {
703
+ triggerRef: context.triggerRef,
704
+ popoverRef: () => contentRef ?? null,
705
+ placement: 'bottom start',
706
+ offset: 8,
707
+ isNonModal: false,
708
+ isKeyboardDismissDisabled: false,
709
+ },
710
+ {
711
+ isOpen: () => context.overlayState.isOpen,
712
+ open: context.overlayState.open,
713
+ close: context.overlayState.close,
714
+ toggle: context.overlayState.toggle,
715
+ }
716
+ );
717
+
718
+ const cleanPopoverProps = () => {
719
+ const { style: _style, ref: _ref, ...rest } = popoverAria.popoverProps as Record<string, unknown>;
720
+ return rest;
721
+ };
722
+
723
+ const mergedStyle = (): JSX.CSSProperties => {
724
+ const popoverStyle = (popoverAria.popoverProps as Record<string, unknown>).style as JSX.CSSProperties | undefined;
725
+ return {
726
+ ...(popoverStyle ?? {}),
727
+ ...(props.style ?? {}),
728
+ };
729
+ };
730
+
731
+ createEffect(() => {
732
+ if (!context.overlayState.isOpen || !contentRef) return;
733
+ if (document.activeElement !== contentRef) {
734
+ contentRef.focus();
735
+ }
736
+ });
737
+
738
+ return (
739
+ <Show when={context.overlayState.isOpen}>
740
+ <Portal mount={portalContainer()}>
741
+ <FocusScope contain restoreFocus autoFocus>
742
+ <div
743
+ ref={contentRef}
744
+ {...cleanPopoverProps()}
745
+ {...context.pickerAria.dialogProps}
746
+ tabIndex={-1}
747
+ class={props.class ?? 'solidaria-DatePickerContent'}
748
+ style={mergedStyle()}
749
+ data-placement={popoverAria.placement() ?? undefined}
750
+ onKeyDown={(event) => {
751
+ if (event.key === 'Escape') {
752
+ event.preventDefault();
753
+ event.stopPropagation();
754
+ context.overlayState.close();
755
+ }
756
+ }}
757
+ >
758
+ {props.children}
759
+ </div>
760
+ </FocusScope>
761
+ </Portal>
762
+ </Show>
763
+ );
764
+ }
765
+
766
+ export function DateRangePickerContent(props: DateRangePickerContentProps): JSX.Element {
767
+ const context = useDateRangePickerContext();
768
+ const portalContext = useUNSAFE_PortalContext();
769
+ let contentRef: HTMLDivElement | undefined;
770
+ const portalContainer = () => portalContext.getContainer?.() ?? undefined;
771
+
772
+ const popoverAria = createPopover(
773
+ {
774
+ triggerRef: context.triggerRef,
775
+ popoverRef: () => contentRef ?? null,
776
+ placement: 'bottom start',
777
+ offset: 8,
778
+ isNonModal: false,
779
+ isKeyboardDismissDisabled: false,
780
+ },
781
+ {
782
+ isOpen: () => context.overlayState.isOpen,
783
+ open: context.overlayState.open,
784
+ close: context.overlayState.close,
785
+ toggle: context.overlayState.toggle,
786
+ }
787
+ );
788
+
789
+ const cleanPopoverProps = () => {
790
+ const { style: _style, ref: _ref, ...rest } = popoverAria.popoverProps as Record<string, unknown>;
791
+ return rest;
792
+ };
793
+
794
+ const mergedStyle = (): JSX.CSSProperties => {
795
+ const popoverStyle = (popoverAria.popoverProps as Record<string, unknown>).style as JSX.CSSProperties | undefined;
796
+ return {
797
+ ...(popoverStyle ?? {}),
798
+ ...(props.style ?? {}),
799
+ };
800
+ };
801
+
802
+ createEffect(() => {
803
+ if (!context.overlayState.isOpen || !contentRef) return;
804
+ if (document.activeElement !== contentRef) {
805
+ contentRef.focus();
806
+ }
807
+ });
353
808
 
354
809
  return (
355
810
  <Show when={context.overlayState.isOpen}>
356
- <div
357
- {...context.pickerAria.dialogProps}
358
- class={props.class ?? 'solidaria-DatePickerContent'}
359
- style={props.style}
360
- >
361
- {props.children}
362
- </div>
811
+ <Portal mount={portalContainer()}>
812
+ <FocusScope contain restoreFocus autoFocus>
813
+ <div
814
+ ref={contentRef}
815
+ {...cleanPopoverProps()}
816
+ {...context.pickerAria.dialogProps}
817
+ tabIndex={-1}
818
+ class={props.class ?? 'solidaria-DateRangePickerContent'}
819
+ style={mergedStyle()}
820
+ data-placement={popoverAria.placement() ?? undefined}
821
+ onKeyDown={(event) => {
822
+ if (event.key === 'Escape') {
823
+ event.preventDefault();
824
+ event.stopPropagation();
825
+ context.overlayState.close();
826
+ }
827
+ }}
828
+ >
829
+ {props.children}
830
+ </div>
831
+ </FocusScope>
832
+ </Portal>
363
833
  </Show>
364
834
  );
365
835
  }