react-event-tracking 1.0.9 → 2.0.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/README.md +143 -39
- package/dist/index.cjs +78 -19
- package/dist/index.d.cts +27 -11
- package/dist/index.d.mts +27 -11
- package/dist/index.d.ts +27 -11
- package/dist/index.mjs +78 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@ A convenient React context for tracking analytics events.
|
|
|
3
3
|
|
|
4
4
|
## Features
|
|
5
5
|
|
|
6
|
+
- **Elegant Type-Safe Api**: enjoy a seamless dot-notation experience with full TypeScript autocompletion.
|
|
6
7
|
- **Nested Contexts**: Automatically merges parameters from parent providers.
|
|
7
8
|
- **Zero Re-renders**: No need to wrap props in `useCallback`/`useMemo`.
|
|
8
9
|
- **Multiple Providers**: Send events to different analytics services.
|
|
@@ -15,6 +16,10 @@ A convenient React context for tracking analytics events.
|
|
|
15
16
|
|
|
16
17
|
- [Installation](#installation)
|
|
17
18
|
- [Quickstart](#quickstart)
|
|
19
|
+
- [Usage Guide](#usage-guide)
|
|
20
|
+
* [Basic Hook](#basic-hook)
|
|
21
|
+
* [Typed Hook Factory](#typed-hook-factory)
|
|
22
|
+
* [Custom Handlers](#custom-handlers)
|
|
18
23
|
- [Advanced Usage](#advanced-usage)
|
|
19
24
|
* [Multiple Trackers & Factory](#multiple-trackers--factory)
|
|
20
25
|
* [Filtering Events](#filtering-events)
|
|
@@ -47,6 +52,7 @@ const Main = () => (
|
|
|
47
52
|
</TrackRoot>
|
|
48
53
|
);
|
|
49
54
|
```
|
|
55
|
+
|
|
50
56
|
2. Wrap any component with shared parameters
|
|
51
57
|
```tsx
|
|
52
58
|
import { TrackProvider } from 'react-event-tracking';
|
|
@@ -58,29 +64,129 @@ const Dashboard = () => (
|
|
|
58
64
|
);
|
|
59
65
|
```
|
|
60
66
|
|
|
61
|
-
|
|
67
|
+
## Usage Guide
|
|
68
|
+
|
|
69
|
+
### Basic Hook
|
|
70
|
+
|
|
71
|
+
Use `useReactEventTracking` for simple event tracking
|
|
72
|
+
|
|
62
73
|
```tsx
|
|
63
|
-
import {
|
|
74
|
+
import { useReactEventTracking } from 'react-event-tracking';
|
|
64
75
|
|
|
65
76
|
const MyButton = () => {
|
|
66
|
-
const {
|
|
77
|
+
const { track } = useReactEventTracking();
|
|
67
78
|
|
|
68
79
|
return (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
80
|
+
{/* Option A: String } */}
|
|
81
|
+
<button onClick={() => track('click', { button_id: '123' })}>
|
|
82
|
+
Click me
|
|
83
|
+
</button>
|
|
84
|
+
|
|
85
|
+
{/* Option B: Object call */}
|
|
86
|
+
<button onClick={() => track({ eventName: 'click', params: { button_id: '456' } })}>
|
|
72
87
|
Click me
|
|
73
88
|
</button>
|
|
74
89
|
|
|
75
|
-
{/* Option B: Object call */}
|
|
76
|
-
<button onClick={() => sendEvent({ eventName: 'click', params: { button_id: '456' } })}>
|
|
77
|
-
Click me too
|
|
78
|
-
</button>
|
|
79
|
-
</>
|
|
80
90
|
);
|
|
81
91
|
};
|
|
82
92
|
```
|
|
83
93
|
|
|
94
|
+
### Typed Hook Factory
|
|
95
|
+
|
|
96
|
+
For a more convenient dot-notation syntax and full TypeScript support, create your own hook using `createReactEventTrackingHook`.
|
|
97
|
+
|
|
98
|
+
1. Create a hook:
|
|
99
|
+
```tsx
|
|
100
|
+
import { createReactEventTrackingHook } from 'react-event-tracking';
|
|
101
|
+
|
|
102
|
+
// LoadingScreen.tsx
|
|
103
|
+
export type LoginScreenEvents = {
|
|
104
|
+
forgot_password: { from: "footer" | "button" }
|
|
105
|
+
logged_in: { timePassed: number }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
type SystemEvents = {
|
|
109
|
+
app_updated: { previous_version: string; current_version: string }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// analytics.ts
|
|
113
|
+
export type AnalyticsEvents = SystemEvents & {
|
|
114
|
+
login_screen: LoginScreenEvents
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const useTracking = createReactEventTrackingHook<AnalyticsEvents>();
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
2. Use it in your components:
|
|
121
|
+
```tsx
|
|
122
|
+
const LoginButton = () => {
|
|
123
|
+
const { track } = useTracking();
|
|
124
|
+
|
|
125
|
+
const handleLogin = () => {
|
|
126
|
+
// This call transforms into "login_screen.logged_in" event with parameters
|
|
127
|
+
track.login_screen.logged_in({ timePassed: 3000 });
|
|
128
|
+
};
|
|
129
|
+
return (
|
|
130
|
+
<button onClick={handleLogin}>
|
|
131
|
+
Login with Google
|
|
132
|
+
</button>
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Custom Handlers
|
|
138
|
+
|
|
139
|
+
Sometimes you need to expose more than just the `track` function, for example, a way to identify users. You can pass custom handlers to the factory.
|
|
140
|
+
|
|
141
|
+
1. Define your handlers type and create the hook:
|
|
142
|
+
```tsx
|
|
143
|
+
type MyCustomHandlers = {
|
|
144
|
+
setUserId: (id: string) => void;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const useTracking = createReactEventTrackingHook<AnalyticsEvents, MyCustomHandlers>();
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
2. Create a custom Root using [TrackRoot.factory](#multiple-trackers--factory):
|
|
151
|
+
```tsx
|
|
152
|
+
import { TrackRoot } from 'react-event-tracking';
|
|
153
|
+
|
|
154
|
+
const CustomTrackRoot = TrackRoot.factory<MyCustomHandlers>({
|
|
155
|
+
onEvent: (name, params) => {
|
|
156
|
+
amplitude.logEvent(name, params);
|
|
157
|
+
},
|
|
158
|
+
customHandlers: {
|
|
159
|
+
setUserId: (id) => {
|
|
160
|
+
amplitude.setUserId(id);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const App = () => (
|
|
166
|
+
<CustomTrackRoot>
|
|
167
|
+
<Main />
|
|
168
|
+
</CustomTrackRoot>
|
|
169
|
+
);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
3. Use it in your components:
|
|
173
|
+
```tsx
|
|
174
|
+
const Profile = () => {
|
|
175
|
+
const { track, setUserId } = useTracking();
|
|
176
|
+
|
|
177
|
+
const handleLogin = () => {
|
|
178
|
+
track.login_screen.logged_in({ timePassed: 3000 });
|
|
179
|
+
setUserId('user_123')
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<button onClick={handleLogin}>
|
|
184
|
+
Login
|
|
185
|
+
</button>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
84
190
|
## Advanced Usage
|
|
85
191
|
|
|
86
192
|
### Multiple Trackers & Factory
|
|
@@ -91,13 +197,13 @@ Use `TrackRoot.factory` to create reusable tracker components:
|
|
|
91
197
|
|
|
92
198
|
1. Create specific trackers
|
|
93
199
|
```tsx
|
|
94
|
-
const TrackRootGoogle = TrackRoot.factory(
|
|
95
|
-
(name, params) => gtag('event', name, params)
|
|
96
|
-
);
|
|
200
|
+
const TrackRootGoogle = TrackRoot.factory({
|
|
201
|
+
onEvent: (name, params) => gtag('event', name, params)
|
|
202
|
+
});
|
|
97
203
|
|
|
98
|
-
const TrackRootAmplitude = TrackRoot.factory(
|
|
99
|
-
(name, params) => amplitude.logEvent(name, params)
|
|
100
|
-
);
|
|
204
|
+
const TrackRootAmplitude = TrackRoot.factory({
|
|
205
|
+
onEvent: (name, params) => amplitude.logEvent(name, params)
|
|
206
|
+
});
|
|
101
207
|
```
|
|
102
208
|
|
|
103
209
|
2. Compose them in your app
|
|
@@ -113,19 +219,19 @@ const App = () => (
|
|
|
113
219
|
|
|
114
220
|
### Filtering Events
|
|
115
221
|
|
|
116
|
-
You can control which events are sent to which provider using the `filter` prop
|
|
222
|
+
You can control which events are sent to which provider using the `filter` prop in `factory`. If the filter returns `false`, the event is skipped for that tracker but continues to bubble up to others.
|
|
117
223
|
|
|
118
224
|
```tsx
|
|
119
225
|
// Google Analytics: only track page_* events
|
|
120
|
-
const TrackRootGoogle = TrackRoot.factory(
|
|
121
|
-
(name, params) => gtag('event', name, params),
|
|
122
|
-
(name) => name.startsWith('page_')
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
// Amplitude: track everything
|
|
126
|
-
const TrackRootAmplitude = TrackRoot.factory(
|
|
127
|
-
(name, params) =>
|
|
128
|
-
);
|
|
226
|
+
const TrackRootGoogle = TrackRoot.factory({
|
|
227
|
+
onEvent: (name, params) => gtag('event', name, params),
|
|
228
|
+
filter: (name) => name.startsWith('page_')
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Amplitude: track everything
|
|
232
|
+
const TrackRootAmplitude = TrackRoot.factory({
|
|
233
|
+
onEvent: (name, params) => amplitude.logEvent(name, params),
|
|
234
|
+
});
|
|
129
235
|
```
|
|
130
236
|
|
|
131
237
|
Compose them in your app:
|
|
@@ -142,16 +248,15 @@ const App = () => (
|
|
|
142
248
|
|
|
143
249
|
### Transforming Events
|
|
144
250
|
|
|
145
|
-
You can modify the event name or parameters before they
|
|
251
|
+
You can modify the event name or parameters before they are sent to the handler using the `transform` prop in `factory`.
|
|
146
252
|
|
|
147
253
|
Note: Transformations apply locally and do not affect the event bubbling up to parent providers.
|
|
148
254
|
|
|
149
255
|
```tsx
|
|
150
256
|
// GDPR Tracker
|
|
151
|
-
const
|
|
152
|
-
(name, params) => amplitude.logEvent(name, params),
|
|
153
|
-
|
|
154
|
-
(name, params) => {
|
|
257
|
+
const AmplitudeUS = TrackRoot.factory({
|
|
258
|
+
onEvent: (name, params) => amplitude.logEvent(name, params),
|
|
259
|
+
transform: (name, params) => {
|
|
155
260
|
if (params?.userRegion === 'EU') {
|
|
156
261
|
// Remove PII (Personally Identifiable Information)
|
|
157
262
|
const { userId, email, ...safeParams } = params;
|
|
@@ -162,7 +267,7 @@ const AmpltitudeUS = TrackRoot.factory(
|
|
|
162
267
|
}
|
|
163
268
|
return { eventName: name, params };
|
|
164
269
|
}
|
|
165
|
-
);
|
|
270
|
+
});
|
|
166
271
|
```
|
|
167
272
|
|
|
168
273
|
### TypeScript Generics Support
|
|
@@ -170,13 +275,12 @@ const AmpltitudeUS = TrackRoot.factory(
|
|
|
170
275
|
`TrackProvider` supports generics for strict typing of the passed parameters.
|
|
171
276
|
|
|
172
277
|
```tsx
|
|
173
|
-
interface
|
|
174
|
-
|
|
175
|
-
isAuthorized: boolean;
|
|
278
|
+
interface ScreenParams {
|
|
279
|
+
screen: "dashboard" | "authScreen"
|
|
176
280
|
}
|
|
177
281
|
|
|
178
282
|
const MyPage = () => (
|
|
179
|
-
<TrackProvider<
|
|
283
|
+
<TrackProvider<ScreenParams> params={{ screen: 'dashboard' }}>
|
|
180
284
|
<Content />
|
|
181
285
|
</TrackProvider>
|
|
182
286
|
);
|
|
@@ -204,12 +308,12 @@ A common pattern is to layer data from global to specific. Here is how parameter
|
|
|
204
308
|
</TrackRoot>
|
|
205
309
|
|
|
206
310
|
// Inside AddToCartButton:
|
|
207
|
-
const {
|
|
311
|
+
const { track } = useReactEventTracking();
|
|
208
312
|
|
|
209
313
|
// 4. EVENT: Action-specific data
|
|
210
314
|
// When clicked, we only pass what changed right now.
|
|
211
315
|
const handleClick = () => {
|
|
212
|
-
|
|
316
|
+
track('add_to_cart', { quantity: 1 });
|
|
213
317
|
};
|
|
214
318
|
```
|
|
215
319
|
|
package/dist/index.cjs
CHANGED
|
@@ -20,20 +20,27 @@ function parseEventArgs(eventNameOrObject, eventParams) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const TrackContext = React__default.createContext(null);
|
|
23
|
-
const
|
|
23
|
+
const useReactEventTracking = () => {
|
|
24
24
|
const ctx = React.useContext(TrackContext);
|
|
25
25
|
if (!ctx) {
|
|
26
|
-
throw new Error("
|
|
26
|
+
throw new Error("useReactEventTracking must be used within TrackRoot");
|
|
27
27
|
}
|
|
28
28
|
return ctx;
|
|
29
29
|
};
|
|
30
30
|
const EmptyParams = {};
|
|
31
|
-
const TrackRootComponent = ({
|
|
31
|
+
const TrackRootComponent = ({
|
|
32
|
+
onEvent,
|
|
33
|
+
children,
|
|
34
|
+
filter,
|
|
35
|
+
transform,
|
|
36
|
+
customHandlers
|
|
37
|
+
}) => {
|
|
32
38
|
const parentCtx = React.useContext(TrackContext);
|
|
33
39
|
const onEventRef = useFreshRef(onEvent);
|
|
34
40
|
const filterRef = useFreshRef(filter);
|
|
35
41
|
const transformRef = useFreshRef(transform);
|
|
36
|
-
|
|
42
|
+
const customHandlersRef = useFreshRef(customHandlers);
|
|
43
|
+
function track(eventNameOrObject, eventParams) {
|
|
37
44
|
const { eventName, params: incomingParams } = parseEventArgs(
|
|
38
45
|
eventNameOrObject,
|
|
39
46
|
eventParams
|
|
@@ -68,20 +75,44 @@ const TrackRootComponent = ({ onEvent, children, filter, transform }) => {
|
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
if (parentCtx) {
|
|
71
|
-
parentCtx.
|
|
78
|
+
parentCtx.track(eventName, incomingParams);
|
|
72
79
|
}
|
|
73
80
|
}
|
|
74
|
-
const
|
|
75
|
-
|
|
81
|
+
const value = React.useMemo(() => {
|
|
82
|
+
return new Proxy({}, {
|
|
83
|
+
get(_, prop) {
|
|
84
|
+
if (prop === track.name) return track;
|
|
85
|
+
const handler = customHandlersRef.current?.[prop];
|
|
86
|
+
if (typeof handler === "function") {
|
|
87
|
+
return (...args) => handler(...args);
|
|
88
|
+
}
|
|
89
|
+
return void 0;
|
|
90
|
+
},
|
|
91
|
+
has(_, prop) {
|
|
92
|
+
return prop === track.name || (customHandlersRef.current ? prop in customHandlersRef.current : false);
|
|
93
|
+
},
|
|
94
|
+
ownKeys() {
|
|
95
|
+
const customKeys = customHandlersRef.current ? Object.keys(customHandlersRef.current) : [];
|
|
96
|
+
return Array.from(/* @__PURE__ */ new Set([track.name, ...customKeys]));
|
|
97
|
+
},
|
|
98
|
+
getOwnPropertyDescriptor(_, prop) {
|
|
99
|
+
return {
|
|
100
|
+
enumerable: true,
|
|
101
|
+
configurable: true
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}, [parentCtx]);
|
|
76
106
|
return /* @__PURE__ */ React__default.createElement(TrackContext.Provider, { value }, children);
|
|
77
107
|
};
|
|
78
|
-
const factory = (
|
|
108
|
+
const factory = (args) => {
|
|
79
109
|
return (props) => /* @__PURE__ */ React__default.createElement(
|
|
80
110
|
TrackRootComponent,
|
|
81
111
|
{
|
|
82
|
-
onEvent,
|
|
83
|
-
filter,
|
|
84
|
-
transform,
|
|
112
|
+
onEvent: args.onEvent,
|
|
113
|
+
filter: args.filter,
|
|
114
|
+
transform: args.transform,
|
|
115
|
+
customHandlers: args.customHandlers,
|
|
85
116
|
...props
|
|
86
117
|
}
|
|
87
118
|
);
|
|
@@ -91,21 +122,20 @@ const TrackProvider = ({
|
|
|
91
122
|
params,
|
|
92
123
|
children
|
|
93
124
|
}) => {
|
|
94
|
-
const ctx =
|
|
125
|
+
const ctx = useReactEventTracking();
|
|
95
126
|
const paramsRef = useFreshRef(params);
|
|
96
|
-
function
|
|
127
|
+
function track(eventNameOrObject, eventParams) {
|
|
97
128
|
const { eventName, params: incomingParams } = parseEventArgs(
|
|
98
129
|
eventNameOrObject,
|
|
99
130
|
eventParams
|
|
100
131
|
);
|
|
101
132
|
const currentParams = paramsRef.current;
|
|
102
|
-
ctx.
|
|
133
|
+
ctx.track(eventName, {
|
|
103
134
|
...currentParams,
|
|
104
135
|
...incomingParams
|
|
105
136
|
});
|
|
106
137
|
}
|
|
107
|
-
const
|
|
108
|
-
const value = React.useMemo(() => ({ sendEvent: sendEventCached }), [sendEventCached]);
|
|
138
|
+
const value = React.useMemo(() => ({ ...ctx, track }), [ctx]);
|
|
109
139
|
return /* @__PURE__ */ React__default.createElement(TrackContext.Provider, { value }, children);
|
|
110
140
|
};
|
|
111
141
|
function useFreshRef(data) {
|
|
@@ -115,7 +145,7 @@ function useFreshRef(data) {
|
|
|
115
145
|
}
|
|
116
146
|
|
|
117
147
|
function useMountEvent(eventNameOrObject, eventParams) {
|
|
118
|
-
const {
|
|
148
|
+
const { track } = useReactEventTracking();
|
|
119
149
|
const counterRef = React.useRef(0);
|
|
120
150
|
const { eventName, params } = parseEventArgs(eventNameOrObject, eventParams);
|
|
121
151
|
React.useEffect(() => {
|
|
@@ -123,11 +153,40 @@ function useMountEvent(eventNameOrObject, eventParams) {
|
|
|
123
153
|
return;
|
|
124
154
|
}
|
|
125
155
|
counterRef.current++;
|
|
126
|
-
|
|
156
|
+
track(eventName, params);
|
|
127
157
|
}, []);
|
|
128
158
|
}
|
|
129
159
|
|
|
160
|
+
function createTracker(path = [], track) {
|
|
161
|
+
return new Proxy(() => {
|
|
162
|
+
}, {
|
|
163
|
+
get(_, prop) {
|
|
164
|
+
return createTracker([...path, prop], track);
|
|
165
|
+
},
|
|
166
|
+
apply(_, __, [params]) {
|
|
167
|
+
const eventName = path.join(".");
|
|
168
|
+
track(eventName, params);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function createReactEventTrackingHook() {
|
|
174
|
+
function useTracking(scope) {
|
|
175
|
+
const ctx = useReactEventTracking();
|
|
176
|
+
const tracker = React.useMemo(() => {
|
|
177
|
+
const prefix = scope ? [String(scope)] : [];
|
|
178
|
+
return {
|
|
179
|
+
...ctx,
|
|
180
|
+
track: createTracker(prefix, ctx.track)
|
|
181
|
+
};
|
|
182
|
+
}, [ctx, scope]);
|
|
183
|
+
return tracker;
|
|
184
|
+
}
|
|
185
|
+
return useTracking;
|
|
186
|
+
}
|
|
187
|
+
|
|
130
188
|
exports.TrackProvider = TrackProvider;
|
|
131
189
|
exports.TrackRoot = TrackRoot;
|
|
190
|
+
exports.createReactEventTrackingHook = createReactEventTrackingHook;
|
|
132
191
|
exports.useMountEvent = useMountEvent;
|
|
133
|
-
exports.
|
|
192
|
+
exports.useReactEventTracking = useReactEventTracking;
|
package/dist/index.d.cts
CHANGED
|
@@ -2,13 +2,19 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import { PropsWithChildren } from 'react';
|
|
3
3
|
|
|
4
4
|
type EventParams<T extends Record<string, any> = Record<string, any>> = T;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
type EventsMap = Record<string, any>;
|
|
6
|
+
type AnyFunction = (...args: any[]) => any;
|
|
7
|
+
type IsLeaf<T> = T extends Record<string, any> ? (keyof T extends never ? true : T[keyof T] extends Record<string, any> ? false : true) : true;
|
|
8
|
+
type FlatTracker<T> = {
|
|
9
|
+
[K in keyof T]: IsLeaf<T[K]> extends true ? (params: T[K]) => void : FlatTracker<T[K]>;
|
|
10
|
+
};
|
|
11
|
+
interface TrackContextValueLegacy {
|
|
12
|
+
track(eventName: string, params?: EventParams): void;
|
|
13
|
+
track(event: EventObject): void;
|
|
8
14
|
}
|
|
9
|
-
type EventObject = {
|
|
10
|
-
eventName:
|
|
11
|
-
params?:
|
|
15
|
+
type EventObject<E extends string = string, P = EventParams> = {
|
|
16
|
+
eventName: E;
|
|
17
|
+
params?: P;
|
|
12
18
|
};
|
|
13
19
|
type EventFilter = (eventName: string, params: EventParams) => boolean;
|
|
14
20
|
type TransformedEvent = {
|
|
@@ -17,14 +23,15 @@ type TransformedEvent = {
|
|
|
17
23
|
};
|
|
18
24
|
type EventTransformer = (eventName: string, params: EventParams) => TransformedEvent;
|
|
19
25
|
|
|
20
|
-
declare const
|
|
21
|
-
type TrackRootProps = PropsWithChildren<{
|
|
26
|
+
declare const useReactEventTracking: () => TrackContextValueLegacy;
|
|
27
|
+
type TrackRootProps<CustomHandlers extends Record<string, AnyFunction>> = PropsWithChildren<{
|
|
22
28
|
onEvent: (eventName: string, params: EventParams) => void;
|
|
23
29
|
filter?: EventFilter;
|
|
24
30
|
transform?: EventTransformer;
|
|
31
|
+
customHandlers?: CustomHandlers;
|
|
25
32
|
}>;
|
|
26
|
-
declare const TrackRoot: (({ onEvent, children, filter, transform }: TrackRootProps) => react_jsx_runtime.JSX.Element) & {
|
|
27
|
-
factory:
|
|
33
|
+
declare const TrackRoot: (<CustomHandlers extends Record<string, AnyFunction> = {}>({ onEvent, children, filter, transform, customHandlers }: TrackRootProps<CustomHandlers>) => react_jsx_runtime.JSX.Element) & {
|
|
34
|
+
factory: <T extends Record<string, AnyFunction> = {}>(args: TrackRootProps<T>) => (props: Omit<TrackRootProps<T>, "onEvent" | "filter" | "transform" | "customHandlers">) => react_jsx_runtime.JSX.Element;
|
|
28
35
|
};
|
|
29
36
|
declare const TrackProvider: <T extends Record<string, any>>({ params, children }: PropsWithChildren<{
|
|
30
37
|
params: EventParams<T>;
|
|
@@ -33,4 +40,13 @@ declare const TrackProvider: <T extends Record<string, any>>({ params, children
|
|
|
33
40
|
declare function useMountEvent(eventName: string, params?: EventParams): void;
|
|
34
41
|
declare function useMountEvent(event: EventObject): void;
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
declare function createReactEventTrackingHook<Map extends EventsMap, CustomHandlers extends Record<string, AnyFunction> = {}>(): {
|
|
44
|
+
(): {
|
|
45
|
+
track: FlatTracker<Map>;
|
|
46
|
+
} & CustomHandlers;
|
|
47
|
+
<K extends keyof Map>(scope: K): {
|
|
48
|
+
track: FlatTracker<Map>[K];
|
|
49
|
+
} & CustomHandlers;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export { TrackProvider, TrackRoot, createReactEventTrackingHook, useMountEvent, useReactEventTracking };
|
package/dist/index.d.mts
CHANGED
|
@@ -2,13 +2,19 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import { PropsWithChildren } from 'react';
|
|
3
3
|
|
|
4
4
|
type EventParams<T extends Record<string, any> = Record<string, any>> = T;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
type EventsMap = Record<string, any>;
|
|
6
|
+
type AnyFunction = (...args: any[]) => any;
|
|
7
|
+
type IsLeaf<T> = T extends Record<string, any> ? (keyof T extends never ? true : T[keyof T] extends Record<string, any> ? false : true) : true;
|
|
8
|
+
type FlatTracker<T> = {
|
|
9
|
+
[K in keyof T]: IsLeaf<T[K]> extends true ? (params: T[K]) => void : FlatTracker<T[K]>;
|
|
10
|
+
};
|
|
11
|
+
interface TrackContextValueLegacy {
|
|
12
|
+
track(eventName: string, params?: EventParams): void;
|
|
13
|
+
track(event: EventObject): void;
|
|
8
14
|
}
|
|
9
|
-
type EventObject = {
|
|
10
|
-
eventName:
|
|
11
|
-
params?:
|
|
15
|
+
type EventObject<E extends string = string, P = EventParams> = {
|
|
16
|
+
eventName: E;
|
|
17
|
+
params?: P;
|
|
12
18
|
};
|
|
13
19
|
type EventFilter = (eventName: string, params: EventParams) => boolean;
|
|
14
20
|
type TransformedEvent = {
|
|
@@ -17,14 +23,15 @@ type TransformedEvent = {
|
|
|
17
23
|
};
|
|
18
24
|
type EventTransformer = (eventName: string, params: EventParams) => TransformedEvent;
|
|
19
25
|
|
|
20
|
-
declare const
|
|
21
|
-
type TrackRootProps = PropsWithChildren<{
|
|
26
|
+
declare const useReactEventTracking: () => TrackContextValueLegacy;
|
|
27
|
+
type TrackRootProps<CustomHandlers extends Record<string, AnyFunction>> = PropsWithChildren<{
|
|
22
28
|
onEvent: (eventName: string, params: EventParams) => void;
|
|
23
29
|
filter?: EventFilter;
|
|
24
30
|
transform?: EventTransformer;
|
|
31
|
+
customHandlers?: CustomHandlers;
|
|
25
32
|
}>;
|
|
26
|
-
declare const TrackRoot: (({ onEvent, children, filter, transform }: TrackRootProps) => react_jsx_runtime.JSX.Element) & {
|
|
27
|
-
factory:
|
|
33
|
+
declare const TrackRoot: (<CustomHandlers extends Record<string, AnyFunction> = {}>({ onEvent, children, filter, transform, customHandlers }: TrackRootProps<CustomHandlers>) => react_jsx_runtime.JSX.Element) & {
|
|
34
|
+
factory: <T extends Record<string, AnyFunction> = {}>(args: TrackRootProps<T>) => (props: Omit<TrackRootProps<T>, "onEvent" | "filter" | "transform" | "customHandlers">) => react_jsx_runtime.JSX.Element;
|
|
28
35
|
};
|
|
29
36
|
declare const TrackProvider: <T extends Record<string, any>>({ params, children }: PropsWithChildren<{
|
|
30
37
|
params: EventParams<T>;
|
|
@@ -33,4 +40,13 @@ declare const TrackProvider: <T extends Record<string, any>>({ params, children
|
|
|
33
40
|
declare function useMountEvent(eventName: string, params?: EventParams): void;
|
|
34
41
|
declare function useMountEvent(event: EventObject): void;
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
declare function createReactEventTrackingHook<Map extends EventsMap, CustomHandlers extends Record<string, AnyFunction> = {}>(): {
|
|
44
|
+
(): {
|
|
45
|
+
track: FlatTracker<Map>;
|
|
46
|
+
} & CustomHandlers;
|
|
47
|
+
<K extends keyof Map>(scope: K): {
|
|
48
|
+
track: FlatTracker<Map>[K];
|
|
49
|
+
} & CustomHandlers;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export { TrackProvider, TrackRoot, createReactEventTrackingHook, useMountEvent, useReactEventTracking };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,13 +2,19 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import { PropsWithChildren } from 'react';
|
|
3
3
|
|
|
4
4
|
type EventParams<T extends Record<string, any> = Record<string, any>> = T;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
type EventsMap = Record<string, any>;
|
|
6
|
+
type AnyFunction = (...args: any[]) => any;
|
|
7
|
+
type IsLeaf<T> = T extends Record<string, any> ? (keyof T extends never ? true : T[keyof T] extends Record<string, any> ? false : true) : true;
|
|
8
|
+
type FlatTracker<T> = {
|
|
9
|
+
[K in keyof T]: IsLeaf<T[K]> extends true ? (params: T[K]) => void : FlatTracker<T[K]>;
|
|
10
|
+
};
|
|
11
|
+
interface TrackContextValueLegacy {
|
|
12
|
+
track(eventName: string, params?: EventParams): void;
|
|
13
|
+
track(event: EventObject): void;
|
|
8
14
|
}
|
|
9
|
-
type EventObject = {
|
|
10
|
-
eventName:
|
|
11
|
-
params?:
|
|
15
|
+
type EventObject<E extends string = string, P = EventParams> = {
|
|
16
|
+
eventName: E;
|
|
17
|
+
params?: P;
|
|
12
18
|
};
|
|
13
19
|
type EventFilter = (eventName: string, params: EventParams) => boolean;
|
|
14
20
|
type TransformedEvent = {
|
|
@@ -17,14 +23,15 @@ type TransformedEvent = {
|
|
|
17
23
|
};
|
|
18
24
|
type EventTransformer = (eventName: string, params: EventParams) => TransformedEvent;
|
|
19
25
|
|
|
20
|
-
declare const
|
|
21
|
-
type TrackRootProps = PropsWithChildren<{
|
|
26
|
+
declare const useReactEventTracking: () => TrackContextValueLegacy;
|
|
27
|
+
type TrackRootProps<CustomHandlers extends Record<string, AnyFunction>> = PropsWithChildren<{
|
|
22
28
|
onEvent: (eventName: string, params: EventParams) => void;
|
|
23
29
|
filter?: EventFilter;
|
|
24
30
|
transform?: EventTransformer;
|
|
31
|
+
customHandlers?: CustomHandlers;
|
|
25
32
|
}>;
|
|
26
|
-
declare const TrackRoot: (({ onEvent, children, filter, transform }: TrackRootProps) => react_jsx_runtime.JSX.Element) & {
|
|
27
|
-
factory:
|
|
33
|
+
declare const TrackRoot: (<CustomHandlers extends Record<string, AnyFunction> = {}>({ onEvent, children, filter, transform, customHandlers }: TrackRootProps<CustomHandlers>) => react_jsx_runtime.JSX.Element) & {
|
|
34
|
+
factory: <T extends Record<string, AnyFunction> = {}>(args: TrackRootProps<T>) => (props: Omit<TrackRootProps<T>, "onEvent" | "filter" | "transform" | "customHandlers">) => react_jsx_runtime.JSX.Element;
|
|
28
35
|
};
|
|
29
36
|
declare const TrackProvider: <T extends Record<string, any>>({ params, children }: PropsWithChildren<{
|
|
30
37
|
params: EventParams<T>;
|
|
@@ -33,4 +40,13 @@ declare const TrackProvider: <T extends Record<string, any>>({ params, children
|
|
|
33
40
|
declare function useMountEvent(eventName: string, params?: EventParams): void;
|
|
34
41
|
declare function useMountEvent(event: EventObject): void;
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
declare function createReactEventTrackingHook<Map extends EventsMap, CustomHandlers extends Record<string, AnyFunction> = {}>(): {
|
|
44
|
+
(): {
|
|
45
|
+
track: FlatTracker<Map>;
|
|
46
|
+
} & CustomHandlers;
|
|
47
|
+
<K extends keyof Map>(scope: K): {
|
|
48
|
+
track: FlatTracker<Map>[K];
|
|
49
|
+
} & CustomHandlers;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export { TrackProvider, TrackRoot, createReactEventTrackingHook, useMountEvent, useReactEventTracking };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useContext,
|
|
1
|
+
import React, { useContext, useMemo, useRef, useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
function parseEventArgs(eventNameOrObject, eventParams) {
|
|
4
4
|
if (typeof eventNameOrObject === "object") {
|
|
@@ -14,20 +14,27 @@ function parseEventArgs(eventNameOrObject, eventParams) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const TrackContext = React.createContext(null);
|
|
17
|
-
const
|
|
17
|
+
const useReactEventTracking = () => {
|
|
18
18
|
const ctx = useContext(TrackContext);
|
|
19
19
|
if (!ctx) {
|
|
20
|
-
throw new Error("
|
|
20
|
+
throw new Error("useReactEventTracking must be used within TrackRoot");
|
|
21
21
|
}
|
|
22
22
|
return ctx;
|
|
23
23
|
};
|
|
24
24
|
const EmptyParams = {};
|
|
25
|
-
const TrackRootComponent = ({
|
|
25
|
+
const TrackRootComponent = ({
|
|
26
|
+
onEvent,
|
|
27
|
+
children,
|
|
28
|
+
filter,
|
|
29
|
+
transform,
|
|
30
|
+
customHandlers
|
|
31
|
+
}) => {
|
|
26
32
|
const parentCtx = useContext(TrackContext);
|
|
27
33
|
const onEventRef = useFreshRef(onEvent);
|
|
28
34
|
const filterRef = useFreshRef(filter);
|
|
29
35
|
const transformRef = useFreshRef(transform);
|
|
30
|
-
|
|
36
|
+
const customHandlersRef = useFreshRef(customHandlers);
|
|
37
|
+
function track(eventNameOrObject, eventParams) {
|
|
31
38
|
const { eventName, params: incomingParams } = parseEventArgs(
|
|
32
39
|
eventNameOrObject,
|
|
33
40
|
eventParams
|
|
@@ -62,20 +69,44 @@ const TrackRootComponent = ({ onEvent, children, filter, transform }) => {
|
|
|
62
69
|
}
|
|
63
70
|
}
|
|
64
71
|
if (parentCtx) {
|
|
65
|
-
parentCtx.
|
|
72
|
+
parentCtx.track(eventName, incomingParams);
|
|
66
73
|
}
|
|
67
74
|
}
|
|
68
|
-
const
|
|
69
|
-
|
|
75
|
+
const value = useMemo(() => {
|
|
76
|
+
return new Proxy({}, {
|
|
77
|
+
get(_, prop) {
|
|
78
|
+
if (prop === track.name) return track;
|
|
79
|
+
const handler = customHandlersRef.current?.[prop];
|
|
80
|
+
if (typeof handler === "function") {
|
|
81
|
+
return (...args) => handler(...args);
|
|
82
|
+
}
|
|
83
|
+
return void 0;
|
|
84
|
+
},
|
|
85
|
+
has(_, prop) {
|
|
86
|
+
return prop === track.name || (customHandlersRef.current ? prop in customHandlersRef.current : false);
|
|
87
|
+
},
|
|
88
|
+
ownKeys() {
|
|
89
|
+
const customKeys = customHandlersRef.current ? Object.keys(customHandlersRef.current) : [];
|
|
90
|
+
return Array.from(/* @__PURE__ */ new Set([track.name, ...customKeys]));
|
|
91
|
+
},
|
|
92
|
+
getOwnPropertyDescriptor(_, prop) {
|
|
93
|
+
return {
|
|
94
|
+
enumerable: true,
|
|
95
|
+
configurable: true
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}, [parentCtx]);
|
|
70
100
|
return /* @__PURE__ */ React.createElement(TrackContext.Provider, { value }, children);
|
|
71
101
|
};
|
|
72
|
-
const factory = (
|
|
102
|
+
const factory = (args) => {
|
|
73
103
|
return (props) => /* @__PURE__ */ React.createElement(
|
|
74
104
|
TrackRootComponent,
|
|
75
105
|
{
|
|
76
|
-
onEvent,
|
|
77
|
-
filter,
|
|
78
|
-
transform,
|
|
106
|
+
onEvent: args.onEvent,
|
|
107
|
+
filter: args.filter,
|
|
108
|
+
transform: args.transform,
|
|
109
|
+
customHandlers: args.customHandlers,
|
|
79
110
|
...props
|
|
80
111
|
}
|
|
81
112
|
);
|
|
@@ -85,21 +116,20 @@ const TrackProvider = ({
|
|
|
85
116
|
params,
|
|
86
117
|
children
|
|
87
118
|
}) => {
|
|
88
|
-
const ctx =
|
|
119
|
+
const ctx = useReactEventTracking();
|
|
89
120
|
const paramsRef = useFreshRef(params);
|
|
90
|
-
function
|
|
121
|
+
function track(eventNameOrObject, eventParams) {
|
|
91
122
|
const { eventName, params: incomingParams } = parseEventArgs(
|
|
92
123
|
eventNameOrObject,
|
|
93
124
|
eventParams
|
|
94
125
|
);
|
|
95
126
|
const currentParams = paramsRef.current;
|
|
96
|
-
ctx.
|
|
127
|
+
ctx.track(eventName, {
|
|
97
128
|
...currentParams,
|
|
98
129
|
...incomingParams
|
|
99
130
|
});
|
|
100
131
|
}
|
|
101
|
-
const
|
|
102
|
-
const value = useMemo(() => ({ sendEvent: sendEventCached }), [sendEventCached]);
|
|
132
|
+
const value = useMemo(() => ({ ...ctx, track }), [ctx]);
|
|
103
133
|
return /* @__PURE__ */ React.createElement(TrackContext.Provider, { value }, children);
|
|
104
134
|
};
|
|
105
135
|
function useFreshRef(data) {
|
|
@@ -109,7 +139,7 @@ function useFreshRef(data) {
|
|
|
109
139
|
}
|
|
110
140
|
|
|
111
141
|
function useMountEvent(eventNameOrObject, eventParams) {
|
|
112
|
-
const {
|
|
142
|
+
const { track } = useReactEventTracking();
|
|
113
143
|
const counterRef = useRef(0);
|
|
114
144
|
const { eventName, params } = parseEventArgs(eventNameOrObject, eventParams);
|
|
115
145
|
useEffect(() => {
|
|
@@ -117,8 +147,36 @@ function useMountEvent(eventNameOrObject, eventParams) {
|
|
|
117
147
|
return;
|
|
118
148
|
}
|
|
119
149
|
counterRef.current++;
|
|
120
|
-
|
|
150
|
+
track(eventName, params);
|
|
121
151
|
}, []);
|
|
122
152
|
}
|
|
123
153
|
|
|
124
|
-
|
|
154
|
+
function createTracker(path = [], track) {
|
|
155
|
+
return new Proxy(() => {
|
|
156
|
+
}, {
|
|
157
|
+
get(_, prop) {
|
|
158
|
+
return createTracker([...path, prop], track);
|
|
159
|
+
},
|
|
160
|
+
apply(_, __, [params]) {
|
|
161
|
+
const eventName = path.join(".");
|
|
162
|
+
track(eventName, params);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function createReactEventTrackingHook() {
|
|
168
|
+
function useTracking(scope) {
|
|
169
|
+
const ctx = useReactEventTracking();
|
|
170
|
+
const tracker = useMemo(() => {
|
|
171
|
+
const prefix = scope ? [String(scope)] : [];
|
|
172
|
+
return {
|
|
173
|
+
...ctx,
|
|
174
|
+
track: createTracker(prefix, ctx.track)
|
|
175
|
+
};
|
|
176
|
+
}, [ctx, scope]);
|
|
177
|
+
return tracker;
|
|
178
|
+
}
|
|
179
|
+
return useTracking;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export { TrackProvider, TrackRoot, createReactEventTrackingHook, useMountEvent, useReactEventTracking };
|