@elogroup-sereduc/portal-aluno-tour 1.0.2 → 1.0.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Tour.d.ts","sourceRoot":"","sources":["../../src/components/Tour.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAY,MAAM,UAAU,CAAC;AAG/C;;GAEG;AACH,wBAAgB,IAAI,CAAC,EACnB,OAAO,EACP,KAAK,EACL,WAAe,EACf,OAAY,EACZ,MAAM,EACN,UAAU,GACX,EAAE,SAAS,kDA4UX"}
1
+ {"version":3,"file":"Tour.d.ts","sourceRoot":"","sources":["../../src/components/Tour.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAY,MAAM,UAAU,CAAC;AAG/C;;GAEG;AACH,wBAAgB,IAAI,CAAC,EACnB,OAAO,EACP,KAAK,EACL,WAAe,EACf,OAAY,EACZ,MAAM,EACN,UAAU,GACX,EAAE,SAAS,kDA0YX"}
@@ -7,10 +7,12 @@ import { HiXMark, HiChevronLeft, HiChevronRight } from "react-icons/hi2";
7
7
  */
8
8
  export function Tour({ enabled, steps, initialStep = 0, options = {}, onExit, onComplete, }) {
9
9
  const [currentStep, setCurrentStep] = useState(initialStep);
10
+ const [isVisible, setIsVisible] = useState(false);
10
11
  const [highlightedElement, setHighlightedElement] = useState(null);
11
12
  const [tooltipPosition, setTooltipPosition] = useState(null);
12
13
  const overlayRef = useRef(null);
13
14
  const tooltipRef = useRef(null);
15
+ const isConfiguredRef = useRef(false);
14
16
  const { nextLabel = "Próximo", prevLabel = "Anterior", skipLabel = "Pular", doneLabel = "Concluir", showProgress = true, showBullets = true, exitOnOverlayClick = false, exitOnEsc = true, } = options;
15
17
  // Calcula a posição da tooltip baseado no elemento destacado
16
18
  const calculateTooltipPosition = useCallback((element, position = "bottom") => {
@@ -42,9 +44,26 @@ export function Tour({ enabled, steps, initialStep = 0, options = {}, onExit, on
42
44
  }
43
45
  return { top, left };
44
46
  }, []);
47
+ // Configura o tour quando steps ou options mudam
48
+ const configureTour = useCallback(() => {
49
+ isConfiguredRef.current = true;
50
+ }, []);
51
+ // Renderiza os steps (mostra/esconde o tour)
52
+ const renderSteps = useCallback(() => {
53
+ if (enabled && steps.length > 0 && !isVisible) {
54
+ setIsVisible(true);
55
+ setCurrentStep(initialStep);
56
+ }
57
+ else if (!enabled && isVisible) {
58
+ setIsVisible(false);
59
+ setCurrentStep(initialStep);
60
+ setHighlightedElement(null);
61
+ setTooltipPosition(null);
62
+ }
63
+ }, [enabled, steps.length, isVisible, initialStep]);
45
64
  // Destaca o elemento atual
46
65
  useEffect(() => {
47
- if (!enabled || currentStep < 0 || currentStep >= steps.length) {
66
+ if (!isVisible || currentStep < 0 || currentStep >= steps.length) {
48
67
  setHighlightedElement(null);
49
68
  setTooltipPosition(null);
50
69
  return;
@@ -63,25 +82,40 @@ export function Tour({ enabled, steps, initialStep = 0, options = {}, onExit, on
63
82
  const position = calculateTooltipPosition(element, step.position);
64
83
  setTooltipPosition(position);
65
84
  }, 300);
66
- }, [enabled, currentStep, steps, calculateTooltipPosition]);
85
+ }, [isVisible, currentStep, steps, calculateTooltipPosition]);
67
86
  // Adiciona overlay e highlight ao elemento
68
87
  useEffect(() => {
69
88
  if (!highlightedElement) {
70
89
  // Remove highlights anteriores
71
90
  document.querySelectorAll(".tour-highlight").forEach((el) => {
72
91
  el.classList.remove("tour-highlight");
92
+ el.style.zIndex = "";
73
93
  });
74
94
  return;
75
95
  }
76
- // Adiciona classe de highlight
96
+ // Adiciona classe de highlight e z-index alto
77
97
  highlightedElement.classList.add("tour-highlight");
98
+ const originalZIndex = highlightedElement.style.zIndex;
99
+ highlightedElement.style.zIndex = "10000";
78
100
  return () => {
79
101
  highlightedElement.classList.remove("tour-highlight");
102
+ highlightedElement.style.zIndex = originalZIndex;
80
103
  };
81
104
  }, [highlightedElement]);
82
- // Handler de teclado (ESC)
105
+ // Configura o tour quando necessário
106
+ useEffect(() => {
107
+ if (!isConfiguredRef.current || steps.length > 0) {
108
+ configureTour();
109
+ renderSteps();
110
+ }
111
+ }, [steps, configureTour, renderSteps]);
112
+ // Atualiza quando enabled muda
83
113
  useEffect(() => {
84
- if (!enabled || !exitOnEsc)
114
+ renderSteps();
115
+ }, [enabled, renderSteps]);
116
+ // Handler de teclado (ESC e setas)
117
+ useEffect(() => {
118
+ if (!isVisible || !exitOnEsc)
85
119
  return;
86
120
  const handleKeyDown = (e) => {
87
121
  if (e.key === "Escape") {
@@ -96,7 +130,19 @@ export function Tour({ enabled, steps, initialStep = 0, options = {}, onExit, on
96
130
  };
97
131
  window.addEventListener("keydown", handleKeyDown);
98
132
  return () => window.removeEventListener("keydown", handleKeyDown);
99
- }, [enabled, exitOnEsc, currentStep, steps.length]);
133
+ }, [isVisible, exitOnEsc, currentStep, steps.length]);
134
+ // Controla overflow do body
135
+ useEffect(() => {
136
+ if (isVisible) {
137
+ document.body.style.overflow = "hidden";
138
+ }
139
+ else {
140
+ document.body.style.overflow = "";
141
+ }
142
+ return () => {
143
+ document.body.style.overflow = "";
144
+ };
145
+ }, [isVisible]);
100
146
  const handleNext = useCallback(() => {
101
147
  if (currentStep < steps.length - 1) {
102
148
  setCurrentStep(currentStep + 1);
@@ -118,6 +164,7 @@ export function Tour({ enabled, steps, initialStep = 0, options = {}, onExit, on
118
164
  handleExit();
119
165
  }, [onComplete]);
120
166
  const handleExit = useCallback(() => {
167
+ setIsVisible(false);
121
168
  setCurrentStep(initialStep);
122
169
  setHighlightedElement(null);
123
170
  setTooltipPosition(null);
@@ -128,14 +175,24 @@ export function Tour({ enabled, steps, initialStep = 0, options = {}, onExit, on
128
175
  handleExit();
129
176
  }
130
177
  }, [exitOnOverlayClick, handleExit]);
131
- if (!enabled) {
178
+ if (!isVisible) {
132
179
  return null;
133
180
  }
134
181
  const currentStepData = steps[currentStep];
135
182
  const isFirstStep = currentStep === 0;
136
183
  const isLastStep = currentStep === steps.length - 1;
137
184
  const progress = ((currentStep + 1) / steps.length) * 100;
138
- return (_jsxs(_Fragment, { children: [_jsx("div", { ref: overlayRef, className: "fixed inset-0 bg-black/60 z-[9998]", onClick: handleOverlayClick, style: { pointerEvents: exitOnOverlayClick ? "auto" : "none" } }), tooltipPosition && currentStepData && highlightedElement && (_jsxs("div", { ref: tooltipRef, className: "fixed z-[9999] max-w-sm", style: {
185
+ return (_jsxs(_Fragment, { children: [_jsx("div", { ref: overlayRef, onClick: handleOverlayClick, style: {
186
+ position: "fixed",
187
+ top: 0,
188
+ left: 0,
189
+ right: 0,
190
+ bottom: 0,
191
+ backgroundColor: "rgba(0, 0, 0, 0.6)",
192
+ zIndex: 9998,
193
+ pointerEvents: exitOnOverlayClick ? "auto" : "none",
194
+ } }), tooltipPosition && currentStepData && highlightedElement && (_jsxs("div", { ref: tooltipRef, className: "max-w-sm", style: {
195
+ position: "fixed",
139
196
  top: currentStepData.position === "bottom" ? `${tooltipPosition.top}px` : undefined,
140
197
  bottom: currentStepData.position === "top" ? `${window.innerHeight - tooltipPosition.top}px` : undefined,
141
198
  left: currentStepData.position === "left" || currentStepData.position === "right"
@@ -144,7 +201,8 @@ export function Tour({ enabled, steps, initialStep = 0, options = {}, onExit, on
144
201
  transform: currentStepData.position === "left" || currentStepData.position === "right"
145
202
  ? "translate(0, -50%)"
146
203
  : "translate(-50%, 0)",
147
- }, children: [_jsxs("div", { className: "bg-white rounded-lg shadow-xl p-6 relative", children: [_jsx("button", { onClick: handleExit, className: "absolute top-2 right-2 p-1 rounded-full hover:bg-gray-100 transition-colors", "aria-label": "Fechar tour", children: _jsx(HiXMark, { className: "w-5 h-5 text-gray-500" }) }), currentStepData.title && (_jsx("h3", { className: "text-lg font-semibold text-gray-900 mb-2 pr-6", children: currentStepData.title })), _jsx("p", { className: "text-gray-700 mb-4", children: currentStepData.intro }), showProgress && (_jsxs("div", { className: "mb-4", children: [_jsx("div", { className: "w-full bg-gray-200 rounded-full h-2", children: _jsx("div", { className: "bg-brand-primary h-2 rounded-full transition-all duration-300", style: { width: `${progress}%` } }) }), _jsxs("p", { className: "text-xs text-gray-500 mt-1 text-center", children: [currentStep + 1, " de ", steps.length] })] })), showBullets && (_jsx("div", { className: "flex justify-center gap-1 mb-4", children: steps.map((_, index) => (_jsx("button", { onClick: () => setCurrentStep(index), className: `w-2 h-2 rounded-full transition-all ${index === currentStep
204
+ zIndex: 10001,
205
+ }, children: [_jsxs("div", { className: "bg-white rounded-lg shadow-xl p-6 relative", children: [_jsx("button", { onClick: handleExit, className: "absolute top-2 right-2 p-1 rounded-full hover:bg-gray-100 transition-colors z-10", "aria-label": "Fechar tour", children: _jsx(HiXMark, { className: "w-5 h-5 text-gray-500" }) }), currentStepData.title && (_jsx("h3", { className: "text-lg font-semibold text-gray-900 mb-2 pr-6", children: currentStepData.title })), _jsx("p", { className: "text-gray-700 mb-4", children: currentStepData.intro }), showProgress && (_jsxs("div", { className: "mb-4", children: [_jsx("div", { className: "w-full bg-gray-200 rounded-full h-2", children: _jsx("div", { className: "bg-brand-primary h-2 rounded-full transition-all duration-300", style: { width: `${progress}%` } }) }), _jsxs("p", { className: "text-xs text-gray-500 mt-1 text-center", children: [currentStep + 1, " de ", steps.length] })] })), showBullets && (_jsx("div", { className: "flex justify-center gap-1 mb-4", children: steps.map((_, index) => (_jsx("button", { onClick: () => setCurrentStep(index), className: `w-2 h-2 rounded-full transition-all ${index === currentStep
148
206
  ? "bg-brand-primary w-6"
149
207
  : "bg-gray-300 hover:bg-gray-400"}`, "aria-label": `Ir para passo ${index + 1}` }, index))) })), _jsxs("div", { className: "flex justify-between items-center gap-2", children: [_jsx("div", { className: "flex gap-2", children: !isFirstStep && (_jsx(Button, { variant: "bordered", onPress: handlePrev, startContent: _jsx(HiChevronLeft, { className: "w-4 h-4" }), children: prevLabel })) }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "light", onPress: handleSkip, children: skipLabel }), _jsx(Button, { color: "primary", onPress: isLastStep ? handleComplete : handleNext, endContent: !isLastStep ? _jsx(HiChevronRight, { className: "w-4 h-4" }) : undefined, children: isLastStep ? doneLabel : nextLabel })] })] })] }), currentStepData.position === "bottom" && (_jsx("div", { className: "absolute w-0 h-0 border-8 border-transparent", style: {
150
208
  top: "-16px",
@@ -168,11 +226,12 @@ export function Tour({ enabled, steps, initialStep = 0, options = {}, onExit, on
168
226
  borderColor: "transparent transparent transparent white",
169
227
  } }))] })), _jsx("style", { children: `
170
228
  .tour-highlight {
171
- position: relative;
172
- z-index: 9999 !important;
229
+ position: relative !important;
230
+ z-index: 10000 !important;
173
231
  outline: 3px solid var(--brand-primary, #0056b0) !important;
174
232
  outline-offset: 2px;
175
233
  border-radius: 4px;
234
+ box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.6) !important;
176
235
  }
177
236
  ` })] }));
178
237
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elogroup-sereduc/portal-aluno-tour",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Componente de tour guiado customizado usando HeroUI para o Portal do Aluno",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,10 +15,12 @@ export function Tour({
15
15
  onComplete,
16
16
  }: TourProps) {
17
17
  const [currentStep, setCurrentStep] = useState(initialStep);
18
+ const [isVisible, setIsVisible] = useState(false);
18
19
  const [highlightedElement, setHighlightedElement] = useState<HTMLElement | null>(null);
19
20
  const [tooltipPosition, setTooltipPosition] = useState<{ top: number; left: number } | null>(null);
20
21
  const overlayRef = useRef<HTMLDivElement>(null);
21
22
  const tooltipRef = useRef<HTMLDivElement>(null);
23
+ const isConfiguredRef = useRef(false);
22
24
 
23
25
  const {
24
26
  nextLabel = "Próximo",
@@ -31,19 +33,6 @@ export function Tour({
31
33
  exitOnEsc = true,
32
34
  } = options;
33
35
 
34
- // Garante que o overlay seja renderizado quando enabled for true
35
- useEffect(() => {
36
- if (enabled) {
37
- // Força o body a não ter scroll quando o tour está ativo
38
- document.body.style.overflow = "hidden";
39
- } else {
40
- document.body.style.overflow = "";
41
- }
42
- return () => {
43
- document.body.style.overflow = "";
44
- };
45
- }, [enabled]);
46
-
47
36
  // Calcula a posição da tooltip baseado no elemento destacado
48
37
  const calculateTooltipPosition = useCallback((element: HTMLElement, position: string = "bottom") => {
49
38
  const rect = element.getBoundingClientRect();
@@ -78,9 +67,27 @@ export function Tour({
78
67
  return { top, left };
79
68
  }, []);
80
69
 
70
+ // Configura o tour quando steps ou options mudam
71
+ const configureTour = useCallback(() => {
72
+ isConfiguredRef.current = true;
73
+ }, []);
74
+
75
+ // Renderiza os steps (mostra/esconde o tour)
76
+ const renderSteps = useCallback(() => {
77
+ if (enabled && steps.length > 0 && !isVisible) {
78
+ setIsVisible(true);
79
+ setCurrentStep(initialStep);
80
+ } else if (!enabled && isVisible) {
81
+ setIsVisible(false);
82
+ setCurrentStep(initialStep);
83
+ setHighlightedElement(null);
84
+ setTooltipPosition(null);
85
+ }
86
+ }, [enabled, steps.length, isVisible, initialStep]);
87
+
81
88
  // Destaca o elemento atual
82
89
  useEffect(() => {
83
- if (!enabled || currentStep < 0 || currentStep >= steps.length) {
90
+ if (!isVisible || currentStep < 0 || currentStep >= steps.length) {
84
91
  setHighlightedElement(null);
85
92
  setTooltipPosition(null);
86
93
  return;
@@ -104,7 +111,7 @@ export function Tour({
104
111
  const position = calculateTooltipPosition(element, step.position);
105
112
  setTooltipPosition(position);
106
113
  }, 300);
107
- }, [enabled, currentStep, steps, calculateTooltipPosition]);
114
+ }, [isVisible, currentStep, steps, calculateTooltipPosition]);
108
115
 
109
116
  // Adiciona overlay e highlight ao elemento
110
117
  useEffect(() => {
@@ -112,6 +119,7 @@ export function Tour({
112
119
  // Remove highlights anteriores
113
120
  document.querySelectorAll(".tour-highlight").forEach((el) => {
114
121
  el.classList.remove("tour-highlight");
122
+ (el as HTMLElement).style.zIndex = "";
115
123
  });
116
124
  return;
117
125
  }
@@ -127,9 +135,22 @@ export function Tour({
127
135
  };
128
136
  }, [highlightedElement]);
129
137
 
130
- // Handler de teclado (ESC)
138
+ // Configura o tour quando necessário
131
139
  useEffect(() => {
132
- if (!enabled || !exitOnEsc) return;
140
+ if (!isConfiguredRef.current || steps.length > 0) {
141
+ configureTour();
142
+ renderSteps();
143
+ }
144
+ }, [steps, configureTour, renderSteps]);
145
+
146
+ // Atualiza quando enabled muda
147
+ useEffect(() => {
148
+ renderSteps();
149
+ }, [enabled, renderSteps]);
150
+
151
+ // Handler de teclado (ESC e setas)
152
+ useEffect(() => {
153
+ if (!isVisible || !exitOnEsc) return;
133
154
 
134
155
  const handleKeyDown = (e: KeyboardEvent) => {
135
156
  if (e.key === "Escape") {
@@ -143,7 +164,19 @@ export function Tour({
143
164
 
144
165
  window.addEventListener("keydown", handleKeyDown);
145
166
  return () => window.removeEventListener("keydown", handleKeyDown);
146
- }, [enabled, exitOnEsc, currentStep, steps.length]);
167
+ }, [isVisible, exitOnEsc, currentStep, steps.length]);
168
+
169
+ // Controla overflow do body
170
+ useEffect(() => {
171
+ if (isVisible) {
172
+ document.body.style.overflow = "hidden";
173
+ } else {
174
+ document.body.style.overflow = "";
175
+ }
176
+ return () => {
177
+ document.body.style.overflow = "";
178
+ };
179
+ }, [isVisible]);
147
180
 
148
181
  const handleNext = useCallback(() => {
149
182
  if (currentStep < steps.length - 1) {
@@ -169,6 +202,7 @@ export function Tour({
169
202
  }, [onComplete]);
170
203
 
171
204
  const handleExit = useCallback(() => {
205
+ setIsVisible(false);
172
206
  setCurrentStep(initialStep);
173
207
  setHighlightedElement(null);
174
208
  setTooltipPosition(null);
@@ -184,7 +218,7 @@ export function Tour({
184
218
  [exitOnOverlayClick, handleExit]
185
219
  );
186
220
 
187
- if (!enabled) {
221
+ if (!isVisible) {
188
222
  return null;
189
223
  }
190
224
 
@@ -195,11 +229,11 @@ export function Tour({
195
229
 
196
230
  return (
197
231
  <>
198
- {/* Overlay escuro - sempre visível quando tour está ativo */}
232
+ {/* Overlay escuro */}
199
233
  <div
200
234
  ref={overlayRef}
201
235
  onClick={handleOverlayClick}
202
- style={{
236
+ style={{
203
237
  position: "fixed",
204
238
  top: 0,
205
239
  left: 0,
@@ -220,12 +254,13 @@ export function Tour({
220
254
  position: "fixed",
221
255
  top: currentStepData.position === "bottom" ? `${tooltipPosition.top}px` : undefined,
222
256
  bottom: currentStepData.position === "top" ? `${window.innerHeight - tooltipPosition.top}px` : undefined,
223
- left: currentStepData.position === "left" || currentStepData.position === "right"
224
- ? `${tooltipPosition.left}px`
257
+ left: currentStepData.position === "left" || currentStepData.position === "right"
258
+ ? `${tooltipPosition.left}px`
225
259
  : `${tooltipPosition.left}px`,
226
- transform: currentStepData.position === "left" || currentStepData.position === "right"
227
- ? "translate(0, -50%)"
228
- : "translate(-50%, 0)",
260
+ transform:
261
+ currentStepData.position === "left" || currentStepData.position === "right"
262
+ ? "translate(0, -50%)"
263
+ : "translate(-50%, 0)",
229
264
  zIndex: 10001,
230
265
  }}
231
266
  >
@@ -233,7 +268,7 @@ export function Tour({
233
268
  {/* Botão fechar */}
234
269
  <button
235
270
  onClick={handleExit}
236
- className="absolute top-2 right-2 p-1 rounded-full hover:bg-gray-100 transition-colors"
271
+ className="absolute top-2 right-2 p-1 rounded-full hover:bg-gray-100 transition-colors z-10"
237
272
  aria-label="Fechar tour"
238
273
  >
239
274
  <HiXMark className="w-5 h-5 text-gray-500" />
@@ -373,4 +408,3 @@ export function Tour({
373
408
  </>
374
409
  );
375
410
  }
376
-