@adecore/database 0.0.1 → 0.16.0-beta.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 (400) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -1
  3. package/dist/DatabaseProvider.d.ts +13 -0
  4. package/dist/DatabaseProvider.js +17 -0
  5. package/dist/actions.d.ts +40 -0
  6. package/dist/actions.js +1 -0
  7. package/dist/client/client.d.ts +5 -0
  8. package/dist/client/client.js +245 -0
  9. package/dist/client/index.d.ts +2 -0
  10. package/dist/client/index.js +2 -0
  11. package/dist/client/types.d.ts +97 -0
  12. package/dist/client/types.js +13 -0
  13. package/dist/client-context.d.ts +16 -0
  14. package/dist/client-context.js +17 -0
  15. package/dist/connections/ConnectionForm.d.ts +10 -0
  16. package/dist/connections/ConnectionForm.js +145 -0
  17. package/dist/connections/ConnectionManager.d.ts +12 -0
  18. package/dist/connections/ConnectionManager.js +93 -0
  19. package/dist/connections/EngineIcon.d.ts +9 -0
  20. package/dist/connections/EngineIcon.js +7 -0
  21. package/dist/connections/connection-config.d.ts +16 -0
  22. package/dist/connections/connection-config.js +107 -0
  23. package/dist/connections/docker.d.ts +9 -0
  24. package/dist/connections/docker.js +38 -0
  25. package/dist/connections/engine-icons.d.ts +3 -0
  26. package/dist/connections/engine-icons.js +3 -0
  27. package/dist/connections/use-docker-containers.d.ts +17 -0
  28. package/dist/connections/use-docker-containers.js +26 -0
  29. package/dist/console/DestructiveDialog.d.ts +9 -0
  30. package/dist/console/DestructiveDialog.js +9 -0
  31. package/dist/console/HistoryPanel.d.ts +10 -0
  32. package/dist/console/HistoryPanel.js +15 -0
  33. package/dist/console/QueryConsole.d.ts +14 -0
  34. package/dist/console/QueryConsole.js +261 -0
  35. package/dist/console/SchemaPicker.d.ts +11 -0
  36. package/dist/console/SchemaPicker.js +8 -0
  37. package/dist/console/SqlEditor.d.ts +12 -0
  38. package/dist/console/SqlEditor.js +61 -0
  39. package/dist/console/StatementResultView.d.ts +18 -0
  40. package/dist/console/StatementResultView.js +32 -0
  41. package/dist/console/TransactionControls.d.ts +13 -0
  42. package/dist/console/TransactionControls.js +12 -0
  43. package/dist/console/destructive.d.ts +7 -0
  44. package/dist/console/destructive.js +43 -0
  45. package/dist/console/editing.d.ts +12 -0
  46. package/dist/console/editing.js +60 -0
  47. package/dist/console/history.d.ts +13 -0
  48. package/dist/console/history.js +43 -0
  49. package/dist/console/labels.d.ts +1 -0
  50. package/dist/console/labels.js +6 -0
  51. package/dist/console/outcome.d.ts +6 -0
  52. package/dist/console/outcome.js +11 -0
  53. package/dist/console/schemas.d.ts +2 -0
  54. package/dist/console/schemas.js +5 -0
  55. package/dist/console/shortcuts.d.ts +2 -0
  56. package/dist/console/shortcuts.js +3 -0
  57. package/dist/console/useConsoleHistory.d.ts +7 -0
  58. package/dist/console/useConsoleHistory.js +22 -0
  59. package/dist/ddl/alter-mysql.d.ts +4 -0
  60. package/dist/ddl/alter-mysql.js +79 -0
  61. package/dist/ddl/alter-sqlite.d.ts +4 -0
  62. package/dist/ddl/alter-sqlite.js +80 -0
  63. package/dist/ddl/alter.d.ts +4 -0
  64. package/dist/ddl/alter.js +24 -0
  65. package/dist/ddl/column.d.ts +9 -0
  66. package/dist/ddl/column.js +62 -0
  67. package/dist/ddl/create.d.ts +12 -0
  68. package/dist/ddl/create.js +74 -0
  69. package/dist/ddl/dialect.d.ts +12 -0
  70. package/dist/ddl/dialect.js +34 -0
  71. package/dist/ddl/diff.d.ts +34 -0
  72. package/dist/ddl/diff.js +111 -0
  73. package/dist/ddl/draft.d.ts +46 -0
  74. package/dist/ddl/draft.js +70 -0
  75. package/dist/ddl/index.d.ts +10 -0
  76. package/dist/ddl/index.js +10 -0
  77. package/dist/ddl/parse.d.ts +17 -0
  78. package/dist/ddl/parse.js +131 -0
  79. package/dist/ddl/statements.d.ts +6 -0
  80. package/dist/ddl/statements.js +12 -0
  81. package/dist/ddl/types.d.ts +6 -0
  82. package/dist/ddl/types.js +52 -0
  83. package/dist/ddl/validate.d.ts +7 -0
  84. package/dist/ddl/validate.js +38 -0
  85. package/dist/designer/ColumnsEditor.d.ts +2 -0
  86. package/dist/designer/ColumnsEditor.js +24 -0
  87. package/dist/designer/DesignerView.d.ts +25 -0
  88. package/dist/designer/DesignerView.js +33 -0
  89. package/dist/designer/ForeignKeysEditor.d.ts +7 -0
  90. package/dist/designer/ForeignKeysEditor.js +24 -0
  91. package/dist/designer/IndexesEditor.d.ts +2 -0
  92. package/dist/designer/IndexesEditor.js +13 -0
  93. package/dist/designer/NamePicker.d.ts +11 -0
  94. package/dist/designer/NamePicker.js +13 -0
  95. package/dist/designer/OptionsEditor.d.ts +2 -0
  96. package/dist/designer/OptionsEditor.js +17 -0
  97. package/dist/designer/SqlPreview.d.ts +12 -0
  98. package/dist/designer/SqlPreview.js +17 -0
  99. package/dist/designer/TableDesigner.d.ts +10 -0
  100. package/dist/designer/TableDesigner.js +103 -0
  101. package/dist/designer/edit.d.ts +17 -0
  102. package/dist/designer/edit.js +115 -0
  103. package/dist/designer/editor-props.d.ts +10 -0
  104. package/dist/designer/editor-props.js +1 -0
  105. package/dist/designer/plan.d.ts +9 -0
  106. package/dist/designer/plan.js +13 -0
  107. package/dist/designer/recover.d.ts +2 -0
  108. package/dist/designer/recover.js +18 -0
  109. package/dist/designer/useReferenceColumns.d.ts +3 -0
  110. package/dist/designer/useReferenceColumns.js +36 -0
  111. package/dist/explorer/DatabaseExplorer.d.ts +13 -0
  112. package/dist/explorer/DatabaseExplorer.js +264 -0
  113. package/dist/explorer/RowMenu.d.ts +11 -0
  114. package/dist/explorer/RowMenu.js +89 -0
  115. package/dist/explorer/TableDialog.d.ts +15 -0
  116. package/dist/explorer/TableDialog.js +50 -0
  117. package/dist/explorer/expansion.d.ts +7 -0
  118. package/dist/explorer/expansion.js +31 -0
  119. package/dist/explorer/table-statements.d.ts +5 -0
  120. package/dist/explorer/table-statements.js +12 -0
  121. package/dist/explorer/tree.d.ts +125 -0
  122. package/dist/explorer/tree.js +295 -0
  123. package/dist/explorer/use-explorer-loads.d.ts +12 -0
  124. package/dist/explorer/use-explorer-loads.js +109 -0
  125. package/dist/explorer/use-stored-expansion.d.ts +9 -0
  126. package/dist/explorer/use-stored-expansion.js +47 -0
  127. package/dist/grid/CellEditor.d.ts +9 -0
  128. package/dist/grid/CellEditor.js +29 -0
  129. package/dist/grid/CopyAsMenu.d.ts +7 -0
  130. package/dist/grid/CopyAsMenu.js +10 -0
  131. package/dist/grid/DataGrid.d.ts +36 -0
  132. package/dist/grid/DataGrid.js +512 -0
  133. package/dist/grid/EnumPicker.d.ts +17 -0
  134. package/dist/grid/EnumPicker.js +53 -0
  135. package/dist/grid/GridHeaderCell.d.ts +36 -0
  136. package/dist/grid/GridHeaderCell.js +69 -0
  137. package/dist/grid/RecordView.d.ts +20 -0
  138. package/dist/grid/RecordView.js +126 -0
  139. package/dist/grid/ValueDock.d.ts +11 -0
  140. package/dist/grid/ValueDock.js +28 -0
  141. package/dist/grid/aggregates.d.ts +22 -0
  142. package/dist/grid/aggregates.js +62 -0
  143. package/dist/grid/cell-range.d.ts +14 -0
  144. package/dist/grid/cell-range.js +11 -0
  145. package/dist/grid/column-layout.d.ts +18 -0
  146. package/dist/grid/column-layout.js +49 -0
  147. package/dist/grid/column-selection.d.ts +11 -0
  148. package/dist/grid/column-selection.js +28 -0
  149. package/dist/grid/column-view.d.ts +10 -0
  150. package/dist/grid/column-view.js +23 -0
  151. package/dist/grid/copy-formats.d.ts +15 -0
  152. package/dist/grid/copy-formats.js +80 -0
  153. package/dist/grid/display.d.ts +20 -0
  154. package/dist/grid/display.js +81 -0
  155. package/dist/grid/edit-value.d.ts +5 -0
  156. package/dist/grid/edit-value.js +55 -0
  157. package/dist/grid/enum-type.d.ts +7 -0
  158. package/dist/grid/enum-type.js +52 -0
  159. package/dist/grid/focused-value.d.ts +4 -0
  160. package/dist/grid/focused-value.js +19 -0
  161. package/dist/grid/layout.d.ts +22 -0
  162. package/dist/grid/layout.js +74 -0
  163. package/dist/grid/navigation.d.ts +15 -0
  164. package/dist/grid/navigation.js +52 -0
  165. package/dist/grid/row-selection.d.ts +9 -0
  166. package/dist/grid/row-selection.js +24 -0
  167. package/dist/grid/sort.d.ts +7 -0
  168. package/dist/grid/sort.js +7 -0
  169. package/dist/grid/types.d.ts +30 -0
  170. package/dist/grid/types.js +9 -0
  171. package/dist/host/helper-path.d.ts +7 -0
  172. package/dist/host/helper-path.js +15 -0
  173. package/dist/host/host.d.ts +2 -0
  174. package/dist/host/host.js +343 -0
  175. package/dist/host/index.d.ts +5 -0
  176. package/dist/host/index.js +4 -0
  177. package/dist/host/spawn.d.ts +7 -0
  178. package/dist/host/spawn.js +98 -0
  179. package/dist/host/types.d.ts +19 -0
  180. package/dist/host/types.js +1 -0
  181. package/dist/host/validate.d.ts +10 -0
  182. package/dist/host/validate.js +339 -0
  183. package/dist/index.d.ts +14 -0
  184. package/dist/index.js +13 -0
  185. package/dist/locales/en.json +577 -0
  186. package/dist/locales/nl.json +577 -0
  187. package/dist/locales.d.ts +4 -0
  188. package/dist/locales.js +14 -0
  189. package/dist/protocol/connection.d.ts +56 -0
  190. package/dist/protocol/connection.js +1 -0
  191. package/dist/protocol/errors.d.ts +7 -0
  192. package/dist/protocol/errors.js +1 -0
  193. package/dist/protocol/index.d.ts +5 -0
  194. package/dist/protocol/index.js +3 -0
  195. package/dist/protocol/messages.d.ts +250 -0
  196. package/dist/protocol/messages.js +2 -0
  197. package/dist/protocol/schema.d.ts +53 -0
  198. package/dist/protocol/schema.js +1 -0
  199. package/dist/protocol/values.d.ts +21 -0
  200. package/dist/protocol/values.js +15 -0
  201. package/dist/sql-split.d.ts +15 -0
  202. package/dist/sql-split.js +201 -0
  203. package/dist/sql.d.ts +29 -0
  204. package/dist/sql.js +104 -0
  205. package/dist/structure/StructureView.d.ts +14 -0
  206. package/dist/structure/StructureView.js +99 -0
  207. package/dist/structure/structure-text.d.ts +2 -0
  208. package/dist/structure/structure-text.js +2 -0
  209. package/dist/table/CommandField.d.ts +14 -0
  210. package/dist/table/CommandField.js +157 -0
  211. package/dist/table/ImportDialog.d.ts +27 -0
  212. package/dist/table/ImportDialog.js +28 -0
  213. package/dist/table/TableStatusBar.d.ts +19 -0
  214. package/dist/table/TableStatusBar.js +22 -0
  215. package/dist/table/TableToolbar.d.ts +43 -0
  216. package/dist/table/TableToolbar.js +16 -0
  217. package/dist/table/TableView.d.ts +13 -0
  218. package/dist/table/TableView.js +508 -0
  219. package/dist/table/command-field.d.ts +57 -0
  220. package/dist/table/command-field.js +203 -0
  221. package/dist/table/grid-rows.d.ts +23 -0
  222. package/dist/table/grid-rows.js +55 -0
  223. package/dist/table/import-mapping.d.ts +7 -0
  224. package/dist/table/import-mapping.js +28 -0
  225. package/dist/table/layout-store.d.ts +13 -0
  226. package/dist/table/layout-store.js +43 -0
  227. package/dist/table/paging.d.ts +12 -0
  228. package/dist/table/paging.js +20 -0
  229. package/dist/table/pending.d.ts +32 -0
  230. package/dist/table/pending.js +94 -0
  231. package/dist/table/references.d.ts +12 -0
  232. package/dist/table/references.js +21 -0
  233. package/dist/table/useLoaded.d.ts +7 -0
  234. package/dist/table/useLoaded.js +36 -0
  235. package/dist/table/useStoredLayout.d.ts +8 -0
  236. package/dist/table/useStoredLayout.js +39 -0
  237. package/dist/table/useTableTransfer.d.ts +51 -0
  238. package/dist/table/useTableTransfer.js +125 -0
  239. package/dist/testing/fake.d.ts +21 -0
  240. package/dist/testing/fake.js +372 -0
  241. package/dist/testing/index.d.ts +1 -0
  242. package/dist/testing/index.js +1 -0
  243. package/dist/use-popup-press.d.ts +1 -0
  244. package/dist/use-popup-press.js +24 -0
  245. package/dist/use-schema-change.d.ts +1 -0
  246. package/dist/use-schema-change.js +13 -0
  247. package/dist/use-stable-callback.d.ts +1 -0
  248. package/dist/use-stable-callback.js +9 -0
  249. package/dist/value/ValuePanel.d.ts +14 -0
  250. package/dist/value/ValuePanel.js +103 -0
  251. package/dist/value/draft.d.ts +33 -0
  252. package/dist/value/draft.js +44 -0
  253. package/dist/value/hex.d.ts +14 -0
  254. package/dist/value/hex.js +46 -0
  255. package/dist/value/json.d.ts +9 -0
  256. package/dist/value/json.js +71 -0
  257. package/dist/value/views.d.ts +22 -0
  258. package/dist/value/views.js +54 -0
  259. package/dist/workbench/DatabaseWorkbench.d.ts +9 -0
  260. package/dist/workbench/DatabaseWorkbench.js +179 -0
  261. package/dist/workbench/DesignerTab.d.ts +10 -0
  262. package/dist/workbench/DesignerTab.js +22 -0
  263. package/dist/workbench/TableTab.d.ts +10 -0
  264. package/dist/workbench/TableTab.js +8 -0
  265. package/dist/workbench/tabs.d.ts +42 -0
  266. package/dist/workbench/tabs.js +139 -0
  267. package/package.json +79 -3
  268. package/src/DatabaseProvider.tsx +37 -0
  269. package/src/actions.ts +34 -0
  270. package/src/client/client.ts +324 -0
  271. package/src/client/index.ts +18 -0
  272. package/src/client/types.ts +161 -0
  273. package/src/client-context.ts +35 -0
  274. package/src/connections/ConnectionForm.tsx +474 -0
  275. package/src/connections/ConnectionManager.tsx +224 -0
  276. package/src/connections/EngineIcon.tsx +18 -0
  277. package/src/connections/connection-config.ts +143 -0
  278. package/src/connections/docker.ts +50 -0
  279. package/src/connections/engine-icons.ts +5 -0
  280. package/src/connections/use-docker-containers.ts +41 -0
  281. package/src/console/DestructiveDialog.tsx +40 -0
  282. package/src/console/HistoryPanel.tsx +67 -0
  283. package/src/console/QueryConsole.tsx +439 -0
  284. package/src/console/SchemaPicker.tsx +32 -0
  285. package/src/console/SqlEditor.tsx +95 -0
  286. package/src/console/StatementResultView.tsx +176 -0
  287. package/src/console/TransactionControls.tsx +53 -0
  288. package/src/console/destructive.ts +60 -0
  289. package/src/console/editing.ts +77 -0
  290. package/src/console/history.ts +62 -0
  291. package/src/console/labels.ts +7 -0
  292. package/src/console/outcome.ts +14 -0
  293. package/src/console/schemas.ts +7 -0
  294. package/src/console/shortcuts.ts +4 -0
  295. package/src/console/useConsoleHistory.ts +39 -0
  296. package/src/ddl/alter-mysql.ts +90 -0
  297. package/src/ddl/alter-sqlite.ts +93 -0
  298. package/src/ddl/alter.ts +29 -0
  299. package/src/ddl/column.ts +74 -0
  300. package/src/ddl/create.ts +92 -0
  301. package/src/ddl/dialect.ts +53 -0
  302. package/src/ddl/diff.ts +164 -0
  303. package/src/ddl/draft.ts +136 -0
  304. package/src/ddl/index.ts +10 -0
  305. package/src/ddl/parse.ts +161 -0
  306. package/src/ddl/statements.ts +20 -0
  307. package/src/ddl/types.ts +60 -0
  308. package/src/ddl/validate.ts +47 -0
  309. package/src/designer/ColumnsEditor.tsx +204 -0
  310. package/src/designer/DesignerView.tsx +184 -0
  311. package/src/designer/ForeignKeysEditor.tsx +132 -0
  312. package/src/designer/IndexesEditor.tsx +63 -0
  313. package/src/designer/NamePicker.tsx +44 -0
  314. package/src/designer/OptionsEditor.tsx +101 -0
  315. package/src/designer/SqlPreview.tsx +56 -0
  316. package/src/designer/TableDesigner.tsx +186 -0
  317. package/src/designer/edit.ts +137 -0
  318. package/src/designer/editor-props.ts +13 -0
  319. package/src/designer/plan.ts +25 -0
  320. package/src/designer/recover.ts +20 -0
  321. package/src/designer/useReferenceColumns.ts +54 -0
  322. package/src/explorer/DatabaseExplorer.tsx +450 -0
  323. package/src/explorer/RowMenu.tsx +134 -0
  324. package/src/explorer/TableDialog.tsx +80 -0
  325. package/src/explorer/expansion.ts +47 -0
  326. package/src/explorer/table-statements.ts +18 -0
  327. package/src/explorer/tree.ts +453 -0
  328. package/src/explorer/use-explorer-loads.ts +157 -0
  329. package/src/explorer/use-stored-expansion.ts +66 -0
  330. package/src/grid/CellEditor.tsx +53 -0
  331. package/src/grid/CopyAsMenu.tsx +33 -0
  332. package/src/grid/DataGrid.tsx +871 -0
  333. package/src/grid/EnumPicker.tsx +108 -0
  334. package/src/grid/GridHeaderCell.tsx +243 -0
  335. package/src/grid/RecordView.tsx +293 -0
  336. package/src/grid/ValueDock.tsx +52 -0
  337. package/src/grid/aggregates.ts +94 -0
  338. package/src/grid/cell-range.ts +26 -0
  339. package/src/grid/column-layout.ts +71 -0
  340. package/src/grid/column-selection.ts +42 -0
  341. package/src/grid/column-view.ts +35 -0
  342. package/src/grid/copy-formats.ts +108 -0
  343. package/src/grid/display.ts +109 -0
  344. package/src/grid/edit-value.ts +60 -0
  345. package/src/grid/enum-type.ts +62 -0
  346. package/src/grid/focused-value.ts +23 -0
  347. package/src/grid/layout.ts +91 -0
  348. package/src/grid/navigation.ts +74 -0
  349. package/src/grid/row-selection.ts +35 -0
  350. package/src/grid/sort.ts +12 -0
  351. package/src/grid/types.ts +59 -0
  352. package/src/host/helper-path.ts +41 -0
  353. package/src/host/host.ts +456 -0
  354. package/src/host/index.ts +5 -0
  355. package/src/host/spawn.ts +124 -0
  356. package/src/host/types.ts +46 -0
  357. package/src/host/validate.ts +402 -0
  358. package/src/index.ts +37 -0
  359. package/src/locales/en.json +577 -0
  360. package/src/locales/nl.json +577 -0
  361. package/src/locales.ts +18 -0
  362. package/src/protocol/connection.ts +86 -0
  363. package/src/protocol/errors.ts +38 -0
  364. package/src/protocol/index.ts +33 -0
  365. package/src/protocol/messages.ts +168 -0
  366. package/src/protocol/schema.ts +76 -0
  367. package/src/protocol/values.ts +52 -0
  368. package/src/sql-split.ts +220 -0
  369. package/src/sql.ts +152 -0
  370. package/src/structure/StructureView.tsx +269 -0
  371. package/src/structure/structure-text.ts +4 -0
  372. package/src/table/CommandField.tsx +318 -0
  373. package/src/table/ImportDialog.tsx +168 -0
  374. package/src/table/TableStatusBar.tsx +91 -0
  375. package/src/table/TableToolbar.tsx +217 -0
  376. package/src/table/TableView.tsx +880 -0
  377. package/src/table/command-field.ts +285 -0
  378. package/src/table/grid-rows.ts +80 -0
  379. package/src/table/import-mapping.ts +37 -0
  380. package/src/table/layout-store.ts +62 -0
  381. package/src/table/paging.ts +33 -0
  382. package/src/table/pending.ts +143 -0
  383. package/src/table/references.ts +38 -0
  384. package/src/table/useLoaded.ts +58 -0
  385. package/src/table/useStoredLayout.ts +53 -0
  386. package/src/table/useTableTransfer.ts +187 -0
  387. package/src/testing/fake.ts +533 -0
  388. package/src/testing/index.ts +1 -0
  389. package/src/use-popup-press.ts +27 -0
  390. package/src/use-schema-change.ts +19 -0
  391. package/src/use-stable-callback.ts +12 -0
  392. package/src/value/ValuePanel.tsx +254 -0
  393. package/src/value/draft.ts +72 -0
  394. package/src/value/hex.ts +62 -0
  395. package/src/value/json.ts +74 -0
  396. package/src/value/views.ts +68 -0
  397. package/src/workbench/DatabaseWorkbench.tsx +348 -0
  398. package/src/workbench/DesignerTab.tsx +37 -0
  399. package/src/workbench/TableTab.tsx +27 -0
  400. package/src/workbench/tabs.ts +184 -0
@@ -0,0 +1,264 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from 'react';
3
+ import clsx from 'clsx';
4
+ import { ChevronRight, CircleAlert, Database, Eye, Folder, KeyRound, Link2, Lock, RectangleVertical, Search, Table } from 'lucide-react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Button, ContextMenu, EmptyState, Icon, Input, Spinner, Tooltip } from '@adecore/ui';
7
+ import { formatNumber } from '@adecore/ui/format';
8
+ import { useDatabaseAction, useDatabaseClient } from '../client-context.js';
9
+ import { EngineIcon } from '../connections/EngineIcon.js';
10
+ import { RowMenu } from './RowMenu.js';
11
+ import { TableDialog } from './TableDialog.js';
12
+ import { connectionKey, flattenTree, hasMenu, isExpandable, isFolderKey, isSelectable, navigate, neededLoads, selectionKey, selectionOf, tabStop, tableOf } from './tree.js';
13
+ import { useExplorerLoads } from './use-explorer-loads.js';
14
+ import { usePopupPress } from '../use-popup-press.js';
15
+ import { useStoredExpansion } from './use-stored-expansion.js';
16
+ const INDENT = 16;
17
+ /* Where the chevron slot of the first level starts inside a row. */
18
+ const BASE_INDENT = 4;
19
+ const CHEVRON_SIZE = 16;
20
+ /*
21
+ * The connections, their schemas and their tables as a tree, loaded as each node opens. Every row
22
+ * selects; a double click or Enter on a table asks the app to open it through `useDatabaseAction`.
23
+ */
24
+ export function DatabaseExplorer({ connections, value, defaultValue = null, onValueChange, showSystemSchemas = false, className, ref }) {
25
+ const { t } = useTranslation('database');
26
+ const client = useDatabaseClient();
27
+ const act = useDatabaseAction();
28
+ const loads = useExplorerLoads(connections);
29
+ const [own, setOwn] = useState(defaultValue);
30
+ const [request, setRequest] = useState(null);
31
+ const { expanded, collapsed, setExpanded, setCollapsed } = useStoredExpansion(connections);
32
+ const pressedInPopup = usePopupPress();
33
+ const [filter, setFilter] = useState('');
34
+ const [activeKey, setActiveKey] = useState(null);
35
+ const rowElements = useRef(new Map());
36
+ const focusAfterRender = useRef(false);
37
+ const selected = value === undefined ? own : value;
38
+ const selectedKey = selected === null ? null : selectionKey(selected);
39
+ const input = {
40
+ connections,
41
+ expanded,
42
+ collapsed,
43
+ showSystemSchemas,
44
+ schemas: loads.schemas,
45
+ tables: loads.tables,
46
+ structures: loads.structures,
47
+ versions: loads.versions
48
+ };
49
+ const rows = flattenTree({ ...input, filter });
50
+ const stop = tabStop(rows, activeKey, selectedKey);
51
+ useEffect(() => {
52
+ for (const target of neededLoads(input)) {
53
+ loads.start(target);
54
+ }
55
+ });
56
+ useEffect(() => {
57
+ if (focusAfterRender.current && activeKey !== null) {
58
+ focusAfterRender.current = false;
59
+ rowElements.current.get(activeKey)?.focus();
60
+ }
61
+ }, [activeKey]);
62
+ const setOpen = (key, open) => {
63
+ // A folder is open until the person closes it, everything else is closed until they open it.
64
+ const folder = isFolderKey(key);
65
+ const toggle = (current) => {
66
+ const next = new Set(current);
67
+ if (open !== folder) {
68
+ next.add(key);
69
+ }
70
+ else {
71
+ next.delete(key);
72
+ }
73
+ return next;
74
+ };
75
+ (folder ? setCollapsed : setExpanded)(toggle);
76
+ };
77
+ const toggle = (row) => {
78
+ setOpen(row.key, !row.expanded);
79
+ };
80
+ const select = (selection) => {
81
+ if (value === undefined) {
82
+ setOwn(selection);
83
+ }
84
+ onValueChange?.(selection);
85
+ };
86
+ const pick = (row) => {
87
+ const selection = selectionOf(row);
88
+ if (selection !== null) {
89
+ select(selection);
90
+ }
91
+ };
92
+ const open = (row) => {
93
+ const ref = tableOf(row);
94
+ if (ref !== null) {
95
+ pick(row);
96
+ act?.({ kind: 'open-table', ref, view: 'data' });
97
+ }
98
+ };
99
+ const activate = (row) => {
100
+ switch (row.kind) {
101
+ case 'table':
102
+ case 'column':
103
+ open(row);
104
+ break;
105
+ case 'connection':
106
+ case 'schema':
107
+ case 'folder':
108
+ pick(row);
109
+ toggle(row);
110
+ break;
111
+ case 'error':
112
+ loads.reset(row.retry);
113
+ break;
114
+ }
115
+ };
116
+ const onKeyDown = (e) => {
117
+ if (e.target.tagName === 'BUTTON') {
118
+ return;
119
+ }
120
+ const action = navigate(rows, activeKey ?? stop, e.key);
121
+ if (action === null) {
122
+ return;
123
+ }
124
+ e.preventDefault();
125
+ if (action.type === 'focus') {
126
+ focusAfterRender.current = true;
127
+ setActiveKey(action.key);
128
+ }
129
+ else if (action.type === 'activate') {
130
+ const row = rows.find((candidate) => candidate.key === action.key);
131
+ if (row !== undefined) {
132
+ activate(row);
133
+ }
134
+ }
135
+ else {
136
+ setOpen(action.key, action.type === 'expand');
137
+ }
138
+ };
139
+ const click = (row) => {
140
+ if (pressedInPopup()) {
141
+ return;
142
+ }
143
+ setActiveKey(row.key);
144
+ pick(row);
145
+ if (row.kind !== 'table' && isExpandable(row)) {
146
+ toggle(row);
147
+ }
148
+ else if (row.kind === 'error') {
149
+ loads.reset(row.retry);
150
+ }
151
+ };
152
+ const refresh = (target) => {
153
+ loads.reset(target);
154
+ };
155
+ const disconnect = (connection) => {
156
+ void client.disconnect(connection.id).catch(() => undefined);
157
+ loads.reset({ connectionId: connection.id });
158
+ setOpen(connectionKey(connection.id), false);
159
+ };
160
+ const finish = (done, to) => {
161
+ setRequest(null);
162
+ const { connectionId, schema, table } = done.ref;
163
+ if (selected?.connectionId === connectionId && selected.schema === schema && selected.table === table) {
164
+ select(to === undefined ? { connectionId, schema } : { connectionId, schema, table: to });
165
+ }
166
+ };
167
+ const connectionOf = (row) => {
168
+ const selection = selectionOf(row);
169
+ return selection === null ? undefined : connections.find((entry) => entry.id === selection.connectionId);
170
+ };
171
+ const isSelected = (row) => row !== undefined && isSelectable(row) && row.key === selectedKey;
172
+ const renderRow = (row, index) => {
173
+ const connection = connectionOf(row);
174
+ const menu = connection !== undefined && hasMenu(row) ? (_jsx(RowMenu, { row: row, connection: connection, onRefresh: refresh, onDisconnect: disconnect, onRequest: setRequest })) : null;
175
+ const selectedRow = isSelected(row);
176
+ const props = {
177
+ role: 'treeitem',
178
+ 'aria-level': row.level,
179
+ 'aria-setsize': row.setSize,
180
+ 'aria-posinset': row.posInSet,
181
+ 'aria-expanded': isExpandable(row) ? row.expanded : undefined,
182
+ 'aria-selected': isSelectable(row) ? selectedRow : undefined,
183
+ 'aria-busy': row.kind === 'loading' ? true : undefined,
184
+ tabIndex: row.focusable ? (row.key === stop ? 0 : -1) : undefined,
185
+ className: clsx(
186
+ // The line box is the row's height, so the text lands on whole pixels in a row of an odd height.
187
+ 'focus-ring relative mx-2 flex h-[25px] min-w-0 items-center gap-1.5 rounded-md pr-2 text-xs leading-[25px] text-text focus-visible:-outline-offset-2', selectedRow ? 'bg-surface-active' : row.focusable && 'hover:bg-surface-hover', selectedRow && isSelected(rows[index - 1]) && 'rounded-t-none', selectedRow && isSelected(rows[index + 1]) && 'rounded-b-none'),
188
+ style: { paddingInlineStart: BASE_INDENT + (row.level - 1) * INDENT },
189
+ ref: (element) => {
190
+ if (element === null) {
191
+ rowElements.current.delete(row.key);
192
+ }
193
+ else {
194
+ rowElements.current.set(row.key, element);
195
+ }
196
+ },
197
+ onClick: () => click(row),
198
+ onDoubleClick: tableOf(row) === null ? undefined : () => open(row),
199
+ onFocus: () => {
200
+ if (row.focusable) {
201
+ setActiveKey(row.key);
202
+ }
203
+ }
204
+ };
205
+ const content = (_jsxs(_Fragment, { children: [Array.from({ length: row.level - 1 }, (_, i) => (_jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute inset-y-0 w-px bg-border", style: { insetInlineStart: BASE_INDENT + i * INDENT + CHEVRON_SIZE / 2 - 1 } }, i))), _jsx(RowContent, { row: row, onToggle: isExpandable(row) ? () => toggle(row) : undefined })] }));
206
+ if (menu === null) {
207
+ return (_jsx("div", { ...props, children: content }, row.key));
208
+ }
209
+ return (_jsxs(ContextMenu.Root, { children: [_jsx(ContextMenu.Trigger, { render: _jsx("div", { ...props }), children: content }), _jsx(ContextMenu.Popup, { children: menu })] }, row.key));
210
+ };
211
+ return (_jsxs("div", { ref: ref, className: clsx('flex min-h-0 min-w-0 flex-col', className), children: [_jsx("div", { className: "shrink-0 border-b border-border p-2", children: _jsx(Input, { size: "sm", icon: Search, type: "search", value: filter, placeholder: t('explorer.filter'), "aria-label": t('explorer.filter'), spellCheck: false, onChange: (e) => setFilter(e.target.value), onKeyDown: (e) => {
212
+ if (e.key === 'Escape') {
213
+ setFilter('');
214
+ }
215
+ } }) }), connections.length === 0 ? (_jsx(EmptyState, { icon: Database, children: t('explorer.noConnections') })) : rows.length === 0 ? (_jsx(EmptyState, { icon: Search, children: t('explorer.noMatches', { query: filter.trim() }) })) : (_jsx("div", { role: "tree", "aria-label": t('explorer.label'), className: "min-h-0 grow overflow-y-auto py-2", onKeyDown: onKeyDown, children: rows.map(renderRow) })), request !== null && _jsx(TableDialog, { request: request, onClose: () => setRequest(null), onDone: finish })] }));
216
+ }
217
+ function RowContent({ row, onToggle }) {
218
+ const { t } = useTranslation('database');
219
+ switch (row.kind) {
220
+ case 'connection':
221
+ return (_jsxs(_Fragment, { children: [_jsx(Chevron, { open: row.expanded, onToggle: onToggle }), _jsx(EngineIcon, { engine: row.connection.config.engine, size: 16, className: "shrink-0 text-text-muted" }), _jsx("span", { className: "min-w-0 truncate", children: row.connection.name || t('connections.untitled') }), row.connection.config.readOnly === true && (_jsx(Tooltip, { label: t('explorer.readOnly'), children: _jsx("span", { className: "inline-flex shrink-0 text-text-faint", children: _jsx(Icon, { icon: Lock, size: 12 }) }) })), row.version !== null && _jsx("span", { className: "min-w-0 shrink-[2] truncate text-text-faint", children: row.version }), row.schemaCount !== null && _jsx(SchemaCount, { shown: row.schemaCount.shown, total: row.schemaCount.total })] }));
222
+ case 'schema':
223
+ return (_jsxs(_Fragment, { children: [_jsx(Chevron, { open: row.expanded, onToggle: onToggle }), _jsx(Icon, { icon: Database, size: 16, className: "shrink-0 text-text-muted" }), _jsx("span", { className: "min-w-0 truncate", children: row.schema })] }));
224
+ case 'folder':
225
+ return (_jsxs(_Fragment, { children: [_jsx(Chevron, { open: row.expanded, onToggle: onToggle }), _jsx(Icon, { icon: Folder, size: 16, className: "shrink-0 text-text-muted" }), _jsx("span", { className: "min-w-0 truncate", children: t(row.group === 'view' ? 'explorer.views' : 'explorer.tables') }), _jsx("span", { className: "shrink-0 text-text-faint tabular-nums", children: formatNumber(row.count) })] }));
226
+ case 'table':
227
+ return (_jsxs(_Fragment, { children: [_jsx(Chevron, { open: row.expanded, onToggle: onToggle }), _jsx(Icon, { icon: row.table.kind === 'view' ? Eye : Table, size: 16, className: "shrink-0 text-text-muted" }), _jsx("span", { className: "min-w-0 truncate", children: row.table.name })] }));
228
+ case 'column':
229
+ return (_jsxs(_Fragment, { children: [_jsx(ChevronSlot, {}), _jsx(ColumnIcon, { primaryKey: row.primaryKey, foreignKey: row.foreignKey }), _jsx("span", { className: "min-w-0 truncate", children: row.column.name }), row.column.type !== '' && _jsx("span", { className: "min-w-0 shrink-[2] truncate text-text-faint", children: row.column.type })] }));
230
+ case 'loading':
231
+ return (_jsxs(_Fragment, { children: [_jsx(ChevronSlot, {}), _jsx(Spinner, { size: 12 }), _jsx("span", { className: "text-text-faint", children: t('explorer.loading') })] }));
232
+ case 'error':
233
+ return _jsx(ErrorContent, { row: row });
234
+ case 'empty':
235
+ return (_jsxs(_Fragment, { children: [_jsx(ChevronSlot, {}), _jsx("span", { className: "text-text-faint", children: t(EMPTY_WORDS[row.of]) })] }));
236
+ }
237
+ }
238
+ const EMPTY_WORDS = { schemas: 'explorer.noSchemas', tables: 'explorer.noTables', columns: 'explorer.noColumns' };
239
+ /* Where a chevron would be, so a row without one lines up with its siblings that have one. */
240
+ function ChevronSlot() {
241
+ return _jsx("span", { className: "size-4 shrink-0" });
242
+ }
243
+ function Chevron({ open, onToggle }) {
244
+ return (_jsx("span", { className: "grid size-4 shrink-0 place-items-center text-text-muted", onClick: (e) => {
245
+ e.stopPropagation();
246
+ onToggle?.();
247
+ }, onDoubleClick: (e) => e.stopPropagation(), children: _jsx(Icon, { icon: ChevronRight, size: 12, className: clsx('transition-transform', open && 'rotate-90') }) }));
248
+ }
249
+ function SchemaCount({ shown, total }) {
250
+ const { t } = useTranslation('database');
251
+ const counts = { shown: formatNumber(shown), total: formatNumber(total) };
252
+ return (_jsx(Tooltip, { label: t('explorer.schemaCountHint', { count: total, ...counts }), children: _jsx("span", { className: "shrink-0 text-text-faint tabular-nums", children: t('explorer.schemaCount', counts) }) }));
253
+ }
254
+ function ColumnIcon({ primaryKey, foreignKey }) {
255
+ const { t } = useTranslation('database');
256
+ if (!primaryKey && !foreignKey) {
257
+ return _jsx(Icon, { icon: RectangleVertical, size: 16, className: "shrink-0 text-text-faint" });
258
+ }
259
+ return (_jsx(Tooltip, { label: t(primaryKey ? 'explorer.primaryKey' : 'explorer.foreignKey'), children: _jsx("span", { className: "inline-flex shrink-0 text-text-muted", children: _jsx(Icon, { icon: primaryKey ? KeyRound : Link2, size: 16 }) }) }));
260
+ }
261
+ function ErrorContent({ row }) {
262
+ const { t } = useTranslation('database');
263
+ return (_jsxs(_Fragment, { children: [_jsx(ChevronSlot, {}), _jsx(Icon, { icon: CircleAlert, size: 16, className: "shrink-0 text-status-error" }), _jsx(Tooltip, { label: row.message, children: _jsx("span", { className: "min-w-0 truncate text-status-error", children: row.message }) }), _jsx(Button, { size: "xs", variant: "ghost", className: "ml-auto", tabIndex: -1, children: t('explorer.retry') })] }));
264
+ }
@@ -0,0 +1,11 @@
1
+ import type { Connection } from '../client/types.ts';
2
+ import type { TableRequest } from './TableDialog.tsx';
3
+ import { type LoadTarget, type TreeRow } from './tree.ts';
4
+ export interface RowMenuProps {
5
+ row: TreeRow;
6
+ connection: Connection;
7
+ onRefresh(target: LoadTarget): void;
8
+ onDisconnect(connection: Connection): void;
9
+ onRequest(request: TableRequest): void;
10
+ }
11
+ export declare function RowMenu({ row, connection, onRefresh, onDisconnect, onRequest }: RowMenuProps): import("react").JSX.Element[];
@@ -0,0 +1,89 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment } from 'react';
3
+ import { Copy, Eraser, PencilRuler, Pencil, Plus, RefreshCw, Settings2, SquareTerminal, Table, TableProperties, Trash2, Unplug } from 'lucide-react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { ContextMenu, copyText, Icon } from '@adecore/ui';
6
+ import { useDatabaseAction, useDatabaseClient } from '../client-context.js';
7
+ import { selectAllSql } from './tree.js';
8
+ /* The items of a row's context menu. An item whose action has nowhere to go, or that writes to a read only connection, is left out. */
9
+ export function RowMenu({ row, connection, onRefresh, onDisconnect, onRequest }) {
10
+ const { t } = useTranslation('database');
11
+ const client = useDatabaseClient();
12
+ const act = useDatabaseAction();
13
+ const writable = connection.config.readOnly !== true;
14
+ const consoleAction = (schema, sql) => ({
15
+ kind: 'open-console',
16
+ connectionId: connection.id,
17
+ ...(schema === undefined ? {} : { schema }),
18
+ ...(sql === undefined ? {} : { sql })
19
+ });
20
+ const copyDdl = async (ref) => {
21
+ const structure = await client.session(connection).structure(ref.schema, ref.table);
22
+ if (structure.ddl !== null) {
23
+ copyText(structure.ddl);
24
+ }
25
+ };
26
+ const item = (key, icon, label, onClick, destructive = false) => (_jsxs(ContextMenu.Item, { className: destructive ? 'text-status-error' : undefined, onClick: onClick, children: [_jsx(Icon, { icon: icon, size: 14 }), label] }, key));
27
+ const actionItem = (key, icon, label, action) => act === undefined ? null : item(key, icon, label, () => act(action));
28
+ const tableGroups = (ref, kind) => {
29
+ const request = (change) => () => onRequest({ change, connection, ref, kind });
30
+ return [
31
+ [
32
+ actionItem('open-data', Table, t('explorer.openData'), { kind: 'open-table', ref, view: 'data' }),
33
+ actionItem('open-structure', TableProperties, t('explorer.openStructure'), { kind: 'open-table', ref, view: 'structure' }),
34
+ kind === 'table' && writable ? actionItem('edit-table', PencilRuler, t('explorer.editTable'), { kind: 'edit-table', ref }) : null,
35
+ actionItem('console', SquareTerminal, t('explorer.newConsoleHere'), consoleAction(ref.schema, selectAllSql(connection.config.engine, ref)))
36
+ ],
37
+ [
38
+ item('copy-name', Copy, t('explorer.copyName'), () => copyText(ref.table)),
39
+ item('copy-ddl', Copy, t('explorer.copyDdl'), () => void copyDdl(ref).catch(() => undefined))
40
+ ],
41
+ writable
42
+ ? [
43
+ kind === 'table' ? item('rename', Pencil, t('explorer.rename.action'), request('rename')) : null,
44
+ kind === 'table' ? item('truncate', Eraser, t('explorer.truncate.action'), request('truncate'), true) : null,
45
+ item('drop', Trash2, t(kind === 'view' ? 'explorer.drop.view.action' : 'explorer.drop.table.action'), request('drop'), true)
46
+ ]
47
+ : []
48
+ ];
49
+ };
50
+ const groupsOf = () => {
51
+ switch (row.kind) {
52
+ case 'connection':
53
+ return [
54
+ [
55
+ actionItem('console', SquareTerminal, t('explorer.newConsole'), consoleAction()),
56
+ item('refresh', RefreshCw, t('explorer.refresh'), () => onRefresh({ connectionId: connection.id })),
57
+ actionItem('manage', Settings2, t('explorer.editConnection'), { kind: 'manage-connection', connectionId: connection.id })
58
+ ],
59
+ [item('disconnect', Unplug, t('explorer.disconnect'), () => onDisconnect(connection))]
60
+ ];
61
+ case 'schema':
62
+ case 'folder':
63
+ return [
64
+ [
65
+ writable
66
+ ? actionItem('new-table', Plus, t('explorer.newTable'), { kind: 'new-table', connectionId: connection.id, schema: row.schema })
67
+ : null,
68
+ actionItem('console', SquareTerminal, t('explorer.newConsoleHere'), consoleAction(row.schema)),
69
+ item('refresh', RefreshCw, t('explorer.refresh'), () => onRefresh({ connectionId: connection.id, schema: row.schema }))
70
+ ]
71
+ ];
72
+ case 'table':
73
+ return tableGroups(row.ref, row.table.kind);
74
+ case 'column':
75
+ return [
76
+ [
77
+ actionItem('open-data', Table, t('explorer.open'), { kind: 'open-table', ref: row.ref, view: 'data' }),
78
+ item('copy-name', Copy, t('explorer.copyName'), () => copyText(row.column.name))
79
+ ]
80
+ ];
81
+ default:
82
+ return [];
83
+ }
84
+ };
85
+ const groups = groupsOf()
86
+ .map((items) => items.filter((entry) => entry !== null))
87
+ .filter((items) => items.length > 0);
88
+ return groups.map((items, i) => (_jsxs(Fragment, { children: [i > 0 && _jsx(ContextMenu.Separator, {}), items] }, i)));
89
+ }
@@ -0,0 +1,15 @@
1
+ import type { Connection, TableRef } from '../client/types.ts';
2
+ import type { TableKind } from '../protocol/index.ts';
3
+ import { type TableChange } from './table-statements.ts';
4
+ export interface TableRequest {
5
+ readonly change: TableChange;
6
+ readonly connection: Connection;
7
+ readonly ref: TableRef;
8
+ readonly kind: TableKind;
9
+ }
10
+ export interface TableDialogProps {
11
+ request: TableRequest;
12
+ onClose(): void;
13
+ onDone(request: TableRequest, to: string | undefined): void;
14
+ }
15
+ export declare function TableDialog({ request, onClose, onDone }: TableDialogProps): import("react").JSX.Element;
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useTranslation } from 'react-i18next';
3
+ import { PromptDialog } from '@adecore/ui';
4
+ import { dialectOf } from '../ddl/index.js';
5
+ import { useDatabaseClient } from '../client-context.js';
6
+ import { statementOf } from './table-statements.js';
7
+ /* The question behind Rename, Truncate and Drop: it runs the statement, keeps a failure in the dialog and sees to it that the listeners hear the shape changed. */
8
+ export function TableDialog({ request, onClose, onDone }) {
9
+ const { t } = useTranslation('database');
10
+ const client = useDatabaseClient();
11
+ const { change, connection, ref, kind } = request;
12
+ const words = change === 'drop' ? `explorer.drop.${kind === 'view' ? 'view' : 'table'}` : `explorer.${change}`;
13
+ const run = async (to) => {
14
+ const session = client.session(connection);
15
+ const dialect = dialectOf(await session.server());
16
+ const { results } = await session.execute(statementOf(dialect, change, ref, kind, to));
17
+ const failed = results.find((result) => result.kind === 'error');
18
+ if (failed !== undefined && failed.kind === 'error') {
19
+ throw new Error(failed.error.message);
20
+ }
21
+ // The client tells its listeners after a `RENAME`, a `DROP` and a `TRUNCATE`, but not after a SQLite emptying, which is a `DELETE FROM`.
22
+ if (change === 'truncate' && dialect.engine === 'sqlite') {
23
+ client.notifySchemaChange({ connectionId: ref.connectionId, schema: ref.schema });
24
+ }
25
+ onDone(request, to);
26
+ };
27
+ const confirm = async (typed) => {
28
+ if (change === 'rename') {
29
+ if (typed === ref.table) {
30
+ onClose();
31
+ return;
32
+ }
33
+ await run(typed);
34
+ }
35
+ else if (change === 'drop') {
36
+ if (typed !== ref.table) {
37
+ throw new Error(t('explorer.drop.mismatch'));
38
+ }
39
+ await run();
40
+ }
41
+ else {
42
+ await run();
43
+ }
44
+ };
45
+ return (_jsx(PromptDialog, { open: true, danger: change !== 'rename', title: t(`${words}.title`, { name: ref.table }), description: change === 'rename' ? undefined : t(`${words}.description`, { name: ref.table }), field: change === 'truncate'
46
+ ? undefined
47
+ : change === 'rename'
48
+ ? { label: t('explorer.rename.field'), initial: ref.table, mono: true }
49
+ : { label: t('explorer.drop.field', { name: ref.table }), placeholder: ref.table, mono: true }, confirmLabel: t(`${words}.confirm`), onConfirm: confirm, onOpenChange: onClose }));
50
+ }
@@ -0,0 +1,7 @@
1
+ export interface Expansion {
2
+ readonly expanded: ReadonlySet<string>;
3
+ readonly collapsed: ReadonlySet<string>;
4
+ }
5
+ export declare const expansionKey: (connectionId: string) => string;
6
+ export declare const parseExpansion: (raw: string | null, connectionId: string) => Expansion;
7
+ export declare const serializeExpansion: (connectionId: string, { expanded, collapsed }: Expansion) => string | null;
@@ -0,0 +1,31 @@
1
+ import { connectionOfKey, isFolderKey } from './tree.js';
2
+ const VERSION = 1;
3
+ export const expansionKey = (connectionId) => `database:explorer:${connectionId}`;
4
+ /* Reads what `serializeExpansion` wrote. Anything that does not fit gives nothing open and no folder closed. */
5
+ export const parseExpansion = (raw, connectionId) => {
6
+ const none = { expanded: new Set(), collapsed: new Set() };
7
+ if (raw === null) {
8
+ return none;
9
+ }
10
+ try {
11
+ const parsed = JSON.parse(raw);
12
+ if (typeof parsed !== 'object' || parsed === null || parsed.version !== VERSION) {
13
+ return none;
14
+ }
15
+ const { expanded, collapsed } = parsed;
16
+ const own = (keys, folders) => new Set(Array.isArray(keys)
17
+ ? keys.filter((key) => typeof key === 'string' && key.length > 2 && isFolderKey(key) === folders && connectionOfKey(key) === connectionId)
18
+ : []);
19
+ return { expanded: own(expanded, false), collapsed: own(collapsed, true) };
20
+ }
21
+ catch {
22
+ return none;
23
+ }
24
+ };
25
+ /* What a connection has open as the text to store, or `null` when everything is as it starts, which removes the key. */
26
+ export const serializeExpansion = (connectionId, { expanded, collapsed }) => {
27
+ const own = (keys) => [...keys].filter((key) => connectionOfKey(key) === connectionId).sort();
28
+ const open = own(expanded);
29
+ const closed = own(collapsed);
30
+ return open.length === 0 && closed.length === 0 ? null : JSON.stringify({ version: VERSION, expanded: open, collapsed: closed });
31
+ };
@@ -0,0 +1,5 @@
1
+ import type { TableRef } from '../client/types.ts';
2
+ import { type Dialect } from '../ddl/index.ts';
3
+ import type { TableKind } from '../protocol/index.ts';
4
+ export type TableChange = 'rename' | 'truncate' | 'drop';
5
+ export declare const statementOf: (dialect: Dialect, change: TableChange, ref: TableRef, kind: TableKind, to?: string) => string;
@@ -0,0 +1,12 @@
1
+ import { dropTableSql, dropViewSql, renameTableSql, truncateTableSql } from '../ddl/index.js';
2
+ /* The statement for a change; `to` is the new name of a rename. */
3
+ export const statementOf = (dialect, change, ref, kind, to = ref.table) => {
4
+ switch (change) {
5
+ case 'rename':
6
+ return renameTableSql(dialect, ref.schema, ref.table, to);
7
+ case 'truncate':
8
+ return truncateTableSql(dialect, ref.schema, ref.table);
9
+ case 'drop':
10
+ return kind === 'view' ? dropViewSql(dialect, ref.schema, ref.table) : dropTableSql(dialect, ref.schema, ref.table);
11
+ }
12
+ };
@@ -0,0 +1,125 @@
1
+ import type { ExplorerSelection } from '../actions.ts';
2
+ import type { Connection, TableRef } from '../client/types.ts';
3
+ import type { ColumnInfo, Engine, SchemaInfo, TableInfo, TableKind, TableStructure } from '../protocol/index.ts';
4
+ export type Load<T> = {
5
+ readonly status: 'idle';
6
+ } | {
7
+ readonly status: 'loading';
8
+ } | {
9
+ readonly status: 'error';
10
+ readonly message: string;
11
+ } | {
12
+ readonly status: 'ready';
13
+ readonly value: T;
14
+ };
15
+ export declare const IDLE: Load<never>;
16
+ export interface LoadTarget {
17
+ readonly connectionId: string;
18
+ readonly schema?: string;
19
+ readonly table?: string;
20
+ }
21
+ export declare const connectionKey: (connectionId: string) => string;
22
+ export declare const schemaKey: (connectionId: string, schema: string) => string;
23
+ export declare const folderKey: (connectionId: string, schema: string, group: TableKind) => string;
24
+ export declare const tableKey: (ref: TableRef) => string;
25
+ export declare const isFolderKey: (key: string) => boolean;
26
+ export declare const connectionOfKey: (key: string) => string;
27
+ export declare const columnKey: (ref: TableRef, column: string) => string;
28
+ export declare const loadKey: (target: LoadTarget) => string;
29
+ export declare const GROUPS: readonly TableKind[];
30
+ interface RowBase {
31
+ readonly key: string;
32
+ readonly level: number;
33
+ readonly parent: string | null;
34
+ readonly posInSet: number;
35
+ readonly setSize: number;
36
+ readonly focusable: boolean;
37
+ }
38
+ export interface ConnectionRow extends RowBase {
39
+ readonly kind: 'connection';
40
+ readonly connection: Connection;
41
+ readonly expanded: boolean;
42
+ readonly version: string | null;
43
+ readonly schemaCount: {
44
+ readonly shown: number;
45
+ readonly total: number;
46
+ } | null;
47
+ }
48
+ export interface SchemaRow extends RowBase {
49
+ readonly kind: 'schema';
50
+ readonly connection: Connection;
51
+ readonly schema: string;
52
+ readonly expanded: boolean;
53
+ }
54
+ export interface FolderRow extends RowBase {
55
+ readonly kind: 'folder';
56
+ readonly connection: Connection;
57
+ readonly schema: string;
58
+ readonly group: TableKind;
59
+ readonly expanded: boolean;
60
+ readonly count: number;
61
+ }
62
+ export interface TableRow extends RowBase {
63
+ readonly kind: 'table';
64
+ readonly ref: TableRef;
65
+ readonly table: TableInfo;
66
+ readonly expanded: boolean;
67
+ }
68
+ export interface ColumnRow extends RowBase {
69
+ readonly kind: 'column';
70
+ readonly ref: TableRef;
71
+ readonly column: ColumnInfo;
72
+ readonly primaryKey: boolean;
73
+ readonly foreignKey: boolean;
74
+ }
75
+ export interface LoadingRow extends RowBase {
76
+ readonly kind: 'loading';
77
+ }
78
+ export interface ErrorRow extends RowBase {
79
+ readonly kind: 'error';
80
+ readonly message: string;
81
+ readonly retry: LoadTarget;
82
+ }
83
+ export interface EmptyRow extends RowBase {
84
+ readonly kind: 'empty';
85
+ readonly of: 'schemas' | 'tables' | 'columns';
86
+ }
87
+ export type TreeRow = ConnectionRow | SchemaRow | FolderRow | TableRow | ColumnRow | LoadingRow | ErrorRow | EmptyRow;
88
+ export type ExpandableRow = ConnectionRow | SchemaRow | FolderRow | TableRow;
89
+ export declare const isExpandable: (row: TreeRow) => row is ExpandableRow;
90
+ export declare const tableOf: (row: TreeRow) => TableRef | null;
91
+ export declare const selectionOf: (row: TreeRow) => ExplorerSelection | null;
92
+ export declare const selectionKey: (selection: ExplorerSelection) => string;
93
+ export declare const hasMenu: (row: TreeRow) => boolean;
94
+ export declare const isSelectable: (row: TreeRow) => boolean;
95
+ export declare const selectAllSql: (engine: Engine, ref: TableRef) => string;
96
+ export interface TreeInput {
97
+ readonly connections: readonly Connection[];
98
+ readonly expanded: ReadonlySet<string>;
99
+ readonly collapsed: ReadonlySet<string>;
100
+ readonly filter: string;
101
+ readonly showSystemSchemas: boolean;
102
+ schemas(connectionId: string): Load<readonly SchemaInfo[]>;
103
+ tables(connectionId: string, schema: string): Load<readonly TableInfo[]>;
104
+ structures(connectionId: string, schema: string, table: string): Load<TableStructure>;
105
+ versions(connectionId: string): string | null;
106
+ }
107
+ export declare const visibleSchemas: (schemas: readonly SchemaInfo[], showSystemSchemas: boolean) => readonly SchemaInfo[];
108
+ export declare const flattenTree: (input: TreeInput) => TreeRow[];
109
+ export declare const neededLoads: (input: Omit<TreeInput, 'filter'>) => LoadTarget[];
110
+ export type TreeAction = {
111
+ readonly type: 'focus';
112
+ readonly key: string;
113
+ } | {
114
+ readonly type: 'expand';
115
+ readonly key: string;
116
+ } | {
117
+ readonly type: 'collapse';
118
+ readonly key: string;
119
+ } | {
120
+ readonly type: 'activate';
121
+ readonly key: string;
122
+ };
123
+ export declare const navigate: (rows: readonly TreeRow[], activeKey: string | null, pressed: string) => TreeAction | null;
124
+ export declare const tabStop: (rows: readonly TreeRow[], activeKey: string | null, selectedKey: string | null) => string | null;
125
+ export {};