subject area overview
This commit is contained in:
parent
ce92a35c9f
commit
8da14317e5
@ -4,7 +4,7 @@ export default function Area(props) {
|
|||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
|
|
||||||
const handleMouseDown = (e, dir) => {
|
const handleMouseDown = (e, dir) => {
|
||||||
props.setResize({id: props.areaData.id, dir: dir});
|
props.setResize({ id: props.areaData.id, dir: dir });
|
||||||
props.setInitCoords({
|
props.setInitCoords({
|
||||||
x: props.areaData.x,
|
x: props.areaData.x,
|
||||||
y: props.areaData.y,
|
y: props.areaData.y,
|
||||||
@ -28,53 +28,61 @@ export default function Area(props) {
|
|||||||
height={props.areaData.height > 0 ? props.areaData.height : 0}
|
height={props.areaData.height > 0 ? props.areaData.height : 0}
|
||||||
onMouseDown={props.onMouseDown}
|
onMouseDown={props.onMouseDown}
|
||||||
>
|
>
|
||||||
<div className="border-2 border-dashed border-blue-600 opacity-70 bg-slate-400 w-fill h-full select-none cursor-move">
|
<div
|
||||||
|
className={`${
|
||||||
|
hovered
|
||||||
|
? "border-4 border-dashed border-[#5891db]"
|
||||||
|
: "border-2 border-slate-400"
|
||||||
|
} w-full h-full cursor-move rounded relative`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="opacity-40 w-fill p-2 h-full"
|
||||||
|
style={{ backgroundColor: props.areaData.color }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="text-gray-900 absolute top-2 left-3 select-none">
|
||||||
{props.areaData.name}
|
{props.areaData.name}
|
||||||
</div>
|
</div>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
{hovered && (
|
{hovered && (
|
||||||
<>
|
<>
|
||||||
<rect
|
<circle
|
||||||
x={props.areaData.x - 5}
|
cx={props.areaData.x}
|
||||||
y={props.areaData.y - 5}
|
cy={props.areaData.y}
|
||||||
width={10}
|
r={6}
|
||||||
height={10}
|
fill="white"
|
||||||
fill="lightblue"
|
stroke="#5891db"
|
||||||
stroke="blue"
|
strokeWidth={3}
|
||||||
strokeWidth={1}
|
|
||||||
cursor="nwse-resize"
|
cursor="nwse-resize"
|
||||||
onMouseDown={(e) => handleMouseDown(e, "tl")}
|
onMouseDown={(e) => handleMouseDown(e, "tl")}
|
||||||
/>
|
/>
|
||||||
<rect
|
<circle
|
||||||
x={props.areaData.x + props.areaData.width - 5}
|
cx={props.areaData.x + props.areaData.width}
|
||||||
y={props.areaData.y - 5}
|
cy={props.areaData.y}
|
||||||
width={10}
|
r={6}
|
||||||
height={10}
|
fill="white"
|
||||||
fill="lightblue"
|
stroke="#5891db"
|
||||||
stroke="blue"
|
strokeWidth={3}
|
||||||
strokeWidth={1}
|
|
||||||
cursor="nesw-resize"
|
cursor="nesw-resize"
|
||||||
onMouseDown={(e) => handleMouseDown(e, "tr")}
|
onMouseDown={(e) => handleMouseDown(e, "tr")}
|
||||||
/>
|
/>
|
||||||
<rect
|
<circle
|
||||||
x={props.areaData.x - 5}
|
cx={props.areaData.x}
|
||||||
y={props.areaData.y + props.areaData.height - 5}
|
cy={props.areaData.y + props.areaData.height}
|
||||||
width={10}
|
r={6}
|
||||||
height={10}
|
fill="white"
|
||||||
fill="lightblue"
|
stroke="#5891db"
|
||||||
stroke="blue"
|
strokeWidth={3}
|
||||||
strokeWidth={1}
|
|
||||||
cursor="nesw-resize"
|
cursor="nesw-resize"
|
||||||
onMouseDown={(e) => handleMouseDown(e, "bl")}
|
onMouseDown={(e) => handleMouseDown(e, "bl")}
|
||||||
/>
|
/>
|
||||||
<rect
|
<circle
|
||||||
x={props.areaData.x + props.areaData.width - 5}
|
cx={props.areaData.x + props.areaData.width}
|
||||||
y={props.areaData.y + props.areaData.height - 5}
|
cy={props.areaData.y + props.areaData.height}
|
||||||
width={10}
|
r={6}
|
||||||
height={10}
|
fill="white"
|
||||||
fill="lightblue"
|
stroke="#5891db"
|
||||||
stroke="blue"
|
strokeWidth={3}
|
||||||
strokeWidth={1}
|
|
||||||
cursor="nwse-resize"
|
cursor="nwse-resize"
|
||||||
onMouseDown={(e) => handleMouseDown(e, "br")}
|
onMouseDown={(e) => handleMouseDown(e, "br")}
|
||||||
/>
|
/>
|
||||||
|
225
src/components/area_overview.jsx
Normal file
225
src/components/area_overview.jsx
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
import React, { useContext, useState } from "react";
|
||||||
|
import {
|
||||||
|
Empty,
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
AutoComplete,
|
||||||
|
Button,
|
||||||
|
Input,
|
||||||
|
Popover,
|
||||||
|
Toast,
|
||||||
|
} from "@douyinfe/semi-ui";
|
||||||
|
import {
|
||||||
|
IllustrationNoContent,
|
||||||
|
IllustrationNoContentDark,
|
||||||
|
} from "@douyinfe/semi-illustrations";
|
||||||
|
import {
|
||||||
|
IconPlus,
|
||||||
|
IconSearch,
|
||||||
|
IconCheckboxTick,
|
||||||
|
IconDeleteStroked,
|
||||||
|
} from "@douyinfe/semi-icons";
|
||||||
|
import { defaultTableTheme, tableThemes } from "../data/data";
|
||||||
|
import { AreaContext } from "../pages/editor";
|
||||||
|
|
||||||
|
export default function AreaOverview(props) {
|
||||||
|
const { areas, setAreas } = useContext(AreaContext);
|
||||||
|
|
||||||
|
const [value, setValue] = useState("");
|
||||||
|
const [filteredResult, setFilteredResult] = useState(
|
||||||
|
areas.map((t) => {
|
||||||
|
return t.name;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleStringSearch = (value) => {
|
||||||
|
setFilteredResult(
|
||||||
|
areas
|
||||||
|
.map((t) => {
|
||||||
|
return t.name;
|
||||||
|
})
|
||||||
|
.filter((i) => i.includes(value))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateArea = (aid, updatedValues) => {
|
||||||
|
setAreas((prev) =>
|
||||||
|
prev.map((a, i) => {
|
||||||
|
if (aid === i) {
|
||||||
|
return {
|
||||||
|
...a,
|
||||||
|
...updatedValues,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Row gutter={6}>
|
||||||
|
<Col span={16}>
|
||||||
|
<AutoComplete
|
||||||
|
data={filteredResult}
|
||||||
|
value={value}
|
||||||
|
showClear
|
||||||
|
prefix={<IconSearch />}
|
||||||
|
placeholder="Search..."
|
||||||
|
emptyContent={<div className="p-3">No areas found</div>}
|
||||||
|
onSearch={(v) => handleStringSearch(v)}
|
||||||
|
onChange={(v) => setValue(v)}
|
||||||
|
onSelect={(v) => {
|
||||||
|
const { id } = areas.find((t) => t.name === v);
|
||||||
|
document
|
||||||
|
.getElementById(`scroll_area_${id}`)
|
||||||
|
.scrollIntoView({ behavior: "smooth" });
|
||||||
|
}}
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Button
|
||||||
|
icon={<IconPlus />}
|
||||||
|
block
|
||||||
|
onClick={() => {
|
||||||
|
const newArea = {
|
||||||
|
id: areas.length,
|
||||||
|
name: `area_${areas.length}`,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
color: defaultTableTheme,
|
||||||
|
};
|
||||||
|
setAreas((prev) => [...prev, newArea]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add area
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{areas.length <= 0 ? (
|
||||||
|
<div className="select-none">
|
||||||
|
<Empty
|
||||||
|
image={
|
||||||
|
<IllustrationNoContent style={{ width: 160, height: 160 }} />
|
||||||
|
}
|
||||||
|
darkModeImage={
|
||||||
|
<IllustrationNoContentDark style={{ width: 160, height: 160 }} />
|
||||||
|
}
|
||||||
|
title="No subject areas"
|
||||||
|
description="Add subject areas to compartmentalize tables!"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="p-2">
|
||||||
|
{areas.map((a, i) => (
|
||||||
|
<Row
|
||||||
|
gutter={6}
|
||||||
|
type="flex"
|
||||||
|
justify="start"
|
||||||
|
align="middle"
|
||||||
|
key={i}
|
||||||
|
id={`scroll_area_${a.id}`}
|
||||||
|
className="my-3"
|
||||||
|
>
|
||||||
|
<Col span={18}>
|
||||||
|
<Input
|
||||||
|
value={a.name}
|
||||||
|
onChange={(value, e) => updateArea(a.id, { name: value })}
|
||||||
|
field="name"
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={3}>
|
||||||
|
<Popover
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between items-center p-2">
|
||||||
|
<div className="font-medium">Theme</div>
|
||||||
|
<Button
|
||||||
|
type="tertiary"
|
||||||
|
size="small"
|
||||||
|
onClick={() =>
|
||||||
|
updateArea(i, { color: defaultTableTheme })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div className="py-3">
|
||||||
|
<div>
|
||||||
|
{tableThemes
|
||||||
|
.slice(0, Math.ceil(tableThemes.length / 2))
|
||||||
|
.map((c) => (
|
||||||
|
<button
|
||||||
|
key={c}
|
||||||
|
style={{ backgroundColor: c }}
|
||||||
|
className="p-3 rounded-full mx-1"
|
||||||
|
onClick={() => updateArea(i, { color: c })}
|
||||||
|
>
|
||||||
|
{a.color === c ? (
|
||||||
|
<IconCheckboxTick
|
||||||
|
style={{ color: "white" }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<IconCheckboxTick style={{ color: c }} />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-3">
|
||||||
|
{tableThemes
|
||||||
|
.slice(Math.ceil(tableThemes.length / 2))
|
||||||
|
.map((c) => (
|
||||||
|
<button
|
||||||
|
key={c}
|
||||||
|
style={{ backgroundColor: c }}
|
||||||
|
className="p-3 rounded-full mx-1"
|
||||||
|
onClick={() => updateArea(i, { color: c })}
|
||||||
|
>
|
||||||
|
<IconCheckboxTick
|
||||||
|
style={{
|
||||||
|
color: a.color === c ? "white" : c,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
trigger="click"
|
||||||
|
position="bottomLeft"
|
||||||
|
showArrow
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="h-[32px] w-[32px] rounded"
|
||||||
|
style={{ backgroundColor: a.color }}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
</Col>
|
||||||
|
<Col span={3}>
|
||||||
|
<Button
|
||||||
|
icon={<IconDeleteStroked />}
|
||||||
|
type="danger"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
Toast.success(`Area deleted!`);
|
||||||
|
setAreas((prev) =>
|
||||||
|
prev
|
||||||
|
.filter((e) => e.id !== i)
|
||||||
|
.map((e, idx) => ({ ...e, id: idx }))
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
// className="delete-button"
|
||||||
|
></Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -590,6 +590,23 @@ export default function ControlPanel(props) {
|
|||||||
>
|
>
|
||||||
Issues
|
Issues
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
|
<Dropdown.Item
|
||||||
|
icon={
|
||||||
|
layout.areas ? (
|
||||||
|
<IconCheckboxTick />
|
||||||
|
) : (
|
||||||
|
<div className="px-2"></div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick={() =>
|
||||||
|
setLayout((prev) => ({
|
||||||
|
...prev,
|
||||||
|
areas: !prev.areas,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Subject areas
|
||||||
|
</Dropdown.Item>
|
||||||
<Dropdown.Item
|
<Dropdown.Item
|
||||||
icon={
|
icon={
|
||||||
layout.editor ? (
|
layout.editor ? (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { React, useContext, useState } from "react";
|
import { React, useState } from "react";
|
||||||
import CodeMirror from "@uiw/react-codemirror";
|
import CodeMirror from "@uiw/react-codemirror";
|
||||||
import { createTheme } from "@uiw/codemirror-themes";
|
import { createTheme } from "@uiw/codemirror-themes";
|
||||||
import { sql } from "@codemirror/lang-sql";
|
import { sql } from "@codemirror/lang-sql";
|
||||||
@ -8,8 +8,7 @@ import Shape from "./shape";
|
|||||||
import { Tabs } from "@douyinfe/semi-ui";
|
import { Tabs } from "@douyinfe/semi-ui";
|
||||||
import TableOverview from "./table_overview";
|
import TableOverview from "./table_overview";
|
||||||
import ReferenceOverview from "./reference_overview";
|
import ReferenceOverview from "./reference_overview";
|
||||||
import { defaultTableTheme } from "../data/data";
|
import AreaOverview from "./area_overview";
|
||||||
import { AreaContext } from "../pages/editor";
|
|
||||||
// import { TableContext } from "../pages/editor";
|
// import { TableContext } from "../pages/editor";
|
||||||
|
|
||||||
const myTheme = createTheme({
|
const myTheme = createTheme({
|
||||||
@ -30,13 +29,13 @@ const EditorPanel = (props) => {
|
|||||||
const [tab, setTab] = useState("1");
|
const [tab, setTab] = useState("1");
|
||||||
// const map = useRef(new Map());
|
// const map = useRef(new Map());
|
||||||
// const {tables, setTables} = useContext(TableContext);
|
// const {tables, setTables} = useContext(TableContext);
|
||||||
const { areas, setAreas } = useContext(AreaContext);
|
|
||||||
|
|
||||||
const tabList = [
|
const tabList = [
|
||||||
{ tab: "Tables", itemKey: "1" },
|
{ tab: "Tables", itemKey: "1" },
|
||||||
{ tab: "Relationships", itemKey: "2" },
|
{ tab: "Relationships", itemKey: "2" },
|
||||||
{ tab: "Shapes", itemKey: "3" },
|
{ tab: "Subject Areas", itemKey: "3" },
|
||||||
{ tab: "Editor", itemKey: "4" },
|
{ tab: "Shapes", itemKey: "4" },
|
||||||
|
{ tab: "Editor", itemKey: "5" },
|
||||||
];
|
];
|
||||||
const contentList = [
|
const contentList = [
|
||||||
<TableOverview
|
<TableOverview
|
||||||
@ -44,6 +43,7 @@ const EditorPanel = (props) => {
|
|||||||
setSelectedTable={props.setSelectedTable}
|
setSelectedTable={props.setSelectedTable}
|
||||||
/>,
|
/>,
|
||||||
<ReferenceOverview />,
|
<ReferenceOverview />,
|
||||||
|
<AreaOverview/>,
|
||||||
<Shape />,
|
<Shape />,
|
||||||
<CodeMirror
|
<CodeMirror
|
||||||
value={props.code}
|
value={props.code}
|
||||||
@ -69,25 +69,6 @@ const EditorPanel = (props) => {
|
|||||||
>
|
>
|
||||||
<div className="p-2">{contentList[parseInt(tab) - 1]}</div>
|
<div className="p-2">{contentList[parseInt(tab) - 1]}</div>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
const newArea = {
|
|
||||||
id: areas.length,
|
|
||||||
name: `area_${areas.length}`,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: 200,
|
|
||||||
height: 200,
|
|
||||||
color: defaultTableTheme,
|
|
||||||
};
|
|
||||||
setAreas((prev) => {
|
|
||||||
const updatedTables = [...prev, newArea];
|
|
||||||
return updatedTables;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
add area
|
|
||||||
</button>
|
|
||||||
<br />
|
<br />
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -132,15 +132,15 @@ export default function Table(props) {
|
|||||||
<Popover
|
<Popover
|
||||||
content={
|
content={
|
||||||
<div className="text-slate-600">
|
<div className="text-slate-600">
|
||||||
<p className="mb-2">
|
<div className="mb-2">
|
||||||
<strong>Comment :</strong>{" "}
|
<strong>Comment :</strong>{" "}
|
||||||
{props.tableData.comment === "" ? (
|
{props.tableData.comment === "" ? (
|
||||||
"No comment"
|
"No comment"
|
||||||
) : (
|
) : (
|
||||||
<div>{props.tableData.comment}</div>
|
<div>{props.tableData.comment}</div>
|
||||||
)}
|
)}
|
||||||
</p>
|
</div>
|
||||||
<p className="text-slate-600">
|
<div className="text-slate-600">
|
||||||
<strong
|
<strong
|
||||||
className={`${
|
className={`${
|
||||||
props.tableData.indices.length === 0 ? "" : "block"
|
props.tableData.indices.length === 0 ? "" : "block"
|
||||||
@ -169,7 +169,7 @@ export default function Table(props) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
position="rightTop"
|
position="rightTop"
|
||||||
|
@ -23,6 +23,7 @@ export default function Editor(props) {
|
|||||||
sidebar: true,
|
sidebar: true,
|
||||||
services: true,
|
services: true,
|
||||||
tables: true,
|
tables: true,
|
||||||
|
areas: true,
|
||||||
relationships: true,
|
relationships: true,
|
||||||
issues: true,
|
issues: true,
|
||||||
editor: true,
|
editor: true,
|
||||||
|
Loading…
Reference in New Issue
Block a user