refactor(core): remove reactivity transform from viewport component
Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
import type { D3ZoomEvent, ZoomTransform } from 'd3-zoom'
|
import type { D3ZoomEvent, ZoomTransform } from 'd3-zoom'
|
||||||
import { zoom, zoomIdentity } from 'd3-zoom'
|
import { zoom, zoomIdentity } from 'd3-zoom'
|
||||||
import { pointer, select } from 'd3-selection'
|
import { pointer, select } from 'd3-selection'
|
||||||
import type { CoordinateExtent, FlowOptions, ViewportTransform } from '../../types'
|
import type { CoordinateExtent, D3ZoomHandler, FlowOptions, ViewportTransform } from '../../types'
|
||||||
import { PanOnScrollMode } from '../../types'
|
import { PanOnScrollMode } from '../../types'
|
||||||
import Pane from '../Pane/Pane.vue'
|
import Pane from '../Pane/Pane.vue'
|
||||||
import Transform from './Transform.vue'
|
import Transform from './Transform.vue'
|
||||||
@@ -27,36 +27,41 @@ const {
|
|||||||
preventScrolling,
|
preventScrolling,
|
||||||
noWheelClassName,
|
noWheelClassName,
|
||||||
noPanClassName,
|
noPanClassName,
|
||||||
setState,
|
|
||||||
emits,
|
emits,
|
||||||
connectionStartHandle,
|
connectionStartHandle,
|
||||||
userSelectionActive,
|
userSelectionActive,
|
||||||
paneDragging,
|
paneDragging,
|
||||||
} = $(useVueFlow())
|
d3Zoom: storeD3Zoom,
|
||||||
|
d3Selection: storeD3Selection,
|
||||||
|
d3ZoomHandler: storeD3ZoomHandler,
|
||||||
|
viewport,
|
||||||
|
viewportRef,
|
||||||
|
} = useVueFlow()
|
||||||
|
|
||||||
const viewportEl = ref<HTMLDivElement>()
|
const viewportEl = ref<HTMLDivElement>()
|
||||||
|
|
||||||
let selectionKeyPressed = $ref(false)
|
const selectionKeyPressed = ref(false)
|
||||||
|
|
||||||
let isZoomingOrPanning = $ref(false)
|
const isZoomingOrPanning = ref(false)
|
||||||
|
|
||||||
let zoomedWithRightMouseButton = $ref(false)
|
let zoomedWithRightMouseButton = false
|
||||||
|
|
||||||
let mouseButton = $ref(0)
|
let mouseButton = 0
|
||||||
|
|
||||||
const panKeyPressed = useKeyPress(panActivationKeyCode)
|
let prevTransform: ViewportTransform = {
|
||||||
|
|
||||||
const shouldPanOnDrag = computed(() => !selectionKeyPressed && panOnDrag && panKeyPressed.value)
|
|
||||||
|
|
||||||
const isSelecting = computed(
|
|
||||||
() => (selectionKeyCode !== true && selectionKeyPressed) || (selectionKeyCode === true && shouldPanOnDrag.value !== true),
|
|
||||||
)
|
|
||||||
|
|
||||||
let prevTransform = $ref<ViewportTransform>({
|
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
zoom: 0,
|
zoom: 0,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
const panKeyPressed = useKeyPress(panActivationKeyCode)
|
||||||
|
|
||||||
|
const shouldPanOnDrag = computed(() => !selectionKeyPressed.value && panOnDrag.value && panKeyPressed.value)
|
||||||
|
|
||||||
|
const isSelecting = computed(
|
||||||
|
() =>
|
||||||
|
(selectionKeyCode.value !== true && selectionKeyPressed.value) || (selectionKeyCode.value === true && !shouldPanOnDrag.value),
|
||||||
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
useResizeObserver(viewportEl, setDimensions)
|
useResizeObserver(viewportEl, setDimensions)
|
||||||
@@ -68,46 +73,44 @@ onMounted(() => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const viewportElement = viewportEl.value!
|
const viewportElement = viewportEl.value!
|
||||||
const bbox = viewportElement.getBoundingClientRect()
|
const bbox = viewportElement.getBoundingClientRect()
|
||||||
const d3Zoom = zoom<HTMLDivElement, any>().scaleExtent([minZoom, maxZoom]).translateExtent(translateExtent)
|
const d3Zoom = zoom<HTMLDivElement, any>().scaleExtent([minZoom.value, maxZoom.value]).translateExtent(translateExtent.value)
|
||||||
const d3Selection = select(viewportElement).call(d3Zoom)
|
const d3Selection = select(viewportElement).call(d3Zoom)
|
||||||
const d3ZoomHandler = d3Selection.on('wheel.zoom')
|
const d3ZoomHandler = d3Selection.on('wheel.zoom')
|
||||||
|
|
||||||
const updatedTransform = zoomIdentity
|
const updatedTransform = zoomIdentity
|
||||||
.translate(defaultViewport.x ?? 0, defaultViewport.y ?? 0)
|
.translate(defaultViewport.value.x ?? 0, defaultViewport.value.y ?? 0)
|
||||||
.scale(clamp(defaultViewport.zoom ?? 1, minZoom, maxZoom))
|
.scale(clamp(defaultViewport.value.zoom ?? 1, minZoom.value, maxZoom.value))
|
||||||
|
|
||||||
const extent: CoordinateExtent = [
|
const extent: CoordinateExtent = [
|
||||||
[0, 0],
|
[0, 0],
|
||||||
[bbox.width, bbox.height],
|
[bbox.width, bbox.height],
|
||||||
]
|
]
|
||||||
|
|
||||||
const constrainedTransform = d3Zoom.constrain()(updatedTransform, extent, translateExtent)
|
const constrainedTransform = d3Zoom.constrain()(updatedTransform, extent, translateExtent.value)
|
||||||
d3Zoom.transform(d3Selection, constrainedTransform)
|
d3Zoom.transform(d3Selection, constrainedTransform)
|
||||||
|
|
||||||
setState({
|
storeD3Zoom.value = d3Zoom
|
||||||
d3Zoom,
|
storeD3Selection.value = d3Selection
|
||||||
d3Selection,
|
storeD3ZoomHandler.value = d3ZoomHandler as D3ZoomHandler
|
||||||
d3ZoomHandler,
|
viewport.value = { x: constrainedTransform.x, y: constrainedTransform.y, zoom: constrainedTransform.k }
|
||||||
viewport: { x: constrainedTransform.x, y: constrainedTransform.y, zoom: constrainedTransform.k },
|
viewportRef.value = viewportElement
|
||||||
viewportRef: viewportElement,
|
|
||||||
})
|
|
||||||
|
|
||||||
useKeyPress($$(selectionKeyCode), (keyPress) => {
|
useKeyPress(selectionKeyCode, (keyPress) => {
|
||||||
selectionKeyPressed = keyPress
|
selectionKeyPressed.value = keyPress
|
||||||
})
|
})
|
||||||
|
|
||||||
const zoomKeyPressed = useKeyPress(zoomActivationKeyCode)
|
const zoomKeyPressed = useKeyPress(zoomActivationKeyCode)
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (selectionKeyPressed && userSelectionActive && !isZoomingOrPanning) {
|
if (selectionKeyPressed.value && userSelectionActive.value && !isZoomingOrPanning.value) {
|
||||||
d3Zoom.on('zoom', null)
|
d3Zoom.on('zoom', null)
|
||||||
} else if (!selectionKeyPressed && !userSelectionActive) {
|
} else if (!selectionKeyPressed.value && !userSelectionActive.value) {
|
||||||
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
setState({ viewport: { x: event.transform.x, y: event.transform.y, zoom: event.transform.k } })
|
viewport.value = { x: event.transform.x, y: event.transform.y, zoom: event.transform.k }
|
||||||
|
|
||||||
const flowTransform = eventToFlowTransform(event.transform)
|
const flowTransform = eventToFlowTransform(event.transform)
|
||||||
|
|
||||||
zoomedWithRightMouseButton = isRightClickPan(panOnDrag, mouseButton ?? 0)
|
zoomedWithRightMouseButton = isRightClickPan(panOnDrag.value, mouseButton ?? 0)
|
||||||
|
|
||||||
emits.viewportChange(flowTransform)
|
emits.viewportChange(flowTransform)
|
||||||
emits.move({ event, flowTransform })
|
emits.move({ event, flowTransform })
|
||||||
@@ -123,12 +126,12 @@ onMounted(() => {
|
|||||||
// we need to remember it here, because it's always 0 in the "zoom" event
|
// we need to remember it here, because it's always 0 in the "zoom" event
|
||||||
mouseButton = event.sourceEvent.button
|
mouseButton = event.sourceEvent.button
|
||||||
|
|
||||||
isZoomingOrPanning = true
|
isZoomingOrPanning.value = true
|
||||||
|
|
||||||
const flowTransform = eventToFlowTransform(event.transform)
|
const flowTransform = eventToFlowTransform(event.transform)
|
||||||
|
|
||||||
if (event.sourceEvent?.type === 'mousedown') {
|
if (event.sourceEvent?.type === 'mousedown') {
|
||||||
setState({ paneDragging: true })
|
paneDragging.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
prevTransform = flowTransform
|
prevTransform = flowTransform
|
||||||
@@ -142,11 +145,11 @@ onMounted(() => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
isZoomingOrPanning = false
|
isZoomingOrPanning.value = false
|
||||||
|
|
||||||
setState({ paneDragging: false })
|
paneDragging.value = false
|
||||||
|
|
||||||
if (isRightClickPan(panOnDrag, mouseButton ?? 0) && !zoomedWithRightMouseButton) {
|
if (isRightClickPan(panOnDrag.value, mouseButton ?? 0) && !zoomedWithRightMouseButton) {
|
||||||
emits.paneContextMenu(event.sourceEvent)
|
emits.paneContextMenu(event.sourceEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,11 +166,11 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (panOnScroll && !zoomKeyPressed.value && !userSelectionActive) {
|
if (panOnScroll.value && !zoomKeyPressed.value && !userSelectionActive.value) {
|
||||||
d3Selection.on(
|
d3Selection.on(
|
||||||
'wheel.zoom',
|
'wheel.zoom',
|
||||||
(event: WheelEvent) => {
|
(event: WheelEvent) => {
|
||||||
if (isWrappedWithClass(event, noWheelClassName)) {
|
if (isWrappedWithClass(event, noWheelClassName.value)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +179,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
const currentZoom = d3Selection.property('__zoom').k || 1
|
const currentZoom = d3Selection.property('__zoom').k || 1
|
||||||
|
|
||||||
if (event.ctrlKey && zoomOnPinch) {
|
if (event.ctrlKey && zoomOnPinch.value) {
|
||||||
const point = pointer(event)
|
const point = pointer(event)
|
||||||
// taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js
|
// 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 pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10
|
||||||
@@ -189,10 +192,14 @@ onMounted(() => {
|
|||||||
// increase scroll speed in firefox
|
// increase scroll speed in firefox
|
||||||
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
||||||
const deltaNormalize = event.deltaMode === 1 ? 20 : 1
|
const deltaNormalize = event.deltaMode === 1 ? 20 : 1
|
||||||
const deltaX = panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize
|
const deltaX = panOnScrollMode.value === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize
|
||||||
const deltaY = panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize
|
const deltaY = panOnScrollMode.value === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize
|
||||||
|
|
||||||
d3Zoom.translateBy(d3Selection, -(deltaX / currentZoom) * panOnScrollSpeed, -(deltaY / currentZoom) * panOnScrollSpeed)
|
d3Zoom.translateBy(
|
||||||
|
d3Selection,
|
||||||
|
-(deltaX / currentZoom) * panOnScrollSpeed.value,
|
||||||
|
-(deltaY / currentZoom) * panOnScrollSpeed.value,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{ passive: false },
|
{ passive: false },
|
||||||
)
|
)
|
||||||
@@ -200,7 +207,7 @@ onMounted(() => {
|
|||||||
d3Selection.on(
|
d3Selection.on(
|
||||||
'wheel.zoom',
|
'wheel.zoom',
|
||||||
function (event: WheelEvent, d) {
|
function (event: WheelEvent, d) {
|
||||||
if (!preventScrolling || isWrappedWithClass(event, noWheelClassName)) {
|
if (!preventScrolling.value || isWrappedWithClass(event, noWheelClassName.value)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,8 +220,8 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
d3Zoom.filter((event: MouseEvent) => {
|
d3Zoom.filter((event: MouseEvent) => {
|
||||||
const zoomScroll = zoomKeyPressed.value || zoomOnScroll
|
const zoomScroll = zoomKeyPressed.value || zoomOnScroll.value
|
||||||
const pinchZoom = zoomOnPinch && event.ctrlKey
|
const pinchZoom = zoomOnPinch.value && event.ctrlKey
|
||||||
|
|
||||||
if (
|
if (
|
||||||
event.button === 1 &&
|
event.button === 1 &&
|
||||||
@@ -225,55 +232,56 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if all interactions are disabled, we prevent all zoom events
|
// if all interactions are disabled, we prevent all zoom events
|
||||||
if (!panOnDrag && !zoomScroll && !panOnScroll && !zoomOnDoubleClick && !zoomOnPinch) {
|
if (!panOnDrag.value && !zoomScroll && !panOnScroll.value && !zoomOnDoubleClick.value && !zoomOnPinch.value) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// during a selection we prevent all other interactions
|
// during a selection we prevent all other interactions
|
||||||
if (userSelectionActive) {
|
if (userSelectionActive.value) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// if zoom on double click is disabled, we prevent the double click event
|
// if zoom on double click is disabled, we prevent the double click event
|
||||||
if (!zoomOnDoubleClick && event.type === 'dblclick') {
|
if (!zoomOnDoubleClick.value && event.type === 'dblclick') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the target element is inside an element with the nowheel class, we prevent zooming
|
// if the target element is inside an element with the nowheel class, we prevent zooming
|
||||||
if (isWrappedWithClass(event, noWheelClassName) && event.type === 'wheel') {
|
if (isWrappedWithClass(event, noWheelClassName.value) && event.type === 'wheel') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the target element is inside an element with the nopan class, we prevent panning
|
// if the target element is inside an element with the nopan class, we prevent panning
|
||||||
if (isWrappedWithClass(event, noPanClassName) && event.type !== 'wheel') {
|
if (isWrappedWithClass(event, noPanClassName.value) && event.type !== 'wheel') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!zoomOnPinch && event.ctrlKey && event.type === 'wheel') {
|
if (!zoomOnPinch.value && event.ctrlKey && event.type === 'wheel') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// when there is no scroll handling enabled, we prevent all wheel events
|
// when there is no scroll handling enabled, we prevent all wheel events
|
||||||
if (!zoomScroll && !panOnScroll && !pinchZoom && event.type === 'wheel') {
|
if (!zoomScroll && !panOnScroll.value && !pinchZoom && event.type === 'wheel') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the pane is not movable, we prevent dragging it with mousestart or touchstart
|
// if the pane is not movable, we prevent dragging it with mousestart or touchstart
|
||||||
if (!panOnDrag && (event.type === 'mousedown' || event.type === 'touchstart')) {
|
if (!panOnDrag.value && (event.type === 'mousedown' || event.type === 'touchstart')) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the pane is only movable using allowed clicks
|
// if the pane is only movable using allowed clicks
|
||||||
if (
|
if (
|
||||||
Array.isArray(panOnDrag) &&
|
Array.isArray(panOnDrag.value) &&
|
||||||
!panOnDrag.includes(event.button) &&
|
!panOnDrag.value.includes(event.button) &&
|
||||||
(event.type === 'mousedown' || event.type === 'touchstart')
|
(event.type === 'mousedown' || event.type === 'touchstart')
|
||||||
) {
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only allow right clicks if pan on drag is set to right-click
|
// We only allow right clicks if pan on drag is set to right-click
|
||||||
const buttonAllowed = (Array.isArray(panOnDrag) && panOnDrag.includes(event.button)) || !event.button || event.button <= 1
|
const buttonAllowed =
|
||||||
|
(Array.isArray(panOnDrag.value) && panOnDrag.value.includes(event.button)) || !event.button || event.button <= 1
|
||||||
|
|
||||||
// default filter for d3-zoom
|
// default filter for d3-zoom
|
||||||
return (!event.ctrlKey || event.type === 'wheel') && buttonAllowed
|
return (!event.ctrlKey || event.type === 'wheel') && buttonAllowed
|
||||||
@@ -311,8 +319,8 @@ function setDimensions() {
|
|||||||
emits.error(new VueFlowError(ErrorCode.MISSING_VIEWPORT_DIMENSIONS))
|
emits.error(new VueFlowError(ErrorCode.MISSING_VIEWPORT_DIMENSIONS))
|
||||||
}
|
}
|
||||||
|
|
||||||
dimensions.width = width || 500
|
dimensions.value.width = width || 500
|
||||||
dimensions.height = height || 500
|
dimensions.value.height = height || 500
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWrappedWithClass(event: Event, className: string | undefined) {
|
function isWrappedWithClass(event: Event, className: string | undefined) {
|
||||||
|
|||||||
Reference in New Issue
Block a user