@proyecto-viviana/solidaria 0.2.2 → 0.2.3

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 (210) hide show
  1. package/dist/autocomplete/createAutocomplete.d.ts +2 -2
  2. package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
  3. package/dist/index.js +233 -234
  4. package/dist/index.js.map +2 -2
  5. package/dist/index.ssr.js +233 -234
  6. package/dist/index.ssr.js.map +2 -2
  7. package/dist/interactions/PressEvent.d.ts +13 -10
  8. package/dist/interactions/PressEvent.d.ts.map +1 -1
  9. package/dist/interactions/createPress.d.ts.map +1 -1
  10. package/dist/interactions/index.d.ts +1 -1
  11. package/dist/interactions/index.d.ts.map +1 -1
  12. package/dist/select/createHiddenSelect.d.ts.map +1 -1
  13. package/dist/toolbar/createToolbar.d.ts.map +1 -1
  14. package/dist/tooltip/createTooltipTrigger.d.ts.map +1 -1
  15. package/package.json +9 -7
  16. package/src/autocomplete/createAutocomplete.ts +341 -0
  17. package/src/autocomplete/index.ts +9 -0
  18. package/src/breadcrumbs/createBreadcrumbs.ts +196 -0
  19. package/src/breadcrumbs/index.ts +8 -0
  20. package/src/button/createButton.ts +142 -0
  21. package/src/button/createToggleButton.ts +101 -0
  22. package/src/button/index.ts +4 -0
  23. package/src/button/types.ts +78 -0
  24. package/src/calendar/createCalendar.ts +138 -0
  25. package/src/calendar/createCalendarCell.ts +187 -0
  26. package/src/calendar/createCalendarGrid.ts +140 -0
  27. package/src/calendar/createRangeCalendar.ts +136 -0
  28. package/src/calendar/createRangeCalendarCell.ts +186 -0
  29. package/src/calendar/index.ts +34 -0
  30. package/src/checkbox/createCheckbox.ts +135 -0
  31. package/src/checkbox/createCheckboxGroup.ts +137 -0
  32. package/src/checkbox/createCheckboxGroupItem.ts +117 -0
  33. package/src/checkbox/createCheckboxGroupState.ts +193 -0
  34. package/src/checkbox/index.ts +13 -0
  35. package/src/color/createColorArea.ts +314 -0
  36. package/src/color/createColorField.ts +137 -0
  37. package/src/color/createColorSlider.ts +197 -0
  38. package/src/color/createColorSwatch.ts +40 -0
  39. package/src/color/createColorWheel.ts +208 -0
  40. package/src/color/index.ts +24 -0
  41. package/src/color/types.ts +116 -0
  42. package/src/combobox/createComboBox.ts +647 -0
  43. package/src/combobox/index.ts +6 -0
  44. package/src/combobox/intl/en-US.json +7 -0
  45. package/src/combobox/intl/es-ES.json +7 -0
  46. package/src/combobox/intl/index.ts +23 -0
  47. package/src/datepicker/createDateField.ts +154 -0
  48. package/src/datepicker/createDatePicker.ts +206 -0
  49. package/src/datepicker/createDateSegment.ts +229 -0
  50. package/src/datepicker/createTimeField.ts +154 -0
  51. package/src/datepicker/index.ts +28 -0
  52. package/src/dialog/createDialog.ts +120 -0
  53. package/src/dialog/index.ts +2 -0
  54. package/src/dialog/types.ts +19 -0
  55. package/src/disclosure/createDisclosure.ts +131 -0
  56. package/src/disclosure/createDisclosureGroup.ts +62 -0
  57. package/src/disclosure/index.ts +11 -0
  58. package/src/dnd/createDrag.ts +209 -0
  59. package/src/dnd/createDraggableCollection.ts +63 -0
  60. package/src/dnd/createDraggableItem.ts +243 -0
  61. package/src/dnd/createDrop.ts +321 -0
  62. package/src/dnd/createDroppableCollection.ts +293 -0
  63. package/src/dnd/createDroppableItem.ts +213 -0
  64. package/src/dnd/index.ts +47 -0
  65. package/src/dnd/types.ts +89 -0
  66. package/src/dnd/utils.ts +294 -0
  67. package/src/focus/FocusScope.tsx +408 -0
  68. package/src/focus/createAutoFocus.ts +321 -0
  69. package/src/focus/createFocusRestore.ts +313 -0
  70. package/src/focus/createVirtualFocus.ts +396 -0
  71. package/src/focus/index.ts +35 -0
  72. package/src/form/createFormReset.ts +51 -0
  73. package/src/form/createFormValidation.ts +224 -0
  74. package/src/form/index.ts +11 -0
  75. package/src/grid/GridKeyboardDelegate.ts +429 -0
  76. package/src/grid/createGrid.ts +261 -0
  77. package/src/grid/createGridCell.ts +182 -0
  78. package/src/grid/createGridRow.ts +153 -0
  79. package/src/grid/index.ts +18 -0
  80. package/src/grid/types.ts +133 -0
  81. package/src/gridlist/createGridList.ts +185 -0
  82. package/src/gridlist/createGridListItem.ts +180 -0
  83. package/src/gridlist/createGridListSelectionCheckbox.ts +59 -0
  84. package/src/gridlist/index.ts +16 -0
  85. package/src/gridlist/types.ts +81 -0
  86. package/src/i18n/NumberFormatter.ts +266 -0
  87. package/src/i18n/createCollator.ts +79 -0
  88. package/src/i18n/createDateFormatter.ts +83 -0
  89. package/src/i18n/createFilter.ts +131 -0
  90. package/src/i18n/createNumberFormatter.ts +52 -0
  91. package/src/i18n/createStringFormatter.ts +87 -0
  92. package/src/i18n/index.ts +40 -0
  93. package/src/i18n/locale.tsx +188 -0
  94. package/src/i18n/utils.ts +99 -0
  95. package/src/index.ts +670 -0
  96. package/src/interactions/FocusableProvider.tsx +44 -0
  97. package/src/interactions/PressEvent.ts +126 -0
  98. package/src/interactions/createFocus.ts +163 -0
  99. package/src/interactions/createFocusRing.ts +89 -0
  100. package/src/interactions/createFocusWithin.ts +206 -0
  101. package/src/interactions/createFocusable.ts +168 -0
  102. package/src/interactions/createHover.ts +254 -0
  103. package/src/interactions/createInteractionModality.ts +424 -0
  104. package/src/interactions/createKeyboard.ts +82 -0
  105. package/src/interactions/createLongPress.ts +174 -0
  106. package/src/interactions/createMove.ts +289 -0
  107. package/src/interactions/createPress.ts +834 -0
  108. package/src/interactions/index.ts +78 -0
  109. package/src/label/createField.ts +145 -0
  110. package/src/label/createLabel.ts +117 -0
  111. package/src/label/createLabels.ts +50 -0
  112. package/src/label/index.ts +19 -0
  113. package/src/landmark/createLandmark.ts +377 -0
  114. package/src/landmark/index.ts +8 -0
  115. package/src/link/createLink.ts +182 -0
  116. package/src/link/index.ts +1 -0
  117. package/src/listbox/createListBox.ts +269 -0
  118. package/src/listbox/createOption.ts +151 -0
  119. package/src/listbox/index.ts +12 -0
  120. package/src/live-announcer/announce.ts +322 -0
  121. package/src/live-announcer/index.ts +9 -0
  122. package/src/menu/createMenu.ts +396 -0
  123. package/src/menu/createMenuItem.ts +149 -0
  124. package/src/menu/createMenuTrigger.ts +88 -0
  125. package/src/menu/index.ts +18 -0
  126. package/src/meter/createMeter.ts +75 -0
  127. package/src/meter/index.ts +1 -0
  128. package/src/numberfield/createNumberField.ts +268 -0
  129. package/src/numberfield/index.ts +5 -0
  130. package/src/overlays/ariaHideOutside.ts +219 -0
  131. package/src/overlays/createInteractOutside.ts +149 -0
  132. package/src/overlays/createModal.tsx +202 -0
  133. package/src/overlays/createOverlay.ts +155 -0
  134. package/src/overlays/createOverlayTrigger.ts +85 -0
  135. package/src/overlays/createPreventScroll.ts +266 -0
  136. package/src/overlays/index.ts +44 -0
  137. package/src/popover/calculatePosition.ts +766 -0
  138. package/src/popover/createOverlayPosition.ts +356 -0
  139. package/src/popover/createPopover.ts +170 -0
  140. package/src/popover/index.ts +24 -0
  141. package/src/progress/createProgressBar.ts +128 -0
  142. package/src/progress/index.ts +5 -0
  143. package/src/radio/createRadio.ts +287 -0
  144. package/src/radio/createRadioGroup.ts +189 -0
  145. package/src/radio/createRadioGroupState.ts +201 -0
  146. package/src/radio/index.ts +23 -0
  147. package/src/searchfield/createSearchField.ts +186 -0
  148. package/src/searchfield/index.ts +2 -0
  149. package/src/select/createHiddenSelect.tsx +236 -0
  150. package/src/select/createSelect.ts +395 -0
  151. package/src/select/index.ts +14 -0
  152. package/src/selection/createTypeSelect.ts +201 -0
  153. package/src/selection/index.ts +6 -0
  154. package/src/separator/createSeparator.ts +82 -0
  155. package/src/separator/index.ts +6 -0
  156. package/src/slider/createSlider.ts +349 -0
  157. package/src/slider/index.ts +2 -0
  158. package/src/ssr/index.tsx +370 -0
  159. package/src/switch/createSwitch.ts +70 -0
  160. package/src/switch/index.ts +1 -0
  161. package/src/table/createTable.ts +526 -0
  162. package/src/table/createTableCell.ts +147 -0
  163. package/src/table/createTableColumnHeader.ts +115 -0
  164. package/src/table/createTableHeaderRow.ts +40 -0
  165. package/src/table/createTableRow.ts +155 -0
  166. package/src/table/createTableRowGroup.ts +32 -0
  167. package/src/table/createTableSelectAllCheckbox.ts +73 -0
  168. package/src/table/createTableSelectionCheckbox.ts +59 -0
  169. package/src/table/index.ts +30 -0
  170. package/src/table/types.ts +165 -0
  171. package/src/tabs/createTabs.ts +472 -0
  172. package/src/tabs/index.ts +14 -0
  173. package/src/tag/createTag.ts +194 -0
  174. package/src/tag/createTagGroup.ts +154 -0
  175. package/src/tag/index.ts +12 -0
  176. package/src/textfield/createTextField.ts +198 -0
  177. package/src/textfield/index.ts +5 -0
  178. package/src/toast/createToast.ts +118 -0
  179. package/src/toast/createToastRegion.ts +100 -0
  180. package/src/toast/index.ts +11 -0
  181. package/src/toggle/createToggle.ts +223 -0
  182. package/src/toggle/createToggleState.ts +94 -0
  183. package/src/toggle/index.ts +7 -0
  184. package/src/toolbar/createToolbar.ts +369 -0
  185. package/src/toolbar/index.ts +6 -0
  186. package/src/tooltip/createTooltip.ts +79 -0
  187. package/src/tooltip/createTooltipTrigger.ts +222 -0
  188. package/src/tooltip/index.ts +6 -0
  189. package/src/tree/createTree.ts +246 -0
  190. package/src/tree/createTreeItem.ts +233 -0
  191. package/src/tree/createTreeSelectionCheckbox.ts +68 -0
  192. package/src/tree/index.ts +16 -0
  193. package/src/tree/types.ts +87 -0
  194. package/src/utils/createDescription.ts +137 -0
  195. package/src/utils/dom.ts +327 -0
  196. package/src/utils/env.ts +54 -0
  197. package/src/utils/events.ts +106 -0
  198. package/src/utils/filterDOMProps.ts +116 -0
  199. package/src/utils/focus.ts +151 -0
  200. package/src/utils/geometry.ts +115 -0
  201. package/src/utils/globalListeners.ts +142 -0
  202. package/src/utils/index.ts +80 -0
  203. package/src/utils/mergeProps.ts +52 -0
  204. package/src/utils/platform.ts +52 -0
  205. package/src/utils/reactivity.ts +36 -0
  206. package/src/utils/textSelection.ts +114 -0
  207. package/src/visually-hidden/createVisuallyHidden.ts +124 -0
  208. package/src/visually-hidden/index.ts +6 -0
  209. package/dist/index.jsx +0 -15845
  210. package/dist/index.jsx.map +0 -7
@@ -0,0 +1,187 @@
1
+ /**
2
+ * createCalendarCell hook for Solidaria
3
+ *
4
+ * Provides the behavior and accessibility implementation for a calendar cell.
5
+ * Based on @react-aria/calendar useCalendarCell
6
+ */
7
+
8
+ import { createSignal, createMemo, onMount } from 'solid-js';
9
+ import { access, type MaybeAccessor } from '../utils/reactivity';
10
+ import type { CalendarState, CalendarDate, DateValue } from '@proyecto-viviana/solid-stately';
11
+ import { isToday as isTodayUtil, DateFormatter, getLocalTimeZone } from '@internationalized/date';
12
+
13
+ // ============================================
14
+ // TYPES
15
+ // ============================================
16
+
17
+ export interface AriaCalendarCellProps {
18
+ /** The date represented by the cell. */
19
+ date: DateValue;
20
+ /** Whether the cell is disabled. */
21
+ isDisabled?: boolean;
22
+ }
23
+
24
+ export interface CalendarCellAria {
25
+ /** Props for the cell element (td or gridcell). */
26
+ cellProps: Record<string, unknown>;
27
+ /** Props for the button inside the cell. */
28
+ buttonProps: Record<string, unknown>;
29
+ /** Whether the cell is selected. */
30
+ isSelected: boolean;
31
+ /** Whether the cell is focused. */
32
+ isFocused: boolean;
33
+ /** Whether the cell is disabled. */
34
+ isDisabled: boolean;
35
+ /** Whether the cell is unavailable (e.g., booked date). */
36
+ isUnavailable: boolean;
37
+ /** Whether the cell is outside the visible month. */
38
+ isOutsideMonth: boolean;
39
+ /** Whether the cell represents today. */
40
+ isToday: boolean;
41
+ /** Whether the cell is pressed. */
42
+ isPressed: boolean;
43
+ /** The formatted date string. */
44
+ formattedDate: string;
45
+ }
46
+
47
+ // ============================================
48
+ // IMPLEMENTATION
49
+ // ============================================
50
+
51
+ /**
52
+ * Provides the behavior and accessibility implementation for a calendar cell.
53
+ */
54
+ export function createCalendarCell<T extends CalendarState>(
55
+ props: MaybeAccessor<AriaCalendarCellProps>,
56
+ state: T,
57
+ ref?: () => HTMLElement | null
58
+ ): CalendarCellAria {
59
+ const getProps = () => access(props);
60
+ const [isPressed, setIsPressed] = createSignal(false);
61
+ const timeZone = getLocalTimeZone();
62
+
63
+ // Get the date from props
64
+ const date = createMemo(() => getProps().date as CalendarDate);
65
+
66
+ // Check states
67
+ const isSelected = createMemo(() => state.isSelected(date()));
68
+ const isFocused = createMemo(() => state.isCellFocused(date()));
69
+ const isDisabled = createMemo(() => {
70
+ return getProps().isDisabled || state.isCellDisabled(date());
71
+ });
72
+ const isUnavailable = createMemo(() => state.isCellUnavailable(date()));
73
+ const isOutsideMonth = createMemo(() => state.isOutsideVisibleRange(date()));
74
+ const isToday = createMemo(() => isTodayUtil(date(), timeZone));
75
+
76
+ // Format the date for display
77
+ const formattedDate = createMemo(() => {
78
+ return date().day.toString();
79
+ });
80
+
81
+ // Handle pointer down - this is where selection happens
82
+ // Using pointerdown instead of click ensures selection happens immediately
83
+ // before focus changes can interfere with the event
84
+ const handlePointerDown = (e: PointerEvent) => {
85
+ if (!isDisabled() && !isUnavailable()) {
86
+ setIsPressed(true);
87
+ // Select the date on pointer down for immediate response
88
+ // This matches React Aria's behavior of using onPressStart
89
+ state.selectDate(date());
90
+ // Prevent default to avoid double-triggering with onClick
91
+ e.preventDefault();
92
+ }
93
+ };
94
+
95
+ // Handle click - kept for accessibility (keyboard Enter/Space)
96
+ const handleClick = () => {
97
+ // Only select on click if not already selected via pointerdown
98
+ // This handles keyboard activation (Enter/Space)
99
+ if (!isDisabled() && !isUnavailable()) {
100
+ state.selectDate(date());
101
+ }
102
+ };
103
+
104
+ const handlePointerUp = () => {
105
+ setIsPressed(false);
106
+ };
107
+
108
+ // Focus the button when it becomes focused in state
109
+ onMount(() => {
110
+ const element = ref?.();
111
+ if (element && isFocused()) {
112
+ element.focus();
113
+ }
114
+ });
115
+
116
+ // Cell props (for the td element)
117
+ const cellProps = createMemo(() => ({
118
+ role: 'gridcell',
119
+ 'aria-disabled': isDisabled() || undefined,
120
+ 'aria-selected': isSelected() || undefined,
121
+ }));
122
+
123
+ // Button props (for the interactive element inside)
124
+ const buttonProps = createMemo(() => {
125
+ const d = date();
126
+ const formatter = new DateFormatter('en-US', {
127
+ weekday: 'long',
128
+ year: 'numeric',
129
+ month: 'long',
130
+ day: 'numeric',
131
+ });
132
+
133
+ return {
134
+ role: 'button',
135
+ tabIndex: isFocused() ? 0 : -1,
136
+ 'aria-label': formatter.format(d.toDate(timeZone)),
137
+ 'aria-disabled': isDisabled() || undefined,
138
+ 'aria-pressed': isPressed() || undefined,
139
+ disabled: isDisabled(),
140
+ onClick: handleClick,
141
+ onPointerDown: handlePointerDown,
142
+ onPointerUp: handlePointerUp,
143
+ onPointerLeave: handlePointerUp,
144
+ onFocus: () => {
145
+ // Only update if this cell isn't already the focused date
146
+ // This prevents infinite loops when focus is programmatically set
147
+ if (!state.isCellFocused(d)) {
148
+ state.setFocusedDate(d);
149
+ }
150
+ state.setFocused(true);
151
+ },
152
+ };
153
+ });
154
+
155
+ return {
156
+ get cellProps() {
157
+ return cellProps();
158
+ },
159
+ get buttonProps() {
160
+ return buttonProps();
161
+ },
162
+ get isSelected() {
163
+ return isSelected();
164
+ },
165
+ get isFocused() {
166
+ return isFocused();
167
+ },
168
+ get isDisabled() {
169
+ return isDisabled();
170
+ },
171
+ get isUnavailable() {
172
+ return isUnavailable();
173
+ },
174
+ get isOutsideMonth() {
175
+ return isOutsideMonth();
176
+ },
177
+ get isToday() {
178
+ return isToday();
179
+ },
180
+ get isPressed() {
181
+ return isPressed();
182
+ },
183
+ get formattedDate() {
184
+ return formattedDate();
185
+ },
186
+ };
187
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * createCalendarGrid hook for Solidaria
3
+ *
4
+ * Provides the behavior and accessibility implementation for a calendar grid.
5
+ * Based on @react-aria/calendar useCalendarGrid
6
+ */
7
+
8
+ import { createMemo, onMount, onCleanup } from 'solid-js';
9
+ import { type MaybeAccessor } from '../utils/reactivity';
10
+ import type { CalendarState, CalendarDate } from '@proyecto-viviana/solid-stately';
11
+
12
+ // ============================================
13
+ // TYPES
14
+ // ============================================
15
+
16
+ export interface AriaCalendarGridProps {
17
+ /** The start date of the grid (defaults to start of focused month). */
18
+ startDate?: CalendarDate;
19
+ /** The end date of the grid (defaults to end of focused month). */
20
+ endDate?: CalendarDate;
21
+ /** The number of weeks to display. */
22
+ weekdayStyle?: 'narrow' | 'short' | 'long';
23
+ }
24
+
25
+ export interface CalendarGridAria {
26
+ /** Props for the grid element (table or grid role). */
27
+ gridProps: Record<string, unknown>;
28
+ /** Props for the header row. */
29
+ headerProps: Record<string, unknown>;
30
+ /** Week day labels for the header. */
31
+ weekDays: string[];
32
+ }
33
+
34
+ // ============================================
35
+ // IMPLEMENTATION
36
+ // ============================================
37
+
38
+ /**
39
+ * Provides the behavior and accessibility implementation for a calendar grid.
40
+ */
41
+ export function createCalendarGrid<T extends CalendarState>(
42
+ _props: MaybeAccessor<AriaCalendarGridProps>,
43
+ state: T,
44
+ ref?: () => HTMLElement | null
45
+ ): CalendarGridAria {
46
+ // Week days for headers
47
+ const weekDays = createMemo(() => state.weekDays());
48
+
49
+ // Handle keyboard navigation
50
+ const handleKeyDown = (e: KeyboardEvent) => {
51
+ if (state.isDisabled()) return;
52
+
53
+ switch (e.key) {
54
+ case 'ArrowLeft':
55
+ e.preventDefault();
56
+ state.focusPreviousDay();
57
+ break;
58
+ case 'ArrowRight':
59
+ e.preventDefault();
60
+ state.focusNextDay();
61
+ break;
62
+ case 'ArrowUp':
63
+ e.preventDefault();
64
+ state.focusPreviousWeek();
65
+ break;
66
+ case 'ArrowDown':
67
+ e.preventDefault();
68
+ state.focusNextWeek();
69
+ break;
70
+ case 'PageUp':
71
+ e.preventDefault();
72
+ if (e.shiftKey) {
73
+ state.focusPreviousSection(); // Previous year
74
+ } else {
75
+ state.focusPreviousPage(); // Previous month
76
+ }
77
+ break;
78
+ case 'PageDown':
79
+ e.preventDefault();
80
+ if (e.shiftKey) {
81
+ state.focusNextSection(); // Next year
82
+ } else {
83
+ state.focusNextPage(); // Next month
84
+ }
85
+ break;
86
+ case 'Home':
87
+ e.preventDefault();
88
+ state.focusPageStart();
89
+ break;
90
+ case 'End':
91
+ e.preventDefault();
92
+ state.focusPageEnd();
93
+ break;
94
+ case 'Enter':
95
+ case ' ':
96
+ e.preventDefault();
97
+ state.selectFocusedDate();
98
+ break;
99
+ }
100
+ };
101
+
102
+ // Register keyboard listener
103
+ onMount(() => {
104
+ const element = ref?.();
105
+ if (element) {
106
+ element.addEventListener('keydown', handleKeyDown);
107
+ onCleanup(() => {
108
+ element.removeEventListener('keydown', handleKeyDown);
109
+ });
110
+ }
111
+ });
112
+
113
+ // Grid props
114
+ const gridProps = createMemo(() => ({
115
+ role: 'grid',
116
+ 'aria-readonly': state.isReadOnly() || undefined,
117
+ 'aria-disabled': state.isDisabled() || undefined,
118
+ tabIndex: state.isFocused() ? 0 : -1,
119
+ onFocus: () => state.setFocused(true),
120
+ onBlur: () => state.setFocused(false),
121
+ onKeyDown: handleKeyDown,
122
+ }));
123
+
124
+ // Header props
125
+ const headerProps = createMemo(() => ({
126
+ role: 'row',
127
+ }));
128
+
129
+ return {
130
+ get gridProps() {
131
+ return gridProps();
132
+ },
133
+ get headerProps() {
134
+ return headerProps();
135
+ },
136
+ get weekDays() {
137
+ return weekDays();
138
+ },
139
+ };
140
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * createRangeCalendar hook for Solidaria
3
+ *
4
+ * Provides the behavior and accessibility implementation for a range calendar component.
5
+ * Based on @react-aria/calendar useRangeCalendar
6
+ */
7
+
8
+ import { createMemo } from 'solid-js';
9
+ import { createId } from '../ssr';
10
+ import { access, type MaybeAccessor } from '../utils/reactivity';
11
+ import { mergeProps } from '../utils/mergeProps';
12
+ import type { RangeCalendarState } from '@proyecto-viviana/solid-stately';
13
+
14
+ // ============================================
15
+ // TYPES
16
+ // ============================================
17
+
18
+ export interface AriaRangeCalendarProps {
19
+ /** An ID for the calendar. */
20
+ id?: string;
21
+ /** Whether the calendar is disabled. */
22
+ isDisabled?: boolean;
23
+ /** Whether the calendar is read-only. */
24
+ isReadOnly?: boolean;
25
+ /** An accessible label for the calendar. */
26
+ 'aria-label'?: string;
27
+ /** The ID of an element that labels the calendar. */
28
+ 'aria-labelledby'?: string;
29
+ /** The ID of an element that describes the calendar. */
30
+ 'aria-describedby'?: string;
31
+ }
32
+
33
+ export interface RangeCalendarAria {
34
+ /** Props for the calendar container element. */
35
+ calendarProps: Record<string, unknown>;
36
+ /** Props for the previous button. */
37
+ prevButtonProps: Record<string, unknown>;
38
+ /** Props for the next button. */
39
+ nextButtonProps: Record<string, unknown>;
40
+ /** Props for the title/heading element. */
41
+ titleProps: Record<string, unknown>;
42
+ /** An accessible label for the title. */
43
+ title: string;
44
+ }
45
+
46
+ // ============================================
47
+ // IMPLEMENTATION
48
+ // ============================================
49
+
50
+ /**
51
+ * Provides the behavior and accessibility implementation for a range calendar component.
52
+ */
53
+ export function createRangeCalendar<T extends RangeCalendarState>(
54
+ props: MaybeAccessor<AriaRangeCalendarProps>,
55
+ state: T
56
+ ): RangeCalendarAria {
57
+ const getProps = () => access(props);
58
+ const id = createId(getProps().id);
59
+ const titleId = createId();
60
+
61
+ // Title (e.g., "December 2024")
62
+ const title = createMemo(() => state.title());
63
+
64
+ // Previous button props
65
+ const prevButtonProps = createMemo(() => {
66
+ const p = getProps();
67
+ const isDisabled = p.isDisabled || state.isDisabled();
68
+
69
+ return {
70
+ 'aria-label': 'Previous month',
71
+ onClick: () => {
72
+ if (!isDisabled) {
73
+ state.focusPreviousPage();
74
+ }
75
+ },
76
+ disabled: isDisabled,
77
+ tabIndex: -1,
78
+ };
79
+ });
80
+
81
+ // Next button props
82
+ const nextButtonProps = createMemo(() => {
83
+ const p = getProps();
84
+ const isDisabled = p.isDisabled || state.isDisabled();
85
+
86
+ return {
87
+ 'aria-label': 'Next month',
88
+ onClick: () => {
89
+ if (!isDisabled) {
90
+ state.focusNextPage();
91
+ }
92
+ },
93
+ disabled: isDisabled,
94
+ tabIndex: -1,
95
+ };
96
+ });
97
+
98
+ // Title props
99
+ const titleProps = createMemo(() => ({
100
+ id: titleId,
101
+ 'aria-live': 'polite' as const,
102
+ }));
103
+
104
+ // Calendar container props
105
+ const calendarProps = createMemo(() => {
106
+ const p = getProps();
107
+
108
+ return mergeProps(
109
+ {
110
+ id,
111
+ role: 'group',
112
+ 'aria-labelledby': p['aria-labelledby'] ?? titleId,
113
+ 'aria-label': p['aria-label'],
114
+ 'aria-describedby': p['aria-describedby'],
115
+ }
116
+ );
117
+ });
118
+
119
+ return {
120
+ get calendarProps() {
121
+ return calendarProps();
122
+ },
123
+ get prevButtonProps() {
124
+ return prevButtonProps();
125
+ },
126
+ get nextButtonProps() {
127
+ return nextButtonProps();
128
+ },
129
+ get titleProps() {
130
+ return titleProps();
131
+ },
132
+ get title() {
133
+ return title();
134
+ },
135
+ };
136
+ }
@@ -0,0 +1,186 @@
1
+ /**
2
+ * createRangeCalendarCell hook for Solidaria
3
+ *
4
+ * Provides the behavior and accessibility implementation for a range calendar cell.
5
+ * Based on @react-aria/calendar useCalendarCell (with range support)
6
+ */
7
+
8
+ import { createSignal, createMemo } from 'solid-js';
9
+ import { access, type MaybeAccessor } from '../utils/reactivity';
10
+ import type { RangeCalendarState, CalendarDate, DateValue } from '@proyecto-viviana/solid-stately';
11
+ import { isToday as isTodayUtil, DateFormatter, getLocalTimeZone } from '@internationalized/date';
12
+
13
+ // ============================================
14
+ // TYPES
15
+ // ============================================
16
+
17
+ export interface AriaRangeCalendarCellProps {
18
+ /** The date represented by the cell. */
19
+ date: DateValue;
20
+ /** Whether the cell is disabled. */
21
+ isDisabled?: boolean;
22
+ }
23
+
24
+ export interface RangeCalendarCellAria {
25
+ /** Props for the cell element (td or gridcell). */
26
+ cellProps: Record<string, unknown>;
27
+ /** Props for the button inside the cell. */
28
+ buttonProps: Record<string, unknown>;
29
+ /** Whether the cell is within the selected range. */
30
+ isSelected: boolean;
31
+ /** Whether the cell is the start of the selection. */
32
+ isSelectionStart: boolean;
33
+ /** Whether the cell is the end of the selection. */
34
+ isSelectionEnd: boolean;
35
+ /** Whether the cell is focused. */
36
+ isFocused: boolean;
37
+ /** Whether the cell is disabled. */
38
+ isDisabled: boolean;
39
+ /** Whether the cell is unavailable (e.g., booked date). */
40
+ isUnavailable: boolean;
41
+ /** Whether the cell is outside the visible month. */
42
+ isOutsideMonth: boolean;
43
+ /** Whether the cell represents today. */
44
+ isToday: boolean;
45
+ /** Whether the cell is pressed. */
46
+ isPressed: boolean;
47
+ /** The formatted date string. */
48
+ formattedDate: string;
49
+ }
50
+
51
+ // ============================================
52
+ // IMPLEMENTATION
53
+ // ============================================
54
+
55
+ /**
56
+ * Provides the behavior and accessibility implementation for a range calendar cell.
57
+ */
58
+ export function createRangeCalendarCell<T extends RangeCalendarState>(
59
+ props: MaybeAccessor<AriaRangeCalendarCellProps>,
60
+ state: T,
61
+ _ref?: () => HTMLElement | null
62
+ ): RangeCalendarCellAria {
63
+ const getProps = () => access(props);
64
+ const [isPressed, setIsPressed] = createSignal(false);
65
+ const timeZone = getLocalTimeZone();
66
+
67
+ // Get the date from props
68
+ const date = createMemo(() => getProps().date as CalendarDate);
69
+
70
+ // Check states
71
+ const isSelected = createMemo(() => state.isSelected(date()));
72
+ const isSelectionStart = createMemo(() => state.isSelectionStart(date()));
73
+ const isSelectionEnd = createMemo(() => state.isSelectionEnd(date()));
74
+ const isFocused = createMemo(() => state.isCellFocused(date()));
75
+ const isDisabled = createMemo(() => {
76
+ return getProps().isDisabled || state.isCellDisabled(date());
77
+ });
78
+ const isUnavailable = createMemo(() => state.isCellUnavailable(date()));
79
+ const isOutsideMonth = createMemo(() => state.isOutsideVisibleRange(date()));
80
+ const isToday = createMemo(() => isTodayUtil(date(), timeZone));
81
+
82
+ // Format the date for display
83
+ const formattedDate = createMemo(() => {
84
+ return date().day.toString();
85
+ });
86
+
87
+ // Handle click
88
+ const handleClick = () => {
89
+ if (!isDisabled() && !isUnavailable()) {
90
+ state.selectDate(date());
91
+ }
92
+ };
93
+
94
+ // Handle pointer events for pressed state
95
+ const handlePointerDown = () => {
96
+ if (!isDisabled() && !isUnavailable()) {
97
+ setIsPressed(true);
98
+ }
99
+ };
100
+
101
+ const handlePointerUp = () => {
102
+ setIsPressed(false);
103
+ };
104
+
105
+ // Handle hover during range selection
106
+ const handlePointerEnter = () => {
107
+ if (state.isDragging() && !isDisabled() && !isUnavailable()) {
108
+ state.setFocusedDate(date());
109
+ }
110
+ };
111
+
112
+ // Cell props (for the td element)
113
+ const cellProps = createMemo(() => ({
114
+ role: 'gridcell',
115
+ 'aria-disabled': isDisabled() || undefined,
116
+ 'aria-selected': isSelected() || undefined,
117
+ }));
118
+
119
+ // Button props (for the interactive element inside)
120
+ const buttonProps = createMemo(() => {
121
+ const d = date();
122
+ const formatter = new DateFormatter('en-US', {
123
+ weekday: 'long',
124
+ year: 'numeric',
125
+ month: 'long',
126
+ day: 'numeric',
127
+ });
128
+
129
+ return {
130
+ role: 'button',
131
+ tabIndex: isFocused() ? 0 : -1,
132
+ 'aria-label': formatter.format(d.toDate(timeZone)),
133
+ 'aria-disabled': isDisabled() || undefined,
134
+ 'aria-pressed': isPressed() || undefined,
135
+ disabled: isDisabled(),
136
+ onClick: handleClick,
137
+ onPointerDown: handlePointerDown,
138
+ onPointerUp: handlePointerUp,
139
+ onPointerLeave: handlePointerUp,
140
+ onPointerEnter: handlePointerEnter,
141
+ onFocus: () => {
142
+ state.setFocusedDate(d);
143
+ state.setFocused(true);
144
+ },
145
+ };
146
+ });
147
+
148
+ return {
149
+ get cellProps() {
150
+ return cellProps();
151
+ },
152
+ get buttonProps() {
153
+ return buttonProps();
154
+ },
155
+ get isSelected() {
156
+ return isSelected();
157
+ },
158
+ get isSelectionStart() {
159
+ return isSelectionStart();
160
+ },
161
+ get isSelectionEnd() {
162
+ return isSelectionEnd();
163
+ },
164
+ get isFocused() {
165
+ return isFocused();
166
+ },
167
+ get isDisabled() {
168
+ return isDisabled();
169
+ },
170
+ get isUnavailable() {
171
+ return isUnavailable();
172
+ },
173
+ get isOutsideMonth() {
174
+ return isOutsideMonth();
175
+ },
176
+ get isToday() {
177
+ return isToday();
178
+ },
179
+ get isPressed() {
180
+ return isPressed();
181
+ },
182
+ get formattedDate() {
183
+ return formattedDate();
184
+ },
185
+ };
186
+ }
@@ -0,0 +1,34 @@
1
+ // Calendar
2
+ export {
3
+ createCalendar,
4
+ type AriaCalendarProps,
5
+ type CalendarAria,
6
+ } from './createCalendar';
7
+
8
+ // Calendar Grid
9
+ export {
10
+ createCalendarGrid,
11
+ type AriaCalendarGridProps,
12
+ type CalendarGridAria,
13
+ } from './createCalendarGrid';
14
+
15
+ // Calendar Cell
16
+ export {
17
+ createCalendarCell,
18
+ type AriaCalendarCellProps,
19
+ type CalendarCellAria,
20
+ } from './createCalendarCell';
21
+
22
+ // Range Calendar
23
+ export {
24
+ createRangeCalendar,
25
+ type AriaRangeCalendarProps,
26
+ type RangeCalendarAria,
27
+ } from './createRangeCalendar';
28
+
29
+ // Range Calendar Cell
30
+ export {
31
+ createRangeCalendarCell,
32
+ type AriaRangeCalendarCellProps,
33
+ type RangeCalendarCellAria,
34
+ } from './createRangeCalendarCell';