Switch to composables for Minimap.vue

This commit is contained in:
mvdbeek
2023-01-09 17:15:46 +01:00
parent 4b201dd081
commit 770ec675bf
4 changed files with 118 additions and 45 deletions
@@ -1,7 +1,7 @@
<template>
<div ref="overview" class="workflow-overview" :style="style" aria-hidden="true">
<div class="workflow-overview-body">
<svg width="100%" height="100%" :viewBox="viewBox" @click="onClick">
<svg width="100%" height="100%" :viewBox="viewBox">
<MinimapNode
class="mini-node"
:node="node"
@@ -9,11 +9,15 @@
v-for="node of Object.values(nodes)"
:key="node.id" />
<rect
class="mini-node"
class="viewport"
ref="rect"
:x="visible.x"
:y="visible.y"
:width="visible.width"
:height="visible.height"
stroke-width="1%"
rx="1%"
fill="white"
fill-opacity="0.5" />
</svg>
</div>
@@ -22,14 +26,89 @@
<script>
import MinimapNode from "./MinimapNode.vue";
import Draggable from "./Draggable.vue";
import { reactive, ref } from "vue";
import { computed, reactive, ref, watchEffect } from "vue";
import { useDraggable } from "@vueuse/core";
export default {
setup() {
setup(props, { emit }) {
const overview = ref(null);
const overviewPosition = reactive(useDraggable(overview, { preventDefault: true }));
return { overview, overviewPosition };
const overviewPosition = reactive(useDraggable(overview, { preventDefault: true, exact: true }));
const size = ref(parseInt(localStorage.getItem("overview-size")) || 150);
const visible = computed(() => {
const x = props.pan.x / -props.scale;
const y = props.pan.y / -props.scale;
const width = props.rootOffset.width / props.scale;
const height = props.rootOffset.bottom / props.scale;
const rval = { x, y, width, height };
return rval;
});
const bounds = computed(() => {
let left = props.pan.x * -1;
let right = props.rootOffset.width;
let top = props.pan.y * -1;
let bottom = props.rootOffset.bottom;
let p;
Object.values(props.steps).forEach((step) => {
const node = props.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 / props.scale,
right: right / props.scale,
top: top / props.scale,
bottom: bottom / props.scale,
};
});
const scaleFactorX = computed(() => {
return (bounds.value.right - bounds.value.left) / size.value;
});
const scaleFactorY = computed(() => {
return (bounds.value.bottom - bounds.value.top) / size.value;
});
const rect = ref(null);
let startX = null;
let startY = null;
const rectPosition = reactive(
useDraggable(rect, {
preventDefault: true,
onStart: (position, event) => {
event.stopPropagation();
startX = event.clientX;
startY = event.clientY;
},
onMove: (position, event) => {
const offsetX = event.clientX - startX;
const offsetY = event.clientY - startY;
console.log("panX", props.pan.x - offsetX * scaleFactorX.value);
console.log("panY", props.pan.y - offsetY * scaleFactorY.value);
emit("pan-by", { x: -offsetX * scaleFactorX.value, y: -offsetY * scaleFactorY.value });
startX = event.clientX;
startY = event.clientY;
},
// onEnd: (position, event) => {
// startX = null;
// startY = null;
// },
})
);
return {
overview,
overviewPosition,
rect,
rectPosition,
size,
visible,
bounds,
scaleFactorX,
scaleFactorY,
startX,
startY,
};
},
components: {
Draggable,
@@ -37,7 +116,6 @@ export default {
},
data() {
return {
size: 150,
maxSize: 300,
minSize: 50,
};
@@ -73,9 +151,6 @@ export default {
type: Object,
},
},
created() {
this.size = parseInt(localStorage.getItem("overview-size")) || this.size;
},
computed: {
style() {
if (this.overviewPosition.x) {
@@ -95,44 +170,23 @@ export default {
}
return { width: `${this.size}px`, height: `${this.size}px` };
},
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}`;
},
},
methods: {
onClick(e) {
console.log("got click", e);
console.log("panX", this.pan.x - offsetX * this.scaleFactorX);
console.log("panY", this.pan.y - offsetY * this.scaleFactorY);
// console.log("got click", e);
// const scaledOffsetX = e.offsetX * this.scaleFactorX;
// const scaledOffsetY = e.offsetY * this.scaleFactorY;
// console.log("scaled offset", scaledOffsetX, scaledOffsetY);
// const x = scaledOffsetX - this.rootOffset.width / 2;
// const y = scaledOffsetY - this.rootOffset.height / 2;
// console.log("adjusted offset", x, y);
console.log("panX", props.pan.x - offsetX * scaleFactorX.value);
this.$emit("moveTo", { x: -x, y: -y });
},
},
};
@@ -2,8 +2,8 @@
<draggable-wrapper
ref="el"
@updatePosition="onUpdatePosition"
@mouseup="makeActive"
@move="onMove"
@mouseup.prevent="makeActive"
@move.prevent="onMove"
:id="idString"
:name="name"
:node-label="label"
@@ -49,7 +49,14 @@
</Draggable>
</div>
</div>
<Minimap :nodes="nodes" :steps="steps" :root-offset="position" :scale="zoomLevel" :pan="currentPan" />
<Minimap
:nodes="nodes"
:steps="steps"
:root-offset="position"
:scale="zoomLevel"
:pan="currentPan"
@pan-by="onPan"
@moveTo="onMoveTo" />
</div>
</template>
<script>
@@ -105,6 +112,13 @@ export default {
},
},
methods: {
onWheel(e) {
console.log("wheel event", e);
},
onMoveTo(moveTo) {
console.log("moving to", moveTo);
this.svgpanzoom.pan(moveTo);
},
onUpdatedCTM(CTM) {
this.transform = CTM;
},
@@ -157,6 +171,8 @@ export default {
currentPan() {
if (this.transform) {
return { x: this.transform.e, y: this.transform.f };
} else {
return { x: 0, y: 0 };
}
},
zoomLevel() {
+3
View File
@@ -204,6 +204,9 @@
// this is $primary-brand / #25537b
filter: invert(26%) sepia(75%) saturate(489%) hue-rotate(166deg) brightness(90%) contrast(89%);
}
.viewport {
stroke: #25537b
}
}
#input-choices-menu {
color: black;