@adecore/database 0.25.9 → 0.26.1

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.
@@ -1,18 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { use, useMemo, useState } from 'react';
3
- import { useTranslation } from 'react-i18next';
2
+ import { use, useMemo } from 'react';
4
3
  import { DatabaseContext } from './client-context.js';
5
- import { addDatabaseResources } from './locales.js';
4
+ import { useDatabaseResources } from './use-database-resources.js';
6
5
  /* Hands every view below it the client and the app's hooks, and adds the package's words to the i18next the app's `UIProvider` was given. Mount it inside that provider. */
7
6
  export function DatabaseProvider({ client, onAction, onNotice, storage, files, numberNotation, children }) {
8
- const { i18n } = useTranslation();
7
+ useDatabaseResources();
9
8
  const outer = use(DatabaseContext);
10
- const [added, setAdded] = useState(null);
11
- // In render, as `UIProvider` does, so the first view already reads its words.
12
- if (added !== i18n) {
13
- addDatabaseResources(i18n);
14
- setAdded(i18n);
15
- }
16
9
  const ownClient = client ?? outer?.client;
17
10
  const ownAction = onAction ?? outer?.onAction;
18
11
  const ownNotice = onNotice ?? outer?.onNotice;
@@ -8,6 +8,7 @@ import { formatNumber } from '@adecore/ui/format';
8
8
  import { assignRef } from '../assign-ref.js';
9
9
  import { useNumberNotation } from '../client-context.js';
10
10
  import { usePopupPress } from '../use-popup-press.js';
11
+ import { useDatabaseResources } from '../use-database-resources.js';
11
12
  import { useStableCallback } from '../use-stable-callback.js';
12
13
  import { fullRect, rectBetween, rectContains } from './cell-range.js';
13
14
  import { captureLayout, restoreLayout, sameLayout } from './column-layout.js';
@@ -32,12 +33,15 @@ const signatureOf = (columns) => columns.map((column) => column.name).join('\u00
32
33
  const textOf = (view) => (view.suffix === undefined ? view.text : `${view.text} ${view.suffix}`);
33
34
  const estimateWidths = (columns, rows, notation) => columns.map((column, index) => estimateColumnWidth(column.name, rows.slice(0, 50).map((row) => textOf(cellView(row.cells[index] ?? null, column.kind, columnNotation(column, notation)))), column.primaryKey === true));
34
35
  /*
35
- * The table of the table view and of the console. Only the rows in view are drawn, at a fixed
36
+ * The table of the table view and of the console, and of any rows an app has on its own: it needs no
37
+ * `DatabaseProvider`, only the `UIProvider` of `@adecore/ui`. Only the rows in view are drawn, at a fixed
36
38
  * height, inside a box as tall as all of them; the header and the row-number gutter stick to the edges
37
39
  * of the scroll box. Focus stays on that box and points at the cell with `aria-activedescendant`, so
38
40
  * a cell scrolled out of the window loses nothing.
39
41
  */
40
42
  export function DataGrid({ columns, rows, label, editable = false, selectedKeys, onSelectedKeysChange, onCommit, loadValue, onNotice, sorts, onSortsChange, onFocusedCellChange, onDeleteSelected, sqlTarget, queryMenu, menu, onDuplicateRows, onFollow, canFollow, onRangeChange, initialLayout, onLayoutChange, focusRequest, columnRequest, empty, className, ref }) {
43
+ // A grid drawn without a `DatabaseProvider`, such as one over the rows of a file, still reads its words.
44
+ useDatabaseResources();
41
45
  const { t } = useTranslation('database');
42
46
  const gridId = useId();
43
47
  const scroller = useRef(null);
@@ -520,5 +524,5 @@ export function DataGrid({ columns, rows, label, editable = false, selectedKeys,
520
524
  rowKeys: selection.has(menuRow.key) ? rows.filter((row) => selection.has(row.key)).map((row) => row.key) : [menuRow.key],
521
525
  cell: menuTarget.column === null ? null : { rowKey: menuRow.key, column: menuTarget.column }
522
526
  };
523
- return (_jsx("div", { ref: attach, role: "grid", "aria-label": label, "aria-rowcount": rows.length + 1, "aria-colcount": order.length + 1, "aria-activedescendant": pickedColumns.length > 0 ? headerId(pickedColumns[0]) : focusCell === null ? undefined : cellId(focusCell.row, focusCell.column), tabIndex: 0, className: clsx('group/grid relative min-h-0 flex-1 overflow-auto bg-surface text-text outline-none', className), onKeyDown: handleKeyDown, onScroll: (event) => setScrolledBlock(scrollBlock(event.currentTarget.scrollTop - HEADER_HEIGHT)), children: _jsxs("div", { className: "relative min-w-full", style: { width: totalWidth }, children: [_jsxs("div", { role: "row", "aria-rowindex": 1, className: "sticky top-0 z-20 flex w-full border-b border-border bg-surface", style: { height: HEADER_HEIGHT }, children: [_jsxs("div", { className: PINNED_GROUP, children: [_jsx("div", { className: "shrink-0 border-r border-border bg-surface bg-clip-border", style: { width: gutter } }), headerCells.slice(0, pinnedShown)] }), headerCells.slice(pinnedShown)] }), _jsxs(ContextMenu.Root, { children: [_jsx(ContextMenu.Trigger, { role: "rowgroup", className: "relative", style: { height: rows.length * ROW_HEIGHT }, children: Array.from({ length: range.end - range.start }, (_, offset) => renderRow(range.start + offset)) }), _jsxs(ContextMenu.Popup, { children: [_jsxs(ContextMenu.Item, { disabled: menuCell === null, onClick: copyCell, children: [_jsx(Icon, { icon: Copy, size: 14 }), t('grid.copyValue'), _jsx(Kbd, { shortcut: EDIT_SHORTCUTS.copy })] }), _jsx(CopyAsMenu, { disabled: block === null, formats: sqlTarget === undefined ? COPY_FORMATS.filter((format) => format !== 'sql') : COPY_FORMATS, onCopy: (format) => void copyAs(format) }), menuContext !== null && menuCell !== null && queryMenu !== undefined && (_jsxs(_Fragment, { children: [_jsx(ContextMenu.Separator, {}), queryMenu(menuContext)] })), _jsx(ContextMenu.Separator, {}), editable && menuCell !== null && (_jsxs(_Fragment, { children: [_jsxs(ContextMenu.Item, { disabled: !settable || !nullable, onClick: () => commitValue(null), children: [_jsx(Icon, { icon: Ban, size: 14 }), t('grid.setNull')] }), hasDefault && (_jsxs(ContextMenu.Item, { disabled: !settable, onClick: () => commitValue({ kind: 'default' }), children: [_jsx(Icon, { icon: RotateCcw, size: 14 }), t('grid.setDefault')] }))] })), menuContext !== null && menu?.(menuContext)] })] }), rows.length === 0 && empty !== undefined && _jsx("div", { className: "sticky left-0 w-fit px-4 py-6 text-xs text-text-muted", children: empty })] }) }));
527
+ return (_jsx("div", { ref: attach, role: "grid", "aria-label": label, "aria-rowcount": rows.length + 1, "aria-colcount": order.length + 1, "aria-activedescendant": pickedColumns.length > 0 ? headerId(pickedColumns[0]) : focusCell === null ? undefined : cellId(focusCell.row, focusCell.column), tabIndex: 0, className: clsx('group/grid relative min-h-0 flex-1 overflow-auto bg-surface text-text outline-none', className), onKeyDown: handleKeyDown, onScroll: (event) => setScrolledBlock(scrollBlock(event.currentTarget.scrollTop - HEADER_HEIGHT)), children: _jsxs("div", { className: "relative min-w-full", style: { width: totalWidth }, children: [_jsxs("div", { role: "row", "aria-rowindex": 1, className: "sticky top-0 z-20 flex w-full border-b border-border bg-surface", style: { height: HEADER_HEIGHT }, children: [_jsxs("div", { className: PINNED_GROUP, children: [_jsx("div", { className: "shrink-0 border-r border-border bg-surface bg-clip-border", style: { width: gutter } }), headerCells.slice(0, pinnedShown)] }), headerCells.slice(pinnedShown)] }), _jsxs(ContextMenu.Root, { children: [_jsx(ContextMenu.Trigger, { role: "rowgroup", className: "relative", style: { height: rows.length * ROW_HEIGHT }, children: Array.from({ length: range.end - range.start }, (_, offset) => renderRow(range.start + offset)) }), _jsxs(ContextMenu.Popup, { children: [_jsxs(ContextMenu.Item, { disabled: menuCell === null, onClick: copyCell, children: [_jsx(Icon, { icon: Copy, size: 14 }), t('grid.copyValue'), _jsx(Kbd, { shortcut: EDIT_SHORTCUTS.copy })] }), _jsx(CopyAsMenu, { disabled: block === null, formats: sqlTarget === undefined ? COPY_FORMATS.filter((format) => format !== 'sql') : COPY_FORMATS, onCopy: (format) => void copyAs(format) }), menuContext !== null && menuCell !== null && queryMenu !== undefined && (_jsxs(_Fragment, { children: [_jsx(ContextMenu.Separator, {}), queryMenu(menuContext)] })), _jsx(ContextMenu.Separator, {}), editable && menuCell !== null && (_jsxs(_Fragment, { children: [_jsxs(ContextMenu.Item, { disabled: !settable || !nullable, onClick: () => commitValue(null), children: [_jsx(Icon, { icon: Ban, size: 14 }), t('grid.setNull')] }), hasDefault && (_jsxs(ContextMenu.Item, { disabled: !settable, onClick: () => commitValue({ kind: 'default' }), children: [_jsx(Icon, { icon: RotateCcw, size: 14 }), t('grid.setDefault')] }))] })), _jsx(ContextMenu.Separator, {}), menuContext !== null && menu?.(menuContext)] })] }), rows.length === 0 && empty !== undefined && _jsx("div", { className: "sticky left-0 w-fit px-4 py-6 text-xs text-text-muted", children: empty })] }) }));
524
528
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
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
2
  export type { DatabaseAction, DatabaseFiles, DatabaseNotice, DatabaseStorage, ExplorerSelection } from './actions.ts';
3
+ export type { Cell, Value, ValueKind } from './protocol/index.ts';
4
+ export type { SortDirection } from './sql.ts';
3
5
  export { ConnectionForm, type ConnectionFormProps } from './connections/ConnectionForm.tsx';
4
6
  export { isValidConfig, type ConfigProblem, type ConfigProblems } from './connections/connection-config.ts';
5
7
  export { ConnectionManager, type ConnectionManagerProps } from './connections/ConnectionManager.tsx';
@@ -9,7 +11,11 @@ export type { ExplorerFolder, ExplorerItem } from './explorer/folders.ts';
9
11
  export { TableDesigner, type TableDesignerProps } from './designer/TableDesigner.tsx';
10
12
  export { DatabaseProvider, type DatabaseProviderProps } from './DatabaseProvider.tsx';
11
13
  export { useDatabaseClient } from './client-context.ts';
14
+ export { DataGrid, type DataGridProps } from './grid/DataGrid.tsx';
15
+ export type { GridLayout } from './grid/column-layout.ts';
12
16
  export type { NumberNotation } from './grid/display.ts';
17
+ export type { GridSort } from './grid/sort.ts';
18
+ export type { FocusedCell, GridColumn, GridMenuContext, GridRow, GridRowState } from './grid/types.ts';
13
19
  export { QueryConsole, type QueryConsoleProps } from './console/QueryConsole.tsx';
14
20
  export type { QueryConsoleEditorHandle, QueryConsoleEditorProps, QueryConsoleRunScope } from './console/editor-slot.ts';
15
21
  export { runScopeOf } from './console/shortcuts.ts';
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ export { DatabaseExplorer } from './explorer/DatabaseExplorer.js';
8
8
  export { TableDesigner } from './designer/TableDesigner.js';
9
9
  export { DatabaseProvider } from './DatabaseProvider.js';
10
10
  export { useDatabaseClient } from './client-context.js';
11
+ export { DataGrid } from './grid/DataGrid.js';
11
12
  export { QueryConsole } from './console/QueryConsole.js';
12
13
  export { runScopeOf } from './console/shortcuts.js';
13
14
  export { StructureView } from './structure/StructureView.js';
@@ -148,13 +148,13 @@ export default {
148
148
  editConnection: 'Edit connection',
149
149
  copyDdl: 'Copy DDL',
150
150
  rename: {
151
- action: 'Rename',
151
+ action: 'Rename…',
152
152
  title: 'Rename "{{name}}"',
153
153
  field: 'New name',
154
154
  confirm: 'Rename'
155
155
  },
156
156
  truncate: {
157
- action: 'Truncate',
157
+ action: 'Truncate…',
158
158
  title: 'Truncate "{{name}}"?',
159
159
  description: 'Deletes every row in "{{name}}". This cannot be undone.',
160
160
  confirm: 'Truncate'
@@ -163,13 +163,13 @@ export default {
163
163
  field: 'Type "{{name}}" to confirm',
164
164
  mismatch: 'The name does not match.',
165
165
  table: {
166
- action: 'Drop',
166
+ action: 'Drop…',
167
167
  title: 'Drop "{{name}}"?',
168
168
  description: 'Deletes the table and every row in it. This cannot be undone.',
169
169
  confirm: 'Drop table'
170
170
  },
171
171
  view: {
172
- action: 'Drop view',
172
+ action: 'Drop view…',
173
173
  title: 'Drop "{{name}}"?',
174
174
  description: 'Deletes the view. The tables behind it are not touched.',
175
175
  confirm: 'Drop view'
@@ -148,13 +148,13 @@ export default {
148
148
  editConnection: 'Verbinding bewerken',
149
149
  copyDdl: 'DDL kopiëren',
150
150
  rename: {
151
- action: 'Hernoemen',
151
+ action: 'Hernoemen…',
152
152
  title: '"{{name}}" hernoemen',
153
153
  field: 'Nieuwe naam',
154
154
  confirm: 'Hernoemen'
155
155
  },
156
156
  truncate: {
157
- action: 'Leegmaken',
157
+ action: 'Leegmaken…',
158
158
  title: '"{{name}}" leegmaken?',
159
159
  description: 'Verwijdert elke rij in "{{name}}". Dit kan niet ongedaan worden gemaakt.',
160
160
  confirm: 'Leegmaken'
@@ -163,13 +163,13 @@ export default {
163
163
  field: 'Typ "{{name}}" om te bevestigen',
164
164
  mismatch: 'De naam komt niet overeen.',
165
165
  table: {
166
- action: 'Verwijderen',
166
+ action: 'Verwijderen…',
167
167
  title: '"{{name}}" verwijderen?',
168
168
  description: 'Verwijdert de tabel en elke rij erin. Dit kan niet ongedaan worden gemaakt.',
169
169
  confirm: 'Tabel verwijderen'
170
170
  },
171
171
  view: {
172
- action: 'View verwijderen',
172
+ action: 'View verwijderen…',
173
173
  title: '"{{name}}" verwijderen?',
174
174
  description: 'Verwijdert de view. De tabellen erachter blijven ongemoeid.',
175
175
  confirm: 'View verwijderen'
@@ -0,0 +1 @@
1
+ export declare function useDatabaseResources(): void;
@@ -0,0 +1,12 @@
1
+ import { useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { addDatabaseResources } from './locales.js';
4
+ /* Adds the package's words to the i18next the app's `UIProvider` was given. In render, as `UIProvider` does, so the first view already reads them. */
5
+ export function useDatabaseResources() {
6
+ const { i18n } = useTranslation();
7
+ const [added, setAdded] = useState(null);
8
+ if (added !== i18n) {
9
+ addDatabaseResources(i18n);
10
+ setAdded(i18n);
11
+ }
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/database",
3
- "version": "0.25.9",
3
+ "version": "0.26.1",
4
4
  "description": "Browse, query and edit SQLite and MySQL databases: React views for the page, a host for the backend and a native helper that talks to the server.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "author": "Bas Milius",
@@ -61,20 +61,20 @@
61
61
  "lucide-react": "^1.47.0"
62
62
  },
63
63
  "optionalDependencies": {
64
- "@adecore/database-darwin-arm64": "0.25.9",
65
- "@adecore/database-linux-arm64": "0.25.9",
66
- "@adecore/database-linux-x64": "0.25.9",
67
- "@adecore/database-win32-x64": "0.25.9"
64
+ "@adecore/database-darwin-arm64": "0.26.1",
65
+ "@adecore/database-linux-arm64": "0.26.1",
66
+ "@adecore/database-linux-x64": "0.26.1",
67
+ "@adecore/database-win32-x64": "0.26.1"
68
68
  },
69
69
  "peerDependencies": {
70
- "@adecore/ui": "^0.25.9",
70
+ "@adecore/ui": "^0.26.1",
71
71
  "i18next": "^26.4.2",
72
72
  "react": "^19.3.0",
73
73
  "react-dom": "^19.3.0",
74
74
  "react-i18next": "^17.0.15"
75
75
  },
76
76
  "devDependencies": {
77
- "@adecore/ui": "0.25.9",
77
+ "@adecore/ui": "0.26.1",
78
78
  "@happy-dom/global-registrator": "^20.14.5",
79
79
  "@types/bun": "^1.4.2",
80
80
  "@types/node": "^24.13.6",
@@ -1,10 +1,9 @@
1
- import { use, useMemo, useState, type ReactNode } from 'react';
2
- import { useTranslation } from 'react-i18next';
1
+ import { use, useMemo, type ReactNode } from 'react';
3
2
  import type { DatabaseAction, DatabaseFiles, DatabaseNotice, DatabaseStorage } from './actions.ts';
4
3
  import type { DatabaseClient } from './client/types.ts';
5
4
  import { DatabaseContext } from './client-context.ts';
6
5
  import type { NumberNotation } from './grid/display.ts';
7
- import { addDatabaseResources } from './locales.ts';
6
+ import { useDatabaseResources } from './use-database-resources.ts';
8
7
 
9
8
  export interface DatabaseProviderProps {
10
9
  /* Required on the outermost provider. A provider inside another takes every prop it leaves out from the one above. */
@@ -26,14 +25,8 @@ export interface DatabaseProviderProps {
26
25
 
27
26
  /* Hands every view below it the client and the app's hooks, and adds the package's words to the i18next the app's `UIProvider` was given. Mount it inside that provider. */
28
27
  export function DatabaseProvider({ client, onAction, onNotice, storage, files, numberNotation, children }: DatabaseProviderProps) {
29
- const { i18n } = useTranslation();
28
+ useDatabaseResources();
30
29
  const outer = use(DatabaseContext);
31
- const [added, setAdded] = useState<typeof i18n | null>(null);
32
- // In render, as `UIProvider` does, so the first view already reads its words.
33
- if (added !== i18n) {
34
- addDatabaseResources(i18n);
35
- setAdded(i18n);
36
- }
37
30
 
38
31
  const ownClient = client ?? outer?.client;
39
32
  const ownAction = onAction ?? outer?.onAction;
@@ -22,6 +22,7 @@ import { assignRef } from '../assign-ref.ts';
22
22
  import type { SqlTarget } from '../sql.ts';
23
23
  import { useNumberNotation } from '../client-context.ts';
24
24
  import { usePopupPress } from '../use-popup-press.ts';
25
+ import { useDatabaseResources } from '../use-database-resources.ts';
25
26
  import { useStableCallback } from '../use-stable-callback.ts';
26
27
  import type { RangeBlock } from './aggregates.ts';
27
28
  import { fullRect, rectBetween, rectContains } from './cell-range.ts';
@@ -75,6 +76,7 @@ export interface DataGridProps {
75
76
  onNotice?(notice: DatabaseNotice): void;
76
77
  /* The sorted columns by name, in order. Only shown unless `onSortsChange` is given, which is what makes the headers sort. */
77
78
  sorts?: readonly GridSort[];
79
+ /* A header's menu picked a sort. The grid draws `rows` in the order given, so the handler orders them and passes `sorts` back. */
78
80
  onSortsChange?(sorts: readonly GridSort[]): void;
79
81
  /* The cell that holds the focus changed, or lost it. */
80
82
  onFocusedCellChange?(cell: FocusedCell | null): void;
@@ -141,7 +143,8 @@ const estimateWidths = (columns: readonly GridColumn[], rows: readonly GridRow[]
141
143
  );
142
144
 
143
145
  /*
144
- * The table of the table view and of the console. Only the rows in view are drawn, at a fixed
146
+ * The table of the table view and of the console, and of any rows an app has on its own: it needs no
147
+ * `DatabaseProvider`, only the `UIProvider` of `@adecore/ui`. Only the rows in view are drawn, at a fixed
145
148
  * height, inside a box as tall as all of them; the header and the row-number gutter stick to the edges
146
149
  * of the scroll box. Focus stays on that box and points at the cell with `aria-activedescendant`, so
147
150
  * a cell scrolled out of the window loses nothing.
@@ -175,6 +178,8 @@ export function DataGrid({
175
178
  className,
176
179
  ref
177
180
  }: DataGridProps) {
181
+ // A grid drawn without a `DatabaseProvider`, such as one over the rows of a file, still reads its words.
182
+ useDatabaseResources();
178
183
  const { t } = useTranslation('database');
179
184
  const gridId = useId();
180
185
  const scroller = useRef<HTMLDivElement | null>(null);
@@ -819,6 +824,7 @@ export function DataGrid({
819
824
  )}
820
825
  </>
821
826
  )}
827
+ <ContextMenu.Separator />
822
828
  {menuContext !== null && menu?.(menuContext)}
823
829
  </ContextMenu.Popup>
824
830
  </ContextMenu.Root>
package/src/grid/types.ts CHANGED
@@ -23,6 +23,7 @@ export interface GridRow {
23
23
  readonly key: string;
24
24
  /* The number in the gutter; `null` for a row that does not exist yet. */
25
25
  readonly number: number | null;
26
+ /* One per column, in the order of the columns. A `null` draws as a faint NULL, so an empty text is `''`. */
26
27
  readonly cells: readonly Shown[];
27
28
  readonly state?: GridRowState;
28
29
  /* Indexes of the cells with a pending edit. */
package/src/index.ts CHANGED
@@ -21,6 +21,8 @@ export {
21
21
  } from './client/index.ts';
22
22
 
23
23
  export type { DatabaseAction, DatabaseFiles, DatabaseNotice, DatabaseStorage, ExplorerSelection } from './actions.ts';
24
+ export type { Cell, Value, ValueKind } from './protocol/index.ts';
25
+ export type { SortDirection } from './sql.ts';
24
26
 
25
27
  export { ConnectionForm, type ConnectionFormProps } from './connections/ConnectionForm.tsx';
26
28
  export { isValidConfig, type ConfigProblem, type ConfigProblems } from './connections/connection-config.ts';
@@ -31,7 +33,11 @@ export type { ExplorerFolder, ExplorerItem } from './explorer/folders.ts';
31
33
  export { TableDesigner, type TableDesignerProps } from './designer/TableDesigner.tsx';
32
34
  export { DatabaseProvider, type DatabaseProviderProps } from './DatabaseProvider.tsx';
33
35
  export { useDatabaseClient } from './client-context.ts';
36
+ export { DataGrid, type DataGridProps } from './grid/DataGrid.tsx';
37
+ export type { GridLayout } from './grid/column-layout.ts';
34
38
  export type { NumberNotation } from './grid/display.ts';
39
+ export type { GridSort } from './grid/sort.ts';
40
+ export type { FocusedCell, GridColumn, GridMenuContext, GridRow, GridRowState } from './grid/types.ts';
35
41
  export { QueryConsole, type QueryConsoleProps } from './console/QueryConsole.tsx';
36
42
  export type { QueryConsoleEditorHandle, QueryConsoleEditorProps, QueryConsoleRunScope } from './console/editor-slot.ts';
37
43
  export { runScopeOf } from './console/shortcuts.ts';
package/src/locales/en.ts CHANGED
@@ -148,13 +148,13 @@ export default {
148
148
  editConnection: 'Edit connection',
149
149
  copyDdl: 'Copy DDL',
150
150
  rename: {
151
- action: 'Rename',
151
+ action: 'Rename…',
152
152
  title: 'Rename "{{name}}"',
153
153
  field: 'New name',
154
154
  confirm: 'Rename'
155
155
  },
156
156
  truncate: {
157
- action: 'Truncate',
157
+ action: 'Truncate…',
158
158
  title: 'Truncate "{{name}}"?',
159
159
  description: 'Deletes every row in "{{name}}". This cannot be undone.',
160
160
  confirm: 'Truncate'
@@ -163,13 +163,13 @@ export default {
163
163
  field: 'Type "{{name}}" to confirm',
164
164
  mismatch: 'The name does not match.',
165
165
  table: {
166
- action: 'Drop',
166
+ action: 'Drop…',
167
167
  title: 'Drop "{{name}}"?',
168
168
  description: 'Deletes the table and every row in it. This cannot be undone.',
169
169
  confirm: 'Drop table'
170
170
  },
171
171
  view: {
172
- action: 'Drop view',
172
+ action: 'Drop view…',
173
173
  title: 'Drop "{{name}}"?',
174
174
  description: 'Deletes the view. The tables behind it are not touched.',
175
175
  confirm: 'Drop view'
package/src/locales/nl.ts CHANGED
@@ -148,13 +148,13 @@ export default {
148
148
  editConnection: 'Verbinding bewerken',
149
149
  copyDdl: 'DDL kopiëren',
150
150
  rename: {
151
- action: 'Hernoemen',
151
+ action: 'Hernoemen…',
152
152
  title: '"{{name}}" hernoemen',
153
153
  field: 'Nieuwe naam',
154
154
  confirm: 'Hernoemen'
155
155
  },
156
156
  truncate: {
157
- action: 'Leegmaken',
157
+ action: 'Leegmaken…',
158
158
  title: '"{{name}}" leegmaken?',
159
159
  description: 'Verwijdert elke rij in "{{name}}". Dit kan niet ongedaan worden gemaakt.',
160
160
  confirm: 'Leegmaken'
@@ -163,13 +163,13 @@ export default {
163
163
  field: 'Typ "{{name}}" om te bevestigen',
164
164
  mismatch: 'De naam komt niet overeen.',
165
165
  table: {
166
- action: 'Verwijderen',
166
+ action: 'Verwijderen…',
167
167
  title: '"{{name}}" verwijderen?',
168
168
  description: 'Verwijdert de tabel en elke rij erin. Dit kan niet ongedaan worden gemaakt.',
169
169
  confirm: 'Tabel verwijderen'
170
170
  },
171
171
  view: {
172
- action: 'View verwijderen',
172
+ action: 'View verwijderen…',
173
173
  title: '"{{name}}" verwijderen?',
174
174
  description: 'Verwijdert de view. De tabellen erachter blijven ongemoeid.',
175
175
  confirm: 'View verwijderen'
@@ -0,0 +1,14 @@
1
+ import { useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import type { i18n as I18n } from 'i18next';
4
+ import { addDatabaseResources } from './locales.ts';
5
+
6
+ /* Adds the package's words to the i18next the app's `UIProvider` was given. In render, as `UIProvider` does, so the first view already reads them. */
7
+ export function useDatabaseResources(): void {
8
+ const { i18n } = useTranslation();
9
+ const [added, setAdded] = useState<I18n | null>(null);
10
+ if (added !== i18n) {
11
+ addDatabaseResources(i18n);
12
+ setAdded(i18n);
13
+ }
14
+ }