Move controlpanel modal and sidesheet into folders

This commit is contained in:
1ilit 2024-04-06 08:19:12 +03:00
parent 1576b3fb96
commit 173b02daa2
14 changed files with 1159 additions and 1012 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
import {
IconCaretdown,
IconCheckboxTick,
IconRowsStroked,
} from "@douyinfe/semi-icons";
import { Dropdown } from "@douyinfe/semi-ui";
import { useLayout } from "../../hooks";
import { enterFullscreen, exitFullscreen } from "../../utils/fullscreen";
export default function LayoutDropdown() {
const { layout, setLayout } = useLayout();
const invertLayout = (component) =>
setLayout((prev) => ({ ...prev, [component]: !prev[component] }));
return (
<Dropdown
position="bottomLeft"
style={{ width: "180px" }}
render={
<Dropdown.Menu>
<Dropdown.Item
icon={
layout.header ? <IconCheckboxTick /> : <div className="px-2" />
}
onClick={() => invertLayout("header")}
>
Header
</Dropdown.Item>
<Dropdown.Item
icon={
layout.sidebar ? <IconCheckboxTick /> : <div className="px-2" />
}
onClick={() => invertLayout("sidebar")}
>
Sidebar
</Dropdown.Item>
<Dropdown.Item
icon={
layout.issues ? <IconCheckboxTick /> : <div className="px-2" />
}
onClick={() => invertLayout("issues")}
>
Issues
</Dropdown.Item>
<Dropdown.Divider />
<Dropdown.Item
icon={<div className="px-2" />}
onClick={() => {
if (layout.fullscreen) {
exitFullscreen();
} else {
enterFullscreen();
}
invertLayout("fullscreen");
}}
>
Fullscreen
</Dropdown.Item>
</Dropdown.Menu>
}
trigger="click"
>
<div className="py-1 px-2 hover-2 rounded flex items-center justify-center">
<IconRowsStroked size="extra-large" />
<div>
<IconCaretdown />
</div>
</div>
</Dropdown>
);
}

View File

@ -0,0 +1,127 @@
import {
ddbDiagramIsValid,
jsonDiagramIsValid,
} from "../../../utils/validateSchema";
import { Upload, Banner } from "@douyinfe/semi-ui";
import { STATUS } from "../../../data/constants";
import { useAreas, useNotes, useTables } from "../../../hooks";
export default function ImportDiagram({ setImportData, error, setError }) {
const { areas } = useAreas();
const { notes } = useNotes();
const { tables, relationships } = useTables();
const diagramIsEmpty = () => {
return (
tables.length === 0 &&
relationships.length === 0 &&
notes.length === 0 &&
areas.length === 0
);
};
return (
<div>
<Upload
action="#"
beforeUpload={({ file, fileList }) => {
const f = fileList[0].fileInstance;
if (!f) {
return;
}
const reader = new FileReader();
reader.onload = async (e) => {
let jsonObject = null;
try {
jsonObject = JSON.parse(e.target.result);
} catch (error) {
setError({
type: STATUS.ERROR,
message: "The file contains an error.",
});
return;
}
if (f.type === "application/json") {
if (!jsonDiagramIsValid(jsonObject)) {
setError({
type: STATUS.ERROR,
message:
"The file is missing necessary properties for a diagram.",
});
return;
}
} else if (f.name.split(".").pop() === "ddb") {
if (!ddbDiagramIsValid(jsonObject)) {
setError({
type: STATUS.ERROR,
message:
"The file is missing necessary properties for a diagram.",
});
return;
}
}
setImportData(jsonObject);
if (diagramIsEmpty()) {
setError({
type: STATUS.OK,
message: "Everything looks good. You can now import.",
});
} else {
setError({
type: STATUS.WARNING,
message:
"The current diagram is not empty. Importing a new diagram will overwrite the current changes.",
});
}
};
reader.readAsText(f);
return {
autoRemove: false,
fileInstance: file.fileInstance,
status: "success",
shouldUpload: false,
};
}}
draggable={true}
dragMainText="Drag and drop the file here or click to upload."
dragSubText="Support json and ddb"
accept="application/json,.ddb"
onRemove={() =>
setError({
type: STATUS.NONE,
message: "",
})
}
onFileChange={() =>
setError({
type: STATUS.NONE,
message: "",
})
}
limit={1}
/>
{error.type === STATUS.ERROR ? (
<Banner
type="danger"
fullMode={false}
description={<div>{error.message}</div>}
/>
) : error.type === STATUS.OK ? (
<Banner
type="info"
fullMode={false}
description={<div>{error.message}</div>}
/>
) : (
error.type === STATUS.WARNING && (
<Banner
type="warning"
fullMode={false}
description={<div>{error.message}</div>}
/>
)
)}
</div>
);
}

View File

@ -0,0 +1,66 @@
import { Upload, Checkbox } from "@douyinfe/semi-ui";
import { STATUS } from "../../../data/constants";
export default function ImportSource({ importData, setImportData, setError }) {
return (
<div>
<Upload
action="#"
beforeUpload={({ file, fileList }) => {
const f = fileList[0].fileInstance;
if (!f) {
return;
}
const reader = new FileReader();
reader.onload = async (e) => {
setImportData((prev) => ({ ...prev, src: e.target.result }));
};
reader.readAsText(f);
return {
autoRemove: false,
fileInstance: file.fileInstance,
status: "success",
shouldUpload: false,
};
}}
draggable={true}
dragMainText="Drag and drop the file here or click to upload."
dragSubText="Upload an sql file to autogenerate your tables and columns."
accept=".sql"
onRemove={() => {
setError({
type: STATUS.NONE,
message: "",
});
setImportData((prev) => ({ ...prev, src: "" }));
}}
onFileChange={() =>
setError({
type: STATUS.NONE,
message: "",
})
}
limit={1}
/>
<div>
<div className="text-xs mb-3 mt-1 opacity-80">
* For the time being loading only MySQL scripts is supported.
</div>
<Checkbox
aria-label="overwrite checkbox"
checked={importData.overwrite}
defaultChecked
onChange={(e) =>
setImportData((prev) => ({
...prev,
overwrite: e.target.checked,
}))
}
>
Overwrite existing diagram
</Checkbox>
</div>
</div>
);
}

View File

@ -0,0 +1,312 @@
import {
Spin,
Input,
Image,
Toast,
Modal as SemiUIModal,
} from "@douyinfe/semi-ui";
import { MODAL, STATUS } from "../../../data/constants";
import { useState } from "react";
import { db } from "../../../data/db";
import {
useAreas,
useNotes,
useSettings,
useTables,
useTransform,
useTypes,
useUndoRedo,
} from "../../../hooks";
import { saveAs } from "file-saver";
import { Parser } from "node-sql-parser";
import { astToDiagram } from "../../../utils/astToDiagram";
import { getModalTitle, getOkText } from "../../../utils/modalTitles";
import Rename from "./Rename";
import Open from "./Open";
import New from "./New";
import ImportDiagram from "./ImportDiagram";
import ImportSource from "./ImportSource";
import Editor from "@monaco-editor/react";
export default function Modal({
modal,
setModal,
title,
setTitle,
prevTitle,
setPrevTitle,
setDiagramId,
exportData,
setExportData,
}) {
const { setTables, setRelationships } = useTables();
const { setNotes } = useNotes();
const { setAreas } = useAreas();
const { setTypes } = useTypes();
const { settings } = useSettings();
const { setTransform } = useTransform();
const { setUndoStack, setRedoStack } = useUndoRedo();
const [importSource, setImportSource] = useState({
src: "",
overwrite: true,
dbms: "MySQL",
});
const [importData, setImportData] = useState(null);
const [error, setError] = useState({
type: STATUS.NONE,
message: "",
});
const [selectedTemplateId, setSelectedTemplateId] = useState(-1);
const [selectedDiagramId, setSelectedDiagramId] = useState(0);
const [saveAsTitle, setSaveAsTitle] = useState(title);
const overwriteDiagram = () => {
setTables(importData.tables);
setRelationships(importData.relationships);
setAreas(importData.subjectAreas);
setNotes(importData.notes);
if (importData.title) {
setTitle(importData.title);
}
};
const loadDiagram = async (id) => {
await db.diagrams
.get(id)
.then((diagram) => {
if (diagram) {
setDiagramId(diagram.id);
setTitle(diagram.name);
setTables(diagram.tables);
setTypes(diagram.types);
setRelationships(diagram.references);
setAreas(diagram.areas);
setNotes(diagram.notes);
setTransform({
pan: diagram.pan,
zoom: diagram.zoom,
});
setUndoStack([]);
setRedoStack([]);
window.name = `d ${diagram.id}`;
} else {
Toast.error("Oops! Something went wrong.");
}
})
.catch(() => {
Toast.error("Oops! Couldn't load diagram.");
});
};
const parseSQLAndLoadDiagram = () => {
const parser = new Parser();
let ast = null;
try {
ast = parser.astify(importData.src, { database: "MySQL" });
} catch (err) {
Toast.error(
"Could not parse the sql file. Make sure there are no syntax errors."
);
return;
}
const d = astToDiagram(ast);
if (importData.overwrite) {
setTables(d.tables);
setRelationships(d.relationships);
setNotes([]);
setAreas([]);
setTypes([]);
setUndoStack([]);
setRedoStack([]);
} else {
setTables((prev) => [...prev, ...d.tables]);
setRelationships((prev) => [...prev, ...d.relationships]);
}
};
const createNewDiagram = (id) => {
const newWindow = window.open("/editor");
newWindow.name = "lt " + id;
};
const getModalOnOk = async () => {
switch (modal) {
case MODAL.IMG:
saveAs(
exportData.data,
`${exportData.filename}.${exportData.extension}`
);
return;
case MODAL.CODE: {
const blob = new Blob([exportData.data], {
type: "application/json",
});
saveAs(blob, `${exportData.filename}.${exportData.extension}`);
return;
}
case MODAL.IMPORT:
if (error.type !== STATUS.ERROR) {
setTransform((prev) => ({ ...prev, pan: { x: 0, y: 0 } }));
overwriteDiagram();
setImportData(null);
setModal(MODAL.NONE);
setUndoStack([]);
setRedoStack([]);
}
return;
case MODAL.IMPORT_SRC:
parseSQLAndLoadDiagram();
setModal(MODAL.NONE);
return;
case MODAL.OPEN:
if (selectedDiagramId === 0) return;
loadDiagram(selectedDiagramId);
setModal(MODAL.NONE);
return;
case MODAL.RENAME:
setPrevTitle(title);
setModal(MODAL.NONE);
return;
case MODAL.SAVEAS:
setTitle(saveAsTitle);
setModal(MODAL.NONE);
return;
case MODAL.NEW:
setModal(MODAL.NONE);
createNewDiagram(selectedTemplateId);
return;
default:
setModal(MODAL.NONE);
return;
}
};
const getModalBody = () => {
switch (modal) {
case MODAL.IMPORT:
return (
<ImportDiagram
setImportData={setImportData}
error={error}
setError={setError}
/>
);
case MODAL.IMPORT_SRC:
return (
<ImportSource
importData={importSource}
setImportData={setImportSource}
setError={setError}
/>
);
case MODAL.NEW:
return (
<New
selectedTemplateId={selectedTemplateId}
setSelectedTemplateId={setSelectedTemplateId}
/>
);
case MODAL.RENAME:
return <Rename title={title} setTitle={setTitle} />;
case MODAL.OPEN:
return (
<Open
selectedDiagramId={selectedDiagramId}
setSelectedDiagramId={setSelectedDiagramId}
/>
);
case MODAL.SAVEAS:
return (
<Input
placeholder="Diagram name"
value={saveAsTitle}
onChange={(v) => setSaveAsTitle(v)}
/>
);
case MODAL.CODE:
case MODAL.IMG:
if (exportData.data !== "" || exportData.data) {
return (
<>
{modal === MODAL.IMG ? (
<Image src={exportData.data} alt="Diagram" height={280} />
) : (
<Editor
height="360px"
value={exportData.data}
language={exportData.extension}
options={{ readOnly: true }}
theme={settings.mode === "light" ? "light" : "vs-dark"}
/>
)}
<div className="text-sm font-semibold mt-2">Filename:</div>
<Input
value={exportData.filename}
placeholder="Filename"
suffix={<div className="p-2">{`.${exportData.extension}`}</div>}
onChange={(value) =>
setExportData((prev) => ({ ...prev, filename: value }))
}
field="filename"
/>
</>
);
} else {
return (
<div className="text-center my-3">
<Spin tip="Loading..." size="large" />
</div>
);
}
default:
return <></>;
}
};
return (
<SemiUIModal
title={getModalTitle(modal)}
visible={modal !== MODAL.NONE}
onOk={getModalOnOk}
afterClose={() => {
setExportData(() => ({
data: "",
extension: "",
filename: `${title}_${new Date().toISOString()}`,
}));
setError({
type: STATUS.NONE,
message: "",
});
setImportData(null);
setImportSource({
src: "",
overwrite: true,
dbms: "MySQL",
});
}}
onCancel={() => {
if (modal === MODAL.RENAME) setTitle(prevTitle);
setModal(MODAL.NONE);
}}
centered
closeOnEsc={true}
okText={getOkText(modal)}
okButtonProps={{
disabled:
(error && error?.type === STATUS.ERROR) ||
(modal === MODAL.IMPORT &&
(error.type === STATUS.ERROR || !importData)) ||
(modal === MODAL.RENAME && title === "") ||
((modal === MODAL.IMG || modal === MODAL.CODE) && !exportData.data) ||
(modal === MODAL.SAVEAS && saveAsTitle === "") ||
(modal === MODAL.IMPORT_SRC && importSource.src === ""),
}}
cancelText="Cancel"
width={modal === MODAL.NEW ? 740 : 600}
>
{getModalBody()}
</SemiUIModal>
);
}

View File

@ -0,0 +1,43 @@
import { db } from "../../../data/db";
import { useSettings } from "../../../hooks";
import { useLiveQuery } from "dexie-react-hooks";
import Thumbnail from "../../Thumbnail";
export default function New({ selectedTemplateId, setSelectedTemplateId }) {
const { settings } = useSettings();
const templates = useLiveQuery(() => db.templates.toArray());
return (
<div className="h-[360px] grid grid-cols-3 gap-2 overflow-auto px-1">
<div onClick={() => setSelectedTemplateId(0)}>
<div
className={`rounded-md h-[180px] border-2 hover:border-dashed ${
selectedTemplateId === 0 ? "border-blue-400" : "border-zinc-400"
}`}
>
<Thumbnail i={0} diagram={{}} zoom={0.24} theme={settings.mode} />
</div>
<div className="text-center mt-1">Blank</div>
</div>
{templates?.map((temp, i) => (
<div key={i} onClick={() => setSelectedTemplateId(temp.id)}>
<div
className={`rounded-md h-[180px] border-2 hover:border-dashed ${
selectedTemplateId === temp.id
? "border-blue-400"
: "border-zinc-400"
}`}
>
<Thumbnail
i={temp.id}
diagram={temp}
zoom={0.24}
theme={settings.mode}
/>
</div>
<div className="text-center mt-1">{temp.title}</div>
</div>
))}
</div>
);
}

View File

@ -0,0 +1,73 @@
import { db } from "../../../data/db";
import { Banner } from "@douyinfe/semi-ui";
import { useLiveQuery } from "dexie-react-hooks";
export default function Open({ selectedDiagramId, setSelectedDiagramId }) {
const diagrams = useLiveQuery(() => db.diagrams.toArray());
const getDiagramSize = (d) => {
const size = JSON.stringify(d).length;
let sizeStr;
if (size >= 1024 && size < 1024 * 1024)
sizeStr = (size / 1024).toFixed(1) + "KB";
else if (size >= 1024 * 1024)
sizeStr = (size / (1024 * 1024)).toFixed(1) + "MB";
else sizeStr = size + "B";
return sizeStr;
};
return (
<div>
{diagrams?.length === 0 ? (
<Banner
fullMode={false}
type="info"
bordered
icon={null}
closeIcon={null}
description={<div>You have no saved diagrams.</div>}
/>
) : (
<div className="max-h-[360px]">
<table className="w-full text-left border-separate border-spacing-x-0">
<thead>
<tr>
<th>Name</th>
<th>Last Modified</th>
<th>Size</th>
</tr>
</thead>
<tbody>
{diagrams?.map((d) => {
return (
<tr
key={d.id}
className={`${
selectedDiagramId === d.id
? "bg-blue-300 bg-opacity-30"
: "hover-1"
}`}
onClick={() => {
setSelectedDiagramId(d.id);
}}
>
<td className="py-1">
<i className="bi bi-file-earmark-text text-[16px] me-1 opacity-60" />
{d.name}
</td>
<td className="py-1">
{d.lastModified.toLocaleDateString() +
" " +
d.lastModified.toLocaleTimeString()}
</td>
<td className="py-1">{getDiagramSize(d)}</td>
</tr>
);
})}
</tbody>
</table>
</div>
)}
</div>
);
}

View File

@ -0,0 +1,11 @@
import { Input } from "@douyinfe/semi-ui";
export default function Rename({ title, setTitle }) {
return (
<Input
placeholder="Diagram name"
value={title}
onChange={(v) => setTitle(v)}
/>
);
}

View File

@ -0,0 +1,61 @@
import { SideSheet as SemiUISideSheet } from "@douyinfe/semi-ui";
import { SIDESHEET } from "../../../data/constants";
import { useSettings } from "../../../hooks";
import timeLine from "../../../assets/process.png";
import timeLineDark from "../../../assets/process_dark.png";
import todo from "../../../assets/calendar.png";
import Timeline from "./Timeline";
import Todo from "./Todo";
export default function Sidesheet({ type, onClose }) {
const { settings } = useSettings();
function getTitle(type) {
switch (type) {
case SIDESHEET.TIMELINE:
return (
<div className="flex items-center">
<img
src={settings.mode === "light" ? timeLine : timeLineDark}
className="w-7"
alt="chat icon"
/>
<div className="ms-3 text-lg">Timeline</div>
</div>
);
case SIDESHEET.TODO:
return (
<div className="flex items-center">
<img src={todo} className="w-7" alt="todo icon" />
<div className="ms-3 text-lg">To-do list</div>
</div>
);
default:
break;
}
}
function getContent(type) {
switch (type) {
case SIDESHEET.TIMELINE:
return <Timeline />;
case SIDESHEET.TODO:
return <Todo />;
default:
break;
}
}
return (
<SemiUISideSheet
visible={type !== SIDESHEET.NONE}
onCancel={onClose}
width={340}
title={getTitle(type)}
style={{ paddingBottom: "16px" }}
bodyStyle={{ padding: "0px" }}
>
{getContent(type)}
</SemiUISideSheet>
);
}

View File

@ -0,0 +1,32 @@
import { useUndoRedo } from "../../../hooks";
import { List } from "@douyinfe/semi-ui";
export default function Timeline() {
const { undoStack } = useUndoRedo();
if (undoStack.length > 0) {
return (
<List className="sidesheet-theme">
{[...undoStack].reverse().map((e, i) => (
<List.Item
key={i}
style={{ padding: "4px 18px 4px 18px" }}
className="hover-1"
>
<div className="flex items-center py-1 w-full">
<i className="block fa-regular fa-circle fa-xs" />
<div className="ms-2">{e.message}</div>
</div>
</List.Item>
))}
</List>
);
} else {
return (
<div className="m-5 sidesheet-theme">
No activity was recorded. You have not added anything to your diagram
yet.
</div>
);
}
}

View File

@ -19,8 +19,8 @@ import {
IconDeleteStroked, IconDeleteStroked,
IconCaretdown, IconCaretdown,
} from "@douyinfe/semi-icons"; } from "@douyinfe/semi-icons";
import { State } from "../../data/constants"; import { State } from "../../../data/constants";
import { useTasks, useSaveState } from "../../hooks"; import { useTasks, useSaveState } from "../../../hooks";
const Priority = { const Priority = {
NONE: 0, NONE: 0,

View File

@ -99,3 +99,28 @@ export const State = {
LOADING: 3, LOADING: 3,
ERROR: 4, ERROR: 4,
}; };
export const MODAL = {
NONE: 0,
IMG: 1,
CODE: 2,
IMPORT: 3,
RENAME: 4,
OPEN: 5,
SAVEAS: 6,
NEW: 7,
IMPORT_SRC: 8,
};
export const STATUS = {
NONE: 0,
WARNING: 1,
ERROR: 2,
OK: 3,
};
export const SIDESHEET = {
NONE: 0,
TODO: 1,
TIMELINE: 2,
};

252
src/utils/astToDiagram.js Normal file
View File

@ -0,0 +1,252 @@
import { Cardinality } from "../data/constants";
export function astToDiagram(ast) {
const tables = [];
const relationships = [];
const inlineForeignKeys = [];
ast.forEach((e) => {
if (e.type === "create") {
if (e.keyword === "table") {
const table = {};
table.name = e.table[0].table;
table.comment = "";
table.color = "#175e7a";
table.fields = [];
table.indices = [];
table.x = 0;
table.y = 0;
e.create_definitions.forEach((d) => {
if (d.resource === "column") {
const field = {};
field.name = d.column.column;
field.type = d.definition.dataType;
field.comment = "";
field.unique = false;
if (d.unique) field.unique = true;
field.increment = false;
if (d.auto_increment) field.increment = true;
field.notNull = false;
if (d.nullable) field.notNull = true;
field.primary = false;
if (d.primary_key) field.primary = true;
field.default = "";
if (d.default_val) field.default = d.default_val.value.value;
if (d.definition["length"]) field.size = d.definition["length"];
field.check = "";
if (d.check) {
let check = "";
if (d.check.definition[0].left.column) {
let value = d.check.definition[0].right.value;
if (
d.check.definition[0].right.type === "double_quote_string" ||
d.check.definition[0].right.type === "single_quote_string"
)
value = "'" + value + "'";
check =
d.check.definition[0].left.column +
" " +
d.check.definition[0].operator +
" " +
value;
} else {
let value = d.check.definition[0].right.value;
if (
d.check.definition[0].left.type === "double_quote_string" ||
d.check.definition[0].left.type === "single_quote_string"
)
value = "'" + value + "'";
check =
value +
" " +
d.check.definition[0].operator +
" " +
d.check.definition[0].right.column;
}
field.check = check;
}
table.fields.push(field);
} else if (d.resource === "constraint") {
if (d.constraint_type === "primary key") {
d.definition.forEach((c) => {
table.fields.forEach((f) => {
if (f.name === c.column && !f.primary) {
f.primary = true;
}
});
});
} else if (d.constraint_type === "FOREIGN KEY") {
inlineForeignKeys.push({ ...d, startTable: e.table[0].table });
}
}
});
tables.push(table);
tables.forEach((e, i) => {
e.id = i;
e.fields.forEach((f, j) => {
f.id = j;
});
});
} else if (e.keyword === "index") {
const index = {};
index.name = e.index;
index.unique = false;
if (e.index_type === "unique") index.unique = true;
index.fields = [];
e.index_columns.forEach((f) => index.fields.push(f.column));
let found = -1;
tables.forEach((t, i) => {
if (found !== -1) return;
if (t.name === e.table.table) {
t.indices.push(index);
found = i;
}
});
if (found !== -1) tables[found].indices.forEach((i, j) => (i.id = j));
}
} else if (e.type === "alter") {
if (
e.expr[0].action === "add" &&
e.expr[0].create_definitions.constraint_type === "FOREIGN KEY"
) {
const relationship = {};
const startTable = e.table[0].table;
const startField = e.expr[0].create_definitions.definition[0].column;
const endTable =
e.expr[0].create_definitions.reference_definition.table[0].table;
const endField =
e.expr[0].create_definitions.reference_definition.definition[0]
.column;
let updateConstraint = "No action";
let deleteConstraint = "No action";
e.expr[0].create_definitions.reference_definition.on_action.forEach(
(c) => {
if (c.type === "on update") {
updateConstraint = c.value.value;
updateConstraint =
updateConstraint[0].toUpperCase() +
updateConstraint.substring(1);
} else if (c.type === "on delete") {
deleteConstraint = c.value.value;
deleteConstraint =
deleteConstraint[0].toUpperCase() +
deleteConstraint.substring(1);
}
}
);
let startTableId = -1;
let startFieldId = -1;
let endTableId = -1;
let endFieldId = -1;
tables.forEach((t) => {
if (t.name === startTable) {
startTableId = t.id;
return;
}
if (t.name === endTable) {
endTableId = t.id;
}
});
if (startTableId === -1 || endTableId === -1) return;
tables[startTableId].fields.forEach((f) => {
if (f.name === startField) {
startFieldId = f.id;
return;
}
if (f.name === endField) {
endFieldId = f.id;
}
});
if (startFieldId === -1 || endFieldId === -1) return;
relationship.name = startTable + "_" + startField + "_fk";
relationship.startTableId = startTableId;
relationship.startFieldId = startFieldId;
relationship.endTableId = endTableId;
relationship.endFieldId = endFieldId;
relationship.updateConstraint = updateConstraint;
relationship.deleteConstraint = deleteConstraint;
relationship.cardinality = Cardinality.ONE_TO_ONE;
relationships.push(relationship);
relationships.forEach((r, i) => (r.id = i));
}
}
});
inlineForeignKeys.forEach((fk) => {
const relationship = {};
const startTable = fk.startTable;
const startField = fk.definition[0].column;
const endTable = fk.reference_definition.table[0].table;
const endField = fk.reference_definition.definition[0].column;
let updateConstraint = "No action";
let deleteConstraint = "No action";
fk.reference_definition.on_action.forEach((c) => {
if (c.type === "on update") {
updateConstraint = c.value.value;
updateConstraint =
updateConstraint[0].toUpperCase() + updateConstraint.substring(1);
} else if (c.type === "on delete") {
deleteConstraint = c.value.value;
deleteConstraint =
deleteConstraint[0].toUpperCase() + deleteConstraint.substring(1);
}
});
let startTableId = -1;
let startFieldId = -1;
let endTableId = -1;
let endFieldId = -1;
tables.forEach((t) => {
if (t.name === startTable) {
startTableId = t.id;
return;
}
if (t.name === endTable) {
endTableId = t.id;
}
});
if (startTableId === -1 || endTableId === -1) return;
tables[startTableId].fields.forEach((f) => {
if (f.name === startField) {
startFieldId = f.id;
return;
}
if (f.name === endField) {
endFieldId = f.id;
}
});
if (startFieldId === -1 || endFieldId === -1) return;
relationship.name = startTable + "_" + startField + "_fk";
relationship.startTableId = startTableId;
relationship.startFieldId = startFieldId;
relationship.endTableId = endTableId;
relationship.endFieldId = endFieldId;
relationship.updateConstraint = updateConstraint;
relationship.deleteConstraint = deleteConstraint;
relationship.cardinality = Cardinality.ONE_TO_ONE;
relationships.push(relationship);
});
relationships.forEach((r, i) => (r.id = i));
return { tables, relationships };
}

44
src/utils/modalTitles.js Normal file
View File

@ -0,0 +1,44 @@
import { MODAL } from "../data/constants";
export const getModalTitle = (modal) => {
switch (modal) {
case MODAL.IMPORT:
case MODAL.IMPORT_SRC:
return "Import diagram";
case MODAL.CODE:
return "Export source";
case MODAL.IMG:
return "Export image";
case MODAL.RENAME:
return "Rename diagram";
case MODAL.OPEN:
return "Open diagram";
case MODAL.SAVEAS:
return "Save as";
case MODAL.NEW:
return "Create new diagram";
default:
return "";
}
};
export const getOkText = (modal) => {
switch (modal) {
case MODAL.IMPORT:
case MODAL.IMPORT_SRC:
return "Import";
case MODAL.CODE:
case MODAL.IMG:
return "Export";
case MODAL.RENAME:
return "Rename";
case MODAL.OPEN:
return "Open";
case MODAL.SAVEAS:
return "Save as";
case MODAL.NEW:
return "Create";
default:
return "Confirm";
}
};