forked from profectus/Profectus
Expose current dragging and receiving nodes
This commit is contained in:
parent
9707ceb66d
commit
2999c971cf
3 changed files with 82 additions and 58 deletions
|
@ -17,9 +17,9 @@
|
||||||
@touchmove="drag"
|
@touchmove="drag"
|
||||||
@mousedown="(e: MouseEvent) => mouseDown(e)"
|
@mousedown="(e: MouseEvent) => mouseDown(e)"
|
||||||
@touchstart="(e: TouchEvent) => mouseDown(e)"
|
@touchstart="(e: TouchEvent) => mouseDown(e)"
|
||||||
@mouseup="() => endDragging(dragging)"
|
@mouseup="() => endDragging(unref(draggingNode))"
|
||||||
@touchend.passive="() => endDragging(dragging)"
|
@touchend.passive="() => endDragging(unref(draggingNode))"
|
||||||
@mouseleave="() => endDragging(dragging)"
|
@mouseleave="() => endDragging(unref(draggingNode))"
|
||||||
>
|
>
|
||||||
<svg class="stage" width="100%" height="100%">
|
<svg class="stage" width="100%" height="100%">
|
||||||
<g class="g1">
|
<g class="g1">
|
||||||
|
@ -36,10 +36,10 @@
|
||||||
<BoardNodeVue
|
<BoardNodeVue
|
||||||
:node="node"
|
:node="node"
|
||||||
:nodeType="types[node.type]"
|
:nodeType="types[node.type]"
|
||||||
:dragging="draggingNode"
|
:dragging="unref(draggingNode)"
|
||||||
:dragged="draggingNode === node ? dragged : undefined"
|
:dragged="unref(draggingNode) === node ? dragged : undefined"
|
||||||
:hasDragged="hasDragged"
|
:hasDragged="hasDragged"
|
||||||
:receivingNode="receivingNode?.id === node.id"
|
:receivingNode="unref(receivingNode)?.id === node.id"
|
||||||
:selectedNode="unref(selectedNode)"
|
:selectedNode="unref(selectedNode)"
|
||||||
:selectedAction="unref(selectedAction)"
|
:selectedAction="unref(selectedAction)"
|
||||||
@mouseDown="mouseDown"
|
@mouseDown="mouseDown"
|
||||||
|
@ -65,7 +65,7 @@ import { getNodeProperty } from "features/boards/board";
|
||||||
import type { StyleValue } from "features/feature";
|
import type { StyleValue } from "features/feature";
|
||||||
import { Visibility, isVisible } from "features/feature";
|
import { Visibility, isVisible } from "features/feature";
|
||||||
import type { ProcessedComputable } from "util/computed";
|
import type { ProcessedComputable } from "util/computed";
|
||||||
import { Ref, computed, ref, toRefs, unref } from "vue";
|
import { Ref, computed, ref, toRefs, unref, watchEffect } from "vue";
|
||||||
import BoardLinkVue from "./BoardLink.vue";
|
import BoardLinkVue from "./BoardLink.vue";
|
||||||
import BoardNodeVue from "./BoardNode.vue";
|
import BoardNodeVue from "./BoardNode.vue";
|
||||||
|
|
||||||
|
@ -81,32 +81,31 @@ const _props = defineProps<{
|
||||||
links: Ref<BoardNodeLink[] | null>;
|
links: Ref<BoardNodeLink[] | null>;
|
||||||
selectedAction: Ref<GenericBoardNodeAction | null>;
|
selectedAction: Ref<GenericBoardNodeAction | null>;
|
||||||
selectedNode: Ref<BoardNode | null>;
|
selectedNode: Ref<BoardNode | null>;
|
||||||
|
draggingNode: Ref<BoardNode | null>;
|
||||||
|
receivingNode: Ref<BoardNode | null>;
|
||||||
mousePosition: Ref<{ x: number; y: number } | null>;
|
mousePosition: Ref<{ x: number; y: number } | null>;
|
||||||
|
setReceivingNode: (node: BoardNode | null) => void;
|
||||||
|
setDraggingNode: (node: BoardNode | null) => void;
|
||||||
}>();
|
}>();
|
||||||
const props = toRefs(_props);
|
const props = toRefs(_props);
|
||||||
|
|
||||||
const lastMousePosition = ref({ x: 0, y: 0 });
|
const lastMousePosition = ref({ x: 0, y: 0 });
|
||||||
const dragged = ref({ x: 0, y: 0 });
|
const dragged = ref({ x: 0, y: 0 });
|
||||||
const dragging = ref<number | null>(null);
|
|
||||||
const hasDragged = ref(false);
|
const hasDragged = ref(false);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const stage = ref<any>(null);
|
const stage = ref<any>(null);
|
||||||
|
|
||||||
const draggingNode = computed(() =>
|
|
||||||
dragging.value == null ? undefined : props.nodes.value.find(node => node.id === dragging.value)
|
|
||||||
);
|
|
||||||
|
|
||||||
const sortedNodes = computed(() => {
|
const sortedNodes = computed(() => {
|
||||||
const nodes = props.nodes.value.slice();
|
const nodes = props.nodes.value.slice();
|
||||||
if (draggingNode.value) {
|
if (props.draggingNode.value) {
|
||||||
const node = nodes.splice(nodes.indexOf(draggingNode.value), 1)[0];
|
const node = nodes.splice(nodes.indexOf(props.draggingNode.value), 1)[0];
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
});
|
});
|
||||||
|
|
||||||
const receivingNode = computed(() => {
|
watchEffect(() => {
|
||||||
const node = draggingNode.value;
|
const node = props.draggingNode.value;
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -116,26 +115,30 @@ const receivingNode = computed(() => {
|
||||||
y: node.position.y + dragged.value.y
|
y: node.position.y + dragged.value.y
|
||||||
};
|
};
|
||||||
let smallestDistance = Number.MAX_VALUE;
|
let smallestDistance = Number.MAX_VALUE;
|
||||||
return props.nodes.value.reduce((smallest: BoardNode | null, curr: BoardNode) => {
|
|
||||||
if (curr.id === node.id) {
|
|
||||||
return smallest;
|
|
||||||
}
|
|
||||||
const nodeType = props.types.value[curr.type];
|
|
||||||
const canAccept = getNodeProperty(nodeType.canAccept, curr);
|
|
||||||
if (!canAccept) {
|
|
||||||
return smallest;
|
|
||||||
}
|
|
||||||
|
|
||||||
const distanceSquared =
|
props.setReceivingNode.value(
|
||||||
Math.pow(position.x - curr.position.x, 2) + Math.pow(position.y - curr.position.y, 2);
|
props.nodes.value.reduce((smallest: BoardNode | null, curr: BoardNode) => {
|
||||||
let size = getNodeProperty(nodeType.size, curr);
|
if (curr.id === node.id) {
|
||||||
if (distanceSquared > smallestDistance || distanceSquared > size * size) {
|
return smallest;
|
||||||
return smallest;
|
}
|
||||||
}
|
const nodeType = props.types.value[curr.type];
|
||||||
|
const canAccept = getNodeProperty(nodeType.canAccept, curr);
|
||||||
|
if (!canAccept) {
|
||||||
|
return smallest;
|
||||||
|
}
|
||||||
|
|
||||||
smallestDistance = distanceSquared;
|
const distanceSquared =
|
||||||
return curr;
|
Math.pow(position.x - curr.position.x, 2) +
|
||||||
}, null);
|
Math.pow(position.y - curr.position.y, 2);
|
||||||
|
let size = getNodeProperty(nodeType.size, curr);
|
||||||
|
if (distanceSquared > smallestDistance || distanceSquared > size * size) {
|
||||||
|
return smallest;
|
||||||
|
}
|
||||||
|
|
||||||
|
smallestDistance = distanceSquared;
|
||||||
|
return curr;
|
||||||
|
}, null)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
@ -144,8 +147,8 @@ function onInit(panzoomInstance: any) {
|
||||||
panzoomInstance.moveTo(stage.value.$el.clientWidth / 2, stage.value.$el.clientHeight / 2);
|
panzoomInstance.moveTo(stage.value.$el.clientWidth / 2, stage.value.$el.clientHeight / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mouseDown(e: MouseEvent | TouchEvent, nodeID: number | null = null, draggable = false) {
|
function mouseDown(e: MouseEvent | TouchEvent, node: BoardNode | null = null, draggable = false) {
|
||||||
if (dragging.value == null) {
|
if (props.draggingNode.value == null) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
@ -169,10 +172,10 @@ function mouseDown(e: MouseEvent | TouchEvent, nodeID: number | null = null, dra
|
||||||
hasDragged.value = false;
|
hasDragged.value = false;
|
||||||
|
|
||||||
if (draggable) {
|
if (draggable) {
|
||||||
dragging.value = nodeID;
|
props.setDraggingNode.value(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nodeID != null) {
|
if (node != null) {
|
||||||
props.state.value.selectedNode = null;
|
props.state.value.selectedNode = null;
|
||||||
props.state.value.selectedAction = null;
|
props.state.value.selectedAction = null;
|
||||||
}
|
}
|
||||||
|
@ -187,7 +190,7 @@ function drag(e: MouseEvent | TouchEvent) {
|
||||||
clientX = e.touches[0].clientX;
|
clientX = e.touches[0].clientX;
|
||||||
clientY = e.touches[0].clientY;
|
clientY = e.touches[0].clientY;
|
||||||
} else {
|
} else {
|
||||||
endDragging(dragging.value);
|
endDragging(props.draggingNode.value);
|
||||||
props.mousePosition.value = null;
|
props.mousePosition.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -214,28 +217,28 @@ function drag(e: MouseEvent | TouchEvent) {
|
||||||
hasDragged.value = true;
|
hasDragged.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dragging.value != null) {
|
if (props.draggingNode.value != null) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function endDragging(nodeID: number | null) {
|
function endDragging(node: BoardNode | null) {
|
||||||
if (dragging.value != null && dragging.value === nodeID && draggingNode.value != null) {
|
if (props.draggingNode.value != null && props.draggingNode.value === node) {
|
||||||
draggingNode.value.position.x += Math.round(dragged.value.x / 25) * 25;
|
props.draggingNode.value.position.x += Math.round(dragged.value.x / 25) * 25;
|
||||||
draggingNode.value.position.y += Math.round(dragged.value.y / 25) * 25;
|
props.draggingNode.value.position.y += Math.round(dragged.value.y / 25) * 25;
|
||||||
|
|
||||||
const nodes = props.nodes.value;
|
const nodes = props.nodes.value;
|
||||||
nodes.push(nodes.splice(nodes.indexOf(draggingNode.value), 1)[0]);
|
nodes.push(nodes.splice(nodes.indexOf(props.draggingNode.value), 1)[0]);
|
||||||
|
|
||||||
if (receivingNode.value) {
|
if (props.receivingNode.value) {
|
||||||
props.types.value[receivingNode.value.type].onDrop?.(
|
props.types.value[props.receivingNode.value.type].onDrop?.(
|
||||||
receivingNode.value,
|
props.receivingNode.value,
|
||||||
draggingNode.value
|
props.draggingNode.value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dragging.value = null;
|
props.setDraggingNode.value(null);
|
||||||
} else if (!hasDragged.value) {
|
} else if (!hasDragged.value) {
|
||||||
props.state.value.selectedNode = null;
|
props.state.value.selectedNode = null;
|
||||||
props.state.value.selectedAction = null;
|
props.state.value.selectedAction = null;
|
||||||
|
|
|
@ -153,7 +153,7 @@ const sqrtTwo = Math.sqrt(2);
|
||||||
const _props = defineProps<{
|
const _props = defineProps<{
|
||||||
node: BoardNode;
|
node: BoardNode;
|
||||||
nodeType: GenericNodeType;
|
nodeType: GenericNodeType;
|
||||||
dragging?: BoardNode;
|
dragging: BoardNode | null;
|
||||||
dragged?: {
|
dragged?: {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
@ -165,8 +165,8 @@ const _props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
const props = toRefs(_props);
|
const props = toRefs(_props);
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "mouseDown", event: MouseEvent | TouchEvent, node: number, isDraggable: boolean): void;
|
(e: "mouseDown", event: MouseEvent | TouchEvent, node: BoardNode, isDraggable: boolean): void;
|
||||||
(e: "endDragging", node: number): void;
|
(e: "endDragging", node: BoardNode): void;
|
||||||
(e: "clickAction", actionId: string): void;
|
(e: "clickAction", actionId: string): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ const isDraggable = computed(() =>
|
||||||
watch(isDraggable, value => {
|
watch(isDraggable, value => {
|
||||||
const node = unref(props.node);
|
const node = unref(props.node);
|
||||||
if (unref(props.dragging) === node && !value) {
|
if (unref(props.dragging) === node && !value) {
|
||||||
emit("endDragging", node.id);
|
emit("endDragging", node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -261,12 +261,12 @@ const style = computed(() => getNodeProperty(props.nodeType.value.style, unref(p
|
||||||
const classes = computed(() => getNodeProperty(props.nodeType.value.classes, unref(props.node)));
|
const classes = computed(() => getNodeProperty(props.nodeType.value.classes, unref(props.node)));
|
||||||
|
|
||||||
function mouseDown(e: MouseEvent | TouchEvent) {
|
function mouseDown(e: MouseEvent | TouchEvent) {
|
||||||
emit("mouseDown", e, props.node.value.id, isDraggable.value);
|
emit("mouseDown", e, props.node.value, isDraggable.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mouseUp(e: MouseEvent | TouchEvent) {
|
function mouseUp(e: MouseEvent | TouchEvent) {
|
||||||
if (!props.hasDragged?.value) {
|
if (!props.hasDragged?.value) {
|
||||||
emit("endDragging", props.node.value.id);
|
emit("endDragging", props.node.value);
|
||||||
props.nodeType.value.onClick?.(props.node.value);
|
props.nodeType.value.onClick?.(props.node.value);
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,6 +258,10 @@ export interface BaseBoard {
|
||||||
selectedNode: Ref<BoardNode | null>;
|
selectedNode: Ref<BoardNode | null>;
|
||||||
/** The currently selected action, if any. */
|
/** The currently selected action, if any. */
|
||||||
selectedAction: Ref<GenericBoardNodeAction | null>;
|
selectedAction: Ref<GenericBoardNodeAction | null>;
|
||||||
|
/** The currently being dragged node, if any. */
|
||||||
|
draggingNode: Ref<BoardNode | null>;
|
||||||
|
/** If dragging a node, the node it's currently being hovered over, if any. */
|
||||||
|
receivingNode: Ref<BoardNode | null>;
|
||||||
/** The current mouse position, if over the board. */
|
/** The current mouse position, if over the board. */
|
||||||
mousePosition: Ref<{ x: number; y: number } | null>;
|
mousePosition: Ref<{ x: number; y: number } | null>;
|
||||||
/** A symbol that helps identify features of the same type. */
|
/** A symbol that helps identify features of the same type. */
|
||||||
|
@ -372,6 +376,8 @@ export function createBoard<T extends BoardOptions>(
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
board.draggingNode = ref(null);
|
||||||
|
board.receivingNode = ref(null);
|
||||||
processComputable(board as T, "visibility");
|
processComputable(board as T, "visibility");
|
||||||
setDefault(board, "visibility", Visibility.Visible);
|
setDefault(board, "visibility", Visibility.Visible);
|
||||||
processComputable(board as T, "width");
|
processComputable(board as T, "width");
|
||||||
|
@ -427,6 +433,15 @@ export function createBoard<T extends BoardOptions>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setDraggingNode(node: BoardNode | null) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
board.draggingNode!.value = node;
|
||||||
|
}
|
||||||
|
function setReceivingNode(node: BoardNode | null) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
board.receivingNode!.value = node;
|
||||||
|
}
|
||||||
|
|
||||||
board[GatherProps] = function (this: GenericBoard) {
|
board[GatherProps] = function (this: GenericBoard) {
|
||||||
const {
|
const {
|
||||||
nodes,
|
nodes,
|
||||||
|
@ -440,7 +455,9 @@ export function createBoard<T extends BoardOptions>(
|
||||||
links,
|
links,
|
||||||
selectedAction,
|
selectedAction,
|
||||||
selectedNode,
|
selectedNode,
|
||||||
mousePosition
|
mousePosition,
|
||||||
|
draggingNode,
|
||||||
|
receivingNode
|
||||||
} = this;
|
} = this;
|
||||||
return {
|
return {
|
||||||
nodes,
|
nodes,
|
||||||
|
@ -454,7 +471,11 @@ export function createBoard<T extends BoardOptions>(
|
||||||
links,
|
links,
|
||||||
selectedAction,
|
selectedAction,
|
||||||
selectedNode,
|
selectedNode,
|
||||||
mousePosition
|
mousePosition,
|
||||||
|
draggingNode,
|
||||||
|
receivingNode,
|
||||||
|
setDraggingNode,
|
||||||
|
setReceivingNode
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue