ninetwo-user-tracking 1.0.6 → 1.0.7

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 ADDED
@@ -0,0 +1,335 @@
1
+ Perfeito. Vamos evoluir o componente `TrackView` para suportar essa lógica de **"Read Confirmation"** (Confirmação de Leitura).
2
+
3
+ A lógica será:
4
+
5
+ 1. Quando o elemento entra na tela: Dispara o evento de **View** (imediato) e inicia um **Timer** de 5 segundos.
6
+ 2. Se o usuário sair da tela antes dos 5s: O timer é cancelado.
7
+ 3. Se o usuário ficar 5s: Dispara o evento `read_confirmation`.
8
+
9
+ ### 1. Código Atualizado: `src/components/TrackView.tsx`
10
+
11
+ Substitua o arquivo atual por este novo código. Adicionei uma prop opcional `readTime` (padrão 5000ms) caso você queira customizar esse tempo no futuro.
12
+
13
+ ```tsx
14
+ 'use client';
15
+
16
+ import React, { useEffect, useRef, useState } from 'react';
17
+ import { pushToDataLayer } from '../utils/gtm';
18
+
19
+ interface TrackViewProps {
20
+ children: React.ReactNode;
21
+ eventName: string;
22
+ category?: string;
23
+ label?: string;
24
+ threshold?: number;
25
+ readTime?: number; // Tempo em ms para confirmar leitura (Default: 5000)
26
+ }
27
+
28
+ export const TrackView: React.FC<TrackViewProps> = ({
29
+ children,
30
+ eventName,
31
+ category,
32
+ label,
33
+ threshold = 0.5,
34
+ readTime = 5000
35
+ }) => {
36
+ const ref = useRef<HTMLDivElement>(null);
37
+ const timerRef = useRef<NodeJS.Timeout | null>(null);
38
+
39
+ // Estados para garantir que dispare apenas uma vez por carregamento de página
40
+ const [hasTriggeredView, setHasTriggeredView] = useState(false);
41
+ const [hasTriggeredRead, setHasTriggeredRead] = useState(false);
42
+
43
+ useEffect(() => {
44
+ if (!ref.current) return;
45
+
46
+ // Se ambos já foram disparados, desliga o observer para economizar recurso
47
+ if (hasTriggeredView && hasTriggeredRead) return;
48
+
49
+ const observer = new IntersectionObserver(
50
+ ([entry]) => {
51
+ if (entry.isIntersecting) {
52
+ // 1. Disparo imediato de Visualização (VIEW)
53
+ if (!hasTriggeredView) {
54
+ pushToDataLayer({
55
+ event: eventName,
56
+ category,
57
+ label,
58
+ type: 'view',
59
+ });
60
+ setHasTriggeredView(true);
61
+ }
62
+
63
+ // 2. Inicia contagem para Confirmação de Leitura (READ CONFIRMATION)
64
+ if (!hasTriggeredRead && !timerRef.current) {
65
+ timerRef.current = setTimeout(() => {
66
+ pushToDataLayer({
67
+ event: `${eventName}`, // Sufixo solicitado
68
+ category,
69
+ label,
70
+ type: 'read_confirmation', // Tipo diferenciado
71
+ });
72
+ setHasTriggeredRead(true);
73
+ }, readTime);
74
+ }
75
+
76
+ } else {
77
+ // Se o usuário saiu da tela antes do tempo, cancela a confirmação
78
+ if (timerRef.current) {
79
+ clearTimeout(timerRef.current);
80
+ timerRef.current = null;
81
+ }
82
+ }
83
+ },
84
+ { threshold }
85
+ );
86
+
87
+ observer.observe(ref.current);
88
+
89
+ return () => {
90
+ observer.disconnect();
91
+ if (timerRef.current) clearTimeout(timerRef.current);
92
+ };
93
+ }, [hasTriggeredView, hasTriggeredRead, eventName, category, label, threshold, readTime]);
94
+
95
+ return <div ref={ref} style={{ display: 'contents' }}>{children}</div>;
96
+ };
97
+
98
+ ```
99
+
100
+ ---
101
+
102
+ ### 2. Atualizar o Build
103
+
104
+ Como alteramos a lógica do componente, não esqueça de rodar o build novamente na pasta do pacote:
105
+
106
+ ```bash
107
+ npm run build
108
+
109
+ ```
110
+
111
+ ---
112
+
113
+ ### 3. Documentação Atualizada (README.md)
114
+
115
+ Aqui está o **README.md** completo e atualizado com a nova funcionalidade de Confirmação de Leitura documentada.
116
+
117
+ Pode clicar em "Copy" e substituir o seu arquivo atual.
118
+
119
+ ```markdown
120
+ # NineTwo User Tracking
121
+
122
+ Pacote de abstração de Analytics para React e Next.js.
123
+ Facilita a implementação do **Google Tag Manager (GTM)** utilizando **Delegação de Eventos** para cliques (via atributos HTML) e **Intersection Observer** para visualizações e confirmação de leitura.
124
+
125
+ ## ✨ Funcionalidades
126
+
127
+ - 🚀 **Zero Boilerplate:** Rastreamento declarativo via atributos `data-nt-ut-*`.
128
+ - 🖱️ **Click Tracking Automático:** Listener global que captura cliques.
129
+ - 👁️ **View Tracking:** Dispara evento ao visualizar elemento.
130
+ - 📖 **Read Confirmation:** Dispara evento secundário automaticamente após 5s de visualização contínua.
131
+ - 💉 **GTM Injection:** Injeção segura do script do GTM.
132
+ - ⚡ **Next.js Ready:** Compatível com App Router (Providers Pattern).
133
+
134
+ ---
135
+
136
+ ## 📦 Instalação
137
+
138
+ ```bash
139
+ npm install ninetwo_user_tracking
140
+
141
+ ```
142
+
143
+ ---
144
+
145
+ ## 🚀 Configuração (Next.js 13+ App Router)
146
+
147
+ ### 1. Crie o componente `app/providers.tsx`
148
+
149
+ ```tsx
150
+ 'use client';
151
+
152
+ import { TrackingProvider } from 'ninetwo_user_tracking';
153
+
154
+ export function Providers({ children }: { children: React.ReactNode }) {
155
+ return (
156
+ <TrackingProvider
157
+ gtmId="GTM-SEU-ID-AQUI"
158
+ debug={process.env.NODE_ENV === 'development'}
159
+ >
160
+ {children}
161
+ </TrackingProvider>
162
+ );
163
+ }
164
+
165
+ ```
166
+
167
+ ### 2. Envolva o `app/layout.tsx`
168
+
169
+ ```tsx
170
+ import { Providers } from "./providers";
171
+ import "./globals.css";
172
+
173
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
174
+ return (
175
+ <html lang="pt-BR">
176
+ <body>
177
+ <Providers>{children}</Providers>
178
+ </body>
179
+ </html>
180
+ );
181
+ }
182
+
183
+ ```
184
+
185
+ ---
186
+
187
+ ## 🖱️ Rastreamento de Cliques (Click)
188
+
189
+ Adicione os atributos `data-nt-ut-*` ao elemento interativo.
190
+
191
+ ```tsx
192
+ <button
193
+ className="btn-primary"
194
+ data-nt-ut-event="add_to_cart"
195
+ data-nt-ut-category="ecommerce"
196
+ data-nt-ut-label="tenis_nike_v2"
197
+ data-nt-ut-type="click" // Opcional (default: click)
198
+ >
199
+ Comprar Agora
200
+ </button>
201
+
202
+ ```
203
+
204
+ ---
205
+
206
+ ## 👁️ Rastreamento de Visualização e Leitura (View/Read)
207
+
208
+ Use o componente `<TrackView>` para monitorar impressões.
209
+ **Novidade:** Se o usuário permanecer com o elemento visível por 5 segundos (padrão), um segundo evento `read_confirmation` será disparado.
210
+
211
+ ```tsx
212
+ import { TrackView } from 'ninetwo_user_tracking';
213
+
214
+ export default function BlogPost() {
215
+ return (
216
+ <TrackView
217
+ eventName="article_view"
218
+ category="blog"
219
+ label="como_aprender_react"
220
+ threshold={0.5} // 50% visível para disparar
221
+ readTime={5000} // (Opcional) Tempo em ms para confirmar leitura
222
+ >
223
+ <article>
224
+ <h1>Como aprender React</h1>
225
+ <p>Conteúdo do artigo...</p>
226
+ </article>
227
+ </TrackView>
228
+ );
229
+ }
230
+
231
+ ```
232
+
233
+ ### Comportamento dos Eventos
234
+
235
+ Neste exemplo acima, dois eventos serão enviados ao DataLayer:
236
+
237
+ 1. **Assim que aparecer:**
238
+ * event: `"article_view"`
239
+ * type: `"view"`
240
+
241
+
242
+ 2. **Após 5 segundos visível:**
243
+ * event: `"article_view"`
244
+ * type: `"read_confirmation"`
245
+
246
+
247
+ ---
248
+
249
+ Aqui está a documentação exclusiva para o rastreamento de **Submit de Formulários**, pronta para copiar e colar.
250
+
251
+ ---
252
+
253
+ ## 📝 Rastreamento de Formulários (Submit)
254
+
255
+ O pacote detecta automaticamente o envio de formulários através de **Event Delegation**.
256
+ Isso significa que você deve adicionar os atributos de rastreamento **diretamente na tag `<form>**`, e não no botão de enviar.
257
+
258
+ O evento será disparado tanto ao clicar no botão `type="submit"` quanto ao pressionar `Enter` dentro de um input.
259
+
260
+ ### Exemplo de Implementação
261
+
262
+ ```tsx
263
+ <form
264
+ action="/api/newsletter"
265
+ method="POST"
266
+ // Atributos de Tracking na tag FORM (Obrigatório)
267
+ data-nt-ut-event="newsletter_signup"
268
+ data-nt-ut-category="leads"
269
+ data-nt-ut-label="footer_form"
270
+ // data-nt-ut-type="submit" -> (Opcional: o padrão já é 'submit' para formulários)
271
+ >
272
+ <div className="flex gap-2">
273
+ <input
274
+ type="email"
275
+ name="email"
276
+ placeholder="Seu melhor e-mail"
277
+ className="border p-2"
278
+ />
279
+ <button type="submit" className="bg-blue-500 text-white p-2">
280
+ Inscrever-se
281
+ </button>
282
+ </div>
283
+ </form>
284
+
285
+ ```
286
+
287
+ ### O que acontece no DataLayer?
288
+
289
+ Quando o usuário envia este formulário, o seguinte objeto é enviado para o GTM:
290
+
291
+ ```javascript
292
+ {
293
+ event: "newsletter_signup", // Valor de data-nt-ut-event
294
+ event_category: "leads", // Valor de data-nt-ut-category
295
+ event_label: "footer_form", // Valor de data-nt-ut-label
296
+ event_type: "submit", // Automático para tags <form>
297
+ interaction_time: "2024-01-20T14:00:00.000Z"
298
+ }
299
+
300
+ ```
301
+
302
+ ---
303
+
304
+ ## ⚙️ Configuração no GTM
305
+
306
+ O pacote envia os dados para `window.dataLayer`.
307
+
308
+ ### Exemplo de Objeto Enviado (Read Confirmation)
309
+
310
+ ```javascript
311
+ {
312
+ event: "article_view",
313
+ event_category: "blog",
314
+ event_label: "como_aprender_react",
315
+ event_type: "read_confirmation",
316
+ interaction_time: "..."
317
+ }
318
+
319
+ ```
320
+
321
+ ### Configuração Recomendada
322
+
323
+ 1. **Variáveis:** Crie variáveis de DataLayer para `event_category`, `event_label` e `event_type`.
324
+ 2. **Trigger:** Use `.*` (Regex) em Evento Personalizado para capturar tudo.
325
+ 3. **Tag GA4:** Mapeie os parâmetros. No GA4, você poderá filtrar eventos onde `type` é igual a `read_confirmation` para medir engajamento real.
326
+
327
+ ---
328
+
329
+ ## License
330
+
331
+ ISC © NineTwo
332
+
333
+ ```
334
+
335
+ ```
package/dist/index.d.mts CHANGED
@@ -13,6 +13,7 @@ interface TrackViewProps {
13
13
  category?: string;
14
14
  label?: string;
15
15
  threshold?: number;
16
+ readTime?: number;
16
17
  }
17
18
  declare const TrackView: React.FC<TrackViewProps>;
18
19
 
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ interface TrackViewProps {
13
13
  category?: string;
14
14
  label?: string;
15
15
  threshold?: number;
16
+ readTime?: number;
16
17
  }
17
18
  declare const TrackView: React.FC<TrackViewProps>;
18
19
 
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ __export(src_exports, {
27
27
  module.exports = __toCommonJS(src_exports);
28
28
 
29
29
  // src/TrackingProvider.tsx
30
- var import_react2 = require("react");
30
+ var import_react3 = require("react");
31
31
 
32
32
  // src/hooks/useAutoTrackClick.ts
33
33
  var import_react = require("react");
@@ -79,6 +79,36 @@ var useAutoTrackClick = (enabled = true) => {
79
79
  }, [enabled]);
80
80
  };
81
81
 
82
+ // src/hooks/useAutoTrackSubmit.ts
83
+ var import_react2 = require("react");
84
+ var useAutoTrackSubmit = (enabled = true) => {
85
+ (0, import_react2.useEffect)(() => {
86
+ if (!enabled || typeof document === "undefined")
87
+ return;
88
+ const handleSubmit = (e) => {
89
+ const target = e.target;
90
+ const formElement = target.closest("form[data-nt-ut-event]");
91
+ if (formElement) {
92
+ const eventName = formElement.getAttribute("data-nt-ut-event");
93
+ const category = formElement.getAttribute("data-nt-ut-category");
94
+ const label = formElement.getAttribute("data-nt-ut-label");
95
+ const type = formElement.getAttribute("data-nt-ut-type");
96
+ pushToDataLayer({
97
+ event: eventName || "form_submit",
98
+ category: category || "form",
99
+ label: label || "",
100
+ type: type || "submit"
101
+ // Padrão agora é 'submit'
102
+ });
103
+ }
104
+ };
105
+ document.addEventListener("submit", handleSubmit, true);
106
+ return () => {
107
+ document.removeEventListener("submit", handleSubmit, true);
108
+ };
109
+ }, [enabled]);
110
+ };
111
+
82
112
  // src/TrackingProvider.tsx
83
113
  var import_jsx_runtime = require("react/jsx-runtime");
84
114
  var TrackingProvider = ({
@@ -87,7 +117,8 @@ var TrackingProvider = ({
87
117
  debug = false
88
118
  }) => {
89
119
  useAutoTrackClick(true);
90
- (0, import_react2.useEffect)(() => {
120
+ useAutoTrackSubmit(true);
121
+ (0, import_react3.useEffect)(() => {
91
122
  if (!gtmId || typeof window === "undefined") {
92
123
  if (debug && !gtmId)
93
124
  console.warn("[NineTwo Tracking] GTM ID n\xE3o fornecido.");
@@ -107,14 +138,14 @@ var TrackingProvider = ({
107
138
  const script = document.createElement("script");
108
139
  script.id = scriptId;
109
140
  script.async = true;
110
- script.src = `https://www.googletagmanager.com/gtag/js?id=${gtmId}`;
141
+ script.src = `https://www.googletagmanager.com/gtm.js?id=${gtmId}`;
111
142
  script.onload = () => {
112
143
  if (debug)
113
144
  console.log(`[NineTwo Tracking] \u2705 GTM carregado com sucesso! (${gtmId})`);
114
145
  };
115
146
  script.onerror = () => {
116
147
  if (debug)
117
- console.error("[NineTwo Tracking] \u274C Erro ao carregar GTM. Verifique AdBlockers.");
148
+ console.error("[NineTwo Tracking] \u274C Erro ao carregar GTM.");
118
149
  };
119
150
  document.head.appendChild(script);
120
151
  }, [gtmId, debug]);
@@ -122,37 +153,66 @@ var TrackingProvider = ({
122
153
  };
123
154
 
124
155
  // src/components/TrackView.tsx
125
- var import_react3 = require("react");
156
+ var import_react4 = require("react");
126
157
  var import_jsx_runtime2 = require("react/jsx-runtime");
127
158
  var TrackView = ({
128
159
  children,
129
160
  eventName,
130
161
  category,
131
162
  label,
132
- threshold = 0.5
163
+ threshold = 0.5,
164
+ readTime = 5e3
133
165
  }) => {
134
- const ref = (0, import_react3.useRef)(null);
135
- const [hasTriggered, setHasTriggered] = (0, import_react3.useState)(false);
136
- (0, import_react3.useEffect)(() => {
166
+ const ref = (0, import_react4.useRef)(null);
167
+ const timerRef = (0, import_react4.useRef)(null);
168
+ const [hasTriggeredView, setHasTriggeredView] = (0, import_react4.useState)(false);
169
+ const [hasTriggeredRead, setHasTriggeredRead] = (0, import_react4.useState)(false);
170
+ (0, import_react4.useEffect)(() => {
171
+ if (!ref.current)
172
+ return;
173
+ if (hasTriggeredView && hasTriggeredRead)
174
+ return;
137
175
  const observer = new IntersectionObserver(
138
176
  ([entry]) => {
139
- if (entry.isIntersecting && !hasTriggered) {
140
- pushToDataLayer({
141
- event: eventName,
142
- category,
143
- label,
144
- type: "view"
145
- });
146
- setHasTriggered(true);
147
- observer.disconnect();
177
+ if (entry.isIntersecting) {
178
+ if (!hasTriggeredView) {
179
+ pushToDataLayer({
180
+ event: eventName,
181
+ category,
182
+ label,
183
+ type: "view"
184
+ });
185
+ setHasTriggeredView(true);
186
+ }
187
+ if (!hasTriggeredRead && !timerRef.current) {
188
+ timerRef.current = setTimeout(() => {
189
+ pushToDataLayer({
190
+ event: `${eventName}_read_confirmation`,
191
+ // Sufixo solicitado
192
+ category,
193
+ label,
194
+ type: "read_confirmation"
195
+ // Tipo diferenciado
196
+ });
197
+ setHasTriggeredRead(true);
198
+ }, readTime);
199
+ }
200
+ } else {
201
+ if (timerRef.current) {
202
+ clearTimeout(timerRef.current);
203
+ timerRef.current = null;
204
+ }
148
205
  }
149
206
  },
150
207
  { threshold }
151
208
  );
152
- if (ref.current)
153
- observer.observe(ref.current);
154
- return () => observer.disconnect();
155
- }, [hasTriggered, eventName, category, label, threshold]);
209
+ observer.observe(ref.current);
210
+ return () => {
211
+ observer.disconnect();
212
+ if (timerRef.current)
213
+ clearTimeout(timerRef.current);
214
+ };
215
+ }, [hasTriggeredView, hasTriggeredRead, eventName, category, label, threshold, readTime]);
156
216
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref, style: { display: "contents" }, children });
157
217
  };
158
218
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/TrackingProvider.tsx
2
- import { useEffect as useEffect2 } from "react";
2
+ import { useEffect as useEffect3 } from "react";
3
3
 
4
4
  // src/hooks/useAutoTrackClick.ts
5
5
  import { useEffect } from "react";
@@ -51,6 +51,36 @@ var useAutoTrackClick = (enabled = true) => {
51
51
  }, [enabled]);
52
52
  };
53
53
 
54
+ // src/hooks/useAutoTrackSubmit.ts
55
+ import { useEffect as useEffect2 } from "react";
56
+ var useAutoTrackSubmit = (enabled = true) => {
57
+ useEffect2(() => {
58
+ if (!enabled || typeof document === "undefined")
59
+ return;
60
+ const handleSubmit = (e) => {
61
+ const target = e.target;
62
+ const formElement = target.closest("form[data-nt-ut-event]");
63
+ if (formElement) {
64
+ const eventName = formElement.getAttribute("data-nt-ut-event");
65
+ const category = formElement.getAttribute("data-nt-ut-category");
66
+ const label = formElement.getAttribute("data-nt-ut-label");
67
+ const type = formElement.getAttribute("data-nt-ut-type");
68
+ pushToDataLayer({
69
+ event: eventName || "form_submit",
70
+ category: category || "form",
71
+ label: label || "",
72
+ type: type || "submit"
73
+ // Padrão agora é 'submit'
74
+ });
75
+ }
76
+ };
77
+ document.addEventListener("submit", handleSubmit, true);
78
+ return () => {
79
+ document.removeEventListener("submit", handleSubmit, true);
80
+ };
81
+ }, [enabled]);
82
+ };
83
+
54
84
  // src/TrackingProvider.tsx
55
85
  import { Fragment, jsx } from "react/jsx-runtime";
56
86
  var TrackingProvider = ({
@@ -59,7 +89,8 @@ var TrackingProvider = ({
59
89
  debug = false
60
90
  }) => {
61
91
  useAutoTrackClick(true);
62
- useEffect2(() => {
92
+ useAutoTrackSubmit(true);
93
+ useEffect3(() => {
63
94
  if (!gtmId || typeof window === "undefined") {
64
95
  if (debug && !gtmId)
65
96
  console.warn("[NineTwo Tracking] GTM ID n\xE3o fornecido.");
@@ -79,14 +110,14 @@ var TrackingProvider = ({
79
110
  const script = document.createElement("script");
80
111
  script.id = scriptId;
81
112
  script.async = true;
82
- script.src = `https://www.googletagmanager.com/gtag/js?id=${gtmId}`;
113
+ script.src = `https://www.googletagmanager.com/gtm.js?id=${gtmId}`;
83
114
  script.onload = () => {
84
115
  if (debug)
85
116
  console.log(`[NineTwo Tracking] \u2705 GTM carregado com sucesso! (${gtmId})`);
86
117
  };
87
118
  script.onerror = () => {
88
119
  if (debug)
89
- console.error("[NineTwo Tracking] \u274C Erro ao carregar GTM. Verifique AdBlockers.");
120
+ console.error("[NineTwo Tracking] \u274C Erro ao carregar GTM.");
90
121
  };
91
122
  document.head.appendChild(script);
92
123
  }, [gtmId, debug]);
@@ -94,37 +125,66 @@ var TrackingProvider = ({
94
125
  };
95
126
 
96
127
  // src/components/TrackView.tsx
97
- import { useEffect as useEffect3, useRef, useState } from "react";
128
+ import { useEffect as useEffect4, useRef, useState } from "react";
98
129
  import { jsx as jsx2 } from "react/jsx-runtime";
99
130
  var TrackView = ({
100
131
  children,
101
132
  eventName,
102
133
  category,
103
134
  label,
104
- threshold = 0.5
135
+ threshold = 0.5,
136
+ readTime = 5e3
105
137
  }) => {
106
138
  const ref = useRef(null);
107
- const [hasTriggered, setHasTriggered] = useState(false);
108
- useEffect3(() => {
139
+ const timerRef = useRef(null);
140
+ const [hasTriggeredView, setHasTriggeredView] = useState(false);
141
+ const [hasTriggeredRead, setHasTriggeredRead] = useState(false);
142
+ useEffect4(() => {
143
+ if (!ref.current)
144
+ return;
145
+ if (hasTriggeredView && hasTriggeredRead)
146
+ return;
109
147
  const observer = new IntersectionObserver(
110
148
  ([entry]) => {
111
- if (entry.isIntersecting && !hasTriggered) {
112
- pushToDataLayer({
113
- event: eventName,
114
- category,
115
- label,
116
- type: "view"
117
- });
118
- setHasTriggered(true);
119
- observer.disconnect();
149
+ if (entry.isIntersecting) {
150
+ if (!hasTriggeredView) {
151
+ pushToDataLayer({
152
+ event: eventName,
153
+ category,
154
+ label,
155
+ type: "view"
156
+ });
157
+ setHasTriggeredView(true);
158
+ }
159
+ if (!hasTriggeredRead && !timerRef.current) {
160
+ timerRef.current = setTimeout(() => {
161
+ pushToDataLayer({
162
+ event: `${eventName}_read_confirmation`,
163
+ // Sufixo solicitado
164
+ category,
165
+ label,
166
+ type: "read_confirmation"
167
+ // Tipo diferenciado
168
+ });
169
+ setHasTriggeredRead(true);
170
+ }, readTime);
171
+ }
172
+ } else {
173
+ if (timerRef.current) {
174
+ clearTimeout(timerRef.current);
175
+ timerRef.current = null;
176
+ }
120
177
  }
121
178
  },
122
179
  { threshold }
123
180
  );
124
- if (ref.current)
125
- observer.observe(ref.current);
126
- return () => observer.disconnect();
127
- }, [hasTriggered, eventName, category, label, threshold]);
181
+ observer.observe(ref.current);
182
+ return () => {
183
+ observer.disconnect();
184
+ if (timerRef.current)
185
+ clearTimeout(timerRef.current);
186
+ };
187
+ }, [hasTriggeredView, hasTriggeredRead, eventName, category, label, threshold, readTime]);
128
188
  return /* @__PURE__ */ jsx2("div", { ref, style: { display: "contents" }, children });
129
189
  };
130
190
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ninetwo-user-tracking",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "User tracking abstraction for React/Nextjs with GTM",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",