Add notes

This commit is contained in:
1ilit 2023-09-19 15:49:09 +03:00
parent 395ba77a37
commit 6f7dbd9f41
7 changed files with 223 additions and 48 deletions

View File

@ -9,12 +9,14 @@ import {
} from "../data/data"; } from "../data/data";
import Area from "./area"; import Area from "./area";
import Relationship from "./relationship"; import Relationship from "./relationship";
import { AreaContext, TableContext } from "../pages/editor"; import { AreaContext, NoteContext, TableContext } from "../pages/editor";
import Note from "./note";
export default function Canvas(props) { export default function Canvas(props) {
const { tables, setTables, relationships, setRelationships } = const { tables, setTables, relationships, setRelationships } =
useContext(TableContext); useContext(TableContext);
const { areas, setAreas } = useContext(AreaContext); const { areas, setAreas } = useContext(AreaContext);
const { notes, setNotes } = useContext(NoteContext);
const [dragging, setDragging] = useState([ObjectType.NONE, -1]); const [dragging, setDragging] = useState([ObjectType.NONE, -1]);
const [linking, setLinking] = useState(false); const [linking, setLinking] = useState(false);
const [line, setLine] = useState({ const [line, setLine] = useState({
@ -68,6 +70,13 @@ export default function Canvas(props) {
y: clientY - area.y, y: clientY - area.y,
}); });
setDragging([ObjectType.AREA, id]); setDragging([ObjectType.AREA, id]);
} else if (type === ObjectType.NOTE) {
const note = notes.find((t) => t.id === id);
setOffset({
x: clientX - note.x,
y: clientY - note.y,
});
setDragging([ObjectType.NOTE, id]);
} }
}; };
@ -91,8 +100,8 @@ export default function Canvas(props) {
const dy = e.clientY - panOffset.y; const dy = e.clientY - panOffset.y;
setPanOffset({ x: e.clientX, y: e.clientY }); setPanOffset({ x: e.clientX, y: e.clientY });
setTables( setTables((prev) =>
tables.map((t) => ({ prev.map((t) => ({
...t, ...t,
x: t.x + dx, x: t.x + dx,
y: t.y + dy, y: t.y + dy,
@ -109,13 +118,9 @@ export default function Canvas(props) {
})) }))
); );
setAreas( setAreas((prev) => prev.map((t) => ({ ...t, x: t.x + dx, y: t.y + dy })));
areas.map((t) => ({
...t, setNotes((prev) => prev.map((n) => ({ ...n, x: n.x + dx, y: n.y + dy })));
x: t.x + dx,
y: t.y + dy,
}))
);
} else if (dragging[0] === ObjectType.TABLE && dragging[1] >= 0) { } else if (dragging[0] === ObjectType.TABLE && dragging[1] >= 0) {
const updatedTables = tables.map((t) => { const updatedTables = tables.map((t) => {
if (t.id === dragging[1]) { if (t.id === dragging[1]) {
@ -163,6 +168,22 @@ export default function Canvas(props) {
return t; return t;
}); });
setAreas(updatedAreas); setAreas(updatedAreas);
} else if (
dragging[0] === ObjectType.NOTE &&
dragging[1] >= 0
) {
setNotes((prev) =>
prev.map((t) => {
if (t.id === dragging[1]) {
return {
...t,
x: e.clientX - offset.x,
y: e.clientY - offset.y,
};
}
return t;
})
);
} else if (areaResize.id !== -1) { } else if (areaResize.id !== -1) {
if (areaResize.dir === "none") return; if (areaResize.dir === "none") return;
@ -374,14 +395,12 @@ export default function Canvas(props) {
key={a.id} key={a.id}
areaData={a} areaData={a}
onMouseDown={(e) => handleMouseDownRect(e, a.id, ObjectType.AREA)} onMouseDown={(e) => handleMouseDownRect(e, a.id, ObjectType.AREA)}
resize={areaResize}
setResize={setAreaResize} setResize={setAreaResize}
initCoords={initCoords} initCoords={initCoords}
setInitCoords={setInitCoords} setInitCoords={setInitCoords}
setAreas={setAreas}
></Area> ></Area>
))} ))}
{tables.map((table, i) => ( {tables.map((table) => (
<Table <Table
key={table.id} key={table.id}
tableData={table} tableData={table}
@ -405,6 +424,13 @@ export default function Canvas(props) {
{relationships.map((e, i) => ( {relationships.map((e, i) => (
<Relationship key={i} data={e} /> <Relationship key={i} data={e} />
))} ))}
{notes.map((n) => (
<Note
key={n.id}
data={n}
onMouseDown={(e) => handleMouseDownRect(e, n.id, ObjectType.NOTE)}
></Note>
))}
</svg> </svg>
</div> </div>
</div> </div>

View File

@ -626,7 +626,7 @@ export default function ControlPanel(props) {
</Dropdown.Item> </Dropdown.Item>
<Dropdown.Item <Dropdown.Item
icon={ icon={
layout.shapes ? ( layout.notes ? (
<IconCheckboxTick /> <IconCheckboxTick />
) : ( ) : (
<div className="px-2"></div> <div className="px-2"></div>
@ -635,11 +635,11 @@ export default function ControlPanel(props) {
onClick={() => onClick={() =>
setLayout((prev) => ({ setLayout((prev) => ({
...prev, ...prev,
shapes: !prev.shapes, notes: !prev.notes,
})) }))
} }
> >
Shapes Notes
</Dropdown.Item> </Dropdown.Item>
</Dropdown.Menu> </Dropdown.Menu>
} }

View File

@ -9,6 +9,7 @@ import ReferenceOverview from "./reference_overview";
import AreaOverview from "./area_overview"; import AreaOverview from "./area_overview";
import { Tab } from "../data/data"; import { Tab } from "../data/data";
import { TabContext } from "../pages/editor"; import { TabContext } from "../pages/editor";
import NotesOverview from "./notes_overview";
const myTheme = createTheme({ const myTheme = createTheme({
dark: "light", dark: "light",
@ -32,8 +33,8 @@ const EditorPanel = (props) => {
{ tab: "Tables", itemKey: Tab.tables }, { tab: "Tables", itemKey: Tab.tables },
{ tab: "Relationships", itemKey: Tab.relationships }, { tab: "Relationships", itemKey: Tab.relationships },
{ tab: "Subject Areas", itemKey: Tab.subject_areas }, { tab: "Subject Areas", itemKey: Tab.subject_areas },
// { tab: "Shapes", itemKey: Tab.shapes },
{ tab: "Editor", itemKey: Tab.editor }, { tab: "Editor", itemKey: Tab.editor },
{ tab: "Notes", itemKey: Tab.notes },
]; ];
const contentList = [ const contentList = [
<TableOverview <TableOverview
@ -51,6 +52,7 @@ const EditorPanel = (props) => {
props.setCode(e); props.setCode(e);
}} }}
/>, />,
<NotesOverview/>
]; ];
return ( return (

78
src/components/note.jsx Normal file
View File

@ -0,0 +1,78 @@
import React, { useContext, useState } from "react";
import { NoteContext } from "../pages/editor";
export default function Note(props) {
const { setNotes } = useContext(NoteContext);
const [size, setSize] = useState({ w: 180, h: 88 });
const r = 3;
const fold = 24;
const handleChange = (e) => {
const textarea = document.getElementById(`note_${props.data.id}`);
const newHeight = textarea.scrollHeight + 16 + 20 + 4;
setSize((prevSize) => ({ ...prevSize, h: newHeight }));
textarea.style.height = "0";
textarea.style.height = textarea.scrollHeight + "px";
setNotes((prev) =>
prev.map((n) => {
if (n.id === props.data.id) {
return { ...n, content: e.target.value };
}
return n;
})
);
};
return (
<g>
<path
d={`M${props.data.x + fold} ${props.data.y} L${
props.data.x + size.w - r
} ${props.data.y} A${r} ${r} 0 0 1 ${props.data.x + size.w} ${
props.data.y + r
} L${props.data.x + size.w} ${
props.data.y + size.h - r
} A${r} ${r} 0 0 1 ${props.data.x + size.w - r} ${
props.data.y + size.h
} L${props.data.x + r} ${props.data.y + size.h} A${r} ${r} 0 0 1 ${
props.data.x
} ${props.data.y + size.h - r} L${props.data.x} ${props.data.y + fold}`}
fill="#fcf7ac"
stroke="#665b25"
strokeLinejoin="round"
strokeWidth="0.6"
/>
<path
d={`M${props.data.x} ${props.data.y + fold} L${
props.data.x + fold - r
} ${props.data.y + fold} A${r} ${r} 0 0 0 ${props.data.x + fold} ${
props.data.y + fold - r
} L${props.data.x + fold} ${props.data.y} L${props.data.x} ${
props.data.y + fold
} Z`}
fill="#fcf7ac"
stroke="#665b25"
strokeLinejoin="round"
strokeWidth="0.6"
/>
<foreignObject
x={props.data.x}
y={props.data.y}
width={size.w}
height={size.h}
onMouseDown={props.onMouseDown}
>
<div className="text-gray-900 select-none w-full h-full cursor-move px-3 py-2">
<label htmlFor={`note_${props.data.id}`} className="ms-5">{props.data.title}</label>
<textarea
id={`note_${props.data.id}`}
value={props.data.content}
onInput={handleChange}
className="mt-1 w-full resize-none outline-none overflow-y-hidden border-none select-none bg-[#fcf7ac]"
></textarea>
</div>
</foreignObject>
</g>
);
}

View File

@ -0,0 +1,65 @@
import React, { useContext } from "react";
import {
Empty,
Row,
Col,
Button,
// Input,
// Popover,
// Toast,
} from "@douyinfe/semi-ui";
import {
IllustrationNoContent,
IllustrationNoContentDark,
} from "@douyinfe/semi-illustrations";
import { IconPlus } from "@douyinfe/semi-icons";
import { NoteContext } from "../pages/editor";
export default function NotesOverview(props) {
const { notes, setNotes } = useContext(NoteContext);
return (
<div>
<Row gutter={6}>
<Col span={24}>
<Button
icon={<IconPlus />}
block
onClick={() => {
const newNote = {
id: notes.length,
x: 0,
y: 0,
title: `note_${notes.length}`,
content: ""
};
setNotes((prev) => [...prev, newNote]);
}}
>
Add note
</Button>
</Col>
</Row>
{notes.length <= 0 ? (
<div className="select-none mt-2">
<Empty
image={
<IllustrationNoContent style={{ width: 160, height: 160 }} />
}
darkModeImage={
<IllustrationNoContentDark style={{ width: 160, height: 160 }} />
}
title="No text notes"
description="Add notes cuz why not!"
/>
</div>
) : (
<div className="p-2">
{notes.map((n, i) => (
<div key={n.id}>{n.title}</div>
))}
</div>
)}
</div>
);
}

View File

@ -61,8 +61,8 @@ const Tab = {
tables: "1", tables: "1",
relationships: "2", relationships: "2",
subject_areas: "3", subject_areas: "3",
// shapes: "4",
editor: "4", editor: "4",
notes: "5",
}; };
const ObjectType = { const ObjectType = {

View File

@ -11,12 +11,14 @@ export const LayoutContext = createContext();
export const TableContext = createContext(); export const TableContext = createContext();
export const AreaContext = createContext(); export const AreaContext = createContext();
export const TabContext = createContext(); export const TabContext = createContext();
export const NoteContext = createContext();
export default function Editor(props) { export default function Editor(props) {
const [code, setCode] = useState(""); const [code, setCode] = useState("");
const [tables, setTables] = useState([]); const [tables, setTables] = useState([]);
const [relationships, setRelationships] = useState([]); const [relationships, setRelationships] = useState([]);
const [areas, setAreas] = useState([]); const [areas, setAreas] = useState([]);
const [notes, setNotes] = useState([]);
const [resize, setResize] = useState(false); const [resize, setResize] = useState(false);
const [width, setWidth] = useState(340); const [width, setWidth] = useState(340);
const [selectedTable, setSelectedTable] = useState(""); const [selectedTable, setSelectedTable] = useState("");
@ -30,7 +32,7 @@ export default function Editor(props) {
relationships: true, relationships: true,
issues: true, issues: true,
editor: true, editor: true,
shapes: true, notes: true,
fullscreen: false, fullscreen: false,
}); });
@ -50,41 +52,43 @@ export default function Editor(props) {
value={{ tables, setTables, relationships, setRelationships }} value={{ tables, setTables, relationships, setRelationships }}
> >
<AreaContext.Provider value={{ areas, setAreas }}> <AreaContext.Provider value={{ areas, setAreas }}>
<TabContext.Provider value={{ tab, setTab }}> <NoteContext.Provider value={{ notes, setNotes }}>
<div className="h-[100vh] overflow-hidden"> <TabContext.Provider value={{ tab, setTab }}>
<ControlPanel /> <div className="h-[100vh] overflow-hidden">
<div <ControlPanel />
className={ <div
layout.header className={
? `flex h-[calc(100vh-123.93px)]` layout.header
: `flex h-[calc(100vh-51.97px)]` ? `flex h-[calc(100vh-123.93px)]`
} : `flex h-[calc(100vh-51.97px)]`
onMouseUp={() => setResize(false)} }
onMouseMove={dragHandler} onMouseUp={() => setResize(false)}
> onMouseMove={dragHandler}
<DndProvider backend={HTML5Backend}> >
{layout.sidebar && ( <DndProvider backend={HTML5Backend}>
<EditorPanel {layout.sidebar && (
<EditorPanel
code={code}
setCode={setCode}
resize={resize}
setResize={setResize}
width={width}
selectedTable={selectedTable}
setSelectedTable={setSelectedTable}
/>
)}
<Canvas
code={code} code={code}
setCode={setCode} setCode={setCode}
resize={resize}
setResize={setResize}
width={width}
selectedTable={selectedTable} selectedTable={selectedTable}
setSelectedTable={setSelectedTable} setSelectedTable={setSelectedTable}
/> />
)} </DndProvider>
<Canvas {layout.services && <Sidebar />}
code={code} </div>
setCode={setCode}
selectedTable={selectedTable}
setSelectedTable={setSelectedTable}
/>
</DndProvider>
{layout.services && <Sidebar />}
</div> </div>
</div> </TabContext.Provider>
</TabContext.Provider> </NoteContext.Provider>
</AreaContext.Provider> </AreaContext.Provider>
</TableContext.Provider> </TableContext.Provider>
</LayoutContext.Provider> </LayoutContext.Provider>