mirror of
https://github.com/drawdb-io/drawdb.git
synced 2025-07-18 10:11:24 +00:00
329 lines
9.8 KiB
JavaScript
329 lines
9.8 KiB
JavaScript
import { useState, useRef } from "react";
|
|
import {
|
|
Collapse,
|
|
Input,
|
|
TextArea,
|
|
Button,
|
|
Card,
|
|
Select,
|
|
} from "@douyinfe/semi-ui";
|
|
import ColorPicker from "../ColorPicker";
|
|
import { IconDeleteStroked } from "@douyinfe/semi-icons";
|
|
import { useDiagram, useSaveState, useUndoRedo } from "../../../hooks";
|
|
import { Action, ObjectType, State, DB } from "../../../data/constants";
|
|
import TableField from "./TableField";
|
|
import IndexDetails from "./IndexDetails";
|
|
import { useTranslation } from "react-i18next";
|
|
import { SortableList } from "../../SortableList/SortableList";
|
|
import { nanoid } from "nanoid";
|
|
|
|
export default function TableInfo({ data }) {
|
|
const { tables, database } = useDiagram();
|
|
const { t } = useTranslation();
|
|
const [indexActiveKey, setIndexActiveKey] = useState("");
|
|
const { deleteTable, updateTable, setTables } = useDiagram();
|
|
const { setUndoStack, setRedoStack } = useUndoRedo();
|
|
const { setSaveState } = useSaveState();
|
|
const [editField, setEditField] = useState({});
|
|
const initialColorRef = useRef(data.color);
|
|
|
|
const handleColorPick = (color) => {
|
|
setUndoStack((prev) => {
|
|
let undoColor = initialColorRef.current;
|
|
const lastColorChange = prev.findLast(
|
|
(e) =>
|
|
e.element === ObjectType.TABLE &&
|
|
e.tid === data.id &&
|
|
e.action === Action.EDIT &&
|
|
e.redo.color,
|
|
);
|
|
if (lastColorChange) {
|
|
undoColor = lastColorChange.redo.color;
|
|
}
|
|
|
|
if (color === undoColor) return prev;
|
|
|
|
const newStack = [
|
|
...prev,
|
|
{
|
|
action: Action.EDIT,
|
|
element: ObjectType.TABLE,
|
|
component: "self",
|
|
tid: data.id,
|
|
undo: { color: undoColor },
|
|
redo: { color: color },
|
|
message: t("edit_table", {
|
|
tableName: data.name,
|
|
extra: "[color]",
|
|
}),
|
|
},
|
|
];
|
|
return newStack;
|
|
});
|
|
setRedoStack([]);
|
|
};
|
|
|
|
const inheritedFieldNames =
|
|
Array.isArray(data.inherits) && data.inherits.length > 0
|
|
? data.inherits
|
|
.map((parentName) => {
|
|
const parent = tables.find((t) => t.name === parentName);
|
|
return parent ? parent.fields.map((f) => f.name) : [];
|
|
})
|
|
.flat()
|
|
: [];
|
|
|
|
return (
|
|
<div>
|
|
<div className="flex items-center mb-2.5">
|
|
<div className="text-md font-semibold break-keep">{t("name")}:</div>
|
|
<Input
|
|
value={data.name}
|
|
validateStatus={data.name.trim() === "" ? "error" : "default"}
|
|
placeholder={t("name")}
|
|
className="ms-2"
|
|
onChange={(value) => updateTable(data.id, { name: value })}
|
|
onFocus={(e) => setEditField({ name: e.target.value })}
|
|
onBlur={(e) => {
|
|
if (e.target.value === editField.name) return;
|
|
setUndoStack((prev) => [
|
|
...prev,
|
|
{
|
|
action: Action.EDIT,
|
|
element: ObjectType.TABLE,
|
|
component: "self",
|
|
tid: data.id,
|
|
undo: editField,
|
|
redo: { name: e.target.value },
|
|
message: t("edit_table", {
|
|
tableName: e.target.value,
|
|
extra: "[name]",
|
|
}),
|
|
},
|
|
]);
|
|
setRedoStack([]);
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<SortableList
|
|
items={data.fields}
|
|
keyPrefix={`table-${data.id}`}
|
|
onChange={(newFields) =>
|
|
setTables((prev) =>
|
|
prev.map((t) =>
|
|
t.id === data.id ? { ...t, fields: newFields } : t,
|
|
),
|
|
)
|
|
}
|
|
afterChange={() => setSaveState(State.SAVING)}
|
|
renderItem={(item, i) => (
|
|
<TableField
|
|
data={item}
|
|
tid={data.id}
|
|
index={i}
|
|
inherited={inheritedFieldNames.includes(item.name)}
|
|
/>
|
|
)}
|
|
/>
|
|
|
|
{database === DB.POSTGRES && (
|
|
<div className="mb-2">
|
|
<div className="text-md font-semibold break-keep">
|
|
{t("inherits")}:
|
|
</div>
|
|
<Select
|
|
multiple
|
|
value={data.inherits || []}
|
|
optionList={tables
|
|
.filter((t) => t.id !== data.id)
|
|
.map((t) => ({ label: t.name, value: t.name }))}
|
|
onChange={(value) => {
|
|
setUndoStack((prev) => [
|
|
...prev,
|
|
{
|
|
action: Action.EDIT,
|
|
element: ObjectType.TABLE,
|
|
component: "self",
|
|
tid: data.id,
|
|
undo: { inherits: data.inherits },
|
|
redo: { inherits: value },
|
|
message: t("edit_table", {
|
|
tableName: data.name,
|
|
extra: "[inherits]",
|
|
}),
|
|
},
|
|
]);
|
|
setRedoStack([]);
|
|
updateTable(data.id, { inherits: value });
|
|
}}
|
|
placeholder={t("inherits")}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{data.indices.length > 0 && (
|
|
<Card
|
|
bodyStyle={{ padding: "4px" }}
|
|
style={{ marginTop: "12px", marginBottom: "12px" }}
|
|
headerLine={false}
|
|
>
|
|
<Collapse
|
|
activeKey={indexActiveKey}
|
|
keepDOM={false}
|
|
lazyRender
|
|
onChange={(itemKey) => setIndexActiveKey(itemKey)}
|
|
accordion
|
|
>
|
|
<Collapse.Panel header={t("indices")} itemKey="1">
|
|
{data.indices.map((idx, k) => (
|
|
<IndexDetails
|
|
key={"index_" + k}
|
|
data={idx}
|
|
iid={k}
|
|
tid={data.id}
|
|
fields={data.fields.map((e) => ({
|
|
value: e.name,
|
|
label: e.name,
|
|
}))}
|
|
/>
|
|
))}
|
|
</Collapse.Panel>
|
|
</Collapse>
|
|
</Card>
|
|
)}
|
|
|
|
<Card
|
|
bodyStyle={{ padding: "4px" }}
|
|
style={{ marginTop: "12px", marginBottom: "12px" }}
|
|
headerLine={false}
|
|
>
|
|
<Collapse keepDOM={false} lazyRender>
|
|
<Collapse.Panel header={t("comment")} itemKey="1">
|
|
<TextArea
|
|
field="comment"
|
|
value={data.comment}
|
|
autosize
|
|
placeholder={t("comment")}
|
|
rows={1}
|
|
onChange={(value) =>
|
|
updateTable(data.id, { comment: value }, false)
|
|
}
|
|
onFocus={(e) => setEditField({ comment: e.target.value })}
|
|
onBlur={(e) => {
|
|
if (e.target.value === editField.comment) return;
|
|
setUndoStack((prev) => [
|
|
...prev,
|
|
{
|
|
action: Action.EDIT,
|
|
element: ObjectType.TABLE,
|
|
component: "self",
|
|
tid: data.id,
|
|
undo: editField,
|
|
redo: { comment: e.target.value },
|
|
message: t("edit_table", {
|
|
tableName: e.target.value,
|
|
extra: "[comment]",
|
|
}),
|
|
},
|
|
]);
|
|
setRedoStack([]);
|
|
}}
|
|
/>
|
|
</Collapse.Panel>
|
|
</Collapse>
|
|
</Card>
|
|
|
|
<div className="flex justify-between items-center gap-1 mb-2">
|
|
<ColorPicker
|
|
usePopover={true}
|
|
value={data.color}
|
|
onChange={(color) => updateTable(data.id, { color })}
|
|
onColorPick={(color) => handleColorPick(color)}
|
|
/>
|
|
<div className="flex gap-1">
|
|
<Button
|
|
block
|
|
onClick={() => {
|
|
setIndexActiveKey("1");
|
|
setUndoStack((prev) => [
|
|
...prev,
|
|
{
|
|
action: Action.EDIT,
|
|
element: ObjectType.TABLE,
|
|
component: "index_add",
|
|
tid: data.id,
|
|
message: t("edit_table", {
|
|
tableName: data.name,
|
|
extra: "[add index]",
|
|
}),
|
|
},
|
|
]);
|
|
setRedoStack([]);
|
|
updateTable(data.id, {
|
|
indices: [
|
|
...data.indices,
|
|
{
|
|
id: data.indices.length,
|
|
name: `${data.name}_index_${data.indices.length}`,
|
|
unique: false,
|
|
fields: [],
|
|
},
|
|
],
|
|
});
|
|
}}
|
|
>
|
|
{t("add_index")}
|
|
</Button>
|
|
<Button
|
|
onClick={() => {
|
|
const id = nanoid();
|
|
setUndoStack((prev) => [
|
|
...prev,
|
|
{
|
|
action: Action.EDIT,
|
|
element: ObjectType.TABLE,
|
|
component: "field_add",
|
|
tid: data.id,
|
|
fid: id,
|
|
message: t("edit_table", {
|
|
tableName: data.name,
|
|
extra: "[add field]",
|
|
}),
|
|
},
|
|
]);
|
|
setRedoStack([]);
|
|
updateTable(data.id, {
|
|
fields: [
|
|
...data.fields,
|
|
{
|
|
id,
|
|
name: "",
|
|
type: "",
|
|
default: "",
|
|
check: "",
|
|
primary: false,
|
|
unique: false,
|
|
notNull: false,
|
|
increment: false,
|
|
comment: "",
|
|
},
|
|
],
|
|
});
|
|
}}
|
|
block
|
|
>
|
|
{t("add_field")}
|
|
</Button>
|
|
<Button
|
|
icon={<IconDeleteStroked />}
|
|
type="danger"
|
|
onClick={() => deleteTable(data.id)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|