11<script setup lang="ts">
22import { createPopper , type NanoPop } from " nanopop" ;
3- import { computed , type ComputedRef , onMounted , type Reactive , reactive , type Ref , ref } from " vue" ;
3+ import {
4+ computed ,
5+ type ComputedRef ,
6+ onMounted ,
7+ type Reactive ,
8+ reactive ,
9+ type Ref ,
10+ ref ,
11+ onUnmounted
12+ } from " vue" ;
413import type { ITourStep } from " ../Types.ts" ;
514import jump from " jump.js" ;
615
716export interface IVTourProps {
8- name? : string ,
17+ name? : string ;
918 steps: ITourStep [];
1019 backdrop? : boolean ;
1120 autoStart? : boolean ;
@@ -18,10 +27,11 @@ export interface IVTourProps {
1827 done: string ;
1928 skip: string ;
2029 };
21- saveToLocalStorage? : ' never' | ' step' | ' end' ;
30+ saveToLocalStorage? : " never" | " step" | " end" ;
2231 hideSkip? : boolean ;
2332 hideArrow? : boolean ;
2433 noScroll? : boolean ;
34+ resizeTimeout? : number ;
2535}
2636export interface IVTourData {
2737 currentStep: number ;
@@ -44,62 +54,81 @@ const _CurrentStep: Reactive<IVTourData> = reactive({
4454});
4555const props = defineProps <IVTourProps >();
4656const emit = defineEmits <{
47- onTourStart: [],
48- onTourEnd: [],
49- onTourStep: []
50- }>()
57+ onTourStart: [];
58+ onTourEnd: [];
59+ onTourStep: [];
60+ }>();
5161defineExpose ({
5262 startTour ,
5363 nextStep ,
5464 lastStep ,
5565 endTour ,
5666 stopTour ,
5767 goToStep ,
58- resetTour
68+ resetTour ,
69+
70+ updatePosition ,
71+ updateHighlight ,
72+ updateBackdrop ,
5973});
6074const getNextLabel: ComputedRef <String > = computed (() => {
61- if (_CurrentStep .currentStep === props .steps .length - 1 ) return props .buttonLabels ?.done || ' Done' ;
62- return props .buttonLabels ?.next || ' Next' ;
75+ if (_CurrentStep .currentStep === props .steps .length - 1 )
76+ return props .buttonLabels ?.done || " Done" ;
77+ return props .buttonLabels ?.next || " Next" ;
6378});
6479
65- const getClipPath = ref (getClipPathValues (' .vjt-highlight' ) ? getClipPathValues (' .vjt-highlight' ) : ' ' );
80+ const getClipPath = ref (
81+ getClipPathValues (" .vjt-highlight" ) ? getClipPathValues (" .vjt-highlight" ) : " "
82+ );
6683
6784function startTour(): void {
68- if (localStorage .getItem (' vjt-' + (props .name || ' default' )) === ' true' ) return ;
69- if (props .saveToLocalStorage === ' step' ) {
70- _CurrentStep .currentStep = parseInt (localStorage .getItem (' vjt-' + (props .name || ' default' )) || ' 0' );
85+ if (localStorage .getItem (" vjt-" + (props .name || " default" )) === " true" )
86+ return ;
87+ if (props .saveToLocalStorage === " step" ) {
88+ _CurrentStep .currentStep = parseInt (
89+ localStorage .getItem (" vjt-" + (props .name || " default" )) || " 0"
90+ );
7191 if (_CurrentStep .currentStep > 0 ) {
7292 _CurrentStep .lastStep = Math .max (_CurrentStep .currentStep - 1 , 0 );
7393 _CurrentStep .nextStep = _CurrentStep .currentStep + 1 ;
7494 }
75- }
76- else _CurrentStep .currentStep = 0 ;
95+ } else _CurrentStep .currentStep = 0 ;
7796
7897 setTimeout (async () => {
7998 await beforeStep (_CurrentStep .currentStep );
8099 if (! _VTour .value ) {
81- _VTour .value = createPopper (document .querySelector (` ${_CurrentStep .getCurrentStep .target } ` ) as HTMLElement , _Tooltip .value ! , {
82- position: _CurrentStep .getCurrentStep .placement || ' right' ,
83- margin: props .margin || ((props .highlight || _CurrentStep .getCurrentStep .highlight ) ? 14 : 8 ),
84- });
100+ _VTour .value = createPopper (
101+ document .querySelector (
102+ ` ${_CurrentStep .getCurrentStep .target } `
103+ ) as HTMLElement ,
104+ _Tooltip .value ! ,
105+ {
106+ position: _CurrentStep .getCurrentStep .placement || " right" ,
107+ margin:
108+ props .margin ||
109+ (props .highlight || _CurrentStep .getCurrentStep .highlight ? 14 : 8 ),
110+ }
111+ );
85112 }
86113 updatePosition ();
87114 emit (" onTourStart" );
88115 }, props .startDelay );
89116}
90117
91118function stopTour(): void {
92- document .querySelector (' #vjt-backdrop' )! .setAttribute (' data-hidden' , ' ' );
93- document .querySelectorAll (' .vjt-highlight' )! .forEach ((element ) => element .classList .remove (' vjt-highlight' ));
94- _Tooltip .value ! .setAttribute (' data-hidden' , ' ' );
119+ document .querySelector (" #vjt-backdrop" )! .setAttribute (" data-hidden" , " " );
120+ document
121+ .querySelectorAll (" .vjt-highlight" )!
122+ .forEach ((element ) => element .classList .remove (" vjt-highlight" ));
123+ _Tooltip .value ! .setAttribute (" data-hidden" , " " );
95124}
96125
97126function resetTour(restart : boolean ): void {
98127 stopTour ();
99128 _CurrentStep .currentStep = 0 ;
100129 _CurrentStep .lastStep = 0 ;
101130 _CurrentStep .nextStep = 1 ;
102- localStorage .removeItem (' vjt-' + (props .name || ' default' ));
131+ localStorage .removeItem (" vjt-" + (props .name || " default" ));
103132 if (restart ) startTour ();
104133}
105134
@@ -132,7 +161,8 @@ async function lastStep() {
132161
133162function endTour(): void {
134163 stopTour ();
135- if (props .saveToLocalStorage !== ' never' ) localStorage .setItem (' vjt-' + (props .name || ' default' ), ' true' );
164+ if (props .saveToLocalStorage !== " never" )
165+ localStorage .setItem (" vjt-" + (props .name || " default" ), " true" );
136166 emit (" onTourEnd" );
137167}
138168
@@ -152,45 +182,87 @@ async function updatePosition(): Promise<void> {
152182 await new Promise <void >((resolve ) => {
153183 updateHighlight ();
154184 updateBackdrop ();
155- _Tooltip .value ! .setAttribute (' data-hidden' , ' ' );
185+ _Tooltip .value ! .setAttribute (" data-hidden" , " " );
156186 if (! props .noScroll && ! _CurrentStep .getCurrentStep .noScroll ) {
157- jump (document .querySelector (` ${_CurrentStep .getCurrentStep .target } ` ) as HTMLElement , {
158- duration: 500 ,
159- offset: - 100 ,
160- callback : () => { resolve () },
161- });
187+ jump (
188+ document .querySelector (
189+ ` ${_CurrentStep .getCurrentStep .target } `
190+ ) as HTMLElement ,
191+ {
192+ duration: 500 ,
193+ offset: - 100 ,
194+ callback : () => {
195+ resolve ();
196+ },
197+ }
198+ );
162199 } else resolve ();
163200 });
164- _Tooltip .value ! .removeAttribute (' data-hidden' );
165- _Tooltip .value ! .setAttribute (' data-arrow' , _VTour .value ! .update ({
166- reference: document .querySelector (` ${_CurrentStep .getCurrentStep .target } ` ) as HTMLElement ,
167- position: _CurrentStep .getCurrentStep .placement || ' right' ,
168- }) || ' right' );
169- if (props .saveToLocalStorage === ' step' ) localStorage .setItem (' vjt-' + (props .name || ' default' ), _CurrentStep .currentStep .toString ());
201+ _Tooltip .value ! .removeAttribute (" data-hidden" );
202+ _Tooltip .value ! .setAttribute (
203+ " data-arrow" ,
204+ _VTour .value ! .update ({
205+ reference: document .querySelector (
206+ ` ${_CurrentStep .getCurrentStep .target } `
207+ ) as HTMLElement ,
208+ position: _CurrentStep .getCurrentStep .placement || " right" ,
209+ }) || " right"
210+ );
211+ if (props .saveToLocalStorage === " step" )
212+ localStorage .setItem (
213+ " vjt-" + (props .name || " default" ),
214+ _CurrentStep .currentStep .toString ()
215+ );
170216 await _CurrentStep .getCurrentStep .onAfter ?.();
171217 emit (" onTourStep" );
172218}
173219
174220function updateHighlight(): void {
175- document .querySelectorAll (' .vjt-highlight' ).forEach ((element ) => element .classList .remove (' vjt-highlight' ));
221+ document
222+ .querySelectorAll (" .vjt-highlight" )
223+ .forEach ((element ) => element .classList .remove (" vjt-highlight" ));
176224 if (! props .highlight && ! _CurrentStep .getCurrentStep .highlight ) return ;
177- (document .querySelector (` ${_CurrentStep .getCurrentStep .target } ` ) as HTMLElement ).classList .add (' vjt-highlight' );
178- getClipPath .value = getClipPathValues (' .vjt-highlight' );
225+ (
226+ document .querySelector (
227+ ` ${_CurrentStep .getCurrentStep .target } `
228+ ) as HTMLElement
229+ ).classList .add (" vjt-highlight" );
230+ getClipPath .value = getClipPathValues (" .vjt-highlight" );
179231}
180232
181233function updateBackdrop(): void {
182- if (props .backdrop || _CurrentStep .getCurrentStep .backdrop ) document .querySelector (' #vjt-backdrop' )! .removeAttribute (' data-hidden' );
183- else document .querySelector (' #vjt-backdrop' )! .setAttribute (' data-hidden' , ' ' );
234+ if (props .backdrop || _CurrentStep .getCurrentStep .backdrop )
235+ document .querySelector (" #vjt-backdrop" )! .removeAttribute (" data-hidden" );
236+ else document .querySelector (" #vjt-backdrop" )! .setAttribute (" data-hidden" , " " );
237+ }
238+
239+ let resizeTimer: NodeJS .Timeout ;
240+ const debounceTime = computed (() => props .resizeTimeout || 250 );
241+
242+ const onResizeEnd = () => {
243+ updatePosition ();
244+
245+ clearTimeout (resizeTimer );
246+
247+ resizeTimer = setTimeout (() => {
248+ updatePosition ();
249+ }, debounceTime .value );
184250}
185251
186252onMounted (() => {
187- _Tooltip .value = document .querySelector (' #vjt-tooltip' ) as HTMLElement ;
253+ _Tooltip .value = document .querySelector (" #vjt-tooltip" ) as HTMLElement ;
188254 if (props .autoStart ) startTour ();
255+
256+ window .addEventListener (" resize" , onResizeEnd );
257+ });
258+
259+ onUnmounted (() => {
260+ window .removeEventListener (" resize" , onResizeEnd );
189261});
190262
191263function getClipPathValues(targetSelector : string ): string {
192264 const targetElement = document .querySelector (targetSelector ) as HTMLElement ;
193- if (! targetElement ) return ' ' ;
265+ if (! targetElement ) return " " ;
194266
195267 const rect = targetElement .getBoundingClientRect ();
196268 return ` polygon(
@@ -206,7 +278,6 @@ function getClipPathValues(targetSelector: string): string {
206278 100% 0%
207279 ) ` ;
208280}
209-
210281 </script >
211282
212283<template >
@@ -217,13 +288,11 @@ function getClipPathValues(targetSelector: string): string {
217288 </slot >
218289 <slot name =" actions" v-bind =" { lastStep, nextStep, endTour, _CurrentStep, getNextLabel, props }" >
219290 <div class =" vjt-actions" >
220- <button v-if =" _CurrentStep.lastStep < _CurrentStep.currentStep" type =" button" @click.prevent =" lastStep()"
221- v-text =" props.buttonLabels?.back || 'Back'" ></button >
222- <button v-if =" !props.hideSkip" type =" button" @click.prevent =" endTour()"
223- v-text =" props.buttonLabels?.skip || 'Skip'" ></button >
291+ <button v-if =" _CurrentStep.lastStep < _CurrentStep.currentStep" type =" button" @click.prevent =" lastStep()" v-text =" props.buttonLabels?.back || 'Back'" ></button >
292+ <button v-if =" !props.hideSkip" type =" button" @click.prevent =" endTour()" v-text =" props.buttonLabels?.skip || 'Skip'" ></button >
224293 <button type =" button" @click.prevent =" nextStep()" v-text =" getNextLabel" ></button >
225294 </div >
226295 </slot >
227296 <div id =" vjt-arrow" v-if =" !props.hideArrow" ></div >
228297 </div >
229- </template >
298+ </template >
0 commit comments