@grainql/analytics-web 1.7.4 → 2.1.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 +71 -718
- package/dist/activity.d.ts +59 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/cjs/activity.d.ts +59 -0
- package/dist/cjs/activity.d.ts.map +1 -0
- package/dist/cjs/activity.js +131 -0
- package/dist/cjs/activity.js.map +1 -0
- package/dist/cjs/consent.d.ts +68 -0
- package/dist/cjs/consent.d.ts.map +1 -0
- package/dist/cjs/consent.js +191 -0
- package/dist/cjs/consent.js.map +1 -0
- package/dist/cjs/cookies.d.ts +28 -0
- package/dist/cjs/cookies.d.ts.map +1 -0
- package/dist/cjs/cookies.js +95 -0
- package/dist/cjs/cookies.js.map +1 -0
- package/dist/cjs/heartbeat.d.ts +42 -0
- package/dist/cjs/heartbeat.d.ts.map +1 -0
- package/dist/cjs/heartbeat.js +92 -0
- package/dist/cjs/heartbeat.js.map +1 -0
- package/dist/cjs/index.d.ts +100 -3
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/page-tracking.d.ts +60 -0
- package/dist/cjs/page-tracking.d.ts.map +1 -0
- package/dist/cjs/page-tracking.js +180 -0
- package/dist/cjs/page-tracking.js.map +1 -0
- package/dist/cjs/react/GrainProvider.d.ts +11 -0
- package/dist/cjs/react/GrainProvider.d.ts.map +1 -0
- package/dist/cjs/react/GrainProvider.js +79 -0
- package/dist/cjs/react/GrainProvider.js.map +1 -0
- package/dist/cjs/react/components/ConsentBanner.d.ts +16 -0
- package/dist/cjs/react/components/ConsentBanner.d.ts.map +1 -0
- package/dist/cjs/react/components/ConsentBanner.js +112 -0
- package/dist/cjs/react/components/ConsentBanner.js.map +1 -0
- package/dist/cjs/react/components/CookieNotice.d.ts +12 -0
- package/dist/cjs/react/components/CookieNotice.d.ts.map +1 -0
- package/dist/cjs/react/components/CookieNotice.js +62 -0
- package/dist/cjs/react/components/CookieNotice.js.map +1 -0
- package/dist/cjs/react/components/PrivacyPreferenceCenter.d.ts +12 -0
- package/dist/cjs/react/components/PrivacyPreferenceCenter.d.ts.map +1 -0
- package/dist/cjs/react/components/PrivacyPreferenceCenter.js +120 -0
- package/dist/cjs/react/components/PrivacyPreferenceCenter.js.map +1 -0
- package/dist/cjs/react/context.d.ts +11 -0
- package/dist/cjs/react/context.d.ts.map +1 -0
- package/dist/cjs/react/context.js +43 -0
- package/dist/cjs/react/context.js.map +1 -0
- package/dist/cjs/react/hooks/useAllConfigs.d.ts +8 -0
- package/dist/cjs/react/hooks/useAllConfigs.d.ts.map +1 -0
- package/dist/cjs/react/hooks/useAllConfigs.js +112 -0
- package/dist/cjs/react/hooks/useAllConfigs.js.map +1 -0
- package/dist/cjs/react/hooks/useConfig.d.ts +9 -0
- package/dist/cjs/react/hooks/useConfig.d.ts.map +1 -0
- package/dist/cjs/react/hooks/useConfig.js +116 -0
- package/dist/cjs/react/hooks/useConfig.js.map +1 -0
- package/dist/cjs/react/hooks/useConsent.d.ts +13 -0
- package/dist/cjs/react/hooks/useConsent.d.ts.map +1 -0
- package/dist/cjs/react/hooks/useConsent.js +84 -0
- package/dist/cjs/react/hooks/useConsent.js.map +1 -0
- package/dist/cjs/react/hooks/useDataDeletion.d.ts +17 -0
- package/dist/cjs/react/hooks/useDataDeletion.d.ts.map +1 -0
- package/dist/cjs/react/hooks/useDataDeletion.js +117 -0
- package/dist/cjs/react/hooks/useDataDeletion.js.map +1 -0
- package/dist/cjs/react/hooks/useGrainAnalytics.d.ts +6 -0
- package/dist/cjs/react/hooks/useGrainAnalytics.d.ts.map +1 -0
- package/dist/cjs/react/hooks/useGrainAnalytics.js +50 -0
- package/dist/cjs/react/hooks/useGrainAnalytics.js.map +1 -0
- package/dist/cjs/react/hooks/usePrivacyPreferences.d.ts +15 -0
- package/dist/cjs/react/hooks/usePrivacyPreferences.d.ts.map +1 -0
- package/dist/cjs/react/hooks/usePrivacyPreferences.js +82 -0
- package/dist/cjs/react/hooks/usePrivacyPreferences.js.map +1 -0
- package/dist/cjs/react/hooks/useTrack.d.ts +9 -0
- package/dist/cjs/react/hooks/useTrack.d.ts.map +1 -0
- package/dist/cjs/react/hooks/useTrack.js +53 -0
- package/dist/cjs/react/hooks/useTrack.js.map +1 -0
- package/dist/cjs/react/index.d.ts +47 -0
- package/dist/cjs/react/index.d.ts.map +1 -0
- package/dist/cjs/react/index.js +59 -0
- package/dist/cjs/react/index.js.map +1 -0
- package/dist/cjs/react/types.d.ts +33 -0
- package/dist/cjs/react/types.d.ts.map +1 -0
- package/dist/cjs/react/types.js +6 -0
- package/dist/cjs/react/types.js.map +1 -0
- package/dist/consent.d.ts +68 -0
- package/dist/consent.d.ts.map +1 -0
- package/dist/cookies.d.ts +28 -0
- package/dist/cookies.d.ts.map +1 -0
- package/dist/esm/activity.d.ts +59 -0
- package/dist/esm/activity.d.ts.map +1 -0
- package/dist/esm/activity.js +127 -0
- package/dist/esm/activity.js.map +1 -0
- package/dist/esm/consent.d.ts +68 -0
- package/dist/esm/consent.d.ts.map +1 -0
- package/dist/esm/consent.js +187 -0
- package/dist/esm/consent.js.map +1 -0
- package/dist/esm/cookies.d.ts +28 -0
- package/dist/esm/cookies.d.ts.map +1 -0
- package/dist/esm/cookies.js +89 -0
- package/dist/esm/cookies.js.map +1 -0
- package/dist/esm/heartbeat.d.ts +42 -0
- package/dist/esm/heartbeat.d.ts.map +1 -0
- package/dist/esm/heartbeat.js +88 -0
- package/dist/esm/heartbeat.js.map +1 -0
- package/dist/esm/index.d.ts +100 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/page-tracking.d.ts +60 -0
- package/dist/esm/page-tracking.d.ts.map +1 -0
- package/dist/esm/page-tracking.js +176 -0
- package/dist/esm/page-tracking.js.map +1 -0
- package/dist/esm/react/GrainProvider.d.ts +11 -0
- package/dist/esm/react/GrainProvider.d.ts.map +1 -0
- package/dist/esm/react/GrainProvider.js +43 -0
- package/dist/esm/react/GrainProvider.js.map +1 -0
- package/dist/esm/react/components/ConsentBanner.d.ts +16 -0
- package/dist/esm/react/components/ConsentBanner.d.ts.map +1 -0
- package/dist/esm/react/components/ConsentBanner.js +76 -0
- package/dist/esm/react/components/ConsentBanner.js.map +1 -0
- package/dist/esm/react/components/CookieNotice.d.ts +12 -0
- package/dist/esm/react/components/CookieNotice.d.ts.map +1 -0
- package/dist/esm/react/components/CookieNotice.js +26 -0
- package/dist/esm/react/components/CookieNotice.js.map +1 -0
- package/dist/esm/react/components/PrivacyPreferenceCenter.d.ts +12 -0
- package/dist/esm/react/components/PrivacyPreferenceCenter.d.ts.map +1 -0
- package/dist/esm/react/components/PrivacyPreferenceCenter.js +84 -0
- package/dist/esm/react/components/PrivacyPreferenceCenter.js.map +1 -0
- package/dist/esm/react/context.d.ts +11 -0
- package/dist/esm/react/context.d.ts.map +1 -0
- package/dist/esm/react/context.js +7 -0
- package/dist/esm/react/context.js.map +1 -0
- package/dist/esm/react/hooks/useAllConfigs.d.ts +8 -0
- package/dist/esm/react/hooks/useAllConfigs.d.ts.map +1 -0
- package/dist/esm/react/hooks/useAllConfigs.js +76 -0
- package/dist/esm/react/hooks/useAllConfigs.js.map +1 -0
- package/dist/esm/react/hooks/useConfig.d.ts +9 -0
- package/dist/esm/react/hooks/useConfig.d.ts.map +1 -0
- package/dist/esm/react/hooks/useConfig.js +80 -0
- package/dist/esm/react/hooks/useConfig.js.map +1 -0
- package/dist/esm/react/hooks/useConsent.d.ts +13 -0
- package/dist/esm/react/hooks/useConsent.d.ts.map +1 -0
- package/dist/esm/react/hooks/useConsent.js +48 -0
- package/dist/esm/react/hooks/useConsent.js.map +1 -0
- package/dist/esm/react/hooks/useDataDeletion.d.ts +17 -0
- package/dist/esm/react/hooks/useDataDeletion.d.ts.map +1 -0
- package/dist/esm/react/hooks/useDataDeletion.js +81 -0
- package/dist/esm/react/hooks/useDataDeletion.js.map +1 -0
- package/dist/esm/react/hooks/useGrainAnalytics.d.ts +6 -0
- package/dist/esm/react/hooks/useGrainAnalytics.d.ts.map +1 -0
- package/dist/esm/react/hooks/useGrainAnalytics.js +14 -0
- package/dist/esm/react/hooks/useGrainAnalytics.js.map +1 -0
- package/dist/esm/react/hooks/usePrivacyPreferences.d.ts +15 -0
- package/dist/esm/react/hooks/usePrivacyPreferences.d.ts.map +1 -0
- package/dist/esm/react/hooks/usePrivacyPreferences.js +46 -0
- package/dist/esm/react/hooks/usePrivacyPreferences.js.map +1 -0
- package/dist/esm/react/hooks/useTrack.d.ts +9 -0
- package/dist/esm/react/hooks/useTrack.d.ts.map +1 -0
- package/dist/esm/react/hooks/useTrack.js +17 -0
- package/dist/esm/react/hooks/useTrack.js.map +1 -0
- package/dist/esm/react/index.d.ts +47 -0
- package/dist/esm/react/index.d.ts.map +1 -0
- package/dist/esm/react/index.js +45 -0
- package/dist/esm/react/index.js.map +1 -0
- package/dist/esm/react/types.d.ts +33 -0
- package/dist/esm/react/types.d.ts.map +1 -0
- package/dist/esm/react/types.js +5 -0
- package/dist/esm/react/types.js.map +1 -0
- package/dist/heartbeat.d.ts +42 -0
- package/dist/heartbeat.d.ts.map +1 -0
- package/dist/index.d.ts +100 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.global.dev.js +903 -12
- package/dist/index.global.dev.js.map +3 -3
- package/dist/index.global.js +2 -2
- package/dist/index.global.js.map +4 -4
- package/dist/index.js +321 -11
- package/dist/index.mjs +321 -11
- package/dist/page-tracking.d.ts +60 -0
- package/dist/page-tracking.d.ts.map +1 -0
- package/dist/react/activity.d.ts +59 -0
- package/dist/react/activity.d.ts.map +1 -0
- package/dist/react/activity.js +130 -0
- package/dist/react/activity.mjs +126 -0
- package/dist/react/consent.d.ts +68 -0
- package/dist/react/consent.d.ts.map +1 -0
- package/dist/react/consent.js +190 -0
- package/dist/react/consent.mjs +186 -0
- package/dist/react/cookies.d.ts +28 -0
- package/dist/react/cookies.d.ts.map +1 -0
- package/dist/react/cookies.js +94 -0
- package/dist/react/cookies.mjs +88 -0
- package/dist/react/heartbeat.d.ts +42 -0
- package/dist/react/heartbeat.d.ts.map +1 -0
- package/dist/react/heartbeat.js +91 -0
- package/dist/react/heartbeat.mjs +87 -0
- package/dist/react/index.d.ts +502 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +1491 -0
- package/dist/react/index.mjs +1486 -0
- package/dist/react/page-tracking.d.ts +60 -0
- package/dist/react/page-tracking.d.ts.map +1 -0
- package/dist/react/page-tracking.js +179 -0
- package/dist/react/page-tracking.mjs +175 -0
- package/dist/react/react/GrainProvider.d.ts +11 -0
- package/dist/react/react/GrainProvider.d.ts.map +1 -0
- package/dist/react/react/GrainProvider.js +45 -0
- package/dist/react/react/GrainProvider.mjs +42 -0
- package/dist/react/react/components/ConsentBanner.d.ts +16 -0
- package/dist/react/react/components/ConsentBanner.d.ts.map +1 -0
- package/dist/react/react/components/ConsentBanner.js +78 -0
- package/dist/react/react/components/ConsentBanner.mjs +75 -0
- package/dist/react/react/components/CookieNotice.d.ts +12 -0
- package/dist/react/react/components/CookieNotice.d.ts.map +1 -0
- package/dist/react/react/components/CookieNotice.js +28 -0
- package/dist/react/react/components/CookieNotice.mjs +25 -0
- package/dist/react/react/components/PrivacyPreferenceCenter.d.ts +12 -0
- package/dist/react/react/components/PrivacyPreferenceCenter.d.ts.map +1 -0
- package/dist/react/react/components/PrivacyPreferenceCenter.js +86 -0
- package/dist/react/react/components/PrivacyPreferenceCenter.mjs +83 -0
- package/dist/react/react/context.d.ts +11 -0
- package/dist/react/react/context.d.ts.map +1 -0
- package/dist/react/react/context.js +9 -0
- package/dist/react/react/context.mjs +6 -0
- package/dist/react/react/hooks/useAllConfigs.d.ts +8 -0
- package/dist/react/react/hooks/useAllConfigs.d.ts.map +1 -0
- package/dist/react/react/hooks/useAllConfigs.js +78 -0
- package/dist/react/react/hooks/useAllConfigs.mjs +75 -0
- package/dist/react/react/hooks/useConfig.d.ts +9 -0
- package/dist/react/react/hooks/useConfig.d.ts.map +1 -0
- package/dist/react/react/hooks/useConfig.js +82 -0
- package/dist/react/react/hooks/useConfig.mjs +79 -0
- package/dist/react/react/hooks/useConsent.d.ts +13 -0
- package/dist/react/react/hooks/useConsent.d.ts.map +1 -0
- package/dist/react/react/hooks/useConsent.js +50 -0
- package/dist/react/react/hooks/useConsent.mjs +47 -0
- package/dist/react/react/hooks/useDataDeletion.d.ts +17 -0
- package/dist/react/react/hooks/useDataDeletion.d.ts.map +1 -0
- package/dist/react/react/hooks/useDataDeletion.js +83 -0
- package/dist/react/react/hooks/useDataDeletion.mjs +80 -0
- package/dist/react/react/hooks/useGrainAnalytics.d.ts +6 -0
- package/dist/react/react/hooks/useGrainAnalytics.d.ts.map +1 -0
- package/dist/react/react/hooks/useGrainAnalytics.js +16 -0
- package/dist/react/react/hooks/useGrainAnalytics.mjs +13 -0
- package/dist/react/react/hooks/usePrivacyPreferences.d.ts +15 -0
- package/dist/react/react/hooks/usePrivacyPreferences.d.ts.map +1 -0
- package/dist/react/react/hooks/usePrivacyPreferences.js +48 -0
- package/dist/react/react/hooks/usePrivacyPreferences.mjs +45 -0
- package/dist/react/react/hooks/useTrack.d.ts +9 -0
- package/dist/react/react/hooks/useTrack.d.ts.map +1 -0
- package/dist/react/react/hooks/useTrack.js +19 -0
- package/dist/react/react/hooks/useTrack.mjs +16 -0
- package/dist/react/react/index.d.ts +47 -0
- package/dist/react/react/index.d.ts.map +1 -0
- package/dist/react/react/index.js +58 -0
- package/dist/react/react/index.mjs +44 -0
- package/dist/react/react/types.d.ts +33 -0
- package/dist/react/react/types.d.ts.map +1 -0
- package/dist/react/react/types.js +5 -0
- package/dist/react/react/types.mjs +4 -0
- package/package.json +20 -2
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Tracking for Grain Analytics
|
|
3
|
+
* Automatically tracks page views with consent-aware behavior
|
|
4
|
+
*/
|
|
5
|
+
export interface PageTrackingConfig {
|
|
6
|
+
stripQueryParams: boolean;
|
|
7
|
+
debug?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface PageTracker {
|
|
10
|
+
trackSystemEvent(eventName: string, properties: Record<string, unknown>): void;
|
|
11
|
+
hasConsent(category?: string): boolean;
|
|
12
|
+
getEffectiveUserId(): string;
|
|
13
|
+
getEphemeralSessionId(): string;
|
|
14
|
+
}
|
|
15
|
+
export declare class PageTrackingManager {
|
|
16
|
+
private config;
|
|
17
|
+
private tracker;
|
|
18
|
+
private isDestroyed;
|
|
19
|
+
private currentPath;
|
|
20
|
+
private originalPushState;
|
|
21
|
+
private originalReplaceState;
|
|
22
|
+
constructor(tracker: PageTracker, config: PageTrackingConfig);
|
|
23
|
+
/**
|
|
24
|
+
* Setup History API listeners (pushState, replaceState, popstate)
|
|
25
|
+
*/
|
|
26
|
+
private setupHistoryListeners;
|
|
27
|
+
/**
|
|
28
|
+
* Setup hash change listener
|
|
29
|
+
*/
|
|
30
|
+
private setupHashChangeListener;
|
|
31
|
+
/**
|
|
32
|
+
* Handle popstate event (back/forward navigation)
|
|
33
|
+
*/
|
|
34
|
+
private handlePopState;
|
|
35
|
+
/**
|
|
36
|
+
* Handle hash change event
|
|
37
|
+
*/
|
|
38
|
+
private handleHashChange;
|
|
39
|
+
/**
|
|
40
|
+
* Track the current page
|
|
41
|
+
*/
|
|
42
|
+
private trackCurrentPage;
|
|
43
|
+
/**
|
|
44
|
+
* Extract path from URL, optionally stripping query parameters
|
|
45
|
+
*/
|
|
46
|
+
private extractPath;
|
|
47
|
+
/**
|
|
48
|
+
* Get the current page path
|
|
49
|
+
*/
|
|
50
|
+
getCurrentPage(): string | null;
|
|
51
|
+
/**
|
|
52
|
+
* Manually track a page view (for custom navigation)
|
|
53
|
+
*/
|
|
54
|
+
trackPage(page: string, properties?: Record<string, unknown>): void;
|
|
55
|
+
/**
|
|
56
|
+
* Destroy the page tracker
|
|
57
|
+
*/
|
|
58
|
+
destroy(): void;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=page-tracking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-tracking.d.ts","sourceRoot":"","sources":["../../src/page-tracking.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/E,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,kBAAkB,IAAI,MAAM,CAAC;IAC7B,qBAAqB,IAAI,MAAM,CAAC;CACjC;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,oBAAoB,CAA4C;gBAE5D,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB;IAY5D;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAqB7B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAK/B;;OAEG;IACH,OAAO,CAAC,cAAc,CAGpB;IAEF;;OAEG;IACH,OAAO,CAAC,gBAAgB,CAGtB;IAEF;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmCxB;;OAEG;IACH,OAAO,CAAC,WAAW;IAmBnB;;OAEG;IACH,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAgCnE;;OAEG;IACH,OAAO,IAAI,IAAI;CAyBhB"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Page Tracking for Grain Analytics
|
|
4
|
+
* Automatically tracks page views with consent-aware behavior
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.PageTrackingManager = void 0;
|
|
8
|
+
class PageTrackingManager {
|
|
9
|
+
constructor(tracker, config) {
|
|
10
|
+
this.isDestroyed = false;
|
|
11
|
+
this.currentPath = null;
|
|
12
|
+
this.originalPushState = null;
|
|
13
|
+
this.originalReplaceState = null;
|
|
14
|
+
/**
|
|
15
|
+
* Handle popstate event (back/forward navigation)
|
|
16
|
+
*/
|
|
17
|
+
this.handlePopState = () => {
|
|
18
|
+
if (this.isDestroyed)
|
|
19
|
+
return;
|
|
20
|
+
this.trackCurrentPage();
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Handle hash change event
|
|
24
|
+
*/
|
|
25
|
+
this.handleHashChange = () => {
|
|
26
|
+
if (this.isDestroyed)
|
|
27
|
+
return;
|
|
28
|
+
this.trackCurrentPage();
|
|
29
|
+
};
|
|
30
|
+
this.tracker = tracker;
|
|
31
|
+
this.config = config;
|
|
32
|
+
// Track initial page load
|
|
33
|
+
this.trackCurrentPage();
|
|
34
|
+
// Setup listeners
|
|
35
|
+
this.setupHistoryListeners();
|
|
36
|
+
this.setupHashChangeListener();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Setup History API listeners (pushState, replaceState, popstate)
|
|
40
|
+
*/
|
|
41
|
+
setupHistoryListeners() {
|
|
42
|
+
if (typeof window === 'undefined' || typeof history === 'undefined')
|
|
43
|
+
return;
|
|
44
|
+
// Wrap pushState
|
|
45
|
+
this.originalPushState = history.pushState;
|
|
46
|
+
history.pushState = (state, title, url) => {
|
|
47
|
+
this.originalPushState?.call(history, state, title, url);
|
|
48
|
+
this.trackCurrentPage();
|
|
49
|
+
};
|
|
50
|
+
// Wrap replaceState
|
|
51
|
+
this.originalReplaceState = history.replaceState;
|
|
52
|
+
history.replaceState = (state, title, url) => {
|
|
53
|
+
this.originalReplaceState?.call(history, state, title, url);
|
|
54
|
+
this.trackCurrentPage();
|
|
55
|
+
};
|
|
56
|
+
// Listen to popstate (back/forward buttons)
|
|
57
|
+
window.addEventListener('popstate', this.handlePopState);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Setup hash change listener
|
|
61
|
+
*/
|
|
62
|
+
setupHashChangeListener() {
|
|
63
|
+
if (typeof window === 'undefined')
|
|
64
|
+
return;
|
|
65
|
+
window.addEventListener('hashchange', this.handleHashChange);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Track the current page
|
|
69
|
+
*/
|
|
70
|
+
trackCurrentPage() {
|
|
71
|
+
if (this.isDestroyed || typeof window === 'undefined')
|
|
72
|
+
return;
|
|
73
|
+
const page = this.extractPath(window.location.href);
|
|
74
|
+
// Don't track if it's the same page
|
|
75
|
+
if (page === this.currentPath) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.currentPath = page;
|
|
79
|
+
const hasConsent = this.tracker.hasConsent('analytics');
|
|
80
|
+
// Base properties (always included)
|
|
81
|
+
const properties = {
|
|
82
|
+
page,
|
|
83
|
+
timestamp: Date.now(),
|
|
84
|
+
};
|
|
85
|
+
// Enhanced properties when consent is granted
|
|
86
|
+
if (hasConsent) {
|
|
87
|
+
properties.referrer = document.referrer || '';
|
|
88
|
+
properties.title = document.title || '';
|
|
89
|
+
properties.full_url = window.location.href;
|
|
90
|
+
}
|
|
91
|
+
// Track the page view event
|
|
92
|
+
this.tracker.trackSystemEvent('page_view', properties);
|
|
93
|
+
if (this.config.debug) {
|
|
94
|
+
console.log('[Page Tracking] Tracked page view:', properties);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Extract path from URL, optionally stripping query parameters
|
|
99
|
+
*/
|
|
100
|
+
extractPath(url) {
|
|
101
|
+
try {
|
|
102
|
+
const urlObj = new URL(url);
|
|
103
|
+
let path = urlObj.pathname + urlObj.hash;
|
|
104
|
+
if (!this.config.stripQueryParams && urlObj.search) {
|
|
105
|
+
path += urlObj.search;
|
|
106
|
+
}
|
|
107
|
+
return path;
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
// If URL parsing fails, return the raw string
|
|
111
|
+
if (this.config.debug) {
|
|
112
|
+
console.warn('[Page Tracking] Failed to parse URL:', url, error);
|
|
113
|
+
}
|
|
114
|
+
return url;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get the current page path
|
|
119
|
+
*/
|
|
120
|
+
getCurrentPage() {
|
|
121
|
+
return this.currentPath;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Manually track a page view (for custom navigation)
|
|
125
|
+
*/
|
|
126
|
+
trackPage(page, properties) {
|
|
127
|
+
if (this.isDestroyed)
|
|
128
|
+
return;
|
|
129
|
+
const hasConsent = this.tracker.hasConsent('analytics');
|
|
130
|
+
// Base properties
|
|
131
|
+
const baseProperties = {
|
|
132
|
+
page,
|
|
133
|
+
timestamp: Date.now(),
|
|
134
|
+
...properties,
|
|
135
|
+
};
|
|
136
|
+
// Enhanced properties when consent is granted
|
|
137
|
+
if (hasConsent && typeof document !== 'undefined') {
|
|
138
|
+
if (!baseProperties.referrer) {
|
|
139
|
+
baseProperties.referrer = document.referrer || '';
|
|
140
|
+
}
|
|
141
|
+
if (!baseProperties.title) {
|
|
142
|
+
baseProperties.title = document.title || '';
|
|
143
|
+
}
|
|
144
|
+
if (!baseProperties.full_url && typeof window !== 'undefined') {
|
|
145
|
+
baseProperties.full_url = window.location.href;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
this.tracker.trackSystemEvent('page_view', baseProperties);
|
|
149
|
+
if (this.config.debug) {
|
|
150
|
+
console.log('[Page Tracking] Manually tracked page:', baseProperties);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Destroy the page tracker
|
|
155
|
+
*/
|
|
156
|
+
destroy() {
|
|
157
|
+
if (this.isDestroyed)
|
|
158
|
+
return;
|
|
159
|
+
// Restore original history methods
|
|
160
|
+
if (typeof history !== 'undefined') {
|
|
161
|
+
if (this.originalPushState) {
|
|
162
|
+
history.pushState = this.originalPushState;
|
|
163
|
+
}
|
|
164
|
+
if (this.originalReplaceState) {
|
|
165
|
+
history.replaceState = this.originalReplaceState;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Remove event listeners
|
|
169
|
+
if (typeof window !== 'undefined') {
|
|
170
|
+
window.removeEventListener('popstate', this.handlePopState);
|
|
171
|
+
window.removeEventListener('hashchange', this.handleHashChange);
|
|
172
|
+
}
|
|
173
|
+
this.isDestroyed = true;
|
|
174
|
+
if (this.config.debug) {
|
|
175
|
+
console.log('[Page Tracking] Destroyed');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.PageTrackingManager = PageTrackingManager;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Tracking for Grain Analytics
|
|
3
|
+
* Automatically tracks page views with consent-aware behavior
|
|
4
|
+
*/
|
|
5
|
+
export class PageTrackingManager {
|
|
6
|
+
constructor(tracker, config) {
|
|
7
|
+
this.isDestroyed = false;
|
|
8
|
+
this.currentPath = null;
|
|
9
|
+
this.originalPushState = null;
|
|
10
|
+
this.originalReplaceState = null;
|
|
11
|
+
/**
|
|
12
|
+
* Handle popstate event (back/forward navigation)
|
|
13
|
+
*/
|
|
14
|
+
this.handlePopState = () => {
|
|
15
|
+
if (this.isDestroyed)
|
|
16
|
+
return;
|
|
17
|
+
this.trackCurrentPage();
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Handle hash change event
|
|
21
|
+
*/
|
|
22
|
+
this.handleHashChange = () => {
|
|
23
|
+
if (this.isDestroyed)
|
|
24
|
+
return;
|
|
25
|
+
this.trackCurrentPage();
|
|
26
|
+
};
|
|
27
|
+
this.tracker = tracker;
|
|
28
|
+
this.config = config;
|
|
29
|
+
// Track initial page load
|
|
30
|
+
this.trackCurrentPage();
|
|
31
|
+
// Setup listeners
|
|
32
|
+
this.setupHistoryListeners();
|
|
33
|
+
this.setupHashChangeListener();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Setup History API listeners (pushState, replaceState, popstate)
|
|
37
|
+
*/
|
|
38
|
+
setupHistoryListeners() {
|
|
39
|
+
if (typeof window === 'undefined' || typeof history === 'undefined')
|
|
40
|
+
return;
|
|
41
|
+
// Wrap pushState
|
|
42
|
+
this.originalPushState = history.pushState;
|
|
43
|
+
history.pushState = (state, title, url) => {
|
|
44
|
+
this.originalPushState?.call(history, state, title, url);
|
|
45
|
+
this.trackCurrentPage();
|
|
46
|
+
};
|
|
47
|
+
// Wrap replaceState
|
|
48
|
+
this.originalReplaceState = history.replaceState;
|
|
49
|
+
history.replaceState = (state, title, url) => {
|
|
50
|
+
this.originalReplaceState?.call(history, state, title, url);
|
|
51
|
+
this.trackCurrentPage();
|
|
52
|
+
};
|
|
53
|
+
// Listen to popstate (back/forward buttons)
|
|
54
|
+
window.addEventListener('popstate', this.handlePopState);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Setup hash change listener
|
|
58
|
+
*/
|
|
59
|
+
setupHashChangeListener() {
|
|
60
|
+
if (typeof window === 'undefined')
|
|
61
|
+
return;
|
|
62
|
+
window.addEventListener('hashchange', this.handleHashChange);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Track the current page
|
|
66
|
+
*/
|
|
67
|
+
trackCurrentPage() {
|
|
68
|
+
if (this.isDestroyed || typeof window === 'undefined')
|
|
69
|
+
return;
|
|
70
|
+
const page = this.extractPath(window.location.href);
|
|
71
|
+
// Don't track if it's the same page
|
|
72
|
+
if (page === this.currentPath) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this.currentPath = page;
|
|
76
|
+
const hasConsent = this.tracker.hasConsent('analytics');
|
|
77
|
+
// Base properties (always included)
|
|
78
|
+
const properties = {
|
|
79
|
+
page,
|
|
80
|
+
timestamp: Date.now(),
|
|
81
|
+
};
|
|
82
|
+
// Enhanced properties when consent is granted
|
|
83
|
+
if (hasConsent) {
|
|
84
|
+
properties.referrer = document.referrer || '';
|
|
85
|
+
properties.title = document.title || '';
|
|
86
|
+
properties.full_url = window.location.href;
|
|
87
|
+
}
|
|
88
|
+
// Track the page view event
|
|
89
|
+
this.tracker.trackSystemEvent('page_view', properties);
|
|
90
|
+
if (this.config.debug) {
|
|
91
|
+
console.log('[Page Tracking] Tracked page view:', properties);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Extract path from URL, optionally stripping query parameters
|
|
96
|
+
*/
|
|
97
|
+
extractPath(url) {
|
|
98
|
+
try {
|
|
99
|
+
const urlObj = new URL(url);
|
|
100
|
+
let path = urlObj.pathname + urlObj.hash;
|
|
101
|
+
if (!this.config.stripQueryParams && urlObj.search) {
|
|
102
|
+
path += urlObj.search;
|
|
103
|
+
}
|
|
104
|
+
return path;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
// If URL parsing fails, return the raw string
|
|
108
|
+
if (this.config.debug) {
|
|
109
|
+
console.warn('[Page Tracking] Failed to parse URL:', url, error);
|
|
110
|
+
}
|
|
111
|
+
return url;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the current page path
|
|
116
|
+
*/
|
|
117
|
+
getCurrentPage() {
|
|
118
|
+
return this.currentPath;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Manually track a page view (for custom navigation)
|
|
122
|
+
*/
|
|
123
|
+
trackPage(page, properties) {
|
|
124
|
+
if (this.isDestroyed)
|
|
125
|
+
return;
|
|
126
|
+
const hasConsent = this.tracker.hasConsent('analytics');
|
|
127
|
+
// Base properties
|
|
128
|
+
const baseProperties = {
|
|
129
|
+
page,
|
|
130
|
+
timestamp: Date.now(),
|
|
131
|
+
...properties,
|
|
132
|
+
};
|
|
133
|
+
// Enhanced properties when consent is granted
|
|
134
|
+
if (hasConsent && typeof document !== 'undefined') {
|
|
135
|
+
if (!baseProperties.referrer) {
|
|
136
|
+
baseProperties.referrer = document.referrer || '';
|
|
137
|
+
}
|
|
138
|
+
if (!baseProperties.title) {
|
|
139
|
+
baseProperties.title = document.title || '';
|
|
140
|
+
}
|
|
141
|
+
if (!baseProperties.full_url && typeof window !== 'undefined') {
|
|
142
|
+
baseProperties.full_url = window.location.href;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
this.tracker.trackSystemEvent('page_view', baseProperties);
|
|
146
|
+
if (this.config.debug) {
|
|
147
|
+
console.log('[Page Tracking] Manually tracked page:', baseProperties);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Destroy the page tracker
|
|
152
|
+
*/
|
|
153
|
+
destroy() {
|
|
154
|
+
if (this.isDestroyed)
|
|
155
|
+
return;
|
|
156
|
+
// Restore original history methods
|
|
157
|
+
if (typeof history !== 'undefined') {
|
|
158
|
+
if (this.originalPushState) {
|
|
159
|
+
history.pushState = this.originalPushState;
|
|
160
|
+
}
|
|
161
|
+
if (this.originalReplaceState) {
|
|
162
|
+
history.replaceState = this.originalReplaceState;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Remove event listeners
|
|
166
|
+
if (typeof window !== 'undefined') {
|
|
167
|
+
window.removeEventListener('popstate', this.handlePopState);
|
|
168
|
+
window.removeEventListener('hashchange', this.handleHashChange);
|
|
169
|
+
}
|
|
170
|
+
this.isDestroyed = true;
|
|
171
|
+
if (this.config.debug) {
|
|
172
|
+
console.log('[Page Tracking] Destroyed');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GrainProvider - Context provider for Grain Analytics
|
|
3
|
+
*
|
|
4
|
+
* Supports two patterns:
|
|
5
|
+
* 1. Provider-managed: Pass `config` prop, provider creates and manages client lifecycle
|
|
6
|
+
* 2. External client: Pass `client` prop, user manages lifecycle
|
|
7
|
+
*/
|
|
8
|
+
import * as React from 'react';
|
|
9
|
+
import type { GrainProviderProps } from './types';
|
|
10
|
+
export declare function GrainProvider({ children, client, config }: GrainProviderProps): React.JSX.Element;
|
|
11
|
+
//# sourceMappingURL=GrainProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrainProvider.d.ts","sourceRoot":"","sources":["../../../src/react/GrainProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,kBAAkB,qBA8C7E"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GrainProvider - Context provider for Grain Analytics
|
|
4
|
+
*
|
|
5
|
+
* Supports two patterns:
|
|
6
|
+
* 1. Provider-managed: Pass `config` prop, provider creates and manages client lifecycle
|
|
7
|
+
* 2. External client: Pass `client` prop, user manages lifecycle
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.GrainProvider = GrainProvider;
|
|
11
|
+
const React = require("react");
|
|
12
|
+
const index_1 = require("../index");
|
|
13
|
+
const context_1 = require("./context");
|
|
14
|
+
function GrainProvider({ children, client, config }) {
|
|
15
|
+
// Validate props
|
|
16
|
+
if (client && config) {
|
|
17
|
+
throw new Error('GrainProvider: Cannot provide both "client" and "config" props. ' +
|
|
18
|
+
'Use "client" for external management or "config" for provider-managed client.');
|
|
19
|
+
}
|
|
20
|
+
if (!client && !config) {
|
|
21
|
+
throw new Error('GrainProvider: Must provide either "client" or "config" prop. ' +
|
|
22
|
+
'Use "client" to pass an existing GrainAnalytics instance, or "config" to create one automatically.');
|
|
23
|
+
}
|
|
24
|
+
const isProviderManaged = Boolean(config);
|
|
25
|
+
const clientRef = React.useRef(null);
|
|
26
|
+
// Initialize client if config is provided
|
|
27
|
+
if (isProviderManaged && !clientRef.current && config) {
|
|
28
|
+
clientRef.current = new index_1.GrainAnalytics(config);
|
|
29
|
+
}
|
|
30
|
+
// Use external client if provided
|
|
31
|
+
const activeClient = client || clientRef.current;
|
|
32
|
+
if (!activeClient) {
|
|
33
|
+
throw new Error('GrainProvider: Failed to initialize client');
|
|
34
|
+
}
|
|
35
|
+
// Cleanup on unmount (only for provider-managed clients)
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
return () => {
|
|
38
|
+
if (isProviderManaged && clientRef.current) {
|
|
39
|
+
clientRef.current.destroy();
|
|
40
|
+
clientRef.current = null;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}, [isProviderManaged]);
|
|
44
|
+
return (React.createElement(context_1.GrainContext.Provider, { value: { client: activeClient, isProviderManaged } }, children));
|
|
45
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GrainProvider - Context provider for Grain Analytics
|
|
3
|
+
*
|
|
4
|
+
* Supports two patterns:
|
|
5
|
+
* 1. Provider-managed: Pass `config` prop, provider creates and manages client lifecycle
|
|
6
|
+
* 2. External client: Pass `client` prop, user manages lifecycle
|
|
7
|
+
*/
|
|
8
|
+
import * as React from 'react';
|
|
9
|
+
import { GrainAnalytics } from '../index';
|
|
10
|
+
import { GrainContext } from './context';
|
|
11
|
+
export function GrainProvider({ children, client, config }) {
|
|
12
|
+
// Validate props
|
|
13
|
+
if (client && config) {
|
|
14
|
+
throw new Error('GrainProvider: Cannot provide both "client" and "config" props. ' +
|
|
15
|
+
'Use "client" for external management or "config" for provider-managed client.');
|
|
16
|
+
}
|
|
17
|
+
if (!client && !config) {
|
|
18
|
+
throw new Error('GrainProvider: Must provide either "client" or "config" prop. ' +
|
|
19
|
+
'Use "client" to pass an existing GrainAnalytics instance, or "config" to create one automatically.');
|
|
20
|
+
}
|
|
21
|
+
const isProviderManaged = Boolean(config);
|
|
22
|
+
const clientRef = React.useRef(null);
|
|
23
|
+
// Initialize client if config is provided
|
|
24
|
+
if (isProviderManaged && !clientRef.current && config) {
|
|
25
|
+
clientRef.current = new GrainAnalytics(config);
|
|
26
|
+
}
|
|
27
|
+
// Use external client if provided
|
|
28
|
+
const activeClient = client || clientRef.current;
|
|
29
|
+
if (!activeClient) {
|
|
30
|
+
throw new Error('GrainProvider: Failed to initialize client');
|
|
31
|
+
}
|
|
32
|
+
// Cleanup on unmount (only for provider-managed clients)
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
return () => {
|
|
35
|
+
if (isProviderManaged && clientRef.current) {
|
|
36
|
+
clientRef.current.destroy();
|
|
37
|
+
clientRef.current = null;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}, [isProviderManaged]);
|
|
41
|
+
return (React.createElement(GrainContext.Provider, { value: { client: activeClient, isProviderManaged } }, children));
|
|
42
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConsentBanner - Glassmorphic consent popup for GDPR compliance
|
|
3
|
+
* Follows Grain Design System specifications
|
|
4
|
+
*/
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
export interface ConsentBannerProps {
|
|
7
|
+
position?: 'top' | 'bottom' | 'center';
|
|
8
|
+
theme?: 'light' | 'dark' | 'glass';
|
|
9
|
+
customText?: string;
|
|
10
|
+
onAccept?: () => void;
|
|
11
|
+
onDecline?: () => void;
|
|
12
|
+
showPreferences?: boolean;
|
|
13
|
+
privacyPolicyUrl?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function ConsentBanner({ position, theme, customText, onAccept, onDecline, showPreferences, privacyPolicyUrl, }: ConsentBannerProps): React.JSX.Element;
|
|
16
|
+
//# sourceMappingURL=ConsentBanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConsentBanner.d.ts","sourceRoot":"","sources":["../../../../src/react/components/ConsentBanner.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACvC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAmB,EACnB,KAAe,EACf,UAAU,EACV,QAAQ,EACR,SAAS,EACT,eAAuB,EACvB,gBAAgB,GACjB,EAAE,kBAAkB,qBA8HpB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ConsentBanner - Glassmorphic consent popup for GDPR compliance
|
|
4
|
+
* Follows Grain Design System specifications
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ConsentBanner = ConsentBanner;
|
|
8
|
+
const React = require("react");
|
|
9
|
+
const useGrainAnalytics_1 = require("../hooks/useGrainAnalytics");
|
|
10
|
+
function ConsentBanner({ position = 'bottom', theme = 'glass', customText, onAccept, onDecline, showPreferences = false, privacyPolicyUrl, }) {
|
|
11
|
+
const client = (0, useGrainAnalytics_1.useGrainAnalytics)();
|
|
12
|
+
const [visible, setVisible] = React.useState(false);
|
|
13
|
+
const [showPreferencesModal, setShowPreferencesModal] = React.useState(false);
|
|
14
|
+
React.useEffect(() => {
|
|
15
|
+
if (!client)
|
|
16
|
+
return;
|
|
17
|
+
// Check if user has already made a consent decision
|
|
18
|
+
const consentState = client.getConsentState();
|
|
19
|
+
if (!consentState) {
|
|
20
|
+
setVisible(true);
|
|
21
|
+
}
|
|
22
|
+
}, [client]);
|
|
23
|
+
const handleAccept = () => {
|
|
24
|
+
if (client) {
|
|
25
|
+
client.grantConsent(['necessary', 'analytics', 'functional']);
|
|
26
|
+
}
|
|
27
|
+
setVisible(false);
|
|
28
|
+
onAccept?.();
|
|
29
|
+
};
|
|
30
|
+
const handleDecline = () => {
|
|
31
|
+
if (client) {
|
|
32
|
+
client.revokeConsent();
|
|
33
|
+
}
|
|
34
|
+
setVisible(false);
|
|
35
|
+
onDecline?.();
|
|
36
|
+
};
|
|
37
|
+
const handleEscape = (e) => {
|
|
38
|
+
if (e.key === 'Escape') {
|
|
39
|
+
handleDecline();
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
if (!visible)
|
|
43
|
+
return null;
|
|
44
|
+
const defaultText = customText ||
|
|
45
|
+
"We use cookies and similar technologies to improve your experience. By accepting, you consent to our use of analytics and functional cookies.";
|
|
46
|
+
// Position styles
|
|
47
|
+
const positionStyles = {
|
|
48
|
+
top: 'top-4 left-4 right-4',
|
|
49
|
+
bottom: 'bottom-4 left-4 right-4',
|
|
50
|
+
center: 'top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2',
|
|
51
|
+
};
|
|
52
|
+
// Theme styles (glassmorphic by default)
|
|
53
|
+
const themeStyles = {
|
|
54
|
+
light: 'bg-white/95 border-gray-200 text-gray-900',
|
|
55
|
+
dark: 'bg-zinc-900/95 border-zinc-800 text-zinc-100',
|
|
56
|
+
glass: 'bg-zinc-950/40 backdrop-blur-xl border-zinc-800/40 text-zinc-100',
|
|
57
|
+
};
|
|
58
|
+
const buttonAcceptStyles = {
|
|
59
|
+
light: 'bg-blue-600 hover:bg-blue-700 text-white',
|
|
60
|
+
dark: 'bg-emerald-600 hover:bg-emerald-700 text-white',
|
|
61
|
+
glass: 'bg-emerald-600 hover:bg-emerald-700 text-white',
|
|
62
|
+
};
|
|
63
|
+
const buttonDeclineStyles = {
|
|
64
|
+
light: 'bg-gray-200 hover:bg-gray-300 text-gray-800',
|
|
65
|
+
dark: 'bg-zinc-800 hover:bg-zinc-700 text-zinc-200',
|
|
66
|
+
glass: 'bg-zinc-900/60 hover:bg-zinc-800/60 text-zinc-300 border border-zinc-800/60',
|
|
67
|
+
};
|
|
68
|
+
return (React.createElement("div", { className: `fixed z-50 max-w-2xl ${positionStyles[position]}`, onKeyDown: handleEscape, role: "dialog", "aria-labelledby": "consent-title", "aria-describedby": "consent-description" },
|
|
69
|
+
React.createElement("div", { className: `rounded-lg shadow-2xl border p-6 transition-all ${themeStyles[theme]}` },
|
|
70
|
+
React.createElement("h2", { id: "consent-title", className: "text-lg font-semibold mb-2" }, "Cookie Consent"),
|
|
71
|
+
React.createElement("p", { id: "consent-description", className: "text-sm opacity-80 mb-4" }, defaultText),
|
|
72
|
+
privacyPolicyUrl && (React.createElement("a", { href: privacyPolicyUrl, target: "_blank", rel: "noopener noreferrer", className: "text-sm underline opacity-70 hover:opacity-100 transition-opacity block mb-4" }, "Read our Privacy Policy")),
|
|
73
|
+
React.createElement("div", { className: "flex flex-wrap gap-2" },
|
|
74
|
+
React.createElement("button", { onClick: handleAccept, className: `px-4 py-2 rounded-lg font-medium transition-all ${buttonAcceptStyles[theme]}`, "aria-label": "Accept cookies" }, "Accept All"),
|
|
75
|
+
React.createElement("button", { onClick: handleDecline, className: `px-4 py-2 rounded-lg font-medium transition-all ${buttonDeclineStyles[theme]}`, "aria-label": "Decline cookies" }, "Decline"),
|
|
76
|
+
showPreferences && (React.createElement("button", { onClick: () => setShowPreferencesModal(true), className: `px-4 py-2 rounded-lg font-medium transition-all ${buttonDeclineStyles[theme]}`, "aria-label": "Manage preferences" }, "Manage Preferences")),
|
|
77
|
+
React.createElement("kbd", { className: "ml-auto px-2 py-1 bg-zinc-900/50 border border-zinc-800 rounded text-[10px] font-mono self-center" }, "ESC")))));
|
|
78
|
+
}
|