Add somewhat working Minimap

This commit is contained in:
mvdbeek
2023-01-09 17:15:45 +01:00
parent 95ddbca065
commit 6c850f11f1
10 changed files with 165 additions and 26 deletions
@@ -19,10 +19,6 @@ export default {
Draggable,
},
props: {
position: {
type: Object,
required: false,
},
rootOffset: {
type: Object,
required: false,
@@ -69,6 +69,7 @@
:steps="steps"
:datatypes-mapper="datatypesMapper"
:get-manager="getManager"
:nodes="nodes"
@onUpdate="onUpdate"
@onClone="onClone"
@onCreate="onInsertTool"
@@ -0,0 +1,100 @@
<template>
<div class="workflow-overview" aria-hidden="true">
<div class="workflow-overview-body">
<div id="overview-container">
<svg width="100%" height="100%" :viewBox="viewBox">
<MinimapNode
class="mini-node"
:node="node"
:step="steps[node.id]"
v-for="node of Object.values(nodes)"
:key="node.id" />
<rect
class="mini-node"
:x="visible.x"
:y="visible.y"
:width="visible.width"
:height="visible.height"
fill-opacity="0.5" />
</svg>
<div id="overview-viewport" />
</div>
</div>
</div>
</template>
<script>
import MinimapNode from "./MinimapNode.vue";
export default {
components: {
MinimapNode,
},
props: {
nodes: {
type: Object,
required: true,
},
steps: {
type: Object,
required: true,
},
width: {
type: Number,
default: 200,
},
height: {
type: Number,
default: 200,
},
scale: {
type: Number,
default: 1,
},
pan: {
type: Object,
default() {
return { x: 0, y: 0 };
},
},
rootOffset: {
type: Object,
},
},
computed: {
visible() {
// translate current rootOffset rectangle into scaled and panned space
const x = this.pan.x / -this.scale;
const y = this.pan.y / -this.scale;
const width = this.rootOffset.width / this.scale;
const height = this.rootOffset.bottom / this.scale;
const rval = { x, y, width, height };
return rval;
},
bounds() {
let left = this.pan.x * -1;
let right = this.rootOffset.width;
let top = this.pan.y * -1;
let bottom = this.rootOffset.bottom;
let p;
Object.values(this.steps).forEach((step) => {
const node = this.nodes[step.id];
p = node.position;
left = Math.min(left, step.position.left);
right = Math.max(right, p.right);
top = Math.min(top, step.position.top);
bottom = Math.max(bottom, p.bottom);
});
return {
left: left / this.scale,
right: right / this.scale,
top: top / this.scale,
bottom: bottom / this.scale,
};
},
viewBox() {
return `${this.bounds.left} ${this.bounds.top} ${this.bounds.right} ${this.bounds.bottom}`;
},
},
};
</script>
@@ -0,0 +1,18 @@
<template>
<rect :x="step.position.left" :y="step.position.top" :width="node.position.width" :height="node.position.height" />
</template>
<script>
export default {
props: {
node: {
type: Object,
},
step: {
type: Object,
},
},
};
</script>
<style></style>
@@ -1,6 +1,6 @@
<template>
<draggable-wrapper
:position="step.position || defaultPosition"
ref="el"
@updatePosition="onUpdatePosition"
@mouseup="makeActive"
@move="onMove"
@@ -65,7 +65,6 @@
:key="input.name"
:input="input"
:get-node="getNode"
:get-manager="getManager"
:datatypes-mapper="datatypesMapper"
:root-offset="rootOffset"
:offset-x="offset.x"
@@ -81,7 +80,6 @@
:output="output"
:post-job-actions="postJobActions"
:get-node="getNode"
:get-manager="getManager"
:root-offset="rootOffset"
:offset-x="offset.x"
:offset-y="offset.y"
@@ -106,12 +104,19 @@ import NodeInput from "./NodeInput";
import NodeOutput from "./NodeOutput";
import DraggableWrapper from "./Draggable";
import { ActiveOutputs } from "./modules/outputs";
import { reactive, ref } from "vue";
import { useElementBounding } from "@vueuse/core";
Vue.use(BootstrapVue);
const OFFSET_RANGE = 100;
export default {
setup() {
const el = ref(null);
const position = reactive(useElementBounding(el));
return { el, position };
},
components: {
DraggableWrapper,
LoadingSpan,
@@ -27,10 +27,6 @@ export default {
type: Function,
required: true,
},
getManager: {
type: Function,
required: true,
},
datatypesMapper: {
type: Object,
required: true,
@@ -126,5 +122,11 @@ export default {
console.log("onDrop", e);
},
},
beforeDestroy() {
this.$store.commit("workflowState/deleteInputTerminalPosition", {
stepId: this.nodeId,
inputName: this.input.name,
});
},
};
</script>
@@ -49,10 +49,6 @@ export default {
type: Function,
required: true,
},
getManager: {
type: Function,
required: true,
},
postJobActions: {
type: Object,
required: true,
@@ -170,6 +166,10 @@ export default {
},
},
beforeDestroy() {
this.$store.commit("workflowState/deleteOutputTerminalPosition", {
stepId: this.getNode().id,
outputName: this.output.name,
});
this.$emit("onRemove", this.output);
},
methods: {
@@ -25,7 +25,7 @@
</g>
</svg>
</SvgPanZoom>
<div class="nodeArea" ref="nodes" :style="style">
<div class="nodeArea" :style="style">
<!-- this div is only necessary so that we synchronize zoom and pan to the svg coordinates -->
<WorkflowNode
v-for="(step, key) in steps"
@@ -49,14 +49,7 @@
</Draggable>
</div>
</div>
<div class="workflow-overview" aria-hidden="true">
<div class="workflow-overview-body">
<div id="overview-container">
<canvas id="overview-canvas" width="0" height="0" />
<div id="overview-viewport" />
</div>
</div>
</div>
<Minimap :nodes="nodes" :steps="steps" :root-offset="position" :scale="zoomLevel" :pan="currentPan" />
</div>
</template>
<script>
@@ -65,6 +58,7 @@ import WorkflowNode from "./Node";
import RawConnector from "./Connector";
import TerminalConnector from "./TerminalConnector";
import Draggable from "./Draggable.vue";
import Minimap from "./Minimap.vue";
import SvgPanZoom from "vue-svg-pan-zoom";
import { reactive, ref } from "vue";
import { useElementBounding } from "@vueuse/core";
@@ -81,6 +75,7 @@ export default {
SvgPanZoom,
TerminalConnector,
WorkflowNode,
Minimap,
ZoomControl,
},
data() {
@@ -104,6 +99,10 @@ export default {
type: Object,
default: null,
},
nodes: {
type: Object,
required: true,
},
},
methods: {
onUpdatedCTM(CTM) {
@@ -155,6 +154,11 @@ export default {
return `transform: matrix(${this.transform.a}, ${this.transform.b}, ${this.transform.c}, ${this.transform.d}, ${this.transform.e}, ${this.transform.f})`;
}
},
currentPan() {
if (this.transform) {
return { x: this.transform.e, y: this.transform.f };
}
},
zoomLevel() {
return this.$store.getters["workflowState/getScale"]();
},
@@ -184,5 +188,8 @@ export default {
return connections;
},
},
beforeDestroy() {
this.svgpanzoom.destroy();
},
};
</script>
+9 -2
View File
@@ -10,7 +10,6 @@ export const state = {
import Vue from "vue";
const getters = {
getInputTerminalPosition: (state) => (stepId, inputName) => {
console.log("getting input for", stepId, inputName);
return state.inputTerminals[stepId]?.[inputName];
},
getOutputTerminalPosition: (state) => (stepId, outputName) => {
@@ -47,6 +46,14 @@ const mutations = {
Vue.set(state.outputTerminals[stepId], outputName, position);
}
},
deleteInputTerminalPosition: (state, { stepId, inputName }) => {
console.log("deleting output");
delete state.inputTerminals[stepId][inputName];
},
deleteOutputTerminalPosition: (state, { stepId, outputName }) => {
console.log("deleting output");
delete state.outputTerminals[stepId][outputName];
},
setConnection: (state, { source, target, connection }) => {
if (!state.connections[source]) {
state.connections[source] = { target: connection };
@@ -61,7 +68,7 @@ const mutations = {
state.scale = scale;
},
setNode: (state, node) => {
state.nodes[node.id] = node;
Vue.set(state.nodes, node.id, node);
},
deleteNode: (state, nodeId) => {
delete state.nodes[nodeId];
+3
View File
@@ -200,6 +200,9 @@
width: 100%;
height: 100%;
}
.mini-node {
color: $brand-primary
}
}
#input-choices-menu {
color: black;