Add enums tab
This commit is contained in:
parent
259835892a
commit
810afe4bc0
@ -51,13 +51,13 @@ import {
|
||||
useTables,
|
||||
useUndoRedo,
|
||||
useSelect,
|
||||
useSaveState,
|
||||
useTypes,
|
||||
useNotes,
|
||||
useAreas,
|
||||
} from "../../hooks";
|
||||
import { enterFullscreen } from "../../utils/fullscreen";
|
||||
import { dataURItoBlob } from "../../utils/utils";
|
||||
import useAreas from "../../hooks/useAreas";
|
||||
import useNotes from "../../hooks/useNotes";
|
||||
import useTypes from "../../hooks/useTypes";
|
||||
import useSaveState from "../../hooks/useSaveState";
|
||||
import { IconAddArea, IconAddNote, IconAddTable } from "../../icons";
|
||||
import LayoutDropdown from "./LayoutDropdown";
|
||||
import Sidesheet from "./SideSheet/Sidesheet";
|
||||
|
85
src/components/EditorSidePanel/EnumsTab/EnumDetails.jsx
Normal file
85
src/components/EditorSidePanel/EnumsTab/EnumDetails.jsx
Normal file
@ -0,0 +1,85 @@
|
||||
import { useState } from "react";
|
||||
import { Button, Input, TagInput } from "@douyinfe/semi-ui";
|
||||
import { IconDeleteStroked } from "@douyinfe/semi-icons";
|
||||
import { useEnums, useUndoRedo } from "../../../hooks";
|
||||
import { Action, ObjectType } from "../../../data/constants";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function EnumDetails({ data, i }) {
|
||||
const { t } = useTranslation();
|
||||
const { deleteEnum, updateEnum } = useEnums();
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
const [editField, setEditField] = useState({});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className="flex justify-center items-center gap-2"
|
||||
id={`scroll_enum_${data.id}`}
|
||||
>
|
||||
<div className="font-semibold">{t("Name")}: </div>
|
||||
<Input
|
||||
value={data.name}
|
||||
placeholder={t("name")}
|
||||
onChange={(value) => updateEnum(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.ENUM,
|
||||
aid: i,
|
||||
undo: editField,
|
||||
redo: { name: e.target.value },
|
||||
message: t("edit_enum", {
|
||||
enumName: e.target.value,
|
||||
extra: "[name]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<TagInput
|
||||
separator={[",", ", ", " ,"]}
|
||||
value={data.values}
|
||||
addOnBlur
|
||||
className="my-2"
|
||||
placeholder={t("values")}
|
||||
onChange={(v) => updateEnum(i, { values: v })}
|
||||
onFocus={() => setEditField({ values: data.values })}
|
||||
onBlur={() => {
|
||||
if (JSON.stringify(editField.values) === JSON.stringify(data.values))
|
||||
return;
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{
|
||||
action: Action.EDIT,
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
eid: i,
|
||||
undo: editField,
|
||||
redo: { values: data.values },
|
||||
message: t("edit_enum", {
|
||||
enumName: data.name,
|
||||
extra: "[values]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
block
|
||||
icon={<IconDeleteStroked />}
|
||||
type="danger"
|
||||
onClick={() => deleteEnum(i, true)}
|
||||
>
|
||||
{t("delete")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
39
src/components/EditorSidePanel/EnumsTab/EnumsTab.jsx
Normal file
39
src/components/EditorSidePanel/EnumsTab/EnumsTab.jsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { Button, Collapse } from "@douyinfe/semi-ui";
|
||||
import { useEnums } from "../../../hooks";
|
||||
import { IconPlus } from "@douyinfe/semi-icons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import SearchBar from "./SearchBar";
|
||||
import EnumDetails from "./EnumDetails";
|
||||
|
||||
export default function EnumsTab() {
|
||||
const { enums, addEnum } = useEnums();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex gap-2">
|
||||
<SearchBar />
|
||||
<div>
|
||||
<Button icon={<IconPlus />} block onClick={() => addEnum()}>
|
||||
{t("add_enum")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Collapse>
|
||||
{enums.map((e, i) => (
|
||||
<Collapse.Panel
|
||||
key={e.name + i}
|
||||
header={
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{e.name}
|
||||
</div>
|
||||
}
|
||||
itemKey={`${i}`}
|
||||
>
|
||||
<EnumDetails data={e} i={i} />
|
||||
</Collapse.Panel>
|
||||
))}
|
||||
</Collapse>
|
||||
</div>
|
||||
);
|
||||
}
|
41
src/components/EditorSidePanel/EnumsTab/SearchBar.jsx
Normal file
41
src/components/EditorSidePanel/EnumsTab/SearchBar.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import { useState } from "react";
|
||||
import { AutoComplete } from "@douyinfe/semi-ui";
|
||||
import { IconSearch } from "@douyinfe/semi-icons";
|
||||
import { useEnums } from "../../../hooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function SearchBar() {
|
||||
const { enums } = useEnums();
|
||||
const [value, setValue] = useState("");
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [filteredResult, setFilteredResult] = useState(
|
||||
enums.map((e) => e.name),
|
||||
);
|
||||
|
||||
const handleStringSearch = (value) => {
|
||||
setFilteredResult(
|
||||
enums.map((e) => e.name).filter((i) => i.includes(value)),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<AutoComplete
|
||||
data={filteredResult}
|
||||
value={value}
|
||||
showClear
|
||||
prefix={<IconSearch />}
|
||||
placeholder={t("search")}
|
||||
onSearch={(v) => handleStringSearch(v)}
|
||||
emptyContent={<div className="p-3 popover-theme">{t("not_found")}</div>}
|
||||
onChange={(v) => setValue(v)}
|
||||
onSelect={(v) => {
|
||||
const i = enums.findIndex((t) => t.name === v);
|
||||
document
|
||||
.getElementById(`scroll_enum_${i}`)
|
||||
.scrollIntoView({ behavior: "smooth" });
|
||||
}}
|
||||
className="w-full"
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { Tabs, TabPane } from "@douyinfe/semi-ui";
|
||||
import { DB, Tab } from "../../data/constants";
|
||||
import { Tab } from "../../data/constants";
|
||||
import { useLayout, useSelect, useTables } from "../../hooks";
|
||||
import RelationshipsTab from "./RelationshipsTab/RelationshipsTab";
|
||||
import TypesTab from "./TypesTab/TypesTab";
|
||||
@ -9,6 +9,8 @@ import NotesTab from "./NotesTab/NotesTab";
|
||||
import TablesTab from "./TablesTab/TablesTab";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useMemo } from "react";
|
||||
import { databases } from "../../data/databases";
|
||||
import EnumsTab from "./EnumsTab/EnumsTab";
|
||||
|
||||
export default function SidePanel({ width, resize, setResize }) {
|
||||
const { layout } = useLayout();
|
||||
@ -27,13 +29,23 @@ export default function SidePanel({ width, resize, setResize }) {
|
||||
{ tab: t("subject_areas"), itemKey: Tab.AREAS, component: <AreasTab /> },
|
||||
{ tab: t("notes"), itemKey: Tab.NOTES, component: <NotesTab /> },
|
||||
];
|
||||
if (database === DB.GENERIC || database === DB.POSTGRES) {
|
||||
|
||||
if (databases[database].hasTypes) {
|
||||
tabs.push({
|
||||
tab: t("types"),
|
||||
itemKey: Tab.TYPES,
|
||||
component: <TypesTab />,
|
||||
});
|
||||
}
|
||||
|
||||
if (databases[database].hasEnums) {
|
||||
tabs.push({
|
||||
tab: t("enums"),
|
||||
itemKey: Tab.ENUMS,
|
||||
component: <EnumsTab />,
|
||||
});
|
||||
}
|
||||
|
||||
return tabs;
|
||||
}, [t, database]);
|
||||
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
useTypes,
|
||||
useTasks,
|
||||
useSaveState,
|
||||
useEnums,
|
||||
} from "../hooks";
|
||||
import FloatingControls from "./FloatingControls";
|
||||
import { Modal } from "@douyinfe/semi-ui";
|
||||
@ -37,6 +38,7 @@ export default function WorkSpace() {
|
||||
const { notes, setNotes } = useNotes();
|
||||
const { saveState, setSaveState } = useSaveState();
|
||||
const { transform, setTransform } = useTransform();
|
||||
const { enums, setEnums } = useEnums();
|
||||
const {
|
||||
tables,
|
||||
relationships,
|
||||
@ -71,12 +73,13 @@ export default function WorkSpace() {
|
||||
lastModified: new Date(),
|
||||
tables: tables,
|
||||
references: relationships,
|
||||
types: types,
|
||||
notes: notes,
|
||||
areas: areas,
|
||||
todos: tasks,
|
||||
pan: transform.pan,
|
||||
zoom: transform.zoom,
|
||||
...(databases[database].hasEnums && { enums: enums }),
|
||||
...(databases[database].hasTypes && { types: types }),
|
||||
})
|
||||
.then((id) => {
|
||||
setId(id);
|
||||
@ -92,12 +95,13 @@ export default function WorkSpace() {
|
||||
lastModified: new Date(),
|
||||
tables: tables,
|
||||
references: relationships,
|
||||
types: types,
|
||||
notes: notes,
|
||||
areas: areas,
|
||||
todos: tasks,
|
||||
pan: transform.pan,
|
||||
zoom: transform.zoom,
|
||||
...(databases[database].hasEnums && { enums: enums }),
|
||||
...(databases[database].hasTypes && { types: types }),
|
||||
})
|
||||
.then(() => {
|
||||
setSaveState(State.SAVED);
|
||||
@ -111,12 +115,13 @@ export default function WorkSpace() {
|
||||
title: title,
|
||||
tables: tables,
|
||||
relationships: relationships,
|
||||
types: types,
|
||||
notes: notes,
|
||||
subjectAreas: areas,
|
||||
todos: tasks,
|
||||
pan: transform.pan,
|
||||
zoom: transform.zoom,
|
||||
...(databases[database].hasEnums && { enums: enums }),
|
||||
...(databases[database].hasTypes && { types: types }),
|
||||
})
|
||||
.then(() => {
|
||||
setSaveState(State.SAVED);
|
||||
@ -138,6 +143,7 @@ export default function WorkSpace() {
|
||||
transform,
|
||||
setSaveState,
|
||||
database,
|
||||
enums,
|
||||
]);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
@ -158,9 +164,14 @@ export default function WorkSpace() {
|
||||
setRelationships(d.references);
|
||||
setNotes(d.notes);
|
||||
setAreas(d.areas);
|
||||
setTypes(d.types);
|
||||
setTasks(d.todos ?? []);
|
||||
setTransform({ pan: d.pan, zoom: d.zoom });
|
||||
if (databases[database].hasTypes) {
|
||||
setTypes(d.types ?? []);
|
||||
}
|
||||
if (databases[database].hasEnums) {
|
||||
setEnums(d.enums ?? []);
|
||||
}
|
||||
window.name = `d ${d.id}`;
|
||||
} else {
|
||||
window.name = "";
|
||||
@ -184,7 +195,6 @@ export default function WorkSpace() {
|
||||
setId(diagram.id);
|
||||
setTitle(diagram.name);
|
||||
setTables(diagram.tables);
|
||||
setTypes(diagram.types);
|
||||
setRelationships(diagram.references);
|
||||
setAreas(diagram.areas);
|
||||
setNotes(diagram.notes);
|
||||
@ -195,6 +205,12 @@ export default function WorkSpace() {
|
||||
});
|
||||
setUndoStack([]);
|
||||
setRedoStack([]);
|
||||
if (databases[database].hasTypes) {
|
||||
setTypes(diagram.types ?? []);
|
||||
}
|
||||
if (databases[database].hasEnums) {
|
||||
setEnums(diagram.enums ?? []);
|
||||
}
|
||||
window.name = `d ${diagram.id}`;
|
||||
} else {
|
||||
window.name = "";
|
||||
@ -218,7 +234,6 @@ export default function WorkSpace() {
|
||||
setId(diagram.id);
|
||||
setTitle(diagram.title);
|
||||
setTables(diagram.tables);
|
||||
setTypes(diagram.types);
|
||||
setRelationships(diagram.relationships);
|
||||
setAreas(diagram.subjectAreas);
|
||||
setTasks(diagram.todos ?? []);
|
||||
@ -229,6 +244,12 @@ export default function WorkSpace() {
|
||||
});
|
||||
setUndoStack([]);
|
||||
setRedoStack([]);
|
||||
if (databases[database].hasTypes) {
|
||||
setTypes(diagram.types ?? []);
|
||||
}
|
||||
if (databases[database].hasEnums) {
|
||||
setEnums(diagram.enums ?? []);
|
||||
}
|
||||
} else {
|
||||
setShowSelectDbModal(true);
|
||||
}
|
||||
@ -269,6 +290,8 @@ export default function WorkSpace() {
|
||||
setTypes,
|
||||
setTasks,
|
||||
setDatabase,
|
||||
database,
|
||||
setEnums,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
82
src/context/EnumsContext.jsx
Normal file
82
src/context/EnumsContext.jsx
Normal file
@ -0,0 +1,82 @@
|
||||
import { createContext, useState } from "react";
|
||||
import { Action, ObjectType } from "../data/constants";
|
||||
import { Toast } from "@douyinfe/semi-ui";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useUndoRedo } from "../hooks";
|
||||
|
||||
export const EnumsContext = createContext(null);
|
||||
|
||||
export default function EnumsContextProvider({ children }) {
|
||||
const { t } = useTranslation();
|
||||
const [enums, setEnums] = useState([]);
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
|
||||
const addEnum = (data, addToHistory = true) => {
|
||||
if (data) {
|
||||
setEnums((prev) => {
|
||||
const temp = prev.slice();
|
||||
temp.splice(data.id, 0, data);
|
||||
return temp;
|
||||
});
|
||||
} else {
|
||||
setEnums((prev) => [
|
||||
...prev,
|
||||
{
|
||||
name: `enum_${prev.length}`,
|
||||
values: [],
|
||||
},
|
||||
]);
|
||||
}
|
||||
if (addToHistory) {
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{
|
||||
action: Action.ADD,
|
||||
element: ObjectType.ENUM,
|
||||
message: t("add_enum"),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteEnum = (id, addToHistory = true) => {
|
||||
if (addToHistory) {
|
||||
Toast.success(t("enum_deleted"));
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{
|
||||
action: Action.DELETE,
|
||||
element: ObjectType.ENUM,
|
||||
id: id,
|
||||
data: enums[id],
|
||||
message: t("delete_enum", {
|
||||
enumName: enums[id].name,
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
}
|
||||
setEnums((prev) => prev.filter((e, i) => i !== id));
|
||||
};
|
||||
|
||||
const updateEnum = (id, values) => {
|
||||
setEnums((prev) =>
|
||||
prev.map((e, i) => (i === id ? { ...e, ...values } : e)),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<EnumsContext.Provider
|
||||
value={{
|
||||
enums,
|
||||
setEnums,
|
||||
addEnum,
|
||||
updateEnum,
|
||||
deleteEnum,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</EnumsContext.Provider>
|
||||
);
|
||||
}
|
@ -10,7 +10,7 @@ export const TablesContext = createContext(null);
|
||||
|
||||
export default function TablesContextProvider({ children }) {
|
||||
const { t } = useTranslation();
|
||||
const [database, setDatabase] = useState("");
|
||||
const [database, setDatabase] = useState(DB.GENERIC);
|
||||
const [tables, setTables] = useState([]);
|
||||
const [relationships, setRelationships] = useState([]);
|
||||
const { transform } = useTransform();
|
||||
|
@ -50,6 +50,7 @@ export const Tab = {
|
||||
AREAS: "3",
|
||||
NOTES: "4",
|
||||
TYPES: "5",
|
||||
ENUMS: "6",
|
||||
};
|
||||
|
||||
export const ObjectType = {
|
||||
@ -59,6 +60,7 @@ export const ObjectType = {
|
||||
NOTE: 3,
|
||||
RELATIONSHIP: 4,
|
||||
TYPE: 5,
|
||||
ENUM: 6,
|
||||
};
|
||||
|
||||
export const Action = {
|
||||
|
@ -18,6 +18,7 @@ export const databases = {
|
||||
label: DB.POSTGRES,
|
||||
image: postgresImage,
|
||||
hasTypes: true,
|
||||
hasEnums: true,
|
||||
},
|
||||
[DB.SQLITE]: {
|
||||
name: "SQLite",
|
||||
|
@ -9,3 +9,4 @@ export { default as useTasks } from "./useTasks";
|
||||
export { default as useTransform } from "./useTransform";
|
||||
export { default as useTypes } from "./useTypes";
|
||||
export { default as useUndoRedo } from "./useUndoRedo";
|
||||
export { default as useEnums } from "./useEnums";
|
||||
|
6
src/hooks/useEnums.js
Normal file
6
src/hooks/useEnums.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { useContext } from "react";
|
||||
import { EnumsContext } from "../context/EnumsContext";
|
||||
|
||||
export default function useEnums() {
|
||||
return useContext(EnumsContext);
|
||||
}
|
@ -8,6 +8,7 @@ import NotesContextProvider from "../context/NotesContext";
|
||||
import TypesContextProvider from "../context/TypesContext";
|
||||
import TasksContextProvider from "../context/TasksContext";
|
||||
import SaveStateContextProvider from "../context/SaveStateContext";
|
||||
import EnumsContextProvider from "../context/EnumsContext";
|
||||
import WorkSpace from "../components/Workspace";
|
||||
|
||||
export default function Editor() {
|
||||
@ -20,11 +21,13 @@ export default function Editor() {
|
||||
<AreasContextProvider>
|
||||
<NotesContextProvider>
|
||||
<TypesContextProvider>
|
||||
<TablesContextProvider>
|
||||
<SaveStateContextProvider>
|
||||
<WorkSpace />
|
||||
</SaveStateContextProvider>
|
||||
</TablesContextProvider>
|
||||
<EnumsContextProvider>
|
||||
<TablesContextProvider>
|
||||
<SaveStateContextProvider>
|
||||
<WorkSpace />
|
||||
</SaveStateContextProvider>
|
||||
</TablesContextProvider>
|
||||
</EnumsContextProvider>
|
||||
</TypesContextProvider>
|
||||
</NotesContextProvider>
|
||||
</AreasContextProvider>
|
||||
|
Loading…
Reference in New Issue
Block a user