diff --git a/src/api/gists.js b/src/api/gists.js index d62c79e..87c15e9 100644 --- a/src/api/gists.js +++ b/src/api/gists.js @@ -43,3 +43,9 @@ export async function getCommits(gistId, perPage = 20, page = 1) { return res.data; } + +export async function getVersion(gistId, sha) { + const res = await axios.get(`${baseUrl}/gists/${gistId}/${sha}`); + + return res.data; +} diff --git a/src/components/EditorCanvas/Area.jsx b/src/components/EditorCanvas/Area.jsx index 4af88b6..c031e52 100644 --- a/src/components/EditorCanvas/Area.jsx +++ b/src/components/EditorCanvas/Area.jsx @@ -257,6 +257,7 @@ function EditPopoverContent({ data }) { const { updateArea, deleteArea } = useAreas(); const { setUndoStack, setRedoStack } = useUndoRedo(); const { t } = useTranslation(); + const {layout} = useLayout(); const initialColorRef = useRef(data.color); const handleColorPick = (color) => { @@ -302,6 +303,7 @@ function EditPopoverContent({ data }) { value={data.name} placeholder={t("name")} className="me-2" + readOnly={layout.readOnly} onChange={(value) => updateArea(data.id, { name: value })} onFocus={(e) => setEditField({ name: e.target.value })} onBlur={(e) => { @@ -325,6 +327,7 @@ function EditPopoverContent({ data }) { /> updateArea(data.id, { color })} onColorPick={(color) => handleColorPick(color)} diff --git a/src/components/EditorCanvas/Canvas.jsx b/src/components/EditorCanvas/Canvas.jsx index d37cfe0..7f8fc98 100644 --- a/src/components/EditorCanvas/Canvas.jsx +++ b/src/components/EditorCanvas/Canvas.jsx @@ -279,21 +279,24 @@ export default function Canvas() { if (!e.isPrimary) return; + if (panning.isPanning) { setTransform((prev) => ({ ...prev, pan: { x: - panning.panStart.x + - (panning.cursorStart.x - pointer.spaces.screen.x) / transform.zoom, + panning.panStart.x + + (panning.cursorStart.x - pointer.spaces.screen.x) / transform.zoom, y: - panning.panStart.y + - (panning.cursorStart.y - pointer.spaces.screen.y) / transform.zoom, + panning.panStart.y + + (panning.cursorStart.y - pointer.spaces.screen.y) / transform.zoom, }, })); return; } + if(layout.readOnly) return; + if (linking) { setLinkingLine({ ...linkingLine, diff --git a/src/components/EditorHeader/ControlPanel.jsx b/src/components/EditorHeader/ControlPanel.jsx index bc77c5f..8e6ebbe 100644 --- a/src/components/EditorHeader/ControlPanel.jsx +++ b/src/components/EditorHeader/ControlPanel.jsx @@ -126,7 +126,7 @@ export default function ControlPanel({ const { selectedElement, setSelectedElement } = useSelect(); const { transform, setTransform } = useTransform(); const { t, i18n } = useTranslation(); - const { setGistId } = useContext(IdContext); + const { version, setGistId } = useContext(IdContext); const navigate = useNavigate(); const invertLayout = (component) => @@ -1751,7 +1751,7 @@ export default function ControlPanel({ /> )}
e.isPrimary && setShowEditName(true)} onPointerLeave={(e) => e.isPrimary && setShowEditName(false)} onPointerDown={(e) => { @@ -1761,12 +1761,20 @@ export default function ControlPanel({ }} onClick={() => setModal(MODAL.RENAME)} > - {window.name.split(" ")[0] === "t" ? "Templates/" : "Diagrams/"} - {title} + + {(window.name.split(" ")[0] === "t" + ? "Templates/" + : "Diagrams/") + title} + + {version && ( + + {version.substring(0, 7)} + + )}
{(showEditName || modal === MODAL.RENAME) && } -
+
{Object.keys(menu).map((category) => ( ))}
- + {layout.readOnly && ( + + {t("read_only")} + + )} + {!layout.readOnly && ( + + ) : null + } + > + {getState()} + + )}
diff --git a/src/components/EditorHeader/Modal/Rename.jsx b/src/components/EditorHeader/Modal/Rename.jsx index 522d5e6..3a8861e 100644 --- a/src/components/EditorHeader/Modal/Rename.jsx +++ b/src/components/EditorHeader/Modal/Rename.jsx @@ -1,14 +1,17 @@ import { Input } from "@douyinfe/semi-ui"; import { useTranslation } from "react-i18next"; +import { useLayout } from "../../../hooks"; export default function Rename({ title, setTitle }) { const { t } = useTranslation(); + const { layout } = useLayout(); return ( setTitle(v)} + readonly={layout.readOnly} /> ); } diff --git a/src/components/EditorHeader/Modal/SetTableWidth.jsx b/src/components/EditorHeader/Modal/SetTableWidth.jsx index 93482e6..aed463c 100644 --- a/src/components/EditorHeader/Modal/SetTableWidth.jsx +++ b/src/components/EditorHeader/Modal/SetTableWidth.jsx @@ -1,13 +1,15 @@ import { InputNumber } from "@douyinfe/semi-ui"; -import { useSettings } from "../../../hooks"; +import { useLayout, useSettings } from "../../../hooks"; export default function SetTableWidth() { + const { layout } = useLayout(); const { settings, setSettings } = useSettings(); return ( { if (c < 180) return; setSettings((prev) => ({ ...prev, tableWidth: c })); diff --git a/src/components/EditorHeader/Modal/Share.jsx b/src/components/EditorHeader/Modal/Share.jsx index 87f843c..5826697 100644 --- a/src/components/EditorHeader/Modal/Share.jsx +++ b/src/components/EditorHeader/Modal/Share.jsx @@ -116,7 +116,7 @@ export default function Share({ title, setModal }) { {!error && ( <>
- +
{t("share_info")}
diff --git a/src/components/EditorHeader/SideSheet/Revisions.jsx b/src/components/EditorHeader/SideSheet/Revisions.jsx index ca1fc5a..4a312ef 100644 --- a/src/components/EditorHeader/SideSheet/Revisions.jsx +++ b/src/components/EditorHeader/SideSheet/Revisions.jsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from "react"; +import { useCallback, useContext, useEffect, useState } from "react"; import { IdContext } from "../../Workspace"; import { useTranslation } from "react-i18next"; import { Button, IconButton, Spin, Steps, Tag, Toast } from "@douyinfe/semi-ui"; @@ -7,15 +7,41 @@ import { IconChevronRight, IconChevronLeft, } from "@douyinfe/semi-icons"; -import { getCommits } from "../../../api/gists"; +import { getCommits, getVersion } from "../../../api/gists"; import { DateTime } from "luxon"; +import { useAreas, useDiagram, useLayout } from "../../../hooks"; export default function Revisions({ open }) { - const { gistId } = useContext(IdContext); + const { gistId, setVersion } = useContext(IdContext); + const { setAreas } = useAreas(); + const { setLayout } = useLayout(); + const { setTables, setRelationships } = useDiagram(); const { t, i18n } = useTranslation(); const [isLoading, setIsLoading] = useState(false); const [revisions, setRevisions] = useState([]); + const loadVersion = useCallback( + async (sha) => { + try { + const version = await getVersion(gistId, sha); + setVersion(sha); + setLayout((prev) => ({ ...prev, readOnly: true })); + + const content = version.data.files["share.json"].content; + + const parsedDiagram = JSON.parse(content); + + setTables(parsedDiagram.tables); + setRelationships(parsedDiagram.relationships); + setAreas(parsedDiagram.subjectAreas); + } catch (e) { + console.log(e); + Toast.error("failed_to_load_diagram"); + } + }, + [gistId, setTables, setRelationships, setAreas, setVersion, setLayout], + ); + useEffect(() => { const getRevisions = async (gistId) => { try { @@ -62,9 +88,7 @@ export default function Revisions({ open }) { {revisions.map((r, i) => ( { - alert(r.version); - }} + onClick={() => loadVersion(r.version)} title={
{`${t("version")} ${revisions.length - i}`} diff --git a/src/components/EditorSidePanel/AreasTab/AreaDetails.jsx b/src/components/EditorSidePanel/AreasTab/AreaDetails.jsx index 7686263..de677e7 100644 --- a/src/components/EditorSidePanel/AreasTab/AreaDetails.jsx +++ b/src/components/EditorSidePanel/AreasTab/AreaDetails.jsx @@ -2,12 +2,13 @@ import { useState, useRef } from "react"; import { Button, Input } from "@douyinfe/semi-ui"; import ColorPicker from "../ColorPicker"; import { IconDeleteStroked } from "@douyinfe/semi-icons"; -import { useAreas, useUndoRedo } from "../../../hooks"; +import { useAreas, useLayout, useUndoRedo } from "../../../hooks"; import { Action, ObjectType } from "../../../data/constants"; import { useTranslation } from "react-i18next"; export default function AreaInfo({ data, i }) { const { t } = useTranslation(); + const { layout } = useLayout(); const { deleteArea, updateArea } = useAreas(); const { setUndoStack, setRedoStack } = useUndoRedo(); const [editField, setEditField] = useState({}); @@ -53,6 +54,7 @@ export default function AreaInfo({ data, i }) { updateArea(data.id, { name: value })} onFocus={(e) => setEditField({ name: e.target.value })} onBlur={(e) => { @@ -77,12 +79,14 @@ export default function AreaInfo({ data, i }) { updateArea(i, { color })} onColorPick={(color) => handleColorPick(color)} />
diff --git a/src/components/EditorSidePanel/ColorPicker.jsx b/src/components/EditorSidePanel/ColorPicker.jsx index c0ed312..3bb4959 100644 --- a/src/components/EditorSidePanel/ColorPicker.jsx +++ b/src/components/EditorSidePanel/ColorPicker.jsx @@ -2,8 +2,9 @@ import { ColorPicker as SemiColorPicker } from "@douyinfe/semi-ui"; import { useState } from "react"; export default function ColorPicker({ - children, value, + readOnly, + children, onChange, onColorPick, ...props @@ -25,6 +26,7 @@ export default function ColorPicker({ {...props} value={SemiColorPicker.colorStringToValue(value)} onChange={({ hex: color }) => { + if (readOnly) return; setPickedColor(color); onChange(color); }} diff --git a/src/components/EditorSidePanel/EnumsTab/EnumDetails.jsx b/src/components/EditorSidePanel/EnumsTab/EnumDetails.jsx index c84e18e..4500275 100644 --- a/src/components/EditorSidePanel/EnumsTab/EnumDetails.jsx +++ b/src/components/EditorSidePanel/EnumsTab/EnumDetails.jsx @@ -1,12 +1,13 @@ import { useState } from "react"; import { Button, Input, TagInput } from "@douyinfe/semi-ui"; import { IconDeleteStroked } from "@douyinfe/semi-icons"; -import { useDiagram, useEnums, useUndoRedo } from "../../../hooks"; +import { useDiagram, useEnums, useLayout, useUndoRedo } from "../../../hooks"; import { Action, ObjectType } from "../../../data/constants"; import { useTranslation } from "react-i18next"; export default function EnumDetails({ data, i }) { const { t } = useTranslation(); + const { layout } = useLayout(); const { deleteEnum, updateEnum } = useEnums(); const { tables, updateField } = useDiagram(); const { setUndoStack, setRedoStack } = useUndoRedo(); @@ -18,6 +19,7 @@ export default function EnumDetails({ data, i }) {
{t("Name")}:
{ @@ -71,7 +73,11 @@ export default function EnumDetails({ data, i }) { className="my-2" placeholder={t("values")} validateStatus={data.values.length === 0 ? "error" : "default"} - onChange={(v) => updateEnum(i, { values: v })} + onChange={(v) => { + if (layout.readOnly) return; + + updateEnum(i, { values: v }); + }} onFocus={() => setEditField({ values: data.values })} onBlur={() => { if (JSON.stringify(editField.values) === JSON.stringify(data.values)) @@ -95,8 +101,9 @@ export default function EnumDetails({ data, i }) { />
diff --git a/src/components/EditorSidePanel/NotesTab/NoteInfo.jsx b/src/components/EditorSidePanel/NotesTab/NoteInfo.jsx index d03289a..8a52e2d 100644 --- a/src/components/EditorSidePanel/NotesTab/NoteInfo.jsx +++ b/src/components/EditorSidePanel/NotesTab/NoteInfo.jsx @@ -3,10 +3,11 @@ import { Button, Collapse, TextArea, Input } from "@douyinfe/semi-ui"; import ColorPicker from "../ColorPicker"; import { IconDeleteStroked } from "@douyinfe/semi-icons"; import { Action, ObjectType } from "../../../data/constants"; -import { useNotes, useUndoRedo } from "../../../hooks"; +import { useLayout, useNotes, useUndoRedo } from "../../../hooks"; import { useTranslation } from "react-i18next"; export default function NoteInfo({ data, nid }) { + const { layout } = useLayout(); const { updateNote, deleteNote } = useNotes(); const { setUndoStack, setRedoStack } = useUndoRedo(); const [editField, setEditField] = useState({}); @@ -62,6 +63,7 @@ export default function NoteInfo({ data, nid }) {
{t("title")}:
updateNote(data.id, { title: value })} onFocus={(e) => setEditField({ title: e.target.value })} @@ -90,6 +92,7 @@ export default function NoteInfo({ data, nid }) { placeholder={t("content")} value={data.content} autosize + readonly={layout.readOnly} onChange={(value) => { const textarea = document.getElementById(`note_${data.id}`); textarea.style.height = "0"; @@ -127,13 +130,15 @@ export default function NoteInfo({ data, nid }) {
updateNote(data.id, { color })} onColorPick={(color) => handleColorPick(color)} />
diff --git a/src/components/EditorSidePanel/NotesTab/NotesTab.jsx b/src/components/EditorSidePanel/NotesTab/NotesTab.jsx index 07b6860..5b6b033 100644 --- a/src/components/EditorSidePanel/NotesTab/NotesTab.jsx +++ b/src/components/EditorSidePanel/NotesTab/NotesTab.jsx @@ -1,6 +1,6 @@ import { Button, Collapse } from "@douyinfe/semi-ui"; import { IconPlus } from "@douyinfe/semi-icons"; -import { useNotes, useSelect } from "../../../hooks"; +import { useLayout, useNotes, useSelect } from "../../../hooks"; import Empty from "../Empty"; import SearchBar from "./SearchBar"; import NoteInfo from "./NoteInfo"; @@ -10,6 +10,7 @@ export default function NotesTab() { const { notes, addNote } = useNotes(); const { selectedElement, setSelectedElement } = useSelect(); const { t } = useTranslation(); + const { layout } = useLayout(); return ( <> @@ -23,7 +24,12 @@ export default function NotesTab() { } />
-
diff --git a/src/components/EditorSidePanel/RelationshipsTab/RelationshipInfo.jsx b/src/components/EditorSidePanel/RelationshipsTab/RelationshipInfo.jsx index 2d7f74d..ae111a4 100644 --- a/src/components/EditorSidePanel/RelationshipsTab/RelationshipInfo.jsx +++ b/src/components/EditorSidePanel/RelationshipsTab/RelationshipInfo.jsx @@ -18,7 +18,7 @@ import { Action, ObjectType, } from "../../../data/constants"; -import { useDiagram, useUndoRedo } from "../../../hooks"; +import { useDiagram, useLayout, useUndoRedo } from "../../../hooks"; import i18n from "../../../i18n/i18n"; import { useTranslation } from "react-i18next"; import { useMemo, useState } from "react"; @@ -38,6 +38,7 @@ export default function RelationshipInfo({ data }) { const { setUndoStack, setRedoStack } = useUndoRedo(); const { tables, deleteRelationship, updateRelationship } = useDiagram(); const { t } = useTranslation(); + const { layout } = useLayout(); const [editField, setEditField] = useState({}); const relValues = useMemo(() => { @@ -98,6 +99,8 @@ export default function RelationshipInfo({ data }) { }; const changeCardinality = (value) => { + if (layout.readOnly) return; + setUndoStack((prev) => [ ...prev, { @@ -117,6 +120,8 @@ export default function RelationshipInfo({ data }) { }; const changeConstraint = (key, value) => { + if (layout.readOnly) return; + const undoKey = `${key}Constraint`; setUndoStack((prev) => [ ...prev, @@ -145,6 +150,7 @@ export default function RelationshipInfo({ data }) { validateStatus={data.name.trim() === "" ? "error" : "default"} placeholder={t("name")} className="ms-2" + readonly={layout.readOnly} onChange={(value) => updateRelationship(data.id, { name: value })} onFocus={(e) => setEditField({ name: e.target.value })} onBlur={(e) => { @@ -196,9 +202,10 @@ export default function RelationshipInfo({ data }) { />
@@ -285,9 +292,10 @@ export default function RelationshipInfo({ data }) {