@proyecto-viviana/solidaria-components 0.3.2 → 0.4.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 (398) hide show
  1. package/dist/ActionBar.js +184 -0
  2. package/dist/ActionBar.js.map +1 -0
  3. package/dist/ActionBar.jsx +128 -0
  4. package/dist/ActionBar.jsx.map +1 -0
  5. package/dist/ActionGroup.js +179 -0
  6. package/dist/ActionGroup.js.map +1 -0
  7. package/dist/ActionGroup.jsx +129 -0
  8. package/dist/ActionGroup.jsx.map +1 -0
  9. package/dist/Alert.js +111 -0
  10. package/dist/Alert.js.map +1 -0
  11. package/dist/Alert.jsx +82 -0
  12. package/dist/Alert.jsx.map +1 -0
  13. package/dist/Autocomplete.js +130 -0
  14. package/dist/Autocomplete.js.map +1 -0
  15. package/dist/Autocomplete.jsx +116 -0
  16. package/dist/Autocomplete.jsx.map +1 -0
  17. package/dist/Breadcrumbs.d.ts.map +1 -1
  18. package/dist/Breadcrumbs.js +334 -0
  19. package/dist/Breadcrumbs.js.map +1 -0
  20. package/dist/Breadcrumbs.jsx +266 -0
  21. package/dist/Breadcrumbs.jsx.map +1 -0
  22. package/dist/Button.d.ts.map +1 -1
  23. package/dist/Button.js +348 -0
  24. package/dist/Button.js.map +1 -0
  25. package/dist/Button.jsx +314 -0
  26. package/dist/Button.jsx.map +1 -0
  27. package/dist/Calendar.d.ts +4 -4
  28. package/dist/Calendar.d.ts.map +1 -1
  29. package/dist/Calendar.js +538 -0
  30. package/dist/Calendar.js.map +1 -0
  31. package/dist/Calendar.jsx +333 -0
  32. package/dist/Calendar.jsx.map +1 -0
  33. package/dist/CalendarPicker.d.ts +33 -0
  34. package/dist/CalendarPicker.d.ts.map +1 -0
  35. package/dist/CalendarPicker.js +44 -0
  36. package/dist/CalendarPicker.js.map +1 -0
  37. package/dist/CalendarPicker.jsx +44 -0
  38. package/dist/CalendarPicker.jsx.map +1 -0
  39. package/dist/Checkbox.d.ts +83 -4
  40. package/dist/Checkbox.d.ts.map +1 -1
  41. package/dist/Checkbox.js +923 -0
  42. package/dist/Checkbox.js.map +1 -0
  43. package/dist/Checkbox.jsx +707 -0
  44. package/dist/Checkbox.jsx.map +1 -0
  45. package/dist/Collection.d.ts.map +1 -1
  46. package/dist/Collection.js +202 -0
  47. package/dist/Collection.js.map +1 -0
  48. package/dist/Collection.jsx +152 -0
  49. package/dist/Collection.jsx.map +1 -0
  50. package/dist/Color.d.ts.map +1 -1
  51. package/dist/Color.js +1720 -0
  52. package/dist/Color.js.map +1 -0
  53. package/dist/Color.jsx +1344 -0
  54. package/dist/Color.jsx.map +1 -0
  55. package/dist/ColorEditor.js +208 -0
  56. package/dist/ColorEditor.js.map +1 -0
  57. package/dist/ColorEditor.jsx +120 -0
  58. package/dist/ColorEditor.jsx.map +1 -0
  59. package/dist/ComboBox.d.ts +4 -4
  60. package/dist/ComboBox.d.ts.map +1 -1
  61. package/dist/ComboBox.js +992 -0
  62. package/dist/ComboBox.js.map +1 -0
  63. package/dist/ComboBox.jsx +714 -0
  64. package/dist/ComboBox.jsx.map +1 -0
  65. package/dist/ContextualHelpTrigger.js +146 -0
  66. package/dist/ContextualHelpTrigger.js.map +1 -0
  67. package/dist/ContextualHelpTrigger.jsx +109 -0
  68. package/dist/ContextualHelpTrigger.jsx.map +1 -0
  69. package/dist/DateField.d.ts +3 -3
  70. package/dist/DateField.d.ts.map +1 -1
  71. package/dist/DateField.js +4 -0
  72. package/dist/DateField.jsx +4 -0
  73. package/dist/DateField2.js +499 -0
  74. package/dist/DateField2.js.map +1 -0
  75. package/dist/DateField2.jsx +317 -0
  76. package/dist/DateField2.jsx.map +1 -0
  77. package/dist/DatePicker.d.ts +4 -4
  78. package/dist/DatePicker.d.ts.map +1 -1
  79. package/dist/DatePicker.js +991 -0
  80. package/dist/DatePicker.js.map +1 -0
  81. package/dist/DatePicker.jsx +682 -0
  82. package/dist/DatePicker.jsx.map +1 -0
  83. package/dist/Dialog.d.ts.map +1 -1
  84. package/dist/Dialog.js +337 -0
  85. package/dist/Dialog.js.map +1 -0
  86. package/dist/Dialog.jsx +192 -0
  87. package/dist/Dialog.jsx.map +1 -0
  88. package/dist/Disclosure.d.ts.map +1 -1
  89. package/dist/Disclosure.js +328 -0
  90. package/dist/Disclosure.js.map +1 -0
  91. package/dist/Disclosure.jsx +230 -0
  92. package/dist/Disclosure.jsx.map +1 -0
  93. package/dist/DragAndDrop.d.ts.map +1 -1
  94. package/dist/DragAndDrop.js +176 -0
  95. package/dist/DragAndDrop.js.map +1 -0
  96. package/dist/DragAndDrop.jsx +160 -0
  97. package/dist/DragAndDrop.jsx.map +1 -0
  98. package/dist/DropZone.d.ts.map +1 -1
  99. package/dist/DropZone.js +189 -0
  100. package/dist/DropZone.js.map +1 -0
  101. package/dist/DropZone.jsx +144 -0
  102. package/dist/DropZone.jsx.map +1 -0
  103. package/dist/FieldError.js +66 -0
  104. package/dist/FieldError.js.map +1 -0
  105. package/dist/FieldError.jsx +47 -0
  106. package/dist/FieldError.jsx.map +1 -0
  107. package/dist/FileTrigger.js +79 -0
  108. package/dist/FileTrigger.js.map +1 -0
  109. package/dist/FileTrigger.jsx +49 -0
  110. package/dist/FileTrigger.jsx.map +1 -0
  111. package/dist/Focusable.js +70 -0
  112. package/dist/Focusable.js.map +1 -0
  113. package/dist/Focusable.jsx +69 -0
  114. package/dist/Focusable.jsx.map +1 -0
  115. package/dist/Form.js +99 -0
  116. package/dist/Form.js.map +1 -0
  117. package/dist/Form.jsx +73 -0
  118. package/dist/Form.jsx.map +1 -0
  119. package/dist/GridList.d.ts +34 -2
  120. package/dist/GridList.d.ts.map +1 -1
  121. package/dist/GridList.js +723 -0
  122. package/dist/GridList.js.map +1 -0
  123. package/dist/GridList.jsx +552 -0
  124. package/dist/GridList.jsx.map +1 -0
  125. package/dist/HiddenTimeInput.js +104 -0
  126. package/dist/HiddenTimeInput.js.map +1 -0
  127. package/dist/HiddenTimeInput.jsx +69 -0
  128. package/dist/HiddenTimeInput.jsx.map +1 -0
  129. package/dist/Icon.d.ts.map +1 -1
  130. package/dist/Icon.js +127 -0
  131. package/dist/Icon.js.map +1 -0
  132. package/dist/Icon.jsx +72 -0
  133. package/dist/Icon.jsx.map +1 -0
  134. package/dist/Keyboard.js +30 -0
  135. package/dist/Keyboard.js.map +1 -0
  136. package/dist/Keyboard.jsx +24 -0
  137. package/dist/Keyboard.jsx.map +1 -0
  138. package/dist/Landmark.js +134 -0
  139. package/dist/Landmark.js.map +1 -0
  140. package/dist/Landmark.jsx +118 -0
  141. package/dist/Landmark.jsx.map +1 -0
  142. package/dist/Link.d.ts.map +1 -1
  143. package/dist/Link.js +219 -0
  144. package/dist/Link.js.map +1 -0
  145. package/dist/Link.jsx +189 -0
  146. package/dist/Link.jsx.map +1 -0
  147. package/dist/ListBox.d.ts.map +1 -1
  148. package/dist/ListBox.js +670 -0
  149. package/dist/ListBox.js.map +1 -0
  150. package/dist/ListBox.jsx +508 -0
  151. package/dist/ListBox.jsx.map +1 -0
  152. package/dist/Menu.d.ts +27 -9
  153. package/dist/Menu.d.ts.map +1 -1
  154. package/dist/Menu.js +1254 -0
  155. package/dist/Menu.js.map +1 -0
  156. package/dist/Menu.jsx +1042 -0
  157. package/dist/Menu.jsx.map +1 -0
  158. package/dist/Meter.d.ts.map +1 -1
  159. package/dist/Meter.js +123 -0
  160. package/dist/Meter.js.map +1 -0
  161. package/dist/Meter.jsx +107 -0
  162. package/dist/Meter.jsx.map +1 -0
  163. package/dist/Modal.d.ts.map +1 -1
  164. package/dist/Modal.js +380 -0
  165. package/dist/Modal.js.map +1 -0
  166. package/dist/Modal.jsx +314 -0
  167. package/dist/Modal.jsx.map +1 -0
  168. package/dist/NumberField.d.ts +3 -3
  169. package/dist/NumberField.d.ts.map +1 -1
  170. package/dist/NumberField.js +573 -0
  171. package/dist/NumberField.js.map +1 -0
  172. package/dist/NumberField.jsx +443 -0
  173. package/dist/NumberField.jsx.map +1 -0
  174. package/dist/Popover.d.ts +10 -2
  175. package/dist/Popover.d.ts.map +1 -1
  176. package/dist/Popover.js +464 -0
  177. package/dist/Popover.js.map +1 -0
  178. package/dist/Popover.jsx +333 -0
  179. package/dist/Popover.jsx.map +1 -0
  180. package/dist/Pressable.js +69 -0
  181. package/dist/Pressable.js.map +1 -0
  182. package/dist/Pressable.jsx +68 -0
  183. package/dist/Pressable.jsx.map +1 -0
  184. package/dist/ProgressBar.d.ts.map +1 -1
  185. package/dist/ProgressBar.js +130 -0
  186. package/dist/ProgressBar.js.map +1 -0
  187. package/dist/ProgressBar.jsx +114 -0
  188. package/dist/ProgressBar.jsx.map +1 -0
  189. package/dist/RadioGroup.d.ts +82 -4
  190. package/dist/RadioGroup.d.ts.map +1 -1
  191. package/dist/RadioGroup.js +931 -0
  192. package/dist/RadioGroup.js.map +1 -0
  193. package/dist/RadioGroup.jsx +681 -0
  194. package/dist/RadioGroup.jsx.map +1 -0
  195. package/dist/RangeCalendar.d.ts.map +1 -1
  196. package/dist/RangeCalendar.js +410 -0
  197. package/dist/RangeCalendar.js.map +1 -0
  198. package/dist/RangeCalendar.jsx +253 -0
  199. package/dist/RangeCalendar.jsx.map +1 -0
  200. package/dist/RouterProvider.js +118 -0
  201. package/dist/RouterProvider.js.map +1 -0
  202. package/dist/RouterProvider.jsx +112 -0
  203. package/dist/RouterProvider.jsx.map +1 -0
  204. package/dist/SearchField.d.ts +2 -2
  205. package/dist/SearchField.d.ts.map +1 -1
  206. package/dist/SearchField.js +643 -0
  207. package/dist/SearchField.js.map +1 -0
  208. package/dist/SearchField.jsx +526 -0
  209. package/dist/SearchField.jsx.map +1 -0
  210. package/dist/Select.d.ts.map +1 -1
  211. package/dist/Select.js +1031 -0
  212. package/dist/Select.js.map +1 -0
  213. package/dist/Select.jsx +744 -0
  214. package/dist/Select.jsx.map +1 -0
  215. package/dist/SelectionIndicator.d.ts.map +1 -1
  216. package/dist/SelectionIndicator.js +86 -0
  217. package/dist/SelectionIndicator.js.map +1 -0
  218. package/dist/SelectionIndicator.jsx +50 -0
  219. package/dist/SelectionIndicator.jsx.map +1 -0
  220. package/dist/Separator.js +97 -0
  221. package/dist/Separator.js.map +1 -0
  222. package/dist/Separator.jsx +85 -0
  223. package/dist/Separator.jsx.map +1 -0
  224. package/dist/SharedElementTransition.d.ts.map +1 -1
  225. package/dist/SharedElementTransition.js +165 -0
  226. package/dist/SharedElementTransition.js.map +1 -0
  227. package/dist/SharedElementTransition.jsx +138 -0
  228. package/dist/SharedElementTransition.jsx.map +1 -0
  229. package/dist/Slider.d.ts +33 -1
  230. package/dist/Slider.d.ts.map +1 -1
  231. package/dist/Slider.js +515 -0
  232. package/dist/Slider.js.map +1 -0
  233. package/dist/Slider.jsx +403 -0
  234. package/dist/Slider.jsx.map +1 -0
  235. package/dist/StepList.js +198 -0
  236. package/dist/StepList.js.map +1 -0
  237. package/dist/StepList.jsx +141 -0
  238. package/dist/StepList.jsx.map +1 -0
  239. package/dist/Switch.d.ts +65 -2
  240. package/dist/Switch.d.ts.map +1 -1
  241. package/dist/Switch.js +534 -0
  242. package/dist/Switch.js.map +1 -0
  243. package/dist/Switch.jsx +393 -0
  244. package/dist/Switch.jsx.map +1 -0
  245. package/dist/Table.d.ts +48 -0
  246. package/dist/Table.d.ts.map +1 -1
  247. package/dist/Table.js +1759 -0
  248. package/dist/Table.js.map +1 -0
  249. package/dist/Table.jsx +1348 -0
  250. package/dist/Table.jsx.map +1 -0
  251. package/dist/Tabs.d.ts +0 -4
  252. package/dist/Tabs.d.ts.map +1 -1
  253. package/dist/Tabs.js +595 -0
  254. package/dist/Tabs.js.map +1 -0
  255. package/dist/Tabs.jsx +367 -0
  256. package/dist/Tabs.jsx.map +1 -0
  257. package/dist/TagGroup.d.ts.map +1 -1
  258. package/dist/TagGroup.js +378 -0
  259. package/dist/TagGroup.js.map +1 -0
  260. package/dist/TagGroup.jsx +270 -0
  261. package/dist/TagGroup.jsx.map +1 -0
  262. package/dist/Text.d.ts +18 -6
  263. package/dist/Text.d.ts.map +1 -1
  264. package/dist/Text.js +42 -0
  265. package/dist/Text.js.map +1 -0
  266. package/dist/Text.jsx +35 -0
  267. package/dist/Text.jsx.map +1 -0
  268. package/dist/TextField.d.ts +6 -6
  269. package/dist/TextField.d.ts.map +1 -1
  270. package/dist/TextField.js +436 -0
  271. package/dist/TextField.js.map +1 -0
  272. package/dist/TextField.jsx +403 -0
  273. package/dist/TextField.jsx.map +1 -0
  274. package/dist/TimeField.d.ts +3 -3
  275. package/dist/TimeField.d.ts.map +1 -1
  276. package/dist/TimeField.js +372 -0
  277. package/dist/TimeField.js.map +1 -0
  278. package/dist/TimeField.jsx +224 -0
  279. package/dist/TimeField.jsx.map +1 -0
  280. package/dist/Toast.d.ts +14 -0
  281. package/dist/Toast.d.ts.map +1 -1
  282. package/dist/Toast.js +551 -0
  283. package/dist/Toast.js.map +1 -0
  284. package/dist/Toast.jsx +361 -0
  285. package/dist/Toast.jsx.map +1 -0
  286. package/dist/ToggleButton.d.ts.map +1 -1
  287. package/dist/ToggleButton.js +123 -0
  288. package/dist/ToggleButton.js.map +1 -0
  289. package/dist/ToggleButton.jsx +88 -0
  290. package/dist/ToggleButton.jsx.map +1 -0
  291. package/dist/ToggleButtonGroup.d.ts.map +1 -1
  292. package/dist/ToggleButtonGroup.js +114 -0
  293. package/dist/ToggleButtonGroup.js.map +1 -0
  294. package/dist/ToggleButtonGroup.jsx +85 -0
  295. package/dist/ToggleButtonGroup.jsx.map +1 -0
  296. package/dist/Toolbar.js +112 -0
  297. package/dist/Toolbar.js.map +1 -0
  298. package/dist/Toolbar.jsx +92 -0
  299. package/dist/Toolbar.jsx.map +1 -0
  300. package/dist/Tooltip.d.ts.map +1 -1
  301. package/dist/Tooltip.js +539 -0
  302. package/dist/Tooltip.js.map +1 -0
  303. package/dist/Tooltip.jsx +465 -0
  304. package/dist/Tooltip.jsx.map +1 -0
  305. package/dist/Tree.d.ts +14 -2
  306. package/dist/Tree.d.ts.map +1 -1
  307. package/dist/Tree.js +1424 -0
  308. package/dist/Tree.js.map +1 -0
  309. package/dist/Tree.jsx +1172 -0
  310. package/dist/Tree.jsx.map +1 -0
  311. package/dist/Virtualizer.d.ts +16 -2
  312. package/dist/Virtualizer.d.ts.map +1 -1
  313. package/dist/Virtualizer.js +2 -0
  314. package/dist/Virtualizer.jsx +2 -0
  315. package/dist/Virtualizer2.js +751 -0
  316. package/dist/Virtualizer2.js.map +1 -0
  317. package/dist/Virtualizer2.jsx +730 -0
  318. package/dist/Virtualizer2.jsx.map +1 -0
  319. package/dist/VirtualizerLayouts.d.ts +18 -0
  320. package/dist/VirtualizerLayouts.d.ts.map +1 -1
  321. package/dist/VisuallyHidden.js +42 -0
  322. package/dist/VisuallyHidden.js.map +1 -0
  323. package/dist/VisuallyHidden.jsx +35 -0
  324. package/dist/VisuallyHidden.jsx.map +1 -0
  325. package/dist/contexts.js +33 -0
  326. package/dist/contexts.js.map +1 -0
  327. package/dist/contexts.jsx +33 -0
  328. package/dist/contexts.jsx.map +1 -0
  329. package/dist/index.d.ts +7 -6
  330. package/dist/index.d.ts.map +1 -1
  331. package/dist/index.js +70 -23932
  332. package/dist/index.jsx +70 -18337
  333. package/dist/useDragAndDrop.js +2 -0
  334. package/dist/useDragAndDrop.jsx +2 -0
  335. package/dist/useDragAndDrop2.js +395 -0
  336. package/dist/useDragAndDrop2.js.map +1 -0
  337. package/dist/useDragAndDrop2.jsx +395 -0
  338. package/dist/useDragAndDrop2.jsx.map +1 -0
  339. package/dist/utils.d.ts +83 -11
  340. package/dist/utils.d.ts.map +1 -1
  341. package/dist/utils.js +285 -0
  342. package/dist/utils.js.map +1 -0
  343. package/dist/utils.jsx +277 -0
  344. package/dist/utils.jsx.map +1 -0
  345. package/dist/virtualizer/Layout.js +144 -0
  346. package/dist/virtualizer/Layout.js.map +1 -0
  347. package/dist/virtualizer/Layout.jsx +144 -0
  348. package/dist/virtualizer/Layout.jsx.map +1 -0
  349. package/package.json +401 -4
  350. package/src/Breadcrumbs.tsx +3 -1
  351. package/src/Button.tsx +22 -2
  352. package/src/Calendar.tsx +27 -10
  353. package/src/CalendarPicker.tsx +73 -0
  354. package/src/Checkbox.tsx +539 -3
  355. package/src/Collection.tsx +9 -3
  356. package/src/Color.tsx +42 -14
  357. package/src/ComboBox.tsx +50 -6
  358. package/src/DateField.tsx +20 -2
  359. package/src/DatePicker.tsx +57 -5
  360. package/src/Dialog.tsx +13 -5
  361. package/src/Disclosure.tsx +3 -1
  362. package/src/DragAndDrop.tsx +3 -1
  363. package/src/DropZone.tsx +3 -1
  364. package/src/GridList.tsx +125 -24
  365. package/src/Icon.tsx +3 -1
  366. package/src/Link.tsx +3 -1
  367. package/src/ListBox.tsx +31 -4
  368. package/src/Menu.tsx +252 -128
  369. package/src/Meter.tsx +3 -1
  370. package/src/Modal.tsx +100 -52
  371. package/src/NumberField.tsx +51 -8
  372. package/src/Popover.tsx +40 -10
  373. package/src/ProgressBar.tsx +3 -1
  374. package/src/RadioGroup.tsx +468 -4
  375. package/src/RangeCalendar.tsx +3 -1
  376. package/src/SearchField.tsx +27 -3
  377. package/src/Select.tsx +22 -7
  378. package/src/SelectionIndicator.tsx +3 -1
  379. package/src/SharedElementTransition.tsx +3 -1
  380. package/src/Slider.tsx +159 -13
  381. package/src/Switch.tsx +395 -2
  382. package/src/Table.tsx +409 -95
  383. package/src/Tabs.tsx +16 -45
  384. package/src/TagGroup.tsx +3 -1
  385. package/src/Text.tsx +31 -10
  386. package/src/TextField.tsx +43 -5
  387. package/src/TimeField.tsx +20 -2
  388. package/src/Toast.tsx +79 -14
  389. package/src/ToggleButton.tsx +3 -1
  390. package/src/ToggleButtonGroup.tsx +3 -1
  391. package/src/Tooltip.tsx +3 -1
  392. package/src/Tree.tsx +77 -9
  393. package/src/Virtualizer.tsx +180 -31
  394. package/src/VirtualizerLayouts.ts +45 -10
  395. package/src/index.ts +50 -0
  396. package/src/utils.tsx +243 -27
  397. package/dist/index.js.map +0 -1
  398. package/dist/index.jsx.map +0 -1
@@ -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
+ }
@@ -508,7 +508,9 @@ export function RangeCalendarCell(props: RangeCalendarCellProps): JSX.Element {
508
508
 
509
509
  const renderProps = useRenderProps(
510
510
  {
511
- children: props.children,
511
+ get children() {
512
+ return props.children;
513
+ },
512
514
  class: props.class,
513
515
  style: props.style,
514
516
  defaultClassName: "solidaria-RangeCalendarCell",
@@ -7,6 +7,7 @@
7
7
 
8
8
  import {
9
9
  type JSX,
10
+ type Context,
10
11
  createContext,
11
12
  createMemo,
12
13
  onCleanup,
@@ -38,7 +39,9 @@ import {
38
39
  type SlotProps,
39
40
  useRenderProps,
40
41
  filterDOMProps,
42
+ Provider,
41
43
  } from "./utils";
44
+ import { TextContext } from "./Text";
42
45
 
43
46
  export interface SearchFieldRenderProps {
44
47
  /** Whether the search field is empty. */
@@ -456,7 +459,9 @@ export function SearchField(props: SearchFieldProps): JSX.Element {
456
459
 
457
460
  const renderProps = useRenderProps(
458
461
  {
459
- children: local.children,
462
+ get children() {
463
+ return local.children;
464
+ },
460
465
  class: local.class,
461
466
  style: local.style,
462
467
  defaultClassName: "solidaria-SearchField",
@@ -544,6 +549,21 @@ export function SearchField(props: SearchFieldProps): JSX.Element {
544
549
  const children = local.children;
545
550
  return typeof children === "function" ? children(childRenderValues) : children;
546
551
  };
552
+ // Provide the description / errorMessage props as `TextContext` slots (mirrors
553
+ // react-aria-components' SearchField), so a `<Text slot="description">` /
554
+ // `<Text slot="errorMessage">` child picks up the `id` its `aria-describedby`
555
+ // references. Additive: existing consumers reading these off
556
+ // `SearchFieldContext` are unaffected.
557
+ const textSlots = {
558
+ slots: {
559
+ get description() {
560
+ return searchFieldAria.descriptionProps;
561
+ },
562
+ get errorMessage() {
563
+ return searchFieldAria.errorMessageProps;
564
+ },
565
+ },
566
+ };
547
567
 
548
568
  return (
549
569
  <FieldErrorContext.Provider value={fieldErrorContext}>
@@ -559,7 +579,9 @@ export function SearchField(props: SearchFieldProps): JSX.Element {
559
579
  data-required={ariaProps.isRequired || undefined}
560
580
  data-readonly={ariaProps.isReadOnly || undefined}
561
581
  >
562
- {fieldChildren()}
582
+ <Provider values={[[TextContext, textSlots]] as Array<[Context<unknown>, unknown]>}>
583
+ {fieldChildren()}
584
+ </Provider>
563
585
  </div>
564
586
  </SearchFieldContext.Provider>
565
587
  </FieldErrorContext.Provider>
@@ -758,7 +780,9 @@ export function SearchFieldClearButton(props: SearchFieldClearButtonProps): JSX.
758
780
 
759
781
  const renderProps = useRenderProps(
760
782
  {
761
- children: props.children,
783
+ get children() {
784
+ return props.children;
785
+ },
762
786
  class: local.class,
763
787
  style: local.style,
764
788
  defaultClassName: "solidaria-SearchField-clear",
package/src/Select.tsx CHANGED
@@ -766,7 +766,9 @@ export function SelectTrigger(props: SelectTriggerProps): JSX.Element {
766
766
 
767
767
  const renderProps = useRenderProps(
768
768
  {
769
- children: props.children,
769
+ get children() {
770
+ return props.children;
771
+ },
770
772
  class: local.class,
771
773
  style: local.style,
772
774
  defaultClassName: "solidaria-Select-trigger",
@@ -871,7 +873,9 @@ export function SelectValue<T>(props: SelectValueProps<T>): JSX.Element {
871
873
 
872
874
  const renderProps = useRenderProps(
873
875
  {
874
- children: props.children ?? defaultSelectValueChildren,
876
+ get children() {
877
+ return props.children ?? defaultSelectValueChildren;
878
+ },
875
879
  class: local.class,
876
880
  style: local.style,
877
881
  defaultClassName: "solidaria-Select-value",
@@ -959,7 +963,9 @@ export function SelectListBox<T>(props: SelectListBoxProps<T>): JSX.Element {
959
963
  );
960
964
 
961
965
  const renderValues = createMemo<SelectListBoxRenderProps>(() => ({
962
- isFocused: state.isFocused(),
966
+ // Focus within the listbox itself (the selection manager's focus state),
967
+ // not the select-level trigger focus.
968
+ isFocused: state.selectionManager.isFocused,
963
969
  }));
964
970
 
965
971
  const renderProps = useRenderProps(
@@ -1004,7 +1010,7 @@ export function SelectListBox<T>(props: SelectListBoxProps<T>): JSX.Element {
1004
1010
  {...cleanListBoxProps()}
1005
1011
  class={renderProps.class()}
1006
1012
  style={renderProps.style()}
1007
- data-focused={state.isFocused() || undefined}
1013
+ data-focused={state.selectionManager.isFocused || undefined}
1008
1014
  data-empty={state.collection().size === 0 || undefined}
1009
1015
  >
1010
1016
  {state.collection().size === 0 && local.renderEmptyState ? (
@@ -1072,6 +1078,7 @@ export function SelectOption<T>(props: SelectOptionProps<T>): JSX.Element {
1072
1078
  }
1073
1079
  const state = context as SelectState<T>;
1074
1080
  const selectContext = useContext(SelectContext) as SelectContextValue<T> | null;
1081
+ const [ref, setRef] = createSignal<HTMLLIElement | null>(null);
1075
1082
 
1076
1083
  const optionAria = createOption<T>(
1077
1084
  {
@@ -1126,6 +1133,7 @@ export function SelectOption<T>(props: SelectOptionProps<T>): JSX.Element {
1126
1133
  }
1127
1134
  },
1128
1135
  },
1136
+ () => ref(),
1129
1137
  );
1130
1138
  const isOptionFocusVisible = () =>
1131
1139
  optionAria.isFocused() && (selectContext?.isFocusVisible() ?? optionAria.isFocusVisible());
@@ -1141,7 +1149,9 @@ export function SelectOption<T>(props: SelectOptionProps<T>): JSX.Element {
1141
1149
 
1142
1150
  const renderProps = useRenderProps(
1143
1151
  {
1144
- children: props.children,
1152
+ get children() {
1153
+ return props.children;
1154
+ },
1145
1155
  class: local.class,
1146
1156
  style: local.style,
1147
1157
  defaultClassName: "solidaria-Select-option",
@@ -1200,6 +1210,7 @@ export function SelectOption<T>(props: SelectOptionProps<T>): JSX.Element {
1200
1210
  return (
1201
1211
  <SelectionIndicatorContext.Provider value={selectionIndicatorContext()}>
1202
1212
  <li
1213
+ ref={setRef}
1203
1214
  {...cleanOptionProps()}
1204
1215
  class={renderProps.class()}
1205
1216
  style={renderProps.style()}
@@ -1254,8 +1265,12 @@ function createSelectListStateAdapter<T>(state: SelectState<T>): ListState<T> {
1254
1265
 
1255
1266
  return {
1256
1267
  collection: state.collection,
1257
- isFocused: state.isFocused,
1258
- setFocused: state.setFocused,
1268
+ selectionManager: state.selectionManager,
1269
+ // Collection-level focus (the selection manager's), not the select-level
1270
+ // trigger focus — upstream listbox internals only ever read
1271
+ // state.selectionManager.isFocused.
1272
+ isFocused: () => state.selectionManager.isFocused,
1273
+ setFocused: (isFocused) => state.selectionManager.setFocused(isFocused),
1259
1274
  focusedKey: state.focusedKey,
1260
1275
  setFocusedKey: (key) => state.setFocusedKey(key ?? null),
1261
1276
  childFocusStrategy: () => null,