@drakkar.software/sunglasses-react 0.2.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/dist/index.d.mts +122 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.js +191 -0
- package/dist/index.mjs +158 -0
- package/package.json +42 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ISunglassesClient, ScreenTrackingOptions, ConsentStatus } from '@drakkar.software/sunglasses-core';
|
|
3
|
+
export { ConsentStatus, ISunglassesClient, ScreenTrackingOptions, SunglassesConfig, SunglassesCore, SunglassesEvent } from '@drakkar.software/sunglasses-core';
|
|
4
|
+
|
|
5
|
+
interface SunglassesProviderProps {
|
|
6
|
+
/** An initialized ISunglassesClient (from SunglassesCore.create()). */
|
|
7
|
+
client: ISunglassesClient;
|
|
8
|
+
/** Optional screen tracking configuration. */
|
|
9
|
+
screenTracking?: ScreenTrackingOptions;
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Provides a SunGlasses client to the React component tree.
|
|
14
|
+
*
|
|
15
|
+
* Place this at the root of your application, wrapping all components that
|
|
16
|
+
* need access to event tracking.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* const client = await SunglassesCore.create({ ... });
|
|
21
|
+
*
|
|
22
|
+
* function App() {
|
|
23
|
+
* return (
|
|
24
|
+
* <SunglassesProvider client={client} screenTracking={{ useHistoryApi: true }}>
|
|
25
|
+
* <Router />
|
|
26
|
+
* </SunglassesProvider>
|
|
27
|
+
* );
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare function SunglassesProvider({ client, screenTracking, children, }: SunglassesProviderProps): React.ReactElement;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Read UTM attribution parameters from the current URL and register them as
|
|
35
|
+
* super properties on the client.
|
|
36
|
+
*
|
|
37
|
+
* Call this once at app startup, immediately after `SunglassesCore.create()`.
|
|
38
|
+
* The registered properties are automatically merged into every subsequent event
|
|
39
|
+
* until `client.unregister()` or `client.reset()` is called.
|
|
40
|
+
*
|
|
41
|
+
* Captured properties (when present):
|
|
42
|
+
* - `utm_source` — traffic source (e.g. "google", "newsletter")
|
|
43
|
+
* - `utm_medium` — marketing channel (e.g. "cpc", "email")
|
|
44
|
+
* - `utm_campaign` — campaign name (e.g. "spring_sale")
|
|
45
|
+
* - `utm_content` — ad variant (e.g. "banner_v2")
|
|
46
|
+
* - `utm_term` — paid search keyword
|
|
47
|
+
* - `$referrer` — full URL of the referring page
|
|
48
|
+
* - `$referring_domain` — hostname of the referring page
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const client = await SunglassesCore.create({ ... });
|
|
53
|
+
* captureUtmParams(client); // call once on initial load
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare function captureUtmParams(client: ISunglassesClient): void;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Access the SunGlasses client from React context.
|
|
60
|
+
* Throws if called outside of a SunglassesProvider.
|
|
61
|
+
*/
|
|
62
|
+
declare function useSunglasses(): ISunglassesClient;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Web screen tracking via the History API.
|
|
66
|
+
*
|
|
67
|
+
* Monkey-patches history.pushState and history.replaceState, and listens to
|
|
68
|
+
* the popstate event. Fires client.screen() on every navigation.
|
|
69
|
+
*
|
|
70
|
+
* Must be called inside a component that has access to the SunGlasses client,
|
|
71
|
+
* typically inside SunglassesProvider. Only one instance of this hook should
|
|
72
|
+
* be active at a time — mounting it in multiple components simultaneously will
|
|
73
|
+
* result in the second instance being a no-op until the first unmounts.
|
|
74
|
+
*/
|
|
75
|
+
declare function useScreenTracking(client: ISunglassesClient, options?: ScreenTrackingOptions): void;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Returns a bound `capture` function for the current SunGlasses client.
|
|
79
|
+
*
|
|
80
|
+
* Shorthand for `useSunglasses().capture` — avoids holding a reference to the
|
|
81
|
+
* entire client when you only need to fire events from a component.
|
|
82
|
+
*
|
|
83
|
+
* The returned function is stable across renders (same reference) as long as
|
|
84
|
+
* the client itself does not change.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```tsx
|
|
88
|
+
* function SubmitButton() {
|
|
89
|
+
* const capture = useCapture();
|
|
90
|
+
* return (
|
|
91
|
+
* <button onClick={() => capture('form_submitted', { formId: 'signup' })}>
|
|
92
|
+
* Submit
|
|
93
|
+
* </button>
|
|
94
|
+
* );
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare function useCapture(): (eventName: string, properties?: Record<string, unknown>) => void;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns the current consent status as a snapshot.
|
|
102
|
+
*
|
|
103
|
+
* This is a convenience hook over `useSunglasses().getConsentStatus()`.
|
|
104
|
+
*
|
|
105
|
+
* **Important:** this hook returns a snapshot at render time and does not
|
|
106
|
+
* subscribe to future changes. Re-renders are not triggered automatically
|
|
107
|
+
* when `optIn()` or `optOut()` are called. If you need the UI to react to
|
|
108
|
+
* consent changes, lift the consent call into a parent component that also
|
|
109
|
+
* manages re-render state, or call `useSunglasses()` directly.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```tsx
|
|
113
|
+
* function ConsentBanner() {
|
|
114
|
+
* const status = useConsentStatus();
|
|
115
|
+
* if (status !== 'unknown') return null;
|
|
116
|
+
* return <Banner />;
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
declare function useConsentStatus(): ConsentStatus;
|
|
121
|
+
|
|
122
|
+
export { SunglassesProvider, type SunglassesProviderProps, captureUtmParams, useCapture, useConsentStatus, useScreenTracking, useSunglasses };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ISunglassesClient, ScreenTrackingOptions, ConsentStatus } from '@drakkar.software/sunglasses-core';
|
|
3
|
+
export { ConsentStatus, ISunglassesClient, ScreenTrackingOptions, SunglassesConfig, SunglassesCore, SunglassesEvent } from '@drakkar.software/sunglasses-core';
|
|
4
|
+
|
|
5
|
+
interface SunglassesProviderProps {
|
|
6
|
+
/** An initialized ISunglassesClient (from SunglassesCore.create()). */
|
|
7
|
+
client: ISunglassesClient;
|
|
8
|
+
/** Optional screen tracking configuration. */
|
|
9
|
+
screenTracking?: ScreenTrackingOptions;
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Provides a SunGlasses client to the React component tree.
|
|
14
|
+
*
|
|
15
|
+
* Place this at the root of your application, wrapping all components that
|
|
16
|
+
* need access to event tracking.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* const client = await SunglassesCore.create({ ... });
|
|
21
|
+
*
|
|
22
|
+
* function App() {
|
|
23
|
+
* return (
|
|
24
|
+
* <SunglassesProvider client={client} screenTracking={{ useHistoryApi: true }}>
|
|
25
|
+
* <Router />
|
|
26
|
+
* </SunglassesProvider>
|
|
27
|
+
* );
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare function SunglassesProvider({ client, screenTracking, children, }: SunglassesProviderProps): React.ReactElement;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Read UTM attribution parameters from the current URL and register them as
|
|
35
|
+
* super properties on the client.
|
|
36
|
+
*
|
|
37
|
+
* Call this once at app startup, immediately after `SunglassesCore.create()`.
|
|
38
|
+
* The registered properties are automatically merged into every subsequent event
|
|
39
|
+
* until `client.unregister()` or `client.reset()` is called.
|
|
40
|
+
*
|
|
41
|
+
* Captured properties (when present):
|
|
42
|
+
* - `utm_source` — traffic source (e.g. "google", "newsletter")
|
|
43
|
+
* - `utm_medium` — marketing channel (e.g. "cpc", "email")
|
|
44
|
+
* - `utm_campaign` — campaign name (e.g. "spring_sale")
|
|
45
|
+
* - `utm_content` — ad variant (e.g. "banner_v2")
|
|
46
|
+
* - `utm_term` — paid search keyword
|
|
47
|
+
* - `$referrer` — full URL of the referring page
|
|
48
|
+
* - `$referring_domain` — hostname of the referring page
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const client = await SunglassesCore.create({ ... });
|
|
53
|
+
* captureUtmParams(client); // call once on initial load
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare function captureUtmParams(client: ISunglassesClient): void;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Access the SunGlasses client from React context.
|
|
60
|
+
* Throws if called outside of a SunglassesProvider.
|
|
61
|
+
*/
|
|
62
|
+
declare function useSunglasses(): ISunglassesClient;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Web screen tracking via the History API.
|
|
66
|
+
*
|
|
67
|
+
* Monkey-patches history.pushState and history.replaceState, and listens to
|
|
68
|
+
* the popstate event. Fires client.screen() on every navigation.
|
|
69
|
+
*
|
|
70
|
+
* Must be called inside a component that has access to the SunGlasses client,
|
|
71
|
+
* typically inside SunglassesProvider. Only one instance of this hook should
|
|
72
|
+
* be active at a time — mounting it in multiple components simultaneously will
|
|
73
|
+
* result in the second instance being a no-op until the first unmounts.
|
|
74
|
+
*/
|
|
75
|
+
declare function useScreenTracking(client: ISunglassesClient, options?: ScreenTrackingOptions): void;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Returns a bound `capture` function for the current SunGlasses client.
|
|
79
|
+
*
|
|
80
|
+
* Shorthand for `useSunglasses().capture` — avoids holding a reference to the
|
|
81
|
+
* entire client when you only need to fire events from a component.
|
|
82
|
+
*
|
|
83
|
+
* The returned function is stable across renders (same reference) as long as
|
|
84
|
+
* the client itself does not change.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```tsx
|
|
88
|
+
* function SubmitButton() {
|
|
89
|
+
* const capture = useCapture();
|
|
90
|
+
* return (
|
|
91
|
+
* <button onClick={() => capture('form_submitted', { formId: 'signup' })}>
|
|
92
|
+
* Submit
|
|
93
|
+
* </button>
|
|
94
|
+
* );
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare function useCapture(): (eventName: string, properties?: Record<string, unknown>) => void;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns the current consent status as a snapshot.
|
|
102
|
+
*
|
|
103
|
+
* This is a convenience hook over `useSunglasses().getConsentStatus()`.
|
|
104
|
+
*
|
|
105
|
+
* **Important:** this hook returns a snapshot at render time and does not
|
|
106
|
+
* subscribe to future changes. Re-renders are not triggered automatically
|
|
107
|
+
* when `optIn()` or `optOut()` are called. If you need the UI to react to
|
|
108
|
+
* consent changes, lift the consent call into a parent component that also
|
|
109
|
+
* manages re-render state, or call `useSunglasses()` directly.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```tsx
|
|
113
|
+
* function ConsentBanner() {
|
|
114
|
+
* const status = useConsentStatus();
|
|
115
|
+
* if (status !== 'unknown') return null;
|
|
116
|
+
* return <Banner />;
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
declare function useConsentStatus(): ConsentStatus;
|
|
121
|
+
|
|
122
|
+
export { SunglassesProvider, type SunglassesProviderProps, captureUtmParams, useCapture, useConsentStatus, useScreenTracking, useSunglasses };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
SunglassesCore: () => import_sunglasses_core.SunglassesCore,
|
|
24
|
+
SunglassesProvider: () => SunglassesProvider,
|
|
25
|
+
captureUtmParams: () => captureUtmParams,
|
|
26
|
+
useCapture: () => useCapture,
|
|
27
|
+
useConsentStatus: () => useConsentStatus,
|
|
28
|
+
useScreenTracking: () => useScreenTracking,
|
|
29
|
+
useSunglasses: () => useSunglasses
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
|
|
33
|
+
// src/SunglassesProvider.tsx
|
|
34
|
+
var import_react3 = require("react");
|
|
35
|
+
|
|
36
|
+
// src/context.ts
|
|
37
|
+
var import_react = require("react");
|
|
38
|
+
var SunglassesContext = (0, import_react.createContext)(null);
|
|
39
|
+
function useSunglasses() {
|
|
40
|
+
const client = (0, import_react.useContext)(SunglassesContext);
|
|
41
|
+
if (client === null) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
"[SunGlasses] useSunglasses() must be called inside a <SunglassesProvider>."
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return client;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/useScreenTracking.ts
|
|
50
|
+
var import_react2 = require("react");
|
|
51
|
+
var historyPatched = false;
|
|
52
|
+
var previousPath = null;
|
|
53
|
+
function useScreenTracking(client, options = {}) {
|
|
54
|
+
const { useHistoryApi = true, screenNameMapper } = options;
|
|
55
|
+
const clientRef = (0, import_react2.useRef)(client);
|
|
56
|
+
clientRef.current = client;
|
|
57
|
+
const mapperRef = (0, import_react2.useRef)(screenNameMapper);
|
|
58
|
+
mapperRef.current = screenNameMapper;
|
|
59
|
+
(0, import_react2.useEffect)(() => {
|
|
60
|
+
if (!useHistoryApi || typeof window === "undefined") return;
|
|
61
|
+
if (historyPatched) {
|
|
62
|
+
console.warn(
|
|
63
|
+
"[SunGlasses] useScreenTracking: history is already patched by another instance \u2014 this instance will not track."
|
|
64
|
+
);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
historyPatched = true;
|
|
68
|
+
const handleRouteChange = (path) => {
|
|
69
|
+
const name = mapperRef.current ? mapperRef.current(path) : path;
|
|
70
|
+
const props = {
|
|
71
|
+
$path: path,
|
|
72
|
+
$url: window.location.href
|
|
73
|
+
};
|
|
74
|
+
if (document.title) props.$title = document.title;
|
|
75
|
+
if (previousPath !== null) {
|
|
76
|
+
props.$referrer = previousPath;
|
|
77
|
+
} else if (document.referrer) {
|
|
78
|
+
props.$referrer = document.referrer;
|
|
79
|
+
}
|
|
80
|
+
previousPath = path;
|
|
81
|
+
clientRef.current.screen(name, props);
|
|
82
|
+
};
|
|
83
|
+
handleRouteChange(window.location.pathname);
|
|
84
|
+
const originalPush = history.pushState.bind(history);
|
|
85
|
+
history.pushState = (...args) => {
|
|
86
|
+
originalPush(...args);
|
|
87
|
+
handleRouteChange(window.location.pathname);
|
|
88
|
+
};
|
|
89
|
+
const originalReplace = history.replaceState.bind(history);
|
|
90
|
+
history.replaceState = (...args) => {
|
|
91
|
+
originalReplace(...args);
|
|
92
|
+
handleRouteChange(window.location.pathname);
|
|
93
|
+
};
|
|
94
|
+
const onPopState = () => handleRouteChange(window.location.pathname);
|
|
95
|
+
window.addEventListener("popstate", onPopState);
|
|
96
|
+
return () => {
|
|
97
|
+
history.pushState = originalPush;
|
|
98
|
+
history.replaceState = originalReplace;
|
|
99
|
+
window.removeEventListener("popstate", onPopState);
|
|
100
|
+
historyPatched = false;
|
|
101
|
+
previousPath = null;
|
|
102
|
+
};
|
|
103
|
+
}, [useHistoryApi]);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/SunglassesProvider.tsx
|
|
107
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
108
|
+
function SunglassesProvider({
|
|
109
|
+
client,
|
|
110
|
+
screenTracking,
|
|
111
|
+
children
|
|
112
|
+
}) {
|
|
113
|
+
(0, import_react3.useEffect)(() => {
|
|
114
|
+
return () => {
|
|
115
|
+
client.shutdown().catch(() => {
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
}, [client]);
|
|
119
|
+
(0, import_react3.useEffect)(() => {
|
|
120
|
+
if (typeof document === "undefined") return;
|
|
121
|
+
const handleVisibilityChange = () => {
|
|
122
|
+
if (document.visibilityState === "hidden") {
|
|
123
|
+
client.flush().catch(() => {
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
128
|
+
return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
129
|
+
}, [client]);
|
|
130
|
+
useScreenTracking(client, screenTracking ?? { useHistoryApi: false });
|
|
131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SunglassesContext.Provider, { value: client, children });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/captureUtmParams.ts
|
|
135
|
+
var UTM_PARAMS = [
|
|
136
|
+
"utm_source",
|
|
137
|
+
"utm_medium",
|
|
138
|
+
"utm_campaign",
|
|
139
|
+
"utm_content",
|
|
140
|
+
"utm_term"
|
|
141
|
+
];
|
|
142
|
+
function captureUtmParams(client) {
|
|
143
|
+
if (typeof window === "undefined") return;
|
|
144
|
+
const params = {};
|
|
145
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
146
|
+
for (const key of UTM_PARAMS) {
|
|
147
|
+
const value = searchParams.get(key);
|
|
148
|
+
if (value) params[key] = value;
|
|
149
|
+
}
|
|
150
|
+
if (document.referrer) {
|
|
151
|
+
params["$referrer"] = document.referrer;
|
|
152
|
+
try {
|
|
153
|
+
params["$referring_domain"] = new URL(document.referrer).hostname;
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (Object.keys(params).length > 0) {
|
|
158
|
+
client.register(params);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/useCapture.ts
|
|
163
|
+
var import_react4 = require("react");
|
|
164
|
+
function useCapture() {
|
|
165
|
+
const client = useSunglasses();
|
|
166
|
+
return (0, import_react4.useCallback)(
|
|
167
|
+
(eventName, properties) => {
|
|
168
|
+
client.capture(eventName, properties);
|
|
169
|
+
},
|
|
170
|
+
[client]
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/useConsentStatus.ts
|
|
175
|
+
function useConsentStatus() {
|
|
176
|
+
const client = useSunglasses();
|
|
177
|
+
return client.getConsentStatus();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/index.ts
|
|
181
|
+
var import_sunglasses_core = require("@drakkar.software/sunglasses-core");
|
|
182
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
183
|
+
0 && (module.exports = {
|
|
184
|
+
SunglassesCore,
|
|
185
|
+
SunglassesProvider,
|
|
186
|
+
captureUtmParams,
|
|
187
|
+
useCapture,
|
|
188
|
+
useConsentStatus,
|
|
189
|
+
useScreenTracking,
|
|
190
|
+
useSunglasses
|
|
191
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// src/SunglassesProvider.tsx
|
|
2
|
+
import { useEffect as useEffect2 } from "react";
|
|
3
|
+
|
|
4
|
+
// src/context.ts
|
|
5
|
+
import { createContext, useContext } from "react";
|
|
6
|
+
var SunglassesContext = createContext(null);
|
|
7
|
+
function useSunglasses() {
|
|
8
|
+
const client = useContext(SunglassesContext);
|
|
9
|
+
if (client === null) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"[SunGlasses] useSunglasses() must be called inside a <SunglassesProvider>."
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
return client;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/useScreenTracking.ts
|
|
18
|
+
import { useEffect, useRef } from "react";
|
|
19
|
+
var historyPatched = false;
|
|
20
|
+
var previousPath = null;
|
|
21
|
+
function useScreenTracking(client, options = {}) {
|
|
22
|
+
const { useHistoryApi = true, screenNameMapper } = options;
|
|
23
|
+
const clientRef = useRef(client);
|
|
24
|
+
clientRef.current = client;
|
|
25
|
+
const mapperRef = useRef(screenNameMapper);
|
|
26
|
+
mapperRef.current = screenNameMapper;
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!useHistoryApi || typeof window === "undefined") return;
|
|
29
|
+
if (historyPatched) {
|
|
30
|
+
console.warn(
|
|
31
|
+
"[SunGlasses] useScreenTracking: history is already patched by another instance \u2014 this instance will not track."
|
|
32
|
+
);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
historyPatched = true;
|
|
36
|
+
const handleRouteChange = (path) => {
|
|
37
|
+
const name = mapperRef.current ? mapperRef.current(path) : path;
|
|
38
|
+
const props = {
|
|
39
|
+
$path: path,
|
|
40
|
+
$url: window.location.href
|
|
41
|
+
};
|
|
42
|
+
if (document.title) props.$title = document.title;
|
|
43
|
+
if (previousPath !== null) {
|
|
44
|
+
props.$referrer = previousPath;
|
|
45
|
+
} else if (document.referrer) {
|
|
46
|
+
props.$referrer = document.referrer;
|
|
47
|
+
}
|
|
48
|
+
previousPath = path;
|
|
49
|
+
clientRef.current.screen(name, props);
|
|
50
|
+
};
|
|
51
|
+
handleRouteChange(window.location.pathname);
|
|
52
|
+
const originalPush = history.pushState.bind(history);
|
|
53
|
+
history.pushState = (...args) => {
|
|
54
|
+
originalPush(...args);
|
|
55
|
+
handleRouteChange(window.location.pathname);
|
|
56
|
+
};
|
|
57
|
+
const originalReplace = history.replaceState.bind(history);
|
|
58
|
+
history.replaceState = (...args) => {
|
|
59
|
+
originalReplace(...args);
|
|
60
|
+
handleRouteChange(window.location.pathname);
|
|
61
|
+
};
|
|
62
|
+
const onPopState = () => handleRouteChange(window.location.pathname);
|
|
63
|
+
window.addEventListener("popstate", onPopState);
|
|
64
|
+
return () => {
|
|
65
|
+
history.pushState = originalPush;
|
|
66
|
+
history.replaceState = originalReplace;
|
|
67
|
+
window.removeEventListener("popstate", onPopState);
|
|
68
|
+
historyPatched = false;
|
|
69
|
+
previousPath = null;
|
|
70
|
+
};
|
|
71
|
+
}, [useHistoryApi]);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/SunglassesProvider.tsx
|
|
75
|
+
import { jsx } from "react/jsx-runtime";
|
|
76
|
+
function SunglassesProvider({
|
|
77
|
+
client,
|
|
78
|
+
screenTracking,
|
|
79
|
+
children
|
|
80
|
+
}) {
|
|
81
|
+
useEffect2(() => {
|
|
82
|
+
return () => {
|
|
83
|
+
client.shutdown().catch(() => {
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
}, [client]);
|
|
87
|
+
useEffect2(() => {
|
|
88
|
+
if (typeof document === "undefined") return;
|
|
89
|
+
const handleVisibilityChange = () => {
|
|
90
|
+
if (document.visibilityState === "hidden") {
|
|
91
|
+
client.flush().catch(() => {
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
96
|
+
return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
97
|
+
}, [client]);
|
|
98
|
+
useScreenTracking(client, screenTracking ?? { useHistoryApi: false });
|
|
99
|
+
return /* @__PURE__ */ jsx(SunglassesContext.Provider, { value: client, children });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/captureUtmParams.ts
|
|
103
|
+
var UTM_PARAMS = [
|
|
104
|
+
"utm_source",
|
|
105
|
+
"utm_medium",
|
|
106
|
+
"utm_campaign",
|
|
107
|
+
"utm_content",
|
|
108
|
+
"utm_term"
|
|
109
|
+
];
|
|
110
|
+
function captureUtmParams(client) {
|
|
111
|
+
if (typeof window === "undefined") return;
|
|
112
|
+
const params = {};
|
|
113
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
114
|
+
for (const key of UTM_PARAMS) {
|
|
115
|
+
const value = searchParams.get(key);
|
|
116
|
+
if (value) params[key] = value;
|
|
117
|
+
}
|
|
118
|
+
if (document.referrer) {
|
|
119
|
+
params["$referrer"] = document.referrer;
|
|
120
|
+
try {
|
|
121
|
+
params["$referring_domain"] = new URL(document.referrer).hostname;
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (Object.keys(params).length > 0) {
|
|
126
|
+
client.register(params);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/useCapture.ts
|
|
131
|
+
import { useCallback } from "react";
|
|
132
|
+
function useCapture() {
|
|
133
|
+
const client = useSunglasses();
|
|
134
|
+
return useCallback(
|
|
135
|
+
(eventName, properties) => {
|
|
136
|
+
client.capture(eventName, properties);
|
|
137
|
+
},
|
|
138
|
+
[client]
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/useConsentStatus.ts
|
|
143
|
+
function useConsentStatus() {
|
|
144
|
+
const client = useSunglasses();
|
|
145
|
+
return client.getConsentStatus();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/index.ts
|
|
149
|
+
import { SunglassesCore } from "@drakkar.software/sunglasses-core";
|
|
150
|
+
export {
|
|
151
|
+
SunglassesCore,
|
|
152
|
+
SunglassesProvider,
|
|
153
|
+
captureUtmParams,
|
|
154
|
+
useCapture,
|
|
155
|
+
useConsentStatus,
|
|
156
|
+
useScreenTracking,
|
|
157
|
+
useSunglasses
|
|
158
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@drakkar.software/sunglasses-react",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "React (web) provider and hooks for SunGlasses event tracking",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@drakkar.software/sunglasses-core": "0.2.0"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"react": ">=18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/react": "^18.3.12",
|
|
26
|
+
"happy-dom": "^15.11.7",
|
|
27
|
+
"react": "^18.3.1",
|
|
28
|
+
"react-dom": "^18.3.1",
|
|
29
|
+
"tsup": "^8.3.5",
|
|
30
|
+
"typescript": "^5.7.2",
|
|
31
|
+
"vitest": "^2.1.8",
|
|
32
|
+
"@drakkar.software/sunglasses-tsconfig": "0.1.0"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
36
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"lint": "eslint src/",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"clean": "rm -rf dist .tsbuildinfo"
|
|
41
|
+
}
|
|
42
|
+
}
|