From 347f1edc3240916c27de73ff9931fddad55b1427 Mon Sep 17 00:00:00 2001 From: Braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Tue, 17 Aug 2021 22:14:39 +0200 Subject: [PATCH] update: Pass selection key code to zoom pane * Use keypress handler in zoom pane instead of passing value as prop --- src/container/FlowRenderer/index.tsx | 2 +- src/container/ZoomPane/index.tsx | 278 ++++++++++++++------------- 2 files changed, 143 insertions(+), 137 deletions(-) diff --git a/src/container/FlowRenderer/index.tsx b/src/container/FlowRenderer/index.tsx index fa38a419..a9da929c 100644 --- a/src/container/FlowRenderer/index.tsx +++ b/src/container/FlowRenderer/index.tsx @@ -126,7 +126,6 @@ export default defineComponent({ return () => ( {slots.default ? slots.default() : ''} {keyPressed.value ? : ''} diff --git a/src/container/ZoomPane/index.tsx b/src/container/ZoomPane/index.tsx index f0c14ea0..8bf15793 100644 --- a/src/container/ZoomPane/index.tsx +++ b/src/container/ZoomPane/index.tsx @@ -2,7 +2,7 @@ import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom'; import { select, pointer } from 'd3-selection'; import { clamp } from '../../utils'; import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode, RevueFlowStore } from '../../types'; -import { computed, defineComponent, inject, PropType, ref } from 'vue'; +import { computed, defineComponent, inject, onUpdated, PropType, ref } from 'vue'; import useKeyPress from '../../hooks/useKeyPress'; import useResizeHandler from '../../hooks/useResizeHandler'; import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'; @@ -23,6 +23,7 @@ interface ZoomPaneProps { defaultZoom?: number; translateExtent?: TranslateExtent; zoomActivationKeyCode?: KeyCode; + selectionKeyCode?: KeyCode; } export default defineComponent({ @@ -99,7 +100,6 @@ export default defineComponent({ const hooks = inject('hooks')!; const zoomPane = templateRef('zoom-pane', null); const prevTransform = ref({ x: 0, y: 0, zoom: 0 }); - const zoomActivationKeyPressed = useKeyPress(props.zoomActivationKeyCode); const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean => prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k; @@ -110,158 +110,164 @@ export default defineComponent({ zoom: eventTransform.k }); - const { onReady } = useResizeHandler(zoomPane, store.updateSize); - onReady(() => { - if (props.defaultPosition && props.defaultZoom) { - const currentTranslateExtent = - typeof props.translateExtent !== 'undefined' ? props.translateExtent : store.translateExtent; - const d3ZoomInstance = zoom().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(currentTranslateExtent); - const selection = select(zoomPane.value as Element).call(d3ZoomInstance); + const init = () => { + const { onReady } = useResizeHandler(zoomPane, store.updateSize); + onReady(() => { + if (props.defaultPosition && props.defaultZoom) { + const currentTranslateExtent = + typeof props.translateExtent !== 'undefined' ? props.translateExtent : store.translateExtent; + const d3ZoomInstance = zoom().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(currentTranslateExtent); + const selection = select(zoomPane.value as Element).call(d3ZoomInstance); - const clampedX = clamp(props.defaultPosition[0], currentTranslateExtent[0][0], currentTranslateExtent[1][0]); - const clampedY = clamp(props.defaultPosition[1], currentTranslateExtent[0][1], currentTranslateExtent[1][1]); - const clampedZoom = clamp(props.defaultZoom, store.minZoom, store.maxZoom); - const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom); + const clampedX = clamp(props.defaultPosition[0], currentTranslateExtent[0][0], currentTranslateExtent[1][0]); + const clampedY = clamp(props.defaultPosition[1], currentTranslateExtent[0][1], currentTranslateExtent[1][1]); + const clampedZoom = clamp(props.defaultZoom, store.minZoom, store.maxZoom); + const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom); - d3ZoomInstance.transform(selection, updatedTransform); + d3ZoomInstance.transform(selection, updatedTransform); - store.initD3Zoom({ - d3Zoom: d3ZoomInstance, - d3Selection: selection, - d3ZoomHandler: selection.on('wheel.zoom'), - // we need to pass transform because zoom handler is not registered when we set the initial transform - transform: [clampedX, clampedY, clampedZoom] + store.initD3Zoom({ + d3Zoom: d3ZoomInstance, + d3Selection: selection, + d3ZoomHandler: selection.on('wheel.zoom'), + // we need to pass transform because zoom handler is not registered when we set the initial transform + transform: [clampedX, clampedY, clampedZoom] + }); + applyZoomHandlers(d3ZoomInstance); + applyZoomFilter(d3ZoomInstance); + } + }); + + const applyZoomHandlers = (d3Zoom: ZoomBehavior) => { + d3Zoom.on('start', (event: D3ZoomEvent) => { + if (viewChanged(prevTransform.value, event.transform)) { + const flowTransform = eventToFlowTransform(event.transform); + prevTransform.value = flowTransform; + + hooks.moveStart.trigger(flowTransform); + } }); - applyZoomHandlers(d3ZoomInstance); - applyZoomFilter(d3ZoomInstance); - } - }); - whenever(zoomActivationKeyPressed, () => { - if (props.panOnScroll) { - store.d3Selection - ?.on('wheel', (event: WheelEvent) => { - event.preventDefault(); - event.stopImmediatePropagation(); + d3Zoom.on('end', (event: D3ZoomEvent) => { + if (viewChanged(prevTransform.value, event.transform)) { + const flowTransform = eventToFlowTransform(event.transform); + prevTransform.value = flowTransform; - const currentZoom = store.d3Selection?.property('__zoom').k || 1; + hooks.moveEnd.trigger(flowTransform); + } + }); - if (event.ctrlKey && props.zoomOnPinch) { - const point = pointer(event); - // taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js - const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10; - const zoom = currentZoom * Math.pow(2, pinchDelta); - if (store.d3Selection) store.d3Zoom?.scaleTo(store.d3Selection, zoom, point); + d3Zoom.on('zoom', (event: D3ZoomEvent) => { + store.transform = [event.transform.x, event.transform.y, event.transform.k]; + hooks.move.trigger(eventToFlowTransform(event.transform)); + }); - return; - } + const selectionKeyPressed = computed(() => props.selectionKeyPressed); + whenever(selectionKeyPressed, () => { + if (selectionKeyPressed.value) { + d3Zoom.on('zoom', null); + } + }); - // increase scroll speed in firefox - // firefox: deltaMode === 1; chrome: deltaMode === 0 - const deltaNormalize = event.deltaMode === 1 ? 20 : 1; - const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize; - const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize; + const zoomActivationKeyPressed = useKeyPress(props.zoomActivationKeyCode); + whenever(zoomActivationKeyPressed, () => { + if (props.panOnScroll) { + store.d3Selection + ?.on('wheel', (event: WheelEvent) => { + event.preventDefault(); + event.stopImmediatePropagation(); - if (store.d3Selection && props.panOnScrollSpeed) - store.d3Zoom?.translateBy( - store.d3Selection, - -(deltaX / currentZoom) * props.panOnScrollSpeed, - -(deltaY / currentZoom) * props.panOnScrollSpeed - ); - }) - .on('wheel.zoom', null); - } else if (typeof store.d3ZoomHandler !== 'undefined') { - store.d3Selection?.on('wheel', null).on('wheel.zoom', store.d3ZoomHandler); - } - }); + const currentZoom = store.d3Selection?.property('__zoom').k || 1; - const applyZoomHandlers = (d3Zoom: ZoomBehavior) => { - d3Zoom.on('start', (event: D3ZoomEvent) => { - if (viewChanged(prevTransform.value, event.transform)) { - const flowTransform = eventToFlowTransform(event.transform); - prevTransform.value = flowTransform; + if (event.ctrlKey && props.zoomOnPinch) { + const point = pointer(event); + // taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js + const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10; + const zoom = currentZoom * Math.pow(2, pinchDelta); + if (store.d3Selection) store.d3Zoom?.scaleTo(store.d3Selection, zoom, point); - hooks.moveStart.trigger(flowTransform); - } - }); + return; + } - d3Zoom.on('end', (event: D3ZoomEvent) => { - if (viewChanged(prevTransform.value, event.transform)) { - const flowTransform = eventToFlowTransform(event.transform); - prevTransform.value = flowTransform; + // increase scroll speed in firefox + // firefox: deltaMode === 1; chrome: deltaMode === 0 + const deltaNormalize = event.deltaMode === 1 ? 20 : 1; + const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize; + const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize; - hooks.moveEnd.trigger(flowTransform); - } - }); + if (store.d3Selection && props.panOnScrollSpeed) + store.d3Zoom?.translateBy( + store.d3Selection, + -(deltaX / currentZoom) * props.panOnScrollSpeed, + -(deltaY / currentZoom) * props.panOnScrollSpeed + ); + }) + .on('wheel.zoom', null); + } else if (typeof store.d3ZoomHandler !== 'undefined') { + store.d3Selection?.on('wheel', null).on('wheel.zoom', store.d3ZoomHandler); + } + }); + }; - d3Zoom.on('zoom', (event: D3ZoomEvent) => { - store.transform = [event.transform.x, event.transform.y, event.transform.k]; - hooks.move.trigger(eventToFlowTransform(event.transform)); - }); + const applyZoomFilter = (d3Zoom: ZoomBehavior) => { + d3Zoom.filter((event: MouseEvent) => { + const zoomScroll = props.zoomOnScroll; + const pinchZoom = props.zoomOnPinch && event.ctrlKey; - const selectionKeyPressed = computed(() => props.selectionKeyPressed); - whenever(selectionKeyPressed, () => { - if (selectionKeyPressed.value) { - d3Zoom.on('zoom', null); - } - }); + // if all interactions are disabled, we prevent all zoom events + if (!props.paneMoveable && !zoomScroll && !props.panOnScroll && !props.zoomOnDoubleClick && !props.zoomOnPinch) { + return false; + } + + // during a selection we prevent all other interactions + if (props.selectionKeyPressed) { + return false; + } + + // if zoom on double click is disabled, we prevent the double click event + if (!props.zoomOnDoubleClick && event.type === 'dblclick') { + return false; + } + + if ((event.target as Element).closest('.nowheel') && event.type === 'wheel') { + return false; + } + + // when the target element is a node, we still allow zooming + if ( + ((event.target as Element).closest('.revue-flow__node') || (event.target as Element).closest('.revue-flow__edge')) && + event.type !== 'wheel' + ) { + return false; + } + + // when the target element is a node selection, we still allow zooming + if ((event.target as Element).closest('.revue-flow__nodesselection') && event.type !== 'wheel') { + return false; + } + + if (!props.zoomOnPinch && event.ctrlKey && event.type === 'wheel') { + return false; + } + + // when there is no scroll handling enabled, we prevent all wheel events + if (!zoomScroll && !props.panOnScroll && !pinchZoom && event.type === 'wheel') { + return false; + } + + // if the pane is not movable, we prevent dragging it with mousestart or touchstart + if (!props.paneMoveable && (event.type === 'mousedown' || event.type === 'touchstart')) { + return false; + } + + // default filter for d3-zoom + return (!event.ctrlKey || event.type === 'wheel') && !event.button; + }); + }; }; - const applyZoomFilter = (d3Zoom: ZoomBehavior) => { - d3Zoom.filter((event: MouseEvent) => { - const zoomScroll = props.zoomOnScroll; - const pinchZoom = props.zoomOnPinch && event.ctrlKey; - - // if all interactions are disabled, we prevent all zoom events - if (!props.paneMoveable && !zoomScroll && !props.panOnScroll && !props.zoomOnDoubleClick && !props.zoomOnPinch) { - return false; - } - - // during a selection we prevent all other interactions - if (props.selectionKeyPressed) { - return false; - } - - // if zoom on double click is disabled, we prevent the double click event - if (!props.zoomOnDoubleClick && event.type === 'dblclick') { - return false; - } - - if ((event.target as Element).closest('.nowheel') && event.type === 'wheel') { - return false; - } - - // when the target element is a node, we still allow zooming - if ( - ((event.target as Element).closest('.revue-flow__node') || (event.target as Element).closest('.revue-flow__edge')) && - event.type !== 'wheel' - ) { - return false; - } - - // when the target element is a node selection, we still allow zooming - if ((event.target as Element).closest('.revue-flow__nodesselection') && event.type !== 'wheel') { - return false; - } - - if (!props.zoomOnPinch && event.ctrlKey && event.type === 'wheel') { - return false; - } - - // when there is no scroll handling enabled, we prevent all wheel events - if (!zoomScroll && !props.panOnScroll && !pinchZoom && event.type === 'wheel') { - return false; - } - - // if the pane is not movable, we prevent dragging it with mousestart or touchstart - if (!props.paneMoveable && (event.type === 'mousedown' || event.type === 'touchstart')) { - return false; - } - - // default filter for d3-zoom - return (!event.ctrlKey || event.type === 'wheel') && !event.button; - }); - }; + init(); + onUpdated(() => init()); return () => (