@proyecto-viviana/solidaria-components 0.5.0 → 0.7.0-rc.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 (561) hide show
  1. package/LICENSE-APACHE-2.0 +201 -0
  2. package/NOTICE +91 -0
  3. package/README.md +4 -7
  4. package/dist/ActionBar.d.ts +2 -10
  5. package/dist/ActionBar.d.ts.map +1 -1
  6. package/dist/ActionBar.js +95 -59
  7. package/dist/ActionBar.js.map +1 -1
  8. package/dist/ActionBar.jsx +42 -15
  9. package/dist/ActionBar.jsx.map +1 -1
  10. package/dist/ActionGroup.d.ts +1 -12
  11. package/dist/ActionGroup.d.ts.map +1 -1
  12. package/dist/ActionGroup.js +66 -44
  13. package/dist/ActionGroup.js.map +1 -1
  14. package/dist/ActionGroup.jsx +25 -10
  15. package/dist/ActionGroup.jsx.map +1 -1
  16. package/dist/Alert.d.ts +4 -1
  17. package/dist/Alert.d.ts.map +1 -1
  18. package/dist/Alert.js +25 -17
  19. package/dist/Alert.js.map +1 -1
  20. package/dist/Alert.jsx +9 -5
  21. package/dist/Alert.jsx.map +1 -1
  22. package/dist/Autocomplete.d.ts +2 -9
  23. package/dist/Autocomplete.d.ts.map +1 -1
  24. package/dist/Autocomplete.js +18 -5
  25. package/dist/Autocomplete.js.map +1 -1
  26. package/dist/Autocomplete.jsx +20 -7
  27. package/dist/Autocomplete.jsx.map +1 -1
  28. package/dist/Breadcrumbs.d.ts +2 -7
  29. package/dist/Breadcrumbs.d.ts.map +1 -1
  30. package/dist/Breadcrumbs.js +71 -51
  31. package/dist/Breadcrumbs.js.map +1 -1
  32. package/dist/Breadcrumbs.jsx +25 -12
  33. package/dist/Breadcrumbs.jsx.map +1 -1
  34. package/dist/Button.d.ts +1 -7
  35. package/dist/Button.d.ts.map +1 -1
  36. package/dist/Button.js +125 -72
  37. package/dist/Button.js.map +1 -1
  38. package/dist/Button.jsx +105 -43
  39. package/dist/Button.jsx.map +1 -1
  40. package/dist/Calendar.d.ts +1 -7
  41. package/dist/Calendar.d.ts.map +1 -1
  42. package/dist/Calendar.js +297 -221
  43. package/dist/Calendar.js.map +1 -1
  44. package/dist/Calendar.jsx +52 -32
  45. package/dist/Calendar.jsx.map +1 -1
  46. package/dist/CalendarPicker.d.ts +1 -10
  47. package/dist/CalendarPicker.d.ts.map +1 -1
  48. package/dist/CalendarPicker.js +12 -0
  49. package/dist/CalendarPicker.js.map +1 -1
  50. package/dist/CalendarPicker.jsx +12 -0
  51. package/dist/CalendarPicker.jsx.map +1 -1
  52. package/dist/Checkbox.d.ts +2 -7
  53. package/dist/Checkbox.d.ts.map +1 -1
  54. package/dist/Checkbox.js +332 -270
  55. package/dist/Checkbox.js.map +1 -1
  56. package/dist/Checkbox.jsx +70 -77
  57. package/dist/Checkbox.jsx.map +1 -1
  58. package/dist/Collection.d.ts +33 -7
  59. package/dist/Collection.d.ts.map +1 -1
  60. package/dist/Collection.js +128 -74
  61. package/dist/Collection.js.map +1 -1
  62. package/dist/Collection.jsx +46 -7
  63. package/dist/Collection.jsx.map +1 -1
  64. package/dist/Color.d.ts +15 -20
  65. package/dist/Color.d.ts.map +1 -1
  66. package/dist/Color.js +572 -334
  67. package/dist/Color.js.map +1 -1
  68. package/dist/Color.jsx +205 -85
  69. package/dist/Color.jsx.map +1 -1
  70. package/dist/ColorEditor.d.ts +3 -1
  71. package/dist/ColorEditor.d.ts.map +1 -1
  72. package/dist/ColorEditor.js +147 -124
  73. package/dist/ColorEditor.js.map +1 -1
  74. package/dist/ColorEditor.jsx +4 -1
  75. package/dist/ColorEditor.jsx.map +1 -1
  76. package/dist/ComboBox.d.ts +31 -10
  77. package/dist/ComboBox.d.ts.map +1 -1
  78. package/dist/ComboBox.js +539 -274
  79. package/dist/ComboBox.js.map +1 -1
  80. package/dist/ComboBox.jsx +213 -70
  81. package/dist/ComboBox.jsx.map +1 -1
  82. package/dist/ContextualHelpTrigger.d.ts +1 -7
  83. package/dist/ContextualHelpTrigger.d.ts.map +1 -1
  84. package/dist/ContextualHelpTrigger.js +102 -72
  85. package/dist/ContextualHelpTrigger.js.map +1 -1
  86. package/dist/ContextualHelpTrigger.jsx +36 -16
  87. package/dist/ContextualHelpTrigger.jsx.map +1 -1
  88. package/dist/DateField.d.ts +2 -7
  89. package/dist/DateField.d.ts.map +1 -1
  90. package/dist/DateField.js +0 -2
  91. package/dist/DateField.jsx +0 -2
  92. package/dist/DateField2.js +194 -179
  93. package/dist/DateField2.js.map +1 -1
  94. package/dist/DateField2.jsx +58 -55
  95. package/dist/DateField2.jsx.map +1 -1
  96. package/dist/DatePicker.d.ts +7 -7
  97. package/dist/DatePicker.d.ts.map +1 -1
  98. package/dist/DatePicker.js +2 -2
  99. package/dist/DatePicker.jsx +2 -2
  100. package/dist/DatePicker2.js +357 -435
  101. package/dist/DatePicker2.js.map +1 -1
  102. package/dist/DatePicker2.jsx +109 -178
  103. package/dist/DatePicker2.jsx.map +1 -1
  104. package/dist/DateRangePickerContext.d.ts +7 -0
  105. package/dist/DateRangePickerContext.d.ts.map +1 -1
  106. package/dist/Dialog.d.ts +4 -9
  107. package/dist/Dialog.d.ts.map +1 -1
  108. package/dist/Dialog.js +168 -101
  109. package/dist/Dialog.js.map +1 -1
  110. package/dist/Dialog.jsx +75 -27
  111. package/dist/Dialog.jsx.map +1 -1
  112. package/dist/Disclosure.d.ts +1 -9
  113. package/dist/Disclosure.d.ts.map +1 -1
  114. package/dist/Disclosure.js +121 -79
  115. package/dist/Disclosure.js.map +1 -1
  116. package/dist/Disclosure.jsx +32 -19
  117. package/dist/Disclosure.jsx.map +1 -1
  118. package/dist/DragAndDrop.d.ts +9 -6
  119. package/dist/DragAndDrop.d.ts.map +1 -1
  120. package/dist/DragAndDrop.js +37 -22
  121. package/dist/DragAndDrop.js.map +1 -1
  122. package/dist/DragAndDrop.jsx +20 -4
  123. package/dist/DragAndDrop.jsx.map +1 -1
  124. package/dist/DragPreview.d.ts +1 -1
  125. package/dist/DragPreview.d.ts.map +1 -1
  126. package/dist/DropZone.d.ts +1 -7
  127. package/dist/DropZone.d.ts.map +1 -1
  128. package/dist/DropZone.js +74 -40
  129. package/dist/DropZone.js.map +1 -1
  130. package/dist/DropZone.jsx +24 -8
  131. package/dist/DropZone.jsx.map +1 -1
  132. package/dist/ElementTag.d.ts +9 -20
  133. package/dist/ElementTag.d.ts.map +1 -1
  134. package/dist/ElementTag.js +8 -210
  135. package/dist/ElementTag.js.map +1 -1
  136. package/dist/ElementTag.jsx +8 -57
  137. package/dist/ElementTag.jsx.map +1 -1
  138. package/dist/FieldError.d.ts +8 -7
  139. package/dist/FieldError.d.ts.map +1 -1
  140. package/dist/FieldError.js +33 -13
  141. package/dist/FieldError.js.map +1 -1
  142. package/dist/FieldError.jsx +20 -5
  143. package/dist/FieldError.jsx.map +1 -1
  144. package/dist/FileTrigger.d.ts +1 -7
  145. package/dist/FileTrigger.d.ts.map +1 -1
  146. package/dist/FileTrigger.js +30 -12
  147. package/dist/FileTrigger.js.map +1 -1
  148. package/dist/FileTrigger.jsx +15 -2
  149. package/dist/FileTrigger.jsx.map +1 -1
  150. package/dist/Focusable.d.ts +2 -8
  151. package/dist/Focusable.d.ts.map +1 -1
  152. package/dist/Focusable.js +23 -6
  153. package/dist/Focusable.js.map +1 -1
  154. package/dist/Focusable.jsx +22 -5
  155. package/dist/Focusable.jsx.map +1 -1
  156. package/dist/Form.d.ts +9 -8
  157. package/dist/Form.d.ts.map +1 -1
  158. package/dist/Form.js +66 -43
  159. package/dist/Form.js.map +1 -1
  160. package/dist/Form.jsx +36 -16
  161. package/dist/Form.jsx.map +1 -1
  162. package/dist/GridList.d.ts +5 -10
  163. package/dist/GridList.d.ts.map +1 -1
  164. package/dist/GridList.js +372 -243
  165. package/dist/GridList.js.map +1 -1
  166. package/dist/GridList.jsx +177 -99
  167. package/dist/GridList.jsx.map +1 -1
  168. package/dist/HiddenDateInput.d.ts +1 -7
  169. package/dist/HiddenDateInput.d.ts.map +1 -1
  170. package/dist/Icon.d.ts +4 -1
  171. package/dist/Icon.d.ts.map +1 -1
  172. package/dist/Icon.js +41 -37
  173. package/dist/Icon.js.map +1 -1
  174. package/dist/Icon.jsx +7 -3
  175. package/dist/Icon.jsx.map +1 -1
  176. package/dist/Keyboard.d.ts +1 -7
  177. package/dist/Keyboard.d.ts.map +1 -1
  178. package/dist/Keyboard.js +24 -11
  179. package/dist/Keyboard.js.map +1 -1
  180. package/dist/Keyboard.jsx +15 -2
  181. package/dist/Keyboard.jsx.map +1 -1
  182. package/dist/Label.d.ts +20 -0
  183. package/dist/Label.d.ts.map +1 -0
  184. package/dist/Label.js +60 -0
  185. package/dist/Label.js.map +1 -0
  186. package/dist/Label.jsx +47 -0
  187. package/dist/Label.jsx.map +1 -0
  188. package/dist/Landmark.d.ts +4 -1
  189. package/dist/Landmark.d.ts.map +1 -1
  190. package/dist/Landmark.js +10 -4
  191. package/dist/Landmark.js.map +1 -1
  192. package/dist/Landmark.jsx +5 -1
  193. package/dist/Landmark.jsx.map +1 -1
  194. package/dist/Link.d.ts +2 -7
  195. package/dist/Link.d.ts.map +1 -1
  196. package/dist/Link.js +56 -36
  197. package/dist/Link.js.map +1 -1
  198. package/dist/Link.jsx +52 -10
  199. package/dist/Link.jsx.map +1 -1
  200. package/dist/ListBox.d.ts +17 -13
  201. package/dist/ListBox.d.ts.map +1 -1
  202. package/dist/ListBox.js +647 -348
  203. package/dist/ListBox.js.map +1 -1
  204. package/dist/ListBox.jsx +339 -236
  205. package/dist/ListBox.jsx.map +1 -1
  206. package/dist/ListDropTargetDelegate.d.ts +3 -0
  207. package/dist/ListDropTargetDelegate.d.ts.map +1 -1
  208. package/dist/Menu.d.ts +39 -30
  209. package/dist/Menu.d.ts.map +1 -1
  210. package/dist/Menu.js +547 -368
  211. package/dist/Menu.js.map +1 -1
  212. package/dist/Menu.jsx +328 -230
  213. package/dist/Menu.jsx.map +1 -1
  214. package/dist/Meter.d.ts +6 -13
  215. package/dist/Meter.d.ts.map +1 -1
  216. package/dist/Meter.js +55 -18
  217. package/dist/Meter.js.map +1 -1
  218. package/dist/Meter.jsx +33 -8
  219. package/dist/Meter.jsx.map +1 -1
  220. package/dist/Modal.d.ts +1 -7
  221. package/dist/Modal.d.ts.map +1 -1
  222. package/dist/Modal.js +104 -56
  223. package/dist/Modal.js.map +1 -1
  224. package/dist/Modal.jsx +68 -38
  225. package/dist/Modal.jsx.map +1 -1
  226. package/dist/NumberField.d.ts +4 -7
  227. package/dist/NumberField.d.ts.map +1 -1
  228. package/dist/NumberField.js +243 -124
  229. package/dist/NumberField.js.map +1 -1
  230. package/dist/NumberField.jsx +115 -26
  231. package/dist/NumberField.jsx.map +1 -1
  232. package/dist/Popover.d.ts +15 -7
  233. package/dist/Popover.d.ts.map +1 -1
  234. package/dist/Popover.js +370 -181
  235. package/dist/Popover.js.map +1 -1
  236. package/dist/Popover.jsx +215 -82
  237. package/dist/Popover.jsx.map +1 -1
  238. package/dist/Pressable.d.ts +2 -8
  239. package/dist/Pressable.d.ts.map +1 -1
  240. package/dist/Pressable.js +23 -6
  241. package/dist/Pressable.js.map +1 -1
  242. package/dist/Pressable.jsx +22 -5
  243. package/dist/Pressable.jsx.map +1 -1
  244. package/dist/PreviewTrigger.d.ts +24 -0
  245. package/dist/PreviewTrigger.d.ts.map +1 -0
  246. package/dist/PreviewTrigger.js +128 -0
  247. package/dist/PreviewTrigger.js.map +1 -0
  248. package/dist/PreviewTrigger.jsx +117 -0
  249. package/dist/PreviewTrigger.jsx.map +1 -0
  250. package/dist/ProgressBar.d.ts +1 -7
  251. package/dist/ProgressBar.d.ts.map +1 -1
  252. package/dist/ProgressBar.js +34 -14
  253. package/dist/ProgressBar.js.map +1 -1
  254. package/dist/ProgressBar.jsx +17 -3
  255. package/dist/ProgressBar.jsx.map +1 -1
  256. package/dist/RadioGroup.d.ts +2 -7
  257. package/dist/RadioGroup.d.ts.map +1 -1
  258. package/dist/RadioGroup.js +388 -269
  259. package/dist/RadioGroup.js.map +1 -1
  260. package/dist/RadioGroup.jsx +110 -99
  261. package/dist/RadioGroup.jsx.map +1 -1
  262. package/dist/RangeCalendar.d.ts +3 -7
  263. package/dist/RangeCalendar.d.ts.map +1 -1
  264. package/dist/RangeCalendar.js +271 -202
  265. package/dist/RangeCalendar.js.map +1 -1
  266. package/dist/RangeCalendar.jsx +56 -37
  267. package/dist/RangeCalendar.jsx.map +1 -1
  268. package/dist/RouterProvider.d.ts +3 -9
  269. package/dist/RouterProvider.d.ts.map +1 -1
  270. package/dist/RouterProvider.js +15 -14
  271. package/dist/RouterProvider.js.map +1 -1
  272. package/dist/RouterProvider.jsx +14 -13
  273. package/dist/RouterProvider.jsx.map +1 -1
  274. package/dist/SearchField.d.ts +4 -7
  275. package/dist/SearchField.d.ts.map +1 -1
  276. package/dist/SearchField.js +191 -164
  277. package/dist/SearchField.js.map +1 -1
  278. package/dist/SearchField.jsx +69 -62
  279. package/dist/SearchField.jsx.map +1 -1
  280. package/dist/Select.d.ts +5 -10
  281. package/dist/Select.d.ts.map +1 -1
  282. package/dist/Select.js +565 -359
  283. package/dist/Select.js.map +1 -1
  284. package/dist/Select.jsx +281 -161
  285. package/dist/Select.jsx.map +1 -1
  286. package/dist/SelectionIndicator.d.ts +1 -4
  287. package/dist/SelectionIndicator.d.ts.map +1 -1
  288. package/dist/SelectionIndicator.js +29 -13
  289. package/dist/SelectionIndicator.js.map +1 -1
  290. package/dist/SelectionIndicator.jsx +18 -4
  291. package/dist/SelectionIndicator.jsx.map +1 -1
  292. package/dist/Separator.d.ts +1 -7
  293. package/dist/Separator.d.ts.map +1 -1
  294. package/dist/Separator.js +22 -6
  295. package/dist/Separator.js.map +1 -1
  296. package/dist/Separator.jsx +16 -3
  297. package/dist/Separator.jsx.map +1 -1
  298. package/dist/SharedElementTransition.d.ts +1 -10
  299. package/dist/SharedElementTransition.d.ts.map +1 -1
  300. package/dist/SharedElementTransition.js +68 -40
  301. package/dist/SharedElementTransition.js.map +1 -1
  302. package/dist/SharedElementTransition.jsx +49 -25
  303. package/dist/SharedElementTransition.jsx.map +1 -1
  304. package/dist/Slider.d.ts +2 -8
  305. package/dist/Slider.d.ts.map +1 -1
  306. package/dist/Slider.js +155 -97
  307. package/dist/Slider.js.map +1 -1
  308. package/dist/Slider.jsx +25 -12
  309. package/dist/Slider.jsx.map +1 -1
  310. package/dist/StepList.d.ts +1 -7
  311. package/dist/StepList.d.ts.map +1 -1
  312. package/dist/StepList.js +112 -68
  313. package/dist/StepList.js.map +1 -1
  314. package/dist/StepList.jsx +58 -20
  315. package/dist/StepList.jsx.map +1 -1
  316. package/dist/Switch.d.ts +2 -9
  317. package/dist/Switch.d.ts.map +1 -1
  318. package/dist/Switch.js +164 -114
  319. package/dist/Switch.js.map +1 -1
  320. package/dist/Switch.jsx +26 -13
  321. package/dist/Switch.jsx.map +1 -1
  322. package/dist/Table.d.ts +2 -1
  323. package/dist/Table.d.ts.map +1 -1
  324. package/dist/Table.js +689 -592
  325. package/dist/Table.js.map +1 -1
  326. package/dist/Table.jsx +317 -237
  327. package/dist/Table.jsx.map +1 -1
  328. package/dist/Tabs.d.ts +2 -7
  329. package/dist/Tabs.d.ts.map +1 -1
  330. package/dist/Tabs.js +278 -293
  331. package/dist/Tabs.js.map +1 -1
  332. package/dist/Tabs.jsx +68 -66
  333. package/dist/Tabs.jsx.map +1 -1
  334. package/dist/TagGroup.d.ts +4 -11
  335. package/dist/TagGroup.d.ts.map +1 -1
  336. package/dist/TagGroup.js +153 -115
  337. package/dist/TagGroup.js.map +1 -1
  338. package/dist/TagGroup.jsx +37 -24
  339. package/dist/TagGroup.jsx.map +1 -1
  340. package/dist/Text.d.ts +3 -3
  341. package/dist/Text.d.ts.map +1 -1
  342. package/dist/Text.js +24 -7
  343. package/dist/Text.js.map +1 -1
  344. package/dist/Text.jsx +17 -5
  345. package/dist/Text.jsx.map +1 -1
  346. package/dist/TextField.d.ts +3 -16
  347. package/dist/TextField.d.ts.map +1 -1
  348. package/dist/TextField.js +104 -129
  349. package/dist/TextField.js.map +1 -1
  350. package/dist/TextField.jsx +76 -102
  351. package/dist/TextField.jsx.map +1 -1
  352. package/dist/TimeField.d.ts +2 -15
  353. package/dist/TimeField.d.ts.map +1 -1
  354. package/dist/TimeField.js +86 -102
  355. package/dist/TimeField.js.map +1 -1
  356. package/dist/TimeField.jsx +32 -49
  357. package/dist/TimeField.jsx.map +1 -1
  358. package/dist/Toast.d.ts +10 -13
  359. package/dist/Toast.d.ts.map +1 -1
  360. package/dist/Toast.js +209 -108
  361. package/dist/Toast.js.map +1 -1
  362. package/dist/Toast.jsx +127 -22
  363. package/dist/Toast.jsx.map +1 -1
  364. package/dist/ToggleButton.d.ts +1 -7
  365. package/dist/ToggleButton.d.ts.map +1 -1
  366. package/dist/ToggleButton.js +57 -35
  367. package/dist/ToggleButton.js.map +1 -1
  368. package/dist/ToggleButton.jsx +26 -12
  369. package/dist/ToggleButton.jsx.map +1 -1
  370. package/dist/ToggleButtonGroup.d.ts +1 -7
  371. package/dist/ToggleButtonGroup.d.ts.map +1 -1
  372. package/dist/ToggleButtonGroup.js +59 -44
  373. package/dist/ToggleButtonGroup.js.map +1 -1
  374. package/dist/ToggleButtonGroup.jsx +25 -12
  375. package/dist/ToggleButtonGroup.jsx.map +1 -1
  376. package/dist/TokenField.d.ts +52 -0
  377. package/dist/TokenField.d.ts.map +1 -0
  378. package/dist/TokenField.js +301 -0
  379. package/dist/TokenField.js.map +1 -0
  380. package/dist/TokenField.jsx +197 -0
  381. package/dist/TokenField.jsx.map +1 -0
  382. package/dist/Toolbar.d.ts +2 -7
  383. package/dist/Toolbar.d.ts.map +1 -1
  384. package/dist/Toolbar.js +35 -16
  385. package/dist/Toolbar.js.map +1 -1
  386. package/dist/Toolbar.jsx +15 -2
  387. package/dist/Toolbar.jsx.map +1 -1
  388. package/dist/Tooltip.d.ts +2 -7
  389. package/dist/Tooltip.d.ts.map +1 -1
  390. package/dist/Tooltip.js +105 -68
  391. package/dist/Tooltip.js.map +1 -1
  392. package/dist/Tooltip.jsx +60 -36
  393. package/dist/Tooltip.jsx.map +1 -1
  394. package/dist/Tree.d.ts +8 -11
  395. package/dist/Tree.d.ts.map +1 -1
  396. package/dist/Tree.js +486 -339
  397. package/dist/Tree.js.map +1 -1
  398. package/dist/Tree.jsx +155 -107
  399. package/dist/Tree.jsx.map +1 -1
  400. package/dist/Virtualizer.d.ts +39 -9
  401. package/dist/Virtualizer.d.ts.map +1 -1
  402. package/dist/Virtualizer.js +2 -2
  403. package/dist/Virtualizer.jsx +2 -2
  404. package/dist/Virtualizer2.js +491 -136
  405. package/dist/Virtualizer2.js.map +1 -1
  406. package/dist/Virtualizer2.jsx +455 -122
  407. package/dist/Virtualizer2.jsx.map +1 -1
  408. package/dist/VirtualizerLayouts.d.ts +55 -2
  409. package/dist/VirtualizerLayouts.d.ts.map +1 -1
  410. package/dist/VisuallyHidden.d.ts +4 -3
  411. package/dist/VisuallyHidden.d.ts.map +1 -1
  412. package/dist/VisuallyHidden.js +18 -11
  413. package/dist/VisuallyHidden.js.map +1 -1
  414. package/dist/VisuallyHidden.jsx +15 -9
  415. package/dist/VisuallyHidden.jsx.map +1 -1
  416. package/dist/contexts.d.ts +25 -0
  417. package/dist/contexts.d.ts.map +1 -1
  418. package/dist/contexts.js +3 -0
  419. package/dist/contexts.js.map +1 -1
  420. package/dist/contexts.jsx +3 -0
  421. package/dist/contexts.jsx.map +1 -1
  422. package/dist/index.d.ts +11 -9
  423. package/dist/index.d.ts.map +1 -1
  424. package/dist/index.js +14 -11
  425. package/dist/index.jsx +14 -11
  426. package/dist/intl/index.d.ts +16 -0
  427. package/dist/intl/index.d.ts.map +1 -0
  428. package/dist/intl.js +212 -0
  429. package/dist/intl.js.map +1 -0
  430. package/dist/intl.jsx +212 -0
  431. package/dist/intl.jsx.map +1 -0
  432. package/dist/useDragAndDrop.d.ts +4 -2
  433. package/dist/useDragAndDrop.d.ts.map +1 -1
  434. package/dist/useDragAndDrop2.js +39 -21
  435. package/dist/useDragAndDrop2.js.map +1 -1
  436. package/dist/useDragAndDrop2.jsx +39 -21
  437. package/dist/useDragAndDrop2.jsx.map +1 -1
  438. package/dist/utils.d.ts +28 -9
  439. package/dist/utils.d.ts.map +1 -1
  440. package/dist/utils.js +96 -24
  441. package/dist/utils.js.map +1 -1
  442. package/dist/utils.jsx +81 -24
  443. package/dist/utils.jsx.map +1 -1
  444. package/dist/virtualizer/Layout.d.ts +9 -0
  445. package/dist/virtualizer/Layout.d.ts.map +1 -1
  446. package/dist/virtualizer/Layout.js +23 -2
  447. package/dist/virtualizer/Layout.js.map +1 -1
  448. package/dist/virtualizer/Layout.jsx +23 -2
  449. package/dist/virtualizer/Layout.jsx.map +1 -1
  450. package/package.json +29 -10
  451. package/src/ActionBar.tsx +50 -24
  452. package/src/ActionGroup.tsx +31 -19
  453. package/src/Alert.tsx +10 -4
  454. package/src/Autocomplete.tsx +24 -15
  455. package/src/Breadcrumbs.tsx +32 -16
  456. package/src/Button.tsx +182 -67
  457. package/src/Calendar.tsx +65 -47
  458. package/src/CalendarPicker.tsx +16 -1
  459. package/src/Checkbox.tsx +84 -116
  460. package/src/Collection.tsx +84 -3
  461. package/src/Color.tsx +282 -104
  462. package/src/ColorEditor.tsx +5 -1
  463. package/src/ComboBox.tsx +384 -109
  464. package/src/ContextualHelpTrigger.tsx +47 -41
  465. package/src/DateField.tsx +74 -106
  466. package/src/DatePicker.tsx +156 -352
  467. package/src/DateRangePickerContext.tsx +8 -0
  468. package/src/Dialog.tsx +105 -38
  469. package/src/Disclosure.tsx +29 -19
  470. package/src/DragAndDrop.tsx +43 -8
  471. package/src/DragPreview.tsx +20 -1
  472. package/src/DropZone.tsx +32 -18
  473. package/src/ElementTag.tsx +14 -104
  474. package/src/FieldError.tsx +39 -7
  475. package/src/FileTrigger.tsx +18 -3
  476. package/src/Focusable.tsx +27 -6
  477. package/src/Form.tsx +57 -11
  478. package/src/GridList.tsx +233 -141
  479. package/src/HiddenDateInput.tsx +19 -3
  480. package/src/Icon.tsx +8 -2
  481. package/src/Keyboard.tsx +18 -2
  482. package/src/Label.tsx +77 -0
  483. package/src/Landmark.tsx +6 -1
  484. package/src/Link.tsx +65 -19
  485. package/src/ListBox.tsx +645 -468
  486. package/src/ListDropTargetDelegate.ts +4 -0
  487. package/src/Menu.tsx +512 -349
  488. package/src/Meter.tsx +42 -8
  489. package/src/Modal.tsx +85 -42
  490. package/src/NumberField.tsx +161 -48
  491. package/src/Popover.tsx +424 -148
  492. package/src/Pressable.tsx +27 -6
  493. package/src/PreviewTrigger.tsx +171 -0
  494. package/src/ProgressBar.tsx +25 -3
  495. package/src/RadioGroup.tsx +230 -161
  496. package/src/RangeCalendar.tsx +75 -63
  497. package/src/RouterProvider.tsx +24 -14
  498. package/src/SearchField.tsx +118 -121
  499. package/src/Select.tsx +454 -274
  500. package/src/SelectionIndicator.tsx +21 -3
  501. package/src/Separator.tsx +17 -1
  502. package/src/SharedElementTransition.tsx +115 -97
  503. package/src/Slider.tsx +35 -17
  504. package/src/StepList.tsx +71 -32
  505. package/src/Switch.tsx +45 -29
  506. package/src/Table.tsx +446 -380
  507. package/src/Tabs.tsx +132 -155
  508. package/src/TagGroup.tsx +53 -40
  509. package/src/Text.tsx +23 -6
  510. package/src/TextField.tsx +118 -159
  511. package/src/TimeField.tsx +34 -95
  512. package/src/Toast.tsx +141 -67
  513. package/src/ToggleButton.tsx +33 -15
  514. package/src/ToggleButtonGroup.tsx +27 -9
  515. package/src/TokenField.tsx +379 -0
  516. package/src/Toolbar.tsx +19 -9
  517. package/src/Tooltip.tsx +126 -84
  518. package/src/Tree.tsx +233 -173
  519. package/src/Virtualizer.tsx +400 -138
  520. package/src/VirtualizerLayouts.ts +211 -26
  521. package/src/VisuallyHidden.tsx +22 -8
  522. package/src/contexts.ts +13 -0
  523. package/src/index.ts +32 -11
  524. package/src/intl/ar-AE.json +6 -0
  525. package/src/intl/bg-BG.json +6 -0
  526. package/src/intl/cs-CZ.json +6 -0
  527. package/src/intl/da-DK.json +6 -0
  528. package/src/intl/de-DE.json +6 -0
  529. package/src/intl/el-GR.json +6 -0
  530. package/src/intl/en-US.json +6 -0
  531. package/src/intl/es-ES.json +6 -0
  532. package/src/intl/et-EE.json +6 -0
  533. package/src/intl/fi-FI.json +6 -0
  534. package/src/intl/fr-FR.json +6 -0
  535. package/src/intl/he-IL.json +6 -0
  536. package/src/intl/hr-HR.json +6 -0
  537. package/src/intl/hu-HU.json +6 -0
  538. package/src/intl/index.ts +125 -0
  539. package/src/intl/it-IT.json +6 -0
  540. package/src/intl/ja-JP.json +6 -0
  541. package/src/intl/ko-KR.json +6 -0
  542. package/src/intl/lt-LT.json +6 -0
  543. package/src/intl/lv-LV.json +6 -0
  544. package/src/intl/nb-NO.json +6 -0
  545. package/src/intl/nl-NL.json +6 -0
  546. package/src/intl/pl-PL.json +6 -0
  547. package/src/intl/pt-BR.json +6 -0
  548. package/src/intl/pt-PT.json +6 -0
  549. package/src/intl/ro-RO.json +6 -0
  550. package/src/intl/ru-RU.json +6 -0
  551. package/src/intl/sk-SK.json +6 -0
  552. package/src/intl/sl-SI.json +6 -0
  553. package/src/intl/sr-SP.json +6 -0
  554. package/src/intl/sv-SE.json +6 -0
  555. package/src/intl/tr-TR.json +6 -0
  556. package/src/intl/uk-UA.json +6 -0
  557. package/src/intl/zh-CN.json +6 -0
  558. package/src/intl/zh-TW.json +6 -0
  559. package/src/useDragAndDrop.ts +30 -32
  560. package/src/utils.tsx +124 -68
  561. package/src/virtualizer/Layout.ts +29 -0
package/src/ListBox.tsx CHANGED
@@ -1,33 +1,45 @@
1
+ /*
2
+ * Copyright 2022 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ // Ported to SolidJS for Proyecto Viviana; based on packages/react-aria-components/src/ListBox.tsx
14
+
1
15
  /**
2
16
  * ListBox component for solidaria-components
3
17
  *
4
18
  * A pre-wired headless listbox that combines state + aria hooks.
5
- * Port of react-aria-components/src/ListBox.tsx
19
+ * Solid adaptation of the pinned ListBox component.
6
20
  */
7
21
 
8
22
  import {
9
- type JSX,
10
23
  createContext,
11
24
  createEffect,
12
25
  createMemo,
26
+ createRenderEffect,
13
27
  createSignal,
14
- on,
15
28
  onCleanup,
16
- splitProps,
29
+ untrack,
17
30
  useContext,
18
31
  For,
19
32
  Show,
33
+ createTrackedEffect,
20
34
  } from "solid-js";
35
+ import type { Context, Accessor } from "solid-js";
36
+ import type { JSX } from "@solidjs/web";
21
37
  import {
22
38
  createListBox,
23
39
  createOption,
24
40
  createFocusRing,
25
41
  createScrollIntoViewOnFocus,
26
42
  createStringFormatter,
27
- createDragSession,
28
- getGlobalDraggingCollectionRef,
29
- getGlobalDraggingKeys,
30
- registerDropItem,
31
43
  dndIntlStrings,
32
44
  mergeProps,
33
45
  moveVirtualFocus,
@@ -44,6 +56,8 @@ import {
44
56
  type ListFilterFn,
45
57
  type Key,
46
58
  type DropTarget,
59
+ type DroppableCollectionState,
60
+ type Collection,
47
61
  } from "@proyecto-viviana/solid-stately";
48
62
  import { useAutocompleteCollection } from "./Autocomplete";
49
63
  import {
@@ -51,18 +65,24 @@ import {
51
65
  type ClassNameOrFunction,
52
66
  type StyleOrFunction,
53
67
  type SlotProps,
68
+ DEFAULT_SLOT,
69
+ OptionContent,
70
+ Provider,
54
71
  useRenderProps,
55
72
  filterDOMProps,
73
+ dataAttr,
56
74
  } from "./utils";
75
+ import { TextContext } from "./Text";
57
76
  import { SharedElementTransition } from "./SharedElementTransition";
58
77
  import {
59
78
  SelectionIndicatorContext,
60
79
  type SelectionIndicatorContextValue,
61
80
  } from "./SelectionIndicator";
62
- import { useVirtualizerContext } from "./Virtualizer";
81
+ import { useVirtualizerContext, PersistedVirtualItem, VirtualizerItem } from "./Virtualizer";
63
82
  import { type DragAndDropHooks } from "./useDragAndDrop";
64
83
  import {
65
84
  getNormalizedDropTargetKey,
85
+ indexesOutsideRange,
66
86
  mergePersistedKeysIntoVirtualRange,
67
87
  useDndPersistedKeys,
68
88
  useRenderDropIndicator,
@@ -70,6 +90,7 @@ import {
70
90
  type DropIndicatorProps,
71
91
  } from "./DragAndDrop";
72
92
  import type { ItemDropTarget } from "@proyecto-viviana/solid-stately";
93
+ import { splitProps } from "@proyecto-viviana/solidaria/utils";
73
94
  import {
74
95
  CollectionRendererContext,
75
96
  Section,
@@ -77,8 +98,10 @@ import {
77
98
  Group,
78
99
  type CollectionEntry,
79
100
  type CollectionRendererContextValue,
101
+ type CollectionSection,
80
102
  type SectionProps,
81
103
  useCollectionRenderer,
104
+ useCollectionRoot,
82
105
  isCollectionSection,
83
106
  flattenCollectionEntries,
84
107
  } from "./Collection";
@@ -166,6 +189,12 @@ export interface ListBoxOptionRenderProps {
166
189
  isDisabled: boolean;
167
190
  }
168
191
 
192
+ export type ListBoxItemProps<T> = ListBoxOptionProps<T>;
193
+ export type ListBoxItemRenderProps = ListBoxOptionRenderProps;
194
+
195
+ /**
196
+ * @deprecated Use {@link ListBoxItemProps}. Removed in a future minor.
197
+ */
169
198
  export interface ListBoxOptionProps<T>
170
199
  extends Omit<AriaOptionProps, "children" | "key">, SlotProps {
171
200
  /** The unique key for the option. */
@@ -214,12 +243,168 @@ export const ListBoxContext = createContext<ListBoxContextValue<unknown> | null>
214
243
  export const ListBoxStateContext = createContext<ListState<unknown> | null>(null);
215
244
  export const ListStateContext = ListBoxStateContext;
216
245
 
246
+ function dropIndicatorLabel(
247
+ target: ItemDropTarget,
248
+ collection: Collection<unknown>,
249
+ format: (
250
+ key: "dropOnItem" | "insertBetween" | "insertAfter" | "insertBefore",
251
+ args?: Record<string, string>,
252
+ ) => string,
253
+ ): string {
254
+ const getText = (key: Key | null): string => {
255
+ if (key == null) return "";
256
+ return collection.getTextValue(key) ?? collection.getItem(key)?.textValue ?? "";
257
+ };
258
+ if (target.dropPosition === "on") {
259
+ return format("dropOnItem", { itemText: getText(target.key) });
260
+ }
261
+ let before: Key | null;
262
+ let after: Key | null;
263
+ if (target.dropPosition === "before") {
264
+ const prevKey = collection.getKeyBefore(target.key);
265
+ const prevNode = prevKey != null ? collection.getItem(prevKey) : null;
266
+ before = prevNode?.type === "item" ? prevNode.key : null;
267
+ } else {
268
+ before = target.key;
269
+ }
270
+ if (target.dropPosition === "after") {
271
+ const nextKey = collection.getKeyAfter(target.key);
272
+ const nextNode = nextKey != null ? collection.getItem(nextKey) : null;
273
+ after = nextNode?.type === "item" ? nextNode.key : null;
274
+ } else {
275
+ after = target.key;
276
+ }
277
+ if (before != null && after != null) {
278
+ return format("insertBetween", {
279
+ beforeItemText: getText(before),
280
+ afterItemText: getText(after),
281
+ });
282
+ }
283
+ if (before != null) {
284
+ return format("insertAfter", { itemText: getText(before) });
285
+ }
286
+ if (after != null) {
287
+ return format("insertBefore", { itemText: getText(after) });
288
+ }
289
+ return "";
290
+ }
291
+
292
+ /**
293
+ * RAC `ListBox.tsx:662-717` `ListBoxDropIndicatorWrapper` — a stable module-level
294
+ * component so Solid does not remount (and unregister) the indicator on every
295
+ * parent ListBox re-render. `useDropIndicator` owns register + focus
296
+ * (`useDroppableItem.ts:49-88`).
297
+ */
298
+ function ListBoxDropIndicatorWrapper(props: DropIndicatorProps): JSX.Element {
299
+ const listContext = useContext(ListBoxContext);
300
+ const listState = useContext(ListBoxStateContext);
301
+ const [el, setEl] = createSignal<HTMLDivElement | null>(null);
302
+ const dndFormatter = createStringFormatter(dndIntlStrings);
303
+ const dropState = listContext?.dropState as DroppableCollectionState | undefined;
304
+ const indicator = listContext?.dragAndDropHooks?.useDropIndicator?.(
305
+ { target: props.target },
306
+ dropState as DroppableCollectionState,
307
+ el,
308
+ );
309
+
310
+ return (
311
+ <Show when={indicator && !indicator.isHidden}>
312
+ <div
313
+ {...indicator?.dropIndicatorProps}
314
+ ref={setEl}
315
+ role="option"
316
+ class="solidaria-DropIndicator"
317
+ aria-label={
318
+ listState
319
+ ? dropIndicatorLabel(props.target, listState.collection(), (key, args) =>
320
+ dndFormatter().format(key, args),
321
+ )
322
+ : undefined
323
+ }
324
+ data-drop-target={dataAttr(indicator?.isDropTarget)}
325
+ />
326
+ </Show>
327
+ );
328
+ }
329
+
330
+ /**
331
+ * Owns only the collection item. Drop-indicator siblings live in their own
332
+ * components so their tracking cannot remount this node.
333
+ *
334
+ * Comparison hyperscript children (`hc` thunks) are one-shot: if the fragment
335
+ * insert that owns the option also tracks indicator `Show`, the effect
336
+ * re-runs, disposes the option, and Chromium maps focus to
337
+ * `listbox:Permissions`. RAC `ListBoxItem` stays mounted while Collection
338
+ * inserts indicator siblings (`react-aria-components/src/ListBox.tsx`).
339
+ */
340
+ function ListBoxRenderedItem<T>(props: {
341
+ item: T;
342
+ renderItem: (item: T) => JSX.Element;
343
+ }): JSX.Element {
344
+ // Comparison `hc` returns a one-shot thunk. Solid's insert treats a function
345
+ // child as an accessor and will call it again when a sibling indicator
346
+ // invalidates. Instantiate once under this owner so the option node stays.
347
+ const child = untrack(() => {
348
+ const rendered = props.renderItem(props.item);
349
+ return typeof rendered === "function" ? (rendered as () => JSX.Element)() : rendered;
350
+ });
351
+ return child as JSX.Element;
352
+ }
353
+
354
+ function ListBoxDropIndicatorSlot(props: {
355
+ itemIndex: number | Accessor<number>;
356
+ position: "before" | "after" | "on";
357
+ renderDropIndicator?: (
358
+ index: number,
359
+ position: "before" | "after" | "on",
360
+ ) => JSX.Element | undefined;
361
+ }): JSX.Element {
362
+ const itemIndex = () =>
363
+ typeof props.itemIndex === "function" ? props.itemIndex() : props.itemIndex;
364
+ return <>{props.renderDropIndicator?.(itemIndex(), props.position)}</>;
365
+ }
366
+
367
+ /**
368
+ * Stable per-item row so drop-indicator mount does not remount the option.
369
+ */
370
+ function ListBoxItemWithDropIndicators<T>(props: {
371
+ item: T;
372
+ itemIndex: number | Accessor<number>;
373
+ isLastInLevel: boolean | Accessor<boolean>;
374
+ renderItem: (item: T) => JSX.Element;
375
+ renderDropIndicator?: (
376
+ index: number,
377
+ position: "before" | "after" | "on",
378
+ ) => JSX.Element | undefined;
379
+ }): JSX.Element {
380
+ const isLast = () =>
381
+ typeof props.isLastInLevel === "function" ? props.isLastInLevel() : props.isLastInLevel;
382
+ return (
383
+ <>
384
+ <ListBoxDropIndicatorSlot
385
+ itemIndex={props.itemIndex}
386
+ position="before"
387
+ renderDropIndicator={props.renderDropIndicator}
388
+ />
389
+ <ListBoxRenderedItem item={props.item} renderItem={props.renderItem} />
390
+ <Show when={isLast()}>
391
+ <ListBoxDropIndicatorSlot
392
+ itemIndex={props.itemIndex}
393
+ position="after"
394
+ renderDropIndicator={props.renderDropIndicator}
395
+ />
396
+ </Show>
397
+ </>
398
+ );
399
+ }
400
+
217
401
  /**
218
402
  * A listbox displays a list of options and allows a user to select one or more of them.
219
403
  */
220
404
  export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
221
405
  const parentContext = useContext(ListBoxContext) as ListBoxContextValue<T> | null;
222
- const contextSlotProps = parentContext?.slots?.[props.slot ?? "default"];
406
+ const contextSlotProps =
407
+ parentContext?.slots?.[typeof props.slot === "string" ? props.slot : "default"];
223
408
  const mergedListBoxProps = contextSlotProps
224
409
  ? (mergeProps(contextSlotProps, props) as ListBoxProps<T>)
225
410
  : props;
@@ -326,8 +511,7 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
326
511
  // option emits aria-posinset/aria-setsize for the windowed (incomplete) DOM.
327
512
  const parentCollectionRenderer = useCollectionRenderer<unknown>();
328
513
  const listBoxAria = createListBox(
329
- {
330
- ...ariaProps,
514
+ mergeProps(ariaProps, {
331
515
  get isVirtualized() {
332
516
  return parentCollectionRenderer?.isVirtualized ?? ariaProps.isVirtualized;
333
517
  },
@@ -357,7 +541,7 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
357
541
  get direction() {
358
542
  return locale().direction;
359
543
  },
360
- },
544
+ }),
361
545
  state,
362
546
  () => listRef(),
363
547
  );
@@ -365,7 +549,10 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
365
549
  const { isFocused, isFocusVisible, focusProps } = createFocusRing();
366
550
 
367
551
  const renderValues = createMemo<ListBoxRenderProps>(() => ({
368
- isFocused: state.isFocused() || isFocused(),
552
+ // RAC ListBox.tsx:366-414: `data-focused` / render-prop `isFocused` come from
553
+ // `useFocusRing` on the listbox element, not collection focus. Virtual focus
554
+ // (ComboBox) leaves the listbox itself unfocused.
555
+ isFocused: isFocused(),
369
556
  isFocusVisible: isFocusVisible(),
370
557
  isDisabled: resolveDisabled(),
371
558
  isEmpty: state.collection().size === 0,
@@ -408,19 +595,19 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
408
595
  ref: () => listRef(),
409
596
  });
410
597
 
411
- // Autocomplete bridge. createListBox reimplements navigation inline (it does
412
- // not route through createSelectableCollection), so the virtual-focus event
413
- // wiring the Autocomplete input relies on lives here, gated on the bridge
414
- // context. This mirrors createSelectableCollection's FOCUS_EVENT/CLEAR_FOCUS_EVENT
415
- // handlers and useSelectableItem's per-item moveVirtualFocus, at the component
416
- // layer, without touching the shared hooks (protecting ComboBox/Picker).
598
+ // Autocomplete bridge. The shared selectable collection owns keyboard
599
+ // navigation and collection focus. This component keeps the Solid DOM handoff
600
+ // that mirrors the focused key to the input's active descendant. Ticket #100
601
+ // tracks moving the remaining virtual-focus work into the shared item layer.
417
602
  if (autocompleteCtx) {
418
603
  // Forward path: the input dispatches FOCUS_EVENT/CLEAR_FOCUS_EVENT onto the
419
604
  // collection element. FOCUS_EVENT marks the collection focused (and, when the
420
- // user types forward, requests the first item); the actual arrow navigation
421
- // arrives as a re-dispatched KeyboardEvent handled by createListBox's onKeyDown.
605
+ // user types forward, requests the first item). The shared collection handles
606
+ // the re-dispatched keyboard event.
422
607
  let shouldVirtualFocusFirst = false;
423
- createEffect(() => {
608
+ createTrackedEffect(() => {
609
+ const _s2Cleanups: Array<() => void> = [];
610
+
424
611
  const list = listRef();
425
612
  if (!list) return;
426
613
  const onFocusEvent = (e: Event) => {
@@ -440,10 +627,14 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
440
627
  };
441
628
  list.addEventListener(FOCUS_EVENT, onFocusEvent);
442
629
  list.addEventListener(CLEAR_FOCUS_EVENT, onClearFocusEvent);
443
- onCleanup(() => {
630
+ _s2Cleanups.push(() => {
444
631
  list.removeEventListener(FOCUS_EVENT, onFocusEvent);
445
632
  list.removeEventListener(CLEAR_FOCUS_EVENT, onClearFocusEvent);
446
633
  });
634
+
635
+ return () => {
636
+ for (const c of _s2Cleanups) c();
637
+ };
447
638
  });
448
639
 
449
640
  // Focus the first item once the (filtered) collection settles after the user
@@ -451,41 +642,53 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
451
642
  // descendant by moving virtual focus onto the collection itself (its focusin
452
643
  // reaches the input's clear branch). Mirrors useSelectableCollection.
453
644
  createEffect(
454
- on(
455
- () => [state.collection().getFirstKey?.() ?? null, state.collection().size] as const,
456
- ([firstKey, size]) => {
457
- if (!shouldVirtualFocusFirst) return;
458
- if (firstKey == null) {
459
- const list = listRef();
460
- if (list) moveVirtualFocus(list);
461
- if (size > 0) shouldVirtualFocusFirst = false;
462
- } else {
463
- state.setFocusedKey(firstKey);
464
- shouldVirtualFocusFirst = false;
465
- }
466
- },
467
- { defer: true },
468
- ),
645
+ () => [state.collection().getFirstKey?.() ?? null, state.collection().size] as const,
646
+ ([firstKey, size]) => {
647
+ if (!shouldVirtualFocusFirst) return;
648
+ if (firstKey == null) {
649
+ const list = listRef();
650
+ if (list) moveVirtualFocus(list);
651
+ if (size > 0) shouldVirtualFocusFirst = false;
652
+ } else {
653
+ state.setFocusedKey(firstKey);
654
+ shouldVirtualFocusFirst = false;
655
+ }
656
+ },
657
+ { defer: true },
469
658
  );
470
659
 
471
660
  // Reverse path: mirror the focused key onto the option's DOM element via a
472
661
  // synthetic, bubbling focusin (moveVirtualFocus). The input's focusin listener
473
662
  // reads target.id into its aria-activedescendant. Option ids are String(key)
474
663
  // in this path (createOption). Real DOM focus stays on the input.
475
- createEffect(() => {
476
- const key = state.focusedKey();
477
- const list = listRef();
478
- if (!list || !state.isFocused()) return;
479
- if (key == null) return;
480
- const el = list.ownerDocument.getElementById(String(key));
481
- if (el) moveVirtualFocus(el);
482
- });
664
+ createEffect(
665
+ () => {
666
+ const key = state.focusedKey();
667
+ const list = listRef();
668
+ const focused = state.isFocused();
669
+ return { key, list, focused };
670
+ },
671
+ ({ key, list, focused }) => {
672
+ if (!list || !focused) return;
673
+ if (key == null) return;
674
+ const el = list.ownerDocument.getElementById(String(key));
675
+ if (el) moveVirtualFocus(el);
676
+ },
677
+ );
483
678
  }
484
679
 
485
- const isEmpty = () => stateProps.items.length === 0;
680
+ // RAC ListBox iterates CollectionRoot's filtered collection, not the
681
+ // unfiltered `items` prop. Keyboard already walks `state.collection()`
682
+ // (createFilteredListState under Autocomplete; baseState otherwise).
486
683
  const getItemNodes = createMemo(() =>
487
684
  Array.from(state.collection()).filter((node) => node.type === "item"),
488
685
  );
686
+ const collectionItems = createMemo(() => getItemNodes().map((node) => node.value as T));
687
+ const isEmpty = () => state.collection().size === 0;
688
+ const isLastDropItem = (itemIndex: number | Accessor<number>) => {
689
+ const i = typeof itemIndex === "function" ? itemIndex() : itemIndex;
690
+ return getItemNodes().length > 0 && i === getItemNodes().length - 1;
691
+ };
489
692
  const getDropTargetByIndex = (
490
693
  index: number,
491
694
  position: "before" | "after" | "on",
@@ -504,40 +707,36 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
504
707
  hooks.ListDropTargetDelegate),
505
708
  );
506
709
  });
507
- const dropState = createMemo(() => {
508
- if (!hasDroppableDnd()) return undefined;
509
- return local.dragAndDropHooks?.useDroppableCollectionState?.({});
510
- });
710
+ const dropStateValue = hasDroppableDnd()
711
+ ? local.dragAndDropHooks?.useDroppableCollectionState?.({
712
+ get collection() {
713
+ return state.collection();
714
+ },
715
+ })
716
+ : undefined;
717
+ const dropState = () => dropStateValue;
511
718
  const hasDraggableDnd = createMemo(() => {
512
719
  const hooks = local.dragAndDropHooks;
513
720
  return Boolean(hooks?.useDraggableCollectionState && hooks.useDraggableCollection);
514
721
  });
515
- const dragState = createMemo(() => {
516
- if (!hasDraggableDnd()) return undefined;
517
- return local.dragAndDropHooks?.useDraggableCollectionState?.({
518
- items: flatItems(),
519
- });
520
- });
521
- createEffect(() => {
522
- if (!hasDraggableDnd()) return;
523
- const hooks = local.dragAndDropHooks;
524
- const activeDragState = dragState();
525
- if (!hooks?.useDraggableCollection || !activeDragState) return;
526
- hooks.useDraggableCollection({}, activeDragState, () => listRef());
527
- });
528
- const droppableCollection = createMemo(() => {
722
+ const dragStateValue = hasDraggableDnd()
723
+ ? local.dragAndDropHooks?.useDraggableCollectionState?.({
724
+ items: flatItems(),
725
+ collection: state.collection(),
726
+ selectedKeys: state.selectionManager.selectedKeys,
727
+ isSelected: (key) => state.selectionManager.isSelected(key),
728
+ })
729
+ : undefined;
730
+ const dragState = () => dragStateValue;
731
+ if (local.dragAndDropHooks?.useDraggableCollection && dragStateValue) {
732
+ local.dragAndDropHooks.useDraggableCollection({}, dragStateValue, () => listRef());
733
+ }
734
+ const droppableCollectionValue = (() => {
529
735
  if (!hasDroppableDnd()) return undefined;
530
736
  const hooks = local.dragAndDropHooks;
531
- const activeDropState = dropState();
737
+ const activeDropState = dropStateValue;
532
738
  if (!hooks?.useDroppableCollection || !activeDropState) return undefined;
533
- const resolveDirection = (): "ltr" | "rtl" => {
534
- const el = listRef();
535
- if (el && typeof window !== "undefined" && typeof window.getComputedStyle === "function") {
536
- const dir = window.getComputedStyle(el).direction;
537
- if (dir === "rtl") return "rtl";
538
- }
539
- return typeof document !== "undefined" && document.dir === "rtl" ? "rtl" : "ltr";
540
- };
739
+ const resolveDirection = (): "ltr" | "rtl" => locale().direction;
541
740
  const dropTargetDelegate =
542
741
  hooks.dropTargetDelegate ??
543
742
  parentCollectionRenderer?.dropTargetDelegate ??
@@ -564,15 +763,21 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
564
763
  // (`navigate()` walks getKeyAfter/getKeyBefore) and post-drop focus
565
764
  // restoration. Reading it here re-registers the drop target when the
566
765
  // collection changes (mirrors upstream keying its effect on the state).
567
- collection: state.collection(),
568
- selectedKeys: state.selectionManager.selectedKeys,
766
+ get collection() {
767
+ return state.collection();
768
+ },
769
+ get selectedKeys() {
770
+ return state.selectionManager.selectedKeys;
771
+ },
569
772
  setSelectedKeys: (keys) => state.selectionManager.setSelectedKeys(keys),
570
773
  setFocusedKey: (key) => state.setFocusedKey(key),
774
+ setFocused: (isFocused) => state.setFocused(isFocused),
571
775
  },
572
776
  activeDropState,
573
777
  () => listRef(),
574
778
  );
575
- });
779
+ })();
780
+ const droppableCollection = () => droppableCollectionValue;
576
781
  const isRootDropTarget = createMemo(() => {
577
782
  return Boolean(dropState()?.target?.type === "root");
578
783
  });
@@ -584,163 +789,14 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
584
789
  if (!target || target.type !== "item") return undefined;
585
790
  return dndRenderDropIndicator()?.(target);
586
791
  };
792
+ // Stable callback so `For` item templates do not track drop-indicator
793
+ // reactivity (that remounts the option). The row component reads this.
794
+ const renderItemDropIndicator = (index: number, position: "before" | "after" | "on") =>
795
+ dndDropIndicator(index, position) ??
796
+ parentCollectionRenderer?.renderDropIndicator?.(index, position);
587
797
 
588
- // ── Drop indicator (Fix B: labeled, self-focusing) ─────────────────────────
589
- // The port of react-aria-components' ListBoxDropIndicatorWrapper +
590
- // useDropIndicator. The droppable-collection state layer diverged to a minimal
591
- // reactive target-holder with no collection reference, so the label (which
592
- // needs the collection's text values) and the self-focus (which needs the live
593
- // drag session) are computed here in the host layer, where both are available.
594
- const dndFormatter = createStringFormatter(dndIntlStrings);
595
- const dragSession = createDragSession();
596
- const dropIndicatorTargetsEqual = (
597
- a: DropTarget | null | undefined,
598
- b: ItemDropTarget,
599
- ): boolean =>
600
- !!a && a.type === "item" && a.key === b.key && a.dropPosition === b.dropPosition;
601
- // Faithful port of useDropIndicator's label logic: "Drop on X" for an on-item
602
- // target, else "Insert between X and Y" / "Insert before X" / "Insert after X"
603
- // resolved from the target's neighbours in the collection.
604
- const dropIndicatorLabel = (target: ItemDropTarget): string => {
605
- const collection = state.collection();
606
- const getText = (key: Key | null): string => {
607
- if (key == null) return "";
608
- return collection.getTextValue(key) ?? collection.getItem(key)?.textValue ?? "";
609
- };
610
- if (target.dropPosition === "on") {
611
- return dndFormatter().format("dropOnItem", { itemText: getText(target.key) });
612
- }
613
- let before: Key | null;
614
- let after: Key | null;
615
- if (target.dropPosition === "before") {
616
- const prevKey = collection.getKeyBefore(target.key);
617
- const prevNode = prevKey != null ? collection.getItem(prevKey) : null;
618
- before = prevNode?.type === "item" ? prevNode.key : null;
619
- } else {
620
- before = target.key;
621
- }
622
- if (target.dropPosition === "after") {
623
- const nextKey = collection.getKeyAfter(target.key);
624
- const nextNode = nextKey != null ? collection.getItem(nextKey) : null;
625
- after = nextNode?.type === "item" ? nextNode.key : null;
626
- } else {
627
- after = target.key;
628
- }
629
- if (before != null && after != null) {
630
- return dndFormatter().format("insertBetween", {
631
- beforeItemText: getText(before),
632
- afterItemText: getText(after),
633
- });
634
- }
635
- if (before != null) {
636
- return dndFormatter().format("insertAfter", { itemText: getText(before) });
637
- }
638
- if (after != null) {
639
- return dndFormatter().format("insertBefore", { itemText: getText(after) });
640
- }
641
- return "";
642
- };
643
- // Wrap the raw Set<string> the DragManager hands drop items into the DragTypes
644
- // delegate the port state expects (mirrors createDroppableCollection.wrapTypes).
645
- const wrapDropTypes = (types: Set<string>) => ({
646
- has: (type: string | string[]) => {
647
- if (typeof type === "string") return types.has(type);
648
- if (Array.isArray(type)) return type.some((t) => typeof t === "string" && types.has(t));
649
- return false;
650
- },
651
- });
652
- const ListBoxDropIndicator = (props: { target: ItemDropTarget }): JSX.Element => {
653
- const [indicatorRef, setIndicatorRef] = createSignal<HTMLDivElement | null>(null);
654
- const isDropTarget = createMemo(() =>
655
- dropIndicatorTargetsEqual(dropState()?.target, props.target),
656
- );
657
- // aria-hidden mirrors useDropIndicator: outside a drag session, or on a
658
- // non-active target, the indicator is hidden from AT ('true'); the active
659
- // target during a session is exposed (undefined). isHidden then tells us
660
- // whether to omit the element entirely (RAC returns null when hidden).
661
- const ariaHidden = createMemo(() =>
662
- !dragSession() ? "true" : isDropTarget() ? undefined : "true",
663
- );
664
- const isHidden = createMemo(() => !isDropTarget() && !!ariaHidden());
665
- // Faithful port of useDroppableItem. Registration (of the indicator element as
666
- // a DragManager droppable item) and the self-focus must happen in one effect,
667
- // registration first: the DragManager's onFocus keys off the dropItems map, so
668
- // if the element self-focused before it was registered, onFocus would treat it
669
- // as an unknown target and redirect focus back to the collection (a pickup
670
- // bounce). React's two useDroppableItem effects run in guaranteed order after
671
- // the ref is attached; Solid's independent effects race, so we fuse them. The
672
- // dropItems map keys off live elements — the ref nulls itself on Show cleanup
673
- // (below), so a hidden indicator unregisters instead of lingering as a stale
674
- // detached entry.
675
- createEffect(() => {
676
- const el = indicatorRef();
677
- if (!el) return;
678
- const unregister = registerDropItem({
679
- element: el,
680
- target: props.target,
681
- getDropOperation: (types, allowedOperations) => {
682
- const st = dropState();
683
- if (!st) return "cancel";
684
- // Thread the same drag identity the collection uses (see
685
- // createDroppableCollection.isInternalDropOperation): an internal
686
- // keyboard reorder must report `isInternal` so the state's feature
687
- // detection keeps before/after indicators valid (and rejects "on" for a
688
- // reorder-only host). Without it every indicator would return "cancel"
689
- // and drop out of the DragManager's ariaHideOutside keep-set.
690
- const isInternal = getGlobalDraggingCollectionRef() === listRef();
691
- return st.getDropOperation(
692
- props.target,
693
- wrapDropTypes(types) as Parameters<typeof st.getDropOperation>[1],
694
- allowedOperations,
695
- isInternal,
696
- getGlobalDraggingKeys(),
697
- );
698
- },
699
- });
700
- onCleanup(unregister);
701
- // While a drag session is active, the current drop target moves real DOM
702
- // focus onto itself, so keyboard drop navigation lands on the labeled
703
- // insertion point rather than the collection element. The self-focus is
704
- // deferred to an animation frame to mirror upstream's timing: in
705
- // useDroppableItem the focus is a *passive* useEffect, so it commits only
706
- // after the browser's microtask checkpoint. That ordering matters because
707
- // the DragManager keeps the active drop indicator visible via
708
- // ariaHideOutside, whose keep-set is `[dragSource, ...registered drop
709
- // items]` (the collection is dropped from the set once it owns items). A
710
- // freshly-mounted indicator is momentarily `inert` — it only leaves the
711
- // keep-set's blind spot once the DragManager's MutationObserver re-runs
712
- // updateValidDropTargets on a later microtask. Focusing synchronously (or
713
- // on a microtask) would call .focus() while the element is still inert and
714
- // silently no-op; the animation frame runs after that refresh, exactly as
715
- // React's passive effect does.
716
- if (dragSession() && isDropTarget()) {
717
- requestAnimationFrame(() => {
718
- if (dragSession() && isDropTarget() && indicatorRef() === el) {
719
- el.focus();
720
- }
721
- });
722
- }
723
- });
724
- return (
725
- <Show when={!isHidden()}>
726
- <div
727
- ref={(el) => {
728
- setIndicatorRef(el);
729
- onCleanup(() => setIndicatorRef(null));
730
- }}
731
- role="option"
732
- class="solidaria-DropIndicator"
733
- aria-roledescription={dndFormatter().format("dropIndicator")}
734
- aria-label={dropIndicatorLabel(props.target)}
735
- aria-hidden={ariaHidden()}
736
- tabindex={-1}
737
- data-drop-target={isDropTarget() || undefined}
738
- />
739
- </Show>
740
- );
741
- };
742
798
  const dropIndicatorContextValue = {
743
- render: (p: DropIndicatorProps) => <ListBoxDropIndicator target={p.target} />,
799
+ render: (p: DropIndicatorProps) => <ListBoxDropIndicatorWrapper {...p} />,
744
800
  };
745
801
 
746
802
  const virtualizer = useVirtualizerContext();
@@ -752,36 +808,34 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
752
808
  );
753
809
  const virtualRange = createMemo(() => {
754
810
  if (!virtualizer || !parentCollectionRenderer?.isVirtualized || hasSections()) return null;
755
- const baseRange = virtualizer.getVisibleRange(stateProps.items.length);
811
+ const itemCount = getItemNodes().length;
812
+ const baseRange = virtualizer.getVisibleRange(itemCount);
756
813
  const itemNodes = getItemNodes();
757
- const persistedIndexes = Array.from(persistedKeys())
758
- .map((key) => itemNodes.findIndex((node) => node.key === key))
759
- .filter((index) => index >= 0);
760
814
  const dropTarget = dropState()?.target;
761
815
  const normalizedDropKey = getNormalizedDropTargetKey(dropTarget, state.collection());
762
- const focusedKey = state.focusedKey();
763
- const focusedIndex =
764
- focusedKey != null ? itemNodes.findIndex((node) => node.key === focusedKey) : -1;
765
816
  const forceIncludeIndexes = [
766
817
  dropTarget?.type === "item" ? itemNodes.findIndex((node) => node.key === dropTarget.key) : -1,
767
818
  normalizedDropKey != null
768
819
  ? itemNodes.findIndex((node) => node.key === normalizedDropKey)
769
820
  : -1,
770
- dropTarget?.type === "item" ? -1 : focusedIndex,
771
821
  ].filter((index) => index >= 0);
772
- return mergePersistedKeysIntoVirtualRange(
773
- baseRange,
774
- persistedIndexes,
775
- stateProps.items.length,
776
- virtualizer,
777
- 80,
778
- {
779
- forceIncludeIndexes,
780
- forceIncludeMaxSpan: 320,
781
- },
782
- );
822
+ return mergePersistedKeysIntoVirtualRange(baseRange, [], itemCount, virtualizer, 80, {
823
+ forceIncludeIndexes,
824
+ forceIncludeMaxSpan: 320,
825
+ });
826
+ });
827
+ const persistedOutsideIndexes = createMemo(() => {
828
+ const range = virtualRange();
829
+ if (!range) return [] as number[];
830
+ const itemNodes = getItemNodes();
831
+ const persistedIndexes = Array.from(persistedKeys())
832
+ .map((key) => itemNodes.findIndex((node) => node.key === key))
833
+ .filter((index) => index >= 0);
834
+ return indexesOutsideRange(range, persistedIndexes);
783
835
  });
784
- createEffect(() => {
836
+ createTrackedEffect(() => {
837
+ const _s2Cleanups: Array<() => void> = [];
838
+
785
839
  if (!virtualizer || !parentCollectionRenderer?.isVirtualized) return;
786
840
  const getItemNodes = () =>
787
841
  Array.from(state.collection()).filter((node) => node.type === "item");
@@ -798,44 +852,54 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
798
852
  key: typeof node.key === "string" || typeof node.key === "number" ? node.key : undefined,
799
853
  };
800
854
  });
801
- onCleanup(() => {
855
+ _s2Cleanups.push(() => {
802
856
  virtualizer.setDropTargetIndexResolver(undefined);
803
857
  virtualizer.setDropTargetItemCountResolver(undefined);
804
858
  virtualizer.setDropTargetResolver(undefined);
805
859
  });
860
+
861
+ return () => {
862
+ for (const c of _s2Cleanups) c();
863
+ };
806
864
  });
807
865
  const visibleItems = createMemo(() => {
808
866
  const range = virtualRange();
809
- if (!range) return stateProps.items;
810
- return stateProps.items.slice(range.start, range.end);
867
+ const items = collectionItems();
868
+ if (!range) return items;
869
+ return items.slice(range.start, range.end);
811
870
  });
812
- // Spacers reserve the windowed-out extent along the virtualizer's primary axis,
813
- // so a horizontal layout offsets along width rather than height.
814
- const virtualSpacerStyle = (size: number): JSX.CSSProperties =>
815
- virtualizer?.orientation === "horizontal" ? { width: `${size}px` } : { height: `${size}px` };
816
871
  const sectionedRenderEntries = createMemo(() => {
872
+ const values = new Set(collectionItems());
817
873
  let globalIndex = 0;
818
- return stateProps.items.map((entry) => {
874
+ const entries: Array<
875
+ | { type: "section"; section: CollectionSection<T>; items: { item: T; index: number }[] }
876
+ | { type: "item"; item: { item: T; index: number } }
877
+ > = [];
878
+ for (const entry of stateProps.items) {
819
879
  if (isCollectionSection(entry)) {
820
- const sectionItems = entry.items.map((item) => ({
821
- item,
822
- index: globalIndex++,
823
- }));
824
- return {
880
+ const sectionItems = entry.items
881
+ .filter((item) => values.has(item))
882
+ .map((item) => ({
883
+ item,
884
+ index: globalIndex++,
885
+ }));
886
+ if (sectionItems.length === 0) continue;
887
+ entries.push({
825
888
  type: "section" as const,
826
889
  section: entry,
827
890
  items: sectionItems,
828
- };
891
+ });
892
+ } else if (values.has(entry as T)) {
893
+ entries.push({
894
+ type: "item" as const,
895
+ item: {
896
+ item: entry as T,
897
+ index: globalIndex++,
898
+ },
899
+ });
829
900
  }
830
- const indexedItem = {
831
- item: entry as T,
832
- index: globalIndex++,
833
- };
834
- return {
835
- type: "item" as const,
836
- item: indexedItem,
837
- };
838
- });
901
+ }
902
+ return entries;
839
903
  });
840
904
  const collectionRenderer = createMemo<CollectionRendererContextValue<unknown>>(() => ({
841
905
  ...parentCollectionRenderer,
@@ -844,9 +908,10 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
844
908
  dndDropIndicator(index, position) ??
845
909
  parentCollectionRenderer?.renderDropIndicator?.(index, position),
846
910
  }));
911
+ const CollectionRoot = useCollectionRoot();
847
912
 
848
913
  return (
849
- <ListBoxContext.Provider
914
+ <ListBoxContext
850
915
  value={
851
916
  {
852
917
  state,
@@ -858,144 +923,208 @@ export function ListBox<T>(props: ListBoxProps<T>): JSX.Element {
858
923
  } as ListBoxContextValue<unknown>
859
924
  }
860
925
  >
861
- <ListBoxStateContext.Provider value={state}>
862
- <CollectionRendererContext.Provider value={collectionRenderer()}>
863
- <DropIndicatorContext.Provider value={dropIndicatorContextValue}>
864
- <>
865
- <Show when={ariaProps.label}>
866
- <span {...cleanLabelProps()}>{ariaProps.label as JSX.Element}</span>
867
- </Show>
868
- <div
869
- {...mergeProps(
870
- domProps(),
871
- cleanListBoxProps(),
872
- cleanFocusProps(),
873
- (droppableCollection()?.collectionProps as Record<string, unknown> | undefined) ??
874
- {},
875
- )}
876
- ref={(el) => {
877
- setListRef(el);
878
- assignRef(local.ref, el);
879
- autocompleteCtx?.collectionRef(el);
880
- }}
881
- class={renderProps.class()}
882
- style={renderProps.style()}
883
- data-focused={state.isFocused() || undefined}
884
- data-focus-visible={isFocusVisible() || undefined}
885
- data-disabled={resolveDisabled() || undefined}
886
- data-empty={isEmpty() || undefined}
887
- data-layout={stateProps.layout}
888
- data-orientation={stateProps.orientation}
889
- data-drop-target={isRootDropTarget() || undefined}
890
- slot={local.slot}
891
- >
892
- <SharedElementTransition>
893
- {isEmpty() && local.renderEmptyState ? (
894
- <div role="option" style={{ display: "contents" }} data-empty-state>
895
- {local.renderEmptyState()}
896
- </div>
897
- ) : hasSections() ? (
898
- <For each={sectionedRenderEntries()}>
899
- {(entry) =>
900
- entry.type === "section" ? (
901
- <div role="presentation" data-section-wrapper>
902
- <Section class="solidaria-ListBox-section">
903
- {entry.section.title != null && (
904
- <Header class="solidaria-ListBox-sectionHeader">
905
- {entry.section.title}
906
- </Header>
907
- )}
908
- <Group class="solidaria-ListBox-sectionGroup">
909
- <div role="group" aria-label={entry.section["aria-label"]}>
910
- <For each={entry.items}>
911
- {(indexedItem) => (
912
- <>
913
- {collectionRenderer().renderDropIndicator?.(
914
- indexedItem.index,
915
- "before",
916
- )}
917
- {collectionRenderer().renderDropIndicator?.(
918
- indexedItem.index,
919
- "on",
920
- )}
921
- {local.children(indexedItem.item)}
922
- {collectionRenderer().renderDropIndicator?.(
923
- indexedItem.index,
924
- "after",
925
- )}
926
- </>
927
- )}
928
- </For>
929
- </div>
930
- </Group>
931
- </Section>
932
- </div>
933
- ) : (
934
- <>
935
- {collectionRenderer().renderDropIndicator?.(entry.item.index, "before")}
936
- {collectionRenderer().renderDropIndicator?.(entry.item.index, "on")}
937
- {local.children(entry.item.item)}
938
- {collectionRenderer().renderDropIndicator?.(entry.item.index, "after")}
939
- </>
940
- )
941
- }
942
- </For>
943
- ) : (
944
- <>
945
- {virtualRange()?.offsetTop ? (
946
- <div
947
- role="presentation"
948
- aria-hidden="true"
949
- style={virtualSpacerStyle(virtualRange()!.offsetTop)}
950
- data-virtualizer-spacer="top"
951
- />
952
- ) : null}
953
- <For each={visibleItems()}>
954
- {(item, index) => {
955
- const itemIndex = () => (virtualRange()?.start ?? 0) + index();
956
- const beforeIndicator = () =>
957
- collectionRenderer().renderDropIndicator?.(itemIndex(), "before");
958
- const onIndicator = () =>
959
- collectionRenderer().renderDropIndicator?.(itemIndex(), "on");
960
- const afterIndicator = () =>
961
- collectionRenderer().renderDropIndicator?.(itemIndex(), "after");
962
- return (
926
+ <ListBoxStateContext value={state}>
927
+ <CollectionRendererContext value={collectionRenderer()}>
928
+ <DropIndicatorContext value={dropIndicatorContextValue}>
929
+ <>
930
+ <Show when={ariaProps.label}>
931
+ <span {...cleanLabelProps()}>{ariaProps.label as JSX.Element}</span>
932
+ </Show>
933
+ <div
934
+ {...mergeProps(
935
+ domProps(),
936
+ cleanListBoxProps(),
937
+ cleanFocusProps(),
938
+ (droppableCollection()?.collectionProps as Record<string, unknown> | undefined) ??
939
+ {},
940
+ )}
941
+ ref={(el) => {
942
+ setListRef(el);
943
+ assignRef(local.ref, el);
944
+ autocompleteCtx?.collectionRef(el);
945
+ }}
946
+ class={renderProps.class()}
947
+ style={renderProps.style()}
948
+ data-focused={dataAttr(isFocused())}
949
+ data-focus-visible={dataAttr(isFocusVisible())}
950
+ data-disabled={dataAttr(resolveDisabled())}
951
+ data-empty={dataAttr(isEmpty())}
952
+ data-layout={stateProps.layout || "stack"}
953
+ data-orientation={stateProps.orientation || "vertical"}
954
+ data-drop-target={dataAttr(isRootDropTarget())}
955
+ slot={local.slot}
956
+ >
957
+ <SharedElementTransition>
958
+ {parentCollectionRenderer?.isVirtualized ? (
959
+ <>
960
+ <CollectionRoot
961
+ collection={virtualRange() ? collectionItems() : []}
962
+ scrollRef={() => listRef()}
963
+ persistedKeys={persistedKeys()}
964
+ >
965
+ {isEmpty() && local.renderEmptyState ? null : hasSections() ? (
966
+ <For each={sectionedRenderEntries()}>
967
+ {(entry) =>
968
+ entry.type === "section" ? (
969
+ <div role="presentation" data-section-wrapper>
970
+ <Section class="solidaria-ListBox-section">
971
+ {entry.section.title != null && (
972
+ <Header class="solidaria-ListBox-sectionHeader">
973
+ {entry.section.title}
974
+ </Header>
975
+ )}
976
+ <Group class="solidaria-ListBox-sectionGroup">
977
+ <div role="group" aria-label={entry.section["aria-label"]}>
978
+ <For each={entry.items}>
979
+ {(indexedItem) => (
980
+ <VirtualizerItem index={indexedItem.index}>
981
+ <ListBoxItemWithDropIndicators
982
+ item={indexedItem.item}
983
+ itemIndex={indexedItem.index}
984
+ isLastInLevel={() =>
985
+ isLastDropItem(indexedItem.index)
986
+ }
987
+ renderItem={local.children}
988
+ renderDropIndicator={renderItemDropIndicator}
989
+ />
990
+ </VirtualizerItem>
991
+ )}
992
+ </For>
993
+ </div>
994
+ </Group>
995
+ </Section>
996
+ </div>
997
+ ) : (
998
+ <VirtualizerItem index={entry.item.index}>
999
+ <ListBoxItemWithDropIndicators
1000
+ item={entry.item.item}
1001
+ itemIndex={entry.item.index}
1002
+ isLastInLevel={() => isLastDropItem(entry.item.index)}
1003
+ renderItem={local.children}
1004
+ renderDropIndicator={renderItemDropIndicator}
1005
+ />
1006
+ </VirtualizerItem>
1007
+ )
1008
+ }
1009
+ </For>
1010
+ ) : (
963
1011
  <>
964
- {beforeIndicator()}
965
- {onIndicator()}
966
- {local.children(item as T)}
967
- {afterIndicator()}
1012
+ <For each={visibleItems()}>
1013
+ {(item, index) => (
1014
+ <VirtualizerItem
1015
+ index={() => (virtualRange()?.start ?? 0) + index()}
1016
+ >
1017
+ <ListBoxItemWithDropIndicators
1018
+ item={item as T}
1019
+ itemIndex={() => (virtualRange()?.start ?? 0) + index()}
1020
+ isLastInLevel={() =>
1021
+ isLastDropItem((virtualRange()?.start ?? 0) + index())
1022
+ }
1023
+ renderItem={local.children}
1024
+ renderDropIndicator={renderItemDropIndicator}
1025
+ />
1026
+ </VirtualizerItem>
1027
+ )}
1028
+ </For>
1029
+ <For each={persistedOutsideIndexes()}>
1030
+ {(index) => (
1031
+ <PersistedVirtualItem index={index}>
1032
+ <ListBoxItemWithDropIndicators
1033
+ item={collectionItems()[index] as T}
1034
+ itemIndex={() => index}
1035
+ isLastInLevel={() => isLastDropItem(index)}
1036
+ renderItem={local.children}
1037
+ renderDropIndicator={renderItemDropIndicator}
1038
+ />
1039
+ </PersistedVirtualItem>
1040
+ )}
1041
+ </For>
968
1042
  </>
969
- );
970
- }}
1043
+ )}
1044
+ </CollectionRoot>
1045
+ {isEmpty() && local.renderEmptyState ? (
1046
+ <div role="option" style={{ display: "contents" }} data-empty-state>
1047
+ {local.renderEmptyState()}
1048
+ </div>
1049
+ ) : null}
1050
+ </>
1051
+ ) : isEmpty() && local.renderEmptyState ? (
1052
+ <div role="option" style={{ display: "contents" }} data-empty-state>
1053
+ {local.renderEmptyState()}
1054
+ </div>
1055
+ ) : hasSections() ? (
1056
+ <For each={sectionedRenderEntries()}>
1057
+ {(entry) =>
1058
+ entry.type === "section" ? (
1059
+ <div role="presentation" data-section-wrapper>
1060
+ <Section class="solidaria-ListBox-section">
1061
+ {entry.section.title != null && (
1062
+ <Header class="solidaria-ListBox-sectionHeader">
1063
+ {entry.section.title}
1064
+ </Header>
1065
+ )}
1066
+ <Group class="solidaria-ListBox-sectionGroup">
1067
+ <div role="group" aria-label={entry.section["aria-label"]}>
1068
+ <For each={entry.items}>
1069
+ {(indexedItem) => (
1070
+ <ListBoxItemWithDropIndicators
1071
+ item={indexedItem.item}
1072
+ itemIndex={indexedItem.index}
1073
+ isLastInLevel={() => isLastDropItem(indexedItem.index)}
1074
+ renderItem={local.children}
1075
+ renderDropIndicator={renderItemDropIndicator}
1076
+ />
1077
+ )}
1078
+ </For>
1079
+ </div>
1080
+ </Group>
1081
+ </Section>
1082
+ </div>
1083
+ ) : (
1084
+ <ListBoxItemWithDropIndicators
1085
+ item={entry.item.item}
1086
+ itemIndex={entry.item.index}
1087
+ isLastInLevel={() => isLastDropItem(entry.item.index)}
1088
+ renderItem={local.children}
1089
+ renderDropIndicator={renderItemDropIndicator}
1090
+ />
1091
+ )
1092
+ }
971
1093
  </For>
972
- {virtualRange()?.offsetBottom ? (
973
- <div
974
- role="presentation"
975
- aria-hidden="true"
976
- style={virtualSpacerStyle(virtualRange()!.offsetBottom)}
977
- data-virtualizer-spacer="bottom"
978
- />
979
- ) : null}
980
- </>
1094
+ ) : (
1095
+ <>
1096
+ <For each={visibleItems()}>
1097
+ {(item, index) => (
1098
+ <ListBoxItemWithDropIndicators
1099
+ item={item as T}
1100
+ itemIndex={() => (virtualRange()?.start ?? 0) + index()}
1101
+ isLastInLevel={() =>
1102
+ isLastDropItem((virtualRange()?.start ?? 0) + index())
1103
+ }
1104
+ renderItem={local.children}
1105
+ renderDropIndicator={renderItemDropIndicator}
1106
+ />
1107
+ )}
1108
+ </For>
1109
+ </>
1110
+ )}
1111
+ </SharedElementTransition>
1112
+ {local.hasMore && local.onLoadMore && (
1113
+ <ListBoxLoadMoreItem onLoadMore={local.onLoadMore} isLoading={local.isLoading} />
981
1114
  )}
982
- </SharedElementTransition>
983
- {local.hasMore && local.onLoadMore && (
984
- <ListBoxLoadMoreItem onLoadMore={local.onLoadMore} isLoading={local.isLoading} />
985
- )}
986
- </div>
987
- </>
988
- </DropIndicatorContext.Provider>
989
- </CollectionRendererContext.Provider>
990
- </ListBoxStateContext.Provider>
991
- </ListBoxContext.Provider>
1115
+ </div>
1116
+ </>
1117
+ </DropIndicatorContext>
1118
+ </CollectionRendererContext>
1119
+ </ListBoxStateContext>
1120
+ </ListBoxContext>
992
1121
  );
993
1122
  }
994
1123
 
995
1124
  /**
996
- * An option in a listbox.
1125
+ * An item in a listbox.
997
1126
  */
998
- export function ListBoxOption<T>(props: ListBoxOptionProps<T>): JSX.Element {
1127
+ export function ListBoxItem<T>(props: ListBoxItemProps<T>): JSX.Element {
999
1128
  const [local, ariaProps] = splitProps(props, [
1000
1129
  "class",
1001
1130
  "style",
@@ -1008,7 +1137,7 @@ export function ListBoxOption<T>(props: ListBoxOptionProps<T>): JSX.Element {
1008
1137
 
1009
1138
  const context = useContext(ListBoxStateContext);
1010
1139
  if (!context) {
1011
- throw new Error("ListBoxOption must be used within a ListBox");
1140
+ throw new Error("ListBoxItem must be used within a ListBox");
1012
1141
  }
1013
1142
  const state = context as ListState<T>;
1014
1143
  const listContext = useContext(ListBoxContext) as ListBoxContextValue<T> | null;
@@ -1021,7 +1150,7 @@ export function ListBoxOption<T>(props: ListBoxOptionProps<T>): JSX.Element {
1021
1150
  return Boolean(ariaProps.isDisabled || listContext?.isDisabled());
1022
1151
  },
1023
1152
  get "aria-label"() {
1024
- return ariaProps["aria-label"] ?? local.textValue;
1153
+ return ariaProps["aria-label"];
1025
1154
  },
1026
1155
  get shouldSelectOnPressUp() {
1027
1156
  return ariaProps.shouldSelectOnPressUp;
@@ -1063,9 +1192,6 @@ export function ListBoxOption<T>(props: ListBoxOptionProps<T>): JSX.Element {
1063
1192
  },
1064
1193
  renderValues,
1065
1194
  );
1066
- const hasPrimitiveLabel = () => {
1067
- return typeof props.children === "string" || typeof props.children === "number";
1068
- };
1069
1195
 
1070
1196
  const selectionIndicatorContext = createMemo<SelectionIndicatorContextValue>(() => ({
1071
1197
  isSelected: optionAria.isSelected,
@@ -1096,24 +1222,59 @@ export function ListBoxOption<T>(props: ListBoxOptionProps<T>): JSX.Element {
1096
1222
  });
1097
1223
 
1098
1224
  const cleanOptionProps = () => {
1099
- const {
1100
- ref: _ref1,
1101
- "aria-describedby": _ariaDescribedby,
1102
- ...rest
1103
- } = optionAria.optionProps as Record<string, unknown>;
1104
- if (!hasPrimitiveLabel() && rest["aria-label"] == null) {
1105
- delete rest["aria-labelledby"];
1106
- }
1225
+ const { ref: _ref1, ...rest } = optionAria.optionProps as Record<string, unknown>;
1107
1226
  return rest;
1108
1227
  };
1228
+
1229
+ const optionTextSlots = {
1230
+ slots: {
1231
+ get [DEFAULT_SLOT]() {
1232
+ return optionAria.labelProps;
1233
+ },
1234
+ get label() {
1235
+ return optionAria.labelProps;
1236
+ },
1237
+ get description() {
1238
+ return optionAria.descriptionProps;
1239
+ },
1240
+ },
1241
+ };
1242
+
1243
+ // Styled hosts (S2 ComboBoxItem / PickerItem) emit `<span slot="label">` rather
1244
+ // than `<Text>`. Stamp the slot id onto that node before `createSlotId` probes
1245
+ // the DOM, matching RAC TextContext + useSlotId.
1246
+ createRenderEffect(
1247
+ () => ({
1248
+ el: ref(),
1249
+ labelId: optionAria.labelProps.id,
1250
+ descriptionId: optionAria.descriptionProps.id,
1251
+ }),
1252
+ ({ el, labelId, descriptionId }) => {
1253
+ if (!el) return;
1254
+ if (labelId) {
1255
+ const label = el.querySelector("[slot='label']");
1256
+ if (label && !label.id) label.id = labelId;
1257
+ }
1258
+ if (descriptionId) {
1259
+ const description = el.querySelector("[slot='description']");
1260
+ if (description && !description.id) description.id = descriptionId;
1261
+ }
1262
+ },
1263
+ );
1109
1264
  const domProps = () => filterDOMProps(ariaProps as Record<string, unknown>, { global: true });
1110
1265
 
1266
+ const selectionMode = () => state.selectionMode();
1267
+
1111
1268
  return (
1112
- <SelectionIndicatorContext.Provider value={selectionIndicatorContext()}>
1269
+ <SelectionIndicatorContext value={selectionIndicatorContext()}>
1113
1270
  <div
1114
1271
  ref={(el) => {
1115
1272
  setRef(el);
1116
1273
  assignRef(local.ref, el);
1274
+ const dragRef = (
1275
+ draggableItem()?.dragProps as { ref?: (el: HTMLDivElement) => void } | undefined
1276
+ )?.ref;
1277
+ if (typeof dragRef === "function") dragRef(el);
1117
1278
  }}
1118
1279
  {...mergeProps(
1119
1280
  domProps(),
@@ -1123,23 +1284,25 @@ export function ListBoxOption<T>(props: ListBoxOptionProps<T>): JSX.Element {
1123
1284
  )}
1124
1285
  class={renderProps.class()}
1125
1286
  style={renderProps.style()}
1126
- data-selected={optionAria.isSelected() || undefined}
1127
- data-focused={optionAria.isFocused() || undefined}
1128
- data-focus-visible={optionAria.isFocusVisible() || undefined}
1129
- data-pressed={optionAria.isPressed() || undefined}
1130
- data-hovered={optionAria.isHovered() || undefined}
1131
- data-disabled={optionAria.isDisabled() || undefined}
1132
- data-dragging={draggableItem()?.isDragging || undefined}
1133
- data-drop-target={droppableItem()?.isDropTarget || undefined}
1287
+ data-selected={dataAttr(optionAria.isSelected())}
1288
+ data-focused={dataAttr(optionAria.isFocused())}
1289
+ data-focus-visible={dataAttr(optionAria.isFocusVisible())}
1290
+ data-pressed={dataAttr(optionAria.isPressed())}
1291
+ data-hovered={dataAttr(optionAria.isHovered())}
1292
+ data-disabled={dataAttr(optionAria.isDisabled())}
1293
+ data-dragging={dataAttr(draggableItem()?.isDragging)}
1294
+ data-drop-target={dataAttr(droppableItem()?.isDropTarget)}
1295
+ data-selection-mode={selectionMode() === "none" ? undefined : selectionMode()}
1134
1296
  slot={local.slot}
1135
1297
  >
1136
- {hasPrimitiveLabel() ? (
1137
- <span {...optionAria.labelProps}>{renderProps.renderChildren()}</span>
1138
- ) : (
1139
- renderProps.renderChildren()
1140
- )}
1298
+ <Provider values={[[TextContext, optionTextSlots] as [Context<unknown>, unknown]]}>
1299
+ <OptionContent
1300
+ render={renderProps.renderChildrenStable}
1301
+ labelProps={optionAria.labelProps}
1302
+ />
1303
+ </Provider>
1141
1304
  </div>
1142
- </SelectionIndicatorContext.Provider>
1305
+ </SelectionIndicatorContext>
1143
1306
  );
1144
1307
  }
1145
1308
 
@@ -1147,8 +1310,9 @@ export function ListBoxOption<T>(props: ListBoxOptionProps<T>): JSX.Element {
1147
1310
  * Load more sentinel item for listbox collections.
1148
1311
  */
1149
1312
  export function ListBoxLoadMoreItem(props: ListBoxLoadMoreItemProps): JSX.Element {
1150
- let sentinelRef: HTMLDivElement | undefined;
1313
+ const [sentinel, setSentinel] = createSignal<HTMLDivElement | undefined>();
1151
1314
  const [isPending, setIsPending] = createSignal(false);
1315
+ const scrollOffsetValue = createMemo(() => props.scrollOffset ?? 1);
1152
1316
 
1153
1317
  const isLoading = () => !!props.isLoading || isPending();
1154
1318
 
@@ -1162,28 +1326,31 @@ export function ListBoxLoadMoreItem(props: ListBoxLoadMoreItemProps): JSX.Elemen
1162
1326
  }
1163
1327
  };
1164
1328
 
1165
- createEffect(() => {
1166
- if (!sentinelRef || typeof IntersectionObserver !== "function") return;
1167
-
1168
- const offset = props.scrollOffset ?? 1;
1169
- const margin = `0px 0px ${100 * offset}% 0px`;
1170
- const observer = new IntersectionObserver(
1171
- (entries) => {
1172
- if (entries[0]?.isIntersecting) {
1173
- void triggerLoadMore();
1174
- }
1175
- },
1176
- { rootMargin: margin },
1177
- );
1178
-
1179
- observer.observe(sentinelRef);
1180
- return () => observer.disconnect();
1181
- });
1329
+ createEffect(
1330
+ () => ({
1331
+ current: sentinel(),
1332
+ scrollOffset: scrollOffsetValue(),
1333
+ }),
1334
+ ({ current, scrollOffset }) => {
1335
+ if (!current || typeof IntersectionObserver !== "function") return;
1336
+ const margin = `0px 0px ${100 * scrollOffset}% 0px`;
1337
+ const observer = new IntersectionObserver(
1338
+ (entries) => {
1339
+ if (entries[0]?.isIntersecting) {
1340
+ void triggerLoadMore();
1341
+ }
1342
+ },
1343
+ { rootMargin: margin },
1344
+ );
1345
+ observer.observe(current);
1346
+ return () => observer.disconnect();
1347
+ },
1348
+ );
1182
1349
 
1183
1350
  const renderProps = useRenderProps(
1184
1351
  {
1185
1352
  get children() {
1186
- return props.children ?? (() => (isLoading() ? "Loading more..." : "Load more"));
1353
+ return props.children;
1187
1354
  },
1188
1355
  class: props.class,
1189
1356
  style: props.style,
@@ -1192,24 +1359,29 @@ export function ListBoxLoadMoreItem(props: ListBoxLoadMoreItemProps): JSX.Elemen
1192
1359
  () => ({ isLoading: isLoading() }),
1193
1360
  );
1194
1361
 
1362
+ // RAC ListBoxLoadMoreItem (ListBox.tsx:781-803): always mount the 0×0
1363
+ // sentinel; render the loader `option` only while `isLoading` and children
1364
+ // exist. A visible "Load more" row when idle/`loading` is a local invention.
1195
1365
  return (
1196
1366
  <>
1197
1367
  <div style={{ position: "relative", width: 0, height: 0, overflow: "hidden" }} inert>
1198
- <div ref={sentinelRef} style={{ position: "absolute", height: "1px", width: "1px" }} />
1199
- </div>
1200
- <div
1201
- role="option"
1202
- aria-disabled={true}
1203
- tabIndex={0}
1204
- onFocus={() => {
1205
- void triggerLoadMore();
1206
- }}
1207
- class={renderProps.class()}
1208
- style={renderProps.style()}
1209
- data-loading={isLoading() || undefined}
1210
- >
1211
- {renderProps.renderChildren()}
1368
+ <div
1369
+ data-testid="loadMoreSentinel"
1370
+ ref={setSentinel}
1371
+ style={{ position: "absolute", height: "1px", width: "1px" }}
1372
+ />
1212
1373
  </div>
1374
+ <Show when={isLoading() && props.children}>
1375
+ <div
1376
+ role="option"
1377
+ tabindex={-1}
1378
+ class={renderProps.class()}
1379
+ style={renderProps.style()}
1380
+ data-loading
1381
+ >
1382
+ {renderProps.renderChildren()}
1383
+ </div>
1384
+ </Show>
1213
1385
  </>
1214
1386
  );
1215
1387
  }
@@ -1221,5 +1393,10 @@ export function ListBoxSection(props: ListBoxSectionProps): JSX.Element {
1221
1393
  return <Section {...props} />;
1222
1394
  }
1223
1395
 
1224
- ListBox.Option = ListBoxOption;
1396
+ /**
1397
+ * @deprecated Use {@link ListBoxItem}. Removed in a future minor.
1398
+ */
1399
+ export const ListBoxOption = ListBoxItem;
1400
+
1401
+ ListBox.Option = ListBoxItem;
1225
1402
  ListBox.LoadMoreItem = ListBoxLoadMoreItem;