fdbck-react 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 fdbck
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # fdbck-react
2
+
3
+ Official React SDK for [fdbck](https://fdbck.sh) — embed feedback questions natively in your React app with Shadow DOM CSS isolation.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install fdbck-react
9
+ ```
10
+
11
+ Requires React 18+.
12
+
13
+ ## Quick start
14
+
15
+ ### 1. Direct token (simplest)
16
+
17
+ Generate a response token on your backend (via the Node.js or Python SDK), then pass it to the widget:
18
+
19
+ ```tsx
20
+ import { FdbckWidget } from 'fdbck-react';
21
+
22
+ function App() {
23
+ return (
24
+ <FdbckWidget
25
+ token="V7xH2kQ9mPn4wR1j"
26
+ mode="inline"
27
+ onSubmit={(value) => console.log('Submitted:', value)}
28
+ />
29
+ );
30
+ }
31
+ ```
32
+
33
+ ### 2. Imperative API (provider pattern)
34
+
35
+ For showing feedback modals/popovers imperatively:
36
+
37
+ ```tsx
38
+ import { FdbckProvider, useFdbck } from 'fdbck-react';
39
+
40
+ function App() {
41
+ return (
42
+ <FdbckProvider>
43
+ <FeedbackButton />
44
+ </FdbckProvider>
45
+ );
46
+ }
47
+
48
+ function FeedbackButton() {
49
+ const { show } = useFdbck();
50
+
51
+ async function handleClick() {
52
+ const result = await show({
53
+ token: 'V7xH2kQ9mPn4wR1j',
54
+ mode: 'modal',
55
+ });
56
+
57
+ if (result.status === 'submitted') {
58
+ console.log('Response:', result.value);
59
+ }
60
+ }
61
+
62
+ return <button onClick={handleClick}>Give feedback</button>;
63
+ }
64
+ ```
65
+
66
+ ## Display modes
67
+
68
+ | Mode | Description |
69
+ |------|-------------|
70
+ | `inline` | Renders the card in the document flow |
71
+ | `modal` | Centered overlay with backdrop, focus trap, Escape to dismiss |
72
+ | `popover` | Bottom-right floating card (desktop), bottom sheet (mobile ≤640px) |
73
+
74
+ ## API reference
75
+
76
+ ### `<FdbckWidget>`
77
+
78
+ Takes a pre-resolved response token and renders the feedback widget.
79
+
80
+ | Prop | Type | Default | Description |
81
+ |------|------|---------|-------------|
82
+ | `token` | `string` | required | Response token |
83
+ | `mode` | `'inline' \| 'modal' \| 'popover'` | `'inline'` | Display mode |
84
+ | `open` | `boolean` | `true` | Whether the widget is visible |
85
+ | `baseUrl` | `string` | `'https://api.fdbck.sh'` | API base URL |
86
+ | `delay` | `number` | `0` | Delay in ms before showing |
87
+ | `autoCloseAfter` | `number` | — | Auto-dismiss after submission (ms) |
88
+ | `closeOnOverlayClick` | `boolean` | `true` | Dismiss modal on backdrop click |
89
+ | `closeOnEscape` | `boolean` | `true` | Dismiss on Escape key |
90
+ | `locale` | `FdbckLocale` | — | Custom text strings |
91
+ | `style` | `FdbckStyle` | — | Style overrides |
92
+ | `onSubmit` | `(value: ResponseValue) => void` | — | Called on successful submission |
93
+ | `onDismiss` | `() => void` | — | Called when dismissed |
94
+ | `onError` | `(error: FdbckError) => void` | — | Called on error |
95
+ | `onLoad` | `(question: QuestionData) => void` | — | Called when question data loads |
96
+
97
+ ### `<FdbckProvider>` + `useFdbck()`
98
+
99
+ Provider for the imperative API.
100
+
101
+ ```tsx
102
+ const { show, dismiss, isActive } = useFdbck();
103
+
104
+ // show() accepts token + display options, returns Promise<FdbckResult>
105
+ const result = await show({ token: '...', mode: 'modal' });
106
+ // result: { status: 'submitted', value: 'Yes' } or { status: 'dismissed' }
107
+ ```
108
+
109
+ ## Theming
110
+
111
+ The widget inherits theme settings from the question (`theme_color`, `theme_mode`). Override layout with the `style` prop:
112
+
113
+ ```tsx
114
+ <FdbckWidget
115
+ token="..."
116
+ style={{
117
+ maxWidth: '400px',
118
+ borderRadius: '1rem',
119
+ fontFamily: 'Inter, sans-serif',
120
+ }}
121
+ />
122
+ ```
123
+
124
+ ## CSS isolation
125
+
126
+ All styles are injected into a Shadow DOM, so the widget's CSS won't leak into your app and your app's CSS won't affect the widget.
127
+
128
+ ## Accessibility
129
+
130
+ - Modal mode includes a focus trap and `role="dialog"` with `aria-modal="true"`
131
+ - Popover mode includes `role="dialog"`
132
+ - All animations respect `prefers-reduced-motion`
133
+ - Interactive elements are keyboard-navigable
134
+
135
+ ## SSR compatibility
136
+
137
+ The widget uses `useEffect` for all DOM operations (Shadow DOM, fetch). It renders nothing on the server and hydrates on the client. Safe for Next.js, Remix, and other SSR frameworks.
138
+
139
+ ## Bundle size
140
+
141
+ ~7KB gzipped (including all CSS).
142
+
143
+ ## License
144
+
145
+ MIT
package/dist/index.css ADDED
@@ -0,0 +1,418 @@
1
+ /* src/styles/base.css */
2
+ *,
3
+ *::before,
4
+ *::after {
5
+ box-sizing: border-box;
6
+ margin: 0;
7
+ padding: 0;
8
+ }
9
+ :host {
10
+ display: block;
11
+ font-family: var(--fdbck-font-family);
12
+ font-size: var(--fdbck-font-size);
13
+ line-height: 1.5;
14
+ color: var(--fdbck-text-primary);
15
+ -webkit-font-smoothing: antialiased;
16
+ }
17
+ .fdbck-card {
18
+ width: var(--fdbck-width);
19
+ max-width: var(--fdbck-max-width);
20
+ background: var(--fdbck-card-bg);
21
+ border: 1px solid var(--fdbck-border);
22
+ border-radius: var(--fdbck-border-radius);
23
+ padding: 1.5rem;
24
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
25
+ }
26
+ .fdbck-welcome {
27
+ font-size: var(--fdbck-font-size);
28
+ color: var(--fdbck-text-secondary);
29
+ margin-bottom: 1rem;
30
+ white-space: pre-line;
31
+ }
32
+ .fdbck-question {
33
+ font-size: 1.125rem;
34
+ font-weight: 600;
35
+ color: var(--fdbck-text-primary);
36
+ margin-bottom: 1.5rem;
37
+ }
38
+ .fdbck-error-text {
39
+ color: #ef4444;
40
+ font-size: var(--fdbck-font-size);
41
+ margin-bottom: 1rem;
42
+ }
43
+ .fdbck-center {
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: center;
47
+ text-align: center;
48
+ flex-direction: column;
49
+ }
50
+ .fdbck-loading {
51
+ min-height: 200px;
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: center;
55
+ }
56
+ .fdbck-spinner {
57
+ width: 1.5rem;
58
+ height: 1.5rem;
59
+ border: 2px solid transparent;
60
+ border-top-color: var(--fdbck-accent);
61
+ border-right-color: var(--fdbck-accent);
62
+ border-radius: 50%;
63
+ animation: fdbck-spin 0.6s linear infinite;
64
+ }
65
+ @keyframes fdbck-spin {
66
+ to {
67
+ transform: rotate(360deg);
68
+ }
69
+ }
70
+ .fdbck-error-state {
71
+ padding: 2rem 0;
72
+ text-align: center;
73
+ }
74
+ .fdbck-error-state h2 {
75
+ font-size: 1.125rem;
76
+ font-weight: 600;
77
+ color: var(--fdbck-text-primary);
78
+ margin-bottom: 0.25rem;
79
+ }
80
+ .fdbck-error-state p {
81
+ color: var(--fdbck-text-secondary);
82
+ }
83
+ .fdbck-retry-btn {
84
+ margin-top: 1rem;
85
+ padding: 0.5rem 1rem;
86
+ border: 1px solid var(--fdbck-border);
87
+ border-radius: 0.5rem;
88
+ background: transparent;
89
+ color: var(--fdbck-text-primary);
90
+ font-size: var(--fdbck-font-size);
91
+ cursor: pointer;
92
+ transition: border-color 0.15s;
93
+ }
94
+ .fdbck-retry-btn:hover {
95
+ border-color: var(--fdbck-accent);
96
+ }
97
+
98
+ /* src/styles/question-types.css */
99
+ .fdbck-option-btn {
100
+ display: flex;
101
+ align-items: center;
102
+ gap: 0.75rem;
103
+ width: 100%;
104
+ padding: 0.75rem 1rem;
105
+ border: 1px solid var(--fdbck-border);
106
+ border-radius: 0.5rem;
107
+ background: transparent;
108
+ color: var(--fdbck-text-primary);
109
+ font-size: var(--fdbck-font-size);
110
+ text-align: left;
111
+ cursor: pointer;
112
+ transition: border-color 0.15s;
113
+ }
114
+ .fdbck-option-btn:hover:not(:disabled) {
115
+ border-color: var(--fdbck-accent);
116
+ }
117
+ .fdbck-option-btn:disabled {
118
+ opacity: 0.5;
119
+ cursor: not-allowed;
120
+ }
121
+ .fdbck-option-btn[data-selected=true] {
122
+ border-color: var(--fdbck-accent);
123
+ }
124
+ .fdbck-yesno-grid {
125
+ display: grid;
126
+ grid-template-columns: 1fr 1fr;
127
+ gap: 0.75rem;
128
+ }
129
+ .fdbck-yesno-grid .fdbck-option-btn {
130
+ justify-content: center;
131
+ font-weight: 500;
132
+ }
133
+ .fdbck-choice-list {
134
+ display: flex;
135
+ flex-direction: column;
136
+ gap: 0.5rem;
137
+ }
138
+ .fdbck-radio {
139
+ width: 1rem;
140
+ height: 1rem;
141
+ border: 2px solid var(--fdbck-indicator-border);
142
+ border-radius: 50%;
143
+ flex-shrink: 0;
144
+ display: flex;
145
+ align-items: center;
146
+ justify-content: center;
147
+ transition: border-color 0.15s;
148
+ }
149
+ .fdbck-radio[data-selected=true] {
150
+ border-color: var(--fdbck-accent);
151
+ }
152
+ .fdbck-radio-dot {
153
+ width: 0.5rem;
154
+ height: 0.5rem;
155
+ border-radius: 50%;
156
+ background: var(--fdbck-accent);
157
+ }
158
+ .fdbck-checkbox {
159
+ width: 1rem;
160
+ height: 1rem;
161
+ border: 2px solid var(--fdbck-indicator-border);
162
+ border-radius: 0.25rem;
163
+ flex-shrink: 0;
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ transition: border-color 0.15s, background-color 0.15s;
168
+ }
169
+ .fdbck-checkbox[data-selected=true] {
170
+ border-color: var(--fdbck-accent);
171
+ background-color: var(--fdbck-accent);
172
+ }
173
+ .fdbck-checkbox svg {
174
+ width: 0.75rem;
175
+ height: 0.75rem;
176
+ color: #fff;
177
+ }
178
+ .fdbck-submit-btn {
179
+ margin-top: 0.5rem;
180
+ padding: 0.625rem 1rem;
181
+ border: none;
182
+ border-radius: 0.5rem;
183
+ background: var(--fdbck-accent);
184
+ color: #fff;
185
+ font-size: var(--fdbck-font-size);
186
+ font-weight: 500;
187
+ cursor: pointer;
188
+ transition: opacity 0.15s;
189
+ }
190
+ .fdbck-submit-btn:disabled {
191
+ opacity: 0.4;
192
+ cursor: not-allowed;
193
+ }
194
+ .fdbck-rating-grid {
195
+ display: grid;
196
+ gap: 0.375rem;
197
+ }
198
+ .fdbck-rating-btn {
199
+ aspect-ratio: 1;
200
+ display: flex;
201
+ align-items: center;
202
+ justify-content: center;
203
+ padding: 0;
204
+ border: 1px solid var(--fdbck-border);
205
+ border-radius: 0.5rem;
206
+ background: transparent;
207
+ color: var(--fdbck-text-primary);
208
+ font-size: var(--fdbck-font-size);
209
+ font-weight: 500;
210
+ cursor: pointer;
211
+ transition:
212
+ border-color 0.15s,
213
+ background-color 0.15s,
214
+ color 0.15s;
215
+ }
216
+ .fdbck-rating-btn:hover:not(:disabled) {
217
+ border-color: var(--fdbck-accent);
218
+ }
219
+ .fdbck-rating-btn:disabled {
220
+ opacity: 0.5;
221
+ cursor: not-allowed;
222
+ }
223
+ .fdbck-rating-btn[data-selected=true] {
224
+ background-color: var(--fdbck-accent);
225
+ border-color: var(--fdbck-accent);
226
+ color: #fff;
227
+ }
228
+ .fdbck-rating-labels {
229
+ display: flex;
230
+ justify-content: space-between;
231
+ margin-top: 0.5rem;
232
+ font-size: 0.75rem;
233
+ color: var(--fdbck-text-muted);
234
+ }
235
+
236
+ /* src/styles/confirmation.css */
237
+ .fdbck-confirmation {
238
+ padding: 2rem 0;
239
+ text-align: center;
240
+ }
241
+ .fdbck-check-circle {
242
+ width: 3rem;
243
+ height: 3rem;
244
+ border-radius: 50%;
245
+ background: var(--fdbck-accent-alpha);
246
+ display: flex;
247
+ align-items: center;
248
+ justify-content: center;
249
+ margin: 0 auto 1rem;
250
+ animation: fdbck-scale-in 0.3s ease-out;
251
+ }
252
+ .fdbck-check-circle svg {
253
+ width: 1.5rem;
254
+ height: 1.5rem;
255
+ color: var(--fdbck-accent);
256
+ }
257
+ .fdbck-confirmation h2 {
258
+ font-size: 1.125rem;
259
+ font-weight: 600;
260
+ color: var(--fdbck-text-primary);
261
+ margin-bottom: 0.25rem;
262
+ }
263
+ .fdbck-confirmation p {
264
+ color: var(--fdbck-text-secondary);
265
+ white-space: pre-line;
266
+ }
267
+ @keyframes fdbck-scale-in {
268
+ from {
269
+ transform: scale(0.5);
270
+ opacity: 0;
271
+ }
272
+ to {
273
+ transform: scale(1);
274
+ opacity: 1;
275
+ }
276
+ }
277
+
278
+ /* src/styles/branding.css */
279
+ .fdbck-branding {
280
+ display: flex;
281
+ justify-content: center;
282
+ margin-top: 1rem;
283
+ padding-top: 0.75rem;
284
+ border-top: 1px solid var(--fdbck-border);
285
+ }
286
+ .fdbck-branding a {
287
+ font-size: 0.75rem;
288
+ color: var(--fdbck-text-muted);
289
+ text-decoration: none;
290
+ transition: color 0.15s;
291
+ }
292
+ .fdbck-branding a:hover {
293
+ color: var(--fdbck-text-secondary);
294
+ }
295
+ .fdbck-branding-name {
296
+ font-family: "JetBrains Mono", monospace;
297
+ font-weight: 500;
298
+ color: var(--fdbck-accent);
299
+ }
300
+
301
+ /* src/styles/modal.css */
302
+ .fdbck-modal-backdrop {
303
+ position: fixed;
304
+ inset: 0;
305
+ background: rgba(0, 0, 0, 0.5);
306
+ display: flex;
307
+ align-items: center;
308
+ justify-content: center;
309
+ padding: 1rem;
310
+ z-index: 2147483647;
311
+ animation: fdbck-fade-in 0.2s ease-out;
312
+ }
313
+ .fdbck-modal-content {
314
+ position: relative;
315
+ animation: fdbck-scale-up 0.2s ease-out;
316
+ }
317
+ .fdbck-close-btn {
318
+ position: absolute;
319
+ top: -0.5rem;
320
+ right: -0.5rem;
321
+ width: 1.75rem;
322
+ height: 1.75rem;
323
+ border: 1px solid var(--fdbck-border);
324
+ border-radius: 50%;
325
+ background: var(--fdbck-card-bg);
326
+ color: var(--fdbck-text-secondary);
327
+ cursor: pointer;
328
+ display: flex;
329
+ align-items: center;
330
+ justify-content: center;
331
+ font-size: 1rem;
332
+ line-height: 1;
333
+ transition: border-color 0.15s;
334
+ z-index: 1;
335
+ }
336
+ .fdbck-close-btn:hover {
337
+ border-color: var(--fdbck-accent);
338
+ }
339
+ @keyframes fdbck-fade-in {
340
+ from {
341
+ opacity: 0;
342
+ }
343
+ to {
344
+ opacity: 1;
345
+ }
346
+ }
347
+ @keyframes fdbck-scale-up {
348
+ from {
349
+ transform: scale(0.95);
350
+ opacity: 0;
351
+ }
352
+ to {
353
+ transform: scale(1);
354
+ opacity: 1;
355
+ }
356
+ }
357
+ @media (prefers-reduced-motion: reduce) {
358
+ .fdbck-modal-backdrop,
359
+ .fdbck-modal-content {
360
+ animation: none;
361
+ }
362
+ }
363
+
364
+ /* src/styles/popover.css */
365
+ .fdbck-popover {
366
+ position: fixed;
367
+ bottom: 1.5rem;
368
+ right: 1.5rem;
369
+ z-index: 2147483647;
370
+ animation: fdbck-slide-up 0.25s ease-out;
371
+ }
372
+ .fdbck-popover .fdbck-close-btn {
373
+ position: absolute;
374
+ top: -0.5rem;
375
+ right: -0.5rem;
376
+ }
377
+ @media (max-width: 640px) {
378
+ .fdbck-popover {
379
+ bottom: 0;
380
+ right: 0;
381
+ left: 0;
382
+ padding: 0;
383
+ animation: fdbck-sheet-up 0.25s ease-out;
384
+ }
385
+ .fdbck-popover .fdbck-card {
386
+ max-width: 100%;
387
+ width: 100%;
388
+ border-radius: var(--fdbck-border-radius) var(--fdbck-border-radius) 0 0;
389
+ border-bottom: none;
390
+ }
391
+ .fdbck-popover .fdbck-close-btn {
392
+ top: 0.75rem;
393
+ right: 0.75rem;
394
+ }
395
+ }
396
+ @keyframes fdbck-slide-up {
397
+ from {
398
+ transform: translateY(1rem);
399
+ opacity: 0;
400
+ }
401
+ to {
402
+ transform: translateY(0);
403
+ opacity: 1;
404
+ }
405
+ }
406
+ @keyframes fdbck-sheet-up {
407
+ from {
408
+ transform: translateY(100%);
409
+ }
410
+ to {
411
+ transform: translateY(0);
412
+ }
413
+ }
414
+ @media (prefers-reduced-motion: reduce) {
415
+ .fdbck-popover {
416
+ animation: none;
417
+ }
418
+ }
@@ -0,0 +1,115 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ /** Error codes produced by the React SDK */
5
+ type FdbckErrorCode = 'invalid_token' | 'already_responded' | 'question_expired' | 'network_error' | 'dismissed' | 'unknown';
6
+ /** Typed error for fdbck React SDK */
7
+ declare class FdbckError extends Error {
8
+ readonly code: FdbckErrorCode;
9
+ constructor(code: FdbckErrorCode, message: string);
10
+ }
11
+
12
+ /** Question type */
13
+ type QuestionType = 'yes_no' | 'single_choice' | 'multiple_choice' | 'rating';
14
+ /** Rating scale options as returned by the API */
15
+ interface RatingOptions {
16
+ min: number;
17
+ max: number;
18
+ min_label?: string;
19
+ max_label?: string;
20
+ }
21
+ /** Question data from GET /v1/f/:token */
22
+ interface QuestionData {
23
+ id: string;
24
+ question: string;
25
+ type: QuestionType;
26
+ options: string[] | RatingOptions;
27
+ theme_color: string | null;
28
+ theme_mode: 'light' | 'dark';
29
+ hide_branding: boolean;
30
+ welcome_message: string | null;
31
+ thank_you_message: string | null;
32
+ }
33
+ /** Response value submitted by the respondent */
34
+ type ResponseValue = string | string[] | number;
35
+ /** Display mode for the widget */
36
+ type FdbckMode = 'inline' | 'modal' | 'popover';
37
+ /** Result returned when a response is submitted or dismissed */
38
+ interface FdbckResult {
39
+ status: 'submitted' | 'dismissed';
40
+ value?: ResponseValue;
41
+ }
42
+ /** Custom locale strings */
43
+ interface FdbckLocale {
44
+ submit?: string;
45
+ poweredBy?: string;
46
+ loading?: string;
47
+ errorTitle?: string;
48
+ errorMessage?: string;
49
+ retry?: string;
50
+ close?: string;
51
+ }
52
+ /** Style overrides for the widget */
53
+ interface FdbckStyle {
54
+ width?: string;
55
+ maxWidth?: string;
56
+ borderRadius?: string;
57
+ fontFamily?: string;
58
+ fontSize?: string;
59
+ }
60
+ /** Props for the low-level FdbckWidget component (takes a pre-resolved token) */
61
+ interface FdbckWidgetProps {
62
+ token: string;
63
+ mode?: FdbckMode;
64
+ open?: boolean;
65
+ baseUrl?: string;
66
+ delay?: number;
67
+ autoCloseAfter?: number;
68
+ closeOnOverlayClick?: boolean;
69
+ closeOnEscape?: boolean;
70
+ locale?: FdbckLocale;
71
+ style?: FdbckStyle;
72
+ onSubmit?: (value: ResponseValue) => void;
73
+ onDismiss?: () => void;
74
+ onError?: (error: FdbckError) => void;
75
+ onLoad?: (question: QuestionData) => void;
76
+ children?: ReactNode;
77
+ }
78
+ /** Props for the FdbckProvider context */
79
+ interface FdbckProviderProps {
80
+ baseUrl?: string;
81
+ locale?: FdbckLocale;
82
+ style?: FdbckStyle;
83
+ children: ReactNode;
84
+ }
85
+ /** Return type from useFdbck hook */
86
+ interface UseFdbckReturn {
87
+ show: (options: UseFdbckOptions) => Promise<FdbckResult>;
88
+ dismiss: () => void;
89
+ isActive: boolean;
90
+ }
91
+ /** Options passed to useFdbck().show() */
92
+ type UseFdbckOptions = {
93
+ token: string;
94
+ } & Omit<FdbckWidgetProps, 'token' | 'onSubmit' | 'onDismiss'>;
95
+ /** Token page response from GET /v1/f/:token */
96
+ interface TokenPageResponse {
97
+ status: 'valid' | 'invalid' | 'already_responded' | 'token_expired' | 'question_ended';
98
+ question?: QuestionData;
99
+ token?: string;
100
+ expires_at?: string;
101
+ }
102
+
103
+ /**
104
+ * Low-level public component. Takes a pre-resolved token and renders the
105
+ * feedback widget in inline, modal, or popover mode.
106
+ */
107
+ declare function FdbckWidget({ token, mode, open, baseUrl, delay, autoCloseAfter, closeOnOverlayClick, closeOnEscape, locale: localeProp, style: styleProp, onSubmit, onDismiss, onError, onLoad, }: FdbckWidgetProps): react_jsx_runtime.JSX.Element | null;
108
+
109
+ /** Context provider for the imperative useFdbck() hook */
110
+ declare function FdbckProvider({ baseUrl, locale, style, children }: FdbckProviderProps): react_jsx_runtime.JSX.Element;
111
+
112
+ /** Imperative API for showing/dismissing feedback widgets. Must be used within FdbckProvider. */
113
+ declare function useFdbck(): UseFdbckReturn;
114
+
115
+ export { FdbckError, type FdbckErrorCode, type FdbckLocale, type FdbckMode, FdbckProvider, type FdbckProviderProps, type FdbckResult, type FdbckStyle, FdbckWidget, type FdbckWidgetProps, type QuestionData, type QuestionType, type RatingOptions, type ResponseValue, type TokenPageResponse, type UseFdbckOptions, type UseFdbckReturn, useFdbck };