@adecore/database 0.0.1 → 0.16.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +152 -1
- package/dist/DatabaseProvider.d.ts +13 -0
- package/dist/DatabaseProvider.js +17 -0
- package/dist/actions.d.ts +40 -0
- package/dist/actions.js +1 -0
- package/dist/client/client.d.ts +5 -0
- package/dist/client/client.js +245 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +2 -0
- package/dist/client/types.d.ts +97 -0
- package/dist/client/types.js +13 -0
- package/dist/client-context.d.ts +16 -0
- package/dist/client-context.js +17 -0
- package/dist/connections/ConnectionForm.d.ts +10 -0
- package/dist/connections/ConnectionForm.js +145 -0
- package/dist/connections/ConnectionManager.d.ts +12 -0
- package/dist/connections/ConnectionManager.js +93 -0
- package/dist/connections/EngineIcon.d.ts +9 -0
- package/dist/connections/EngineIcon.js +7 -0
- package/dist/connections/connection-config.d.ts +16 -0
- package/dist/connections/connection-config.js +107 -0
- package/dist/connections/docker.d.ts +9 -0
- package/dist/connections/docker.js +38 -0
- package/dist/connections/engine-icons.d.ts +3 -0
- package/dist/connections/engine-icons.js +3 -0
- package/dist/connections/use-docker-containers.d.ts +17 -0
- package/dist/connections/use-docker-containers.js +26 -0
- package/dist/console/DestructiveDialog.d.ts +9 -0
- package/dist/console/DestructiveDialog.js +9 -0
- package/dist/console/HistoryPanel.d.ts +10 -0
- package/dist/console/HistoryPanel.js +15 -0
- package/dist/console/QueryConsole.d.ts +14 -0
- package/dist/console/QueryConsole.js +261 -0
- package/dist/console/SchemaPicker.d.ts +11 -0
- package/dist/console/SchemaPicker.js +8 -0
- package/dist/console/SqlEditor.d.ts +12 -0
- package/dist/console/SqlEditor.js +61 -0
- package/dist/console/StatementResultView.d.ts +18 -0
- package/dist/console/StatementResultView.js +32 -0
- package/dist/console/TransactionControls.d.ts +13 -0
- package/dist/console/TransactionControls.js +12 -0
- package/dist/console/destructive.d.ts +7 -0
- package/dist/console/destructive.js +43 -0
- package/dist/console/editing.d.ts +12 -0
- package/dist/console/editing.js +60 -0
- package/dist/console/history.d.ts +13 -0
- package/dist/console/history.js +43 -0
- package/dist/console/labels.d.ts +1 -0
- package/dist/console/labels.js +6 -0
- package/dist/console/outcome.d.ts +6 -0
- package/dist/console/outcome.js +11 -0
- package/dist/console/schemas.d.ts +2 -0
- package/dist/console/schemas.js +5 -0
- package/dist/console/shortcuts.d.ts +2 -0
- package/dist/console/shortcuts.js +3 -0
- package/dist/console/useConsoleHistory.d.ts +7 -0
- package/dist/console/useConsoleHistory.js +22 -0
- package/dist/ddl/alter-mysql.d.ts +4 -0
- package/dist/ddl/alter-mysql.js +79 -0
- package/dist/ddl/alter-sqlite.d.ts +4 -0
- package/dist/ddl/alter-sqlite.js +80 -0
- package/dist/ddl/alter.d.ts +4 -0
- package/dist/ddl/alter.js +24 -0
- package/dist/ddl/column.d.ts +9 -0
- package/dist/ddl/column.js +62 -0
- package/dist/ddl/create.d.ts +12 -0
- package/dist/ddl/create.js +74 -0
- package/dist/ddl/dialect.d.ts +12 -0
- package/dist/ddl/dialect.js +34 -0
- package/dist/ddl/diff.d.ts +34 -0
- package/dist/ddl/diff.js +111 -0
- package/dist/ddl/draft.d.ts +46 -0
- package/dist/ddl/draft.js +70 -0
- package/dist/ddl/index.d.ts +10 -0
- package/dist/ddl/index.js +10 -0
- package/dist/ddl/parse.d.ts +17 -0
- package/dist/ddl/parse.js +131 -0
- package/dist/ddl/statements.d.ts +6 -0
- package/dist/ddl/statements.js +12 -0
- package/dist/ddl/types.d.ts +6 -0
- package/dist/ddl/types.js +52 -0
- package/dist/ddl/validate.d.ts +7 -0
- package/dist/ddl/validate.js +38 -0
- package/dist/designer/ColumnsEditor.d.ts +2 -0
- package/dist/designer/ColumnsEditor.js +24 -0
- package/dist/designer/DesignerView.d.ts +25 -0
- package/dist/designer/DesignerView.js +33 -0
- package/dist/designer/ForeignKeysEditor.d.ts +7 -0
- package/dist/designer/ForeignKeysEditor.js +24 -0
- package/dist/designer/IndexesEditor.d.ts +2 -0
- package/dist/designer/IndexesEditor.js +13 -0
- package/dist/designer/NamePicker.d.ts +11 -0
- package/dist/designer/NamePicker.js +13 -0
- package/dist/designer/OptionsEditor.d.ts +2 -0
- package/dist/designer/OptionsEditor.js +17 -0
- package/dist/designer/SqlPreview.d.ts +12 -0
- package/dist/designer/SqlPreview.js +17 -0
- package/dist/designer/TableDesigner.d.ts +10 -0
- package/dist/designer/TableDesigner.js +103 -0
- package/dist/designer/edit.d.ts +17 -0
- package/dist/designer/edit.js +115 -0
- package/dist/designer/editor-props.d.ts +10 -0
- package/dist/designer/editor-props.js +1 -0
- package/dist/designer/plan.d.ts +9 -0
- package/dist/designer/plan.js +13 -0
- package/dist/designer/recover.d.ts +2 -0
- package/dist/designer/recover.js +18 -0
- package/dist/designer/useReferenceColumns.d.ts +3 -0
- package/dist/designer/useReferenceColumns.js +36 -0
- package/dist/explorer/DatabaseExplorer.d.ts +13 -0
- package/dist/explorer/DatabaseExplorer.js +264 -0
- package/dist/explorer/RowMenu.d.ts +11 -0
- package/dist/explorer/RowMenu.js +89 -0
- package/dist/explorer/TableDialog.d.ts +15 -0
- package/dist/explorer/TableDialog.js +50 -0
- package/dist/explorer/expansion.d.ts +7 -0
- package/dist/explorer/expansion.js +31 -0
- package/dist/explorer/table-statements.d.ts +5 -0
- package/dist/explorer/table-statements.js +12 -0
- package/dist/explorer/tree.d.ts +125 -0
- package/dist/explorer/tree.js +295 -0
- package/dist/explorer/use-explorer-loads.d.ts +12 -0
- package/dist/explorer/use-explorer-loads.js +109 -0
- package/dist/explorer/use-stored-expansion.d.ts +9 -0
- package/dist/explorer/use-stored-expansion.js +47 -0
- package/dist/grid/CellEditor.d.ts +9 -0
- package/dist/grid/CellEditor.js +29 -0
- package/dist/grid/CopyAsMenu.d.ts +7 -0
- package/dist/grid/CopyAsMenu.js +10 -0
- package/dist/grid/DataGrid.d.ts +36 -0
- package/dist/grid/DataGrid.js +512 -0
- package/dist/grid/EnumPicker.d.ts +17 -0
- package/dist/grid/EnumPicker.js +53 -0
- package/dist/grid/GridHeaderCell.d.ts +36 -0
- package/dist/grid/GridHeaderCell.js +69 -0
- package/dist/grid/RecordView.d.ts +20 -0
- package/dist/grid/RecordView.js +126 -0
- package/dist/grid/ValueDock.d.ts +11 -0
- package/dist/grid/ValueDock.js +28 -0
- package/dist/grid/aggregates.d.ts +22 -0
- package/dist/grid/aggregates.js +62 -0
- package/dist/grid/cell-range.d.ts +14 -0
- package/dist/grid/cell-range.js +11 -0
- package/dist/grid/column-layout.d.ts +18 -0
- package/dist/grid/column-layout.js +49 -0
- package/dist/grid/column-selection.d.ts +11 -0
- package/dist/grid/column-selection.js +28 -0
- package/dist/grid/column-view.d.ts +10 -0
- package/dist/grid/column-view.js +23 -0
- package/dist/grid/copy-formats.d.ts +15 -0
- package/dist/grid/copy-formats.js +80 -0
- package/dist/grid/display.d.ts +20 -0
- package/dist/grid/display.js +81 -0
- package/dist/grid/edit-value.d.ts +5 -0
- package/dist/grid/edit-value.js +55 -0
- package/dist/grid/enum-type.d.ts +7 -0
- package/dist/grid/enum-type.js +52 -0
- package/dist/grid/focused-value.d.ts +4 -0
- package/dist/grid/focused-value.js +19 -0
- package/dist/grid/layout.d.ts +22 -0
- package/dist/grid/layout.js +74 -0
- package/dist/grid/navigation.d.ts +15 -0
- package/dist/grid/navigation.js +52 -0
- package/dist/grid/row-selection.d.ts +9 -0
- package/dist/grid/row-selection.js +24 -0
- package/dist/grid/sort.d.ts +7 -0
- package/dist/grid/sort.js +7 -0
- package/dist/grid/types.d.ts +30 -0
- package/dist/grid/types.js +9 -0
- package/dist/host/helper-path.d.ts +7 -0
- package/dist/host/helper-path.js +15 -0
- package/dist/host/host.d.ts +2 -0
- package/dist/host/host.js +343 -0
- package/dist/host/index.d.ts +5 -0
- package/dist/host/index.js +4 -0
- package/dist/host/spawn.d.ts +7 -0
- package/dist/host/spawn.js +98 -0
- package/dist/host/types.d.ts +19 -0
- package/dist/host/types.js +1 -0
- package/dist/host/validate.d.ts +10 -0
- package/dist/host/validate.js +339 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +13 -0
- package/dist/locales/en.json +577 -0
- package/dist/locales/nl.json +577 -0
- package/dist/locales.d.ts +4 -0
- package/dist/locales.js +14 -0
- package/dist/protocol/connection.d.ts +56 -0
- package/dist/protocol/connection.js +1 -0
- package/dist/protocol/errors.d.ts +7 -0
- package/dist/protocol/errors.js +1 -0
- package/dist/protocol/index.d.ts +5 -0
- package/dist/protocol/index.js +3 -0
- package/dist/protocol/messages.d.ts +250 -0
- package/dist/protocol/messages.js +2 -0
- package/dist/protocol/schema.d.ts +53 -0
- package/dist/protocol/schema.js +1 -0
- package/dist/protocol/values.d.ts +21 -0
- package/dist/protocol/values.js +15 -0
- package/dist/sql-split.d.ts +15 -0
- package/dist/sql-split.js +201 -0
- package/dist/sql.d.ts +29 -0
- package/dist/sql.js +104 -0
- package/dist/structure/StructureView.d.ts +14 -0
- package/dist/structure/StructureView.js +99 -0
- package/dist/structure/structure-text.d.ts +2 -0
- package/dist/structure/structure-text.js +2 -0
- package/dist/table/CommandField.d.ts +14 -0
- package/dist/table/CommandField.js +157 -0
- package/dist/table/ImportDialog.d.ts +27 -0
- package/dist/table/ImportDialog.js +28 -0
- package/dist/table/TableStatusBar.d.ts +19 -0
- package/dist/table/TableStatusBar.js +22 -0
- package/dist/table/TableToolbar.d.ts +43 -0
- package/dist/table/TableToolbar.js +16 -0
- package/dist/table/TableView.d.ts +13 -0
- package/dist/table/TableView.js +508 -0
- package/dist/table/command-field.d.ts +57 -0
- package/dist/table/command-field.js +203 -0
- package/dist/table/grid-rows.d.ts +23 -0
- package/dist/table/grid-rows.js +55 -0
- package/dist/table/import-mapping.d.ts +7 -0
- package/dist/table/import-mapping.js +28 -0
- package/dist/table/layout-store.d.ts +13 -0
- package/dist/table/layout-store.js +43 -0
- package/dist/table/paging.d.ts +12 -0
- package/dist/table/paging.js +20 -0
- package/dist/table/pending.d.ts +32 -0
- package/dist/table/pending.js +94 -0
- package/dist/table/references.d.ts +12 -0
- package/dist/table/references.js +21 -0
- package/dist/table/useLoaded.d.ts +7 -0
- package/dist/table/useLoaded.js +36 -0
- package/dist/table/useStoredLayout.d.ts +8 -0
- package/dist/table/useStoredLayout.js +39 -0
- package/dist/table/useTableTransfer.d.ts +51 -0
- package/dist/table/useTableTransfer.js +125 -0
- package/dist/testing/fake.d.ts +21 -0
- package/dist/testing/fake.js +372 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +1 -0
- package/dist/use-popup-press.d.ts +1 -0
- package/dist/use-popup-press.js +24 -0
- package/dist/use-schema-change.d.ts +1 -0
- package/dist/use-schema-change.js +13 -0
- package/dist/use-stable-callback.d.ts +1 -0
- package/dist/use-stable-callback.js +9 -0
- package/dist/value/ValuePanel.d.ts +14 -0
- package/dist/value/ValuePanel.js +103 -0
- package/dist/value/draft.d.ts +33 -0
- package/dist/value/draft.js +44 -0
- package/dist/value/hex.d.ts +14 -0
- package/dist/value/hex.js +46 -0
- package/dist/value/json.d.ts +9 -0
- package/dist/value/json.js +71 -0
- package/dist/value/views.d.ts +22 -0
- package/dist/value/views.js +54 -0
- package/dist/workbench/DatabaseWorkbench.d.ts +9 -0
- package/dist/workbench/DatabaseWorkbench.js +179 -0
- package/dist/workbench/DesignerTab.d.ts +10 -0
- package/dist/workbench/DesignerTab.js +22 -0
- package/dist/workbench/TableTab.d.ts +10 -0
- package/dist/workbench/TableTab.js +8 -0
- package/dist/workbench/tabs.d.ts +42 -0
- package/dist/workbench/tabs.js +139 -0
- package/package.json +79 -3
- package/src/DatabaseProvider.tsx +37 -0
- package/src/actions.ts +34 -0
- package/src/client/client.ts +324 -0
- package/src/client/index.ts +18 -0
- package/src/client/types.ts +161 -0
- package/src/client-context.ts +35 -0
- package/src/connections/ConnectionForm.tsx +474 -0
- package/src/connections/ConnectionManager.tsx +224 -0
- package/src/connections/EngineIcon.tsx +18 -0
- package/src/connections/connection-config.ts +143 -0
- package/src/connections/docker.ts +50 -0
- package/src/connections/engine-icons.ts +5 -0
- package/src/connections/use-docker-containers.ts +41 -0
- package/src/console/DestructiveDialog.tsx +40 -0
- package/src/console/HistoryPanel.tsx +67 -0
- package/src/console/QueryConsole.tsx +439 -0
- package/src/console/SchemaPicker.tsx +32 -0
- package/src/console/SqlEditor.tsx +95 -0
- package/src/console/StatementResultView.tsx +176 -0
- package/src/console/TransactionControls.tsx +53 -0
- package/src/console/destructive.ts +60 -0
- package/src/console/editing.ts +77 -0
- package/src/console/history.ts +62 -0
- package/src/console/labels.ts +7 -0
- package/src/console/outcome.ts +14 -0
- package/src/console/schemas.ts +7 -0
- package/src/console/shortcuts.ts +4 -0
- package/src/console/useConsoleHistory.ts +39 -0
- package/src/ddl/alter-mysql.ts +90 -0
- package/src/ddl/alter-sqlite.ts +93 -0
- package/src/ddl/alter.ts +29 -0
- package/src/ddl/column.ts +74 -0
- package/src/ddl/create.ts +92 -0
- package/src/ddl/dialect.ts +53 -0
- package/src/ddl/diff.ts +164 -0
- package/src/ddl/draft.ts +136 -0
- package/src/ddl/index.ts +10 -0
- package/src/ddl/parse.ts +161 -0
- package/src/ddl/statements.ts +20 -0
- package/src/ddl/types.ts +60 -0
- package/src/ddl/validate.ts +47 -0
- package/src/designer/ColumnsEditor.tsx +204 -0
- package/src/designer/DesignerView.tsx +184 -0
- package/src/designer/ForeignKeysEditor.tsx +132 -0
- package/src/designer/IndexesEditor.tsx +63 -0
- package/src/designer/NamePicker.tsx +44 -0
- package/src/designer/OptionsEditor.tsx +101 -0
- package/src/designer/SqlPreview.tsx +56 -0
- package/src/designer/TableDesigner.tsx +186 -0
- package/src/designer/edit.ts +137 -0
- package/src/designer/editor-props.ts +13 -0
- package/src/designer/plan.ts +25 -0
- package/src/designer/recover.ts +20 -0
- package/src/designer/useReferenceColumns.ts +54 -0
- package/src/explorer/DatabaseExplorer.tsx +450 -0
- package/src/explorer/RowMenu.tsx +134 -0
- package/src/explorer/TableDialog.tsx +80 -0
- package/src/explorer/expansion.ts +47 -0
- package/src/explorer/table-statements.ts +18 -0
- package/src/explorer/tree.ts +453 -0
- package/src/explorer/use-explorer-loads.ts +157 -0
- package/src/explorer/use-stored-expansion.ts +66 -0
- package/src/grid/CellEditor.tsx +53 -0
- package/src/grid/CopyAsMenu.tsx +33 -0
- package/src/grid/DataGrid.tsx +871 -0
- package/src/grid/EnumPicker.tsx +108 -0
- package/src/grid/GridHeaderCell.tsx +243 -0
- package/src/grid/RecordView.tsx +293 -0
- package/src/grid/ValueDock.tsx +52 -0
- package/src/grid/aggregates.ts +94 -0
- package/src/grid/cell-range.ts +26 -0
- package/src/grid/column-layout.ts +71 -0
- package/src/grid/column-selection.ts +42 -0
- package/src/grid/column-view.ts +35 -0
- package/src/grid/copy-formats.ts +108 -0
- package/src/grid/display.ts +109 -0
- package/src/grid/edit-value.ts +60 -0
- package/src/grid/enum-type.ts +62 -0
- package/src/grid/focused-value.ts +23 -0
- package/src/grid/layout.ts +91 -0
- package/src/grid/navigation.ts +74 -0
- package/src/grid/row-selection.ts +35 -0
- package/src/grid/sort.ts +12 -0
- package/src/grid/types.ts +59 -0
- package/src/host/helper-path.ts +41 -0
- package/src/host/host.ts +456 -0
- package/src/host/index.ts +5 -0
- package/src/host/spawn.ts +124 -0
- package/src/host/types.ts +46 -0
- package/src/host/validate.ts +402 -0
- package/src/index.ts +37 -0
- package/src/locales/en.json +577 -0
- package/src/locales/nl.json +577 -0
- package/src/locales.ts +18 -0
- package/src/protocol/connection.ts +86 -0
- package/src/protocol/errors.ts +38 -0
- package/src/protocol/index.ts +33 -0
- package/src/protocol/messages.ts +168 -0
- package/src/protocol/schema.ts +76 -0
- package/src/protocol/values.ts +52 -0
- package/src/sql-split.ts +220 -0
- package/src/sql.ts +152 -0
- package/src/structure/StructureView.tsx +269 -0
- package/src/structure/structure-text.ts +4 -0
- package/src/table/CommandField.tsx +318 -0
- package/src/table/ImportDialog.tsx +168 -0
- package/src/table/TableStatusBar.tsx +91 -0
- package/src/table/TableToolbar.tsx +217 -0
- package/src/table/TableView.tsx +880 -0
- package/src/table/command-field.ts +285 -0
- package/src/table/grid-rows.ts +80 -0
- package/src/table/import-mapping.ts +37 -0
- package/src/table/layout-store.ts +62 -0
- package/src/table/paging.ts +33 -0
- package/src/table/pending.ts +143 -0
- package/src/table/references.ts +38 -0
- package/src/table/useLoaded.ts +58 -0
- package/src/table/useStoredLayout.ts +53 -0
- package/src/table/useTableTransfer.ts +187 -0
- package/src/testing/fake.ts +533 -0
- package/src/testing/index.ts +1 -0
- package/src/use-popup-press.ts +27 -0
- package/src/use-schema-change.ts +19 -0
- package/src/use-stable-callback.ts +12 -0
- package/src/value/ValuePanel.tsx +254 -0
- package/src/value/draft.ts +72 -0
- package/src/value/hex.ts +62 -0
- package/src/value/json.ts +74 -0
- package/src/value/views.ts +68 -0
- package/src/workbench/DatabaseWorkbench.tsx +348 -0
- package/src/workbench/DesignerTab.tsx +37 -0
- package/src/workbench/TableTab.tsx +27 -0
- package/src/workbench/tabs.ts +184 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { TableStructure } from '../protocol/index.ts';
|
|
2
|
+
import { type TableOptions } from './parse.ts';
|
|
3
|
+
export interface ColumnDraft {
|
|
4
|
+
readonly key: string;
|
|
5
|
+
readonly originalName: string | null;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly type: string;
|
|
8
|
+
readonly nullable: boolean;
|
|
9
|
+
readonly defaultValue: string | null;
|
|
10
|
+
readonly autoIncrement: boolean;
|
|
11
|
+
readonly comment: string;
|
|
12
|
+
readonly generated: boolean;
|
|
13
|
+
readonly generatedClause: string | null;
|
|
14
|
+
}
|
|
15
|
+
export interface IndexDraft {
|
|
16
|
+
readonly key: string;
|
|
17
|
+
readonly originalName: string | null;
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly columns: readonly string[];
|
|
20
|
+
readonly unique: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface ForeignKeyDraft {
|
|
23
|
+
readonly key: string;
|
|
24
|
+
readonly originalName: string | null;
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly columns: readonly string[];
|
|
27
|
+
readonly referencedSchema: string;
|
|
28
|
+
readonly referencedTable: string;
|
|
29
|
+
readonly referencedColumns: readonly string[];
|
|
30
|
+
readonly onUpdate: string | null;
|
|
31
|
+
readonly onDelete: string | null;
|
|
32
|
+
}
|
|
33
|
+
export interface TableDraft {
|
|
34
|
+
readonly originalName: string | null;
|
|
35
|
+
readonly name: string;
|
|
36
|
+
readonly columns: readonly ColumnDraft[];
|
|
37
|
+
readonly primaryKey: readonly string[];
|
|
38
|
+
readonly indexes: readonly IndexDraft[];
|
|
39
|
+
readonly foreignKeys: readonly ForeignKeyDraft[];
|
|
40
|
+
readonly options: TableOptions;
|
|
41
|
+
}
|
|
42
|
+
export declare const nextKey: () => string;
|
|
43
|
+
export declare const emptyColumn: (name?: string) => ColumnDraft;
|
|
44
|
+
export declare const emptyDraft: () => TableDraft;
|
|
45
|
+
export declare const isAutoIndex: (name: string) => boolean;
|
|
46
|
+
export declare const draftOf: (structure: TableStructure) => TableDraft;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { generatedClausesOf, NO_OPTIONS, optionsOf } from './parse.js';
|
|
2
|
+
let counter = 0;
|
|
3
|
+
/* A key no other item of this page has. */
|
|
4
|
+
export const nextKey = () => `new-${++counter}`;
|
|
5
|
+
export const emptyColumn = (name = '') => ({
|
|
6
|
+
key: nextKey(),
|
|
7
|
+
originalName: null,
|
|
8
|
+
name,
|
|
9
|
+
type: '',
|
|
10
|
+
nullable: true,
|
|
11
|
+
defaultValue: null,
|
|
12
|
+
autoIncrement: false,
|
|
13
|
+
comment: '',
|
|
14
|
+
generated: false,
|
|
15
|
+
generatedClause: null
|
|
16
|
+
});
|
|
17
|
+
export const emptyDraft = () => ({
|
|
18
|
+
originalName: null,
|
|
19
|
+
name: '',
|
|
20
|
+
columns: [],
|
|
21
|
+
primaryKey: [],
|
|
22
|
+
indexes: [],
|
|
23
|
+
foreignKeys: [],
|
|
24
|
+
options: NO_OPTIONS
|
|
25
|
+
});
|
|
26
|
+
/* SQLite names the index behind a `UNIQUE` or `PRIMARY KEY` constraint itself; such an index cannot be created or dropped by name. */
|
|
27
|
+
export const isAutoIndex = (name) => name.startsWith('sqlite_autoindex_');
|
|
28
|
+
const foreignKeyDraftOf = (foreignKey, at) => ({
|
|
29
|
+
key: `foreign-key:${at}`,
|
|
30
|
+
originalName: foreignKey.name ?? '',
|
|
31
|
+
name: foreignKey.name ?? '',
|
|
32
|
+
columns: foreignKey.columns,
|
|
33
|
+
referencedSchema: foreignKey.referencedSchema,
|
|
34
|
+
referencedTable: foreignKey.referencedTable,
|
|
35
|
+
referencedColumns: foreignKey.referencedColumns,
|
|
36
|
+
onUpdate: foreignKey.onUpdate,
|
|
37
|
+
onDelete: foreignKey.onDelete
|
|
38
|
+
});
|
|
39
|
+
/* The draft that changes nothing: what a structure already says. */
|
|
40
|
+
export const draftOf = (structure) => {
|
|
41
|
+
const clauses = generatedClausesOf(structure.ddl);
|
|
42
|
+
return {
|
|
43
|
+
originalName: structure.name,
|
|
44
|
+
name: structure.name,
|
|
45
|
+
columns: structure.columns.map((column) => ({
|
|
46
|
+
key: `column:${column.name}`,
|
|
47
|
+
originalName: column.name,
|
|
48
|
+
name: column.name,
|
|
49
|
+
type: column.type,
|
|
50
|
+
nullable: column.nullable,
|
|
51
|
+
defaultValue: column.defaultValue,
|
|
52
|
+
autoIncrement: column.autoIncrement,
|
|
53
|
+
comment: column.comment ?? '',
|
|
54
|
+
generated: column.generated,
|
|
55
|
+
generatedClause: column.generated ? (clauses.get(column.name) ?? null) : null
|
|
56
|
+
})),
|
|
57
|
+
primaryKey: structure.primaryKey,
|
|
58
|
+
indexes: structure.indexes
|
|
59
|
+
.filter((index) => !index.primary)
|
|
60
|
+
.map((index) => ({
|
|
61
|
+
key: `index:${index.name}`,
|
|
62
|
+
originalName: index.name,
|
|
63
|
+
name: index.name,
|
|
64
|
+
columns: index.columns,
|
|
65
|
+
unique: index.unique
|
|
66
|
+
})),
|
|
67
|
+
foreignKeys: structure.foreignKeys.map(foreignKeyDraftOf),
|
|
68
|
+
options: optionsOf(structure)
|
|
69
|
+
};
|
|
70
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { alterTableSql } from './alter.ts';
|
|
2
|
+
export { columnSpecSql, defaultClause } from './column.ts';
|
|
3
|
+
export { createIndexSql, createTableSql } from './create.ts';
|
|
4
|
+
export { atLeast, dialectOf, type Dialect } from './dialect.ts';
|
|
5
|
+
export { diffOf, dropsColumns, type TableDiff } from './diff.ts';
|
|
6
|
+
export { draftOf, emptyColumn, emptyDraft, isAutoIndex, nextKey, type ColumnDraft, type ForeignKeyDraft, type IndexDraft, type TableDraft } from './draft.ts';
|
|
7
|
+
export { NO_OPTIONS, optionsOf, type TableOptions } from './parse.ts';
|
|
8
|
+
export { dropIndexSql, dropTableSql, dropViewSql, renameTableSql, truncateTableSql } from './statements.ts';
|
|
9
|
+
export { FOREIGN_KEY_ACTIONS, MYSQL_CHARSETS, MYSQL_ENGINES, collationSuggestionsOf, typeSuggestionsOf } from './types.ts';
|
|
10
|
+
export { validateDraft, type DraftProblem } from './validate.ts';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { alterTableSql } from './alter.js';
|
|
2
|
+
export { columnSpecSql, defaultClause } from './column.js';
|
|
3
|
+
export { createIndexSql, createTableSql } from './create.js';
|
|
4
|
+
export { atLeast, dialectOf } from './dialect.js';
|
|
5
|
+
export { diffOf, dropsColumns } from './diff.js';
|
|
6
|
+
export { draftOf, emptyColumn, emptyDraft, isAutoIndex, nextKey } from './draft.js';
|
|
7
|
+
export { NO_OPTIONS, optionsOf } from './parse.js';
|
|
8
|
+
export { dropIndexSql, dropTableSql, dropViewSql, renameTableSql, truncateTableSql } from './statements.js';
|
|
9
|
+
export { FOREIGN_KEY_ACTIONS, MYSQL_CHARSETS, MYSQL_ENGINES, collationSuggestionsOf, typeSuggestionsOf } from './types.js';
|
|
10
|
+
export { validateDraft } from './validate.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TableStructure } from '../protocol/index.ts';
|
|
2
|
+
export interface Definition {
|
|
3
|
+
readonly items: readonly string[];
|
|
4
|
+
readonly tail: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const definitionOf: (ddl: string | null) => Definition | null;
|
|
7
|
+
export declare const generatedClausesOf: (ddl: string | null) => ReadonlyMap<string, string>;
|
|
8
|
+
export interface TableOptions {
|
|
9
|
+
readonly engine: string;
|
|
10
|
+
readonly charset: string;
|
|
11
|
+
readonly collation: string;
|
|
12
|
+
readonly comment: string;
|
|
13
|
+
readonly withoutRowid: boolean;
|
|
14
|
+
readonly strict: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare const NO_OPTIONS: TableOptions;
|
|
17
|
+
export declare const optionsOf: (structure: Pick<TableStructure, 'ddl'>) => TableOptions;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
const CLOSERS = { "'": "'", '"': '"', '`': '`', '[': ']' };
|
|
2
|
+
/* The characters of SQL that are not inside a quote, for a scan that has to ignore a comma in a default or a comment. */
|
|
3
|
+
function* unquoted(text, backslash) {
|
|
4
|
+
let depth = 0;
|
|
5
|
+
let closer = null;
|
|
6
|
+
for (let i = 0; i < text.length; i++) {
|
|
7
|
+
const char = text[i];
|
|
8
|
+
if (closer !== null) {
|
|
9
|
+
if (backslash && char === '\\' && closer === "'") {
|
|
10
|
+
i++;
|
|
11
|
+
}
|
|
12
|
+
else if (char === closer) {
|
|
13
|
+
if (closer !== ']' && text[i + 1] === closer) {
|
|
14
|
+
i++;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
closer = null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (char in CLOSERS) {
|
|
23
|
+
closer = CLOSERS[char];
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (char === ')') {
|
|
27
|
+
depth--;
|
|
28
|
+
}
|
|
29
|
+
yield { index: i, char, depth };
|
|
30
|
+
if (char === '(') {
|
|
31
|
+
depth++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/* Splits a `CREATE TABLE` into its definitions and its table options, or `null` when it holds no parenthesis. */
|
|
36
|
+
export const definitionOf = (ddl) => {
|
|
37
|
+
if (ddl === null) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
// Only a MySQL statement reads a backslash as an escape, and only a MySQL statement quotes with backticks.
|
|
41
|
+
const backslash = ddl.includes('`');
|
|
42
|
+
let open = -1;
|
|
43
|
+
let close = -1;
|
|
44
|
+
const commas = [];
|
|
45
|
+
for (const { index, char, depth } of unquoted(ddl, backslash)) {
|
|
46
|
+
if (open < 0) {
|
|
47
|
+
if (char === '(' && depth === 0) {
|
|
48
|
+
open = index;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (close < 0) {
|
|
52
|
+
if (char === ')' && depth === 0) {
|
|
53
|
+
close = index;
|
|
54
|
+
}
|
|
55
|
+
else if (char === ',' && depth === 1) {
|
|
56
|
+
commas.push(index);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (open < 0 || close < 0) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const bounds = [open, ...commas, close];
|
|
64
|
+
return {
|
|
65
|
+
items: bounds.slice(1).map((end, at) => ddl.slice(bounds[at] + 1, end).trim()),
|
|
66
|
+
tail: ddl.slice(close + 1)
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
/* The name a definition starts with, unquoted. */
|
|
70
|
+
const leadingName = (item) => {
|
|
71
|
+
const mark = item[0];
|
|
72
|
+
if (mark === '`' || mark === '"') {
|
|
73
|
+
const end = item.indexOf(mark, 1);
|
|
74
|
+
return end < 0 ? null : item.slice(1, end).replaceAll(mark + mark, mark);
|
|
75
|
+
}
|
|
76
|
+
if (mark === '[') {
|
|
77
|
+
const end = item.indexOf(']');
|
|
78
|
+
return end < 0 ? null : item.slice(1, end);
|
|
79
|
+
}
|
|
80
|
+
return /^[^\s(]+/.exec(item)?.[0] ?? null;
|
|
81
|
+
};
|
|
82
|
+
const AS_OPEN = /\b(?:GENERATED\s+ALWAYS\s+)?AS\s*\(/i;
|
|
83
|
+
const AS_SUFFIX = /^\s*(?:VIRTUAL|STORED|PERSISTENT)\b/i;
|
|
84
|
+
/* The `GENERATED ALWAYS AS (...) STORED` clause of a column definition, or `null` when it has none. */
|
|
85
|
+
const generatedClauseOf = (item) => {
|
|
86
|
+
// A default or a comment may hold the words, so the strings are blanked before the search.
|
|
87
|
+
const masked = item.replace(/'(?:[^']|'')*'/g, (text) => ' '.repeat(text.length));
|
|
88
|
+
const start = AS_OPEN.exec(masked);
|
|
89
|
+
if (start === null) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const open = start.index + start[0].length - 1;
|
|
93
|
+
let end = -1;
|
|
94
|
+
for (const { index, char, depth } of unquoted(masked.slice(open), false)) {
|
|
95
|
+
if (char === ')' && depth === 0) {
|
|
96
|
+
end = open + index + 1;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (end < 0) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const suffix = AS_SUFFIX.exec(masked.slice(end));
|
|
104
|
+
return item.slice(start.index, end + (suffix?.[0].length ?? 0)).trim();
|
|
105
|
+
};
|
|
106
|
+
/* The generated clause of every generated column in a table's DDL, by column name. */
|
|
107
|
+
export const generatedClausesOf = (ddl) => {
|
|
108
|
+
const clauses = new Map();
|
|
109
|
+
for (const item of definitionOf(ddl)?.items ?? []) {
|
|
110
|
+
const name = leadingName(item);
|
|
111
|
+
const clause = name === null ? null : generatedClauseOf(item);
|
|
112
|
+
if (name !== null && clause !== null) {
|
|
113
|
+
clauses.set(name, clause);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return clauses;
|
|
117
|
+
};
|
|
118
|
+
export const NO_OPTIONS = { engine: '', charset: '', collation: '', comment: '', withoutRowid: false, strict: false };
|
|
119
|
+
export const optionsOf = (structure) => {
|
|
120
|
+
const tail = definitionOf(structure.ddl)?.tail ?? '';
|
|
121
|
+
const word = (pattern) => pattern.exec(tail)?.[1] ?? '';
|
|
122
|
+
const comment = /\bCOMMENT\s*=?\s*'((?:[^'\\]|''|\\.)*)'/i.exec(tail)?.[1] ?? '';
|
|
123
|
+
return {
|
|
124
|
+
engine: word(/\bENGINE\s*=\s*(\w+)/i),
|
|
125
|
+
charset: word(/\b(?:DEFAULT\s+)?(?:CHARSET|CHARACTER\s+SET)\s*=?\s*(\w+)/i),
|
|
126
|
+
collation: word(/\bCOLLATE\s*=?\s*(\w+)/i),
|
|
127
|
+
comment: comment.replaceAll("''", "'").replace(/\\(.)/g, '$1'),
|
|
128
|
+
withoutRowid: /\bWITHOUT\s+ROWID\b/i.test(tail),
|
|
129
|
+
strict: /\bSTRICT\b/i.test(tail)
|
|
130
|
+
};
|
|
131
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Dialect } from './dialect.ts';
|
|
2
|
+
export declare const dropTableSql: (dialect: Dialect, schema: string, table: string) => string;
|
|
3
|
+
export declare const dropViewSql: (dialect: Dialect, schema: string, view: string) => string;
|
|
4
|
+
export declare const truncateTableSql: (dialect: Dialect, schema: string, table: string) => string;
|
|
5
|
+
export declare const renameTableSql: (dialect: Dialect, schema: string, from: string, to: string) => string;
|
|
6
|
+
export declare const dropIndexSql: (dialect: Dialect, schema: string, table: string, index: string) => string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { quote, sqliteSchemaPrefix, tableName } from './dialect.js';
|
|
2
|
+
export const dropTableSql = (dialect, schema, table) => `DROP TABLE ${tableName(dialect, schema, table)}`;
|
|
3
|
+
export const dropViewSql = (dialect, schema, view) => `DROP VIEW ${tableName(dialect, schema, view)}`;
|
|
4
|
+
/* SQLite has no `TRUNCATE`; deleting every row is what it does instead. */
|
|
5
|
+
export const truncateTableSql = (dialect, schema, table) => dialect.engine === 'sqlite' ? `DELETE FROM ${tableName(dialect, schema, table)}` : `TRUNCATE TABLE ${tableName(dialect, schema, table)}`;
|
|
6
|
+
export const renameTableSql = (dialect, schema, from, to) => dialect.engine === 'sqlite'
|
|
7
|
+
? `ALTER TABLE ${tableName(dialect, schema, from)} RENAME TO ${quote(dialect, to)}`
|
|
8
|
+
: `RENAME TABLE ${tableName(dialect, schema, from)} TO ${tableName(dialect, schema, to)}`;
|
|
9
|
+
/* MySQL drops an index from the table it is on; SQLite names it alone, in its schema. */
|
|
10
|
+
export const dropIndexSql = (dialect, schema, table, index) => dialect.engine === 'sqlite'
|
|
11
|
+
? `DROP INDEX ${sqliteSchemaPrefix(dialect, schema)}${quote(dialect, index)}`
|
|
12
|
+
: `DROP INDEX ${quote(dialect, index)} ON ${tableName(dialect, schema, table)}`;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Dialect } from './dialect.ts';
|
|
2
|
+
export declare const typeSuggestionsOf: (dialect: Dialect) => readonly string[];
|
|
3
|
+
export declare const MYSQL_ENGINES: readonly ['InnoDB', 'MyISAM', 'MEMORY', 'ARCHIVE', 'CSV'];
|
|
4
|
+
export declare const MYSQL_CHARSETS: readonly ['utf8mb4', 'utf8mb3', 'latin1', 'ascii', 'binary'];
|
|
5
|
+
export declare const collationSuggestionsOf: (charset: string) => readonly string[];
|
|
6
|
+
export declare const FOREIGN_KEY_ACTIONS: readonly ['CASCADE', 'SET NULL', 'RESTRICT', 'NO ACTION', 'SET DEFAULT'];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const SQLITE_TYPES = ['INTEGER', 'TEXT', 'REAL', 'BLOB', 'NUMERIC'];
|
|
2
|
+
const MYSQL_TYPES = [
|
|
3
|
+
'tinyint',
|
|
4
|
+
'smallint',
|
|
5
|
+
'mediumint',
|
|
6
|
+
'int',
|
|
7
|
+
'bigint',
|
|
8
|
+
'bigint unsigned',
|
|
9
|
+
'decimal(10,2)',
|
|
10
|
+
'float',
|
|
11
|
+
'double',
|
|
12
|
+
'bit(1)',
|
|
13
|
+
'char(36)',
|
|
14
|
+
'varchar(255)',
|
|
15
|
+
'varchar(64)',
|
|
16
|
+
'tinytext',
|
|
17
|
+
'text',
|
|
18
|
+
'mediumtext',
|
|
19
|
+
'longtext',
|
|
20
|
+
'binary(16)',
|
|
21
|
+
'varbinary(255)',
|
|
22
|
+
'tinyblob',
|
|
23
|
+
'blob',
|
|
24
|
+
'mediumblob',
|
|
25
|
+
'longblob',
|
|
26
|
+
'date',
|
|
27
|
+
'time',
|
|
28
|
+
'datetime',
|
|
29
|
+
'datetime(6)',
|
|
30
|
+
'timestamp',
|
|
31
|
+
'timestamp(6)',
|
|
32
|
+
'year',
|
|
33
|
+
'json',
|
|
34
|
+
"enum('a','b')",
|
|
35
|
+
"set('a','b')"
|
|
36
|
+
];
|
|
37
|
+
/* The types a type field offers; any other text is still allowed, since both engines accept more than a list holds. */
|
|
38
|
+
export const typeSuggestionsOf = (dialect) => (dialect.engine === 'sqlite' ? SQLITE_TYPES : MYSQL_TYPES);
|
|
39
|
+
export const MYSQL_ENGINES = ['InnoDB', 'MyISAM', 'MEMORY', 'ARCHIVE', 'CSV'];
|
|
40
|
+
export const MYSQL_CHARSETS = ['utf8mb4', 'utf8mb3', 'latin1', 'ascii', 'binary'];
|
|
41
|
+
/* Collations to offer for a character set, which is the family it is named after. */
|
|
42
|
+
export const collationSuggestionsOf = (charset) => {
|
|
43
|
+
if (charset === '') {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
if (charset === 'binary') {
|
|
47
|
+
return ['binary'];
|
|
48
|
+
}
|
|
49
|
+
const suggestions = [`${charset}_general_ci`, `${charset}_unicode_ci`, `${charset}_bin`];
|
|
50
|
+
return charset === 'utf8mb4' ? ['utf8mb4_0900_ai_ci', 'utf8mb4_unicode_520_ci', ...suggestions] : suggestions;
|
|
51
|
+
};
|
|
52
|
+
export const FOREIGN_KEY_ACTIONS = ['CASCADE', 'SET NULL', 'RESTRICT', 'NO ACTION', 'SET DEFAULT'];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Dialect } from './dialect.ts';
|
|
2
|
+
import type { TableDraft } from './draft.ts';
|
|
3
|
+
export interface DraftProblem {
|
|
4
|
+
readonly code: 'tableName' | 'noColumns' | 'columnName' | 'columnDuplicate' | 'columnType' | 'indexName' | 'indexColumns' | 'foreignKey';
|
|
5
|
+
readonly subject: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const validateDraft: (dialect: Dialect, draft: TableDraft) => DraftProblem[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const validateDraft = (dialect, draft) => {
|
|
2
|
+
const problems = [];
|
|
3
|
+
if (draft.name.trim() === '') {
|
|
4
|
+
problems.push({ code: 'tableName', subject: '' });
|
|
5
|
+
}
|
|
6
|
+
if (draft.columns.length === 0) {
|
|
7
|
+
problems.push({ code: 'noColumns', subject: '' });
|
|
8
|
+
}
|
|
9
|
+
const seen = new Set();
|
|
10
|
+
for (const column of draft.columns) {
|
|
11
|
+
const folded = column.name.toLowerCase();
|
|
12
|
+
if (column.name.trim() === '') {
|
|
13
|
+
problems.push({ code: 'columnName', subject: '' });
|
|
14
|
+
}
|
|
15
|
+
else if (seen.has(folded)) {
|
|
16
|
+
problems.push({ code: 'columnDuplicate', subject: column.name });
|
|
17
|
+
}
|
|
18
|
+
seen.add(folded);
|
|
19
|
+
if (dialect.engine === 'mysql' && column.type.trim() === '' && column.name.trim() !== '') {
|
|
20
|
+
problems.push({ code: 'columnType', subject: column.name });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
for (const index of draft.indexes) {
|
|
24
|
+
if (index.columns.length === 0) {
|
|
25
|
+
problems.push({ code: 'indexColumns', subject: index.name });
|
|
26
|
+
}
|
|
27
|
+
if (dialect.engine === 'sqlite' && index.name.trim() === '') {
|
|
28
|
+
problems.push({ code: 'indexName', subject: '' });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const foreignKey of draft.foreignKeys) {
|
|
32
|
+
const complete = foreignKey.columns.length > 0 && foreignKey.referencedTable !== '' && foreignKey.referencedColumns.length === foreignKey.columns.length;
|
|
33
|
+
if (!complete) {
|
|
34
|
+
problems.push({ code: 'foreignKey', subject: foreignKey.name });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return problems;
|
|
38
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId } from 'react';
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import { ArrowDown, ArrowUp, KeyRound, Plus, Trash2 } from 'lucide-react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { Button, Checkbox, EmptyState, Icon, IconButton, Input, Pill } from '@adecore/ui';
|
|
7
|
+
import { formatNumber } from '@adecore/ui/format';
|
|
8
|
+
import { typeSuggestionsOf } from '../ddl/index.js';
|
|
9
|
+
import { addColumn, moveColumn, patchColumn, removeColumn, togglePrimaryKey } from './edit.js';
|
|
10
|
+
const TH = 'sticky top-0 z-10 h-7 bg-surface px-2 text-left text-xs font-medium whitespace-nowrap text-text-faint';
|
|
11
|
+
const TD = 'border-t border-border-soft px-2 py-1 align-middle';
|
|
12
|
+
/* The columns of the table as rows to edit: name, type, null, default, key, comment and where each one sits. */
|
|
13
|
+
export function ColumnsEditor({ draft, dialect, disabled, onChange, className, ref }) {
|
|
14
|
+
const { t } = useTranslation('database');
|
|
15
|
+
const listId = useId();
|
|
16
|
+
const mysql = dialect.engine === 'mysql';
|
|
17
|
+
const defaultType = typeSuggestionsOf(dialect)[0];
|
|
18
|
+
return (_jsxs("div", { ref: ref, className: clsx('flex min-h-0 flex-col', className), children: [draft.columns.length === 0 ? (_jsx(EmptyState, { children: t('designer.columns.empty') })) : (_jsxs("div", { children: [_jsx("datalist", { id: listId, children: typeSuggestionsOf(dialect).map((type) => (_jsx("option", { value: type }, type))) }), _jsxs("table", { className: "w-full border-collapse", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", className: TH, children: _jsx("span", { className: "sr-only", children: t('designer.columns.order') }) }), _jsx("th", { scope: "col", className: TH, children: t('designer.columns.name') }), _jsx("th", { scope: "col", className: TH, children: t('designer.columns.type') }), _jsx("th", { scope: "col", className: clsx(TH, 'text-center'), children: t('designer.columns.notNull') }), _jsx("th", { scope: "col", className: TH, children: t('designer.columns.default') }), mysql && (_jsx("th", { scope: "col", className: clsx(TH, 'text-center'), children: t('designer.columns.autoIncrement') })), _jsx("th", { scope: "col", className: TH, children: t('designer.columns.primaryKey') }), mysql && (_jsx("th", { scope: "col", className: TH, children: t('designer.columns.comment') })), _jsx("th", { scope: "col", className: TH, children: _jsx("span", { className: "sr-only", children: t('designer.columns.remove') }) })] }) }), _jsx("tbody", { children: draft.columns.map((column, at) => {
|
|
19
|
+
const keyAt = draft.primaryKey.indexOf(column.name);
|
|
20
|
+
const locked = disabled || column.generated;
|
|
21
|
+
const patch = (change) => onChange(patchColumn(draft, column.key, change));
|
|
22
|
+
return (_jsxs("tr", { children: [_jsx("td", { className: clsx(TD, 'whitespace-nowrap'), children: _jsxs("span", { className: "flex", children: [_jsx(IconButton, { icon: ArrowUp, size: "xs", label: t('designer.columns.moveUp'), disabled: disabled || at === 0, onClick: () => onChange(moveColumn(draft, column.key, -1)) }), _jsx(IconButton, { icon: ArrowDown, size: "xs", label: t('designer.columns.moveDown'), disabled: disabled || at === draft.columns.length - 1, onClick: () => onChange(moveColumn(draft, column.key, 1)) })] }) }), _jsx("td", { className: clsx(TD, 'min-w-40'), children: _jsx(Input, { size: "sm", className: "w-full", "aria-label": t('designer.columns.name'), spellCheck: false, value: column.name, disabled: locked, onChange: (event) => patch({ name: event.target.value }) }) }), _jsx("td", { className: clsx(TD, 'min-w-36'), children: _jsx(Input, { size: "sm", mono: true, className: "w-full", "aria-label": t('designer.columns.type'), list: listId, spellCheck: false, value: column.type, disabled: locked, onChange: (event) => patch({ type: event.target.value }) }) }), _jsx("td", { className: TD, children: _jsx(Checkbox, { className: "mx-auto", label: t('designer.columns.notNullFor', { name: column.name }), checked: !column.nullable, disabled: locked, onCheckedChange: (checked) => patch({ nullable: !checked }) }) }), _jsx("td", { className: clsx(TD, 'min-w-36'), children: column.generated ? (_jsx(Pill, { mono: true, children: column.generatedClause ?? t('designer.columns.generated') })) : (_jsx(Input, { size: "sm", mono: true, className: "w-full", "aria-label": t('designer.columns.default'), placeholder: t('designer.columns.noDefault'), spellCheck: false, value: column.defaultValue ?? '', disabled: disabled, onChange: (event) => patch({ defaultValue: event.target.value === '' ? null : event.target.value }) })) }), mysql && (_jsx("td", { className: TD, children: _jsx(Checkbox, { className: "mx-auto", label: t('designer.columns.autoIncrementFor', { name: column.name }), checked: column.autoIncrement, disabled: locked, onCheckedChange: (checked) => patch({ autoIncrement: checked }) }) })), _jsx("td", { className: clsx(TD, 'whitespace-nowrap'), children: _jsxs("span", { className: "flex items-center gap-1", children: [_jsx(IconButton, { icon: KeyRound, size: "xs", label: keyAt < 0 ? t('designer.columns.addToKey') : t('designer.columns.removeFromKey'), "aria-pressed": keyAt >= 0, disabled: locked, onClick: () => onChange(togglePrimaryKey(draft, column.key)) }), keyAt >= 0 && draft.primaryKey.length > 1 && (_jsx("span", { className: "text-xs text-text-muted", children: formatNumber(keyAt + 1) }))] }) }), mysql && (_jsx("td", { className: clsx(TD, 'min-w-40'), children: _jsx(Input, { size: "sm", className: "w-full", "aria-label": t('designer.columns.comment'), value: column.comment, disabled: locked, onChange: (event) => patch({ comment: event.target.value }) }) })), _jsx("td", { className: TD, children: _jsx(IconButton, { icon: Trash2, size: "xs", label: t('designer.columns.remove'), disabled: disabled, onClick: () => onChange(removeColumn(draft, column.key)) }) })] }, column.key));
|
|
23
|
+
}) })] })] })), _jsx("div", { className: "shrink-0 border-t border-border-soft px-3 py-2", children: _jsxs(Button, { variant: "secondary", size: "sm", disabled: disabled, onClick: () => onChange(addColumn(draft, defaultType)), children: [_jsx(Icon, { icon: Plus, size: 12 }), t('designer.columns.add')] }) })] }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Ref } from 'react';
|
|
2
|
+
import type { Dialect, TableDraft } from '../ddl/index.ts';
|
|
3
|
+
import { type Plan } from './plan.ts';
|
|
4
|
+
export interface DesignerViewProps {
|
|
5
|
+
dialect: Dialect;
|
|
6
|
+
schema: string;
|
|
7
|
+
draft: TableDraft;
|
|
8
|
+
isNew: boolean;
|
|
9
|
+
changed: boolean;
|
|
10
|
+
tables: readonly string[];
|
|
11
|
+
plan: Plan;
|
|
12
|
+
readOnlyReason: string | null;
|
|
13
|
+
failure: string | null;
|
|
14
|
+
confirming: boolean;
|
|
15
|
+
referenceColumns(table: string): readonly string[] | null;
|
|
16
|
+
onChange(draft: TableDraft): void;
|
|
17
|
+
onRequestApply(): void;
|
|
18
|
+
onConfirm(): Promise<void>;
|
|
19
|
+
onCancelConfirm(): void;
|
|
20
|
+
onRevert(): void;
|
|
21
|
+
onDismissFailure(): void;
|
|
22
|
+
className?: string;
|
|
23
|
+
ref?: Ref<HTMLDivElement>;
|
|
24
|
+
}
|
|
25
|
+
export declare function DesignerView({ dialect, schema, draft, isNew, changed, tables, plan, readOnlyReason, failure, confirming, referenceColumns, onChange, onRequestApply, onConfirm, onCancelConfirm, onRevert, onDismissFailure, className, ref }: DesignerViewProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { CircleAlert, Lock, Undo2 } from 'lucide-react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { Banner, Button, DisabledReason, Field, Icon, Input, PromptDialog, Tabs, isApplePlatform, matchesShortcut, shortcut } from '@adecore/ui';
|
|
6
|
+
import { formatNumber } from '@adecore/ui/format';
|
|
7
|
+
import { ColumnsEditor } from './ColumnsEditor.js';
|
|
8
|
+
import { ForeignKeysEditor } from './ForeignKeysEditor.js';
|
|
9
|
+
import { IndexesEditor } from './IndexesEditor.js';
|
|
10
|
+
import { OptionsEditor } from './OptionsEditor.js';
|
|
11
|
+
import { renameTable } from './edit.js';
|
|
12
|
+
import { scriptOf } from './plan.js';
|
|
13
|
+
import { SqlPreview } from './SqlPreview.js';
|
|
14
|
+
const APPLY_SHORTCUT = /* @__PURE__ */ shortcut('Mod+S');
|
|
15
|
+
/* The designer with everything it needs handed in: the name, the four tabs, what Apply would run and the confirmation before it does. */
|
|
16
|
+
export function DesignerView({ dialect, schema, draft, isNew, changed, tables, plan, readOnlyReason, failure, confirming, referenceColumns, onChange, onRequestApply, onConfirm, onCancelConfirm, onRevert, onDismissFailure, className, ref }) {
|
|
17
|
+
const { t } = useTranslation('database');
|
|
18
|
+
const disabled = readOnlyReason !== null;
|
|
19
|
+
const applyReason = readOnlyReason ?? (plan.problems.length > 0 ? t('designer.fixProblems') : plan.statements.length === 0 ? t('designer.noChanges') : null);
|
|
20
|
+
function handleKeyDown(event) {
|
|
21
|
+
if (matchesShortcut(APPLY_SHORTCUT, event.nativeEvent, isApplePlatform())) {
|
|
22
|
+
event.preventDefault();
|
|
23
|
+
if (applyReason === null) {
|
|
24
|
+
onRequestApply();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return (_jsxs("div", { ref: ref, className: clsx('flex min-h-0 min-w-0 flex-col', className), onKeyDown: handleKeyDown, children: [_jsxs("div", { className: "flex shrink-0 items-end gap-3 border-b border-border-soft px-3 py-2", children: [_jsx(Field, { label: t('designer.tableName'), className: "w-80", children: _jsx(Input, { mono: true, spellCheck: false, placeholder: t('designer.tableNamePlaceholder'), value: draft.name, disabled: disabled, onChange: (event) => onChange(renameTable(draft, event.target.value)) }) }), _jsxs("div", { className: "ml-auto flex items-center gap-2", children: [_jsxs(Button, { variant: "secondary", disabled: !changed, onClick: onRevert, children: [_jsx(Icon, { icon: Undo2, size: 12 }), t('designer.revert')] }), _jsx(DisabledReason, { reason: applyReason, children: _jsx("span", { className: "inline-flex", children: _jsx(Button, { variant: "primary", disabled: applyReason !== null, onClick: onRequestApply, children: isNew ? t('designer.create') : t('designer.apply') }) }) })] })] }), readOnlyReason !== null && (_jsx("div", { className: "shrink-0 px-3 pt-3", children: _jsx(Banner, { icon: Lock, tone: "neutral", className: "w-full", message: readOnlyReason }) })), failure !== null && (_jsx("div", { className: "shrink-0 px-3 pt-3", children: _jsx(Banner, { icon: CircleAlert, tone: "error", className: "w-full", message: failure, children: _jsx(Button, { variant: "secondary", size: "sm", onClick: onDismissFailure, children: t('designer.dismiss') }) }) })), _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('designer.tabs.columns'), _jsx(Tabs.Count, { value: draft.columns.length })] }), _jsxs(Tabs.Tab, { value: "indexes", children: [t('designer.tabs.indexes'), _jsx(Tabs.Count, { value: draft.indexes.length })] }), _jsxs(Tabs.Tab, { value: "foreignKeys", children: [t('designer.tabs.foreignKeys'), _jsx(Tabs.Count, { value: draft.foreignKeys.length })] }), _jsx(Tabs.Tab, { value: "options", children: t('designer.tabs.options') })] }), _jsx(Tabs.Panel, { value: "columns", className: "min-h-0 grow overflow-auto", children: _jsx(ColumnsEditor, { draft: draft, dialect: dialect, disabled: disabled, onChange: onChange }) }), _jsx(Tabs.Panel, { value: "indexes", className: "min-h-0 grow overflow-auto", children: _jsx(IndexesEditor, { draft: draft, dialect: dialect, disabled: disabled, onChange: onChange }) }), _jsx(Tabs.Panel, { value: "foreignKeys", className: "min-h-0 grow overflow-auto", children: _jsx(ForeignKeysEditor, { draft: draft, dialect: dialect, disabled: disabled, schema: schema, tables: tables, referenceColumns: referenceColumns, onChange: onChange }) }), _jsx(Tabs.Panel, { value: "options", className: "min-h-0 grow overflow-auto", children: _jsx(OptionsEditor, { draft: draft, dialect: dialect, disabled: disabled, onChange: onChange }) })] }), _jsx(SqlPreview, { plan: plan, className: "shrink-0" }), _jsxs(PromptDialog, { open: confirming, danger: plan.destructive, title: t('designer.confirm.title', { count: plan.statements.length, formatted: formatNumber(plan.statements.length) }), description: isNew ? t('designer.confirm.create', { table: draft.name }) : t('designer.confirm.alter', { table: draft.originalName }), confirmLabel: isNew ? t('designer.create') : t('designer.apply'), confirmBusyLabel: t('designer.applying'), onConfirm: onConfirm, onOpenChange: (open) => {
|
|
29
|
+
if (!open) {
|
|
30
|
+
onCancelConfirm();
|
|
31
|
+
}
|
|
32
|
+
}, children: [plan.destructive && _jsx("p", { className: "mt-3 text-xs text-status-error", children: t('designer.confirm.destructive') }), _jsx("pre", { className: "mt-3 max-h-64 overflow-auto rounded-md bg-surface-sunken p-2 font-mono text-code whitespace-pre text-text select-text", children: scriptOf(plan.statements) })] })] }));
|
|
33
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EditorProps } from './editor-props.ts';
|
|
2
|
+
export interface ForeignKeysEditorProps extends EditorProps {
|
|
3
|
+
schema: string;
|
|
4
|
+
tables: readonly string[];
|
|
5
|
+
referenceColumns(table: string): readonly string[] | null;
|
|
6
|
+
}
|
|
7
|
+
export declare function ForeignKeysEditor({ draft, schema, tables, referenceColumns, disabled, onChange, className, ref }: ForeignKeysEditorProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { Plus, Trash2 } from 'lucide-react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { Button, EmptyState, Field, Icon, IconButton, Input, Select, Spinner } from '@adecore/ui';
|
|
6
|
+
import { FOREIGN_KEY_ACTIONS } from '../ddl/index.js';
|
|
7
|
+
import { NamePicker } from './NamePicker.js';
|
|
8
|
+
import { addForeignKey, patchForeignKey, removeForeignKey, setReferencedTable, toggleForeignKeyColumn } from './edit.js';
|
|
9
|
+
/* The engine's own behavior, which a key has without a clause. */
|
|
10
|
+
const DEFAULT_ACTION = 'default';
|
|
11
|
+
/* The foreign keys of the table. Each points at a table of the same schema, picked from the list, and at columns of that table. */
|
|
12
|
+
export function ForeignKeysEditor({ draft, schema, tables, referenceColumns, disabled, onChange, className, ref }) {
|
|
13
|
+
const { t } = useTranslation('database');
|
|
14
|
+
const columns = draft.columns.map((column) => column.name);
|
|
15
|
+
const actions = [
|
|
16
|
+
{ value: DEFAULT_ACTION, label: t('designer.foreignKeys.engineDefault') },
|
|
17
|
+
...FOREIGN_KEY_ACTIONS.map((action) => ({ value: action, label: action }))
|
|
18
|
+
];
|
|
19
|
+
return (_jsxs("div", { ref: ref, className: clsx('flex min-h-0 flex-col', className), children: [_jsxs("div", { children: [draft.foreignKeys.length === 0 && _jsx(EmptyState, { children: t('designer.foreignKeys.empty') }), draft.foreignKeys.map((foreignKey) => {
|
|
20
|
+
const referenced = foreignKey.referencedTable === '' ? [] : referenceColumns(foreignKey.referencedTable);
|
|
21
|
+
const choices = tables.includes(foreignKey.referencedTable) || foreignKey.referencedTable === '' ? tables : [...tables, foreignKey.referencedTable];
|
|
22
|
+
return (_jsxs("div", { className: "flex flex-col gap-3 border-b border-border-soft px-3 py-3", children: [_jsxs("div", { className: "flex items-end gap-3", children: [_jsx(Field, { label: t('designer.foreignKeys.name'), className: "w-56", children: _jsx(Input, { size: "sm", spellCheck: false, value: foreignKey.name, disabled: disabled, onChange: (event) => onChange(patchForeignKey(draft, foreignKey.key, { name: event.target.value })) }) }), _jsx(Field, { label: t('designer.foreignKeys.referencedTable'), group: true, className: "w-56", children: _jsx(Select, { size: "sm", className: "w-full", label: t('designer.foreignKeys.referencedTable'), placeholder: t('designer.foreignKeys.pickTable'), value: foreignKey.referencedTable === '' ? null : foreignKey.referencedTable, disabled: disabled, items: choices.map((table) => ({ value: table, label: table })), onValueChange: (table) => onChange(setReferencedTable(draft, foreignKey.key, schema, table)) }) }), _jsx(Field, { label: t('designer.foreignKeys.onUpdate'), group: true, className: "w-40", children: _jsx(Select, { size: "sm", className: "w-full", label: t('designer.foreignKeys.onUpdate'), value: foreignKey.onUpdate ?? DEFAULT_ACTION, disabled: disabled, items: actions, onValueChange: (action) => onChange(patchForeignKey(draft, foreignKey.key, { onUpdate: action === DEFAULT_ACTION ? null : action })) }) }), _jsx(Field, { label: t('designer.foreignKeys.onDelete'), group: true, className: "w-40", children: _jsx(Select, { size: "sm", className: "w-full", label: t('designer.foreignKeys.onDelete'), value: foreignKey.onDelete ?? DEFAULT_ACTION, disabled: disabled, items: actions, onValueChange: (action) => onChange(patchForeignKey(draft, foreignKey.key, { onDelete: action === DEFAULT_ACTION ? null : action })) }) }), _jsx(IconButton, { icon: Trash2, size: "xs", className: "mb-1", label: t('designer.foreignKeys.remove'), disabled: disabled, onClick: () => onChange(removeForeignKey(draft, foreignKey.key)) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Field, { label: t('designer.foreignKeys.columns'), group: true, children: _jsx(NamePicker, { label: t('designer.foreignKeys.columns'), options: columns, value: foreignKey.columns, disabled: disabled, onToggle: (name) => onChange(toggleForeignKeyColumn(draft, foreignKey.key, 'columns', name)) }) }), _jsx(Field, { label: t('designer.foreignKeys.referencedColumns'), group: true, children: referenced === null ? (_jsx(Spinner, { size: 14, label: t('designer.foreignKeys.loadingColumns') })) : (_jsx(NamePicker, { label: t('designer.foreignKeys.referencedColumns'), options: referenced, value: foreignKey.referencedColumns, disabled: disabled, onToggle: (name) => onChange(toggleForeignKeyColumn(draft, foreignKey.key, 'referencedColumns', name)) })) })] })] }, foreignKey.key));
|
|
23
|
+
})] }), _jsx("div", { className: "shrink-0 px-3 py-2", children: _jsxs(Button, { variant: "secondary", size: "sm", disabled: disabled, onClick: () => onChange(addForeignKey(draft, schema)), children: [_jsx(Icon, { icon: Plus, size: 12 }), t('designer.foreignKeys.add')] }) })] }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { Plus, Trash2 } from 'lucide-react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { Button, Checkbox, EmptyState, Icon, IconButton, Input } from '@adecore/ui';
|
|
6
|
+
import { NamePicker } from './NamePicker.js';
|
|
7
|
+
import { addIndex, patchIndex, removeIndex, toggleIndexColumn } from './edit.js';
|
|
8
|
+
/* The indexes of the table other than its primary key, each over columns picked from the draft. */
|
|
9
|
+
export function IndexesEditor({ draft, disabled, onChange, className, ref }) {
|
|
10
|
+
const { t } = useTranslation('database');
|
|
11
|
+
const columns = draft.columns.map((column) => column.name);
|
|
12
|
+
return (_jsxs("div", { ref: ref, className: clsx('flex min-h-0 flex-col', className), children: [_jsxs("div", { children: [draft.indexes.length === 0 && _jsx(EmptyState, { children: t('designer.indexes.empty') }), draft.indexes.map((index) => (_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 border-b border-border-soft px-3 py-2", children: [_jsx(Input, { size: "sm", className: "w-56", "aria-label": t('designer.indexes.name'), spellCheck: false, value: index.name, disabled: disabled, onChange: (event) => onChange(patchIndex(draft, index.key, { name: event.target.value })) }), _jsxs("label", { className: "flex items-center gap-2 text-xs text-text-muted", children: [_jsx(Checkbox, { label: t('designer.indexes.unique'), checked: index.unique, disabled: disabled, onCheckedChange: (checked) => onChange(patchIndex(draft, index.key, { unique: checked })) }), t('designer.indexes.unique')] }), _jsx(NamePicker, { className: "min-w-0 grow", label: t('designer.indexes.columns'), options: columns, value: index.columns, disabled: disabled, onToggle: (name) => onChange(toggleIndexColumn(draft, index.key, name)) }), _jsx(IconButton, { icon: Trash2, size: "xs", label: t('designer.indexes.remove'), disabled: disabled, onClick: () => onChange(removeIndex(draft, index.key)) })] }, index.key)))] }), _jsx("div", { className: "shrink-0 px-3 py-2", children: _jsxs(Button, { variant: "secondary", size: "sm", disabled: disabled, onClick: () => onChange(addIndex(draft)), children: [_jsx(Icon, { icon: Plus, size: 12 }), t('designer.indexes.add')] }) })] }));
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Ref } from 'react';
|
|
2
|
+
export interface NamePickerProps {
|
|
3
|
+
options: readonly string[];
|
|
4
|
+
value: readonly string[];
|
|
5
|
+
onToggle(name: string): void;
|
|
6
|
+
label: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
className?: string;
|
|
9
|
+
ref?: Ref<HTMLDivElement>;
|
|
10
|
+
}
|
|
11
|
+
export declare function NamePicker({ options, value, onToggle, label, disabled, className, ref }: NamePickerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Button } from '@adecore/ui';
|
|
5
|
+
import { formatNumber } from '@adecore/ui/format';
|
|
6
|
+
/* Picks some of a few names, such as the columns of an index, and numbers the picks once there is more than one. */
|
|
7
|
+
export function NamePicker({ options, value, onToggle, label, disabled = false, className, ref }) {
|
|
8
|
+
const { t } = useTranslation('database');
|
|
9
|
+
return (_jsxs("div", { ref: ref, role: "group", "aria-label": label, className: clsx('flex flex-wrap items-center gap-1', className), children: [options.length === 0 && _jsx("span", { className: "text-xs text-text-faint", children: t('designer.noColumnsToPick') }), options.map((name) => {
|
|
10
|
+
const position = value.indexOf(name);
|
|
11
|
+
return (_jsxs(Button, { size: "xs", variant: position < 0 ? 'ghost' : 'secondary', "aria-pressed": position >= 0, disabled: disabled, onClick: () => onToggle(name), children: [position >= 0 && value.length > 1 && _jsx("span", { className: "text-text-faint", children: formatNumber(position + 1) }), name] }, name));
|
|
12
|
+
})] }));
|
|
13
|
+
}
|