@perspective-ai/sdk-react 1.0.0-alpha.2 → 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/README.md +139 -166
- package/dist/index.cjs +317 -270
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +121 -54
- package/dist/index.d.ts +121 -54
- package/dist/index.js +317 -271
- package/dist/index.js.map +1 -1
- package/package.json +9 -2
- package/src/FloatBubble.test.tsx +0 -39
- package/src/FloatBubble.tsx +22 -48
- package/src/hooks/useFloatBubble.test.ts +91 -0
- package/src/hooks/useFloatBubble.ts +180 -0
- package/src/hooks/usePopup.test.ts +219 -0
- package/src/hooks/usePopup.ts +190 -0
- package/src/hooks/useSlider.test.ts +150 -0
- package/src/hooks/useSlider.ts +181 -0
- package/src/index.ts +26 -29
- package/src/PopupButton.test.tsx +0 -273
- package/src/PopupButton.tsx +0 -208
- package/src/SliderButton.test.tsx +0 -279
- package/src/SliderButton.tsx +0 -208
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @perspective-ai/sdk-react
|
|
2
2
|
|
|
3
|
-
React components for [Perspective AI](https://getperspective.ai).
|
|
3
|
+
React hooks and components for [Perspective AI](https://getperspective.ai).
|
|
4
4
|
|
|
5
5
|
> **Not using React?** Use [`@perspective-ai/sdk`](https://www.npmjs.com/package/@perspective-ai/sdk) for vanilla JavaScript.
|
|
6
6
|
|
|
@@ -15,88 +15,134 @@ npm install @perspective-ai/sdk-react
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
|
17
17
|
```tsx
|
|
18
|
-
import {
|
|
18
|
+
import { usePopup } from "@perspective-ai/sdk-react";
|
|
19
19
|
|
|
20
20
|
function App() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
</PopupButton>
|
|
28
|
-
);
|
|
21
|
+
const { open } = usePopup({
|
|
22
|
+
researchId: "your-research-id",
|
|
23
|
+
onSubmit: () => console.log("Interview completed!"),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return <button onClick={open}>Give Feedback</button>;
|
|
29
27
|
}
|
|
30
28
|
```
|
|
31
29
|
|
|
32
|
-
##
|
|
30
|
+
## API Overview
|
|
33
31
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `Fullpage`
|
|
32
|
+
| API | Type | Description |
|
|
33
|
+
| ---------------- | --------- | -------------------------------------- |
|
|
34
|
+
| `usePopup` | Hook | Open popup modal programmatically |
|
|
35
|
+
| `useSlider` | Hook | Open slider panel programmatically |
|
|
36
|
+
| `useFloatBubble` | Hook | Floating chat bubble lifecycle |
|
|
37
|
+
| `Widget` | Component | Inline embed in a container |
|
|
38
|
+
| `Fullpage` | Component | Full viewport takeover |
|
|
39
|
+
| `FloatBubble` | Component | Convenience wrapper for useFloatBubble |
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
**Mental Model:**
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
- **Hooks** for overlays (popup, slider, float bubble) - you control the trigger
|
|
44
|
+
- **Components** for embeds (widget, fullpage) - render inline DOM
|
|
45
|
+
|
|
46
|
+
## Hooks
|
|
47
|
+
|
|
48
|
+
### usePopup
|
|
49
|
+
|
|
50
|
+
Open a popup modal with your own trigger element.
|
|
45
51
|
|
|
46
52
|
```tsx
|
|
47
|
-
import {
|
|
53
|
+
import { usePopup } from "@perspective-ai/sdk-react";
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
+
function App() {
|
|
56
|
+
const { open, close, isOpen } = usePopup({
|
|
57
|
+
researchId: "your-research-id",
|
|
58
|
+
onSubmit: () => console.log("Done!"),
|
|
59
|
+
});
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
return (
|
|
62
|
+
<>
|
|
63
|
+
<button onClick={open}>Take Survey</button>
|
|
64
|
+
{isOpen && <span>Survey is open</span>}
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
57
69
|
|
|
58
|
-
|
|
70
|
+
**Programmatic trigger:**
|
|
59
71
|
|
|
60
72
|
```tsx
|
|
61
|
-
|
|
73
|
+
const { open } = usePopup({ researchId: "xxx" });
|
|
62
74
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Take Interview
|
|
69
|
-
</PopupButton>;
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (userCompletedCheckout) {
|
|
77
|
+
open();
|
|
78
|
+
}
|
|
79
|
+
}, [userCompletedCheckout, open]);
|
|
70
80
|
```
|
|
71
81
|
|
|
72
82
|
**Controlled mode:**
|
|
73
83
|
|
|
74
84
|
```tsx
|
|
75
|
-
const [
|
|
85
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
86
|
+
|
|
87
|
+
const popup = usePopup({
|
|
88
|
+
researchId: "xxx",
|
|
89
|
+
open: isOpen,
|
|
90
|
+
onOpenChange: setIsOpen,
|
|
91
|
+
});
|
|
76
92
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
</PopupButton>;
|
|
93
|
+
// External control
|
|
94
|
+
<button onClick={() => setIsOpen(true)}>Open from anywhere</button>;
|
|
80
95
|
```
|
|
81
96
|
|
|
82
|
-
###
|
|
97
|
+
### useSlider
|
|
83
98
|
|
|
84
|
-
|
|
99
|
+
Open a slider panel with your own trigger element.
|
|
85
100
|
|
|
86
101
|
```tsx
|
|
87
|
-
import {
|
|
102
|
+
import { useSlider } from "@perspective-ai/sdk-react";
|
|
88
103
|
|
|
89
|
-
|
|
104
|
+
function App() {
|
|
105
|
+
const { open, close, isOpen } = useSlider({
|
|
106
|
+
researchId: "your-research-id",
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return <button onClick={open}>Open Feedback Panel</button>;
|
|
110
|
+
}
|
|
90
111
|
```
|
|
91
112
|
|
|
92
|
-
###
|
|
113
|
+
### useFloatBubble
|
|
93
114
|
|
|
94
|
-
|
|
115
|
+
Manage a floating chat bubble lifecycle.
|
|
95
116
|
|
|
96
117
|
```tsx
|
|
97
|
-
import {
|
|
118
|
+
import { useFloatBubble } from "@perspective-ai/sdk-react";
|
|
98
119
|
|
|
99
|
-
|
|
120
|
+
function App() {
|
|
121
|
+
const { open, close, isOpen } = useFloatBubble({
|
|
122
|
+
researchId: "your-research-id",
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Bubble mounts on component mount
|
|
126
|
+
// Use open/close for programmatic control
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Components
|
|
132
|
+
|
|
133
|
+
### Widget
|
|
134
|
+
|
|
135
|
+
Inline embed that renders in a container.
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import { Widget } from "@perspective-ai/sdk-react";
|
|
139
|
+
|
|
140
|
+
<Widget
|
|
141
|
+
researchId="your-research-id"
|
|
142
|
+
onSubmit={() => console.log("Done!")}
|
|
143
|
+
className="my-widget"
|
|
144
|
+
style={{ height: 600 }}
|
|
145
|
+
/>;
|
|
100
146
|
```
|
|
101
147
|
|
|
102
148
|
### Fullpage
|
|
@@ -109,14 +155,22 @@ import { Fullpage } from "@perspective-ai/sdk-react";
|
|
|
109
155
|
<Fullpage researchId="your-research-id" />;
|
|
110
156
|
```
|
|
111
157
|
|
|
112
|
-
|
|
158
|
+
### FloatBubble
|
|
113
159
|
|
|
114
|
-
|
|
160
|
+
Convenience wrapper around `useFloatBubble` hook.
|
|
115
161
|
|
|
116
|
-
```
|
|
117
|
-
import
|
|
162
|
+
```tsx
|
|
163
|
+
import { FloatBubble } from "@perspective-ai/sdk-react";
|
|
164
|
+
|
|
165
|
+
<FloatBubble researchId="your-research-id" />;
|
|
166
|
+
```
|
|
118
167
|
|
|
119
|
-
|
|
168
|
+
## Hook Options
|
|
169
|
+
|
|
170
|
+
All hooks accept options from `EmbedConfig`:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
interface UsePopupOptions {
|
|
120
174
|
researchId: string;
|
|
121
175
|
host?: string;
|
|
122
176
|
theme?: "light" | "dark" | "system";
|
|
@@ -137,128 +191,43 @@ interface EmbedConfig {
|
|
|
137
191
|
onClose?: () => void;
|
|
138
192
|
onNavigate?: (url: string) => void;
|
|
139
193
|
onError?: (error: EmbedError) => void;
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Widget
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
import type { WidgetProps } from "@perspective-ai/sdk-react";
|
|
147
194
|
|
|
148
|
-
|
|
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;
|
|
195
|
+
// Controlled mode
|
|
174
196
|
open?: boolean;
|
|
175
197
|
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
198
|
}
|
|
219
199
|
```
|
|
220
200
|
|
|
221
|
-
|
|
201
|
+
## Hook Return Types
|
|
222
202
|
|
|
223
203
|
```typescript
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
204
|
+
interface UsePopupReturn {
|
|
205
|
+
open: () => void;
|
|
206
|
+
close: () => void;
|
|
207
|
+
toggle: () => void;
|
|
208
|
+
isOpen: boolean;
|
|
209
|
+
handle: EmbedHandle | null;
|
|
229
210
|
}
|
|
230
211
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
readonly isOpen: boolean;
|
|
238
|
-
readonly researchId: string;
|
|
212
|
+
interface UseSliderReturn {
|
|
213
|
+
open: () => void;
|
|
214
|
+
close: () => void;
|
|
215
|
+
toggle: () => void;
|
|
216
|
+
isOpen: boolean;
|
|
217
|
+
handle: EmbedHandle | null;
|
|
239
218
|
}
|
|
240
219
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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;
|
|
220
|
+
interface UseFloatBubbleReturn {
|
|
221
|
+
open: () => void;
|
|
222
|
+
close: () => void;
|
|
223
|
+
toggle: () => void;
|
|
224
|
+
unmount: () => void;
|
|
225
|
+
isOpen: boolean;
|
|
226
|
+
handle: FloatHandle | null;
|
|
258
227
|
}
|
|
259
228
|
```
|
|
260
229
|
|
|
261
|
-
## Hooks
|
|
230
|
+
## Other Hooks
|
|
262
231
|
|
|
263
232
|
### useThemeSync
|
|
264
233
|
|
|
@@ -287,11 +256,15 @@ All types are exported:
|
|
|
287
256
|
|
|
288
257
|
```typescript
|
|
289
258
|
import type {
|
|
259
|
+
// Hook types
|
|
260
|
+
UsePopupOptions,
|
|
261
|
+
UsePopupReturn,
|
|
262
|
+
UseSliderOptions,
|
|
263
|
+
UseSliderReturn,
|
|
264
|
+
UseFloatBubbleOptions,
|
|
265
|
+
UseFloatBubbleReturn,
|
|
266
|
+
// Component types
|
|
290
267
|
WidgetProps,
|
|
291
|
-
PopupButtonProps,
|
|
292
|
-
PopupButtonHandle,
|
|
293
|
-
SliderButtonProps,
|
|
294
|
-
SliderButtonHandle,
|
|
295
268
|
FloatBubbleProps,
|
|
296
269
|
FullpageProps,
|
|
297
270
|
} from "@perspective-ai/sdk-react";
|
|
@@ -309,7 +282,7 @@ import type {
|
|
|
309
282
|
|
|
310
283
|
## SSR Safety
|
|
311
284
|
|
|
312
|
-
All components are SSR-safe and include the `"use client"` directive. Works with Next.js, Remix, and other React frameworks.
|
|
285
|
+
All hooks and components are SSR-safe and include the `"use client"` directive. Works with Next.js, Remix, and other React frameworks.
|
|
313
286
|
|
|
314
287
|
## License
|
|
315
288
|
|