1
1
<script setup lang="ts">
2
+ import type { ModuleImport , ModuleListItem , SessionContext } from ' ~/shared/types'
2
3
import type { HierarchyLink , HierarchyNode } from ' d3-hierarchy'
3
- import type { ModuleImport , ModuleListItem , SessionContext } from ' ~~/shared/types'
4
- import { useEventListener } from ' @vueuse/core'
4
+ import { onKeyPressed , useEventListener , useMagicKeys } from ' @vueuse/core'
5
5
import { hierarchy , tree } from ' d3-hierarchy'
6
6
import { linkHorizontal , linkVertical } from ' d3-shape'
7
7
import { computed , nextTick , onMounted , reactive , ref , shallowReactive , shallowRef , useTemplateRef , watch } from ' vue'
8
+ import { useZoomElement } from ' ~/composables/zoomElement'
8
9
9
10
const props = defineProps <{
10
11
session: SessionContext
@@ -35,14 +36,32 @@ const container = useTemplateRef<HTMLDivElement>('container')
35
36
const isGrabbing = ref (false )
36
37
const width = ref (window .innerWidth )
37
38
const height = ref (window .innerHeight )
38
- const scale = ref (1 )
39
39
const nodesRefMap = shallowReactive (new Map <string , HTMLDivElement >())
40
40
41
41
const nodes = shallowRef <HierarchyNode <Node >[]>([])
42
42
const links = shallowRef <Link []>([])
43
43
const nodesMap = shallowReactive (new Map <string , HierarchyNode <Node >>())
44
44
const linksMap = shallowReactive (new Map <string , Link >())
45
45
46
+ const ZOOM_MIN = 0.4
47
+ const ZOOM_MAX = 2
48
+ const { control } = useMagicKeys ()
49
+ const { scale, zoomIn, zoomOut } = useZoomElement (container , {
50
+ wheel: control ,
51
+ minScale: ZOOM_MIN ,
52
+ maxScale: ZOOM_MAX ,
53
+ })
54
+
55
+ onKeyPressed ([' -' , ' _' ], (e ) => {
56
+ if (e .ctrlKey )
57
+ zoomOut ()
58
+ })
59
+
60
+ onKeyPressed ([' =' , ' +' ], (e ) => {
61
+ if (e .ctrlKey )
62
+ zoomIn ()
63
+ })
64
+
46
65
const modulesMap = computed (() => {
47
66
const map = new Map <string , ModuleListItem >()
48
67
for (const module of props .modules ) {
@@ -220,26 +239,30 @@ onMounted(() => {
220
239
:class =" isGrabbing ? 'cursor-grabbing' : ''"
221
240
>
222
241
<div
223
- absolute left-0 top-0
224
- :style =" {
225
- width: `${width}px`,
226
- height: `${height}px`,
227
- }"
228
- class =" bg-dots"
229
- />
230
- <svg pointer-events-none absolute left-0 top-0 z-graph-link :width =" width" :height =" height" >
231
- <g >
232
- <path
233
- v-for =" link of links"
234
- :key =" link.id"
235
- :d =" generateLink(link)!"
236
- :class =" getLinkColor(link)"
237
- :stroke-dasharray =" link.import?.kind === 'dynamic-import' ? '3 6' : undefined"
238
- fill =" none"
239
- />
240
- </g >
241
- </svg >
242
- <!-- <svg pointer-events-none absolute left-0 top-0 z-graph-link-active :width="width" :height="height">
242
+ flex =" ~ items-center justify-center"
243
+ :style =" { transform: `scale(${scale})`, transformOrigin: '0 0' }"
244
+ >
245
+ <div
246
+ absolute left-0 top-0
247
+ :style =" {
248
+ width: `${width}px`,
249
+ height: `${height}px`,
250
+ }"
251
+ class =" bg-dots"
252
+ />
253
+ <svg pointer-events-none absolute left-0 top-0 z-graph-link :width =" width" :height =" height" >
254
+ <g >
255
+ <path
256
+ v-for =" link of links"
257
+ :key =" link.id"
258
+ :d =" generateLink(link)!"
259
+ :class =" getLinkColor(link)"
260
+ :stroke-dasharray =" link.import?.kind === 'dynamic-import' ? '3 6' : undefined"
261
+ fill =" none"
262
+ />
263
+ </g >
264
+ </svg >
265
+ <!-- <svg pointer-events-none absolute left-0 top-0 z-graph-link-active :width="width" :height="height">
243
266
<g>
244
267
<path
245
268
v-for="link of links"
@@ -250,31 +273,65 @@ onMounted(() => {
250
273
/>
251
274
</g>
252
275
</svg> -->
253
- <template
254
- v-for =" node of nodes "
255
- :key =" node .data .module .id "
256
- >
257
- <template v-if =" node .data .module .id !== ' ~root' " >
258
- <DisplayModuleId
259
- :id =" node.data.module.id"
260
- :ref =" (el: any) => nodesRefMap.set(node.data.module.id, el?.$el)"
261
- absolute hover =" bg-active" block px2 p1 bg-glass z-graph-node
262
- border =" ~ base rounded"
263
- :link =" true"
264
- :session =" session"
265
- :pkg =" node.data.module"
266
- :minimal =" true"
267
- :style =" {
268
- left: `${node.x}px`,
269
- top: `${node.y}px`,
270
- minWidth: graphRender === 'normal' ? `${SPACING.width}px` : undefined,
271
- transform: 'translate(-50%, -50%)',
272
- maxWidth: '400px',
273
- maxHeight: '50px',
274
- overflow: 'hidden',
275
- }"
276
- />
276
+ <template
277
+ v-for =" node of nodes "
278
+ :key =" node .data .module .id "
279
+ >
280
+ <template v-if =" node .data .module .id !== ' ~root' " >
281
+ <DisplayModuleId
282
+ :id =" node.data.module.id"
283
+ :ref =" (el: any) => nodesRefMap.set(node.data.module.id, el?.$el)"
284
+ absolute hover =" bg-active" block px2 p1 bg-glass z-graph-node
285
+ border =" ~ base rounded"
286
+ :link =" true"
287
+ :session =" session"
288
+ :pkg =" node.data.module"
289
+ :minimal =" true"
290
+ :style =" {
291
+ left: `${node.x}px`,
292
+ top: `${node.y}px`,
293
+ minWidth: graphRender === 'normal' ? `${SPACING.width}px` : undefined,
294
+ transform: 'translate(-50%, -50%)',
295
+ maxWidth: '400px',
296
+ maxHeight: '50px',
297
+ overflow: 'hidden',
298
+ }"
299
+ />
300
+ </template >
277
301
</template >
278
- </template >
302
+ </div >
303
+ <div
304
+ fixed right-6 bottom-6 z-panel-nav flex =" ~ col gap-2 items-center"
305
+ >
306
+ <div w-10 flex =" ~ items-center justify-center" >
307
+ <DisplayTimeoutView :content =" `${Math.round(scale * 100)}%`" class =" text-sm" />
308
+ </div >
309
+
310
+ <div bg-glass rounded-full border border-base shadow >
311
+ <button
312
+ v-tooltip.left =" 'Zoom In (Ctrl + =)'"
313
+ :disabled =" scale >= ZOOM_MAX"
314
+ w-10 h-10 rounded-full hover:bg-active op-fade
315
+ hover:op100 disabled:op20 disabled:bg-none
316
+ disabled:cursor-not-allowed
317
+ flex =" ~ items-center justify-center"
318
+ title =" Zoom In (Ctrl + =)"
319
+ @click =" zoomIn()"
320
+ >
321
+ <div i-ph-magnifying-glass-plus-duotone />
322
+ </button >
323
+ <button
324
+ v-tooltip.left =" 'Zoom Out (Ctrl + -)'"
325
+ :disabled =" scale <= ZOOM_MIN"
326
+ w-10 h-10 rounded-full hover:bg-active op-fade hover:op100
327
+ disabled:op20 disabled:bg-none disabled:cursor-not-allowed
328
+ flex =" ~ items-center justify-center"
329
+ title =" Zoom Out (Ctrl + -)"
330
+ @click =" zoomOut()"
331
+ >
332
+ <div i-ph-magnifying-glass-minus-duotone />
333
+ </button >
334
+ </div >
335
+ </div >
279
336
</div >
280
337
</template >
0 commit comments