unclean SPARC
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
/* ****************************************************
|
||||
* NODE MAP
|
||||
* ****************************************************
|
||||
*/
|
||||
|
||||
[eicnodemap] {
|
||||
border: 1px solid var(--eicui-base-color-grey-25) !important;
|
||||
background: var(--eicui-base-color-grey-10);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
[eicnodemap][resizable] { resize:vertical; }
|
||||
[eicnodemap] svg .entity {
|
||||
cursor:pointer;
|
||||
transition: all 0.7s;
|
||||
}
|
||||
[eicnodemap] svg .entity .icon {
|
||||
font-family: 'glyphs';
|
||||
font-size: 24px;
|
||||
}
|
||||
[eicnodemap] svg .entity .bg {
|
||||
fill: var(--app-color-white);
|
||||
transition: all 0.7s;
|
||||
}
|
||||
[eicnodemap] svg .entity.selected > .bg {
|
||||
stroke: var(--eicui-base-color-accessible-focus);
|
||||
stroke-width: 3px;
|
||||
}
|
||||
|
||||
[eicnodemap] .entity[primary] .bg { fill: var(--eicui-base-color-primary-100); }
|
||||
[eicnodemap] .entity[secondary] .bg { fill: var(--eicui-base-color-grey-25); }
|
||||
[eicnodemap] .entity[info] .bg { fill: var(--eicui-base-color-info-100); }
|
||||
[eicnodemap] .entity[success] .bg { fill: var(--eicui-base-color-success-100); }
|
||||
[eicnodemap] .entity[warning] .bg { fill: var(--eicui-base-color-warning-100); }
|
||||
[eicnodemap] .entity[danger] .bg { fill: var(--eicui-base-color-danger-100); }
|
||||
[eicnodemap] .entity[accent] .bg { fill: var(--eicui-base-color-accent-100); }
|
||||
[eicnodemap] svg .entity[primary].selected > .bg { fill: var(--eicui-base-color-primary-110); }
|
||||
[eicnodemap] .entity text {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
text-align: center;
|
||||
fill: var(--app-color-black);
|
||||
stroke-width: 0.1;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
transition: all 0.7s;
|
||||
}
|
||||
[eicnodemap] .entity[primary] text,
|
||||
[eicnodemap] .entity[info] text,
|
||||
[eicnodemap] .entity[success] text,
|
||||
[eicnodemap] .entity[danger] text { fill: var(--app-color-white) }
|
||||
[eicnodemap] .entity .title { font-size: normal; font-weight: bold; }
|
||||
[eicnodemap] .entity .subtitle { font-size: smaller; font-weight: normal; }
|
||||
|
||||
[eicnodemap] .entity .badge[primary] .bg { fill: var(--eicui-base-color-primary-100); }
|
||||
[eicnodemap] .entity .badge[success] .bg { fill: var(--eicui-base-color-success-100); }
|
||||
[eicnodemap] .entity .badge[danger] .bg { fill: var(--eicui-base-color-danger-100); }
|
||||
[eicnodemap] .entity .badge[warning] .bg { fill: var(--eicui-base-color-warning-100); }
|
||||
[eicnodemap] .entity .badge[accent] .bg { fill: var(--eicui-base-color-accent-100); }
|
||||
[eicnodemap] .entity .badge[info] .bg { fill: var(--eicui-base-color-info-100); }
|
||||
[eicnodemap] .entity .badge text {
|
||||
fill: var(--app-color-white);
|
||||
font-size: normal;
|
||||
}
|
||||
[eicnodemap] .entity .badge[xxsmall] text { font-size: xx-small; }
|
||||
[eicnodemap] .entity .badge[xsmall] text { font-size: x-small; }
|
||||
[eicnodemap] .entity .badge[small] text { font-size: small; }
|
||||
[eicnodemap] .entity .badge[large] text { font-size: large; }
|
||||
[eicnodemap] .entity .badge[xlarge] text { font-size:x-large }
|
||||
[eicnodemap] .entity .badge[xxlarge] text { font-size:xx-large }
|
||||
[eicnodemap] .entity .type { font-size: 11px; }
|
||||
[eicnodemap] .relation {
|
||||
fill: none;
|
||||
stroke: var(--app-color-info);
|
||||
stroke-width: 4px;
|
||||
stroke-dasharray: 10,5;
|
||||
transition: all 0.7s;
|
||||
}
|
||||
[eicnodemap] .entity[disabled] {
|
||||
pointer-events: none;
|
||||
}
|
||||
[eicnodemap] .entity[disabled] .title,
|
||||
[eicnodemap] .entity[disabled] .subtitle {
|
||||
opacity: 0.45;
|
||||
}
|
||||
+545
@@ -0,0 +1,545 @@
|
||||
|
||||
/**
|
||||
* @augments EicComponent
|
||||
* @category EICUI/AdvancedComponents
|
||||
*/
|
||||
class NodeMap extends EicComponent {
|
||||
|
||||
entities = [];
|
||||
relations = [];
|
||||
|
||||
constructor(el, options) {
|
||||
|
||||
let defaultOptions = {
|
||||
resizable: true,
|
||||
allowDrag: true,
|
||||
orientation: 'linear',
|
||||
entity: {
|
||||
width: 200,
|
||||
height: 50,
|
||||
gap: 40
|
||||
},
|
||||
map: {
|
||||
position: { x: 0, y: 0 },
|
||||
size: { width: 0, height: 0 },
|
||||
dragging: false,
|
||||
},
|
||||
scale: {
|
||||
current: 1.0,
|
||||
factor: 0.085,
|
||||
min: 0.5,
|
||||
max: 4
|
||||
},
|
||||
guides: true
|
||||
}
|
||||
|
||||
super(el, {...defaultOptions, ...(options || {})});
|
||||
|
||||
if(this._constructed) return this;
|
||||
|
||||
this.el.setAttribute('eicnodemap','');
|
||||
|
||||
if(this.options.resizable)
|
||||
this.el.setAttribute('resizable','');
|
||||
else
|
||||
this.el.removeAttribute('resizable');
|
||||
|
||||
this.el.innerHTML = '';
|
||||
this.map = ui.create(`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"></svg>`)
|
||||
this.el.append(this.map);
|
||||
|
||||
this.viewbox = { x: 0, y: 0, w: this.el.clientWidth, h: this.el.clientHeight };
|
||||
|
||||
if(this.options.allowDrag) {
|
||||
this.map.addEventListener('wheel', this.onMapScroll.bind(this));
|
||||
this.map.addEventListener('mousedown', this.onMapDragStart.bind(this));
|
||||
this.map.addEventListener('mousemove', this.onMapDragMove.bind(this));
|
||||
this.mouseUpHandler = this.onMapDragStop.bind(this);
|
||||
this.entityUpHandler = this.onEntityDragStop.bind(this);
|
||||
}
|
||||
}
|
||||
|
||||
set loading(state) {
|
||||
if(state == true)
|
||||
this.el.setAttribute('loading','');
|
||||
else
|
||||
this.el.removeAttribute('loading');
|
||||
}
|
||||
get loading() { return this.el.hasAttribute('loading') }
|
||||
|
||||
set data(value) {
|
||||
|
||||
this.clear();
|
||||
|
||||
if(value.entities) for(let entity of value.entities) this.addEntity(entity);
|
||||
if(value.relations) for(let relation of value.relations) this.addRelation(relation);
|
||||
|
||||
this.reorder();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.map.innerHTML = '';
|
||||
this.entities = [];
|
||||
}
|
||||
|
||||
reorder() {
|
||||
switch(this.options.orientation) {
|
||||
case 'linear':
|
||||
this.reorderLinear();
|
||||
break;
|
||||
case 'radial':
|
||||
default:
|
||||
this.reorderRadial();
|
||||
}
|
||||
}
|
||||
|
||||
reorderLinear() {
|
||||
|
||||
// 1. prepare geometries and identify starters and enders
|
||||
for(let entity of this.entities) {
|
||||
entity.geometry = {
|
||||
starter: false,
|
||||
ender: false,
|
||||
depth: null,
|
||||
siblings: null,
|
||||
index: null
|
||||
};
|
||||
|
||||
if(!entity.relations.find(r => r.target.id == entity.id)) {
|
||||
entity.geometry.starter = true;
|
||||
entity.geometry.depth = 0
|
||||
}
|
||||
|
||||
entity.geometry.ender = !entity.relations.find(r => r.source.id == entity.id);
|
||||
}
|
||||
|
||||
// 2. dig paths using starters
|
||||
let starters = this.entities.filter(o => o.geometry.starter == true)
|
||||
for(let entity of starters) {
|
||||
this.digLinearPath(entity)
|
||||
}
|
||||
|
||||
// 3. resolve siblings
|
||||
let maxSiblings = 0;
|
||||
for(let entity of this.entities) {
|
||||
entity.geometry.siblings = this.entities.filter(o => o.geometry.depth == entity.geometry.depth).length - 1
|
||||
maxSiblings = Math.max(maxSiblings, entity.geometry.siblings);
|
||||
let i = 0;
|
||||
|
||||
for(let relation of entity.relations) {
|
||||
if(relation.target.id != entity.id && !relation.target.geometry.index) {
|
||||
relation.target.geometry.index = i;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 4. reposition
|
||||
let maxHeight = 3 * maxSiblings * (this.options.entity.height + (this.options.entity.gap))
|
||||
for(let entity of this.entities) {
|
||||
|
||||
entity.x = this.options.entity.gap + (this.options.entity.width + this.options.entity.gap) * entity.geometry.depth;
|
||||
|
||||
entity.y = maxHeight * (entity.geometry.index + 1) / (entity.geometry.siblings + 2);
|
||||
for(let relation of entity.relations) {
|
||||
relation.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
this.center();
|
||||
}
|
||||
|
||||
digLinearPath(entity) {
|
||||
for(let relation of entity.relations) {
|
||||
if(relation.target.id != entity.id) {
|
||||
if(!relation.target.geometry.depth) {
|
||||
relation.target.geometry.depth = entity.geometry.depth + 1;
|
||||
this.digLinearPath(relation.target)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
center() {
|
||||
let box = this.map.getBBox();
|
||||
this.options.scale.current = Math.max(
|
||||
(box.height + this.options.entity.gap) / (this.viewbox.h ),
|
||||
(box.width + this.options.entity.gap) / (this.viewbox.w )
|
||||
)
|
||||
|
||||
this.viewbox.y = box.y - (this.options.entity.gap / 2);
|
||||
this.viewbox.x = 20;
|
||||
this.setViewbox();
|
||||
}
|
||||
|
||||
reorderRadial() {
|
||||
let a = (Math.PI * 2) / this.entities.length;
|
||||
let r = (this.options.entity.width) * Math.log(this.entities.length);
|
||||
let i = 0;
|
||||
let center = { x: this.viewbox.w / 2, y: this.viewbox.h / 2 }
|
||||
|
||||
for(let entity of this.entities) {
|
||||
let pos = {
|
||||
x: center.x + (Math.cos(a * i - Math.PI * .5) * r) - (this.options.entity.width / 2),
|
||||
y: center.y + (Math.sin(a * i - Math.PI * .5) * r) - (this.options.entity.height / 2)
|
||||
}
|
||||
entity.x = pos.x;
|
||||
entity.y = pos.y;
|
||||
|
||||
for(let relation of entity.relations) relation.redraw();
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
getEntityById(id) { return this.entities.find(entity => entity.id == id); }
|
||||
|
||||
addEntity(entity) {
|
||||
let index = this.entities.length;
|
||||
let options = { ...entity, ...this.options.entity };
|
||||
let item = new NodeMapNode(null, options);
|
||||
item.x = this.options.entity.gap + (this.options.entity.width + this.options.entity.gap) * index;
|
||||
item.y = 30;
|
||||
|
||||
item.el.addEventListener('selected', this.onEntitySelect.bind(this));
|
||||
|
||||
this.entities.push(item);
|
||||
this.map.append(item.el);
|
||||
}
|
||||
|
||||
removeEntity(entity) {
|
||||
let parents = entity.relations.filter(r => r.target.id == entity.id);
|
||||
let childs = entity.relations.filter(r => r.source.id == entity.id);
|
||||
|
||||
for(let relation of parents) {
|
||||
let parent = relation.source;
|
||||
parent.relations = parent.relations.filter(r => r.target.id != entity.id);
|
||||
|
||||
for(let relation of childs) {
|
||||
let child = relation.target;
|
||||
child.relations = child.relations.filter(r => r.source.id != entity.id);
|
||||
this.addRelation({source: parent.id, target: child.id});
|
||||
}
|
||||
}
|
||||
|
||||
entity.relations.forEach(r => r.el.remove())
|
||||
entity.el.remove();
|
||||
this.entities = this.entities.filter(e => e.id != entity.id)
|
||||
|
||||
this.reorder();
|
||||
}
|
||||
|
||||
addRelation(relation) {
|
||||
let source = this.entities.find(entity => entity.id == relation.source);
|
||||
let target = this.entities.find(entity => entity.id == relation.target);
|
||||
let item = new NodeMapRelation(source, target);
|
||||
source.relations.push(item);
|
||||
target.relations.push(item);
|
||||
this.map.prepend(item.el);
|
||||
}
|
||||
|
||||
onEntityDragStart(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.current = event.currentTarget
|
||||
|
||||
this.mouseStartPosition = { x: event.pageX, y: event.pageY }
|
||||
this.entityStartPosition = { x: Number.parseFloat(this.current.getAttribute("x")), y: Number.parseFloat(this.current.getAttribute("y")) }
|
||||
|
||||
window.addEventListener("mouseup", this.entityUpHandler);
|
||||
|
||||
this.entityDrag = true;
|
||||
}
|
||||
|
||||
|
||||
onEntityMove(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.mousePosition = { x: event.offsetX, y: event.offsetY };
|
||||
|
||||
if(this.entityDrag) {
|
||||
this.current.setAttribute("x", this.entityStartPosition.x - (this.mouseStartPosition.x - event.pageX) * this.options.scale.current)
|
||||
this.current.setAttribute("y", this.entityStartPosition.y - (this.mouseStartPosition.y - event.pageY) * this.options.scale.current)
|
||||
|
||||
let entity = this.entities.find(item => item.data.id == (this.options.entity.current.getAttribute("data-id")))
|
||||
let pos = { x: Number.parseFloat(this.options.entity.current.getAttribute("x")), y: Number.parseFloat(this.options.entity.current.getAttribute("y")) }
|
||||
|
||||
/*
|
||||
for(let line of entity.lines) {
|
||||
if(line.getAttribute("data-source") == entity.data.id) {
|
||||
line.setAttribute("x1", pos.x + (this.options.entity.width / 2));
|
||||
line.setAttribute("y1", pos.y + (this.options.entity.height / 2));
|
||||
} else {
|
||||
line.setAttribute("x2", pos.x + (this.options.entity.width / 2));
|
||||
line.setAttribute("y2", pos.y + (this.options.entity.height / 2));
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
onEntityDragStop(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
window.removeEventListener("mouseup", this.entityUpHandler);
|
||||
this.options.entity.dragging = false;
|
||||
}
|
||||
|
||||
onEntitySelect(event) {
|
||||
let entity = event.detail;
|
||||
|
||||
this.entities.forEach(item => {
|
||||
item.el.classList.remove('selected');
|
||||
item.el.classList.remove('related');
|
||||
//item.lines.forEach(line => line.classList.remove('related'))
|
||||
});
|
||||
entity.el.classList.add('selected');
|
||||
|
||||
/*
|
||||
entity.lines.forEach(line => {
|
||||
line.classList.add('related');
|
||||
let other = this.entities.find(item => (item.data.id == line.getAttribute('data-target') && line.getAttribute('data-source') == selected.data.id) || (item.data.id == line.getAttribute('data-source') && line.getAttribute('data-target') == selected.data.id));
|
||||
other.el.classList.add('related');
|
||||
});
|
||||
*/
|
||||
|
||||
this.click(entity);
|
||||
}
|
||||
|
||||
onMapScroll(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let scale = 1. + (event.deltaY < 0 ? - this.options.scale.factor : this.options.scale.factor);
|
||||
|
||||
if((this.options.scale.current * scale < this.options.scale.max) && (this.options.scale.current * scale > this.options.scale.min))
|
||||
{
|
||||
let pos = {
|
||||
x: (this.mousePosition.x * this.options.scale.current) + this.viewbox.x,
|
||||
y: (this.mousePosition.y * this.options.scale.current) + this.viewbox.y
|
||||
}
|
||||
|
||||
this.viewbox.x = (this.viewbox.x - pos.x) * scale + pos.x;
|
||||
this.viewbox.y = (this.viewbox.y - pos.y) * scale + pos.y;
|
||||
this.options.scale.current *= scale;
|
||||
|
||||
this.setViewbox();
|
||||
}
|
||||
}
|
||||
|
||||
onMapDragStart(event) {
|
||||
this.mouseStartPosition = { x: event.pageX, y: event.pageY }
|
||||
this.viewboxStartPosition = { x: this.viewbox.x, y: this.viewbox.y }
|
||||
this.options.map.dragging = true;
|
||||
|
||||
window.addEventListener("mouseup", this.mouseUpHandler);
|
||||
}
|
||||
|
||||
onMapDragMove(event) {
|
||||
this.mousePosition = { x: event.offsetX, y: event.offsetY };
|
||||
|
||||
if(this.options.map.dragging) {
|
||||
this.viewbox.x = this.viewboxStartPosition.x + (this.mouseStartPosition.x - event.pageX) * this.options.scale.current;
|
||||
this.viewbox.y = this.viewboxStartPosition.y + (this.mouseStartPosition.y - event.pageY) * this.options.scale.current;
|
||||
this.setViewbox();
|
||||
}
|
||||
}
|
||||
|
||||
onMapDragStop(event) {
|
||||
window.removeEventListener("mouseup", this.mouseUpHandler);
|
||||
this.options.map.dragging = false;
|
||||
}
|
||||
|
||||
onMapResize(event) {
|
||||
this.viewbox.w = this.find('.results').clientWidth;
|
||||
//this.viewbox.w = this.map.viewBox.baseVal.width;
|
||||
this.viewbox.h = this.find('.results').clientHeight;
|
||||
//this.viewbox.h = this.map.viewBox.baseVal.height;
|
||||
|
||||
this.setViewbox();
|
||||
}
|
||||
|
||||
setViewbox() { this.map.setAttribute("viewBox", this.viewbox.x + " " + this.viewbox.y + " " + (this.viewbox.w * this.options.scale.current) + " " + (this.viewbox.h * this.options.scale.current)); }
|
||||
|
||||
click() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @augments EicComponent
|
||||
* @category EICUI/AdvancedComponents
|
||||
*/
|
||||
class NodeMapNode extends EicComponent {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
relations = [];
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
id = "";
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
options = {
|
||||
badge: {
|
||||
size: 'large',
|
||||
severity: 'danger'
|
||||
}
|
||||
}
|
||||
|
||||
constructor(el, options) {
|
||||
super(el, options);
|
||||
|
||||
this.setOptions(options);
|
||||
|
||||
if(!el) {
|
||||
this.el = ui.create(
|
||||
`<svg class="entity" ${options.severity ? options.severity: '' } x="0" y="0" text-anchor="middle">
|
||||
<rect class="bg" width="${options.width}" height="${options.height}" x="2" y="5" rx="4"></rect>
|
||||
<text dominant-baseline="middle" text-anchor="middle" class="title" x="${options.width / 2}" y="${options.height / 3}">${options.title}</text>
|
||||
<text dominant-baseline="middle" text-anchor="middle" class="subtitle" x="${options.width / 2}" y="${(options.height / 3) * 2}">${options.subtitle}</text>
|
||||
</svg>`
|
||||
);
|
||||
|
||||
if(options.badge) this.badge = options.badge;
|
||||
}
|
||||
|
||||
if(options.data) {
|
||||
for(let property in options.data) {
|
||||
this.el.setAttribute('data-' + property, options.data[property]);
|
||||
if(property == "id") this.id = options.data[property];
|
||||
}
|
||||
}
|
||||
|
||||
this.el.addEventListener('click', this.onClick.bind(this));
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
set x(value) { this.el.setAttribute('x', value); }
|
||||
get x() { return parseInt(this.el.getAttribute('x')); }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
set y(value) { this.el.setAttribute('y', value); }
|
||||
get y() { return parseInt(this.el.getAttribute('y')); }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
set disabled(state) { state ? this.el.setAttribute('disabled',''): this.el.removeAttribute('disabled'); }
|
||||
get disabled() { return this.el.hasAttribute('disabled'); }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
get width() { return parseInt(this.el.querySelector('rect').getAttribute('width')); }
|
||||
get height() { return parseInt(this.el.querySelector('rect').getAttribute('height')); }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
set title(str) { this.el.querySelector('.title').innerHTML = str; }
|
||||
set subtitle(str) { this.el.querySelector('.subtitle').innerHTML = str; }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
set badge(value) {
|
||||
|
||||
if(!this._badge && value != 0) {
|
||||
let r = 14;
|
||||
|
||||
switch(this.options.badge.size) {
|
||||
case 'xxsmall': r = 8; break;
|
||||
case 'xsmall': r = 10; break;
|
||||
case 'small': r = 12; break;
|
||||
case 'large': r = 16; break;
|
||||
case 'xlarge': r = 18; break;
|
||||
case 'xxlarge': r = 20; break;
|
||||
case 'xxxlarge': r = 24; break;
|
||||
}
|
||||
|
||||
this._badge = ui.create(
|
||||
`<svg class="badge" x="${this.options.width - r}" y="0" ${this.options.badge.size} ${this.options.badge.severity} text-anchor="middle">
|
||||
<circle class="bg" cx="${r}" cy="${r}" r="${r}"></circle>
|
||||
<text dominant-baseline="middle" text-anchor="middle" x="${r}" y="${r}"></text>
|
||||
</svg>`
|
||||
);
|
||||
this.el.append(this._badge)
|
||||
|
||||
}
|
||||
|
||||
this._badge.querySelector('text').textContent = value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
set data(value) { }
|
||||
|
||||
onClick(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.el.dispatchEvent(new CustomEvent('selected', {detail: this}))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @augments EicComponent
|
||||
* @category EICUI/AdvancedComponents
|
||||
*/
|
||||
class NodeMapRelation extends EicComponent {
|
||||
|
||||
source = null;
|
||||
target = null;
|
||||
|
||||
constructor(source, target, options) {
|
||||
super(null, options);
|
||||
|
||||
this.setOptions(options);
|
||||
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
|
||||
//this.el = ui.create( `<line class="relation" x1="0" y1="0" x2="0" y2="0" data-source="${source.id}" data-target="${target.id}" />` );
|
||||
this.el = document.createElementNS('http://www.w3.org/2000/svg','path');
|
||||
this.el.setAttribute('class', 'relation');
|
||||
|
||||
this.el.dataset.source = source.id;
|
||||
this.el.dataset.target = target.id;
|
||||
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
redraw() {
|
||||
|
||||
let o = {
|
||||
x: this.source.x + this.source.width / 2,
|
||||
y: this.source.y + this.source.height / 2
|
||||
}
|
||||
|
||||
let d = {
|
||||
x: o.x + Math.abs((this.target.y + this.target.height / 2) - o.y),
|
||||
y: this.target.y + this.target.height / 2
|
||||
}
|
||||
|
||||
let p1 = { x: o.x, y: d.y }
|
||||
|
||||
let f = {
|
||||
x: this.target.x + this.target.width / 2,
|
||||
y: this.target.y + this.target.height / 2
|
||||
}
|
||||
|
||||
let path = `M ${o.x} ${o.y} C ${p1.x} ${p1.y} ${p1.x} ${p1.y} ${d.x} ${d.y} L ${f.x} ${f.y}`
|
||||
|
||||
this.el.setAttribute('d', path)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user