refactor(types): Remove internal __vf field
* fields are merged into the GraphNode / Node interface * Remove passing props from VueFlow container, use injected state Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -1,75 +1,37 @@
|
||||
<script lang="ts" setup>
|
||||
import { CSSProperties } from 'vue'
|
||||
import {
|
||||
ConnectionLineType,
|
||||
ConnectionMode,
|
||||
Dimensions,
|
||||
EdgeComponent,
|
||||
GraphEdge,
|
||||
GraphNode,
|
||||
HandleType,
|
||||
Transform,
|
||||
XYPosition,
|
||||
} from '../../types'
|
||||
import EdgeWrapper from '../../components/Edges/EdgeWrapper.vue'
|
||||
import ConnectionLine from '../../components/ConnectionLine/ConnectionLine.vue'
|
||||
import { useStore } from '../../composables'
|
||||
import MarkerDefinitions from './MarkerDefinitions.vue'
|
||||
|
||||
interface EdgeRendererProps {
|
||||
edges: GraphEdge[]
|
||||
nodes: GraphNode[]
|
||||
edgeTypes: Record<string, EdgeComponent>
|
||||
transform: Transform
|
||||
dimensions: Dimensions
|
||||
connectionLineType?: ConnectionLineType
|
||||
connectionLineStyle?: CSSProperties
|
||||
arrowHeadColor?: string
|
||||
markerEndId?: string
|
||||
elementsSelectable?: boolean
|
||||
edgeUpdaterRadius?: number
|
||||
connectionNodeId?: string
|
||||
connectionHandleType?: HandleType
|
||||
connectionHandleId?: string
|
||||
connectionPosition?: XYPosition
|
||||
connectionMode: ConnectionMode
|
||||
nodesConnectable?: boolean
|
||||
edgesUpdatable?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<EdgeRendererProps>(), {
|
||||
arrowHeadColor: '#b1b1b7',
|
||||
connectionLineType: ConnectionLineType.Bezier,
|
||||
edgeUpdaterRadius: 10,
|
||||
handleEdgeUpdate: false,
|
||||
})
|
||||
|
||||
const store = useStore()
|
||||
const getType = (type?: string) => {
|
||||
const t = type ?? 'default'
|
||||
let edgeType = props.edgeTypes[t]
|
||||
let edgeType = store.getEdgeTypes[t]
|
||||
if (!edgeType) {
|
||||
edgeType = props.edgeTypes.default
|
||||
edgeType = store.getEdgeTypes.default
|
||||
console.warn(`Edge type "${type}" not found. Using fallback type "default".`)
|
||||
}
|
||||
return edgeType
|
||||
}
|
||||
|
||||
const sourceNode = controlledComputed(
|
||||
() => props.connectionNodeId,
|
||||
() => store.connectionNodeId,
|
||||
() => {
|
||||
if (props.connectionNodeId) return props.nodes[props.nodes.map((n) => n.id).indexOf(props.connectionNodeId)]
|
||||
if (store.connectionNodeId) return store.getNodes[store.getNodes.map((n) => n.id).indexOf(store.connectionNodeId)]
|
||||
},
|
||||
)
|
||||
const connectionLineVisible = controlledComputed(
|
||||
() => props.connectionNodeId,
|
||||
() => store.connectionNodeId,
|
||||
() =>
|
||||
!!(
|
||||
sourceNode.value &&
|
||||
(typeof sourceNode.value.connectable === 'undefined' ? props.nodesConnectable : sourceNode.value.connectable) &&
|
||||
props.connectionNodeId &&
|
||||
props.connectionHandleType
|
||||
(typeof sourceNode.value.connectable === 'undefined' ? store.nodesConnectable : sourceNode.value.connectable) &&
|
||||
store.connectionNodeId &&
|
||||
store.connectionHandleType
|
||||
),
|
||||
)
|
||||
const transform = computed(() => `translate(${props.transform[0]},${props.transform[1]}) scale(${props.transform[2]})`)
|
||||
const transform = computed(() => `translate(${store.transform[0]},${store.transform[1]}) scale(${store.transform[2]})`)
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
@@ -77,37 +39,25 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<svg :width="props.dimensions.width" :height="props.dimensions.height" class="vue-flow__edges">
|
||||
<MarkerDefinitions :color="props.arrowHeadColor" />
|
||||
<svg :width="store.dimensions.width" :height="store.dimensions.height" class="vue-flow__edges">
|
||||
<MarkerDefinitions :color="store.arrowHeadColor" />
|
||||
<g :transform="transform">
|
||||
<EdgeWrapper
|
||||
v-for="(edge, i) of props.edges"
|
||||
v-for="(edge, i) of store.getEdges"
|
||||
:key="`${edge.id}-${i}`"
|
||||
:edge="edge"
|
||||
:component="getType(edge.type)"
|
||||
:selectable="props.elementsSelectable"
|
||||
:updatable="typeof edge.updatable === 'undefined' ? props.edgesUpdatable : edge.updatable"
|
||||
:edge-updater-radius="props.edgeUpdaterRadius"
|
||||
:dimensions="props.dimensions"
|
||||
:transform="props.transform"
|
||||
:selectable="store.elementsSelectable"
|
||||
:updatable="typeof edge.updatable === 'undefined' ? store.edgesUpdatable : edge.updatable"
|
||||
:edge-updater-radius="store.edgeUpdaterRadius"
|
||||
:dimensions="store.dimensions"
|
||||
:transform="store.transform"
|
||||
>
|
||||
<template #default="edgeProps">
|
||||
<slot :name="`edge-${edge.type}`" v-bind="edgeProps"></slot>
|
||||
</template>
|
||||
</EdgeWrapper>
|
||||
<ConnectionLine
|
||||
v-if="connectionLineVisible && sourceNode"
|
||||
:source-node="sourceNode"
|
||||
:connection-line-style="props.connectionLineStyle"
|
||||
:connection-line-type="props.connectionLineType"
|
||||
:connection-handle-id="props.connectionHandleId"
|
||||
:connection-node-id="props.connectionNodeId"
|
||||
:connection-handle-type="props.connectionHandleType"
|
||||
:connection-position="props.connectionPosition"
|
||||
:connection-mode="props.connectionMode"
|
||||
:nodes="props.nodes"
|
||||
:transform="props.transform"
|
||||
>
|
||||
<ConnectionLine v-if="connectionLineVisible && sourceNode" :source-node="sourceNode">
|
||||
<template #default="customConnectionLineProps">
|
||||
<slot name="custom-connection-line" v-bind="customConnectionLineProps"></slot>
|
||||
</template>
|
||||
|
||||
@@ -1,40 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { GraphNode, NodeComponent, SnapGrid, Transform } from '../../types'
|
||||
import { GraphNode } from '../../types'
|
||||
import NodeWrapper from '../../components/Nodes/NodeWrapper.vue'
|
||||
import { useStore } from '../../composables'
|
||||
|
||||
interface NodeRendererProps {
|
||||
nodes?: GraphNode[]
|
||||
nodeTypes: Record<string, NodeComponent>
|
||||
selectNodesOnDrag?: boolean
|
||||
snapToGrid?: boolean
|
||||
snapGrid?: SnapGrid
|
||||
transform: Transform
|
||||
elementsSelectable?: boolean
|
||||
nodesDraggable?: boolean
|
||||
nodesConnectable?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<NodeRendererProps>(), {
|
||||
selectNodesOnDrag: true,
|
||||
nodesConnectable: true,
|
||||
nodesDraggable: true,
|
||||
elementsSelectable: true,
|
||||
snapToGrid: false,
|
||||
snapGrid: () => [15, 15],
|
||||
})
|
||||
|
||||
const store = useStore()
|
||||
const getType = (type?: string) => {
|
||||
const t = type ?? 'default'
|
||||
let nodeType = props.nodeTypes[t]
|
||||
let nodeType = store.getNodeTypes[t]
|
||||
if (!nodeType) {
|
||||
nodeType = props.nodeTypes.default
|
||||
nodeType = store.getNodeTypes.default
|
||||
console.warn(`Node type "${type}" not found. Using fallback type "default".`)
|
||||
}
|
||||
return nodeType
|
||||
}
|
||||
|
||||
const transform = computed(() => `translate(${props.transform[0]}px,${props.transform[1]}px) scale(${props.transform[2]})`)
|
||||
const snapGrid = (node: GraphNode) => node.snapGrid ?? (props.snapToGrid ? props.snapGrid : undefined)
|
||||
const transform = computed(() => `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`)
|
||||
const snapGrid = (node: GraphNode) => node.snapGrid ?? (store.snapToGrid ? store.snapGrid : undefined)
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
@@ -44,16 +25,16 @@ export default {
|
||||
<template>
|
||||
<div class="vue-flow__nodes" :style="{ transform }">
|
||||
<NodeWrapper
|
||||
v-for="(node, i) of props.nodes"
|
||||
v-for="(node, i) of store.getNodes"
|
||||
:key="`${node.id}-${i}`"
|
||||
:node="node"
|
||||
:component="getType(node.type)"
|
||||
:draggable="typeof node.draggable === 'undefined' ? props.nodesDraggable : node.draggable"
|
||||
:selectable="typeof node.selectable === 'undefined' ? props.elementsSelectable : node.selectable"
|
||||
:connectable="typeof node.connectable === 'undefined' ? props.nodesConnectable : node.connectable"
|
||||
:draggable="typeof node.draggable === 'undefined' ? store.nodesDraggable : node.draggable"
|
||||
:selectable="typeof node.selectable === 'undefined' ? store.elementsSelectable : node.selectable"
|
||||
:connectable="typeof node.connectable === 'undefined' ? store.nodesConnectable : node.connectable"
|
||||
:snap-grid="snapGrid(node)"
|
||||
:select-nodes-on-drag="props.selectNodesOnDrag"
|
||||
:scale="props.transform[2]"
|
||||
:select-nodes-on-drag="store.selectNodesOnDrag"
|
||||
:scale="store.transform[2]"
|
||||
>
|
||||
<template #default="nodeProps">
|
||||
<slot :name="`node-${node.type}`" v-bind="nodeProps"></slot>
|
||||
|
||||
@@ -5,24 +5,6 @@ import { getConnectedEdges, isGraphNode } from '../../utils'
|
||||
import NodesSelection from '../../components/NodesSelection/NodesSelection.vue'
|
||||
import UserSelection from '../../components/UserSelection/UserSelection.vue'
|
||||
|
||||
interface SelectionPaneProps {
|
||||
edges: GraphEdge[]
|
||||
selectedElements?: FlowElements
|
||||
selectionKeyCode?: KeyCode
|
||||
deleteKeyCode?: KeyCode
|
||||
multiSelectionKeyCode?: KeyCode
|
||||
elementsSelectable?: boolean
|
||||
nodesSelectionActive?: boolean
|
||||
selectionActive?: boolean
|
||||
}
|
||||
const props = withDefaults(defineProps<SelectionPaneProps>(), {
|
||||
selectionKeyCode: 'Shift',
|
||||
deleteKeyCode: 'Backspace',
|
||||
multiSelectionKeyCode: 'Meta',
|
||||
elementsSelectable: true,
|
||||
selectedElements: () => [],
|
||||
})
|
||||
|
||||
const store = useStore()
|
||||
|
||||
const onClick = (event: MouseEvent) => {
|
||||
@@ -38,11 +20,11 @@ const onWheel = (event: WheelEvent) => store.hooks.paneScroll.trigger(event)
|
||||
const userSelection = ref(false)
|
||||
|
||||
tryOnMounted(() => {
|
||||
useKeyPress(props.deleteKeyCode, (keyPressed) => {
|
||||
if (keyPressed && props.selectedElements) {
|
||||
const selectedNodes = props.selectedElements.filter(isGraphNode)
|
||||
const connectedEdges = getConnectedEdges(selectedNodes, props.edges)
|
||||
const elementsToRemove = [...props.selectedElements, ...connectedEdges].reduce(
|
||||
useKeyPress(store.deleteKeyCode, (keyPressed) => {
|
||||
if (keyPressed && store.selectedElements) {
|
||||
const selectedNodes = store.selectedElements.filter(isGraphNode)
|
||||
const connectedEdges = getConnectedEdges(selectedNodes, store.getEdges)
|
||||
const elementsToRemove = [...store.selectedElements, ...connectedEdges].reduce(
|
||||
(res, item) => res.set(item.id, item),
|
||||
new Map<string, FlowElement>(),
|
||||
)
|
||||
@@ -53,12 +35,12 @@ tryOnMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
useKeyPress(props.multiSelectionKeyCode, (keyPressed) => {
|
||||
useKeyPress(store.multiSelectionKeyCode, (keyPressed) => {
|
||||
store.multiSelectionActive = keyPressed
|
||||
})
|
||||
|
||||
useKeyPress(props.selectionKeyCode, (keyPressed) => {
|
||||
userSelection.value = keyPressed && (props.selectionActive || props.elementsSelectable)
|
||||
useKeyPress(store.selectionKeyCode, (keyPressed) => {
|
||||
userSelection.value = keyPressed && (store.selectionActive || store.elementsSelectable)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
@@ -70,12 +52,7 @@ export default {
|
||||
</script>
|
||||
<template>
|
||||
<slot></slot>
|
||||
<UserSelection v-if="userSelection" id="user-selection" :key="`user-selection-${store.id}`" />
|
||||
<NodesSelection
|
||||
v-if="props.nodesSelectionActive"
|
||||
id="nodes-selection"
|
||||
:key="`nodes-selction-${store.id}`"
|
||||
:selected-elements="props.selectedElements"
|
||||
/>
|
||||
<UserSelection v-if="userSelection" :key="`user-selection-${store.id}`" />
|
||||
<NodesSelection v-if="store.nodesSelectionActive" :key="`nodes-selction-${store.id}`" />
|
||||
<div :key="`flow-pane-${store.id}`" class="vue-flow__pane" @click="onClick" @contextmenu="onContextMenu" @wheel="onWheel" />
|
||||
</template>
|
||||
|
||||
@@ -1,16 +1,4 @@
|
||||
<script lang="ts" setup>
|
||||
import { CSSProperties } from 'vue'
|
||||
import {
|
||||
ConnectionLineType,
|
||||
ConnectionMode,
|
||||
Elements,
|
||||
PanOnScrollMode,
|
||||
KeyCode,
|
||||
CoordinateExtent,
|
||||
NodeTypes,
|
||||
EdgeTypes,
|
||||
FlowOptions,
|
||||
} from '../../types'
|
||||
import Renderer from '../Renderer/Renderer.vue'
|
||||
import ZoomPane from '../ZoomPane/ZoomPane.vue'
|
||||
import SelectionPane from '../SelectionPane/SelectionPane.vue'
|
||||
@@ -18,47 +6,7 @@ import NodeRenderer from '../NodeRenderer/NodeRenderer.vue'
|
||||
import EdgeRenderer from '../EdgeRenderer/EdgeRenderer.vue'
|
||||
import LoadingIndicator from '../../components/Loading/LoadingIndicator.vue'
|
||||
import { createHooks, initFlow } from '../../composables'
|
||||
|
||||
interface FlowProps extends FlowOptions {
|
||||
id?: string
|
||||
modelValue?: Elements
|
||||
nodeTypes?: NodeTypes
|
||||
edgeTypes?: EdgeTypes
|
||||
connectionMode?: ConnectionMode
|
||||
connectionLineType?: ConnectionLineType
|
||||
connectionLineStyle?: CSSProperties
|
||||
deleteKeyCode?: KeyCode
|
||||
selectionKeyCode?: KeyCode
|
||||
multiSelectionKeyCode?: KeyCode
|
||||
zoomActivationKeyCode?: KeyCode
|
||||
preventScrolling?: boolean
|
||||
snapToGrid?: boolean
|
||||
snapGrid?: [number, number]
|
||||
onlyRenderVisibleElements?: boolean
|
||||
edgesUpdatable?: boolean
|
||||
nodesDraggable?: boolean
|
||||
nodesConnectable?: boolean
|
||||
elementsSelectable?: boolean
|
||||
selectNodesOnDrag?: boolean
|
||||
paneMoveable?: boolean
|
||||
minZoom?: number
|
||||
maxZoom?: number
|
||||
defaultZoom?: number
|
||||
defaultPosition?: [number, number]
|
||||
translateExtent?: CoordinateExtent
|
||||
nodeExtent?: CoordinateExtent
|
||||
arrowHeadColor?: string
|
||||
markerEndId?: string
|
||||
zoomOnScroll?: boolean
|
||||
zoomOnPinch?: boolean
|
||||
panOnScroll?: boolean
|
||||
panOnScrollSpeed?: number
|
||||
panOnScrollMode?: PanOnScrollMode
|
||||
zoomOnDoubleClick?: boolean
|
||||
edgeUpdaterRadius?: number
|
||||
storageKey?: string
|
||||
loading?: string
|
||||
}
|
||||
import type { FlowProps } from '../../types/flow'
|
||||
|
||||
const props = withDefaults(defineProps<FlowProps>(), {
|
||||
modelValue: () => [],
|
||||
@@ -119,45 +67,10 @@ export default {
|
||||
<Suspense timeout="0">
|
||||
<template #default>
|
||||
<Renderer :key="`renderer-${store.id}`" :elements="elements">
|
||||
<ZoomPane
|
||||
:key="`zoom-pane-${store.id}`"
|
||||
:prevent-scrolling="store.preventScrolling"
|
||||
:selection-key-code="store.selectionKeyCode"
|
||||
:zoom-activation-key-code="store.zoomActivationKeyCode"
|
||||
:default-zoom="store.defaultZoom"
|
||||
:default-position="store.defaultPosition"
|
||||
:zoom-on-scroll="store.zoomOnScroll"
|
||||
:zoom-on-pinch="store.zoomOnPinch"
|
||||
:zoom-on-double-click="store.zoomOnDoubleClick"
|
||||
:pan-on-scroll="store.panOnScroll"
|
||||
:pan-on-scroll-speed="store.panOnScrollSpeed"
|
||||
:pan-on-scroll-mode="store.panOnScrollMode"
|
||||
:pane-moveable="store.paneMoveable"
|
||||
>
|
||||
<ZoomPane :key="`zoom-pane-${store.id}`">
|
||||
<template #default="zoomPaneProps">
|
||||
<SelectionPane
|
||||
:key="`selection-pane-${store.id}`"
|
||||
:edges="store.getEdges"
|
||||
:selected-elements="store.selectedElements"
|
||||
:selection-active="store.selectionActive"
|
||||
:nodes-selection-active="store.nodesSelectionActive"
|
||||
:elements-selectable="store.elementsSelectable"
|
||||
:delete-key-code="store.deleteKeyCode"
|
||||
:multi-selection-key-code="store.multiSelectionKeyCode"
|
||||
:selection-key-code="store.selectionKeyCode"
|
||||
>
|
||||
<NodeRenderer
|
||||
:key="`node-renderer-${store.id}`"
|
||||
:nodes="store.getNodes"
|
||||
:node-types="store.getNodeTypes"
|
||||
:snap-to-grid="store.snapToGrid"
|
||||
:snap-grid="store.snapGrid"
|
||||
:select-nodes-on-drag="store.selectNodesOnDrag"
|
||||
:transform="store.transform"
|
||||
:elements-selectable="store.elementsSelectable"
|
||||
:nodes-draggable="store.nodesDraggable"
|
||||
:nodes-connectable="store.nodesConnectable"
|
||||
>
|
||||
<SelectionPane :key="`selection-pane-${store.id}`">
|
||||
<NodeRenderer :key="`node-renderer-${store.id}`">
|
||||
<template
|
||||
v-for="nodeName of Object.keys(store.getNodeTypes)"
|
||||
#[`node-${nodeName}`]="nodeProps"
|
||||
@@ -166,28 +79,7 @@ export default {
|
||||
<slot :name="`node-${nodeName}`" v-bind="nodeProps" />
|
||||
</template>
|
||||
</NodeRenderer>
|
||||
<EdgeRenderer
|
||||
:key="`edge-renderer-${store.id}`"
|
||||
:transform="store.transform"
|
||||
:dimensions="store.dimensions"
|
||||
:edges="store.getEdges"
|
||||
:edge-types="store.getEdgeTypes"
|
||||
:nodes="store.getNodes"
|
||||
:selected-elements="store.selectedElements"
|
||||
:elements-selectable="store.elementsSelectable"
|
||||
:connection-node-id="store.connectionNodeId"
|
||||
:connction-handle-id="store.connectionHandleId"
|
||||
:connection-handle-type="store.connectionHandleType"
|
||||
:connection-position="store.connectionPosition"
|
||||
:connection-mode="store.connectionMode"
|
||||
:nodes-connectable="store.nodesConnectable"
|
||||
:connection-line-type="store.connectionLineType"
|
||||
:connection-line-style="store.connectionLineStyle"
|
||||
:arrow-head-color="store.arrowHeadColor"
|
||||
:marker-end-id="store.markerEndId"
|
||||
:only-render-visible-elements="store.onlyRenderVisibleElements"
|
||||
:edges-updatable="store.edgesUpdatable"
|
||||
>
|
||||
<EdgeRenderer :key="`edge-renderer-${store.id}`">
|
||||
<template
|
||||
v-for="edgeName of Object.keys(store.getEdgeTypes)"
|
||||
#[`edge-${edgeName}`]="edgeProps"
|
||||
|
||||
@@ -1,39 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom'
|
||||
import { pointer, select } from 'd3-selection'
|
||||
import { FlowTransform, KeyCode, PanOnScrollMode } from '../../types'
|
||||
import { FlowTransform, PanOnScrollMode } from '../../types'
|
||||
import { useKeyPress, useStore, useWindow } from '../../composables'
|
||||
import { clamp } from '../../utils'
|
||||
import { clamp, clampPosition } from '../../utils'
|
||||
|
||||
interface ZoomPaneProps {
|
||||
selectionKeyCode?: KeyCode
|
||||
zoomActivationKeyCode?: KeyCode
|
||||
paneMoveable?: boolean
|
||||
defaultZoom?: number
|
||||
defaultPosition?: [number, number]
|
||||
zoomOnScroll?: boolean
|
||||
zoomOnPinch?: boolean
|
||||
panOnScroll?: boolean
|
||||
panOnScrollSpeed?: number
|
||||
panOnScrollMode?: PanOnScrollMode
|
||||
zoomOnDoubleClick?: boolean
|
||||
preventScrolling?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<ZoomPaneProps>(), {
|
||||
selectionKeyCode: 'Shift',
|
||||
zoomActivationKeyCode: 'Meta',
|
||||
defaultZoom: 1,
|
||||
defaultPosition: () => [0, 0],
|
||||
zoomOnScroll: true,
|
||||
zoomOnPinch: true,
|
||||
zoomOnDoubleClick: true,
|
||||
panOnScroll: false,
|
||||
panOnScrollSpeed: 0.5,
|
||||
panOnScrollMode: PanOnScrollMode.Free,
|
||||
paneMoveable: true,
|
||||
preventScrolling: true,
|
||||
})
|
||||
const store = useStore()
|
||||
const zoomPaneEl = templateRef<HTMLDivElement>('zoomPane', null)
|
||||
|
||||
@@ -50,10 +21,11 @@ const eventToFlowTransform = (eventTransform: ZoomTransform): FlowTransform => (
|
||||
|
||||
const noWheel = (event: Event) => (event.target as Element).closest('.nowheel')
|
||||
|
||||
const clampedX = clamp(props.defaultPosition[0], store.translateExtent[0][0], store.translateExtent[1][0])
|
||||
const clampedY = clamp(props.defaultPosition[1], store.translateExtent[0][1], store.translateExtent[1][1])
|
||||
const clampedZoom = clamp(props.defaultZoom, store.minZoom, store.maxZoom)
|
||||
const transform = ref({ x: clampedX, y: clampedY, zoom: clampedZoom })
|
||||
const clampedZoom = clamp(store.defaultZoom, store.minZoom, store.maxZoom)
|
||||
const transform = ref({
|
||||
...clampPosition({ x: store.defaultPosition[0], y: store.defaultPosition[1] }, store.translateExtent),
|
||||
zoom: clampedZoom,
|
||||
})
|
||||
const d3Zoom = ref(zoom<HTMLDivElement, any>().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(store.translateExtent))
|
||||
const d3Selection = ref()
|
||||
|
||||
@@ -66,7 +38,7 @@ nextTick(async () => {
|
||||
const d3s = d3Selection.value!
|
||||
const d3ZoomHandler = d3s.on('wheel.zoom')
|
||||
|
||||
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom)
|
||||
const updatedTransform = zoomIdentity.translate(transform.value.x, transform.value.y).scale(transform.value.zoom)
|
||||
d3z.transform(d3s, updatedTransform)
|
||||
store.initD3Zoom({ d3Zoom: d3z, d3Selection: d3s, d3ZoomHandler })
|
||||
store.transform = [updatedTransform.x, updatedTransform.y, updatedTransform.k]
|
||||
@@ -87,7 +59,7 @@ nextTick(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
useKeyPress(props.selectionKeyCode, (keyPress) => {
|
||||
useKeyPress(store.selectionKeyCode, (keyPress) => {
|
||||
if (keyPress) {
|
||||
d3z.on('zoom', null)
|
||||
} else {
|
||||
@@ -101,8 +73,8 @@ nextTick(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
useKeyPress(props.zoomActivationKeyCode, (keyPress) => {
|
||||
if (props.panOnScroll && keyPress) {
|
||||
useKeyPress(store.zoomActivationKeyCode, (keyPress) => {
|
||||
if (store.panOnScroll && keyPress) {
|
||||
d3s
|
||||
?.on('wheel', (event: WheelEvent) => {
|
||||
if (noWheel(event)) return
|
||||
@@ -111,7 +83,7 @@ nextTick(async () => {
|
||||
|
||||
const currentZoom = d3s?.property('__zoom').k || 1
|
||||
|
||||
if (event.ctrlKey && props.zoomOnPinch) {
|
||||
if (event.ctrlKey && store.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
|
||||
@@ -124,40 +96,40 @@ nextTick(async () => {
|
||||
// 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 deltaX = store.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize
|
||||
const deltaY = store.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize
|
||||
|
||||
if (d3s && props.panOnScrollSpeed)
|
||||
if (d3s && store.panOnScrollSpeed)
|
||||
d3z?.translateBy(
|
||||
d3s,
|
||||
-(deltaX / currentZoom) * props.panOnScrollSpeed,
|
||||
-(deltaY / currentZoom) * props.panOnScrollSpeed,
|
||||
-(deltaX / currentZoom) * store.panOnScrollSpeed,
|
||||
-(deltaY / currentZoom) * store.panOnScrollSpeed,
|
||||
)
|
||||
})
|
||||
.on('wheel.zoom', null)
|
||||
} else if (typeof d3ZoomHandler !== 'undefined') {
|
||||
d3s
|
||||
?.on('wheel', (event: WheelEvent) => {
|
||||
if ((!props.zoomOnScroll && props.preventScrolling) || !props.preventScrolling || noWheel(event)) return
|
||||
if ((!store.zoomOnScroll && store.preventScrolling) || !store.preventScrolling || noWheel(event)) return
|
||||
event.preventDefault()
|
||||
})
|
||||
.on('wheel.zoom', d3ZoomHandler)
|
||||
}
|
||||
})
|
||||
|
||||
const keyPress = useKeyPress(props.selectionKeyCode)
|
||||
const keyPress = useKeyPress(store.selectionKeyCode)
|
||||
d3z.filter((event: MouseEvent) => {
|
||||
const pinchZoom = props.zoomOnPinch && event.ctrlKey
|
||||
const pinchZoom = store.zoomOnPinch && event.ctrlKey
|
||||
|
||||
// if all interactions are disabled, we prevent all zoom events
|
||||
if (!props.paneMoveable && !props.zoomOnScroll && !props.panOnScroll && !props.zoomOnDoubleClick && !props.zoomOnPinch)
|
||||
if (!store.paneMoveable && !store.zoomOnScroll && !store.panOnScroll && !store.zoomOnDoubleClick && !store.zoomOnPinch)
|
||||
return false
|
||||
|
||||
// during a selection we prevent all other interactions
|
||||
if (keyPress.value) return false
|
||||
|
||||
// if zoom on double click is disabled, we prevent the double click event
|
||||
if (!props.zoomOnDoubleClick && event.type === 'dblclick') return false
|
||||
if (!store.zoomOnDoubleClick && event.type === 'dblclick') return false
|
||||
|
||||
if (noWheel(event) && event.type === 'wheel') return false
|
||||
|
||||
@@ -171,13 +143,13 @@ nextTick(async () => {
|
||||
// when the target element is a node selection, we still allow zooming
|
||||
if ((event.target as Element).closest('.vue-flow__nodesselection') && event.type !== 'wheel') return false
|
||||
|
||||
if (!props.zoomOnPinch && event.ctrlKey && event.type === 'wheel') return false
|
||||
if (!store.zoomOnPinch && event.ctrlKey && event.type === 'wheel') return false
|
||||
|
||||
// when there is no scroll handling enabled, we prevent all wheel events
|
||||
if (!props.zoomOnScroll && !props.panOnScroll && !pinchZoom && event.type === 'wheel') return false
|
||||
if (!store.zoomOnScroll && !store.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
|
||||
if (!store.paneMoveable && (event.type === 'mousedown' || event.type === 'touchstart')) return false
|
||||
|
||||
// default filter for d3-zoom
|
||||
return (!event.ctrlKey || event.type === 'wheel') && !event.button
|
||||
@@ -190,24 +162,24 @@ const { width, height } = useElementBounding(zoomPaneEl)
|
||||
const window = useWindow()
|
||||
if ('screen' in window) await until(() => store.isReady).toBe(true)
|
||||
|
||||
watch(
|
||||
[width, height],
|
||||
([newWidth, newHeight]) => {
|
||||
store.dimensions.width = newWidth
|
||||
store.dimensions.height = newHeight
|
||||
},
|
||||
{ flush: 'sync', immediate: true },
|
||||
)
|
||||
watch(
|
||||
transform,
|
||||
(val) =>
|
||||
(store.transform = [
|
||||
clamp(val.x, store.translateExtent[0][0], store.translateExtent[1][0]),
|
||||
clamp(val.y, store.translateExtent[0][1], store.translateExtent[1][1]),
|
||||
clamp(val.zoom, store.minZoom, store.maxZoom),
|
||||
]),
|
||||
{ flush: 'sync', immediate: true },
|
||||
)
|
||||
nextTick(() => {
|
||||
watch(
|
||||
[width, height],
|
||||
([newWidth, newHeight]) => {
|
||||
store.dimensions.width = newWidth
|
||||
store.dimensions.height = newHeight
|
||||
},
|
||||
{ flush: 'sync', immediate: true },
|
||||
)
|
||||
watch(
|
||||
transform,
|
||||
(val) => {
|
||||
const { x, y } = clampPosition(val, store.translateExtent)
|
||||
store.transform = [x, y, clamp(val.zoom, store.minZoom, store.maxZoom)]
|
||||
},
|
||||
{ flush: 'sync', immediate: true },
|
||||
)
|
||||
})
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
@@ -216,11 +188,6 @@ export default {
|
||||
</script>
|
||||
<template>
|
||||
<div ref="zoomPane" class="vue-flow__zoompane">
|
||||
<slot
|
||||
v-bind="{
|
||||
transform: store.transform,
|
||||
dimensions: { width, height },
|
||||
}"
|
||||
/>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user