@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
@@ -3,9 +3,9 @@ import { cn } from "./lib/cn.js";
3
3
  import { Button } from "./ui/button.js";
4
4
  import { DUPLICATE_ENVIRONMENT_VARIABLE_MESSAGE, collectEnvironmentVariables, rowsToEnvFileText } from "./lib/env-file.js";
5
5
  import { useEnvironmentVariables } from "./hooks/use-environment-variables.js";
6
+ import { InputGroup, InputGroupAddon } from "./ui/input-group.js";
6
7
  import { Input } from "./ui/input.js";
7
- import { InputGroup, InputGroupAddon, InputGroupInput } from "./ui/input-group.js";
8
- import { createContext, use, useId } from "react";
8
+ import { createContext, use, useId, useRef, useState } from "react";
9
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
10
  import { AlertTriangleIcon, EyeIcon, EyeOffIcon, FileUpIcon, PlusIcon, RotateCcwIcon, SaveIcon, Trash2Icon } from "lucide-react";
11
11
  //#region src/environment-variables.tsx
@@ -15,18 +15,28 @@ function useEnvironmentVariablesContext(componentName) {
15
15
  if (!context) throw new Error(`${componentName} must be used inside <EnvironmentVariables.Root>.`);
16
16
  return context;
17
17
  }
18
- function EnvironmentVariablesRoot({ editor, disabled = false, readOnly = false, rowErrors, keyLabel = "Key", valueLabel = "Value", keyPlaceholder = "OPENAI_API_KEY", valuePlaceholder = "sk-...", duplicateKeyMessage = DUPLICATE_ENVIRONMENT_VARIABLE_MESSAGE, onSubmit, className, children, ...props }) {
19
- function handleSubmit(event) {
18
+ function EnvironmentVariablesRoot({ editor, disabled = false, readOnly = false, rowErrors, keyLabel = "Key", valueLabel = "Value", keyPlaceholder = "OPENAI_API_KEY", valuePlaceholder = "sk-…", duplicateKeyMessage = DUPLICATE_ENVIRONMENT_VARIABLE_MESSAGE, revealValueLabel = (key) => `Show value for ${key}`, onSubmit, className, children, ...props }) {
19
+ const pendingRef = useRef(false);
20
+ const [pending, setPending] = useState(false);
21
+ async function handleSubmit(event) {
20
22
  if (event.defaultPrevented || !onSubmit) return;
21
23
  event.preventDefault();
22
- if (editor.hasDuplicateKeys) return;
24
+ if (pendingRef.current || editor.hasDuplicateKeys) return;
23
25
  const rows = editor.getRowsForSubmit();
24
- onSubmit({
26
+ const payload = {
25
27
  rows,
26
28
  variables: collectEnvironmentVariables(rows),
27
29
  envFileText: rowsToEnvFileText(rows),
28
30
  reset: () => editor.resetRows(rows)
29
- });
31
+ };
32
+ pendingRef.current = true;
33
+ setPending(true);
34
+ try {
35
+ await onSubmit(payload);
36
+ } finally {
37
+ pendingRef.current = false;
38
+ setPending(false);
39
+ }
30
40
  }
31
41
  return /* @__PURE__ */ jsx(EnvironmentVariablesContext.Provider, {
32
42
  value: {
@@ -38,7 +48,9 @@ function EnvironmentVariablesRoot({ editor, disabled = false, readOnly = false,
38
48
  valueLabel,
39
49
  keyPlaceholder,
40
50
  valuePlaceholder,
41
- duplicateKeyMessage
51
+ duplicateKeyMessage,
52
+ revealValueLabel,
53
+ pending
42
54
  },
43
55
  children: /* @__PURE__ */ jsx("form", {
44
56
  "data-control-ui": "environment-variables",
@@ -47,6 +59,7 @@ function EnvironmentVariablesRoot({ editor, disabled = false, readOnly = false,
47
59
  "data-surface": "panel",
48
60
  "data-disabled": disabled ? "true" : void 0,
49
61
  "data-readonly": readOnly ? "true" : void 0,
62
+ "aria-busy": pending || void 0,
50
63
  className: cn("flex min-w-0 flex-col", className),
51
64
  onSubmit: handleSubmit,
52
65
  ...props,
@@ -157,6 +170,7 @@ function EnvironmentVariablesUploadButton({ inputLabel = "Import .env file", chi
157
170
  type: "file",
158
171
  accept: ".env,text/plain",
159
172
  className: "sr-only",
173
+ tabIndex: -1,
160
174
  disabled: isDisabled,
161
175
  "aria-label": inputLabel,
162
176
  onChange: handleChange
@@ -203,7 +217,7 @@ function EnvironmentVariablesRows({ rowErrors, className, children, ...props })
203
217
  });
204
218
  }
205
219
  function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props }) {
206
- const { editor, disabled, readOnly, rowErrors: contextRowErrors, keyLabel, valueLabel, keyPlaceholder, valuePlaceholder, duplicateKeyMessage } = useEnvironmentVariablesContext("EnvironmentVariables.Row");
220
+ const { editor, disabled, readOnly, rowErrors: contextRowErrors, keyLabel, valueLabel, keyPlaceholder, valuePlaceholder, duplicateKeyMessage, revealValueLabel } = useEnvironmentVariablesContext("EnvironmentVariables.Row");
207
221
  const resolvedRowErrors = rowErrors ?? contextRowErrors;
208
222
  const keyError = resolvedRowErrors?.[index]?.key ?? (editor.rowHasDuplicateKey(index) ? duplicateKeyMessage : null);
209
223
  const valueError = resolvedRowErrors?.[index]?.value;
@@ -211,8 +225,11 @@ function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props })
211
225
  const rowId = editor.getRowId(index);
212
226
  const keyInputId = `${rowId}-key`;
213
227
  const valueInputId = `${rowId}-value`;
214
- const revealValueLabel = editor.isValueRevealed(index) ? "Hide value" : "Show value";
215
- const removeRowLabel = `Remove environment variable ${row.key.trim() || index + 1}`;
228
+ const keyErrorId = `${keyInputId}-error`;
229
+ const valueErrorId = `${valueInputId}-error`;
230
+ const rowName = row.key.trim() || `row ${index + 1}`;
231
+ const valueRevealed = editor.isValueRevealed(index);
232
+ const removeRowLabel = `Remove environment variable ${rowName}`;
216
233
  function handlePaste(event) {
217
234
  if (editor.handlePaste(index, event.clipboardData.getData("text"))) event.preventDefault();
218
235
  }
@@ -235,19 +252,18 @@ function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props })
235
252
  children: keyLabel
236
253
  }),
237
254
  /* @__PURE__ */ jsx(Input, {
238
- "data-control-ui": "environment-variables",
239
- "data-control-family": "environment-variables",
240
- "data-slot": "key-input",
241
255
  id: keyInputId,
242
256
  size: "sm",
243
257
  value: row.key,
244
258
  placeholder: keyPlaceholder,
245
259
  disabled: isDisabled,
246
260
  "aria-invalid": Boolean(keyError),
261
+ "aria-describedby": keyError ? keyErrorId : void 0,
247
262
  onChange: (event) => editor.updateRow(index, { key: event.target.value }),
248
263
  onPaste: handlePaste
249
264
  }),
250
265
  keyError ? /* @__PURE__ */ jsx("p", {
266
+ id: keyErrorId,
251
267
  "data-control-ui": "environment-variables",
252
268
  "data-control-family": "environment-variables",
253
269
  "data-slot": "field-error",
@@ -267,35 +283,30 @@ function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props })
267
283
  children: valueLabel
268
284
  }),
269
285
  /* @__PURE__ */ jsxs(InputGroup, {
270
- "data-control-ui": "environment-variables",
271
- "data-control-family": "environment-variables",
272
- "data-slot": "value-group",
273
- "data-invalid": valueError ? "true" : void 0,
274
286
  size: "sm",
275
- children: [/* @__PURE__ */ jsx(InputGroupInput, {
276
- "data-control-ui": "environment-variables",
277
- "data-control-family": "environment-variables",
278
- "data-slot": "value-input",
287
+ children: [/* @__PURE__ */ jsx(Input, {
279
288
  id: valueInputId,
280
289
  value: row.value,
281
290
  placeholder: valuePlaceholder,
282
291
  disabled: isDisabled,
283
292
  "aria-invalid": Boolean(valueError),
284
- type: editor.isValueRevealed(index) ? "text" : "password",
293
+ "aria-describedby": valueError ? valueErrorId : void 0,
294
+ type: valueRevealed ? "text" : "password",
285
295
  onChange: (event) => editor.updateRow(index, { value: event.target.value }),
286
296
  onPaste: handlePaste
287
297
  }), /* @__PURE__ */ jsx(InputGroupAddon, {
288
- className: "pr-1",
298
+ className: "pe-1",
289
299
  children: /* @__PURE__ */ jsx(Button, {
290
300
  type: "button",
291
301
  variant: "quiet",
292
302
  size: "sm",
293
303
  iconOnly: true,
294
304
  disabled: isDisabled,
295
- "aria-label": revealValueLabel,
296
- title: revealValueLabel,
305
+ "aria-label": revealValueLabel(rowName),
306
+ "aria-pressed": valueRevealed,
307
+ title: revealValueLabel(rowName),
297
308
  onClick: () => editor.toggleValueVisibility(index),
298
- children: editor.isValueRevealed(index) ? /* @__PURE__ */ jsx(EyeOffIcon, {
309
+ children: valueRevealed ? /* @__PURE__ */ jsx(EyeOffIcon, {
299
310
  "aria-hidden": "true",
300
311
  className: "size-3.5"
301
312
  }) : /* @__PURE__ */ jsx(EyeIcon, {
@@ -306,6 +317,7 @@ function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props })
306
317
  })]
307
318
  }),
308
319
  valueError ? /* @__PURE__ */ jsx("p", {
320
+ id: valueErrorId,
309
321
  "data-control-ui": "environment-variables",
310
322
  "data-control-family": "environment-variables",
311
323
  "data-slot": "field-error",
@@ -322,7 +334,7 @@ function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props })
322
334
  disabled,
323
335
  "aria-label": removeRowLabel,
324
336
  title: "Remove variable",
325
- className: "absolute top-2 right-1 sm:static sm:justify-self-end sm:self-start",
337
+ className: "absolute top-2 end-1 sm:static sm:justify-self-end sm:self-start",
326
338
  onClick: () => editor.removeRow(index),
327
339
  children: /* @__PURE__ */ jsx(Trash2Icon, {
328
340
  "aria-hidden": "true",
@@ -371,14 +383,14 @@ function EnvironmentVariablesResetButton({ children = "Reset", variant = "quiet"
371
383
  });
372
384
  }
373
385
  function EnvironmentVariablesSubmitButton({ children = "Save", variant = "solid", tone = "primary", size = "sm", disabled, ...props }) {
374
- const { editor, disabled: contextDisabled, readOnly } = useEnvironmentVariablesContext("EnvironmentVariables.SubmitButton");
386
+ const { editor, disabled: contextDisabled, readOnly, pending } = useEnvironmentVariablesContext("EnvironmentVariables.SubmitButton");
375
387
  if (readOnly) return null;
376
388
  return /* @__PURE__ */ jsxs(Button, {
377
389
  type: "submit",
378
390
  variant,
379
391
  tone,
380
392
  size,
381
- disabled: disabled ?? (contextDisabled || editor.hasDuplicateKeys),
393
+ disabled: disabled ?? (contextDisabled || pending || editor.hasDuplicateKeys),
382
394
  ...props,
383
395
  children: [/* @__PURE__ */ jsx(SaveIcon, {
384
396
  "aria-hidden": "true",
@@ -449,7 +461,7 @@ function EnvironmentVariablesReadOnlyList({ rows, emptyMessage = "No environment
449
461
  }))
450
462
  });
451
463
  }
452
- function EnvironmentVariablesReadOnlyItem({ name, value = "********", revealed = false, className, ...props }) {
464
+ function EnvironmentVariablesReadOnlyItem({ name, value, revealed = false, hiddenValueLabel = "Hidden value", className, ...props }) {
453
465
  return /* @__PURE__ */ jsxs("div", {
454
466
  "data-control-ui": "environment-variables",
455
467
  "data-control-family": "environment-variables",
@@ -461,13 +473,21 @@ function EnvironmentVariablesReadOnlyItem({ name, value = "********", revealed =
461
473
  "data-control-family": "environment-variables",
462
474
  "data-slot": "readonly-key",
463
475
  className: "min-w-0 truncate",
476
+ title: typeof name === "string" ? name : void 0,
464
477
  children: name
465
478
  }), /* @__PURE__ */ jsx("span", {
466
479
  "data-control-ui": "environment-variables",
467
480
  "data-control-family": "environment-variables",
468
481
  "data-slot": "readonly-value",
469
482
  className: "min-w-0 truncate",
470
- children: revealed ? value : "********"
483
+ title: revealed && typeof value === "string" ? value : void 0,
484
+ children: revealed ? value : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
485
+ "aria-hidden": "true",
486
+ children: "••••••••"
487
+ }), /* @__PURE__ */ jsx("span", {
488
+ className: "sr-only",
489
+ children: hiddenValueLabel
490
+ })] })
471
491
  })]
472
492
  });
473
493
  }
@@ -0,0 +1,32 @@
1
+ import { FilterBarDraft, FilterBarField, FilterBarItem, FilterBarOperator, FilterBarScalar, FilterBarStage, FilterBarValue } from "./types.js";
2
+ import { CSSProperties, KeyboardEvent } from "react";
3
+ //#region src/filter-bar/context.d.ts
4
+ export type FilterBarExit = {
5
+ item: FilterBarItem;
6
+ style: CSSProperties;
7
+ };
8
+ export type FilterBarContextValue = {
9
+ fields: readonly FilterBarField[];
10
+ operators: readonly FilterBarOperator[];
11
+ items: readonly FilterBarItem[];
12
+ draft: FilterBarDraft | null;
13
+ exits: FilterBarExit[];
14
+ disabled: boolean;
15
+ readOnly: boolean;
16
+ register: (key: string, element: HTMLElement | null) => void;
17
+ start: (owner: string, anchor: HTMLElement, stage?: FilterBarStage, item?: FilterBarItem, query?: string) => void;
18
+ close: (restoreFocus?: boolean) => void;
19
+ back: () => void;
20
+ setQuery: (query: string) => void;
21
+ selectField: (fieldId: string) => void;
22
+ selectOperator: (operatorId: string) => void;
23
+ selectValues: (values: FilterBarScalar[]) => void;
24
+ commit: (value: FilterBarValue) => void;
25
+ remove: (item: FilterBarItem) => void;
26
+ clear: () => void;
27
+ releaseExit: (id: string) => void;
28
+ navigate: (event: KeyboardEvent<HTMLElement>, itemId?: string, part?: string) => void;
29
+ };
30
+ export declare const FilterBarContext: import("react").Context<FilterBarContextValue | null>;
31
+ export declare function useFilterBarContext(): FilterBarContextValue;
32
+ //#endregion
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { createContext, use } from "react";
3
+ //#region src/filter-bar/context.tsx
4
+ const FilterBarContext = createContext(null);
5
+ function useFilterBarContext() {
6
+ const context = use(FilterBarContext);
7
+ if (!context) throw new Error("FilterBar parts must be used inside FilterBar.Root.");
8
+ return context;
9
+ }
10
+ //#endregion
11
+ export { FilterBarContext, useFilterBarContext };
@@ -0,0 +1,14 @@
1
+ import { ComboboxInputProps } from "../ui/combobox.js";
2
+ import { ReactNode, RefObject } from "react";
3
+ //#region src/filter-bar/editor.d.ts
4
+ export declare function FilterBarEditor({ owner, mode, anchorRef, children }: {
5
+ owner: string;
6
+ mode: "input" | "button";
7
+ anchorRef?: RefObject<HTMLInputElement | null>;
8
+ children: ReactNode;
9
+ }): import("react").JSX.Element;
10
+ export declare function FilterBarInlineInput({ owner, inputRef, className, ...props }: Omit<ComboboxInputProps, "ref"> & {
11
+ owner: string;
12
+ inputRef: RefObject<HTMLInputElement | null>;
13
+ }): import("react").JSX.Element;
14
+ //#endregion
@@ -0,0 +1,216 @@
1
+ "use client";
2
+ import { Button } from "../ui/button.js";
3
+ import { useFilterBarContext } from "./context.js";
4
+ import { allowsCustomValue, fieldOperators, fieldOptions, parseCustomValue } from "./model.js";
5
+ import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "../ui/combobox.js";
6
+ import { useRef } from "react";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/filter-bar/editor.tsx
9
+ function valueChoice(value, label) {
10
+ return {
11
+ id: `${typeof value}:${value}`,
12
+ value,
13
+ label
14
+ };
15
+ }
16
+ function sameChoice(first, second) {
17
+ return first.id === second.id;
18
+ }
19
+ function valueChoices(field, query) {
20
+ const choices = fieldOptions(field).map((option) => ({
21
+ ...valueChoice(option.value, option.label),
22
+ icon: option.icon,
23
+ disabled: option.disabled
24
+ }));
25
+ const customValue = allowsCustomValue(field) ? parseCustomValue(field, query) : void 0;
26
+ if (customValue !== void 0 && !choices.some((choice) => choice.value === customValue)) choices.push(valueChoice(customValue, `Use “${customValue}”`));
27
+ return choices;
28
+ }
29
+ function editorChoices(context, draft) {
30
+ if (!draft || draft.stage === "field") return context.fields.filter((candidate) => fieldOperators(candidate, context.operators).length > 0).map((candidate) => ({
31
+ id: candidate.id,
32
+ label: candidate.label,
33
+ icon: candidate.icon
34
+ }));
35
+ const field = context.fields.find((candidate) => candidate.id === draft.fieldId);
36
+ if (!field) return [];
37
+ if (draft.stage === "operator") return fieldOperators(field, context.operators).map((operator) => ({
38
+ id: operator.id,
39
+ label: operator.label,
40
+ ariaLabel: operator.ariaLabel
41
+ }));
42
+ return valueChoices(field, draft.query);
43
+ }
44
+ function searchLabel(stage, field) {
45
+ if (stage === "field") return "Filter field";
46
+ if (stage === "operator") return "Filter operator";
47
+ return `${field?.label ?? "Filter"} value`;
48
+ }
49
+ function statusId(owner) {
50
+ return `${owner}-filter-status`;
51
+ }
52
+ function handleSelection(context, draft, choice) {
53
+ if (!choice || !draft) return;
54
+ const field = context.fields.find((candidate) => candidate.id === draft.fieldId);
55
+ if (draft.stage === "value" && (field?.loading || field?.error)) return;
56
+ if (Array.isArray(choice)) context.selectValues(choice.flatMap((selected) => selected.value === void 0 ? [] : [selected.value]));
57
+ else if (draft.stage === "field") context.selectField(choice.id);
58
+ else if (draft.stage === "operator") context.selectOperator(choice.id);
59
+ else if (choice.value !== void 0) context.commit(choice.value);
60
+ }
61
+ function handleEditorKeyDown(context, draft, event) {
62
+ if (event.nativeEvent.isComposing) return false;
63
+ const backwards = event.key === "Escape" || event.key === "Backspace" && draft?.query === "";
64
+ if (draft && backwards) {
65
+ event.preventDefault();
66
+ event.stopPropagation();
67
+ context.back();
68
+ return true;
69
+ }
70
+ const operator = context.operators.find((candidate) => candidate.id === draft?.operatorId);
71
+ const field = context.fields.find((candidate) => candidate.id === draft?.fieldId);
72
+ if (!(draft?.stage === "value" && operator?.arity === "many" && event.key === "Enter" && (event.metaKey || event.ctrlKey)) || !draft || draft.values.length === 0 || field?.loading || field?.error) return false;
73
+ event.preventDefault();
74
+ context.commit(draft.values);
75
+ return true;
76
+ }
77
+ function EditorOptions({ field, invalidNumber, owner }) {
78
+ if (field?.loading || field?.error) return /* @__PURE__ */ jsx("div", {
79
+ id: statusId(owner),
80
+ className: "p-3 text-caption",
81
+ children: field.error ?? "Loading values…"
82
+ });
83
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ComboboxEmpty, { children: invalidNumber ? "Enter a number, like 42 or 3.5." : "No matching options." }), /* @__PURE__ */ jsx(ComboboxList, { children: (choice) => /* @__PURE__ */ jsx(ComboboxItem, {
84
+ value: choice,
85
+ disabled: choice.disabled,
86
+ "aria-label": choice.ariaLabel,
87
+ children: /* @__PURE__ */ jsxs("span", {
88
+ className: "flex min-w-0 items-center gap-2",
89
+ children: [choice.icon, /* @__PURE__ */ jsx("span", {
90
+ className: "truncate",
91
+ title: choice.label,
92
+ children: choice.label
93
+ })]
94
+ })
95
+ }, choice.id) })] });
96
+ }
97
+ function handleOpenChange(context, draft, anchorRef, owner, open, eventDetails) {
98
+ if (open && !draft && anchorRef?.current && eventDetails.reason !== "input-change") context.start(owner, anchorRef.current);
99
+ if (open || !draft) return;
100
+ if (eventDetails.reason === "item-press" || eventDetails.reason === "escape-key") eventDetails.cancel();
101
+ else context.close(false);
102
+ }
103
+ function editorView(context, draft) {
104
+ const field = context.fields.find((candidate) => candidate.id === draft?.fieldId);
105
+ const operator = context.operators.find((candidate) => candidate.id === draft?.operatorId);
106
+ const stage = draft?.stage ?? "field";
107
+ const many = stage === "value" && operator?.arity === "many";
108
+ const query = draft?.query ?? "";
109
+ const choices = editorChoices(context, draft);
110
+ const selected = (draft?.values ?? []).map((value) => choices.find((choice) => choice.value === value) ?? valueChoice(value, field?.formatValue?.(value) ?? String(value)));
111
+ const customNumber = field?.type === "number" && allowsCustomValue(field);
112
+ return {
113
+ field,
114
+ stage,
115
+ many,
116
+ query,
117
+ choices,
118
+ selected,
119
+ invalidNumber: stage === "value" && Boolean(customNumber) && query.trim() !== "" && field !== void 0 && parseCustomValue(field, query) === void 0,
120
+ remote: stage === "value" && field?.optionsMode === "remote",
121
+ unavailable: stage === "value" && Boolean(field?.loading || field?.error),
122
+ label: searchLabel(stage, field),
123
+ described: stage === "value" && Boolean(field?.error)
124
+ };
125
+ }
126
+ function FilterBarEditor({ owner, mode, anchorRef, children }) {
127
+ const context = useFilterBarContext();
128
+ const searchRef = useRef(null);
129
+ const draft = context.draft?.owner === owner ? context.draft : null;
130
+ const { field, stage, many, query, choices, selected, invalidNumber, remote, unavailable, label, described } = editorView(context, draft);
131
+ const canCommitMany = selected.length > 0 && !unavailable && !context.disabled && !context.readOnly;
132
+ return /* @__PURE__ */ jsxs(Combobox, {
133
+ items: choices,
134
+ multiple: many,
135
+ value: many ? selected : null,
136
+ inputValue: query,
137
+ open: draft !== null,
138
+ disabled: context.disabled,
139
+ readOnly: context.readOnly,
140
+ modal: false,
141
+ isItemEqualToValue: sameChoice,
142
+ filter: remote ? null : void 0,
143
+ onValueChange: (choice) => handleSelection(context, draft, choice),
144
+ onInputValueChange: (nextQuery, eventDetails) => {
145
+ if (eventDetails.reason !== "input-change" && eventDetails.reason !== "input-clear") return;
146
+ if (draft) context.setQuery(nextQuery);
147
+ else if (anchorRef?.current) context.start(owner, anchorRef.current, "field", void 0, nextQuery);
148
+ },
149
+ onOpenChange: (open, eventDetails) => handleOpenChange(context, draft, anchorRef, owner, open, eventDetails),
150
+ children: [children, /* @__PURE__ */ jsxs(ComboboxContent, {
151
+ anchor: draft?.anchor,
152
+ initialFocus: mode === "input" ? false : searchRef,
153
+ finalFocus: false,
154
+ className: "w-72 max-h-(--available-height) overflow-y-auto",
155
+ onKeyDown: (event) => {
156
+ if (event.key === "Escape" && !event.defaultPrevented) {
157
+ event.preventDefault();
158
+ context.back();
159
+ }
160
+ },
161
+ children: [
162
+ mode === "button" && /* @__PURE__ */ jsx("div", {
163
+ className: "p-2",
164
+ children: /* @__PURE__ */ jsx(ComboboxInput, {
165
+ ref: searchRef,
166
+ size: "sm",
167
+ "aria-label": label,
168
+ "aria-describedby": described ? statusId(owner) : void 0,
169
+ placeholder: label,
170
+ inputMode: field?.type === "number" && stage === "value" ? "decimal" : void 0,
171
+ onKeyDown: (event) => handleEditorKeyDown(context, draft, event)
172
+ })
173
+ }),
174
+ /* @__PURE__ */ jsx(EditorOptions, {
175
+ field: stage === "value" ? field : void 0,
176
+ invalidNumber,
177
+ owner
178
+ }),
179
+ many && /* @__PURE__ */ jsx("div", {
180
+ className: "flex justify-end p-2",
181
+ children: /* @__PURE__ */ jsx(Button, {
182
+ disabled: !canCommitMany,
183
+ onClick: () => context.commit(draft?.values ?? []),
184
+ children: "Apply"
185
+ })
186
+ })
187
+ ]
188
+ })]
189
+ });
190
+ }
191
+ function FilterBarInlineInput({ owner, inputRef, className, ...props }) {
192
+ const context = useFilterBarContext();
193
+ const draft = context.draft?.owner === owner ? context.draft : null;
194
+ const { label, described } = editorView(context, draft);
195
+ function handleKeyDown(event) {
196
+ props.onKeyDown?.(event);
197
+ if (event.defaultPrevented || event.nativeEvent.isComposing) return;
198
+ if (handleEditorKeyDown(context, draft, event) || event.currentTarget.value !== "") return;
199
+ const last = context.items.at(-1);
200
+ if (event.key === "Backspace" && !draft && last) {
201
+ event.preventDefault();
202
+ context.remove(last);
203
+ } else context.navigate(event);
204
+ }
205
+ return /* @__PURE__ */ jsx(ComboboxInput, {
206
+ ref: inputRef,
207
+ size: "sm",
208
+ className,
209
+ ...props,
210
+ "aria-label": draft ? label : props["aria-label"],
211
+ "aria-describedby": described ? statusId(owner) : props["aria-describedby"],
212
+ onKeyDown: handleKeyDown
213
+ });
214
+ }
215
+ //#endregion
216
+ export { FilterBarEditor, FilterBarInlineInput };
@@ -0,0 +1,10 @@
1
+ import { FilterBarField, FilterBarItem, FilterBarOperator, FilterBarOption, FilterBarScalar } from "./types.js";
2
+ //#region src/filter-bar/model.d.ts
3
+ export declare function fieldOperators(field: FilterBarField, operators: readonly FilterBarOperator[]): FilterBarOperator[];
4
+ export declare function fieldOptions(field: FilterBarField): readonly FilterBarOption[];
5
+ export declare function parseCustomValue(field: FilterBarField, query: string): FilterBarScalar | undefined;
6
+ export declare function allowsCustomValue(field: FilterBarField): boolean;
7
+ export declare function formatFilterValue(item: FilterBarItem, field: FilterBarField | undefined): string;
8
+ export declare function describeFilter(item: FilterBarItem, fields: readonly FilterBarField[], operators: readonly FilterBarOperator[]): string;
9
+ export declare function filterNavigationKeys(items: readonly FilterBarItem[]): string[];
10
+ //#endregion
@@ -0,0 +1,57 @@
1
+ //#region src/filter-bar/model.ts
2
+ const booleanOptions = [{
3
+ value: true,
4
+ label: "True"
5
+ }, {
6
+ value: false,
7
+ label: "False"
8
+ }];
9
+ function fieldOperators(field, operators) {
10
+ return operators.filter((operator) => !field.operators || field.operators.includes(operator.id));
11
+ }
12
+ function fieldOptions(field) {
13
+ return field.options ?? (field.type === "boolean" ? booleanOptions : []);
14
+ }
15
+ function parseCustomValue(field, query) {
16
+ if (!query.trim()) return void 0;
17
+ if (field.type === "number") {
18
+ if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(query.trim())) return void 0;
19
+ const number = Number(query);
20
+ return Number.isFinite(number) ? number : void 0;
21
+ }
22
+ if (field.type === "boolean") {
23
+ if (query.toLowerCase() === "true") return true;
24
+ if (query.toLowerCase() === "false") return false;
25
+ return;
26
+ }
27
+ return query.trim();
28
+ }
29
+ function allowsCustomValue(field) {
30
+ return field.allowCustomValue ?? (field.options === void 0 && field.type !== "boolean" && field.optionsMode !== "remote");
31
+ }
32
+ function formatFilterValue(item, field) {
33
+ if (item.value === null) return "";
34
+ return (Array.isArray(item.value) ? item.value : [item.value]).map((value) => field?.formatValue?.(value) ?? fieldOptionsForLabel(field, value)).join(", ");
35
+ }
36
+ function fieldOptionsForLabel(field, value) {
37
+ return (field ? fieldOptions(field).find((option) => option.value === value)?.label : void 0) ?? String(value);
38
+ }
39
+ function describeFilter(item, fields, operators) {
40
+ const field = fields.find((candidate) => candidate.id === item.fieldId);
41
+ const operator = operators.find((candidate) => candidate.id === item.operatorId);
42
+ return [
43
+ field?.label ?? item.fieldId,
44
+ operator?.ariaLabel ?? operator?.label,
45
+ formatFilterValue(item, field)
46
+ ].filter(Boolean).join(" ");
47
+ }
48
+ function filterNavigationKeys(items) {
49
+ return items.flatMap((item) => [
50
+ "field",
51
+ "operator",
52
+ ...item.value === null ? [] : ["value"],
53
+ "remove"
54
+ ].map((segment) => `${item.id}:${segment}`));
55
+ }
56
+ //#endregion
57
+ export { allowsCustomValue, describeFilter, fieldOperators, fieldOptions, filterNavigationKeys, formatFilterValue, parseCustomValue };
@@ -0,0 +1,10 @@
1
+ //#region src/filter-bar/motion.d.ts
2
+ export declare function filterMotionEnabled(element: HTMLElement): boolean;
3
+ export declare function useFilterBarMotion(): {
4
+ register(key: string, element: HTMLElement | null): void;
5
+ capture(): void;
6
+ cancel: () => void;
7
+ play(): void;
8
+ rootRef: import("react").RefObject<HTMLFieldSetElement | null>;
9
+ };
10
+ //#endregion
@@ -0,0 +1,84 @@
1
+ "use client";
2
+ import { prefersReducedMotion, readDurationMs } from "../lib/motion.js";
3
+ import { useLayoutEffect, useRef, useState } from "react";
4
+ //#region src/filter-bar/motion.ts
5
+ function filterMotionEnabled(element) {
6
+ return !prefersReducedMotion(element) && readDurationMs(element, "--duration-fast") > 0;
7
+ }
8
+ function animateMovement(element, before, after, duration, easing) {
9
+ const x = before.left - after.left;
10
+ const y = before.top - after.top;
11
+ if (Math.abs(x) < 1 && Math.abs(y) < 1) return null;
12
+ return element.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: "translate(0, 0)" }], {
13
+ duration,
14
+ easing
15
+ });
16
+ }
17
+ function animateLayout(elements, before, after, animations, duration, easing) {
18
+ for (const [key, bounds] of after) {
19
+ const origin = before.get(key);
20
+ const element = elements.get(key);
21
+ if (!origin || !element) continue;
22
+ const animation = animateMovement(element, origin, bounds, duration, easing);
23
+ if (animation) animations.set(key, animation);
24
+ }
25
+ }
26
+ function useFilterBarMotion() {
27
+ const rootRef = useRef(null);
28
+ const [motion] = useState(() => {
29
+ const elements = /* @__PURE__ */ new Map();
30
+ const animations = /* @__PURE__ */ new Map();
31
+ let captured = null;
32
+ function measure() {
33
+ return new Map([...elements].map(([key, element]) => [key, element.getBoundingClientRect()]));
34
+ }
35
+ function cancel() {
36
+ for (const animation of animations.values()) animation.cancel();
37
+ animations.clear();
38
+ }
39
+ return {
40
+ register(key, element) {
41
+ if (element) elements.set(key, element);
42
+ else elements.delete(key);
43
+ },
44
+ capture() {
45
+ captured = measure();
46
+ },
47
+ cancel,
48
+ play() {
49
+ const root = rootRef.current;
50
+ if (!root || !filterMotionEnabled(root)) {
51
+ cancel();
52
+ captured = null;
53
+ return;
54
+ }
55
+ if (!captured) return;
56
+ const origin = captured;
57
+ captured = null;
58
+ cancel();
59
+ const next = measure();
60
+ const duration = readDurationMs(root, "--duration-fast");
61
+ const easing = getComputedStyle(root).getPropertyValue("--ease-standard").trim();
62
+ animateLayout(elements, origin, next, animations, duration, easing);
63
+ }
64
+ };
65
+ });
66
+ useLayoutEffect(() => motion.play());
67
+ useLayoutEffect(() => {
68
+ const media = window.matchMedia("(prefers-reduced-motion: reduce)");
69
+ function cancelReducedMotion() {
70
+ if (media.matches) motion.cancel();
71
+ }
72
+ media.addEventListener("change", cancelReducedMotion);
73
+ return () => {
74
+ media.removeEventListener("change", cancelReducedMotion);
75
+ motion.cancel();
76
+ };
77
+ }, [motion]);
78
+ return {
79
+ rootRef,
80
+ ...motion
81
+ };
82
+ }
83
+ //#endregion
84
+ export { filterMotionEnabled, useFilterBarMotion };