@proyecto-viviana/solidaria 0.4.0 → 0.4.2

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 (480) hide show
  1. package/dist/_chunk/DragManager.js +627 -0
  2. package/dist/_chunk/DragManager.js.map +1 -0
  3. package/dist/_chunk/DragManager.jsx +627 -0
  4. package/dist/_chunk/DragManager.jsx.map +1 -0
  5. package/dist/_chunk/FocusScope.js +400 -0
  6. package/dist/_chunk/FocusScope.js.map +1 -0
  7. package/dist/_chunk/FocusScope.jsx +375 -0
  8. package/dist/_chunk/FocusScope.jsx.map +1 -0
  9. package/dist/_chunk/actiongroup.js +13 -79
  10. package/dist/_chunk/actiongroup.js.map +1 -1
  11. package/dist/_chunk/actiongroup.jsx +13 -79
  12. package/dist/_chunk/actiongroup.jsx.map +1 -1
  13. package/dist/_chunk/{live-announcer.js → announce.js} +1 -1
  14. package/dist/_chunk/announce.js.map +1 -0
  15. package/dist/_chunk/{live-announcer.jsx → announce.jsx} +1 -1
  16. package/dist/_chunk/announce.jsx.map +1 -0
  17. package/dist/_chunk/autocomplete.js +56 -8
  18. package/dist/_chunk/autocomplete.js.map +1 -1
  19. package/dist/_chunk/autocomplete.jsx +56 -8
  20. package/dist/_chunk/autocomplete.jsx.map +1 -1
  21. package/dist/_chunk/breadcrumbs.js +6 -4
  22. package/dist/_chunk/breadcrumbs.js.map +1 -1
  23. package/dist/_chunk/breadcrumbs.jsx +6 -4
  24. package/dist/_chunk/breadcrumbs.jsx.map +1 -1
  25. package/dist/_chunk/button.js +8 -9
  26. package/dist/_chunk/button.js.map +1 -1
  27. package/dist/_chunk/button.jsx +8 -9
  28. package/dist/_chunk/button.jsx.map +1 -1
  29. package/dist/_chunk/calendar.js +27 -26
  30. package/dist/_chunk/calendar.js.map +1 -1
  31. package/dist/_chunk/calendar.jsx +27 -26
  32. package/dist/_chunk/calendar.jsx.map +1 -1
  33. package/dist/_chunk/checkbox.js +5 -4
  34. package/dist/_chunk/checkbox.js.map +1 -1
  35. package/dist/_chunk/checkbox.jsx +5 -4
  36. package/dist/_chunk/checkbox.jsx.map +1 -1
  37. package/dist/_chunk/color.js +302 -18
  38. package/dist/_chunk/color.js.map +1 -1
  39. package/dist/_chunk/color.jsx +302 -18
  40. package/dist/_chunk/color.jsx.map +1 -1
  41. package/dist/_chunk/combobox.js +638 -12
  42. package/dist/_chunk/combobox.js.map +1 -1
  43. package/dist/_chunk/combobox.jsx +638 -12
  44. package/dist/_chunk/combobox.jsx.map +1 -1
  45. package/dist/_chunk/constants.js +19 -0
  46. package/dist/_chunk/constants.js.map +1 -0
  47. package/dist/_chunk/constants.jsx +19 -0
  48. package/dist/_chunk/constants.jsx.map +1 -0
  49. package/dist/_chunk/createCollator.js +3 -171
  50. package/dist/_chunk/createCollator.js.map +1 -1
  51. package/dist/_chunk/createCollator.jsx +3 -165
  52. package/dist/_chunk/createCollator.jsx.map +1 -1
  53. package/dist/_chunk/createField.js +2 -1
  54. package/dist/_chunk/createField.js.map +1 -1
  55. package/dist/_chunk/createField.jsx +2 -1
  56. package/dist/_chunk/createField.jsx.map +1 -1
  57. package/dist/_chunk/createFocus.js +12 -8
  58. package/dist/_chunk/createFocus.js.map +1 -1
  59. package/dist/_chunk/createFocus.jsx +12 -8
  60. package/dist/_chunk/createFocus.jsx.map +1 -1
  61. package/dist/_chunk/createFocusWithin.js +12 -13
  62. package/dist/_chunk/createFocusWithin.js.map +1 -1
  63. package/dist/_chunk/createFocusWithin.jsx +12 -13
  64. package/dist/_chunk/createFocusWithin.jsx.map +1 -1
  65. package/dist/_chunk/createFocusable.js +3 -3
  66. package/dist/_chunk/createFocusable.js.map +1 -1
  67. package/dist/_chunk/createFocusable.jsx +3 -3
  68. package/dist/_chunk/createFocusable.jsx.map +1 -1
  69. package/dist/_chunk/{form.js → createFormReset.js} +1 -1
  70. package/dist/_chunk/createFormReset.js.map +1 -0
  71. package/dist/_chunk/{form.jsx → createFormReset.jsx} +1 -1
  72. package/dist/_chunk/createFormReset.jsx.map +1 -0
  73. package/dist/_chunk/createHover.js +2 -1
  74. package/dist/_chunk/createHover.js.map +1 -1
  75. package/dist/_chunk/createHover.jsx +2 -1
  76. package/dist/_chunk/createHover.jsx.map +1 -1
  77. package/dist/_chunk/createInteractionModality.js +2 -3
  78. package/dist/_chunk/createInteractionModality.js.map +1 -1
  79. package/dist/_chunk/createInteractionModality.jsx +2 -3
  80. package/dist/_chunk/createInteractionModality.jsx.map +1 -1
  81. package/dist/_chunk/createLabel.js +2 -1
  82. package/dist/_chunk/createLabel.js.map +1 -1
  83. package/dist/_chunk/createLabel.jsx +2 -1
  84. package/dist/_chunk/createLabel.jsx.map +1 -1
  85. package/dist/_chunk/createLabels.js +3 -2
  86. package/dist/_chunk/createLabels.js.map +1 -1
  87. package/dist/_chunk/createLabels.jsx +3 -2
  88. package/dist/_chunk/createLabels.jsx.map +1 -1
  89. package/dist/_chunk/createLandmark.js +1 -1
  90. package/dist/_chunk/createLandmark.jsx +1 -1
  91. package/dist/_chunk/createProgressBar.js +2 -1
  92. package/dist/_chunk/createProgressBar.js.map +1 -1
  93. package/dist/_chunk/createProgressBar.jsx +2 -1
  94. package/dist/_chunk/createProgressBar.jsx.map +1 -1
  95. package/dist/_chunk/createSelectableItem.js +7 -3
  96. package/dist/_chunk/createSelectableItem.js.map +1 -1
  97. package/dist/_chunk/createSelectableItem.jsx +7 -3
  98. package/dist/_chunk/createSelectableItem.jsx.map +1 -1
  99. package/dist/_chunk/createStringFormatter.js +61 -0
  100. package/dist/_chunk/createStringFormatter.js.map +1 -0
  101. package/dist/_chunk/createStringFormatter.jsx +61 -0
  102. package/dist/_chunk/createStringFormatter.jsx.map +1 -0
  103. package/dist/_chunk/createToggle.js +5 -4
  104. package/dist/_chunk/createToggle.js.map +1 -1
  105. package/dist/_chunk/createToggle.jsx +5 -4
  106. package/dist/_chunk/createToggle.jsx.map +1 -1
  107. package/dist/_chunk/datepicker.js +1413 -1100
  108. package/dist/_chunk/datepicker.js.map +1 -1
  109. package/dist/_chunk/datepicker.jsx +1413 -1100
  110. package/dist/_chunk/datepicker.jsx.map +1 -1
  111. package/dist/_chunk/disclosure.js +2 -2
  112. package/dist/_chunk/disclosure.js.map +1 -1
  113. package/dist/_chunk/disclosure.jsx +2 -2
  114. package/dist/_chunk/disclosure.jsx.map +1 -1
  115. package/dist/_chunk/dnd.js +1586 -495
  116. package/dist/_chunk/dnd.js.map +1 -1
  117. package/dist/_chunk/dnd.jsx +1586 -495
  118. package/dist/_chunk/dnd.jsx.map +1 -1
  119. package/dist/_chunk/env.js +1 -19
  120. package/dist/_chunk/env.js.map +1 -1
  121. package/dist/_chunk/env.jsx +1 -19
  122. package/dist/_chunk/env.jsx.map +1 -1
  123. package/dist/_chunk/focus.js +1 -1
  124. package/dist/_chunk/focus.js.map +1 -1
  125. package/dist/_chunk/focus.jsx +1 -1
  126. package/dist/_chunk/focus.jsx.map +1 -1
  127. package/dist/_chunk/focus2.js +54 -318
  128. package/dist/_chunk/focus2.js.map +1 -1
  129. package/dist/_chunk/focus2.jsx +53 -292
  130. package/dist/_chunk/focus2.jsx.map +1 -1
  131. package/dist/_chunk/grid.js +1 -1
  132. package/dist/_chunk/grid.jsx +1 -1
  133. package/dist/_chunk/gridlist.js +14 -9
  134. package/dist/_chunk/gridlist.js.map +1 -1
  135. package/dist/_chunk/gridlist.jsx +14 -9
  136. package/dist/_chunk/gridlist.jsx.map +1 -1
  137. package/dist/_chunk/i18n.js +3 -58
  138. package/dist/_chunk/i18n.js.map +1 -1
  139. package/dist/_chunk/i18n.jsx +3 -58
  140. package/dist/_chunk/i18n.jsx.map +1 -1
  141. package/dist/_chunk/interactions.js +99 -9
  142. package/dist/_chunk/interactions.js.map +1 -1
  143. package/dist/_chunk/interactions.jsx +97 -9
  144. package/dist/_chunk/interactions.jsx.map +1 -1
  145. package/dist/_chunk/link.js +2 -1
  146. package/dist/_chunk/link.js.map +1 -1
  147. package/dist/_chunk/link.jsx +2 -1
  148. package/dist/_chunk/link.jsx.map +1 -1
  149. package/dist/_chunk/listbox.js +42 -10
  150. package/dist/_chunk/listbox.js.map +1 -1
  151. package/dist/_chunk/listbox.jsx +42 -10
  152. package/dist/_chunk/listbox.jsx.map +1 -1
  153. package/dist/_chunk/locale.js +173 -0
  154. package/dist/_chunk/locale.js.map +1 -0
  155. package/dist/_chunk/locale.jsx +167 -0
  156. package/dist/_chunk/locale.jsx.map +1 -0
  157. package/dist/_chunk/menu.js +5 -4
  158. package/dist/_chunk/menu.js.map +1 -1
  159. package/dist/_chunk/menu.jsx +5 -4
  160. package/dist/_chunk/menu.jsx.map +1 -1
  161. package/dist/_chunk/numberfield.js +3 -2
  162. package/dist/_chunk/numberfield.js.map +1 -1
  163. package/dist/_chunk/numberfield.jsx +3 -2
  164. package/dist/_chunk/numberfield.jsx.map +1 -1
  165. package/dist/_chunk/overlays.js +1 -1
  166. package/dist/_chunk/overlays.jsx +1 -1
  167. package/dist/_chunk/popover.js +3 -3
  168. package/dist/_chunk/popover.js.map +1 -1
  169. package/dist/_chunk/popover.jsx +3 -3
  170. package/dist/_chunk/popover.jsx.map +1 -1
  171. package/dist/_chunk/radio.js +7 -6
  172. package/dist/_chunk/radio.js.map +1 -1
  173. package/dist/_chunk/radio.jsx +7 -6
  174. package/dist/_chunk/radio.jsx.map +1 -1
  175. package/dist/_chunk/reactivity.js +21 -0
  176. package/dist/_chunk/reactivity.js.map +1 -0
  177. package/dist/_chunk/reactivity.jsx +21 -0
  178. package/dist/_chunk/reactivity.jsx.map +1 -0
  179. package/dist/_chunk/searchfield.js +2 -1
  180. package/dist/_chunk/searchfield.js.map +1 -1
  181. package/dist/_chunk/searchfield.jsx +2 -1
  182. package/dist/_chunk/searchfield.jsx.map +1 -1
  183. package/dist/_chunk/select.js +8 -5
  184. package/dist/_chunk/select.js.map +1 -1
  185. package/dist/_chunk/select.jsx +8 -5
  186. package/dist/_chunk/select.jsx.map +1 -1
  187. package/dist/_chunk/selection.js +8 -21
  188. package/dist/_chunk/selection.js.map +1 -1
  189. package/dist/_chunk/selection.jsx +8 -21
  190. package/dist/_chunk/selection.jsx.map +1 -1
  191. package/dist/_chunk/separator.js +1 -1
  192. package/dist/_chunk/separator.jsx +1 -1
  193. package/dist/_chunk/slider.js +5 -4
  194. package/dist/_chunk/slider.js.map +1 -1
  195. package/dist/_chunk/slider.jsx +5 -4
  196. package/dist/_chunk/slider.jsx.map +1 -1
  197. package/dist/_chunk/spinbutton.js +153 -0
  198. package/dist/_chunk/spinbutton.js.map +1 -0
  199. package/dist/_chunk/spinbutton.jsx +153 -0
  200. package/dist/_chunk/spinbutton.jsx.map +1 -0
  201. package/dist/_chunk/table.js +45 -8
  202. package/dist/_chunk/table.js.map +1 -1
  203. package/dist/_chunk/table.jsx +45 -8
  204. package/dist/_chunk/table.jsx.map +1 -1
  205. package/dist/_chunk/tabs.js +11 -8
  206. package/dist/_chunk/tabs.js.map +1 -1
  207. package/dist/_chunk/tabs.jsx +11 -8
  208. package/dist/_chunk/tabs.jsx.map +1 -1
  209. package/dist/_chunk/tag.js +54 -18
  210. package/dist/_chunk/tag.js.map +1 -1
  211. package/dist/_chunk/tag.jsx +54 -18
  212. package/dist/_chunk/tag.jsx.map +1 -1
  213. package/dist/_chunk/textfield.js +2 -1
  214. package/dist/_chunk/textfield.js.map +1 -1
  215. package/dist/_chunk/textfield.jsx +2 -1
  216. package/dist/_chunk/textfield.jsx.map +1 -1
  217. package/dist/_chunk/toolbar.js +4 -36
  218. package/dist/_chunk/toolbar.js.map +1 -1
  219. package/dist/_chunk/toolbar.jsx +4 -36
  220. package/dist/_chunk/toolbar.jsx.map +1 -1
  221. package/dist/_chunk/tooltip.js +2 -2
  222. package/dist/_chunk/tooltip.js.map +1 -1
  223. package/dist/_chunk/tooltip.jsx +2 -2
  224. package/dist/_chunk/tooltip.jsx.map +1 -1
  225. package/dist/_chunk/tree.js +42 -14
  226. package/dist/_chunk/tree.js.map +1 -1
  227. package/dist/_chunk/tree.jsx +42 -14
  228. package/dist/_chunk/tree.jsx.map +1 -1
  229. package/dist/_chunk/utils.js +59 -19
  230. package/dist/_chunk/utils.js.map +1 -1
  231. package/dist/_chunk/utils.jsx +59 -19
  232. package/dist/_chunk/utils.jsx.map +1 -1
  233. package/dist/_chunk/visually-hidden.js +3 -2
  234. package/dist/_chunk/visually-hidden.js.map +1 -1
  235. package/dist/_chunk/visually-hidden.jsx +3 -2
  236. package/dist/_chunk/visually-hidden.jsx.map +1 -1
  237. package/dist/actiongroup/createActionGroup.d.ts.map +1 -1
  238. package/dist/autocomplete/createAutocomplete.d.ts +10 -7
  239. package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
  240. package/dist/breadcrumbs/createBreadcrumbs.d.ts.map +1 -1
  241. package/dist/button/createToggleButtonGroup.d.ts.map +1 -1
  242. package/dist/calendar/createCalendar.d.ts.map +1 -1
  243. package/dist/calendar/createCalendarCell.d.ts.map +1 -1
  244. package/dist/calendar/createCalendarGrid.d.ts.map +1 -1
  245. package/dist/calendar/createRangeCalendar.d.ts.map +1 -1
  246. package/dist/calendar/createRangeCalendarCell.d.ts.map +1 -1
  247. package/dist/calendar/intl/index.d.ts +1 -1
  248. package/dist/calendar/intl/index.d.ts.map +1 -1
  249. package/dist/collections/index.js +1 -1
  250. package/dist/collections/index.jsx +1 -1
  251. package/dist/color/createColorArea.d.ts.map +1 -1
  252. package/dist/color/createColorField.d.ts.map +1 -1
  253. package/dist/color/createColorSlider.d.ts.map +1 -1
  254. package/dist/color/createColorSwatch.d.ts.map +1 -1
  255. package/dist/color/createColorWheel.d.ts.map +1 -1
  256. package/dist/color/intl/index.d.ts +30 -0
  257. package/dist/color/intl/index.d.ts.map +1 -0
  258. package/dist/combobox/createComboBox.d.ts.map +1 -1
  259. package/dist/combobox/intl/generated-locales.d.ts +16 -0
  260. package/dist/combobox/intl/generated-locales.d.ts.map +1 -0
  261. package/dist/combobox/intl/index.d.ts +2 -2
  262. package/dist/combobox/intl/index.d.ts.map +1 -1
  263. package/dist/datepicker/createDateField.d.ts +45 -23
  264. package/dist/datepicker/createDateField.d.ts.map +1 -1
  265. package/dist/datepicker/createDatePicker.d.ts +34 -6
  266. package/dist/datepicker/createDatePicker.d.ts.map +1 -1
  267. package/dist/datepicker/createDatePickerGroup.d.ts +13 -13
  268. package/dist/datepicker/createDatePickerGroup.d.ts.map +1 -1
  269. package/dist/datepicker/createDateRangePicker.d.ts +55 -7
  270. package/dist/datepicker/createDateRangePicker.d.ts.map +1 -1
  271. package/dist/datepicker/createDateSegment.d.ts +6 -15
  272. package/dist/datepicker/createDateSegment.d.ts.map +1 -1
  273. package/dist/datepicker/createDisplayNames.d.ts +17 -0
  274. package/dist/datepicker/createDisplayNames.d.ts.map +1 -0
  275. package/dist/datepicker/createTimeField.d.ts +12 -49
  276. package/dist/datepicker/createTimeField.d.ts.map +1 -1
  277. package/dist/datepicker/index.d.ts +3 -3
  278. package/dist/datepicker/index.d.ts.map +1 -1
  279. package/dist/datepicker/index.js +2 -2
  280. package/dist/datepicker/index.jsx +2 -2
  281. package/dist/datepicker/intl.d.ts +12 -0
  282. package/dist/datepicker/intl.d.ts.map +1 -0
  283. package/dist/dnd/DragManager.d.ts +86 -0
  284. package/dist/dnd/DragManager.d.ts.map +1 -0
  285. package/dist/dnd/DropTargetKeyboardNavigation.d.ts +42 -0
  286. package/dist/dnd/DropTargetKeyboardNavigation.d.ts.map +1 -0
  287. package/dist/dnd/createDrag.d.ts.map +1 -1
  288. package/dist/dnd/createDraggableCollection.d.ts.map +1 -1
  289. package/dist/dnd/createDraggableItem.d.ts +12 -7
  290. package/dist/dnd/createDraggableItem.d.ts.map +1 -1
  291. package/dist/dnd/createDroppableCollection.d.ts +7 -15
  292. package/dist/dnd/createDroppableCollection.d.ts.map +1 -1
  293. package/dist/dnd/index.d.ts +5 -1
  294. package/dist/dnd/index.d.ts.map +1 -1
  295. package/dist/dnd/index.js +3 -2
  296. package/dist/dnd/index.jsx +3 -2
  297. package/dist/dnd/intl/index.d.ts +39 -0
  298. package/dist/dnd/intl/index.d.ts.map +1 -0
  299. package/dist/dnd/utils.d.ts +5 -0
  300. package/dist/dnd/utils.d.ts.map +1 -1
  301. package/dist/focus/FocusScope.d.ts +7 -0
  302. package/dist/focus/FocusScope.d.ts.map +1 -1
  303. package/dist/focus/index.d.ts +2 -1
  304. package/dist/focus/index.d.ts.map +1 -1
  305. package/dist/focus/index.js +3 -2
  306. package/dist/focus/index.jsx +3 -2
  307. package/dist/focus/virtualFocus.d.ts +26 -0
  308. package/dist/focus/virtualFocus.d.ts.map +1 -0
  309. package/dist/form/index.js +1 -1
  310. package/dist/form/index.jsx +1 -1
  311. package/dist/gridlist/createGridList.d.ts.map +1 -1
  312. package/dist/i18n/index.js +4 -2
  313. package/dist/i18n/index.jsx +4 -2
  314. package/dist/index.d.ts +3 -3
  315. package/dist/index.d.ts.map +1 -1
  316. package/dist/index.js +18 -12
  317. package/dist/index.jsx +18 -12
  318. package/dist/interactions/FocusableProvider.d.ts.map +1 -1
  319. package/dist/interactions/createFocus.d.ts.map +1 -1
  320. package/dist/interactions/createFocusWithin.d.ts.map +1 -1
  321. package/dist/interactions/createInteractionModality.d.ts.map +1 -1
  322. package/dist/interactions/index.js +1 -2
  323. package/dist/interactions/index.jsx +1 -2
  324. package/dist/label/createLabels.d.ts.map +1 -1
  325. package/dist/listbox/createListBox.d.ts.map +1 -1
  326. package/dist/listbox/createOption.d.ts +1 -1
  327. package/dist/listbox/createOption.d.ts.map +1 -1
  328. package/dist/live-announcer/index.js +1 -1
  329. package/dist/live-announcer/index.jsx +1 -1
  330. package/dist/select/createSelect.d.ts +2 -0
  331. package/dist/select/createSelect.d.ts.map +1 -1
  332. package/dist/selection/createSelectableItem.d.ts.map +1 -1
  333. package/dist/selection/index.js +2 -1
  334. package/dist/selection/index.jsx +2 -1
  335. package/dist/spinbutton/createSpinButton.d.ts +41 -0
  336. package/dist/spinbutton/createSpinButton.d.ts.map +1 -0
  337. package/dist/spinbutton/index.d.ts +3 -0
  338. package/dist/spinbutton/index.d.ts.map +1 -0
  339. package/dist/spinbutton/index.js +2 -0
  340. package/dist/spinbutton/index.jsx +2 -0
  341. package/dist/spinbutton/intl.d.ts +5 -0
  342. package/dist/spinbutton/intl.d.ts.map +1 -0
  343. package/dist/table/createTable.d.ts.map +1 -1
  344. package/dist/table/createTableColumnHeader.d.ts.map +1 -1
  345. package/dist/table/createTableRow.d.ts.map +1 -1
  346. package/dist/table/createTableSelectionCheckbox.d.ts.map +1 -1
  347. package/dist/table/types.d.ts +8 -0
  348. package/dist/table/types.d.ts.map +1 -1
  349. package/dist/table/utils.d.ts +15 -0
  350. package/dist/table/utils.d.ts.map +1 -0
  351. package/dist/tabs/createTabs.d.ts +1 -0
  352. package/dist/tabs/createTabs.d.ts.map +1 -1
  353. package/dist/tag/createTag.d.ts.map +1 -1
  354. package/dist/tag/createTagGroup.d.ts +7 -0
  355. package/dist/tag/createTagGroup.d.ts.map +1 -1
  356. package/dist/toolbar/createToolbar.d.ts.map +1 -1
  357. package/dist/tree/createTree.d.ts.map +1 -1
  358. package/dist/tree/createTreeSelectionCheckbox.d.ts.map +1 -1
  359. package/dist/utils/animation.d.ts.map +1 -1
  360. package/dist/utils/dom.d.ts +1 -0
  361. package/dist/utils/dom.d.ts.map +1 -1
  362. package/dist/utils/index.d.ts +1 -1
  363. package/dist/utils/index.d.ts.map +1 -1
  364. package/dist/utils/index.js +4 -3
  365. package/dist/utils/index.jsx +4 -3
  366. package/package.json +2 -2
  367. package/src/actiongroup/createActionGroup.ts +26 -109
  368. package/src/autocomplete/createAutocomplete.ts +136 -15
  369. package/src/breadcrumbs/createBreadcrumbs.ts +12 -4
  370. package/src/button/createToggleButtonGroup.ts +19 -14
  371. package/src/calendar/createCalendar.ts +3 -4
  372. package/src/calendar/createCalendarCell.ts +17 -3
  373. package/src/calendar/createCalendarGrid.ts +5 -4
  374. package/src/calendar/createRangeCalendar.ts +27 -17
  375. package/src/calendar/createRangeCalendarCell.ts +17 -3
  376. package/src/calendar/intl/index.ts +8 -0
  377. package/src/color/createColorArea.ts +17 -10
  378. package/src/color/createColorField.ts +8 -1
  379. package/src/color/createColorSlider.ts +1 -2
  380. package/src/color/createColorSwatch.ts +4 -2
  381. package/src/color/createColorWheel.ts +0 -6
  382. package/src/color/intl/index.ts +316 -0
  383. package/src/combobox/createComboBox.ts +35 -4
  384. package/src/combobox/intl/generated-locales.ts +635 -0
  385. package/src/combobox/intl/index.ts +8 -2
  386. package/src/datepicker/createDateField.ts +228 -213
  387. package/src/datepicker/createDatePicker.ts +157 -146
  388. package/src/datepicker/createDatePickerGroup.ts +88 -82
  389. package/src/datepicker/createDateRangePicker.ts +230 -179
  390. package/src/datepicker/createDateSegment.ts +386 -389
  391. package/src/datepicker/createDisplayNames.ts +45 -0
  392. package/src/datepicker/createTimeField.ts +36 -129
  393. package/src/datepicker/index.ts +13 -14
  394. package/src/datepicker/intl.ts +633 -0
  395. package/src/dnd/DragManager.ts +693 -0
  396. package/src/dnd/DropTargetKeyboardNavigation.ts +337 -0
  397. package/src/dnd/createDrag.ts +31 -5
  398. package/src/dnd/createDraggableCollection.ts +33 -15
  399. package/src/dnd/createDraggableItem.ts +92 -5
  400. package/src/dnd/createDroppableCollection.ts +487 -373
  401. package/src/dnd/index.ts +11 -0
  402. package/src/dnd/intl/ar-AE.json +29 -0
  403. package/src/dnd/intl/bg-BG.json +29 -0
  404. package/src/dnd/intl/cs-CZ.json +29 -0
  405. package/src/dnd/intl/da-DK.json +29 -0
  406. package/src/dnd/intl/de-DE.json +29 -0
  407. package/src/dnd/intl/el-GR.json +29 -0
  408. package/src/dnd/intl/en-US.json +29 -0
  409. package/src/dnd/intl/es-ES.json +29 -0
  410. package/src/dnd/intl/et-EE.json +29 -0
  411. package/src/dnd/intl/fi-FI.json +29 -0
  412. package/src/dnd/intl/fr-FR.json +29 -0
  413. package/src/dnd/intl/he-IL.json +29 -0
  414. package/src/dnd/intl/hr-HR.json +29 -0
  415. package/src/dnd/intl/hu-HU.json +29 -0
  416. package/src/dnd/intl/index.ts +139 -0
  417. package/src/dnd/intl/it-IT.json +29 -0
  418. package/src/dnd/intl/ja-JP.json +29 -0
  419. package/src/dnd/intl/ko-KR.json +29 -0
  420. package/src/dnd/intl/lt-LT.json +29 -0
  421. package/src/dnd/intl/lv-LV.json +29 -0
  422. package/src/dnd/intl/nb-NO.json +29 -0
  423. package/src/dnd/intl/nl-NL.json +29 -0
  424. package/src/dnd/intl/pl-PL.json +29 -0
  425. package/src/dnd/intl/pt-BR.json +29 -0
  426. package/src/dnd/intl/pt-PT.json +29 -0
  427. package/src/dnd/intl/ro-RO.json +29 -0
  428. package/src/dnd/intl/ru-RU.json +29 -0
  429. package/src/dnd/intl/sk-SK.json +29 -0
  430. package/src/dnd/intl/sl-SI.json +29 -0
  431. package/src/dnd/intl/sr-SP.json +29 -0
  432. package/src/dnd/intl/sv-SE.json +29 -0
  433. package/src/dnd/intl/tr-TR.json +29 -0
  434. package/src/dnd/intl/uk-UA.json +29 -0
  435. package/src/dnd/intl/zh-CN.json +29 -0
  436. package/src/dnd/intl/zh-TW.json +29 -0
  437. package/src/dnd/utils.ts +36 -0
  438. package/src/focus/FocusScope.tsx +120 -12
  439. package/src/focus/index.ts +8 -0
  440. package/src/focus/virtualFocus.ts +55 -0
  441. package/src/gridlist/createGridList.ts +52 -16
  442. package/src/index.ts +11 -3
  443. package/src/interactions/FocusableProvider.tsx +11 -6
  444. package/src/interactions/createFocus.ts +26 -21
  445. package/src/interactions/createFocusWithin.ts +23 -24
  446. package/src/interactions/createInteractionModality.ts +5 -3
  447. package/src/label/createLabels.ts +7 -4
  448. package/src/listbox/createListBox.ts +74 -2
  449. package/src/listbox/createOption.ts +16 -1
  450. package/src/select/createSelect.ts +23 -2
  451. package/src/selection/createSelectableItem.ts +14 -0
  452. package/src/spinbutton/createSpinButton.ts +163 -0
  453. package/src/spinbutton/index.ts +10 -0
  454. package/src/spinbutton/intl.ts +115 -0
  455. package/src/table/createTable.ts +26 -1
  456. package/src/table/createTableColumnHeader.ts +10 -0
  457. package/src/table/createTableRow.ts +10 -0
  458. package/src/table/createTableSelectionCheckbox.ts +24 -1
  459. package/src/table/types.ts +8 -0
  460. package/src/table/utils.ts +41 -0
  461. package/src/tabs/createTabs.ts +53 -12
  462. package/src/tag/createTag.ts +21 -32
  463. package/src/tag/createTagGroup.ts +92 -0
  464. package/src/toolbar/createToolbar.ts +6 -58
  465. package/src/tree/createTree.ts +69 -14
  466. package/src/tree/createTreeSelectionCheckbox.ts +14 -1
  467. package/src/utils/animation.ts +119 -27
  468. package/src/utils/dom.ts +3 -2
  469. package/src/utils/index.ts +2 -0
  470. package/dist/_chunk/createLongPress.js +0 -91
  471. package/dist/_chunk/createLongPress.js.map +0 -1
  472. package/dist/_chunk/createLongPress.jsx +0 -91
  473. package/dist/_chunk/createLongPress.jsx.map +0 -1
  474. package/dist/_chunk/form.js.map +0 -1
  475. package/dist/_chunk/form.jsx.map +0 -1
  476. package/dist/_chunk/live-announcer.js.map +0 -1
  477. package/dist/_chunk/live-announcer.jsx.map +0 -1
  478. package/dist/datepicker/createTimeSegment.d.ts +0 -29
  479. package/dist/datepicker/createTimeSegment.d.ts.map +0 -1
  480. package/src/datepicker/createTimeSegment.ts +0 -352
@@ -1 +1 @@
1
- {"version":3,"file":"tag.jsx","names":[],"sources":["../../src/tag/createTagGroup.ts","../../src/tag/createTag.ts"],"sourcesContent":["/**\n * TagGroup hook for Solidaria\n *\n * Provides the behavior and accessibility implementation for a tag group component.\n * A tag group is a focusable list of labels, categories, keywords, filters, or other items,\n * with support for keyboard navigation, selection, and removal.\n *\n * Based on @react-aria/tag useTagGroup\n */\n\nimport { createEffect, onCleanup } from \"solid-js\";\nimport { createLabel } from \"../label/createLabel\";\nimport { filterDOMProps } from \"../utils/filterDOMProps\";\nimport { mergeProps } from \"../utils/mergeProps\";\nimport { createId } from \"../ssr\";\nimport { access, type MaybeAccessor } from \"../utils/reactivity\";\nimport type { ListState, Key } from \"@proyecto-viviana/solid-stately\";\n\nexport interface AriaTagGroupProps {\n /** An ID for the tag group. */\n id?: string;\n /** Whether the tag group is disabled. */\n isDisabled?: boolean;\n /** The label for the tag group. */\n label?: string;\n /** An accessible label for the tag group when no visible label is provided. */\n \"aria-label\"?: string;\n /** The ID of an element that labels the tag group. */\n \"aria-labelledby\"?: string;\n /** The ID of an element that describes the tag group. */\n \"aria-describedby\"?: string;\n /** A description of the tag group. */\n description?: string;\n /** An error message for the tag group. */\n errorMessage?: string;\n /** Handler that is called when a user removes a tag. */\n onRemove?: (keys: Set<Key>) => void;\n}\n\nexport interface TagGroupAria {\n /** Props for the tag group container element. */\n gridProps: Record<string, unknown>;\n /** Props for the tag group's visible label (if any). */\n labelProps: Record<string, unknown>;\n /** Props for the tag group description element, if any. */\n descriptionProps: Record<string, unknown>;\n /** Props for the tag group error message element, if any. */\n errorMessageProps: Record<string, unknown>;\n}\n\n// Shared data between tag group and tags\nconst tagGroupData = new WeakMap<object, TagGroupData>();\n\ninterface TagGroupData {\n id: string;\n onRemove?: (keys: Set<Key>) => void;\n}\n\nexport function getTagGroupData(state: ListState): TagGroupData | undefined {\n return tagGroupData.get(state);\n}\n\n/**\n * Provides the behavior and accessibility implementation for a tag group component.\n * A tag group is a focusable list of labels, categories, keywords, filters, or other items,\n * with support for keyboard navigation, selection, and removal.\n */\nexport function createTagGroup<T>(\n props: MaybeAccessor<AriaTagGroupProps>,\n state: ListState<T>,\n _ref?: () => HTMLElement | null,\n): TagGroupAria {\n const getProps = () => access(props);\n const id = createId(getProps().id);\n const descriptionId = createId();\n const errorMessageId = createId();\n const getFallbackAriaLabel = () => {\n const p = getProps();\n return !p.label && !p[\"aria-label\"] && !p[\"aria-labelledby\"] ? \"Tag list\" : undefined;\n };\n const sharedData: TagGroupData = {\n id,\n get onRemove() {\n return getProps().onRemove;\n },\n };\n\n // Filter DOM props\n const domProps = () =>\n filterDOMProps(getProps() as unknown as Record<string, unknown>, { labelable: true });\n\n // Create label handling\n const { labelProps, fieldProps } = createLabel({\n get label() {\n return getProps().label;\n },\n get \"aria-label\"() {\n return getProps()[\"aria-label\"] ?? getFallbackAriaLabel();\n },\n get \"aria-labelledby\"() {\n return getProps()[\"aria-labelledby\"];\n },\n labelElementType: \"span\",\n });\n\n // Share data with child tags before they create their aria state.\n tagGroupData.set(state, sharedData);\n\n // Clean up the shared state when the tag group owner is disposed.\n createEffect(() => {\n tagGroupData.set(state, sharedData);\n\n onCleanup(() => {\n if (tagGroupData.get(state) === sharedData) {\n tagGroupData.delete(state);\n }\n });\n });\n\n // Build aria-describedby\n const getAriaDescribedBy = () => {\n const p = getProps();\n const ids: string[] = [];\n if (p[\"aria-describedby\"]) {\n ids.push(p[\"aria-describedby\"]);\n }\n if (p.description) {\n ids.push(descriptionId);\n }\n if (p.errorMessage) {\n ids.push(errorMessageId);\n }\n return ids.length > 0 ? ids.join(\" \") : undefined;\n };\n\n return {\n get gridProps() {\n const p = getProps();\n const hasItems = state.collection().size > 0;\n\n return mergeProps(domProps(), fieldProps as Record<string, unknown>, {\n id,\n role: hasItems ? \"grid\" : \"group\",\n \"aria-multiselectable\": hasItems && state.selectionMode() === \"multiple\" ? true : undefined,\n \"aria-atomic\": false,\n \"aria-relevant\": \"additions\",\n \"aria-describedby\": getAriaDescribedBy(),\n \"aria-disabled\": p.isDisabled || undefined,\n });\n },\n get labelProps() {\n return labelProps as Record<string, unknown>;\n },\n get descriptionProps() {\n return {\n id: descriptionId,\n };\n },\n get errorMessageProps() {\n return {\n id: errorMessageId,\n };\n },\n };\n}\n","/**\n * Tag hook for Solidaria\n *\n * Provides the behavior and accessibility implementation for a tag component.\n * Tags are individual items within a TagGroup.\n *\n * Based on @react-aria/tag useTag\n */\n\nimport { createMemo } from \"solid-js\";\nimport { createFocusable } from \"../interactions/createFocusable\";\nimport { createPress } from \"../interactions/createPress\";\nimport { filterDOMProps } from \"../utils/filterDOMProps\";\nimport { mergeProps } from \"../utils/mergeProps\";\nimport { createId } from \"../ssr\";\nimport { access, type MaybeAccessor } from \"../utils/reactivity\";\nimport { getTagGroupData } from \"./createTagGroup\";\nimport type { ListState, Key } from \"@proyecto-viviana/solid-stately\";\n\nexport interface AriaTagProps {\n /** The unique key for this tag. */\n key: Key;\n /** The role for the tag root. Components use row semantics inside a grid. */\n role?: \"option\" | \"row\";\n /** Whether the tag is disabled. */\n isDisabled?: boolean;\n /** A text value for the tag used for accessibility. */\n textValue?: string;\n}\n\nexport interface TagAria {\n /** Props for the tag row element. */\n rowProps: Record<string, unknown>;\n /** Props for the tag cell element. */\n gridCellProps: Record<string, unknown>;\n /** Props for the tag remove button. */\n removeButtonProps: Record<string, unknown>;\n /** Whether the tag can be removed. */\n allowsRemoving: boolean;\n /** Whether the tag is selected. */\n isSelected: boolean;\n /** Whether the tag is disabled. */\n isDisabled: boolean;\n /** Whether the tag is focused. */\n isFocused: boolean;\n /** Whether the tag is pressed. */\n isPressed: boolean;\n}\n\n/**\n * Provides the behavior and accessibility implementation for a tag component.\n * Tags are individual items within a TagGroup.\n */\nexport function createTag<T>(\n props: MaybeAccessor<AriaTagProps>,\n state: ListState<T>,\n ref: () => HTMLElement | null,\n): TagAria {\n const getProps = () => access(props);\n const rowId = createId();\n const cellId = createId();\n const removeButtonId = createId();\n\n // Get shared data from tag group\n const getData = () => getTagGroupData(state);\n\n // Get key\n const key = () => getProps().key;\n\n // Compute states\n const isDisabled = createMemo(() => {\n const p = getProps();\n return p.isDisabled || state.isDisabled(key());\n });\n\n const isSelected = createMemo(() => {\n return state.isSelected(key());\n });\n\n const isSelectable = createMemo(() => state.selectionMode() !== \"none\");\n\n const isFocused = createMemo(() => {\n return state.focusedKey() === key();\n });\n\n const getFirstFocusableKey = (): Key | null => {\n const collection = state.collection();\n let candidate = collection.getFirstKey();\n while (candidate != null && state.isDisabled(candidate)) {\n candidate = collection.getKeyAfter(candidate);\n }\n return candidate;\n };\n\n const getLastFocusableKey = (): Key | null => {\n const collection = state.collection();\n let candidate = collection.getLastKey();\n while (candidate != null && state.isDisabled(candidate)) {\n candidate = collection.getKeyBefore(candidate);\n }\n return candidate;\n };\n\n const getNextFocusableKey = (fromKey: Key): Key | null => {\n const collection = state.collection();\n let candidate = collection.getKeyAfter(fromKey);\n while (candidate != null && state.isDisabled(candidate)) {\n candidate = collection.getKeyAfter(candidate);\n }\n\n if (candidate != null) {\n return candidate;\n }\n\n return getFirstFocusableKey();\n };\n\n const getPreviousFocusableKey = (fromKey: Key): Key | null => {\n const collection = state.collection();\n let candidate = collection.getKeyBefore(fromKey);\n while (candidate != null && state.isDisabled(candidate)) {\n candidate = collection.getKeyBefore(candidate);\n }\n\n if (candidate != null) {\n return candidate;\n }\n\n return getLastFocusableKey();\n };\n\n const focusKey = (nextKey: Key | null) => {\n if (nextKey == null) {\n return;\n }\n\n state.setFocusedKey(nextKey);\n const currentElement = ref();\n\n if (!currentElement) {\n return;\n }\n\n if (nextKey === key()) {\n currentElement.focus();\n return;\n }\n\n const tagList = currentElement.parentElement;\n if (!tagList) {\n return;\n }\n\n const nextTag = Array.from(\n tagList.querySelectorAll<HTMLElement>('[role=\"option\"], [role=\"row\"]'),\n ).find((el) => el.getAttribute(\"data-key\") === String(nextKey));\n\n nextTag?.focus();\n };\n\n // Handle press for selection\n const { pressProps, isPressed } = createPress({\n isDisabled,\n onPress: () => {\n if (!isDisabled()) {\n state.setFocusedKey(key());\n state.toggleSelection(key());\n }\n },\n });\n\n // Handle focusable\n const { focusableProps } = createFocusable(\n {\n isDisabled,\n onFocus: () => {\n state.setFocusedKey(key());\n },\n },\n ref,\n );\n\n // Handle keyboard for navigation and removal\n const handleKeyDown = (e: KeyboardEvent) => {\n if (isDisabled()) return;\n\n switch (e.key) {\n case \"ArrowRight\":\n case \"ArrowDown\":\n e.preventDefault();\n focusKey(getNextFocusableKey(key()));\n return;\n case \"ArrowLeft\":\n case \"ArrowUp\":\n e.preventDefault();\n focusKey(getPreviousFocusableKey(key()));\n return;\n case \"Home\":\n e.preventDefault();\n focusKey(getFirstFocusableKey());\n return;\n case \"End\":\n e.preventDefault();\n focusKey(getLastFocusableKey());\n return;\n default:\n break;\n }\n\n if (e.key === \"Delete\" || e.key === \"Backspace\") {\n e.preventDefault();\n const data = getData();\n if (data?.onRemove) {\n // Remove selected keys if this tag is selected, otherwise just this tag\n if (isSelected()) {\n const selection = state.selectedKeys();\n const keysToRemove =\n selection === \"all\"\n ? new Set(Array.from(state.collection()).map((item) => (item as { key: Key }).key))\n : new Set(selection);\n data.onRemove(keysToRemove);\n } else {\n data.onRemove(new Set([key()]));\n }\n }\n }\n };\n\n // Compute tabIndex\n const tabIndex = createMemo(() => {\n if (isDisabled()) return -1;\n\n if (isFocused()) {\n return 0;\n }\n\n if (state.focusedKey() !== null) {\n return -1;\n }\n\n const collection = state.collection();\n let defaultTabStop: Key | null = null;\n\n if (state.selectionMode() !== \"none\") {\n for (const item of collection) {\n if (!state.isDisabled(item.key) && state.isSelected(item.key)) {\n defaultTabStop = item.key;\n break;\n }\n }\n }\n\n if (defaultTabStop == null) {\n defaultTabStop = getFirstFocusableKey();\n }\n\n if (key() === defaultTabStop) {\n return 0;\n }\n\n return -1;\n });\n\n // Filter DOM props\n const domProps = () => filterDOMProps(getProps() as unknown as Record<string, unknown>);\n\n // Check if removal is allowed\n const allowsRemoving = createMemo(() => {\n const data = getData();\n return !!data?.onRemove;\n });\n\n const rootRole = createMemo(() => getProps().role ?? \"option\");\n\n return {\n get rowProps() {\n return mergeProps(\n domProps(),\n focusableProps as Record<string, unknown>,\n pressProps as Record<string, unknown>,\n {\n id: rowId,\n role: rootRole(),\n tabIndex: tabIndex(),\n \"data-key\": String(key()),\n \"aria-label\": getProps().textValue,\n \"aria-selected\": isSelectable() ? isSelected() : undefined,\n \"aria-disabled\": isDisabled() || undefined,\n onKeyDown: handleKeyDown,\n },\n );\n },\n get gridCellProps() {\n return {\n id: cellId,\n role: \"gridcell\",\n \"aria-describedby\": allowsRemoving() ? removeButtonId : undefined,\n };\n },\n get removeButtonProps() {\n const data = getData();\n return {\n id: removeButtonId,\n \"aria-label\": \"Remove\",\n \"aria-labelledby\": `${removeButtonId} ${rowId}`,\n isDisabled: isDisabled(),\n onPress: () => {\n if (data?.onRemove && !isDisabled()) {\n data.onRemove(new Set([key()]));\n }\n },\n };\n },\n get allowsRemoving() {\n return allowsRemoving();\n },\n get isSelected() {\n return isSelected();\n },\n get isDisabled() {\n return isDisabled();\n },\n get isFocused() {\n return isFocused();\n },\n get isPressed() {\n return isPressed();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tag.jsx","names":["mergeProps","mergeProps"],"sources":["../../src/tag/createTagGroup.ts","../../src/tag/createTag.ts"],"sourcesContent":["/**\n * TagGroup hook for Solidaria\n *\n * Provides the behavior and accessibility implementation for a tag group component.\n * A tag group is a focusable list of labels, categories, keywords, filters, or other items,\n * with support for keyboard navigation, selection, and removal.\n *\n * Based on @react-aria/tag useTagGroup\n */\n\nimport { createEffect, onCleanup } from \"solid-js\";\nimport { createLabel } from \"../label/createLabel\";\nimport { filterDOMProps } from \"../utils/filterDOMProps\";\nimport { mergeProps } from \"../utils/mergeProps\";\nimport { createId } from \"../ssr\";\nimport { access, type MaybeAccessor } from \"../utils/reactivity\";\nimport type { ListState, Key } from \"@proyecto-viviana/solid-stately\";\n\nexport interface AriaTagGroupProps {\n /** An ID for the tag group. */\n id?: string;\n /** Whether the tag group is disabled. */\n isDisabled?: boolean;\n /** The label for the tag group. */\n label?: string;\n /** An accessible label for the tag group when no visible label is provided. */\n \"aria-label\"?: string;\n /** The ID of an element that labels the tag group. */\n \"aria-labelledby\"?: string;\n /** The ID of an element that describes the tag group. */\n \"aria-describedby\"?: string;\n /** A description of the tag group. */\n description?: string;\n /** An error message for the tag group. */\n errorMessage?: string;\n /** Handler that is called when a user removes a tag. */\n onRemove?: (keys: Set<Key>) => void;\n /**\n * The layout direction, threaded from `useLocale`. A TagGroup navigates on the\n * inline axis, so ArrowLeft/ArrowRight flip under RTL (mirrors the direction\n * the ListKeyboardDelegate receives from useTagGroup).\n */\n direction?: \"ltr\" | \"rtl\";\n}\n\nexport interface TagGroupAria {\n /** Props for the tag group container element. */\n gridProps: Record<string, unknown>;\n /** Props for the tag group's visible label (if any). */\n labelProps: Record<string, unknown>;\n /** Props for the tag group description element, if any. */\n descriptionProps: Record<string, unknown>;\n /** Props for the tag group error message element, if any. */\n errorMessageProps: Record<string, unknown>;\n}\n\n// Shared data between tag group and tags\nconst tagGroupData = new WeakMap<object, TagGroupData>();\n\ninterface TagGroupData {\n id: string;\n onRemove?: (keys: Set<Key>) => void;\n direction: \"ltr\" | \"rtl\";\n}\n\nexport function getTagGroupData(state: ListState): TagGroupData | undefined {\n return tagGroupData.get(state);\n}\n\n/**\n * Provides the behavior and accessibility implementation for a tag group component.\n * A tag group is a focusable list of labels, categories, keywords, filters, or other items,\n * with support for keyboard navigation, selection, and removal.\n */\nexport function createTagGroup<T>(\n props: MaybeAccessor<AriaTagGroupProps>,\n state: ListState<T>,\n _ref?: () => HTMLElement | null,\n): TagGroupAria {\n const getProps = () => access(props);\n const id = createId(getProps().id);\n const descriptionId = createId();\n const errorMessageId = createId();\n const getFallbackAriaLabel = () => {\n const p = getProps();\n return !p.label && !p[\"aria-label\"] && !p[\"aria-labelledby\"] ? \"Tag list\" : undefined;\n };\n const sharedData: TagGroupData = {\n id,\n get onRemove() {\n return getProps().onRemove;\n },\n get direction() {\n return getProps().direction ?? \"ltr\";\n },\n };\n\n // Filter DOM props\n const domProps = () =>\n filterDOMProps(getProps() as unknown as Record<string, unknown>, { labelable: true });\n\n // Create label handling\n const { labelProps, fieldProps } = createLabel({\n get label() {\n return getProps().label;\n },\n get \"aria-label\"() {\n return getProps()[\"aria-label\"] ?? getFallbackAriaLabel();\n },\n get \"aria-labelledby\"() {\n return getProps()[\"aria-labelledby\"];\n },\n labelElementType: \"span\",\n });\n\n // Share data with child tags before they create their aria state.\n tagGroupData.set(state, sharedData);\n\n // Clean up the shared state when the tag group owner is disposed.\n createEffect(() => {\n tagGroupData.set(state, sharedData);\n\n onCleanup(() => {\n if (tagGroupData.get(state) === sharedData) {\n tagGroupData.delete(state);\n }\n });\n });\n\n // Build aria-describedby\n const getAriaDescribedBy = () => {\n const p = getProps();\n const ids: string[] = [];\n if (p[\"aria-describedby\"]) {\n ids.push(p[\"aria-describedby\"]);\n }\n if (p.description) {\n ids.push(descriptionId);\n }\n if (p.errorMessage) {\n ids.push(errorMessageId);\n }\n return ids.length > 0 ? ids.join(\" \") : undefined;\n };\n\n const getRef = () => _ref?.() ?? null;\n\n // First/last navigable (non-disabled) row keys — the entry targets.\n const getFirstNavigableKey = (): Key | null => {\n const collection = state.collection();\n let candidate = collection.getFirstKey();\n while (candidate != null && state.isDisabled(candidate)) {\n candidate = collection.getKeyAfter(candidate);\n }\n return candidate;\n };\n\n const getLastNavigableKey = (): Key | null => {\n const collection = state.collection();\n let candidate = collection.getLastKey();\n while (candidate != null && state.isDisabled(candidate)) {\n candidate = collection.getKeyBefore(candidate);\n }\n return candidate;\n };\n\n // Focus marshalling — the grid container is a trampoline. Its roving tabIndex\n // is 0 only while `focusedKey == null` (see gridProps below), so a forward Tab\n // that reaches the standalone container lands on the container itself; move the\n // focused key onto the first/last row and the post-commit effect below pulls\n // REAL DOM focus there while the container rolls to tabIndex -1. Mirrors\n // useSelectableCollection's `onFocus` (via useTagGroup → useGridList). A\n // Shift+Tab arriving directly on a row is handled by the row's own focusable\n // onFocus, since every non-disabled row is a tab stop when nothing is focused.\n const onFocus = (e: FocusEvent) => {\n state.setFocused(true);\n\n // `focus` is non-bubbling in Solid, so this fires only when the container\n // itself receives focus; ignore anything that bubbled from a descendant.\n const container = e.currentTarget as HTMLElement | null;\n if (container && e.target && !container.contains(e.target as Node)) return;\n if (state.focusedKey() != null || getProps().isDisabled) return;\n\n const relatedTarget = e.relatedTarget as Element | null;\n // Focus arriving from an element that FOLLOWS the group in document order\n // means the user shift-tabbed backward into it → enter at the LAST row;\n // otherwise (forward Tab, or programmatic) enter at the FIRST.\n const enterKey =\n relatedTarget &&\n container &&\n container.compareDocumentPosition(relatedTarget) & Node.DOCUMENT_POSITION_FOLLOWING\n ? getLastNavigableKey()\n : getFirstNavigableKey();\n if (enterKey != null) {\n state.setFocusedKey(enterKey);\n }\n };\n\n const onBlur = () => {\n state.setFocused(false);\n };\n\n // Once the roving tabindex for the focused key has committed to the DOM, move\n // browser focus onto that row (looked up by its stable data-key), mirroring\n // useSelectableCollection. Only manage focus while it already lives inside the\n // container — i.e. the user is navigating via the trampoline — so a background\n // focusedKey change never yanks focus from elsewhere on the page.\n createEffect(() => {\n const key = state.focusedKey();\n const el = getRef();\n if (!el || key == null) return;\n\n const active = document.activeElement;\n if (!active || (active !== el && !el.contains(active))) return;\n\n const target = el.querySelector<HTMLElement>(`[data-key=\"${CSS.escape(String(key))}\"]`);\n if (target && target !== active) {\n target.focus();\n }\n });\n\n return {\n get gridProps() {\n const p = getProps();\n const hasItems = state.collection().size > 0;\n\n return mergeProps(domProps(), fieldProps as Record<string, unknown>, {\n id,\n role: hasItems ? \"grid\" : \"group\",\n \"aria-multiselectable\": hasItems && state.selectionMode() === \"multiple\" ? true : undefined,\n \"aria-atomic\": false,\n \"aria-relevant\": \"additions\",\n \"aria-describedby\": getAriaDescribedBy(),\n \"aria-disabled\": p.isDisabled || undefined,\n // Roving container tabIndex mirrors useSelectableCollection: the container\n // is tabbable (0) only while nothing is focused, then rolls to -1 once a\n // row takes focus so Tab exits the group.\n tabIndex: p.isDisabled ? undefined : state.focusedKey() != null ? -1 : 0,\n onFocus,\n onBlur,\n });\n },\n get labelProps() {\n return labelProps as Record<string, unknown>;\n },\n get descriptionProps() {\n return {\n id: descriptionId,\n };\n },\n get errorMessageProps() {\n return {\n id: errorMessageId,\n };\n },\n };\n}\n","/**\n * Tag hook for Solidaria\n *\n * Provides the behavior and accessibility implementation for a tag component.\n * Tags are individual items within a TagGroup.\n *\n * Based on @react-aria/tag useTag\n */\n\nimport { createMemo } from \"solid-js\";\nimport { createFocusable } from \"../interactions/createFocusable\";\nimport { createPress } from \"../interactions/createPress\";\nimport { filterDOMProps } from \"../utils/filterDOMProps\";\nimport { mergeProps } from \"../utils/mergeProps\";\nimport { createId } from \"../ssr\";\nimport { access, type MaybeAccessor } from \"../utils/reactivity\";\nimport { getTagGroupData } from \"./createTagGroup\";\nimport type { ListState, Key } from \"@proyecto-viviana/solid-stately\";\n\nexport interface AriaTagProps {\n /** The unique key for this tag. */\n key: Key;\n /** The role for the tag root. Components use row semantics inside a grid. */\n role?: \"option\" | \"row\";\n /** Whether the tag is disabled. */\n isDisabled?: boolean;\n /** A text value for the tag used for accessibility. */\n textValue?: string;\n}\n\nexport interface TagAria {\n /** Props for the tag row element. */\n rowProps: Record<string, unknown>;\n /** Props for the tag cell element. */\n gridCellProps: Record<string, unknown>;\n /** Props for the tag remove button. */\n removeButtonProps: Record<string, unknown>;\n /** Whether the tag can be removed. */\n allowsRemoving: boolean;\n /** Whether the tag is selected. */\n isSelected: boolean;\n /** Whether the tag is disabled. */\n isDisabled: boolean;\n /** Whether the tag is focused. */\n isFocused: boolean;\n /** Whether the tag is pressed. */\n isPressed: boolean;\n}\n\n/**\n * Provides the behavior and accessibility implementation for a tag component.\n * Tags are individual items within a TagGroup.\n */\nexport function createTag<T>(\n props: MaybeAccessor<AriaTagProps>,\n state: ListState<T>,\n ref: () => HTMLElement | null,\n): TagAria {\n const getProps = () => access(props);\n const rowId = createId();\n const cellId = createId();\n const removeButtonId = createId();\n\n // Get shared data from tag group\n const getData = () => getTagGroupData(state);\n\n // Get key\n const key = () => getProps().key;\n\n // Compute states\n const isDisabled = createMemo(() => {\n const p = getProps();\n return p.isDisabled || state.isDisabled(key());\n });\n\n const isSelected = createMemo(() => {\n return state.isSelected(key());\n });\n\n const isSelectable = createMemo(() => state.selectionMode() !== \"none\");\n\n const isFocused = createMemo(() => {\n return state.focusedKey() === key();\n });\n\n const getFirstFocusableKey = (): Key | null => {\n const collection = state.collection();\n let candidate = collection.getFirstKey();\n while (candidate != null && state.isDisabled(candidate)) {\n candidate = collection.getKeyAfter(candidate);\n }\n return candidate;\n };\n\n const getLastFocusableKey = (): Key | null => {\n const collection = state.collection();\n let candidate = collection.getLastKey();\n while (candidate != null && state.isDisabled(candidate)) {\n candidate = collection.getKeyBefore(candidate);\n }\n return candidate;\n };\n\n const getNextFocusableKey = (fromKey: Key): Key | null => {\n const collection = state.collection();\n let candidate = collection.getKeyAfter(fromKey);\n while (candidate != null && state.isDisabled(candidate)) {\n candidate = collection.getKeyAfter(candidate);\n }\n\n if (candidate != null) {\n return candidate;\n }\n\n return getFirstFocusableKey();\n };\n\n const getPreviousFocusableKey = (fromKey: Key): Key | null => {\n const collection = state.collection();\n let candidate = collection.getKeyBefore(fromKey);\n while (candidate != null && state.isDisabled(candidate)) {\n candidate = collection.getKeyBefore(candidate);\n }\n\n if (candidate != null) {\n return candidate;\n }\n\n return getLastFocusableKey();\n };\n\n const focusKey = (nextKey: Key | null) => {\n if (nextKey == null) {\n return;\n }\n\n state.setFocusedKey(nextKey);\n const currentElement = ref();\n\n if (!currentElement) {\n return;\n }\n\n if (nextKey === key()) {\n currentElement.focus();\n return;\n }\n\n const tagList = currentElement.parentElement;\n if (!tagList) {\n return;\n }\n\n const nextTag = Array.from(\n tagList.querySelectorAll<HTMLElement>('[role=\"option\"], [role=\"row\"]'),\n ).find((el) => el.getAttribute(\"data-key\") === String(nextKey));\n\n nextTag?.focus();\n };\n\n // Handle press for selection\n const { pressProps, isPressed } = createPress({\n isDisabled,\n onPress: () => {\n if (!isDisabled()) {\n state.setFocusedKey(key());\n state.toggleSelection(key());\n }\n },\n });\n\n // Handle focusable\n const { focusableProps } = createFocusable(\n {\n isDisabled,\n onFocus: () => {\n state.setFocusedKey(key());\n },\n },\n ref,\n );\n\n // Handle keyboard for navigation and removal\n const handleKeyDown = (e: KeyboardEvent) => {\n if (isDisabled()) return;\n\n // A TagGroup is inherently horizontal (useTagGroup passes\n // `orientation: 'horizontal'` to the ListKeyboardDelegate), so the inline\n // (Left/Right) axis is the navigation axis and flips under RTL, while the\n // block (Up/Down) axis stays DOM-ordered. Mirror ListKeyboardDelegate:\n // getKeyRightOf = rtl ? previous : next; getKeyLeftOf = rtl ? next : previous;\n // getKeyBelow/Above are never direction-flipped.\n const isRtl = getData()?.direction === \"rtl\";\n\n switch (e.key) {\n case \"ArrowRight\":\n e.preventDefault();\n focusKey(isRtl ? getPreviousFocusableKey(key()) : getNextFocusableKey(key()));\n return;\n case \"ArrowLeft\":\n e.preventDefault();\n focusKey(isRtl ? getNextFocusableKey(key()) : getPreviousFocusableKey(key()));\n return;\n case \"ArrowDown\":\n e.preventDefault();\n focusKey(getNextFocusableKey(key()));\n return;\n case \"ArrowUp\":\n e.preventDefault();\n focusKey(getPreviousFocusableKey(key()));\n return;\n case \"Home\":\n e.preventDefault();\n focusKey(getFirstFocusableKey());\n return;\n case \"End\":\n e.preventDefault();\n focusKey(getLastFocusableKey());\n return;\n default:\n break;\n }\n\n if (e.key === \"Delete\" || e.key === \"Backspace\") {\n e.preventDefault();\n const data = getData();\n if (data?.onRemove) {\n // Remove selected keys if this tag is selected, otherwise just this tag\n if (isSelected()) {\n const selection = state.selectedKeys();\n const keysToRemove =\n selection === \"all\"\n ? new Set(Array.from(state.collection()).map((item) => (item as { key: Key }).key))\n : new Set(selection);\n data.onRemove(keysToRemove);\n } else {\n data.onRemove(new Set([key()]));\n }\n }\n }\n };\n\n // Compute tabIndex. Mirror useTag (vendored @react-aria/tag/src/useTag.ts):\n // tabIndex = (!isDisabled && (isFocused || focusedKey == null)) ? 0 : -1\n // Every non-disabled row is a tab stop when nothing is focused yet (so native\n // Shift+Tab from a following element lands on the LAST row); once a key is\n // focused, only that row keeps tabIndex 0 (roving single tab stop).\n const tabIndex = createMemo(() => {\n if (isDisabled()) return -1;\n return isFocused() || state.focusedKey() == null ? 0 : -1;\n });\n\n // Filter DOM props\n const domProps = () => filterDOMProps(getProps() as unknown as Record<string, unknown>);\n\n // Check if removal is allowed\n const allowsRemoving = createMemo(() => {\n const data = getData();\n return !!data?.onRemove;\n });\n\n const rootRole = createMemo(() => getProps().role ?? \"option\");\n\n return {\n get rowProps() {\n return mergeProps(\n domProps(),\n focusableProps as Record<string, unknown>,\n pressProps as Record<string, unknown>,\n {\n id: rowId,\n role: rootRole(),\n tabIndex: tabIndex(),\n \"data-key\": String(key()),\n \"aria-label\": getProps().textValue,\n \"aria-selected\": isSelectable() ? isSelected() : undefined,\n \"aria-disabled\": isDisabled() || undefined,\n onKeyDown: handleKeyDown,\n },\n );\n },\n get gridCellProps() {\n return {\n id: cellId,\n role: \"gridcell\",\n \"aria-describedby\": allowsRemoving() ? removeButtonId : undefined,\n };\n },\n get removeButtonProps() {\n const data = getData();\n return {\n id: removeButtonId,\n \"aria-label\": \"Remove\",\n \"aria-labelledby\": `${removeButtonId} ${rowId}`,\n isDisabled: isDisabled(),\n onPress: () => {\n if (data?.onRemove && !isDisabled()) {\n data.onRemove(new Set([key()]));\n }\n },\n };\n },\n get allowsRemoving() {\n return allowsRemoving();\n },\n get isSelected() {\n return isSelected();\n },\n get isDisabled() {\n return isDisabled();\n },\n get isFocused() {\n return isFocused();\n },\n get isPressed() {\n return isPressed();\n },\n };\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
- import { i as access, o as mergeProps } from "./env.js";
1
+ import { i as mergeProps } from "./env.js";
2
2
  import { t as filterDOMProps } from "./filterDOMProps.js";
3
+ import { t as access } from "./reactivity.js";
3
4
  import { n as createFocusable } from "./createFocusable.js";
4
5
  import { t as createField } from "./createField.js";
5
6
  //#region src/textfield/createTextField.ts
@@ -1 +1 @@
1
- {"version":3,"file":"textfield.js","names":[],"sources":["../../src/textfield/createTextField.ts"],"sourcesContent":["/**\n * TextField hook for Solidaria\n *\n * Provides the behavior and accessibility implementation for a text field.\n *\n * This is a 1:1 port of @react-aria/textfield's useTextField hook.\n */\n\nimport { JSX } from \"solid-js\";\nimport { createField, type AriaFieldProps, type FieldAria } from \"../label\";\nimport { createFocusable, type FocusableDOMProps, type FocusableProps } from \"../interactions\";\nimport { mergeProps, filterDOMProps } from \"../utils\";\nimport { type MaybeAccessor, access } from \"../utils/reactivity\";\n\nexport interface AriaTextFieldProps extends AriaFieldProps, FocusableProps, FocusableDOMProps {\n /** The current value (controlled). */\n value?: string;\n /** The default value (uncontrolled). */\n defaultValue?: string;\n /** Handler that is called when the value changes. */\n onChange?: (value: string) => void;\n /** Whether the input is disabled. */\n isDisabled?: boolean;\n /** Whether the input is read only. */\n isReadOnly?: boolean;\n /** Whether the input is required. */\n isRequired?: boolean;\n /** Whether to use native HTML form validation or ARIA validation semantics. */\n validationBehavior?: \"aria\" | \"native\";\n /** The type of input to render. */\n type?: \"text\" | \"search\" | \"url\" | \"tel\" | \"email\" | \"password\" | string;\n /** The input mode hint for virtual keyboards. */\n inputMode?: \"none\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | \"search\";\n /** Hint for the enter key action on virtual keyboards. */\n enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\";\n /** The name of the input element, used when submitting an HTML form. */\n name?: string;\n /** Associates the input with a form element by id. */\n form?: string;\n /** Regex pattern to validate the input value. */\n pattern?: string;\n /** The maximum number of characters supported by the input. */\n maxLength?: number;\n /** The minimum number of characters required by the input. */\n minLength?: number;\n /** Placeholder text for the input. */\n placeholder?: string;\n /** Whether to enable auto complete. */\n autoComplete?: string;\n /** Whether to enable auto correct. */\n autoCorrect?: string;\n /** Whether to enable spell check. */\n spellCheck?: \"true\" | \"false\";\n /** Controls whether and how text input is automatically capitalized. */\n autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\";\n /** The element type to use for the input. Defaults to 'input'. */\n inputElementType?: \"input\" | \"textarea\";\n\n // Clipboard events\n onCopy?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n onCut?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n onPaste?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n\n // Composition events\n onCompositionStart?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n onCompositionEnd?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n onCompositionUpdate?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n\n // Selection events\n onSelect?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, Event>;\n\n // Input events\n onBeforeInput?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, InputEvent>;\n onInput?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, InputEvent>;\n}\n\nexport interface TextFieldAria<\n T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends Omit<FieldAria, \"fieldProps\"> {\n /** Props for the input element. */\n inputProps: JSX.InputHTMLAttributes<T>;\n /** Whether the text field is invalid. */\n isInvalid: boolean;\n}\n\n/**\n * Provides the behavior and accessibility implementation for a text field.\n * Text fields allow users to input text with a keyboard.\n *\n * @param props - Props for the text field.\n * @param ref - Optional ref callback for the input element.\n */\nexport function createTextField<\n T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n>(props: MaybeAccessor<AriaTextFieldProps>, ref?: (el: T) => void): TextFieldAria<T> {\n const getProps = () => access(props);\n let lastInputValue: string | undefined;\n\n const eventWithCurrentTarget = (\n event: InputEvent,\n element: HTMLInputElement | HTMLTextAreaElement,\n ) =>\n new Proxy(event, {\n get(target, property, receiver) {\n if (property === \"target\" || property === \"currentTarget\") {\n return element;\n }\n\n const value = Reflect.get(target, property, receiver);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n\n // Get field accessibility props (label, description, error message)\n const { labelProps, fieldProps, descriptionProps, errorMessageProps } = createField(props);\n\n // Get focusable props\n const { focusableProps } = createFocusable(\n {\n get isDisabled() {\n return getProps().isDisabled;\n },\n get autoFocus() {\n return getProps().autoFocus;\n },\n get excludeFromTabOrder() {\n return getProps().excludeFromTabOrder;\n },\n onFocus: getProps().onFocus,\n onBlur: getProps().onBlur,\n onFocusChange: getProps().onFocusChange,\n onKeyDown: getProps().onKeyDown,\n onKeyUp: getProps().onKeyUp,\n },\n ref as ((el: HTMLElement) => void) | undefined,\n );\n\n // Filter DOM props\n const getDomProps = () =>\n filterDOMProps(getProps() as Record<string, unknown>, { labelable: true });\n\n // Build input props\n const getInputProps = (): JSX.InputHTMLAttributes<T> => {\n const p = getProps();\n const isInvalid = p.isInvalid ?? false;\n const isTextarea = p.inputElementType === \"textarea\";\n const validationBehavior = p.validationBehavior ?? \"native\";\n\n return mergeProps(\n getDomProps(),\n {\n disabled: p.isDisabled,\n readOnly: p.isReadOnly,\n required: validationBehavior === \"native\" && p.isRequired,\n \"aria-required\": (validationBehavior === \"aria\" && p.isRequired) || undefined,\n \"aria-invalid\": isInvalid || undefined,\n value: p.value ?? p.defaultValue ?? \"\",\n onChange: (e: Event) => {\n const target = e.target as HTMLInputElement | HTMLTextAreaElement;\n if (target.value !== lastInputValue) {\n p.onChange?.(target.value);\n }\n },\n // Don't include type and pattern for textarea elements\n type: isTextarea ? undefined : (p.type ?? \"text\"),\n inputMode: p.inputMode,\n enterKeyHint: p.enterKeyHint,\n name: p.name,\n form: p.form,\n pattern: isTextarea ? undefined : p.pattern,\n maxLength: p.maxLength,\n minLength: p.minLength,\n placeholder: p.placeholder,\n autoComplete: p.autoComplete,\n autoCorrect: p.autoCorrect,\n autoCapitalize: p.autoCapitalize,\n spellCheck: p.spellCheck,\n\n // Clipboard events\n onCopy: p.onCopy,\n onCut: p.onCut,\n onPaste: p.onPaste,\n\n // Composition events\n onCompositionStart: p.onCompositionStart,\n onCompositionEnd: p.onCompositionEnd,\n onCompositionUpdate: p.onCompositionUpdate,\n\n // Selection events\n onSelect: p.onSelect,\n\n // Input events\n onBeforeInput: p.onBeforeInput,\n onInput: (e: InputEvent) => {\n const target = e.target as HTMLInputElement | HTMLTextAreaElement;\n const nextValue = target.value;\n p.onInput?.(\n eventWithCurrentTarget(e, target) as Parameters<NonNullable<typeof p.onInput>>[0],\n );\n lastInputValue = nextValue;\n p.onChange?.(nextValue);\n },\n },\n focusableProps as Record<string, unknown>,\n fieldProps as Record<string, unknown>,\n ) as JSX.InputHTMLAttributes<T>;\n };\n\n const getIsInvalid = () => {\n return getProps().isInvalid ?? false;\n };\n\n return {\n get labelProps() {\n return labelProps;\n },\n get inputProps() {\n return getInputProps();\n },\n get descriptionProps() {\n return descriptionProps;\n },\n get errorMessageProps() {\n return errorMessageProps;\n },\n get isInvalid() {\n return getIsInvalid();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;AA4FA,SAAgB,gBAEd,OAA0C,KAAyC;CACnF,MAAM,iBAAiB,OAAO,KAAK;CACnC,IAAI;CAEJ,MAAM,0BACJ,OACA,YAEA,IAAI,MAAM,OAAO,EACf,IAAI,QAAQ,UAAU,UAAU;EAC9B,IAAI,aAAa,YAAY,aAAa,iBACxC,OAAO;EAGT,MAAM,QAAQ,QAAQ,IAAI,QAAQ,UAAU,QAAQ;EACpD,OAAO,OAAO,UAAU,aAAa,MAAM,KAAK,MAAM,IAAI;CAC5D,EACF,CAAC;CAGH,MAAM,EAAE,YAAY,YAAY,kBAAkB,sBAAsB,YAAY,KAAK;CAGzF,MAAM,EAAE,mBAAmB,gBACzB;EACE,IAAI,aAAa;GACf,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,IAAI,YAAY;GACd,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,IAAI,sBAAsB;GACxB,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,SAAS,SAAS,CAAC,CAAC;EACpB,QAAQ,SAAS,CAAC,CAAC;EACnB,eAAe,SAAS,CAAC,CAAC;EAC1B,WAAW,SAAS,CAAC,CAAC;EACtB,SAAS,SAAS,CAAC,CAAC;CACtB,GACA,GACF;CAGA,MAAM,oBACJ,eAAe,SAAS,GAA8B,EAAE,WAAW,KAAK,CAAC;CAG3E,MAAM,sBAAkD;EACtD,MAAM,IAAI,SAAS;EACnB,MAAM,YAAY,EAAE,aAAa;EACjC,MAAM,aAAa,EAAE,qBAAqB;EAC1C,MAAM,qBAAqB,EAAE,sBAAsB;EAEnD,OAAO,WACL,YAAY,GACZ;GACE,UAAU,EAAE;GACZ,UAAU,EAAE;GACZ,UAAU,uBAAuB,YAAY,EAAE;GAC/C,iBAAkB,uBAAuB,UAAU,EAAE,cAAe,KAAA;GACpE,gBAAgB,aAAa,KAAA;GAC7B,OAAO,EAAE,SAAS,EAAE,gBAAgB;GACpC,WAAW,MAAa;IACtB,MAAM,SAAS,EAAE;IACjB,IAAI,OAAO,UAAU,gBACnB,EAAE,WAAW,OAAO,KAAK;GAE7B;GAEA,MAAM,aAAa,KAAA,IAAa,EAAE,QAAQ;GAC1C,WAAW,EAAE;GACb,cAAc,EAAE;GAChB,MAAM,EAAE;GACR,MAAM,EAAE;GACR,SAAS,aAAa,KAAA,IAAY,EAAE;GACpC,WAAW,EAAE;GACb,WAAW,EAAE;GACb,aAAa,EAAE;GACf,cAAc,EAAE;GAChB,aAAa,EAAE;GACf,gBAAgB,EAAE;GAClB,YAAY,EAAE;GAGd,QAAQ,EAAE;GACV,OAAO,EAAE;GACT,SAAS,EAAE;GAGX,oBAAoB,EAAE;GACtB,kBAAkB,EAAE;GACpB,qBAAqB,EAAE;GAGvB,UAAU,EAAE;GAGZ,eAAe,EAAE;GACjB,UAAU,MAAkB;IAC1B,MAAM,SAAS,EAAE;IACjB,MAAM,YAAY,OAAO;IACzB,EAAE,UACA,uBAAuB,GAAG,MAAM,CAClC;IACA,iBAAiB;IACjB,EAAE,WAAW,SAAS;GACxB;EACF,GACA,gBACA,UACF;CACF;CAEA,MAAM,qBAAqB;EACzB,OAAO,SAAS,CAAC,CAAC,aAAa;CACjC;CAEA,OAAO;EACL,IAAI,aAAa;GACf,OAAO;EACT;EACA,IAAI,aAAa;GACf,OAAO,cAAc;EACvB;EACA,IAAI,mBAAmB;GACrB,OAAO;EACT;EACA,IAAI,oBAAoB;GACtB,OAAO;EACT;EACA,IAAI,YAAY;GACd,OAAO,aAAa;EACtB;CACF;AACF"}
1
+ {"version":3,"file":"textfield.js","names":[],"sources":["../../src/textfield/createTextField.ts"],"sourcesContent":["/**\n * TextField hook for Solidaria\n *\n * Provides the behavior and accessibility implementation for a text field.\n *\n * This is a 1:1 port of @react-aria/textfield's useTextField hook.\n */\n\nimport { JSX } from \"solid-js\";\nimport { createField, type AriaFieldProps, type FieldAria } from \"../label\";\nimport { createFocusable, type FocusableDOMProps, type FocusableProps } from \"../interactions\";\nimport { mergeProps, filterDOMProps } from \"../utils\";\nimport { type MaybeAccessor, access } from \"../utils/reactivity\";\n\nexport interface AriaTextFieldProps extends AriaFieldProps, FocusableProps, FocusableDOMProps {\n /** The current value (controlled). */\n value?: string;\n /** The default value (uncontrolled). */\n defaultValue?: string;\n /** Handler that is called when the value changes. */\n onChange?: (value: string) => void;\n /** Whether the input is disabled. */\n isDisabled?: boolean;\n /** Whether the input is read only. */\n isReadOnly?: boolean;\n /** Whether the input is required. */\n isRequired?: boolean;\n /** Whether to use native HTML form validation or ARIA validation semantics. */\n validationBehavior?: \"aria\" | \"native\";\n /** The type of input to render. */\n type?: \"text\" | \"search\" | \"url\" | \"tel\" | \"email\" | \"password\" | string;\n /** The input mode hint for virtual keyboards. */\n inputMode?: \"none\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | \"search\";\n /** Hint for the enter key action on virtual keyboards. */\n enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\";\n /** The name of the input element, used when submitting an HTML form. */\n name?: string;\n /** Associates the input with a form element by id. */\n form?: string;\n /** Regex pattern to validate the input value. */\n pattern?: string;\n /** The maximum number of characters supported by the input. */\n maxLength?: number;\n /** The minimum number of characters required by the input. */\n minLength?: number;\n /** Placeholder text for the input. */\n placeholder?: string;\n /** Whether to enable auto complete. */\n autoComplete?: string;\n /** Whether to enable auto correct. */\n autoCorrect?: string;\n /** Whether to enable spell check. */\n spellCheck?: \"true\" | \"false\";\n /** Controls whether and how text input is automatically capitalized. */\n autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\";\n /** The element type to use for the input. Defaults to 'input'. */\n inputElementType?: \"input\" | \"textarea\";\n\n // Clipboard events\n onCopy?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n onCut?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n onPaste?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n\n // Composition events\n onCompositionStart?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n onCompositionEnd?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n onCompositionUpdate?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n\n // Selection events\n onSelect?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, Event>;\n\n // Input events\n onBeforeInput?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, InputEvent>;\n onInput?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, InputEvent>;\n}\n\nexport interface TextFieldAria<\n T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends Omit<FieldAria, \"fieldProps\"> {\n /** Props for the input element. */\n inputProps: JSX.InputHTMLAttributes<T>;\n /** Whether the text field is invalid. */\n isInvalid: boolean;\n}\n\n/**\n * Provides the behavior and accessibility implementation for a text field.\n * Text fields allow users to input text with a keyboard.\n *\n * @param props - Props for the text field.\n * @param ref - Optional ref callback for the input element.\n */\nexport function createTextField<\n T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n>(props: MaybeAccessor<AriaTextFieldProps>, ref?: (el: T) => void): TextFieldAria<T> {\n const getProps = () => access(props);\n let lastInputValue: string | undefined;\n\n const eventWithCurrentTarget = (\n event: InputEvent,\n element: HTMLInputElement | HTMLTextAreaElement,\n ) =>\n new Proxy(event, {\n get(target, property, receiver) {\n if (property === \"target\" || property === \"currentTarget\") {\n return element;\n }\n\n const value = Reflect.get(target, property, receiver);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n\n // Get field accessibility props (label, description, error message)\n const { labelProps, fieldProps, descriptionProps, errorMessageProps } = createField(props);\n\n // Get focusable props\n const { focusableProps } = createFocusable(\n {\n get isDisabled() {\n return getProps().isDisabled;\n },\n get autoFocus() {\n return getProps().autoFocus;\n },\n get excludeFromTabOrder() {\n return getProps().excludeFromTabOrder;\n },\n onFocus: getProps().onFocus,\n onBlur: getProps().onBlur,\n onFocusChange: getProps().onFocusChange,\n onKeyDown: getProps().onKeyDown,\n onKeyUp: getProps().onKeyUp,\n },\n ref as ((el: HTMLElement) => void) | undefined,\n );\n\n // Filter DOM props\n const getDomProps = () =>\n filterDOMProps(getProps() as Record<string, unknown>, { labelable: true });\n\n // Build input props\n const getInputProps = (): JSX.InputHTMLAttributes<T> => {\n const p = getProps();\n const isInvalid = p.isInvalid ?? false;\n const isTextarea = p.inputElementType === \"textarea\";\n const validationBehavior = p.validationBehavior ?? \"native\";\n\n return mergeProps(\n getDomProps(),\n {\n disabled: p.isDisabled,\n readOnly: p.isReadOnly,\n required: validationBehavior === \"native\" && p.isRequired,\n \"aria-required\": (validationBehavior === \"aria\" && p.isRequired) || undefined,\n \"aria-invalid\": isInvalid || undefined,\n value: p.value ?? p.defaultValue ?? \"\",\n onChange: (e: Event) => {\n const target = e.target as HTMLInputElement | HTMLTextAreaElement;\n if (target.value !== lastInputValue) {\n p.onChange?.(target.value);\n }\n },\n // Don't include type and pattern for textarea elements\n type: isTextarea ? undefined : (p.type ?? \"text\"),\n inputMode: p.inputMode,\n enterKeyHint: p.enterKeyHint,\n name: p.name,\n form: p.form,\n pattern: isTextarea ? undefined : p.pattern,\n maxLength: p.maxLength,\n minLength: p.minLength,\n placeholder: p.placeholder,\n autoComplete: p.autoComplete,\n autoCorrect: p.autoCorrect,\n autoCapitalize: p.autoCapitalize,\n spellCheck: p.spellCheck,\n\n // Clipboard events\n onCopy: p.onCopy,\n onCut: p.onCut,\n onPaste: p.onPaste,\n\n // Composition events\n onCompositionStart: p.onCompositionStart,\n onCompositionEnd: p.onCompositionEnd,\n onCompositionUpdate: p.onCompositionUpdate,\n\n // Selection events\n onSelect: p.onSelect,\n\n // Input events\n onBeforeInput: p.onBeforeInput,\n onInput: (e: InputEvent) => {\n const target = e.target as HTMLInputElement | HTMLTextAreaElement;\n const nextValue = target.value;\n p.onInput?.(\n eventWithCurrentTarget(e, target) as Parameters<NonNullable<typeof p.onInput>>[0],\n );\n lastInputValue = nextValue;\n p.onChange?.(nextValue);\n },\n },\n focusableProps as Record<string, unknown>,\n fieldProps as Record<string, unknown>,\n ) as JSX.InputHTMLAttributes<T>;\n };\n\n const getIsInvalid = () => {\n return getProps().isInvalid ?? false;\n };\n\n return {\n get labelProps() {\n return labelProps;\n },\n get inputProps() {\n return getInputProps();\n },\n get descriptionProps() {\n return descriptionProps;\n },\n get errorMessageProps() {\n return errorMessageProps;\n },\n get isInvalid() {\n return getIsInvalid();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;AA4FA,SAAgB,gBAEd,OAA0C,KAAyC;CACnF,MAAM,iBAAiB,OAAO,KAAK;CACnC,IAAI;CAEJ,MAAM,0BACJ,OACA,YAEA,IAAI,MAAM,OAAO,EACf,IAAI,QAAQ,UAAU,UAAU;EAC9B,IAAI,aAAa,YAAY,aAAa,iBACxC,OAAO;EAGT,MAAM,QAAQ,QAAQ,IAAI,QAAQ,UAAU,QAAQ;EACpD,OAAO,OAAO,UAAU,aAAa,MAAM,KAAK,MAAM,IAAI;CAC5D,EACF,CAAC;CAGH,MAAM,EAAE,YAAY,YAAY,kBAAkB,sBAAsB,YAAY,KAAK;CAGzF,MAAM,EAAE,mBAAmB,gBACzB;EACE,IAAI,aAAa;GACf,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,IAAI,YAAY;GACd,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,IAAI,sBAAsB;GACxB,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,SAAS,SAAS,CAAC,CAAC;EACpB,QAAQ,SAAS,CAAC,CAAC;EACnB,eAAe,SAAS,CAAC,CAAC;EAC1B,WAAW,SAAS,CAAC,CAAC;EACtB,SAAS,SAAS,CAAC,CAAC;CACtB,GACA,GACF;CAGA,MAAM,oBACJ,eAAe,SAAS,GAA8B,EAAE,WAAW,KAAK,CAAC;CAG3E,MAAM,sBAAkD;EACtD,MAAM,IAAI,SAAS;EACnB,MAAM,YAAY,EAAE,aAAa;EACjC,MAAM,aAAa,EAAE,qBAAqB;EAC1C,MAAM,qBAAqB,EAAE,sBAAsB;EAEnD,OAAO,WACL,YAAY,GACZ;GACE,UAAU,EAAE;GACZ,UAAU,EAAE;GACZ,UAAU,uBAAuB,YAAY,EAAE;GAC/C,iBAAkB,uBAAuB,UAAU,EAAE,cAAe,KAAA;GACpE,gBAAgB,aAAa,KAAA;GAC7B,OAAO,EAAE,SAAS,EAAE,gBAAgB;GACpC,WAAW,MAAa;IACtB,MAAM,SAAS,EAAE;IACjB,IAAI,OAAO,UAAU,gBACnB,EAAE,WAAW,OAAO,KAAK;GAE7B;GAEA,MAAM,aAAa,KAAA,IAAa,EAAE,QAAQ;GAC1C,WAAW,EAAE;GACb,cAAc,EAAE;GAChB,MAAM,EAAE;GACR,MAAM,EAAE;GACR,SAAS,aAAa,KAAA,IAAY,EAAE;GACpC,WAAW,EAAE;GACb,WAAW,EAAE;GACb,aAAa,EAAE;GACf,cAAc,EAAE;GAChB,aAAa,EAAE;GACf,gBAAgB,EAAE;GAClB,YAAY,EAAE;GAGd,QAAQ,EAAE;GACV,OAAO,EAAE;GACT,SAAS,EAAE;GAGX,oBAAoB,EAAE;GACtB,kBAAkB,EAAE;GACpB,qBAAqB,EAAE;GAGvB,UAAU,EAAE;GAGZ,eAAe,EAAE;GACjB,UAAU,MAAkB;IAC1B,MAAM,SAAS,EAAE;IACjB,MAAM,YAAY,OAAO;IACzB,EAAE,UACA,uBAAuB,GAAG,MAAM,CAClC;IACA,iBAAiB;IACjB,EAAE,WAAW,SAAS;GACxB;EACF,GACA,gBACA,UACF;CACF;CAEA,MAAM,qBAAqB;EACzB,OAAO,SAAS,CAAC,CAAC,aAAa;CACjC;CAEA,OAAO;EACL,IAAI,aAAa;GACf,OAAO;EACT;EACA,IAAI,aAAa;GACf,OAAO,cAAc;EACvB;EACA,IAAI,mBAAmB;GACrB,OAAO;EACT;EACA,IAAI,oBAAoB;GACtB,OAAO;EACT;EACA,IAAI,YAAY;GACd,OAAO,aAAa;EACtB;CACF;AACF"}
@@ -1,5 +1,6 @@
1
- import { i as access, o as mergeProps } from "./env.jsx";
1
+ import { i as mergeProps } from "./env.jsx";
2
2
  import { t as filterDOMProps } from "./filterDOMProps.jsx";
3
+ import { t as access } from "./reactivity.jsx";
3
4
  import { n as createFocusable } from "./createFocusable.jsx";
4
5
  import { t as createField } from "./createField.jsx";
5
6
  //#region src/textfield/createTextField.ts
@@ -1 +1 @@
1
- {"version":3,"file":"textfield.jsx","names":[],"sources":["../../src/textfield/createTextField.ts"],"sourcesContent":["/**\n * TextField hook for Solidaria\n *\n * Provides the behavior and accessibility implementation for a text field.\n *\n * This is a 1:1 port of @react-aria/textfield's useTextField hook.\n */\n\nimport { JSX } from \"solid-js\";\nimport { createField, type AriaFieldProps, type FieldAria } from \"../label\";\nimport { createFocusable, type FocusableDOMProps, type FocusableProps } from \"../interactions\";\nimport { mergeProps, filterDOMProps } from \"../utils\";\nimport { type MaybeAccessor, access } from \"../utils/reactivity\";\n\nexport interface AriaTextFieldProps extends AriaFieldProps, FocusableProps, FocusableDOMProps {\n /** The current value (controlled). */\n value?: string;\n /** The default value (uncontrolled). */\n defaultValue?: string;\n /** Handler that is called when the value changes. */\n onChange?: (value: string) => void;\n /** Whether the input is disabled. */\n isDisabled?: boolean;\n /** Whether the input is read only. */\n isReadOnly?: boolean;\n /** Whether the input is required. */\n isRequired?: boolean;\n /** Whether to use native HTML form validation or ARIA validation semantics. */\n validationBehavior?: \"aria\" | \"native\";\n /** The type of input to render. */\n type?: \"text\" | \"search\" | \"url\" | \"tel\" | \"email\" | \"password\" | string;\n /** The input mode hint for virtual keyboards. */\n inputMode?: \"none\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | \"search\";\n /** Hint for the enter key action on virtual keyboards. */\n enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\";\n /** The name of the input element, used when submitting an HTML form. */\n name?: string;\n /** Associates the input with a form element by id. */\n form?: string;\n /** Regex pattern to validate the input value. */\n pattern?: string;\n /** The maximum number of characters supported by the input. */\n maxLength?: number;\n /** The minimum number of characters required by the input. */\n minLength?: number;\n /** Placeholder text for the input. */\n placeholder?: string;\n /** Whether to enable auto complete. */\n autoComplete?: string;\n /** Whether to enable auto correct. */\n autoCorrect?: string;\n /** Whether to enable spell check. */\n spellCheck?: \"true\" | \"false\";\n /** Controls whether and how text input is automatically capitalized. */\n autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\";\n /** The element type to use for the input. Defaults to 'input'. */\n inputElementType?: \"input\" | \"textarea\";\n\n // Clipboard events\n onCopy?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n onCut?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n onPaste?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n\n // Composition events\n onCompositionStart?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n onCompositionEnd?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n onCompositionUpdate?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n\n // Selection events\n onSelect?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, Event>;\n\n // Input events\n onBeforeInput?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, InputEvent>;\n onInput?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, InputEvent>;\n}\n\nexport interface TextFieldAria<\n T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends Omit<FieldAria, \"fieldProps\"> {\n /** Props for the input element. */\n inputProps: JSX.InputHTMLAttributes<T>;\n /** Whether the text field is invalid. */\n isInvalid: boolean;\n}\n\n/**\n * Provides the behavior and accessibility implementation for a text field.\n * Text fields allow users to input text with a keyboard.\n *\n * @param props - Props for the text field.\n * @param ref - Optional ref callback for the input element.\n */\nexport function createTextField<\n T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n>(props: MaybeAccessor<AriaTextFieldProps>, ref?: (el: T) => void): TextFieldAria<T> {\n const getProps = () => access(props);\n let lastInputValue: string | undefined;\n\n const eventWithCurrentTarget = (\n event: InputEvent,\n element: HTMLInputElement | HTMLTextAreaElement,\n ) =>\n new Proxy(event, {\n get(target, property, receiver) {\n if (property === \"target\" || property === \"currentTarget\") {\n return element;\n }\n\n const value = Reflect.get(target, property, receiver);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n\n // Get field accessibility props (label, description, error message)\n const { labelProps, fieldProps, descriptionProps, errorMessageProps } = createField(props);\n\n // Get focusable props\n const { focusableProps } = createFocusable(\n {\n get isDisabled() {\n return getProps().isDisabled;\n },\n get autoFocus() {\n return getProps().autoFocus;\n },\n get excludeFromTabOrder() {\n return getProps().excludeFromTabOrder;\n },\n onFocus: getProps().onFocus,\n onBlur: getProps().onBlur,\n onFocusChange: getProps().onFocusChange,\n onKeyDown: getProps().onKeyDown,\n onKeyUp: getProps().onKeyUp,\n },\n ref as ((el: HTMLElement) => void) | undefined,\n );\n\n // Filter DOM props\n const getDomProps = () =>\n filterDOMProps(getProps() as Record<string, unknown>, { labelable: true });\n\n // Build input props\n const getInputProps = (): JSX.InputHTMLAttributes<T> => {\n const p = getProps();\n const isInvalid = p.isInvalid ?? false;\n const isTextarea = p.inputElementType === \"textarea\";\n const validationBehavior = p.validationBehavior ?? \"native\";\n\n return mergeProps(\n getDomProps(),\n {\n disabled: p.isDisabled,\n readOnly: p.isReadOnly,\n required: validationBehavior === \"native\" && p.isRequired,\n \"aria-required\": (validationBehavior === \"aria\" && p.isRequired) || undefined,\n \"aria-invalid\": isInvalid || undefined,\n value: p.value ?? p.defaultValue ?? \"\",\n onChange: (e: Event) => {\n const target = e.target as HTMLInputElement | HTMLTextAreaElement;\n if (target.value !== lastInputValue) {\n p.onChange?.(target.value);\n }\n },\n // Don't include type and pattern for textarea elements\n type: isTextarea ? undefined : (p.type ?? \"text\"),\n inputMode: p.inputMode,\n enterKeyHint: p.enterKeyHint,\n name: p.name,\n form: p.form,\n pattern: isTextarea ? undefined : p.pattern,\n maxLength: p.maxLength,\n minLength: p.minLength,\n placeholder: p.placeholder,\n autoComplete: p.autoComplete,\n autoCorrect: p.autoCorrect,\n autoCapitalize: p.autoCapitalize,\n spellCheck: p.spellCheck,\n\n // Clipboard events\n onCopy: p.onCopy,\n onCut: p.onCut,\n onPaste: p.onPaste,\n\n // Composition events\n onCompositionStart: p.onCompositionStart,\n onCompositionEnd: p.onCompositionEnd,\n onCompositionUpdate: p.onCompositionUpdate,\n\n // Selection events\n onSelect: p.onSelect,\n\n // Input events\n onBeforeInput: p.onBeforeInput,\n onInput: (e: InputEvent) => {\n const target = e.target as HTMLInputElement | HTMLTextAreaElement;\n const nextValue = target.value;\n p.onInput?.(\n eventWithCurrentTarget(e, target) as Parameters<NonNullable<typeof p.onInput>>[0],\n );\n lastInputValue = nextValue;\n p.onChange?.(nextValue);\n },\n },\n focusableProps as Record<string, unknown>,\n fieldProps as Record<string, unknown>,\n ) as JSX.InputHTMLAttributes<T>;\n };\n\n const getIsInvalid = () => {\n return getProps().isInvalid ?? false;\n };\n\n return {\n get labelProps() {\n return labelProps;\n },\n get inputProps() {\n return getInputProps();\n },\n get descriptionProps() {\n return descriptionProps;\n },\n get errorMessageProps() {\n return errorMessageProps;\n },\n get isInvalid() {\n return getIsInvalid();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;AA4FA,SAAgB,gBAEd,OAA0C,KAAyC;CACnF,MAAM,iBAAiB,OAAO,KAAK;CACnC,IAAI;CAEJ,MAAM,0BACJ,OACA,YAEA,IAAI,MAAM,OAAO,EACf,IAAI,QAAQ,UAAU,UAAU;EAC9B,IAAI,aAAa,YAAY,aAAa,iBACxC,OAAO;EAGT,MAAM,QAAQ,QAAQ,IAAI,QAAQ,UAAU,QAAQ;EACpD,OAAO,OAAO,UAAU,aAAa,MAAM,KAAK,MAAM,IAAI;CAC5D,EACF,CAAC;CAGH,MAAM,EAAE,YAAY,YAAY,kBAAkB,sBAAsB,YAAY,KAAK;CAGzF,MAAM,EAAE,mBAAmB,gBACzB;EACE,IAAI,aAAa;GACf,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,IAAI,YAAY;GACd,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,IAAI,sBAAsB;GACxB,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,SAAS,SAAS,CAAC,CAAC;EACpB,QAAQ,SAAS,CAAC,CAAC;EACnB,eAAe,SAAS,CAAC,CAAC;EAC1B,WAAW,SAAS,CAAC,CAAC;EACtB,SAAS,SAAS,CAAC,CAAC;CACtB,GACA,GACF;CAGA,MAAM,oBACJ,eAAe,SAAS,GAA8B,EAAE,WAAW,KAAK,CAAC;CAG3E,MAAM,sBAAkD;EACtD,MAAM,IAAI,SAAS;EACnB,MAAM,YAAY,EAAE,aAAa;EACjC,MAAM,aAAa,EAAE,qBAAqB;EAC1C,MAAM,qBAAqB,EAAE,sBAAsB;EAEnD,OAAO,WACL,YAAY,GACZ;GACE,UAAU,EAAE;GACZ,UAAU,EAAE;GACZ,UAAU,uBAAuB,YAAY,EAAE;GAC/C,iBAAkB,uBAAuB,UAAU,EAAE,cAAe,KAAA;GACpE,gBAAgB,aAAa,KAAA;GAC7B,OAAO,EAAE,SAAS,EAAE,gBAAgB;GACpC,WAAW,MAAa;IACtB,MAAM,SAAS,EAAE;IACjB,IAAI,OAAO,UAAU,gBACnB,EAAE,WAAW,OAAO,KAAK;GAE7B;GAEA,MAAM,aAAa,KAAA,IAAa,EAAE,QAAQ;GAC1C,WAAW,EAAE;GACb,cAAc,EAAE;GAChB,MAAM,EAAE;GACR,MAAM,EAAE;GACR,SAAS,aAAa,KAAA,IAAY,EAAE;GACpC,WAAW,EAAE;GACb,WAAW,EAAE;GACb,aAAa,EAAE;GACf,cAAc,EAAE;GAChB,aAAa,EAAE;GACf,gBAAgB,EAAE;GAClB,YAAY,EAAE;GAGd,QAAQ,EAAE;GACV,OAAO,EAAE;GACT,SAAS,EAAE;GAGX,oBAAoB,EAAE;GACtB,kBAAkB,EAAE;GACpB,qBAAqB,EAAE;GAGvB,UAAU,EAAE;GAGZ,eAAe,EAAE;GACjB,UAAU,MAAkB;IAC1B,MAAM,SAAS,EAAE;IACjB,MAAM,YAAY,OAAO;IACzB,EAAE,UACA,uBAAuB,GAAG,MAAM,CAClC;IACA,iBAAiB;IACjB,EAAE,WAAW,SAAS;GACxB;EACF,GACA,gBACA,UACF;CACF;CAEA,MAAM,qBAAqB;EACzB,OAAO,SAAS,CAAC,CAAC,aAAa;CACjC;CAEA,OAAO;EACL,IAAI,aAAa;GACf,OAAO;EACT;EACA,IAAI,aAAa;GACf,OAAO,cAAc;EACvB;EACA,IAAI,mBAAmB;GACrB,OAAO;EACT;EACA,IAAI,oBAAoB;GACtB,OAAO;EACT;EACA,IAAI,YAAY;GACd,OAAO,aAAa;EACtB;CACF;AACF"}
1
+ {"version":3,"file":"textfield.jsx","names":[],"sources":["../../src/textfield/createTextField.ts"],"sourcesContent":["/**\n * TextField hook for Solidaria\n *\n * Provides the behavior and accessibility implementation for a text field.\n *\n * This is a 1:1 port of @react-aria/textfield's useTextField hook.\n */\n\nimport { JSX } from \"solid-js\";\nimport { createField, type AriaFieldProps, type FieldAria } from \"../label\";\nimport { createFocusable, type FocusableDOMProps, type FocusableProps } from \"../interactions\";\nimport { mergeProps, filterDOMProps } from \"../utils\";\nimport { type MaybeAccessor, access } from \"../utils/reactivity\";\n\nexport interface AriaTextFieldProps extends AriaFieldProps, FocusableProps, FocusableDOMProps {\n /** The current value (controlled). */\n value?: string;\n /** The default value (uncontrolled). */\n defaultValue?: string;\n /** Handler that is called when the value changes. */\n onChange?: (value: string) => void;\n /** Whether the input is disabled. */\n isDisabled?: boolean;\n /** Whether the input is read only. */\n isReadOnly?: boolean;\n /** Whether the input is required. */\n isRequired?: boolean;\n /** Whether to use native HTML form validation or ARIA validation semantics. */\n validationBehavior?: \"aria\" | \"native\";\n /** The type of input to render. */\n type?: \"text\" | \"search\" | \"url\" | \"tel\" | \"email\" | \"password\" | string;\n /** The input mode hint for virtual keyboards. */\n inputMode?: \"none\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | \"search\";\n /** Hint for the enter key action on virtual keyboards. */\n enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\";\n /** The name of the input element, used when submitting an HTML form. */\n name?: string;\n /** Associates the input with a form element by id. */\n form?: string;\n /** Regex pattern to validate the input value. */\n pattern?: string;\n /** The maximum number of characters supported by the input. */\n maxLength?: number;\n /** The minimum number of characters required by the input. */\n minLength?: number;\n /** Placeholder text for the input. */\n placeholder?: string;\n /** Whether to enable auto complete. */\n autoComplete?: string;\n /** Whether to enable auto correct. */\n autoCorrect?: string;\n /** Whether to enable spell check. */\n spellCheck?: \"true\" | \"false\";\n /** Controls whether and how text input is automatically capitalized. */\n autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\";\n /** The element type to use for the input. Defaults to 'input'. */\n inputElementType?: \"input\" | \"textarea\";\n\n // Clipboard events\n onCopy?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n onCut?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n onPaste?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, ClipboardEvent>;\n\n // Composition events\n onCompositionStart?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n onCompositionEnd?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n onCompositionUpdate?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, CompositionEvent>;\n\n // Selection events\n onSelect?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, Event>;\n\n // Input events\n onBeforeInput?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, InputEvent>;\n onInput?: JSX.EventHandler<HTMLInputElement | HTMLTextAreaElement, InputEvent>;\n}\n\nexport interface TextFieldAria<\n T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends Omit<FieldAria, \"fieldProps\"> {\n /** Props for the input element. */\n inputProps: JSX.InputHTMLAttributes<T>;\n /** Whether the text field is invalid. */\n isInvalid: boolean;\n}\n\n/**\n * Provides the behavior and accessibility implementation for a text field.\n * Text fields allow users to input text with a keyboard.\n *\n * @param props - Props for the text field.\n * @param ref - Optional ref callback for the input element.\n */\nexport function createTextField<\n T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n>(props: MaybeAccessor<AriaTextFieldProps>, ref?: (el: T) => void): TextFieldAria<T> {\n const getProps = () => access(props);\n let lastInputValue: string | undefined;\n\n const eventWithCurrentTarget = (\n event: InputEvent,\n element: HTMLInputElement | HTMLTextAreaElement,\n ) =>\n new Proxy(event, {\n get(target, property, receiver) {\n if (property === \"target\" || property === \"currentTarget\") {\n return element;\n }\n\n const value = Reflect.get(target, property, receiver);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n\n // Get field accessibility props (label, description, error message)\n const { labelProps, fieldProps, descriptionProps, errorMessageProps } = createField(props);\n\n // Get focusable props\n const { focusableProps } = createFocusable(\n {\n get isDisabled() {\n return getProps().isDisabled;\n },\n get autoFocus() {\n return getProps().autoFocus;\n },\n get excludeFromTabOrder() {\n return getProps().excludeFromTabOrder;\n },\n onFocus: getProps().onFocus,\n onBlur: getProps().onBlur,\n onFocusChange: getProps().onFocusChange,\n onKeyDown: getProps().onKeyDown,\n onKeyUp: getProps().onKeyUp,\n },\n ref as ((el: HTMLElement) => void) | undefined,\n );\n\n // Filter DOM props\n const getDomProps = () =>\n filterDOMProps(getProps() as Record<string, unknown>, { labelable: true });\n\n // Build input props\n const getInputProps = (): JSX.InputHTMLAttributes<T> => {\n const p = getProps();\n const isInvalid = p.isInvalid ?? false;\n const isTextarea = p.inputElementType === \"textarea\";\n const validationBehavior = p.validationBehavior ?? \"native\";\n\n return mergeProps(\n getDomProps(),\n {\n disabled: p.isDisabled,\n readOnly: p.isReadOnly,\n required: validationBehavior === \"native\" && p.isRequired,\n \"aria-required\": (validationBehavior === \"aria\" && p.isRequired) || undefined,\n \"aria-invalid\": isInvalid || undefined,\n value: p.value ?? p.defaultValue ?? \"\",\n onChange: (e: Event) => {\n const target = e.target as HTMLInputElement | HTMLTextAreaElement;\n if (target.value !== lastInputValue) {\n p.onChange?.(target.value);\n }\n },\n // Don't include type and pattern for textarea elements\n type: isTextarea ? undefined : (p.type ?? \"text\"),\n inputMode: p.inputMode,\n enterKeyHint: p.enterKeyHint,\n name: p.name,\n form: p.form,\n pattern: isTextarea ? undefined : p.pattern,\n maxLength: p.maxLength,\n minLength: p.minLength,\n placeholder: p.placeholder,\n autoComplete: p.autoComplete,\n autoCorrect: p.autoCorrect,\n autoCapitalize: p.autoCapitalize,\n spellCheck: p.spellCheck,\n\n // Clipboard events\n onCopy: p.onCopy,\n onCut: p.onCut,\n onPaste: p.onPaste,\n\n // Composition events\n onCompositionStart: p.onCompositionStart,\n onCompositionEnd: p.onCompositionEnd,\n onCompositionUpdate: p.onCompositionUpdate,\n\n // Selection events\n onSelect: p.onSelect,\n\n // Input events\n onBeforeInput: p.onBeforeInput,\n onInput: (e: InputEvent) => {\n const target = e.target as HTMLInputElement | HTMLTextAreaElement;\n const nextValue = target.value;\n p.onInput?.(\n eventWithCurrentTarget(e, target) as Parameters<NonNullable<typeof p.onInput>>[0],\n );\n lastInputValue = nextValue;\n p.onChange?.(nextValue);\n },\n },\n focusableProps as Record<string, unknown>,\n fieldProps as Record<string, unknown>,\n ) as JSX.InputHTMLAttributes<T>;\n };\n\n const getIsInvalid = () => {\n return getProps().isInvalid ?? false;\n };\n\n return {\n get labelProps() {\n return labelProps;\n },\n get inputProps() {\n return getInputProps();\n },\n get descriptionProps() {\n return descriptionProps;\n },\n get errorMessageProps() {\n return errorMessageProps;\n },\n get isInvalid() {\n return getIsInvalid();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;AA4FA,SAAgB,gBAEd,OAA0C,KAAyC;CACnF,MAAM,iBAAiB,OAAO,KAAK;CACnC,IAAI;CAEJ,MAAM,0BACJ,OACA,YAEA,IAAI,MAAM,OAAO,EACf,IAAI,QAAQ,UAAU,UAAU;EAC9B,IAAI,aAAa,YAAY,aAAa,iBACxC,OAAO;EAGT,MAAM,QAAQ,QAAQ,IAAI,QAAQ,UAAU,QAAQ;EACpD,OAAO,OAAO,UAAU,aAAa,MAAM,KAAK,MAAM,IAAI;CAC5D,EACF,CAAC;CAGH,MAAM,EAAE,YAAY,YAAY,kBAAkB,sBAAsB,YAAY,KAAK;CAGzF,MAAM,EAAE,mBAAmB,gBACzB;EACE,IAAI,aAAa;GACf,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,IAAI,YAAY;GACd,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,IAAI,sBAAsB;GACxB,OAAO,SAAS,CAAC,CAAC;EACpB;EACA,SAAS,SAAS,CAAC,CAAC;EACpB,QAAQ,SAAS,CAAC,CAAC;EACnB,eAAe,SAAS,CAAC,CAAC;EAC1B,WAAW,SAAS,CAAC,CAAC;EACtB,SAAS,SAAS,CAAC,CAAC;CACtB,GACA,GACF;CAGA,MAAM,oBACJ,eAAe,SAAS,GAA8B,EAAE,WAAW,KAAK,CAAC;CAG3E,MAAM,sBAAkD;EACtD,MAAM,IAAI,SAAS;EACnB,MAAM,YAAY,EAAE,aAAa;EACjC,MAAM,aAAa,EAAE,qBAAqB;EAC1C,MAAM,qBAAqB,EAAE,sBAAsB;EAEnD,OAAO,WACL,YAAY,GACZ;GACE,UAAU,EAAE;GACZ,UAAU,EAAE;GACZ,UAAU,uBAAuB,YAAY,EAAE;GAC/C,iBAAkB,uBAAuB,UAAU,EAAE,cAAe,KAAA;GACpE,gBAAgB,aAAa,KAAA;GAC7B,OAAO,EAAE,SAAS,EAAE,gBAAgB;GACpC,WAAW,MAAa;IACtB,MAAM,SAAS,EAAE;IACjB,IAAI,OAAO,UAAU,gBACnB,EAAE,WAAW,OAAO,KAAK;GAE7B;GAEA,MAAM,aAAa,KAAA,IAAa,EAAE,QAAQ;GAC1C,WAAW,EAAE;GACb,cAAc,EAAE;GAChB,MAAM,EAAE;GACR,MAAM,EAAE;GACR,SAAS,aAAa,KAAA,IAAY,EAAE;GACpC,WAAW,EAAE;GACb,WAAW,EAAE;GACb,aAAa,EAAE;GACf,cAAc,EAAE;GAChB,aAAa,EAAE;GACf,gBAAgB,EAAE;GAClB,YAAY,EAAE;GAGd,QAAQ,EAAE;GACV,OAAO,EAAE;GACT,SAAS,EAAE;GAGX,oBAAoB,EAAE;GACtB,kBAAkB,EAAE;GACpB,qBAAqB,EAAE;GAGvB,UAAU,EAAE;GAGZ,eAAe,EAAE;GACjB,UAAU,MAAkB;IAC1B,MAAM,SAAS,EAAE;IACjB,MAAM,YAAY,OAAO;IACzB,EAAE,UACA,uBAAuB,GAAG,MAAM,CAClC;IACA,iBAAiB;IACjB,EAAE,WAAW,SAAS;GACxB;EACF,GACA,gBACA,UACF;CACF;CAEA,MAAM,qBAAqB;EACzB,OAAO,SAAS,CAAC,CAAC,aAAa;CACjC;CAEA,OAAO;EACL,IAAI,aAAa;GACf,OAAO;EACT;EACA,IAAI,aAAa;GACf,OAAO,cAAc;EACvB;EACA,IAAI,mBAAmB;GACrB,OAAO;EACT;EACA,IAAI,oBAAoB;GACtB,OAAO;EACT;EACA,IAAI,YAAY;GACd,OAAO,aAAa;EACtB;CACF;AACF"}
@@ -1,6 +1,6 @@
1
- import { i as access } from "./env.js";
1
+ import { t as access } from "./reactivity.js";
2
2
  import { g as isTabbable, p as isFocusable, s as getOwnerDocument, t as focusSafely } from "./focus.js";
3
- import { a as useLocale } from "./createCollator.js";
3
+ import { i as useLocale } from "./locale.js";
4
4
  import { createSignal, onCleanup, onMount } from "solid-js";
5
5
  //#region src/toolbar/createToolbar.ts
6
6
  /**
@@ -25,32 +25,6 @@ function getActiveElement(doc) {
25
25
  while (activeElement?.shadowRoot?.activeElement) activeElement = activeElement.shadowRoot.activeElement;
26
26
  return activeElement;
27
27
  }
28
- const TEXT_INPUT_TYPES = new Set([
29
- "",
30
- "text",
31
- "search",
32
- "url",
33
- "tel",
34
- "password",
35
- "email",
36
- "number",
37
- "date",
38
- "datetime-local",
39
- "month",
40
- "time",
41
- "week"
42
- ]);
43
- function isTextInputLikeElement(target) {
44
- if (!(target instanceof HTMLElement)) return false;
45
- if (target.isContentEditable || !!target.closest("[contenteditable=\"true\"]")) return true;
46
- if (target.getAttribute("role") === "textbox") return true;
47
- if (target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) return true;
48
- if (target instanceof HTMLInputElement) {
49
- const type = target.type.toLowerCase();
50
- return TEXT_INPUT_TYPES.has(type);
51
- }
52
- return false;
53
- }
54
28
  function createFocusManager(ref) {
55
29
  return {
56
30
  focusNext(opts = {}) {
@@ -157,12 +131,6 @@ function createToolbar(props = {}) {
157
131
  const target = e.target;
158
132
  if (!(target instanceof Element) || !root.contains(target)) return;
159
133
  const doc = getOwnerDocument(root);
160
- if (isTextInputLikeElement(target)) switch (e.key) {
161
- case "ArrowRight":
162
- case "ArrowLeft":
163
- case "ArrowDown":
164
- case "ArrowUp": return;
165
- }
166
134
  const isRTL = locale().direction === "rtl";
167
135
  const isHorizontal = orientation() === "horizontal";
168
136
  let handled = false;
@@ -239,13 +207,13 @@ function createToolbar(props = {}) {
239
207
  return isInToolbar() ? "group" : "toolbar";
240
208
  },
241
209
  get "aria-orientation"() {
242
- return isInToolbar() ? void 0 : orientation();
210
+ return orientation();
243
211
  },
244
212
  get "aria-label"() {
245
213
  return ariaLabel();
246
214
  },
247
215
  get "aria-labelledby"() {
248
- return ariaLabel() ? void 0 : ariaLabelledby();
216
+ return ariaLabel() == null ? ariaLabelledby() : void 0;
249
217
  },
250
218
  ref: setRef
251
219
  },
@@ -1 +1 @@
1
- {"version":3,"file":"toolbar.js","names":[],"sources":["../../src/toolbar/createToolbar.ts"],"sourcesContent":["/**\n * createToolbar - Accessibility hook for toolbar elements\n *\n * Provides keyboard navigation between toolbar items using arrow keys.\n * Based on @react-aria/toolbar useToolbar.\n */\n\nimport { createSignal, onMount, onCleanup, type Accessor } from \"solid-js\";\nimport { type MaybeAccessor, access } from \"../utils\";\nimport { useLocale } from \"../i18n\";\nimport { getOwnerDocument, isFocusable, isTabbable } from \"../utils\";\nimport { focusSafely } from \"../utils/focus\";\n\nexport type Orientation = \"horizontal\" | \"vertical\";\n\nexport interface AriaToolbarProps {\n /** The orientation of the toolbar. @default 'horizontal' */\n orientation?: MaybeAccessor<Orientation>;\n /** An accessibility label for the toolbar. */\n \"aria-label\"?: MaybeAccessor<string>;\n /** Identifies the element (or elements) that labels the toolbar. */\n \"aria-labelledby\"?: MaybeAccessor<string>;\n}\n\nexport interface ToolbarAria {\n /** Props for the toolbar container element. */\n toolbarProps: {\n role: \"toolbar\" | \"group\";\n \"aria-orientation\"?: Orientation;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n tabIndex?: number;\n ref: (el: HTMLElement) => void;\n };\n /** The orientation of the toolbar. */\n orientation: Accessor<Orientation>;\n}\n\ninterface FocusManagerOptions {\n from?: Element;\n tabbable?: boolean;\n wrap?: boolean;\n accept?: (node: Element) => boolean;\n}\n\ninterface FocusManager {\n focusNext(opts?: FocusManagerOptions): HTMLElement | null;\n focusPrevious(opts?: FocusManagerOptions): HTMLElement | null;\n focusFirst(opts?: FocusManagerOptions): HTMLElement | null;\n focusLast(opts?: FocusManagerOptions): HTMLElement | null;\n}\n\nfunction getFocusableElements(root: Element, tabbable = false): HTMLElement[] {\n const elements: HTMLElement[] = [];\n const filter = tabbable ? isTabbable : isFocusable;\n\n // Check the root element itself\n if (filter(root)) {\n elements.push(root as HTMLElement);\n }\n\n // Check all descendants\n const descendants = root.querySelectorAll(\"*\");\n for (let i = 0; i < descendants.length; i++) {\n const el = descendants[i];\n if (filter(el)) {\n elements.push(el as HTMLElement);\n }\n }\n\n return elements;\n}\n\nfunction getActiveElement(doc: Document): Element | null {\n let activeElement = doc.activeElement;\n while (activeElement?.shadowRoot?.activeElement) {\n activeElement = activeElement.shadowRoot.activeElement;\n }\n return activeElement;\n}\n\nconst TEXT_INPUT_TYPES = new Set([\n \"\",\n \"text\",\n \"search\",\n \"url\",\n \"tel\",\n \"password\",\n \"email\",\n \"number\",\n \"date\",\n \"datetime-local\",\n \"month\",\n \"time\",\n \"week\",\n]);\n\nfunction isTextInputLikeElement(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) {\n return false;\n }\n\n if (target.isContentEditable || !!target.closest('[contenteditable=\"true\"]')) {\n return true;\n }\n\n if (target.getAttribute(\"role\") === \"textbox\") {\n return true;\n }\n\n if (target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) {\n return true;\n }\n\n if (target instanceof HTMLInputElement) {\n const type = target.type.toLowerCase();\n return TEXT_INPUT_TYPES.has(type);\n }\n\n return false;\n}\n\nfunction createFocusManager(ref: Accessor<HTMLElement | undefined>): FocusManager {\n return {\n focusNext(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { from, tabbable = true, wrap = false, accept } = opts;\n const doc = getOwnerDocument(root);\n const current = from || getActiveElement(doc);\n\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (!current || elements.length === 0) return null;\n\n const currentIndex = elements.indexOf(current as HTMLElement);\n let nextIndex = currentIndex + 1;\n\n if (nextIndex >= elements.length) {\n if (wrap) {\n nextIndex = 0;\n } else {\n return null;\n }\n }\n\n const nextElement = elements[nextIndex];\n if (nextElement) {\n focusSafely(nextElement);\n return nextElement;\n }\n\n return null;\n },\n\n focusPrevious(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { from, tabbable = true, wrap = false, accept } = opts;\n const doc = getOwnerDocument(root);\n const current = from || getActiveElement(doc);\n\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (!current || elements.length === 0) return null;\n\n const currentIndex = elements.indexOf(current as HTMLElement);\n let prevIndex = currentIndex - 1;\n\n if (prevIndex < 0) {\n if (wrap) {\n prevIndex = elements.length - 1;\n } else {\n return null;\n }\n }\n\n const prevElement = elements[prevIndex];\n if (prevElement) {\n focusSafely(prevElement);\n return prevElement;\n }\n\n return null;\n },\n\n focusFirst(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { tabbable = true, accept } = opts;\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (elements.length > 0) {\n focusSafely(elements[0]);\n return elements[0];\n }\n\n return null;\n },\n\n focusLast(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { tabbable = true, accept } = opts;\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (elements.length > 0) {\n const lastElement = elements[elements.length - 1];\n focusSafely(lastElement);\n return lastElement;\n }\n\n return null;\n },\n };\n}\n\n/**\n * Provides the behavior and accessibility implementation for a toolbar.\n * A toolbar is a container for a set of interactive controls with arrow key navigation.\n *\n * @example\n * ```tsx\n * let ref;\n * const { toolbarProps } = createToolbar({ orientation: 'horizontal' });\n * return (\n * <div {...toolbarProps}>\n * <Button>Cut</Button>\n * <Button>Copy</Button>\n * <Button>Paste</Button>\n * </div>\n * );\n * ```\n */\nexport function createToolbar(props: AriaToolbarProps = {}): ToolbarAria {\n let toolbarRef: HTMLElement | undefined;\n const [isInToolbar, setIsInToolbar] = createSignal(false);\n let lastFocusedElement: Element | null = null;\n\n const locale = useLocale();\n const orientation = () => access(props.orientation) ?? \"horizontal\";\n const ariaLabel = () => access(props[\"aria-label\"]);\n const ariaLabelledby = () => access(props[\"aria-labelledby\"]);\n\n const focusManager = createFocusManager(() => toolbarRef);\n\n // Check if this toolbar is nested inside another toolbar\n onMount(() => {\n if (toolbarRef) {\n const parentToolbar = toolbarRef.parentElement?.closest('[role=\"toolbar\"]');\n setIsInToolbar(!!parentToolbar);\n }\n });\n\n // Keyboard event handler\n const onKeyDown = (e: KeyboardEvent) => {\n const root = toolbarRef;\n if (!root) return;\n\n // Don't handle if nested toolbar (parent handles navigation)\n if (isInToolbar()) return;\n\n const target = e.target;\n if (!(target instanceof Element) || !root.contains(target)) {\n return;\n }\n\n const doc = getOwnerDocument(root);\n\n // Text entry controls keep the arrow keys for caret/value navigation.\n // Upstream useToolbar has no such guard; a text input placed inside a\n // toolbar would otherwise lose caret movement. Kept (narrowed to arrows)\n // as a suspected divergence pending a dedicated ActionBar/Toolbar cert.\n if (isTextInputLikeElement(target)) {\n switch (e.key) {\n case \"ArrowRight\":\n case \"ArrowLeft\":\n case \"ArrowDown\":\n case \"ArrowUp\":\n return;\n }\n }\n\n const dir = locale().direction;\n const isRTL = dir === \"rtl\";\n const isHorizontal = orientation() === \"horizontal\";\n\n let handled = false;\n\n switch (e.key) {\n case \"ArrowRight\":\n if (isHorizontal) {\n if (isRTL) {\n focusManager.focusPrevious({ tabbable: true });\n } else {\n focusManager.focusNext({ tabbable: true });\n }\n handled = true;\n }\n break;\n case \"ArrowLeft\":\n if (isHorizontal) {\n if (isRTL) {\n focusManager.focusNext({ tabbable: true });\n } else {\n focusManager.focusPrevious({ tabbable: true });\n }\n handled = true;\n }\n break;\n case \"ArrowDown\":\n if (!isHorizontal) {\n focusManager.focusNext({ tabbable: true });\n handled = true;\n }\n break;\n case \"ArrowUp\":\n if (!isHorizontal) {\n focusManager.focusPrevious({ tabbable: true });\n handled = true;\n }\n break;\n case \"Tab\":\n // Move focus to the first/last child so the browser's own Tab handling\n // then carries focus OUT of the whole toolbar. Faithful to upstream\n // useToolbar: there is no roving tabindex — every item stays tabbable,\n // so a bare Tab would otherwise step to the next item instead of leaving.\n lastFocusedElement = getActiveElement(doc);\n if (e.shiftKey) {\n focusManager.focusFirst({ tabbable: true });\n } else {\n focusManager.focusLast({ tabbable: true });\n }\n return;\n }\n\n if (handled) {\n e.preventDefault();\n e.stopPropagation();\n }\n };\n\n // Record the last focused child when focus moves out of the toolbar.\n const onBlur = (e: FocusEvent) => {\n const root = toolbarRef;\n if (!root) return;\n if (isInToolbar()) return;\n\n const relatedTarget = e.relatedTarget as Node | null;\n if (!(relatedTarget && root.contains(relatedTarget)) && !lastFocusedElement) {\n lastFocusedElement = e.target as Element;\n }\n };\n\n // Restore focus to the last focused child when focus returns into the toolbar.\n // If that element was removed, focus() is a no-op and the browser lands on the\n // first/last child per direction — matching upstream useToolbar.\n const onFocus = (e: FocusEvent) => {\n const root = toolbarRef;\n if (!root) return;\n if (isInToolbar()) return;\n\n const relatedTarget = e.relatedTarget as Node | null;\n const target = e.target;\n if (\n lastFocusedElement &&\n !(relatedTarget && root.contains(relatedTarget)) &&\n target instanceof Node &&\n root.contains(target)\n ) {\n focusSafely(lastFocusedElement as HTMLElement);\n lastFocusedElement = null;\n }\n };\n\n // Set up capture event listeners\n const setRef = (el: HTMLElement) => {\n toolbarRef = el;\n\n // Use capture phase so container handlers run before the focused child's.\n el.addEventListener(\"keydown\", onKeyDown, true);\n el.addEventListener(\"focus\", onFocus, true);\n el.addEventListener(\"blur\", onBlur, true);\n\n onCleanup(() => {\n el.removeEventListener(\"keydown\", onKeyDown, true);\n el.removeEventListener(\"focus\", onFocus, true);\n el.removeEventListener(\"blur\", onBlur, true);\n });\n };\n\n return {\n toolbarProps: {\n get role() {\n return isInToolbar() ? \"group\" : \"toolbar\";\n },\n get \"aria-orientation\"() {\n return isInToolbar() ? undefined : orientation();\n },\n get \"aria-label\"() {\n return ariaLabel();\n },\n get \"aria-labelledby\"() {\n // Only use aria-labelledby if no aria-label is provided\n return ariaLabel() ? undefined : ariaLabelledby();\n },\n ref: setRef,\n },\n orientation,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"toolbar.js","names":[],"sources":["../../src/toolbar/createToolbar.ts"],"sourcesContent":["/**\n * createToolbar - Accessibility hook for toolbar elements\n *\n * Provides keyboard navigation between toolbar items using arrow keys.\n * Based on @react-aria/toolbar useToolbar.\n */\n\nimport { createSignal, onMount, onCleanup, type Accessor } from \"solid-js\";\nimport { type MaybeAccessor, access } from \"../utils\";\nimport { useLocale } from \"../i18n\";\nimport { getOwnerDocument, isFocusable, isTabbable } from \"../utils\";\nimport { focusSafely } from \"../utils/focus\";\n\nexport type Orientation = \"horizontal\" | \"vertical\";\n\nexport interface AriaToolbarProps {\n /** The orientation of the toolbar. @default 'horizontal' */\n orientation?: MaybeAccessor<Orientation>;\n /** An accessibility label for the toolbar. */\n \"aria-label\"?: MaybeAccessor<string>;\n /** Identifies the element (or elements) that labels the toolbar. */\n \"aria-labelledby\"?: MaybeAccessor<string>;\n}\n\nexport interface ToolbarAria {\n /** Props for the toolbar container element. */\n toolbarProps: {\n role: \"toolbar\" | \"group\";\n \"aria-orientation\"?: Orientation;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n tabIndex?: number;\n ref: (el: HTMLElement) => void;\n };\n /** The orientation of the toolbar. */\n orientation: Accessor<Orientation>;\n}\n\ninterface FocusManagerOptions {\n from?: Element;\n tabbable?: boolean;\n wrap?: boolean;\n accept?: (node: Element) => boolean;\n}\n\ninterface FocusManager {\n focusNext(opts?: FocusManagerOptions): HTMLElement | null;\n focusPrevious(opts?: FocusManagerOptions): HTMLElement | null;\n focusFirst(opts?: FocusManagerOptions): HTMLElement | null;\n focusLast(opts?: FocusManagerOptions): HTMLElement | null;\n}\n\nfunction getFocusableElements(root: Element, tabbable = false): HTMLElement[] {\n const elements: HTMLElement[] = [];\n const filter = tabbable ? isTabbable : isFocusable;\n\n // Check the root element itself\n if (filter(root)) {\n elements.push(root as HTMLElement);\n }\n\n // Check all descendants\n const descendants = root.querySelectorAll(\"*\");\n for (let i = 0; i < descendants.length; i++) {\n const el = descendants[i];\n if (filter(el)) {\n elements.push(el as HTMLElement);\n }\n }\n\n return elements;\n}\n\nfunction getActiveElement(doc: Document): Element | null {\n let activeElement = doc.activeElement;\n while (activeElement?.shadowRoot?.activeElement) {\n activeElement = activeElement.shadowRoot.activeElement;\n }\n return activeElement;\n}\n\nfunction createFocusManager(ref: Accessor<HTMLElement | undefined>): FocusManager {\n return {\n focusNext(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { from, tabbable = true, wrap = false, accept } = opts;\n const doc = getOwnerDocument(root);\n const current = from || getActiveElement(doc);\n\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (!current || elements.length === 0) return null;\n\n const currentIndex = elements.indexOf(current as HTMLElement);\n let nextIndex = currentIndex + 1;\n\n if (nextIndex >= elements.length) {\n if (wrap) {\n nextIndex = 0;\n } else {\n return null;\n }\n }\n\n const nextElement = elements[nextIndex];\n if (nextElement) {\n focusSafely(nextElement);\n return nextElement;\n }\n\n return null;\n },\n\n focusPrevious(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { from, tabbable = true, wrap = false, accept } = opts;\n const doc = getOwnerDocument(root);\n const current = from || getActiveElement(doc);\n\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (!current || elements.length === 0) return null;\n\n const currentIndex = elements.indexOf(current as HTMLElement);\n let prevIndex = currentIndex - 1;\n\n if (prevIndex < 0) {\n if (wrap) {\n prevIndex = elements.length - 1;\n } else {\n return null;\n }\n }\n\n const prevElement = elements[prevIndex];\n if (prevElement) {\n focusSafely(prevElement);\n return prevElement;\n }\n\n return null;\n },\n\n focusFirst(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { tabbable = true, accept } = opts;\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (elements.length > 0) {\n focusSafely(elements[0]);\n return elements[0];\n }\n\n return null;\n },\n\n focusLast(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { tabbable = true, accept } = opts;\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (elements.length > 0) {\n const lastElement = elements[elements.length - 1];\n focusSafely(lastElement);\n return lastElement;\n }\n\n return null;\n },\n };\n}\n\n/**\n * Provides the behavior and accessibility implementation for a toolbar.\n * A toolbar is a container for a set of interactive controls with arrow key navigation.\n *\n * @example\n * ```tsx\n * let ref;\n * const { toolbarProps } = createToolbar({ orientation: 'horizontal' });\n * return (\n * <div {...toolbarProps}>\n * <Button>Cut</Button>\n * <Button>Copy</Button>\n * <Button>Paste</Button>\n * </div>\n * );\n * ```\n */\nexport function createToolbar(props: AriaToolbarProps = {}): ToolbarAria {\n let toolbarRef: HTMLElement | undefined;\n const [isInToolbar, setIsInToolbar] = createSignal(false);\n let lastFocusedElement: Element | null = null;\n\n const locale = useLocale();\n const orientation = () => access(props.orientation) ?? \"horizontal\";\n const ariaLabel = () => access(props[\"aria-label\"]);\n const ariaLabelledby = () => access(props[\"aria-labelledby\"]);\n\n const focusManager = createFocusManager(() => toolbarRef);\n\n // Check if this toolbar is nested inside another toolbar\n onMount(() => {\n if (toolbarRef) {\n const parentToolbar = toolbarRef.parentElement?.closest('[role=\"toolbar\"]');\n setIsInToolbar(!!parentToolbar);\n }\n });\n\n // Keyboard event handler\n const onKeyDown = (e: KeyboardEvent) => {\n const root = toolbarRef;\n if (!root) return;\n\n // Don't handle if nested toolbar (parent handles navigation)\n if (isInToolbar()) return;\n\n const target = e.target;\n if (!(target instanceof Element) || !root.contains(target)) {\n return;\n }\n\n const doc = getOwnerDocument(root);\n\n const dir = locale().direction;\n const isRTL = dir === \"rtl\";\n const isHorizontal = orientation() === \"horizontal\";\n\n let handled = false;\n\n switch (e.key) {\n case \"ArrowRight\":\n if (isHorizontal) {\n if (isRTL) {\n focusManager.focusPrevious({ tabbable: true });\n } else {\n focusManager.focusNext({ tabbable: true });\n }\n handled = true;\n }\n break;\n case \"ArrowLeft\":\n if (isHorizontal) {\n if (isRTL) {\n focusManager.focusNext({ tabbable: true });\n } else {\n focusManager.focusPrevious({ tabbable: true });\n }\n handled = true;\n }\n break;\n case \"ArrowDown\":\n if (!isHorizontal) {\n focusManager.focusNext({ tabbable: true });\n handled = true;\n }\n break;\n case \"ArrowUp\":\n if (!isHorizontal) {\n focusManager.focusPrevious({ tabbable: true });\n handled = true;\n }\n break;\n case \"Tab\":\n // Move focus to the first/last child so the browser's own Tab handling\n // then carries focus OUT of the whole toolbar. Faithful to upstream\n // useToolbar: there is no roving tabindex — every item stays tabbable,\n // so a bare Tab would otherwise step to the next item instead of leaving.\n lastFocusedElement = getActiveElement(doc);\n if (e.shiftKey) {\n focusManager.focusFirst({ tabbable: true });\n } else {\n focusManager.focusLast({ tabbable: true });\n }\n return;\n }\n\n if (handled) {\n e.preventDefault();\n e.stopPropagation();\n }\n };\n\n // Record the last focused child when focus moves out of the toolbar.\n const onBlur = (e: FocusEvent) => {\n const root = toolbarRef;\n if (!root) return;\n if (isInToolbar()) return;\n\n const relatedTarget = e.relatedTarget as Node | null;\n if (!(relatedTarget && root.contains(relatedTarget)) && !lastFocusedElement) {\n lastFocusedElement = e.target as Element;\n }\n };\n\n // Restore focus to the last focused child when focus returns into the toolbar.\n // If that element was removed, focus() is a no-op and the browser lands on the\n // first/last child per direction — matching upstream useToolbar.\n const onFocus = (e: FocusEvent) => {\n const root = toolbarRef;\n if (!root) return;\n if (isInToolbar()) return;\n\n const relatedTarget = e.relatedTarget as Node | null;\n const target = e.target;\n if (\n lastFocusedElement &&\n !(relatedTarget && root.contains(relatedTarget)) &&\n target instanceof Node &&\n root.contains(target)\n ) {\n focusSafely(lastFocusedElement as HTMLElement);\n lastFocusedElement = null;\n }\n };\n\n // Set up capture event listeners\n const setRef = (el: HTMLElement) => {\n toolbarRef = el;\n\n // Use capture phase so container handlers run before the focused child's.\n el.addEventListener(\"keydown\", onKeyDown, true);\n el.addEventListener(\"focus\", onFocus, true);\n el.addEventListener(\"blur\", onBlur, true);\n\n onCleanup(() => {\n el.removeEventListener(\"keydown\", onKeyDown, true);\n el.removeEventListener(\"focus\", onFocus, true);\n el.removeEventListener(\"blur\", onBlur, true);\n });\n };\n\n return {\n toolbarProps: {\n get role() {\n return isInToolbar() ? \"group\" : \"toolbar\";\n },\n get \"aria-orientation\"() {\n // Upstream useToolbar emits aria-orientation unconditionally, on the\n // nested role=\"group\" too — not only on the root role=\"toolbar\".\n return orientation();\n },\n get \"aria-label\"() {\n return ariaLabel();\n },\n get \"aria-labelledby\"() {\n // Mirror useToolbar: `aria-label == null ? aria-labelledby : undefined`.\n // An explicit empty-string aria-label still suppresses labelledby.\n return ariaLabel() == null ? ariaLabelledby() : undefined;\n },\n ref: setRef,\n },\n orientation,\n };\n}\n"],"mappings":";;;;;;;;;;;AAoDA,SAAS,qBAAqB,MAAe,WAAW,OAAsB;CAC5E,MAAM,WAA0B,CAAC;CACjC,MAAM,SAAS,WAAW,aAAa;CAGvC,IAAI,OAAO,IAAI,GACb,SAAS,KAAK,IAAmB;CAInC,MAAM,cAAc,KAAK,iBAAiB,GAAG;CAC7C,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;EAC3C,MAAM,KAAK,YAAY;EACvB,IAAI,OAAO,EAAE,GACX,SAAS,KAAK,EAAiB;CAEnC;CAEA,OAAO;AACT;AAEA,SAAS,iBAAiB,KAA+B;CACvD,IAAI,gBAAgB,IAAI;CACxB,OAAO,eAAe,YAAY,eAChC,gBAAgB,cAAc,WAAW;CAE3C,OAAO;AACT;AAEA,SAAS,mBAAmB,KAAsD;CAChF,OAAO;EACL,UAAU,OAA4B,CAAC,GAAG;GACxC,MAAM,OAAO,IAAI;GACjB,IAAI,CAAC,MAAM,OAAO;GAElB,MAAM,EAAE,MAAM,WAAW,MAAM,OAAO,OAAO,WAAW;GACxD,MAAM,MAAM,iBAAiB,IAAI;GACjC,MAAM,UAAU,QAAQ,iBAAiB,GAAG;GAE5C,IAAI,WAAW,qBAAqB,MAAM,QAAQ;GAClD,IAAI,QACF,WAAW,SAAS,OAAO,MAAM;GAGnC,IAAI,CAAC,WAAW,SAAS,WAAW,GAAG,OAAO;GAG9C,IAAI,YADiB,SAAS,QAAQ,OACX,IAAI;GAE/B,IAAI,aAAa,SAAS,QACxB,IAAI,MACF,YAAY;QAEZ,OAAO;GAIX,MAAM,cAAc,SAAS;GAC7B,IAAI,aAAa;IACf,YAAY,WAAW;IACvB,OAAO;GACT;GAEA,OAAO;EACT;EAEA,cAAc,OAA4B,CAAC,GAAG;GAC5C,MAAM,OAAO,IAAI;GACjB,IAAI,CAAC,MAAM,OAAO;GAElB,MAAM,EAAE,MAAM,WAAW,MAAM,OAAO,OAAO,WAAW;GACxD,MAAM,MAAM,iBAAiB,IAAI;GACjC,MAAM,UAAU,QAAQ,iBAAiB,GAAG;GAE5C,IAAI,WAAW,qBAAqB,MAAM,QAAQ;GAClD,IAAI,QACF,WAAW,SAAS,OAAO,MAAM;GAGnC,IAAI,CAAC,WAAW,SAAS,WAAW,GAAG,OAAO;GAG9C,IAAI,YADiB,SAAS,QAAQ,OACX,IAAI;GAE/B,IAAI,YAAY,GACd,IAAI,MACF,YAAY,SAAS,SAAS;QAE9B,OAAO;GAIX,MAAM,cAAc,SAAS;GAC7B,IAAI,aAAa;IACf,YAAY,WAAW;IACvB,OAAO;GACT;GAEA,OAAO;EACT;EAEA,WAAW,OAA4B,CAAC,GAAG;GACzC,MAAM,OAAO,IAAI;GACjB,IAAI,CAAC,MAAM,OAAO;GAElB,MAAM,EAAE,WAAW,MAAM,WAAW;GACpC,IAAI,WAAW,qBAAqB,MAAM,QAAQ;GAClD,IAAI,QACF,WAAW,SAAS,OAAO,MAAM;GAGnC,IAAI,SAAS,SAAS,GAAG;IACvB,YAAY,SAAS,EAAE;IACvB,OAAO,SAAS;GAClB;GAEA,OAAO;EACT;EAEA,UAAU,OAA4B,CAAC,GAAG;GACxC,MAAM,OAAO,IAAI;GACjB,IAAI,CAAC,MAAM,OAAO;GAElB,MAAM,EAAE,WAAW,MAAM,WAAW;GACpC,IAAI,WAAW,qBAAqB,MAAM,QAAQ;GAClD,IAAI,QACF,WAAW,SAAS,OAAO,MAAM;GAGnC,IAAI,SAAS,SAAS,GAAG;IACvB,MAAM,cAAc,SAAS,SAAS,SAAS;IAC/C,YAAY,WAAW;IACvB,OAAO;GACT;GAEA,OAAO;EACT;CACF;AACF;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,cAAc,QAA0B,CAAC,GAAgB;CACvE,IAAI;CACJ,MAAM,CAAC,aAAa,kBAAkB,aAAa,KAAK;CACxD,IAAI,qBAAqC;CAEzC,MAAM,SAAS,UAAU;CACzB,MAAM,oBAAoB,OAAO,MAAM,WAAW,KAAK;CACvD,MAAM,kBAAkB,OAAO,MAAM,aAAa;CAClD,MAAM,uBAAuB,OAAO,MAAM,kBAAkB;CAE5D,MAAM,eAAe,yBAAyB,UAAU;CAGxD,cAAc;EACZ,IAAI,YAAY;GACd,MAAM,gBAAgB,WAAW,eAAe,QAAQ,oBAAkB;GAC1E,eAAe,CAAC,CAAC,aAAa;EAChC;CACF,CAAC;CAGD,MAAM,aAAa,MAAqB;EACtC,MAAM,OAAO;EACb,IAAI,CAAC,MAAM;EAGX,IAAI,YAAY,GAAG;EAEnB,MAAM,SAAS,EAAE;EACjB,IAAI,EAAE,kBAAkB,YAAY,CAAC,KAAK,SAAS,MAAM,GACvD;EAGF,MAAM,MAAM,iBAAiB,IAAI;EAGjC,MAAM,QADM,OAAO,CAAC,CAAC,cACC;EACtB,MAAM,eAAe,YAAY,MAAM;EAEvC,IAAI,UAAU;EAEd,QAAQ,EAAE,KAAV;GACE,KAAK;IACH,IAAI,cAAc;KAChB,IAAI,OACF,aAAa,cAAc,EAAE,UAAU,KAAK,CAAC;UAE7C,aAAa,UAAU,EAAE,UAAU,KAAK,CAAC;KAE3C,UAAU;IACZ;IACA;GACF,KAAK;IACH,IAAI,cAAc;KAChB,IAAI,OACF,aAAa,UAAU,EAAE,UAAU,KAAK,CAAC;UAEzC,aAAa,cAAc,EAAE,UAAU,KAAK,CAAC;KAE/C,UAAU;IACZ;IACA;GACF,KAAK;IACH,IAAI,CAAC,cAAc;KACjB,aAAa,UAAU,EAAE,UAAU,KAAK,CAAC;KACzC,UAAU;IACZ;IACA;GACF,KAAK;IACH,IAAI,CAAC,cAAc;KACjB,aAAa,cAAc,EAAE,UAAU,KAAK,CAAC;KAC7C,UAAU;IACZ;IACA;GACF,KAAK;IAKH,qBAAqB,iBAAiB,GAAG;IACzC,IAAI,EAAE,UACJ,aAAa,WAAW,EAAE,UAAU,KAAK,CAAC;SAE1C,aAAa,UAAU,EAAE,UAAU,KAAK,CAAC;IAE3C;EACJ;EAEA,IAAI,SAAS;GACX,EAAE,eAAe;GACjB,EAAE,gBAAgB;EACpB;CACF;CAGA,MAAM,UAAU,MAAkB;EAChC,MAAM,OAAO;EACb,IAAI,CAAC,MAAM;EACX,IAAI,YAAY,GAAG;EAEnB,MAAM,gBAAgB,EAAE;EACxB,IAAI,EAAE,iBAAiB,KAAK,SAAS,aAAa,MAAM,CAAC,oBACvD,qBAAqB,EAAE;CAE3B;CAKA,MAAM,WAAW,MAAkB;EACjC,MAAM,OAAO;EACb,IAAI,CAAC,MAAM;EACX,IAAI,YAAY,GAAG;EAEnB,MAAM,gBAAgB,EAAE;EACxB,MAAM,SAAS,EAAE;EACjB,IACE,sBACA,EAAE,iBAAiB,KAAK,SAAS,aAAa,MAC9C,kBAAkB,QAClB,KAAK,SAAS,MAAM,GACpB;GACA,YAAY,kBAAiC;GAC7C,qBAAqB;EACvB;CACF;CAGA,MAAM,UAAU,OAAoB;EAClC,aAAa;EAGb,GAAG,iBAAiB,WAAW,WAAW,IAAI;EAC9C,GAAG,iBAAiB,SAAS,SAAS,IAAI;EAC1C,GAAG,iBAAiB,QAAQ,QAAQ,IAAI;EAExC,gBAAgB;GACd,GAAG,oBAAoB,WAAW,WAAW,IAAI;GACjD,GAAG,oBAAoB,SAAS,SAAS,IAAI;GAC7C,GAAG,oBAAoB,QAAQ,QAAQ,IAAI;EAC7C,CAAC;CACH;CAEA,OAAO;EACL,cAAc;GACZ,IAAI,OAAO;IACT,OAAO,YAAY,IAAI,UAAU;GACnC;GACA,IAAI,qBAAqB;IAGvB,OAAO,YAAY;GACrB;GACA,IAAI,eAAe;IACjB,OAAO,UAAU;GACnB;GACA,IAAI,oBAAoB;IAGtB,OAAO,UAAU,KAAK,OAAO,eAAe,IAAI,KAAA;GAClD;GACA,KAAK;EACP;EACA;CACF;AACF"}
@@ -1,6 +1,6 @@
1
- import { i as access } from "./env.jsx";
1
+ import { t as access } from "./reactivity.jsx";
2
2
  import { g as isTabbable, p as isFocusable, s as getOwnerDocument, t as focusSafely } from "./focus.jsx";
3
- import { a as useLocale } from "./createCollator.jsx";
3
+ import { i as useLocale } from "./locale.jsx";
4
4
  import { createSignal, onCleanup, onMount } from "solid-js";
5
5
  //#region src/toolbar/createToolbar.ts
6
6
  /**
@@ -25,32 +25,6 @@ function getActiveElement(doc) {
25
25
  while (activeElement?.shadowRoot?.activeElement) activeElement = activeElement.shadowRoot.activeElement;
26
26
  return activeElement;
27
27
  }
28
- const TEXT_INPUT_TYPES = new Set([
29
- "",
30
- "text",
31
- "search",
32
- "url",
33
- "tel",
34
- "password",
35
- "email",
36
- "number",
37
- "date",
38
- "datetime-local",
39
- "month",
40
- "time",
41
- "week"
42
- ]);
43
- function isTextInputLikeElement(target) {
44
- if (!(target instanceof HTMLElement)) return false;
45
- if (target.isContentEditable || !!target.closest("[contenteditable=\"true\"]")) return true;
46
- if (target.getAttribute("role") === "textbox") return true;
47
- if (target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) return true;
48
- if (target instanceof HTMLInputElement) {
49
- const type = target.type.toLowerCase();
50
- return TEXT_INPUT_TYPES.has(type);
51
- }
52
- return false;
53
- }
54
28
  function createFocusManager(ref) {
55
29
  return {
56
30
  focusNext(opts = {}) {
@@ -157,12 +131,6 @@ function createToolbar(props = {}) {
157
131
  const target = e.target;
158
132
  if (!(target instanceof Element) || !root.contains(target)) return;
159
133
  const doc = getOwnerDocument(root);
160
- if (isTextInputLikeElement(target)) switch (e.key) {
161
- case "ArrowRight":
162
- case "ArrowLeft":
163
- case "ArrowDown":
164
- case "ArrowUp": return;
165
- }
166
134
  const isRTL = locale().direction === "rtl";
167
135
  const isHorizontal = orientation() === "horizontal";
168
136
  let handled = false;
@@ -239,13 +207,13 @@ function createToolbar(props = {}) {
239
207
  return isInToolbar() ? "group" : "toolbar";
240
208
  },
241
209
  get "aria-orientation"() {
242
- return isInToolbar() ? void 0 : orientation();
210
+ return orientation();
243
211
  },
244
212
  get "aria-label"() {
245
213
  return ariaLabel();
246
214
  },
247
215
  get "aria-labelledby"() {
248
- return ariaLabel() ? void 0 : ariaLabelledby();
216
+ return ariaLabel() == null ? ariaLabelledby() : void 0;
249
217
  },
250
218
  ref: setRef
251
219
  },
@@ -1 +1 @@
1
- {"version":3,"file":"toolbar.jsx","names":[],"sources":["../../src/toolbar/createToolbar.ts"],"sourcesContent":["/**\n * createToolbar - Accessibility hook for toolbar elements\n *\n * Provides keyboard navigation between toolbar items using arrow keys.\n * Based on @react-aria/toolbar useToolbar.\n */\n\nimport { createSignal, onMount, onCleanup, type Accessor } from \"solid-js\";\nimport { type MaybeAccessor, access } from \"../utils\";\nimport { useLocale } from \"../i18n\";\nimport { getOwnerDocument, isFocusable, isTabbable } from \"../utils\";\nimport { focusSafely } from \"../utils/focus\";\n\nexport type Orientation = \"horizontal\" | \"vertical\";\n\nexport interface AriaToolbarProps {\n /** The orientation of the toolbar. @default 'horizontal' */\n orientation?: MaybeAccessor<Orientation>;\n /** An accessibility label for the toolbar. */\n \"aria-label\"?: MaybeAccessor<string>;\n /** Identifies the element (or elements) that labels the toolbar. */\n \"aria-labelledby\"?: MaybeAccessor<string>;\n}\n\nexport interface ToolbarAria {\n /** Props for the toolbar container element. */\n toolbarProps: {\n role: \"toolbar\" | \"group\";\n \"aria-orientation\"?: Orientation;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n tabIndex?: number;\n ref: (el: HTMLElement) => void;\n };\n /** The orientation of the toolbar. */\n orientation: Accessor<Orientation>;\n}\n\ninterface FocusManagerOptions {\n from?: Element;\n tabbable?: boolean;\n wrap?: boolean;\n accept?: (node: Element) => boolean;\n}\n\ninterface FocusManager {\n focusNext(opts?: FocusManagerOptions): HTMLElement | null;\n focusPrevious(opts?: FocusManagerOptions): HTMLElement | null;\n focusFirst(opts?: FocusManagerOptions): HTMLElement | null;\n focusLast(opts?: FocusManagerOptions): HTMLElement | null;\n}\n\nfunction getFocusableElements(root: Element, tabbable = false): HTMLElement[] {\n const elements: HTMLElement[] = [];\n const filter = tabbable ? isTabbable : isFocusable;\n\n // Check the root element itself\n if (filter(root)) {\n elements.push(root as HTMLElement);\n }\n\n // Check all descendants\n const descendants = root.querySelectorAll(\"*\");\n for (let i = 0; i < descendants.length; i++) {\n const el = descendants[i];\n if (filter(el)) {\n elements.push(el as HTMLElement);\n }\n }\n\n return elements;\n}\n\nfunction getActiveElement(doc: Document): Element | null {\n let activeElement = doc.activeElement;\n while (activeElement?.shadowRoot?.activeElement) {\n activeElement = activeElement.shadowRoot.activeElement;\n }\n return activeElement;\n}\n\nconst TEXT_INPUT_TYPES = new Set([\n \"\",\n \"text\",\n \"search\",\n \"url\",\n \"tel\",\n \"password\",\n \"email\",\n \"number\",\n \"date\",\n \"datetime-local\",\n \"month\",\n \"time\",\n \"week\",\n]);\n\nfunction isTextInputLikeElement(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) {\n return false;\n }\n\n if (target.isContentEditable || !!target.closest('[contenteditable=\"true\"]')) {\n return true;\n }\n\n if (target.getAttribute(\"role\") === \"textbox\") {\n return true;\n }\n\n if (target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) {\n return true;\n }\n\n if (target instanceof HTMLInputElement) {\n const type = target.type.toLowerCase();\n return TEXT_INPUT_TYPES.has(type);\n }\n\n return false;\n}\n\nfunction createFocusManager(ref: Accessor<HTMLElement | undefined>): FocusManager {\n return {\n focusNext(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { from, tabbable = true, wrap = false, accept } = opts;\n const doc = getOwnerDocument(root);\n const current = from || getActiveElement(doc);\n\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (!current || elements.length === 0) return null;\n\n const currentIndex = elements.indexOf(current as HTMLElement);\n let nextIndex = currentIndex + 1;\n\n if (nextIndex >= elements.length) {\n if (wrap) {\n nextIndex = 0;\n } else {\n return null;\n }\n }\n\n const nextElement = elements[nextIndex];\n if (nextElement) {\n focusSafely(nextElement);\n return nextElement;\n }\n\n return null;\n },\n\n focusPrevious(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { from, tabbable = true, wrap = false, accept } = opts;\n const doc = getOwnerDocument(root);\n const current = from || getActiveElement(doc);\n\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (!current || elements.length === 0) return null;\n\n const currentIndex = elements.indexOf(current as HTMLElement);\n let prevIndex = currentIndex - 1;\n\n if (prevIndex < 0) {\n if (wrap) {\n prevIndex = elements.length - 1;\n } else {\n return null;\n }\n }\n\n const prevElement = elements[prevIndex];\n if (prevElement) {\n focusSafely(prevElement);\n return prevElement;\n }\n\n return null;\n },\n\n focusFirst(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { tabbable = true, accept } = opts;\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (elements.length > 0) {\n focusSafely(elements[0]);\n return elements[0];\n }\n\n return null;\n },\n\n focusLast(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { tabbable = true, accept } = opts;\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (elements.length > 0) {\n const lastElement = elements[elements.length - 1];\n focusSafely(lastElement);\n return lastElement;\n }\n\n return null;\n },\n };\n}\n\n/**\n * Provides the behavior and accessibility implementation for a toolbar.\n * A toolbar is a container for a set of interactive controls with arrow key navigation.\n *\n * @example\n * ```tsx\n * let ref;\n * const { toolbarProps } = createToolbar({ orientation: 'horizontal' });\n * return (\n * <div {...toolbarProps}>\n * <Button>Cut</Button>\n * <Button>Copy</Button>\n * <Button>Paste</Button>\n * </div>\n * );\n * ```\n */\nexport function createToolbar(props: AriaToolbarProps = {}): ToolbarAria {\n let toolbarRef: HTMLElement | undefined;\n const [isInToolbar, setIsInToolbar] = createSignal(false);\n let lastFocusedElement: Element | null = null;\n\n const locale = useLocale();\n const orientation = () => access(props.orientation) ?? \"horizontal\";\n const ariaLabel = () => access(props[\"aria-label\"]);\n const ariaLabelledby = () => access(props[\"aria-labelledby\"]);\n\n const focusManager = createFocusManager(() => toolbarRef);\n\n // Check if this toolbar is nested inside another toolbar\n onMount(() => {\n if (toolbarRef) {\n const parentToolbar = toolbarRef.parentElement?.closest('[role=\"toolbar\"]');\n setIsInToolbar(!!parentToolbar);\n }\n });\n\n // Keyboard event handler\n const onKeyDown = (e: KeyboardEvent) => {\n const root = toolbarRef;\n if (!root) return;\n\n // Don't handle if nested toolbar (parent handles navigation)\n if (isInToolbar()) return;\n\n const target = e.target;\n if (!(target instanceof Element) || !root.contains(target)) {\n return;\n }\n\n const doc = getOwnerDocument(root);\n\n // Text entry controls keep the arrow keys for caret/value navigation.\n // Upstream useToolbar has no such guard; a text input placed inside a\n // toolbar would otherwise lose caret movement. Kept (narrowed to arrows)\n // as a suspected divergence pending a dedicated ActionBar/Toolbar cert.\n if (isTextInputLikeElement(target)) {\n switch (e.key) {\n case \"ArrowRight\":\n case \"ArrowLeft\":\n case \"ArrowDown\":\n case \"ArrowUp\":\n return;\n }\n }\n\n const dir = locale().direction;\n const isRTL = dir === \"rtl\";\n const isHorizontal = orientation() === \"horizontal\";\n\n let handled = false;\n\n switch (e.key) {\n case \"ArrowRight\":\n if (isHorizontal) {\n if (isRTL) {\n focusManager.focusPrevious({ tabbable: true });\n } else {\n focusManager.focusNext({ tabbable: true });\n }\n handled = true;\n }\n break;\n case \"ArrowLeft\":\n if (isHorizontal) {\n if (isRTL) {\n focusManager.focusNext({ tabbable: true });\n } else {\n focusManager.focusPrevious({ tabbable: true });\n }\n handled = true;\n }\n break;\n case \"ArrowDown\":\n if (!isHorizontal) {\n focusManager.focusNext({ tabbable: true });\n handled = true;\n }\n break;\n case \"ArrowUp\":\n if (!isHorizontal) {\n focusManager.focusPrevious({ tabbable: true });\n handled = true;\n }\n break;\n case \"Tab\":\n // Move focus to the first/last child so the browser's own Tab handling\n // then carries focus OUT of the whole toolbar. Faithful to upstream\n // useToolbar: there is no roving tabindex — every item stays tabbable,\n // so a bare Tab would otherwise step to the next item instead of leaving.\n lastFocusedElement = getActiveElement(doc);\n if (e.shiftKey) {\n focusManager.focusFirst({ tabbable: true });\n } else {\n focusManager.focusLast({ tabbable: true });\n }\n return;\n }\n\n if (handled) {\n e.preventDefault();\n e.stopPropagation();\n }\n };\n\n // Record the last focused child when focus moves out of the toolbar.\n const onBlur = (e: FocusEvent) => {\n const root = toolbarRef;\n if (!root) return;\n if (isInToolbar()) return;\n\n const relatedTarget = e.relatedTarget as Node | null;\n if (!(relatedTarget && root.contains(relatedTarget)) && !lastFocusedElement) {\n lastFocusedElement = e.target as Element;\n }\n };\n\n // Restore focus to the last focused child when focus returns into the toolbar.\n // If that element was removed, focus() is a no-op and the browser lands on the\n // first/last child per direction — matching upstream useToolbar.\n const onFocus = (e: FocusEvent) => {\n const root = toolbarRef;\n if (!root) return;\n if (isInToolbar()) return;\n\n const relatedTarget = e.relatedTarget as Node | null;\n const target = e.target;\n if (\n lastFocusedElement &&\n !(relatedTarget && root.contains(relatedTarget)) &&\n target instanceof Node &&\n root.contains(target)\n ) {\n focusSafely(lastFocusedElement as HTMLElement);\n lastFocusedElement = null;\n }\n };\n\n // Set up capture event listeners\n const setRef = (el: HTMLElement) => {\n toolbarRef = el;\n\n // Use capture phase so container handlers run before the focused child's.\n el.addEventListener(\"keydown\", onKeyDown, true);\n el.addEventListener(\"focus\", onFocus, true);\n el.addEventListener(\"blur\", onBlur, true);\n\n onCleanup(() => {\n el.removeEventListener(\"keydown\", onKeyDown, true);\n el.removeEventListener(\"focus\", onFocus, true);\n el.removeEventListener(\"blur\", onBlur, true);\n });\n };\n\n return {\n toolbarProps: {\n get role() {\n return isInToolbar() ? \"group\" : \"toolbar\";\n },\n get \"aria-orientation\"() {\n return isInToolbar() ? undefined : orientation();\n },\n get \"aria-label\"() {\n return ariaLabel();\n },\n get \"aria-labelledby\"() {\n // Only use aria-labelledby if no aria-label is provided\n return ariaLabel() ? undefined : ariaLabelledby();\n },\n ref: setRef,\n },\n orientation,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"toolbar.jsx","names":[],"sources":["../../src/toolbar/createToolbar.ts"],"sourcesContent":["/**\n * createToolbar - Accessibility hook for toolbar elements\n *\n * Provides keyboard navigation between toolbar items using arrow keys.\n * Based on @react-aria/toolbar useToolbar.\n */\n\nimport { createSignal, onMount, onCleanup, type Accessor } from \"solid-js\";\nimport { type MaybeAccessor, access } from \"../utils\";\nimport { useLocale } from \"../i18n\";\nimport { getOwnerDocument, isFocusable, isTabbable } from \"../utils\";\nimport { focusSafely } from \"../utils/focus\";\n\nexport type Orientation = \"horizontal\" | \"vertical\";\n\nexport interface AriaToolbarProps {\n /** The orientation of the toolbar. @default 'horizontal' */\n orientation?: MaybeAccessor<Orientation>;\n /** An accessibility label for the toolbar. */\n \"aria-label\"?: MaybeAccessor<string>;\n /** Identifies the element (or elements) that labels the toolbar. */\n \"aria-labelledby\"?: MaybeAccessor<string>;\n}\n\nexport interface ToolbarAria {\n /** Props for the toolbar container element. */\n toolbarProps: {\n role: \"toolbar\" | \"group\";\n \"aria-orientation\"?: Orientation;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n tabIndex?: number;\n ref: (el: HTMLElement) => void;\n };\n /** The orientation of the toolbar. */\n orientation: Accessor<Orientation>;\n}\n\ninterface FocusManagerOptions {\n from?: Element;\n tabbable?: boolean;\n wrap?: boolean;\n accept?: (node: Element) => boolean;\n}\n\ninterface FocusManager {\n focusNext(opts?: FocusManagerOptions): HTMLElement | null;\n focusPrevious(opts?: FocusManagerOptions): HTMLElement | null;\n focusFirst(opts?: FocusManagerOptions): HTMLElement | null;\n focusLast(opts?: FocusManagerOptions): HTMLElement | null;\n}\n\nfunction getFocusableElements(root: Element, tabbable = false): HTMLElement[] {\n const elements: HTMLElement[] = [];\n const filter = tabbable ? isTabbable : isFocusable;\n\n // Check the root element itself\n if (filter(root)) {\n elements.push(root as HTMLElement);\n }\n\n // Check all descendants\n const descendants = root.querySelectorAll(\"*\");\n for (let i = 0; i < descendants.length; i++) {\n const el = descendants[i];\n if (filter(el)) {\n elements.push(el as HTMLElement);\n }\n }\n\n return elements;\n}\n\nfunction getActiveElement(doc: Document): Element | null {\n let activeElement = doc.activeElement;\n while (activeElement?.shadowRoot?.activeElement) {\n activeElement = activeElement.shadowRoot.activeElement;\n }\n return activeElement;\n}\n\nfunction createFocusManager(ref: Accessor<HTMLElement | undefined>): FocusManager {\n return {\n focusNext(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { from, tabbable = true, wrap = false, accept } = opts;\n const doc = getOwnerDocument(root);\n const current = from || getActiveElement(doc);\n\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (!current || elements.length === 0) return null;\n\n const currentIndex = elements.indexOf(current as HTMLElement);\n let nextIndex = currentIndex + 1;\n\n if (nextIndex >= elements.length) {\n if (wrap) {\n nextIndex = 0;\n } else {\n return null;\n }\n }\n\n const nextElement = elements[nextIndex];\n if (nextElement) {\n focusSafely(nextElement);\n return nextElement;\n }\n\n return null;\n },\n\n focusPrevious(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { from, tabbable = true, wrap = false, accept } = opts;\n const doc = getOwnerDocument(root);\n const current = from || getActiveElement(doc);\n\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (!current || elements.length === 0) return null;\n\n const currentIndex = elements.indexOf(current as HTMLElement);\n let prevIndex = currentIndex - 1;\n\n if (prevIndex < 0) {\n if (wrap) {\n prevIndex = elements.length - 1;\n } else {\n return null;\n }\n }\n\n const prevElement = elements[prevIndex];\n if (prevElement) {\n focusSafely(prevElement);\n return prevElement;\n }\n\n return null;\n },\n\n focusFirst(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { tabbable = true, accept } = opts;\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (elements.length > 0) {\n focusSafely(elements[0]);\n return elements[0];\n }\n\n return null;\n },\n\n focusLast(opts: FocusManagerOptions = {}) {\n const root = ref();\n if (!root) return null;\n\n const { tabbable = true, accept } = opts;\n let elements = getFocusableElements(root, tabbable);\n if (accept) {\n elements = elements.filter(accept);\n }\n\n if (elements.length > 0) {\n const lastElement = elements[elements.length - 1];\n focusSafely(lastElement);\n return lastElement;\n }\n\n return null;\n },\n };\n}\n\n/**\n * Provides the behavior and accessibility implementation for a toolbar.\n * A toolbar is a container for a set of interactive controls with arrow key navigation.\n *\n * @example\n * ```tsx\n * let ref;\n * const { toolbarProps } = createToolbar({ orientation: 'horizontal' });\n * return (\n * <div {...toolbarProps}>\n * <Button>Cut</Button>\n * <Button>Copy</Button>\n * <Button>Paste</Button>\n * </div>\n * );\n * ```\n */\nexport function createToolbar(props: AriaToolbarProps = {}): ToolbarAria {\n let toolbarRef: HTMLElement | undefined;\n const [isInToolbar, setIsInToolbar] = createSignal(false);\n let lastFocusedElement: Element | null = null;\n\n const locale = useLocale();\n const orientation = () => access(props.orientation) ?? \"horizontal\";\n const ariaLabel = () => access(props[\"aria-label\"]);\n const ariaLabelledby = () => access(props[\"aria-labelledby\"]);\n\n const focusManager = createFocusManager(() => toolbarRef);\n\n // Check if this toolbar is nested inside another toolbar\n onMount(() => {\n if (toolbarRef) {\n const parentToolbar = toolbarRef.parentElement?.closest('[role=\"toolbar\"]');\n setIsInToolbar(!!parentToolbar);\n }\n });\n\n // Keyboard event handler\n const onKeyDown = (e: KeyboardEvent) => {\n const root = toolbarRef;\n if (!root) return;\n\n // Don't handle if nested toolbar (parent handles navigation)\n if (isInToolbar()) return;\n\n const target = e.target;\n if (!(target instanceof Element) || !root.contains(target)) {\n return;\n }\n\n const doc = getOwnerDocument(root);\n\n const dir = locale().direction;\n const isRTL = dir === \"rtl\";\n const isHorizontal = orientation() === \"horizontal\";\n\n let handled = false;\n\n switch (e.key) {\n case \"ArrowRight\":\n if (isHorizontal) {\n if (isRTL) {\n focusManager.focusPrevious({ tabbable: true });\n } else {\n focusManager.focusNext({ tabbable: true });\n }\n handled = true;\n }\n break;\n case \"ArrowLeft\":\n if (isHorizontal) {\n if (isRTL) {\n focusManager.focusNext({ tabbable: true });\n } else {\n focusManager.focusPrevious({ tabbable: true });\n }\n handled = true;\n }\n break;\n case \"ArrowDown\":\n if (!isHorizontal) {\n focusManager.focusNext({ tabbable: true });\n handled = true;\n }\n break;\n case \"ArrowUp\":\n if (!isHorizontal) {\n focusManager.focusPrevious({ tabbable: true });\n handled = true;\n }\n break;\n case \"Tab\":\n // Move focus to the first/last child so the browser's own Tab handling\n // then carries focus OUT of the whole toolbar. Faithful to upstream\n // useToolbar: there is no roving tabindex — every item stays tabbable,\n // so a bare Tab would otherwise step to the next item instead of leaving.\n lastFocusedElement = getActiveElement(doc);\n if (e.shiftKey) {\n focusManager.focusFirst({ tabbable: true });\n } else {\n focusManager.focusLast({ tabbable: true });\n }\n return;\n }\n\n if (handled) {\n e.preventDefault();\n e.stopPropagation();\n }\n };\n\n // Record the last focused child when focus moves out of the toolbar.\n const onBlur = (e: FocusEvent) => {\n const root = toolbarRef;\n if (!root) return;\n if (isInToolbar()) return;\n\n const relatedTarget = e.relatedTarget as Node | null;\n if (!(relatedTarget && root.contains(relatedTarget)) && !lastFocusedElement) {\n lastFocusedElement = e.target as Element;\n }\n };\n\n // Restore focus to the last focused child when focus returns into the toolbar.\n // If that element was removed, focus() is a no-op and the browser lands on the\n // first/last child per direction — matching upstream useToolbar.\n const onFocus = (e: FocusEvent) => {\n const root = toolbarRef;\n if (!root) return;\n if (isInToolbar()) return;\n\n const relatedTarget = e.relatedTarget as Node | null;\n const target = e.target;\n if (\n lastFocusedElement &&\n !(relatedTarget && root.contains(relatedTarget)) &&\n target instanceof Node &&\n root.contains(target)\n ) {\n focusSafely(lastFocusedElement as HTMLElement);\n lastFocusedElement = null;\n }\n };\n\n // Set up capture event listeners\n const setRef = (el: HTMLElement) => {\n toolbarRef = el;\n\n // Use capture phase so container handlers run before the focused child's.\n el.addEventListener(\"keydown\", onKeyDown, true);\n el.addEventListener(\"focus\", onFocus, true);\n el.addEventListener(\"blur\", onBlur, true);\n\n onCleanup(() => {\n el.removeEventListener(\"keydown\", onKeyDown, true);\n el.removeEventListener(\"focus\", onFocus, true);\n el.removeEventListener(\"blur\", onBlur, true);\n });\n };\n\n return {\n toolbarProps: {\n get role() {\n return isInToolbar() ? \"group\" : \"toolbar\";\n },\n get \"aria-orientation\"() {\n // Upstream useToolbar emits aria-orientation unconditionally, on the\n // nested role=\"group\" too — not only on the root role=\"toolbar\".\n return orientation();\n },\n get \"aria-label\"() {\n return ariaLabel();\n },\n get \"aria-labelledby\"() {\n // Mirror useToolbar: `aria-label == null ? aria-labelledby : undefined`.\n // An explicit empty-string aria-label still suppresses labelledby.\n return ariaLabel() == null ? ariaLabelledby() : undefined;\n },\n ref: setRef,\n },\n orientation,\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { o as mergeProps } from "./env.js";
1
+ import { i as mergeProps$1 } from "./env.js";
2
2
  import { t as filterDOMProps } from "./filterDOMProps.js";
3
3
  import { n as createFocusable } from "./createFocusable.js";
4
4
  import { t as createHover } from "./createHover.js";
@@ -34,7 +34,7 @@ function createTooltip(props = {}, state) {
34
34
  onHoverStart: () => state?.open(true),
35
35
  onHoverEnd: () => state?.close()
36
36
  });
37
- return { tooltipProps: mergeProps(domProps, hoverProps, { role: "tooltip" }) };
37
+ return { tooltipProps: mergeProps$1(domProps, hoverProps, { role: "tooltip" }) };
38
38
  }
39
39
  //#endregion
40
40
  //#region src/tooltip/createTooltipTrigger.ts