@elogroup-sereduc/portal-aluno-tour 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -0
- package/package.json +35 -0
- package/src/components/Tour.tsx +349 -0
- package/src/index.ts +3 -0
- package/src/types/index.ts +110 -0
- package/tsconfig.json +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Portal Aluno Tour
|
|
2
|
+
|
|
3
|
+
Componente de tour guiado customizado usando HeroUI para o Portal do Aluno.
|
|
4
|
+
|
|
5
|
+
## Instalação
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @elogroup-sereduc/portal-aluno-tour
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Uso
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { Tour } from "@elogroup-sereduc/portal-aluno-tour";
|
|
15
|
+
import { useState } from "react";
|
|
16
|
+
|
|
17
|
+
function MyComponent() {
|
|
18
|
+
const [tourEnabled, setTourEnabled] = useState(false);
|
|
19
|
+
|
|
20
|
+
const steps = [
|
|
21
|
+
{
|
|
22
|
+
element: ".my-element",
|
|
23
|
+
intro: "Este é o primeiro passo do tour",
|
|
24
|
+
position: "bottom",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
element: ".another-element",
|
|
28
|
+
intro: "Este é o segundo passo",
|
|
29
|
+
position: "right",
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<>
|
|
35
|
+
<button onClick={() => setTourEnabled(true)}>Iniciar Tour</button>
|
|
36
|
+
<Tour
|
|
37
|
+
enabled={tourEnabled}
|
|
38
|
+
steps={steps}
|
|
39
|
+
onExit={() => setTourEnabled(false)}
|
|
40
|
+
options={{
|
|
41
|
+
nextLabel: "Próximo",
|
|
42
|
+
prevLabel: "Anterior",
|
|
43
|
+
skipLabel: "Pular",
|
|
44
|
+
doneLabel: "Concluir",
|
|
45
|
+
}}
|
|
46
|
+
/>
|
|
47
|
+
</>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Props
|
|
53
|
+
|
|
54
|
+
### TourProps
|
|
55
|
+
|
|
56
|
+
- `enabled: boolean` - Se o tour está ativo
|
|
57
|
+
- `steps: TourStep[]` - Array de passos do tour
|
|
58
|
+
- `initialStep?: number` - Passo inicial (padrão: 0)
|
|
59
|
+
- `options?: TourOptions` - Opções de configuração
|
|
60
|
+
- `onExit?: () => void` - Callback quando o tour é encerrado
|
|
61
|
+
- `onComplete?: () => void` - Callback quando o tour é concluído
|
|
62
|
+
|
|
63
|
+
### TourStep
|
|
64
|
+
|
|
65
|
+
- `element: string` - Seletor CSS do elemento
|
|
66
|
+
- `intro: string` - Texto explicativo
|
|
67
|
+
- `position?: "top" | "bottom" | "left" | "right"` - Posição da tooltip
|
|
68
|
+
- `title?: string` - Título do passo (opcional)
|
|
69
|
+
|
|
70
|
+
### TourOptions
|
|
71
|
+
|
|
72
|
+
- `nextLabel?: string` - Label do botão "Próximo"
|
|
73
|
+
- `prevLabel?: string` - Label do botão "Anterior"
|
|
74
|
+
- `skipLabel?: string` - Label do botão "Pular"
|
|
75
|
+
- `doneLabel?: string` - Label do botão "Concluir"
|
|
76
|
+
- `showProgress?: boolean` - Mostrar barra de progresso
|
|
77
|
+
- `showBullets?: boolean` - Mostrar bullets de navegação
|
|
78
|
+
- `exitOnOverlayClick?: boolean` - Permitir fechar clicando no overlay
|
|
79
|
+
- `exitOnEsc?: boolean` - Permitir fechar com ESC
|
|
80
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@elogroup-sereduc/portal-aluno-tour",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Componente de tour guiado customizado usando HeroUI para o Portal do Aluno",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "tsc --watch"
|
|
10
|
+
},
|
|
11
|
+
"peerDependencies": {
|
|
12
|
+
"react": "^18.0.0",
|
|
13
|
+
"react-dom": "^18.0.0",
|
|
14
|
+
"@heroui/button": "^2.0.0",
|
|
15
|
+
"@heroui/system": "^2.0.0",
|
|
16
|
+
"react-icons": "^5.0.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/react": "^18.0.0",
|
|
20
|
+
"@types/react-dom": "^18.0.0",
|
|
21
|
+
"typescript": "^5.0.0"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"tour",
|
|
25
|
+
"onboarding",
|
|
26
|
+
"heroui",
|
|
27
|
+
"react"
|
|
28
|
+
],
|
|
29
|
+
"author": "Leandro Passos - Elogroup",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { useEffect, useState, useCallback, useRef } from "react";
|
|
2
|
+
import { Button } from "@heroui/button";
|
|
3
|
+
import { TourProps, TourStep } from "../types";
|
|
4
|
+
import { HiX, HiChevronLeft, HiChevronRight } from "react-icons/hi2";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Componente de tour guiado customizado usando HeroUI
|
|
8
|
+
*/
|
|
9
|
+
export function Tour({
|
|
10
|
+
enabled,
|
|
11
|
+
steps,
|
|
12
|
+
initialStep = 0,
|
|
13
|
+
options = {},
|
|
14
|
+
onExit,
|
|
15
|
+
onComplete,
|
|
16
|
+
}: TourProps) {
|
|
17
|
+
const [currentStep, setCurrentStep] = useState(initialStep);
|
|
18
|
+
const [highlightedElement, setHighlightedElement] = useState<HTMLElement | null>(null);
|
|
19
|
+
const [tooltipPosition, setTooltipPosition] = useState<{ top: number; left: number } | null>(null);
|
|
20
|
+
const overlayRef = useRef<HTMLDivElement>(null);
|
|
21
|
+
const tooltipRef = useRef<HTMLDivElement>(null);
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
nextLabel = "Próximo",
|
|
25
|
+
prevLabel = "Anterior",
|
|
26
|
+
skipLabel = "Pular",
|
|
27
|
+
doneLabel = "Concluir",
|
|
28
|
+
showProgress = true,
|
|
29
|
+
showBullets = true,
|
|
30
|
+
exitOnOverlayClick = false,
|
|
31
|
+
exitOnEsc = true,
|
|
32
|
+
} = options;
|
|
33
|
+
|
|
34
|
+
// Calcula a posição da tooltip baseado no elemento destacado
|
|
35
|
+
const calculateTooltipPosition = useCallback((element: HTMLElement, position: string = "bottom") => {
|
|
36
|
+
const rect = element.getBoundingClientRect();
|
|
37
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
38
|
+
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
|
39
|
+
|
|
40
|
+
let top = 0;
|
|
41
|
+
let left = 0;
|
|
42
|
+
|
|
43
|
+
switch (position) {
|
|
44
|
+
case "top":
|
|
45
|
+
top = rect.top + scrollTop - 10;
|
|
46
|
+
left = rect.left + scrollLeft + rect.width / 2;
|
|
47
|
+
break;
|
|
48
|
+
case "bottom":
|
|
49
|
+
top = rect.bottom + scrollTop + 10;
|
|
50
|
+
left = rect.left + scrollLeft + rect.width / 2;
|
|
51
|
+
break;
|
|
52
|
+
case "left":
|
|
53
|
+
top = rect.top + scrollTop + rect.height / 2;
|
|
54
|
+
left = rect.left + scrollLeft - 10;
|
|
55
|
+
break;
|
|
56
|
+
case "right":
|
|
57
|
+
top = rect.top + scrollTop + rect.height / 2;
|
|
58
|
+
left = rect.right + scrollLeft + 10;
|
|
59
|
+
break;
|
|
60
|
+
default:
|
|
61
|
+
top = rect.bottom + scrollTop + 10;
|
|
62
|
+
left = rect.left + scrollLeft + rect.width / 2;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { top, left };
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
// Destaca o elemento atual
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!enabled || currentStep < 0 || currentStep >= steps.length) {
|
|
71
|
+
setHighlightedElement(null);
|
|
72
|
+
setTooltipPosition(null);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const step = steps[currentStep];
|
|
77
|
+
const element = document.querySelector(step.element) as HTMLElement;
|
|
78
|
+
|
|
79
|
+
if (!element) {
|
|
80
|
+
console.warn(`Elemento não encontrado: ${step.element}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
setHighlightedElement(element);
|
|
85
|
+
|
|
86
|
+
// Scroll para o elemento
|
|
87
|
+
element.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
88
|
+
|
|
89
|
+
// Calcula posição da tooltip após um pequeno delay para garantir que o scroll terminou
|
|
90
|
+
setTimeout(() => {
|
|
91
|
+
const position = calculateTooltipPosition(element, step.position);
|
|
92
|
+
setTooltipPosition(position);
|
|
93
|
+
}, 300);
|
|
94
|
+
}, [enabled, currentStep, steps, calculateTooltipPosition]);
|
|
95
|
+
|
|
96
|
+
// Adiciona overlay e highlight ao elemento
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (!highlightedElement) {
|
|
99
|
+
// Remove highlights anteriores
|
|
100
|
+
document.querySelectorAll(".tour-highlight").forEach((el) => {
|
|
101
|
+
el.classList.remove("tour-highlight");
|
|
102
|
+
});
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Adiciona classe de highlight
|
|
107
|
+
highlightedElement.classList.add("tour-highlight");
|
|
108
|
+
|
|
109
|
+
return () => {
|
|
110
|
+
highlightedElement.classList.remove("tour-highlight");
|
|
111
|
+
};
|
|
112
|
+
}, [highlightedElement]);
|
|
113
|
+
|
|
114
|
+
// Handler de teclado (ESC)
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (!enabled || !exitOnEsc) return;
|
|
117
|
+
|
|
118
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
119
|
+
if (e.key === "Escape") {
|
|
120
|
+
handleExit();
|
|
121
|
+
} else if (e.key === "ArrowRight") {
|
|
122
|
+
handleNext();
|
|
123
|
+
} else if (e.key === "ArrowLeft") {
|
|
124
|
+
handlePrev();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
129
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
130
|
+
}, [enabled, exitOnEsc, currentStep, steps.length]);
|
|
131
|
+
|
|
132
|
+
const handleNext = useCallback(() => {
|
|
133
|
+
if (currentStep < steps.length - 1) {
|
|
134
|
+
setCurrentStep(currentStep + 1);
|
|
135
|
+
} else {
|
|
136
|
+
handleComplete();
|
|
137
|
+
}
|
|
138
|
+
}, [currentStep, steps.length]);
|
|
139
|
+
|
|
140
|
+
const handlePrev = useCallback(() => {
|
|
141
|
+
if (currentStep > 0) {
|
|
142
|
+
setCurrentStep(currentStep - 1);
|
|
143
|
+
}
|
|
144
|
+
}, [currentStep]);
|
|
145
|
+
|
|
146
|
+
const handleSkip = useCallback(() => {
|
|
147
|
+
handleExit();
|
|
148
|
+
}, []);
|
|
149
|
+
|
|
150
|
+
const handleComplete = useCallback(() => {
|
|
151
|
+
onComplete?.();
|
|
152
|
+
handleExit();
|
|
153
|
+
}, [onComplete]);
|
|
154
|
+
|
|
155
|
+
const handleExit = useCallback(() => {
|
|
156
|
+
setCurrentStep(initialStep);
|
|
157
|
+
setHighlightedElement(null);
|
|
158
|
+
setTooltipPosition(null);
|
|
159
|
+
onExit?.();
|
|
160
|
+
}, [initialStep, onExit]);
|
|
161
|
+
|
|
162
|
+
const handleOverlayClick = useCallback(
|
|
163
|
+
(e: React.MouseEvent<HTMLDivElement>) => {
|
|
164
|
+
if (exitOnOverlayClick && e.target === overlayRef.current) {
|
|
165
|
+
handleExit();
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
[exitOnOverlayClick, handleExit]
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
if (!enabled) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const currentStepData = steps[currentStep];
|
|
176
|
+
const isFirstStep = currentStep === 0;
|
|
177
|
+
const isLastStep = currentStep === steps.length - 1;
|
|
178
|
+
const progress = ((currentStep + 1) / steps.length) * 100;
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<>
|
|
182
|
+
{/* Overlay escuro */}
|
|
183
|
+
<div
|
|
184
|
+
ref={overlayRef}
|
|
185
|
+
className="fixed inset-0 bg-black/60 z-[9998]"
|
|
186
|
+
onClick={handleOverlayClick}
|
|
187
|
+
style={{ pointerEvents: exitOnOverlayClick ? "auto" : "none" }}
|
|
188
|
+
/>
|
|
189
|
+
|
|
190
|
+
{/* Tooltip */}
|
|
191
|
+
{tooltipPosition && currentStepData && highlightedElement && (
|
|
192
|
+
<div
|
|
193
|
+
ref={tooltipRef}
|
|
194
|
+
className="fixed z-[9999] max-w-sm"
|
|
195
|
+
style={{
|
|
196
|
+
top: currentStepData.position === "bottom" ? `${tooltipPosition.top}px` : undefined,
|
|
197
|
+
bottom: currentStepData.position === "top" ? `${window.innerHeight - tooltipPosition.top}px` : undefined,
|
|
198
|
+
left: currentStepData.position === "left" || currentStepData.position === "right"
|
|
199
|
+
? `${tooltipPosition.left}px`
|
|
200
|
+
: `${tooltipPosition.left}px`,
|
|
201
|
+
transform: currentStepData.position === "left" || currentStepData.position === "right"
|
|
202
|
+
? "translate(0, -50%)"
|
|
203
|
+
: "translate(-50%, 0)",
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
<div className="bg-white rounded-lg shadow-xl p-6 relative">
|
|
207
|
+
{/* Botão fechar */}
|
|
208
|
+
<button
|
|
209
|
+
onClick={handleExit}
|
|
210
|
+
className="absolute top-2 right-2 p-1 rounded-full hover:bg-gray-100 transition-colors"
|
|
211
|
+
aria-label="Fechar tour"
|
|
212
|
+
>
|
|
213
|
+
<HiX className="w-5 h-5 text-gray-500" />
|
|
214
|
+
</button>
|
|
215
|
+
|
|
216
|
+
{/* Título (se fornecido) */}
|
|
217
|
+
{currentStepData.title && (
|
|
218
|
+
<h3 className="text-lg font-semibold text-gray-900 mb-2 pr-6">
|
|
219
|
+
{currentStepData.title}
|
|
220
|
+
</h3>
|
|
221
|
+
)}
|
|
222
|
+
|
|
223
|
+
{/* Conteúdo */}
|
|
224
|
+
<p className="text-gray-700 mb-4">{currentStepData.intro}</p>
|
|
225
|
+
|
|
226
|
+
{/* Progresso */}
|
|
227
|
+
{showProgress && (
|
|
228
|
+
<div className="mb-4">
|
|
229
|
+
<div className="w-full bg-gray-200 rounded-full h-2">
|
|
230
|
+
<div
|
|
231
|
+
className="bg-brand-primary h-2 rounded-full transition-all duration-300"
|
|
232
|
+
style={{ width: `${progress}%` }}
|
|
233
|
+
/>
|
|
234
|
+
</div>
|
|
235
|
+
<p className="text-xs text-gray-500 mt-1 text-center">
|
|
236
|
+
{currentStep + 1} de {steps.length}
|
|
237
|
+
</p>
|
|
238
|
+
</div>
|
|
239
|
+
)}
|
|
240
|
+
|
|
241
|
+
{/* Bullets */}
|
|
242
|
+
{showBullets && (
|
|
243
|
+
<div className="flex justify-center gap-1 mb-4">
|
|
244
|
+
{steps.map((_, index) => (
|
|
245
|
+
<button
|
|
246
|
+
key={index}
|
|
247
|
+
onClick={() => setCurrentStep(index)}
|
|
248
|
+
className={`w-2 h-2 rounded-full transition-all ${
|
|
249
|
+
index === currentStep
|
|
250
|
+
? "bg-brand-primary w-6"
|
|
251
|
+
: "bg-gray-300 hover:bg-gray-400"
|
|
252
|
+
}`}
|
|
253
|
+
aria-label={`Ir para passo ${index + 1}`}
|
|
254
|
+
/>
|
|
255
|
+
))}
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
|
|
259
|
+
{/* Botões de navegação */}
|
|
260
|
+
<div className="flex justify-between items-center gap-2">
|
|
261
|
+
<div className="flex gap-2">
|
|
262
|
+
{!isFirstStep && (
|
|
263
|
+
<Button
|
|
264
|
+
variant="bordered"
|
|
265
|
+
onPress={handlePrev}
|
|
266
|
+
startContent={<HiChevronLeft className="w-4 h-4" />}
|
|
267
|
+
>
|
|
268
|
+
{prevLabel}
|
|
269
|
+
</Button>
|
|
270
|
+
)}
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
<div className="flex gap-2">
|
|
274
|
+
<Button variant="light" onPress={handleSkip}>
|
|
275
|
+
{skipLabel}
|
|
276
|
+
</Button>
|
|
277
|
+
<Button
|
|
278
|
+
color="primary"
|
|
279
|
+
onPress={isLastStep ? handleComplete : handleNext}
|
|
280
|
+
endContent={!isLastStep ? <HiChevronRight className="w-4 h-4" /> : undefined}
|
|
281
|
+
>
|
|
282
|
+
{isLastStep ? doneLabel : nextLabel}
|
|
283
|
+
</Button>
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
{/* Seta indicadora */}
|
|
289
|
+
{currentStepData.position === "bottom" && (
|
|
290
|
+
<div
|
|
291
|
+
className="absolute w-0 h-0 border-8 border-transparent"
|
|
292
|
+
style={{
|
|
293
|
+
top: "-16px",
|
|
294
|
+
left: "50%",
|
|
295
|
+
transform: "translateX(-50%)",
|
|
296
|
+
borderColor: "transparent transparent white transparent",
|
|
297
|
+
}}
|
|
298
|
+
/>
|
|
299
|
+
)}
|
|
300
|
+
{currentStepData.position === "top" && (
|
|
301
|
+
<div
|
|
302
|
+
className="absolute w-0 h-0 border-8 border-transparent"
|
|
303
|
+
style={{
|
|
304
|
+
bottom: "-16px",
|
|
305
|
+
left: "50%",
|
|
306
|
+
transform: "translateX(-50%)",
|
|
307
|
+
borderColor: "white transparent transparent transparent",
|
|
308
|
+
}}
|
|
309
|
+
/>
|
|
310
|
+
)}
|
|
311
|
+
{currentStepData.position === "right" && (
|
|
312
|
+
<div
|
|
313
|
+
className="absolute w-0 h-0 border-8 border-transparent"
|
|
314
|
+
style={{
|
|
315
|
+
left: "-16px",
|
|
316
|
+
top: "50%",
|
|
317
|
+
transform: "translateY(-50%)",
|
|
318
|
+
borderColor: "transparent white transparent transparent",
|
|
319
|
+
}}
|
|
320
|
+
/>
|
|
321
|
+
)}
|
|
322
|
+
{currentStepData.position === "left" && (
|
|
323
|
+
<div
|
|
324
|
+
className="absolute w-0 h-0 border-8 border-transparent"
|
|
325
|
+
style={{
|
|
326
|
+
right: "-16px",
|
|
327
|
+
top: "50%",
|
|
328
|
+
transform: "translateY(-50%)",
|
|
329
|
+
borderColor: "transparent transparent transparent white",
|
|
330
|
+
}}
|
|
331
|
+
/>
|
|
332
|
+
)}
|
|
333
|
+
</div>
|
|
334
|
+
)}
|
|
335
|
+
|
|
336
|
+
{/* Estilos inline para highlight */}
|
|
337
|
+
<style>{`
|
|
338
|
+
.tour-highlight {
|
|
339
|
+
position: relative;
|
|
340
|
+
z-index: 9999 !important;
|
|
341
|
+
outline: 3px solid var(--brand-primary, #0056b0) !important;
|
|
342
|
+
outline-offset: 2px;
|
|
343
|
+
border-radius: 4px;
|
|
344
|
+
}
|
|
345
|
+
`}</style>
|
|
346
|
+
</>
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tipos para o componente de tour
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface TourStep {
|
|
6
|
+
/**
|
|
7
|
+
* Seletor CSS do elemento a ser destacado
|
|
8
|
+
*/
|
|
9
|
+
element: string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Texto explicativo do passo
|
|
13
|
+
*/
|
|
14
|
+
intro: string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Posição da tooltip em relação ao elemento
|
|
18
|
+
* @default "bottom"
|
|
19
|
+
*/
|
|
20
|
+
position?: "top" | "bottom" | "left" | "right";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Título do passo (opcional)
|
|
24
|
+
*/
|
|
25
|
+
title?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TourOptions {
|
|
29
|
+
/**
|
|
30
|
+
* Label do botão "Próximo"
|
|
31
|
+
* @default "Próximo"
|
|
32
|
+
*/
|
|
33
|
+
nextLabel?: string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Label do botão "Anterior"
|
|
37
|
+
* @default "Anterior"
|
|
38
|
+
*/
|
|
39
|
+
prevLabel?: string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Label do botão "Pular"
|
|
43
|
+
* @default "Pular"
|
|
44
|
+
*/
|
|
45
|
+
skipLabel?: string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Label do botão "Concluir"
|
|
49
|
+
* @default "Concluir"
|
|
50
|
+
*/
|
|
51
|
+
doneLabel?: string;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Mostrar barra de progresso
|
|
55
|
+
* @default true
|
|
56
|
+
*/
|
|
57
|
+
showProgress?: boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Mostrar bullets de navegação
|
|
61
|
+
* @default true
|
|
62
|
+
*/
|
|
63
|
+
showBullets?: boolean;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Permitir fechar clicando no overlay
|
|
67
|
+
* @default false
|
|
68
|
+
*/
|
|
69
|
+
exitOnOverlayClick?: boolean;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Permitir fechar com ESC
|
|
73
|
+
* @default true
|
|
74
|
+
*/
|
|
75
|
+
exitOnEsc?: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface TourProps {
|
|
79
|
+
/**
|
|
80
|
+
* Se o tour está habilitado/ativo
|
|
81
|
+
*/
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Array de passos do tour
|
|
86
|
+
*/
|
|
87
|
+
steps: TourStep[];
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Passo inicial (índice)
|
|
91
|
+
* @default 0
|
|
92
|
+
*/
|
|
93
|
+
initialStep?: number;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Opções de configuração do tour
|
|
97
|
+
*/
|
|
98
|
+
options?: TourOptions;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Callback quando o tour é encerrado
|
|
102
|
+
*/
|
|
103
|
+
onExit?: () => void;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Callback quando o tour é concluído
|
|
107
|
+
*/
|
|
108
|
+
onComplete?: () => void;
|
|
109
|
+
}
|
|
110
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationMap": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"rootDir": "./src",
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"moduleResolution": "bundler",
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true,
|
|
18
|
+
"noEmit": false
|
|
19
|
+
},
|
|
20
|
+
"include": ["src/**/*"],
|
|
21
|
+
"exclude": ["node_modules", "dist"]
|
|
22
|
+
}
|
|
23
|
+
|