@shellapps/experience-react 1.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/dist/index.d.mts +49 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.js +179 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +137 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +47 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import React__default from 'react';
|
|
4
|
+
import { ExperienceConfig, Experience } from '@shellapps/experience';
|
|
5
|
+
|
|
6
|
+
interface ExperienceProviderProps {
|
|
7
|
+
appId: string;
|
|
8
|
+
apiKey: string;
|
|
9
|
+
profileId?: string;
|
|
10
|
+
options?: Partial<Omit<ExperienceConfig, 'appId' | 'apiKey'>>;
|
|
11
|
+
children: React__default.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
declare function ExperienceProvider({ appId, apiKey, profileId, options, children }: ExperienceProviderProps): react_jsx_runtime.JSX.Element;
|
|
14
|
+
|
|
15
|
+
interface ErrorBoundaryProps {
|
|
16
|
+
fallback?: React__default.ReactNode;
|
|
17
|
+
showCommentForm?: boolean;
|
|
18
|
+
onError?: (error: Error, errorInfo: React__default.ErrorInfo) => void;
|
|
19
|
+
children: React__default.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
interface ErrorBoundaryState {
|
|
22
|
+
hasError: boolean;
|
|
23
|
+
error: Error | null;
|
|
24
|
+
comment: string;
|
|
25
|
+
submitted: boolean;
|
|
26
|
+
}
|
|
27
|
+
declare class ErrorBoundary extends React__default.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
28
|
+
static contextType: React__default.Context<Experience | null>;
|
|
29
|
+
context: Experience | null;
|
|
30
|
+
state: ErrorBoundaryState;
|
|
31
|
+
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
|
|
32
|
+
componentDidCatch(error: Error, errorInfo: React__default.ErrorInfo): void;
|
|
33
|
+
private handleSubmitComment;
|
|
34
|
+
render(): string | number | boolean | Iterable<React__default.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
declare function useTrack(): {
|
|
38
|
+
track: (eventName: string, metadata?: Record<string, unknown>) => void;
|
|
39
|
+
trackPageView: () => void;
|
|
40
|
+
};
|
|
41
|
+
declare function useFlag<T>(flagName: string, defaultValue: T): T;
|
|
42
|
+
declare function useTranslation(): {
|
|
43
|
+
t: (key: string) => string;
|
|
44
|
+
locale: "en";
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
declare const ExperienceContext: React.Context<Experience | null>;
|
|
48
|
+
|
|
49
|
+
export { ErrorBoundary, ExperienceContext, ExperienceProvider, type ExperienceProviderProps, useFlag, useTrack, useTranslation };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import React__default from 'react';
|
|
4
|
+
import { ExperienceConfig, Experience } from '@shellapps/experience';
|
|
5
|
+
|
|
6
|
+
interface ExperienceProviderProps {
|
|
7
|
+
appId: string;
|
|
8
|
+
apiKey: string;
|
|
9
|
+
profileId?: string;
|
|
10
|
+
options?: Partial<Omit<ExperienceConfig, 'appId' | 'apiKey'>>;
|
|
11
|
+
children: React__default.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
declare function ExperienceProvider({ appId, apiKey, profileId, options, children }: ExperienceProviderProps): react_jsx_runtime.JSX.Element;
|
|
14
|
+
|
|
15
|
+
interface ErrorBoundaryProps {
|
|
16
|
+
fallback?: React__default.ReactNode;
|
|
17
|
+
showCommentForm?: boolean;
|
|
18
|
+
onError?: (error: Error, errorInfo: React__default.ErrorInfo) => void;
|
|
19
|
+
children: React__default.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
interface ErrorBoundaryState {
|
|
22
|
+
hasError: boolean;
|
|
23
|
+
error: Error | null;
|
|
24
|
+
comment: string;
|
|
25
|
+
submitted: boolean;
|
|
26
|
+
}
|
|
27
|
+
declare class ErrorBoundary extends React__default.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
28
|
+
static contextType: React__default.Context<Experience | null>;
|
|
29
|
+
context: Experience | null;
|
|
30
|
+
state: ErrorBoundaryState;
|
|
31
|
+
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
|
|
32
|
+
componentDidCatch(error: Error, errorInfo: React__default.ErrorInfo): void;
|
|
33
|
+
private handleSubmitComment;
|
|
34
|
+
render(): string | number | boolean | Iterable<React__default.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
declare function useTrack(): {
|
|
38
|
+
track: (eventName: string, metadata?: Record<string, unknown>) => void;
|
|
39
|
+
trackPageView: () => void;
|
|
40
|
+
};
|
|
41
|
+
declare function useFlag<T>(flagName: string, defaultValue: T): T;
|
|
42
|
+
declare function useTranslation(): {
|
|
43
|
+
t: (key: string) => string;
|
|
44
|
+
locale: "en";
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
declare const ExperienceContext: React.Context<Experience | null>;
|
|
48
|
+
|
|
49
|
+
export { ErrorBoundary, ExperienceContext, ExperienceProvider, type ExperienceProviderProps, useFlag, useTrack, useTranslation };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ErrorBoundary: () => ErrorBoundary,
|
|
34
|
+
ExperienceContext: () => ExperienceContext,
|
|
35
|
+
ExperienceProvider: () => ExperienceProvider,
|
|
36
|
+
useFlag: () => useFlag,
|
|
37
|
+
useTrack: () => useTrack,
|
|
38
|
+
useTranslation: () => useTranslation
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(index_exports);
|
|
41
|
+
|
|
42
|
+
// src/ExperienceProvider.tsx
|
|
43
|
+
var import_react2 = require("react");
|
|
44
|
+
var import_experience = require("@shellapps/experience");
|
|
45
|
+
|
|
46
|
+
// src/context.ts
|
|
47
|
+
var import_react = require("react");
|
|
48
|
+
var ExperienceContext = (0, import_react.createContext)(null);
|
|
49
|
+
|
|
50
|
+
// src/ExperienceProvider.tsx
|
|
51
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
52
|
+
function ExperienceProvider({ appId, apiKey, profileId, options, children }) {
|
|
53
|
+
const instanceRef = (0, import_react2.useRef)(null);
|
|
54
|
+
if (!instanceRef.current) {
|
|
55
|
+
const instance = import_experience.Experience.init({ appId, apiKey, ...options });
|
|
56
|
+
if (profileId) instance.identify(profileId);
|
|
57
|
+
instanceRef.current = instance;
|
|
58
|
+
}
|
|
59
|
+
(0, import_react2.useEffect)(() => {
|
|
60
|
+
if (profileId && instanceRef.current) {
|
|
61
|
+
instanceRef.current.identify(profileId);
|
|
62
|
+
}
|
|
63
|
+
}, [profileId]);
|
|
64
|
+
(0, import_react2.useEffect)(() => {
|
|
65
|
+
const handler = (e) => {
|
|
66
|
+
let el = e.target;
|
|
67
|
+
while (el) {
|
|
68
|
+
const tid = el.getAttribute?.("data-t");
|
|
69
|
+
if (tid) {
|
|
70
|
+
instanceRef.current?.track("element_click", { elementTid: tid });
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
el = el.parentElement;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
document.addEventListener("click", handler, true);
|
|
77
|
+
return () => document.removeEventListener("click", handler, true);
|
|
78
|
+
}, []);
|
|
79
|
+
(0, import_react2.useEffect)(() => {
|
|
80
|
+
return () => {
|
|
81
|
+
instanceRef.current?.shutdown();
|
|
82
|
+
};
|
|
83
|
+
}, []);
|
|
84
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExperienceContext.Provider, { value: instanceRef.current, children });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/ErrorBoundary.tsx
|
|
88
|
+
var import_react3 = __toESM(require("react"));
|
|
89
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
90
|
+
var ErrorBoundary = class extends import_react3.default.Component {
|
|
91
|
+
constructor() {
|
|
92
|
+
super(...arguments);
|
|
93
|
+
this.state = {
|
|
94
|
+
hasError: false,
|
|
95
|
+
error: null,
|
|
96
|
+
comment: "",
|
|
97
|
+
submitted: false
|
|
98
|
+
};
|
|
99
|
+
this.handleSubmitComment = () => {
|
|
100
|
+
if (this.state.comment.trim() && this.state.error) {
|
|
101
|
+
this.context?.captureMessage(
|
|
102
|
+
`User feedback: ${this.state.comment}`,
|
|
103
|
+
"info"
|
|
104
|
+
);
|
|
105
|
+
this.setState({ submitted: true });
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
static getDerivedStateFromError(error) {
|
|
110
|
+
return { hasError: true, error };
|
|
111
|
+
}
|
|
112
|
+
componentDidCatch(error, errorInfo) {
|
|
113
|
+
this.context?.captureError(error, {
|
|
114
|
+
componentStack: errorInfo.componentStack || ""
|
|
115
|
+
});
|
|
116
|
+
this.props.onError?.(error, errorInfo);
|
|
117
|
+
}
|
|
118
|
+
render() {
|
|
119
|
+
if (this.state.hasError) {
|
|
120
|
+
if (this.props.fallback) {
|
|
121
|
+
return this.props.fallback;
|
|
122
|
+
}
|
|
123
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { padding: 20, textAlign: "center" }, children: [
|
|
124
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: "Something went wrong" }),
|
|
125
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: this.state.error?.message }),
|
|
126
|
+
this.props.showCommentForm && !this.state.submitted && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
127
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
128
|
+
"textarea",
|
|
129
|
+
{
|
|
130
|
+
placeholder: "Tell us what happened...",
|
|
131
|
+
value: this.state.comment,
|
|
132
|
+
onChange: (e) => this.setState({ comment: e.target.value }),
|
|
133
|
+
style: { width: "100%", minHeight: 80, marginTop: 10 }
|
|
134
|
+
}
|
|
135
|
+
),
|
|
136
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: this.handleSubmitComment, style: { marginTop: 8 }, children: "Submit" })
|
|
137
|
+
] }),
|
|
138
|
+
this.state.submitted && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: "Thank you for your feedback!" })
|
|
139
|
+
] });
|
|
140
|
+
}
|
|
141
|
+
return this.props.children;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
ErrorBoundary.contextType = ExperienceContext;
|
|
145
|
+
|
|
146
|
+
// src/hooks.ts
|
|
147
|
+
var import_react4 = require("react");
|
|
148
|
+
function useExperience() {
|
|
149
|
+
const ctx = (0, import_react4.useContext)(ExperienceContext);
|
|
150
|
+
if (!ctx) throw new Error("useExperience must be used within ExperienceProvider");
|
|
151
|
+
return ctx;
|
|
152
|
+
}
|
|
153
|
+
function useTrack() {
|
|
154
|
+
const experience = useExperience();
|
|
155
|
+
return {
|
|
156
|
+
track: (eventName, metadata) => experience.track(eventName, metadata),
|
|
157
|
+
trackPageView: () => experience.trackPageView()
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function useFlag(flagName, defaultValue) {
|
|
161
|
+
const experience = useExperience();
|
|
162
|
+
return experience.getFlag(flagName, defaultValue);
|
|
163
|
+
}
|
|
164
|
+
function useTranslation() {
|
|
165
|
+
return {
|
|
166
|
+
t: (key) => key,
|
|
167
|
+
locale: "en"
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
171
|
+
0 && (module.exports = {
|
|
172
|
+
ErrorBoundary,
|
|
173
|
+
ExperienceContext,
|
|
174
|
+
ExperienceProvider,
|
|
175
|
+
useFlag,
|
|
176
|
+
useTrack,
|
|
177
|
+
useTranslation
|
|
178
|
+
});
|
|
179
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/ExperienceProvider.tsx","../src/context.ts","../src/ErrorBoundary.tsx","../src/hooks.ts"],"sourcesContent":["export { ExperienceProvider } from './ExperienceProvider';\nexport type { ExperienceProviderProps } from './ExperienceProvider';\nexport { ErrorBoundary } from './ErrorBoundary';\nexport { useTrack, useFlag, useTranslation } from './hooks';\nexport { ExperienceContext } from './context';\n","import React, { useEffect, useRef } from 'react';\nimport { Experience } from '@shellapps/experience';\nimport type { ExperienceConfig } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\nexport interface ExperienceProviderProps {\n appId: string;\n apiKey: string;\n profileId?: string;\n options?: Partial<Omit<ExperienceConfig, 'appId' | 'apiKey'>>;\n children: React.ReactNode;\n}\n\nexport function ExperienceProvider({ appId, apiKey, profileId, options, children }: ExperienceProviderProps) {\n const instanceRef = useRef<Experience | null>(null);\n\n if (!instanceRef.current) {\n const instance = Experience.init({ appId, apiKey, ...options });\n if (profileId) instance.identify(profileId);\n instanceRef.current = instance;\n }\n\n useEffect(() => {\n if (profileId && instanceRef.current) {\n instanceRef.current.identify(profileId);\n }\n }, [profileId]);\n\n // data-t auto-tracking\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n let el = e.target as HTMLElement | null;\n while (el) {\n const tid = el.getAttribute?.('data-t');\n if (tid) {\n instanceRef.current?.track('element_click', { elementTid: tid });\n break;\n }\n el = el.parentElement;\n }\n };\n document.addEventListener('click', handler, true);\n return () => document.removeEventListener('click', handler, true);\n }, []);\n\n useEffect(() => {\n return () => {\n instanceRef.current?.shutdown();\n };\n }, []);\n\n return (\n <ExperienceContext.Provider value={instanceRef.current}>\n {children}\n </ExperienceContext.Provider>\n );\n}\n","import { createContext } from 'react';\nimport type { Experience } from '@shellapps/experience';\n\nexport const ExperienceContext = createContext<Experience | null>(null);\n","import React from 'react';\nimport type { Experience } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\ninterface ErrorBoundaryProps {\n fallback?: React.ReactNode;\n showCommentForm?: boolean;\n onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n children: React.ReactNode;\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n error: Error | null;\n comment: string;\n submitted: boolean;\n}\n\nexport class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {\n static contextType = ExperienceContext;\n declare context: Experience | null;\n\n state: ErrorBoundaryState = {\n hasError: false,\n error: null,\n comment: '',\n submitted: false,\n };\n\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n this.context?.captureError(error, {\n componentStack: errorInfo.componentStack || '',\n });\n this.props.onError?.(error, errorInfo);\n }\n\n private handleSubmitComment = () => {\n if (this.state.comment.trim() && this.state.error) {\n this.context?.captureMessage(\n `User feedback: ${this.state.comment}`,\n 'info',\n );\n this.setState({ submitted: true });\n }\n };\n\n render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n return (\n <div style={{ padding: 20, textAlign: 'center' }}>\n <h2>Something went wrong</h2>\n <p>{this.state.error?.message}</p>\n {this.props.showCommentForm && !this.state.submitted && (\n <div>\n <textarea\n placeholder=\"Tell us what happened...\"\n value={this.state.comment}\n onChange={(e) => this.setState({ comment: e.target.value })}\n style={{ width: '100%', minHeight: 80, marginTop: 10 }}\n />\n <button onClick={this.handleSubmitComment} style={{ marginTop: 8 }}>\n Submit\n </button>\n </div>\n )}\n {this.state.submitted && <p>Thank you for your feedback!</p>}\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n","import { useContext } from 'react';\nimport { ExperienceContext } from './context';\n\nfunction useExperience() {\n const ctx = useContext(ExperienceContext);\n if (!ctx) throw new Error('useExperience must be used within ExperienceProvider');\n return ctx;\n}\n\nexport function useTrack() {\n const experience = useExperience();\n return {\n track: (eventName: string, metadata?: Record<string, unknown>) => experience.track(eventName, metadata),\n trackPageView: () => experience.trackPageView(),\n };\n}\n\nexport function useFlag<T>(flagName: string, defaultValue: T): T {\n const experience = useExperience();\n return experience.getFlag(flagName, defaultValue);\n}\n\nexport function useTranslation() {\n return {\n t: (key: string) => key,\n locale: 'en' as const,\n };\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","React","import_react"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/ExperienceProvider.tsx
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { Experience } from "@shellapps/experience";
|
|
4
|
+
|
|
5
|
+
// src/context.ts
|
|
6
|
+
import { createContext } from "react";
|
|
7
|
+
var ExperienceContext = createContext(null);
|
|
8
|
+
|
|
9
|
+
// src/ExperienceProvider.tsx
|
|
10
|
+
import { jsx } from "react/jsx-runtime";
|
|
11
|
+
function ExperienceProvider({ appId, apiKey, profileId, options, children }) {
|
|
12
|
+
const instanceRef = useRef(null);
|
|
13
|
+
if (!instanceRef.current) {
|
|
14
|
+
const instance = Experience.init({ appId, apiKey, ...options });
|
|
15
|
+
if (profileId) instance.identify(profileId);
|
|
16
|
+
instanceRef.current = instance;
|
|
17
|
+
}
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (profileId && instanceRef.current) {
|
|
20
|
+
instanceRef.current.identify(profileId);
|
|
21
|
+
}
|
|
22
|
+
}, [profileId]);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const handler = (e) => {
|
|
25
|
+
let el = e.target;
|
|
26
|
+
while (el) {
|
|
27
|
+
const tid = el.getAttribute?.("data-t");
|
|
28
|
+
if (tid) {
|
|
29
|
+
instanceRef.current?.track("element_click", { elementTid: tid });
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
el = el.parentElement;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
document.addEventListener("click", handler, true);
|
|
36
|
+
return () => document.removeEventListener("click", handler, true);
|
|
37
|
+
}, []);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
return () => {
|
|
40
|
+
instanceRef.current?.shutdown();
|
|
41
|
+
};
|
|
42
|
+
}, []);
|
|
43
|
+
return /* @__PURE__ */ jsx(ExperienceContext.Provider, { value: instanceRef.current, children });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/ErrorBoundary.tsx
|
|
47
|
+
import React2 from "react";
|
|
48
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
49
|
+
var ErrorBoundary = class extends React2.Component {
|
|
50
|
+
constructor() {
|
|
51
|
+
super(...arguments);
|
|
52
|
+
this.state = {
|
|
53
|
+
hasError: false,
|
|
54
|
+
error: null,
|
|
55
|
+
comment: "",
|
|
56
|
+
submitted: false
|
|
57
|
+
};
|
|
58
|
+
this.handleSubmitComment = () => {
|
|
59
|
+
if (this.state.comment.trim() && this.state.error) {
|
|
60
|
+
this.context?.captureMessage(
|
|
61
|
+
`User feedback: ${this.state.comment}`,
|
|
62
|
+
"info"
|
|
63
|
+
);
|
|
64
|
+
this.setState({ submitted: true });
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
static getDerivedStateFromError(error) {
|
|
69
|
+
return { hasError: true, error };
|
|
70
|
+
}
|
|
71
|
+
componentDidCatch(error, errorInfo) {
|
|
72
|
+
this.context?.captureError(error, {
|
|
73
|
+
componentStack: errorInfo.componentStack || ""
|
|
74
|
+
});
|
|
75
|
+
this.props.onError?.(error, errorInfo);
|
|
76
|
+
}
|
|
77
|
+
render() {
|
|
78
|
+
if (this.state.hasError) {
|
|
79
|
+
if (this.props.fallback) {
|
|
80
|
+
return this.props.fallback;
|
|
81
|
+
}
|
|
82
|
+
return /* @__PURE__ */ jsxs("div", { style: { padding: 20, textAlign: "center" }, children: [
|
|
83
|
+
/* @__PURE__ */ jsx2("h2", { children: "Something went wrong" }),
|
|
84
|
+
/* @__PURE__ */ jsx2("p", { children: this.state.error?.message }),
|
|
85
|
+
this.props.showCommentForm && !this.state.submitted && /* @__PURE__ */ jsxs("div", { children: [
|
|
86
|
+
/* @__PURE__ */ jsx2(
|
|
87
|
+
"textarea",
|
|
88
|
+
{
|
|
89
|
+
placeholder: "Tell us what happened...",
|
|
90
|
+
value: this.state.comment,
|
|
91
|
+
onChange: (e) => this.setState({ comment: e.target.value }),
|
|
92
|
+
style: { width: "100%", minHeight: 80, marginTop: 10 }
|
|
93
|
+
}
|
|
94
|
+
),
|
|
95
|
+
/* @__PURE__ */ jsx2("button", { onClick: this.handleSubmitComment, style: { marginTop: 8 }, children: "Submit" })
|
|
96
|
+
] }),
|
|
97
|
+
this.state.submitted && /* @__PURE__ */ jsx2("p", { children: "Thank you for your feedback!" })
|
|
98
|
+
] });
|
|
99
|
+
}
|
|
100
|
+
return this.props.children;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
ErrorBoundary.contextType = ExperienceContext;
|
|
104
|
+
|
|
105
|
+
// src/hooks.ts
|
|
106
|
+
import { useContext } from "react";
|
|
107
|
+
function useExperience() {
|
|
108
|
+
const ctx = useContext(ExperienceContext);
|
|
109
|
+
if (!ctx) throw new Error("useExperience must be used within ExperienceProvider");
|
|
110
|
+
return ctx;
|
|
111
|
+
}
|
|
112
|
+
function useTrack() {
|
|
113
|
+
const experience = useExperience();
|
|
114
|
+
return {
|
|
115
|
+
track: (eventName, metadata) => experience.track(eventName, metadata),
|
|
116
|
+
trackPageView: () => experience.trackPageView()
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function useFlag(flagName, defaultValue) {
|
|
120
|
+
const experience = useExperience();
|
|
121
|
+
return experience.getFlag(flagName, defaultValue);
|
|
122
|
+
}
|
|
123
|
+
function useTranslation() {
|
|
124
|
+
return {
|
|
125
|
+
t: (key) => key,
|
|
126
|
+
locale: "en"
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
ErrorBoundary,
|
|
131
|
+
ExperienceContext,
|
|
132
|
+
ExperienceProvider,
|
|
133
|
+
useFlag,
|
|
134
|
+
useTrack,
|
|
135
|
+
useTranslation
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ExperienceProvider.tsx","../src/context.ts","../src/ErrorBoundary.tsx","../src/hooks.ts"],"sourcesContent":["import React, { useEffect, useRef } from 'react';\nimport { Experience } from '@shellapps/experience';\nimport type { ExperienceConfig } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\nexport interface ExperienceProviderProps {\n appId: string;\n apiKey: string;\n profileId?: string;\n options?: Partial<Omit<ExperienceConfig, 'appId' | 'apiKey'>>;\n children: React.ReactNode;\n}\n\nexport function ExperienceProvider({ appId, apiKey, profileId, options, children }: ExperienceProviderProps) {\n const instanceRef = useRef<Experience | null>(null);\n\n if (!instanceRef.current) {\n const instance = Experience.init({ appId, apiKey, ...options });\n if (profileId) instance.identify(profileId);\n instanceRef.current = instance;\n }\n\n useEffect(() => {\n if (profileId && instanceRef.current) {\n instanceRef.current.identify(profileId);\n }\n }, [profileId]);\n\n // data-t auto-tracking\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n let el = e.target as HTMLElement | null;\n while (el) {\n const tid = el.getAttribute?.('data-t');\n if (tid) {\n instanceRef.current?.track('element_click', { elementTid: tid });\n break;\n }\n el = el.parentElement;\n }\n };\n document.addEventListener('click', handler, true);\n return () => document.removeEventListener('click', handler, true);\n }, []);\n\n useEffect(() => {\n return () => {\n instanceRef.current?.shutdown();\n };\n }, []);\n\n return (\n <ExperienceContext.Provider value={instanceRef.current}>\n {children}\n </ExperienceContext.Provider>\n );\n}\n","import { createContext } from 'react';\nimport type { Experience } from '@shellapps/experience';\n\nexport const ExperienceContext = createContext<Experience | null>(null);\n","import React from 'react';\nimport type { Experience } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\ninterface ErrorBoundaryProps {\n fallback?: React.ReactNode;\n showCommentForm?: boolean;\n onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n children: React.ReactNode;\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n error: Error | null;\n comment: string;\n submitted: boolean;\n}\n\nexport class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {\n static contextType = ExperienceContext;\n declare context: Experience | null;\n\n state: ErrorBoundaryState = {\n hasError: false,\n error: null,\n comment: '',\n submitted: false,\n };\n\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n this.context?.captureError(error, {\n componentStack: errorInfo.componentStack || '',\n });\n this.props.onError?.(error, errorInfo);\n }\n\n private handleSubmitComment = () => {\n if (this.state.comment.trim() && this.state.error) {\n this.context?.captureMessage(\n `User feedback: ${this.state.comment}`,\n 'info',\n );\n this.setState({ submitted: true });\n }\n };\n\n render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n return (\n <div style={{ padding: 20, textAlign: 'center' }}>\n <h2>Something went wrong</h2>\n <p>{this.state.error?.message}</p>\n {this.props.showCommentForm && !this.state.submitted && (\n <div>\n <textarea\n placeholder=\"Tell us what happened...\"\n value={this.state.comment}\n onChange={(e) => this.setState({ comment: e.target.value })}\n style={{ width: '100%', minHeight: 80, marginTop: 10 }}\n />\n <button onClick={this.handleSubmitComment} style={{ marginTop: 8 }}>\n Submit\n </button>\n </div>\n )}\n {this.state.submitted && <p>Thank you for your feedback!</p>}\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n","import { useContext } from 'react';\nimport { ExperienceContext } from './context';\n\nfunction useExperience() {\n const ctx = useContext(ExperienceContext);\n if (!ctx) throw new Error('useExperience must be used within ExperienceProvider');\n return ctx;\n}\n\nexport function useTrack() {\n const experience = useExperience();\n return {\n track: (eventName: string, metadata?: Record<string, unknown>) => experience.track(eventName, metadata),\n trackPageView: () => experience.trackPageView(),\n };\n}\n\nexport function useFlag<T>(flagName: string, defaultValue: T): T {\n const experience = useExperience();\n return experience.getFlag(flagName, defaultValue);\n}\n\nexport function useTranslation() {\n return {\n t: (key: string) => key,\n locale: 'en' as const,\n };\n}\n"],"mappings":"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","names":["React","jsx","React"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shellapps/experience-react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React SDK for @shellapps/experience",
|
|
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
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"clean": "rm -rf dist"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"react",
|
|
25
|
+
"analytics",
|
|
26
|
+
"experience",
|
|
27
|
+
"shellapps"
|
|
28
|
+
],
|
|
29
|
+
"author": "Alex Hewitt-Procter <alexhp@hotmail.co.uk>",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@shellapps/experience": "^1.0.0",
|
|
33
|
+
"react": ">=17"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/react": "^18.2.60",
|
|
37
|
+
"react": "^18.2.0",
|
|
38
|
+
"tsup": "^8.0.1",
|
|
39
|
+
"typescript": "^5.3.3"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/ShellTechnology/shellapps-js.git",
|
|
44
|
+
"directory": "packages/experience-react"
|
|
45
|
+
},
|
|
46
|
+
"gitHead": "97840ec6e98bfb5289b83abbe5f618ef3c4c663f"
|
|
47
|
+
}
|