Files
drawdb/src/components/EditorSidePanel/TablesTab/TableInfo.jsx
Karen Mkrtumyan a5718f8359 Refactor dragging logic (#523)
* refactor dragging logic

* Remove unnecessary undefined in TableInfo.jsx
2025-07-05 22:37:58 +04:00

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>
);
}