mirror of
https://github.com/chartdb/chartdb.git
synced 2025-10-23 07:11:56 +00:00
Compare commits
3 Commits
e34740c6d1
...
619cdc564c
Author | SHA1 | Date | |
---|---|---|---|
|
619cdc564c | ||
|
459698b5d0 | ||
|
7ad0e7712d |
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,5 +1,25 @@
|
||||
# Changelog
|
||||
|
||||
## [1.17.0](https://github.com/chartdb/chartdb/compare/v1.16.0...v1.17.0) (2025-10-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* create relationships on canvas modal ([#946](https://github.com/chartdb/chartdb/issues/946)) ([34475ad](https://github.com/chartdb/chartdb/commit/34475add32f11323589ef092ccf2a8e9152ff272))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add auto-increment field detection in smart-query import ([#935](https://github.com/chartdb/chartdb/issues/935)) ([57b3b87](https://github.com/chartdb/chartdb/commit/57b3b8777fd0a445abf0ba6603faab612d469d5c))
|
||||
* add rels export dbml ([#937](https://github.com/chartdb/chartdb/issues/937)) ([c3c646b](https://github.com/chartdb/chartdb/commit/c3c646bf7cbb1328f4b2eb85c9a7e929f0fcd3b9))
|
||||
* add support for parsing default values in DBML ([#948](https://github.com/chartdb/chartdb/issues/948)) ([459698b](https://github.com/chartdb/chartdb/commit/459698b5d0a1ff23a3719c2e55e4ab2e2384c4fe))
|
||||
* add timestampz and int as datatypes to postgres ([#940](https://github.com/chartdb/chartdb/issues/940)) ([b15bc94](https://github.com/chartdb/chartdb/commit/b15bc945acb96d7cb3832b3b1b607dfcaef9e5ca))
|
||||
* auto-enter edit mode when creating new tables from canvas ([#943](https://github.com/chartdb/chartdb/issues/943)) ([bcd8aa9](https://github.com/chartdb/chartdb/commit/bcd8aa9378aa563f40a2b6802cc503be4c882356))
|
||||
* dbml diff fields types preview ([#934](https://github.com/chartdb/chartdb/issues/934)) ([bb03309](https://github.com/chartdb/chartdb/commit/bb033091b1f64b888822be1423a80f16f5314f6b))
|
||||
* exit table edit on area click ([#945](https://github.com/chartdb/chartdb/issues/945)) ([38fedce](https://github.com/chartdb/chartdb/commit/38fedcec0c10ea2b3f0b7fc92ca1f5ac9e540389))
|
||||
* manipulate schema directly from the canvas ([#947](https://github.com/chartdb/chartdb/issues/947)) ([7ad0e77](https://github.com/chartdb/chartdb/commit/7ad0e7712de975a23b2a337dc0a4a7fb4b122bd1))
|
||||
* prevent text input glitch when editing table field names ([#944](https://github.com/chartdb/chartdb/issues/944)) ([498655e](https://github.com/chartdb/chartdb/commit/498655e7b77e57eaf641ba86263ce1ef60b93e16))
|
||||
|
||||
## [1.16.0](https://github.com/chartdb/chartdb/compare/v1.15.1...v1.16.0) (2025-09-24)
|
||||
|
||||
|
||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "chartdb",
|
||||
"version": "1.16.0",
|
||||
"version": "1.17.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "chartdb",
|
||||
"version": "1.16.0",
|
||||
"version": "1.17.0",
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^0.0.51",
|
||||
"@dbml/core": "^3.13.9",
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chartdb",
|
||||
"private": true,
|
||||
"version": "1.16.0",
|
||||
"version": "1.17.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
@@ -13,6 +13,55 @@ import { exportSQLite } from './export-per-type/sqlite';
|
||||
import { exportMySQL } from './export-per-type/mysql';
|
||||
import { escapeSQLComment } from './export-per-type/common';
|
||||
|
||||
// Function to format default values with proper quoting
|
||||
const formatDefaultValue = (value: string): string => {
|
||||
const trimmed = value.trim();
|
||||
|
||||
// SQL keywords and function-like keywords that don't need quotes
|
||||
const keywords = [
|
||||
'TRUE',
|
||||
'FALSE',
|
||||
'NULL',
|
||||
'CURRENT_TIMESTAMP',
|
||||
'CURRENT_DATE',
|
||||
'CURRENT_TIME',
|
||||
'NOW',
|
||||
'GETDATE',
|
||||
'NEWID',
|
||||
'UUID',
|
||||
];
|
||||
if (keywords.includes(trimmed.toUpperCase())) {
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
// Function calls (contain parentheses) don't need quotes
|
||||
if (trimmed.includes('(') && trimmed.includes(')')) {
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
// Numbers don't need quotes
|
||||
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
// Already quoted strings - keep as is
|
||||
if (
|
||||
(trimmed.startsWith("'") && trimmed.endsWith("'")) ||
|
||||
(trimmed.startsWith('"') && trimmed.endsWith('"'))
|
||||
) {
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
// Check if it's a simple identifier (alphanumeric, no spaces) that might be a currency or enum
|
||||
// These typically don't have spaces and are short (< 10 chars)
|
||||
if (/^[A-Z][A-Z0-9_]*$/i.test(trimmed) && trimmed.length <= 10) {
|
||||
return trimmed; // Treat as unquoted identifier (e.g., EUR, USD)
|
||||
}
|
||||
|
||||
// Everything else needs to be quoted and escaped
|
||||
return `'${trimmed.replace(/'/g, "''")}'`;
|
||||
};
|
||||
|
||||
// Function to simplify verbose data type names
|
||||
const simplifyDataType = (typeName: string): string => {
|
||||
const typeMap: Record<string, string> = {};
|
||||
@@ -391,7 +440,9 @@ export const exportBaseSQL = ({
|
||||
}
|
||||
}
|
||||
|
||||
sqlScript += ` DEFAULT ${fieldDefault}`;
|
||||
// Format default value with proper quoting
|
||||
const formattedDefault = formatDefaultValue(fieldDefault);
|
||||
sqlScript += ` DEFAULT ${formattedDefault}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -295,4 +295,51 @@ describe('DBML Import cases', () => {
|
||||
it('should handle case 2 - tables with relationships', async () => {
|
||||
await testDBMLImportCase('2');
|
||||
});
|
||||
|
||||
it('should handle table with default values', async () => {
|
||||
const dbmlContent = `Table "public"."products" {
|
||||
"id" bigint [pk, not null]
|
||||
"name" varchar(255) [not null]
|
||||
"price" decimal(10,2) [not null, default: 0]
|
||||
"is_active" boolean [not null, default: true]
|
||||
"status" varchar(50) [not null, default: "deprecated"]
|
||||
"description" varchar(100) [default: \`complex "value" with quotes\`]
|
||||
"created_at" timestamp [not null, default: "now()"]
|
||||
|
||||
Indexes {
|
||||
(name) [name: "idx_products_name"]
|
||||
}
|
||||
}`;
|
||||
|
||||
const result = await importDBMLToDiagram(dbmlContent, {
|
||||
databaseType: DatabaseType.POSTGRESQL,
|
||||
});
|
||||
|
||||
expect(result.tables).toHaveLength(1);
|
||||
const table = result.tables![0];
|
||||
expect(table.name).toBe('products');
|
||||
expect(table.fields).toHaveLength(7);
|
||||
|
||||
// Check numeric default (0)
|
||||
const priceField = table.fields.find((f) => f.name === 'price');
|
||||
expect(priceField?.default).toBe('0');
|
||||
|
||||
// Check boolean default (true)
|
||||
const isActiveField = table.fields.find((f) => f.name === 'is_active');
|
||||
expect(isActiveField?.default).toBe('true');
|
||||
|
||||
// Check string default with all quotes removed
|
||||
const statusField = table.fields.find((f) => f.name === 'status');
|
||||
expect(statusField?.default).toBe('deprecated');
|
||||
|
||||
// Check backtick string - all quotes removed
|
||||
const descField = table.fields.find((f) => f.name === 'description');
|
||||
expect(descField?.default).toBe('complex value with quotes');
|
||||
|
||||
// Check function default with all quotes removed
|
||||
const createdAtField = table.fields.find(
|
||||
(f) => f.name === 'created_at'
|
||||
);
|
||||
expect(createdAtField?.default).toBe('now()');
|
||||
});
|
||||
});
|
||||
|
@@ -89,6 +89,7 @@ interface DBMLField {
|
||||
precision?: number | null;
|
||||
scale?: number | null;
|
||||
note?: string | { value: string } | null;
|
||||
default?: string | null;
|
||||
}
|
||||
|
||||
interface DBMLIndexColumn {
|
||||
@@ -334,6 +335,20 @@ export const importDBMLToDiagram = async (
|
||||
schema: schemaName,
|
||||
note: table.note,
|
||||
fields: table.fields.map((field): DBMLField => {
|
||||
// Extract default value and remove all quotes
|
||||
let defaultValue: string | undefined;
|
||||
if (
|
||||
field.dbdefault !== undefined &&
|
||||
field.dbdefault !== null
|
||||
) {
|
||||
const rawDefault = String(
|
||||
field.dbdefault.value
|
||||
);
|
||||
// Remove ALL quotes (single, double, backticks) to clean the value
|
||||
// The SQL export layer will handle adding proper quotes when needed
|
||||
defaultValue = rawDefault.replace(/['"`]/g, '');
|
||||
}
|
||||
|
||||
return {
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
@@ -342,6 +357,7 @@ export const importDBMLToDiagram = async (
|
||||
not_null: field.not_null,
|
||||
increment: field.increment,
|
||||
note: field.note,
|
||||
default: defaultValue,
|
||||
...getFieldExtraAttributes(field, allEnums),
|
||||
} satisfies DBMLField;
|
||||
}),
|
||||
@@ -488,6 +504,7 @@ export const importDBMLToDiagram = async (
|
||||
precision: field.precision,
|
||||
scale: field.scale,
|
||||
...(fieldComment ? { comments: fieldComment } : {}),
|
||||
...(field.default ? { default: field.default } : {}),
|
||||
};
|
||||
});
|
||||
|
||||
|
@@ -14,14 +14,14 @@ import { Table, Workflow, Group, View } from 'lucide-react';
|
||||
import { useDiagramFilter } from '@/context/diagram-filter-context/use-diagram-filter';
|
||||
import { useLocalConfig } from '@/hooks/use-local-config';
|
||||
import { useCanvas } from '@/hooks/use-canvas';
|
||||
import type { DBTable } from '@/lib/domain';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
|
||||
export const CanvasContextMenu: React.FC<React.PropsWithChildren> = ({
|
||||
children,
|
||||
}) => {
|
||||
const { createTable, readonly, createArea } = useChartDB();
|
||||
const { createTable, readonly, createArea, databaseType } = useChartDB();
|
||||
const { schemasDisplayed } = useDiagramFilter();
|
||||
const { openCreateRelationshipDialog, openTableSchemaDialog } = useDialog();
|
||||
const { openCreateRelationshipDialog } = useDialog();
|
||||
const { screenToFlowPosition } = useReactFlow();
|
||||
const { t } = useTranslation();
|
||||
const { showDBViews } = useLocalConfig();
|
||||
@@ -36,30 +36,23 @@ export const CanvasContextMenu: React.FC<React.PropsWithChildren> = ({
|
||||
y: event.clientY,
|
||||
});
|
||||
|
||||
let newTable: DBTable | null = null;
|
||||
// Auto-select schema with priority: default schema > first displayed schema > undefined
|
||||
let schema: string | undefined = undefined;
|
||||
if (schemasDisplayed.length > 0) {
|
||||
const defaultSchemaName = defaultSchemas[databaseType];
|
||||
const defaultSchemaInList = schemasDisplayed.find(
|
||||
(s) => s.name === defaultSchemaName
|
||||
);
|
||||
schema = defaultSchemaInList
|
||||
? defaultSchemaInList.name
|
||||
: schemasDisplayed[0]?.name;
|
||||
}
|
||||
|
||||
if (schemasDisplayed.length > 1) {
|
||||
openTableSchemaDialog({
|
||||
onConfirm: async ({ schema }) => {
|
||||
newTable = await createTable({
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
schema: schema.name,
|
||||
});
|
||||
},
|
||||
schemas: schemasDisplayed,
|
||||
});
|
||||
} else {
|
||||
const schema =
|
||||
schemasDisplayed?.length === 1
|
||||
? schemasDisplayed[0]?.name
|
||||
: undefined;
|
||||
newTable = await createTable({
|
||||
const newTable = await createTable({
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
schema,
|
||||
});
|
||||
}
|
||||
|
||||
if (newTable) {
|
||||
setEditTableModeTable({ tableId: newTable.id });
|
||||
@@ -68,9 +61,9 @@ export const CanvasContextMenu: React.FC<React.PropsWithChildren> = ({
|
||||
[
|
||||
createTable,
|
||||
screenToFlowPosition,
|
||||
openTableSchemaDialog,
|
||||
schemasDisplayed,
|
||||
setEditTableModeTable,
|
||||
databaseType,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -81,32 +74,24 @@ export const CanvasContextMenu: React.FC<React.PropsWithChildren> = ({
|
||||
y: event.clientY,
|
||||
});
|
||||
|
||||
let newView: DBTable | null = null;
|
||||
// Auto-select schema with priority: default schema > first displayed schema > undefined
|
||||
let schema: string | undefined = undefined;
|
||||
if (schemasDisplayed.length > 0) {
|
||||
const defaultSchemaName = defaultSchemas[databaseType];
|
||||
const defaultSchemaInList = schemasDisplayed.find(
|
||||
(s) => s.name === defaultSchemaName
|
||||
);
|
||||
schema = defaultSchemaInList
|
||||
? defaultSchemaInList.name
|
||||
: schemasDisplayed[0]?.name;
|
||||
}
|
||||
|
||||
if (schemasDisplayed.length > 1) {
|
||||
openTableSchemaDialog({
|
||||
onConfirm: async ({ schema }) => {
|
||||
newView = await createTable({
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
schema: schema.name,
|
||||
isView: true,
|
||||
});
|
||||
},
|
||||
schemas: schemasDisplayed,
|
||||
});
|
||||
} else {
|
||||
const schema =
|
||||
schemasDisplayed?.length === 1
|
||||
? schemasDisplayed[0]?.name
|
||||
: undefined;
|
||||
newView = await createTable({
|
||||
const newView = await createTable({
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
schema,
|
||||
isView: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (newView) {
|
||||
setEditTableModeTable({ tableId: newView.id });
|
||||
@@ -115,9 +100,9 @@ export const CanvasContextMenu: React.FC<React.PropsWithChildren> = ({
|
||||
[
|
||||
createTable,
|
||||
screenToFlowPosition,
|
||||
openTableSchemaDialog,
|
||||
schemasDisplayed,
|
||||
setEditTableModeTable,
|
||||
databaseType,
|
||||
]
|
||||
);
|
||||
|
||||
|
@@ -1,7 +1,13 @@
|
||||
import { Input } from '@/components/input/input';
|
||||
import type { DBTable } from '@/lib/domain';
|
||||
import { FileType2, X } from 'lucide-react';
|
||||
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
||||
import { FileType2, X, SquarePlus } from 'lucide-react';
|
||||
import React, {
|
||||
useEffect,
|
||||
useState,
|
||||
useRef,
|
||||
useCallback,
|
||||
useMemo,
|
||||
} from 'react';
|
||||
import { TableEditModeField } from './table-edit-mode-field';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ScrollArea } from '@/components/scroll-area/scroll-area';
|
||||
@@ -11,6 +17,14 @@ import { Separator } from '@/components/separator/separator';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import { useUpdateTable } from '@/hooks/use-update-table';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SelectBox } from '@/components/select-box/select-box';
|
||||
import type { SelectBoxOption } from '@/components/select-box/select-box';
|
||||
import {
|
||||
databasesWithSchemas,
|
||||
schemaNameToSchemaId,
|
||||
} from '@/lib/domain/db-schema';
|
||||
import type { DBSchema } from '@/lib/domain/db-schema';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
|
||||
export interface TableEditModeProps {
|
||||
table: DBTable;
|
||||
@@ -25,7 +39,8 @@ export const TableEditMode: React.FC<TableEditModeProps> = React.memo(
|
||||
const scrollAreaRef = useRef<HTMLDivElement>(null);
|
||||
const fieldRefs = useRef<Map<string, HTMLDivElement>>(new Map());
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const { createField, updateTable } = useChartDB();
|
||||
const { createField, updateTable, schemas, databaseType } =
|
||||
useChartDB();
|
||||
const { t } = useTranslation();
|
||||
const { tableName, handleTableNameChange } = useUpdateTable(table);
|
||||
const [focusFieldId, setFocusFieldId] = useState<string | undefined>(
|
||||
@@ -33,6 +48,39 @@ export const TableEditMode: React.FC<TableEditModeProps> = React.memo(
|
||||
);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// Schema-related state
|
||||
const [isCreatingNewSchema, setIsCreatingNewSchema] = useState(false);
|
||||
const [newSchemaName, setNewSchemaName] = useState('');
|
||||
const [selectedSchemaId, setSelectedSchemaId] = useState<string>(() =>
|
||||
table.schema ? schemaNameToSchemaId(table.schema) : ''
|
||||
);
|
||||
|
||||
// Sync selectedSchemaId when table.schema changes
|
||||
useEffect(() => {
|
||||
setSelectedSchemaId(
|
||||
table.schema ? schemaNameToSchemaId(table.schema) : ''
|
||||
);
|
||||
}, [table.schema]);
|
||||
|
||||
const supportsSchemas = useMemo(
|
||||
() => databasesWithSchemas.includes(databaseType),
|
||||
[databaseType]
|
||||
);
|
||||
|
||||
const defaultSchemaName = useMemo(
|
||||
() => defaultSchemas?.[databaseType],
|
||||
[databaseType]
|
||||
);
|
||||
|
||||
const schemaOptions: SelectBoxOption[] = useMemo(
|
||||
() =>
|
||||
schemas.map((schema) => ({
|
||||
value: schema.id,
|
||||
label: schema.name,
|
||||
})),
|
||||
[schemas]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setFocusFieldId(focusFieldIdProp);
|
||||
if (!focusFieldIdProp) {
|
||||
@@ -115,6 +163,43 @@ export const TableEditMode: React.FC<TableEditModeProps> = React.memo(
|
||||
[updateTable, table.id]
|
||||
);
|
||||
|
||||
const handleSchemaChange = useCallback(
|
||||
(schemaId: string) => {
|
||||
const schema = schemas.find((s) => s.id === schemaId);
|
||||
if (schema) {
|
||||
updateTable(table.id, { schema: schema.name });
|
||||
setSelectedSchemaId(schemaId);
|
||||
}
|
||||
},
|
||||
[schemas, updateTable, table.id]
|
||||
);
|
||||
|
||||
const handleCreateNewSchema = useCallback(() => {
|
||||
if (newSchemaName.trim()) {
|
||||
const trimmedName = newSchemaName.trim();
|
||||
const newSchema: DBSchema = {
|
||||
id: schemaNameToSchemaId(trimmedName),
|
||||
name: trimmedName,
|
||||
tableCount: 0,
|
||||
};
|
||||
updateTable(table.id, { schema: newSchema.name });
|
||||
setSelectedSchemaId(newSchema.id);
|
||||
setIsCreatingNewSchema(false);
|
||||
setNewSchemaName('');
|
||||
}
|
||||
}, [newSchemaName, updateTable, table.id]);
|
||||
|
||||
const handleToggleSchemaMode = useCallback(() => {
|
||||
if (isCreatingNewSchema && newSchemaName.trim()) {
|
||||
// If we're leaving create mode with a value, create the schema
|
||||
handleCreateNewSchema();
|
||||
} else {
|
||||
// Otherwise just toggle modes
|
||||
setIsCreatingNewSchema(!isCreatingNewSchema);
|
||||
setNewSchemaName('');
|
||||
}
|
||||
}, [isCreatingNewSchema, newSchemaName, handleCreateNewSchema]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
@@ -134,18 +219,60 @@ export const TableEditMode: React.FC<TableEditModeProps> = React.memo(
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div
|
||||
className="h-2 rounded-t-[6px]"
|
||||
className="h-2 cursor-move rounded-t-[6px]"
|
||||
style={{ backgroundColor: color }}
|
||||
></div>
|
||||
<div className="group flex h-9 items-center justify-between gap-2 bg-slate-200 px-2 dark:bg-slate-900">
|
||||
<div className="group flex h-9 cursor-move items-center justify-between gap-2 bg-slate-200 px-2 dark:bg-slate-900">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2">
|
||||
<ColorPicker
|
||||
color={color}
|
||||
onChange={handleColorChange}
|
||||
disabled={table.isView}
|
||||
popoverOnMouseDown={(e) => e.stopPropagation()}
|
||||
popoverOnClick={(e) => e.stopPropagation()}
|
||||
{supportsSchemas && !isCreatingNewSchema && (
|
||||
<SelectBox
|
||||
options={schemaOptions}
|
||||
value={selectedSchemaId}
|
||||
onChange={(value) =>
|
||||
handleSchemaChange(value as string)
|
||||
}
|
||||
placeholder={
|
||||
defaultSchemaName || 'Select schema'
|
||||
}
|
||||
className="h-6 min-h-6 w-20 shrink-0 rounded-sm border-slate-600 bg-background py-0 pl-2 pr-0.5 text-sm"
|
||||
popoverClassName="w-[200px]"
|
||||
commandOnMouseDown={(e) => e.stopPropagation()}
|
||||
commandOnClick={(e) => e.stopPropagation()}
|
||||
footerButtons={
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-full justify-center rounded-none text-xs"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleToggleSchemaMode();
|
||||
}}
|
||||
>
|
||||
<SquarePlus className="!size-3.5" />
|
||||
Create new schema
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{supportsSchemas && isCreatingNewSchema && (
|
||||
<Input
|
||||
value={newSchemaName}
|
||||
onChange={(e) =>
|
||||
setNewSchemaName(e.target.value)
|
||||
}
|
||||
placeholder={`Enter schema name${defaultSchemaName ? ` (e.g. ${defaultSchemaName})` : ''}`}
|
||||
className="h-6 w-28 shrink-0 rounded-sm border-slate-600 bg-background text-sm"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleCreateNewSchema();
|
||||
} else if (e.key === 'Escape') {
|
||||
handleToggleSchemaMode();
|
||||
}
|
||||
}}
|
||||
onBlur={handleToggleSchemaMode}
|
||||
autoFocus
|
||||
/>
|
||||
)}
|
||||
<Input
|
||||
ref={inputRef}
|
||||
className="h-6 flex-1 rounded-sm border-slate-600 bg-background text-sm"
|
||||
@@ -179,7 +306,22 @@ export const TableEditMode: React.FC<TableEditModeProps> = React.memo(
|
||||
</ScrollArea>
|
||||
|
||||
<Separator />
|
||||
<div className="flex items-center justify-between p-2">
|
||||
<div className="flex cursor-move items-center justify-between p-2">
|
||||
<div className="flex items-center gap-2">
|
||||
{!table.isView ? (
|
||||
<>
|
||||
<ColorPicker
|
||||
color={color}
|
||||
onChange={handleColorChange}
|
||||
popoverOnMouseDown={(e) =>
|
||||
e.stopPropagation()
|
||||
}
|
||||
popoverOnClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 p-2 text-xs"
|
||||
@@ -188,6 +330,7 @@ export const TableEditMode: React.FC<TableEditModeProps> = React.memo(
|
||||
<FileType2 className="mr-1 h-4" />
|
||||
{t('side_panel.tables_section.table.add_field')}
|
||||
</Button>
|
||||
</div>
|
||||
<span className="text-xs font-medium text-muted-foreground">
|
||||
{table.fields.length}{' '}
|
||||
{t('side_panel.tables_section.table.fields')}
|
||||
|
Reference in New Issue
Block a user