@landform.io/sdk 0.1.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.
Files changed (63) hide show
  1. package/dist/api/index.cjs +17 -0
  2. package/dist/api/index.cjs.map +1 -0
  3. package/dist/api/index.d.cts +48 -0
  4. package/dist/api/index.d.ts +48 -0
  5. package/dist/api/index.js +4 -0
  6. package/dist/api/index.js.map +1 -0
  7. package/dist/chunk-52W6RI6C.cjs +4 -0
  8. package/dist/chunk-52W6RI6C.cjs.map +1 -0
  9. package/dist/chunk-DASQI7IA.cjs +4 -0
  10. package/dist/chunk-DASQI7IA.cjs.map +1 -0
  11. package/dist/chunk-EL7YGOTY.js +3 -0
  12. package/dist/chunk-EL7YGOTY.js.map +1 -0
  13. package/dist/chunk-GVOTY5NG.js +3 -0
  14. package/dist/chunk-GVOTY5NG.js.map +1 -0
  15. package/dist/chunk-HUIMGLDC.js +488 -0
  16. package/dist/chunk-HUIMGLDC.js.map +1 -0
  17. package/dist/chunk-I6L5OCM3.js +1657 -0
  18. package/dist/chunk-I6L5OCM3.js.map +1 -0
  19. package/dist/chunk-IQXKFO6Q.cjs +55 -0
  20. package/dist/chunk-IQXKFO6Q.cjs.map +1 -0
  21. package/dist/chunk-P2GUKJHH.cjs +117 -0
  22. package/dist/chunk-P2GUKJHH.cjs.map +1 -0
  23. package/dist/chunk-PDUJU32P.js +687 -0
  24. package/dist/chunk-PDUJU32P.js.map +1 -0
  25. package/dist/chunk-PKHTPGWQ.js +114 -0
  26. package/dist/chunk-PKHTPGWQ.js.map +1 -0
  27. package/dist/chunk-V7WAYO2Q.js +48 -0
  28. package/dist/chunk-V7WAYO2Q.js.map +1 -0
  29. package/dist/chunk-WHV333XL.cjs +1684 -0
  30. package/dist/chunk-WHV333XL.cjs.map +1 -0
  31. package/dist/chunk-WIFNU3FA.cjs +497 -0
  32. package/dist/chunk-WIFNU3FA.cjs.map +1 -0
  33. package/dist/chunk-ZLOP4BTK.cjs +695 -0
  34. package/dist/chunk-ZLOP4BTK.cjs.map +1 -0
  35. package/dist/components/index.cjs +99 -0
  36. package/dist/components/index.cjs.map +1 -0
  37. package/dist/components/index.d.cts +166 -0
  38. package/dist/components/index.d.ts +166 -0
  39. package/dist/components/index.js +6 -0
  40. package/dist/components/index.js.map +1 -0
  41. package/dist/hooks/index.cjs +35 -0
  42. package/dist/hooks/index.cjs.map +1 -0
  43. package/dist/hooks/index.d.cts +75 -0
  44. package/dist/hooks/index.d.ts +75 -0
  45. package/dist/hooks/index.js +6 -0
  46. package/dist/hooks/index.js.map +1 -0
  47. package/dist/index-DoRZImTl.d.cts +590 -0
  48. package/dist/index-DoRZImTl.d.ts +590 -0
  49. package/dist/index.cjs +186 -0
  50. package/dist/index.cjs.map +1 -0
  51. package/dist/index.d.cts +8 -0
  52. package/dist/index.d.ts +8 -0
  53. package/dist/index.js +9 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/theme/index.cjs +61 -0
  56. package/dist/theme/index.cjs.map +1 -0
  57. package/dist/theme/index.d.cts +65 -0
  58. package/dist/theme/index.d.ts +65 -0
  59. package/dist/theme/index.js +4 -0
  60. package/dist/theme/index.js.map +1 -0
  61. package/dist/useForm-D1mB6REv.d.ts +48 -0
  62. package/dist/useForm-kF8xK1mJ.d.cts +48 -0
  63. package/package.json +101 -0
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ require('../chunk-52W6RI6C.cjs');
4
+ var chunkP2GUKJHH_cjs = require('../chunk-P2GUKJHH.cjs');
5
+
6
+
7
+
8
+ Object.defineProperty(exports, "LandformClient", {
9
+ enumerable: true,
10
+ get: function () { return chunkP2GUKJHH_cjs.LandformClient; }
11
+ });
12
+ Object.defineProperty(exports, "createClient", {
13
+ enumerable: true,
14
+ get: function () { return chunkP2GUKJHH_cjs.createClient; }
15
+ });
16
+ //# sourceMappingURL=index.cjs.map
17
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
@@ -0,0 +1,48 @@
1
+ import { a3 as ResponseAnswers, a9 as ResponseOutcome, aa as ResponseMetadata } from '../index-DoRZImTl.cjs';
2
+
3
+ interface LandformClientConfig {
4
+ baseUrl?: string;
5
+ projectId: string;
6
+ onError?: (error: Error) => void;
7
+ }
8
+ interface StartResponseParams {
9
+ sessionId?: string;
10
+ metadata?: ResponseMetadata;
11
+ hiddenFields?: Record<string, string>;
12
+ }
13
+ interface StartResponseResult {
14
+ id: string;
15
+ sessionId: string;
16
+ }
17
+ interface UpdateAnswersParams {
18
+ responseId: string;
19
+ sessionId: string;
20
+ answers: ResponseAnswers;
21
+ lastFieldRef?: string;
22
+ calculatedValues?: Record<string, number>;
23
+ }
24
+ interface CompleteResponseParams {
25
+ responseId: string;
26
+ sessionId: string;
27
+ answers?: ResponseAnswers;
28
+ outcome?: ResponseOutcome;
29
+ calculatedValues?: Record<string, number>;
30
+ captchaToken?: string;
31
+ }
32
+ interface TrackEventParams {
33
+ event: "view" | "start";
34
+ sessionId?: string;
35
+ }
36
+ declare class LandformClient {
37
+ private baseUrl;
38
+ private projectId;
39
+ private onError?;
40
+ constructor(config: LandformClientConfig);
41
+ startResponse(params?: StartResponseParams): Promise<StartResponseResult>;
42
+ updateAnswers(params: UpdateAnswersParams): Promise<void>;
43
+ completeResponse(params: CompleteResponseParams): Promise<void>;
44
+ trackEvent(params: TrackEventParams): Promise<void>;
45
+ }
46
+ declare function createClient(config: LandformClientConfig): LandformClient;
47
+
48
+ export { type CompleteResponseParams, LandformClient, type LandformClientConfig, type StartResponseParams, type StartResponseResult, type UpdateAnswersParams, createClient };
@@ -0,0 +1,48 @@
1
+ import { a3 as ResponseAnswers, a9 as ResponseOutcome, aa as ResponseMetadata } from '../index-DoRZImTl.js';
2
+
3
+ interface LandformClientConfig {
4
+ baseUrl?: string;
5
+ projectId: string;
6
+ onError?: (error: Error) => void;
7
+ }
8
+ interface StartResponseParams {
9
+ sessionId?: string;
10
+ metadata?: ResponseMetadata;
11
+ hiddenFields?: Record<string, string>;
12
+ }
13
+ interface StartResponseResult {
14
+ id: string;
15
+ sessionId: string;
16
+ }
17
+ interface UpdateAnswersParams {
18
+ responseId: string;
19
+ sessionId: string;
20
+ answers: ResponseAnswers;
21
+ lastFieldRef?: string;
22
+ calculatedValues?: Record<string, number>;
23
+ }
24
+ interface CompleteResponseParams {
25
+ responseId: string;
26
+ sessionId: string;
27
+ answers?: ResponseAnswers;
28
+ outcome?: ResponseOutcome;
29
+ calculatedValues?: Record<string, number>;
30
+ captchaToken?: string;
31
+ }
32
+ interface TrackEventParams {
33
+ event: "view" | "start";
34
+ sessionId?: string;
35
+ }
36
+ declare class LandformClient {
37
+ private baseUrl;
38
+ private projectId;
39
+ private onError?;
40
+ constructor(config: LandformClientConfig);
41
+ startResponse(params?: StartResponseParams): Promise<StartResponseResult>;
42
+ updateAnswers(params: UpdateAnswersParams): Promise<void>;
43
+ completeResponse(params: CompleteResponseParams): Promise<void>;
44
+ trackEvent(params: TrackEventParams): Promise<void>;
45
+ }
46
+ declare function createClient(config: LandformClientConfig): LandformClient;
47
+
48
+ export { type CompleteResponseParams, LandformClient, type LandformClientConfig, type StartResponseParams, type StartResponseResult, type UpdateAnswersParams, createClient };
@@ -0,0 +1,4 @@
1
+ import '../chunk-GVOTY5NG.js';
2
+ export { LandformClient, createClient } from '../chunk-PKHTPGWQ.js';
3
+ //# sourceMappingURL=index.js.map
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,4 @@
1
+ 'use strict';
2
+
3
+ //# sourceMappingURL=chunk-52W6RI6C.cjs.map
4
+ //# sourceMappingURL=chunk-52W6RI6C.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-52W6RI6C.cjs"}
@@ -0,0 +1,4 @@
1
+ 'use strict';
2
+
3
+ //# sourceMappingURL=chunk-DASQI7IA.cjs.map
4
+ //# sourceMappingURL=chunk-DASQI7IA.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-DASQI7IA.cjs"}
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=chunk-EL7YGOTY.js.map
3
+ //# sourceMappingURL=chunk-EL7YGOTY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-EL7YGOTY.js"}
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=chunk-GVOTY5NG.js.map
3
+ //# sourceMappingURL=chunk-GVOTY5NG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-GVOTY5NG.js"}
@@ -0,0 +1,488 @@
1
+ import { generateThemeCSS, generateComponentStyles, isBrowser, injectThemeCSS } from './chunk-PDUJU32P.js';
2
+ import { LandformClient } from './chunk-PKHTPGWQ.js';
3
+ import { createContext, useState, useMemo, useRef, useEffect, useCallback, useContext } from 'react';
4
+ import { nanoid } from 'nanoid';
5
+
6
+ // src/utils/storage.ts
7
+ var AUTOSAVE_PREFIX = "lf-autosave-";
8
+ var SUBMITTED_PREFIX = "lf-submitted-";
9
+ var COOKIE_CONSENT_KEY = "lf-cookie-consent";
10
+ function saveProgress(projectId, answers, currentIndex, responseId) {
11
+ if (typeof window === "undefined") return;
12
+ try {
13
+ const data = {
14
+ answers,
15
+ currentIndex,
16
+ responseId,
17
+ timestamp: Date.now()
18
+ };
19
+ localStorage.setItem(AUTOSAVE_PREFIX + projectId, JSON.stringify(data));
20
+ } catch (error) {
21
+ console.error("Error saving form progress:", error);
22
+ }
23
+ }
24
+ function loadProgress(projectId) {
25
+ if (typeof window === "undefined") return null;
26
+ try {
27
+ const stored = localStorage.getItem(AUTOSAVE_PREFIX + projectId);
28
+ if (!stored) return null;
29
+ const data = JSON.parse(stored);
30
+ const maxAge = 7 * 24 * 60 * 60 * 1e3;
31
+ if (Date.now() - data.timestamp > maxAge) {
32
+ clearProgress(projectId);
33
+ return null;
34
+ }
35
+ return data;
36
+ } catch (error) {
37
+ console.error("Error loading form progress:", error);
38
+ return null;
39
+ }
40
+ }
41
+ function clearProgress(projectId) {
42
+ if (typeof window === "undefined") return;
43
+ try {
44
+ localStorage.removeItem(AUTOSAVE_PREFIX + projectId);
45
+ } catch (error) {
46
+ console.error("Error clearing form progress:", error);
47
+ }
48
+ }
49
+ function markAsSubmitted(projectId) {
50
+ if (typeof window === "undefined") return;
51
+ try {
52
+ localStorage.setItem(SUBMITTED_PREFIX + projectId, String(Date.now()));
53
+ } catch (error) {
54
+ console.error("Error marking form as submitted:", error);
55
+ }
56
+ }
57
+ function hasSubmitted(projectId) {
58
+ if (typeof window === "undefined") return false;
59
+ try {
60
+ return localStorage.getItem(SUBMITTED_PREFIX + projectId) !== null;
61
+ } catch (error) {
62
+ console.error("Error checking submission status:", error);
63
+ return false;
64
+ }
65
+ }
66
+ function hasCookieConsent() {
67
+ if (typeof window === "undefined") return false;
68
+ try {
69
+ return localStorage.getItem(COOKIE_CONSENT_KEY) === "true";
70
+ } catch (error) {
71
+ console.error("Error checking cookie consent:", error);
72
+ return false;
73
+ }
74
+ }
75
+ function setCookieConsent(consented) {
76
+ if (typeof window === "undefined") return;
77
+ try {
78
+ if (consented) {
79
+ localStorage.setItem(COOKIE_CONSENT_KEY, "true");
80
+ } else {
81
+ localStorage.removeItem(COOKIE_CONSENT_KEY);
82
+ }
83
+ } catch (error) {
84
+ console.error("Error setting cookie consent:", error);
85
+ }
86
+ }
87
+
88
+ // src/hooks/useForm.ts
89
+ function validateFieldValue(field, value) {
90
+ if (!field.validations?.required) {
91
+ return true;
92
+ }
93
+ if (value === void 0 || value === null || value === "") {
94
+ return false;
95
+ }
96
+ if (Array.isArray(value) && value.length === 0) {
97
+ return false;
98
+ }
99
+ return true;
100
+ }
101
+ function useForm(options) {
102
+ const {
103
+ projectId,
104
+ content,
105
+ settings,
106
+ baseUrl = "",
107
+ initialAnswers = {},
108
+ onComplete,
109
+ onError
110
+ } = options;
111
+ const [responseId, setResponseId] = useState(null);
112
+ const [sessionId] = useState(() => nanoid());
113
+ const [currentIndex, setCurrentIndex] = useState(0);
114
+ const [answers, setAnswers] = useState(initialAnswers);
115
+ const [errors, setErrors] = useState({});
116
+ const [isStarted, setIsStarted] = useState(false);
117
+ const [isCompleted, setIsCompleted] = useState(false);
118
+ const [isSubmitting, setIsSubmitting] = useState(false);
119
+ const [isDuplicateSubmission, setIsDuplicateSubmission] = useState(false);
120
+ const [captchaToken, setCaptchaToken] = useState(null);
121
+ const [showCaptcha, setShowCaptcha] = useState(false);
122
+ const client = useMemo(
123
+ () => new LandformClient({ baseUrl, projectId, onError }),
124
+ [baseUrl, projectId, onError]
125
+ );
126
+ const hasTrackedView = useRef(false);
127
+ const hasLoadedProgress = useRef(false);
128
+ const autosaveTimeoutRef = useRef(null);
129
+ useEffect(() => {
130
+ if (!hasTrackedView.current) {
131
+ hasTrackedView.current = true;
132
+ client.trackEvent({ event: "view", sessionId });
133
+ }
134
+ }, [client, sessionId]);
135
+ useEffect(() => {
136
+ if (settings.duplicatePrevention && hasSubmitted(projectId)) {
137
+ setIsDuplicateSubmission(true);
138
+ }
139
+ }, [projectId, settings.duplicatePrevention]);
140
+ useEffect(() => {
141
+ if (hasLoadedProgress.current || !settings.autosaveProgress) return;
142
+ hasLoadedProgress.current = true;
143
+ const savedProgress = loadProgress(projectId);
144
+ if (savedProgress) {
145
+ setAnswers(savedProgress.answers);
146
+ setCurrentIndex(savedProgress.currentIndex);
147
+ if (savedProgress.responseId) {
148
+ setResponseId(savedProgress.responseId);
149
+ setIsStarted(true);
150
+ }
151
+ }
152
+ }, [projectId, settings.autosaveProgress]);
153
+ useEffect(() => {
154
+ if (!settings.autosaveProgress || isCompleted) return;
155
+ if (autosaveTimeoutRef.current) {
156
+ clearTimeout(autosaveTimeoutRef.current);
157
+ }
158
+ autosaveTimeoutRef.current = setTimeout(() => {
159
+ saveProgress(projectId, answers, currentIndex, responseId);
160
+ }, 500);
161
+ return () => {
162
+ if (autosaveTimeoutRef.current) {
163
+ clearTimeout(autosaveTimeoutRef.current);
164
+ }
165
+ };
166
+ }, [projectId, answers, currentIndex, responseId, settings.autosaveProgress, isCompleted]);
167
+ const sequence = useMemo(() => {
168
+ const items = [];
169
+ for (const screen of content.welcomeScreens) {
170
+ items.push({ type: "welcome", screen });
171
+ }
172
+ content.fields.forEach((field, index) => {
173
+ items.push({ type: "field", field, index });
174
+ });
175
+ for (const screen of content.thankYouScreens) {
176
+ items.push({ type: "thankYou", screen });
177
+ }
178
+ return items;
179
+ }, [content]);
180
+ const currentItem = sequence[currentIndex] || null;
181
+ const totalFields = content.fields.length;
182
+ const answeredCount = Object.keys(answers).length;
183
+ const progress = totalFields > 0 ? answeredCount / totalFields * 100 : 0;
184
+ const firstFieldIndex = content.welcomeScreens.length;
185
+ const lastFieldIndex = firstFieldIndex + content.fields.length - 1;
186
+ const canGoBack = currentIndex > firstFieldIndex;
187
+ const canGoNext = currentIndex < sequence.length - 1;
188
+ const isOnWelcome = currentItem?.type === "welcome";
189
+ const isOnThankYou = currentItem?.type === "thankYou";
190
+ const isOnField = currentItem?.type === "field";
191
+ const start = useCallback(async () => {
192
+ try {
193
+ const result = await client.startResponse({ sessionId });
194
+ setResponseId(result.id);
195
+ setIsStarted(true);
196
+ client.trackEvent({ event: "start", sessionId });
197
+ if (currentItem?.type === "welcome") {
198
+ setCurrentIndex((prev) => prev + 1);
199
+ }
200
+ } catch (error) {
201
+ onError?.(error);
202
+ }
203
+ }, [client, sessionId, currentItem, onError]);
204
+ const validateCurrentWithValue = useCallback((pendingValue) => {
205
+ if (currentItem?.type !== "field") return true;
206
+ const field = currentItem.field;
207
+ const value = pendingValue !== void 0 ? pendingValue : answers[field.ref];
208
+ const isValid = validateFieldValue(field, value);
209
+ if (!isValid) {
210
+ const requiredText = settings.systemMessages?.requiredText || "This field is required";
211
+ setErrors((prev) => ({
212
+ ...prev,
213
+ [field.ref]: requiredText
214
+ }));
215
+ }
216
+ return isValid;
217
+ }, [currentItem, answers, settings.systemMessages?.requiredText]);
218
+ const validateCurrent = useCallback(() => {
219
+ return validateCurrentWithValue();
220
+ }, [validateCurrentWithValue]);
221
+ const submit = useCallback(async () => {
222
+ if (!responseId) return;
223
+ if (settings.captchaEnabled && !captchaToken) {
224
+ setShowCaptcha(true);
225
+ return;
226
+ }
227
+ setIsSubmitting(true);
228
+ try {
229
+ await client.completeResponse({
230
+ responseId,
231
+ sessionId,
232
+ answers,
233
+ captchaToken: captchaToken || void 0
234
+ });
235
+ setIsCompleted(true);
236
+ setCurrentIndex(sequence.length - 1);
237
+ clearProgress(projectId);
238
+ if (settings.duplicatePrevention) {
239
+ markAsSubmitted(projectId);
240
+ }
241
+ onComplete?.(answers);
242
+ } catch (error) {
243
+ onError?.(error);
244
+ } finally {
245
+ setIsSubmitting(false);
246
+ setShowCaptcha(false);
247
+ }
248
+ }, [responseId, sessionId, answers, captchaToken, client, sequence.length, projectId, settings.captchaEnabled, settings.duplicatePrevention, onComplete, onError]);
249
+ const next = useCallback(async (pendingValue) => {
250
+ if (currentItem?.type === "field") {
251
+ const isValid = validateCurrentWithValue(pendingValue);
252
+ if (!isValid) {
253
+ return;
254
+ }
255
+ if (currentIndex === lastFieldIndex) {
256
+ if (pendingValue !== void 0) {
257
+ setAnswers((prev) => ({ ...prev, [currentItem.field.ref]: pendingValue }));
258
+ }
259
+ await submit();
260
+ return;
261
+ }
262
+ const answersToSave = pendingValue !== void 0 ? { ...answers, [currentItem.field.ref]: pendingValue } : answers;
263
+ if (responseId) {
264
+ client.updateAnswers({
265
+ responseId,
266
+ sessionId,
267
+ answers: answersToSave,
268
+ lastFieldRef: currentItem.field.ref
269
+ }).catch((error) => {
270
+ onError?.(error);
271
+ });
272
+ }
273
+ }
274
+ setCurrentIndex((prev) => Math.min(prev + 1, sequence.length - 1));
275
+ }, [
276
+ currentItem,
277
+ currentIndex,
278
+ lastFieldIndex,
279
+ sequence.length,
280
+ responseId,
281
+ sessionId,
282
+ answers,
283
+ client,
284
+ onError,
285
+ validateCurrentWithValue,
286
+ submit
287
+ ]);
288
+ const previous = useCallback(() => {
289
+ setCurrentIndex((prev) => Math.max(prev - 1, firstFieldIndex));
290
+ }, [firstFieldIndex]);
291
+ const goTo = useCallback(
292
+ (index) => {
293
+ if (index >= 0 && index < sequence.length) {
294
+ setCurrentIndex(index);
295
+ }
296
+ },
297
+ [sequence.length]
298
+ );
299
+ const setAnswer = useCallback((fieldRef, value) => {
300
+ setAnswers((prev) => ({ ...prev, [fieldRef]: value }));
301
+ setErrors((prev) => {
302
+ const newErrors = { ...prev };
303
+ delete newErrors[fieldRef];
304
+ return newErrors;
305
+ });
306
+ }, []);
307
+ const reset = useCallback(() => {
308
+ setResponseId(null);
309
+ setCurrentIndex(0);
310
+ setAnswers(initialAnswers);
311
+ setErrors({});
312
+ setIsStarted(false);
313
+ setIsCompleted(false);
314
+ setIsSubmitting(false);
315
+ }, [initialAnswers]);
316
+ useEffect(() => {
317
+ if (content.welcomeScreens.length === 0 && !isStarted) {
318
+ start();
319
+ }
320
+ }, [content.welcomeScreens.length, isStarted, start]);
321
+ return {
322
+ // State
323
+ currentIndex,
324
+ currentItem,
325
+ answers,
326
+ errors,
327
+ isStarted,
328
+ isCompleted,
329
+ isSubmitting,
330
+ responseId,
331
+ sessionId,
332
+ isDuplicateSubmission,
333
+ captchaToken,
334
+ showCaptcha,
335
+ // Computed
336
+ sequence,
337
+ totalFields,
338
+ answeredCount,
339
+ progress,
340
+ canGoBack,
341
+ canGoNext,
342
+ isOnWelcome,
343
+ isOnThankYou,
344
+ isOnField,
345
+ // Actions
346
+ start,
347
+ next,
348
+ previous,
349
+ goTo,
350
+ setAnswer,
351
+ validateCurrent,
352
+ submit,
353
+ reset,
354
+ setCaptchaToken
355
+ };
356
+ }
357
+ var ThemeContext = createContext(null);
358
+ function useThemeContext() {
359
+ const theme = useContext(ThemeContext);
360
+ if (!theme) {
361
+ throw new Error("useThemeContext must be used within a ThemeProvider");
362
+ }
363
+ return theme;
364
+ }
365
+ function useTheme(options) {
366
+ const { theme, inject = true } = options;
367
+ const cssVariables = useMemo(() => generateThemeCSS(theme), [theme]);
368
+ const componentStyles = useMemo(() => generateComponentStyles(), []);
369
+ useEffect(() => {
370
+ if (inject && isBrowser()) {
371
+ const cleanup = injectThemeCSS(theme);
372
+ return cleanup;
373
+ }
374
+ }, [theme, inject]);
375
+ const backgroundStyle = useMemo(() => {
376
+ const bg = theme.background;
377
+ switch (bg.type) {
378
+ case "gradient":
379
+ return { background: bg.value };
380
+ case "image":
381
+ return {
382
+ backgroundImage: `url(${bg.value})`,
383
+ backgroundSize: "cover",
384
+ backgroundPosition: bg.position || "center"
385
+ };
386
+ case "video":
387
+ return {};
388
+ default:
389
+ return {};
390
+ }
391
+ }, [theme.background]);
392
+ return {
393
+ theme,
394
+ cssVariables,
395
+ componentStyles,
396
+ backgroundStyle
397
+ };
398
+ }
399
+ function useField(options) {
400
+ const { field, value, error, onChange, onNext } = options;
401
+ const isEmpty = value === void 0 || value === "" || Array.isArray(value) && value.length === 0;
402
+ const onKeyDown = useCallback(
403
+ (e) => {
404
+ if (e.key === "Enter" && !e.shiftKey && field.type !== "long_text") {
405
+ e.preventDefault();
406
+ onNext?.();
407
+ }
408
+ },
409
+ [field.type, onNext]
410
+ );
411
+ const placeholder = useMemo(() => {
412
+ if ("properties" in field && field.properties) {
413
+ const props = field.properties;
414
+ if ("placeholder" in props) {
415
+ return props.placeholder;
416
+ }
417
+ }
418
+ return void 0;
419
+ }, [field]);
420
+ const getInputProps = useCallback(() => {
421
+ return {
422
+ value: value || "",
423
+ onChange: (e) => {
424
+ onChange(e.target.value);
425
+ },
426
+ onKeyDown,
427
+ placeholder,
428
+ "aria-invalid": !!error,
429
+ "aria-describedby": error ? `${field.ref}-error` : void 0
430
+ };
431
+ }, [value, onChange, onKeyDown, field.ref, error, placeholder]);
432
+ const getTextareaProps = useCallback(() => {
433
+ return {
434
+ value: value || "",
435
+ onChange: (e) => {
436
+ onChange(e.target.value);
437
+ },
438
+ placeholder,
439
+ "aria-invalid": !!error,
440
+ "aria-describedby": error ? `${field.ref}-error` : void 0
441
+ };
442
+ }, [value, onChange, field.ref, error, placeholder]);
443
+ return {
444
+ // Field info
445
+ ref: field.ref,
446
+ type: field.type,
447
+ title: field.title,
448
+ description: field.description,
449
+ required: field.validations?.required || false,
450
+ // State
451
+ value,
452
+ error,
453
+ isEmpty,
454
+ // Handlers
455
+ onChange,
456
+ onKeyDown,
457
+ // Helpers
458
+ getInputProps,
459
+ getTextareaProps
460
+ };
461
+ }
462
+ function useProgress(options) {
463
+ const { content, answers, currentIndex } = options;
464
+ return useMemo(() => {
465
+ const totalFields = content.fields.length;
466
+ const answeredCount = Object.keys(answers).length;
467
+ const welcomeCount = content.welcomeScreens.length;
468
+ const currentFieldIndex = Math.max(0, currentIndex - welcomeCount);
469
+ const percentage = totalFields > 0 ? answeredCount / totalFields * 100 : 0;
470
+ const segments = content.fields.map((field, index) => ({
471
+ index,
472
+ fieldRef: field.ref,
473
+ completed: !!answers[field.ref],
474
+ current: index === currentFieldIndex
475
+ }));
476
+ return {
477
+ totalFields,
478
+ answeredCount,
479
+ currentFieldIndex,
480
+ percentage,
481
+ segments
482
+ };
483
+ }, [content, answers, currentIndex]);
484
+ }
485
+
486
+ export { ThemeContext, hasCookieConsent, setCookieConsent, useField, useForm, useProgress, useTheme, useThemeContext };
487
+ //# sourceMappingURL=chunk-HUIMGLDC.js.map
488
+ //# sourceMappingURL=chunk-HUIMGLDC.js.map