Skip to content

Commit ddff6fb

Browse files
committed
Improvement - VueUiDashboard - Various improvements (user options, pdf & png generation) #246
1 parent a29c591 commit ddff6fb

File tree

7 files changed

+260
-72
lines changed

7 files changed

+260
-72
lines changed

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import ArenaVueUiWorld from "../TestingArena/ArenaVueUiWorld.vue";
5757
import ArenaVueUiTable from "../TestingArena/ArenaVueUiTable.vue";
5858
import ArenaVueUiRidgeline from "../TestingArena/ArenaVueUiRidgeline.vue";
5959
import ArenaVueUiChord from "../TestingArena/ArenaVueUiChord.vue"
60+
import ArenaVueUiDashboard from "../TestingArena/ArenaVueUiDashboard.vue";
6061
6162
6263
/**
@@ -122,10 +123,11 @@ const mapping = ref({
122123
VueUiTable: markRaw(ArenaVueUiTable),
123124
VueUiRidgeline: markRaw(ArenaVueUiRidgeline),
124125
VueUiChord: markRaw(ArenaVueUiChord),
126+
VueUiDashboard: markRaw(ArenaVueUiDashboard)
125127
})
126128
127129
const options = computed(() => Object.keys(mapping.value));
128-
const selectedComponent = ref('VueUiWorld');
130+
const selectedComponent = ref('VueUiDashboard');
129131
130132
/**
131133
* Legacy testing arena where some non chart components can be tested

src/components/vue-ui-dashboard.vue

Lines changed: 163 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import pdf from '../pdf';
44
import { useNestedProp } from "../useNestedProp";
55
import { useConfig } from '../useConfig';
66
import { createUid } from '../lib';
7+
import BaseIcon from '../atoms/BaseIcon.vue';
8+
import { usePrinter } from '../usePrinter';
9+
import UserOptions from '../atoms/UserOptions.vue';
10+
import { useUserOptionState } from '../useUserOptionState';
11+
import PenAndPaper from '../atoms/PenAndPaper.vue';
712
813
const builtInComponents = {
914
VueDataUi : defineAsyncComponent(() => import("../components/vue-data-ui.vue")),
@@ -72,6 +77,10 @@ const builtInComponents = {
7277
7378
const { vue_ui_dashboard: DEFAULT_CONFIG } = useConfig();
7479
80+
const dashboardRef = ref(null);
81+
const userOptionsRef = ref(null);
82+
const svgRef = ref(null);
83+
7584
const props = defineProps({
7685
dataset: Array,
7786
config: Object
@@ -92,11 +101,25 @@ function toggleLock() {
92101
isLocked.value = !isLocked.value;
93102
}
94103
104+
watch(() => props.config, () => {
105+
isLocked.value = FINAL_CONFIG.value.locked;
106+
userOptionsVisible.value = !FINAL_CONFIG.value.userOptions.showOnChartHover;
107+
})
108+
95109
const gridSize = 1;
96-
const items = ref(props.dataset.map((item, i) => ({
97-
...item,
98-
index: i
99-
})));
110+
111+
function setItems() {
112+
return props.dataset.map((item, i) => ({
113+
...item,
114+
index: i
115+
}))
116+
}
117+
118+
const items = ref(setItems());
119+
120+
watch(() => props.dataset, () => {
121+
items.value = setItems();
122+
})
100123
101124
const resolvedItems = computed(() =>
102125
items.value.map(item => ({
@@ -114,7 +137,6 @@ const resizeStart = ref({ x: 0, y: 0 });
114137
const dashboardContainer = ref(null);
115138
const isDragOrResize = ref(false);
116139
const changeIndex = ref(null);
117-
const isPrinting = ref(false);
118140
const isPaused = ref(false);
119141
120142
function handleInteraction(event) {
@@ -131,15 +153,14 @@ function handleInteractionEnd(event) {
131153
}
132154
}
133155
134-
function generatePdf(){
135-
isPrinting.value = true;
136-
pdf({
137-
domElement: document.getElementById(`vue-ui-dashboard_${uid.value}`),
138-
fileName: 'vue-ui-dashboard'
139-
}).finally(() => {
140-
isPrinting.value = false;
141-
});
142-
}
156+
const { isPrinting, isImaging, generatePdf, generateImage } = usePrinter({
157+
elementId: `vue-ui-dashboard_${uid.value}`,
158+
fileName: FINAL_CONFIG.value.userOptions.print.filename || 'dashboard',
159+
options: {
160+
...FINAL_CONFIG.value.userOptions.print,
161+
aspectRatio: FINAL_CONFIG.value.style.board.aspectRatio
162+
},
163+
});
143164
144165
function startDrag (index) {
145166
if(isLocked.value) return;
@@ -190,31 +211,31 @@ function startResize(index, direction) {
190211
191212
function checkDirection(item, deltaX, deltaY) {
192213
if (resizing.value.direction.includes('top')) {
193-
const newHeight = item.height - (deltaY / dashboardContainer.value.offsetHeight) * 100;
194-
if (newHeight >= gridSize) {
195-
item.top += (deltaY / dashboardContainer.value.offsetHeight) * 100;
196-
item.height = newHeight;
197-
}
214+
const newHeight = item.height - (deltaY / dashboardContainer.value.offsetHeight) * 100;
215+
if (newHeight >= gridSize) {
216+
item.top += (deltaY / dashboardContainer.value.offsetHeight) * 100;
217+
item.height = newHeight;
198218
}
199-
if (resizing.value.direction.includes('bottom')) {
200-
const newHeight = item.height + (deltaY / dashboardContainer.value.offsetHeight) * 100;
201-
if (newHeight >= gridSize) {
202-
item.height = newHeight;
203-
}
219+
}
220+
if (resizing.value.direction.includes('bottom')) {
221+
const newHeight = item.height + (deltaY / dashboardContainer.value.offsetHeight) * 100;
222+
if (newHeight >= gridSize) {
223+
item.height = newHeight;
204224
}
205-
if (resizing.value.direction.includes('left')) {
206-
const newWidth = item.width - (deltaX / dashboardContainer.value.offsetWidth) * 100;
207-
if (newWidth >= gridSize) {
208-
item.left += (deltaX / dashboardContainer.value.offsetWidth) * 100;
209-
item.width = newWidth;
210-
}
225+
}
226+
if (resizing.value.direction.includes('left')) {
227+
const newWidth = item.width - (deltaX / dashboardContainer.value.offsetWidth) * 100;
228+
if (newWidth >= gridSize) {
229+
item.left += (deltaX / dashboardContainer.value.offsetWidth) * 100;
230+
item.width = newWidth;
211231
}
212-
if (resizing.value.direction.includes('right')) {
213-
const newWidth = item.width + (deltaX / dashboardContainer.value.offsetWidth) * 100;
214-
if (newWidth >= gridSize) {
215-
item.width = newWidth;
216-
}
232+
}
233+
if (resizing.value.direction.includes('right')) {
234+
const newWidth = item.width + (deltaX / dashboardContainer.value.offsetWidth) * 100;
235+
if (newWidth >= gridSize) {
236+
item.width = newWidth;
217237
}
238+
}
218239
}
219240
220241
function onMouseMove(event) {
@@ -362,6 +383,21 @@ function getItemsPositions() {
362383
return items.value;
363384
}
364385
386+
const { userOptionsVisible, setUserOptionsVisibility, keepUserOptionState } = useUserOptionState({ config: FINAL_CONFIG.value });
387+
388+
const isAnnotator = ref(false);
389+
function toggleAnnotator() {
390+
isAnnotator.value = !isAnnotator.value;
391+
}
392+
393+
function showOptions() {
394+
setUserOptionsVisibility(true);
395+
}
396+
397+
function hideOptions() {
398+
setUserOptionsVisibility(false);
399+
}
400+
365401
defineExpose({
366402
generatePdf,
367403
getItemsPositions,
@@ -371,25 +407,22 @@ defineExpose({
371407

372408
<template>
373409
<div
410+
:id="`vue-ui-dashboard_${uid}`"
374411
@mousedown="handleInteraction"
375412
@mouseup="handleInteractionEnd"
376413
@touchstart="handleInteraction"
377414
@touchend="handleInteractionEnd"
415+
@mouseenter="showOptions"
416+
@mouseleave="hideOptions"
417+
ref="dashboardRef"
418+
:style="{
419+
position: 'relative'
420+
}"
378421
>
379-
<div data-dom-to-png-ignore style="width: 100%; display:flex; justify-content: end;" v-if="FINAL_CONFIG.allowPrint">
380-
<button class="vue-ui-dashboard-button" @click="generatePdf" :disabled="isPrinting" style="margin-top:12px" :style="`color:${FINAL_CONFIG.style.board.color}`">
381-
<svg class="vue-ui-dashboard-print-icon" xmlns="http://www.w3.org/2000/svg" v-if="isPrinting" width="20" height="20" viewBox="0 0 24 24" stroke-width="1.5" :stroke="FINAL_CONFIG.style.board.color" fill="none" stroke-linecap="round" stroke-linejoin="round">
382-
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
383-
<path d="M18 16v.01" />
384-
<path d="M6 16v.01" />
385-
<path d="M12 5v.01" />
386-
<path d="M12 12v.01" />
387-
<path d="M12 1a4 4 0 0 1 2.001 7.464l.001 .072a3.998 3.998 0 0 1 1.987 3.758l.22 .128a3.978 3.978 0 0 1 1.591 -.417l.2 -.005a4 4 0 1 1 -3.994 3.77l-.28 -.16c-.522 .25 -1.108 .39 -1.726 .39c-.619 0 -1.205 -.14 -1.728 -.391l-.279 .16l.007 .231a4 4 0 1 1 -2.212 -3.579l.222 -.129a3.998 3.998 0 0 1 1.988 -3.756l.002 -.071a4 4 0 0 1 -1.995 -3.265l-.005 -.2a4 4 0 0 1 4 -4z" />
388-
</svg>
389-
<span v-else>PDF</span>
390-
</button>
391-
</div>
392-
<div class="vue-ui-dashboard-container" ref="dashboardContainer" :id="`vue-ui-dashboard_${uid}`" :style="`border:${borderBoard}; background:${boardColor}; aspect-ratio:${aspectRatio}`">
422+
<div
423+
class="vue-ui-dashboard-container"
424+
ref="dashboardContainer"
425+
:style="`outline:${borderBoard}; background:${boardColor}; aspect-ratio:${aspectRatio};${isAnnotator ? 'pointer-events:none' : ''}`">
393426
<div
394427
class="vue-ui-dashboard-grid-container"
395428
ref="container"
@@ -463,6 +496,72 @@ defineExpose({
463496
</div>
464497
</div>
465498
</div>
499+
500+
<svg
501+
:style="{
502+
width: '100%',
503+
height: '100%',
504+
pointerEvents: 'none',
505+
position: 'absolute',
506+
top: 0,
507+
left: 0,
508+
zIndex: 2
509+
}"
510+
ref="svgRef"
511+
/>
512+
513+
<PenAndPaper
514+
v-if="FINAL_CONFIG.userOptions.buttons.annotator && dashboardContainer && svgRef"
515+
:color="FINAL_CONFIG.style.board.color"
516+
:backgroundColor="FINAL_CONFIG.style.board.backgroundColor"
517+
:active="isAnnotator"
518+
:svgRef="svgRef"
519+
@close="toggleAnnotator"
520+
:style="{
521+
zIndex: 2
522+
}"
523+
/>
524+
525+
<UserOptions
526+
v-if="((FINAL_CONFIG.allowPrint /* deprecated, but still ok */) || FINAL_CONFIG.userOptions.show) && (FINAL_CONFIG.userOptions.buttons.pdf || FINAL_CONFIG.userOptions.buttons.img)"
527+
ref="userOptionsRef"
528+
:backgroundColor="FINAL_CONFIG.style.board.backgroundColor"
529+
:color="FINAL_CONFIG.style.board.color"
530+
:isPrinting="isPrinting"
531+
:isImaging="isImaging"
532+
:uid="uid"
533+
:position="FINAL_CONFIG.userOptions.position"
534+
:hasTooltip="false"
535+
:hasPdf="FINAL_CONFIG.userOptions.buttons.pdf"
536+
:hasImg="FINAL_CONFIG.userOptions.buttons.img"
537+
:hasXls="false"
538+
:hasTable="false"
539+
:hasLabel="false"
540+
:hasFullscreen="false"
541+
:chartElement="dashboardContainer"
542+
:callbacks="FINAL_CONFIG.userOptions.callbacks"
543+
:hasAnnotator="FINAL_CONFIG.userOptions.buttons.annotator"
544+
:isAnnotation="isAnnotator"
545+
:printScale="FINAL_CONFIG.userOptions.print.scale"
546+
:titles="{ ...FINAL_CONFIG.userOptions.buttonTitles }"
547+
@generatePdf="generatePdf"
548+
@generateImage="generateImage"
549+
@toggleAnnotator="toggleAnnotator"
550+
:style="{ visibility: keepUserOptionState ? userOptionsVisible ? 'visible' : 'hidden' : 'visible', zIndex: 2 }"
551+
>
552+
<template #menuIcon="{ isOpen, color }" v-if="$slots.menuIcon">
553+
<slot name="menuIcon" v-bind="{ isOpen, color }" />
554+
</template>
555+
<template #optionPdf v-if="$slots.optionPdf">
556+
<slot name="optionPdf" />
557+
</template>
558+
<template #optionImg v-if="$slots.optionImg">
559+
<slot name="optionImg" />
560+
</template>
561+
<template v-if="$slots.optionAnnotator" #optionAnnotator="{ toggleAnnotator, isAnnotator }">
562+
<slot name="optionAnnotator" v-bind="{ toggleAnnotator, isAnnotator }" />
563+
</template>
564+
</UserOptions>
466565
</div>
467566
</template>
468567

@@ -513,6 +612,7 @@ defineExpose({
513612
background: v-bind(handleColor);
514613
opacity: 0;
515614
transition: opacity 0.1s ease-in-out;
615+
z-index: 2;
516616
}
517617
518618
.vue-ui-dashboard-resize-handle:hover {
@@ -544,20 +644,29 @@ defineExpose({
544644
}
545645
546646
.vue-ui-dashboard-button {
547-
margin: 6px 0;
647+
all: unset;
648+
padding: 3px;
548649
border-radius: 3px;
549-
height: 30px;
550-
border: 1px solid #b9bfc4;
650+
height: auto;
651+
border: 1px solid transparent;
551652
background: inherit;
552653
display: flex;
553654
align-items:center;
554655
justify-content: center;
555-
font-family: inherit;
656+
width: fit-content;
657+
white-space: nowrap;
556658
cursor: pointer;
659+
position: relative;
660+
opacity: 0.9;
661+
transition: opacity 0.2s ease-in-out;
557662
}
558663
.vue-ui-dashboard-button:hover {
559-
background: rgba(0,0,0,0.05);
664+
opacity: 1;
665+
}
666+
.vue-ui-dashboard-button:focus-visible {
667+
outline: 1px solid #CCCCCC;
560668
}
669+
561670
.vue-ui-dashboard-print-icon {
562671
animation: smartspin 0.5s infinite linear;
563672
}

0 commit comments

Comments
 (0)