|
1 | 1 | <script lang="ts" setup> |
2 | | -import type { CoordinateExtent, GraphNode, PanelPosition } from '@vue-flow/core' |
| 2 | +import type { GraphNode, PanelPosition } from '@vue-flow/core' |
3 | 3 | import { Panel, getBoundsOfRects, getConnectedEdges, getRectOfNodes, useVueFlow } from '@vue-flow/core' |
4 | | -import { zoom, zoomIdentity } from 'd3-zoom' |
5 | | -import type { D3ZoomEvent } from 'd3-zoom' |
6 | | -import { pointer, select } from 'd3-selection' |
7 | | -import { computed, provide, ref, toRef, useAttrs, watchEffect } from 'vue' |
| 4 | +import { computed, onMounted, onUnmounted, provide, ref, toRef, useAttrs, watch } from 'vue' |
| 5 | +import type { XYMinimapInstance } from '@xyflow/system' |
| 6 | +import { XYMinimap } from '@xyflow/system' |
8 | 7 | import type { MiniMapEmits, MiniMapNodeFunc, MiniMapProps, MiniMapSlots, ShapeRendering } from './types' |
9 | 8 | import MiniMapNode from './MiniMapNode.vue' |
10 | 9 | import { Slots } from './types' |
@@ -39,10 +38,12 @@ const attrs: Record<string, any> = useAttrs() |
39 | 38 | const defaultWidth = 200 |
40 | 39 | const defaultHeight = 150 |
41 | 40 |
|
42 | | -const { id, edges, viewport, translateExtent, dimensions, emits, d3Selection, d3Zoom, getNodesInitialized } = useVueFlow() |
| 41 | +const { id, edges, viewport, translateExtent, dimensions, emits, panZoom, getNodesInitialized } = useVueFlow() |
43 | 42 |
|
44 | 43 | const el = ref<SVGElement>() |
45 | 44 |
|
| 45 | +let minimapInstance: XYMinimapInstance | null = null |
| 46 | +
|
46 | 47 | provide(Slots, slots) |
47 | 48 |
|
48 | 49 | const elementWidth = toRef(() => width ?? attrs.style?.width ?? defaultWidth) |
@@ -116,64 +117,54 @@ const d = computed(() => { |
116 | 117 | a${maskBorderRadius},${maskBorderRadius} 0 0 1 ${maskBorderRadius},-${maskBorderRadius}z` |
117 | 118 | }) |
118 | 119 |
|
119 | | -watchEffect( |
120 | | - (onCleanup) => { |
121 | | - if (el.value) { |
122 | | - const selection = select(el.value as Element) |
123 | | -
|
124 | | - const zoomHandler = (event: D3ZoomEvent<SVGSVGElement, any>) => { |
125 | | - if (event.sourceEvent.type !== 'wheel' || !d3Selection.value || !d3Zoom.value) { |
126 | | - return |
127 | | - } |
128 | | -
|
129 | | - const pinchDelta = |
130 | | - -event.sourceEvent.deltaY * |
131 | | - (event.sourceEvent.deltaMode === 1 ? 0.05 : event.sourceEvent.deltaMode ? 1 : 0.002) * |
132 | | - zoomStep |
133 | | - const zoom = viewport.value.zoom * 2 ** pinchDelta |
134 | | -
|
135 | | - d3Zoom.value.scaleTo(d3Selection.value, zoom) |
| 120 | +onMounted(() => { |
| 121 | + watch( |
| 122 | + panZoom, |
| 123 | + (panZoomInstance) => { |
| 124 | + if (el.value && panZoomInstance) { |
| 125 | + minimapInstance = XYMinimap({ |
| 126 | + domNode: el.value, |
| 127 | + panZoom: panZoomInstance, |
| 128 | + getTransform: () => [viewport.value.x, viewport.value.y, viewport.value.zoom], |
| 129 | + getViewScale: () => viewScale.value, |
| 130 | + }) |
136 | 131 | } |
137 | | -
|
138 | | - const panHandler = (event: D3ZoomEvent<HTMLDivElement, any>) => { |
139 | | - if (event.sourceEvent.type !== 'mousemove' || !d3Selection.value || !d3Zoom.value) { |
140 | | - return |
141 | | - } |
142 | | -
|
143 | | - const moveScale = viewScale.value * Math.max(1, viewport.value.zoom) * (inversePan ? -1 : 1) |
144 | | -
|
145 | | - const position = { |
146 | | - x: viewport.value.x - event.sourceEvent.movementX * moveScale, |
147 | | - y: viewport.value.y - event.sourceEvent.movementY * moveScale, |
148 | | - } |
149 | | -
|
150 | | - const extent: CoordinateExtent = [ |
151 | | - [0, 0], |
152 | | - [dimensions.value.width, dimensions.value.height], |
153 | | - ] |
154 | | -
|
155 | | - const nextTransform = zoomIdentity.translate(position.x, position.y).scale(viewport.value.zoom) |
156 | | - const constrainedTransform = d3Zoom.value.constrain()(nextTransform, extent, translateExtent.value) |
157 | | -
|
158 | | - d3Zoom.value.transform(d3Selection.value, constrainedTransform) |
159 | | - } |
160 | | -
|
161 | | - const zoomAndPanHandler = zoom() |
162 | | - .on('zoom', pannable ? panHandler : () => {}) |
163 | | - .on('zoom.wheel', zoomable ? zoomHandler : () => {}) |
164 | | -
|
165 | | - selection.call(zoomAndPanHandler) |
166 | | -
|
167 | | - onCleanup(() => { |
168 | | - selection.on('zoom', null) |
| 132 | + }, |
| 133 | + { immediate: true }, |
| 134 | + ) |
| 135 | +
|
| 136 | + onUnmounted(() => { |
| 137 | + minimapInstance?.destroy() |
| 138 | + }) |
| 139 | +
|
| 140 | + watch( |
| 141 | + [ |
| 142 | + () => pannable, |
| 143 | + () => zoomable, |
| 144 | + () => inversePan, |
| 145 | + () => zoomStep, |
| 146 | + translateExtent, |
| 147 | + () => dimensions.value.height, |
| 148 | + () => dimensions.value.width, |
| 149 | + ], |
| 150 | + () => { |
| 151 | + minimapInstance?.update({ |
| 152 | + translateExtent: translateExtent.value, |
| 153 | + width: dimensions.value.width, |
| 154 | + height: dimensions.value.height, |
| 155 | + inversePan, |
| 156 | + pannable, |
| 157 | + zoomStep, |
| 158 | + zoomable, |
169 | 159 | }) |
170 | | - } |
171 | | - }, |
172 | | - { flush: 'post' }, |
173 | | -) |
| 160 | + }, |
| 161 | + { immediate: true }, |
| 162 | + ) |
| 163 | +}) |
174 | 164 |
|
175 | 165 | function onSvgClick(event: MouseEvent) { |
176 | | - const [x, y] = pointer(event) |
| 166 | + const [x, y] = minimapInstance?.pointer(event) || [0, 0] |
| 167 | +
|
177 | 168 | emit('click', { event, position: { x, y } }) |
178 | 169 | } |
179 | 170 |
|
|
0 commit comments