@elogroup-sereduc/portal-aluno-tour 1.0.1 → 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.1",
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",
@@ -65,9 +67,27 @@ export function Tour({
65
67
  return { top, left };
66
68
  }, []);
67
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
+
68
88
  // Destaca o elemento atual
69
89
  useEffect(() => {
70
- if (!enabled || currentStep < 0 || currentStep >= steps.length) {
90
+ if (!isVisible || currentStep < 0 || currentStep >= steps.length) {
71
91
  setHighlightedElement(null);
72
92
  setTooltipPosition(null);
73
93
  return;
@@ -91,7 +111,7 @@ export function Tour({
91
111
  const position = calculateTooltipPosition(element, step.position);
92
112
  setTooltipPosition(position);
93
113
  }, 300);
94
- }, [enabled, currentStep, steps, calculateTooltipPosition]);
114
+ }, [isVisible, currentStep, steps, calculateTooltipPosition]);
95
115
 
96
116
  // Adiciona overlay e highlight ao elemento
97
117
  useEffect(() => {
@@ -99,21 +119,38 @@ export function Tour({
99
119
  // Remove highlights anteriores
100
120
  document.querySelectorAll(".tour-highlight").forEach((el) => {
101
121
  el.classList.remove("tour-highlight");
122
+ (el as HTMLElement).style.zIndex = "";
102
123
  });
103
124
  return;
104
125
  }
105
126
 
106
- // Adiciona classe de highlight
127
+ // Adiciona classe de highlight e z-index alto
107
128
  highlightedElement.classList.add("tour-highlight");
129
+ const originalZIndex = highlightedElement.style.zIndex;
130
+ highlightedElement.style.zIndex = "10000";
108
131
 
109
132
  return () => {
110
133
  highlightedElement.classList.remove("tour-highlight");
134
+ highlightedElement.style.zIndex = originalZIndex;
111
135
  };
112
136
  }, [highlightedElement]);
113
137
 
114
- // Handler de teclado (ESC)
138
+ // Configura o tour quando necessário
139
+ useEffect(() => {
140
+ if (!isConfiguredRef.current || steps.length > 0) {
141
+ configureTour();
142
+ renderSteps();
143
+ }
144
+ }, [steps, configureTour, renderSteps]);
145
+
146
+ // Atualiza quando enabled muda
115
147
  useEffect(() => {
116
- if (!enabled || !exitOnEsc) return;
148
+ renderSteps();
149
+ }, [enabled, renderSteps]);
150
+
151
+ // Handler de teclado (ESC e setas)
152
+ useEffect(() => {
153
+ if (!isVisible || !exitOnEsc) return;
117
154
 
118
155
  const handleKeyDown = (e: KeyboardEvent) => {
119
156
  if (e.key === "Escape") {
@@ -127,7 +164,19 @@ export function Tour({
127
164
 
128
165
  window.addEventListener("keydown", handleKeyDown);
129
166
  return () => window.removeEventListener("keydown", handleKeyDown);
130
- }, [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]);
131
180
 
132
181
  const handleNext = useCallback(() => {
133
182
  if (currentStep < steps.length - 1) {
@@ -153,6 +202,7 @@ export function Tour({
153
202
  }, [onComplete]);
154
203
 
155
204
  const handleExit = useCallback(() => {
205
+ setIsVisible(false);
156
206
  setCurrentStep(initialStep);
157
207
  setHighlightedElement(null);
158
208
  setTooltipPosition(null);
@@ -168,7 +218,7 @@ export function Tour({
168
218
  [exitOnOverlayClick, handleExit]
169
219
  );
170
220
 
171
- if (!enabled) {
221
+ if (!isVisible) {
172
222
  return null;
173
223
  }
174
224
 
@@ -182,32 +232,43 @@ export function Tour({
182
232
  {/* Overlay escuro */}
183
233
  <div
184
234
  ref={overlayRef}
185
- className="fixed inset-0 bg-black/60 z-[9998]"
186
235
  onClick={handleOverlayClick}
187
- style={{ pointerEvents: exitOnOverlayClick ? "auto" : "none" }}
236
+ style={{
237
+ position: "fixed",
238
+ top: 0,
239
+ left: 0,
240
+ right: 0,
241
+ bottom: 0,
242
+ backgroundColor: "rgba(0, 0, 0, 0.6)",
243
+ zIndex: 9998,
244
+ pointerEvents: exitOnOverlayClick ? "auto" : "none",
245
+ }}
188
246
  />
189
247
 
190
248
  {/* Tooltip */}
191
249
  {tooltipPosition && currentStepData && highlightedElement && (
192
250
  <div
193
251
  ref={tooltipRef}
194
- className="fixed z-[9999] max-w-sm"
252
+ className="max-w-sm"
195
253
  style={{
254
+ position: "fixed",
196
255
  top: currentStepData.position === "bottom" ? `${tooltipPosition.top}px` : undefined,
197
256
  bottom: currentStepData.position === "top" ? `${window.innerHeight - tooltipPosition.top}px` : undefined,
198
- left: currentStepData.position === "left" || currentStepData.position === "right"
199
- ? `${tooltipPosition.left}px`
257
+ left: currentStepData.position === "left" || currentStepData.position === "right"
258
+ ? `${tooltipPosition.left}px`
200
259
  : `${tooltipPosition.left}px`,
201
- transform: currentStepData.position === "left" || currentStepData.position === "right"
202
- ? "translate(0, -50%)"
203
- : "translate(-50%, 0)",
260
+ transform:
261
+ currentStepData.position === "left" || currentStepData.position === "right"
262
+ ? "translate(0, -50%)"
263
+ : "translate(-50%, 0)",
264
+ zIndex: 10001,
204
265
  }}
205
266
  >
206
267
  <div className="bg-white rounded-lg shadow-xl p-6 relative">
207
268
  {/* Botão fechar */}
208
269
  <button
209
270
  onClick={handleExit}
210
- 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"
211
272
  aria-label="Fechar tour"
212
273
  >
213
274
  <HiXMark className="w-5 h-5 text-gray-500" />
@@ -336,14 +397,14 @@ export function Tour({
336
397
  {/* Estilos inline para highlight */}
337
398
  <style>{`
338
399
  .tour-highlight {
339
- position: relative;
340
- z-index: 9999 !important;
400
+ position: relative !important;
401
+ z-index: 10000 !important;
341
402
  outline: 3px solid var(--brand-primary, #0056b0) !important;
342
403
  outline-offset: 2px;
343
404
  border-radius: 4px;
405
+ box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.6) !important;
344
406
  }
345
407
  `}</style>
346
408
  </>
347
409
  );
348
410
  }
349
-