@proyecto-viviana/solidaria-components 0.3.2 → 0.4.1

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 (417) hide show
  1. package/dist/ActionBar.d.ts.map +1 -1
  2. package/dist/ActionBar.js +175 -0
  3. package/dist/ActionBar.js.map +1 -0
  4. package/dist/ActionBar.jsx +119 -0
  5. package/dist/ActionBar.jsx.map +1 -0
  6. package/dist/ActionGroup.d.ts.map +1 -1
  7. package/dist/ActionGroup.js +179 -0
  8. package/dist/ActionGroup.js.map +1 -0
  9. package/dist/ActionGroup.jsx +129 -0
  10. package/dist/ActionGroup.jsx.map +1 -0
  11. package/dist/Alert.js +111 -0
  12. package/dist/Alert.js.map +1 -0
  13. package/dist/Alert.jsx +82 -0
  14. package/dist/Alert.jsx.map +1 -0
  15. package/dist/Autocomplete.d.ts +2 -2
  16. package/dist/Autocomplete.d.ts.map +1 -1
  17. package/dist/Autocomplete.js +130 -0
  18. package/dist/Autocomplete.js.map +1 -0
  19. package/dist/Autocomplete.jsx +116 -0
  20. package/dist/Autocomplete.jsx.map +1 -0
  21. package/dist/Breadcrumbs.d.ts.map +1 -1
  22. package/dist/Breadcrumbs.js +333 -0
  23. package/dist/Breadcrumbs.js.map +1 -0
  24. package/dist/Breadcrumbs.jsx +261 -0
  25. package/dist/Breadcrumbs.jsx.map +1 -0
  26. package/dist/Button.d.ts.map +1 -1
  27. package/dist/Button.js +348 -0
  28. package/dist/Button.js.map +1 -0
  29. package/dist/Button.jsx +314 -0
  30. package/dist/Button.jsx.map +1 -0
  31. package/dist/Calendar.d.ts +4 -4
  32. package/dist/Calendar.d.ts.map +1 -1
  33. package/dist/Calendar.js +573 -0
  34. package/dist/Calendar.js.map +1 -0
  35. package/dist/Calendar.jsx +342 -0
  36. package/dist/Calendar.jsx.map +1 -0
  37. package/dist/CalendarPicker.d.ts +33 -0
  38. package/dist/CalendarPicker.d.ts.map +1 -0
  39. package/dist/CalendarPicker.js +44 -0
  40. package/dist/CalendarPicker.js.map +1 -0
  41. package/dist/CalendarPicker.jsx +44 -0
  42. package/dist/CalendarPicker.jsx.map +1 -0
  43. package/dist/Checkbox.d.ts +83 -4
  44. package/dist/Checkbox.d.ts.map +1 -1
  45. package/dist/Checkbox.js +923 -0
  46. package/dist/Checkbox.js.map +1 -0
  47. package/dist/Checkbox.jsx +707 -0
  48. package/dist/Checkbox.jsx.map +1 -0
  49. package/dist/Collection.d.ts.map +1 -1
  50. package/dist/Collection.js +202 -0
  51. package/dist/Collection.js.map +1 -0
  52. package/dist/Collection.jsx +152 -0
  53. package/dist/Collection.jsx.map +1 -0
  54. package/dist/Color.d.ts.map +1 -1
  55. package/dist/Color.js +1717 -0
  56. package/dist/Color.js.map +1 -0
  57. package/dist/Color.jsx +1340 -0
  58. package/dist/Color.jsx.map +1 -0
  59. package/dist/ColorEditor.js +208 -0
  60. package/dist/ColorEditor.js.map +1 -0
  61. package/dist/ColorEditor.jsx +120 -0
  62. package/dist/ColorEditor.jsx.map +1 -0
  63. package/dist/ComboBox.d.ts +8 -4
  64. package/dist/ComboBox.d.ts.map +1 -1
  65. package/dist/ComboBox.js +1000 -0
  66. package/dist/ComboBox.js.map +1 -0
  67. package/dist/ComboBox.jsx +724 -0
  68. package/dist/ComboBox.jsx.map +1 -0
  69. package/dist/ContextualHelpTrigger.js +146 -0
  70. package/dist/ContextualHelpTrigger.js.map +1 -0
  71. package/dist/ContextualHelpTrigger.jsx +109 -0
  72. package/dist/ContextualHelpTrigger.jsx.map +1 -0
  73. package/dist/DateField.d.ts +14 -5
  74. package/dist/DateField.d.ts.map +1 -1
  75. package/dist/DateField.js +4 -0
  76. package/dist/DateField.jsx +4 -0
  77. package/dist/DateField2.js +502 -0
  78. package/dist/DateField2.js.map +1 -0
  79. package/dist/DateField2.jsx +354 -0
  80. package/dist/DateField2.jsx.map +1 -0
  81. package/dist/DatePicker.d.ts +46 -7
  82. package/dist/DatePicker.d.ts.map +1 -1
  83. package/dist/DatePicker.js +7 -0
  84. package/dist/DatePicker.jsx +7 -0
  85. package/dist/DatePicker2.js +1171 -0
  86. package/dist/DatePicker2.js.map +1 -0
  87. package/dist/DatePicker2.jsx +820 -0
  88. package/dist/DatePicker2.jsx.map +1 -0
  89. package/dist/DateRangePickerContext.d.ts +2 -0
  90. package/dist/DateRangePickerContext.d.ts.map +1 -1
  91. package/dist/Dialog.d.ts.map +1 -1
  92. package/dist/Dialog.js +337 -0
  93. package/dist/Dialog.js.map +1 -0
  94. package/dist/Dialog.jsx +192 -0
  95. package/dist/Dialog.jsx.map +1 -0
  96. package/dist/Disclosure.d.ts.map +1 -1
  97. package/dist/Disclosure.js +331 -0
  98. package/dist/Disclosure.js.map +1 -0
  99. package/dist/Disclosure.jsx +230 -0
  100. package/dist/Disclosure.jsx.map +1 -0
  101. package/dist/DragAndDrop.d.ts.map +1 -1
  102. package/dist/DragAndDrop.js +176 -0
  103. package/dist/DragAndDrop.js.map +1 -0
  104. package/dist/DragAndDrop.jsx +160 -0
  105. package/dist/DragAndDrop.jsx.map +1 -0
  106. package/dist/DropZone.d.ts.map +1 -1
  107. package/dist/DropZone.js +189 -0
  108. package/dist/DropZone.js.map +1 -0
  109. package/dist/DropZone.jsx +144 -0
  110. package/dist/DropZone.jsx.map +1 -0
  111. package/dist/FieldError.js +66 -0
  112. package/dist/FieldError.js.map +1 -0
  113. package/dist/FieldError.jsx +47 -0
  114. package/dist/FieldError.jsx.map +1 -0
  115. package/dist/FileTrigger.js +79 -0
  116. package/dist/FileTrigger.js.map +1 -0
  117. package/dist/FileTrigger.jsx +49 -0
  118. package/dist/FileTrigger.jsx.map +1 -0
  119. package/dist/Focusable.js +70 -0
  120. package/dist/Focusable.js.map +1 -0
  121. package/dist/Focusable.jsx +69 -0
  122. package/dist/Focusable.jsx.map +1 -0
  123. package/dist/Form.js +99 -0
  124. package/dist/Form.js.map +1 -0
  125. package/dist/Form.jsx +73 -0
  126. package/dist/Form.jsx.map +1 -0
  127. package/dist/GridList.d.ts +34 -2
  128. package/dist/GridList.d.ts.map +1 -1
  129. package/dist/GridList.js +723 -0
  130. package/dist/GridList.js.map +1 -0
  131. package/dist/GridList.jsx +552 -0
  132. package/dist/GridList.jsx.map +1 -0
  133. package/dist/Icon.d.ts.map +1 -1
  134. package/dist/Icon.js +127 -0
  135. package/dist/Icon.js.map +1 -0
  136. package/dist/Icon.jsx +72 -0
  137. package/dist/Icon.jsx.map +1 -0
  138. package/dist/Keyboard.js +30 -0
  139. package/dist/Keyboard.js.map +1 -0
  140. package/dist/Keyboard.jsx +24 -0
  141. package/dist/Keyboard.jsx.map +1 -0
  142. package/dist/Landmark.js +134 -0
  143. package/dist/Landmark.js.map +1 -0
  144. package/dist/Landmark.jsx +118 -0
  145. package/dist/Landmark.jsx.map +1 -0
  146. package/dist/Link.d.ts.map +1 -1
  147. package/dist/Link.js +219 -0
  148. package/dist/Link.js.map +1 -0
  149. package/dist/Link.jsx +189 -0
  150. package/dist/Link.jsx.map +1 -0
  151. package/dist/ListBox.d.ts +2 -2
  152. package/dist/ListBox.d.ts.map +1 -1
  153. package/dist/ListBox.js +831 -0
  154. package/dist/ListBox.js.map +1 -0
  155. package/dist/ListBox.jsx +644 -0
  156. package/dist/ListBox.jsx.map +1 -0
  157. package/dist/Menu.d.ts +27 -9
  158. package/dist/Menu.d.ts.map +1 -1
  159. package/dist/Menu.js +1254 -0
  160. package/dist/Menu.js.map +1 -0
  161. package/dist/Menu.jsx +1042 -0
  162. package/dist/Menu.jsx.map +1 -0
  163. package/dist/Meter.d.ts.map +1 -1
  164. package/dist/Meter.js +123 -0
  165. package/dist/Meter.js.map +1 -0
  166. package/dist/Meter.jsx +107 -0
  167. package/dist/Meter.jsx.map +1 -0
  168. package/dist/Modal.d.ts.map +1 -1
  169. package/dist/Modal.js +382 -0
  170. package/dist/Modal.js.map +1 -0
  171. package/dist/Modal.jsx +314 -0
  172. package/dist/Modal.jsx.map +1 -0
  173. package/dist/NumberField.d.ts +3 -3
  174. package/dist/NumberField.d.ts.map +1 -1
  175. package/dist/NumberField.js +573 -0
  176. package/dist/NumberField.js.map +1 -0
  177. package/dist/NumberField.jsx +443 -0
  178. package/dist/NumberField.jsx.map +1 -0
  179. package/dist/Popover.d.ts +10 -2
  180. package/dist/Popover.d.ts.map +1 -1
  181. package/dist/Popover.js +471 -0
  182. package/dist/Popover.js.map +1 -0
  183. package/dist/Popover.jsx +354 -0
  184. package/dist/Popover.jsx.map +1 -0
  185. package/dist/Pressable.js +69 -0
  186. package/dist/Pressable.js.map +1 -0
  187. package/dist/Pressable.jsx +68 -0
  188. package/dist/Pressable.jsx.map +1 -0
  189. package/dist/ProgressBar.d.ts.map +1 -1
  190. package/dist/ProgressBar.js +130 -0
  191. package/dist/ProgressBar.js.map +1 -0
  192. package/dist/ProgressBar.jsx +114 -0
  193. package/dist/ProgressBar.jsx.map +1 -0
  194. package/dist/RadioGroup.d.ts +82 -4
  195. package/dist/RadioGroup.d.ts.map +1 -1
  196. package/dist/RadioGroup.js +931 -0
  197. package/dist/RadioGroup.js.map +1 -0
  198. package/dist/RadioGroup.jsx +681 -0
  199. package/dist/RadioGroup.jsx.map +1 -0
  200. package/dist/RangeCalendar.d.ts.map +1 -1
  201. package/dist/RangeCalendar.js +546 -0
  202. package/dist/RangeCalendar.js.map +1 -0
  203. package/dist/RangeCalendar.jsx +323 -0
  204. package/dist/RangeCalendar.jsx.map +1 -0
  205. package/dist/RouterProvider.js +118 -0
  206. package/dist/RouterProvider.js.map +1 -0
  207. package/dist/RouterProvider.jsx +112 -0
  208. package/dist/RouterProvider.jsx.map +1 -0
  209. package/dist/SearchField.d.ts +2 -2
  210. package/dist/SearchField.d.ts.map +1 -1
  211. package/dist/SearchField.js +667 -0
  212. package/dist/SearchField.js.map +1 -0
  213. package/dist/SearchField.jsx +550 -0
  214. package/dist/SearchField.jsx.map +1 -0
  215. package/dist/Select.d.ts +7 -0
  216. package/dist/Select.d.ts.map +1 -1
  217. package/dist/Select.js +1044 -0
  218. package/dist/Select.js.map +1 -0
  219. package/dist/Select.jsx +759 -0
  220. package/dist/Select.jsx.map +1 -0
  221. package/dist/SelectionIndicator.d.ts.map +1 -1
  222. package/dist/SelectionIndicator.js +86 -0
  223. package/dist/SelectionIndicator.js.map +1 -0
  224. package/dist/SelectionIndicator.jsx +50 -0
  225. package/dist/SelectionIndicator.jsx.map +1 -0
  226. package/dist/Separator.js +97 -0
  227. package/dist/Separator.js.map +1 -0
  228. package/dist/Separator.jsx +85 -0
  229. package/dist/Separator.jsx.map +1 -0
  230. package/dist/SharedElementTransition.d.ts.map +1 -1
  231. package/dist/SharedElementTransition.js +167 -0
  232. package/dist/SharedElementTransition.js.map +1 -0
  233. package/dist/SharedElementTransition.jsx +140 -0
  234. package/dist/SharedElementTransition.jsx.map +1 -0
  235. package/dist/Slider.d.ts +33 -1
  236. package/dist/Slider.d.ts.map +1 -1
  237. package/dist/Slider.js +515 -0
  238. package/dist/Slider.js.map +1 -0
  239. package/dist/Slider.jsx +403 -0
  240. package/dist/Slider.jsx.map +1 -0
  241. package/dist/StepList.d.ts.map +1 -1
  242. package/dist/StepList.js +191 -0
  243. package/dist/StepList.js.map +1 -0
  244. package/dist/StepList.jsx +136 -0
  245. package/dist/StepList.jsx.map +1 -0
  246. package/dist/Switch.d.ts +65 -2
  247. package/dist/Switch.d.ts.map +1 -1
  248. package/dist/Switch.js +534 -0
  249. package/dist/Switch.js.map +1 -0
  250. package/dist/Switch.jsx +393 -0
  251. package/dist/Switch.jsx.map +1 -0
  252. package/dist/Table.d.ts +50 -0
  253. package/dist/Table.d.ts.map +1 -1
  254. package/dist/Table.js +1766 -0
  255. package/dist/Table.js.map +1 -0
  256. package/dist/Table.jsx +1355 -0
  257. package/dist/Table.jsx.map +1 -0
  258. package/dist/Tabs.d.ts +0 -4
  259. package/dist/Tabs.d.ts.map +1 -1
  260. package/dist/Tabs.js +598 -0
  261. package/dist/Tabs.js.map +1 -0
  262. package/dist/Tabs.jsx +367 -0
  263. package/dist/Tabs.jsx.map +1 -0
  264. package/dist/TagGroup.d.ts +3 -1
  265. package/dist/TagGroup.d.ts.map +1 -1
  266. package/dist/TagGroup.js +382 -0
  267. package/dist/TagGroup.js.map +1 -0
  268. package/dist/TagGroup.jsx +273 -0
  269. package/dist/TagGroup.jsx.map +1 -0
  270. package/dist/Text.d.ts +18 -6
  271. package/dist/Text.d.ts.map +1 -1
  272. package/dist/Text.js +42 -0
  273. package/dist/Text.js.map +1 -0
  274. package/dist/Text.jsx +35 -0
  275. package/dist/Text.jsx.map +1 -0
  276. package/dist/TextField.d.ts +6 -6
  277. package/dist/TextField.d.ts.map +1 -1
  278. package/dist/TextField.js +439 -0
  279. package/dist/TextField.js.map +1 -0
  280. package/dist/TextField.jsx +406 -0
  281. package/dist/TextField.jsx.map +1 -0
  282. package/dist/TimeField.d.ts +17 -53
  283. package/dist/TimeField.d.ts.map +1 -1
  284. package/dist/TimeField.js +278 -0
  285. package/dist/TimeField.js.map +1 -0
  286. package/dist/TimeField.jsx +218 -0
  287. package/dist/TimeField.jsx.map +1 -0
  288. package/dist/Toast.d.ts +14 -0
  289. package/dist/Toast.d.ts.map +1 -1
  290. package/dist/Toast.js +551 -0
  291. package/dist/Toast.js.map +1 -0
  292. package/dist/Toast.jsx +361 -0
  293. package/dist/Toast.jsx.map +1 -0
  294. package/dist/ToggleButton.d.ts.map +1 -1
  295. package/dist/ToggleButton.js +123 -0
  296. package/dist/ToggleButton.js.map +1 -0
  297. package/dist/ToggleButton.jsx +88 -0
  298. package/dist/ToggleButton.jsx.map +1 -0
  299. package/dist/ToggleButtonGroup.d.ts.map +1 -1
  300. package/dist/ToggleButtonGroup.js +114 -0
  301. package/dist/ToggleButtonGroup.js.map +1 -0
  302. package/dist/ToggleButtonGroup.jsx +85 -0
  303. package/dist/ToggleButtonGroup.jsx.map +1 -0
  304. package/dist/Toolbar.js +112 -0
  305. package/dist/Toolbar.js.map +1 -0
  306. package/dist/Toolbar.jsx +92 -0
  307. package/dist/Toolbar.jsx.map +1 -0
  308. package/dist/Tooltip.d.ts.map +1 -1
  309. package/dist/Tooltip.js +539 -0
  310. package/dist/Tooltip.js.map +1 -0
  311. package/dist/Tooltip.jsx +465 -0
  312. package/dist/Tooltip.jsx.map +1 -0
  313. package/dist/Tree.d.ts +14 -2
  314. package/dist/Tree.d.ts.map +1 -1
  315. package/dist/Tree.js +1424 -0
  316. package/dist/Tree.js.map +1 -0
  317. package/dist/Tree.jsx +1172 -0
  318. package/dist/Tree.jsx.map +1 -0
  319. package/dist/Virtualizer.d.ts +16 -2
  320. package/dist/Virtualizer.d.ts.map +1 -1
  321. package/dist/Virtualizer.js +2 -0
  322. package/dist/Virtualizer.jsx +2 -0
  323. package/dist/Virtualizer2.js +751 -0
  324. package/dist/Virtualizer2.js.map +1 -0
  325. package/dist/Virtualizer2.jsx +730 -0
  326. package/dist/Virtualizer2.jsx.map +1 -0
  327. package/dist/VirtualizerLayouts.d.ts +18 -0
  328. package/dist/VirtualizerLayouts.d.ts.map +1 -1
  329. package/dist/VisuallyHidden.js +42 -0
  330. package/dist/VisuallyHidden.js.map +1 -0
  331. package/dist/VisuallyHidden.jsx +35 -0
  332. package/dist/VisuallyHidden.jsx.map +1 -0
  333. package/dist/contexts.js +33 -0
  334. package/dist/contexts.js.map +1 -0
  335. package/dist/contexts.jsx +33 -0
  336. package/dist/contexts.jsx.map +1 -0
  337. package/dist/index.d.ts +9 -9
  338. package/dist/index.d.ts.map +1 -1
  339. package/dist/index.js +69 -23932
  340. package/dist/index.jsx +69 -18337
  341. package/dist/useDragAndDrop.d.ts +0 -8
  342. package/dist/useDragAndDrop.d.ts.map +1 -1
  343. package/dist/useDragAndDrop.js +2 -0
  344. package/dist/useDragAndDrop.jsx +2 -0
  345. package/dist/useDragAndDrop2.js +399 -0
  346. package/dist/useDragAndDrop2.js.map +1 -0
  347. package/dist/useDragAndDrop2.jsx +399 -0
  348. package/dist/useDragAndDrop2.jsx.map +1 -0
  349. package/dist/utils.d.ts +84 -11
  350. package/dist/utils.d.ts.map +1 -1
  351. package/dist/utils.js +285 -0
  352. package/dist/utils.js.map +1 -0
  353. package/dist/utils.jsx +277 -0
  354. package/dist/utils.jsx.map +1 -0
  355. package/dist/virtualizer/Layout.js +144 -0
  356. package/dist/virtualizer/Layout.js.map +1 -0
  357. package/dist/virtualizer/Layout.jsx +144 -0
  358. package/dist/virtualizer/Layout.jsx.map +1 -0
  359. package/package.json +395 -4
  360. package/src/ActionBar.tsx +10 -12
  361. package/src/ActionGroup.tsx +5 -3
  362. package/src/Autocomplete.tsx +14 -9
  363. package/src/Breadcrumbs.tsx +58 -39
  364. package/src/Button.tsx +22 -2
  365. package/src/Calendar.tsx +57 -11
  366. package/src/CalendarPicker.tsx +73 -0
  367. package/src/Checkbox.tsx +539 -3
  368. package/src/Collection.tsx +9 -3
  369. package/src/Color.tsx +84 -41
  370. package/src/ComboBox.tsx +97 -21
  371. package/src/DateField.tsx +304 -53
  372. package/src/DatePicker.tsx +402 -62
  373. package/src/DateRangePickerContext.tsx +2 -0
  374. package/src/Dialog.tsx +13 -5
  375. package/src/Disclosure.tsx +7 -1
  376. package/src/DragAndDrop.tsx +3 -1
  377. package/src/DropZone.tsx +3 -1
  378. package/src/GridList.tsx +133 -26
  379. package/src/Icon.tsx +3 -1
  380. package/src/Link.tsx +3 -1
  381. package/src/ListBox.tsx +354 -25
  382. package/src/Menu.tsx +252 -128
  383. package/src/Meter.tsx +3 -1
  384. package/src/Modal.tsx +120 -56
  385. package/src/NumberField.tsx +51 -8
  386. package/src/Popover.tsx +68 -17
  387. package/src/ProgressBar.tsx +3 -1
  388. package/src/RadioGroup.tsx +468 -4
  389. package/src/RangeCalendar.tsx +123 -2
  390. package/src/SearchField.tsx +80 -4
  391. package/src/Select.tsx +122 -26
  392. package/src/SelectionIndicator.tsx +3 -1
  393. package/src/SharedElementTransition.tsx +95 -37
  394. package/src/Slider.tsx +159 -13
  395. package/src/StepList.tsx +0 -7
  396. package/src/Switch.tsx +395 -2
  397. package/src/Table.tsx +428 -99
  398. package/src/Tabs.tsx +17 -45
  399. package/src/TagGroup.tsx +29 -7
  400. package/src/Text.tsx +31 -10
  401. package/src/TextField.tsx +44 -6
  402. package/src/TimeField.tsx +127 -192
  403. package/src/Toast.tsx +79 -14
  404. package/src/ToggleButton.tsx +3 -1
  405. package/src/ToggleButtonGroup.tsx +3 -1
  406. package/src/Tooltip.tsx +3 -1
  407. package/src/Tree.tsx +91 -13
  408. package/src/Virtualizer.tsx +180 -31
  409. package/src/VirtualizerLayouts.ts +45 -10
  410. package/src/index.ts +51 -8
  411. package/src/useDragAndDrop.ts +8 -10
  412. package/src/utils.tsx +246 -30
  413. package/dist/HiddenTimeInput.d.ts +0 -25
  414. package/dist/HiddenTimeInput.d.ts.map +0 -1
  415. package/dist/index.js.map +0 -1
  416. package/dist/index.jsx.map +0 -1
  417. package/src/HiddenTimeInput.tsx +0 -133
@@ -8,6 +8,7 @@
8
8
  import {
9
9
  type JSX,
10
10
  type ParentProps,
11
+ type Context,
11
12
  createContext,
12
13
  createMemo,
13
14
  createSignal,
@@ -43,9 +44,11 @@ import {
43
44
  type ClassNameOrFunction,
44
45
  type StyleOrFunction,
45
46
  type SlotProps,
47
+ Provider,
46
48
  useRenderProps,
47
49
  filterDOMProps,
48
50
  } from "./utils";
51
+ import { TextContext } from "./Text";
49
52
 
50
53
  type RefLike<T> = ((el: T) => void) | { current?: T | null } | undefined;
51
54
 
@@ -147,6 +150,15 @@ export interface RadioGroupProps
147
150
  description?: JSX.Element;
148
151
  /** An error message for the radio group. */
149
152
  errorMessage?: JSX.Element;
153
+ /**
154
+ * Whether this component renders the visible description/error help-text nodes.
155
+ * Defaults to `true`. A styled layer (e.g. solid-spectrum) passes `false` to keep
156
+ * the id-minting and `aria-describedby` wiring here (so child radios inherit the
157
+ * group's shared description via `radioGroupData`) while owning the visible node
158
+ * itself — mirroring RAC, where the group exposes a `TextContext`/`FieldError`
159
+ * slot rather than rendering its own help text.
160
+ */
161
+ renderHelpText?: boolean;
150
162
  }
151
163
 
152
164
  export interface RadioProps extends Omit<AriaRadioProps, "children">, SlotProps {
@@ -213,7 +225,14 @@ export function RadioGroup(props: ParentProps<RadioGroupProps>): JSX.Element {
213
225
  props,
214
226
  ) as ParentProps<RadioGroupProps>)
215
227
  : props;
216
- const [local, ariaProps] = splitProps(mergedProps, ["class", "style", "render", "ref", "slot"]);
228
+ const [local, ariaProps] = splitProps(mergedProps, [
229
+ "class",
230
+ "style",
231
+ "render",
232
+ "ref",
233
+ "slot",
234
+ "renderHelpText",
235
+ ]);
217
236
 
218
237
  const state = createRadioGroupState(() => ({
219
238
  value: mergedProps.value,
@@ -255,7 +274,9 @@ export function RadioGroup(props: ParentProps<RadioGroupProps>): JSX.Element {
255
274
 
256
275
  const renderProps = useRenderProps(
257
276
  {
258
- children: props.children,
277
+ get children() {
278
+ return props.children;
279
+ },
259
280
  class: local.class,
260
281
  style: local.style,
261
282
  defaultClassName: "solidaria-RadioGroup",
@@ -366,12 +387,12 @@ export function RadioGroup(props: ParentProps<RadioGroupProps>): JSX.Element {
366
387
  return (
367
388
  <>
368
389
  {renderedChildren()}
369
- <Show when={mergedProps.description}>
390
+ <Show when={(local.renderHelpText ?? true) && mergedProps.description}>
370
391
  <div {...(groupAria.descriptionProps as unknown as JSX.HTMLAttributes<HTMLDivElement>)}>
371
392
  {mergedProps.description}
372
393
  </div>
373
394
  </Show>
374
- <Show when={isInvalid() && mergedProps.errorMessage}>
395
+ <Show when={(local.renderHelpText ?? true) && isInvalid() && mergedProps.errorMessage}>
375
396
  <div {...(groupAria.errorMessageProps as unknown as JSX.HTMLAttributes<HTMLDivElement>)}>
376
397
  {mergedProps.errorMessage}
377
398
  </div>
@@ -766,3 +787,446 @@ export function Radio(props: RadioProps): JSX.Element {
766
787
  </Show>
767
788
  );
768
789
  }
790
+
791
+ // ============================================================================
792
+ // RadioField + RadioButton — the RAC form-field split (RAC 1.19)
793
+ // ----------------------------------------------------------------------------
794
+ // Upstream split the monolithic Radio into a RadioField wrapper (owns the radio
795
+ // aria + optional description) containing a RadioButton control (the clickable
796
+ // indicator + label). Mirrors react-aria-components/src/RadioGroup.tsx. The
797
+ // legacy `Radio` above stays as the deprecated monolith for back-compat.
798
+ //
799
+ // Spine note: upstream wires the two halves with InternalRadioContext +
800
+ // SelectionIndicatorContext + TextContext slots through `<Provider>`. Our
801
+ // `<Provider>` is inert and TextContext carries no slots yet
802
+ // (`port-context-slots`), so — exactly like RadioImpl already does — we use a
803
+ // native Solid context and bridge `description`/`errorMessage` with explicit
804
+ // ids. Like the legacy `Radio`, both halves require a surrounding RadioGroup.
805
+ // ============================================================================
806
+
807
+ export interface RadioFieldRenderProps {
808
+ /** Whether the radio is selected. */
809
+ isSelected: boolean;
810
+ /** Whether the radio is disabled. */
811
+ isDisabled: boolean;
812
+ /** Whether the radio is read only. */
813
+ isReadOnly: boolean;
814
+ /** Whether the radio is invalid. */
815
+ isInvalid: boolean;
816
+ /** Whether the radio is required. */
817
+ isRequired: boolean;
818
+ }
819
+
820
+ export interface RadioButtonRenderProps extends RadioRenderProps {}
821
+
822
+ export interface RadioFieldProps extends Omit<AriaRadioProps, "children">, SlotProps {
823
+ /** The children of the component (typically a `RadioButton`). A function may receive render props. */
824
+ children?: RenderChildren<RadioFieldRenderProps>;
825
+ /** The CSS className for the element. */
826
+ class?: ClassNameOrFunction<RadioFieldRenderProps>;
827
+ /** The inline style for the element. */
828
+ style?: StyleOrFunction<RadioFieldRenderProps>;
829
+ /** Ref for the radio field root element. */
830
+ ref?: RefLike<HTMLDivElement>;
831
+ /** Ref for the underlying input element. */
832
+ inputRef?: RefLike<HTMLInputElement>;
833
+ }
834
+
835
+ export interface RadioButtonProps extends SlotProps {
836
+ /** The children of the component. A function may receive render props. */
837
+ children?: RenderChildren<RadioButtonRenderProps>;
838
+ /** The CSS className for the element. */
839
+ class?: ClassNameOrFunction<RadioButtonRenderProps>;
840
+ /** The inline style for the element. */
841
+ style?: StyleOrFunction<RadioButtonRenderProps>;
842
+ /** Ref for the outer label element. */
843
+ ref?: RefLike<HTMLLabelElement>;
844
+ /** Handler called when hover starts. */
845
+ onHoverStart?: () => void;
846
+ /** Handler called when hover ends. */
847
+ onHoverEnd?: () => void;
848
+ /** Handler called when hover state changes. */
849
+ onHoverChange?: (isHovered: boolean) => void;
850
+ }
851
+
852
+ export interface RadioFieldContextValue extends Partial<RadioFieldProps> {
853
+ slots?: Record<string, Partial<RadioFieldProps>>;
854
+ }
855
+ export const RadioFieldContext = createContext<RadioFieldContextValue | null>(null);
856
+
857
+ /** Carries the radio aria + state from a RadioField/Radio wrapper to its RadioButton. */
858
+ interface InternalRadioContextValue {
859
+ isSelected: () => boolean;
860
+ isPressed: () => boolean;
861
+ isDisabled: () => boolean;
862
+ labelProps: () => JSX.LabelHTMLAttributes<HTMLLabelElement>;
863
+ inputProps: () => JSX.InputHTMLAttributes<HTMLInputElement>;
864
+ setInputRef: (el: HTMLInputElement) => void;
865
+ defaultClassName: string;
866
+ }
867
+ const InternalRadioContext = createContext<InternalRadioContextValue | null>(null);
868
+
869
+ /**
870
+ * A RadioField represents an individual option within a radio group, containing a
871
+ * `RadioButton` and optional description. Must be rendered inside a `RadioGroup`.
872
+ * Per-option help text is wired through a TextContext description slot — render it
873
+ * with `<Text slot="description">`. (Radios have no per-option error slot; errors
874
+ * are reported at the group level.)
875
+ *
876
+ * @example
877
+ * ```tsx
878
+ * <RadioGroup>
879
+ * <RadioField value="a">
880
+ * <RadioButton>Option A</RadioButton>
881
+ * <Text slot="description">The first option</Text>
882
+ * </RadioField>
883
+ * </RadioGroup>
884
+ * ```
885
+ */
886
+ export function RadioField(props: RadioFieldProps): JSX.Element {
887
+ const getState = createMemo(() => useContext(RadioGroupStateContext));
888
+ return (
889
+ <Show when={getState()} fallback={null} keyed>
890
+ {(state) => <RadioFieldImpl fieldProps={props} state={state} />}
891
+ </Show>
892
+ );
893
+ }
894
+
895
+ function RadioFieldImpl(props: {
896
+ fieldProps: RadioFieldProps;
897
+ state: RadioGroupState;
898
+ }): JSX.Element {
899
+ const [inputElement, setInputElement] = createSignal<HTMLInputElement | null>(null);
900
+ const { state } = props;
901
+ const contextProps = useContext(RadioFieldContext);
902
+ const contextSlotProps = contextProps?.slots?.[props.fieldProps.slot ?? "default"];
903
+ const contextBaseProps = createMemo<Partial<RadioFieldProps>>(() => {
904
+ if (!contextProps) return {};
905
+ const { slots: _slots, ...rest } = contextProps;
906
+ return rest;
907
+ });
908
+ const fieldProps = contextProps
909
+ ? (mergeProps(contextBaseProps(), contextSlotProps ?? {}, props.fieldProps) as RadioFieldProps)
910
+ : props.fieldProps;
911
+ const inputRefs = createMemo(
912
+ () =>
913
+ [contextBaseProps().inputRef, contextSlotProps?.inputRef, props.fieldProps.inputRef].filter(
914
+ Boolean,
915
+ ) as RefLike<HTMLInputElement>[],
916
+ );
917
+
918
+ // `children` is split out of ariaProps so neither the inputAriaProps key-copy
919
+ // loop nor the hook accessor spread eagerly reads it — reading a Solid
920
+ // `children` getter instantiates the nested RadioButton, and doing so OUTSIDE
921
+ // InternalRadioContext both breaks its binding and recurses.
922
+ const [local, ariaProps] = splitProps(fieldProps, [
923
+ "class",
924
+ "style",
925
+ "ref",
926
+ "inputRef",
927
+ "slot",
928
+ "children",
929
+ ]);
930
+ const inputAriaProps = createMemo(() => {
931
+ const clean: Record<string, unknown> = {};
932
+ for (const key in ariaProps as Record<string, unknown>) {
933
+ if (!key.startsWith("data-")) {
934
+ clean[key] = (ariaProps as Record<string, unknown>)[key];
935
+ }
936
+ }
937
+ return clean as typeof ariaProps;
938
+ });
939
+
940
+ const radioAria = createRadio(
941
+ () => ({
942
+ ...inputAriaProps(),
943
+ // The hook reads `children` only to decide if an aria-label is needed; the
944
+ // visible label lives in the RadioButton, so report presence as a literal.
945
+ children: true,
946
+ }),
947
+ state,
948
+ inputElement,
949
+ );
950
+
951
+ // The hook mints the description slot id and bakes the combined aria-describedby
952
+ // (own description + the group's shared description/error) into the input; the
953
+ // field exposes that id through TextContext so a `<Text slot="description">`
954
+ // child resolves it. Mirrors react-aria-components' RadioField Provider value
955
+ // list (description slot only — radios have no per-option error slot).
956
+ const textSlots = {
957
+ slots: {
958
+ get description() {
959
+ return radioAria.descriptionProps;
960
+ },
961
+ },
962
+ };
963
+
964
+ const setInputRef = (el: HTMLInputElement) => {
965
+ setInputElement(el);
966
+ el.addEventListener("invalid", (event) => {
967
+ state.updateValidation(getNativeValidation(el));
968
+ state.commitValidation();
969
+ el.focus();
970
+ event.preventDefault();
971
+ });
972
+ el.addEventListener("change", () => {
973
+ state.updateValidation(el.validity.valid ? validValidation : getNativeValidation(el));
974
+ state.commitValidation();
975
+ });
976
+ for (const ref of inputRefs()) {
977
+ assignRef(ref, el);
978
+ }
979
+ };
980
+ const setFieldRef = (el: HTMLDivElement) => {
981
+ assignRef(local.ref, el);
982
+ };
983
+
984
+ const internalContext: InternalRadioContextValue = {
985
+ isSelected: () => radioAria.isSelected(),
986
+ isPressed: () => radioAria.isPressed(),
987
+ isDisabled: () => radioAria.isDisabled,
988
+ labelProps: () => radioAria.labelProps,
989
+ inputProps: () => radioAria.inputProps,
990
+ setInputRef,
991
+ defaultClassName: "solidaria-RadioButton",
992
+ };
993
+
994
+ const selectionIndicatorContext = createMemo<SelectionIndicatorContextValue>(() => ({
995
+ isSelected: radioAria.isSelected,
996
+ }));
997
+
998
+ const renderValues = createMemo<RadioFieldRenderProps>(() => ({
999
+ isSelected: radioAria.isSelected(),
1000
+ isDisabled: radioAria.isDisabled,
1001
+ isReadOnly: state.isReadOnly,
1002
+ isInvalid: state.isInvalid,
1003
+ isRequired: state.isRequired,
1004
+ }));
1005
+
1006
+ const renderProps = useRenderProps(
1007
+ {
1008
+ children: fieldProps.children,
1009
+ class: local.class,
1010
+ style: local.style,
1011
+ defaultClassName: "solidaria-RadioField",
1012
+ },
1013
+ renderValues,
1014
+ );
1015
+
1016
+ const domProps = createMemo(() => {
1017
+ const filtered = filterDOMProps(ariaProps, { global: true });
1018
+ delete (filtered as Record<string, unknown>).id;
1019
+ delete (filtered as Record<string, unknown>).onClick;
1020
+ return filtered;
1021
+ });
1022
+
1023
+ // Children are accessed inside the providers (component-execution owner) so a
1024
+ // nested RadioButton's useContext binds to InternalRadioContext.
1025
+ const FieldChildren = () => {
1026
+ const childRenderValues: RadioFieldRenderProps = {
1027
+ get isSelected() {
1028
+ return radioAria.isSelected();
1029
+ },
1030
+ get isDisabled() {
1031
+ return radioAria.isDisabled;
1032
+ },
1033
+ get isReadOnly() {
1034
+ return state.isReadOnly;
1035
+ },
1036
+ get isInvalid() {
1037
+ return state.isInvalid;
1038
+ },
1039
+ get isRequired() {
1040
+ return state.isRequired;
1041
+ },
1042
+ };
1043
+ const renderedChildren = createMemo(() => {
1044
+ const children = fieldProps.children;
1045
+ return typeof children === "function" ? children(childRenderValues) : children;
1046
+ });
1047
+ return <>{renderedChildren()}</>;
1048
+ };
1049
+
1050
+ return (
1051
+ <div
1052
+ {...domProps()}
1053
+ ref={setFieldRef}
1054
+ class={renderProps.class()}
1055
+ style={renderProps.style()}
1056
+ slot={local.slot}
1057
+ data-selected={radioAria.isSelected() || undefined}
1058
+ data-disabled={radioAria.isDisabled || undefined}
1059
+ data-readonly={state.isReadOnly || undefined}
1060
+ data-invalid={state.isInvalid || undefined}
1061
+ data-required={state.isRequired || undefined}
1062
+ >
1063
+ <SelectionIndicatorContext.Provider value={selectionIndicatorContext()}>
1064
+ <InternalRadioContext.Provider value={internalContext}>
1065
+ <Provider values={[[TextContext, textSlots]] as Array<[Context<unknown>, unknown]>}>
1066
+ <FieldChildren />
1067
+ </Provider>
1068
+ </InternalRadioContext.Provider>
1069
+ </SelectionIndicatorContext.Provider>
1070
+ </div>
1071
+ );
1072
+ }
1073
+
1074
+ /**
1075
+ * A RadioButton is the clickable area of a radio, including the indicator and label.
1076
+ * Must be rendered inside a `RadioField` (or the legacy `Radio`).
1077
+ */
1078
+ export function RadioButton(props: RadioButtonProps): JSX.Element {
1079
+ const getCtx = createMemo(() => useContext(InternalRadioContext));
1080
+ const getState = createMemo(() => useContext(RadioGroupStateContext));
1081
+ return (
1082
+ <Show when={getCtx()} fallback={null} keyed>
1083
+ {(ctx) => (
1084
+ <Show when={getState()} fallback={null} keyed>
1085
+ {(state) => <RadioButtonImpl buttonProps={props} ctx={ctx} state={state} />}
1086
+ </Show>
1087
+ )}
1088
+ </Show>
1089
+ );
1090
+ }
1091
+
1092
+ function RadioButtonImpl(props: {
1093
+ buttonProps: RadioButtonProps;
1094
+ ctx: InternalRadioContextValue;
1095
+ state: RadioGroupState;
1096
+ }): JSX.Element {
1097
+ const { ctx, state } = props;
1098
+
1099
+ const { isFocused, isFocusVisible, focusProps } = createFocusRing();
1100
+ const { isHovered, hoverProps } = createHover({
1101
+ get isDisabled() {
1102
+ return ctx.isDisabled() || state.isReadOnly;
1103
+ },
1104
+ onHoverStart: props.buttonProps.onHoverStart,
1105
+ onHoverEnd: props.buttonProps.onHoverEnd,
1106
+ onHoverChange: props.buttonProps.onHoverChange,
1107
+ });
1108
+
1109
+ const renderValues = createMemo<RadioButtonRenderProps>(() => ({
1110
+ isSelected: ctx.isSelected(),
1111
+ isHovered: isHovered(),
1112
+ isPressed: ctx.isPressed(),
1113
+ isFocused: isFocused(),
1114
+ isFocusVisible: isFocusVisible(),
1115
+ isDisabled: ctx.isDisabled(),
1116
+ isReadOnly: state.isReadOnly,
1117
+ isInvalid: state.isInvalid,
1118
+ isRequired: state.isRequired,
1119
+ }));
1120
+
1121
+ const renderProps = useRenderProps(
1122
+ {
1123
+ children: props.buttonProps.children,
1124
+ class: props.buttonProps.class,
1125
+ style: props.buttonProps.style,
1126
+ defaultClassName: ctx.defaultClassName,
1127
+ },
1128
+ renderValues,
1129
+ );
1130
+
1131
+ const cleanLabelProps = () => {
1132
+ const { ref: _ref1, ...rest } = ctx.labelProps() as Record<string, unknown>;
1133
+ return rest;
1134
+ };
1135
+ const cleanHoverProps = () => {
1136
+ const { ref: _ref2, ...rest } = hoverProps as Record<string, unknown>;
1137
+ return rest;
1138
+ };
1139
+ const cleanInputProps = () => {
1140
+ const {
1141
+ ref: _ref3,
1142
+ onFocus: _onFocus,
1143
+ onBlur: _onBlur,
1144
+ ...rest
1145
+ } = ctx.inputProps() as Record<string, unknown>;
1146
+ return rest;
1147
+ };
1148
+ const cleanFocusProps = () => {
1149
+ const {
1150
+ ref: _ref4,
1151
+ onFocus: _onFocus,
1152
+ onBlur: _onBlur,
1153
+ ...rest
1154
+ } = focusProps as Record<string, unknown>;
1155
+ return rest;
1156
+ };
1157
+ const handleInputFocus: JSX.EventHandler<HTMLInputElement, FocusEvent> = (event) => {
1158
+ (
1159
+ ctx.inputProps() as unknown as { onFocus?: JSX.EventHandler<HTMLInputElement, FocusEvent> }
1160
+ ).onFocus?.(event);
1161
+ (
1162
+ focusProps as unknown as { onFocus?: JSX.EventHandler<HTMLInputElement, FocusEvent> }
1163
+ ).onFocus?.(event);
1164
+ };
1165
+ const handleInputBlur: JSX.EventHandler<HTMLInputElement, FocusEvent> = (event) => {
1166
+ (
1167
+ ctx.inputProps() as unknown as { onBlur?: JSX.EventHandler<HTMLInputElement, FocusEvent> }
1168
+ ).onBlur?.(event);
1169
+ (focusProps as unknown as { onBlur?: JSX.EventHandler<HTMLInputElement, FocusEvent> }).onBlur?.(
1170
+ event,
1171
+ );
1172
+ };
1173
+ const handleInputClick: JSX.EventHandler<HTMLInputElement, MouseEvent> = (event) => {
1174
+ (
1175
+ ctx.inputProps() as unknown as { onClick?: JSX.EventHandler<HTMLInputElement, MouseEvent> }
1176
+ ).onClick?.(event);
1177
+ };
1178
+ const handleInputInvalid: JSX.EventHandler<HTMLInputElement, Event> = (event) => {
1179
+ state.updateValidation(getNativeValidation(event.currentTarget));
1180
+ state.commitValidation();
1181
+ event.currentTarget.focus();
1182
+ event.preventDefault();
1183
+ };
1184
+ const handleInputChange: JSX.EventHandler<HTMLInputElement, Event> = (event) => {
1185
+ (
1186
+ ctx.inputProps() as unknown as { onChange?: JSX.EventHandler<HTMLInputElement, Event> }
1187
+ ).onChange?.(event);
1188
+ state.updateValidation(
1189
+ event.currentTarget.validity.valid
1190
+ ? validValidation
1191
+ : getNativeValidation(event.currentTarget),
1192
+ );
1193
+ state.commitValidation();
1194
+ };
1195
+ const setButtonRef = (el: HTMLLabelElement) => {
1196
+ assignRef(props.buttonProps.ref, el);
1197
+ };
1198
+
1199
+ return (
1200
+ <label
1201
+ {...cleanLabelProps()}
1202
+ {...cleanHoverProps()}
1203
+ ref={setButtonRef}
1204
+ class={renderProps.class()}
1205
+ style={renderProps.style()}
1206
+ slot={props.buttonProps.slot}
1207
+ data-selected={ctx.isSelected() || undefined}
1208
+ data-pressed={ctx.isPressed() || undefined}
1209
+ data-hovered={isHovered() || undefined}
1210
+ data-focused={isFocused() || undefined}
1211
+ data-focus-visible={isFocusVisible() || undefined}
1212
+ data-disabled={ctx.isDisabled() || undefined}
1213
+ data-readonly={state.isReadOnly || undefined}
1214
+ data-invalid={state.isInvalid || undefined}
1215
+ data-required={state.isRequired || undefined}
1216
+ >
1217
+ <VisuallyHidden>
1218
+ <input
1219
+ ref={ctx.setInputRef}
1220
+ {...cleanInputProps()}
1221
+ {...cleanFocusProps()}
1222
+ onFocus={handleInputFocus}
1223
+ onBlur={handleInputBlur}
1224
+ onInvalid={handleInputInvalid}
1225
+ onChange={handleInputChange}
1226
+ onClick={handleInputClick}
1227
+ />
1228
+ </VisuallyHidden>
1229
+ {renderProps.renderChildren()}
1230
+ </label>
1231
+ );
1232
+ }
@@ -196,6 +196,13 @@ export function useRangeCalendarContext(): RangeCalendarState<DateValue> {
196
196
  export function RangeCalendar<T extends DateValue = CalendarDate>(
197
197
  props: RangeCalendarProps<T>,
198
198
  ): JSX.Element {
199
+ // When rendered inside a composite that owns the range state (e.g.
200
+ // DateRangePicker provides `RangeCalendarContext`), reuse that shared state
201
+ // instead of minting a fresh one — otherwise the composite's focus/anchor
202
+ // flags (e.g. the popover-open `setFocused(true)`) drive a different state
203
+ // instance than the one this grid renders. Mirrors the single `Calendar`.
204
+ const inheritedState = useContext(RangeCalendarContext);
205
+
199
206
  // Use hydration-safe pattern for client-only rendering
200
207
  const isHydrated = useIsHydrated();
201
208
 
@@ -209,11 +216,106 @@ export function RangeCalendar<T extends DateValue = CalendarDate>(
209
216
  />
210
217
  }
211
218
  >
212
- <RangeCalendarInner {...props} />
219
+ <Show when={inheritedState} fallback={<RangeCalendarInner {...props} />}>
220
+ <RangeCalendarWithState
221
+ state={inheritedState as RangeCalendarState<DateValue>}
222
+ {...props}
223
+ />
224
+ </Show>
213
225
  </Show>
214
226
  );
215
227
  }
216
228
 
229
+ /**
230
+ * RangeCalendar variant that renders against an externally-owned state (from
231
+ * `RangeCalendarContext`) rather than creating its own. Mirrors the single
232
+ * `CalendarWithState`.
233
+ */
234
+ function RangeCalendarWithState<T extends DateValue = CalendarDate>(
235
+ props: RangeCalendarProps<T> & { state: RangeCalendarState<DateValue> },
236
+ ): JSX.Element {
237
+ const [local, , rest] = splitProps(
238
+ props,
239
+ ["children", "class", "style", "slot", "state", "ref"],
240
+ [
241
+ "value",
242
+ "defaultValue",
243
+ "onChange",
244
+ "minValue",
245
+ "maxValue",
246
+ "isDisabled",
247
+ "isReadOnly",
248
+ "focusedValue",
249
+ "defaultFocusedValue",
250
+ "onFocusChange",
251
+ "locale",
252
+ "createCalendar",
253
+ "isDateUnavailable",
254
+ "visibleMonths",
255
+ "pageBehavior",
256
+ "selectionAlignment",
257
+ "isDateDisabled",
258
+ "validationState",
259
+ "allowsNonContiguousRanges",
260
+ "firstDayOfWeek",
261
+ ],
262
+ );
263
+
264
+ const state = () => props.state;
265
+
266
+ const calendarAria = createRangeCalendar(rest, state());
267
+
268
+ const renderValues = createMemo<RangeCalendarRenderProps>(() => ({
269
+ isDisabled: state().isDisabled(),
270
+ isReadOnly: state().isReadOnly(),
271
+ isInvalid: state().isValueInvalid(),
272
+ isDragging: state().isDragging(),
273
+ }));
274
+
275
+ const renderProps = useRenderProps(
276
+ {
277
+ class: local.class,
278
+ style: local.style,
279
+ defaultClassName: "solidaria-RangeCalendar",
280
+ },
281
+ renderValues,
282
+ );
283
+
284
+ return (
285
+ <RangeCalendarStateContext.Provider value={state()}>
286
+ <RangeCalendarContext.Provider value={state()}>
287
+ <div
288
+ {...calendarAria.calendarProps}
289
+ ref={(el) => assignRef(local.ref, el)}
290
+ class={renderProps.class()}
291
+ style={renderProps.style()}
292
+ data-disabled={dataAttr(state().isDisabled())}
293
+ data-readonly={dataAttr(state().isReadOnly())}
294
+ data-invalid={dataAttr(state().isValueInvalid())}
295
+ data-dragging={dataAttr(state().isDragging())}
296
+ >
297
+ <VisuallyHidden>
298
+ <h2>{String(calendarAria.calendarProps["aria-label"] ?? "")}</h2>
299
+ </VisuallyHidden>
300
+ {props.children}
301
+ {/* For touch screen readers, a visually hidden next button after the
302
+ * month grid so it's easy to navigate after reaching the end without
303
+ * going all the way back to the start. Mirrors react-aria-components
304
+ * Calendar (and the headless Calendar). */}
305
+ <VisuallyHidden>
306
+ <button
307
+ aria-label={String(calendarAria.nextButtonProps["aria-label"] ?? "")}
308
+ disabled={Boolean(calendarAria.nextButtonProps.disabled)}
309
+ onClick={() => state().focusNextPage()}
310
+ tabIndex={-1}
311
+ />
312
+ </VisuallyHidden>
313
+ </div>
314
+ </RangeCalendarContext.Provider>
315
+ </RangeCalendarStateContext.Provider>
316
+ );
317
+ }
318
+
217
319
  /**
218
320
  * Internal RangeCalendar component that renders after client mount.
219
321
  */
@@ -284,6 +386,18 @@ function RangeCalendarInner<T extends DateValue = CalendarDate>(
284
386
  <h2>{String(calendarAria.calendarProps["aria-label"] ?? "")}</h2>
285
387
  </VisuallyHidden>
286
388
  {props.children}
389
+ {/* For touch screen readers, a visually hidden next button after the
390
+ * month grid so it's easy to navigate after reaching the end without
391
+ * going all the way back to the start. Mirrors react-aria-components
392
+ * Calendar (and the headless Calendar). */}
393
+ <VisuallyHidden>
394
+ <button
395
+ aria-label={String(calendarAria.nextButtonProps["aria-label"] ?? "")}
396
+ disabled={Boolean(calendarAria.nextButtonProps.disabled)}
397
+ onClick={() => state.focusNextPage()}
398
+ tabIndex={-1}
399
+ />
400
+ </VisuallyHidden>
287
401
  </div>
288
402
  </RangeCalendarContext.Provider>
289
403
  </RangeCalendarStateContext.Provider>
@@ -350,6 +464,11 @@ export function RangeCalendarButton(props: RangeCalendarButtonProps): JSX.Elemen
350
464
  class={props.class ?? "solidaria-RangeCalendarButton"}
351
465
  style={props.style}
352
466
  disabled={isDisabled()}
467
+ // Mirror useFocusable (same as CalendarButton): always set an explicit
468
+ // tabIndex on an enabled focusable nav button (0), and drop it entirely
469
+ // when disabled — so the prev/next buttons appear in the roving tab order
470
+ // exactly as the S2 oracle does.
471
+ tabindex={isDisabled() ? undefined : 0}
353
472
  >
354
473
  {props.children}
355
474
  </button>
@@ -508,7 +627,9 @@ export function RangeCalendarCell(props: RangeCalendarCellProps): JSX.Element {
508
627
 
509
628
  const renderProps = useRenderProps(
510
629
  {
511
- children: props.children,
630
+ get children() {
631
+ return props.children;
632
+ },
512
633
  class: props.class,
513
634
  style: props.style,
514
635
  defaultClassName: "solidaria-RangeCalendarCell",