mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-05-01 07:23:58 +00:00
nice diagram lines
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { isConnectedByReference } from './designerTools';
|
import { intersectLineBox } from './designerMath';
|
||||||
import contextMenu from '../utility/contextMenu';
|
|
||||||
|
|
||||||
export let reference;
|
export let reference;
|
||||||
export let onRemoveReference;
|
export let onRemoveReference;
|
||||||
@@ -13,11 +12,9 @@
|
|||||||
let src = null;
|
let src = null;
|
||||||
let dst = null;
|
let dst = null;
|
||||||
|
|
||||||
let minpos;
|
let arrowPt = null;
|
||||||
let columnsY = [];
|
let arrowAngle = 0;
|
||||||
|
|
||||||
const buswi = 10;
|
|
||||||
const extwi = 25;
|
|
||||||
const arwi = 12;
|
const arwi = 12;
|
||||||
const arhi = 12;
|
const arhi = 12;
|
||||||
const arpad = 3;
|
const arpad = 3;
|
||||||
@@ -34,103 +31,51 @@
|
|||||||
const targetRect = targetTable.getRect();
|
const targetRect = targetTable.getRect();
|
||||||
if (!sourceRect || !targetRect) return null;
|
if (!sourceRect || !targetRect) return null;
|
||||||
|
|
||||||
const possibilities = [];
|
src = {
|
||||||
possibilities.push({ xsrc: sourceRect.left - buswi, dirsrc: -1, xdst: targetRect.left - buswi, dirdst: -1 });
|
x: (sourceRect.left + sourceRect.right) / 2,
|
||||||
possibilities.push({ xsrc: sourceRect.left - buswi, dirsrc: -1, xdst: targetRect.right + buswi, dirdst: 1 });
|
y: (sourceRect.top + sourceRect.bottom) / 2,
|
||||||
possibilities.push({ xsrc: sourceRect.right + buswi, dirsrc: 1, xdst: targetRect.left - buswi, dirdst: -1 });
|
};
|
||||||
possibilities.push({ xsrc: sourceRect.right + buswi, dirsrc: 1, xdst: targetRect.right + buswi, dirdst: 1 });
|
|
||||||
|
|
||||||
minpos = _.minBy(possibilities, p => Math.abs(p.xsrc - p.xdst));
|
dst = {
|
||||||
|
x: (targetRect.left + targetRect.right) / 2,
|
||||||
|
y: (targetRect.top + targetRect.bottom) / 2,
|
||||||
|
};
|
||||||
|
|
||||||
let srcY = _.mean(columns.map(x => sourceTable.getColumnY(x.source)));
|
arrowPt = intersectLineBox(src, dst, targetRect);
|
||||||
let dstY = _.mean(columns.map(x => targetTable.getColumnY(x.target)));
|
arrowAngle = Math.atan2(dst.y - src.y, dst.x - src.x);
|
||||||
|
|
||||||
if (columns.length == 0) {
|
|
||||||
srcY = sourceTable.getColumnY('');
|
|
||||||
dstY = targetTable.getColumnY('');
|
|
||||||
}
|
|
||||||
|
|
||||||
src = { x: minpos.xsrc, y: srcY };
|
|
||||||
dst = { x: minpos.xdst, y: dstY };
|
|
||||||
|
|
||||||
columnsY = columns.map((col, colIndex) => {
|
|
||||||
const y1 = sourceTable.getColumnY(col.source);
|
|
||||||
const y2 = targetTable.getColumnY(col.target);
|
|
||||||
return [y1, y2];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
domTables;
|
domTables;
|
||||||
recomputePosition();
|
recomputePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMenu() {
|
|
||||||
const isConnected = isConnectedByReference(
|
|
||||||
designer,
|
|
||||||
{ designerId: reference?.sourceId },
|
|
||||||
{ designerId: reference?.targetId },
|
|
||||||
reference
|
|
||||||
);
|
|
||||||
const setJoinType = joinType => {
|
|
||||||
onChangeReference({
|
|
||||||
...reference,
|
|
||||||
joinType,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return [
|
|
||||||
{ text: 'Remove', onClick: () => onRemoveReference(reference) },
|
|
||||||
!isConnected && [
|
|
||||||
{ divider: true },
|
|
||||||
{ onClick: () => setJoinType('INNER JOIN'), text: 'Set INNER JOIN' },
|
|
||||||
{ onClick: () => setJoinType('LEFT JOIN'), text: 'Set LEFT JOIN' },
|
|
||||||
{ onClick: () => setJoinType('RIGHT JOIN'), text: 'Set RIGHT JOIN' },
|
|
||||||
{ onClick: () => setJoinType('FULL OUTER JOIN'), text: 'Set FULL OUTER JOIN' },
|
|
||||||
{ onClick: () => setJoinType('CROSS JOIN'), text: 'Set CROSS JOIN' },
|
|
||||||
{ onClick: () => setJoinType('WHERE EXISTS'), text: 'Set WHERE EXISTS' },
|
|
||||||
{ onClick: () => setJoinType('WHERE NOT EXISTS'), text: 'Set WHERE NOT EXISTS' },
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if src && dst && minpos}
|
<svg>
|
||||||
<svg>
|
{#if src && dst}
|
||||||
<polyline
|
<polyline
|
||||||
points={`
|
points={`
|
||||||
${src.x},${src.y}
|
${src.x},${src.y}
|
||||||
${src.x + extwi * minpos.dirsrc},${src.y}
|
|
||||||
${dst.x + extwi * minpos.dirdst},${dst.y}
|
|
||||||
${dst.x},${dst.y}
|
${dst.x},${dst.y}
|
||||||
`}
|
`}
|
||||||
/>
|
/>
|
||||||
{#each columnsY as coly}
|
{/if}
|
||||||
<polyline
|
|
||||||
points={`
|
|
||||||
${src.x},${src.y}
|
|
||||||
${src.x},${coly[0]}
|
|
||||||
${src.x - buswi * minpos.dirsrc},${coly[0]}
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
<polyline
|
|
||||||
points={`
|
|
||||||
${dst.x},${dst.y}
|
|
||||||
${dst.x},${coly[1]}
|
|
||||||
${dst.x - buswi * minpos.dirdst},${coly[1]}
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
<polygon
|
|
||||||
points={`
|
|
||||||
${dst.x - buswi * minpos.dirdst + arpad * minpos.dirdst},${dst.y}
|
|
||||||
${dst.x + arwi * minpos.dirdst - buswi * minpos.dirdst + arpad * minpos.dirdst},${dst.y + arhi / 2}
|
|
||||||
${dst.x + arwi * minpos.dirdst - buswi * minpos.dirdst + arpad * minpos.dirdst},${dst.y - arhi / 2}
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
|
{#if arrowPt}
|
||||||
|
<g transform={`translate(${arrowPt.x} ${arrowPt.y})`}>
|
||||||
|
<polygon
|
||||||
|
transform={`rotate(${180 + (arrowAngle * 180) / Math.PI})`}
|
||||||
|
points={`
|
||||||
|
0,0
|
||||||
|
${arwi},${arhi / 2}
|
||||||
|
${arwi},${-arhi / 2}
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
{/if}
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<!-- -->
|
||||||
<style>
|
<style>
|
||||||
svg {
|
svg {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
92
packages/web/src/designer/designerMath.ts
Normal file
92
packages/web/src/designer/designerMath.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
interface IPoint {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IBoxBounds {
|
||||||
|
left: number;
|
||||||
|
top: number;
|
||||||
|
right: number;
|
||||||
|
bottom: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// export class Vector {
|
||||||
|
// constructor(public x: number, public y: number) {}
|
||||||
|
|
||||||
|
// static random() {
|
||||||
|
// return new Vector(10.0 * (Math.random() - 0.5), 10.0 * (Math.random() - 0.5));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// add(v2: Vector) {
|
||||||
|
// return new Vector(this.x + v2.x, this.y + v2.y);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// subtract(v2: Vector) {
|
||||||
|
// return new Vector(this.x - v2.x, this.y - v2.y);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// multiply(n: number) {
|
||||||
|
// return new Vector(this.x * n, this.y * n);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// divide(n: number) {
|
||||||
|
// return new Vector(this.x / n || 0, this.y / n || 0); // Avoid divide by zero errors..
|
||||||
|
// }
|
||||||
|
|
||||||
|
// magnitude() {
|
||||||
|
// return Math.sqrt(this.x * this.x + this.y * this.y);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// normal(n: number) {
|
||||||
|
// return new Vector(-this.y, this.x);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// normalise() {
|
||||||
|
// return this.divide(this.magnitude());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// helpers for figuring out where to draw arrows
|
||||||
|
export function intersectLineLine(p1: IPoint, p2: IPoint, p3: IPoint, p4: IPoint): IPoint {
|
||||||
|
var denom = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y);
|
||||||
|
|
||||||
|
// lines are parallel
|
||||||
|
if (denom === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ua = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) / denom;
|
||||||
|
var ub = ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x)) / denom;
|
||||||
|
|
||||||
|
if (ua < 0 || ua > 1 || ub < 0 || ub > 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: p1.x + ua * (p2.x - p1.x),
|
||||||
|
y: p1.y + ua * (p2.y - p1.y),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function intersectLineBox(p1: IPoint, p2: IPoint, box: IBoxBounds): IPoint {
|
||||||
|
var tl = { x: box.left, y: box.top };
|
||||||
|
var tr = { x: box.right, y: box.top };
|
||||||
|
var bl = { x: box.left, y: box.bottom };
|
||||||
|
var br = { x: box.right, y: box.bottom };
|
||||||
|
|
||||||
|
var result;
|
||||||
|
if ((result = intersectLineLine(p1, p2, tl, tr))) {
|
||||||
|
return result;
|
||||||
|
} // top
|
||||||
|
if ((result = intersectLineLine(p1, p2, tr, br))) {
|
||||||
|
return result;
|
||||||
|
} // right
|
||||||
|
if ((result = intersectLineLine(p1, p2, br, bl))) {
|
||||||
|
return result;
|
||||||
|
} // bottom
|
||||||
|
if ((result = intersectLineLine(p1, p2, bl, tl))) {
|
||||||
|
return result;
|
||||||
|
} // left
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user