@ctrl-ui/react 0.8.7 → 0.8.8

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 (351) hide show
  1. package/dist/action-bar.d.ts +15 -7
  2. package/dist/action-bar.js +40 -20
  3. package/dist/activity.js +8 -16
  4. package/dist/audio-recorder.d.ts +14 -4
  5. package/dist/audio-recorder.js +82 -29
  6. package/dist/audio-visualizer.js +1 -1
  7. package/dist/blocks/coding-agent.js +39 -39
  8. package/dist/blocks/design-canvas-inspector.js +2 -2
  9. package/dist/blocks/design-canvas.d.ts +5 -1
  10. package/dist/blocks/design-canvas.js +200 -94
  11. package/dist/blocks/discussion.d.ts +28 -0
  12. package/dist/blocks/discussion.js +67 -0
  13. package/dist/blocks/file-explorer-data.d.ts +1 -0
  14. package/dist/blocks/file-explorer-data.js +4 -1
  15. package/dist/blocks/file-explorer.d.ts +9 -1
  16. package/dist/blocks/file-explorer.js +461 -192
  17. package/dist/blocks/settings.d.ts +2 -1
  18. package/dist/blocks/settings.js +102 -80
  19. package/dist/blocks/team-chat.d.ts +12 -0
  20. package/dist/blocks/team-chat.js +26 -0
  21. package/dist/blocks/theme-toggle.js +23 -31
  22. package/dist/chat-composer-attachment.d.ts +4 -1
  23. package/dist/chat-composer-attachment.js +35 -10
  24. package/dist/chat-composer-editor/extensions/mention.d.ts +5 -1
  25. package/dist/chat-composer-editor/extensions/mention.js +58 -27
  26. package/dist/chat-composer-editor/ghost.js +2 -4
  27. package/dist/chat-composer-editor/types.d.ts +5 -0
  28. package/dist/chat-composer-editor.d.ts +1 -1
  29. package/dist/chat-composer-editor.js +57 -30
  30. package/dist/chat-composer.d.ts +13 -32
  31. package/dist/chat-composer.js +53 -13
  32. package/dist/chat-layout.d.ts +19 -0
  33. package/dist/chat-layout.js +91 -40
  34. package/dist/chat-message/social.d.ts +26 -0
  35. package/dist/chat-message/social.js +122 -0
  36. package/dist/chat-message.d.ts +28 -29
  37. package/dist/chat-message.js +106 -25
  38. package/dist/context.d.ts +2 -2
  39. package/dist/context.js +39 -24
  40. package/dist/dynamic-notification-canvas-loop.d.ts +24 -0
  41. package/dist/dynamic-notification-canvas-loop.js +113 -0
  42. package/dist/dynamic-notification-glass.js +23 -114
  43. package/dist/dynamic-notification-liquid.js +14 -118
  44. package/dist/dynamic-notification.d.ts +16 -8
  45. package/dist/dynamic-notification.js +86 -23
  46. package/dist/email/email-brand.d.ts +7 -4
  47. package/dist/email/email-brand.js +9 -6
  48. package/dist/email/email-code.d.ts +3 -2
  49. package/dist/email/email-code.js +11 -9
  50. package/dist/email/email-footer.d.ts +5 -3
  51. package/dist/email/email-footer.js +11 -5
  52. package/dist/email/email.d.ts +31 -9
  53. package/dist/email/email.js +50 -26
  54. package/dist/email/templates.d.ts +10 -9
  55. package/dist/email/templates.js +102 -38
  56. package/dist/email/theme.js +1 -1
  57. package/dist/environment-variables.d.ts +4 -2
  58. package/dist/environment-variables.js +52 -32
  59. package/dist/filter-bar/editor.js +20 -8
  60. package/dist/filter-bar/model.d.ts +1 -0
  61. package/dist/filter-bar/model.js +10 -1
  62. package/dist/filter-bar/motion.js +4 -4
  63. package/dist/filter-bar/parts.js +20 -9
  64. package/dist/filter-bar/types.d.ts +1 -0
  65. package/dist/filter-bar.js +20 -14
  66. package/dist/hooks/use-audio-recorder.js +12 -6
  67. package/dist/hooks/use-chat-composer.d.ts +19 -7
  68. package/dist/hooks/use-chat-composer.js +32 -5
  69. package/dist/hooks/use-chat-message.d.ts +28 -2
  70. package/dist/hooks/use-chat-message.js +41 -10
  71. package/dist/hooks/use-chat-thread-scroll.js +43 -7
  72. package/dist/hooks/use-color-area.d.ts +3 -3
  73. package/dist/hooks/use-color-area.js +13 -12
  74. package/dist/hooks/use-copy-to-clipboard.d.ts +2 -1
  75. package/dist/hooks/use-copy-to-clipboard.js +20 -20
  76. package/dist/hooks/use-dynamic-notification.d.ts +6 -2
  77. package/dist/hooks/use-dynamic-notification.js +26 -10
  78. package/dist/hooks/use-mobile.d.ts +3 -2
  79. package/dist/hooks/use-mobile.js +8 -4
  80. package/dist/hooks/use-textarea-trigger-menu.js +2 -1
  81. package/dist/hooks/use-trigger-menu.d.ts +54 -0
  82. package/dist/hooks/use-trigger-menu.js +102 -47
  83. package/dist/hooks/use-user-ask.d.ts +2 -0
  84. package/dist/hooks/use-user-ask.js +35 -41
  85. package/dist/inline-attachment.d.ts +6 -2
  86. package/dist/inline-attachment.js +36 -15
  87. package/dist/inline-citation.d.ts +12 -5
  88. package/dist/inline-citation.js +26 -17
  89. package/dist/knob-contracts/audio-recorder-knobs.d.ts +1 -1
  90. package/dist/knob-contracts/audio-recorder-knobs.js +2 -1
  91. package/dist/knob-contracts/audio-visualizer-knobs.d.ts +1 -1
  92. package/dist/knob-contracts/audio-visualizer-knobs.js +2 -1
  93. package/dist/knob-contracts/avatar-knobs.d.ts +1 -1
  94. package/dist/knob-contracts/avatar-knobs.js +6 -1
  95. package/dist/knob-contracts/badge-knobs.d.ts +1 -1
  96. package/dist/knob-contracts/badge-knobs.js +2 -1
  97. package/dist/knob-contracts/breadcrumb-knobs.d.ts +4 -0
  98. package/dist/knob-contracts/breadcrumb-knobs.js +8 -0
  99. package/dist/knob-contracts/button-knobs.d.ts +1 -1
  100. package/dist/knob-contracts/button-knobs.js +1 -0
  101. package/dist/knob-contracts/chat-message-knobs.d.ts +1 -1
  102. package/dist/knob-contracts/chat-message-knobs.js +8 -1
  103. package/dist/knob-contracts/code-diff-knobs.d.ts +1 -1
  104. package/dist/knob-contracts/code-diff-knobs.js +2 -0
  105. package/dist/knob-contracts/code-knobs.d.ts +1 -1
  106. package/dist/knob-contracts/code-knobs.js +2 -1
  107. package/dist/knob-contracts/dynamic-notification-knobs.d.ts +1 -1
  108. package/dist/knob-contracts/dynamic-notification-knobs.js +2 -0
  109. package/dist/knob-contracts/emoji-picker-knobs.d.ts +4 -0
  110. package/dist/knob-contracts/emoji-picker-knobs.js +10 -0
  111. package/dist/knob-contracts/field-knobs.d.ts +1 -1
  112. package/dist/knob-contracts/field-knobs.js +5 -1
  113. package/dist/knob-contracts/filter-bar-knobs.d.ts +1 -1
  114. package/dist/knob-contracts/filter-bar-knobs.js +1 -2
  115. package/dist/knob-contracts/markdown-knobs.d.ts +1 -1
  116. package/dist/knob-contracts/markdown-knobs.js +3 -1
  117. package/dist/knob-contracts/page-layout-knobs.d.ts +1 -1
  118. package/dist/knob-contracts/page-layout-knobs.js +2 -1
  119. package/dist/knob-contracts/popup-knobs.d.ts +1 -1
  120. package/dist/knob-contracts/popup-knobs.js +27 -0
  121. package/dist/knob-contracts/range-knobs.d.ts +1 -1
  122. package/dist/knob-contracts/range-knobs.js +1 -0
  123. package/dist/knob-contracts/rich-tooltip-knobs.d.ts +1 -1
  124. package/dist/knob-contracts/rich-tooltip-knobs.js +2 -1
  125. package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
  126. package/dist/knob-contracts/sidebar-knobs.js +27 -2
  127. package/dist/knob-contracts/spinner-knobs.d.ts +1 -1
  128. package/dist/knob-contracts/spinner-knobs.js +1 -1
  129. package/dist/knob-contracts/stepper-knobs.d.ts +1 -1
  130. package/dist/knob-contracts/stepper-knobs.js +4 -1
  131. package/dist/knob-contracts/switch-knobs.d.ts +1 -1
  132. package/dist/knob-contracts/switch-knobs.js +5 -0
  133. package/dist/knob-contracts/table-of-contents-knobs.d.ts +1 -1
  134. package/dist/knob-contracts/table-of-contents-knobs.js +2 -1
  135. package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
  136. package/dist/knob-contracts/tabs-knobs.js +1 -0
  137. package/dist/knob-contracts/tree-knobs.d.ts +1 -1
  138. package/dist/knob-contracts/tree-knobs.js +2 -1
  139. package/dist/lib/cn.js +0 -4
  140. package/dist/lib/contrast.d.ts +1 -0
  141. package/dist/lib/contrast.js +17 -5
  142. package/dist/lib/dropzone-validation.js +41 -28
  143. package/dist/lib/env-file.d.ts +1 -1
  144. package/dist/lib/env-file.js +5 -5
  145. package/dist/lib/motion.d.ts +4 -0
  146. package/dist/lib/motion.js +15 -0
  147. package/dist/lib/phone-input-format.js +1 -1
  148. package/dist/lib/trigger-detect.js +6 -8
  149. package/dist/markdown-block.js +2 -1
  150. package/dist/popup-parts.d.ts +8 -0
  151. package/dist/popup-parts.js +43 -0
  152. package/dist/source-badge.js +3 -1
  153. package/dist/styles/code.css +0 -2
  154. package/dist/styles/effects.css +6 -32
  155. package/dist/styles/index.css +10 -5
  156. package/dist/styles/recipes/accordion.css +9 -6
  157. package/dist/styles/recipes/action-bar.css +0 -15
  158. package/dist/styles/recipes/activity.css +5 -11
  159. package/dist/styles/recipes/alert.css +4 -4
  160. package/dist/styles/recipes/audio-recorder.css +15 -3
  161. package/dist/styles/recipes/audio-visualizer.css +13 -2
  162. package/dist/styles/recipes/avatar.css +78 -1
  163. package/dist/styles/recipes/badge.css +14 -4
  164. package/dist/styles/recipes/breadcrumb.css +77 -0
  165. package/dist/styles/recipes/button-group.css +5 -7
  166. package/dist/styles/recipes/button-states.css +66 -0
  167. package/dist/styles/recipes/button.css +24 -83
  168. package/dist/styles/recipes/calendar.css +17 -23
  169. package/dist/styles/recipes/card.css +2 -2
  170. package/dist/styles/recipes/chat-composer-attachment.css +9 -3
  171. package/dist/styles/recipes/chat-composer.css +7 -10
  172. package/dist/styles/recipes/chat-layout.css +35 -0
  173. package/dist/styles/recipes/chat-message-social.css +123 -0
  174. package/dist/styles/recipes/chat-message.css +157 -37
  175. package/dist/styles/recipes/choice.css +18 -21
  176. package/dist/styles/recipes/code-diff.css +46 -33
  177. package/dist/styles/recipes/code.css +36 -16
  178. package/dist/styles/recipes/collapsible.css +7 -2
  179. package/dist/styles/recipes/color-picker-details.css +3 -4
  180. package/dist/styles/recipes/color-picker-values.css +9 -8
  181. package/dist/styles/recipes/color-picker.css +11 -2
  182. package/dist/styles/recipes/context.css +3 -3
  183. package/dist/styles/recipes/dockable-panel.css +7 -4
  184. package/dist/styles/recipes/dropzone.css +7 -8
  185. package/dist/styles/recipes/dynamic-notification-input.css +8 -5
  186. package/dist/styles/recipes/dynamic-notification-motion.css +12 -21
  187. package/dist/styles/recipes/dynamic-notification.css +40 -26
  188. package/dist/styles/recipes/emoji-picker.css +132 -0
  189. package/dist/styles/recipes/empty.css +5 -4
  190. package/dist/styles/recipes/environment-variables.css +4 -15
  191. package/dist/styles/recipes/field-parts.css +30 -24
  192. package/dist/styles/recipes/field.css +61 -26
  193. package/dist/styles/recipes/filter-bar.css +18 -31
  194. package/dist/styles/recipes/gradient-editor.css +18 -7
  195. package/dist/styles/recipes/infinite-canvas.css +0 -1
  196. package/dist/styles/recipes/inline-attachment.css +3 -3
  197. package/dist/styles/recipes/inline-citation.css +10 -4
  198. package/dist/styles/recipes/item.css +23 -9
  199. package/dist/styles/recipes/markdown-block.css +5 -4
  200. package/dist/styles/recipes/markdown.css +68 -26
  201. package/dist/styles/recipes/morphing-panel.css +8 -8
  202. package/dist/styles/recipes/page-layout.css +20 -12
  203. package/dist/styles/recipes/pagination.css +4 -5
  204. package/dist/styles/recipes/phone-input.css +12 -8
  205. package/dist/styles/recipes/popup-controls.css +166 -33
  206. package/dist/styles/recipes/popup-drawer.css +3 -14
  207. package/dist/styles/recipes/popup-lists.css +128 -27
  208. package/dist/styles/recipes/popup-motion.css +4 -45
  209. package/dist/styles/recipes/popup-toast.css +55 -26
  210. package/dist/styles/recipes/popup.css +139 -141
  211. package/dist/styles/recipes/range.css +54 -18
  212. package/dist/styles/recipes/resizable.css +2 -7
  213. package/dist/styles/recipes/rich-tooltip.css +24 -34
  214. package/dist/styles/recipes/scroll-area.css +8 -1
  215. package/dist/styles/recipes/sidebar-menu.css +149 -34
  216. package/dist/styles/recipes/sidebar-parts.css +154 -0
  217. package/dist/styles/recipes/sidebar.css +187 -128
  218. package/dist/styles/recipes/source-badge.css +1 -1
  219. package/dist/styles/recipes/spinner.css +8 -1
  220. package/dist/styles/recipes/stepper.css +31 -13
  221. package/dist/styles/recipes/switch.css +68 -23
  222. package/dist/styles/recipes/table-of-contents.css +9 -1
  223. package/dist/styles/recipes/table.css +14 -10
  224. package/dist/styles/recipes/tabs.css +17 -12
  225. package/dist/styles/recipes/task-list.css +7 -14
  226. package/dist/styles/recipes/thread-rail.css +42 -19
  227. package/dist/styles/recipes/timeline.css +2 -0
  228. package/dist/styles/recipes/toolbar.css +27 -25
  229. package/dist/styles/recipes/track-highlight.css +10 -7
  230. package/dist/styles/recipes/tree.css +31 -4
  231. package/dist/styles/recipes/user-ask-details.css +0 -1
  232. package/dist/styles/recipes/user-ask.css +4 -4
  233. package/dist/styles/theme.css +37 -16
  234. package/dist/task-list.d.ts +4 -1
  235. package/dist/task-list.js +24 -8
  236. package/dist/thread-rail.d.ts +1 -1
  237. package/dist/thread-rail.js +61 -19
  238. package/dist/transcript-divider.js +2 -1
  239. package/dist/ui/alert-dialog.js +1 -1
  240. package/dist/ui/alert.d.ts +3 -1
  241. package/dist/ui/alert.js +14 -8
  242. package/dist/ui/autocomplete.js +4 -2
  243. package/dist/ui/avatar.d.ts +8 -0
  244. package/dist/ui/avatar.js +23 -3
  245. package/dist/ui/badge.js +1 -1
  246. package/dist/ui/breadcrumb.d.ts +27 -0
  247. package/dist/ui/breadcrumb.js +88 -0
  248. package/dist/ui/button.d.ts +2 -1
  249. package/dist/ui/button.js +3 -3
  250. package/dist/ui/calendar.js +2 -2
  251. package/dist/ui/card.d.ts +3 -1
  252. package/dist/ui/card.js +12 -7
  253. package/dist/ui/checkbox.d.ts +2 -0
  254. package/dist/ui/code-diff.d.ts +1 -1
  255. package/dist/ui/code-diff.js +72 -28
  256. package/dist/ui/code.d.ts +5 -3
  257. package/dist/ui/code.js +28 -29
  258. package/dist/ui/collapsible.js +1 -1
  259. package/dist/ui/color-picker.d.ts +5 -2
  260. package/dist/ui/color-picker.js +85 -37
  261. package/dist/ui/combobox-disabled-selection.d.ts +1 -2
  262. package/dist/ui/combobox-disabled-selection.js +7 -9
  263. package/dist/ui/combobox.js +14 -6
  264. package/dist/ui/command.d.ts +8 -2
  265. package/dist/ui/command.js +30 -14
  266. package/dist/ui/context-menu.js +4 -1
  267. package/dist/ui/dialog.d.ts +2 -1
  268. package/dist/ui/dialog.js +4 -23
  269. package/dist/ui/dockable-panel-geometry.d.ts +1 -0
  270. package/dist/ui/dockable-panel-geometry.js +7 -1
  271. package/dist/ui/dockable-panel.d.ts +9 -6
  272. package/dist/ui/dockable-panel.js +63 -23
  273. package/dist/ui/drawer.js +1 -1
  274. package/dist/ui/dropdown-menu.js +5 -4
  275. package/dist/ui/dropzone.d.ts +2 -2
  276. package/dist/ui/dropzone.js +75 -16
  277. package/dist/ui/emoji-picker.d.ts +18 -0
  278. package/dist/ui/emoji-picker.js +136 -0
  279. package/dist/ui/empty.d.ts +3 -1
  280. package/dist/ui/empty.js +12 -7
  281. package/dist/ui/gradient-editor.d.ts +1 -1
  282. package/dist/ui/gradient-editor.js +37 -21
  283. package/dist/ui/hover-card.js +1 -1
  284. package/dist/ui/infinite-canvas.d.ts +3 -2
  285. package/dist/ui/infinite-canvas.js +63 -19
  286. package/dist/ui/input-group.d.ts +0 -3
  287. package/dist/ui/input-group.js +4 -14
  288. package/dist/ui/input-otp.d.ts +1 -1
  289. package/dist/ui/input-otp.js +32 -15
  290. package/dist/ui/input.js +4 -4
  291. package/dist/ui/item.d.ts +2 -1
  292. package/dist/ui/item.js +12 -8
  293. package/dist/ui/live-status.d.ts +7 -0
  294. package/dist/ui/live-status.js +17 -0
  295. package/dist/ui/markdown-elements.d.ts +6 -0
  296. package/dist/ui/markdown-elements.js +31 -1
  297. package/dist/ui/menubar.js +3 -3
  298. package/dist/ui/morphing-panel.js +3 -2
  299. package/dist/ui/native-select.d.ts +1 -1
  300. package/dist/ui/native-select.js +24 -11
  301. package/dist/ui/navigation-menu.js +1 -1
  302. package/dist/ui/page-layout.d.ts +2 -2
  303. package/dist/ui/page-layout.js +2 -4
  304. package/dist/ui/pagination.d.ts +7 -5
  305. package/dist/ui/pagination.js +19 -13
  306. package/dist/ui/phone-input.d.ts +1 -0
  307. package/dist/ui/phone-input.js +58 -60
  308. package/dist/ui/popover.js +1 -1
  309. package/dist/ui/radio-group.d.ts +2 -0
  310. package/dist/ui/resizable.d.ts +3 -2
  311. package/dist/ui/resizable.js +5 -3
  312. package/dist/ui/responsive-dialog.d.ts +1 -1
  313. package/dist/ui/responsive-dialog.js +4 -21
  314. package/dist/ui/rich-tooltip.d.ts +10 -4
  315. package/dist/ui/rich-tooltip.js +82 -34
  316. package/dist/ui/scroll-area.d.ts +1 -1
  317. package/dist/ui/scroll-area.js +43 -28
  318. package/dist/ui/select.js +1 -1
  319. package/dist/ui/sheet.js +1 -1
  320. package/dist/ui/sidebar-menu.d.ts +5 -1
  321. package/dist/ui/sidebar-menu.js +32 -34
  322. package/dist/ui/sidebar-provider.d.ts +10 -1
  323. package/dist/ui/sidebar-provider.js +39 -14
  324. package/dist/ui/sidebar-resize-rail.js +8 -4
  325. package/dist/ui/sidebar.d.ts +11 -6
  326. package/dist/ui/sidebar.js +47 -24
  327. package/dist/ui/slider.js +31 -22
  328. package/dist/ui/spinner.d.ts +3 -2
  329. package/dist/ui/spinner.js +3 -2
  330. package/dist/ui/stepper.d.ts +8 -1
  331. package/dist/ui/stepper.js +74 -25
  332. package/dist/ui/switch.d.ts +1 -0
  333. package/dist/ui/table-of-contents.d.ts +2 -2
  334. package/dist/ui/table-of-contents.js +20 -8
  335. package/dist/ui/table.js +4 -2
  336. package/dist/ui/textarea.d.ts +1 -1
  337. package/dist/ui/textarea.js +20 -9
  338. package/dist/ui/timeline.d.ts +3 -1
  339. package/dist/ui/timeline.js +17 -9
  340. package/dist/ui/toast.d.ts +11 -5
  341. package/dist/ui/toast.js +101 -55
  342. package/dist/ui/toolbar.d.ts +3 -2
  343. package/dist/ui/toolbar.js +4 -2
  344. package/dist/ui/tooltip.js +4 -21
  345. package/dist/ui/tree.js +121 -81
  346. package/dist/ui/trigger-menu.js +2 -1
  347. package/dist/user-ask.d.ts +10 -5
  348. package/dist/user-ask.js +32 -12
  349. package/package.json +5 -1
  350. package/dist/hooks/use-keyboard-navigation.d.ts +0 -3
  351. package/dist/hooks/use-keyboard-navigation.js +0 -22
package/dist/ui/toast.js CHANGED
@@ -1,38 +1,106 @@
1
1
  "use client";
2
- import { controlEffectsAttribute } from "../skin.js";
3
2
  import { cn } from "../lib/cn.js";
3
+ import { controlEffectsAttribute } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
5
  import { Button } from "./button.js";
6
+ import { PopupCloseButton } from "../popup-parts.js";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { CircleCheckIcon, CircleXIcon, InfoIcon, TriangleAlertIcon } from "lucide-react";
7
9
  import { Toast } from "@base-ui/react/toast";
8
10
  //#region src/ui/toast.tsx
9
11
  const toastManager = Toast.createToastManager();
10
- function withType(type) {
11
- return (title, options) => toastManager.add({
12
+ const errorDefaults = {
13
+ timeout: 0,
14
+ priority: "high"
15
+ };
16
+ function persistenceDefaults(options) {
17
+ if (options.type === "error") return errorDefaults;
18
+ return options.actionProps ? { timeout: 0 } : void 0;
19
+ }
20
+ function show(title, options = {}) {
21
+ return toastManager.add({
12
22
  title,
23
+ ...persistenceDefaults(options),
24
+ ...options
25
+ });
26
+ }
27
+ function withType(type) {
28
+ return (title, options) => show(title, {
13
29
  type,
14
30
  ...options
15
31
  });
16
32
  }
17
- const toast = Object.assign((title, options) => toastManager.add({
18
- title,
19
- ...options
20
- }), {
33
+ function persistentError(update) {
34
+ return typeof update === "string" ? {
35
+ ...errorDefaults,
36
+ description: update
37
+ } : {
38
+ ...errorDefaults,
39
+ ...update
40
+ };
41
+ }
42
+ const toast = Object.assign(show, {
21
43
  success: withType("success"),
22
44
  error: withType("error"),
23
45
  warning: withType("warning"),
24
46
  info: withType("info"),
25
- message: (title, options) => toastManager.add({
26
- title,
27
- ...options
47
+ message: (title, options) => show(title, options),
48
+ promise: (promise, options) => toastManager.promise(promise, {
49
+ ...options,
50
+ error: (reason) => persistentError(typeof options.error === "function" ? options.error(reason) : options.error)
28
51
  }),
29
- promise: (...args) => toastManager.promise(...args),
30
52
  dismiss: (id) => toastManager.close(id)
31
53
  });
54
+ const statusIcons = {
55
+ success: CircleCheckIcon,
56
+ error: CircleXIcon,
57
+ warning: TriangleAlertIcon,
58
+ info: InfoIcon
59
+ };
60
+ function isToastStatus(type) {
61
+ return type !== void 0 && Object.hasOwn(statusIcons, type);
62
+ }
32
63
  const useToast = Toast.useToastManager;
33
- function ToastList({ rootStyle, indicatorStyle, actionStyle, closeStyle }) {
64
+ function ToastIndicator({ status, style }) {
65
+ if (!status) return null;
66
+ const StatusIcon = statusIcons[status];
67
+ return /* @__PURE__ */ jsx("span", {
68
+ "aria-hidden": "true",
69
+ "data-control-ui": "toast",
70
+ "data-control-family": "popup",
71
+ "data-popup-kind": "toast",
72
+ "data-slot": "indicator",
73
+ style,
74
+ className: "shrink-0",
75
+ children: /* @__PURE__ */ jsx(StatusIcon, { className: "size-4" })
76
+ });
77
+ }
78
+ function ToastText({ entry, statusPrefix }) {
79
+ return /* @__PURE__ */ jsxs("div", {
80
+ className: "flex min-w-0 flex-1 flex-col",
81
+ children: [entry.title ? /* @__PURE__ */ jsxs(Toast.Title, {
82
+ "data-control-ui": "toast",
83
+ "data-control-family": "popup",
84
+ "data-popup-kind": "toast",
85
+ "data-slot": "title",
86
+ children: [statusPrefix, entry.title]
87
+ }) : null, entry.description ? /* @__PURE__ */ jsxs(Toast.Description, {
88
+ "data-control-ui": "toast",
89
+ "data-control-family": "popup",
90
+ "data-popup-kind": "toast",
91
+ "data-slot": "description",
92
+ children: [entry.title ? null : statusPrefix, entry.description]
93
+ }) : null]
94
+ });
95
+ }
96
+ function ToastList({ rootStyle, indicatorStyle, actionStyle, closeStyle, closeLabel, statusLabels }) {
34
97
  const { toasts } = Toast.useToastManager();
35
98
  return toasts.map((entry) => {
99
+ const status = isToastStatus(entry.type) ? entry.type : void 0;
100
+ const statusPrefix = status ? /* @__PURE__ */ jsx("span", {
101
+ className: "sr-only",
102
+ children: `${statusLabels[status]}: `
103
+ }) : null;
36
104
  return /* @__PURE__ */ jsx(Toast.Root, {
37
105
  toast: entry,
38
106
  "data-control-ui": "toast",
@@ -43,7 +111,7 @@ function ToastList({ rootStyle, indicatorStyle, actionStyle, closeStyle }) {
43
111
  style: rootStyle,
44
112
  "data-slot": "root",
45
113
  "data-surface": "floating",
46
- className: cn("[--gap:0.75rem] [--peek:0.75rem] [--scale:calc(max(0,1-(var(--toast-index)*0.1)))] [--shrink:calc(1-var(--scale))] [--height:var(--toast-frontmost-height,var(--toast-height))] [--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))]", "absolute right-0 bottom-0 left-auto z-[calc(var(--z-toast)-var(--toast-index))] h-[var(--height)] w-full select-none data-[expanded]:h-[var(--toast-height)]", "after:absolute after:top-full after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']"),
114
+ className: cn("[--gap:0.75rem] [--peek:0.75rem] [--scale:calc(max(0,1-(var(--toast-index)*0.1)))] [--shrink:calc(1-var(--scale))] [--height:var(--toast-frontmost-height,var(--toast-height))] [--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))]", "absolute end-0 bottom-0 z-[calc(var(--z-toast)-var(--toast-index))] h-[var(--height)] w-full select-none data-[expanded]:h-[var(--toast-height)]", "after:absolute after:top-full after:start-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']"),
47
115
  children: /* @__PURE__ */ jsxs(Toast.Content, {
48
116
  "data-control-ui": "toast",
49
117
  "data-control-family": "popup",
@@ -51,28 +119,13 @@ function ToastList({ rootStyle, indicatorStyle, actionStyle, closeStyle }) {
51
119
  "data-slot": "content",
52
120
  className: "flex items-start",
53
121
  children: [
54
- entry.type && entry.type !== "message" ? /* @__PURE__ */ jsx("span", {
55
- "aria-hidden": "true",
56
- "data-control-ui": "toast",
57
- "data-control-family": "popup",
58
- "data-popup-kind": "toast",
59
- "data-slot": "indicator",
60
- style: indicatorStyle,
61
- className: "shrink-0"
62
- }) : null,
63
- /* @__PURE__ */ jsxs("div", {
64
- className: "flex min-w-0 flex-1 flex-col",
65
- children: [entry.title ? /* @__PURE__ */ jsx(Toast.Title, {
66
- "data-control-ui": "toast",
67
- "data-control-family": "popup",
68
- "data-popup-kind": "toast",
69
- "data-slot": "title"
70
- }) : null, entry.description ? /* @__PURE__ */ jsx(Toast.Description, {
71
- "data-control-ui": "toast",
72
- "data-control-family": "popup",
73
- "data-popup-kind": "toast",
74
- "data-slot": "description"
75
- }) : null]
122
+ /* @__PURE__ */ jsx(ToastIndicator, {
123
+ status,
124
+ style: indicatorStyle
125
+ }),
126
+ /* @__PURE__ */ jsx(ToastText, {
127
+ entry,
128
+ statusPrefix
76
129
  }),
77
130
  entry.actionProps ? /* @__PURE__ */ jsx(Toast.Action, {
78
131
  style: actionStyle,
@@ -86,25 +139,11 @@ function ToastList({ rootStyle, indicatorStyle, actionStyle, closeStyle }) {
86
139
  }) : null,
87
140
  /* @__PURE__ */ jsx(Toast.Close, {
88
141
  style: closeStyle,
89
- "aria-label": "Close",
90
- render: (renderProps) => /* @__PURE__ */ jsx(Button, {
142
+ render: (renderProps) => /* @__PURE__ */ jsx(PopupCloseButton, {
91
143
  ...renderProps,
92
144
  "data-popup-kind": "toast",
93
- "data-popup-part": "close",
94
- variant: "quiet",
95
- size: "xs",
96
- iconOnly: true
97
- }),
98
- children: /* @__PURE__ */ jsx("svg", {
99
- viewBox: "0 0 16 16",
100
- "aria-hidden": "true",
101
- fill: "none",
102
- children: /* @__PURE__ */ jsx("path", {
103
- d: "M4 4 12 12M12 4 4 12",
104
- stroke: "currentColor",
105
- strokeWidth: "1.4",
106
- strokeLinecap: "round"
107
- })
145
+ label: closeLabel,
146
+ variant: "quiet"
108
147
  })
109
148
  })
110
149
  ]
@@ -112,7 +151,7 @@ function ToastList({ rootStyle, indicatorStyle, actionStyle, closeStyle }) {
112
151
  }, entry.id);
113
152
  });
114
153
  }
115
- function Toaster({ className, timeout, limit, rootStyle, indicatorStyle, actionStyle, closeStyle }) {
154
+ function Toaster({ className, timeout, limit, rootStyle, indicatorStyle, actionStyle, closeStyle, closeLabel = "Dismiss notification", successLabel = "Success", errorLabel = "Error", warningLabel = "Warning", infoLabel = "Info" }) {
116
155
  const skin = useSkin();
117
156
  return /* @__PURE__ */ jsx(Toast.Provider, {
118
157
  toastManager,
@@ -125,12 +164,19 @@ function Toaster({ className, timeout, limit, rootStyle, indicatorStyle, actionS
125
164
  "data-slot": "viewport",
126
165
  "data-skin": skin.id,
127
166
  "data-effects": controlEffectsAttribute(skin.effects),
128
- className: cn("fixed right-4 bottom-4 z-(--z-toast) mx-auto w-[calc(100vw-2rem)] sm:right-6 sm:bottom-6 sm:w-[22.5rem]", className),
167
+ className: cn("fixed end-4 bottom-4 z-(--z-toast) mx-auto w-[calc(100vw-2rem)] sm:end-6 sm:bottom-6 sm:w-[22.5rem]", className),
129
168
  children: /* @__PURE__ */ jsx(ToastList, {
130
169
  rootStyle,
131
170
  indicatorStyle,
132
171
  actionStyle,
133
- closeStyle
172
+ closeStyle,
173
+ closeLabel,
174
+ statusLabels: {
175
+ success: successLabel,
176
+ error: errorLabel,
177
+ warning: warningLabel,
178
+ info: infoLabel
179
+ }
134
180
  })
135
181
  }) })
136
182
  });
@@ -1,6 +1,6 @@
1
1
  import { ToolbarKnobStyle } from "../knob-contracts/toolbar-knobs.js";
2
- import { CSSProperties, ComponentProps } from "react";
3
2
  import { Toolbar as Toolbar$1 } from "@base-ui/react/toolbar";
3
+ import { CSSProperties, ComponentProps } from "react";
4
4
  //#region src/ui/toolbar.d.ts
5
5
  export type ToolbarVariant = "default" | "inverse" | "floating";
6
6
  export type ToolbarLinkVariant = "default" | "track";
@@ -15,6 +15,7 @@ export type ToolbarButtonProps = Omit<ComponentProps<"button">, "style"> & {
15
15
  };
16
16
  export type ToolbarLinkProps = Omit<ComponentProps<"a">, "style"> & {
17
17
  variant?: ToolbarLinkVariant;
18
+ active?: boolean;
18
19
  style?: CSSProperties & ToolbarKnobStyle;
19
20
  };
20
21
  export type ToolbarGroupProps = ComponentProps<"div"> & {
@@ -31,7 +32,7 @@ export declare function Toolbar({ orientation, variant, className, children, ...
31
32
  type RefinedToolbarButtonProps = ToolbarButtonProps & Pick<ComponentProps<typeof Toolbar$1.Button>, "render">;
32
33
  export declare function ToolbarButton({ iconOnly, className, ...props }: RefinedToolbarButtonProps): import("react").JSX.Element;
33
34
  type RefinedToolbarLinkProps = ToolbarLinkProps & Pick<ComponentProps<typeof Toolbar$1.Link>, "render">;
34
- export declare function ToolbarLink({ variant, className, ...props }: RefinedToolbarLinkProps): import("react").JSX.Element;
35
+ export declare function ToolbarLink({ variant, active, className, ...props }: RefinedToolbarLinkProps): import("react").JSX.Element;
35
36
  export declare function ToolbarGroup({ className, ...props }: ToolbarGroupProps): import("react").JSX.Element;
36
37
  export declare function ToolbarSeparator({ orientation, className, ...props }: ToolbarSeparatorProps): import("react").JSX.Element;
37
38
  type RefinedToolbarInputProps = ToolbarInputProps & Pick<ComponentProps<typeof Toolbar$1.Input>, "render">;
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
- import { jsx, jsxs } from "react/jsx-runtime";
4
3
  import { Toolbar as Toolbar$1 } from "@base-ui/react/toolbar";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region src/ui/toolbar.tsx
6
6
  function Toolbar({ orientation = "horizontal", variant = "default", className, children, ...props }) {
7
7
  return /* @__PURE__ */ jsxs(Toolbar$1.Root, {
@@ -37,7 +37,7 @@ function ToolbarButton({ iconOnly = false, className, ...props }) {
37
37
  ...props
38
38
  });
39
39
  }
40
- function ToolbarLink({ variant = "default", className, ...props }) {
40
+ function ToolbarLink({ variant = "default", active = false, className, ...props }) {
41
41
  return /* @__PURE__ */ jsx(Toolbar$1.Link, {
42
42
  "data-control-ui": "toolbar",
43
43
  "data-control-family": "toolbar",
@@ -45,6 +45,8 @@ function ToolbarLink({ variant = "default", className, ...props }) {
45
45
  "data-control": "true",
46
46
  "data-size": "sm",
47
47
  "data-variant": variant,
48
+ "data-active": active || void 0,
49
+ "aria-current": active ? "page" : void 0,
48
50
  className: cn("inline-flex shrink-0 items-center justify-center [&_svg]:block [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", variant === "track" && "relative z-[2]", className),
49
51
  ...props
50
52
  });
@@ -1,7 +1,8 @@
1
1
  "use client";
2
- import { controlEffectsAttribute } from "../skin.js";
3
2
  import { cn } from "../lib/cn.js";
3
+ import { controlEffectsAttribute } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
+ import { PopupArrowShape } from "../popup-parts.js";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  import { Tooltip as Tooltip$1 } from "@base-ui/react/tooltip";
7
8
  //#region src/ui/tooltip.tsx
@@ -64,29 +65,11 @@ function TooltipContent({ className, children, side = "top", sideOffset = 8, ali
64
65
  "data-control-family": "popup",
65
66
  "data-popup-kind": "tooltip",
66
67
  "data-slot": "arrow",
67
- className: cn("flex", "data-[side=top]:-bottom-[8px]", "data-[side=bottom]:-top-[8px]", "data-[side=left]:-right-[10px]", "data-[side=right]:-left-[10px]"),
68
- children: /* @__PURE__ */ jsx(TooltipArrowSvg, {})
68
+ className: "flex",
69
+ children: /* @__PURE__ */ jsx(PopupArrowShape, {})
69
70
  }) : null]
70
71
  })
71
72
  }) });
72
73
  }
73
- function TooltipArrowSvg() {
74
- return /* @__PURE__ */ jsx("svg", {
75
- "aria-hidden": "true",
76
- focusable: "false",
77
- width: "12",
78
- height: "8",
79
- viewBox: "0 0 12 8",
80
- fill: "none",
81
- overflow: "visible",
82
- children: /* @__PURE__ */ jsx("path", {
83
- "data-control-ui": "tooltip",
84
- "data-popup-kind": "tooltip",
85
- "data-control-family": "popup",
86
- "data-slot": "arrow-shape",
87
- d: "M0 7L4 2Q6 0 8 2L12 7L12 8L0 8Z"
88
- })
89
- });
90
- }
91
74
  //#endregion
92
75
  export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
package/dist/ui/tree.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { useSkin } from "../skin-provider.js";
4
4
  import { TrackHighlight } from "../extensions/track-highlight.js";
5
- import { Children, createContext, isValidElement, useContext, useRef, useState } from "react";
5
+ import { Children, createContext, isValidElement, useContext, useEffect, useId, useRef, useState } from "react";
6
+ import { useRender } from "@base-ui/react/use-render";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
7
8
  import { ChevronRightIcon } from "lucide-react";
8
- import { useRender } from "@base-ui/react/use-render";
9
9
  import { Collapsible } from "@base-ui/react/collapsible";
10
10
  //#region src/ui/tree.tsx
11
11
  const TYPEAHEAD_TIMEOUT = 500;
@@ -34,8 +34,18 @@ function compareTreeItems(a, b) {
34
34
  if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;
35
35
  return 0;
36
36
  }
37
- function visibleItems(items) {
38
- return [...items.values()].filter((item) => !item.disabled && item.node.checkVisibility()).sort(compareTreeItems);
37
+ function isUnderExpandedAncestors(item, items, expanded) {
38
+ for (let parentValue = item.parentValue; parentValue !== void 0; parentValue = items.get(parentValue)?.parentValue) if (!expanded.has(parentValue)) return false;
39
+ return true;
40
+ }
41
+ function reachableItems(items, expanded) {
42
+ return [...items.values()].filter((item) => isUnderExpandedAncestors(item, items, expanded)).sort(compareTreeItems);
43
+ }
44
+ function resolveTabStop(items, expanded, selected, focusedValue) {
45
+ const reachable = reachableItems(items, expanded);
46
+ if (reachable.some((item) => item.value === focusedValue)) return focusedValue;
47
+ const enabled = reachable.filter((item) => !item.disabled);
48
+ return (enabled.find((item) => selected.has(item.value)) ?? enabled[0])?.value ?? null;
39
49
  }
40
50
  function focusItem(item, ctx) {
41
51
  if (!item) return;
@@ -55,40 +65,56 @@ function handleTypeahead(event, items, index, ctx, typeahead) {
55
65
  const now = Date.now();
56
66
  typeahead.current.query = now - typeahead.current.at > TYPEAHEAD_TIMEOUT ? event.key : typeahead.current.query + event.key;
57
67
  typeahead.current.at = now;
58
- const query = typeahead.current.query.toLowerCase();
59
- const match = [...items.slice(index + 1), ...items.slice(0, index + 1)].find((item) => labelOf(item, ctx).toLowerCase().startsWith(query));
68
+ const typed = typeahead.current.query.toLowerCase();
69
+ const query = [...typed].every((character) => character === typed[0]) ? typed.charAt(0) : typed;
70
+ const searchStart = query.length === 1 ? index + 1 : index;
71
+ const match = [...items.slice(searchStart), ...items.slice(0, searchStart)].find((item) => labelOf(item, ctx).toLowerCase().startsWith(query));
60
72
  if (match) focusItem(match, ctx);
61
73
  }
62
- function registeredItemFromTarget(target, root, itemsByNode) {
63
- let node = target;
64
- while (node && root.contains(node)) {
65
- const item = itemsByNode.get(node);
66
- if (item) return item;
67
- if (node === root) return;
68
- node = node.parentElement;
69
- }
70
- }
71
74
  function getTreeKeyboardState(event, ctx) {
72
75
  if (event.defaultPrevented || !(event.target instanceof HTMLElement)) return null;
73
- const current = ctx.getItemFromTarget(event.target, event.currentTarget);
74
- if (!current) return null;
75
- const items = ctx.getVisibleItems();
76
- const index = items.indexOf(current);
76
+ const item = ctx.getItemFromTarget(event.target, event.currentTarget);
77
+ if (!item) return null;
78
+ const items = ctx.getNavigableItems();
79
+ const index = items.indexOf(item);
77
80
  if (index === -1) return null;
78
81
  return {
79
- value: current.value,
82
+ item,
80
83
  items,
81
84
  index,
82
- isBranch: current.expandable,
83
- isExpanded: ctx.expanded.has(current.value),
84
- level: current.level
85
+ isExpanded: ctx.expanded.has(item.value)
85
86
  };
86
87
  }
88
+ const RTL_ARROW_KEYS = {
89
+ ArrowLeft: "ArrowRight",
90
+ ArrowRight: "ArrowLeft"
91
+ };
92
+ function handleInlineArrow(key, { item, items, index, isExpanded }, ctx) {
93
+ const isBranch = item.expandable && !item.disabled;
94
+ if (key === "ArrowRight") {
95
+ if (!isBranch) return;
96
+ if (isExpanded) focusItem(items[index + 1], ctx);
97
+ else ctx.toggleExpanded(item.value, true, "keyboard");
98
+ return;
99
+ }
100
+ if (isBranch && isExpanded) ctx.toggleExpanded(item.value, false, "keyboard");
101
+ else focusParentItem(items, index, item.level, ctx);
102
+ }
103
+ function activateItem(key, item, ctx) {
104
+ if (item.disabled) return;
105
+ if (key === " ") {
106
+ ctx.select(item.value, "keyboard", ctx.selectionMode === "multiple");
107
+ return;
108
+ }
109
+ ctx.select(item.value, "keyboard", false);
110
+ if (item.expandable) ctx.toggleExpanded(item.value, void 0, "keyboard");
111
+ }
87
112
  function handleTreeKeyDown(event, ctx, typeahead) {
88
113
  const state = getTreeKeyboardState(event, ctx);
89
114
  if (!state) return;
90
- const { value, items, index, isBranch, isExpanded, level } = state;
91
- switch (event.key) {
115
+ const { item, items, index } = state;
116
+ const key = getComputedStyle(event.currentTarget).direction === "rtl" && RTL_ARROW_KEYS[event.key] || event.key;
117
+ switch (key) {
92
118
  case "ArrowDown":
93
119
  event.preventDefault();
94
120
  focusItem(items[index + 1], ctx);
@@ -98,14 +124,9 @@ function handleTreeKeyDown(event, ctx, typeahead) {
98
124
  focusItem(items[index - 1], ctx);
99
125
  break;
100
126
  case "ArrowRight":
101
- event.preventDefault();
102
- if (isBranch && !isExpanded) ctx.toggleExpanded(value, true, "keyboard");
103
- else if (isBranch && isExpanded) focusItem(items[index + 1], ctx);
104
- break;
105
127
  case "ArrowLeft":
106
128
  event.preventDefault();
107
- if (isBranch && isExpanded) ctx.toggleExpanded(value, false, "keyboard");
108
- else focusParentItem(items, index, level, ctx);
129
+ handleInlineArrow(key, state, ctx);
109
130
  break;
110
131
  case "Home":
111
132
  event.preventDefault();
@@ -116,13 +137,9 @@ function handleTreeKeyDown(event, ctx, typeahead) {
116
137
  focusItem(items[items.length - 1], ctx);
117
138
  break;
118
139
  case "Enter":
119
- event.preventDefault();
120
- ctx.select(value, "keyboard", false);
121
- if (isBranch) ctx.toggleExpanded(value, void 0, "keyboard");
122
- break;
123
140
  case " ":
124
141
  event.preventDefault();
125
- ctx.select(value, "keyboard", ctx.selectionMode === "multiple");
142
+ activateItem(key, item, ctx);
126
143
  break;
127
144
  default: handleTypeahead(event, items, index, ctx, typeahead);
128
145
  }
@@ -132,31 +149,23 @@ function Tree({ selectionMode = "single", indicator, value, defaultValue, onValu
132
149
  const [selectedState, setSelectedState] = useState(() => new Set(defaultValue));
133
150
  const [expandedState, setExpandedState] = useState(() => new Set(defaultExpandedValue));
134
151
  const [focusedValue, setFocusedValue] = useState(null);
152
+ const [items, setItems] = useState(() => /* @__PURE__ */ new Map());
153
+ const [registerItem] = useState(() => (item) => {
154
+ setItems((current) => new Map(current).set(item.value, item));
155
+ return () => setItems((current) => {
156
+ if (current.get(item.value) !== item) return current;
157
+ const next = new Map(current);
158
+ next.delete(item.value);
159
+ return next;
160
+ });
161
+ });
135
162
  const selected = value ? new Set(value) : selectedState;
136
163
  const expanded = expandedValue ? new Set(expandedValue) : expandedState;
137
164
  const typeahead = useRef({
138
165
  query: "",
139
166
  at: 0
140
167
  });
141
- const itemsRef = useRef(/* @__PURE__ */ new Map());
142
- const itemsByNodeRef = useRef(/* @__PURE__ */ new WeakMap());
143
168
  const labelsRef = useRef(/* @__PURE__ */ new Map());
144
- const seededFocusRef = useRef(false);
145
- const registerItem = (item) => {
146
- const previous = itemsRef.current.get(item.value);
147
- if (previous) itemsByNodeRef.current.delete(previous.node);
148
- itemsRef.current.set(item.value, item);
149
- itemsByNodeRef.current.set(item.node, item);
150
- if (!seededFocusRef.current && !item.disabled) {
151
- seededFocusRef.current = true;
152
- setFocusedValue(item.value);
153
- }
154
- return () => {
155
- if (itemsRef.current.get(item.value) !== item) return;
156
- itemsRef.current.delete(item.value);
157
- itemsByNodeRef.current.delete(item.node);
158
- };
159
- };
160
169
  const registerLabel = (itemValue, node) => {
161
170
  if (!node) return;
162
171
  labelsRef.current.set(itemValue, node);
@@ -164,10 +173,15 @@ function Tree({ selectionMode = "single", indicator, value, defaultValue, onValu
164
173
  if (labelsRef.current.get(itemValue) === node) labelsRef.current.delete(itemValue);
165
174
  };
166
175
  };
167
- const getVisibleItems = () => visibleItems(itemsRef.current);
168
- const getItemFromTarget = (target, root) => registeredItemFromTarget(target, root, itemsByNodeRef.current);
176
+ const getNavigableItems = () => reachableItems(items, expanded).filter((item) => item.node.checkVisibility());
177
+ const getItemFromTarget = (target, root) => {
178
+ const node = target.closest("[data-control-family=\"tree\"][data-slot=\"item\"]");
179
+ if (!(node instanceof HTMLLIElement) || !root.contains(node)) return;
180
+ const item = items.get(node.dataset.value ?? "");
181
+ return item?.node === node ? item : void 0;
182
+ };
169
183
  const getLabel = (itemValue) => labelsRef.current.get(itemValue)?.textContent ?? "";
170
- const getItem = (itemValue) => itemsRef.current.get(itemValue);
184
+ const getItem = (itemValue) => items.get(itemValue);
171
185
  const select = (itemValue, reason, toggle) => {
172
186
  if (selectionMode === "none") return;
173
187
  let next;
@@ -201,10 +215,10 @@ function Tree({ selectionMode = "single", indicator, value, defaultValue, onValu
201
215
  indicator: resolvedIndicator,
202
216
  selected,
203
217
  expanded,
204
- focusedValue,
218
+ tabStopValue: resolveTabStop(items, expanded, selected, focusedValue),
205
219
  registerItem,
206
220
  registerLabel,
207
- getVisibleItems,
221
+ getNavigableItems,
208
222
  getItemFromTarget,
209
223
  getLabel,
210
224
  getItem,
@@ -252,33 +266,48 @@ function Tree({ selectionMode = "single", indicator, value, defaultValue, onValu
252
266
  }
253
267
  function TreeItem({ value, disabled = false, label, className, style, children, ref, ...props }) {
254
268
  const tree = useTree();
255
- const level = (useContext(TreeItemContext)?.level ?? 0) + 1;
269
+ const parent = useContext(TreeItemContext);
270
+ const parentValue = parent?.value;
271
+ const level = (parent?.level ?? 0) + 1;
256
272
  const expandable = Children.toArray(children).some((child) => isValidElement(child) && child.type === TreeItemContent);
273
+ const labelId = useId();
274
+ const [labelled, setLabelled] = useState(false);
275
+ const nodeRef = useRef(null);
276
+ const { registerItem } = tree;
277
+ useEffect(() => {
278
+ const node = nodeRef.current;
279
+ if (!node) return;
280
+ return registerItem({
281
+ value,
282
+ parentValue,
283
+ node,
284
+ level,
285
+ disabled,
286
+ expandable,
287
+ label
288
+ });
289
+ }, [
290
+ registerItem,
291
+ value,
292
+ parentValue,
293
+ level,
294
+ disabled,
295
+ expandable,
296
+ label
297
+ ]);
257
298
  const expanded = expandable && tree.expanded.has(value);
258
299
  const selected = tree.selected.has(value);
259
300
  const itemContext = {
260
301
  value,
261
302
  level,
262
303
  disabled,
263
- expandable
304
+ expandable,
305
+ labelId,
306
+ setLabelled
264
307
  };
265
308
  const itemRef = (node) => {
266
- const itemNode = node instanceof HTMLLIElement ? node : null;
267
- const refCleanup = setRef(ref, itemNode);
268
- if (!itemNode) return;
269
- const unregister = tree.registerItem({
270
- value,
271
- node: itemNode,
272
- level,
273
- disabled,
274
- expandable,
275
- label
276
- });
277
- return () => {
278
- unregister();
279
- if (typeof refCleanup === "function") refCleanup();
280
- else setRef(ref, null);
281
- };
309
+ nodeRef.current = node instanceof HTMLLIElement ? node : null;
310
+ return setRef(ref, nodeRef.current);
282
311
  };
283
312
  const baseClass = cn("list-none", className);
284
313
  const itemStyle = {
@@ -286,6 +315,7 @@ function TreeItem({ value, disabled = false, label, className, style, children,
286
315
  ...style
287
316
  };
288
317
  const shared = {
318
+ "aria-labelledby": labelled ? labelId : void 0,
289
319
  ...props,
290
320
  "data-control-ui": "tree",
291
321
  "data-control-family": "tree",
@@ -298,7 +328,7 @@ function TreeItem({ value, disabled = false, label, className, style, children,
298
328
  "aria-level": level,
299
329
  "aria-selected": tree.selectionMode === "none" ? void 0 : selected,
300
330
  "aria-disabled": disabled || void 0,
301
- tabIndex: tree.focusedValue === value ? 0 : -1,
331
+ tabIndex: tree.tabStopValue === value ? 0 : -1,
302
332
  style: itemStyle
303
333
  };
304
334
  if (!expandable) return /* @__PURE__ */ jsx(TreeItemContext.Provider, {
@@ -321,7 +351,7 @@ function TreeItem({ value, disabled = false, label, className, style, children,
321
351
  ...shared,
322
352
  role: "treeitem",
323
353
  "aria-expanded": expanded,
324
- tabIndex: tree.focusedValue === value ? 0 : -1,
354
+ tabIndex: shared.tabIndex,
325
355
  "data-state": expanded ? "open" : "closed",
326
356
  className: cn(renderProps.className, baseClass)
327
357
  }),
@@ -353,7 +383,7 @@ function TreeItemTrigger({ className, children, onClick, render, ...props }) {
353
383
  if (item.expandable) tree.toggleExpanded(item.value);
354
384
  focusItem(tree.getItem(item.value), tree);
355
385
  },
356
- className: cn("flex cursor-pointer select-none items-center", "pl-[calc(var(--\\_tree-level,1)*1.25rem)]", "aria-disabled:pointer-events-none", className),
386
+ className: cn("flex cursor-pointer select-none items-center", "aria-disabled:pointer-events-none", className),
357
387
  children
358
388
  }
359
389
  });
@@ -378,12 +408,20 @@ function TreeItemIndicator({ className, children, ...props }) {
378
408
  "aria-hidden": true,
379
409
  className: cn("inline-flex shrink-0 items-center justify-center", className),
380
410
  ...props,
381
- children: children ?? /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-4" })
411
+ children: children ?? /* @__PURE__ */ jsx(ChevronRightIcon, {
412
+ "data-icon-dir": "inline",
413
+ className: "size-4"
414
+ })
382
415
  });
383
416
  }
384
417
  function TreeItemLabel({ className, children, render, ref, ...props }) {
385
418
  const tree = useTree();
386
419
  const item = useTreeItem();
420
+ const { setLabelled } = item;
421
+ useEffect(() => {
422
+ setLabelled(true);
423
+ return () => setLabelled(false);
424
+ }, [setLabelled]);
387
425
  const labelRef = (node) => {
388
426
  const unregister = tree.registerLabel(item.value, node);
389
427
  const cleanup = setRef(ref, node);
@@ -398,8 +436,10 @@ function TreeItemLabel({ className, children, render, ref, ...props }) {
398
436
  defaultTagName: "span",
399
437
  render,
400
438
  props: {
439
+ title: typeof children === "string" ? children : void 0,
401
440
  ...props,
402
441
  ref: labelRef,
442
+ id: item.labelId,
403
443
  "data-control-ui": "tree",
404
444
  "data-control-family": "tree",
405
445
  "data-slot": "item-label",