Add sidesheet for editing relationships
This commit is contained in:
parent
6cd0a691dc
commit
557ce72961
@ -87,6 +87,8 @@ export default function Canvas() {
|
||||
* @param {ObjectType[keyof ObjectType]} type
|
||||
*/
|
||||
const handlePointerDownOnElement = (e, id, type) => {
|
||||
if (selectedElement.open && !layout.sidebar) return;
|
||||
|
||||
if (!e.isPrimary) return;
|
||||
|
||||
if (type === ObjectType.TABLE) {
|
||||
@ -138,6 +140,8 @@ export default function Canvas() {
|
||||
* @param {PointerEvent} e
|
||||
*/
|
||||
const handlePointerMove = (e) => {
|
||||
if (selectedElement.open && !layout.sidebar) return;
|
||||
|
||||
if (!e.isPrimary) return;
|
||||
|
||||
if (linking) {
|
||||
@ -226,6 +230,8 @@ export default function Canvas() {
|
||||
* @param {PointerEvent} e
|
||||
*/
|
||||
const handlePointerDown = (e) => {
|
||||
if (selectedElement.open && !layout.sidebar) return;
|
||||
|
||||
if (!e.isPrimary) return;
|
||||
|
||||
// don't pan if the sidesheet for editing a table is open
|
||||
@ -309,6 +315,8 @@ export default function Canvas() {
|
||||
* @param {PointerEvent} e
|
||||
*/
|
||||
const handlePointerUp = (e) => {
|
||||
if (selectedElement.open && !layout.sidebar) return;
|
||||
|
||||
if (!e.isPrimary) return;
|
||||
|
||||
if (coordsDidUpdate(dragging.element)) {
|
||||
|
@ -3,6 +3,8 @@ import { Cardinality, ObjectType, Tab } from "../../data/constants";
|
||||
import { calcPath } from "../../utils/calcPath";
|
||||
import { useDiagram, useSettings, useLayout, useSelect } from "../../hooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SideSheet } from "@douyinfe/semi-ui";
|
||||
import RelationshipInfo from "../EditorSidePanel/RelationshipsTab/RelationshipInfo";
|
||||
|
||||
export default function Relationship({ data }) {
|
||||
const { settings } = useSettings();
|
||||
@ -79,67 +81,90 @@ export default function Relationship({ data }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<g className="select-none group" onDoubleClick={edit}>
|
||||
<path
|
||||
ref={pathRef}
|
||||
d={calcPath(
|
||||
{
|
||||
...data,
|
||||
startTable: {
|
||||
x: tables[data.startTableId].x,
|
||||
y: tables[data.startTableId].y,
|
||||
<>
|
||||
<g className="select-none group" onDoubleClick={edit}>
|
||||
<path
|
||||
ref={pathRef}
|
||||
d={calcPath(
|
||||
{
|
||||
...data,
|
||||
startTable: {
|
||||
x: tables[data.startTableId].x,
|
||||
y: tables[data.startTableId].y,
|
||||
},
|
||||
endTable: {
|
||||
x: tables[data.endTableId].x,
|
||||
y: tables[data.endTableId].y,
|
||||
},
|
||||
},
|
||||
endTable: {
|
||||
x: tables[data.endTableId].x,
|
||||
y: tables[data.endTableId].y,
|
||||
},
|
||||
},
|
||||
settings.tableWidth,
|
||||
settings.tableWidth,
|
||||
)}
|
||||
stroke="gray"
|
||||
className="group-hover:stroke-sky-700"
|
||||
fill="none"
|
||||
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"
|
||||
className="group-hover:stroke-sky-700"
|
||||
fill="none"
|
||||
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>
|
||||
</>
|
||||
)}
|
||||
</g>
|
||||
</g>
|
||||
<SideSheet
|
||||
title={t("edit")}
|
||||
size="small"
|
||||
visible={
|
||||
selectedElement.element === ObjectType.RELATIONSHIP &&
|
||||
selectedElement.id === data.id &&
|
||||
selectedElement.open &&
|
||||
!layout.sidebar
|
||||
}
|
||||
onCancel={() => {
|
||||
setSelectedElement((prev) => ({
|
||||
...prev,
|
||||
open: false,
|
||||
}));
|
||||
}}
|
||||
style={{ paddingBottom: "16px" }}
|
||||
>
|
||||
<div className="sidesheet-theme">
|
||||
<RelationshipInfo data={data} />
|
||||
</div>
|
||||
</SideSheet>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,4 @@
|
||||
import {
|
||||
Collapse,
|
||||
Row,
|
||||
Col,
|
||||
Select,
|
||||
Button,
|
||||
Popover,
|
||||
Table,
|
||||
} from "@douyinfe/semi-ui";
|
||||
import { Row, Col, Select, Button, Popover, Table } from "@douyinfe/semi-ui";
|
||||
import {
|
||||
IconDeleteStroked,
|
||||
IconLoopTextStroked,
|
||||
@ -128,110 +120,100 @@ export default function RelationshipInfo({ data }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div id={`scroll_ref_${data.id}`}>
|
||||
<Collapse.Panel
|
||||
header={
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{data.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 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="font-semibold my-1">{t("cardinality")}:</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>
|
||||
</Collapse.Panel>
|
||||
</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 className="font-semibold my-1">{t("cardinality")}:</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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -14,34 +14,47 @@ export default function RelationshipsTab() {
|
||||
return (
|
||||
<>
|
||||
<SearchBar />
|
||||
<Collapse
|
||||
activeKey={
|
||||
selectedElement.open &&
|
||||
selectedElement.element === ObjectType.RELATIONSHIP
|
||||
? `${selectedElement.id}`
|
||||
: ""
|
||||
}
|
||||
keepDOM
|
||||
lazyRender
|
||||
onChange={(k) =>
|
||||
setSelectedElement((prev) => ({
|
||||
...prev,
|
||||
open: true,
|
||||
id: parseInt(k),
|
||||
element: ObjectType.RELATIONSHIP,
|
||||
}))
|
||||
}
|
||||
accordion
|
||||
>
|
||||
{relationships.length <= 0 ? (
|
||||
<Empty
|
||||
title={t("no_relationships")}
|
||||
text={t("no_relationships_text")}
|
||||
/>
|
||||
) : (
|
||||
relationships.map((r) => <RelationshipInfo key={r.id} data={r} />)
|
||||
)}
|
||||
</Collapse>
|
||||
{relationships.length <= 0 ? (
|
||||
<Empty
|
||||
title={t("no_relationships")}
|
||||
text={t("no_relationships_text")}
|
||||
/>
|
||||
) : (
|
||||
<Collapse
|
||||
activeKey={
|
||||
selectedElement.open &&
|
||||
selectedElement.element === ObjectType.RELATIONSHIP
|
||||
? `${selectedElement.id}`
|
||||
: ""
|
||||
}
|
||||
keepDOM
|
||||
lazyRender
|
||||
onChange={(k) =>
|
||||
setSelectedElement((prev) => ({
|
||||
...prev,
|
||||
open: true,
|
||||
id: parseInt(k),
|
||||
element: ObjectType.RELATIONSHIP,
|
||||
}))
|
||||
}
|
||||
accordion
|
||||
>
|
||||
{relationships.map((r) => (
|
||||
<div id={`scroll_ref_${r.id}`} key={"relationship_" + r.id}>
|
||||
<Collapse.Panel
|
||||
header={
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{r.name}
|
||||
</div>
|
||||
}
|
||||
itemKey={`${r.id}`}
|
||||
>
|
||||
<RelationshipInfo data={r} />
|
||||
</Collapse.Panel>
|
||||
</div>
|
||||
))}
|
||||
</Collapse>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ function Relationship({ relationship, tables }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<g className="select-none" onClick={() => console.log(pathRef.current)}>
|
||||
<g className="select-none">
|
||||
<path
|
||||
ref={pathRef}
|
||||
d={calcPath({
|
||||
|
Loading…
Reference in New Issue
Block a user