import { INodeUi } from '@/Interface'; import mixins from 'vue-typed-mixins'; import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers'; import { nodeIndex } from '@/components/mixins/nodeIndex'; export const mouseSelect = mixins( deviceSupportHelpers, nodeIndex, ).extend({ data () { return { selectActive: false, selectBox: document.createElement('span'), }; }, mounted () { this.createSelectBox(); }, methods: { createSelectBox () { this.selectBox.id = 'select-box'; this.selectBox.style.margin = '0px auto'; this.selectBox.style.border = '2px dotted #FF0000'; // Positioned absolutely within #node-view. This is consistent with how nodes are positioned. this.selectBox.style.position = 'absolute'; this.selectBox.style.zIndex = '100'; this.selectBox.style.visibility = 'hidden'; this.selectBox.addEventListener('mouseup', this.mouseUpMouseSelect); const nodeViewEl = this.$el.querySelector('#node-view') as HTMLDivElement; nodeViewEl.appendChild(this.selectBox); }, isCtrlKeyPressed (e: MouseEvent | KeyboardEvent): boolean { if (this.isTouchDevice === true) { return true; } if (this.isMacOs) { return e.metaKey; } return e.ctrlKey; }, /** * Gets mouse position within the node view. Both node view offset and scale (zoom) are considered when * calculating position. * * @param event - mouse event within node view */ getMousePositionWithinNodeView (event: MouseEvent) { // @ts-ignore const nodeViewScale = this.nodeViewScale; const offsetPosition = this.$store.getters.getNodeViewOffsetPosition; return { x: (event.pageX - offsetPosition[0]) / nodeViewScale, y: (event.pageY - offsetPosition[1]) / nodeViewScale, }; }, showSelectBox (event: MouseEvent) { this.selectBox = Object.assign(this.selectBox, this.getMousePositionWithinNodeView(event)); // @ts-ignore this.selectBox.style.left = this.selectBox.x + 'px'; // @ts-ignore this.selectBox.style.top = this.selectBox.y + 'px'; this.selectBox.style.visibility = 'visible'; this.selectActive = true; }, updateSelectBox (event: MouseEvent) { const selectionBox = this.getSelectionBox(event); this.selectBox.style.left = selectionBox.x + 'px'; this.selectBox.style.top = selectionBox.y + 'px'; this.selectBox.style.width = selectionBox.width + 'px'; this.selectBox.style.height = selectionBox.height + 'px'; }, hideSelectBox () { this.selectBox.style.visibility = 'hidden'; // @ts-ignore this.selectBox.x = 0; // @ts-ignore this.selectBox.y = 0; this.selectBox.style.left = '0px'; this.selectBox.style.top = '0px'; this.selectBox.style.width = '0px'; this.selectBox.style.height = '0px'; this.selectActive = false; }, getSelectionBox (event: MouseEvent) { const {x, y} = this.getMousePositionWithinNodeView(event); return { // @ts-ignore x: Math.min(x, this.selectBox.x), // @ts-ignore y: Math.min(y, this.selectBox.y), // @ts-ignore width: Math.abs(x - this.selectBox.x), // @ts-ignore height: Math.abs(y - this.selectBox.y), }; }, getNodesInSelection (event: MouseEvent): INodeUi[] { const returnNodes: INodeUi[] = []; const selectionBox = this.getSelectionBox(event); // Go through all nodes and check if they are selected this.$store.getters.allNodes.forEach((node: INodeUi) => { // TODO: Currently always uses the top left corner for checking. Should probably use the center instead if (node.position[0] < selectionBox.x || node.position[0] > (selectionBox.x + selectionBox.width)) { return; } if (node.position[1] < selectionBox.y || node.position[1] > (selectionBox.y + selectionBox.height)) { return; } returnNodes.push(node); }); return returnNodes; }, mouseDownMouseSelect (e: MouseEvent) { if (this.isCtrlKeyPressed(e) === true) { // We only care about it when the ctrl key is not pressed at the same time. // So we exit when it is pressed. return; } if (this.$store.getters.isActionActive('dragActive')) { // If a node does currently get dragged we do not activate the selection return; } this.showSelectBox(e); // @ts-ignore // Leave like this. Do not add a anonymous function because then remove would not work anymore this.$el.addEventListener('mousemove', this.mouseMoveSelect); }, mouseUpMouseSelect (e: MouseEvent) { if (this.selectActive === false) { if (this.isTouchDevice === true) { // @ts-ignore if (e.target && e.target.id.includes('node-view')) { // Deselect all nodes this.deselectAllNodes(); } } // If it is not active return direcly. // Else normal node dragging will not work. return; } // @ts-ignore this.$el.removeEventListener('mousemove', this.mouseMoveSelect); // Deselect all nodes this.deselectAllNodes(); // Select the nodes which are in the selection box const selectedNodes = this.getNodesInSelection(e); selectedNodes.forEach((node) => { this.nodeSelected(node); }); this.hideSelectBox(); }, mouseMoveSelect (e: MouseEvent) { if (e.buttons === 0) { // Mouse button is not pressed anymore so stop selection mode // Happens normally when mouse leave the view pressed and then // comes back unpressed. this.mouseUpMouseSelect(e); return; } this.updateSelectBox(e); }, nodeDeselected (node: INodeUi) { this.$store.commit('removeNodeFromSelection', node); const nodeElement = `node-${this.getNodeIndex(node.name)}`; // @ts-ignore this.instance.removeFromDragSelection(nodeElement); }, nodeSelected (node: INodeUi) { this.$store.commit('addSelectedNode', node); const nodeElement = `node-${this.getNodeIndex(node.name)}`; // @ts-ignore this.instance.addToDragSelection(nodeElement); }, deselectAllNodes () { // @ts-ignore this.instance.clearDragSelection(); this.$store.commit('resetSelectedNodes'); this.$store.commit('setLastSelectedNode', null); this.$store.commit('setLastSelectedNodeOutputIndex', null); this.$store.commit('setActiveNode', null); }, }, });