@adecore/database 0.39.0 → 0.40.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.
- package/dist/console/QueryConsole.js +2 -10
- package/dist/console/SchemaPicker.js +0 -1
- package/dist/console/StatementResultView.js +1 -1
- package/dist/ddl/diff.js +1 -1
- package/dist/explorer/tree.js +0 -1
- package/dist/export-formats.d.ts +2 -0
- package/dist/export-formats.js +1 -0
- package/dist/grid/CopyAsMenu.js +0 -1
- package/dist/grid/DataGrid.js +10 -10
- package/dist/grid/column-layout.d.ts +0 -1
- package/dist/grid/column-layout.js +1 -2
- package/dist/grid/column-view.js +3 -2
- package/dist/grid/display.js +1 -2
- package/dist/host/host.js +1 -2
- package/dist/host/validate.d.ts +3 -0
- package/dist/host/validate.js +18 -15
- package/dist/same-list.d.ts +1 -0
- package/dist/same-list.js +1 -0
- package/dist/stored-size.d.ts +1 -0
- package/dist/stored-size.js +10 -0
- package/dist/table/TableToolbar.js +1 -1
- package/dist/table/TableView.js +2 -10
- package/package.json +15 -15
- package/src/DatabaseProvider.tsx +2 -0
- package/src/KeyIcon.tsx +1 -0
- package/src/client/client.ts +13 -0
- package/src/client-context.ts +2 -0
- package/src/connections/ConnectionForm.tsx +11 -0
- package/src/connections/ConnectionManager.tsx +9 -0
- package/src/connections/connection-config.ts +21 -0
- package/src/connections/docker.ts +2 -0
- package/src/connections/use-docker-containers.ts +3 -0
- package/src/console/DestructiveDialog.tsx +1 -0
- package/src/console/QueryConsole.tsx +38 -10
- package/src/console/SchemaPicker.tsx +1 -1
- package/src/console/SqlEditor.tsx +10 -0
- package/src/console/StatementResultView.tsx +7 -2
- package/src/console/TransactionControls.tsx +1 -0
- package/src/console/destructive.ts +11 -0
- package/src/console/editing.ts +10 -0
- package/src/console/history.ts +8 -0
- package/src/console/labels.ts +1 -0
- package/src/console/outcome.ts +1 -0
- package/src/console/result-keys.ts +16 -0
- package/src/console/shortcuts.ts +2 -0
- package/src/ddl/alter-mysql.ts +16 -0
- package/src/ddl/alter-sqlite.ts +21 -0
- package/src/ddl/alter.ts +2 -0
- package/src/ddl/column.ts +11 -0
- package/src/ddl/create.ts +15 -0
- package/src/ddl/dialect.ts +4 -0
- package/src/ddl/diff.ts +22 -2
- package/src/ddl/draft.ts +1 -0
- package/src/ddl/parse.ts +31 -0
- package/src/ddl/types.ts +3 -0
- package/src/ddl/validate.ts +12 -0
- package/src/designer/ColumnsEditor.tsx +3 -0
- package/src/designer/DesignerView.tsx +2 -0
- package/src/designer/ForeignKeysEditor.tsx +4 -0
- package/src/designer/IndexesEditor.tsx +1 -0
- package/src/designer/NamePicker.tsx +2 -0
- package/src/designer/OptionsEditor.tsx +4 -0
- package/src/designer/SqlPreview.tsx +2 -0
- package/src/designer/TableDesigner.tsx +14 -0
- package/src/designer/edit.ts +13 -0
- package/src/designer/plan.ts +3 -0
- package/src/designer/recover.ts +1 -0
- package/src/designer/useReferenceColumns.ts +5 -0
- package/src/explorer/DatabaseExplorer.tsx +40 -0
- package/src/explorer/RowMenu.tsx +2 -0
- package/src/explorer/TableDialog.tsx +6 -0
- package/src/explorer/expansion.ts +8 -0
- package/src/explorer/tree.ts +73 -2
- package/src/explorer/use-explorer-loads.ts +15 -0
- package/src/explorer/use-stored-expansion.ts +4 -0
- package/src/export-formats.ts +3 -0
- package/src/grid/CellEditor.tsx +1 -0
- package/src/grid/CopyAsMenu.tsx +1 -1
- package/src/grid/DataGrid.tsx +93 -23
- package/src/grid/EnumPicker.tsx +6 -0
- package/src/grid/GridHeaderCell.tsx +11 -0
- package/src/grid/GridRowView.tsx +3 -0
- package/src/grid/RecordView.tsx +19 -0
- package/src/grid/ValueDock.tsx +2 -0
- package/src/grid/aggregates.ts +12 -0
- package/src/grid/column-layout.ts +12 -4
- package/src/grid/column-selection.ts +9 -0
- package/src/grid/column-view.ts +6 -2
- package/src/grid/copy-formats.ts +13 -0
- package/src/grid/display.ts +17 -3
- package/src/grid/edit-value.ts +11 -0
- package/src/grid/enum-type.ts +15 -0
- package/src/grid/focused-value.ts +4 -0
- package/src/grid/layout.ts +12 -0
- package/src/grid/navigation.ts +5 -0
- package/src/grid/row-selection.ts +7 -0
- package/src/grid/sort.ts +1 -0
- package/src/grid/whole-cells.ts +8 -0
- package/src/host/host.ts +16 -3
- package/src/host/spawn.ts +2 -0
- package/src/host/validate.ts +37 -15
- package/src/same-list.ts +2 -0
- package/src/sql-split.ts +28 -0
- package/src/sql.ts +20 -0
- package/src/stored-size.ts +10 -0
- package/src/structure/StructureView.tsx +18 -0
- package/src/table/CommandField.tsx +11 -0
- package/src/table/ImportDialog.tsx +4 -0
- package/src/table/TableStatusBar.tsx +1 -0
- package/src/table/TableToolbar.tsx +2 -2
- package/src/table/TableView.tsx +50 -10
- package/src/table/command-field.ts +40 -0
- package/src/table/grid-rows.ts +13 -0
- package/src/table/import-mapping.ts +5 -0
- package/src/table/layout-store.ts +7 -0
- package/src/table/paging.ts +1 -0
- package/src/table/pending.ts +18 -0
- package/src/table/references.ts +4 -0
- package/src/table/useLoaded.ts +3 -0
- package/src/table/useStoredLayout.ts +2 -0
- package/src/table/useTableTransfer.ts +17 -1
- package/src/testing/fake.ts +24 -0
- package/src/use-database-resources.ts +1 -0
- package/src/use-popup-press.ts +3 -0
- package/src/value/ValueEditor.tsx +6 -0
- package/src/value/draft.ts +7 -0
- package/src/value/hex.ts +8 -0
- package/src/value/json.ts +16 -0
- package/src/value/views.ts +14 -0
- package/src/workbench/DatabaseWorkbench.tsx +22 -0
- package/src/workbench/tabs.ts +34 -0
|
@@ -87,13 +87,17 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
|
|
|
87
87
|
const known = useMemo(() => new Set(connections.map((connection) => connection.id)), [connections]);
|
|
88
88
|
// While there are no connections they may still be loading, so nothing is dropped then.
|
|
89
89
|
const pruned = known.size > 0 ? pruneTabs(state, known) : state;
|
|
90
|
+
|
|
90
91
|
if (pruned !== state) {
|
|
91
92
|
setState(pruned);
|
|
92
93
|
}
|
|
94
|
+
|
|
93
95
|
const isOpen = (id: string): boolean => state.tabs.some((tab) => tab.id === id);
|
|
96
|
+
|
|
94
97
|
if (![...dirty].every(isOpen)) {
|
|
95
98
|
setDirty(new Set([...dirty].filter(isOpen)));
|
|
96
99
|
}
|
|
100
|
+
|
|
97
101
|
const tabs = state.tabs.filter((tab) => known.has(connectionIdOf(tab)));
|
|
98
102
|
const active = tabs.find((tab) => tab.id === state.activeId) ?? tabs.at(-1) ?? null;
|
|
99
103
|
const canManage = onConnectionsChange !== undefined;
|
|
@@ -104,33 +108,42 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
|
|
|
104
108
|
|
|
105
109
|
const onAction = useStableCallback((action: DatabaseAction): void => {
|
|
106
110
|
const connectionId = action.kind === 'open-table' || action.kind === 'edit-table' ? action.ref.connectionId : action.connectionId;
|
|
111
|
+
|
|
107
112
|
if (!known.has(connectionId) || (action.kind === 'manage-connection' && !canManage)) {
|
|
108
113
|
outer?.(action);
|
|
114
|
+
|
|
109
115
|
return;
|
|
110
116
|
}
|
|
117
|
+
|
|
111
118
|
switch (action.kind) {
|
|
112
119
|
case 'open-table':
|
|
113
120
|
setSelection(action.ref);
|
|
114
121
|
setState((current) => openTable(current, action.ref, action.view, action.where));
|
|
122
|
+
|
|
115
123
|
break;
|
|
116
124
|
case 'open-console':
|
|
117
125
|
setState((current) => openConsole(current, action.connectionId, action.schema, action.sql));
|
|
126
|
+
|
|
118
127
|
break;
|
|
119
128
|
case 'new-table':
|
|
120
129
|
setState((current) => openDesigner(current, action.connectionId, action.schema));
|
|
130
|
+
|
|
121
131
|
break;
|
|
122
132
|
case 'edit-table':
|
|
123
133
|
setState((current) => openDesigner(current, action.ref.connectionId, action.ref.schema, action.ref.table));
|
|
134
|
+
|
|
124
135
|
break;
|
|
125
136
|
case 'manage-connection':
|
|
126
137
|
setManaged(action.connectionId);
|
|
127
138
|
setManaging(true);
|
|
139
|
+
|
|
128
140
|
break;
|
|
129
141
|
}
|
|
130
142
|
});
|
|
131
143
|
|
|
132
144
|
function newConsole(): void {
|
|
133
145
|
const context = consoleContext();
|
|
146
|
+
|
|
134
147
|
if (context !== null) {
|
|
135
148
|
setState((current) => openConsole(current, context.connectionId, context.schema));
|
|
136
149
|
}
|
|
@@ -141,10 +154,13 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
|
|
|
141
154
|
if (active !== null) {
|
|
142
155
|
return { connectionId: connectionIdOf(active), schema: active.kind === 'table' ? active.ref.schema : active.schema };
|
|
143
156
|
}
|
|
157
|
+
|
|
144
158
|
if (selection !== null && known.has(selection.connectionId)) {
|
|
145
159
|
return { connectionId: selection.connectionId, schema: selection.schema };
|
|
146
160
|
}
|
|
161
|
+
|
|
147
162
|
const first = connections[0];
|
|
163
|
+
|
|
148
164
|
return first === undefined ? null : { connectionId: first.id };
|
|
149
165
|
}
|
|
150
166
|
|
|
@@ -179,12 +195,15 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
|
|
|
179
195
|
if (current.has(id) === isDirty) {
|
|
180
196
|
return current;
|
|
181
197
|
}
|
|
198
|
+
|
|
182
199
|
const next = new Set(current);
|
|
200
|
+
|
|
183
201
|
if (isDirty) {
|
|
184
202
|
next.add(id);
|
|
185
203
|
} else {
|
|
186
204
|
next.delete(id);
|
|
187
205
|
}
|
|
206
|
+
|
|
188
207
|
return next;
|
|
189
208
|
});
|
|
190
209
|
}
|
|
@@ -202,9 +221,11 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
|
|
|
202
221
|
|
|
203
222
|
const viewOf = (tab: WorkbenchTab) => {
|
|
204
223
|
const connection = connections.find((entry) => entry.id === connectionIdOf(tab));
|
|
224
|
+
|
|
205
225
|
if (connection === undefined) {
|
|
206
226
|
return null;
|
|
207
227
|
}
|
|
228
|
+
|
|
208
229
|
switch (tab.kind) {
|
|
209
230
|
case 'table':
|
|
210
231
|
return <TableTab connection={connection} tab={tab} onDirtyChange={(isDirty) => markDirty(tab.id, isDirty)} />;
|
|
@@ -321,6 +342,7 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
|
|
|
321
342
|
if (closing !== null) {
|
|
322
343
|
closeById(closing);
|
|
323
344
|
}
|
|
345
|
+
|
|
324
346
|
setClosing(null);
|
|
325
347
|
}}
|
|
326
348
|
onOpenChange={(open) => {
|
package/src/workbench/tabs.ts
CHANGED
|
@@ -32,14 +32,18 @@ const add = (state: WorkbenchState, tab: WorkbenchTab): WorkbenchState => ({ ...
|
|
|
32
32
|
|
|
33
33
|
const update = (state: WorkbenchState, id: string, change: (tab: WorkbenchTab) => WorkbenchTab): WorkbenchState => {
|
|
34
34
|
let changed = false;
|
|
35
|
+
|
|
35
36
|
const tabs = state.tabs.map((tab) => {
|
|
36
37
|
if (tab.id !== id) {
|
|
37
38
|
return tab;
|
|
38
39
|
}
|
|
40
|
+
|
|
39
41
|
const next = change(tab);
|
|
40
42
|
changed ||= next !== tab;
|
|
43
|
+
|
|
41
44
|
return next;
|
|
42
45
|
});
|
|
46
|
+
|
|
43
47
|
return changed ? { ...state, tabs } : state;
|
|
44
48
|
};
|
|
45
49
|
|
|
@@ -51,27 +55,34 @@ export const openTable = (state: WorkbenchState, ref: TableRef, view: TableViewM
|
|
|
51
55
|
if (where !== undefined && where.trim() !== '') {
|
|
52
56
|
return add({ ...state, serial: state.serial + 1 }, { id: `filtered:${state.serial + 1}`, kind: 'table', ref, view, where });
|
|
53
57
|
}
|
|
58
|
+
|
|
54
59
|
const id = tableTabId(ref);
|
|
55
60
|
const existing = state.tabs.find((tab) => tab.id === id);
|
|
61
|
+
|
|
56
62
|
if (existing === undefined) {
|
|
57
63
|
return add(state, { id, kind: 'table', ref, view });
|
|
58
64
|
}
|
|
65
|
+
|
|
59
66
|
return focusTab(setTableView(state, id, view), id);
|
|
60
67
|
};
|
|
61
68
|
|
|
62
69
|
export const openConsole = (state: WorkbenchState, connectionId: string, schema?: string, sql = ''): WorkbenchState => {
|
|
63
70
|
const serial = state.serial + 1;
|
|
64
71
|
const consoles = state.consoles + 1;
|
|
72
|
+
|
|
65
73
|
return add({ ...state, serial, consoles }, { id: `console:${serial}`, kind: 'console', connectionId, schema, sql, number: consoles });
|
|
66
74
|
};
|
|
67
75
|
|
|
68
76
|
/* A table is designed in one tab; a new table gets a tab each time. */
|
|
69
77
|
export const openDesigner = (state: WorkbenchState, connectionId: string, schema: string, table?: string): WorkbenchState => {
|
|
70
78
|
const existing = table === undefined ? undefined : state.tabs.find((tab) => isDesignerOf(tab, connectionId, schema, table));
|
|
79
|
+
|
|
71
80
|
if (existing !== undefined) {
|
|
72
81
|
return focusTab(state, existing.id);
|
|
73
82
|
}
|
|
83
|
+
|
|
74
84
|
const serial = state.serial + 1;
|
|
85
|
+
|
|
75
86
|
return add({ ...state, serial }, { id: `designer:${serial}`, kind: 'designer', connectionId, schema, table });
|
|
76
87
|
};
|
|
77
88
|
|
|
@@ -84,29 +95,37 @@ export const setConsoleSql = (state: WorkbenchState, id: string, sql: string): W
|
|
|
84
95
|
/* The designer of a new table became the designer of that table, in the same tab. Closes any other designer of it. */
|
|
85
96
|
export const setDesignerTable = (state: WorkbenchState, id: string, table: string): WorkbenchState => {
|
|
86
97
|
const subject = state.tabs.find((tab) => tab.id === id);
|
|
98
|
+
|
|
87
99
|
if (subject?.kind !== 'designer' || subject.table === table) {
|
|
88
100
|
return state;
|
|
89
101
|
}
|
|
102
|
+
|
|
90
103
|
const tabs = state.tabs.filter((tab) => tab.id === id || !isDesignerOf(tab, subject.connectionId, subject.schema, table));
|
|
104
|
+
|
|
91
105
|
return update({ ...state, tabs }, id, (tab) => (tab.kind === 'designer' ? { ...tab, table } : tab));
|
|
92
106
|
};
|
|
93
107
|
|
|
94
108
|
/* The tab next to the closed one comes forward when the closed one was in front. */
|
|
95
109
|
export const closeTab = (state: WorkbenchState, id: string): WorkbenchState => {
|
|
96
110
|
const at = state.tabs.findIndex((tab) => tab.id === id);
|
|
111
|
+
|
|
97
112
|
if (at < 0) {
|
|
98
113
|
return state;
|
|
99
114
|
}
|
|
115
|
+
|
|
100
116
|
const tabs = state.tabs.filter((tab) => tab.id !== id);
|
|
117
|
+
|
|
101
118
|
return { ...state, tabs, activeId: state.activeId === id ? (tabs[Math.min(at, tabs.length - 1)]?.id ?? null) : state.activeId };
|
|
102
119
|
};
|
|
103
120
|
|
|
104
121
|
/* Drops the tabs of connections that are gone. */
|
|
105
122
|
export const pruneTabs = (state: WorkbenchState, connectionIds: ReadonlySet<string>): WorkbenchState => {
|
|
106
123
|
const tabs = state.tabs.filter((tab) => connectionIds.has(connectionIdOf(tab)));
|
|
124
|
+
|
|
107
125
|
if (tabs.length === state.tabs.length) {
|
|
108
126
|
return state;
|
|
109
127
|
}
|
|
128
|
+
|
|
110
129
|
return { ...state, tabs, activeId: tabs.some((tab) => tab.id === state.activeId) ? state.activeId : (tabs.at(-1)?.id ?? null) };
|
|
111
130
|
};
|
|
112
131
|
|
|
@@ -118,6 +137,7 @@ const isOptionalText = (value: unknown): value is string | undefined => value ==
|
|
|
118
137
|
|
|
119
138
|
const refOf = (value: unknown): TableRef | null => {
|
|
120
139
|
const ref = value as Partial<Record<keyof TableRef, unknown>> | null;
|
|
140
|
+
|
|
121
141
|
return typeof ref === 'object' && ref !== null && isText(ref.connectionId) && isText(ref.schema) && isText(ref.table)
|
|
122
142
|
? { connectionId: ref.connectionId, schema: ref.schema, table: ref.table }
|
|
123
143
|
: null;
|
|
@@ -125,14 +145,18 @@ const refOf = (value: unknown): TableRef | null => {
|
|
|
125
145
|
|
|
126
146
|
const tabOf = (value: unknown): WorkbenchTab | null => {
|
|
127
147
|
const raw = value as Record<string, unknown> | null;
|
|
148
|
+
|
|
128
149
|
if (typeof raw !== 'object' || raw === null || !isText(raw.id)) {
|
|
129
150
|
return null;
|
|
130
151
|
}
|
|
152
|
+
|
|
131
153
|
const id = raw.id;
|
|
154
|
+
|
|
132
155
|
switch (raw.kind) {
|
|
133
156
|
case 'table': {
|
|
134
157
|
const ref = refOf(raw.ref);
|
|
135
158
|
const view = raw.view === 'structure' ? 'structure' : 'data';
|
|
159
|
+
|
|
136
160
|
return ref === null || !isOptionalText(raw.where) ? null : { id, kind: 'table', ref, view, where: raw.where };
|
|
137
161
|
}
|
|
138
162
|
case 'console':
|
|
@@ -153,25 +177,35 @@ export const parseState = (raw: string | null): WorkbenchState => {
|
|
|
153
177
|
if (raw === null) {
|
|
154
178
|
return emptyState;
|
|
155
179
|
}
|
|
180
|
+
|
|
156
181
|
let data: Record<string, unknown>;
|
|
182
|
+
|
|
157
183
|
try {
|
|
158
184
|
data = JSON.parse(raw) as Record<string, unknown>;
|
|
159
185
|
} catch {
|
|
160
186
|
return emptyState;
|
|
161
187
|
}
|
|
188
|
+
|
|
162
189
|
if (typeof data !== 'object' || data === null || data.version !== 1 || !Array.isArray(data.tabs)) {
|
|
163
190
|
return emptyState;
|
|
164
191
|
}
|
|
192
|
+
|
|
165
193
|
const seen = new Set<string>();
|
|
194
|
+
|
|
166
195
|
const tabs = data.tabs.map(tabOf).filter((tab): tab is WorkbenchTab => {
|
|
167
196
|
if (tab === null || seen.has(tab.id)) {
|
|
168
197
|
return false;
|
|
169
198
|
}
|
|
199
|
+
|
|
170
200
|
seen.add(tab.id);
|
|
201
|
+
|
|
171
202
|
return true;
|
|
172
203
|
});
|
|
204
|
+
|
|
173
205
|
const activeId = typeof data.activeId === 'string' && seen.has(data.activeId) ? data.activeId : (tabs.at(-1)?.id ?? null);
|
|
206
|
+
|
|
174
207
|
const counter = (value: unknown): number => (typeof value === 'number' && Number.isFinite(value) && value >= 0 ? Math.floor(value) : 0);
|
|
208
|
+
|
|
175
209
|
return {
|
|
176
210
|
tabs,
|
|
177
211
|
activeId,
|