diff --git a/src/components/EditorHeader/ControlPanel.jsx b/src/components/EditorHeader/ControlPanel.jsx index 28b196da4..d02916955 100644 --- a/src/components/EditorHeader/ControlPanel.jsx +++ b/src/components/EditorHeader/ControlPanel.jsx @@ -165,7 +165,7 @@ export default function ControlPanel({ } else if (a.element === ObjectType.RELATIONSHIP) { deleteRelationship(a.data.relationship.id, false); } else if (a.element === ObjectType.TYPE) { - deleteType(types.length - 1, false); + deleteType(a.data.type.id, false); } else if (a.element === ObjectType.ENUM) { deleteEnum(enums.length - 1, false); } @@ -199,7 +199,7 @@ export default function ControlPanel({ } else if (a.element === ObjectType.AREA) { addArea(a.data, false); } else if (a.element === ObjectType.TYPE) { - addType({ id: a.id, ...a.data }, false); + addType(a.data, false); } else if (a.element === ObjectType.ENUM) { addEnum({ id: a.id, ...a.data }, false); } @@ -258,9 +258,12 @@ export default function ControlPanel({ updateRelationship(a.rid, a.undo); } else if (a.element === ObjectType.TYPE) { if (a.component === "field_add") { + const type = types.find((t, i) => + typeof a.tid === "number" ? i === a.tid : t.id === a.tid, + ); updateType(a.tid, { - fields: types[a.tid].fields.filter( - (_, i) => i !== types[a.tid].fields.length - 1, + fields: type.fields.filter((f, i) => + f.id ? f.id !== a.data.field.id : i !== type.fields.length - 1, ), }); } @@ -334,7 +337,7 @@ export default function ControlPanel({ } else if (a.element === ObjectType.RELATIONSHIP) { addRelationship(a.data, false); } else if (a.element === ObjectType.TYPE) { - addType(null, false); + addType(a.data, false); } else if (a.element === ObjectType.ENUM) { addEnum(null, false); } @@ -367,7 +370,7 @@ export default function ControlPanel({ } else if (a.element === ObjectType.AREA) { deleteArea(a.data.id, false); } else if (a.element === ObjectType.TYPE) { - deleteType(a.id, false); + deleteType(a.data.type.id, false); } else if (a.element === ObjectType.ENUM) { deleteEnum(a.id, false); } @@ -436,14 +439,11 @@ export default function ControlPanel({ updateRelationship(a.rid, a.redo); } else if (a.element === ObjectType.TYPE) { if (a.component === "field_add") { + const type = types.find((t, i) => + typeof a.tid === "number" ? i === a.tid : t.id === a.tid, + ); updateType(a.tid, { - fields: [ - ...types[a.tid].fields, - { - name: "", - type: "", - }, - ], + fields: [...type.fields, a.data.field], }); } else if (a.component === "field") { updateType(a.tid, { diff --git a/src/components/EditorSidePanel/TypesTab/TypeInfo.jsx b/src/components/EditorSidePanel/TypesTab/TypeInfo.jsx index f3c7337a5..cfbe75c29 100644 --- a/src/components/EditorSidePanel/TypesTab/TypeInfo.jsx +++ b/src/components/EditorSidePanel/TypesTab/TypeInfo.jsx @@ -13,6 +13,7 @@ import { IconDeleteStroked, IconPlus } from "@douyinfe/semi-icons"; import { useUndoRedo, useTypes, useDiagram, useLayout } from "../../../hooks"; import TypeField from "./TypeField"; import { useTranslation } from "react-i18next"; +import { nanoid } from "nanoid"; export default function TypeInfo({ index, data }) { const { layout } = useLayout(); @@ -22,8 +23,11 @@ export default function TypeInfo({ index, data }) { const [editField, setEditField] = useState({}); const { t } = useTranslation(); + // TODO: remove indexes, not a valid case after adding id to types + const typeId = data.id ?? index; + return ( -
+
@@ -41,7 +45,7 @@ export default function TypeInfo({ index, data }) { placeholder={t("name")} className="ms-2" onChange={(value) => { - updateType(index, { name: value }); + updateType(typeId, { name: value }); tables.forEach((table) => { table.fields.forEach((field) => { if (field.type.toLowerCase() === data.name.toLowerCase()) { @@ -71,7 +75,7 @@ export default function TypeInfo({ index, data }) { action: Action.EDIT, element: ObjectType.TYPE, component: "self", - tid: index, + tid: typeId, undo: editField, redo: { name: e.target.value }, updatedFields, @@ -103,7 +107,7 @@ export default function TypeInfo({ index, data }) { placeholder={t("comment")} rows={1} onChange={(value) => - updateType(index, { comment: value }, false) + updateType(typeId, { comment: value }, false) } onFocus={(e) => setEditField({ comment: e.target.value })} onBlur={(e) => { @@ -114,7 +118,7 @@ export default function TypeInfo({ index, data }) { action: Action.EDIT, element: ObjectType.TYPE, component: "self", - tid: index, + tid: typeId, undo: editField, redo: { comment: e.target.value }, message: t("edit_type", { @@ -135,13 +139,22 @@ export default function TypeInfo({ index, data }) { icon={} disabled={layout.readOnly} onClick={() => { + const newField = { + name: "", + type: "", + id: nanoid(), + }; setUndoStack((prev) => [ ...prev, { action: Action.EDIT, element: ObjectType.TYPE, component: "field_add", - tid: index, + data: { + field: newField, + index: data.fields.length, + }, + tid: typeId, message: t("edit_type", { typeName: data.name, extra: "[add field]", @@ -149,14 +162,8 @@ export default function TypeInfo({ index, data }) { }, ]); setRedoStack([]); - updateType(index, { - fields: [ - ...data.fields, - { - name: "", - type: "", - }, - ], + updateType(typeId, { + fields: [...data.fields, newField], }); }} block @@ -170,7 +177,7 @@ export default function TypeInfo({ index, data }) { type="danger" disabled={layout.readOnly} icon={} - onClick={() => deleteType(index)} + onClick={() => deleteType(typeId)} > {t("delete")} diff --git a/src/components/Workspace.jsx b/src/components/Workspace.jsx index 88f291beb..efe8bc9bd 100644 --- a/src/components/Workspace.jsx +++ b/src/components/Workspace.jsx @@ -26,6 +26,7 @@ import { databases } from "../data/databases"; import { isRtl } from "../i18n/utils/rtl"; import { useSearchParams } from "react-router-dom"; import { get, SHARE_FILENAME } from "../api/gists"; +import { nanoid } from "nanoid"; export const IdContext = createContext({ gistId: "", @@ -196,7 +197,23 @@ export default function WorkSpace() { setTasks(d.todos ?? []); setTransform({ pan: d.pan, zoom: d.zoom }); if (databases[database].hasTypes) { - setTypes(d.types ?? []); + if (d.types) { + setTypes( + d.types.map((t) => + t.id + ? t + : { + ...t, + id: nanoid(), + fields: t.fields.map((f) => + f.id ? f : { ...f, id: nanoid() }, + ), + }, + ), + ); + } else { + setTypes([]); + } } if (databases[database].hasEnums) { setEnums(d.enums ?? []); @@ -238,7 +255,23 @@ export default function WorkSpace() { setUndoStack([]); setRedoStack([]); if (databases[database].hasTypes) { - setTypes(diagram.types ?? []); + if (diagram.types) { + setTypes( + diagram.types.map((t) => + t.id + ? t + : { + ...t, + id: nanoid(), + fields: t.fields.map((f) => + f.id ? f : { ...f, id: nanoid() }, + ), + }, + ), + ); + } else { + setTypes([]); + } } if (databases[database].hasEnums) { setEnums(diagram.enums ?? []); @@ -277,7 +310,23 @@ export default function WorkSpace() { setUndoStack([]); setRedoStack([]); if (databases[database].hasTypes) { - setTypes(diagram.types ?? []); + if (diagram.types) { + setTypes( + diagram.types.map((t) => + t.id + ? t + : { + ...t, + id: nanoid(), + fields: t.fields.map((f) => + f.id ? f : { ...f, id: nanoid() }, + ), + }, + ), + ); + } else { + setTypes([]); + } } if (databases[database].hasEnums) { setEnums(diagram.enums ?? []); @@ -308,7 +357,23 @@ export default function WorkSpace() { setAreas(parsedDiagram.subjectAreas); setTransform(parsedDiagram.transform); if (databases[parsedDiagram.database].hasTypes) { - setTypes(parsedDiagram.types ?? []); + if (parsedDiagram.types) { + setTypes( + parsedDiagram.types.map((t) => + t.id + ? t + : { + ...t, + id: nanoid(), + fields: t.fields.map((f) => + f.id ? f : { ...f, id: nanoid() }, + ), + }, + ), + ); + } else { + setTypes([]); + } } if (databases[parsedDiagram.database].hasEnums) { setEnums(parsedDiagram.enums ?? []); diff --git a/src/context/TypesContext.jsx b/src/context/TypesContext.jsx index 24f78436e..4eb4ce8d5 100644 --- a/src/context/TypesContext.jsx +++ b/src/context/TypesContext.jsx @@ -3,6 +3,7 @@ import { Action, ObjectType } from "../data/constants"; import { useUndoRedo } from "../hooks"; import { Toast } from "@douyinfe/semi-ui"; import { useTranslation } from "react-i18next"; +import { nanoid } from "nanoid"; export const TypesContext = createContext(null); @@ -12,16 +13,18 @@ export default function TypesContextProvider({ children }) { const { setUndoStack, setRedoStack } = useUndoRedo(); const addType = (data, addToHistory = true) => { + const id = nanoid(); if (data) { setTypes((prev) => { const temp = prev.slice(); - temp.splice(data.id, 0, data); + temp.splice(data.index, 0, data.type); return temp; }); } else { setTypes((prev) => [ ...prev, { + id, name: `type_${prev.length}`, fields: [], comment: "", @@ -32,6 +35,15 @@ export default function TypesContextProvider({ children }) { setUndoStack((prev) => [ ...prev, { + data: { + index: types.length, + type: data?.type ?? { + id, + name: `type_${prev.length}`, + fields: [], + comment: "", + }, + }, action: Action.ADD, element: ObjectType.TYPE, message: t("add_type"), @@ -43,27 +55,35 @@ export default function TypesContextProvider({ children }) { const deleteType = (id, addToHistory = true) => { if (addToHistory) { + const deletedTypeIndex = types.findIndex((e, i) => + typeof id === "number" ? i === id : e.id === id, + ); Toast.success(t("type_deleted")); setUndoStack((prev) => [ ...prev, { action: Action.DELETE, element: ObjectType.TYPE, - id: id, - data: types[id], + data: { type: types[deletedTypeIndex], index: deletedTypeIndex }, message: t("delete_type", { - typeName: types[id].name, + typeName: types[deletedTypeIndex].name, }), }, ]); setRedoStack([]); } - setTypes((prev) => prev.filter((e, i) => i !== id)); + setTypes((prev) => + prev.filter((e, i) => (typeof id === "number" ? i !== id : e.id !== id)), + ); }; const updateType = (id, values) => { setTypes((prev) => - prev.map((e, i) => (i === id ? { ...e, ...values } : e)), + prev.map((item, index) => { + const isMatch = typeof id === "number" ? index === id : item.id === id; + + return isMatch ? { ...item, ...values } : item; + }), ); }; diff --git a/src/data/schemas.js b/src/data/schemas.js index 4955821da..79476fa3b 100644 --- a/src/data/schemas.js +++ b/src/data/schemas.js @@ -97,12 +97,14 @@ export const noteSchema = { export const typeSchema = { type: "object", properties: { + id: { type: ["string"] }, name: { type: "string" }, fields: { type: "array", items: { type: "object", properties: { + id: { type: ["string"] }, name: { type: "string" }, type: { type: "string" }, values: {