@adecore/database 0.17.0-beta.2 → 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.
- package/README.md +1 -1
- package/dist/DatabaseProvider.d.ts +4 -3
- package/dist/DatabaseProvider.js +15 -3
- package/dist/KeyIcon.d.ts +4 -0
- package/dist/KeyIcon.js +14 -0
- package/dist/actions.d.ts +12 -0
- package/dist/client-context.d.ts +3 -1
- package/dist/client-context.js +2 -0
- package/dist/code-text.d.ts +1 -0
- package/dist/code-text.js +2 -0
- package/dist/column-keys.d.ts +5 -0
- package/dist/column-keys.js +2 -0
- package/dist/connections/ConnectionManager.d.ts +3 -2
- package/dist/connections/ConnectionManager.js +27 -16
- package/dist/connections/connection-config.js +2 -0
- package/dist/console/QueryConsole.d.ts +8 -3
- package/dist/console/QueryConsole.js +110 -50
- package/dist/console/SqlEditor.d.ts +2 -12
- package/dist/console/SqlEditor.js +29 -18
- package/dist/console/StatementResultView.d.ts +5 -1
- package/dist/console/StatementResultView.js +18 -5
- package/dist/console/TransactionControls.js +3 -6
- package/dist/console/editor-slot.d.ts +18 -0
- package/dist/console/editor-slot.js +1 -0
- package/dist/console/result-keys.d.ts +15 -0
- package/dist/console/result-keys.js +79 -0
- package/dist/designer/ColumnsEditor.js +2 -2
- package/dist/designer/DesignerView.d.ts +3 -2
- package/dist/designer/DesignerView.js +5 -4
- package/dist/designer/SqlPreview.js +2 -1
- package/dist/designer/TableDesigner.d.ts +3 -2
- package/dist/designer/TableDesigner.js +2 -2
- package/dist/explorer/DatabaseExplorer.d.ts +4 -1
- package/dist/explorer/DatabaseExplorer.js +79 -23
- package/dist/explorer/RowMenu.js +2 -2
- package/dist/explorer/expansion.js +2 -2
- package/dist/explorer/folders.d.ts +16 -0
- package/dist/explorer/folders.js +1 -0
- package/dist/explorer/tree.d.ts +55 -4
- package/dist/explorer/tree.js +137 -23
- package/dist/grid/CellEditor.js +3 -1
- package/dist/grid/DataGrid.d.ts +2 -2
- package/dist/grid/DataGrid.js +61 -59
- package/dist/grid/EnumPicker.js +2 -1
- package/dist/grid/GridHeaderCell.d.ts +1 -2
- package/dist/grid/GridHeaderCell.js +12 -5
- package/dist/grid/GridRowView.d.ts +49 -0
- package/dist/grid/GridRowView.js +51 -0
- package/dist/grid/RecordView.d.ts +4 -2
- package/dist/grid/RecordView.js +71 -22
- package/dist/grid/ValueDock.d.ts +3 -1
- package/dist/grid/ValueDock.js +9 -3
- package/dist/grid/cell-id.d.ts +1 -0
- package/dist/grid/cell-id.js +2 -0
- package/dist/grid/layout.d.ts +3 -2
- package/dist/grid/layout.js +15 -6
- package/dist/grid/types.d.ts +8 -1
- package/dist/grid/types.js +4 -3
- package/dist/index.d.ts +5 -1
- package/dist/index.js +2 -0
- package/dist/locales/en.json +32 -15
- package/dist/locales/nl.json +32 -15
- package/dist/protocol/index.d.ts +1 -1
- package/dist/protocol/messages.d.ts +1 -1
- package/dist/protocol/messages.js +1 -1
- package/dist/protocol/schema.d.ts +17 -0
- package/dist/structure/StructureView.d.ts +5 -3
- package/dist/structure/StructureView.js +8 -6
- package/dist/table/CommandField.js +2 -2
- package/dist/table/TableStatusBar.js +2 -2
- package/dist/table/TableToolbar.d.ts +3 -4
- package/dist/table/TableToolbar.js +4 -3
- package/dist/table/TableView.d.ts +4 -2
- package/dist/table/TableView.js +63 -91
- package/dist/table/grid-rows.js +4 -1
- package/dist/table/useTableTransfer.d.ts +3 -1
- package/dist/table/useTableTransfer.js +19 -5
- package/dist/testing/fake.d.ts +3 -1
- package/dist/testing/fake.js +7 -1
- package/dist/value/ValueEditor.d.ts +9 -0
- package/dist/value/ValueEditor.js +102 -0
- package/dist/value/ValuePanel.d.ts +1 -1
- package/dist/value/ValuePanel.js +3 -94
- package/dist/workbench/DatabaseWorkbench.js +4 -7
- package/package.json +6 -6
- package/src/DatabaseProvider.tsx +24 -5
- package/src/KeyIcon.tsx +15 -0
- package/src/actions.ts +28 -2
- package/src/client-context.ts +5 -1
- package/src/code-text.ts +2 -0
- package/src/column-keys.ts +8 -0
- package/src/connections/ConnectionManager.tsx +98 -68
- package/src/connections/connection-config.ts +2 -0
- package/src/console/QueryConsole.tsx +251 -150
- package/src/console/SqlEditor.tsx +33 -37
- package/src/console/StatementResultView.tsx +61 -27
- package/src/console/TransactionControls.tsx +8 -11
- package/src/console/editor-slot.ts +27 -0
- package/src/console/result-keys.ts +109 -0
- package/src/designer/ColumnsEditor.tsx +3 -3
- package/src/designer/DesignerView.tsx +14 -3
- package/src/designer/SqlPreview.tsx +2 -1
- package/src/designer/TableDesigner.tsx +5 -2
- package/src/explorer/DatabaseExplorer.tsx +170 -26
- package/src/explorer/RowMenu.tsx +2 -2
- package/src/explorer/expansion.ts +4 -4
- package/src/explorer/folders.ts +25 -0
- package/src/explorer/tree.ts +219 -28
- package/src/grid/CellEditor.tsx +3 -2
- package/src/grid/DataGrid.tsx +114 -176
- package/src/grid/EnumPicker.tsx +2 -1
- package/src/grid/GridHeaderCell.tsx +14 -9
- package/src/grid/GridRowView.tsx +184 -0
- package/src/grid/RecordView.tsx +188 -75
- package/src/grid/ValueDock.tsx +12 -3
- package/src/grid/cell-id.ts +2 -0
- package/src/grid/layout.ts +16 -6
- package/src/grid/types.ts +15 -3
- package/src/index.ts +5 -1
- package/src/locales/en.json +32 -15
- package/src/locales/nl.json +32 -15
- package/src/protocol/index.ts +13 -1
- package/src/protocol/messages.ts +1 -1
- package/src/protocol/schema.ts +28 -0
- package/src/structure/StructureView.tsx +16 -6
- package/src/table/CommandField.tsx +2 -2
- package/src/table/TableStatusBar.tsx +3 -3
- package/src/table/TableToolbar.tsx +107 -92
- package/src/table/TableView.tsx +95 -132
- package/src/table/grid-rows.ts +5 -3
- package/src/table/useTableTransfer.ts +21 -6
- package/src/testing/fake.ts +18 -1
- package/src/value/ValueEditor.tsx +213 -0
- package/src/value/ValuePanel.tsx +4 -206
- package/src/workbench/DatabaseWorkbench.tsx +9 -10
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import { Spinner } from '@adecore/ui';
|
|
5
|
+
import { formatNumber } from '@adecore/ui/format';
|
|
6
|
+
import { CODE_TEXT } from '../code-text.js';
|
|
7
|
+
import { CellEditor } from './CellEditor.js';
|
|
8
|
+
import { cellIdOf } from './cell-id.js';
|
|
9
|
+
import { cellView } from './display.js';
|
|
10
|
+
import { EnumPicker } from './EnumPicker.js';
|
|
11
|
+
import { ROW_HEIGHT } from './layout.js';
|
|
12
|
+
/*
|
|
13
|
+
* The gutter and the pinned columns stick as one block, so the edge of one can never drift from the next, at any
|
|
14
|
+
* zoom. Each part paints its background under its own line as well: the line is an alpha, and what scrolls beneath
|
|
15
|
+
* the block would otherwise show through it.
|
|
16
|
+
*/
|
|
17
|
+
export const PINNED_GROUP = 'sticky left-0 z-10 flex shrink-0';
|
|
18
|
+
const TONE = {
|
|
19
|
+
value: '',
|
|
20
|
+
null: 'text-text-faint',
|
|
21
|
+
default: 'text-text-faint italic'
|
|
22
|
+
};
|
|
23
|
+
const ROW_STATE = {
|
|
24
|
+
inserted: 'bg-positive/10',
|
|
25
|
+
deleted: 'bg-status-error/10 text-text-faint'
|
|
26
|
+
};
|
|
27
|
+
/* One row of the grid. Memoized on props that only change for this row, so a scroll draws the rows that came into view and none other. */
|
|
28
|
+
export const GridRowView = memo(function GridRowView({ row, index, selected, focusedColumn, rangeStart, rangeEnd, editing, look, actions }) {
|
|
29
|
+
const { columns, order, widths, pinnedShown, gutter, notation, enumTypes, picked } = look;
|
|
30
|
+
const tint = row.state === undefined ? (selected ? 'bg-accent-soft/40' : '') : ROW_STATE[row.state];
|
|
31
|
+
const cells = order.map((columnIndex, position) => {
|
|
32
|
+
const column = columns[columnIndex];
|
|
33
|
+
const cell = row.cells[columnIndex] ?? null;
|
|
34
|
+
const view = cellView(cell, column.kind, notation);
|
|
35
|
+
const cellPosition = { row: index, column: columnIndex };
|
|
36
|
+
const isFocused = focusedColumn === columnIndex;
|
|
37
|
+
const ranged = picked.has(columnIndex) || (position >= rangeStart && position <= rangeEnd && rangeStart >= 0);
|
|
38
|
+
const edited = row.edited?.has(columnIndex) === true;
|
|
39
|
+
const isEditing = editing !== null && editing.column === columnIndex;
|
|
40
|
+
const pinned = position < pinnedShown;
|
|
41
|
+
const enumType = enumTypes[columnIndex] ?? null;
|
|
42
|
+
return (_jsxs("div", { id: cellIdOf(look.gridId, index, columnIndex), role: "gridcell", "aria-colindex": position + 2, "aria-selected": ranged, "data-focused": isFocused ? '' : undefined, "data-ranged": ranged ? '' : undefined, className: clsx(CODE_TEXT, 'flex h-full shrink-0 items-center overflow-hidden px-3 whitespace-nowrap outline-0 select-none data-[focused]:outline-1 data-[focused]:-outline-offset-1 data-[focused]:outline-border-strong group-focus-within/grid:data-[focused]:outline-accent', position === order.length - 1 ? 'border-r-0' : 'border-r', view.align === 'end' ? 'justify-end tabular-nums' : 'justify-start', pinned
|
|
43
|
+
? [
|
|
44
|
+
'relative bg-clip-border',
|
|
45
|
+
position === pinnedShown - 1 ? 'border-border-strong' : 'border-border-soft',
|
|
46
|
+
ranged ? 'bg-accent-soft' : ['bg-surface', tint === '' && 'group-hover/row:bg-surface-hover']
|
|
47
|
+
]
|
|
48
|
+
: ['relative border-border-soft', ranged ? 'bg-accent-soft' : edited && 'bg-accent/10']), style: { width: widths[position] }, onPointerDown: (event) => actions.pressCell(event, cellPosition, isEditing), onPointerEnter: () => actions.dragTo(cellPosition), onDoubleClick: () => actions.beginEdit(cellPosition), onContextMenu: () => actions.openCellMenu(cellPosition), children: [pinned && !ranged && _jsx("span", { "aria-hidden": true, className: clsx('pointer-events-none absolute inset-0', tint, edited && 'bg-accent/10') }), isEditing && editing.loading && _jsx(Spinner, { size: 12, label: look.loadingLabel }), isEditing && !editing.loading && enumType !== null && (_jsx(EnumPicker, { autoOpen: true, type: enumType, value: typeof cell === 'string' ? cell : null, nullable: column.nullable !== false, label: column.name, onDone: actions.finishPick })), isEditing && !editing.loading && enumType === null && (_jsx(CellEditor, { value: editing.draft, label: column.name, onValueChange: actions.changeDraft, onCommit: (move) => actions.finishEdit(true, move), onCancel: () => actions.finishEdit(false) })), !isEditing && (_jsxs(_Fragment, { children: [_jsx("span", { className: clsx('min-w-0 truncate', TONE[view.tone], row.state === 'deleted' && 'line-through'), children: view.text }), view.suffix !== undefined && _jsx("span", { className: "ml-2 shrink-0 text-text-faint", children: view.suffix })] }))] }, columnIndex));
|
|
49
|
+
});
|
|
50
|
+
return (_jsxs("div", { role: "row", "aria-rowindex": index + 2, "aria-selected": selected, className: clsx('group/row absolute inset-x-0 flex border-b border-border-soft', tint, tint === '' && 'hover:bg-surface-hover'), style: { top: index * ROW_HEIGHT, height: ROW_HEIGHT }, children: [_jsxs("div", { className: PINNED_GROUP, children: [_jsx("div", { role: "rowheader", "data-selected": selected ? '' : undefined, className: clsx('flex shrink-0 cursor-default items-center justify-end border-r border-border bg-surface bg-clip-border pr-2 pl-1 font-mono text-xs text-text-faint tabular-nums select-none data-[selected]:bg-accent-soft data-[selected]:text-text', row.state === 'inserted' && 'text-positive'), style: { width: gutter }, onClick: (event) => actions.pressRowNumber(index, event), onContextMenu: () => actions.openRowMenu(index), children: row.number === null ? '+' : formatNumber(row.number) }), cells.slice(0, pinnedShown)] }), cells.slice(pinnedShown)] }));
|
|
51
|
+
});
|
|
@@ -6,15 +6,17 @@ export interface RecordViewProps {
|
|
|
6
6
|
rows: readonly GridRow[];
|
|
7
7
|
index: number;
|
|
8
8
|
onIndexChange(index: number): void;
|
|
9
|
+
focusedColumn: number | null;
|
|
9
10
|
editable?: boolean;
|
|
10
11
|
onCommit?(rowKey: string, column: number, value: EditValue): void;
|
|
11
12
|
loadValue?(rowKey: string, column: number): Promise<Value | undefined>;
|
|
12
|
-
onFocusedCellChange?(cell: FocusedCell
|
|
13
|
+
onFocusedCellChange?(cell: FocusedCell): void;
|
|
13
14
|
onFollow?(cell: FocusedCell): void;
|
|
14
15
|
canFollow?(cell: FocusedCell): boolean;
|
|
16
|
+
onClose(): void;
|
|
15
17
|
label: string;
|
|
16
18
|
empty?: string;
|
|
17
19
|
className?: string;
|
|
18
20
|
ref?: Ref<HTMLDivElement>;
|
|
19
21
|
}
|
|
20
|
-
export declare function RecordView({ columns, rows, index, onIndexChange, editable, onCommit, loadValue, onFocusedCellChange, onFollow, canFollow, label, empty, className, ref }: RecordViewProps): import("react").JSX.Element;
|
|
22
|
+
export declare function RecordView({ columns, rows, index, onIndexChange, focusedColumn, editable, onCommit, loadValue, onFocusedCellChange, onFollow, canFollow, onClose, label, empty, className, ref }: RecordViewProps): import("react").JSX.Element;
|
package/dist/grid/RecordView.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
|
-
import { ArrowUpRight, Ban, ChevronDown, ChevronUp, Copy, RotateCcw } from 'lucide-react';
|
|
4
|
+
import { ArrowUpRight, Ban, ChevronDown, ChevronUp, Copy, Maximize2, Minimize2, RotateCcw, X } from 'lucide-react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
|
-
import { ContextMenu, copyText, EDIT_SHORTCUTS, Icon, IconButton, Input, isApplePlatform, isModHeld, Kbd, shortcut, Spinner } from '@adecore/ui';
|
|
6
|
+
import { ButtonGroup, ContextMenu, copyText, EDIT_SHORTCUTS, Icon, IconButton, Input, isApplePlatform, isModHeld, Kbd, shortcut, Spinner } from '@adecore/ui';
|
|
7
7
|
import { formatNumber } from '@adecore/ui/format';
|
|
8
8
|
import { useNumberNotation } from '../client-context.js';
|
|
9
|
+
import { CODE_TEXT } from '../code-text.js';
|
|
10
|
+
import { ValueEditor } from '../value/ValueEditor.js';
|
|
9
11
|
import { cellView, copyTextOf, isNumericKind, isPreview, numeralText } from './display.js';
|
|
10
12
|
import { draftOf, parseDraft } from './edit-value.js';
|
|
11
13
|
import { parseEnumType } from './enum-type.js';
|
|
12
14
|
import { EnumPicker } from './EnumPicker.js';
|
|
15
|
+
import { wholeValueOf } from './focused-value.js';
|
|
13
16
|
const PREVIOUS_ROW = shortcut('Mod+ArrowUp');
|
|
14
17
|
const NEXT_ROW = shortcut('Mod+ArrowDown');
|
|
15
18
|
const textOf = (cell, kind, notation, readOnly) => {
|
|
@@ -25,19 +28,28 @@ const textOf = (cell, kind, notation, readOnly) => {
|
|
|
25
28
|
}
|
|
26
29
|
return draftOf(cell);
|
|
27
30
|
};
|
|
31
|
+
/* A value that a field is too small for: a long text, JSON or binary, which opens up in place to the whole of it. */
|
|
32
|
+
const opensUp = (cell, kind) => kind === 'json' || kind === 'binary' || isPreview(cell) || (typeof cell === 'string' && cell.includes('\n'));
|
|
28
33
|
/*
|
|
29
|
-
*
|
|
30
|
-
* to the same `onCommit` the grid uses, so they pile up as pending changes there. Up and Down move
|
|
31
|
-
* between the fields; with Mod
|
|
34
|
+
* The row of the grid's focused cell as a list of fields, beside the grid. It follows the grid's focus, and
|
|
35
|
+
* edits go to the same `onCommit` the grid uses, so they pile up as pending changes there. Up and Down move
|
|
36
|
+
* between the fields, Tab too; with Mod, Up and Down move between the rows.
|
|
32
37
|
*/
|
|
33
|
-
export function RecordView({ columns, rows, index, onIndexChange, editable = false, onCommit, loadValue, onFocusedCellChange, onFollow, canFollow, label, empty, className, ref }) {
|
|
38
|
+
export function RecordView({ columns, rows, index, onIndexChange, focusedColumn, editable = false, onCommit, loadValue, onFocusedCellChange, onFollow, canFollow, onClose, label, empty, className, ref }) {
|
|
34
39
|
const { t } = useTranslation('database');
|
|
35
40
|
const notation = useNumberNotation();
|
|
36
41
|
const fields = useRef([]);
|
|
42
|
+
const blocks = useRef([]);
|
|
37
43
|
const [editing, setEditing] = useState(null);
|
|
38
|
-
const
|
|
44
|
+
const [opened, setOpened] = useState(() => new Set());
|
|
45
|
+
const row = index >= 0 ? rows[index] : undefined;
|
|
39
46
|
const enumTypes = useMemo(() => columns.map((column) => parseEnumType(column.type)), [columns]);
|
|
40
47
|
const apple = isApplePlatform();
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (focusedColumn !== null) {
|
|
50
|
+
blocks.current[focusedColumn]?.scrollIntoView({ block: 'nearest' });
|
|
51
|
+
}
|
|
52
|
+
}, [focusedColumn, row?.key]);
|
|
41
53
|
const canEdit = (column) => editable && row !== undefined && row.locked !== true && row.state !== 'deleted' && columns[column]?.readOnly !== true;
|
|
42
54
|
const commitEdit = () => {
|
|
43
55
|
if (editing !== null && !editing.loading && row !== undefined && editing.draft !== editing.initial) {
|
|
@@ -73,8 +85,18 @@ export function RecordView({ columns, rows, index, onIndexChange, editable = fal
|
|
|
73
85
|
? { ...now, draft }
|
|
74
86
|
: { column, draft, initial: textOf(row?.cells[column] ?? null, columns[column].kind, 'database', false), loading: false });
|
|
75
87
|
};
|
|
76
|
-
const
|
|
77
|
-
|
|
88
|
+
const toggleOpened = (column) => {
|
|
89
|
+
setOpened((now) => {
|
|
90
|
+
const next = new Set(now);
|
|
91
|
+
if (!next.delete(column)) {
|
|
92
|
+
next.add(column);
|
|
93
|
+
}
|
|
94
|
+
return next;
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
const moveRow = (to) => {
|
|
98
|
+
commitEdit();
|
|
99
|
+
onIndexChange(Math.max(0, Math.min(to, rows.length - 1)));
|
|
78
100
|
};
|
|
79
101
|
const handleKeyDown = (event, column) => {
|
|
80
102
|
if (event.nativeEvent.isComposing) {
|
|
@@ -85,11 +107,10 @@ export function RecordView({ columns, rows, index, onIndexChange, editable = fal
|
|
|
85
107
|
event.preventDefault();
|
|
86
108
|
const direction = event.key === 'ArrowUp' ? -1 : 1;
|
|
87
109
|
if (mod) {
|
|
88
|
-
|
|
89
|
-
onIndexChange(Math.max(0, Math.min(index + direction, rows.length - 1)));
|
|
110
|
+
moveRow(index + direction);
|
|
90
111
|
}
|
|
91
112
|
else {
|
|
92
|
-
|
|
113
|
+
fields.current[Math.max(0, Math.min(column + direction, columns.length - 1))]?.focus();
|
|
93
114
|
}
|
|
94
115
|
}
|
|
95
116
|
else if (event.key === 'Enter') {
|
|
@@ -103,7 +124,7 @@ export function RecordView({ columns, rows, index, onIndexChange, editable = fal
|
|
|
103
124
|
}
|
|
104
125
|
};
|
|
105
126
|
const cellOf = (column) => (row === undefined ? null : { rowKey: row.key, column });
|
|
106
|
-
return (_jsxs("div", { ref: ref, className: clsx('flex min-h-0 flex-1 flex-col bg-surface', className), children: [_jsxs("div", { className: "flex h-10 shrink-0 items-center gap-
|
|
127
|
+
return (_jsxs("div", { ref: ref, className: clsx('flex min-h-0 min-w-0 flex-1 flex-col bg-surface', className), children: [_jsxs("div", { className: "flex h-10 shrink-0 items-center gap-1 border-b border-border px-2", children: [_jsxs(ButtonGroup, { children: [_jsx(IconButton, { icon: ChevronUp, size: "sm", label: t('table.record.previous'), kbd: PREVIOUS_ROW, disabled: row === undefined || index <= 0, onClick: () => moveRow(index - 1) }), _jsx(IconButton, { icon: ChevronDown, size: "sm", label: t('table.record.next'), kbd: NEXT_ROW, disabled: row === undefined || index >= rows.length - 1, onClick: () => moveRow(index + 1) })] }), row !== undefined && (_jsxs("span", { className: "flex min-w-0 items-baseline gap-2 px-1", children: [_jsx("span", { className: "truncate text-sm font-medium text-text tabular-nums", children: row.number === null ? t('table.record.newRow') : t('table.record.row', { number: formatNumber(row.number) }) }), _jsx("span", { className: "truncate text-xs text-text-faint tabular-nums", children: t('table.record.position', { index: formatNumber(index + 1), count: formatNumber(rows.length) }) })] })), _jsx(IconButton, { icon: X, size: "sm", label: t('table.record.close'), className: "ml-auto", onClick: onClose })] }), row === undefined ? (_jsx("p", { className: "px-4 py-6 text-xs text-text-muted", children: empty })) : (_jsx("div", { role: "group", "aria-label": label, className: "min-h-0 flex-1 overflow-auto py-1", children: columns.map((column, position) => {
|
|
107
128
|
const cell = row.cells[position] ?? null;
|
|
108
129
|
const enumType = enumTypes[position] ?? null;
|
|
109
130
|
const active = editing !== null && editing.column === position;
|
|
@@ -113,14 +134,42 @@ export function RecordView({ columns, rows, index, onIndexChange, editable = fal
|
|
|
113
134
|
const followable = cellKey !== null && onFollow !== undefined && (canFollow?.(cellKey) ?? true);
|
|
114
135
|
const nullable = column.nullable !== false;
|
|
115
136
|
const placeholder = cell === null ? 'NULL' : typeof cell === 'object' && cell.kind === 'default' ? 'DEFAULT' : undefined;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
137
|
+
const expandable = enumType === null && opensUp(cell, column.kind);
|
|
138
|
+
const expanded = expandable && opened.has(position);
|
|
139
|
+
return (_jsxs(ContextMenu.Root, { children: [_jsxs(ContextMenu.Trigger, { ref: (node) => {
|
|
140
|
+
blocks.current[position] = node;
|
|
141
|
+
}, "data-focused": focusedColumn === position ? '' : undefined, className: clsx('flex flex-col gap-1 border-l-2 border-transparent px-3 py-1.5 data-[focused]:border-accent data-[focused]:bg-accent-soft/40', row.edited?.has(position) && 'bg-accent/10'), onFocusCapture: () => cellKey !== null && onFocusedCellChange?.(cellKey), children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: clsx(CODE_TEXT, 'min-w-0 truncate text-text'), children: column.name }), column.type !== '' && _jsx("span", { className: "min-w-0 truncate text-xs text-text-faint", children: column.type }), _jsxs(ButtonGroup, { className: "ml-auto shrink-0", children: [followable && (_jsx(IconButton, { icon: ArrowUpRight, size: "xs", label: t('grid.followReference'), onClick: () => cellKey !== null && onFollow?.(cellKey) })), expandable && (_jsx(IconButton, { icon: expanded ? Minimize2 : Maximize2, size: "xs", label: t(expanded ? 'table.record.collapse' : 'table.record.expand'), "aria-expanded": expanded, onClick: () => toggleOpened(position) }))] })] }), expanded ? (_jsx(WholeValue, { row: row, column: position, columns: columns, editable: editableField, loadValue: loadValue, onCommit: (value) => commitValue(position, value) }, `${row.key}:${position}`)) : enumType !== null ? (_jsx(EnumPicker, { look: "field", type: enumType, value: typeof cell === 'string' ? cell : null, nullable: nullable, label: column.name, disabled: !editableField, ref: (node) => {
|
|
142
|
+
fields.current[position] = node;
|
|
143
|
+
}, onDone: (result) => {
|
|
144
|
+
if (result !== undefined && result.value !== (typeof cell === 'string' ? cell : null)) {
|
|
145
|
+
commitValue(position, result.value);
|
|
146
|
+
}
|
|
147
|
+
} })) : (_jsxs("span", { className: "relative flex min-w-0 items-center", children: [_jsx(Input, { size: "sm", spellCheck: false, "aria-label": column.name, ref: (node) => {
|
|
123
148
|
fields.current[position] = node;
|
|
124
|
-
}, value: active ? editing.draft : textOf(cell, column.kind, notation, !editableField), placeholder: placeholder, readOnly: !editableField || (preview && (!active || editing.loading)), className: clsx('min-w-0 flex-1', row.state === 'deleted' && 'line-through'), onFocus: () => void beginEdit(position), onBlur: commitEdit, onChange: (event) => changeDraft(position, event.target.value), onKeyDown: (event) => handleKeyDown(event, position) })
|
|
149
|
+
}, value: active ? editing.draft : textOf(cell, column.kind, notation, !editableField), placeholder: placeholder, readOnly: !editableField || (preview && (!active || editing.loading)), className: clsx(CODE_TEXT, 'min-w-0 flex-1', row.state === 'deleted' && 'line-through'), onFocus: () => void beginEdit(position), onBlur: commitEdit, onChange: (event) => changeDraft(position, event.target.value), onKeyDown: (event) => handleKeyDown(event, position) }), active && editing.loading && _jsx(Spinner, { size: 12, label: t('grid.loadingValue'), className: "absolute right-2" })] }))] }), _jsxs(ContextMenu.Popup, { children: [_jsxs(ContextMenu.Item, { onClick: () => copyText(copyTextOf(cell)), children: [_jsx(Icon, { icon: Copy, size: 14 }), t('grid.copyValue'), _jsx(Kbd, { shortcut: EDIT_SHORTCUTS.copy })] }), editable && (_jsxs(_Fragment, { children: [_jsx(ContextMenu.Separator, {}), _jsxs(ContextMenu.Item, { disabled: !editableField || !nullable, onClick: () => commitValue(position, null), children: [_jsx(Icon, { icon: Ban, size: 14 }), t('grid.setNull')] }), _jsxs(ContextMenu.Item, { disabled: !editableField, onClick: () => commitValue(position, { kind: 'default' }), children: [_jsx(Icon, { icon: RotateCcw, size: 14 }), t('grid.setDefault')] })] }))] })] }, position));
|
|
125
150
|
}) }))] }));
|
|
126
151
|
}
|
|
152
|
+
/* A field opened up to its whole value, fetched with `loadValue` when the row holds only a preview. */
|
|
153
|
+
function WholeValue({ row, column, columns, editable, loadValue, onCommit }) {
|
|
154
|
+
const { t } = useTranslation('database');
|
|
155
|
+
const cell = row.cells[column] ?? null;
|
|
156
|
+
const known = wholeValueOf(cell);
|
|
157
|
+
const [fetched, setFetched] = useState(null);
|
|
158
|
+
const value = known ?? (fetched !== null && fetched.cell === cell ? fetched.value : undefined);
|
|
159
|
+
const waiting = known === undefined && (fetched === null || fetched.cell !== cell);
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (known !== undefined || loadValue === undefined) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
let live = true;
|
|
165
|
+
void loadValue(row.key, column).then((whole) => {
|
|
166
|
+
if (live) {
|
|
167
|
+
setFetched({ cell, value: whole });
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
return () => {
|
|
171
|
+
live = false;
|
|
172
|
+
};
|
|
173
|
+
}, [known, loadValue, row.key, column, cell]);
|
|
174
|
+
return (_jsx("div", { className: "flex h-72 min-h-0 flex-col overflow-hidden rounded-md border border-border bg-surface", children: value !== undefined ? (_jsx(ValueEditor, { column: columns[column], value: value, editable: editable, onCommit: onCommit })) : (_jsx("div", { className: "grid min-h-0 grow place-items-center text-xs text-text-muted", children: waiting && loadValue !== undefined ? _jsx(Spinner, { size: 14, label: t('value.loading') }) : t('value.notLoaded') })) }));
|
|
175
|
+
}
|
package/dist/grid/ValueDock.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ export interface ValueDockProps {
|
|
|
5
5
|
open: boolean;
|
|
6
6
|
panel: ReactNode;
|
|
7
7
|
children: ReactNode;
|
|
8
|
+
width?: number;
|
|
9
|
+
onWidthChange?(width: number): void;
|
|
8
10
|
className?: string;
|
|
9
11
|
ref?: Ref<HTMLDivElement>;
|
|
10
12
|
}
|
|
11
|
-
export declare function ValueDock({ open, panel, children, className, ref }: ValueDockProps): import("react").JSX.Element;
|
|
13
|
+
export declare function ValueDock({ open, panel, children, width: given, onWidthChange, className, ref }: ValueDockProps): import("react").JSX.Element;
|
package/dist/grid/ValueDock.js
CHANGED
|
@@ -5,16 +5,22 @@ import { ColumnResizeHandle, useColumnResize } from '@adecore/ui';
|
|
|
5
5
|
export const VALUE_PANEL_WIDTH = 320;
|
|
6
6
|
export const VALUE_PANEL_MIN_WIDTH = 240;
|
|
7
7
|
/* A grid with a panel along its right edge that a person can widen by its left edge. */
|
|
8
|
-
export function ValueDock({ open, panel, children, className, ref }) {
|
|
8
|
+
export function ValueDock({ open, panel, children, width: given, onWidthChange, className, ref }) {
|
|
9
9
|
const root = useRef(null);
|
|
10
10
|
const dock = useRef(null);
|
|
11
|
-
const [
|
|
11
|
+
const [own, setOwn] = useState(VALUE_PANEL_WIDTH);
|
|
12
|
+
const width = given ?? own;
|
|
12
13
|
const { startResize } = useColumnResize(dock, {
|
|
13
14
|
size: width,
|
|
14
15
|
min: VALUE_PANEL_MIN_WIDTH,
|
|
15
16
|
from: 'right',
|
|
16
17
|
max: () => (root.current?.clientWidth ?? 960) - VALUE_PANEL_MIN_WIDTH,
|
|
17
|
-
onSize:
|
|
18
|
+
onSize: (next) => {
|
|
19
|
+
if (given === undefined) {
|
|
20
|
+
setOwn(next);
|
|
21
|
+
}
|
|
22
|
+
onWidthChange?.(next);
|
|
23
|
+
}
|
|
18
24
|
});
|
|
19
25
|
return (_jsxs("div", { ref: (node) => {
|
|
20
26
|
root.current = node;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const cellIdOf: (gridId: string, row: number, column: number) => string;
|
package/dist/grid/layout.d.ts
CHANGED
|
@@ -4,14 +4,15 @@ export declare const MIN_COLUMN_WIDTH = 64;
|
|
|
4
4
|
export declare const MAX_COLUMN_WIDTH = 320;
|
|
5
5
|
export declare const MAX_RESIZED_WIDTH = 1200;
|
|
6
6
|
export declare const MAX_FIT_WIDTH = 640;
|
|
7
|
-
export declare const
|
|
7
|
+
export declare const ROW_BLOCK = 16;
|
|
8
8
|
export declare const DOUBLE_CLICK_MS = 350;
|
|
9
9
|
export declare const FALLBACK_VIEWPORT_HEIGHT = 600;
|
|
10
10
|
export interface VisibleRange {
|
|
11
11
|
readonly start: number;
|
|
12
12
|
readonly end: number;
|
|
13
13
|
}
|
|
14
|
-
export declare const
|
|
14
|
+
export declare const scrollBlock: (top: number) => number;
|
|
15
|
+
export declare const drawnRange: (block: number, height: number, count: number) => VisibleRange;
|
|
15
16
|
export declare const scrollToReveal: (start: number, size: number, scroll: number, viewport: number, leading: number) => number;
|
|
16
17
|
export declare const columnOffsets: (widths: readonly number[]) => number[];
|
|
17
18
|
export declare const gutterWidth: (rowCount: number) => number;
|
package/dist/grid/layout.js
CHANGED
|
@@ -7,7 +7,8 @@ export const MAX_COLUMN_WIDTH = 320;
|
|
|
7
7
|
export const MAX_RESIZED_WIDTH = 1200;
|
|
8
8
|
/* How wide fitting a column to its content may make it. */
|
|
9
9
|
export const MAX_FIT_WIDTH = 640;
|
|
10
|
-
|
|
10
|
+
/* The window of drawn rows moves in blocks of this many rows, so a scroll within a block draws nothing. */
|
|
11
|
+
export const ROW_BLOCK = 16;
|
|
11
12
|
/* Two presses on a resize handle this close are a double click. */
|
|
12
13
|
export const DOUBLE_CLICK_MS = 350;
|
|
13
14
|
/* What a viewport is taken to be until it is measured, so a first paint already holds rows. */
|
|
@@ -20,14 +21,22 @@ const GUTTER_MIN_WIDTH = 40;
|
|
|
20
21
|
const SAMPLE_ROWS = 50;
|
|
21
22
|
/* The arrow and the position number a sorted header carries after its name. */
|
|
22
23
|
const SORT_ROOM = 24;
|
|
23
|
-
/* The
|
|
24
|
-
export const
|
|
24
|
+
/* The block the top of the viewport is in, for a viewport `top` pixels into the rows. */
|
|
25
|
+
export const scrollBlock = (top) => Math.floor(Math.max(0, top) / (ROW_BLOCK * ROW_HEIGHT));
|
|
26
|
+
/*
|
|
27
|
+
* The rows to draw while the top of a viewport `height` pixels tall is in `block`: those in view and a
|
|
28
|
+
* viewport more on each side, rounded out to whole blocks. A fast scroll finds its next rows drawn, and
|
|
29
|
+
* the window moves, and the grid draws, once per block instead of once per row.
|
|
30
|
+
*/
|
|
31
|
+
export const drawnRange = (block, height, count) => {
|
|
25
32
|
if (count <= 0 || height <= 0) {
|
|
26
33
|
return { start: 0, end: 0 };
|
|
27
34
|
}
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
35
|
+
const page = Math.ceil(height / ROW_HEIGHT);
|
|
36
|
+
const top = Math.max(0, block) * ROW_BLOCK;
|
|
37
|
+
const start = Math.max(0, Math.floor((top - page) / ROW_BLOCK) * ROW_BLOCK);
|
|
38
|
+
const end = Math.min(count, Math.ceil((top + ROW_BLOCK + 2 * page) / ROW_BLOCK) * ROW_BLOCK);
|
|
39
|
+
return { start: Math.min(start, end), end };
|
|
31
40
|
};
|
|
32
41
|
/*
|
|
33
42
|
* Where to scroll so that an item at `start` that is `size` long is in view. `leading` is the part of
|
package/dist/grid/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ColumnInfo, ResultColumn } from '../protocol/index.ts';
|
|
|
2
2
|
import type { Shown } from './display.ts';
|
|
3
3
|
export interface GridColumn extends ResultColumn {
|
|
4
4
|
readonly primaryKey?: boolean;
|
|
5
|
+
readonly foreignKey?: boolean;
|
|
5
6
|
readonly nullable?: boolean;
|
|
6
7
|
readonly readOnly?: boolean;
|
|
7
8
|
readonly defaultValue?: string | null;
|
|
@@ -16,11 +17,17 @@ export interface GridRow {
|
|
|
16
17
|
readonly edited?: ReadonlySet<number>;
|
|
17
18
|
readonly locked?: boolean;
|
|
18
19
|
}
|
|
19
|
-
export declare const gridColumnOf: (column: ResultColumn, info: ColumnInfo | undefined,
|
|
20
|
+
export declare const gridColumnOf: (column: ResultColumn, info: ColumnInfo | undefined, keys: {
|
|
21
|
+
readonly primaryKey: readonly string[];
|
|
22
|
+
readonly foreignKey: readonly string[];
|
|
23
|
+
}) => GridColumn;
|
|
20
24
|
export interface FocusedCell {
|
|
21
25
|
readonly rowKey: string;
|
|
22
26
|
readonly column: number;
|
|
23
27
|
}
|
|
28
|
+
export interface FocusRequest extends FocusedCell {
|
|
29
|
+
readonly quiet?: boolean;
|
|
30
|
+
}
|
|
24
31
|
export interface GridMenuContext {
|
|
25
32
|
readonly rowKeys: readonly string[];
|
|
26
33
|
readonly cell: FocusedCell | null;
|
package/dist/grid/types.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
/* Builds the column of a table from the result column
|
|
2
|
-
export const gridColumnOf = (column, info,
|
|
1
|
+
/* Builds the column of a table from the result column, the structure's column of the same name and the columns of its keys. */
|
|
2
|
+
export const gridColumnOf = (column, info, keys) => ({
|
|
3
3
|
...column,
|
|
4
|
-
primaryKey: primaryKey.includes(column.name),
|
|
4
|
+
primaryKey: keys.primaryKey.includes(column.name),
|
|
5
|
+
foreignKey: keys.foreignKey.includes(column.name),
|
|
5
6
|
nullable: info?.nullable,
|
|
6
7
|
readOnly: info?.generated === true,
|
|
7
8
|
defaultValue: info?.defaultValue,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
export { createDatabaseClient, DatabaseRequestError, type Connection, type DatabaseClient, type DatabaseClientOptions, type DatabaseSession, type DatabaseTransport, type ExecuteOptions, type ExecuteResult, type ExportRequest, type ExportResult, type ImportRequest, type PageQuery, type RequestOptions, type RowsQuery, type SchemaChange, type TableRef } from './client/index.ts';
|
|
2
|
-
export type { DatabaseAction, DatabaseFiles, DatabaseStorage, ExplorerSelection } from './actions.ts';
|
|
2
|
+
export type { DatabaseAction, DatabaseFiles, DatabaseNotice, DatabaseStorage, ExplorerSelection } from './actions.ts';
|
|
3
3
|
export { ConnectionForm, type ConnectionFormProps } from './connections/ConnectionForm.tsx';
|
|
4
|
+
export { configProblems, isValidConfig, type ConfigProblem, type ConfigProblems } from './connections/connection-config.ts';
|
|
4
5
|
export { ConnectionManager, type ConnectionManagerProps } from './connections/ConnectionManager.tsx';
|
|
6
|
+
export { connectionFromContainer, containerTitle, isMysqlContainer, withContainer } from './connections/docker.ts';
|
|
5
7
|
export { DatabaseExplorer, type DatabaseExplorerProps } from './explorer/DatabaseExplorer.tsx';
|
|
8
|
+
export type { ExplorerFolder, ExplorerItem } from './explorer/folders.ts';
|
|
6
9
|
export { TableDesigner, type TableDesignerProps } from './designer/TableDesigner.tsx';
|
|
7
10
|
export { DatabaseProvider, type DatabaseProviderProps } from './DatabaseProvider.tsx';
|
|
8
11
|
export { useDatabaseClient } from './client-context.ts';
|
|
9
12
|
export type { NumberNotation } from './grid/display.ts';
|
|
10
13
|
export { QueryConsole, type QueryConsoleProps } from './console/QueryConsole.tsx';
|
|
14
|
+
export type { QueryConsoleEditorHandle, QueryConsoleEditorProps, QueryConsoleRunScope } from './console/editor-slot.ts';
|
|
11
15
|
export { StructureView, type StructureViewProps } from './structure/StructureView.tsx';
|
|
12
16
|
export { TableView, type TableViewProps } from './table/TableView.tsx';
|
|
13
17
|
export { DatabaseWorkbench, type DatabaseWorkbenchProps } from './workbench/DatabaseWorkbench.tsx';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/* The views of the package and the client they read through. Every name here is public API; `exports.test.ts` turns a change to this list into a diff someone has to accept. */
|
|
2
2
|
export { createDatabaseClient, DatabaseRequestError } from './client/index.js';
|
|
3
3
|
export { ConnectionForm } from './connections/ConnectionForm.js';
|
|
4
|
+
export { configProblems, isValidConfig } from './connections/connection-config.js';
|
|
4
5
|
export { ConnectionManager } from './connections/ConnectionManager.js';
|
|
6
|
+
export { connectionFromContainer, containerTitle, isMysqlContainer, withContainer } from './connections/docker.js';
|
|
5
7
|
export { DatabaseExplorer } from './explorer/DatabaseExplorer.js';
|
|
6
8
|
export { TableDesigner } from './designer/TableDesigner.js';
|
|
7
9
|
export { DatabaseProvider } from './DatabaseProvider.js';
|
package/dist/locales/en.json
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"test": {
|
|
18
18
|
"action": "Test connection",
|
|
19
19
|
"success": "Connected to {{server}}.",
|
|
20
|
-
"failed": "The connection failed."
|
|
20
|
+
"failed": "The connection failed.",
|
|
21
|
+
"dismiss": "Dismiss"
|
|
21
22
|
},
|
|
22
23
|
"delete": {
|
|
23
24
|
"action": "Delete",
|
|
@@ -116,6 +117,18 @@
|
|
|
116
117
|
"views": "Views",
|
|
117
118
|
"primaryKey": "Primary key",
|
|
118
119
|
"foreignKey": "Foreign key",
|
|
120
|
+
"primaryAndForeignKey": "Primary key and foreign key",
|
|
121
|
+
"parts": {
|
|
122
|
+
"columns": "Columns",
|
|
123
|
+
"keys": "Keys",
|
|
124
|
+
"foreignKeys": "Foreign keys",
|
|
125
|
+
"indexes": "Indexes",
|
|
126
|
+
"checks": "Checks",
|
|
127
|
+
"triggers": "Triggers"
|
|
128
|
+
},
|
|
129
|
+
"references": "{{columns}} → {{table}} ({{referenced}})",
|
|
130
|
+
"unique": "unique",
|
|
131
|
+
"check": "Check",
|
|
119
132
|
"readOnly": "Read only",
|
|
120
133
|
"schemaCount": "{{shown}} of {{total}}",
|
|
121
134
|
"schemaCountHint_one": "{{shown}} of {{total}} schema shown, system schemas are hidden",
|
|
@@ -272,7 +285,6 @@
|
|
|
272
285
|
},
|
|
273
286
|
"cloneRows": "Clone selected rows",
|
|
274
287
|
"revertRows": "Revert selected rows",
|
|
275
|
-
"valuePanel": "Value editor",
|
|
276
288
|
"firstPage": "First page",
|
|
277
289
|
"lastPage": "Last page",
|
|
278
290
|
"lastPageCounts": "Counts the rows first",
|
|
@@ -295,7 +307,10 @@
|
|
|
295
307
|
"next": "Next row",
|
|
296
308
|
"newRow": "New row",
|
|
297
309
|
"row": "Row {{number}}",
|
|
298
|
-
"position": "{{index}} of {{count}} on this page"
|
|
310
|
+
"position": "{{index}} of {{count}} on this page",
|
|
311
|
+
"close": "Close record view",
|
|
312
|
+
"expand": "Show the whole value",
|
|
313
|
+
"collapse": "Hide the whole value"
|
|
299
314
|
},
|
|
300
315
|
"aggregates": {
|
|
301
316
|
"sum": "Sum",
|
|
@@ -313,7 +328,8 @@
|
|
|
313
328
|
"running": "Exporting as {{format}}",
|
|
314
329
|
"cancel": "Cancel",
|
|
315
330
|
"done_one": "Exported {{rows}} row.",
|
|
316
|
-
"done_other": "Exported {{rows}} rows."
|
|
331
|
+
"done_other": "Exported {{rows}} rows.",
|
|
332
|
+
"failed": "Export failed"
|
|
317
333
|
},
|
|
318
334
|
"import": {
|
|
319
335
|
"menu": "Import from file",
|
|
@@ -329,7 +345,8 @@
|
|
|
329
345
|
"skip": "Skip",
|
|
330
346
|
"preview": "First {{count}} rows of the file",
|
|
331
347
|
"cancel": "Cancel",
|
|
332
|
-
"confirm": "Import"
|
|
348
|
+
"confirm": "Import",
|
|
349
|
+
"readFailed": "Could not read the file"
|
|
333
350
|
},
|
|
334
351
|
"more": "More actions",
|
|
335
352
|
"of": "of",
|
|
@@ -357,6 +374,8 @@
|
|
|
357
374
|
"running": "Running",
|
|
358
375
|
"cancelled": "The statement was cancelled.",
|
|
359
376
|
"result": "Result",
|
|
377
|
+
"results": "Results",
|
|
378
|
+
"closeResults": "Close results",
|
|
360
379
|
"noRows": "The statement returned no rows.",
|
|
361
380
|
"rows": "Rows: {{rows}}",
|
|
362
381
|
"hasMore": "More rows exist but are not shown.",
|
|
@@ -369,10 +388,6 @@
|
|
|
369
388
|
"runAll": "Run all",
|
|
370
389
|
"loading": "Loading",
|
|
371
390
|
"dismiss": "Dismiss",
|
|
372
|
-
"ranStatement": "Ran: {{statement}}",
|
|
373
|
-
"ranSelection": "Ran the selection",
|
|
374
|
-
"ranAll_one": "Ran {{formatted}} statement",
|
|
375
|
-
"ranAll_other": "Ran all {{formatted}} statements",
|
|
376
391
|
"rowRange": "Rows: {{from}} to {{to}}",
|
|
377
392
|
"page": "Page {{page}}",
|
|
378
393
|
"previousPage": "Previous page",
|
|
@@ -384,15 +399,15 @@
|
|
|
384
399
|
"schema": "Schema",
|
|
385
400
|
"schemaPlaceholder": "Schema",
|
|
386
401
|
"transaction": {
|
|
387
|
-
"
|
|
388
|
-
"
|
|
389
|
-
"manual": "Manual",
|
|
390
|
-
"hint": "Auto commits every run. Manual keeps a transaction open until you commit or roll back.",
|
|
402
|
+
"autoCommit": "Auto-commit",
|
|
403
|
+
"hint": "On, every run commits. Off, a transaction stays open until you commit or roll back.",
|
|
391
404
|
"open": "Transaction open",
|
|
392
405
|
"commit": "Commit",
|
|
393
406
|
"rollback": "Roll back",
|
|
394
407
|
"endTitle": "End the transaction?",
|
|
395
|
-
"endDescription": "A transaction is still open. Commit it to keep its changes, or roll it back to discard them."
|
|
408
|
+
"endDescription": "A transaction is still open. Commit it to keep its changes, or roll it back to discard them.",
|
|
409
|
+
"commitFailed": "Could not commit",
|
|
410
|
+
"rollbackFailed": "Could not roll back"
|
|
396
411
|
},
|
|
397
412
|
"destructive": {
|
|
398
413
|
"title": "Run destructive statements?",
|
|
@@ -414,7 +429,9 @@
|
|
|
414
429
|
"hint": "Click to edit, double click to run.",
|
|
415
430
|
"rows_one": "{{formatted}} row",
|
|
416
431
|
"rows_other": "{{formatted}} rows"
|
|
417
|
-
}
|
|
432
|
+
},
|
|
433
|
+
"pageFailed": "Could not read the page",
|
|
434
|
+
"exportFailed": "Export failed"
|
|
418
435
|
},
|
|
419
436
|
"value": {
|
|
420
437
|
"title": "Value",
|