diff --git a/src/components/ColorPallete.jsx b/src/components/ColorPallete.jsx
new file mode 100644
index 0000000..b822d1c
--- /dev/null
+++ b/src/components/ColorPallete.jsx
@@ -0,0 +1,55 @@
+import { Button } from "@douyinfe/semi-ui";
+import { IconCheckboxTick } from "@douyinfe/semi-icons";
+import { tableThemes } from "../data/constants";
+
+export default function ColorPallete({
+ currentColor,
+ onClearColor,
+ onPickColor,
+}) {
+ return (
+
+
+
+
+
+ {tableThemes.slice(0, Math.ceil(tableThemes.length / 2)).map((c) => (
+
+ ))}
+
+
+ {tableThemes.slice(Math.ceil(tableThemes.length / 2)).map((c) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/EditorCanvas/Table.jsx b/src/components/EditorCanvas/Table.jsx
index 2d28f99..1b9fc4c 100644
--- a/src/components/EditorCanvas/Table.jsx
+++ b/src/components/EditorCanvas/Table.jsx
@@ -45,8 +45,13 @@ export default function Table(props) {
const [hoveredField, setHoveredField] = useState(-1);
const [editField, setEditField] = useState({});
const { layout } = useLayout();
- const { deleteTable, updateTable, updateField, setRelationships } =
- useTables();
+ const {
+ deleteTable,
+ updateTable,
+ updateField,
+ deleteField,
+ setRelationships,
+ } = useTables();
const { settings } = useSettings();
const { types } = useTypes();
const { setUndoStack, setRedoStack } = useUndoRedo();
@@ -1269,63 +1274,7 @@ export default function Table(props) {
backgroundColor: "#d42020",
}}
icon={}
- onClick={() => {
- setUndoStack((prev) => [
- ...prev,
- {
- action: Action.EDIT,
- element: ObjectType.TABLE,
- component: "field_delete",
- tid: props.tableData.id,
- data: fieldData,
- message: `Delete field`,
- },
- ]);
- setRedoStack([]);
- setRelationships((prev) =>
- prev
- .filter(
- (e) =>
- !(
- (e.startTableId === props.tableData.id &&
- e.startFieldId === index) ||
- (e.endTableId === props.tableData.id &&
- e.endFieldId === index)
- )
- )
- .map((e, i) => ({ ...e, id: i }))
- );
- setRelationships((prev) => {
- return prev.map((e) => {
- if (
- e.startTableId === props.tableData.id &&
- e.startFieldId > fieldData.id
- ) {
- return {
- ...e,
- startFieldId: e.startFieldId - 1,
- };
- }
- if (
- e.endTableId === props.tableData.id &&
- e.endFieldId > fieldData.id
- ) {
- return {
- ...e,
- endFieldId: e.endFieldId - 1,
- };
- }
- return e;
- });
- });
- updateTable(props.tableData.id, {
- fields: props.tableData.fields
- .filter((e) => e.id !== fieldData.id)
- .map((t, i) => {
- return { ...t, id: i };
- }),
- });
- }}
+ onClick={() => deleteField(fieldData, props.tableData.id)}
/>
) : (
diff --git a/src/components/EditorSidePanel/AreasOverview.jsx b/src/components/EditorSidePanel/AreasOverview.jsx
deleted file mode 100644
index d1a59e2..0000000
--- a/src/components/EditorSidePanel/AreasOverview.jsx
+++ /dev/null
@@ -1,227 +0,0 @@
-import { useState } from "react";
-import {
- Row,
- Col,
- AutoComplete,
- Button,
- Input,
- Popover,
- Toast,
-} from "@douyinfe/semi-ui";
-import {
- IconPlus,
- IconSearch,
- IconCheckboxTick,
- IconDeleteStroked,
-} from "@douyinfe/semi-icons";
-import {
- defaultBlue,
- tableThemes,
- Action,
- ObjectType,
- State,
-} from "../../data/constants";
-import { useUndoRedo, useAreas, useSaveState } from "../../hooks";
-import Empty from "./Empty";
-
-export default function AreasOverview() {
- const { setSaveState } = useSaveState();
- const { areas, addArea, deleteArea, updateArea } = useAreas();
- const { setUndoStack, setRedoStack } = useUndoRedo();
- const [editField, setEditField] = useState({});
- const [searchText, setSearchText] = useState("");
- const [filteredResult, setFilteredResult] = useState(
- areas.map((t) => t.name)
- );
-
- const handleStringSearch = (value) => {
- setFilteredResult(
- areas.map((t) => t.name).filter((i) => i.includes(value))
- );
- };
-
- return (
-
-
-
- }
- placeholder="Search..."
- emptyContent={
- No areas found
- }
- onSearch={(v) => handleStringSearch(v)}
- onChange={(v) => setSearchText(v)}
- onSelect={(v) => {
- const { id } = areas.find((t) => t.name === v);
- document
- .getElementById(`scroll_area_${id}`)
- .scrollIntoView({ behavior: "smooth" });
- }}
- className="w-full"
- />
-
-
- } block onClick={addArea}>
- Add area
-
-
-
- {areas.length <= 0 ? (
-
- ) : (
-
- {areas.map((a, i) => (
-
-
- updateArea(a.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.AREA,
- aid: i,
- undo: editField,
- redo: { name: e.target.value },
- message: `Edit area name to ${e.target.value}`,
- },
- ]);
- setRedoStack([]);
- }}
- />
-
-
-
-
-
Theme
-
-
-
-
-
- {tableThemes
- .slice(0, Math.ceil(tableThemes.length / 2))
- .map((c) => (
-
- ))}
-
-
- {tableThemes
- .slice(Math.ceil(tableThemes.length / 2))
- .map((c) => (
-
- ))}
-
-
-
- }
- trigger="click"
- position="bottomLeft"
- showArrow
- >
-
-
-
-
-
}
- type="danger"
- onClick={() => {
- Toast.success(`Area deleted!`);
- deleteArea(i, true);
- }}
- />
-
-
- ))}
-
- )}
-
- );
-}
diff --git a/src/components/EditorSidePanel/AreasTab/AreaDetails.jsx b/src/components/EditorSidePanel/AreasTab/AreaDetails.jsx
new file mode 100644
index 0000000..3c9802b
--- /dev/null
+++ b/src/components/EditorSidePanel/AreasTab/AreaDetails.jsx
@@ -0,0 +1,101 @@
+import { useState } from "react";
+import { Row, Col, Button, Input, Popover, Toast } from "@douyinfe/semi-ui";
+import { IconDeleteStroked } from "@douyinfe/semi-icons";
+import { useAreas, useSaveState, useUndoRedo } from "../../../hooks";
+import {
+ Action,
+ ObjectType,
+ State,
+ defaultBlue,
+} from "../../../data/constants";
+import ColorPallete from "../../ColorPallete";
+
+export default function AreaInfo({ data, i }) {
+ const { setSaveState } = useSaveState();
+ const { deleteArea, updateArea } = useAreas();
+ const { setUndoStack, setRedoStack } = useUndoRedo();
+ const [editField, setEditField] = useState({});
+
+ return (
+
+
+ updateArea(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.AREA,
+ aid: i,
+ undo: editField,
+ redo: { name: e.target.value },
+ message: `Edit area name to ${e.target.value}`,
+ },
+ ]);
+ setRedoStack([]);
+ }}
+ />
+
+
+
+ {
+ updateArea(i, { color: defaultBlue });
+ setSaveState(State.SAVING);
+ }}
+ onPickColor={(c) => {
+ setUndoStack((prev) => [
+ ...prev,
+ {
+ action: Action.EDIT,
+ element: ObjectType.AREA,
+ aid: i,
+ undo: { color: data.color },
+ redo: { color: c },
+ message: `Edit area color to ${c}`,
+ },
+ ]);
+ setRedoStack([]);
+ updateArea(i, { color: c });
+ }}
+ />
+
+ }
+ trigger="click"
+ position="bottomLeft"
+ showArrow
+ >
+
+
+
+
+ }
+ type="danger"
+ onClick={() => {
+ Toast.success(`Area deleted!`);
+ deleteArea(i, true);
+ }}
+ />
+
+
+ );
+}
diff --git a/src/components/EditorSidePanel/AreasTab/AreasTab.jsx b/src/components/EditorSidePanel/AreasTab/AreasTab.jsx
new file mode 100644
index 0000000..a049d43
--- /dev/null
+++ b/src/components/EditorSidePanel/AreasTab/AreasTab.jsx
@@ -0,0 +1,37 @@
+import { Row, Col, Button } from "@douyinfe/semi-ui";
+import { IconPlus } from "@douyinfe/semi-icons";
+import Empty from "../Empty";
+import { useAreas } from "../../../hooks";
+import SearchBar from "./SearchBar";
+import AreaInfo from "./AreaDetails";
+
+export default function AreasTab() {
+ const { areas, addArea } = useAreas();
+
+ return (
+
+
+
+
+
+
+ } block onClick={addArea}>
+ Add area
+
+
+
+ {areas.length <= 0 ? (
+
+ ) : (
+
+ {areas.map((a, i) => (
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/src/components/EditorSidePanel/AreasTab/SearchBar.jsx b/src/components/EditorSidePanel/AreasTab/SearchBar.jsx
new file mode 100644
index 0000000..c42191a
--- /dev/null
+++ b/src/components/EditorSidePanel/AreasTab/SearchBar.jsx
@@ -0,0 +1,39 @@
+import { useState } from "react";
+import { useAreas } from "../../../hooks";
+import { AutoComplete } from "@douyinfe/semi-ui";
+import { IconSearch } from "@douyinfe/semi-icons";
+
+export default function SearchBar() {
+ const { areas } = useAreas();
+ const [searchText, setSearchText] = useState("");
+
+ const [filteredResult, setFilteredResult] = useState(
+ areas.map((t) => t.name)
+ );
+
+ const handleStringSearch = (value) => {
+ setFilteredResult(
+ areas.map((t) => t.name).filter((i) => i.includes(value))
+ );
+ };
+
+ return (
+ }
+ placeholder="Search..."
+ emptyContent={No areas found
}
+ onSearch={(v) => handleStringSearch(v)}
+ onChange={(v) => setSearchText(v)}
+ onSelect={(v) => {
+ const { id } = areas.find((t) => t.name === v);
+ document
+ .getElementById(`scroll_area_${id}`)
+ .scrollIntoView({ behavior: "smooth" });
+ }}
+ className="w-full"
+ />
+ );
+}
diff --git a/src/components/EditorSidePanel/NotesOverview.jsx b/src/components/EditorSidePanel/NotesOverview.jsx
deleted file mode 100644
index df8a912..0000000
--- a/src/components/EditorSidePanel/NotesOverview.jsx
+++ /dev/null
@@ -1,212 +0,0 @@
-import { useState } from "react";
-import {
- Row,
- Col,
- Button,
- Collapse,
- AutoComplete,
- TextArea,
- Popover,
- Input,
- Toast,
-} from "@douyinfe/semi-ui";
-import {
- IconDeleteStroked,
- IconPlus,
- IconSearch,
- IconCheckboxTick,
-} from "@douyinfe/semi-icons";
-import { noteThemes, Action, ObjectType } from "../../data/constants";
-import { useUndoRedo, useNotes } from "../../hooks";
-import Empty from "./Empty";
-
-export default function NotesOverview() {
- const { notes, updateNote, addNote, deleteNote } = useNotes();
- const { setUndoStack, setRedoStack } = useUndoRedo();
- const [searchText, setSearchText] = useState("");
- const [editField, setEditField] = useState({});
- const [activeKey, setActiveKey] = useState("");
- const [filteredResult, setFilteredResult] = useState(
- notes.map((t) => t.title)
- );
-
- const handleStringSearch = (value) => {
- setFilteredResult(
- notes.map((t) => t.title).filter((i) => i.includes(value))
- );
- };
-
- return (
-
-
-
- }
- placeholder="Search..."
- emptyContent={
- No notes found
- }
- onSearch={(v) => handleStringSearch(v)}
- onChange={(v) => setSearchText(v)}
- onSelect={(v) => {
- const { id } = notes.find((t) => t.title === v);
- setActiveKey(`${id}`);
- document
- .getElementById(`scroll_note_${id}`)
- .scrollIntoView({ behavior: "smooth" });
- }}
- className="w-full"
- />
-
-
- } block onClick={() => addNote()}>
- Add note
-
-
-
- {notes.length <= 0 ? (
-
- ) : (
-
setActiveKey(k)}
- accordion
- >
- {notes.map((n, i) => (
-
- {n.title}
-
- }
- itemKey={`${n.id}`}
- id={`scroll_note_${n.id}`}
- key={n.id}
- >
-
-
Title:
-
updateNote(n.id, { title: value })}
- onFocus={(e) => setEditField({ title: e.target.value })}
- onBlur={(e) => {
- if (e.target.value === editField.title) return;
- setUndoStack((prev) => [
- ...prev,
- {
- action: Action.EDIT,
- element: ObjectType.NOTE,
- nid: n.id,
- undo: editField,
- redo: { title: e.target.value },
- message: `Edit note title to "${e.target.name}"`,
- },
- ]);
- setRedoStack([]);
- }}
- />
-
-
-
-
- ))}
-
- )}
-
- );
-}
diff --git a/src/components/EditorSidePanel/NotesTab/NoteInfo.jsx b/src/components/EditorSidePanel/NotesTab/NoteInfo.jsx
new file mode 100644
index 0000000..ea73b63
--- /dev/null
+++ b/src/components/EditorSidePanel/NotesTab/NoteInfo.jsx
@@ -0,0 +1,148 @@
+import { useState } from "react";
+import {
+ Button,
+ Collapse,
+ TextArea,
+ Popover,
+ Input,
+ Toast,
+} from "@douyinfe/semi-ui";
+import { IconDeleteStroked, IconCheckboxTick } from "@douyinfe/semi-icons";
+import { noteThemes, Action, ObjectType } from "../../../data/constants";
+import { useNotes, useUndoRedo } from "../../../hooks";
+
+export default function NoteInfo({ data, nid }) {
+ const { updateNote, deleteNote } = useNotes();
+ const { setUndoStack, setRedoStack } = useUndoRedo();
+ const [editField, setEditField] = useState({});
+
+ return (
+
+ {data.title}
+
+ }
+ itemKey={`${data.id}`}
+ id={`scroll_note_${data.id}`}
+ >
+
+
Title:
+
updateNote(data.id, { title: value })}
+ onFocus={(e) => setEditField({ title: e.target.value })}
+ onBlur={(e) => {
+ if (e.target.value === editField.title) return;
+ setUndoStack((prev) => [
+ ...prev,
+ {
+ action: Action.EDIT,
+ element: ObjectType.NOTE,
+ nid: data.id,
+ undo: editField,
+ redo: { title: e.target.value },
+ message: `Edit note title to "${e.target.name}"`,
+ },
+ ]);
+ setRedoStack([]);
+ }}
+ />
+
+
+
+
+ );
+}
diff --git a/src/components/EditorSidePanel/NotesTab/NotesTab.jsx b/src/components/EditorSidePanel/NotesTab/NotesTab.jsx
new file mode 100644
index 0000000..951fcad
--- /dev/null
+++ b/src/components/EditorSidePanel/NotesTab/NotesTab.jsx
@@ -0,0 +1,40 @@
+import { useState } from "react";
+import { Row, Col, Button, Collapse } from "@douyinfe/semi-ui";
+import { IconPlus } from "@douyinfe/semi-icons";
+import { useNotes } from "../../../hooks";
+import Empty from "../Empty";
+import SearchBar from "./SearchBar";
+import NoteInfo from "./NoteInfo";
+
+export default function NotesTab() {
+ const { notes, addNote } = useNotes();
+ const [activeKey, setActiveKey] = useState("");
+
+ return (
+ <>
+
+
+
+
+
+ } block onClick={() => addNote()}>
+ Add note
+
+
+
+ {notes.length <= 0 ? (
+
+ ) : (
+ setActiveKey(k)}
+ accordion
+ >
+ {notes.map((n, i) => (
+
+ ))}
+
+ )}
+ >
+ );
+}
diff --git a/src/components/EditorSidePanel/NotesTab/SearchBar.jsx b/src/components/EditorSidePanel/NotesTab/SearchBar.jsx
new file mode 100644
index 0000000..66a981f
--- /dev/null
+++ b/src/components/EditorSidePanel/NotesTab/SearchBar.jsx
@@ -0,0 +1,39 @@
+import { useState } from "react";
+import { AutoComplete } from "@douyinfe/semi-ui";
+import { IconSearch } from "@douyinfe/semi-icons";
+import { useNotes } from "../../../hooks";
+
+export default function SearchBar({ setActiveKey }) {
+ const { notes } = useNotes();
+ const [searchText, setSearchText] = useState("");
+ const [filteredResult, setFilteredResult] = useState(
+ notes.map((t) => t.title)
+ );
+
+ const handleStringSearch = (value) => {
+ setFilteredResult(
+ notes.map((t) => t.title).filter((i) => i.includes(value))
+ );
+ };
+
+ return (
+ }
+ placeholder="Search..."
+ emptyContent={No notes found
}
+ onSearch={(v) => handleStringSearch(v)}
+ onChange={(v) => setSearchText(v)}
+ onSelect={(v) => {
+ const { id } = notes.find((t) => t.title === v);
+ setActiveKey(`${id}`);
+ document
+ .getElementById(`scroll_note_${id}`)
+ .scrollIntoView({ behavior: "smooth" });
+ }}
+ className="w-full"
+ />
+ );
+}
diff --git a/src/components/EditorSidePanel/RelationshipsOverview.jsx b/src/components/EditorSidePanel/RelationshipsOverview.jsx
deleted file mode 100644
index 57491c4..0000000
--- a/src/components/EditorSidePanel/RelationshipsOverview.jsx
+++ /dev/null
@@ -1,292 +0,0 @@
-import { useState } from "react";
-import {
- AutoComplete,
- Collapse,
- Row,
- Col,
- Select,
- Button,
- Popover,
- Table,
-} from "@douyinfe/semi-ui";
-import {
- IconDeleteStroked,
- IconLoopTextStroked,
- IconMore,
- IconSearch,
-} from "@douyinfe/semi-icons";
-import {
- Cardinality,
- Constraint,
- Action,
- ObjectType,
-} from "../../data/constants";
-import { useTables, useUndoRedo } from "../../hooks";
-import Empty from "./Empty";
-
-export default function RelationshipsOverview() {
- const { relationships } = useTables();
- const [refActiveIndex, setRefActiveIndex] = useState("");
- const [searchText, setSearchText] = useState("");
- const [filteredResult, setFilteredResult] = useState(
- relationships.map((t) => t.name)
- );
-
- const handleStringSearch = (value) => {
- setFilteredResult(
- relationships.map((t) => t.name).filter((i) => i.includes(value))
- );
- };
-
- return (
- <>
- }
- placeholder="Search..."
- emptyContent={
- No relationships found
- }
- onSearch={(v) => handleStringSearch(v)}
- onChange={(v) => setSearchText(v)}
- onSelect={(v) => {
- const { id } = relationships.find((t) => t.name === v);
- setRefActiveIndex(`${id}`);
- document
- .getElementById(`scroll_ref_${id}`)
- .scrollIntoView({ behavior: "smooth" });
- }}
- className="w-full"
- />
- setRefActiveIndex(k)}
- accordion
- >
- {relationships.length <= 0 ? (
-
- ) : (
- relationships.map((r) => )
- )}
-
- >
- );
-}
-
-function RelationshipPanel({ data }) {
- const columns = [
- {
- title: "Primary",
- dataIndex: "primary",
- },
- {
- title: "Foreign",
- dataIndex: "foreign",
- },
- ];
- const { tables, setRelationships, deleteRelationship } = useTables();
- const { setUndoStack, setRedoStack } = useUndoRedo();
-
- return (
-
-
- {data.name}
-
- }
- itemKey={`${data.id}`}
- >
-
-
- Primary:
- {tables[data.endTableId].name}
-
-
- Foreign:
- {tables[data.startTableId].name}
-
-
-
-
-
- }
- block
- onClick={() => {
- setUndoStack((prev) => [
- ...prev,
- {
- action: Action.EDIT,
- element: ObjectType.RELATIONSHIP,
- rid: data.id,
- undo: {
- startTableId: data.startTableId,
- startFieldId: data.startFieldId,
- endTableId: data.endTableId,
- endFieldId: data.endFieldId,
- },
- redo: {
- startTableId: data.endTableId,
- startFieldId: data.endFieldId,
- endTableId: data.startTableId,
- endFieldId: data.startFieldId,
- },
- message: `Swap primary and foreign tables`,
- },
- ]);
- setRedoStack([]);
- setRelationships((prev) =>
- prev.map((e, idx) =>
- idx === data.id
- ? {
- ...e,
- startTableId: e.endTableId,
- startFieldId: e.endFieldId,
- endTableId: e.startTableId,
- endFieldId: e.startFieldId,
- }
- : e
- )
- );
- }}
- >
- Swap
-
-
-
- }
- trigger="click"
- position="rightTop"
- showArrow
- >
-
} type="tertiary" />
-
-
-
- Cardinality
-
-
-
- On update:
-
-
-
- On delete:
-
-
-
- }
- block
- type="danger"
- onClick={() => deleteRelationship(data.id, true)}
- >
- Delete
-
-
-
- );
-}
diff --git a/src/components/EditorSidePanel/RelationshipsTab/RelationshipInfo.jsx b/src/components/EditorSidePanel/RelationshipsTab/RelationshipInfo.jsx
new file mode 100644
index 0000000..e273d8a
--- /dev/null
+++ b/src/components/EditorSidePanel/RelationshipsTab/RelationshipInfo.jsx
@@ -0,0 +1,228 @@
+import {
+ Collapse,
+ Row,
+ Col,
+ Select,
+ Button,
+ Popover,
+ Table,
+} from "@douyinfe/semi-ui";
+import {
+ IconDeleteStroked,
+ IconLoopTextStroked,
+ IconMore,
+} from "@douyinfe/semi-icons";
+import {
+ Cardinality,
+ Constraint,
+ Action,
+ ObjectType,
+} from "../../../data/constants";
+import { useTables, useUndoRedo } from "../../../hooks";
+
+const columns = [
+ {
+ title: "Primary",
+ dataIndex: "primary",
+ },
+ {
+ title: "Foreign",
+ dataIndex: "foreign",
+ },
+];
+
+export default function RelationshipInfo({ data }) {
+ const { setUndoStack, setRedoStack } = useUndoRedo();
+ const { tables, setRelationships, deleteRelationship } = useTables();
+
+ const swapKeys = () => {
+ setUndoStack((prev) => [
+ ...prev,
+ {
+ action: Action.EDIT,
+ element: ObjectType.RELATIONSHIP,
+ rid: data.id,
+ undo: {
+ startTableId: data.startTableId,
+ startFieldId: data.startFieldId,
+ endTableId: data.endTableId,
+ endFieldId: data.endFieldId,
+ },
+ redo: {
+ startTableId: data.endTableId,
+ startFieldId: data.endFieldId,
+ endTableId: data.startTableId,
+ endFieldId: data.startFieldId,
+ },
+ message: `Swap primary and foreign tables`,
+ },
+ ]);
+ setRedoStack([]);
+ setRelationships((prev) =>
+ prev.map((e, idx) =>
+ idx === data.id
+ ? {
+ ...e,
+ startTableId: e.endTableId,
+ startFieldId: e.endFieldId,
+ endTableId: e.startTableId,
+ endFieldId: e.startFieldId,
+ }
+ : e
+ )
+ );
+ };
+
+ const changeCardinality = (value) => {
+ setUndoStack((prev) => [
+ ...prev,
+ {
+ action: Action.EDIT,
+ element: ObjectType.RELATIONSHIP,
+ rid: data.id,
+ undo: { cardinality: data.cardinality },
+ redo: { cardinality: value },
+ message: `Edit relationship cardinality`,
+ },
+ ]);
+ setRedoStack([]);
+ setRelationships((prev) =>
+ prev.map((e, idx) => (idx === data.id ? { ...e, cardinality: value } : e))
+ );
+ };
+
+ const changeConstraint = (key, value) => {
+ const undoKey = `${key}Constraint`;
+ console.log({
+ action: Action.EDIT,
+ element: ObjectType.RELATIONSHIP,
+ rid: data.id,
+ undo: { [undoKey]: data[undoKey] },
+ redo: { [undoKey]: value },
+ message: `Edit relationship ${key} constraint`,
+ });
+ setUndoStack((prev) => [
+ ...prev,
+ {
+ action: Action.EDIT,
+ element: ObjectType.RELATIONSHIP,
+ rid: data.id,
+ undo: { [undoKey]: data[undoKey] },
+ redo: { [undoKey]: value },
+ message: `Edit relationship ${key} constraint`,
+ },
+ ]);
+ setRedoStack([]);
+ setRelationships((prev) =>
+ prev.map((e, idx) => (idx === data.id ? { ...e, [undoKey]: value } : e))
+ );
+ };
+
+ return (
+
+
+ {data.name}
+
+ }
+ itemKey={`${data.id}`}
+ >
+
+
+ Primary:
+ {tables[data.endTableId].name}
+
+
+ Foreign:
+ {tables[data.startTableId].name}
+
+
+
+
+
+ }
+ block
+ onClick={swapKeys}
+ >
+ Swap
+
+
+
+ }
+ trigger="click"
+ position="rightTop"
+ showArrow
+ >
+
} type="tertiary" />
+
+
+
+ Cardinality
+