@adecore/database 0.17.0 → 0.18.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 (135) hide show
  1. package/README.md +1 -1
  2. package/dist/DatabaseProvider.d.ts +4 -3
  3. package/dist/DatabaseProvider.js +15 -3
  4. package/dist/KeyIcon.d.ts +4 -0
  5. package/dist/KeyIcon.js +14 -0
  6. package/dist/actions.d.ts +12 -0
  7. package/dist/client-context.d.ts +3 -1
  8. package/dist/client-context.js +2 -0
  9. package/dist/code-text.d.ts +1 -0
  10. package/dist/code-text.js +2 -0
  11. package/dist/column-keys.d.ts +5 -0
  12. package/dist/column-keys.js +2 -0
  13. package/dist/connections/ConnectionManager.d.ts +3 -2
  14. package/dist/connections/ConnectionManager.js +27 -16
  15. package/dist/connections/connection-config.js +2 -0
  16. package/dist/console/QueryConsole.d.ts +8 -3
  17. package/dist/console/QueryConsole.js +110 -50
  18. package/dist/console/SqlEditor.d.ts +2 -12
  19. package/dist/console/SqlEditor.js +29 -18
  20. package/dist/console/StatementResultView.d.ts +5 -1
  21. package/dist/console/StatementResultView.js +18 -5
  22. package/dist/console/TransactionControls.js +3 -6
  23. package/dist/console/editor-slot.d.ts +18 -0
  24. package/dist/console/editor-slot.js +1 -0
  25. package/dist/console/result-keys.d.ts +15 -0
  26. package/dist/console/result-keys.js +79 -0
  27. package/dist/designer/ColumnsEditor.js +2 -2
  28. package/dist/designer/DesignerView.d.ts +3 -2
  29. package/dist/designer/DesignerView.js +5 -4
  30. package/dist/designer/SqlPreview.js +2 -1
  31. package/dist/designer/TableDesigner.d.ts +3 -2
  32. package/dist/designer/TableDesigner.js +2 -2
  33. package/dist/explorer/DatabaseExplorer.d.ts +4 -1
  34. package/dist/explorer/DatabaseExplorer.js +79 -23
  35. package/dist/explorer/RowMenu.js +2 -2
  36. package/dist/explorer/expansion.js +2 -2
  37. package/dist/explorer/folders.d.ts +16 -0
  38. package/dist/explorer/folders.js +1 -0
  39. package/dist/explorer/tree.d.ts +55 -4
  40. package/dist/explorer/tree.js +137 -23
  41. package/dist/grid/CellEditor.js +3 -1
  42. package/dist/grid/DataGrid.d.ts +2 -2
  43. package/dist/grid/DataGrid.js +61 -59
  44. package/dist/grid/EnumPicker.js +2 -1
  45. package/dist/grid/GridHeaderCell.d.ts +1 -2
  46. package/dist/grid/GridHeaderCell.js +12 -5
  47. package/dist/grid/GridRowView.d.ts +49 -0
  48. package/dist/grid/GridRowView.js +51 -0
  49. package/dist/grid/RecordView.d.ts +4 -2
  50. package/dist/grid/RecordView.js +71 -22
  51. package/dist/grid/ValueDock.d.ts +3 -1
  52. package/dist/grid/ValueDock.js +9 -3
  53. package/dist/grid/cell-id.d.ts +1 -0
  54. package/dist/grid/cell-id.js +2 -0
  55. package/dist/grid/layout.d.ts +3 -2
  56. package/dist/grid/layout.js +15 -6
  57. package/dist/grid/types.d.ts +8 -1
  58. package/dist/grid/types.js +4 -3
  59. package/dist/index.d.ts +5 -1
  60. package/dist/index.js +2 -0
  61. package/dist/locales/en.json +32 -15
  62. package/dist/locales/nl.json +32 -15
  63. package/dist/protocol/index.d.ts +1 -1
  64. package/dist/protocol/messages.d.ts +1 -1
  65. package/dist/protocol/messages.js +1 -1
  66. package/dist/protocol/schema.d.ts +17 -0
  67. package/dist/structure/StructureView.d.ts +5 -3
  68. package/dist/structure/StructureView.js +8 -6
  69. package/dist/table/CommandField.js +2 -2
  70. package/dist/table/TableStatusBar.js +2 -2
  71. package/dist/table/TableToolbar.d.ts +3 -4
  72. package/dist/table/TableToolbar.js +4 -3
  73. package/dist/table/TableView.d.ts +4 -2
  74. package/dist/table/TableView.js +63 -91
  75. package/dist/table/grid-rows.js +4 -1
  76. package/dist/table/useTableTransfer.d.ts +3 -1
  77. package/dist/table/useTableTransfer.js +19 -5
  78. package/dist/testing/fake.d.ts +3 -1
  79. package/dist/testing/fake.js +7 -1
  80. package/dist/value/ValueEditor.d.ts +9 -0
  81. package/dist/value/ValueEditor.js +102 -0
  82. package/dist/value/ValuePanel.d.ts +1 -1
  83. package/dist/value/ValuePanel.js +3 -94
  84. package/dist/workbench/DatabaseWorkbench.js +4 -7
  85. package/package.json +6 -6
  86. package/src/DatabaseProvider.tsx +24 -5
  87. package/src/KeyIcon.tsx +15 -0
  88. package/src/actions.ts +28 -2
  89. package/src/client-context.ts +5 -1
  90. package/src/code-text.ts +2 -0
  91. package/src/column-keys.ts +8 -0
  92. package/src/connections/ConnectionManager.tsx +98 -68
  93. package/src/connections/connection-config.ts +2 -0
  94. package/src/console/QueryConsole.tsx +251 -150
  95. package/src/console/SqlEditor.tsx +33 -37
  96. package/src/console/StatementResultView.tsx +61 -27
  97. package/src/console/TransactionControls.tsx +8 -11
  98. package/src/console/editor-slot.ts +27 -0
  99. package/src/console/result-keys.ts +109 -0
  100. package/src/designer/ColumnsEditor.tsx +3 -3
  101. package/src/designer/DesignerView.tsx +14 -3
  102. package/src/designer/SqlPreview.tsx +2 -1
  103. package/src/designer/TableDesigner.tsx +5 -2
  104. package/src/explorer/DatabaseExplorer.tsx +170 -26
  105. package/src/explorer/RowMenu.tsx +2 -2
  106. package/src/explorer/expansion.ts +4 -4
  107. package/src/explorer/folders.ts +25 -0
  108. package/src/explorer/tree.ts +219 -28
  109. package/src/grid/CellEditor.tsx +3 -2
  110. package/src/grid/DataGrid.tsx +114 -176
  111. package/src/grid/EnumPicker.tsx +2 -1
  112. package/src/grid/GridHeaderCell.tsx +14 -9
  113. package/src/grid/GridRowView.tsx +184 -0
  114. package/src/grid/RecordView.tsx +188 -75
  115. package/src/grid/ValueDock.tsx +12 -3
  116. package/src/grid/cell-id.ts +2 -0
  117. package/src/grid/layout.ts +16 -6
  118. package/src/grid/types.ts +15 -3
  119. package/src/index.ts +5 -1
  120. package/src/locales/en.json +32 -15
  121. package/src/locales/nl.json +32 -15
  122. package/src/protocol/index.ts +13 -1
  123. package/src/protocol/messages.ts +1 -1
  124. package/src/protocol/schema.ts +28 -0
  125. package/src/structure/StructureView.tsx +16 -6
  126. package/src/table/CommandField.tsx +2 -2
  127. package/src/table/TableStatusBar.tsx +3 -3
  128. package/src/table/TableToolbar.tsx +107 -92
  129. package/src/table/TableView.tsx +95 -132
  130. package/src/table/grid-rows.ts +5 -3
  131. package/src/table/useTableTransfer.ts +21 -6
  132. package/src/testing/fake.ts +18 -1
  133. package/src/value/ValueEditor.tsx +213 -0
  134. package/src/value/ValuePanel.tsx +4 -206
  135. package/src/workbench/DatabaseWorkbench.tsx +9 -10
@@ -17,7 +17,8 @@
17
17
  "test": {
18
18
  "action": "Verbinding testen",
19
19
  "success": "Verbonden met {{server}}.",
20
- "failed": "De verbinding is mislukt."
20
+ "failed": "De verbinding is mislukt.",
21
+ "dismiss": "Sluiten"
21
22
  },
22
23
  "delete": {
23
24
  "action": "Verwijderen",
@@ -116,6 +117,18 @@
116
117
  "views": "Views",
117
118
  "primaryKey": "Primaire sleutel",
118
119
  "foreignKey": "Refererende sleutel",
120
+ "primaryAndForeignKey": "Primaire en refererende sleutel",
121
+ "parts": {
122
+ "columns": "Kolommen",
123
+ "keys": "Sleutels",
124
+ "foreignKeys": "Refererende sleutels",
125
+ "indexes": "Indexen",
126
+ "checks": "Checks",
127
+ "triggers": "Triggers"
128
+ },
129
+ "references": "{{columns}} → {{table}} ({{referenced}})",
130
+ "unique": "uniek",
131
+ "check": "Check",
119
132
  "readOnly": "Alleen lezen",
120
133
  "schemaCount": "{{shown}} van {{total}}",
121
134
  "schemaCountHint_one": "{{shown}} van {{total}} schema getoond, systeemschema's zijn verborgen",
@@ -272,7 +285,6 @@
272
285
  },
273
286
  "cloneRows": "Geselecteerde rijen klonen",
274
287
  "revertRows": "Geselecteerde rijen terugdraaien",
275
- "valuePanel": "Waardeneditor",
276
288
  "firstPage": "Eerste pagina",
277
289
  "lastPage": "Laatste pagina",
278
290
  "lastPageCounts": "Telt eerst de rijen",
@@ -295,7 +307,10 @@
295
307
  "next": "Volgende rij",
296
308
  "newRow": "Nieuwe rij",
297
309
  "row": "Rij {{number}}",
298
- "position": "{{index}} van {{count}} op deze pagina"
310
+ "position": "{{index}} van {{count}} op deze pagina",
311
+ "close": "Recordweergave sluiten",
312
+ "expand": "Hele waarde tonen",
313
+ "collapse": "Hele waarde verbergen"
299
314
  },
300
315
  "aggregates": {
301
316
  "sum": "Som",
@@ -313,7 +328,8 @@
313
328
  "running": "Exporteren als {{format}}",
314
329
  "cancel": "Annuleren",
315
330
  "done_one": "{{rows}} rij geëxporteerd.",
316
- "done_other": "{{rows}} rijen geëxporteerd."
331
+ "done_other": "{{rows}} rijen geëxporteerd.",
332
+ "failed": "Exporteren is mislukt"
317
333
  },
318
334
  "import": {
319
335
  "menu": "Importeren uit bestand",
@@ -329,7 +345,8 @@
329
345
  "skip": "Overslaan",
330
346
  "preview": "Eerste {{count}} rijen van het bestand",
331
347
  "cancel": "Annuleren",
332
- "confirm": "Importeren"
348
+ "confirm": "Importeren",
349
+ "readFailed": "Het bestand kon niet worden gelezen"
333
350
  },
334
351
  "more": "Meer acties",
335
352
  "of": "van",
@@ -357,6 +374,8 @@
357
374
  "running": "Bezig",
358
375
  "cancelled": "De opdracht is geannuleerd.",
359
376
  "result": "Resultaat",
377
+ "results": "Resultaten",
378
+ "closeResults": "Resultaten sluiten",
360
379
  "noRows": "De opdracht gaf geen rijen terug.",
361
380
  "rows": "Rijen: {{rows}}",
362
381
  "hasMore": "Er zijn meer rijen, maar die worden niet getoond.",
@@ -369,10 +388,6 @@
369
388
  "runAll": "Alles uitvoeren",
370
389
  "loading": "Laden",
371
390
  "dismiss": "Sluiten",
372
- "ranStatement": "Uitgevoerd: {{statement}}",
373
- "ranSelection": "De selectie is uitgevoerd",
374
- "ranAll_one": "{{formatted}} opdracht uitgevoerd",
375
- "ranAll_other": "Alle {{formatted}} opdrachten uitgevoerd",
376
391
  "rowRange": "Rijen: {{from}} tot {{to}}",
377
392
  "page": "Pagina {{page}}",
378
393
  "previousPage": "Vorige pagina",
@@ -384,15 +399,15 @@
384
399
  "schema": "Schema",
385
400
  "schemaPlaceholder": "Schema",
386
401
  "transaction": {
387
- "mode": "Transactiemodus",
388
- "auto": "Automatisch",
389
- "manual": "Handmatig",
390
- "hint": "Automatisch legt elke uitvoering vast. Handmatig houdt een transactie open tot je vastlegt of terugdraait.",
402
+ "autoCommit": "Automatisch vastleggen",
403
+ "hint": "Aan legt elke uitvoering vast. Uit houdt een transactie open tot je vastlegt of terugdraait.",
391
404
  "open": "Transactie open",
392
405
  "commit": "Vastleggen",
393
406
  "rollback": "Terugdraaien",
394
407
  "endTitle": "De transactie afsluiten?",
395
- "endDescription": "Er staat nog een transactie open. Leg haar vast om de wijzigingen te bewaren, of draai haar terug om ze weg te gooien."
408
+ "endDescription": "Er staat nog een transactie open. Leg haar vast om de wijzigingen te bewaren, of draai haar terug om ze weg te gooien.",
409
+ "commitFailed": "Vastleggen is mislukt",
410
+ "rollbackFailed": "Terugdraaien is mislukt"
396
411
  },
397
412
  "destructive": {
398
413
  "title": "Destructieve opdrachten uitvoeren?",
@@ -414,7 +429,9 @@
414
429
  "hint": "Klik om te bewerken, dubbelklik om uit te voeren.",
415
430
  "rows_one": "{{formatted}} rij",
416
431
  "rows_other": "{{formatted}} rijen"
417
- }
432
+ },
433
+ "pageFailed": "De pagina kon niet worden gelezen",
434
+ "exportFailed": "Exporteren is mislukt"
418
435
  },
419
436
  "value": {
420
437
  "title": "Waarde",
@@ -1,5 +1,5 @@
1
1
  export type { ConnectionConfig, DockerContainer, DockerTunnel, Engine, MysqlConnectionConfig, MysqlTlsMode, ServerInfo, SqliteConnectionConfig, SshTunnel, Tunnel } from './connection.ts';
2
2
  export type { DatabaseError, DatabaseErrorCode } from './errors.ts';
3
3
  export { PROTOCOL_VERSION, type DatabaseMethod, type DatabaseMethods, type DatabaseParams, type DatabaseRequest, type DatabaseResponse, type DatabaseResult, type ExportSource, type FileFormat, type HelperReady, type RowChange, type RowKey, type RowsResult, type StatementResult } from './messages.ts';
4
- export type { ColumnInfo, ForeignKeyInfo, IndexInfo, ResultColumn, SchemaInfo, TableInfo, TableKind, TableStructure } from './schema.ts';
4
+ export type { CheckInfo, ColumnInfo, ColumnSource, ForeignKeyInfo, IndexInfo, ResultColumn, SchemaInfo, TableInfo, TableKind, TableStructure, TriggerInfo } from './schema.ts';
5
5
  export { valueOfCell, type BinaryCell, type BinaryValue, type Cell, type EditValue, type LongTextCell, type Value, type ValueKind } from './values.ts';
@@ -2,7 +2,7 @@ import type { ConnectionConfig, DockerContainer, ServerInfo } from './connection
2
2
  import type { DatabaseError } from './errors.ts';
3
3
  import type { ResultColumn, SchemaInfo, TableInfo, TableStructure } from './schema.ts';
4
4
  import type { Cell, EditValue, Value } from './values.ts';
5
- export declare const PROTOCOL_VERSION = 2;
5
+ export declare const PROTOCOL_VERSION = 3;
6
6
  export type RowKey = Readonly<Record<string, Value>>;
7
7
  export type RowChange = {
8
8
  readonly kind: 'insert';
@@ -1,2 +1,2 @@
1
1
  /* Raised whenever a message changes shape, so a host refuses a helper from another release. */
2
- export const PROTOCOL_VERSION = 2;
2
+ export const PROTOCOL_VERSION = 3;
@@ -35,6 +35,15 @@ export interface ForeignKeyInfo {
35
35
  readonly onUpdate: string | null;
36
36
  readonly onDelete: string | null;
37
37
  }
38
+ export interface CheckInfo {
39
+ readonly name: string | null;
40
+ readonly expression: string;
41
+ }
42
+ export interface TriggerInfo {
43
+ readonly name: string;
44
+ readonly timing: string;
45
+ readonly event: string;
46
+ }
38
47
  export interface TableStructure {
39
48
  readonly schema: string;
40
49
  readonly name: string;
@@ -44,10 +53,18 @@ export interface TableStructure {
44
53
  readonly rowKey: readonly string[] | null;
45
54
  readonly indexes: readonly IndexInfo[];
46
55
  readonly foreignKeys: readonly ForeignKeyInfo[];
56
+ readonly checks?: readonly CheckInfo[];
57
+ readonly triggers?: readonly TriggerInfo[];
47
58
  readonly ddl: string | null;
48
59
  }
60
+ export interface ColumnSource {
61
+ readonly schema: string;
62
+ readonly table: string;
63
+ readonly column: string;
64
+ }
49
65
  export interface ResultColumn {
50
66
  readonly name: string;
51
67
  readonly type: string;
52
68
  readonly kind: ValueKind;
69
+ readonly source?: ColumnSource;
53
70
  }
@@ -1,14 +1,16 @@
1
- import { type Ref } from 'react';
1
+ import { type ReactNode, type Ref } from 'react';
2
2
  import type { Connection } from '../client/types.ts';
3
3
  import type { TableStructure } from '../protocol/index.ts';
4
4
  export interface StructureViewProps {
5
5
  connection: Connection;
6
6
  schema: string;
7
7
  table: string;
8
+ toolbarStart?: ReactNode;
8
9
  className?: string;
9
10
  ref?: Ref<HTMLDivElement>;
10
11
  }
11
- export declare function StructureView({ connection, schema, table, className, ref }: StructureViewProps): import("react").JSX.Element;
12
- export declare function StructureTabs({ structure }: {
12
+ export declare function StructureView({ connection, schema, table, toolbarStart, className, ref }: StructureViewProps): import("react").JSX.Element;
13
+ export declare function StructureTabs({ structure, start }: {
13
14
  structure: TableStructure;
15
+ start?: ReactNode;
14
16
  }): import("react").JSX.Element;
@@ -3,15 +3,16 @@ import { useEffect, useState } from 'react';
3
3
  import clsx from 'clsx';
4
4
  import { CircleAlert, Copy, KeyRound, Link2 } from 'lucide-react';
5
5
  import { useTranslation } from 'react-i18next';
6
- import { Banner, Button, EmptyState, Icon, Pill, Spinner, Tabs, copyText, messageOf } from '@adecore/ui';
6
+ import { Banner, Button, EmptyState, Icon, Pill, Separator, Spinner, Tabs, copyText, messageOf } from '@adecore/ui';
7
7
  import { useDatabaseClient } from '../client-context.js';
8
+ import { CODE_TEXT } from '../code-text.js';
8
9
  import { useSchemaChange } from '../use-schema-change.js';
9
10
  import { referenceOf } from './structure-text.js';
10
11
  const TH = 'sticky top-0 h-7 bg-surface px-3 text-left text-xs font-medium whitespace-nowrap text-text-faint';
11
12
  const TD = 'h-7 border-t border-border-soft px-3 text-xs whitespace-nowrap text-text select-text';
12
- const MONO = 'font-mono text-code';
13
+ const MONO = CODE_TEXT;
13
14
  /* The columns, indexes, foreign keys and DDL of one table or view. */
14
- export function StructureView({ connection, schema, table, className, ref }) {
15
+ export function StructureView({ connection, schema, table, toolbarStart, className, ref }) {
15
16
  const { t } = useTranslation('database');
16
17
  const client = useDatabaseClient();
17
18
  const [answer, setAnswer] = useState(null);
@@ -36,11 +37,12 @@ export function StructureView({ connection, schema, table, className, ref }) {
36
37
  current = false;
37
38
  };
38
39
  }, [client, connection, schema, table, attempt, reloads, t]);
39
- return (_jsxs("div", { ref: ref, className: clsx('flex min-h-0 min-w-0 flex-col', className), children: [load.status === 'loading' && (_jsx("div", { className: "grid grow place-items-center py-8 text-text-muted", children: _jsx(Spinner, { size: 20, label: t('structure.loading') }) })), load.status === 'error' && (_jsx("div", { className: "p-3", children: _jsx(Banner, { icon: CircleAlert, tone: "error", className: "w-full", message: load.message, children: _jsx(Button, { variant: "secondary", size: "sm", onClick: () => setAttempt(attempt + 1), children: t('structure.retry') }) }) })), load.status === 'ready' && _jsx(StructureTabs, { structure: load.structure })] }));
40
+ return (_jsxs("div", { ref: ref, className: clsx('flex min-h-0 min-w-0 flex-col', className), children: [load.status === 'loading' && (_jsx("div", { className: "grid grow place-items-center py-8 text-text-muted", children: _jsx(Spinner, { size: 20, label: t('structure.loading') }) })), load.status === 'error' && (_jsx("div", { className: "p-3", children: _jsx(Banner, { icon: CircleAlert, tone: "error", className: "w-full", message: load.message, children: _jsx(Button, { variant: "secondary", size: "sm", onClick: () => setAttempt(attempt + 1), children: t('structure.retry') }) }) })), load.status === 'ready' && _jsx(StructureTabs, { structure: load.structure, start: toolbarStart })] }));
40
41
  }
41
- export function StructureTabs({ structure }) {
42
+ export function StructureTabs({ structure, start }) {
42
43
  const { t } = useTranslation('database');
43
- return (_jsxs(Tabs.Root, { defaultValue: "columns", className: "flex min-h-0 grow flex-col", children: [_jsxs(Tabs.List, { className: "shrink-0 px-3", children: [_jsxs(Tabs.Tab, { value: "columns", children: [t('structure.columns'), _jsx(Tabs.Count, { value: structure.columns.length })] }), _jsxs(Tabs.Tab, { value: "indexes", children: [t('structure.indexes'), _jsx(Tabs.Count, { value: structure.indexes.length })] }), _jsxs(Tabs.Tab, { value: "foreignKeys", children: [t('structure.foreignKeys'), _jsx(Tabs.Count, { value: structure.foreignKeys.length })] }), _jsx(Tabs.Tab, { value: "ddl", children: t('structure.ddl') })] }), _jsx(Tabs.Panel, { value: "columns", className: "min-h-0 grow overflow-auto", children: _jsx(ColumnsTable, { structure: structure }) }), _jsx(Tabs.Panel, { value: "indexes", className: "min-h-0 grow overflow-auto", children: _jsx(IndexesTable, { indexes: structure.indexes }) }), _jsx(Tabs.Panel, { value: "foreignKeys", className: "min-h-0 grow overflow-auto", children: _jsx(ForeignKeysTable, { foreignKeys: structure.foreignKeys }) }), _jsx(Tabs.Panel, { value: "ddl", className: "min-h-0 grow overflow-auto", children: _jsx(Ddl, { ddl: structure.ddl }) })] }));
44
+ const head = start === undefined || start === null || start === false ? undefined : (_jsxs("span", { className: "flex items-center gap-4", children: [start, _jsx(Separator, {})] }));
45
+ return (_jsxs(Tabs.Root, { defaultValue: "columns", className: "flex min-h-0 grow flex-col", children: [_jsxs(Tabs.List, { className: "shrink-0 px-3", start: head, children: [_jsxs(Tabs.Tab, { value: "columns", children: [t('structure.columns'), _jsx(Tabs.Count, { value: structure.columns.length })] }), _jsxs(Tabs.Tab, { value: "indexes", children: [t('structure.indexes'), _jsx(Tabs.Count, { value: structure.indexes.length })] }), _jsxs(Tabs.Tab, { value: "foreignKeys", children: [t('structure.foreignKeys'), _jsx(Tabs.Count, { value: structure.foreignKeys.length })] }), _jsx(Tabs.Tab, { value: "ddl", children: t('structure.ddl') })] }), _jsx(Tabs.Panel, { value: "columns", className: "min-h-0 grow overflow-auto", children: _jsx(ColumnsTable, { structure: structure }) }), _jsx(Tabs.Panel, { value: "indexes", className: "min-h-0 grow overflow-auto", children: _jsx(IndexesTable, { indexes: structure.indexes }) }), _jsx(Tabs.Panel, { value: "foreignKeys", className: "min-h-0 grow overflow-auto", children: _jsx(ForeignKeysTable, { foreignKeys: structure.foreignKeys }) }), _jsx(Tabs.Panel, { value: "ddl", className: "min-h-0 grow overflow-auto", children: _jsx(Ddl, { ddl: structure.ddl }) })] }));
44
46
  }
45
47
  function Grid({ heads, children }) {
46
48
  return (_jsxs("table", { className: "w-full border-collapse", children: [_jsx("thead", { children: _jsx("tr", { children: heads.map((head) => (_jsx("th", { scope: "col", className: TH, children: head }, head))) }) }), _jsx("tbody", { children: children })] }));
@@ -3,7 +3,7 @@ import { useId, useImperativeHandle, useMemo, useRef, useState } from 'react';
3
3
  import clsx from 'clsx';
4
4
  import { ArrowDown, ArrowDownWideNarrow, ArrowRightToLine, ArrowUp, Code, Filter, Search, X } from 'lucide-react';
5
5
  import { useTranslation } from 'react-i18next';
6
- import { Icon, IconButton, Kbd, KEY_SHORTCUTS, Menu, shortcut } from '@adecore/ui';
6
+ import { Icon, IconButton, Kbd, KEY_SHORTCUTS, Keys, Menu, shortcut } from '@adecore/ui';
7
7
  import { addChip, filterStartText, removeChipAt, replaceChipAt, suggest, toggleSort } from './command-field.js';
8
8
  const FOCUS_SHORTCUT = shortcut('Mod+F');
9
9
  const SUGGESTION_ICON = {
@@ -126,7 +126,7 @@ export function CommandField({ chips, columns, engine, onChipsChange, onJumpToCo
126
126
  clear();
127
127
  }
128
128
  };
129
- return (_jsxs("div", { ref: ref, className: clsx('field field-sm focus-ring-within relative flex h-auto min-h-7 min-w-0 flex-1 flex-wrap items-center gap-1 py-0.5', className), onClick: (event) => event.target === event.currentTarget && input.current?.focus(), children: [_jsx(Icon, { icon: Search, size: 14, className: "shrink-0 text-text-faint" }), chips.map((chip, index) => index === editing ? null : (_jsx(ChipView, { chip: chip, onActivate: () => activate(index, chip), onRemove: () => onChipsChange(removeChipAt(chips, index)) }, `${chip.kind}:${chip.kind === 'sort' ? chip.column : chip.text}`))), _jsx("input", { ref: input, role: "combobox", "aria-label": t('table.command.label'), "aria-expanded": open, "aria-controls": open ? listId : undefined, "aria-activedescendant": open ? optionId(active) : undefined, "aria-autocomplete": "list", value: text, placeholder: chips.length === 0 ? t('table.command.placeholder') : undefined, spellCheck: false, autoComplete: "off", className: "min-w-24 flex-1 bg-transparent font-mono text-code text-text outline-none placeholder:font-sans placeholder:text-text-faint", onChange: (event) => change(event.target.value), onKeyDown: handleKeyDown, onFocus: () => setFocused(true), onBlur: handleBlur }), _jsx(Kbd, { shortcut: FOCUS_SHORTCUT, variant: "inline", className: "shrink-0" }), open && (_jsx("div", { id: listId, role: "listbox", "aria-label": t('table.command.suggestions'), className: "menu-popup absolute top-full left-0 z-(--z-popup) mt-1.5 max-h-80 w-full max-w-xl",
129
+ return (_jsxs("div", { ref: ref, className: clsx('field field-sm focus-ring-within relative flex h-auto min-h-7 min-w-0 flex-1 flex-wrap items-center gap-1 py-0.5', className), onClick: (event) => event.target === event.currentTarget && input.current?.focus(), children: [_jsx(Icon, { icon: Search, size: 14, className: "shrink-0 text-text-faint" }), chips.map((chip, index) => index === editing ? null : (_jsx(ChipView, { chip: chip, onActivate: () => activate(index, chip), onRemove: () => onChipsChange(removeChipAt(chips, index)) }, `${chip.kind}:${chip.kind === 'sort' ? chip.column : chip.text}`))), _jsx("input", { ref: input, role: "combobox", "aria-label": t('table.command.label'), "aria-expanded": open, "aria-controls": open ? listId : undefined, "aria-activedescendant": open ? optionId(active) : undefined, "aria-autocomplete": "list", value: text, placeholder: chips.length === 0 ? t('table.command.placeholder') : undefined, spellCheck: false, autoComplete: "off", className: "min-w-24 flex-1 bg-transparent font-mono text-code text-text outline-none placeholder:font-sans placeholder:text-text-faint", onChange: (event) => change(event.target.value), onKeyDown: handleKeyDown, onFocus: () => setFocused(true), onBlur: handleBlur }), _jsx(Keys, { shortcut: FOCUS_SHORTCUT, size: "sm", className: "shrink-0" }), open && (_jsx("div", { id: listId, role: "listbox", "aria-label": t('table.command.suggestions'), className: "menu-popup absolute top-full left-0 z-(--z-popup) mt-1.5 max-h-80 w-full max-w-xl",
130
130
  // The input keeps the focus, or the popup would close under the pointer.
131
131
  onPointerDown: (event) => event.preventDefault(), children: suggestions.map((suggestion, index) => {
132
132
  const column = columnOf(suggestion);
@@ -2,7 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
2
2
  import clsx from 'clsx';
3
3
  import { ChevronLeft, ChevronRight } from 'lucide-react';
4
4
  import { useTranslation } from 'react-i18next';
5
- import { Button, IconButton, Spinner, Tooltip } from '@adecore/ui';
5
+ import { Button, ButtonGroup, IconButton, Spinner, Tooltip } from '@adecore/ui';
6
6
  import { formatDecimal, formatNumber, formatRounded } from '@adecore/ui/format';
7
7
  import { useNumberNotation } from '../client-context.js';
8
8
  import { numeralText } from '../grid/display.js';
@@ -10,7 +10,7 @@ import { numeralText } from '../grid/display.js';
10
10
  export function TableStatusBar({ elapsedMs, bounds, counting, selection, onCount, onPrevious, onNext, className, ref }) {
11
11
  const { t } = useTranslation('database');
12
12
  return (_jsxs("footer", { ref: ref, className: clsx('flex h-9 shrink-0 items-center gap-3 border-t border-border px-3 text-xs text-text-muted', className), children: [_jsxs("span", { className: "flex items-center gap-1 tabular-nums", children: [bounds.to === 0 ? t('table.noRows') : t('table.range', { from: formatNumber(bounds.from), to: formatNumber(bounds.to) }), bounds.to > 0 &&
13
- (bounds.exact ? (_jsx("span", { children: t('table.total', { total: formatNumber(bounds.total) }) })) : (_jsxs(_Fragment, { children: [_jsx("span", { children: t('table.of') }), _jsxs(Button, { size: "xs", "aria-label": t('table.count'), disabled: counting, onClick: onCount, children: [counting && _jsx(Spinner, { size: 12 }), t('table.atLeast', { total: formatNumber(bounds.total) })] })] })))] }), _jsxs("span", { className: "flex items-center", children: [_jsx(IconButton, { icon: ChevronLeft, size: "sm", label: t('table.previousPage'), disabled: !bounds.hasPrevious, onClick: onPrevious }), _jsx(IconButton, { icon: ChevronRight, size: "sm", label: t('table.nextPage'), disabled: !bounds.hasNext, onClick: onNext })] }), _jsx(Tooltip, { label: t('table.queryTime'), children: _jsx("span", { className: "text-text-faint tabular-nums", children: t('table.elapsed', { value: formatDecimal(elapsedMs) }) }) }), selection != null && _jsx(SelectionSummary, { selection: selection })] }));
13
+ (bounds.exact ? (_jsx("span", { children: t('table.total', { total: formatNumber(bounds.total) }) })) : (_jsxs(_Fragment, { children: [_jsx("span", { children: t('table.of') }), _jsxs(Button, { size: "xs", "aria-label": t('table.count'), disabled: counting, onClick: onCount, children: [counting && _jsx(Spinner, { size: 12 }), t('table.atLeast', { total: formatNumber(bounds.total) })] })] })))] }), _jsxs(ButtonGroup, { children: [_jsx(IconButton, { icon: ChevronLeft, size: "sm", label: t('table.previousPage'), disabled: !bounds.hasPrevious, onClick: onPrevious }), _jsx(IconButton, { icon: ChevronRight, size: "sm", label: t('table.nextPage'), disabled: !bounds.hasNext, onClick: onNext })] }), _jsx(Tooltip, { label: t('table.queryTime'), children: _jsx("span", { className: "text-text-faint tabular-nums", children: t('table.elapsed', { value: formatDecimal(elapsedMs) }) }) }), selection != null && _jsx(SelectionSummary, { selection: selection })] }));
14
14
  }
15
15
  function SelectionSummary({ selection }) {
16
16
  const { t } = useTranslation('database');
@@ -1,4 +1,4 @@
1
- import type { Ref } from 'react';
1
+ import type { ReactNode, Ref } from 'react';
2
2
  import type { Engine, FileFormat } from '../protocol/index.ts';
3
3
  import type { Chip, CommandColumn } from './command-field.ts';
4
4
  import { type PageBounds } from './paging.ts';
@@ -8,11 +8,11 @@ export interface TransferMenu {
8
8
  onImport?(): void;
9
9
  }
10
10
  export interface TableToolbarProps {
11
+ start?: ReactNode;
11
12
  refreshing: boolean;
12
13
  readOnlyReason: string | null;
13
14
  hasSelection: boolean;
14
15
  canRevertSelection: boolean;
15
- valuePanelOpen: boolean;
16
16
  recordViewOpen?: boolean;
17
17
  transfer?: TransferMenu;
18
18
  pendingCount: number;
@@ -30,7 +30,6 @@ export interface TableToolbarProps {
30
30
  onDeleteRows(): void;
31
31
  onCloneRows(): void;
32
32
  onRevertRows(): void;
33
- onToggleValuePanel(): void;
34
33
  onToggleRecordView?(): void;
35
34
  onPageSizeChange(size: number): void;
36
35
  onFirstPage(): void;
@@ -40,4 +39,4 @@ export interface TableToolbarProps {
40
39
  className?: string;
41
40
  ref?: Ref<HTMLDivElement>;
42
41
  }
43
- export declare function TableToolbar({ refreshing, readOnlyReason, hasSelection, canRevertSelection, valuePanelOpen, recordViewOpen, transfer, pendingCount, submitting, engine, chips, columns, pageSize, bounds, commandRef, onChipsChange, onJumpToColumn, onRefresh, onAddRow, onDeleteRows, onCloneRows, onRevertRows, onToggleValuePanel, onToggleRecordView, onPageSizeChange, onFirstPage, onLastPage, onSubmit, onRevert, className, ref }: TableToolbarProps): import("react").JSX.Element;
42
+ export declare function TableToolbar({ start, refreshing, readOnlyReason, hasSelection, canRevertSelection, recordViewOpen, transfer, pendingCount, submitting, engine, chips, columns, pageSize, bounds, commandRef, onChipsChange, onJumpToColumn, onRefresh, onAddRow, onDeleteRows, onCloneRows, onRevertRows, onToggleRecordView, onPageSizeChange, onFirstPage, onLastPage, onSubmit, onRevert, className, ref }: TableToolbarProps): import("react").JSX.Element;
@@ -2,15 +2,16 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
2
2
  import clsx from 'clsx';
3
3
  import { ChevronsLeft, ChevronsRight, CopyPlus, Download, Ellipsis, FileInput, PanelRight, Plus, RefreshCw, Rows3, TableProperties, Trash2, Undo2 } from 'lucide-react';
4
4
  import { useTranslation } from 'react-i18next';
5
- import { Button, Icon, IconButton, Menu } from '@adecore/ui';
5
+ import { Button, ButtonGroup, Icon, IconButton, Menu, Separator } from '@adecore/ui';
6
6
  import { formatNumber } from '@adecore/ui/format';
7
7
  import { CommandField } from './CommandField.js';
8
8
  import { PAGE_SIZES } from './paging.js';
9
9
  const EXPORT_FORMATS = ['csv', 'tsv', 'json', 'sql'];
10
10
  /* The command field for filtering, sorting and jumping to a column, refresh, add row, the submit of pending changes, and a menu of the rest. */
11
- export function TableToolbar({ refreshing, readOnlyReason, hasSelection, canRevertSelection, valuePanelOpen, recordViewOpen = false, transfer, pendingCount, submitting, engine, chips, columns, pageSize, bounds, commandRef, onChipsChange, onJumpToColumn, onRefresh, onAddRow, onDeleteRows, onCloneRows, onRevertRows, onToggleValuePanel, onToggleRecordView, onPageSizeChange, onFirstPage, onLastPage, onSubmit, onRevert, className, ref }) {
11
+ export function TableToolbar({ start, refreshing, readOnlyReason, hasSelection, canRevertSelection, recordViewOpen = false, transfer, pendingCount, submitting, engine, chips, columns, pageSize, bounds, commandRef, onChipsChange, onJumpToColumn, onRefresh, onAddRow, onDeleteRows, onCloneRows, onRevertRows, onToggleRecordView, onPageSizeChange, onFirstPage, onLastPage, onSubmit, onRevert, className, ref }) {
12
12
  const { t } = useTranslation('database');
13
13
  const readOnly = readOnlyReason !== null;
14
+ const hasStart = start !== undefined && start !== null && start !== false;
14
15
  const tooltipOf = (label) => (readOnly ? t('table.unavailable', { action: label, reason: readOnlyReason }) : label);
15
- return (_jsxs("div", { ref: ref, className: clsx('flex shrink-0 items-center gap-2 border-b border-border px-3 py-2', className), children: [_jsx(CommandField, { chips: chips, columns: columns, engine: engine, inputRef: commandRef, onChipsChange: onChipsChange, onJumpToColumn: onJumpToColumn }), pendingCount > 0 && (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "primary", size: "sm", disabled: submitting, onClick: onSubmit, children: t('table.submit', { changes: formatNumber(pendingCount) }) }), _jsx(Button, { size: "sm", disabled: submitting, onClick: onRevert, children: t('table.revert') })] })), _jsx(IconButton, { icon: RefreshCw, size: "sm", label: t('table.refresh'), busy: refreshing, onClick: onRefresh }), _jsx(IconButton, { icon: Plus, size: "sm", label: t('table.addRow'), tooltip: tooltipOf(t('table.addRow')), "aria-disabled": readOnly || undefined, onClick: () => !readOnly && onAddRow() }), _jsxs(Menu.Root, { children: [_jsx(IconButton, { icon: Ellipsis, size: "sm", label: t('table.more'), render: _jsx(Menu.Trigger, {}) }), _jsxs(Menu.Popup, { align: "end", children: [_jsxs(Menu.SubmenuRoot, { children: [_jsxs(Menu.SubmenuTrigger, { children: [_jsx(Icon, { icon: Rows3, size: 14 }), t('table.pageSize')] }), _jsx(Menu.Popup, { children: _jsx(Menu.RadioGroup, { value: String(pageSize), onValueChange: (value) => onPageSizeChange(Number(value)), children: PAGE_SIZES.map((size) => (_jsx(Menu.RadioItem, { value: String(size), children: t('table.perPage', { size: formatNumber(size) }) }, size))) }) })] }), _jsxs(Menu.Item, { disabled: !bounds.hasPrevious, onClick: onFirstPage, children: [_jsx(Icon, { icon: ChevronsLeft, size: 14 }), t('table.firstPage')] }), _jsxs(Menu.Item, { disabled: !bounds.hasNext, onClick: onLastPage, children: [_jsx(Icon, { icon: ChevronsRight, size: 14 }), t('table.lastPage'), !bounds.exact && _jsx(Menu.Hint, { children: t('table.lastPageCounts') })] }), _jsx(Menu.Separator, {}), onToggleRecordView !== undefined && (_jsxs(Menu.CheckboxItem, { checked: recordViewOpen, onCheckedChange: onToggleRecordView, children: [_jsx(Icon, { icon: TableProperties, size: 14 }), t('table.recordView')] })), _jsxs(Menu.CheckboxItem, { checked: valuePanelOpen, onCheckedChange: onToggleValuePanel, children: [_jsx(Icon, { icon: PanelRight, size: 14 }), t('table.valuePanel')] }), _jsx(Menu.Separator, {}), readOnly && _jsx(Menu.Label, { children: readOnlyReason }), _jsxs(Menu.Item, { disabled: readOnly || !hasSelection, onClick: onCloneRows, children: [_jsx(Icon, { icon: CopyPlus, size: 14 }), t('table.cloneRows')] }), _jsxs(Menu.Item, { disabled: !canRevertSelection, onClick: onRevertRows, children: [_jsx(Icon, { icon: Undo2, size: 14 }), t('table.revertRows')] }), _jsxs(Menu.Item, { disabled: readOnly || !hasSelection, onClick: onDeleteRows, children: [_jsx(Icon, { icon: Trash2, size: 14 }), t('table.deleteRows')] }), transfer !== undefined && (_jsxs(_Fragment, { children: [_jsx(Menu.Separator, {}), _jsxs(Menu.SubmenuRoot, { children: [_jsxs(Menu.SubmenuTrigger, { disabled: transfer.busy, children: [_jsx(Icon, { icon: Download, size: 14 }), t('table.export.label')] }), _jsx(Menu.Popup, { children: EXPORT_FORMATS.map((format) => (_jsx(Menu.Item, { onClick: () => transfer.onExport(format), children: t('table.export.as', { format: format.toUpperCase() }) }, format))) })] }), transfer.onImport !== undefined && (_jsxs(Menu.Item, { onClick: transfer.onImport, children: [_jsx(Icon, { icon: FileInput, size: 14 }), t('table.import.menu')] }))] }))] })] })] }));
16
+ return (_jsxs("div", { ref: ref, className: clsx('flex shrink-0 items-center gap-2 border-b border-border px-3 py-2', className), children: [hasStart && (_jsxs(_Fragment, { children: [start, _jsx(Separator, {})] })), _jsx(CommandField, { chips: chips, columns: columns, engine: engine, inputRef: commandRef, onChipsChange: onChipsChange, onJumpToColumn: onJumpToColumn }), hasStart && _jsx(Separator, {}), pendingCount > 0 && (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "primary", size: "sm", disabled: submitting, onClick: onSubmit, children: t('table.submit', { changes: formatNumber(pendingCount) }) }), _jsx(Button, { size: "sm", disabled: submitting, onClick: onRevert, children: t('table.revert') })] })), _jsxs(ButtonGroup, { children: [_jsx(IconButton, { icon: RefreshCw, size: "sm", label: t('table.refresh'), busy: refreshing, onClick: onRefresh }), _jsx(IconButton, { icon: Plus, size: "sm", label: t('table.addRow'), tooltip: tooltipOf(t('table.addRow')), "aria-disabled": readOnly || undefined, onClick: () => !readOnly && onAddRow() }), onToggleRecordView !== undefined && (_jsx(IconButton, { icon: PanelRight, size: "sm", label: t('table.recordView'), "aria-pressed": recordViewOpen, active: recordViewOpen, onClick: onToggleRecordView })), _jsxs(Menu.Root, { children: [_jsx(IconButton, { icon: Ellipsis, size: "sm", label: t('table.more'), render: _jsx(Menu.Trigger, {}) }), _jsxs(Menu.Popup, { align: "end", children: [_jsxs(Menu.SubmenuRoot, { children: [_jsxs(Menu.SubmenuTrigger, { children: [_jsx(Icon, { icon: Rows3, size: 14 }), t('table.pageSize')] }), _jsx(Menu.Popup, { children: _jsx(Menu.RadioGroup, { value: String(pageSize), onValueChange: (value) => onPageSizeChange(Number(value)), children: PAGE_SIZES.map((size) => (_jsx(Menu.RadioItem, { value: String(size), children: t('table.perPage', { size: formatNumber(size) }) }, size))) }) })] }), _jsxs(Menu.Item, { disabled: !bounds.hasPrevious, onClick: onFirstPage, children: [_jsx(Icon, { icon: ChevronsLeft, size: 14 }), t('table.firstPage')] }), _jsxs(Menu.Item, { disabled: !bounds.hasNext, onClick: onLastPage, children: [_jsx(Icon, { icon: ChevronsRight, size: 14 }), t('table.lastPage'), !bounds.exact && _jsx(Menu.Hint, { children: t('table.lastPageCounts') })] }), _jsx(Menu.Separator, {}), onToggleRecordView !== undefined && (_jsxs(Menu.CheckboxItem, { checked: recordViewOpen, onCheckedChange: onToggleRecordView, children: [_jsx(Icon, { icon: TableProperties, size: 14 }), t('table.recordView')] })), _jsx(Menu.Separator, {}), readOnly && _jsx(Menu.Label, { children: readOnlyReason }), _jsxs(Menu.Item, { disabled: readOnly || !hasSelection, onClick: onCloneRows, children: [_jsx(Icon, { icon: CopyPlus, size: 14 }), t('table.cloneRows')] }), _jsxs(Menu.Item, { disabled: !canRevertSelection, onClick: onRevertRows, children: [_jsx(Icon, { icon: Undo2, size: 14 }), t('table.revertRows')] }), _jsxs(Menu.Item, { disabled: readOnly || !hasSelection, onClick: onDeleteRows, children: [_jsx(Icon, { icon: Trash2, size: 14 }), t('table.deleteRows')] }), transfer !== undefined && (_jsxs(_Fragment, { children: [_jsx(Menu.Separator, {}), _jsxs(Menu.SubmenuRoot, { children: [_jsxs(Menu.SubmenuTrigger, { disabled: transfer.busy, children: [_jsx(Icon, { icon: Download, size: 14 }), t('table.export.label')] }), _jsx(Menu.Popup, { children: EXPORT_FORMATS.map((format) => (_jsx(Menu.Item, { onClick: () => transfer.onExport(format), children: t('table.export.as', { format: format.toUpperCase() }) }, format))) })] }), transfer.onImport !== undefined && (_jsxs(Menu.Item, { onClick: transfer.onImport, children: [_jsx(Icon, { icon: FileInput, size: 14 }), t('table.import.menu')] }))] }))] })] })] })] }));
16
17
  }
@@ -1,13 +1,15 @@
1
- import { type Ref } from 'react';
1
+ import { type ReactNode, type Ref } from 'react';
2
2
  import { type Connection } from '../client/types.ts';
3
+ export declare const RECORD_WIDTH_KEY = "database:record-view";
3
4
  export interface TableViewProps {
4
5
  connection: Connection;
5
6
  schema: string;
6
7
  table: string;
8
+ toolbarStart?: ReactNode;
7
9
  defaultWhere?: string;
8
10
  defaultOrderBy?: string;
9
11
  onDirtyChange?(dirty: boolean): void;
10
12
  className?: string;
11
13
  ref?: Ref<HTMLDivElement>;
12
14
  }
13
- export declare function TableView({ connection, schema, table, defaultWhere, defaultOrderBy, onDirtyChange, className, ref }: TableViewProps): import("react").JSX.Element;
15
+ export declare function TableView({ connection, schema, table, toolbarStart, defaultWhere, defaultOrderBy, onDirtyChange, className, ref }: TableViewProps): import("react").JSX.Element;