|
|
@@ -17,15 +17,15 @@
|
|
|
-->
|
|
|
|
|
|
<template>
|
|
|
- <div class="er-diagram">
|
|
|
+ <div ref="erd" class="er-diagram">
|
|
|
<div class="buttons-panel">
|
|
|
<button class="btn btn-default btn-sm" title="Toggle Fullscreen" @click="toggleFS">
|
|
|
- <expand-icon v-pre class="fa fa-expand" />
|
|
|
+ <expand-icon v-pre class="fa fa-expand">​</expand-icon>
|
|
|
</button>
|
|
|
</div>
|
|
|
|
|
|
<div class="erd-scroll-panel">
|
|
|
- <div class="erd-container">
|
|
|
+ <div v-if="groups" class="erd-container">
|
|
|
<div v-for="(group, index) in groups" :key="index" class="entity-group">
|
|
|
<div v-for="entity in group" :key="entity.id" class="entity-container">
|
|
|
<TableEntity
|
|
|
@@ -44,7 +44,7 @@
|
|
|
</div>
|
|
|
|
|
|
<!-- Relations -->
|
|
|
- <svg class="erd-relations">
|
|
|
+ <svg v-if="relations" class="erd-relations">
|
|
|
<path
|
|
|
v-for="(relation, index) in relations"
|
|
|
:key="index"
|
|
|
@@ -59,7 +59,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts">
|
|
|
- import { Component, Prop, Vue } from 'vue-property-decorator';
|
|
|
+ import { defineComponent, PropType } from 'vue';
|
|
|
|
|
|
import { groupEntities } from './lib/processor';
|
|
|
import { IEntity, IRelation } from './lib/interfaces';
|
|
|
@@ -70,8 +70,6 @@
|
|
|
import TableEntity from './comps/table-entity.vue';
|
|
|
import LiteralEntity from './comps/literal-entity.vue';
|
|
|
|
|
|
- import './er-diagram.scss';
|
|
|
-
|
|
|
const CURVATURE = 40;
|
|
|
|
|
|
interface IPos {
|
|
|
@@ -79,84 +77,107 @@
|
|
|
y: number;
|
|
|
}
|
|
|
|
|
|
- @Component({
|
|
|
+ export default defineComponent({
|
|
|
components: {
|
|
|
TableEntity,
|
|
|
LiteralEntity
|
|
|
- }
|
|
|
- })
|
|
|
- export default class ERDiagram extends Vue {
|
|
|
- @Prop()
|
|
|
- entities!: IEntity[];
|
|
|
- @Prop()
|
|
|
- relations!: IRelation[];
|
|
|
-
|
|
|
- EntityTypes = EntityTypes;
|
|
|
-
|
|
|
- get groups(): IEntity[][] | undefined {
|
|
|
- if (this.entities && this.relations) {
|
|
|
- return groupEntities(this.entities, this.relations);
|
|
|
+ },
|
|
|
+
|
|
|
+ props: {
|
|
|
+ entities: {
|
|
|
+ type: Object as PropType<IEntity[]>,
|
|
|
+ default: []
|
|
|
+ },
|
|
|
+ relations: {
|
|
|
+ type: Object as PropType<IRelation[]>,
|
|
|
+ default: []
|
|
|
}
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- getSelectorPosition(selector: string, offset: IPos | undefined = undefined): IPos | undefined {
|
|
|
- const element = this.$el.querySelector(selector);
|
|
|
- if (element) {
|
|
|
- const rect = element.getBoundingClientRect();
|
|
|
- let x = rect.left;
|
|
|
- let y = rect.top;
|
|
|
+ emits: ['toggle-fullscreen', 'entity-clicked'],
|
|
|
|
|
|
- if (offset) {
|
|
|
- x = x - offset.x;
|
|
|
- y = y - offset.y + 1;
|
|
|
- }
|
|
|
+ setup() {
|
|
|
+ return {
|
|
|
+ EntityTypes
|
|
|
+ };
|
|
|
+ },
|
|
|
|
|
|
- return { x, y };
|
|
|
+ computed: {
|
|
|
+ groups(): IEntity[][] | undefined {
|
|
|
+ if (this.entities && this.relations) {
|
|
|
+ return groupEntities(this.entities, this.relations);
|
|
|
+ }
|
|
|
+ return undefined;
|
|
|
}
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- plotRelations(): void {
|
|
|
- const relationPaths: NodeListOf<HTMLElement> = this.$el.querySelectorAll<HTMLElement>(
|
|
|
- '.relation-path'
|
|
|
- );
|
|
|
- const offset: IPos | undefined = this.getSelectorPosition('.erd-relations');
|
|
|
-
|
|
|
- relationPaths.forEach(element => {
|
|
|
- const leftPos = this.getSelectorPosition(
|
|
|
- `[data-entity-id~="${element.dataset.entityIdLeft}"] .right-point`,
|
|
|
- offset
|
|
|
- );
|
|
|
- const rightPos = this.getSelectorPosition(
|
|
|
- `[data-entity-id~="${element.dataset.entityIdRight}"] .left-point`,
|
|
|
- offset
|
|
|
- );
|
|
|
-
|
|
|
- if (leftPos && rightPos) {
|
|
|
- const path: string =
|
|
|
- `M ${leftPos.x},${leftPos.y} C ${leftPos.x + CURVATURE},${leftPos.y} ` +
|
|
|
- `${rightPos.x - CURVATURE},${rightPos.y} ${rightPos.x},${rightPos.y}`;
|
|
|
- element.setAttribute('d', path);
|
|
|
-
|
|
|
- element.style.visibility = 'visible';
|
|
|
- } else {
|
|
|
- element.style.visibility = 'hidden';
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
updated(): void {
|
|
|
this.plotRelations();
|
|
|
- }
|
|
|
+ },
|
|
|
mounted(): void {
|
|
|
this.plotRelations();
|
|
|
+ },
|
|
|
+
|
|
|
+ methods: {
|
|
|
+ getSelectorPosition(
|
|
|
+ selector: string,
|
|
|
+ offset: IPos | undefined = undefined
|
|
|
+ ): IPos | undefined {
|
|
|
+ const element = this.$el.querySelector(selector);
|
|
|
+ if (element) {
|
|
|
+ const rect = element.getBoundingClientRect();
|
|
|
+ let x = rect.left;
|
|
|
+ let y = rect.top;
|
|
|
+
|
|
|
+ if (offset) {
|
|
|
+ x = x - offset.x;
|
|
|
+ y = y - offset.y + 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return { x, y };
|
|
|
+ }
|
|
|
+ },
|
|
|
+ plotRelations(): void {
|
|
|
+ const erdEl = this.$refs.erd as HTMLElement;
|
|
|
+ const relationPaths: NodeListOf<HTMLElement> = erdEl.querySelectorAll<HTMLElement>(
|
|
|
+ '.relation-path'
|
|
|
+ );
|
|
|
+ const offset: IPos | undefined = this.getSelectorPosition('.erd-relations');
|
|
|
+
|
|
|
+ relationPaths.forEach(element => {
|
|
|
+ const leftPos = this.getSelectorPosition(
|
|
|
+ `[data-entity-id~="${element.dataset.entityIdLeft}"] .right-point`,
|
|
|
+ offset
|
|
|
+ );
|
|
|
+ const rightPos = this.getSelectorPosition(
|
|
|
+ `[data-entity-id~="${element.dataset.entityIdRight}"] .left-point`,
|
|
|
+ offset
|
|
|
+ );
|
|
|
+
|
|
|
+ if (leftPos && rightPos) {
|
|
|
+ const path: string =
|
|
|
+ `M ${leftPos.x},${leftPos.y} C ${leftPos.x + CURVATURE},${leftPos.y} ` +
|
|
|
+ `${rightPos.x - CURVATURE},${rightPos.y} ${rightPos.x},${rightPos.y}`;
|
|
|
+ element.setAttribute('d', path);
|
|
|
+
|
|
|
+ element.style.visibility = 'visible';
|
|
|
+ } else {
|
|
|
+ element.style.visibility = 'hidden';
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ toggleFS(): void {
|
|
|
+ toggleFullScreen(this.$el);
|
|
|
+ this.$emit('toggle-fullscreen');
|
|
|
+ },
|
|
|
+
|
|
|
+ entityClicked(entity: IEntity): void {
|
|
|
+ this.$emit('entity-clicked', entity);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- toggleFS(): void {
|
|
|
- toggleFullScreen(this.$el);
|
|
|
- this.$emit('toggle-fullscreen');
|
|
|
- }
|
|
|
-
|
|
|
- entityClicked(entity: IEntity): void {
|
|
|
- this.$emit('entity-clicked', entity);
|
|
|
- }
|
|
|
- }
|
|
|
+ });
|
|
|
</script>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+ @import './er-diagram.scss';
|
|
|
+</style>
|