@perspective-ai/sdk-react 1.0.0-alpha.2
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 +316 -0
- package/dist/index.cjs +529 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +96 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.js +521 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
- package/src/FloatBubble.test.tsx +173 -0
- package/src/FloatBubble.tsx +83 -0
- package/src/Fullpage.test.tsx +164 -0
- package/src/Fullpage.tsx +83 -0
- package/src/PopupButton.test.tsx +273 -0
- package/src/PopupButton.tsx +208 -0
- package/src/SliderButton.test.tsx +279 -0
- package/src/SliderButton.tsx +208 -0
- package/src/Widget.test.tsx +308 -0
- package/src/Widget.tsx +100 -0
- package/src/hooks/useStableCallback.test.ts +83 -0
- package/src/hooks/useStableCallback.ts +20 -0
- package/src/hooks/useThemeSync.test.ts +127 -0
- package/src/hooks/useThemeSync.ts +36 -0
- package/src/index.ts +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# @perspective-ai/sdk-react
|
|
2
|
+
|
|
3
|
+
React components for [Perspective AI](https://getperspective.ai).
|
|
4
|
+
|
|
5
|
+
> **Not using React?** Use [`@perspective-ai/sdk`](https://www.npmjs.com/package/@perspective-ai/sdk) for vanilla JavaScript.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @perspective-ai/sdk-react
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Peer Dependencies:** React 18+ or 19+
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { PopupButton } from "@perspective-ai/sdk-react";
|
|
19
|
+
|
|
20
|
+
function App() {
|
|
21
|
+
return (
|
|
22
|
+
<PopupButton
|
|
23
|
+
researchId="your-research-id"
|
|
24
|
+
onSubmit={() => console.log("Interview completed!")}
|
|
25
|
+
>
|
|
26
|
+
Give Feedback
|
|
27
|
+
</PopupButton>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Components
|
|
33
|
+
|
|
34
|
+
| Component | Description |
|
|
35
|
+
| -------------- | ------------------------------- |
|
|
36
|
+
| `Widget` | Inline embed in a container |
|
|
37
|
+
| `PopupButton` | Button that opens a popup modal |
|
|
38
|
+
| `SliderButton` | Button that opens a side panel |
|
|
39
|
+
| `FloatBubble` | Floating chat bubble in corner |
|
|
40
|
+
| `Fullpage` | Full viewport takeover |
|
|
41
|
+
|
|
42
|
+
### Widget
|
|
43
|
+
|
|
44
|
+
Inline embed that renders in a container.
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { Widget } from "@perspective-ai/sdk-react";
|
|
48
|
+
|
|
49
|
+
<Widget
|
|
50
|
+
researchId="your-research-id"
|
|
51
|
+
onSubmit={() => console.log("Done!")}
|
|
52
|
+
className="my-widget" // or style={{ height: 600 }}
|
|
53
|
+
/>;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### PopupButton
|
|
57
|
+
|
|
58
|
+
Button that opens a popup modal when clicked.
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { PopupButton } from "@perspective-ai/sdk-react";
|
|
62
|
+
|
|
63
|
+
<PopupButton
|
|
64
|
+
researchId="your-research-id"
|
|
65
|
+
onSubmit={() => console.log("Done!")}
|
|
66
|
+
className="btn btn-primary"
|
|
67
|
+
>
|
|
68
|
+
Take Interview
|
|
69
|
+
</PopupButton>;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Controlled mode:**
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
const [open, setOpen] = useState(false);
|
|
76
|
+
|
|
77
|
+
<PopupButton researchId="xxx" open={open} onOpenChange={setOpen}>
|
|
78
|
+
Take Interview
|
|
79
|
+
</PopupButton>;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### SliderButton
|
|
83
|
+
|
|
84
|
+
Button that opens a side panel when clicked.
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { SliderButton } from "@perspective-ai/sdk-react";
|
|
88
|
+
|
|
89
|
+
<SliderButton researchId="your-research-id">Open Interview</SliderButton>;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### FloatBubble
|
|
93
|
+
|
|
94
|
+
Floating chat bubble in the corner of the screen.
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { FloatBubble } from "@perspective-ai/sdk-react";
|
|
98
|
+
|
|
99
|
+
<FloatBubble researchId="your-research-id" />;
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Fullpage
|
|
103
|
+
|
|
104
|
+
Full viewport takeover embed.
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { Fullpage } from "@perspective-ai/sdk-react";
|
|
108
|
+
|
|
109
|
+
<Fullpage researchId="your-research-id" />;
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Props
|
|
113
|
+
|
|
114
|
+
All components accept props from `EmbedConfig`:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import type { EmbedConfig } from "@perspective-ai/sdk-react";
|
|
118
|
+
|
|
119
|
+
interface EmbedConfig {
|
|
120
|
+
researchId: string;
|
|
121
|
+
host?: string;
|
|
122
|
+
theme?: "light" | "dark" | "system";
|
|
123
|
+
params?: Record<string, string>;
|
|
124
|
+
brand?: {
|
|
125
|
+
light?: {
|
|
126
|
+
primary?: string;
|
|
127
|
+
secondary?: string;
|
|
128
|
+
bg?: string;
|
|
129
|
+
text?: string;
|
|
130
|
+
};
|
|
131
|
+
dark?: { primary?: string; secondary?: string; bg?: string; text?: string };
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Callbacks
|
|
135
|
+
onReady?: () => void;
|
|
136
|
+
onSubmit?: (data: { researchId: string }) => void;
|
|
137
|
+
onClose?: () => void;
|
|
138
|
+
onNavigate?: (url: string) => void;
|
|
139
|
+
onError?: (error: EmbedError) => void;
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Widget
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import type { WidgetProps } from "@perspective-ai/sdk-react";
|
|
147
|
+
|
|
148
|
+
interface WidgetProps extends EmbedConfig {
|
|
149
|
+
embedRef?: RefObject<EmbedHandle | null>;
|
|
150
|
+
className?: string;
|
|
151
|
+
style?: CSSProperties;
|
|
152
|
+
// ...other div props
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### PopupButton / SliderButton
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import type {
|
|
160
|
+
PopupButtonProps,
|
|
161
|
+
SliderButtonProps,
|
|
162
|
+
} from "@perspective-ai/sdk-react";
|
|
163
|
+
|
|
164
|
+
interface PopupButtonProps extends EmbedConfig {
|
|
165
|
+
children: ReactNode;
|
|
166
|
+
open?: boolean; // Controlled mode
|
|
167
|
+
onOpenChange?: (open: boolean) => void;
|
|
168
|
+
embedRef?: RefObject<PopupButtonHandle | null>;
|
|
169
|
+
// ...other button props
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
interface SliderButtonProps extends EmbedConfig {
|
|
173
|
+
children: ReactNode;
|
|
174
|
+
open?: boolean;
|
|
175
|
+
onOpenChange?: (open: boolean) => void;
|
|
176
|
+
embedRef?: RefObject<SliderButtonHandle | null>;
|
|
177
|
+
// ...other button props
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### FloatBubble / Fullpage
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import type {
|
|
185
|
+
FloatBubbleProps,
|
|
186
|
+
FullpageProps,
|
|
187
|
+
} from "@perspective-ai/sdk-react";
|
|
188
|
+
|
|
189
|
+
interface FloatBubbleProps extends EmbedConfig {
|
|
190
|
+
embedRef?: RefObject<FloatHandle | null>;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
interface FullpageProps extends EmbedConfig {
|
|
194
|
+
embedRef?: RefObject<EmbedHandle | null>;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Programmatic Control
|
|
199
|
+
|
|
200
|
+
Use `embedRef` to control components:
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
import { useRef } from "react";
|
|
204
|
+
import { PopupButton, type PopupButtonHandle } from "@perspective-ai/sdk-react";
|
|
205
|
+
|
|
206
|
+
function App() {
|
|
207
|
+
const ref = useRef<PopupButtonHandle>(null);
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<>
|
|
211
|
+
<PopupButton researchId="xxx" embedRef={ref}>
|
|
212
|
+
Open
|
|
213
|
+
</PopupButton>
|
|
214
|
+
<button onClick={() => ref.current?.open()}>Open Programmatically</button>
|
|
215
|
+
<button onClick={() => ref.current?.close()}>Close</button>
|
|
216
|
+
</>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Handle Types
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
// Widget, Fullpage
|
|
225
|
+
interface EmbedHandle {
|
|
226
|
+
unmount(): void;
|
|
227
|
+
update(options): void;
|
|
228
|
+
destroy(): void; // deprecated, use unmount
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// PopupButton
|
|
232
|
+
interface PopupButtonHandle {
|
|
233
|
+
open(): void;
|
|
234
|
+
close(): void;
|
|
235
|
+
toggle(): void;
|
|
236
|
+
unmount(): void;
|
|
237
|
+
readonly isOpen: boolean;
|
|
238
|
+
readonly researchId: string;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// SliderButton
|
|
242
|
+
interface SliderButtonHandle {
|
|
243
|
+
open(): void;
|
|
244
|
+
close(): void;
|
|
245
|
+
toggle(): void;
|
|
246
|
+
unmount(): void;
|
|
247
|
+
readonly isOpen: boolean;
|
|
248
|
+
readonly researchId: string;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// FloatBubble
|
|
252
|
+
interface FloatHandle {
|
|
253
|
+
open(): void;
|
|
254
|
+
close(): void;
|
|
255
|
+
toggle(): void;
|
|
256
|
+
unmount(): void;
|
|
257
|
+
readonly isOpen: boolean;
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Hooks
|
|
262
|
+
|
|
263
|
+
### useThemeSync
|
|
264
|
+
|
|
265
|
+
Sync theme between your app and embedded interviews:
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
import { useThemeSync } from "@perspective-ai/sdk-react";
|
|
269
|
+
|
|
270
|
+
function App() {
|
|
271
|
+
const [theme, setTheme] = useState<"light" | "dark">("light");
|
|
272
|
+
|
|
273
|
+
// Syncs theme changes to all active embeds
|
|
274
|
+
useThemeSync(theme);
|
|
275
|
+
|
|
276
|
+
return (
|
|
277
|
+
<button onClick={() => setTheme((t) => (t === "light" ? "dark" : "light"))}>
|
|
278
|
+
Toggle Theme
|
|
279
|
+
</button>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## TypeScript
|
|
285
|
+
|
|
286
|
+
All types are exported:
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import type {
|
|
290
|
+
WidgetProps,
|
|
291
|
+
PopupButtonProps,
|
|
292
|
+
PopupButtonHandle,
|
|
293
|
+
SliderButtonProps,
|
|
294
|
+
SliderButtonHandle,
|
|
295
|
+
FloatBubbleProps,
|
|
296
|
+
FullpageProps,
|
|
297
|
+
} from "@perspective-ai/sdk-react";
|
|
298
|
+
|
|
299
|
+
// Re-exported from @perspective-ai/sdk
|
|
300
|
+
import type {
|
|
301
|
+
EmbedConfig,
|
|
302
|
+
EmbedHandle,
|
|
303
|
+
FloatHandle,
|
|
304
|
+
BrandColors,
|
|
305
|
+
ThemeValue,
|
|
306
|
+
EmbedError,
|
|
307
|
+
} from "@perspective-ai/sdk-react";
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## SSR Safety
|
|
311
|
+
|
|
312
|
+
All components are SSR-safe and include the `"use client"` directive. Works with Next.js, Remix, and other React frameworks.
|
|
313
|
+
|
|
314
|
+
## License
|
|
315
|
+
|
|
316
|
+
MIT
|