ninetwo-user-tracking 1.0.10 → 1.0.12
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/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +100 -20
- package/dist/index.mjs +100 -20
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -14,7 +14,16 @@ interface TrackViewProps {
|
|
|
14
14
|
label?: string;
|
|
15
15
|
threshold?: number;
|
|
16
16
|
readTime?: number;
|
|
17
|
+
debug?: boolean;
|
|
17
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* TrackView
|
|
21
|
+
* - Dispara {eventName, type: 'view'} quando o elemento entra na viewport (threshold)
|
|
22
|
+
* - Dispara {eventName+'_read_confirmation', type: 'read_confirmation'} se o elemento permanecer visível por `readTime`
|
|
23
|
+
*
|
|
24
|
+
* Observação: evita usar `display: 'contents'` no wrapper porque em alguns cenários o IntersectionObserver não detecta.
|
|
25
|
+
* Se o wrapper realmente usa display: contents, tentamos observar o primeiro filho disponível.
|
|
26
|
+
*/
|
|
18
27
|
declare const TrackView: React.FC<TrackViewProps>;
|
|
19
28
|
|
|
20
29
|
declare const useAutoTrackClick: (enabled?: boolean) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,16 @@ interface TrackViewProps {
|
|
|
14
14
|
label?: string;
|
|
15
15
|
threshold?: number;
|
|
16
16
|
readTime?: number;
|
|
17
|
+
debug?: boolean;
|
|
17
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* TrackView
|
|
21
|
+
* - Dispara {eventName, type: 'view'} quando o elemento entra na viewport (threshold)
|
|
22
|
+
* - Dispara {eventName+'_read_confirmation', type: 'read_confirmation'} se o elemento permanecer visível por `readTime`
|
|
23
|
+
*
|
|
24
|
+
* Observação: evita usar `display: 'contents'` no wrapper porque em alguns cenários o IntersectionObserver não detecta.
|
|
25
|
+
* Se o wrapper realmente usa display: contents, tentamos observar o primeiro filho disponível.
|
|
26
|
+
*/
|
|
18
27
|
declare const TrackView: React.FC<TrackViewProps>;
|
|
19
28
|
|
|
20
29
|
declare const useAutoTrackClick: (enabled?: boolean) => void;
|
package/dist/index.js
CHANGED
|
@@ -36,14 +36,28 @@ var import_react = require("react");
|
|
|
36
36
|
var pushToDataLayer = (props) => {
|
|
37
37
|
if (typeof window === "undefined")
|
|
38
38
|
return;
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
const { event, category, label, type, ...rest } = props;
|
|
40
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
41
|
+
const params = {
|
|
42
|
+
event_category: category,
|
|
43
|
+
event_label: label,
|
|
44
|
+
event_type: type,
|
|
45
|
+
interaction_time: timestamp,
|
|
46
|
+
...rest
|
|
47
|
+
// permite enviar dados extras
|
|
48
|
+
};
|
|
49
|
+
if (typeof window.gtag === "function") {
|
|
50
|
+
try {
|
|
51
|
+
window.gtag("event", event, params);
|
|
52
|
+
return;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.warn("[NineTwo Tracking] gtag falhou, fallback para dataLayer", err);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
window.dataLayer = window.dataLayer || [];
|
|
58
|
+
window.dataLayer.push({
|
|
59
|
+
event,
|
|
60
|
+
...params
|
|
47
61
|
});
|
|
48
62
|
};
|
|
49
63
|
|
|
@@ -205,21 +219,53 @@ var TrackView = ({
|
|
|
205
219
|
category,
|
|
206
220
|
label,
|
|
207
221
|
threshold = 0.5,
|
|
208
|
-
readTime = 5e3
|
|
222
|
+
readTime = 5e3,
|
|
223
|
+
debug = false
|
|
209
224
|
}) => {
|
|
210
225
|
const ref = (0, import_react4.useRef)(null);
|
|
211
226
|
const timerRef = (0, import_react4.useRef)(null);
|
|
212
227
|
const [hasTriggeredView, setHasTriggeredView] = (0, import_react4.useState)(false);
|
|
213
228
|
const [hasTriggeredRead, setHasTriggeredRead] = (0, import_react4.useState)(false);
|
|
214
229
|
(0, import_react4.useEffect)(() => {
|
|
215
|
-
|
|
230
|
+
const rootEl = ref.current;
|
|
231
|
+
if (!rootEl)
|
|
216
232
|
return;
|
|
217
233
|
if (hasTriggeredView && hasTriggeredRead)
|
|
218
234
|
return;
|
|
235
|
+
if (typeof window !== "undefined" && !("IntersectionObserver" in window)) {
|
|
236
|
+
if (!hasTriggeredView) {
|
|
237
|
+
pushToDataLayer({
|
|
238
|
+
event: eventName,
|
|
239
|
+
category,
|
|
240
|
+
label,
|
|
241
|
+
type: "view"
|
|
242
|
+
});
|
|
243
|
+
setHasTriggeredView(true);
|
|
244
|
+
}
|
|
245
|
+
if (!hasTriggeredRead) {
|
|
246
|
+
timerRef.current = window.setTimeout(() => {
|
|
247
|
+
pushToDataLayer({
|
|
248
|
+
event: `${eventName}_read_confirmation`,
|
|
249
|
+
category,
|
|
250
|
+
label,
|
|
251
|
+
type: "read_confirmation"
|
|
252
|
+
});
|
|
253
|
+
setHasTriggeredRead(true);
|
|
254
|
+
}, readTime);
|
|
255
|
+
}
|
|
256
|
+
return () => {
|
|
257
|
+
if (timerRef.current) {
|
|
258
|
+
window.clearTimeout(timerRef.current);
|
|
259
|
+
timerRef.current = null;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
219
263
|
const observer = new IntersectionObserver(
|
|
220
264
|
([entry]) => {
|
|
221
265
|
if (entry.isIntersecting) {
|
|
222
266
|
if (!hasTriggeredView) {
|
|
267
|
+
if (debug)
|
|
268
|
+
console.log("[TrackView] view ->", eventName, { category, label });
|
|
223
269
|
pushToDataLayer({
|
|
224
270
|
event: eventName,
|
|
225
271
|
category,
|
|
@@ -228,36 +274,70 @@ var TrackView = ({
|
|
|
228
274
|
});
|
|
229
275
|
setHasTriggeredView(true);
|
|
230
276
|
}
|
|
231
|
-
if (!hasTriggeredRead &&
|
|
232
|
-
timerRef.current = setTimeout(() => {
|
|
277
|
+
if (!hasTriggeredRead && timerRef.current === null) {
|
|
278
|
+
timerRef.current = window.setTimeout(() => {
|
|
279
|
+
if (debug)
|
|
280
|
+
console.log("[TrackView] read_confirmation ->", eventName, { category, label });
|
|
233
281
|
pushToDataLayer({
|
|
234
282
|
event: `${eventName}_read_confirmation`,
|
|
235
|
-
// Sufixo solicitado
|
|
236
283
|
category,
|
|
237
284
|
label,
|
|
238
285
|
type: "read_confirmation"
|
|
239
|
-
// Tipo diferenciado
|
|
240
286
|
});
|
|
241
287
|
setHasTriggeredRead(true);
|
|
288
|
+
timerRef.current = null;
|
|
242
289
|
}, readTime);
|
|
243
290
|
}
|
|
244
291
|
} else {
|
|
245
292
|
if (timerRef.current) {
|
|
246
|
-
|
|
293
|
+
if (debug)
|
|
294
|
+
console.log("[TrackView] saiu antes do readTime, cancelando timer ->", eventName);
|
|
295
|
+
window.clearTimeout(timerRef.current);
|
|
247
296
|
timerRef.current = null;
|
|
248
297
|
}
|
|
249
298
|
}
|
|
250
299
|
},
|
|
251
300
|
{ threshold }
|
|
252
301
|
);
|
|
253
|
-
|
|
302
|
+
let elementToObserve = rootEl;
|
|
303
|
+
try {
|
|
304
|
+
const cs = window.getComputedStyle(rootEl);
|
|
305
|
+
if (cs && cs.display === "contents") {
|
|
306
|
+
const firstChild = rootEl.firstElementChild;
|
|
307
|
+
if (firstChild) {
|
|
308
|
+
elementToObserve = firstChild;
|
|
309
|
+
if (debug)
|
|
310
|
+
console.log("[TrackView] wrapper display:contents \u2014 observando primeiro filho", firstChild);
|
|
311
|
+
} else {
|
|
312
|
+
if (debug)
|
|
313
|
+
console.log("[TrackView] wrapper display:contents mas sem filhos \u2014 observando wrapper", rootEl);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
} catch (e) {
|
|
317
|
+
if (debug)
|
|
318
|
+
console.warn("[TrackView] erro ao checar computedStyle", e);
|
|
319
|
+
}
|
|
320
|
+
if (elementToObserve) {
|
|
321
|
+
observer.observe(elementToObserve);
|
|
322
|
+
}
|
|
254
323
|
return () => {
|
|
255
324
|
observer.disconnect();
|
|
256
|
-
if (timerRef.current)
|
|
257
|
-
clearTimeout(timerRef.current);
|
|
325
|
+
if (timerRef.current) {
|
|
326
|
+
window.clearTimeout(timerRef.current);
|
|
327
|
+
timerRef.current = null;
|
|
328
|
+
}
|
|
258
329
|
};
|
|
259
|
-
}, [
|
|
260
|
-
|
|
330
|
+
}, [
|
|
331
|
+
hasTriggeredView,
|
|
332
|
+
hasTriggeredRead,
|
|
333
|
+
eventName,
|
|
334
|
+
category,
|
|
335
|
+
label,
|
|
336
|
+
threshold,
|
|
337
|
+
readTime,
|
|
338
|
+
debug
|
|
339
|
+
]);
|
|
340
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref, style: { display: "block" }, children });
|
|
261
341
|
};
|
|
262
342
|
// Annotate the CommonJS export names for ESM import in node:
|
|
263
343
|
0 && (module.exports = {
|
package/dist/index.mjs
CHANGED
|
@@ -8,14 +8,28 @@ import { useEffect } from "react";
|
|
|
8
8
|
var pushToDataLayer = (props) => {
|
|
9
9
|
if (typeof window === "undefined")
|
|
10
10
|
return;
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
const { event, category, label, type, ...rest } = props;
|
|
12
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
13
|
+
const params = {
|
|
14
|
+
event_category: category,
|
|
15
|
+
event_label: label,
|
|
16
|
+
event_type: type,
|
|
17
|
+
interaction_time: timestamp,
|
|
18
|
+
...rest
|
|
19
|
+
// permite enviar dados extras
|
|
20
|
+
};
|
|
21
|
+
if (typeof window.gtag === "function") {
|
|
22
|
+
try {
|
|
23
|
+
window.gtag("event", event, params);
|
|
24
|
+
return;
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.warn("[NineTwo Tracking] gtag falhou, fallback para dataLayer", err);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
window.dataLayer = window.dataLayer || [];
|
|
30
|
+
window.dataLayer.push({
|
|
31
|
+
event,
|
|
32
|
+
...params
|
|
19
33
|
});
|
|
20
34
|
};
|
|
21
35
|
|
|
@@ -177,21 +191,53 @@ var TrackView = ({
|
|
|
177
191
|
category,
|
|
178
192
|
label,
|
|
179
193
|
threshold = 0.5,
|
|
180
|
-
readTime = 5e3
|
|
194
|
+
readTime = 5e3,
|
|
195
|
+
debug = false
|
|
181
196
|
}) => {
|
|
182
197
|
const ref = useRef(null);
|
|
183
198
|
const timerRef = useRef(null);
|
|
184
199
|
const [hasTriggeredView, setHasTriggeredView] = useState(false);
|
|
185
200
|
const [hasTriggeredRead, setHasTriggeredRead] = useState(false);
|
|
186
201
|
useEffect4(() => {
|
|
187
|
-
|
|
202
|
+
const rootEl = ref.current;
|
|
203
|
+
if (!rootEl)
|
|
188
204
|
return;
|
|
189
205
|
if (hasTriggeredView && hasTriggeredRead)
|
|
190
206
|
return;
|
|
207
|
+
if (typeof window !== "undefined" && !("IntersectionObserver" in window)) {
|
|
208
|
+
if (!hasTriggeredView) {
|
|
209
|
+
pushToDataLayer({
|
|
210
|
+
event: eventName,
|
|
211
|
+
category,
|
|
212
|
+
label,
|
|
213
|
+
type: "view"
|
|
214
|
+
});
|
|
215
|
+
setHasTriggeredView(true);
|
|
216
|
+
}
|
|
217
|
+
if (!hasTriggeredRead) {
|
|
218
|
+
timerRef.current = window.setTimeout(() => {
|
|
219
|
+
pushToDataLayer({
|
|
220
|
+
event: `${eventName}_read_confirmation`,
|
|
221
|
+
category,
|
|
222
|
+
label,
|
|
223
|
+
type: "read_confirmation"
|
|
224
|
+
});
|
|
225
|
+
setHasTriggeredRead(true);
|
|
226
|
+
}, readTime);
|
|
227
|
+
}
|
|
228
|
+
return () => {
|
|
229
|
+
if (timerRef.current) {
|
|
230
|
+
window.clearTimeout(timerRef.current);
|
|
231
|
+
timerRef.current = null;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
191
235
|
const observer = new IntersectionObserver(
|
|
192
236
|
([entry]) => {
|
|
193
237
|
if (entry.isIntersecting) {
|
|
194
238
|
if (!hasTriggeredView) {
|
|
239
|
+
if (debug)
|
|
240
|
+
console.log("[TrackView] view ->", eventName, { category, label });
|
|
195
241
|
pushToDataLayer({
|
|
196
242
|
event: eventName,
|
|
197
243
|
category,
|
|
@@ -200,36 +246,70 @@ var TrackView = ({
|
|
|
200
246
|
});
|
|
201
247
|
setHasTriggeredView(true);
|
|
202
248
|
}
|
|
203
|
-
if (!hasTriggeredRead &&
|
|
204
|
-
timerRef.current = setTimeout(() => {
|
|
249
|
+
if (!hasTriggeredRead && timerRef.current === null) {
|
|
250
|
+
timerRef.current = window.setTimeout(() => {
|
|
251
|
+
if (debug)
|
|
252
|
+
console.log("[TrackView] read_confirmation ->", eventName, { category, label });
|
|
205
253
|
pushToDataLayer({
|
|
206
254
|
event: `${eventName}_read_confirmation`,
|
|
207
|
-
// Sufixo solicitado
|
|
208
255
|
category,
|
|
209
256
|
label,
|
|
210
257
|
type: "read_confirmation"
|
|
211
|
-
// Tipo diferenciado
|
|
212
258
|
});
|
|
213
259
|
setHasTriggeredRead(true);
|
|
260
|
+
timerRef.current = null;
|
|
214
261
|
}, readTime);
|
|
215
262
|
}
|
|
216
263
|
} else {
|
|
217
264
|
if (timerRef.current) {
|
|
218
|
-
|
|
265
|
+
if (debug)
|
|
266
|
+
console.log("[TrackView] saiu antes do readTime, cancelando timer ->", eventName);
|
|
267
|
+
window.clearTimeout(timerRef.current);
|
|
219
268
|
timerRef.current = null;
|
|
220
269
|
}
|
|
221
270
|
}
|
|
222
271
|
},
|
|
223
272
|
{ threshold }
|
|
224
273
|
);
|
|
225
|
-
|
|
274
|
+
let elementToObserve = rootEl;
|
|
275
|
+
try {
|
|
276
|
+
const cs = window.getComputedStyle(rootEl);
|
|
277
|
+
if (cs && cs.display === "contents") {
|
|
278
|
+
const firstChild = rootEl.firstElementChild;
|
|
279
|
+
if (firstChild) {
|
|
280
|
+
elementToObserve = firstChild;
|
|
281
|
+
if (debug)
|
|
282
|
+
console.log("[TrackView] wrapper display:contents \u2014 observando primeiro filho", firstChild);
|
|
283
|
+
} else {
|
|
284
|
+
if (debug)
|
|
285
|
+
console.log("[TrackView] wrapper display:contents mas sem filhos \u2014 observando wrapper", rootEl);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} catch (e) {
|
|
289
|
+
if (debug)
|
|
290
|
+
console.warn("[TrackView] erro ao checar computedStyle", e);
|
|
291
|
+
}
|
|
292
|
+
if (elementToObserve) {
|
|
293
|
+
observer.observe(elementToObserve);
|
|
294
|
+
}
|
|
226
295
|
return () => {
|
|
227
296
|
observer.disconnect();
|
|
228
|
-
if (timerRef.current)
|
|
229
|
-
clearTimeout(timerRef.current);
|
|
297
|
+
if (timerRef.current) {
|
|
298
|
+
window.clearTimeout(timerRef.current);
|
|
299
|
+
timerRef.current = null;
|
|
300
|
+
}
|
|
230
301
|
};
|
|
231
|
-
}, [
|
|
232
|
-
|
|
302
|
+
}, [
|
|
303
|
+
hasTriggeredView,
|
|
304
|
+
hasTriggeredRead,
|
|
305
|
+
eventName,
|
|
306
|
+
category,
|
|
307
|
+
label,
|
|
308
|
+
threshold,
|
|
309
|
+
readTime,
|
|
310
|
+
debug
|
|
311
|
+
]);
|
|
312
|
+
return /* @__PURE__ */ jsx2("div", { ref, style: { display: "block" }, children });
|
|
233
313
|
};
|
|
234
314
|
export {
|
|
235
315
|
TrackView,
|