mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Add somewhat working Minimap
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -200,6 +200,9 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.mini-node {
|
||||
color: $brand-primary
|
||||
}
|
||||
}
|
||||
#input-choices-menu {
|
||||
color: black;
|
||||
|
||||
Reference in New Issue
Block a user