Add sidesheet for editing relationships

This commit is contained in:
1ilit 2024-09-07 23:52:03 +04:00
parent 6cd0a691dc
commit 557ce72961
5 changed files with 230 additions and 202 deletions

View File

@ -87,6 +87,8 @@ export default function Canvas() {
* @param {ObjectType[keyof ObjectType]} type * @param {ObjectType[keyof ObjectType]} type
*/ */
const handlePointerDownOnElement = (e, id, type) => { const handlePointerDownOnElement = (e, id, type) => {
if (selectedElement.open && !layout.sidebar) return;
if (!e.isPrimary) return; if (!e.isPrimary) return;
if (type === ObjectType.TABLE) { if (type === ObjectType.TABLE) {
@ -138,6 +140,8 @@ export default function Canvas() {
* @param {PointerEvent} e * @param {PointerEvent} e
*/ */
const handlePointerMove = (e) => { const handlePointerMove = (e) => {
if (selectedElement.open && !layout.sidebar) return;
if (!e.isPrimary) return; if (!e.isPrimary) return;
if (linking) { if (linking) {
@ -226,6 +230,8 @@ export default function Canvas() {
* @param {PointerEvent} e * @param {PointerEvent} e
*/ */
const handlePointerDown = (e) => { const handlePointerDown = (e) => {
if (selectedElement.open && !layout.sidebar) return;
if (!e.isPrimary) return; if (!e.isPrimary) return;
// don't pan if the sidesheet for editing a table is open // don't pan if the sidesheet for editing a table is open
@ -309,6 +315,8 @@ export default function Canvas() {
* @param {PointerEvent} e * @param {PointerEvent} e
*/ */
const handlePointerUp = (e) => { const handlePointerUp = (e) => {
if (selectedElement.open && !layout.sidebar) return;
if (!e.isPrimary) return; if (!e.isPrimary) return;
if (coordsDidUpdate(dragging.element)) { if (coordsDidUpdate(dragging.element)) {

View File

@ -3,6 +3,8 @@ import { Cardinality, ObjectType, Tab } from "../../data/constants";
import { calcPath } from "../../utils/calcPath"; import { calcPath } from "../../utils/calcPath";
import { useDiagram, useSettings, useLayout, useSelect } from "../../hooks"; import { useDiagram, useSettings, useLayout, useSelect } from "../../hooks";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { SideSheet } from "@douyinfe/semi-ui";
import RelationshipInfo from "../EditorSidePanel/RelationshipsTab/RelationshipInfo";
export default function Relationship({ data }) { export default function Relationship({ data }) {
const { settings } = useSettings(); const { settings } = useSettings();
@ -79,67 +81,90 @@ export default function Relationship({ data }) {
}; };
return ( return (
<g className="select-none group" onDoubleClick={edit}> <>
<path <g className="select-none group" onDoubleClick={edit}>
ref={pathRef} <path
d={calcPath( ref={pathRef}
{ d={calcPath(
...data, {
startTable: { ...data,
x: tables[data.startTableId].x, startTable: {
y: tables[data.startTableId].y, x: tables[data.startTableId].x,
y: tables[data.startTableId].y,
},
endTable: {
x: tables[data.endTableId].x,
y: tables[data.endTableId].y,
},
}, },
endTable: { settings.tableWidth,
x: tables[data.endTableId].x, )}
y: tables[data.endTableId].y, stroke="gray"
}, className="group-hover:stroke-sky-700"
}, fill="none"
settings.tableWidth, strokeWidth={2}
cursor="pointer"
/>
{pathRef.current && settings.showCardinality && (
<>
<circle
cx={cardinalityStartX}
cy={cardinalityStartY}
r="12"
fill="grey"
className="group-hover:fill-sky-700"
/>
<text
x={cardinalityStartX}
y={cardinalityStartY}
fill="white"
strokeWidth="0.5"
textAnchor="middle"
alignmentBaseline="middle"
>
{cardinalityStart}
</text>
<circle
cx={cardinalityEndX}
cy={cardinalityEndY}
r="12"
fill="grey"
className="group-hover:fill-sky-700"
/>
<text
x={cardinalityEndX}
y={cardinalityEndY}
fill="white"
strokeWidth="0.5"
textAnchor="middle"
alignmentBaseline="middle"
>
{cardinalityEnd}
</text>
</>
)} )}
stroke="gray" </g>
className="group-hover:stroke-sky-700" <SideSheet
fill="none" title={t("edit")}
strokeWidth={2} size="small"
cursor="pointer" visible={
/> selectedElement.element === ObjectType.RELATIONSHIP &&
{pathRef.current && settings.showCardinality && ( selectedElement.id === data.id &&
<> selectedElement.open &&
<circle !layout.sidebar
cx={cardinalityStartX} }
cy={cardinalityStartY} onCancel={() => {
r="12" setSelectedElement((prev) => ({
fill="grey" ...prev,
className="group-hover:fill-sky-700" open: false,
/> }));
<text }}
x={cardinalityStartX} style={{ paddingBottom: "16px" }}
y={cardinalityStartY} >
fill="white" <div className="sidesheet-theme">
strokeWidth="0.5" <RelationshipInfo data={data} />
textAnchor="middle" </div>
alignmentBaseline="middle" </SideSheet>
> </>
{cardinalityStart}
</text>
<circle
cx={cardinalityEndX}
cy={cardinalityEndY}
r="12"
fill="grey"
className="group-hover:fill-sky-700"
/>
<text
x={cardinalityEndX}
y={cardinalityEndY}
fill="white"
strokeWidth="0.5"
textAnchor="middle"
alignmentBaseline="middle"
>
{cardinalityEnd}
</text>
</>
)}
</g>
); );
} }

View File

@ -1,12 +1,4 @@
import { import { Row, Col, Select, Button, Popover, Table } from "@douyinfe/semi-ui";
Collapse,
Row,
Col,
Select,
Button,
Popover,
Table,
} from "@douyinfe/semi-ui";
import { import {
IconDeleteStroked, IconDeleteStroked,
IconLoopTextStroked, IconLoopTextStroked,
@ -128,110 +120,100 @@ export default function RelationshipInfo({ data }) {
}; };
return ( return (
<div id={`scroll_ref_${data.id}`}> <>
<Collapse.Panel <div className="flex justify-between items-center mb-3">
header={ <div className="me-3">
<div className="overflow-hidden text-ellipsis whitespace-nowrap"> <span className="font-semibold">{t("primary")}: </span>
{data.name} {tables[data.endTableId].name}
</div>
}
itemKey={`${data.id}`}
>
<div className="flex justify-between items-center mb-3">
<div className="me-3">
<span className="font-semibold">{t("primary")}: </span>
{tables[data.endTableId].name}
</div>
<div className="mx-1">
<span className="font-semibold">{t("foreign")}: </span>
{tables[data.startTableId].name}
</div>
<div className="ms-1">
<Popover
content={
<div className="p-2 popover-theme">
<Table
columns={columns}
dataSource={[
{
key: "1",
foreign: `${tables[data.startTableId].name}(${
tables[data.startTableId].fields[data.startFieldId]
.name
})`,
primary: `${tables[data.endTableId].name}(${
tables[data.endTableId].fields[data.endFieldId].name
})`,
},
]}
pagination={false}
size="small"
bordered
/>
<div className="mt-2">
<Button
icon={<IconLoopTextStroked />}
block
onClick={swapKeys}
>
{t("swap")}
</Button>
</div>
</div>
}
trigger="click"
position="rightTop"
showArrow
>
<Button icon={<IconMore />} type="tertiary" />
</Popover>
</div>
</div> </div>
<div className="font-semibold my-1">{t("cardinality")}:</div> <div className="mx-1">
<Select <span className="font-semibold">{t("foreign")}: </span>
optionList={Object.values(Cardinality).map((v) => ({ {tables[data.startTableId].name}
label: t(v), </div>
value: v, <div className="ms-1">
}))} <Popover
value={data.cardinality} content={
className="w-full" <div className="p-2 popover-theme">
onChange={changeCardinality} <Table
/> columns={columns}
<Row gutter={6} className="my-3"> dataSource={[
<Col span={12}> {
<div className="font-semibold">{t("on_update")}: </div> key: "1",
<Select foreign: `${tables[data.startTableId].name}(${
optionList={Object.values(Constraint).map((v) => ({ tables[data.startTableId].fields[data.startFieldId].name
label: v, })`,
value: v, primary: `${tables[data.endTableId].name}(${
}))} tables[data.endTableId].fields[data.endFieldId].name
value={data.updateConstraint} })`,
className="w-full" },
onChange={(value) => changeConstraint("update", value)} ]}
/> pagination={false}
</Col> size="small"
<Col span={12}> bordered
<div className="font-semibold">{t("on_delete")}: </div> />
<Select <div className="mt-2">
optionList={Object.values(Constraint).map((v) => ({ <Button
label: v, icon={<IconLoopTextStroked />}
value: v, block
}))} onClick={swapKeys}
value={data.deleteConstraint} >
className="w-full" {t("swap")}
onChange={(value) => changeConstraint("delete", value)} </Button>
/> </div>
</Col> </div>
</Row> }
<Button trigger="click"
icon={<IconDeleteStroked />} position="rightTop"
block showArrow
type="danger" >
onClick={() => deleteRelationship(data.id)} <Button icon={<IconMore />} type="tertiary" />
> </Popover>
{t("delete")} </div>
</Button> </div>
</Collapse.Panel> <div className="font-semibold my-1">{t("cardinality")}:</div>
</div> <Select
optionList={Object.values(Cardinality).map((v) => ({
label: t(v),
value: v,
}))}
value={data.cardinality}
className="w-full"
onChange={changeCardinality}
/>
<Row gutter={6} className="my-3">
<Col span={12}>
<div className="font-semibold">{t("on_update")}: </div>
<Select
optionList={Object.values(Constraint).map((v) => ({
label: v,
value: v,
}))}
value={data.updateConstraint}
className="w-full"
onChange={(value) => changeConstraint("update", value)}
/>
</Col>
<Col span={12}>
<div className="font-semibold">{t("on_delete")}: </div>
<Select
optionList={Object.values(Constraint).map((v) => ({
label: v,
value: v,
}))}
value={data.deleteConstraint}
className="w-full"
onChange={(value) => changeConstraint("delete", value)}
/>
</Col>
</Row>
<Button
icon={<IconDeleteStroked />}
block
type="danger"
onClick={() => deleteRelationship(data.id)}
>
{t("delete")}
</Button>
</>
); );
} }

View File

@ -14,34 +14,47 @@ export default function RelationshipsTab() {
return ( return (
<> <>
<SearchBar /> <SearchBar />
<Collapse {relationships.length <= 0 ? (
activeKey={ <Empty
selectedElement.open && title={t("no_relationships")}
selectedElement.element === ObjectType.RELATIONSHIP text={t("no_relationships_text")}
? `${selectedElement.id}` />
: "" ) : (
} <Collapse
keepDOM activeKey={
lazyRender selectedElement.open &&
onChange={(k) => selectedElement.element === ObjectType.RELATIONSHIP
setSelectedElement((prev) => ({ ? `${selectedElement.id}`
...prev, : ""
open: true, }
id: parseInt(k), keepDOM
element: ObjectType.RELATIONSHIP, lazyRender
})) onChange={(k) =>
} setSelectedElement((prev) => ({
accordion ...prev,
> open: true,
{relationships.length <= 0 ? ( id: parseInt(k),
<Empty element: ObjectType.RELATIONSHIP,
title={t("no_relationships")} }))
text={t("no_relationships_text")} }
/> accordion
) : ( >
relationships.map((r) => <RelationshipInfo key={r.id} data={r} />) {relationships.map((r) => (
)} <div id={`scroll_ref_${r.id}`} key={"relationship_" + r.id}>
</Collapse> <Collapse.Panel
header={
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
{r.name}
</div>
}
itemKey={`${r.id}`}
>
<RelationshipInfo data={r} />
</Collapse.Panel>
</div>
))}
</Collapse>
)}
</> </>
); );
} }

View File

@ -117,7 +117,7 @@ function Relationship({ relationship, tables }) {
} }
return ( return (
<g className="select-none" onClick={() => console.log(pathRef.current)}> <g className="select-none">
<path <path
ref={pathRef} ref={pathRef}
d={calcPath({ d={calcPath({