@ctrl-ui/react 0.8.7 → 0.8.9

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 (341) hide show
  1. package/dist/action-bar.d.ts +15 -7
  2. package/dist/action-bar.js +39 -19
  3. package/dist/activity.js +7 -15
  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.d.ts +5 -1
  9. package/dist/blocks/design-canvas.js +198 -92
  10. package/dist/blocks/discussion.d.ts +28 -0
  11. package/dist/blocks/discussion.js +67 -0
  12. package/dist/blocks/file-explorer-data.d.ts +1 -0
  13. package/dist/blocks/file-explorer-data.js +4 -1
  14. package/dist/blocks/file-explorer.d.ts +9 -1
  15. package/dist/blocks/file-explorer.js +461 -192
  16. package/dist/blocks/settings.d.ts +2 -1
  17. package/dist/blocks/settings.js +101 -79
  18. package/dist/blocks/team-chat.d.ts +12 -0
  19. package/dist/blocks/team-chat.js +26 -0
  20. package/dist/blocks/theme-toggle.js +23 -31
  21. package/dist/chat-composer-attachment.d.ts +4 -1
  22. package/dist/chat-composer-attachment.js +35 -10
  23. package/dist/chat-composer-editor/extensions/mention.d.ts +5 -1
  24. package/dist/chat-composer-editor/extensions/mention.js +59 -28
  25. package/dist/chat-composer-editor/ghost.js +2 -4
  26. package/dist/chat-composer-editor/types.d.ts +5 -0
  27. package/dist/chat-composer-editor.d.ts +1 -1
  28. package/dist/chat-composer-editor.js +57 -30
  29. package/dist/chat-composer.d.ts +13 -32
  30. package/dist/chat-composer.js +52 -12
  31. package/dist/chat-layout.d.ts +19 -0
  32. package/dist/chat-layout.js +91 -40
  33. package/dist/chat-message/social.d.ts +26 -0
  34. package/dist/chat-message/social.js +122 -0
  35. package/dist/chat-message.d.ts +28 -29
  36. package/dist/chat-message.js +106 -25
  37. package/dist/context.d.ts +2 -2
  38. package/dist/context.js +38 -23
  39. package/dist/dynamic-notification-canvas-loop.d.ts +24 -0
  40. package/dist/dynamic-notification-canvas-loop.js +113 -0
  41. package/dist/dynamic-notification-glass.js +23 -114
  42. package/dist/dynamic-notification-liquid.js +14 -118
  43. package/dist/dynamic-notification.d.ts +16 -8
  44. package/dist/dynamic-notification.js +85 -22
  45. package/dist/email/email-brand.d.ts +7 -4
  46. package/dist/email/email-brand.js +9 -6
  47. package/dist/email/email-code.d.ts +3 -2
  48. package/dist/email/email-code.js +11 -9
  49. package/dist/email/email-footer.d.ts +5 -3
  50. package/dist/email/email-footer.js +11 -5
  51. package/dist/email/email.d.ts +31 -9
  52. package/dist/email/email.js +50 -26
  53. package/dist/email/templates.d.ts +10 -9
  54. package/dist/email/templates.js +102 -38
  55. package/dist/email/theme.js +1 -1
  56. package/dist/environment-variables.d.ts +4 -2
  57. package/dist/environment-variables.js +52 -32
  58. package/dist/filter-bar/editor.js +20 -8
  59. package/dist/filter-bar/model.d.ts +1 -0
  60. package/dist/filter-bar/model.js +10 -1
  61. package/dist/filter-bar/motion.js +4 -4
  62. package/dist/filter-bar/parts.js +19 -8
  63. package/dist/filter-bar/types.d.ts +1 -0
  64. package/dist/filter-bar.js +19 -13
  65. package/dist/hooks/use-audio-recorder.js +12 -6
  66. package/dist/hooks/use-chat-composer.d.ts +19 -7
  67. package/dist/hooks/use-chat-composer.js +32 -5
  68. package/dist/hooks/use-chat-message.d.ts +28 -2
  69. package/dist/hooks/use-chat-message.js +41 -10
  70. package/dist/hooks/use-chat-thread-scroll.js +43 -7
  71. package/dist/hooks/use-color-area.d.ts +3 -3
  72. package/dist/hooks/use-color-area.js +13 -12
  73. package/dist/hooks/use-copy-to-clipboard.d.ts +2 -1
  74. package/dist/hooks/use-copy-to-clipboard.js +20 -20
  75. package/dist/hooks/use-dynamic-notification.d.ts +6 -2
  76. package/dist/hooks/use-dynamic-notification.js +26 -10
  77. package/dist/hooks/use-mobile.d.ts +3 -2
  78. package/dist/hooks/use-mobile.js +8 -4
  79. package/dist/hooks/use-textarea-trigger-menu.js +2 -1
  80. package/dist/hooks/use-trigger-menu.d.ts +54 -0
  81. package/dist/hooks/use-trigger-menu.js +102 -47
  82. package/dist/hooks/use-user-ask.d.ts +2 -0
  83. package/dist/hooks/use-user-ask.js +35 -41
  84. package/dist/inline-attachment.d.ts +6 -2
  85. package/dist/inline-attachment.js +36 -15
  86. package/dist/inline-citation.d.ts +12 -5
  87. package/dist/inline-citation.js +25 -16
  88. package/dist/knob-contracts/audio-recorder-knobs.d.ts +1 -1
  89. package/dist/knob-contracts/audio-recorder-knobs.js +2 -1
  90. package/dist/knob-contracts/audio-visualizer-knobs.d.ts +1 -1
  91. package/dist/knob-contracts/audio-visualizer-knobs.js +2 -1
  92. package/dist/knob-contracts/avatar-knobs.d.ts +1 -1
  93. package/dist/knob-contracts/avatar-knobs.js +6 -1
  94. package/dist/knob-contracts/badge-knobs.d.ts +1 -1
  95. package/dist/knob-contracts/badge-knobs.js +2 -1
  96. package/dist/knob-contracts/breadcrumb-knobs.d.ts +4 -0
  97. package/dist/knob-contracts/breadcrumb-knobs.js +8 -0
  98. package/dist/knob-contracts/button-knobs.d.ts +1 -1
  99. package/dist/knob-contracts/button-knobs.js +1 -0
  100. package/dist/knob-contracts/chat-message-knobs.d.ts +1 -1
  101. package/dist/knob-contracts/chat-message-knobs.js +8 -1
  102. package/dist/knob-contracts/code-diff-knobs.d.ts +1 -1
  103. package/dist/knob-contracts/code-diff-knobs.js +2 -0
  104. package/dist/knob-contracts/code-knobs.d.ts +1 -1
  105. package/dist/knob-contracts/code-knobs.js +2 -1
  106. package/dist/knob-contracts/dynamic-notification-knobs.d.ts +1 -1
  107. package/dist/knob-contracts/dynamic-notification-knobs.js +2 -0
  108. package/dist/knob-contracts/emoji-picker-knobs.d.ts +4 -0
  109. package/dist/knob-contracts/emoji-picker-knobs.js +10 -0
  110. package/dist/knob-contracts/field-knobs.d.ts +1 -1
  111. package/dist/knob-contracts/field-knobs.js +5 -1
  112. package/dist/knob-contracts/filter-bar-knobs.d.ts +1 -1
  113. package/dist/knob-contracts/filter-bar-knobs.js +1 -2
  114. package/dist/knob-contracts/markdown-knobs.d.ts +1 -1
  115. package/dist/knob-contracts/markdown-knobs.js +3 -1
  116. package/dist/knob-contracts/page-layout-knobs.d.ts +1 -1
  117. package/dist/knob-contracts/page-layout-knobs.js +2 -1
  118. package/dist/knob-contracts/popup-knobs.d.ts +1 -1
  119. package/dist/knob-contracts/popup-knobs.js +27 -0
  120. package/dist/knob-contracts/range-knobs.d.ts +1 -1
  121. package/dist/knob-contracts/range-knobs.js +1 -0
  122. package/dist/knob-contracts/rich-tooltip-knobs.d.ts +1 -1
  123. package/dist/knob-contracts/rich-tooltip-knobs.js +2 -1
  124. package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
  125. package/dist/knob-contracts/sidebar-knobs.js +27 -2
  126. package/dist/knob-contracts/spinner-knobs.d.ts +1 -1
  127. package/dist/knob-contracts/spinner-knobs.js +1 -1
  128. package/dist/knob-contracts/stepper-knobs.d.ts +1 -1
  129. package/dist/knob-contracts/stepper-knobs.js +4 -1
  130. package/dist/knob-contracts/switch-knobs.d.ts +1 -1
  131. package/dist/knob-contracts/switch-knobs.js +5 -0
  132. package/dist/knob-contracts/table-of-contents-knobs.d.ts +1 -1
  133. package/dist/knob-contracts/table-of-contents-knobs.js +2 -1
  134. package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
  135. package/dist/knob-contracts/tabs-knobs.js +1 -0
  136. package/dist/knob-contracts/tree-knobs.d.ts +1 -1
  137. package/dist/knob-contracts/tree-knobs.js +2 -1
  138. package/dist/lib/cn.js +0 -4
  139. package/dist/lib/contrast.d.ts +1 -0
  140. package/dist/lib/contrast.js +17 -5
  141. package/dist/lib/dropzone-validation.js +41 -28
  142. package/dist/lib/env-file.d.ts +1 -1
  143. package/dist/lib/env-file.js +5 -5
  144. package/dist/lib/motion.d.ts +4 -0
  145. package/dist/lib/motion.js +15 -0
  146. package/dist/lib/trigger-detect.js +6 -8
  147. package/dist/markdown-block.js +2 -1
  148. package/dist/popup-parts.d.ts +8 -0
  149. package/dist/popup-parts.js +43 -0
  150. package/dist/source-badge.js +3 -1
  151. package/dist/styles/code.css +0 -2
  152. package/dist/styles/effects.css +6 -32
  153. package/dist/styles/index.css +10 -5
  154. package/dist/styles/recipes/accordion.css +9 -6
  155. package/dist/styles/recipes/action-bar.css +0 -15
  156. package/dist/styles/recipes/activity.css +5 -11
  157. package/dist/styles/recipes/alert.css +4 -4
  158. package/dist/styles/recipes/audio-recorder.css +15 -3
  159. package/dist/styles/recipes/audio-visualizer.css +13 -2
  160. package/dist/styles/recipes/avatar.css +78 -1
  161. package/dist/styles/recipes/badge.css +14 -4
  162. package/dist/styles/recipes/breadcrumb.css +77 -0
  163. package/dist/styles/recipes/button-group.css +5 -7
  164. package/dist/styles/recipes/button-states.css +66 -0
  165. package/dist/styles/recipes/button.css +24 -83
  166. package/dist/styles/recipes/calendar.css +17 -23
  167. package/dist/styles/recipes/card.css +2 -2
  168. package/dist/styles/recipes/chat-composer-attachment.css +9 -3
  169. package/dist/styles/recipes/chat-composer.css +7 -10
  170. package/dist/styles/recipes/chat-layout.css +35 -0
  171. package/dist/styles/recipes/chat-message-social.css +123 -0
  172. package/dist/styles/recipes/chat-message.css +157 -37
  173. package/dist/styles/recipes/choice.css +18 -21
  174. package/dist/styles/recipes/code-diff.css +46 -33
  175. package/dist/styles/recipes/code.css +36 -16
  176. package/dist/styles/recipes/collapsible.css +7 -2
  177. package/dist/styles/recipes/color-picker-details.css +3 -4
  178. package/dist/styles/recipes/color-picker-values.css +9 -8
  179. package/dist/styles/recipes/color-picker.css +11 -2
  180. package/dist/styles/recipes/context.css +3 -3
  181. package/dist/styles/recipes/dockable-panel.css +7 -4
  182. package/dist/styles/recipes/dropzone.css +7 -8
  183. package/dist/styles/recipes/dynamic-notification-input.css +8 -5
  184. package/dist/styles/recipes/dynamic-notification-motion.css +12 -21
  185. package/dist/styles/recipes/dynamic-notification.css +40 -26
  186. package/dist/styles/recipes/emoji-picker.css +132 -0
  187. package/dist/styles/recipes/empty.css +5 -4
  188. package/dist/styles/recipes/environment-variables.css +4 -15
  189. package/dist/styles/recipes/field-parts.css +30 -24
  190. package/dist/styles/recipes/field.css +61 -26
  191. package/dist/styles/recipes/filter-bar.css +18 -31
  192. package/dist/styles/recipes/gradient-editor.css +18 -7
  193. package/dist/styles/recipes/infinite-canvas.css +0 -1
  194. package/dist/styles/recipes/inline-attachment.css +3 -3
  195. package/dist/styles/recipes/inline-citation.css +10 -4
  196. package/dist/styles/recipes/item.css +23 -9
  197. package/dist/styles/recipes/markdown-block.css +5 -4
  198. package/dist/styles/recipes/markdown.css +68 -26
  199. package/dist/styles/recipes/morphing-panel.css +8 -8
  200. package/dist/styles/recipes/page-layout.css +20 -12
  201. package/dist/styles/recipes/pagination.css +4 -5
  202. package/dist/styles/recipes/phone-input.css +12 -8
  203. package/dist/styles/recipes/popup-controls.css +166 -33
  204. package/dist/styles/recipes/popup-drawer.css +3 -14
  205. package/dist/styles/recipes/popup-lists.css +128 -27
  206. package/dist/styles/recipes/popup-motion.css +4 -45
  207. package/dist/styles/recipes/popup-toast.css +55 -26
  208. package/dist/styles/recipes/popup.css +139 -141
  209. package/dist/styles/recipes/range.css +54 -18
  210. package/dist/styles/recipes/resizable.css +2 -7
  211. package/dist/styles/recipes/rich-tooltip.css +24 -34
  212. package/dist/styles/recipes/scroll-area.css +8 -1
  213. package/dist/styles/recipes/sidebar-menu.css +166 -34
  214. package/dist/styles/recipes/sidebar-parts.css +192 -0
  215. package/dist/styles/recipes/sidebar.css +188 -128
  216. package/dist/styles/recipes/source-badge.css +1 -1
  217. package/dist/styles/recipes/spinner.css +8 -1
  218. package/dist/styles/recipes/stepper.css +31 -13
  219. package/dist/styles/recipes/switch.css +68 -23
  220. package/dist/styles/recipes/table-of-contents.css +9 -1
  221. package/dist/styles/recipes/table.css +14 -10
  222. package/dist/styles/recipes/tabs.css +17 -12
  223. package/dist/styles/recipes/task-list.css +7 -14
  224. package/dist/styles/recipes/thread-rail.css +42 -19
  225. package/dist/styles/recipes/timeline.css +2 -0
  226. package/dist/styles/recipes/toolbar.css +27 -25
  227. package/dist/styles/recipes/track-highlight.css +10 -7
  228. package/dist/styles/recipes/tree.css +31 -4
  229. package/dist/styles/recipes/user-ask-details.css +0 -1
  230. package/dist/styles/recipes/user-ask.css +4 -4
  231. package/dist/styles/theme.css +37 -16
  232. package/dist/task-list.d.ts +4 -1
  233. package/dist/task-list.js +24 -8
  234. package/dist/thread-rail.d.ts +1 -1
  235. package/dist/thread-rail.js +61 -19
  236. package/dist/transcript-divider.js +2 -1
  237. package/dist/ui/alert.d.ts +3 -1
  238. package/dist/ui/alert.js +14 -8
  239. package/dist/ui/autocomplete.js +3 -1
  240. package/dist/ui/avatar.d.ts +8 -0
  241. package/dist/ui/avatar.js +23 -3
  242. package/dist/ui/badge.js +1 -1
  243. package/dist/ui/breadcrumb.d.ts +27 -0
  244. package/dist/ui/breadcrumb.js +88 -0
  245. package/dist/ui/button.d.ts +2 -1
  246. package/dist/ui/button.js +1 -1
  247. package/dist/ui/calendar.js +2 -2
  248. package/dist/ui/card.d.ts +3 -1
  249. package/dist/ui/card.js +12 -7
  250. package/dist/ui/checkbox.d.ts +2 -0
  251. package/dist/ui/code-diff.d.ts +1 -1
  252. package/dist/ui/code-diff.js +71 -27
  253. package/dist/ui/code.d.ts +5 -3
  254. package/dist/ui/code.js +26 -27
  255. package/dist/ui/color-picker.d.ts +5 -2
  256. package/dist/ui/color-picker.js +84 -36
  257. package/dist/ui/combobox-disabled-selection.d.ts +1 -2
  258. package/dist/ui/combobox-disabled-selection.js +7 -9
  259. package/dist/ui/combobox.js +13 -5
  260. package/dist/ui/command.d.ts +8 -2
  261. package/dist/ui/command.js +30 -14
  262. package/dist/ui/context-menu.js +3 -0
  263. package/dist/ui/dialog.d.ts +2 -1
  264. package/dist/ui/dialog.js +3 -22
  265. package/dist/ui/dockable-panel-geometry.d.ts +1 -0
  266. package/dist/ui/dockable-panel-geometry.js +7 -1
  267. package/dist/ui/dockable-panel.d.ts +9 -6
  268. package/dist/ui/dockable-panel.js +63 -23
  269. package/dist/ui/dropdown-menu.js +4 -3
  270. package/dist/ui/dropzone.d.ts +2 -2
  271. package/dist/ui/dropzone.js +75 -16
  272. package/dist/ui/emoji-picker.d.ts +18 -0
  273. package/dist/ui/emoji-picker.js +136 -0
  274. package/dist/ui/empty.d.ts +3 -1
  275. package/dist/ui/empty.js +12 -7
  276. package/dist/ui/gradient-editor.d.ts +1 -1
  277. package/dist/ui/gradient-editor.js +37 -21
  278. package/dist/ui/infinite-canvas.d.ts +3 -2
  279. package/dist/ui/infinite-canvas.js +63 -19
  280. package/dist/ui/input-group.d.ts +0 -3
  281. package/dist/ui/input-group.js +3 -13
  282. package/dist/ui/input-otp.d.ts +1 -1
  283. package/dist/ui/input-otp.js +32 -15
  284. package/dist/ui/input.js +4 -4
  285. package/dist/ui/item.d.ts +2 -1
  286. package/dist/ui/item.js +11 -7
  287. package/dist/ui/live-status.d.ts +7 -0
  288. package/dist/ui/live-status.js +17 -0
  289. package/dist/ui/markdown-elements.d.ts +6 -0
  290. package/dist/ui/markdown-elements.js +31 -1
  291. package/dist/ui/menubar.js +2 -2
  292. package/dist/ui/morphing-panel.js +2 -1
  293. package/dist/ui/native-select.d.ts +1 -1
  294. package/dist/ui/native-select.js +24 -11
  295. package/dist/ui/page-layout.d.ts +2 -2
  296. package/dist/ui/page-layout.js +2 -4
  297. package/dist/ui/pagination.d.ts +7 -5
  298. package/dist/ui/pagination.js +19 -13
  299. package/dist/ui/phone-input.d.ts +1 -0
  300. package/dist/ui/phone-input.js +56 -58
  301. package/dist/ui/radio-group.d.ts +2 -0
  302. package/dist/ui/resizable.d.ts +3 -2
  303. package/dist/ui/resizable.js +5 -3
  304. package/dist/ui/responsive-dialog.d.ts +1 -1
  305. package/dist/ui/responsive-dialog.js +4 -21
  306. package/dist/ui/rich-tooltip.d.ts +10 -4
  307. package/dist/ui/rich-tooltip.js +81 -33
  308. package/dist/ui/scroll-area.d.ts +1 -1
  309. package/dist/ui/scroll-area.js +43 -28
  310. package/dist/ui/sidebar-menu.d.ts +5 -1
  311. package/dist/ui/sidebar-menu.js +34 -34
  312. package/dist/ui/sidebar-provider.d.ts +10 -1
  313. package/dist/ui/sidebar-provider.js +38 -13
  314. package/dist/ui/sidebar-resize-rail.js +7 -3
  315. package/dist/ui/sidebar.d.ts +11 -6
  316. package/dist/ui/sidebar.js +48 -24
  317. package/dist/ui/slider.js +31 -22
  318. package/dist/ui/spinner.d.ts +3 -2
  319. package/dist/ui/spinner.js +3 -2
  320. package/dist/ui/stepper.d.ts +8 -1
  321. package/dist/ui/stepper.js +74 -25
  322. package/dist/ui/switch.d.ts +1 -0
  323. package/dist/ui/table-of-contents.d.ts +2 -2
  324. package/dist/ui/table-of-contents.js +20 -8
  325. package/dist/ui/table.js +4 -2
  326. package/dist/ui/textarea.d.ts +1 -1
  327. package/dist/ui/textarea.js +20 -9
  328. package/dist/ui/timeline.d.ts +3 -1
  329. package/dist/ui/timeline.js +17 -9
  330. package/dist/ui/toast.d.ts +11 -5
  331. package/dist/ui/toast.js +100 -54
  332. package/dist/ui/toolbar.d.ts +2 -1
  333. package/dist/ui/toolbar.js +3 -1
  334. package/dist/ui/tooltip.js +3 -20
  335. package/dist/ui/tree.js +120 -80
  336. package/dist/ui/trigger-menu.js +1 -0
  337. package/dist/user-ask.d.ts +10 -5
  338. package/dist/user-ask.js +31 -11
  339. package/package.json +5 -1
  340. package/dist/hooks/use-keyboard-navigation.d.ts +0 -3
  341. package/dist/hooks/use-keyboard-navigation.js +0 -22
@@ -1,11 +1,12 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { Button } from "./button.js";
4
+ import { LiveStatus } from "./live-status.js";
4
5
  import { ScrollArea } from "./scroll-area.js";
5
6
  import { useIsMobile } from "../hooks/use-mobile.js";
6
7
  import { Drawer, DrawerBody, DrawerContent } from "./drawer.js";
7
- import { clampDockablePanelPosition, dockablePanelSideAt, oppositeDockablePanelSide } from "./dockable-panel-geometry.js";
8
- import { createContext, use, useRef, useState } from "react";
8
+ import { clampDockablePanelPosition, dockablePanelSideAt, dockablePanelSideForKey, oppositeDockablePanelSide } from "./dockable-panel-geometry.js";
9
+ import { createContext, use, useEffect, useEffectEvent, useId, useRef, useState } from "react";
9
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
11
  //#region src/ui/dockable-panel.tsx
11
12
  const DockablePanelContext = createContext(null);
@@ -23,24 +24,43 @@ function useControllableState(controlled, defaultValue, onChange) {
23
24
  }
24
25
  return [value, setValue];
25
26
  }
26
- function DockablePanel({ open: openProp, defaultOpen = true, onOpenChange, placement: placementProp, defaultPlacement = "right", onPlacementChange, className, style, children, "aria-label": ariaLabel = "Dockable panel", ...props }) {
27
+ function DockablePanel({ open: openProp, defaultOpen = true, onOpenChange, placement: placementProp, defaultPlacement = "right", onPlacementChange, finalFocus, className, style, children, "aria-label": ariaLabel = "Dockable panel", ...props }) {
27
28
  const [open, setOpen] = useControllableState(openProp, defaultOpen, onOpenChange);
28
29
  const [placement, setPlacementState] = useControllableState(placementProp, defaultPlacement, onPlacementChange);
29
30
  const [dragging, setDragging] = useState(false);
30
31
  const [potentialDock, setPotentialDock] = useState(placement);
31
32
  const [dragPosition, setDragPosition] = useState(null);
32
33
  const [dragHeight, setDragHeight] = useState(0);
34
+ const [announcement, setAnnouncement] = useState("");
33
35
  const isMobile = useIsMobile();
36
+ const titleId = useId();
34
37
  const panelRef = useRef(null);
35
38
  const potentialDockRef = useRef(placement);
36
39
  const dragRef = useRef(null);
40
+ const returnFocusRef = useRef(null);
41
+ const restoreFocusRef = useRef(false);
42
+ const restoreFocus = useEffectEvent(() => {
43
+ const target = finalFocus?.current ?? returnFocusRef.current;
44
+ if (target instanceof HTMLElement && target.isConnected) target.focus();
45
+ });
46
+ useEffect(() => {
47
+ if (open) {
48
+ returnFocusRef.current = document.activeElement;
49
+ return;
50
+ }
51
+ if (!restoreFocusRef.current) return;
52
+ restoreFocusRef.current = false;
53
+ restoreFocus();
54
+ }, [open]);
37
55
  function setPlacement(next) {
38
56
  setPlacementState(next);
57
+ setAnnouncement(`Panel docked ${next}.`);
39
58
  }
40
59
  function togglePlacement() {
41
60
  setPlacement(oppositeDockablePanelSide(placement));
42
61
  }
43
62
  function close() {
63
+ restoreFocusRef.current = panelRef.current?.contains(document.activeElement) ?? false;
44
64
  setOpen(false);
45
65
  }
46
66
  function finishDrag(event, shouldDock) {
@@ -103,11 +123,11 @@ function DockablePanel({ open: openProp, defaultOpen = true, onOpenChange, place
103
123
  }
104
124
  const context = {
105
125
  close,
106
- dragging,
107
126
  isMobile,
108
127
  placement,
109
128
  setPlacement,
110
129
  togglePlacement,
130
+ titleId,
111
131
  dragHandleProps: {
112
132
  onPointerDown,
113
133
  onPointerMove,
@@ -168,15 +188,7 @@ function DockablePanel({ open: openProp, defaultOpen = true, onOpenChange, place
168
188
  transform: `translate3d(${dragPosition.x}px, ${dragPosition.y}px, 0)`
169
189
  } : style,
170
190
  ...props,
171
- children: [children, /* @__PURE__ */ jsxs("span", {
172
- className: "sr-only",
173
- "aria-live": "polite",
174
- children: [
175
- "Panel docked ",
176
- placement,
177
- "."
178
- ]
179
- })]
191
+ children: [children, /* @__PURE__ */ jsx(LiveStatus, { message: announcement })]
180
192
  })]
181
193
  });
182
194
  }
@@ -189,23 +201,28 @@ function DockablePanelHeader({ className, ...props }) {
189
201
  ...props
190
202
  });
191
203
  }
192
- function DockablePanelDragHandle({ className, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, children, ...props }) {
204
+ function DockablePanelDragHandle({ className, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, children, dragLabel = "Drag panel", dockHintLabel = "Drag, or use arrow keys to dock", ...props }) {
193
205
  const context = useDockablePanelContext();
206
+ const hintId = useId();
194
207
  if (context.isMobile) return /* @__PURE__ */ jsx("div", {
195
208
  "data-control-ui": "dockable-panel",
196
209
  "data-control-family": "dockable-panel",
197
210
  "data-slot": "drag-handle",
198
211
  className: cn("flex min-w-0 flex-1 items-center self-stretch", className),
212
+ ...props,
199
213
  children
200
214
  });
201
- return /* @__PURE__ */ jsx("button", {
202
- type: "button",
215
+ function dockWithKeyboard(event) {
216
+ const next = dockablePanelSideForKey(event.key, context.placement);
217
+ if (!next) return;
218
+ event.preventDefault();
219
+ if (!event.repeat) context.setPlacement(next);
220
+ }
221
+ return /* @__PURE__ */ jsxs("div", {
203
222
  "data-control-ui": "dockable-panel",
204
223
  "data-control-family": "dockable-panel",
205
224
  "data-slot": "drag-handle",
206
- "aria-label": "Drag panel",
207
- "aria-pressed": context.dragging,
208
- className: cn("flex min-w-0 flex-1 cursor-grab touch-none items-center self-stretch active:cursor-grabbing", className),
225
+ className: cn("relative flex min-w-0 flex-1 cursor-grab touch-none items-center self-stretch active:cursor-grabbing", className),
209
226
  onPointerDown: (event) => {
210
227
  onPointerDown?.(event);
211
228
  if (!event.defaultPrevented) context.dragHandleProps.onPointerDown(event);
@@ -223,16 +240,39 @@ function DockablePanelDragHandle({ className, onPointerDown, onPointerMove, onPo
223
240
  if (!event.defaultPrevented) context.dragHandleProps.onPointerCancel(event);
224
241
  },
225
242
  ...props,
226
- children
243
+ children: [
244
+ children,
245
+ /* @__PURE__ */ jsx("button", {
246
+ type: "button",
247
+ "data-control-ui": "dockable-panel",
248
+ "data-control-family": "dockable-panel",
249
+ "data-slot": "drag-button",
250
+ "aria-label": dragLabel,
251
+ "aria-labelledby": context.titleId,
252
+ "aria-describedby": hintId,
253
+ "aria-keyshortcuts": "ArrowLeft ArrowRight Enter Space",
254
+ className: "pointer-events-none absolute inset-0",
255
+ onKeyDown: dockWithKeyboard
256
+ }),
257
+ /* @__PURE__ */ jsx("span", {
258
+ id: hintId,
259
+ hidden: true,
260
+ children: dockHintLabel
261
+ })
262
+ ]
227
263
  });
228
264
  }
229
- function DockablePanelTitle({ className, ...props }) {
265
+ function DockablePanelTitle({ className, children, ...props }) {
266
+ const { titleId } = useDockablePanelContext();
230
267
  return /* @__PURE__ */ jsx("h2", {
231
268
  "data-control-ui": "dockable-panel",
232
269
  "data-control-family": "dockable-panel",
233
270
  "data-slot": "title",
234
271
  className: cn("truncate", className),
235
- ...props
272
+ title: typeof children === "string" ? children : void 0,
273
+ ...props,
274
+ id: titleId,
275
+ children
236
276
  });
237
277
  }
238
278
  function DockablePanelActions({ className, ...props }) {
@@ -240,7 +280,7 @@ function DockablePanelActions({ className, ...props }) {
240
280
  "data-control-ui": "dockable-panel",
241
281
  "data-control-family": "dockable-panel",
242
282
  "data-slot": "actions",
243
- className: cn("ml-auto flex shrink-0 items-center", className),
283
+ className: cn("ms-auto flex shrink-0 items-center", className),
244
284
  ...props
245
285
  });
246
286
  }
@@ -30,7 +30,7 @@ function DropdownMenuTrigger({ size = "sm", iconOnly = false, variant = "surface
30
30
  children
31
31
  });
32
32
  }
33
- function DropdownMenuContent({ className, children, side = "bottom", sideOffset = 6, align = "start", alignOffset, collisionBoundary, collisionPadding, collisionAvoidance, ...props }) {
33
+ function DropdownMenuContent({ className, children, side, sideOffset = 6, align = "start", alignOffset, collisionBoundary, collisionPadding, collisionAvoidance, ...props }) {
34
34
  const skin = useSkin();
35
35
  return /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(Menu.Positioner, {
36
36
  "data-skin": skin.id,
@@ -76,7 +76,7 @@ function DropdownMenuSeparator({ className, ...props }) {
76
76
  });
77
77
  }
78
78
  function DropdownMenuLabel({ className, ...props }) {
79
- return /* @__PURE__ */ jsx("div", {
79
+ return /* @__PURE__ */ jsx(Menu.GroupLabel, {
80
80
  "data-control-ui": "dropdown-menu",
81
81
  "data-slot": "label",
82
82
  "data-control-family": "popup",
@@ -162,6 +162,7 @@ function DropdownMenuSubTrigger({ className, children, ...props }) {
162
162
  "data-control-ui": "dropdown-menu",
163
163
  "data-control-family": "popup",
164
164
  "data-slot": "sub-trigger-indicator",
165
+ "data-icon-dir": "inline",
165
166
  "aria-hidden": "true",
166
167
  children: "›"
167
168
  })]
@@ -171,7 +172,6 @@ function DropdownMenuSubContent(props) {
171
172
  return /* @__PURE__ */ jsx(DropdownMenuContent, {
172
173
  "data-control-ui": "dropdown-menu",
173
174
  "data-slot": "sub-content",
174
- side: "right",
175
175
  sideOffset: 0,
176
176
  alignOffset: -3,
177
177
  ...props
@@ -183,6 +183,7 @@ function DropdownMenuShortcut(props) {
183
183
  "data-control-family": "popup",
184
184
  "data-popup-part": "shortcut",
185
185
  "data-slot": "shortcut",
186
+ "aria-hidden": "true",
186
187
  ...props
187
188
  });
188
189
  }
@@ -57,12 +57,12 @@ export declare function useDropzoneContext(): DropzoneContextValue;
57
57
  export declare function Dropzone({ value, defaultValue, onValueChange, policy, disabled, drag, paste, getFilesFromEvent, onDrop, onError, className, children, ...props }: DropzoneProps): import("react").JSX.Element;
58
58
  export declare function DropzoneArea({ className, ...props }: DropzoneAreaProps): import("react").JSX.Element;
59
59
  export declare function DropzoneInput({ className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: DropzoneInputProps): import("react").JSX.Element;
60
- export declare function DropzoneTrigger({ className, children, onClick, disabled, ...props }: DropzoneTriggerProps): import("react").JSX.Element;
60
+ export declare function DropzoneTrigger({ className, children, onClick, disabled, ref, ...props }: DropzoneTriggerProps): import("react").JSX.Element;
61
61
  export declare function DropzoneOverlay({ scope, className, children, ...props }: DropzoneOverlayProps): import("react").JSX.Element;
62
62
  export declare function DropzoneFileList({ className, children, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: DropzoneFileListProps): import("react").JSX.Element;
63
63
  export declare function DropzoneRejectionList({ className, children, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: DropzoneRejectionListProps): import("react").JSX.Element;
64
64
  export declare function DropzoneStatus({ className, children, role, "aria-live": ariaLive, ...props }: DropzoneStatusProps): import("react").JSX.Element;
65
- export declare function DropzoneRemove({ file, children, onClick, disabled, ...props }: DropzoneRemoveProps): import("react").JSX.Element;
65
+ export declare function DropzoneRemove({ file, children, onClick, disabled, ref, ...props }: DropzoneRemoveProps): import("react").JSX.Element;
66
66
  export declare function DropzoneClear({ children, onClick, disabled, ...props }: DropzoneClearProps): import("react").JSX.Element;
67
67
  //#endregion
68
68
  export { type DropzoneAccept, type DropzoneDropDetails, DropzoneErrorCode, type DropzoneErrorCodeValue, type DropzoneEvent, type DropzoneFileError, type DropzoneFileRejection, type DropzoneGetFilesFromEvent, type DropzonePolicy, type DropzoneSelectionMode, type DropzoneValidator, type DropzoneValidatorResult, type DropzoneValueChangeDetails, type DropzoneValueChangeReason, type DropzoneVisualState, type UseDropzoneOptions, type UseDropzoneReturn, formatDropzoneFileSize, useDropzone };
@@ -9,6 +9,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
9
9
  import { CircleAlert, CloudUpload, FileIcon, LoaderCircle, Search, XIcon } from "lucide-react";
10
10
  //#region src/ui/dropzone.tsx
11
11
  const DropzoneContext = createContext(null);
12
+ const DropzoneFocusContext = createContext(null);
12
13
  const fileRenderKeys = /* @__PURE__ */ new WeakMap();
13
14
  let nextFileRenderKey = 0;
14
15
  function getFileRenderKey(file) {
@@ -24,8 +25,48 @@ function useDropzoneContext() {
24
25
  if (!context) throw new Error("Dropzone parts must be used inside Dropzone.");
25
26
  return context;
26
27
  }
28
+ function createFocusTargets() {
29
+ const triggers = /* @__PURE__ */ new Set();
30
+ const removes = /* @__PURE__ */ new Set();
31
+ const inDocumentOrder = (nodes) => [...nodes].filter((node) => !node.matches(":disabled")).sort((a, b) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
32
+ return {
33
+ register(kind, node) {
34
+ const nodes = kind === "trigger" ? triggers : removes;
35
+ nodes.add(node);
36
+ return () => {
37
+ nodes.delete(node);
38
+ };
39
+ },
40
+ focusAfterRemoval(control, all) {
41
+ const siblings = all ? [] : inDocumentOrder(removes);
42
+ const index = siblings.indexOf(control);
43
+ (siblings[index + 1] ?? siblings[index - 1] ?? inDocumentOrder(triggers)[0])?.focus();
44
+ }
45
+ };
46
+ }
47
+ function useFocusTargetRef(kind, ref) {
48
+ const focusTargets = useContext(DropzoneFocusContext);
49
+ return (node) => {
50
+ const unregister = focusTargets?.register(kind, node);
51
+ let release;
52
+ if (typeof ref === "function") {
53
+ const cleanup = ref(node);
54
+ release = typeof cleanup === "function" ? cleanup : () => ref(null);
55
+ } else if (ref) {
56
+ ref.current = node;
57
+ release = () => {
58
+ ref.current = null;
59
+ };
60
+ }
61
+ return () => {
62
+ unregister?.();
63
+ release?.();
64
+ };
65
+ };
66
+ }
27
67
  function Dropzone({ value, defaultValue, onValueChange, policy, disabled, drag, paste, getFilesFromEvent, onDrop, onError, className, children, ...props }) {
28
68
  const [globalOverlayCount, setGlobalOverlayCount] = useState(0);
69
+ const [focusTargets] = useState(createFocusTargets);
29
70
  const [registerGlobalOverlay] = useState(() => () => {
30
71
  let registered = true;
31
72
  setGlobalOverlayCount((count) => count + 1);
@@ -55,15 +96,18 @@ function Dropzone({ value, defaultValue, onValueChange, policy, disabled, drag,
55
96
  const empty = dropzone.value.length === 0 && dropzone.fileRejections.length === 0;
56
97
  return /* @__PURE__ */ jsx(DropzoneContext.Provider, {
57
98
  value: context,
58
- children: /* @__PURE__ */ jsx("div", {
59
- ...props,
60
- "data-control-ui": "dropzone",
61
- "data-control-family": "dropzone",
62
- "data-slot": "root",
63
- "data-disabled": dropzone.disabled ? "true" : void 0,
64
- "data-empty": empty ? "true" : void 0,
65
- className: cn("min-w-0", className),
66
- children
99
+ children: /* @__PURE__ */ jsx(DropzoneFocusContext.Provider, {
100
+ value: focusTargets,
101
+ children: /* @__PURE__ */ jsx("div", {
102
+ ...props,
103
+ "data-control-ui": "dropzone",
104
+ "data-control-family": "dropzone",
105
+ "data-slot": "root",
106
+ "data-disabled": dropzone.disabled ? "true" : void 0,
107
+ "data-empty": empty ? "true" : void 0,
108
+ className: cn("min-w-0", className),
109
+ children
110
+ })
67
111
  })
68
112
  });
69
113
  }
@@ -89,26 +133,32 @@ function DropzoneInput({ className, "aria-label": ariaLabel, "aria-labelledby":
89
133
  });
90
134
  return /* @__PURE__ */ jsx("input", {
91
135
  ...inputProps,
136
+ tabIndex: -1,
92
137
  "data-control-ui": "dropzone",
93
138
  "data-control-family": "dropzone",
94
139
  "data-slot": "input",
95
140
  className: cn("sr-only", className)
96
141
  });
97
142
  }
98
- function DropzoneTrigger({ className, children, onClick, disabled, ...props }) {
143
+ function DropzoneTrigger({ className, children, onClick, disabled, ref, ...props }) {
99
144
  const context = useDropzoneContext();
100
- const triggerDisabled = disabled || context.disabled || context.isProcessing;
145
+ const triggerRef = useFocusTargetRef("trigger", ref);
146
+ const triggerDisabled = disabled || context.disabled;
147
+ const busy = context.isProcessing && !triggerDisabled;
101
148
  return /* @__PURE__ */ jsx("button", {
102
149
  ...props,
150
+ ref: triggerRef,
103
151
  type: "button",
104
152
  disabled: triggerDisabled,
153
+ "aria-disabled": busy ? "true" : void 0,
105
154
  "data-control-ui": "dropzone",
106
155
  "data-control-family": "dropzone",
107
156
  "data-slot": "trigger",
108
157
  "data-state": context.visualState,
109
- "data-disabled": triggerDisabled ? "true" : void 0,
110
- className: cn("flex w-full cursor-pointer flex-col items-center justify-center disabled:cursor-not-allowed", className),
158
+ "data-disabled": triggerDisabled || busy ? "true" : void 0,
159
+ className: cn("flex w-full cursor-pointer flex-col items-center justify-center disabled:cursor-not-allowed aria-disabled:cursor-not-allowed", className),
111
160
  onClick: (event) => {
161
+ if (busy) return;
112
162
  onClick?.(event);
113
163
  if (!event.defaultPrevented) context.open();
114
164
  },
@@ -193,10 +243,13 @@ function DropzoneStatus({ className, children, role = "status", "aria-live": ari
193
243
  children: children ? children(context) : getDropzoneStatusMessage(context)
194
244
  });
195
245
  }
196
- function DropzoneRemove({ file, children, onClick, disabled, ...props }) {
246
+ function DropzoneRemove({ file, children, onClick, disabled, ref, ...props }) {
197
247
  const context = useDropzoneContext();
248
+ const focusTargets = useContext(DropzoneFocusContext);
249
+ const removeRef = useFocusTargetRef("remove", ref);
198
250
  return /* @__PURE__ */ jsx(Button, {
199
251
  ...props,
252
+ ref: removeRef,
200
253
  type: "button",
201
254
  variant: props.variant ?? "quiet",
202
255
  size: props.size ?? "sm",
@@ -205,13 +258,16 @@ function DropzoneRemove({ file, children, onClick, disabled, ...props }) {
205
258
  disabled: disabled || context.disabled || context.isProcessing,
206
259
  onClick: (event) => {
207
260
  onClick?.(event);
208
- if (!event.defaultPrevented) context.removeFile(file);
261
+ if (event.defaultPrevented) return;
262
+ focusTargets?.focusAfterRemoval(event.currentTarget, false);
263
+ context.removeFile(file);
209
264
  },
210
265
  children: children ?? /* @__PURE__ */ jsx(XIcon, { "aria-hidden": "true" })
211
266
  });
212
267
  }
213
268
  function DropzoneClear({ children, onClick, disabled, ...props }) {
214
269
  const context = useDropzoneContext();
270
+ const focusTargets = useContext(DropzoneFocusContext);
215
271
  const empty = context.value.length === 0 && context.fileRejections.length === 0;
216
272
  return /* @__PURE__ */ jsx(Button, {
217
273
  ...props,
@@ -221,7 +277,9 @@ function DropzoneClear({ children, onClick, disabled, ...props }) {
221
277
  disabled: disabled || context.disabled || context.isProcessing || empty,
222
278
  onClick: (event) => {
223
279
  onClick?.(event);
224
- if (!event.defaultPrevented) context.reset();
280
+ if (event.defaultPrevented) return;
281
+ focusTargets?.focusAfterRemoval(event.currentTarget, true);
282
+ context.reset();
225
283
  },
226
284
  children: children ?? "Clear all"
227
285
  });
@@ -234,6 +292,7 @@ function DropzoneFeedback({ state }) {
234
292
  "data-control-ui": "dropzone",
235
293
  "data-control-family": "dropzone",
236
294
  "data-slot": "feedback-spinner",
295
+ "data-motion-essential": "",
237
296
  "aria-hidden": "true"
238
297
  });
239
298
  message = "Checking files…";
@@ -0,0 +1,18 @@
1
+ import { EmojiPickerKnobStyle } from "../knob-contracts/emoji-picker-knobs.js";
2
+ import { CSSProperties, ComponentProps } from "react";
3
+ import { EmojiPicker as EmojiPicker$1 } from "frimousse";
4
+ //#region src/ui/emoji-picker.d.ts
5
+ type EmojiPickerStyle = {
6
+ style?: CSSProperties & EmojiPickerKnobStyle;
7
+ };
8
+ export type EmojiPickerProps = Omit<ComponentProps<typeof EmojiPicker$1.Root>, "style"> & EmojiPickerStyle;
9
+ export declare function EmojiPicker({ className, columns, ...props }: EmojiPickerProps): import("react").JSX.Element;
10
+ export type EmojiPickerSearchProps = Omit<ComponentProps<typeof EmojiPicker$1.Search>, "style"> & EmojiPickerStyle;
11
+ export declare function EmojiPickerSearch({ className, placeholder, "aria-label": ariaLabel, ...props }: EmojiPickerSearchProps): import("react").JSX.Element;
12
+ export type EmojiPickerContentProps = Omit<ComponentProps<typeof EmojiPicker$1.Viewport>, "style"> & EmojiPickerStyle;
13
+ export declare function EmojiPickerContent({ className, ...props }: EmojiPickerContentProps): import("react").JSX.Element;
14
+ export type EmojiPickerFooterProps = Omit<ComponentProps<"div">, "style" | "children"> & EmojiPickerStyle & {
15
+ placeholder?: string;
16
+ };
17
+ export declare function EmojiPickerFooter({ className, placeholder, ...props }: EmojiPickerFooterProps): import("react").JSX.Element;
18
+ //#endregion
@@ -0,0 +1,136 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { Spinner } from "./spinner.js";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { SearchIcon } from "lucide-react";
6
+ import { EmojiPicker as EmojiPicker$1 } from "frimousse";
7
+ //#region src/ui/emoji-picker.tsx
8
+ function EmojiPicker({ className, columns = 9, ...props }) {
9
+ return /* @__PURE__ */ jsx(EmojiPicker$1.Root, {
10
+ "data-control-ui": "emoji-picker",
11
+ "data-control-family": "emoji-picker",
12
+ "data-slot": "root",
13
+ columns,
14
+ className: cn("isolate flex h-72 w-fit flex-col", className),
15
+ ...props
16
+ });
17
+ }
18
+ function EmojiPickerSearch({ className, placeholder = "Search emoji…", "aria-label": ariaLabel = "Search emoji", ...props }) {
19
+ return /* @__PURE__ */ jsxs("div", {
20
+ "data-control-ui": "emoji-picker",
21
+ "data-control-family": "emoji-picker",
22
+ "data-slot": "search",
23
+ className: "flex items-center",
24
+ children: [/* @__PURE__ */ jsx(SearchIcon, {
25
+ "aria-hidden": "true",
26
+ "data-control-ui": "emoji-picker",
27
+ "data-control-family": "emoji-picker",
28
+ "data-slot": "search-icon"
29
+ }), /* @__PURE__ */ jsx(EmojiPicker$1.Search, {
30
+ "data-control-ui": "emoji-picker",
31
+ "data-control-family": "emoji-picker",
32
+ "data-slot": "search-input",
33
+ placeholder,
34
+ "aria-label": ariaLabel,
35
+ className: cn("min-w-0 flex-1", className),
36
+ ...props
37
+ })]
38
+ });
39
+ }
40
+ function EmojiPickerRow({ children, className, ...props }) {
41
+ return /* @__PURE__ */ jsx("div", {
42
+ "data-control-ui": "emoji-picker",
43
+ "data-control-family": "emoji-picker",
44
+ "data-slot": "row",
45
+ className: cn("scroll-my-1", className),
46
+ ...props,
47
+ children
48
+ });
49
+ }
50
+ function EmojiPickerEmoji({ emoji, className, ...props }) {
51
+ return /* @__PURE__ */ jsx("button", {
52
+ "data-control-ui": "emoji-picker",
53
+ "data-control-family": "emoji-picker",
54
+ "data-slot": "emoji",
55
+ "data-active": emoji.isActive ? "" : void 0,
56
+ className: cn("flex items-center justify-center", className),
57
+ ...props,
58
+ children: emoji.emoji
59
+ });
60
+ }
61
+ function EmojiPickerCategoryHeader({ category, className, ...props }) {
62
+ return /* @__PURE__ */ jsx("div", {
63
+ "data-control-ui": "emoji-picker",
64
+ "data-control-family": "emoji-picker",
65
+ "data-slot": "category-header",
66
+ className,
67
+ ...props,
68
+ children: category.label
69
+ });
70
+ }
71
+ function EmojiPickerContent({ className, ...props }) {
72
+ return /* @__PURE__ */ jsxs(EmojiPicker$1.Viewport, {
73
+ "data-control-ui": "emoji-picker",
74
+ "data-control-family": "emoji-picker",
75
+ "data-slot": "content",
76
+ className: cn("relative flex-1 outline-none", className),
77
+ ...props,
78
+ children: [
79
+ /* @__PURE__ */ jsx(EmojiPicker$1.Loading, {
80
+ "data-control-ui": "emoji-picker",
81
+ "data-control-family": "emoji-picker",
82
+ "data-slot": "loading",
83
+ className: "absolute inset-0 flex items-center justify-center",
84
+ children: /* @__PURE__ */ jsx(Spinner, { label: "Loading emoji" })
85
+ }),
86
+ /* @__PURE__ */ jsx(EmojiPicker$1.Empty, {
87
+ "data-control-ui": "emoji-picker",
88
+ "data-control-family": "emoji-picker",
89
+ "data-slot": "empty",
90
+ className: "absolute inset-0 flex items-center justify-center",
91
+ children: "No emoji found."
92
+ }),
93
+ /* @__PURE__ */ jsx(EmojiPicker$1.List, {
94
+ "data-control-ui": "emoji-picker",
95
+ "data-control-family": "emoji-picker",
96
+ "data-slot": "list",
97
+ className: "select-none",
98
+ components: {
99
+ Row: EmojiPickerRow,
100
+ Emoji: EmojiPickerEmoji,
101
+ CategoryHeader: EmojiPickerCategoryHeader
102
+ }
103
+ })
104
+ ]
105
+ });
106
+ }
107
+ function EmojiPickerFooter({ className, placeholder = "Pick an emoji…", ...props }) {
108
+ return /* @__PURE__ */ jsx("div", {
109
+ "data-control-ui": "emoji-picker",
110
+ "data-control-family": "emoji-picker",
111
+ "data-slot": "footer",
112
+ className: cn("flex min-w-0 items-center", className),
113
+ ...props,
114
+ children: /* @__PURE__ */ jsx(EmojiPicker$1.ActiveEmoji, { children: ({ emoji }) => emoji ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
115
+ "data-control-ui": "emoji-picker",
116
+ "data-control-family": "emoji-picker",
117
+ "data-slot": "footer-emoji",
118
+ className: "flex-none",
119
+ children: emoji.emoji
120
+ }), /* @__PURE__ */ jsx("span", {
121
+ "data-control-ui": "emoji-picker",
122
+ "data-control-family": "emoji-picker",
123
+ "data-slot": "footer-label",
124
+ className: "truncate",
125
+ children: emoji.label
126
+ })] }) : /* @__PURE__ */ jsx("span", {
127
+ "data-control-ui": "emoji-picker",
128
+ "data-control-family": "emoji-picker",
129
+ "data-slot": "footer-placeholder",
130
+ className: "truncate",
131
+ children: placeholder
132
+ }) })
133
+ });
134
+ }
135
+ //#endregion
136
+ export { EmojiPicker, EmojiPickerContent, EmojiPickerFooter, EmojiPickerSearch };
@@ -1,3 +1,4 @@
1
+ import { RenderProp } from "../control-props.js";
1
2
  import { EmptyKnobStyle } from "../knob-contracts/empty-knobs.js";
2
3
  import { CSSProperties, ComponentProps } from "react";
3
4
  //#region src/ui/empty.d.ts
@@ -17,12 +18,13 @@ export type EmptyDescriptionProps = Omit<ComponentProps<"p">, "style"> & {
17
18
  style?: CSSProperties & EmptyKnobStyle;
18
19
  };
19
20
  export type EmptyTitleProps = Omit<ComponentProps<"div">, "style"> & {
21
+ render?: RenderProp<ComponentProps<"div">>;
20
22
  style?: CSSProperties & EmptyKnobStyle;
21
23
  };
22
24
  export declare function Empty({ className, ...props }: EmptyProps): import("react").JSX.Element;
23
25
  export declare function EmptyHeader({ className, ...props }: EmptyHeaderProps): import("react").JSX.Element;
24
26
  export declare function EmptyMedia({ className, ...props }: EmptyMediaProps): import("react").JSX.Element;
25
- export declare function EmptyTitle({ className, ...props }: EmptyTitleProps): import("react").JSX.Element;
27
+ export declare function EmptyTitle({ render, ...props }: EmptyTitleProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
26
28
  export declare function EmptyDescription({ className, ...props }: EmptyDescriptionProps): import("react").JSX.Element;
27
29
  export declare function EmptyContent({ className, ...props }: EmptyContentProps): import("react").JSX.Element;
28
30
  //#endregion
package/dist/ui/empty.js CHANGED
@@ -1,5 +1,7 @@
1
+ "use client";
1
2
  import { cn } from "../lib/cn.js";
2
3
  import { jsx } from "react/jsx-runtime";
4
+ import { useRender } from "@base-ui/react/use-render";
3
5
  //#region src/ui/empty.tsx
4
6
  function Empty({ className, ...props }) {
5
7
  return /* @__PURE__ */ jsx("div", {
@@ -28,13 +30,16 @@ function EmptyMedia({ className, ...props }) {
28
30
  ...props
29
31
  });
30
32
  }
31
- function EmptyTitle({ className, ...props }) {
32
- return /* @__PURE__ */ jsx("div", {
33
- "data-control-ui": "empty",
34
- "data-control-family": "empty",
35
- "data-slot": "title",
36
- className,
37
- ...props
33
+ function EmptyTitle({ render, ...props }) {
34
+ return useRender({
35
+ defaultTagName: "div",
36
+ render,
37
+ props: {
38
+ ...props,
39
+ "data-control-ui": "empty",
40
+ "data-control-family": "empty",
41
+ "data-slot": "title"
42
+ }
38
43
  });
39
44
  }
40
45
  function EmptyDescription({ className, ...props }) {
@@ -39,7 +39,7 @@ export type GradientEditorTypeSelectProps = {
39
39
  export declare function GradientEditor({ defaultStops, defaultType, defaultAngle, onValueChange, className, children, value: _value, ...props }: GradientEditorProps): import("react").JSX.Element;
40
40
  export declare function GradientEditorPreview({ className, style, "aria-label": ariaLabel, ...props }: GradientEditorPreviewProps): import("react").JSX.Element;
41
41
  export declare function GradientEditorTrack({ className, children, style, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: GradientEditorTrackProps): import("react").JSX.Element;
42
- export declare function GradientEditorStop({ stop, className, style, onPointerDown, onKeyDown, onFocus, onDoubleClick, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: GradientEditorStopProps): import("react").JSX.Element;
42
+ export declare function GradientEditorStop({ stop, className, style, onPointerDown, onKeyDown, onFocus, onDoubleClick, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ref, ...props }: GradientEditorStopProps): import("react").JSX.Element;
43
43
  export declare function GradientEditorStopAdd({ className, children, ...props }: GradientEditorStopAddProps): import("react").JSX.Element;
44
44
  export declare function GradientEditorStopColor(): import("react").JSX.Element | null;
45
45
  export declare function GradientEditorTypeSelect({ size, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy }: GradientEditorTypeSelectProps): import("react").JSX.Element;