@proyecto-viviana/solidaria-components 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -272
  3. package/dist/ActionBar.d.ts +79 -0
  4. package/dist/ActionBar.d.ts.map +1 -0
  5. package/dist/ActionGroup.d.ts +74 -0
  6. package/dist/ActionGroup.d.ts.map +1 -0
  7. package/dist/Alert.d.ts +70 -0
  8. package/dist/Alert.d.ts.map +1 -0
  9. package/dist/Autocomplete.d.ts +5 -5
  10. package/dist/Autocomplete.d.ts.map +1 -1
  11. package/dist/Breadcrumbs.d.ts +27 -8
  12. package/dist/Breadcrumbs.d.ts.map +1 -1
  13. package/dist/Button.d.ts +28 -5
  14. package/dist/Button.d.ts.map +1 -1
  15. package/dist/Calendar.d.ts +51 -7
  16. package/dist/Calendar.d.ts.map +1 -1
  17. package/dist/Checkbox.d.ts +33 -8
  18. package/dist/Checkbox.d.ts.map +1 -1
  19. package/dist/Collection.d.ts +130 -0
  20. package/dist/Collection.d.ts.map +1 -0
  21. package/dist/Color.d.ts +210 -9
  22. package/dist/Color.d.ts.map +1 -1
  23. package/dist/ColorEditor.d.ts +42 -0
  24. package/dist/ColorEditor.d.ts.map +1 -0
  25. package/dist/ComboBox.d.ts +146 -16
  26. package/dist/ComboBox.d.ts.map +1 -1
  27. package/dist/ContextualHelpTrigger.d.ts +40 -0
  28. package/dist/ContextualHelpTrigger.d.ts.map +1 -0
  29. package/dist/DateField.d.ts +35 -8
  30. package/dist/DateField.d.ts.map +1 -1
  31. package/dist/DatePicker.d.ts +101 -5
  32. package/dist/DatePicker.d.ts.map +1 -1
  33. package/dist/DateRangePickerContext.d.ts +30 -0
  34. package/dist/DateRangePickerContext.d.ts.map +1 -0
  35. package/dist/Dialog.d.ts +5 -5
  36. package/dist/Dialog.d.ts.map +1 -1
  37. package/dist/Disclosure.d.ts +25 -5
  38. package/dist/Disclosure.d.ts.map +1 -1
  39. package/dist/DragAndDrop.d.ts +80 -0
  40. package/dist/DragAndDrop.d.ts.map +1 -0
  41. package/dist/DragPreview.d.ts +14 -0
  42. package/dist/DragPreview.d.ts.map +1 -0
  43. package/dist/DropZone.d.ts +27 -0
  44. package/dist/DropZone.d.ts.map +1 -0
  45. package/dist/FieldError.d.ts +27 -0
  46. package/dist/FieldError.d.ts.map +1 -0
  47. package/dist/FileTrigger.d.ts +26 -0
  48. package/dist/FileTrigger.d.ts.map +1 -0
  49. package/dist/Focusable.d.ts +27 -0
  50. package/dist/Focusable.d.ts.map +1 -0
  51. package/dist/Form.d.ts +41 -0
  52. package/dist/Form.d.ts.map +1 -0
  53. package/dist/GridList.d.ts +69 -10
  54. package/dist/GridList.d.ts.map +1 -1
  55. package/dist/HiddenDateInput.d.ts +26 -0
  56. package/dist/HiddenDateInput.d.ts.map +1 -0
  57. package/dist/HiddenTimeInput.d.ts +25 -0
  58. package/dist/HiddenTimeInput.d.ts.map +1 -0
  59. package/dist/Icon.d.ts +57 -0
  60. package/dist/Icon.d.ts.map +1 -0
  61. package/dist/Keyboard.d.ts +13 -0
  62. package/dist/Keyboard.d.ts.map +1 -0
  63. package/dist/Landmark.d.ts +3 -3
  64. package/dist/Landmark.d.ts.map +1 -1
  65. package/dist/Link.d.ts +10 -4
  66. package/dist/Link.d.ts.map +1 -1
  67. package/dist/ListBox.d.ts +73 -11
  68. package/dist/ListBox.d.ts.map +1 -1
  69. package/dist/ListDropTargetDelegate.d.ts +38 -0
  70. package/dist/ListDropTargetDelegate.d.ts.map +1 -0
  71. package/dist/Menu.d.ts +79 -10
  72. package/dist/Menu.d.ts.map +1 -1
  73. package/dist/Meter.d.ts +4 -4
  74. package/dist/Meter.d.ts.map +1 -1
  75. package/dist/Modal.d.ts +6 -4
  76. package/dist/Modal.d.ts.map +1 -1
  77. package/dist/NumberField.d.ts +10 -12
  78. package/dist/NumberField.d.ts.map +1 -1
  79. package/dist/Popover.d.ts +32 -7
  80. package/dist/Popover.d.ts.map +1 -1
  81. package/dist/Pressable.d.ts +27 -0
  82. package/dist/Pressable.d.ts.map +1 -0
  83. package/dist/ProgressBar.d.ts +6 -4
  84. package/dist/ProgressBar.d.ts.map +1 -1
  85. package/dist/RadioGroup.d.ts +43 -9
  86. package/dist/RadioGroup.d.ts.map +1 -1
  87. package/dist/RangeCalendar.d.ts +39 -7
  88. package/dist/RangeCalendar.d.ts.map +1 -1
  89. package/dist/RouterProvider.d.ts +75 -0
  90. package/dist/RouterProvider.d.ts.map +1 -0
  91. package/dist/SearchField.d.ts +23 -21
  92. package/dist/SearchField.d.ts.map +1 -1
  93. package/dist/Select.d.ts +48 -7
  94. package/dist/Select.d.ts.map +1 -1
  95. package/dist/SelectionIndicator.d.ts +30 -0
  96. package/dist/SelectionIndicator.d.ts.map +1 -0
  97. package/dist/Separator.d.ts +9 -3
  98. package/dist/Separator.d.ts.map +1 -1
  99. package/dist/SharedElementTransition.d.ts +41 -0
  100. package/dist/SharedElementTransition.d.ts.map +1 -0
  101. package/dist/Slider.d.ts +15 -8
  102. package/dist/Slider.d.ts.map +1 -1
  103. package/dist/StepList.d.ts +90 -0
  104. package/dist/StepList.d.ts.map +1 -0
  105. package/dist/Switch.d.ts +11 -5
  106. package/dist/Switch.d.ts.map +1 -1
  107. package/dist/Table.d.ts +222 -19
  108. package/dist/Table.d.ts.map +1 -1
  109. package/dist/Tabs.d.ts +47 -10
  110. package/dist/Tabs.d.ts.map +1 -1
  111. package/dist/TagGroup.d.ts +22 -10
  112. package/dist/TagGroup.d.ts.map +1 -1
  113. package/dist/Text.d.ts +10 -0
  114. package/dist/Text.d.ts.map +1 -0
  115. package/dist/TextField.d.ts +19 -11
  116. package/dist/TextField.d.ts.map +1 -1
  117. package/dist/TimeField.d.ts +32 -7
  118. package/dist/TimeField.d.ts.map +1 -1
  119. package/dist/Toast.d.ts +29 -14
  120. package/dist/Toast.d.ts.map +1 -1
  121. package/dist/ToggleButton.d.ts +36 -0
  122. package/dist/ToggleButton.d.ts.map +1 -0
  123. package/dist/ToggleButtonGroup.d.ts +33 -0
  124. package/dist/ToggleButtonGroup.d.ts.map +1 -0
  125. package/dist/Toolbar.d.ts +7 -3
  126. package/dist/Toolbar.d.ts.map +1 -1
  127. package/dist/Tooltip.d.ts +58 -7
  128. package/dist/Tooltip.d.ts.map +1 -1
  129. package/dist/Tree.d.ts +102 -11
  130. package/dist/Tree.d.ts.map +1 -1
  131. package/dist/Virtualizer.d.ts +61 -0
  132. package/dist/Virtualizer.d.ts.map +1 -0
  133. package/dist/VirtualizerLayouts.d.ts +82 -0
  134. package/dist/VirtualizerLayouts.d.ts.map +1 -0
  135. package/dist/VisuallyHidden.d.ts +4 -2
  136. package/dist/VisuallyHidden.d.ts.map +1 -1
  137. package/dist/contexts.d.ts +6 -1
  138. package/dist/contexts.d.ts.map +1 -1
  139. package/dist/index.d.ts +73 -39
  140. package/dist/index.d.ts.map +1 -1
  141. package/dist/index.js +23342 -10644
  142. package/dist/index.js.map +1 -7
  143. package/dist/index.jsx +18110 -0
  144. package/dist/index.jsx.map +1 -0
  145. package/dist/useDragAndDrop.d.ts +93 -0
  146. package/dist/useDragAndDrop.d.ts.map +1 -0
  147. package/dist/utils.d.ts +8 -2
  148. package/dist/utils.d.ts.map +1 -1
  149. package/dist/virtualizer/Layout.d.ts +79 -0
  150. package/dist/virtualizer/Layout.d.ts.map +1 -0
  151. package/package.json +33 -32
  152. package/src/ActionBar.tsx +251 -0
  153. package/src/ActionGroup.tsx +277 -0
  154. package/src/Alert.tsx +152 -0
  155. package/src/Autocomplete.tsx +39 -44
  156. package/src/Breadcrumbs.tsx +227 -72
  157. package/src/Button.tsx +315 -74
  158. package/src/Calendar.tsx +347 -141
  159. package/src/Checkbox.tsx +414 -123
  160. package/src/Collection.tsx +350 -0
  161. package/src/Color.tsx +1325 -284
  162. package/src/ColorEditor.tsx +213 -0
  163. package/src/ComboBox.tsx +644 -245
  164. package/src/ContextualHelpTrigger.tsx +195 -0
  165. package/src/DateField.tsx +274 -106
  166. package/src/DatePicker.tsx +892 -111
  167. package/src/DateRangePickerContext.tsx +44 -0
  168. package/src/Dialog.tsx +173 -104
  169. package/src/Disclosure.tsx +158 -105
  170. package/src/DragAndDrop.tsx +340 -0
  171. package/src/DragPreview.tsx +47 -0
  172. package/src/DropZone.tsx +233 -0
  173. package/src/FieldError.tsx +89 -0
  174. package/src/FileTrigger.tsx +83 -0
  175. package/src/Focusable.tsx +103 -0
  176. package/src/Form.tsx +140 -0
  177. package/src/GridList.tsx +542 -128
  178. package/src/HiddenDateInput.tsx +153 -0
  179. package/src/HiddenTimeInput.tsx +133 -0
  180. package/src/Icon.tsx +133 -0
  181. package/src/Keyboard.tsx +26 -0
  182. package/src/Landmark.tsx +37 -63
  183. package/src/Link.tsx +132 -69
  184. package/src/ListBox.tsx +656 -106
  185. package/src/ListDropTargetDelegate.ts +283 -0
  186. package/src/Menu.tsx +1234 -132
  187. package/src/Meter.tsx +44 -58
  188. package/src/Modal.tsx +262 -166
  189. package/src/NumberField.tsx +267 -151
  190. package/src/Popover.tsx +452 -343
  191. package/src/Pressable.tsx +108 -0
  192. package/src/ProgressBar.tsx +54 -59
  193. package/src/RadioGroup.tsx +533 -121
  194. package/src/RangeCalendar.tsx +249 -150
  195. package/src/RouterProvider.tsx +223 -0
  196. package/src/SearchField.tsx +460 -133
  197. package/src/Select.tsx +804 -233
  198. package/src/SelectionIndicator.tsx +108 -0
  199. package/src/Separator.tsx +47 -49
  200. package/src/SharedElementTransition.tsx +264 -0
  201. package/src/Slider.tsx +148 -98
  202. package/src/StepList.tsx +272 -0
  203. package/src/Switch.tsx +93 -46
  204. package/src/Table.tsx +1551 -225
  205. package/src/Tabs.tsx +377 -123
  206. package/src/TagGroup.tsx +233 -135
  207. package/src/Text.tsx +18 -0
  208. package/src/TextField.tsx +413 -86
  209. package/src/TimeField.tsx +232 -222
  210. package/src/Toast.tsx +306 -160
  211. package/src/ToggleButton.tsx +169 -0
  212. package/src/ToggleButtonGroup.tsx +141 -0
  213. package/src/Toolbar.tsx +61 -70
  214. package/src/Tooltip.tsx +473 -116
  215. package/src/Tree.tsx +1514 -175
  216. package/src/Virtualizer.tsx +730 -0
  217. package/src/VirtualizerLayouts.ts +280 -0
  218. package/src/VisuallyHidden.tsx +32 -38
  219. package/src/contexts.ts +29 -36
  220. package/src/index.ts +972 -620
  221. package/src/useDragAndDrop.ts +367 -0
  222. package/src/utils.tsx +69 -50
  223. package/src/virtualizer/Layout.ts +192 -0
  224. package/dist/index.ssr.js +0 -9785
  225. package/dist/index.ssr.js.map +0 -7
package/src/Calendar.tsx CHANGED
@@ -7,6 +7,7 @@
7
7
 
8
8
  import {
9
9
  type JSX,
10
+ type Accessor,
10
11
  createContext,
11
12
  createMemo,
12
13
  createSignal,
@@ -15,22 +16,25 @@ import {
15
16
  For,
16
17
  Index,
17
18
  Show,
18
- } from 'solid-js';
19
+ } from "solid-js";
19
20
 
20
21
  import {
21
22
  createCalendar,
22
23
  createCalendarGrid,
23
24
  createCalendarCell,
25
+ createHover,
24
26
  type AriaCalendarProps,
25
27
  type AriaCalendarGridProps,
26
- } from '@proyecto-viviana/solidaria';
28
+ } from "@proyecto-viviana/solidaria";
27
29
  import {
28
30
  createCalendarState,
29
31
  type CalendarState,
30
32
  type CalendarStateProps,
31
33
  type CalendarDate,
32
34
  type DateValue,
33
- } from '@proyecto-viviana/solid-stately';
35
+ endOfMonth,
36
+ isSameMonth,
37
+ } from "@proyecto-viviana/solid-stately";
34
38
  import {
35
39
  type RenderChildren,
36
40
  type ClassNameOrFunction,
@@ -39,22 +43,36 @@ import {
39
43
  useRenderProps,
40
44
  dataAttr,
41
45
  useIsHydrated,
42
- } from './utils';
46
+ } from "./utils";
47
+ import { VisuallyHidden } from "./VisuallyHidden";
43
48
 
44
- // ============================================
45
- // TYPES
46
- // ============================================
49
+ type RefLike<T> = ((el: T) => void) | { current?: T | null } | undefined;
50
+
51
+ function assignRef<T>(ref: RefLike<T>, el: T): void {
52
+ if (!ref) {
53
+ return;
54
+ }
55
+
56
+ if (typeof ref === "function") {
57
+ ref(el);
58
+ } else {
59
+ ref.current = el;
60
+ }
61
+ }
47
62
 
48
63
  export interface CalendarRenderProps {
49
64
  /** Whether the calendar is disabled. */
50
65
  isDisabled: boolean;
51
66
  /** Whether the calendar is read-only. */
52
67
  isReadOnly: boolean;
68
+ /** Whether the current selection is invalid. */
69
+ isInvalid: boolean;
53
70
  }
54
71
 
55
72
  export interface CalendarProps<T extends DateValue = DateValue>
56
- extends Omit<AriaCalendarProps, 'id' | 'isDisabled' | 'isReadOnly'>,
57
- Omit<CalendarStateProps<T>, 'locale'>,
73
+ extends
74
+ Omit<AriaCalendarProps, "id" | "isDisabled" | "isReadOnly">,
75
+ Omit<CalendarStateProps<T>, "locale">,
58
76
  SlotProps {
59
77
  /** The children of the component. */
60
78
  children?: JSX.Element;
@@ -64,6 +82,8 @@ export interface CalendarProps<T extends DateValue = DateValue>
64
82
  style?: StyleOrFunction<CalendarRenderProps>;
65
83
  /** The locale to use for formatting. */
66
84
  locale?: string;
85
+ /** Ref for the calendar root element. */
86
+ ref?: RefLike<HTMLDivElement>;
67
87
  }
68
88
 
69
89
  export interface CalendarGridRenderProps {
@@ -71,13 +91,16 @@ export interface CalendarGridRenderProps {
71
91
  isDisabled: boolean;
72
92
  }
73
93
 
74
- export interface CalendarGridProps extends Omit<AriaCalendarGridProps, 'startDate' | 'endDate'>, SlotProps {
94
+ export interface CalendarGridProps
95
+ extends Omit<AriaCalendarGridProps, "startDate" | "endDate">, SlotProps {
75
96
  /** The children of the component (render function receiving weeks). */
76
97
  children?: (date: CalendarDate) => JSX.Element;
77
98
  /** The CSS className for the element. */
78
99
  class?: ClassNameOrFunction<CalendarGridRenderProps>;
79
100
  /** The inline style for the element. */
80
101
  style?: StyleOrFunction<CalendarGridRenderProps>;
102
+ /** Class name for weekday header cells. */
103
+ headerCellClass?: string;
81
104
  /** Number of weeks to offset from the start. */
82
105
  offset?: { months?: number };
83
106
  }
@@ -87,16 +110,30 @@ export interface CalendarCellRenderProps {
87
110
  isSelected: boolean;
88
111
  /** Whether the cell is focused. */
89
112
  isFocused: boolean;
113
+ /** Whether the cell should display a keyboard focus ring. */
114
+ isFocusVisible: boolean;
90
115
  /** Whether the cell is disabled. */
91
116
  isDisabled: boolean;
92
117
  /** Whether the cell is unavailable. */
93
118
  isUnavailable: boolean;
119
+ /** Whether the cell is part of an invalid selection. */
120
+ isInvalid: boolean;
94
121
  /** Whether the cell is outside the visible month. */
95
122
  isOutsideMonth: boolean;
96
123
  /** Whether the cell represents today. */
97
124
  isToday: boolean;
98
125
  /** Whether the cell is pressed. */
99
126
  isPressed: boolean;
127
+ /** Whether the cell is hovered. */
128
+ isHovered: boolean;
129
+ /** Whether the cell is the first day slot in its row. */
130
+ isFirstChild: boolean;
131
+ /** Whether the cell is the last day slot in its row. */
132
+ isLastChild: boolean;
133
+ /** Whether the cell is in the first rendered week row. */
134
+ isFirstWeek: boolean;
135
+ /** Whether the cell is in the last rendered week row. */
136
+ isLastWeek: boolean;
100
137
  /** The formatted date string. */
101
138
  formattedDate: string;
102
139
  }
@@ -110,6 +147,10 @@ export interface CalendarCellProps extends SlotProps {
110
147
  class?: ClassNameOrFunction<CalendarCellRenderProps>;
111
148
  /** The inline style for the element. */
112
149
  style?: StyleOrFunction<CalendarCellRenderProps>;
150
+ /** Class name for the table cell wrapper. */
151
+ cellClass?: ClassNameOrFunction<CalendarCellRenderProps>;
152
+ /** Inline style for the table cell wrapper. */
153
+ cellStyle?: StyleOrFunction<CalendarCellRenderProps>;
113
154
  }
114
155
 
115
156
  export interface CalendarHeaderCellProps extends SlotProps {
@@ -121,24 +162,34 @@ export interface CalendarHeaderCellProps extends SlotProps {
121
162
  style?: JSX.CSSProperties;
122
163
  }
123
164
 
124
- // ============================================
125
- // CONTEXT
126
- // ============================================
165
+ export interface CalendarGridHeaderProps extends SlotProps {
166
+ children?: JSX.Element;
167
+ class?: string;
168
+ style?: JSX.CSSProperties;
169
+ }
170
+
171
+ export interface CalendarGridBodyProps extends SlotProps {
172
+ children?: JSX.Element;
173
+ class?: string;
174
+ style?: JSX.CSSProperties;
175
+ }
127
176
 
128
177
  export const CalendarContext = createContext<CalendarState<DateValue> | null>(null);
178
+ const CalendarGridMonthContext = createContext<Accessor<CalendarDate> | null>(null);
179
+ const CalendarGridCellPositionContext = createContext<Accessor<{
180
+ weekIndex: number;
181
+ dayIndex: number;
182
+ lastWeekIndex: number;
183
+ }> | null>(null);
129
184
 
130
185
  export function useCalendarContext(): CalendarState<DateValue> {
131
186
  const context = useContext(CalendarContext);
132
187
  if (!context) {
133
- throw new Error('Calendar components must be used within a Calendar');
188
+ throw new Error("Calendar components must be used within a Calendar");
134
189
  }
135
190
  return context;
136
191
  }
137
192
 
138
- // ============================================
139
- // CALENDAR COMPONENT
140
- // ============================================
141
-
142
193
  /**
143
194
  * A calendar displays a grid of days in a month and allows users to select a single date.
144
195
  *
@@ -156,95 +207,181 @@ export function useCalendarContext(): CalendarState<DateValue> {
156
207
  * </Calendar>
157
208
  * ```
158
209
  */
159
- export function Calendar<T extends DateValue = CalendarDate>(
160
- props: CalendarProps<T>
161
- ): JSX.Element {
210
+ export function Calendar<T extends DateValue = CalendarDate>(props: CalendarProps<T>): JSX.Element {
211
+ const inheritedState = useContext(CalendarContext);
212
+
162
213
  // Use hydration-safe pattern for client-only rendering
163
214
  const isHydrated = useIsHydrated();
164
215
 
165
216
  return (
166
217
  <Show
167
218
  when={isHydrated()}
168
- fallback={<div class="solidaria-Calendar solidaria-Calendar--placeholder" aria-hidden="true" />}
219
+ fallback={
220
+ <div class="solidaria-Calendar solidaria-Calendar--placeholder" aria-hidden="true" />
221
+ }
169
222
  >
170
- <CalendarInner {...props} />
223
+ <Show when={inheritedState} fallback={<CalendarInner {...props} />}>
224
+ <CalendarWithState state={inheritedState as CalendarState<DateValue>} {...props} />
225
+ </Show>
171
226
  </Show>
172
227
  );
173
228
  }
174
229
 
230
+ function CalendarWithState<T extends DateValue = CalendarDate>(
231
+ props: CalendarProps<T> & { state: CalendarState<DateValue> },
232
+ ): JSX.Element {
233
+ const [local, stateProps, rest] = splitProps(
234
+ props,
235
+ ["children", "class", "style", "slot", "state", "ref"],
236
+ [
237
+ "value",
238
+ "defaultValue",
239
+ "onChange",
240
+ "minValue",
241
+ "maxValue",
242
+ "isDisabled",
243
+ "isReadOnly",
244
+ "autoFocus",
245
+ "focusedValue",
246
+ "defaultFocusedValue",
247
+ "onFocusChange",
248
+ "locale",
249
+ "createCalendar",
250
+ "isDateUnavailable",
251
+ "visibleMonths",
252
+ "pageBehavior",
253
+ "selectionAlignment",
254
+ "isDateDisabled",
255
+ "validationState",
256
+ "errorMessage",
257
+ "firstDayOfWeek",
258
+ ],
259
+ );
260
+
261
+ const state = () => props.state;
262
+ const calendarAria = createCalendar(
263
+ () => ({
264
+ ...rest,
265
+ errorMessage: stateProps.errorMessage,
266
+ }),
267
+ state(),
268
+ );
269
+
270
+ const renderValues = createMemo<CalendarRenderProps>(() => ({
271
+ isDisabled: state().isDisabled(),
272
+ isReadOnly: state().isReadOnly(),
273
+ isInvalid: state().isValueInvalid(),
274
+ }));
275
+
276
+ const renderProps = useRenderProps(
277
+ {
278
+ class: local.class,
279
+ style: local.style,
280
+ defaultClassName: "solidaria-Calendar",
281
+ },
282
+ renderValues,
283
+ );
284
+
285
+ return (
286
+ <div
287
+ {...calendarAria.calendarProps}
288
+ ref={(el) => assignRef(local.ref, el)}
289
+ class={renderProps.class()}
290
+ style={renderProps.style()}
291
+ data-disabled={dataAttr(state().isDisabled())}
292
+ data-readonly={dataAttr(state().isReadOnly())}
293
+ data-invalid={dataAttr(state().isValueInvalid())}
294
+ >
295
+ <VisuallyHidden>
296
+ <h2>{String(calendarAria.calendarProps["aria-label"] ?? "")}</h2>
297
+ </VisuallyHidden>
298
+ {props.children}
299
+ </div>
300
+ );
301
+ }
302
+
175
303
  /**
176
304
  * Internal Calendar component that renders after client mount.
177
305
  */
178
- function CalendarInner<T extends DateValue = CalendarDate>(
179
- props: CalendarProps<T>
180
- ): JSX.Element {
306
+ function CalendarInner<T extends DateValue = CalendarDate>(props: CalendarProps<T>): JSX.Element {
181
307
  const [local, stateProps, rest] = splitProps(
182
308
  props,
183
- ['children', 'class', 'style', 'slot'],
309
+ ["children", "class", "style", "slot", "ref"],
184
310
  [
185
- 'value',
186
- 'defaultValue',
187
- 'onChange',
188
- 'minValue',
189
- 'maxValue',
190
- 'isDisabled',
191
- 'isReadOnly',
192
- 'autoFocus',
193
- 'focusedValue',
194
- 'defaultFocusedValue',
195
- 'onFocusChange',
196
- 'locale',
197
- 'isDateUnavailable',
198
- 'visibleMonths',
199
- 'isDateDisabled',
200
- 'validationState',
201
- 'errorMessage',
202
- 'firstDayOfWeek',
203
- ]
311
+ "value",
312
+ "defaultValue",
313
+ "onChange",
314
+ "minValue",
315
+ "maxValue",
316
+ "isDisabled",
317
+ "isReadOnly",
318
+ "autoFocus",
319
+ "focusedValue",
320
+ "defaultFocusedValue",
321
+ "onFocusChange",
322
+ "locale",
323
+ "createCalendar",
324
+ "isDateUnavailable",
325
+ "visibleMonths",
326
+ "pageBehavior",
327
+ "selectionAlignment",
328
+ "isDateDisabled",
329
+ "validationState",
330
+ "errorMessage",
331
+ "firstDayOfWeek",
332
+ ],
204
333
  );
205
334
 
206
335
  // Create calendar state
207
336
  const state = createCalendarState(stateProps);
208
337
 
209
338
  // Create calendar ARIA props
210
- const calendarAria = createCalendar(rest, state as unknown as CalendarState<DateValue>);
339
+ const calendarAria = createCalendar(
340
+ () => ({
341
+ ...rest,
342
+ errorMessage: stateProps.errorMessage,
343
+ }),
344
+ state as unknown as CalendarState<DateValue>,
345
+ );
211
346
 
212
- // Render props values
213
347
  const renderValues = createMemo<CalendarRenderProps>(() => ({
214
348
  isDisabled: state.isDisabled(),
215
349
  isReadOnly: state.isReadOnly(),
350
+ isInvalid: state.isValueInvalid(),
216
351
  }));
217
352
 
218
- // Resolve render props
219
353
  const renderProps = useRenderProps(
220
354
  {
221
355
  class: local.class,
222
356
  style: local.style,
223
- defaultClassName: 'solidaria-Calendar',
357
+ defaultClassName: "solidaria-Calendar",
224
358
  },
225
- renderValues
359
+ renderValues,
226
360
  );
227
361
 
228
362
  return (
229
363
  <CalendarContext.Provider value={state as unknown as CalendarState<DateValue>}>
230
364
  <div
231
365
  {...calendarAria.calendarProps}
366
+ ref={(el) => assignRef(local.ref, el)}
232
367
  class={renderProps.class()}
233
368
  style={renderProps.style()}
234
369
  data-disabled={dataAttr(state.isDisabled())}
235
370
  data-readonly={dataAttr(state.isReadOnly())}
371
+ data-invalid={dataAttr(state.isValueInvalid())}
236
372
  >
373
+ <VisuallyHidden>
374
+ <h2>{String(calendarAria.calendarProps["aria-label"] ?? "")}</h2>
375
+ </VisuallyHidden>
237
376
  {props.children}
238
377
  </div>
239
378
  </CalendarContext.Provider>
240
379
  );
241
380
  }
242
381
 
243
- // ============================================
244
- // CALENDAR HEADING COMPONENT
245
- // ============================================
246
-
247
382
  export interface CalendarHeadingProps extends SlotProps {
383
+ /** The children of the component. */
384
+ children?: RenderChildren<{ title: string }>;
248
385
  /** The CSS className for the element. */
249
386
  class?: string;
250
387
  /** The inline style for the element. */
@@ -256,25 +393,29 @@ export interface CalendarHeadingProps extends SlotProps {
256
393
  */
257
394
  export function CalendarHeading(props: CalendarHeadingProps): JSX.Element {
258
395
  const state = useCalendarContext();
396
+ const renderValues = createMemo(() => ({
397
+ title: state.title(),
398
+ }));
399
+ const renderProps = useRenderProps(
400
+ {
401
+ children: props.children,
402
+ class: props.class,
403
+ style: props.style,
404
+ defaultClassName: "solidaria-CalendarHeading",
405
+ },
406
+ renderValues,
407
+ );
259
408
 
260
409
  return (
261
- <h2
262
- class={props.class ?? 'solidaria-CalendarHeading'}
263
- style={props.style}
264
- aria-live="polite"
265
- >
266
- {state.title()}
410
+ <h2 class={renderProps.class()} style={renderProps.style()} aria-live="polite">
411
+ {typeof props.children === "function" ? renderProps.renderChildren() : state.title()}
267
412
  </h2>
268
413
  );
269
414
  }
270
415
 
271
- // ============================================
272
- // CALENDAR BUTTON COMPONENT
273
- // ============================================
274
-
275
416
  export interface CalendarButtonProps extends SlotProps {
276
417
  /** The slot for this button (previous or next). */
277
- slot?: 'previous' | 'next';
418
+ slot?: "previous" | "next";
278
419
  /** The children of the component. */
279
420
  children?: JSX.Element;
280
421
  /** The CSS className for the element. */
@@ -293,7 +434,7 @@ export function CalendarButton(props: CalendarButtonProps): JSX.Element {
293
434
  const calendarAria = createCalendar({}, state);
294
435
 
295
436
  const buttonProps = createMemo(() => {
296
- if (props.slot === 'previous') {
437
+ if (props.slot === "previous") {
297
438
  return calendarAria.prevButtonProps;
298
439
  }
299
440
  return calendarAria.nextButtonProps;
@@ -302,7 +443,7 @@ export function CalendarButton(props: CalendarButtonProps): JSX.Element {
302
443
  return (
303
444
  <button
304
445
  {...buttonProps()}
305
- class={props.class ?? 'solidaria-CalendarButton'}
446
+ class={props.class ?? "solidaria-CalendarButton"}
306
447
  style={props.style}
307
448
  disabled={props.isDisabled || state.isDisabled()}
308
449
  >
@@ -311,161 +452,226 @@ export function CalendarButton(props: CalendarButtonProps): JSX.Element {
311
452
  );
312
453
  }
313
454
 
314
- // ============================================
315
- // CALENDAR GRID COMPONENT
316
- // ============================================
317
-
318
455
  /**
319
456
  * Displays a grid of calendar cells.
320
457
  */
321
458
  export function CalendarGrid(props: CalendarGridProps): JSX.Element {
322
459
  const state = useCalendarContext();
323
460
  const [gridRef, setGridRef] = createSignal<HTMLTableElement | null>(null);
461
+ const startDate = createMemo(() => {
462
+ const offsetMonths = props.offset?.months ?? 0;
463
+ const baseStart = state.visibleRange().start;
464
+ return offsetMonths ? baseStart.add({ months: offsetMonths }) : baseStart;
465
+ });
324
466
 
325
- // Create grid ARIA props
326
467
  const gridAria = createCalendarGrid(
327
468
  {
469
+ startDate: startDate(),
470
+ endDate: endOfMonth(startDate()),
328
471
  weekdayStyle: props.weekdayStyle,
329
472
  },
330
473
  state,
331
- gridRef
474
+ gridRef,
332
475
  );
333
476
 
334
- // Render props values
335
477
  const renderValues = createMemo<CalendarGridRenderProps>(() => ({
336
478
  isDisabled: state.isDisabled(),
337
479
  }));
338
480
 
339
- // Resolve render props
340
481
  const renderProps = useRenderProps(
341
482
  {
342
483
  class: props.class,
343
484
  style: props.style,
344
- defaultClassName: 'solidaria-CalendarGrid',
485
+ defaultClassName: "solidaria-CalendarGrid",
345
486
  },
346
- renderValues
487
+ renderValues,
347
488
  );
348
489
 
349
490
  // Memoize ALL dates for the grid at once to avoid reactive loops.
350
491
  // This breaks the cycle where accessing visibleRange() inside For loop
351
492
  // would cause infinite re-renders.
352
493
  const allDates = createMemo(() => {
353
- const numWeeks = state.getWeeksInMonth();
494
+ const monthStart = startDate();
495
+ const numWeeks = state.getWeeksInMonth(monthStart);
354
496
  const weekDates: (CalendarDate | null)[][] = [];
355
497
 
356
498
  for (let weekIndex = 0; weekIndex < numWeeks; weekIndex++) {
357
- weekDates.push(state.getDatesInWeek(weekIndex));
499
+ weekDates.push(state.getDatesInWeek(weekIndex, monthStart));
358
500
  }
359
501
 
360
502
  return weekDates;
361
503
  });
362
504
 
363
505
  return (
364
- <table
365
- ref={setGridRef}
366
- {...gridAria.gridProps}
367
- class={renderProps.class()}
368
- style={renderProps.style()}
369
- >
370
- <thead {...gridAria.headerProps}>
371
- <tr>
372
- <For each={gridAria.weekDays}>
373
- {(day) => (
374
- <th scope="col" class="solidaria-CalendarHeaderCell">
375
- {day}
376
- </th>
506
+ <CalendarGridMonthContext.Provider value={startDate}>
507
+ <table
508
+ ref={setGridRef}
509
+ {...gridAria.gridProps}
510
+ class={renderProps.class()}
511
+ style={renderProps.style()}
512
+ >
513
+ <thead {...gridAria.headerProps}>
514
+ <tr>
515
+ <For each={gridAria.weekDays}>
516
+ {(day) => (
517
+ <th scope="col" class={props.headerCellClass ?? "solidaria-CalendarHeaderCell"}>
518
+ {day}
519
+ </th>
520
+ )}
521
+ </For>
522
+ </tr>
523
+ </thead>
524
+ <tbody>
525
+ <Index each={allDates()}>
526
+ {(weekDates, weekIndex) => (
527
+ <tr>
528
+ <Index each={weekDates()}>
529
+ {(date, dayIndex) => (
530
+ <Show when={date()} fallback={<td />}>
531
+ <CalendarGridCellPositionContext.Provider
532
+ value={() => ({
533
+ weekIndex,
534
+ dayIndex,
535
+ lastWeekIndex: allDates().length - 1,
536
+ })}
537
+ >
538
+ {props.children?.(date()!)}
539
+ </CalendarGridCellPositionContext.Provider>
540
+ </Show>
541
+ )}
542
+ </Index>
543
+ </tr>
377
544
  )}
378
- </For>
379
- </tr>
380
- </thead>
381
- <tbody>
382
- <Index each={allDates()}>
383
- {(weekDates) => (
384
- <tr>
385
- <Index each={weekDates()}>
386
- {(date) => (
387
- <Show when={date()}>
388
- <td role="gridcell">
389
- {props.children?.(date()!)}
390
- </td>
391
- </Show>
392
- )}
393
- </Index>
394
- </tr>
395
- )}
396
- </Index>
397
- </tbody>
398
- </table>
545
+ </Index>
546
+ </tbody>
547
+ </table>
548
+ </CalendarGridMonthContext.Provider>
399
549
  );
400
550
  }
401
551
 
402
- // ============================================
403
- // CALENDAR CELL COMPONENT
404
- // ============================================
552
+ export function CalendarGridHeader(props: CalendarGridHeaderProps): JSX.Element {
553
+ return (
554
+ <thead class={props.class ?? "solidaria-CalendarGridHeader"} style={props.style}>
555
+ {props.children}
556
+ </thead>
557
+ );
558
+ }
559
+
560
+ export function CalendarGridBody(props: CalendarGridBodyProps): JSX.Element {
561
+ return (
562
+ <tbody class={props.class ?? "solidaria-CalendarGridBody"} style={props.style}>
563
+ {props.children}
564
+ </tbody>
565
+ );
566
+ }
567
+
568
+ export function CalendarHeaderCell(props: CalendarHeaderCellProps): JSX.Element {
569
+ return (
570
+ <th scope="col" class={props.class ?? "solidaria-CalendarHeaderCell"} style={props.style}>
571
+ {props.children}
572
+ </th>
573
+ );
574
+ }
405
575
 
406
576
  /**
407
577
  * A cell in the calendar grid representing a single day.
408
578
  */
409
579
  export function CalendarCell(props: CalendarCellProps): JSX.Element {
410
580
  const state = useCalendarContext();
581
+ const currentMonthStart = useContext(CalendarGridMonthContext);
582
+ const cellPosition = useContext(CalendarGridCellPositionContext);
411
583
  const [cellRef, setCellRef] = createSignal<HTMLDivElement | null>(null);
584
+ const isOutsideMonth = createMemo(
585
+ () => currentMonthStart != null && !isSameMonth(currentMonthStart(), props.date),
586
+ );
587
+ const position = createMemo(
588
+ () =>
589
+ cellPosition?.() ?? {
590
+ weekIndex: 0,
591
+ dayIndex: 0,
592
+ lastWeekIndex: 0,
593
+ },
594
+ );
412
595
 
413
- // Create cell ARIA props
414
596
  const cellAria = createCalendarCell(
415
- { date: props.date },
597
+ () => ({
598
+ date: props.date,
599
+ isOutsideMonth: isOutsideMonth(),
600
+ }),
416
601
  state,
417
- cellRef
602
+ cellRef,
418
603
  );
604
+ const { hoverProps, isHovered } = createHover(() => ({
605
+ isDisabled: cellAria.isDisabled || cellAria.isUnavailable,
606
+ }));
419
607
 
420
- // Render props values
421
608
  const renderValues = createMemo<CalendarCellRenderProps>(() => ({
422
609
  isSelected: cellAria.isSelected,
423
610
  isFocused: cellAria.isFocused,
611
+ isFocusVisible: cellAria.isFocusVisible,
424
612
  isDisabled: cellAria.isDisabled,
425
613
  isUnavailable: cellAria.isUnavailable,
614
+ isInvalid: cellAria.isInvalid,
426
615
  isOutsideMonth: cellAria.isOutsideMonth,
427
616
  isToday: cellAria.isToday,
428
617
  isPressed: cellAria.isPressed,
618
+ isHovered: isHovered(),
619
+ isFirstChild: position().dayIndex === 0,
620
+ isLastChild: position().dayIndex === 6,
621
+ isFirstWeek: position().weekIndex === 0,
622
+ isLastWeek: position().weekIndex === position().lastWeekIndex,
429
623
  formattedDate: cellAria.formattedDate,
430
624
  }));
431
625
 
432
- // Resolve render props
433
626
  const renderProps = useRenderProps(
434
627
  {
435
628
  children: props.children,
436
629
  class: props.class,
437
630
  style: props.style,
438
- defaultClassName: 'solidaria-CalendarCell',
631
+ defaultClassName: "solidaria-CalendarCell",
632
+ },
633
+ renderValues,
634
+ );
635
+ const cellRenderProps = useRenderProps(
636
+ {
637
+ class: props.cellClass,
638
+ style: props.cellStyle,
639
+ defaultClassName: "solidaria-CalendarCellWrapper",
439
640
  },
440
- renderValues
641
+ renderValues,
441
642
  );
442
643
 
443
644
  // Determine children content - avoid Show for SSR hydration compatibility
444
645
  const getChildren = () => {
445
- if (typeof props.children === 'function') {
646
+ if (typeof props.children === "function") {
446
647
  return renderProps.renderChildren();
447
648
  }
448
649
  return cellAria.formattedDate;
449
650
  };
450
651
 
451
652
  return (
452
- <div
453
- ref={setCellRef}
454
- {...cellAria.buttonProps}
455
- class={renderProps.class()}
456
- style={renderProps.style()}
457
- data-selected={dataAttr(cellAria.isSelected)}
458
- data-focused={dataAttr(cellAria.isFocused)}
459
- data-disabled={dataAttr(cellAria.isDisabled)}
460
- data-unavailable={dataAttr(cellAria.isUnavailable)}
461
- data-outside-month={dataAttr(cellAria.isOutsideMonth)}
462
- data-today={dataAttr(cellAria.isToday)}
463
- data-pressed={dataAttr(cellAria.isPressed)}
464
- >
465
- {getChildren()}
466
- </div>
653
+ <td {...cellAria.cellProps} class={cellRenderProps.class()} style={cellRenderProps.style()}>
654
+ <div
655
+ ref={setCellRef}
656
+ {...cellAria.buttonProps}
657
+ {...hoverProps}
658
+ class={renderProps.class()}
659
+ style={renderProps.style()}
660
+ data-selected={dataAttr(cellAria.isSelected)}
661
+ data-focused={dataAttr(cellAria.isFocused)}
662
+ data-focus-visible={dataAttr(cellAria.isFocusVisible)}
663
+ data-disabled={dataAttr(cellAria.isDisabled)}
664
+ data-unavailable={dataAttr(cellAria.isUnavailable)}
665
+ data-invalid={dataAttr(cellAria.isInvalid)}
666
+ data-outside-month={dataAttr(cellAria.isOutsideMonth)}
667
+ data-today={dataAttr(cellAria.isToday)}
668
+ data-pressed={dataAttr(cellAria.isPressed)}
669
+ data-hovered={dataAttr(isHovered())}
670
+ >
671
+ {getChildren()}
672
+ </div>
673
+ </td>
467
674
  );
468
675
  }
469
676
 
470
- // Re-export types
471
677
  export type { CalendarState, CalendarDate, DateValue };