Skip to content

Commit 3d54eaa

Browse files
committed
Improvement - VueUiWordCloud & VueUiMolecule - Add zoom reset button
1 parent 550d01f commit 3d54eaa

File tree

4 files changed

+147
-8
lines changed

4 files changed

+147
-8
lines changed

TestingArena/ArenaVueUiWordCloud.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,9 @@ const step = ref(0)
156156
WATERMARK
157157
</div>
158158
</template>
159-
<template #reset-action="{ reset }">
159+
<!-- <template #reset-action="{ reset }">
160160
<button @click="reset()">REFRESH</button>
161-
</template>
161+
</template> -->
162162
</LocalVueUiWordCloud>
163163
</template>
164164

src/components/vue-ui-molecule.vue

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import PenAndPaper from "../atoms/PenAndPaper.vue";
3535
import { useUserOptionState } from "../useUserOptionState";
3636
import { useChartAccessibility } from "../useChartAccessibility";
3737
import usePanZoom from "../usePanZoom";
38+
import BaseIcon from "../atoms/BaseIcon.vue";
3839
3940
const { vue_ui_molecule: DEFAULT_CONFIG } = useConfig();
4041
@@ -445,7 +446,7 @@ function toggleAnnotator() {
445446
446447
const active = computed(() => !isAnnotator.value)
447448
448-
const { viewBox } = usePanZoom(svgRef, {
449+
const { viewBox, resetZoom, isZoom } = usePanZoom(svgRef, {
449450
x: 0,
450451
y: 0,
451452
width: svg.value.width <= 0 ? 10 : svg.value.width,
@@ -638,6 +639,22 @@ defineExpose({
638639
<slot name="watermark" v-bind="{ isPrinting: isPrinting || isImaging }"/>
639640
</div>
640641

642+
<div v-if="isZoom" data-html2canvas-ignore class="reset-wrapper">
643+
<slot name="reset-action" :reset="resetZoom">
644+
<button
645+
data-cy-reset
646+
tabindex="0"
647+
role="button"
648+
class="vue-data-ui-refresh-button"
649+
:style="{
650+
background: FINAL_CONFIG.style.chart.backgroundColor
651+
}"
652+
@click="resetZoom(true)">
653+
<BaseIcon name="refresh" :stroke="FINAL_CONFIG.style.chart.color" />
654+
</button>
655+
</slot>
656+
</div>
657+
641658
<Skeleton
642659
v-if="!isDataset"
643660
:config="{
@@ -723,4 +740,37 @@ defineExpose({
723740
user-select: none;
724741
position: relative;
725742
}
743+
744+
.reset-wrapper {
745+
display: flex;
746+
flex-direction: row;
747+
align-items: center;
748+
justify-content: center;
749+
padding: 0 24px;
750+
height: 40px;
751+
position: absolute;
752+
bottom: 12px;
753+
right: 0;
754+
}
755+
756+
.vue-data-ui-refresh-button {
757+
outline: none;
758+
border: none;
759+
background: transparent;
760+
height: 36px;
761+
width: 36px;
762+
display: flex;
763+
align-items: center;
764+
justify-content: center;
765+
border-radius: 50%;
766+
cursor: pointer;
767+
transition: transform 0.2s ease-in-out;
768+
transform-origin: center;
769+
&:focus {
770+
outline: 1px solid v-bind(slicerColor);
771+
}
772+
&:hover {
773+
transform: rotate(-90deg)
774+
}
775+
}
726776
</style>

src/components/vue-ui-word-cloud.vue

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { useUserOptionState } from '../useUserOptionState';
3030
import { useChartAccessibility } from '../useChartAccessibility';
3131
import usePanZoom from '../usePanZoom';
3232
import { positionWords } from '../wordcloud';
33+
import BaseIcon from '../atoms/BaseIcon.vue';
3334
3435
const { vue_ui_word_cloud: DEFAULT_CONFIG } = useConfig();
3536
@@ -118,7 +119,6 @@ watch(() => props.config, (_newCfg) => {
118119
prepareChart();
119120
titleStep.value += 1;
120121
tableStep.value += 1;
121-
refreshSlicer();
122122
123123
// Reset mutable config
124124
mutableConfig.value.showTable = FINAL_CONFIG.value.table.show;
@@ -334,7 +334,7 @@ function toggleAnnotator() {
334334
335335
const active = computed(() => !isAnnotator.value && FINAL_CONFIG.value.style.chart.zoom.show)
336336
337-
const { viewBox } = usePanZoom(svgRef, {
337+
const { viewBox, resetZoom, isZoom } = usePanZoom(svgRef, {
338338
x: 0,
339339
y: 0,
340340
width: svg.value.width <= 0 ? 10 : svg.value.width,
@@ -393,6 +393,7 @@ function useTooltip(word) {
393393
<template>
394394
<div class="vue-ui-word-cloud" ref="wordCloudChart" :id="`wordCloud_${uid}`"
395395
:style="`width: 100%; font-family:${FINAL_CONFIG.style.fontFamily};background:${FINAL_CONFIG.style.chart.backgroundColor};${FINAL_CONFIG.responsive ? 'height:100%' : ''}`" @mouseenter="() => setUserOptionsVisibility(true)" @mouseleave="() => setUserOptionsVisibility(false)">
396+
396397
<PenAndPaper
397398
v-if="FINAL_CONFIG.userOptions.buttons.annotator"
398399
:svgRef="svgRef"
@@ -522,6 +523,22 @@ function useTooltip(word) {
522523
<slot name="watermark" v-bind="{ isPrinting: isPrinting || isImaging }"/>
523524
</div>
524525

526+
<div v-if="isZoom" data-html2canvas-ignore class="reset-wrapper">
527+
<slot name="reset-action" :reset="resetZoom">
528+
<button
529+
data-cy-reset
530+
tabindex="0"
531+
role="button"
532+
class="vue-data-ui-refresh-button"
533+
:style="{
534+
background: FINAL_CONFIG.style.chart.backgroundColor
535+
}"
536+
@click="resetZoom(true)">
537+
<BaseIcon name="refresh" :stroke="FINAL_CONFIG.style.chart.color" />
538+
</button>
539+
</slot>
540+
</div>
541+
525542
<Tooltip
526543
:show="mutableConfig.showTooltip && isTooltip"
527544
:backgroundColor="FINAL_CONFIG.style.chart.tooltip.backgroundColor"
@@ -582,7 +599,7 @@ function useTooltip(word) {
582599
</div>
583600
</template>
584601

585-
<style scoped>
602+
<style scoped lang="scss">
586603
@import "../vue-data-ui.css";
587604
588605
.vue-ui-word-cloud * {
@@ -616,5 +633,36 @@ text.animated {
616633
opacity: 0.5 !important;
617634
}
618635
636+
.reset-wrapper {
637+
display: flex;
638+
flex-direction: row;
639+
align-items: center;
640+
justify-content: center;
641+
padding: 0 24px;
642+
height: 40px;
643+
position: absolute;
644+
bottom: 12px;
645+
right: 0;
646+
}
619647
648+
.vue-data-ui-refresh-button {
649+
outline: none;
650+
border: none;
651+
background: transparent;
652+
height: 36px;
653+
width: 36px;
654+
display: flex;
655+
align-items: center;
656+
justify-content: center;
657+
border-radius: 50%;
658+
cursor: pointer;
659+
transition: transform 0.2s ease-in-out;
660+
transform-origin: center;
661+
&:focus {
662+
outline: 1px solid v-bind(slicerColor);
663+
}
664+
&:hover {
665+
transform: rotate(-90deg)
666+
}
667+
}
620668
</style>

src/usePanZoom.js

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ref, onMounted, onUnmounted, watch, nextTick, watchEffect } from 'vue';
1+
import { ref, onMounted, onUnmounted, watch, nextTick, watchEffect, computed } from 'vue';
22

33
export default function usePanZoom(svgRef, initialViewBox = { x: 0, y: 0, width: 100, height: 100 }, speed = 1, activeRef) {
44
const viewBox = ref({ ...initialViewBox });
@@ -10,6 +10,13 @@ export default function usePanZoom(svgRef, initialViewBox = { x: 0, y: 0, width:
1010
const pinchStartViewBox = ref(null);
1111
const isPinching = ref(false);
1212

13+
const isZoom = computed(() => {
14+
return viewBox.value.x !== initialViewBox.x ||
15+
viewBox.value.y !== initialViewBox.y ||
16+
viewBox.value.width !== initialViewBox.width ||
17+
viewBox.value.height !== initialViewBox.height
18+
})
19+
1320
let velocity = { x: 0, y: 0 };
1421
let animationFrame = null;
1522
let zoomAnimationFrame = null;
@@ -70,6 +77,40 @@ export default function usePanZoom(svgRef, initialViewBox = { x: 0, y: 0, width:
7077
isPanning.value = false;
7178
};
7279

80+
function resetZoom(animated = false) {
81+
if (!animated) {
82+
viewBox.value = { ...initialViewBox };
83+
scale.value = 1;
84+
return;
85+
}
86+
87+
const startViewBox = { ...viewBox.value };
88+
const startScale = scale.value;
89+
const duration = 300;
90+
let start = null;
91+
92+
function animate(ts) {
93+
if (!start) start = ts;
94+
const progress = Math.min((ts - start) / duration, 1);
95+
96+
viewBox.value = {
97+
x: startViewBox.x + (initialViewBox.x - startViewBox.x) * progress,
98+
y: startViewBox.y + (initialViewBox.y - startViewBox.y) * progress,
99+
width: startViewBox.width + (initialViewBox.width - startViewBox.width) * progress,
100+
height: startViewBox.height + (initialViewBox.height - startViewBox.height) * progress,
101+
};
102+
scale.value = startScale + (1 - startScale) * progress;
103+
104+
if (progress < 1) {
105+
requestAnimationFrame(animate);
106+
} else {
107+
viewBox.value = { ...initialViewBox };
108+
scale.value = 1;
109+
}
110+
}
111+
requestAnimationFrame(animate);
112+
}
113+
73114
function zoom(event) {
74115
event.preventDefault();
75116
const zoomFactor = event.deltaY > 0 ? 0.9 : 1.1;
@@ -213,5 +254,5 @@ export default function usePanZoom(svgRef, initialViewBox = { x: 0, y: 0, width:
213254
};
214255
});
215256

216-
return { viewBox };
257+
return { viewBox, resetZoom, isZoom };
217258
}

0 commit comments

Comments
 (0)