@@ -23,7 +23,6 @@ import Tooltip from "../atoms/Tooltip.vue";
23
23
import RecursiveCircles from " ../atoms/RecursiveCircles.vue" ;
24
24
import RecursiveLinks from " ../atoms/RecursiveLinks.vue" ;
25
25
import RecursiveLabels from " ../atoms/RecursiveLabels.vue" ;
26
- import BaseDirectionPad from " ../atoms/BaseDirectionPad.vue" ;
27
26
import Skeleton from " ./vue-ui-skeleton.vue" ;
28
27
import Accordion from " ./vue-ui-accordion.vue" ;
29
28
import { useNestedProp } from " ../useNestedProp" ;
@@ -33,6 +32,7 @@ import PackageVersion from "../atoms/PackageVersion.vue";
33
32
import PenAndPaper from " ../atoms/PenAndPaper.vue" ;
34
33
import { useUserOptionState } from " ../useUserOptionState" ;
35
34
import { useChartAccessibility } from " ../useChartAccessibility" ;
35
+ import usePanZoom from " ../usePanZoom" ;
36
36
37
37
const { vue_ui_molecule: DEFAULT_CONFIG } = useConfig ();
38
38
@@ -51,6 +51,8 @@ const props = defineProps({
51
51
},
52
52
});
53
53
54
+ const emit = defineEmits ([' selectNode' ]);
55
+
54
56
const isDataset = computed (() => {
55
57
return !! props .dataset && props .dataset .length ;
56
58
});
@@ -73,10 +75,6 @@ const details = ref(null);
73
75
const isTooltip = ref (false );
74
76
const tooltipContent = ref (" " );
75
77
const moleculeChart = ref (null );
76
- const zoomedId = ref (null );
77
- const isZoom = ref (false );
78
- const zoomReference = ref (null );
79
- const selectedNode = ref (null );
80
78
const step = ref (0 );
81
79
const titleStep = ref (0 );
82
80
const tableStep = ref (0 );
@@ -254,90 +252,6 @@ const convertedDataset = computed(() => {
254
252
return processNodes (props .dataset );
255
253
})
256
254
257
- function restoreViewBox () {
258
- isZoom .value = false ;
259
- zoomedId .value = null ;
260
- selectedNode .value = null ;
261
- zoomReference .value = null ;
262
- zoomOnNode ({
263
- polygonPath: {
264
- coordinates: [{x: svg .value .width / 2 , y: svg .value .height / 2 }]
265
- },
266
- circleRadius: 24 ,
267
- })
268
- }
269
-
270
- const currentAnimationFrame = ref (null );
271
-
272
-
273
- function zoomOnNode (node ) {
274
- moleculeChart .value .focus ();
275
-
276
- nextTick (() => {
277
- if (currentAnimationFrame .value ) {
278
- cancelAnimationFrame (currentAnimationFrame .value );
279
- }
280
- const vb = dynamicViewBox .value .split (' ' );
281
- const startX = parseFloat (vb[0 ]);
282
- const startY = parseFloat (vb[1 ]);
283
- const startWidth = parseFloat (vb[2 ]);
284
- const startHeight = parseFloat (vb[3 ]);
285
- const { x , y } = node .polygonPath .coordinates [0 ];
286
- const { circleRadius } = node;
287
- const sizer = 8.34 ;
288
- const targetX = x - circleRadius * sizer;
289
- const targetY = y - circleRadius * sizer;
290
- const targetWidth = circleRadius * sizer * 2 ;
291
- const targetHeight = circleRadius * sizer * 2 ;
292
-
293
- const distance = Math .sqrt ((targetX - startX) ** 2 + (targetY - startY) ** 2 );
294
- const numSteps = Math .min (1200 , Math .max (20 , Math .floor (distance / 10 )));
295
- const stepX = (targetX - startX) / numSteps;
296
- const stepY = (targetY - startY) / numSteps;
297
- const stepWidth = (targetWidth - startWidth) / numSteps;
298
- const stepHeight = (targetHeight - startHeight) / numSteps;
299
- let currentStep = 0 ;
300
-
301
- function animateZoom () {
302
- dynamicViewBox .value = ` ${ startX + stepX * currentStep} ${ startY + stepY * currentStep} ${ startWidth + stepWidth * currentStep} ${ startHeight + stepHeight * currentStep} ` ;
303
- currentStep += FINAL_CONFIG .value .style .chart .zoom .speed ;
304
-
305
- if (currentStep <= numSteps) {
306
- currentAnimationFrame .value = requestAnimationFrame (animateZoom);
307
- }
308
-
309
- }
310
- animateZoom ();
311
- })
312
- }
313
-
314
- function zoom (node ) {
315
- if (zoomedId .value === node .uid ) {
316
- restoreViewBox ();
317
-
318
- } else {
319
- zoomedId .value = node .uid ;
320
- selectedNode .value = node;
321
- zoomReference .value = {
322
- circleRadius: node .circleRadius ,
323
- polygonPath: {
324
- coordinates: [{ x: node .polygonPath .coordinates [0 ].x , y: node .polygonPath .coordinates [0 ].y }]
325
- }
326
- }
327
- zoomOnNode (node)
328
- isZoom .value = node .uid !== convertedDataset .value [0 ].uid
329
- }
330
- }
331
-
332
- function unzoom (event ) {
333
- if (event .target .nodeName !== ' circle' ) {
334
- restoreViewBox ();
335
- isZoom .value = false ;
336
- } else {
337
- return ;
338
- }
339
- }
340
-
341
255
const dataTooltipSlot = ref (null );
342
256
343
257
function createTooltipContent (node ) {
@@ -391,22 +305,6 @@ function hover(node) {
391
305
}
392
306
}
393
307
394
- function move (direction ) {
395
- if (direction === " right" ) {
396
- zoomReference .value .polygonPath .coordinates [0 ].x += zoomReference .value .circleRadius ;
397
- }
398
- if (direction === " left" ) {
399
- zoomReference .value .polygonPath .coordinates [0 ].x -= zoomReference .value .circleRadius ;
400
- }
401
- if (direction === " top" ) {
402
- zoomReference .value .polygonPath .coordinates [0 ].y -= zoomReference .value .circleRadius ;
403
- }
404
- if (direction === " bottom" ) {
405
- zoomReference .value .polygonPath .coordinates [0 ].y += zoomReference .value .circleRadius ;
406
- }
407
- zoomOnNode (zoomReference .value );
408
- }
409
-
410
308
function convertDatasetToCSVFormat (dataset ) {
411
309
const flattenedData = [];
412
310
@@ -521,6 +419,17 @@ function toggleAnnotator() {
521
419
isAnnotator .value = ! isAnnotator .value ;
522
420
}
523
421
422
+ const { viewBox } = usePanZoom (svgRef, {
423
+ x: 0 ,
424
+ y: 0 ,
425
+ width: svg .value .width <= 0 ? 10 : svg .value .width ,
426
+ height: svg .value .height <= 0 ? 10 : svg .value .height ,
427
+ }, FINAL_CONFIG .value .style .chart .zoom .speed )
428
+
429
+ function selectNode (node ) {
430
+ emit (' selectNode' , node)
431
+ }
432
+
524
433
defineExpose ({
525
434
getData,
526
435
generatePdf,
@@ -641,10 +550,9 @@ defineExpose({
641
550
:xmlns =" XMLNS"
642
551
v-if =" isDataset"
643
552
data-cy =" cluster-svg"
644
- :viewBox =" dynamicViewBox "
553
+ :viewBox =" `${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}` "
645
554
:class =" { 'vue-data-ui-fullscreen--on': isFullscreen, 'vue-data-ui-fulscreen--off': !isFullscreen }"
646
555
:style =" `overflow: hidden; background:transparent;color:${FINAL_CONFIG.style.chart.color}`"
647
- @click.stop =" unzoom($event)"
648
556
>
649
557
<PackageVersion />
650
558
@@ -681,9 +589,13 @@ defineExpose({
681
589
:hoveredUid =" hoveredUid"
682
590
:stroke =" FINAL_CONFIG.style.chart.nodes.stroke"
683
591
:strokeHovered =" FINAL_CONFIG.style.chart.nodes.strokeHovered"
684
- @zoom = " zoom "
592
+ @click = " selectNode "
685
593
@hover =" hover"
686
- />
594
+ >
595
+ <template #node =" { node } " >
596
+ <slot name =" node" v-bind =" { node }" />
597
+ </template >
598
+ </RecursiveCircles >
687
599
<RecursiveLabels
688
600
v-if =" mutableConfig.showDataLabels"
689
601
:dataset =" convertedDataset"
@@ -693,18 +605,6 @@ defineExpose({
693
605
<slot name =" svg" :svg =" svg" />
694
606
</svg >
695
607
696
- <BaseDirectionPad
697
- v-if =" isZoom"
698
- :key =" `direction_pad_${step}`"
699
- :color =" FINAL_CONFIG.style.chart.color"
700
- :isFullscreen =" isFullscreen"
701
- @moveLeft =" move('left')"
702
- @moveRight =" move('right')"
703
- @moveTop =" move('top')"
704
- @moveBottom =" move('bottom')"
705
- @reset =" restoreViewBox(); isZoom = false"
706
- />
707
-
708
608
<div v-if =" $slots.watermark" class =" vue-data-ui-watermark" >
709
609
<slot name =" watermark" v-bind =" { isPrinting: isPrinting || isImaging }" />
710
610
</div >
0 commit comments