@ctrl-ui/react 0.8.6 → 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 (376) 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 +54 -57
  8. package/dist/blocks/design-canvas-data.d.ts +32 -0
  9. package/dist/blocks/design-canvas-data.js +100 -0
  10. package/dist/blocks/design-canvas-inspector.d.ts +8 -0
  11. package/dist/blocks/design-canvas-inspector.js +238 -0
  12. package/dist/blocks/design-canvas.d.ts +21 -0
  13. package/dist/blocks/design-canvas.js +452 -0
  14. package/dist/blocks/discussion.d.ts +28 -0
  15. package/dist/blocks/discussion.js +67 -0
  16. package/dist/blocks/file-explorer-data.d.ts +1 -0
  17. package/dist/blocks/file-explorer-data.js +4 -1
  18. package/dist/blocks/file-explorer.d.ts +9 -1
  19. package/dist/blocks/file-explorer.js +461 -192
  20. package/dist/blocks/settings.d.ts +2 -1
  21. package/dist/blocks/settings.js +101 -79
  22. package/dist/blocks/team-chat.d.ts +12 -0
  23. package/dist/blocks/team-chat.js +26 -0
  24. package/dist/blocks/theme-toggle.js +23 -31
  25. package/dist/chat-composer-attachment.d.ts +12 -3
  26. package/dist/chat-composer-attachment.js +61 -19
  27. package/dist/chat-composer-editor/extensions/mention.d.ts +5 -1
  28. package/dist/chat-composer-editor/extensions/mention.js +58 -27
  29. package/dist/chat-composer-editor/ghost.js +2 -4
  30. package/dist/chat-composer-editor/types.d.ts +5 -0
  31. package/dist/chat-composer-editor.d.ts +1 -1
  32. package/dist/chat-composer-editor.js +71 -28
  33. package/dist/chat-composer.d.ts +13 -32
  34. package/dist/chat-composer.js +54 -13
  35. package/dist/chat-layout.d.ts +19 -0
  36. package/dist/chat-layout.js +96 -32
  37. package/dist/chat-message/social.d.ts +26 -0
  38. package/dist/chat-message/social.js +122 -0
  39. package/dist/chat-message.d.ts +28 -29
  40. package/dist/chat-message.js +106 -25
  41. package/dist/context.d.ts +2 -2
  42. package/dist/context.js +38 -23
  43. package/dist/dynamic-notification-canvas-loop.d.ts +24 -0
  44. package/dist/dynamic-notification-canvas-loop.js +113 -0
  45. package/dist/dynamic-notification-glass.js +23 -114
  46. package/dist/dynamic-notification-liquid.js +14 -118
  47. package/dist/dynamic-notification.d.ts +16 -8
  48. package/dist/dynamic-notification.js +85 -22
  49. package/dist/email/email-brand.d.ts +7 -4
  50. package/dist/email/email-brand.js +9 -6
  51. package/dist/email/email-code.d.ts +3 -2
  52. package/dist/email/email-code.js +11 -9
  53. package/dist/email/email-footer.d.ts +5 -3
  54. package/dist/email/email-footer.js +11 -5
  55. package/dist/email/email.d.ts +31 -9
  56. package/dist/email/email.js +50 -26
  57. package/dist/email/templates.d.ts +10 -9
  58. package/dist/email/templates.js +102 -38
  59. package/dist/email/theme.js +1 -1
  60. package/dist/environment-variables.d.ts +4 -2
  61. package/dist/environment-variables.js +52 -32
  62. package/dist/filter-bar/context.d.ts +32 -0
  63. package/dist/filter-bar/context.js +11 -0
  64. package/dist/filter-bar/editor.d.ts +14 -0
  65. package/dist/filter-bar/editor.js +216 -0
  66. package/dist/filter-bar/model.d.ts +10 -0
  67. package/dist/filter-bar/model.js +57 -0
  68. package/dist/filter-bar/motion.d.ts +10 -0
  69. package/dist/filter-bar/motion.js +84 -0
  70. package/dist/filter-bar/parts.d.ts +19 -0
  71. package/dist/filter-bar/parts.js +244 -0
  72. package/dist/filter-bar/types.d.ts +54 -0
  73. package/dist/filter-bar/types.js +0 -0
  74. package/dist/filter-bar.d.ts +27 -0
  75. package/dist/filter-bar.js +310 -0
  76. package/dist/hooks/use-audio-recorder.js +12 -6
  77. package/dist/hooks/use-chat-composer.d.ts +21 -7
  78. package/dist/hooks/use-chat-composer.js +32 -5
  79. package/dist/hooks/use-chat-message.d.ts +28 -2
  80. package/dist/hooks/use-chat-message.js +41 -10
  81. package/dist/hooks/use-chat-thread-scroll.js +43 -7
  82. package/dist/hooks/use-color-area.d.ts +3 -3
  83. package/dist/hooks/use-color-area.js +13 -12
  84. package/dist/hooks/use-copy-to-clipboard.d.ts +2 -1
  85. package/dist/hooks/use-copy-to-clipboard.js +20 -20
  86. package/dist/hooks/use-dropzone.d.ts +7 -4
  87. package/dist/hooks/use-dropzone.js +34 -3
  88. package/dist/hooks/use-dynamic-notification.d.ts +6 -2
  89. package/dist/hooks/use-dynamic-notification.js +26 -10
  90. package/dist/hooks/use-mobile.d.ts +3 -2
  91. package/dist/hooks/use-mobile.js +8 -4
  92. package/dist/hooks/use-textarea-trigger-menu.js +2 -1
  93. package/dist/hooks/use-trigger-menu.d.ts +54 -0
  94. package/dist/hooks/use-trigger-menu.js +102 -47
  95. package/dist/hooks/use-user-ask.d.ts +2 -0
  96. package/dist/hooks/use-user-ask.js +35 -41
  97. package/dist/inline-attachment.d.ts +6 -2
  98. package/dist/inline-attachment.js +44 -29
  99. package/dist/inline-citation.d.ts +12 -5
  100. package/dist/inline-citation.js +25 -16
  101. package/dist/knob-contracts/audio-recorder-knobs.d.ts +1 -1
  102. package/dist/knob-contracts/audio-recorder-knobs.js +2 -1
  103. package/dist/knob-contracts/audio-visualizer-knobs.d.ts +1 -1
  104. package/dist/knob-contracts/audio-visualizer-knobs.js +2 -1
  105. package/dist/knob-contracts/avatar-knobs.d.ts +1 -1
  106. package/dist/knob-contracts/avatar-knobs.js +6 -1
  107. package/dist/knob-contracts/badge-knobs.d.ts +1 -1
  108. package/dist/knob-contracts/badge-knobs.js +2 -1
  109. package/dist/knob-contracts/breadcrumb-knobs.d.ts +4 -0
  110. package/dist/knob-contracts/breadcrumb-knobs.js +8 -0
  111. package/dist/knob-contracts/button-knobs.d.ts +1 -1
  112. package/dist/knob-contracts/button-knobs.js +1 -0
  113. package/dist/knob-contracts/chat-layout-knobs.d.ts +1 -1
  114. package/dist/knob-contracts/chat-layout-knobs.js +2 -1
  115. package/dist/knob-contracts/chat-message-knobs.d.ts +1 -1
  116. package/dist/knob-contracts/chat-message-knobs.js +8 -1
  117. package/dist/knob-contracts/choice-knobs.d.ts +1 -1
  118. package/dist/knob-contracts/choice-knobs.js +1 -0
  119. package/dist/knob-contracts/code-diff-knobs.d.ts +1 -1
  120. package/dist/knob-contracts/code-diff-knobs.js +2 -0
  121. package/dist/knob-contracts/code-knobs.d.ts +1 -1
  122. package/dist/knob-contracts/code-knobs.js +2 -1
  123. package/dist/knob-contracts/collapsible-knobs.d.ts +1 -1
  124. package/dist/knob-contracts/collapsible-knobs.js +1 -1
  125. package/dist/knob-contracts/dynamic-notification-knobs.d.ts +1 -1
  126. package/dist/knob-contracts/dynamic-notification-knobs.js +2 -0
  127. package/dist/knob-contracts/emoji-picker-knobs.d.ts +4 -0
  128. package/dist/knob-contracts/emoji-picker-knobs.js +10 -0
  129. package/dist/knob-contracts/field-knobs.d.ts +1 -1
  130. package/dist/knob-contracts/field-knobs.js +5 -1
  131. package/dist/knob-contracts/filter-bar-knobs.d.ts +4 -0
  132. package/dist/knob-contracts/filter-bar-knobs.js +15 -0
  133. package/dist/knob-contracts/infinite-canvas-knobs.d.ts +1 -1
  134. package/dist/knob-contracts/infinite-canvas-knobs.js +3 -0
  135. package/dist/knob-contracts/markdown-knobs.d.ts +1 -1
  136. package/dist/knob-contracts/markdown-knobs.js +3 -1
  137. package/dist/knob-contracts/page-layout-knobs.d.ts +1 -1
  138. package/dist/knob-contracts/page-layout-knobs.js +4 -1
  139. package/dist/knob-contracts/popup-knobs.d.ts +1 -1
  140. package/dist/knob-contracts/popup-knobs.js +28 -0
  141. package/dist/knob-contracts/range-knobs.d.ts +1 -1
  142. package/dist/knob-contracts/range-knobs.js +1 -0
  143. package/dist/knob-contracts/rich-tooltip-knobs.d.ts +1 -1
  144. package/dist/knob-contracts/rich-tooltip-knobs.js +2 -1
  145. package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
  146. package/dist/knob-contracts/sidebar-knobs.js +27 -2
  147. package/dist/knob-contracts/spinner-knobs.d.ts +1 -1
  148. package/dist/knob-contracts/spinner-knobs.js +1 -1
  149. package/dist/knob-contracts/stepper-knobs.d.ts +1 -1
  150. package/dist/knob-contracts/stepper-knobs.js +4 -1
  151. package/dist/knob-contracts/switch-knobs.d.ts +1 -1
  152. package/dist/knob-contracts/switch-knobs.js +5 -0
  153. package/dist/knob-contracts/table-of-contents-knobs.d.ts +1 -1
  154. package/dist/knob-contracts/table-of-contents-knobs.js +4 -5
  155. package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
  156. package/dist/knob-contracts/tabs-knobs.js +1 -0
  157. package/dist/knob-contracts/tree-knobs.d.ts +1 -1
  158. package/dist/knob-contracts/tree-knobs.js +2 -1
  159. package/dist/lib/cn.js +0 -4
  160. package/dist/lib/contrast.d.ts +1 -0
  161. package/dist/lib/contrast.js +17 -5
  162. package/dist/lib/dropzone-validation.js +41 -28
  163. package/dist/lib/env-file.d.ts +1 -1
  164. package/dist/lib/env-file.js +5 -5
  165. package/dist/lib/motion.d.ts +4 -0
  166. package/dist/lib/motion.js +15 -0
  167. package/dist/lib/trigger-detect.js +6 -8
  168. package/dist/markdown-block.js +13 -8
  169. package/dist/popup-parts.d.ts +8 -0
  170. package/dist/popup-parts.js +43 -0
  171. package/dist/scripts/contrast-eval.js +5 -4
  172. package/dist/source-badge.js +3 -1
  173. package/dist/styles/code.css +0 -2
  174. package/dist/styles/effects.css +6 -32
  175. package/dist/styles/index.css +14 -8
  176. package/dist/styles/recipes/accordion.css +9 -6
  177. package/dist/styles/recipes/action-bar.css +0 -15
  178. package/dist/styles/recipes/activity.css +5 -11
  179. package/dist/styles/recipes/alert.css +4 -4
  180. package/dist/styles/recipes/audio-recorder.css +15 -3
  181. package/dist/styles/recipes/audio-visualizer.css +13 -2
  182. package/dist/styles/recipes/avatar.css +78 -1
  183. package/dist/styles/recipes/badge.css +14 -4
  184. package/dist/styles/recipes/breadcrumb.css +77 -0
  185. package/dist/styles/recipes/button-group.css +6 -8
  186. package/dist/styles/recipes/button-states.css +66 -0
  187. package/dist/styles/recipes/button.css +26 -85
  188. package/dist/styles/recipes/calendar.css +17 -23
  189. package/dist/styles/recipes/card.css +2 -2
  190. package/dist/styles/recipes/chat-composer-attachment.css +9 -3
  191. package/dist/styles/recipes/chat-composer.css +21 -16
  192. package/dist/styles/recipes/chat-layout.css +64 -2
  193. package/dist/styles/recipes/chat-message-social.css +123 -0
  194. package/dist/styles/recipes/chat-message.css +157 -37
  195. package/dist/styles/recipes/choice.css +25 -21
  196. package/dist/styles/recipes/code-diff.css +51 -26
  197. package/dist/styles/recipes/code.css +44 -18
  198. package/dist/styles/recipes/collapsible.css +43 -1
  199. package/dist/styles/recipes/color-picker-details.css +3 -4
  200. package/dist/styles/recipes/color-picker-values.css +9 -8
  201. package/dist/styles/recipes/color-picker.css +23 -4
  202. package/dist/styles/recipes/context.css +3 -3
  203. package/dist/styles/recipes/dockable-panel.css +18 -7
  204. package/dist/styles/recipes/dropzone.css +7 -8
  205. package/dist/styles/recipes/dynamic-notification-input.css +8 -5
  206. package/dist/styles/recipes/dynamic-notification-motion.css +12 -21
  207. package/dist/styles/recipes/dynamic-notification.css +40 -26
  208. package/dist/styles/recipes/emoji-picker.css +132 -0
  209. package/dist/styles/recipes/empty.css +5 -4
  210. package/dist/styles/recipes/environment-variables.css +4 -15
  211. package/dist/styles/recipes/field-parts.css +89 -21
  212. package/dist/styles/recipes/field.css +83 -26
  213. package/dist/styles/recipes/filter-bar.css +161 -0
  214. package/dist/styles/recipes/gradient-editor.css +18 -7
  215. package/dist/styles/recipes/infinite-canvas.css +51 -4
  216. package/dist/styles/recipes/inline-attachment.css +9 -4
  217. package/dist/styles/recipes/inline-citation.css +10 -4
  218. package/dist/styles/recipes/item.css +23 -9
  219. package/dist/styles/recipes/markdown-block.css +5 -4
  220. package/dist/styles/recipes/markdown.css +68 -26
  221. package/dist/styles/recipes/morphing-panel.css +8 -8
  222. package/dist/styles/recipes/page-layout.css +36 -10
  223. package/dist/styles/recipes/pagination.css +5 -6
  224. package/dist/styles/recipes/phone-input.css +12 -8
  225. package/dist/styles/recipes/popup-controls.css +175 -26
  226. package/dist/styles/recipes/popup-drawer.css +35 -44
  227. package/dist/styles/recipes/popup-lists.css +139 -29
  228. package/dist/styles/recipes/popup-motion.css +4 -45
  229. package/dist/styles/recipes/popup-toast.css +55 -26
  230. package/dist/styles/recipes/popup.css +149 -153
  231. package/dist/styles/recipes/range.css +55 -19
  232. package/dist/styles/recipes/resizable.css +36 -7
  233. package/dist/styles/recipes/rich-tooltip.css +24 -34
  234. package/dist/styles/recipes/scroll-area.css +8 -1
  235. package/dist/styles/recipes/sidebar-menu.css +149 -34
  236. package/dist/styles/recipes/sidebar-parts.css +154 -0
  237. package/dist/styles/recipes/sidebar.css +187 -128
  238. package/dist/styles/recipes/source-badge.css +1 -1
  239. package/dist/styles/recipes/spinner.css +12 -1
  240. package/dist/styles/recipes/stepper.css +31 -13
  241. package/dist/styles/recipes/switch.css +68 -23
  242. package/dist/styles/recipes/table-of-contents.css +49 -41
  243. package/dist/styles/recipes/table.css +14 -10
  244. package/dist/styles/recipes/tabs.css +38 -12
  245. package/dist/styles/recipes/task-list.css +7 -14
  246. package/dist/styles/recipes/thread-rail.css +42 -19
  247. package/dist/styles/recipes/timeline.css +2 -0
  248. package/dist/styles/recipes/toolbar.css +27 -25
  249. package/dist/styles/recipes/track-highlight.css +12 -8
  250. package/dist/styles/recipes/transcript-divider.css +2 -2
  251. package/dist/styles/recipes/tree.css +31 -4
  252. package/dist/styles/recipes/user-ask-details.css +0 -1
  253. package/dist/styles/recipes/user-ask.css +4 -4
  254. package/dist/styles/theme.css +287 -107
  255. package/dist/task-list.d.ts +4 -1
  256. package/dist/task-list.js +24 -8
  257. package/dist/thread-rail.d.ts +1 -1
  258. package/dist/thread-rail.js +61 -19
  259. package/dist/transcript-divider.js +2 -1
  260. package/dist/ui/alert.d.ts +3 -1
  261. package/dist/ui/alert.js +14 -8
  262. package/dist/ui/autocomplete.js +7 -1
  263. package/dist/ui/avatar.d.ts +8 -0
  264. package/dist/ui/avatar.js +23 -3
  265. package/dist/ui/badge.js +1 -1
  266. package/dist/ui/breadcrumb.d.ts +27 -0
  267. package/dist/ui/breadcrumb.js +88 -0
  268. package/dist/ui/button.d.ts +2 -1
  269. package/dist/ui/button.js +1 -1
  270. package/dist/ui/calendar.js +2 -2
  271. package/dist/ui/card.d.ts +3 -1
  272. package/dist/ui/card.js +12 -7
  273. package/dist/ui/checkbox-group.js +1 -1
  274. package/dist/ui/checkbox.d.ts +2 -0
  275. package/dist/ui/code-diff.d.ts +1 -1
  276. package/dist/ui/code-diff.js +71 -27
  277. package/dist/ui/code.d.ts +5 -3
  278. package/dist/ui/code.js +27 -28
  279. package/dist/ui/color-picker.d.ts +5 -2
  280. package/dist/ui/color-picker.js +84 -36
  281. package/dist/ui/combobox-disabled-selection.d.ts +1 -2
  282. package/dist/ui/combobox-disabled-selection.js +7 -8
  283. package/dist/ui/combobox.d.ts +5 -23
  284. package/dist/ui/combobox.js +36 -16
  285. package/dist/ui/command.d.ts +8 -2
  286. package/dist/ui/command.js +39 -15
  287. package/dist/ui/context-menu.js +3 -0
  288. package/dist/ui/dialog.d.ts +2 -1
  289. package/dist/ui/dialog.js +3 -22
  290. package/dist/ui/dockable-panel-geometry.d.ts +1 -0
  291. package/dist/ui/dockable-panel-geometry.js +7 -1
  292. package/dist/ui/dockable-panel.d.ts +9 -6
  293. package/dist/ui/dockable-panel.js +81 -33
  294. package/dist/ui/drawer.d.ts +7 -4
  295. package/dist/ui/drawer.js +35 -22
  296. package/dist/ui/dropdown-menu.js +4 -3
  297. package/dist/ui/dropzone.d.ts +5 -3
  298. package/dist/ui/dropzone.js +78 -17
  299. package/dist/ui/emoji-picker.d.ts +18 -0
  300. package/dist/ui/emoji-picker.js +136 -0
  301. package/dist/ui/empty.d.ts +3 -1
  302. package/dist/ui/empty.js +12 -7
  303. package/dist/ui/field.js +1 -1
  304. package/dist/ui/gradient-editor.d.ts +1 -1
  305. package/dist/ui/gradient-editor.js +37 -21
  306. package/dist/ui/infinite-canvas.d.ts +13 -1
  307. package/dist/ui/infinite-canvas.js +139 -20
  308. package/dist/ui/input-group.d.ts +2 -4
  309. package/dist/ui/input-group.js +13 -14
  310. package/dist/ui/input-otp.d.ts +1 -1
  311. package/dist/ui/input-otp.js +32 -15
  312. package/dist/ui/input.js +11 -2
  313. package/dist/ui/item.d.ts +2 -1
  314. package/dist/ui/item.js +11 -7
  315. package/dist/ui/live-status.d.ts +7 -0
  316. package/dist/ui/live-status.js +17 -0
  317. package/dist/ui/markdown-elements.d.ts +6 -0
  318. package/dist/ui/markdown-elements.js +42 -9
  319. package/dist/ui/menubar.js +2 -2
  320. package/dist/ui/morphing-panel.js +2 -1
  321. package/dist/ui/native-select.d.ts +1 -1
  322. package/dist/ui/native-select.js +24 -11
  323. package/dist/ui/number-field.d.ts +1 -1
  324. package/dist/ui/number-field.js +13 -2
  325. package/dist/ui/page-layout.d.ts +2 -2
  326. package/dist/ui/page-layout.js +2 -4
  327. package/dist/ui/pagination.d.ts +7 -5
  328. package/dist/ui/pagination.js +19 -13
  329. package/dist/ui/phone-input.d.ts +1 -0
  330. package/dist/ui/phone-input.js +56 -58
  331. package/dist/ui/radio-group.d.ts +3 -0
  332. package/dist/ui/radio-group.js +11 -1
  333. package/dist/ui/resizable.d.ts +20 -2
  334. package/dist/ui/resizable.js +130 -8
  335. package/dist/ui/responsive-dialog.d.ts +1 -1
  336. package/dist/ui/responsive-dialog.js +9 -23
  337. package/dist/ui/rich-tooltip.d.ts +10 -4
  338. package/dist/ui/rich-tooltip.js +81 -33
  339. package/dist/ui/scroll-area.d.ts +4 -1
  340. package/dist/ui/scroll-area.js +44 -28
  341. package/dist/ui/select.d.ts +2 -1
  342. package/dist/ui/select.js +57 -32
  343. package/dist/ui/sidebar-menu.d.ts +5 -1
  344. package/dist/ui/sidebar-menu.js +31 -33
  345. package/dist/ui/sidebar-provider.d.ts +10 -1
  346. package/dist/ui/sidebar-provider.js +38 -13
  347. package/dist/ui/sidebar-resize-rail.js +7 -3
  348. package/dist/ui/sidebar.d.ts +11 -6
  349. package/dist/ui/sidebar.js +46 -23
  350. package/dist/ui/slider.js +31 -22
  351. package/dist/ui/spinner.d.ts +3 -2
  352. package/dist/ui/spinner.js +3 -2
  353. package/dist/ui/stepper.d.ts +8 -1
  354. package/dist/ui/stepper.js +74 -25
  355. package/dist/ui/switch.d.ts +1 -0
  356. package/dist/ui/table-of-contents.d.ts +5 -4
  357. package/dist/ui/table-of-contents.js +143 -52
  358. package/dist/ui/table.js +15 -9
  359. package/dist/ui/textarea.d.ts +1 -1
  360. package/dist/ui/textarea.js +20 -9
  361. package/dist/ui/timeline.d.ts +3 -1
  362. package/dist/ui/timeline.js +17 -9
  363. package/dist/ui/toast.d.ts +11 -5
  364. package/dist/ui/toast.js +100 -54
  365. package/dist/ui/toggle.d.ts +1 -1
  366. package/dist/ui/toggle.js +2 -1
  367. package/dist/ui/toolbar.d.ts +3 -2
  368. package/dist/ui/toolbar.js +4 -2
  369. package/dist/ui/tooltip.js +3 -20
  370. package/dist/ui/tree.js +120 -80
  371. package/dist/ui/trigger-menu.js +7 -2
  372. package/dist/user-ask.d.ts +10 -5
  373. package/dist/user-ask.js +31 -11
  374. package/package.json +5 -1
  375. package/dist/hooks/use-keyboard-navigation.d.ts +0 -3
  376. package/dist/hooks/use-keyboard-navigation.js +0 -22
@@ -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
  }
27
- function Dropzone({ value, defaultValue, onValueChange, policy, disabled, getFilesFromEvent, onDrop, onError, className, children, ...props }) {
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
+ }
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);
@@ -41,6 +82,8 @@ function Dropzone({ value, defaultValue, onValueChange, policy, disabled, getFil
41
82
  onValueChange,
42
83
  policy,
43
84
  disabled,
85
+ drag,
86
+ paste,
44
87
  getFilesFromEvent,
45
88
  onDrop,
46
89
  onError,
@@ -53,15 +96,18 @@ function Dropzone({ value, defaultValue, onValueChange, policy, disabled, getFil
53
96
  const empty = dropzone.value.length === 0 && dropzone.fileRejections.length === 0;
54
97
  return /* @__PURE__ */ jsx(DropzoneContext.Provider, {
55
98
  value: context,
56
- children: /* @__PURE__ */ jsx("div", {
57
- ...props,
58
- "data-control-ui": "dropzone",
59
- "data-control-family": "dropzone",
60
- "data-slot": "root",
61
- "data-disabled": dropzone.disabled ? "true" : void 0,
62
- "data-empty": empty ? "true" : void 0,
63
- className: cn("min-w-0", className),
64
- 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
+ })
65
111
  })
66
112
  });
67
113
  }
@@ -87,26 +133,32 @@ function DropzoneInput({ className, "aria-label": ariaLabel, "aria-labelledby":
87
133
  });
88
134
  return /* @__PURE__ */ jsx("input", {
89
135
  ...inputProps,
136
+ tabIndex: -1,
90
137
  "data-control-ui": "dropzone",
91
138
  "data-control-family": "dropzone",
92
139
  "data-slot": "input",
93
140
  className: cn("sr-only", className)
94
141
  });
95
142
  }
96
- function DropzoneTrigger({ className, children, onClick, disabled, ...props }) {
143
+ function DropzoneTrigger({ className, children, onClick, disabled, ref, ...props }) {
97
144
  const context = useDropzoneContext();
98
- 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;
99
148
  return /* @__PURE__ */ jsx("button", {
100
149
  ...props,
150
+ ref: triggerRef,
101
151
  type: "button",
102
152
  disabled: triggerDisabled,
153
+ "aria-disabled": busy ? "true" : void 0,
103
154
  "data-control-ui": "dropzone",
104
155
  "data-control-family": "dropzone",
105
156
  "data-slot": "trigger",
106
157
  "data-state": context.visualState,
107
- "data-disabled": triggerDisabled ? "true" : void 0,
108
- 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),
109
160
  onClick: (event) => {
161
+ if (busy) return;
110
162
  onClick?.(event);
111
163
  if (!event.defaultPrevented) context.open();
112
164
  },
@@ -191,10 +243,13 @@ function DropzoneStatus({ className, children, role = "status", "aria-live": ari
191
243
  children: children ? children(context) : getDropzoneStatusMessage(context)
192
244
  });
193
245
  }
194
- function DropzoneRemove({ file, children, onClick, disabled, ...props }) {
246
+ function DropzoneRemove({ file, children, onClick, disabled, ref, ...props }) {
195
247
  const context = useDropzoneContext();
248
+ const focusTargets = useContext(DropzoneFocusContext);
249
+ const removeRef = useFocusTargetRef("remove", ref);
196
250
  return /* @__PURE__ */ jsx(Button, {
197
251
  ...props,
252
+ ref: removeRef,
198
253
  type: "button",
199
254
  variant: props.variant ?? "quiet",
200
255
  size: props.size ?? "sm",
@@ -203,13 +258,16 @@ function DropzoneRemove({ file, children, onClick, disabled, ...props }) {
203
258
  disabled: disabled || context.disabled || context.isProcessing,
204
259
  onClick: (event) => {
205
260
  onClick?.(event);
206
- if (!event.defaultPrevented) context.removeFile(file);
261
+ if (event.defaultPrevented) return;
262
+ focusTargets?.focusAfterRemoval(event.currentTarget, false);
263
+ context.removeFile(file);
207
264
  },
208
265
  children: children ?? /* @__PURE__ */ jsx(XIcon, { "aria-hidden": "true" })
209
266
  });
210
267
  }
211
268
  function DropzoneClear({ children, onClick, disabled, ...props }) {
212
269
  const context = useDropzoneContext();
270
+ const focusTargets = useContext(DropzoneFocusContext);
213
271
  const empty = context.value.length === 0 && context.fileRejections.length === 0;
214
272
  return /* @__PURE__ */ jsx(Button, {
215
273
  ...props,
@@ -219,7 +277,9 @@ function DropzoneClear({ children, onClick, disabled, ...props }) {
219
277
  disabled: disabled || context.disabled || context.isProcessing || empty,
220
278
  onClick: (event) => {
221
279
  onClick?.(event);
222
- if (!event.defaultPrevented) context.reset();
280
+ if (event.defaultPrevented) return;
281
+ focusTargets?.focusAfterRemoval(event.currentTarget, true);
282
+ context.reset();
223
283
  },
224
284
  children: children ?? "Clear all"
225
285
  });
@@ -232,6 +292,7 @@ function DropzoneFeedback({ state }) {
232
292
  "data-control-ui": "dropzone",
233
293
  "data-control-family": "dropzone",
234
294
  "data-slot": "feedback-spinner",
295
+ "data-motion-essential": "",
235
296
  "aria-hidden": "true"
236
297
  });
237
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,4 +1,6 @@
1
+ "use client";
1
2
  import { cn } from "../lib/cn.js";
3
+ import { useRender } from "@base-ui/react/use-render";
2
4
  import { jsx } from "react/jsx-runtime";
3
5
  //#region src/ui/empty.tsx
4
6
  function Empty({ className, ...props }) {
@@ -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 }) {
package/dist/ui/field.js CHANGED
@@ -2,9 +2,9 @@
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { Separator } from "./separator.js";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
- import { cva } from "class-variance-authority";
6
5
  import { Field as Field$1 } from "@base-ui/react/field";
7
6
  import { Fieldset } from "@base-ui/react/fieldset";
7
+ import { cva } from "class-variance-authority";
8
8
  //#region src/ui/field.tsx
9
9
  const fieldVariants = cva("group/field flex w-full", {
10
10
  variants: { orientation: {
@@ -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;
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
+ import { useColorArea } from "../hooks/use-color-area.js";
3
4
  import { formatGradient } from "../lib/color.js";
4
5
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select.js";
5
6
  import { ColorPicker, ColorPickerAlpha, ColorPickerArea, ColorPickerContent, ColorPickerFormatSelect, ColorPickerHue, ColorPickerInput, ColorPickerTrigger } from "./color-picker.js";
@@ -30,6 +31,7 @@ function GradientEditor({ defaultStops = DEFAULT_STOPS, defaultType = "linear",
30
31
  const [selectedId, setSelectedId] = useState(defaultStops[0]?.id ?? "stop-1");
31
32
  const idCounter = useRef(defaultStops.length);
32
33
  const trackRef = useRef(null);
34
+ const [stopNodes] = useState(() => /* @__PURE__ */ new Map());
33
35
  const gradient = formatGradient(stops, type, angle);
34
36
  function updateStops(nextStops) {
35
37
  setStops(nextStops);
@@ -58,7 +60,7 @@ function GradientEditor({ defaultStops = DEFAULT_STOPS, defaultType = "linear",
58
60
  if (stops.length <= 2) return;
59
61
  const nextStops = stops.filter((stop) => stop.id !== id);
60
62
  updateStops(nextStops);
61
- if (selectedId === id) setSelectedId(nextStops[0]?.id ?? selectedId);
63
+ setSelectedId((current) => current === id ? nextStops[0]?.id ?? current : current);
62
64
  };
63
65
  const setType = (nextType) => {
64
66
  setTypeState(nextType);
@@ -71,6 +73,7 @@ function GradientEditor({ defaultStops = DEFAULT_STOPS, defaultType = "linear",
71
73
  selectedId,
72
74
  gradient,
73
75
  trackRef,
76
+ stopNodes,
74
77
  select: setSelectedId,
75
78
  setStopColor,
76
79
  setStopPosition,
@@ -130,30 +133,35 @@ function GradientEditorTrack({ className, children, style, "aria-label": ariaLab
130
133
  children: children ?? stops.map((stop) => /* @__PURE__ */ jsx(GradientEditorStop, { stop }, stop.id))
131
134
  });
132
135
  }
133
- function GradientEditorStop({ stop, className, style, onPointerDown, onKeyDown, onFocus, onDoubleClick, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }) {
134
- const { stops, selectedId, select, setStopPosition, removeStop, trackRef } = useGradientEditor();
136
+ function GradientEditorStop({ stop, className, style, onPointerDown, onKeyDown, onFocus, onDoubleClick, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ref, ...props }) {
137
+ const { stops, selectedId, select, setStopPosition, removeStop, trackRef, stopNodes } = useGradientEditor();
135
138
  const selected = stop.id === selectedId;
136
139
  const canRemove = stops.length > 2;
140
+ const { onPointerDown: startDrag } = useColorArea((offset, rect) => setStopPosition(stop.id, offset.x / rect.width), trackRef);
137
141
  function handlePointerDown(event) {
138
142
  onPointerDown?.(event);
139
143
  if (event.defaultPrevented) return;
140
144
  select(stop.id);
141
145
  event.currentTarget.focus();
142
- event.preventDefault();
143
- const track = trackRef.current;
144
- if (!track) return;
145
- const move = (moveEvent) => {
146
- const rect = track.getBoundingClientRect();
147
- setStopPosition(stop.id, (moveEvent.clientX - rect.left) / rect.width);
148
- };
149
- const up = () => {
150
- window.removeEventListener("pointermove", move);
151
- window.removeEventListener("pointerup", up);
152
- document.body.style.userSelect = "";
146
+ startDrag(event);
147
+ }
148
+ function registerStop(node) {
149
+ stopNodes.set(stop.id, node);
150
+ const cleanup = typeof ref === "function" ? ref(node) : void 0;
151
+ if (ref && typeof ref === "object") ref.current = node;
152
+ return () => {
153
+ stopNodes.delete(stop.id);
154
+ if (typeof cleanup === "function") cleanup();
155
+ else if (typeof ref === "function") ref(null);
156
+ else if (ref) ref.current = null;
153
157
  };
154
- document.body.style.userSelect = "none";
155
- window.addEventListener("pointermove", move);
156
- window.addEventListener("pointerup", up);
158
+ }
159
+ function remove() {
160
+ if (!canRemove) return;
161
+ const index = stops.findIndex((candidate) => candidate.id === stop.id);
162
+ const neighbour = stops[index + 1] ?? stops[index - 1];
163
+ if (neighbour) stopNodes.get(neighbour.id)?.focus();
164
+ removeStop(stop.id);
157
165
  }
158
166
  function handleFocus(event) {
159
167
  onFocus?.(event);
@@ -194,27 +202,35 @@ function GradientEditorStop({ stop, className, style, onPointerDown, onKeyDown,
194
202
  case "Delete":
195
203
  if (!canRemove) return;
196
204
  event.preventDefault();
197
- removeStop(stop.id);
205
+ remove();
198
206
  }
199
207
  }
200
208
  function handleDoubleClick(event) {
201
209
  onDoubleClick?.(event);
202
- if (!event.defaultPrevented) removeStop(stop.id);
210
+ if (!event.defaultPrevented) remove();
203
211
  }
204
- const defaultLabel = `Gradient stop at ${Math.round(stop.position * 100)}%. Use arrow keys to move${canRemove ? ", Delete to remove" : ""}.`;
212
+ const percent = Math.round(stop.position * 100);
213
+ const defaultLabel = `Gradient stop ${stops.indexOf(stop) + 1}`;
205
214
  return /* @__PURE__ */ jsx("button", {
215
+ ref: registerStop,
206
216
  type: "button",
217
+ role: "slider",
207
218
  "data-control-ui": "gradient-editor",
208
219
  "data-control-family": "gradient-editor",
209
220
  "data-slot": "stop",
210
221
  "data-selected": selected ? "true" : void 0,
211
222
  "aria-label": ariaLabelledBy === void 0 ? ariaLabel ?? defaultLabel : ariaLabel,
212
223
  "aria-labelledby": ariaLabelledBy,
224
+ "aria-orientation": "horizontal",
225
+ "aria-valuemin": 0,
226
+ "aria-valuemax": 100,
227
+ "aria-valuenow": percent,
228
+ "aria-valuetext": `${percent}%, ${stop.color}`,
213
229
  onPointerDown: handlePointerDown,
214
230
  onFocus: handleFocus,
215
231
  onKeyDown: handleKeyDown,
216
232
  onDoubleClick: handleDoubleClick,
217
- className: cn("-translate-x-1/2 absolute top-1/2 -translate-y-1/2 cursor-grab touch-pan-y overflow-hidden active:cursor-grabbing", className),
233
+ className: cn("-translate-x-1/2 absolute top-1/2 -translate-y-1/2 cursor-grab touch-pan-y active:cursor-grabbing", className),
218
234
  style: {
219
235
  ...style,
220
236
  left: `${stop.position * 100}%`,
@@ -22,6 +22,17 @@ export type InfiniteCanvasProps = Omit<ComponentProps<"section">, "onChange" | "
22
22
  export type InfiniteCanvasContentProps = ComponentProps<"div"> & {
23
23
  style?: CSSProperties & InfiniteCanvasKnobStyle;
24
24
  };
25
+ export type InfiniteCanvasPoint = {
26
+ x: number;
27
+ y: number;
28
+ };
29
+ export type InfiniteCanvasItemProps = Omit<ComponentProps<"div">, "style"> & {
30
+ x: number;
31
+ y: number;
32
+ onPositionChange?: (position: InfiniteCanvasPoint) => void;
33
+ movableItemLabel?: string;
34
+ style?: CSSProperties & InfiniteCanvasKnobStyle;
35
+ };
25
36
  export type InfiniteCanvasControlsProps = Omit<ComponentProps<"div">, "children" | "style"> & {
26
37
  style?: CSSProperties & InfiniteCanvasKnobStyle;
27
38
  };
@@ -33,7 +44,8 @@ type InfiniteCanvasContextValue = {
33
44
  zoomBy: (factor: number, reason: InfiniteCanvasMoveReason) => void;
34
45
  };
35
46
  export declare function useInfiniteCanvas(): InfiniteCanvasContextValue;
36
- export declare function InfiniteCanvas({ transform: controlledTransform, defaultTransform, onTransformChange, minScale: minScaleProp, maxScale: maxScaleProp, className, children, style, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onWheel, onKeyDown, "aria-label": ariaLabel, ...props }: InfiniteCanvasProps): import("react").JSX.Element;
47
+ export declare function InfiniteCanvas({ transform: controlledTransform, defaultTransform, onTransformChange, minScale: minScaleProp, maxScale: maxScaleProp, className, children, style, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onWheel, onKeyDown, "aria-label": ariaLabel, "aria-keyshortcuts": ariaKeyShortcuts, ...props }: InfiniteCanvasProps): import("react").JSX.Element;
37
48
  export declare function InfiniteCanvasContent({ className, style, ...props }: InfiniteCanvasContentProps): import("react").JSX.Element;
49
+ export declare function InfiniteCanvasItem({ x, y, onPositionChange, movableItemLabel, className, style, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onKeyDown, ...props }: InfiniteCanvasItemProps): import("react").JSX.Element;
38
50
  export declare function InfiniteCanvasControls({ className, ...props }: InfiniteCanvasControlsProps): import("react").JSX.Element;
39
51
  //#endregion