@tulipfarm/sprout 0.0.0-stage → 0.1.0

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 (306) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +183 -2
  3. package/dist/components/accordion.d.ts +28 -0
  4. package/dist/components/accordion.js +26 -0
  5. package/dist/components/alert-dialog.d.ts +60 -0
  6. package/dist/components/alert-dialog.js +69 -0
  7. package/dist/components/alert.d.ts +30 -0
  8. package/dist/components/alert.js +35 -0
  9. package/dist/components/aspect-ratio.d.ts +7 -0
  10. package/dist/components/aspect-ratio.js +6 -0
  11. package/dist/components/attachment.d.ts +118 -0
  12. package/dist/components/attachment.js +285 -0
  13. package/dist/components/avatar.d.ts +32 -0
  14. package/dist/components/avatar.js +73 -0
  15. package/dist/components/badge.d.ts +21 -0
  16. package/dist/components/badge.js +27 -0
  17. package/dist/components/brand-icon.d.ts +28 -0
  18. package/dist/components/brand-icon.js +78 -0
  19. package/dist/components/breadcrumb.d.ts +29 -0
  20. package/dist/components/breadcrumb.js +44 -0
  21. package/dist/components/button-group.d.ts +28 -0
  22. package/dist/components/button-group.js +36 -0
  23. package/dist/components/button.d.ts +79 -0
  24. package/dist/components/button.js +93 -0
  25. package/dist/components/calendar.d.ts +27 -0
  26. package/dist/components/calendar.js +107 -0
  27. package/dist/components/canvas.d.ts +78 -0
  28. package/dist/components/canvas.js +180 -0
  29. package/dist/components/card.d.ts +43 -0
  30. package/dist/components/card.js +42 -0
  31. package/dist/components/carousel.d.ts +57 -0
  32. package/dist/components/carousel.js +422 -0
  33. package/dist/components/chart.d.ts +112 -0
  34. package/dist/components/chart.js +172 -0
  35. package/dist/components/chat-confirmation.d.ts +42 -0
  36. package/dist/components/chat-confirmation.js +62 -0
  37. package/dist/components/chat-plan.d.ts +21 -0
  38. package/dist/components/chat-plan.js +35 -0
  39. package/dist/components/chat-sources.d.ts +64 -0
  40. package/dist/components/chat-sources.js +66 -0
  41. package/dist/components/checkbox.d.ts +16 -0
  42. package/dist/components/checkbox.js +17 -0
  43. package/dist/components/choice-list.d.ts +53 -0
  44. package/dist/components/choice-list.js +36 -0
  45. package/dist/components/code-block.d.ts +35 -0
  46. package/dist/components/code-block.js +95 -0
  47. package/dist/components/collapsible.d.ts +24 -0
  48. package/dist/components/collapsible.js +27 -0
  49. package/dist/components/combobox.d.ts +95 -0
  50. package/dist/components/combobox.js +106 -0
  51. package/dist/components/command.d.ts +94 -0
  52. package/dist/components/command.js +414 -0
  53. package/dist/components/context-menu.d.ts +57 -0
  54. package/dist/components/context-menu.js +60 -0
  55. package/dist/components/dialog.d.ts +60 -0
  56. package/dist/components/dialog.js +79 -0
  57. package/dist/components/direction.d.ts +6 -0
  58. package/dist/components/direction.js +6 -0
  59. package/dist/components/dropdown-menu.d.ts +62 -0
  60. package/dist/components/dropdown-menu.js +59 -0
  61. package/dist/components/field.d.ts +117 -0
  62. package/dist/components/field.js +150 -0
  63. package/dist/components/file-viewer.d.ts +120 -0
  64. package/dist/components/file-viewer.js +373 -0
  65. package/dist/components/form.d.ts +87 -0
  66. package/dist/components/form.js +55 -0
  67. package/dist/components/glass-banner.d.ts +27 -0
  68. package/dist/components/glass-banner.js +116 -0
  69. package/dist/components/gradient-banner.d.ts +37 -0
  70. package/dist/components/gradient-banner.js +43 -0
  71. package/dist/components/hover-card.d.ts +27 -0
  72. package/dist/components/hover-card.js +26 -0
  73. package/dist/components/input-group.d.ts +68 -0
  74. package/dist/components/input-group.js +88 -0
  75. package/dist/components/input-otp.d.ts +38 -0
  76. package/dist/components/input-otp.js +43 -0
  77. package/dist/components/input.d.ts +16 -0
  78. package/dist/components/input.js +18 -0
  79. package/dist/components/kbd.d.ts +12 -0
  80. package/dist/components/kbd.js +11 -0
  81. package/dist/components/label.d.ts +8 -0
  82. package/dist/components/label.js +10 -0
  83. package/dist/components/mention-list.d.ts +54 -0
  84. package/dist/components/mention-list.js +31 -0
  85. package/dist/components/menubar.d.ts +64 -0
  86. package/dist/components/menubar.js +68 -0
  87. package/dist/components/message-actions.d.ts +34 -0
  88. package/dist/components/message-actions.js +68 -0
  89. package/dist/components/message-response.d.ts +20 -0
  90. package/dist/components/message-response.js +46 -0
  91. package/dist/components/message-scroller.d.ts +71 -0
  92. package/dist/components/message-scroller.js +217 -0
  93. package/dist/components/message.d.ts +49 -0
  94. package/dist/components/message.js +59 -0
  95. package/dist/components/navigation-menu.d.ts +45 -0
  96. package/dist/components/navigation-menu.js +54 -0
  97. package/dist/components/notice-banner.d.ts +14 -0
  98. package/dist/components/notice-banner.js +11 -0
  99. package/dist/components/page-content.d.ts +7 -0
  100. package/dist/components/page-content.js +9 -0
  101. package/dist/components/page-header.d.ts +37 -0
  102. package/dist/components/page-header.js +53 -0
  103. package/dist/components/pagination.d.ts +29 -0
  104. package/dist/components/pagination.js +29 -0
  105. package/dist/components/plan-outline.d.ts +60 -0
  106. package/dist/components/plan-outline.js +108 -0
  107. package/dist/components/popover.d.ts +34 -0
  108. package/dist/components/popover.js +35 -0
  109. package/dist/components/progress.d.ts +27 -0
  110. package/dist/components/progress.js +35 -0
  111. package/dist/components/prompt-list.d.ts +23 -0
  112. package/dist/components/prompt-list.js +11 -0
  113. package/dist/components/questionnaire.d.ts +148 -0
  114. package/dist/components/questionnaire.js +1001 -0
  115. package/dist/components/radio-group.d.ts +16 -0
  116. package/dist/components/radio-group.js +21 -0
  117. package/dist/components/reasoning-effort.d.ts +24 -0
  118. package/dist/components/reasoning-effort.js +269 -0
  119. package/dist/components/resizable.d.ts +67 -0
  120. package/dist/components/resizable.js +536 -0
  121. package/dist/components/result-card.d.ts +58 -0
  122. package/dist/components/result-card.js +88 -0
  123. package/dist/components/review-card.d.ts +62 -0
  124. package/dist/components/review-card.js +49 -0
  125. package/dist/components/rich-text-editor.d.ts +51 -0
  126. package/dist/components/rich-text-editor.js +248 -0
  127. package/dist/components/schema-diagram.d.ts +52 -0
  128. package/dist/components/schema-diagram.js +226 -0
  129. package/dist/components/scroll-area.d.ts +22 -0
  130. package/dist/components/scroll-area.js +19 -0
  131. package/dist/components/search-dialog.d.ts +103 -0
  132. package/dist/components/search-dialog.js +64 -0
  133. package/dist/components/search-field.d.ts +17 -0
  134. package/dist/components/search-field.js +27 -0
  135. package/dist/components/select.d.ts +57 -0
  136. package/dist/components/select.js +57 -0
  137. package/dist/components/selection-slot.d.ts +18 -0
  138. package/dist/components/selection-slot.js +53 -0
  139. package/dist/components/separator.d.ts +48 -0
  140. package/dist/components/separator.js +54 -0
  141. package/dist/components/settings-list.d.ts +26 -0
  142. package/dist/components/settings-list.js +17 -0
  143. package/dist/components/sheet.d.ts +70 -0
  144. package/dist/components/sheet.js +77 -0
  145. package/dist/components/sidebar.d.ts +70 -0
  146. package/dist/components/sidebar.js +187 -0
  147. package/dist/components/skeleton.d.ts +31 -0
  148. package/dist/components/skeleton.js +32 -0
  149. package/dist/components/slider.d.ts +17 -0
  150. package/dist/components/slider.js +24 -0
  151. package/dist/components/spinner.d.ts +37 -0
  152. package/dist/components/spinner.js +43 -0
  153. package/dist/components/state-panel-samples.d.ts +25 -0
  154. package/dist/components/state-panel-samples.js +423 -0
  155. package/dist/components/state-panel.d.ts +35 -0
  156. package/dist/components/state-panel.js +29 -0
  157. package/dist/components/state-sample.d.ts +63 -0
  158. package/dist/components/state-sample.js +42 -0
  159. package/dist/components/switch.d.ts +12 -0
  160. package/dist/components/switch.js +14 -0
  161. package/dist/components/table-nuqs.d.ts +16 -0
  162. package/dist/components/table-nuqs.js +59 -0
  163. package/dist/components/table.d.ts +120 -0
  164. package/dist/components/table.js +663 -0
  165. package/dist/components/tabs.d.ts +37 -0
  166. package/dist/components/tabs.js +36 -0
  167. package/dist/components/textarea.d.ts +18 -0
  168. package/dist/components/textarea.js +19 -0
  169. package/dist/components/toast.d.ts +45 -0
  170. package/dist/components/toast.js +83 -0
  171. package/dist/components/toggle-group.d.ts +30 -0
  172. package/dist/components/toggle-group.js +39 -0
  173. package/dist/components/toggle.d.ts +27 -0
  174. package/dist/components/toggle.js +21 -0
  175. package/dist/components/tool-activity.d.ts +79 -0
  176. package/dist/components/tool-activity.js +226 -0
  177. package/dist/components/toolbar-status.d.ts +21 -0
  178. package/dist/components/toolbar-status.js +14 -0
  179. package/dist/components/tooltip.d.ts +31 -0
  180. package/dist/components/tooltip.js +99 -0
  181. package/dist/components/typed-text.d.ts +17 -0
  182. package/dist/components/typed-text.js +56 -0
  183. package/dist/index.d.ts +88 -0
  184. package/dist/index.js +88 -0
  185. package/dist/internal/brand-glyphs.d.ts +342 -0
  186. package/dist/internal/brand-glyphs.js +238 -0
  187. package/dist/internal/classes.d.ts +3 -0
  188. package/dist/internal/classes.js +20 -0
  189. package/dist/internal/fieldset.d.ts +6 -0
  190. package/dist/internal/fieldset.js +38 -0
  191. package/dist/internal/file-kind.d.ts +4 -0
  192. package/dist/internal/file-kind.js +62 -0
  193. package/dist/internal/languages.d.ts +7 -0
  194. package/dist/internal/languages.js +1010 -0
  195. package/dist/internal/message-scroller.d.ts +102 -0
  196. package/dist/internal/message-scroller.js +637 -0
  197. package/dist/internal/pressable.d.ts +2 -0
  198. package/dist/internal/pressable.js +29 -0
  199. package/dist/internal/syntax.d.ts +14 -0
  200. package/dist/internal/syntax.js +137 -0
  201. package/dist/styles/accordion.css +102 -0
  202. package/dist/styles/alert-dialog.css +198 -0
  203. package/dist/styles/alert.css +149 -0
  204. package/dist/styles/aspect-ratio.css +8 -0
  205. package/dist/styles/attachment.css +720 -0
  206. package/dist/styles/avatar.css +186 -0
  207. package/dist/styles/badge.css +178 -0
  208. package/dist/styles/brand-icon.css +104 -0
  209. package/dist/styles/breadcrumb.css +121 -0
  210. package/dist/styles/button-group.css +113 -0
  211. package/dist/styles/button.css +380 -0
  212. package/dist/styles/calendar.css +295 -0
  213. package/dist/styles/canvas.css +327 -0
  214. package/dist/styles/card.css +146 -0
  215. package/dist/styles/carousel.css +107 -0
  216. package/dist/styles/chart.css +227 -0
  217. package/dist/styles/chat-confirmation.css +29 -0
  218. package/dist/styles/chat-plan.css +109 -0
  219. package/dist/styles/chat-sources.css +191 -0
  220. package/dist/styles/checkbox.css +123 -0
  221. package/dist/styles/choice-list.css +343 -0
  222. package/dist/styles/code-block.css +211 -0
  223. package/dist/styles/collapsible.css +72 -0
  224. package/dist/styles/combobox.css +347 -0
  225. package/dist/styles/command.css +233 -0
  226. package/dist/styles/context-menu.css +151 -0
  227. package/dist/styles/dialog.css +237 -0
  228. package/dist/styles/dropdown-menu.css +150 -0
  229. package/dist/styles/field.css +341 -0
  230. package/dist/styles/file-viewer.css +278 -0
  231. package/dist/styles/form.css +155 -0
  232. package/dist/styles/glass-banner.css +308 -0
  233. package/dist/styles/gradient-banner.css +256 -0
  234. package/dist/styles/hover-card.css +95 -0
  235. package/dist/styles/index.css +262 -0
  236. package/dist/styles/input-group.css +290 -0
  237. package/dist/styles/input-otp.css +138 -0
  238. package/dist/styles/input.css +99 -0
  239. package/dist/styles/kbd.css +59 -0
  240. package/dist/styles/label.css +89 -0
  241. package/dist/styles/mention-list.css +166 -0
  242. package/dist/styles/menubar.css +226 -0
  243. package/dist/styles/message-actions.css +83 -0
  244. package/dist/styles/message-response.css +176 -0
  245. package/dist/styles/message-scroller.css +136 -0
  246. package/dist/styles/message.css +310 -0
  247. package/dist/styles/navigation-menu.css +291 -0
  248. package/dist/styles/notice-banner.css +68 -0
  249. package/dist/styles/page-content.css +21 -0
  250. package/dist/styles/page-header.css +156 -0
  251. package/dist/styles/pagination.css +72 -0
  252. package/dist/styles/plan-outline.css +393 -0
  253. package/dist/styles/popover.css +140 -0
  254. package/dist/styles/progress.css +93 -0
  255. package/dist/styles/prompt-list.css +165 -0
  256. package/dist/styles/questionnaire.css +323 -0
  257. package/dist/styles/radio-group.css +120 -0
  258. package/dist/styles/reasoning-effort.css +157 -0
  259. package/dist/styles/resizable.css +108 -0
  260. package/dist/styles/result-card.css +215 -0
  261. package/dist/styles/review-card.css +235 -0
  262. package/dist/styles/rich-text-editor.css +169 -0
  263. package/dist/styles/schema-diagram.css +419 -0
  264. package/dist/styles/scroll-area.css +77 -0
  265. package/dist/styles/search-dialog.css +363 -0
  266. package/dist/styles/search-field.css +17 -0
  267. package/dist/styles/select.css +285 -0
  268. package/dist/styles/selection-slot.css +31 -0
  269. package/dist/styles/separator.css +206 -0
  270. package/dist/styles/settings-list.css +81 -0
  271. package/dist/styles/sheet.css +361 -0
  272. package/dist/styles/sidebar.css +363 -0
  273. package/dist/styles/skeleton.css +80 -0
  274. package/dist/styles/slider.css +142 -0
  275. package/dist/styles/spinner.css +39 -0
  276. package/dist/styles/state-panel-samples.css +41 -0
  277. package/dist/styles/state-panel.css +182 -0
  278. package/dist/styles/state-sample.css +285 -0
  279. package/dist/styles/switch.css +128 -0
  280. package/dist/styles/table.css +604 -0
  281. package/dist/styles/tabs.css +223 -0
  282. package/dist/styles/textarea.css +78 -0
  283. package/dist/styles/themes/chatgpt.css +125 -0
  284. package/dist/styles/themes/claude.css +125 -0
  285. package/dist/styles/themes/cloudflare.css +125 -0
  286. package/dist/styles/themes/discord.css +125 -0
  287. package/dist/styles/themes/github.css +125 -0
  288. package/dist/styles/themes/grok.css +125 -0
  289. package/dist/styles/themes/linear.css +126 -0
  290. package/dist/styles/themes/neutral.css +125 -0
  291. package/dist/styles/themes/notion.css +125 -0
  292. package/dist/styles/themes/perplexity.css +125 -0
  293. package/dist/styles/themes/raycast.css +125 -0
  294. package/dist/styles/themes/spotify.css +125 -0
  295. package/dist/styles/themes/stripe.css +125 -0
  296. package/dist/styles/themes/supabase.css +125 -0
  297. package/dist/styles/themes/tulipfarm.css +125 -0
  298. package/dist/styles/themes/vercel.css +126 -0
  299. package/dist/styles/toast.css +197 -0
  300. package/dist/styles/toggle-group.css +161 -0
  301. package/dist/styles/toggle.css +119 -0
  302. package/dist/styles/tool-activity.css +396 -0
  303. package/dist/styles/toolbar-status.css +52 -0
  304. package/dist/styles/tooltip.css +122 -0
  305. package/dist/styles/typed-text.css +17 -0
  306. package/package.json +443 -4
@@ -0,0 +1,226 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Background, BaseEdge, EdgeLabelRenderer, getSmoothStepPath, Handle, Panel, Position, ReactFlow, ReactFlowProvider, useNodesInitialized, useReactFlow, useStore, } from "@xyflow/react";
4
+ import { useEffect, useId, useMemo, useSyncExternalStore, } from "react";
5
+ import { Add } from "reicon-react/icons/Add";
6
+ import { Key } from "reicon-react/icons/Key";
7
+ import { Minus } from "reicon-react/icons/Minus";
8
+ import { Scan } from "reicon-react/icons/Scan";
9
+ import { cn } from "../internal/classes.js";
10
+ import { Badge } from "./badge.js";
11
+ import { Button } from "./button.js";
12
+ export const schemaTableStateNames = [
13
+ "new",
14
+ "changed",
15
+ "unchanged",
16
+ "linked",
17
+ ];
18
+ export const schemaChangeNames = ["added", "changed", "removed"];
19
+ export const schemaDiagramLayoutNames = [
20
+ "auto",
21
+ "horizontal",
22
+ "vertical",
23
+ ];
24
+ const tableWidth = 264;
25
+ const columnGap = 80;
26
+ const rowGap = 48;
27
+ const minZoom = 0.2;
28
+ const maxZoom = 1.5;
29
+ const fitOptions = { padding: 0.15, maxZoom: 1 };
30
+ const panStep = 48;
31
+ const tableHeight = (table) => 44 +
32
+ (table.fields.length === 0
33
+ ? 32
34
+ : table.fields.reduce((sum, field) => sum + (field.detail ? 48 : 32), 0));
35
+ const stateBadge = {
36
+ new: { label: "New", variant: "success" },
37
+ changed: { label: "Changed", variant: "warning" },
38
+ linked: { label: "Existing", variant: "secondary" },
39
+ unchanged: null,
40
+ };
41
+ /** Referenced tables sit one column right of (or below) the tables that link to them. */
42
+ function layout(tables, links, direction) {
43
+ const node = (table, x, y) => ({
44
+ id: table.id,
45
+ type: "table",
46
+ position: { x, y },
47
+ data: { table, direction },
48
+ ariaRole: "group",
49
+ ariaLabel: `${table.label} table`,
50
+ domAttributes: { "aria-roledescription": "table card" },
51
+ sourcePosition: direction === "vertical" ? Position.Bottom : Position.Right,
52
+ targetPosition: direction === "vertical" ? Position.Top : Position.Left,
53
+ });
54
+ if (direction === "vertical") {
55
+ const ordered = [];
56
+ const seen = new Set();
57
+ const add = (table) => {
58
+ if (table && !seen.has(table.id)) {
59
+ ordered.push(table);
60
+ seen.add(table.id);
61
+ }
62
+ };
63
+ for (const link of links)
64
+ for (const id of [link.source, link.target])
65
+ add(tables.find((table) => table.id === id));
66
+ for (const table of tables)
67
+ add(table);
68
+ let y = 0;
69
+ return ordered.map((table) => {
70
+ const placed = node(table, 0, y);
71
+ y += tableHeight(table) + rowGap;
72
+ return placed;
73
+ });
74
+ }
75
+ const depth = new Map(tables.map((table) => [table.id, 0]));
76
+ for (let pass = 0; pass < tables.length; pass++) {
77
+ let moved = false;
78
+ for (const link of links) {
79
+ const next = (depth.get(link.source) ?? 0) + 1;
80
+ if (link.source !== link.target &&
81
+ next > (depth.get(link.target) ?? 0) &&
82
+ next < tables.length) {
83
+ depth.set(link.target, next);
84
+ moved = true;
85
+ }
86
+ }
87
+ if (!moved)
88
+ break;
89
+ }
90
+ const columns = new Map();
91
+ return tables.map((table) => {
92
+ const column = depth.get(table.id) ?? 0;
93
+ const y = columns.get(column) ?? 0;
94
+ columns.set(column, y + tableHeight(table) + rowGap);
95
+ return node(table, column * (tableWidth + columnGap), y);
96
+ });
97
+ }
98
+ function TableCard({ data: { table, direction } }) {
99
+ const badge = stateBadge[table.state];
100
+ const vertical = direction === "vertical";
101
+ return (_jsxs("section", { "data-slot": "schema-table", "data-state": table.state, className: "sprout-schema-table", children: [_jsx(Handle, { type: "target", position: vertical ? Position.Top : Position.Left, isConnectable: false, className: "sprout-schema-handle sprout-schema-handle-target" }), _jsxs("header", { className: "sprout-schema-table-header", children: [_jsx("h3", { className: "sprout-schema-table-title", title: table.label, children: table.label }), badge && _jsx(Badge, { variant: badge.variant, children: badge.label })] }), table.fields.length === 0 ? (_jsx("p", { className: "sprout-schema-table-empty", children: table.state === "linked" ? "Fields not shown" : "No fields" })) : (_jsx("ul", { className: "sprout-schema-fields", children: table.fields.map((field) => (_jsxs("li", { "data-slot": "schema-field", "data-change": field.change, className: "sprout-schema-field", children: [_jsxs("div", { className: "sprout-schema-field-line", children: [_jsx("span", { className: "sprout-schema-field-key", children: field.unique && (_jsx(Key, { size: 11, "aria-label": "Unique", role: "img" })) }), _jsxs("span", { className: "sprout-schema-field-name", title: field.label, children: [field.label, field.required && (_jsxs("span", { className: "sprout-schema-field-required", children: [_jsx("span", { "aria-hidden": "true", children: " *" }), _jsx("span", { className: "sprout-schema-sr-only", children: ", required" })] }))] }), _jsx("span", { className: "sprout-schema-field-type", children: field.type }), field.change && (_jsxs("span", { className: "sprout-schema-sr-only", children: [", ", field.change] }))] }), field.detail && (_jsx("p", { className: "sprout-schema-field-detail", title: field.detail, children: field.detail })), _jsx(Handle, { id: field.id, type: "source", position: vertical ? Position.Bottom : Position.Right, isConnectable: false, className: "sprout-schema-handle" })] }, field.id))) }))] }));
102
+ }
103
+ function LinkLine({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data, }) {
104
+ const [path, labelX, labelY] = getSmoothStepPath({
105
+ sourceX,
106
+ sourceY,
107
+ targetX,
108
+ targetY,
109
+ sourcePosition,
110
+ targetPosition,
111
+ borderRadius: 12,
112
+ offset: 24,
113
+ });
114
+ return (_jsxs(_Fragment, { children: [_jsx(BaseEdge, { id: id, path: path }), data?.label && (_jsx(EdgeLabelRenderer, { children: _jsx("span", { "aria-hidden": "true", className: "nodrag nopan sprout-schema-link-label", style: {
115
+ transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
116
+ }, children: data.label }) }))] }));
117
+ }
118
+ const nodeTypes = { table: TableCard };
119
+ const edgeTypes = { link: LinkLine };
120
+ const mobileQuery = "(max-width: 767px)";
121
+ const motionQuery = "(prefers-reduced-motion: reduce)";
122
+ function subscribeMobile(callback) {
123
+ const media = window.matchMedia(mobileQuery);
124
+ media.addEventListener("change", callback);
125
+ return () => media.removeEventListener("change", callback);
126
+ }
127
+ function useViewportMobile() {
128
+ return useSyncExternalStore(subscribeMobile, () => window.matchMedia(mobileQuery).matches, () => false);
129
+ }
130
+ const motion = (ms) => typeof window !== "undefined" && window.matchMedia(motionQuery).matches
131
+ ? 0
132
+ : ms;
133
+ // The canvas panel animates open, so the first fit sees a narrower pane than the final one.
134
+ function FitOnResize({ nodes }) {
135
+ const { fitView } = useReactFlow();
136
+ const width = useStore((state) => state.width);
137
+ const height = useStore((state) => state.height);
138
+ const measured = useNodesInitialized();
139
+ useEffect(() => {
140
+ if (!measured)
141
+ return;
142
+ const frame = requestAnimationFrame(() => void fitView(fitOptions));
143
+ return () => cancelAnimationFrame(frame);
144
+ }, [fitView, width, height, measured, nodes]);
145
+ return null;
146
+ }
147
+ function ZoomControls({ position, }) {
148
+ const { zoomIn, zoomOut, fitView } = useReactFlow();
149
+ const zoom = useStore((state) => state.transform[2]);
150
+ return (_jsxs(Panel, { position: position, className: "sprout-schema-controls", "data-slot": "schema-diagram-controls", role: "toolbar", "aria-label": "Diagram view", "aria-orientation": "vertical", children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Zoom in", title: "Zoom in", disabled: zoom >= maxZoom, onClick: () => void zoomIn({ duration: motion(160) }), children: _jsx(Add, { size: 16, "aria-hidden": "true" }) }), _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Zoom out", title: "Zoom out", disabled: zoom <= minZoom, onClick: () => void zoomOut({ duration: motion(160) }), children: _jsx(Minus, { size: 16, "aria-hidden": "true" }) }), _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Fit to view", title: "Fit to view", onClick: () => void fitView({ ...fitOptions, duration: motion(180) }), children: _jsx(Scan, { size: 16, "aria-hidden": "true" }) })] }));
151
+ }
152
+ function useKeyboardView() {
153
+ const { getViewport, setViewport, zoomIn, zoomOut, fitView } = useReactFlow();
154
+ return (event) => {
155
+ const target = event.target;
156
+ if (event.altKey || event.ctrlKey || event.metaKey)
157
+ return;
158
+ if (target !== event.currentTarget &&
159
+ !target.classList.contains("react-flow__node"))
160
+ return;
161
+ const step = panStep * (event.shiftKey ? 4 : 1);
162
+ const pan = {
163
+ ArrowLeft: [step, 0],
164
+ ArrowRight: [-step, 0],
165
+ ArrowUp: [0, step],
166
+ ArrowDown: [0, -step],
167
+ };
168
+ const move = pan[event.key];
169
+ if (move) {
170
+ const { x, y, zoom } = getViewport();
171
+ void setViewport({ x: x + move[0], y: y + move[1], zoom }, { duration: motion(120) });
172
+ }
173
+ else if (event.key === "+" || event.key === "=") {
174
+ void zoomIn({ duration: motion(160) });
175
+ }
176
+ else if (event.key === "-" || event.key === "_") {
177
+ void zoomOut({ duration: motion(160) });
178
+ }
179
+ else if (event.key === "0") {
180
+ void fitView({ ...fitOptions, duration: motion(180) });
181
+ }
182
+ else
183
+ return;
184
+ event.preventDefault();
185
+ };
186
+ }
187
+ const keyboardHint = "Arrow keys pan, plus and minus zoom, and 0 fits the diagram. Tab moves between tables.";
188
+ function DiagramFlow({ nodes, edges, direction, label, hintId, }) {
189
+ const onKeyDown = useKeyboardView();
190
+ return (_jsxs(ReactFlow, { className: "sprout-schema-flow", "data-slot": "schema-diagram-viewport", "aria-label": label, "aria-describedby": hintId, tabIndex: 0, onKeyDown: onKeyDown, nodes: nodes, edges: edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, fitView: true, fitViewOptions: fitOptions, minZoom: minZoom, maxZoom: maxZoom, nodesConnectable: false, elementsSelectable: false, edgesFocusable: false, ariaLabelConfig: {
191
+ "node.a11yDescription.default": keyboardHint,
192
+ "node.a11yDescription.keyboardDisabled": keyboardHint,
193
+ }, proOptions: { hideAttribution: true }, children: [_jsx(Background, { gap: 16, size: 1 }), _jsx(FitOnResize, { nodes: nodes }), _jsx(ZoomControls, { position: direction === "vertical" ? "top-right" : "bottom-right" })] }));
194
+ }
195
+ /**
196
+ * A pan-and-zoom diagram of tables, their fields, and the links between them,
197
+ * with proposed changes marked on each table and field.
198
+ */
199
+ export function SchemaDiagram({ tables, links, label, layout: layoutName = "auto", className, ...props }) {
200
+ const mobile = useViewportMobile();
201
+ const direction = layoutName === "auto" ? (mobile ? "vertical" : "horizontal") : layoutName;
202
+ const hintId = useId();
203
+ const nodes = useMemo(() => layout(tables, links, direction), [tables, links, direction]);
204
+ const edges = useMemo(() => {
205
+ const names = new Map(tables.map((table) => [table.id, table]));
206
+ return links.map((link) => {
207
+ const source = names.get(link.source);
208
+ const field = source?.fields.find((candidate) => candidate.id === link.field);
209
+ const from = `${source?.label ?? link.source}${field ? ` ${field.label}` : ""}`;
210
+ return {
211
+ id: link.id,
212
+ type: "link",
213
+ source: link.source,
214
+ sourceHandle: link.field,
215
+ target: link.target,
216
+ ariaLabel: `${from} links to ${names.get(link.target)?.label ?? link.target}: ${link.label}`,
217
+ data: { label: link.label },
218
+ };
219
+ });
220
+ }, [tables, links]);
221
+ return (_jsxs("div", { ...props, role: "figure", "aria-label": label, "data-slot": "schema-diagram", "data-layout": direction, className: cn("sprout-schema-diagram", className), children: [_jsx("p", { id: hintId, className: "sprout-schema-sr-only", children: keyboardHint }), _jsx(ReactFlowProvider, { children: _jsx(DiagramFlow, { nodes: nodes, edges: edges, direction: direction, label: label, hintId: hintId }) }, direction)] }));
222
+ }
223
+ /** Explains the change colors, the unique key, and the required mark. */
224
+ export function SchemaLegend({ className, ...props }) {
225
+ return (_jsxs("ul", { "aria-label": "Legend", "data-slot": "schema-legend", ...props, className: cn("sprout-schema-legend", className), children: [schemaChangeNames.map((change) => (_jsxs("li", { className: "sprout-schema-legend-item", children: [_jsx("span", { "aria-hidden": "true", "data-change": change, className: "sprout-schema-legend-swatch" }), change[0].toUpperCase() + change.slice(1)] }, change))), _jsxs("li", { className: "sprout-schema-legend-item", children: [_jsx(Key, { size: 11, "aria-hidden": "true" }), "Unique"] }), _jsx("li", { className: "sprout-schema-legend-item", children: "* Required" })] }));
226
+ }
@@ -0,0 +1,22 @@
1
+ import { ScrollArea as BaseScrollArea } from "@base-ui/react/scroll-area";
2
+ export declare const scrollAreaSizeNames: readonly ["auto", "sm", "md"];
3
+ export declare const scrollAreaVariantNames: readonly ["plain", "table"];
4
+ export declare const scrollAreaOrientationNames: readonly ["vertical", "horizontal", "both"];
5
+ export type ScrollAreaSize = (typeof scrollAreaSizeNames)[number];
6
+ export type ScrollAreaVariant = (typeof scrollAreaVariantNames)[number];
7
+ export type ScrollAreaOrientation = (typeof scrollAreaOrientationNames)[number];
8
+ export interface ScrollAreaProps extends BaseScrollArea.Root.Props {
9
+ /** `"sm"` is 10rem tall, `"md"` is 16rem tall. `"auto"` follows its content or your className. */
10
+ size?: ScrollAreaSize;
11
+ /** `"table"` adds a bordered surface, caps the height at 60dvh, and makes the viewport a labeled region. */
12
+ variant?: ScrollAreaVariant;
13
+ /** Which scrollbars to draw. Each one appears only while its axis overflows. */
14
+ orientation?: ScrollAreaOrientation;
15
+ /** Accessible name for the scrolling viewport. */
16
+ viewportLabel?: string | undefined;
17
+ }
18
+ /** A scrolling region with thin, theme-aware scrollbars that keeps native keyboard and wheel scrolling. */
19
+ export declare function ScrollArea({ className, children, size, variant, orientation, viewportLabel, ...props }: ScrollAreaProps): import("react").JSX.Element;
20
+ export type ScrollBarProps = BaseScrollArea.Scrollbar.Props;
21
+ /** One scrollbar track and thumb. `ScrollArea` draws these for you from `orientation`. */
22
+ export declare function ScrollBar({ className, orientation, ...props }: ScrollBarProps): import("react").JSX.Element;
@@ -0,0 +1,19 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ScrollArea as BaseScrollArea } from "@base-ui/react/scroll-area";
4
+ import { stateClassName } from "../internal/classes.js";
5
+ export const scrollAreaSizeNames = ["auto", "sm", "md"];
6
+ export const scrollAreaVariantNames = ["plain", "table"];
7
+ export const scrollAreaOrientationNames = [
8
+ "vertical",
9
+ "horizontal",
10
+ "both",
11
+ ];
12
+ /** A scrolling region with thin, theme-aware scrollbars that keeps native keyboard and wheel scrolling. */
13
+ export function ScrollArea({ className, children, size = "auto", variant = "plain", orientation = "vertical", viewportLabel, ...props }) {
14
+ return (_jsxs(BaseScrollArea.Root, { "data-slot": "scroll-area", "data-size": size, "data-variant": variant, className: stateClassName("sprout-scroll-area", className), ...props, children: [_jsx(BaseScrollArea.Viewport, { "data-slot": "scroll-area-viewport", role: variant === "table" ? "region" : undefined, "aria-label": viewportLabel, className: "sprout-scroll-area-viewport", children: children }), orientation !== "horizontal" && _jsx(ScrollBar, {}), orientation !== "vertical" && _jsx(ScrollBar, { orientation: "horizontal" }), _jsx(BaseScrollArea.Corner, { "data-slot": "scroll-area-corner", className: "sprout-scroll-area-corner" })] }));
15
+ }
16
+ /** One scrollbar track and thumb. `ScrollArea` draws these for you from `orientation`. */
17
+ export function ScrollBar({ className, orientation = "vertical", ...props }) {
18
+ return (_jsx(BaseScrollArea.Scrollbar, { "data-slot": "scroll-area-scrollbar", orientation: orientation, className: stateClassName("sprout-scroll-area-scrollbar", className), ...props, children: _jsx(BaseScrollArea.Thumb, { "data-slot": "scroll-area-thumb", className: "sprout-scroll-area-thumb" }) }));
19
+ }
@@ -0,0 +1,103 @@
1
+ import { type ReactNode, type Ref } from "react";
2
+ import { type DialogContentProps } from "./dialog.js";
3
+ export interface SearchDialogProps {
4
+ open: boolean;
5
+ onOpenChange: (open: boolean) => void;
6
+ /** Visually hidden dialog title. It also names the results combobox. */
7
+ title: string;
8
+ /** Visually hidden description of what the search covers. */
9
+ description: string;
10
+ className?: string;
11
+ /** Where the portal renders. Defaults to `document.body`. */
12
+ container?: DialogContentProps["container"];
13
+ children: ReactNode;
14
+ }
15
+ /**
16
+ * A wide search palette pinned near the top of the viewport. You filter and
17
+ * group results yourself; arrow keys wrap and Enter opens the active item.
18
+ */
19
+ export declare function SearchDialog({ open, onOpenChange, title, description, className, container, children, }: SearchDialogProps): import("react").JSX.Element;
20
+ export interface SearchInputProps {
21
+ ref?: Ref<HTMLInputElement>;
22
+ value: string;
23
+ onValueChange: (value: string) => void;
24
+ placeholder: string;
25
+ /** Accessible name of the search field. */
26
+ label: string;
27
+ /** Accessible name of the trailing close button. */
28
+ closeLabel?: string;
29
+ }
30
+ /** The search field with a leading magnifier and a trailing close button. */
31
+ export declare function SearchInput({ ref, value, onValueChange, placeholder, label, closeLabel, }: SearchInputProps): import("react").JSX.Element;
32
+ export interface SearchFiltersProps {
33
+ /** Accessible name of the filter group. */
34
+ label: string;
35
+ children: ReactNode;
36
+ }
37
+ /** A row of toggle filters under the input. It scrolls sideways when narrow. */
38
+ export declare function SearchFilters({ label, children }: SearchFiltersProps): import("react").JSX.Element;
39
+ export interface SearchFilterProps {
40
+ pressed: boolean;
41
+ onPressedChange: (pressed: boolean) => void;
42
+ icon?: ReactNode;
43
+ children: ReactNode;
44
+ }
45
+ /** A pressable filter chip. */
46
+ export declare function SearchFilter({ pressed, onPressedChange, icon, children, }: SearchFilterProps): import("react").JSX.Element;
47
+ export interface SearchListProps {
48
+ /** Accessible name of the results listbox. */
49
+ label: string;
50
+ children: ReactNode;
51
+ }
52
+ /** The scrolling results area. It keeps a steady height so typing does not resize the dialog. */
53
+ export declare function SearchList({ label, children }: SearchListProps): import("react").JSX.Element;
54
+ export interface SearchGroupProps {
55
+ heading: string;
56
+ /** Shown as a badge after the heading. A group with a count of `0` is not rendered. */
57
+ count: number;
58
+ children: ReactNode;
59
+ }
60
+ /** Results under a heading with a count. Groups after the first get a dashed divider. */
61
+ export declare function SearchGroup({ heading, count, children }: SearchGroupProps): import("react").JSX.Element | null;
62
+ export interface SearchItemIconProps {
63
+ children: ReactNode;
64
+ }
65
+ /** A muted icon tile for an item's `media`. */
66
+ export declare function SearchItemIcon({ children }: SearchItemIconProps): import("react").JSX.Element;
67
+ export interface SearchItemProps {
68
+ /** Unique within the dialog. */
69
+ value: string;
70
+ onSelect: () => void;
71
+ /** Leading icon or avatar, centered in a 28px box. */
72
+ media: ReactNode;
73
+ title: string;
74
+ /** Muted text after the title, separated by a dot. */
75
+ meta?: string | undefined;
76
+ /** A second muted line. */
77
+ detail?: string | undefined;
78
+ /** Trailing hint such as a shortcut. It swaps for the Enter key while active. */
79
+ trailing?: ReactNode;
80
+ }
81
+ /** One result. The active item shows an Enter key hint. */
82
+ export declare function SearchItem({ value, onSelect, media, title, meta, detail, trailing, }: SearchItemProps): import("react").JSX.Element;
83
+ export interface SearchEmptyProps {
84
+ /** One to three icons shown as fanned tiles over a faint grid. */
85
+ media?: ReactNode;
86
+ title: string;
87
+ description: string;
88
+ /** A recovery action such as Clear search. */
89
+ action?: ReactNode;
90
+ }
91
+ /** Fills the list when nothing matches and is announced as a status. */
92
+ export declare function SearchEmpty({ media, title, description, action, }: SearchEmptyProps): import("react").JSX.Element;
93
+ export interface SearchFooterProps {
94
+ children: ReactNode;
95
+ }
96
+ /** A tinted strip of keyboard hints. Hidden below 640px, where there is no keyboard to hint at. */
97
+ export declare function SearchFooter({ children }: SearchFooterProps): import("react").JSX.Element;
98
+ export interface SearchHintProps {
99
+ keys: string;
100
+ children: ReactNode;
101
+ }
102
+ /** A key and what it does. */
103
+ export declare function SearchHint({ keys, children }: SearchHintProps): import("react").JSX.Element;
@@ -0,0 +1,64 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Toggle } from "@base-ui/react/toggle";
4
+ import { Children, Fragment, isValidElement, } from "react";
5
+ import { X } from "reicon-react/icons/X";
6
+ import { cn } from "../internal/classes.js";
7
+ import { Button } from "./button.js";
8
+ import { Command, CommandGroup, CommandInput, CommandItem, CommandList, } from "./command.js";
9
+ import { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle, } from "./dialog.js";
10
+ /**
11
+ * A wide search palette pinned near the top of the viewport. You filter and
12
+ * group results yourself; arrow keys wrap and Enter opens the active item.
13
+ */
14
+ export function SearchDialog({ open, onOpenChange, title, description, className, container, children, }) {
15
+ return (_jsx(Dialog, { open: open, onOpenChange: (next) => onOpenChange(next), children: _jsxs(DialogContent, { "data-slot": "search-dialog", placement: "top", showCloseButton: false, container: container, className: cn("sprout-search-dialog", className), children: [_jsx(DialogTitle, { className: "sprout-command-sr-only", children: title }), _jsx(DialogDescription, { className: "sprout-command-sr-only", children: description }), _jsx(Command, { shouldFilter: false, loop: true, label: title, className: "sprout-search-command", children: children })] }) }));
16
+ }
17
+ /** The search field with a leading magnifier and a trailing close button. */
18
+ export function SearchInput({ ref, value, onValueChange, placeholder, label, closeLabel = "Close search", }) {
19
+ return (_jsxs("div", { "data-slot": "search-input", className: "sprout-search-input", children: [_jsx(CommandInput, { ref: ref, value: value, onValueChange: onValueChange, placeholder: placeholder, "aria-label": label }), _jsx(DialogClose, { render: _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": closeLabel }), children: _jsx(X, { size: 16, "aria-hidden": "true" }) })] }));
20
+ }
21
+ /** A row of toggle filters under the input. It scrolls sideways when narrow. */
22
+ export function SearchFilters({ label, children }) {
23
+ return (_jsx("div", { "data-slot": "search-filters", role: "group", "aria-label": label, className: "sprout-search-filters", children: children }));
24
+ }
25
+ /** A pressable filter chip. */
26
+ export function SearchFilter({ pressed, onPressedChange, icon, children, }) {
27
+ return (_jsxs(Toggle, { "data-slot": "search-filter", pressed: pressed, onPressedChange: (next) => onPressedChange(next), className: "sprout-search-filter", children: [icon, children] }));
28
+ }
29
+ /** The scrolling results area. It keeps a steady height so typing does not resize the dialog. */
30
+ export function SearchList({ label, children }) {
31
+ return (_jsx(CommandList, { "aria-label": label, className: "sprout-search-list", children: children }));
32
+ }
33
+ /** Results under a heading with a count. Groups after the first get a dashed divider. */
34
+ export function SearchGroup({ heading, count, children }) {
35
+ if (count === 0)
36
+ return null;
37
+ return (_jsx(CommandGroup, { className: "sprout-search-group", heading: _jsxs("span", { className: "sprout-search-group-heading", children: [heading, " ", _jsx("span", { "data-slot": "search-group-count", className: "sprout-search-group-count", children: count })] }), children: children }));
38
+ }
39
+ /** A muted icon tile for an item's `media`. */
40
+ export function SearchItemIcon({ children }) {
41
+ return (_jsx("span", { "data-slot": "search-item-icon", className: "sprout-search-item-icon", children: children }));
42
+ }
43
+ /** One result. The active item shows an Enter key hint. */
44
+ export function SearchItem({ value, onSelect, media, title, meta, detail, trailing, }) {
45
+ return (_jsxs(CommandItem, { value: value, onSelect: () => onSelect(), className: "sprout-search-item", children: [_jsx("span", { "data-slot": "search-item-media", className: "sprout-search-item-media", children: media }), _jsxs("span", { className: "sprout-search-item-text", children: [_jsxs("span", { className: "sprout-search-item-line", children: [_jsx("span", { "data-slot": "search-item-title", className: "sprout-search-item-title", children: title }), meta && (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", className: "sprout-search-item-dot", children: "\u00B7" }), _jsx("span", { "data-slot": "search-item-meta", className: "sprout-search-item-meta", children: meta })] }))] }), detail && (_jsx("span", { "data-slot": "search-item-detail", className: "sprout-search-item-detail", children: detail }))] }), trailing && (_jsx("span", { "data-slot": "search-item-trailing", className: "sprout-search-item-trailing", children: trailing })), _jsx("kbd", { "aria-hidden": "true", className: "sprout-search-kbd sprout-search-item-enter", children: "\u21B5" })] }));
46
+ }
47
+ function tiles(media) {
48
+ const nodes = isValidElement(media) && media.type === Fragment
49
+ ? media.props.children
50
+ : media;
51
+ return Children.toArray(nodes).map((node, index) => (_jsx("span", { "data-slot": "search-empty-tile", className: "sprout-search-empty-tile", children: node }, index)));
52
+ }
53
+ /** Fills the list when nothing matches and is announced as a status. */
54
+ export function SearchEmpty({ media, title, description, action, }) {
55
+ return (_jsxs("div", { "data-slot": "search-empty", role: "status", className: "sprout-search-empty", children: [media && (_jsx("div", { "aria-hidden": "true", className: "sprout-search-empty-media", children: _jsx("span", { className: "sprout-search-empty-tiles", children: tiles(media) }) })), _jsxs("div", { className: "sprout-search-empty-text", children: [_jsx("p", { className: "sprout-search-empty-title", children: title }), _jsx("p", { className: "sprout-search-empty-description", children: description })] }), action] }));
56
+ }
57
+ /** A tinted strip of keyboard hints. Hidden below 640px, where there is no keyboard to hint at. */
58
+ export function SearchFooter({ children }) {
59
+ return (_jsx("footer", { "data-slot": "search-footer", className: "sprout-search-footer", children: children }));
60
+ }
61
+ /** A key and what it does. */
62
+ export function SearchHint({ keys, children }) {
63
+ return (_jsxs("span", { "data-slot": "search-hint", className: "sprout-search-hint", children: [_jsx("kbd", { className: "sprout-search-kbd", children: keys }), children] }));
64
+ }
@@ -0,0 +1,17 @@
1
+ export interface SearchFieldProps {
2
+ /** Accessible name of the input. It is also the placeholder when none is given. */
3
+ label: string;
4
+ value: string;
5
+ onChange: (value: string) => void;
6
+ placeholder?: string | undefined;
7
+ maxLength?: number | undefined;
8
+ id?: string | undefined;
9
+ /** Classes for the group. It is full width up to 24rem by default. */
10
+ className?: string | undefined;
11
+ }
12
+ /**
13
+ * An `InputGroup` preset for search: a leading magnifier and a clear button
14
+ * that appears once there is text. Clear and Escape empty the value and keep
15
+ * focus in the input. A disabled ancestor `fieldset` disables both controls.
16
+ */
17
+ export declare function SearchField({ label, value, onChange, placeholder, maxLength, id, className, }: SearchFieldProps): import("react").JSX.Element;
@@ -0,0 +1,27 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef } from "react";
4
+ import { Search } from "reicon-react/icons/Search";
5
+ import { X } from "reicon-react/icons/X";
6
+ import { cn } from "../internal/classes.js";
7
+ import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, } from "./input-group.js";
8
+ /**
9
+ * An `InputGroup` preset for search: a leading magnifier and a clear button
10
+ * that appears once there is text. Clear and Escape empty the value and keep
11
+ * focus in the input. A disabled ancestor `fieldset` disables both controls.
12
+ */
13
+ export function SearchField({ label, value, onChange, placeholder, maxLength, id, className, }) {
14
+ const input = useRef(null);
15
+ // The first Escape clears the text; with no text it reaches popovers and dialogs.
16
+ const onKeyDown = (event) => {
17
+ if (event.key !== "Escape" || !value)
18
+ return;
19
+ event.preventDefault();
20
+ event.stopPropagation();
21
+ onChange("");
22
+ };
23
+ return (_jsxs(InputGroup, { "data-slot": "search-field", className: cn("sprout-search-field", className), children: [_jsx(InputGroupAddon, { children: _jsx(Search, { size: 16, "aria-hidden": "true" }) }), _jsx(InputGroupInput, { ref: input, id: id, type: "search", "aria-label": label, maxLength: maxLength, value: value, placeholder: placeholder ?? label, autoComplete: "off", spellCheck: false, onChange: (event) => onChange(event.currentTarget.value), onKeyDown: onKeyDown }), value && (_jsx(InputGroupAddon, { align: "inline-end", children: _jsx(InputGroupButton, { "data-slot": "search-field-clear", size: "icon-xs", "aria-label": "Clear search", className: "sprout-search-field-clear", onClick: () => {
24
+ onChange("");
25
+ input.current?.focus();
26
+ }, children: _jsx(X, { size: 14, "aria-hidden": "true" }) }) }))] }));
27
+ }
@@ -0,0 +1,57 @@
1
+ import { Select as SelectPrimitive } from "@base-ui/react/select";
2
+ export declare const selectTriggerSizeNames: readonly ["default", "sm", "calendar"];
3
+ export declare const selectTriggerWidthNames: readonly ["fit", "full"];
4
+ export type SelectTriggerSize = (typeof selectTriggerSizeNames)[number];
5
+ export type SelectTriggerWidth = (typeof selectTriggerWidthNames)[number];
6
+ export type SelectProps<Value = unknown, Multiple extends boolean | undefined = false> = SelectPrimitive.Root.Props<Value, Multiple>;
7
+ /**
8
+ * Holds the value and open state. It takes every Base UI select root prop,
9
+ * including `items`, `value`, `defaultValue`, `onValueChange`, `disabled`,
10
+ * `required`, and `name`.
11
+ */
12
+ export declare const Select: typeof SelectPrimitive.Root;
13
+ export type SelectGroupProps = SelectPrimitive.Group.Props;
14
+ export declare function SelectGroup({ className, ...props }: SelectGroupProps): import("react").JSX.Element;
15
+ export type SelectValueProps = SelectPrimitive.Value.Props;
16
+ /**
17
+ * Shows the chosen item. Pass `placeholder` for the empty state, or children
18
+ * to show a custom label such as a name looked up from the value.
19
+ */
20
+ export declare function SelectValue({ className, ...props }: SelectValueProps): import("react").JSX.Element;
21
+ export interface SelectTriggerProps extends SelectPrimitive.Trigger.Props {
22
+ /**
23
+ * `default` is 32px for forms. `sm` is 28px for toolbars, filters, table
24
+ * rows, and inline sentences. `calendar` is a 32px control with small text
25
+ * for calendar headers.
26
+ */
27
+ size?: SelectTriggerSize;
28
+ /** `fit` sizes to the value. `full` fills the parent, as in form fields. */
29
+ width?: SelectTriggerWidth;
30
+ }
31
+ export declare function SelectTrigger({ className, size, width, children, ...rest }: SelectTriggerProps): import("react").JSX.Element;
32
+ type PositionerProps = Pick<SelectPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger" | "collisionPadding" | "collisionAvoidance" | "sticky" | "positionMethod">;
33
+ export interface SelectContentProps extends SelectPrimitive.Popup.Props, PositionerProps {
34
+ /** Where the portal renders. Defaults to `document.body`. */
35
+ container?: SelectPrimitive.Portal.Props["container"];
36
+ }
37
+ /**
38
+ * The list surface. It renders its own portal, positioner, list, and scroll
39
+ * arrows. By default the chosen item lines up over the trigger; pass
40
+ * `alignItemWithTrigger={false}` to open below it instead.
41
+ */
42
+ export declare function SelectContent({ side, sideOffset, align, alignOffset, alignItemWithTrigger, collisionPadding, collisionAvoidance, sticky, positionMethod, container, className, children, ...props }: SelectContentProps): import("react").JSX.Element;
43
+ export type SelectLabelProps = SelectPrimitive.GroupLabel.Props;
44
+ export declare function SelectLabel({ className, ...props }: SelectLabelProps): import("react").JSX.Element;
45
+ export type SelectItemProps = SelectPrimitive.Item.Props;
46
+ /**
47
+ * One choice. Children may hold an icon, rich content, or plain text. Long
48
+ * text wraps instead of being cut off.
49
+ */
50
+ export declare function SelectItem({ className, children, ...props }: SelectItemProps): import("react").JSX.Element;
51
+ export type SelectSeparatorProps = SelectPrimitive.Separator.Props;
52
+ export declare function SelectSeparator({ className, ...props }: SelectSeparatorProps): import("react").JSX.Element;
53
+ export type SelectScrollUpButtonProps = SelectPrimitive.ScrollUpArrow.Props;
54
+ export declare function SelectScrollUpButton({ className, ...props }: SelectScrollUpButtonProps): import("react").JSX.Element;
55
+ export type SelectScrollDownButtonProps = SelectPrimitive.ScrollDownArrow.Props;
56
+ export declare function SelectScrollDownButton({ className, ...props }: SelectScrollDownButtonProps): import("react").JSX.Element;
57
+ export {};
@@ -0,0 +1,57 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Select as SelectPrimitive } from "@base-ui/react/select";
4
+ import { Check } from "reicon-react/icons/Check";
5
+ import { ChevronDown } from "reicon-react/icons/ChevronDown";
6
+ import { ChevronUp } from "reicon-react/icons/ChevronUp";
7
+ import { cn, stateClassName } from "../internal/classes.js";
8
+ import { useFieldControl } from "./field.js";
9
+ export const selectTriggerSizeNames = ["default", "sm", "calendar"];
10
+ export const selectTriggerWidthNames = ["fit", "full"];
11
+ /**
12
+ * Holds the value and open state. It takes every Base UI select root prop,
13
+ * including `items`, `value`, `defaultValue`, `onValueChange`, `disabled`,
14
+ * `required`, and `name`.
15
+ */
16
+ export const Select = SelectPrimitive.Root;
17
+ export function SelectGroup({ className, ...props }) {
18
+ return (_jsx(SelectPrimitive.Group, { "data-slot": "select-group", ...props, className: stateClassName("sprout-select-group", className) }));
19
+ }
20
+ /**
21
+ * Shows the chosen item. Pass `placeholder` for the empty state, or children
22
+ * to show a custom label such as a name looked up from the value.
23
+ */
24
+ export function SelectValue({ className, ...props }) {
25
+ return (_jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props, className: stateClassName("sprout-select-value", className) }));
26
+ }
27
+ export function SelectTrigger({ className, size = "default", width = "fit", children, ...rest }) {
28
+ const props = useFieldControl(rest);
29
+ return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, "data-width": width, ...props, className: stateClassName(cn("sprout-select-trigger", `sprout-select-trigger-${size}`, `sprout-select-trigger-${width}`), className), children: [children, _jsx(SelectPrimitive.Icon, { className: "sprout-select-icon", children: _jsx(ChevronDown, { "aria-hidden": "true" }) })] }));
30
+ }
31
+ /**
32
+ * The list surface. It renders its own portal, positioner, list, and scroll
33
+ * arrows. By default the chosen item lines up over the trigger; pass
34
+ * `alignItemWithTrigger={false}` to open below it instead.
35
+ */
36
+ export function SelectContent({ side = "bottom", sideOffset = 4, align = "center", alignOffset = 0, alignItemWithTrigger = true, collisionPadding = 8, collisionAvoidance, sticky, positionMethod, container, className, children, ...props }) {
37
+ return (_jsx(SelectPrimitive.Portal, { container: container, children: _jsx(SelectPrimitive.Positioner, { className: "sprout-select-positioner", side: side, sideOffset: sideOffset, align: align, alignOffset: alignOffset, alignItemWithTrigger: alignItemWithTrigger, collisionPadding: collisionPadding, collisionAvoidance: collisionAvoidance, sticky: sticky, positionMethod: positionMethod, children: _jsxs(SelectPrimitive.Popup, { "data-slot": "select-content", "data-align-trigger": alignItemWithTrigger, ...props, className: stateClassName("sprout-select-content", className), children: [_jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.List, { className: "sprout-select-list", children: children }), _jsx(SelectScrollDownButton, {})] }) }) }));
38
+ }
39
+ export function SelectLabel({ className, ...props }) {
40
+ return (_jsx(SelectPrimitive.GroupLabel, { "data-slot": "select-label", ...props, className: stateClassName("sprout-select-label", className) }));
41
+ }
42
+ /**
43
+ * One choice. Children may hold an icon, rich content, or plain text. Long
44
+ * text wraps instead of being cut off.
45
+ */
46
+ export function SelectItem({ className, children, ...props }) {
47
+ return (_jsxs(SelectPrimitive.Item, { "data-slot": "select-item", ...props, className: stateClassName("sprout-select-item", className), children: [_jsx(SelectPrimitive.ItemText, { className: "sprout-select-item-text", children: children }), _jsx(SelectPrimitive.ItemIndicator, { className: "sprout-select-item-indicator", children: _jsx(Check, { "aria-hidden": "true" }) })] }));
48
+ }
49
+ export function SelectSeparator({ className, ...props }) {
50
+ return (_jsx(SelectPrimitive.Separator, { "data-slot": "select-separator", ...props, className: stateClassName("sprout-select-separator", className) }));
51
+ }
52
+ export function SelectScrollUpButton({ className, ...props }) {
53
+ return (_jsx(SelectPrimitive.ScrollUpArrow, { "data-slot": "select-scroll-up-button", ...props, className: stateClassName("sprout-select-scroll-button sprout-select-scroll-up", className), children: _jsx(ChevronUp, { "aria-hidden": "true" }) }));
54
+ }
55
+ export function SelectScrollDownButton({ className, ...props }) {
56
+ return (_jsx(SelectPrimitive.ScrollDownArrow, { "data-slot": "select-scroll-down-button", ...props, className: stateClassName("sprout-select-scroll-button sprout-select-scroll-down", className), children: _jsx(ChevronDown, { "aria-hidden": "true" }) }));
57
+ }
@@ -0,0 +1,18 @@
1
+ import { type ComponentProps, type ReactNode } from "react";
2
+ export interface SelectionSlotProps extends Omit<ComponentProps<"div">, "children"> {
3
+ /** Shows `selection` in place of `children`. */
4
+ active: boolean;
5
+ /** The toolbar for the current selection, such as a count and bulk actions. */
6
+ selection: ReactNode;
7
+ /** The tools shown while nothing is selected, such as search and filters. */
8
+ children: ReactNode;
9
+ }
10
+ /**
11
+ * Swaps a toolbar for a selection toolbar in the same place. Both share one
12
+ * grid cell, so the row keeps the height of the taller one and nothing below
13
+ * it moves. The hidden tools stay mounted, so their state survives, but are
14
+ * inert. Focus moves into the selection when it came from the hidden tools,
15
+ * and returns to where it was when the selection closes. If that element is
16
+ * gone, focus that was in the selection goes to the first tool.
17
+ */
18
+ export declare function SelectionSlot({ active, selection, children, className, ...props }: SelectionSlotProps): import("react").JSX.Element;