Add search for relationships

This commit is contained in:
1ilit 2023-09-19 15:48:32 +03:00
parent 4fec674ac6
commit 525ca9f5c7
5 changed files with 316 additions and 268 deletions

View File

@ -319,7 +319,7 @@ export default function ControlPanel() {
</Dropdown.Menu> </Dropdown.Menu>
} }
> >
<div className="px-3 py-1 hover:bg-gray-100 rounded-md"> <div className="px-3 py-1 hover:bg-gray-100 rounded">
{category} {category}
</div> </div>
</Dropdown> </Dropdown>
@ -363,7 +363,7 @@ export default function ControlPanel() {
</div> </div>
</nav> </nav>
)} )}
<div className="p-2 px-5 flex justify-between items-center rounded-xl bg-slate-100 my-1 mx-6 text-slate-700"> <div className="p-2 px-5 flex justify-between items-center rounded-xl bg-slate-100 my-1 mx-6 text-slate-700 select-none">
<div className="flex justify-start items-center"> <div className="flex justify-start items-center">
<Dropdown <Dropdown
position="bottomLeft" position="bottomLeft"
@ -396,7 +396,7 @@ export default function ControlPanel() {
} }
trigger="click" trigger="click"
> >
<div className="py-1 px-2 hover:bg-slate-200 rounded-md"> <div className="py-1 px-2 hover:bg-slate-200 rounded">
<i className="fa-solid fa-table-list"></i> <IconCaretdown /> <i className="fa-solid fa-table-list"></i> <IconCaretdown />
</div> </div>
</Dropdown> </Dropdown>
@ -435,33 +435,27 @@ export default function ControlPanel() {
} }
trigger="click" trigger="click"
> >
<div className="py-1 px-2 hover:bg-slate-200 rounded-md"> <div className="py-1 px-2 hover:bg-slate-200 rounded">
zoom <IconCaretdown /> zoom <IconCaretdown />
</div> </div>
</Dropdown> </Dropdown>
<button <button
className="py-1 px-2 hover:bg-slate-200 rounded-md" className="py-1 px-2 hover:bg-slate-200 rounded"
title="Zoom in" title="Zoom in"
> >
<i className="fa-solid fa-magnifying-glass-plus"></i> <i className="fa-solid fa-magnifying-glass-plus"></i>
</button> </button>
<button <button
className="py-1 px-2 hover:bg-slate-200 rounded-md" className="py-1 px-2 hover:bg-slate-200 rounded"
title="Zoom out" title="Zoom out"
> >
<i className="fa-solid fa-magnifying-glass-minus"></i> <i className="fa-solid fa-magnifying-glass-minus"></i>
</button> </button>
<Divider layout="vertical" margin="8px" /> <Divider layout="vertical" margin="8px" />
<button <button className="py-1 px-2 hover:bg-slate-200 rounded" title="Undo">
className="py-1 px-2 hover:bg-slate-200 rounded-md"
title="Undo"
>
<i className="fa-solid fa-rotate-left "></i> <i className="fa-solid fa-rotate-left "></i>
</button> </button>
<button <button className="py-1 px-2 hover:bg-slate-200 rounded" title="Redo">
className="py-1 px-2 hover:bg-slate-200 rounded-md"
title="Redo"
>
<i className="fa-solid fa-rotate-right"></i> <i className="fa-solid fa-rotate-right"></i>
</button> </button>
<Divider layout="vertical" margin="8px" /> <Divider layout="vertical" margin="8px" />
@ -478,31 +472,25 @@ export default function ControlPanel() {
} }
trigger="click" trigger="click"
> >
<div className="py-1 px-2 hover:bg-slate-200 rounded-md"> <div className="py-1 px-2 hover:bg-slate-200 rounded">
<i className="fa-solid fa-plus"></i> <IconCaretdown /> <i className="fa-solid fa-plus"></i> <IconCaretdown />
</div> </div>
</Dropdown> </Dropdown>
<button <button className="py-1 px-2 hover:bg-slate-200 rounded" title="Edit">
className="py-1 px-2 hover:bg-slate-200 rounded-md"
title="Edit"
>
<i className="fa-solid fa-pen-to-square"></i> <i className="fa-solid fa-pen-to-square"></i>
</button> </button>
<button <button
className="py-1 px-2 hover:bg-slate-200 rounded-md" className="py-1 px-2 hover:bg-slate-200 rounded"
title="Delete" title="Delete"
> >
<i className="fa-solid fa-trash"></i> <i className="fa-solid fa-trash"></i>
</button> </button>
<Divider layout="vertical" margin="8px" /> <Divider layout="vertical" margin="8px" />
<button <button className="py-1 px-2 hover:bg-slate-200 rounded" title="Save">
className="py-1 px-2 hover:bg-slate-200 rounded-md"
title="Save"
>
<i className="fa-regular fa-floppy-disk"></i> <i className="fa-regular fa-floppy-disk"></i>
</button> </button>
<button <button
className="py-1 px-2 hover:bg-slate-200 rounded-md" className="py-1 px-2 hover:bg-slate-200 rounded"
title="Commit" title="Commit"
> >
<i className="fa-solid fa-code-branch"></i> <i className="fa-solid fa-code-branch"></i>

View File

@ -30,40 +30,32 @@ const EditorPanel = (props) => {
const tabList = [ const tabList = [
{ tab: "Tables", itemKey: "1" }, { tab: "Tables", itemKey: "1" },
{ tab: "References", itemKey: "2" }, { tab: "Relationships", itemKey: "2" },
{ tab: "Shapes", itemKey: "3" }, { tab: "Shapes", itemKey: "3" },
{ tab: "Editor", itemKey: "4" }, { tab: "Editor", itemKey: "4" },
]; ];
const contentList = [ const contentList = [
<div> <TableOverview tables={props.tables} setTables={props.setTables} />,
<TableOverview tables={props.tables} setTables={props.setTables} /> <ReferenceOverview
</div>, relationships={props.relationships}
<div> setRelationships={props.setRelationships}
<ReferenceOverview tables={props.tables}
relationships={props.relationships} />,
setRelationships={props.setRelationships} <Shape />,
tables={props.tables} <CodeMirror
/> value={props.code}
</div>, height="100%"
<div> theme={myTheme}
<Shape /> extensions={[sql()]}
</div>, onChange={(e) => {
<div> props.setCode(e);
<CodeMirror }}
value={props.code} />,
height="100%"
theme={myTheme}
extensions={[sql()]}
onChange={(e) => {
props.setCode(e);
}}
/>
</div>,
]; ];
return ( return (
<div className="h-screen flex overflow-clip relative"> <div className="h-screen flex overflow-clip relative">
<div className="mt-2" style={{ width: `${props.width}px` }}> <div style={{ width: `${props.width}px` }}>
<Tabs <Tabs
type="card" type="card"
tabList={tabList} tabList={tabList}
@ -72,7 +64,7 @@ const EditorPanel = (props) => {
}} }}
collapsible collapsible
> >
{contentList[parseInt(tab) - 1]} <div className="p-2">{contentList[parseInt(tab) - 1]}</div>
</Tabs> </Tabs>
<button <button
onClick={() => { onClick={() => {
@ -151,7 +143,9 @@ const EditorPanel = (props) => {
</button> </button>
</div> </div>
<div <div
className={`flex justify-center items-center p-1 h-full hover:bg-slate-300 cursor-col-resize ${props.resize? "bg-slate-300": ""}`} className={`flex justify-center items-center p-1 h-full hover:bg-slate-300 cursor-col-resize ${
props.resize ? "bg-slate-300" : ""
}`}
onMouseDown={() => props.setResize(true)} onMouseDown={() => props.setResize(true)}
> >
<div className="w-1 border-x border-white h-1/6" /> <div className="w-1 border-x border-white h-1/6" />

View File

@ -1,6 +1,8 @@
import React from "react"; import React, { useState } from "react";
import { import {
AutoComplete,
Collapse, Collapse,
Empty,
Form, Form,
Row, Row,
Col, Col,
@ -14,7 +16,12 @@ import {
IconDeleteStroked, IconDeleteStroked,
IconLoopTextStroked, IconLoopTextStroked,
IconMore, IconMore,
IconSearch,
} from "@douyinfe/semi-icons"; } from "@douyinfe/semi-icons";
import {
IllustrationNoContent,
IllustrationNoContentDark,
} from "@douyinfe/semi-illustrations";
import { Cardinality, Constraint } from "../data/data"; import { Cardinality, Constraint } from "../data/data";
export default function ReferenceOverview(props) { export default function ReferenceOverview(props) {
@ -28,158 +35,219 @@ export default function ReferenceOverview(props) {
dataIndex: "foreign", dataIndex: "foreign",
}, },
]; ];
const [refActiveIndex, setRefActiveIndex] = useState("");
const [value, setValue] = useState("");
const [filteredResult, setFilteredResult] = useState(
props.relationships.map((t) => {
return t.name;
})
);
const handleStringSearch = (value) => {
setFilteredResult(
props.relationships
.map((t) => {
return t.name;
})
.filter((i) => i.includes(value))
);
};
return ( return (
<Collapse> <>
{props.relationships.map((r, i) => ( <AutoComplete
<Collapse.Panel key={i} header={<div>{r.name}</div>} itemKey={`${i}`}> data={filteredResult}
<Form value={value}
onChange={(value) => showClear
props.setRelationships((prev) => prefix={<IconSearch />}
prev.map((e, idx) => placeholder="Search..."
idx === i ? { ...e, ...value.values } : e emptyContent={<div className="p-3">No relationships found</div>}
) onSearch={(v) => handleStringSearch(v)}
) onChange={(v) => setValue(v)}
} onSelect={(v) => {
> const { id } = props.relationships.find((t) => t.name === v);
<div className="flex justify-between items-center my-1"> setRefActiveIndex(`${id}`);
<div className="me-3"> document
<strong>Primary: </strong> .getElementById(`scroll_ref_${id}`)
{props.tables[r.endTableId].name} .scrollIntoView({ behavior: "smooth" });
</div> }}
<div className="mx-1"> className="w-full"
<strong>Foreign: </strong> />
{props.tables[r.startTableId].name} <Collapse
</div> activeKey={refActiveIndex}
<div className="ms-1"> onChange={(k) => setRefActiveIndex(k)}
<Popover accordion
content={ >
<div className="p-2 w-[260px]"> {props.relationships.length <= 0 ? (
<Table <div className="select-none">
columns={columns} <Empty
dataSource={[ image={
{ <IllustrationNoContent style={{ width: 160, height: 160 }} />
key: "1", }
foreign: props.tables[r.startTableId].name, darkModeImage={
primary: props.tables[r.endTableId].name, <IllustrationNoContentDark
}, style={{ width: 160, height: 160 }}
{ />
key: "2", }
foreign: title="No relationships"
props.tables[r.startTableId].fields[ description="Drag to connect fields and form relationships!"
r.startFieldId />
].name, </div>
primary: ) : (
props.tables[r.endTableId].fields[r.endFieldId] props.relationships.map((r, i) => (
.name, <div id={`scroll_ref_${r.id}`} key={i}>
}, <Collapse.Panel header={<div>{r.name}</div>} itemKey={`${i}`}>
]} <Form
pagination={false} onChange={(value) =>
bordered props.setRelationships((prev) =>
/> prev.map((e, idx) =>
<div className="mt-2"> idx === i ? { ...e, ...value.values } : e
<Button icon={<IconLoopTextStroked />} block> )
Swap
</Button>
</div>
</div>
}
trigger="click"
position="rightTop"
showArrow
>
<Button icon={<IconMore />} type="tertiary"></Button>
</Popover>
</div>
</div>
<Form.Input initValue={r.name} field="name" label="Name" />
<Form.Select
optionList={Object.values(Cardinality).map((v) => ({
label: v,
value: v,
}))}
field="cardinality"
label="Cardinality"
initValue={r.cardinality}
className="w-full"
></Form.Select>
<Row gutter={6}>
<Col span={12}>
<Form.Select
optionList={Object.values(Constraint).map((v) => ({
label: v,
value: v,
}))}
field="updateConstraint"
label="On update"
initValue={r.updateConstraint}
className="w-full"
></Form.Select>
</Col>
<Col span={12}>
<Form.Select
optionList={Object.values(Constraint).map((v) => ({
label: v,
value: v,
}))}
field="deleteConstraint"
label="On delete"
initValue={r.deleteConstraint}
className="w-full"
></Form.Select>
</Col>
</Row>
<div className="flex justify-between items-center my-3">
<label htmlFor="unique" className="font-medium text-black">
Mandetory
</label>
<Checkbox
value="mandetory"
defaultChecked={r.mandetory}
onChange={(checkedValues) =>
props.setRelationships((prev) =>
prev.map((e, idx) =>
idx === i
? {
...e,
[checkedValues.target.value]:
checkedValues.target.checked,
}
: e
) )
) }
} >
></Checkbox> <div className="flex justify-between items-center my-1">
<div className="me-3">
<strong>Primary: </strong>
{props.tables[r.endTableId].name}
</div>
<div className="mx-1">
<strong>Foreign: </strong>
{props.tables[r.startTableId].name}
</div>
<div className="ms-1">
<Popover
content={
<div className="p-2 w-[260px]">
<Table
columns={columns}
dataSource={[
{
key: "1",
foreign: props.tables[r.startTableId].name,
primary: props.tables[r.endTableId].name,
},
{
key: "2",
foreign:
props.tables[r.startTableId].fields[
r.startFieldId
].name,
primary:
props.tables[r.endTableId].fields[
r.endFieldId
].name,
},
]}
pagination={false}
bordered
/>
<div className="mt-2">
<Button icon={<IconLoopTextStroked />} block>
Swap
</Button>
</div>
</div>
}
trigger="click"
position="rightTop"
showArrow
>
<Button icon={<IconMore />} type="tertiary"></Button>
</Popover>
</div>
</div>
<Form.Input initValue={r.name} field="name" label="Name" />
<Form.Select
optionList={Object.values(Cardinality).map((v) => ({
label: v,
value: v,
}))}
field="cardinality"
label="Cardinality"
initValue={r.cardinality}
className="w-full"
></Form.Select>
<Row gutter={6}>
<Col span={12}>
<Form.Select
optionList={Object.values(Constraint).map((v) => ({
label: v,
value: v,
}))}
field="updateConstraint"
label="On update"
initValue={r.updateConstraint}
className="w-full"
></Form.Select>
</Col>
<Col span={12}>
<Form.Select
optionList={Object.values(Constraint).map((v) => ({
label: v,
value: v,
}))}
field="deleteConstraint"
label="On delete"
initValue={r.deleteConstraint}
className="w-full"
></Form.Select>
</Col>
</Row>
<div className="flex justify-between items-center my-3">
<label htmlFor="unique" className="font-medium text-black">
Mandetory
</label>
<Checkbox
value="mandetory"
defaultChecked={r.mandetory}
onChange={(checkedValues) =>
props.setRelationships((prev) =>
prev.map((e, idx) =>
idx === i
? {
...e,
[checkedValues.target.value]:
checkedValues.target.checked,
}
: e
)
)
}
></Checkbox>
</div>
</Form>
<Row gutter={6} className="mt-1">
<Col span={12}>
<Button
icon={<IconRowsStroked />}
disabled={r.cardinality === Cardinality.ONE_TO_ONE}
block
>
Extract to table
</Button>
</Col>
<Col span={12}>
<Button
icon={<IconDeleteStroked />}
block
type="danger"
onClick={() =>
props.setRelationships((prev) =>
prev
.filter((e) => e.id !== i)
.map((e, idx) => ({ ...e, id: idx }))
)
}
>
Delete
</Button>
</Col>
</Row>
</Collapse.Panel>
</div> </div>
</Form> ))
<Row gutter={6} className="mt-1"> )}
<Col span={12}> </Collapse>
<Button </>
icon={<IconRowsStroked />}
disabled={r.cardinality === Cardinality.ONE_TO_ONE}
block
>
Extract to table
</Button>
</Col>
<Col span={12}>
<Button
icon={<IconDeleteStroked />}
block
type="danger"
onClick={() =>
props.setRelationships((prev) =>
prev
.filter((e) => e.id !== i)
.map((e, idx) => ({ ...e, id: idx }))
)
}
>
Delete
</Button>
</Col>
</Row>
</Collapse.Panel>
))}
</Collapse>
); );
} }

View File

@ -80,67 +80,65 @@ export default function TableOverview(props) {
return ( return (
<> <>
<div className="p-2"> <Row gutter={6}>
<Row gutter={6}> <Col span={16}>
<Col span={16}> <AutoComplete
<AutoComplete data={filteredResult}
data={filteredResult} value={value}
value={value} showClear
showClear prefix={<IconSearch />}
prefix={<IconSearch />} placeholder="Search..."
placeholder="Search..." emptyContent={<div className="p-3">No tables found</div>}
emptyContent={<div className="p-3">No tables found</div>} onSearch={(v) => handleStringSearch(v)}
onSearch={(v) => handleStringSearch(v)} onChange={(v) => setValue(v)}
onChange={(v) => setValue(v)} onSelect={(v) => {
onSelect={(v) => { const { id } = props.tables.find((t) => t.name === v);
const { id, name } = props.tables.find((t) => t.name === v); setTableActiveKey(`${id}`);
setTableActiveKey(`${id}`); document
document .getElementById(`scroll_table_${id}`)
.getElementById(`${name}_scroll_id`) .scrollIntoView({ behavior: "smooth" });
.scrollIntoView({ behavior: "smooth" }); }}
}} className="w-full"
className="w-full" />
/> </Col>
</Col> <Col span={8}>
<Col span={8}> <Button
<Button icon={<IconPlus />}
icon={<IconPlus />} block
block onClick={() => {
onClick={() => { const id =
const id = props.tables.length === 0
props.tables.length === 0 ? 0
? 0 : props.tables[props.tables.length - 1].id + 1;
: props.tables[props.tables.length - 1].id + 1; const newTable = {
const newTable = { id: id,
id: id, name: `table_${id}`,
name: `table_${id}`, x: 0,
x: 0, y: 0,
y: 0, fields: [
fields: [ {
{ name: "id",
name: "id", type: "UUID",
type: "UUID", default: "",
default: "", check: "",
check: "", primary: true,
primary: true, unique: true,
unique: true, notNull: true,
notNull: true, increment: true,
increment: true, comment: "",
comment: "", },
}, ],
], comment: "",
comment: "", indices: [],
indices: [], color: defaultTableTheme,
color: defaultTableTheme, };
}; props.setTables((prev) => [...prev, newTable]);
props.setTables((prev) => [...prev, newTable]); }}
}} >
> Add table
Add table </Button>
</Button> </Col>
</Col> </Row>
</Row>
</div>
<Collapse <Collapse
activeKey={tableActiveKey} activeKey={tableActiveKey}
onChange={(k) => setTableActiveKey(k)} onChange={(k) => setTableActiveKey(k)}
@ -163,7 +161,7 @@ export default function TableOverview(props) {
</div> </div>
) : ( ) : (
props.tables.map((t, i) => ( props.tables.map((t, i) => (
<div id={`${t.name}_scroll_id`} key={t.id}> <div id={`scroll_table_${t.id}`} key={t.id}>
<Collapse.Panel header={<div>{t.name}</div>} itemKey={`${t.id}`}> <Collapse.Panel header={<div>{t.name}</div>} itemKey={`${t.id}`}>
{t.fields.map((f, j) => ( {t.fields.map((f, j) => (
<Form <Form

View File

@ -12,12 +12,12 @@ export default function Editor(props) {
const [relationships, setRelationships] = useState([]); const [relationships, setRelationships] = useState([]);
const [areas, setAreas] = useState([]); const [areas, setAreas] = useState([]);
const [resize, setResize] = useState(false); const [resize, setResize] = useState(false);
const [width, setWidth] = useState(320); const [width, setWidth] = useState(340);
const dragHandler = (e) => { const dragHandler = (e) => {
if (!resize) return; if (!resize) return;
const w = e.clientX; const w = e.clientX;
if (w > 320) setWidth(w); if (w > 340) setWidth(w);
}; };
return ( return (