@@ -4,6 +4,11 @@ import pdf from '../pdf';
4
4
import { useNestedProp } from " ../useNestedProp" ;
5
5
import { useConfig } from ' ../useConfig' ;
6
6
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' ;
7
12
8
13
const builtInComponents = {
9
14
VueDataUi : defineAsyncComponent (() => import (" ../components/vue-data-ui.vue" )),
@@ -72,6 +77,10 @@ const builtInComponents = {
72
77
73
78
const { vue_ui_dashboard: DEFAULT_CONFIG } = useConfig ();
74
79
80
+ const dashboardRef = ref (null );
81
+ const userOptionsRef = ref (null );
82
+ const svgRef = ref (null );
83
+
75
84
const props = defineProps ({
76
85
dataset: Array ,
77
86
config: Object
@@ -92,11 +101,25 @@ function toggleLock() {
92
101
isLocked .value = ! isLocked .value ;
93
102
}
94
103
104
+ watch (() => props .config , () => {
105
+ isLocked .value = FINAL_CONFIG .value .locked ;
106
+ userOptionsVisible .value = ! FINAL_CONFIG .value .userOptions .showOnChartHover ;
107
+ })
108
+
95
109
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
+ })
100
123
101
124
const resolvedItems = computed (() =>
102
125
items .value .map (item => ({
@@ -114,7 +137,6 @@ const resizeStart = ref({ x: 0, y: 0 });
114
137
const dashboardContainer = ref (null );
115
138
const isDragOrResize = ref (false );
116
139
const changeIndex = ref (null );
117
- const isPrinting = ref (false );
118
140
const isPaused = ref (false );
119
141
120
142
function handleInteraction (event ) {
@@ -131,15 +153,14 @@ function handleInteractionEnd(event) {
131
153
}
132
154
}
133
155
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
+ });
143
164
144
165
function startDrag (index ) {
145
166
if (isLocked .value ) return ;
@@ -190,31 +211,31 @@ function startResize(index, direction) {
190
211
191
212
function checkDirection (item , deltaX , deltaY ) {
192
213
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;
198
218
}
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;
204
224
}
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;
211
231
}
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;
217
237
}
238
+ }
218
239
}
219
240
220
241
function onMouseMove (event ) {
@@ -362,6 +383,21 @@ function getItemsPositions() {
362
383
return items .value ;
363
384
}
364
385
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
+
365
401
defineExpose ({
366
402
generatePdf,
367
403
getItemsPositions,
@@ -371,25 +407,22 @@ defineExpose({
371
407
372
408
<template >
373
409
<div
410
+ :id =" `vue-ui-dashboard_${uid}`"
374
411
@mousedown =" handleInteraction"
375
412
@mouseup =" handleInteractionEnd"
376
413
@touchstart =" handleInteraction"
377
414
@touchend =" handleInteractionEnd"
415
+ @mouseenter =" showOptions"
416
+ @mouseleave =" hideOptions"
417
+ ref =" dashboardRef"
418
+ :style =" {
419
+ position: 'relative'
420
+ }"
378
421
>
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' : ''}`" >
393
426
<div
394
427
class =" vue-ui-dashboard-grid-container"
395
428
ref =" container"
@@ -463,6 +496,72 @@ defineExpose({
463
496
</div >
464
497
</div >
465
498
</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 >
466
565
</div >
467
566
</template >
468
567
@@ -513,6 +612,7 @@ defineExpose({
513
612
background : v-bind(handleColor);
514
613
opacity : 0 ;
515
614
transition : opacity 0.1s ease-in-out ;
615
+ z-index : 2 ;
516
616
}
517
617
518
618
.vue-ui-dashboard-resize-handle :hover {
@@ -544,20 +644,29 @@ defineExpose({
544
644
}
545
645
546
646
.vue-ui-dashboard-button {
547
- margin : 6px 0 ;
647
+ all : unset ;
648
+ padding : 3px ;
548
649
border-radius : 3px ;
549
- height : 30 px ;
550
- border : 1px solid #b9bfc4 ;
650
+ height : auto ;
651
+ border : 1px solid transparent ;
551
652
background : inherit ;
552
653
display : flex ;
553
654
align-items :center ;
554
655
justify-content : center ;
555
- font-family : inherit ;
656
+ width : fit-content ;
657
+ white-space : nowrap ;
556
658
cursor : pointer ;
659
+ position : relative ;
660
+ opacity : 0.9 ;
661
+ transition : opacity 0.2s ease-in-out ;
557
662
}
558
663
.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 ;
560
668
}
669
+
561
670
.vue-ui-dashboard-print-icon {
562
671
animation : smartspin 0.5s infinite linear ;
563
672
}
0 commit comments