@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,1657 @@
1
+ import { useForm, ThemeContext, useThemeContext, useField, useProgress, hasCookieConsent, useTheme, setCookieConsent } from './chunk-HUIMGLDC.js';
2
+ import React2, { createContext, useContext, useMemo, useState, useEffect, useRef, useCallback } from 'react';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+
5
+ var FormContext = createContext(null);
6
+ function useFormContext() {
7
+ const ctx = useContext(FormContext);
8
+ if (!ctx) {
9
+ throw new Error("useFormContext must be used within a FormProvider");
10
+ }
11
+ return ctx;
12
+ }
13
+ function FormProvider({ children, ...options }) {
14
+ const form = useForm(options);
15
+ const contextValue = useMemo(
16
+ () => ({
17
+ ...form,
18
+ content: options.content,
19
+ settings: options.settings
20
+ }),
21
+ [form, options.content, options.settings]
22
+ );
23
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: options.theme, children: /* @__PURE__ */ jsx(FormContext.Provider, { value: contextValue, children }) });
24
+ }
25
+ function WelcomeScreen({ screen, onStart }) {
26
+ const { title, properties, attachment } = screen;
27
+ return /* @__PURE__ */ jsxs("div", { className: "lf-screen", children: [
28
+ attachment && attachment.type === "image" && /* @__PURE__ */ jsx(
29
+ "img",
30
+ {
31
+ src: attachment.href,
32
+ alt: attachment.properties?.description || "",
33
+ style: { maxWidth: "100%", maxHeight: "300px", marginBottom: "2rem" }
34
+ }
35
+ ),
36
+ /* @__PURE__ */ jsx("h1", { className: "lf-screen-title", children: title }),
37
+ properties.description && /* @__PURE__ */ jsx("p", { className: "lf-screen-description", children: properties.description }),
38
+ properties.showButton && /* @__PURE__ */ jsx("button", { type: "button", className: "lf-button", onClick: onStart, children: properties.buttonText || "Start" })
39
+ ] });
40
+ }
41
+ function ThankYouScreen({ screen }) {
42
+ const { title, properties, attachment } = screen;
43
+ const handleButtonClick = () => {
44
+ switch (properties.buttonMode) {
45
+ case "reload":
46
+ window.location.reload();
47
+ break;
48
+ case "redirect":
49
+ if (properties.redirectUrl) {
50
+ window.location.href = properties.redirectUrl;
51
+ }
52
+ break;
53
+ }
54
+ };
55
+ return /* @__PURE__ */ jsxs("div", { className: "lf-screen", children: [
56
+ attachment && attachment.type === "image" && /* @__PURE__ */ jsx(
57
+ "img",
58
+ {
59
+ src: attachment.href,
60
+ alt: attachment.properties?.description || "",
61
+ style: { maxWidth: "100%", maxHeight: "300px", marginBottom: "2rem" }
62
+ }
63
+ ),
64
+ /* @__PURE__ */ jsx("h1", { className: "lf-screen-title", children: title }),
65
+ properties.description && /* @__PURE__ */ jsx("p", { className: "lf-screen-description", children: properties.description }),
66
+ properties.showButton && /* @__PURE__ */ jsx("button", { type: "button", className: "lf-button", onClick: handleButtonClick, children: properties.buttonText || "Done" })
67
+ ] });
68
+ }
69
+ function QuestionHeader({
70
+ field,
71
+ questionNumber,
72
+ showQuestionNumber
73
+ }) {
74
+ return /* @__PURE__ */ jsxs("div", { style: { marginBottom: "var(--lf-spacing-question)" }, children: [
75
+ showQuestionNumber && questionNumber !== void 0 && /* @__PURE__ */ jsx(
76
+ "span",
77
+ {
78
+ className: "lf-question-description",
79
+ style: { display: "block", marginBottom: "0.25rem", fontSize: "0.875rem" },
80
+ children: questionNumber
81
+ }
82
+ ),
83
+ /* @__PURE__ */ jsxs("h2", { className: "lf-question-title", children: [
84
+ field.title,
85
+ field.validations?.required && /* @__PURE__ */ jsx("span", { className: "lf-required", style: { marginLeft: "0.25rem" }, children: "*" })
86
+ ] }),
87
+ field.description && /* @__PURE__ */ jsx(
88
+ "p",
89
+ {
90
+ className: "lf-question-description",
91
+ style: { marginTop: "0.5rem", fontSize: "1rem" },
92
+ children: field.description
93
+ }
94
+ )
95
+ ] });
96
+ }
97
+ function MultipleChoice({
98
+ field,
99
+ value,
100
+ error,
101
+ onChange,
102
+ onNext,
103
+ showQuestionNumber,
104
+ questionNumber,
105
+ showKeyHints,
106
+ systemMessages
107
+ }) {
108
+ const theme = useThemeContext();
109
+ const selectedValues = value || [];
110
+ const { choices, allowMultipleSelection } = field.properties;
111
+ const isCardStyle = theme.componentVariants?.choiceStyle === "card";
112
+ const choiceLayout = theme.componentVariants?.choiceLayout || "single-column";
113
+ const choiceIndicator = theme.componentVariants?.choiceIndicator || "checkmark";
114
+ const choiceContainerMaxWidth = theme.componentVariants?.choiceContainerMaxWidth;
115
+ const handleChoiceClick = (choiceRef) => {
116
+ if (allowMultipleSelection) {
117
+ const newValues = selectedValues.includes(choiceRef) ? selectedValues.filter((v) => v !== choiceRef) : [...selectedValues, choiceRef];
118
+ onChange(newValues);
119
+ } else {
120
+ const newValue = [choiceRef];
121
+ onChange(newValue);
122
+ onNext(newValue);
123
+ }
124
+ };
125
+ React2.useEffect(() => {
126
+ const handleKeyDown = (e) => {
127
+ const key = e.key.toUpperCase();
128
+ const index = key.charCodeAt(0) - 65;
129
+ if (index >= 0 && index < choices.length) {
130
+ handleChoiceClick(choices[index].ref);
131
+ }
132
+ };
133
+ if (showKeyHints) {
134
+ window.addEventListener("keydown", handleKeyDown);
135
+ return () => window.removeEventListener("keydown", handleKeyDown);
136
+ }
137
+ }, [choices, showKeyHints, selectedValues]);
138
+ const containerStyle = choiceLayout === "single-column" ? {
139
+ display: "flex",
140
+ flexDirection: "column",
141
+ gap: "var(--lf-spacing-option)"
142
+ } : {
143
+ display: "grid",
144
+ gridTemplateColumns: choiceLayout === "two-column" ? "repeat(2, 1fr)" : "repeat(auto-fit, minmax(160px, 1fr))",
145
+ gap: "var(--lf-spacing-option)",
146
+ maxWidth: choiceContainerMaxWidth || void 0,
147
+ margin: choiceContainerMaxWidth ? "0 auto" : void 0
148
+ };
149
+ const renderIndicator = (isSelected) => {
150
+ if (choiceIndicator === "none") {
151
+ return null;
152
+ }
153
+ if (choiceIndicator === "arrow") {
154
+ return /* @__PURE__ */ jsx("span", { className: "lf-choice-indicator", children: /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", children: [
155
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "9", fill: isSelected ? "#111113" : "white" }),
156
+ /* @__PURE__ */ jsx(
157
+ "path",
158
+ {
159
+ d: "M8 5.5L11.5 9L8 12.5",
160
+ stroke: isSelected ? "white" : "black",
161
+ strokeWidth: "2.5",
162
+ strokeLinecap: "round",
163
+ strokeLinejoin: "round"
164
+ }
165
+ )
166
+ ] }) });
167
+ }
168
+ if (choiceIndicator === "radio") {
169
+ return /* @__PURE__ */ jsx("span", { className: "lf-choice-indicator", children: /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", children: [
170
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "8", stroke: isSelected ? "var(--lf-color-primary)" : "#E5E7EB", strokeWidth: "2", fill: "none" }),
171
+ isSelected && /* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "5", fill: "var(--lf-color-primary)" })
172
+ ] }) });
173
+ }
174
+ return /* @__PURE__ */ jsx("span", { className: "lf-choice-indicator", children: isSelected && /* @__PURE__ */ jsx(
175
+ "svg",
176
+ {
177
+ width: "14",
178
+ height: "14",
179
+ viewBox: "0 0 24 24",
180
+ fill: "none",
181
+ stroke: "currentColor",
182
+ strokeWidth: "3",
183
+ strokeLinecap: "round",
184
+ strokeLinejoin: "round",
185
+ children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
186
+ }
187
+ ) });
188
+ };
189
+ return /* @__PURE__ */ jsxs("div", { className: "lf-field", children: [
190
+ /* @__PURE__ */ jsx(
191
+ QuestionHeader,
192
+ {
193
+ field,
194
+ questionNumber,
195
+ showQuestionNumber
196
+ }
197
+ ),
198
+ allowMultipleSelection && /* @__PURE__ */ jsx("p", { className: "lf-field-hint", style: { marginBottom: "var(--lf-spacing-option)", opacity: 0.7 }, children: systemMessages?.multipleSelectionHint || "Choose as many as you like" }),
199
+ /* @__PURE__ */ jsx("div", { style: containerStyle, children: choices.map((choice, index) => {
200
+ const isSelected = selectedValues.includes(choice.ref);
201
+ if (isCardStyle) {
202
+ return /* @__PURE__ */ jsxs(
203
+ "button",
204
+ {
205
+ type: "button",
206
+ onClick: () => handleChoiceClick(choice.ref),
207
+ className: `lf-choice-card ${isSelected ? "lf-choice-card-selected" : ""}`,
208
+ children: [
209
+ /* @__PURE__ */ jsx("span", { style: { flex: 1 }, children: choice.label }),
210
+ renderIndicator(isSelected)
211
+ ]
212
+ },
213
+ choice.ref
214
+ );
215
+ }
216
+ return /* @__PURE__ */ jsxs(
217
+ "button",
218
+ {
219
+ type: "button",
220
+ onClick: () => handleChoiceClick(choice.ref),
221
+ className: `lf-choice ${isSelected ? "lf-choice-selected" : ""}`,
222
+ children: [
223
+ showKeyHints && /* @__PURE__ */ jsx("span", { className: "lf-key-hint", children: String.fromCharCode(65 + index) }),
224
+ /* @__PURE__ */ jsx("span", { style: { flex: 1 }, children: choice.label }),
225
+ isSelected && /* @__PURE__ */ jsx(
226
+ "svg",
227
+ {
228
+ width: "20",
229
+ height: "20",
230
+ viewBox: "0 0 24 24",
231
+ fill: "none",
232
+ stroke: "currentColor",
233
+ strokeWidth: "3",
234
+ strokeLinecap: "round",
235
+ strokeLinejoin: "round",
236
+ style: { color: "var(--lf-color-primary)" },
237
+ children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
238
+ }
239
+ )
240
+ ]
241
+ },
242
+ choice.ref
243
+ );
244
+ }) }),
245
+ error && /* @__PURE__ */ jsx("p", { id: `${field.ref}-error`, className: "lf-error", children: error })
246
+ ] });
247
+ }
248
+ function NumberField({
249
+ field,
250
+ value,
251
+ error,
252
+ onChange,
253
+ onNext,
254
+ showQuestionNumber,
255
+ questionNumber
256
+ }) {
257
+ const { onKeyDown } = useField({
258
+ field,
259
+ value,
260
+ error,
261
+ onChange,
262
+ onNext
263
+ });
264
+ const handleChange = (e) => {
265
+ const val = e.target.value;
266
+ if (val === "") {
267
+ onChange("");
268
+ } else {
269
+ const num = parseFloat(val);
270
+ if (!isNaN(num)) {
271
+ onChange(num);
272
+ }
273
+ }
274
+ };
275
+ return /* @__PURE__ */ jsxs("div", { className: "lf-field", children: [
276
+ /* @__PURE__ */ jsx(
277
+ QuestionHeader,
278
+ {
279
+ field,
280
+ questionNumber,
281
+ showQuestionNumber
282
+ }
283
+ ),
284
+ /* @__PURE__ */ jsx(
285
+ "input",
286
+ {
287
+ type: "number",
288
+ value: value !== void 0 ? String(value) : "",
289
+ onChange: handleChange,
290
+ onKeyDown,
291
+ placeholder: field.properties?.placeholder,
292
+ min: field.validations?.minValue,
293
+ max: field.validations?.maxValue,
294
+ className: "lf-input-underline",
295
+ "aria-invalid": !!error,
296
+ "aria-describedby": error ? `${field.ref}-error` : void 0
297
+ }
298
+ ),
299
+ error && /* @__PURE__ */ jsx("p", { id: `${field.ref}-error`, className: "lf-error", children: error })
300
+ ] });
301
+ }
302
+ function OpinionScale({
303
+ field,
304
+ value,
305
+ error,
306
+ onChange,
307
+ onNext,
308
+ showQuestionNumber,
309
+ questionNumber
310
+ }) {
311
+ const { steps, startAtOne, labels } = field.properties;
312
+ const currentValue = value;
313
+ const startValue = startAtOne ? 1 : 0;
314
+ const handleClick = (rating) => {
315
+ onChange(rating);
316
+ onNext(rating);
317
+ };
318
+ return /* @__PURE__ */ jsxs("div", { className: "lf-field", children: [
319
+ /* @__PURE__ */ jsx(
320
+ QuestionHeader,
321
+ {
322
+ field,
323
+ questionNumber,
324
+ showQuestionNumber
325
+ }
326
+ ),
327
+ /* @__PURE__ */ jsxs("div", { children: [
328
+ /* @__PURE__ */ jsx(
329
+ "div",
330
+ {
331
+ style: {
332
+ display: "flex",
333
+ gap: "0.5rem",
334
+ flexWrap: "wrap"
335
+ },
336
+ children: Array.from({ length: steps }, (_, i) => startValue + i).map((num) => /* @__PURE__ */ jsx(
337
+ "button",
338
+ {
339
+ type: "button",
340
+ onClick: () => handleClick(num),
341
+ className: `lf-scale-button ${currentValue === num ? "lf-scale-button-selected" : ""}`,
342
+ children: num
343
+ },
344
+ num
345
+ ))
346
+ }
347
+ ),
348
+ (labels.left || labels.right) && /* @__PURE__ */ jsxs(
349
+ "div",
350
+ {
351
+ style: {
352
+ display: "flex",
353
+ justifyContent: "space-between",
354
+ marginTop: "0.75rem",
355
+ fontSize: "0.875rem",
356
+ color: "var(--lf-color-placeholder)"
357
+ },
358
+ children: [
359
+ /* @__PURE__ */ jsx("span", { children: labels.left }),
360
+ labels.center && /* @__PURE__ */ jsx("span", { children: labels.center }),
361
+ /* @__PURE__ */ jsx("span", { children: labels.right })
362
+ ]
363
+ }
364
+ )
365
+ ] }),
366
+ error && /* @__PURE__ */ jsx("p", { id: `${field.ref}-error`, className: "lf-error", children: error })
367
+ ] });
368
+ }
369
+ var SHAPES = {
370
+ star: (filled) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: filled ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("polygon", { points: "12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" }) }),
371
+ heart: (filled) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: filled ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" }) }),
372
+ thumbsup: (filled) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: filled ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3" }) }),
373
+ trophy: (filled) => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: filled ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "2", children: [
374
+ /* @__PURE__ */ jsx("path", { d: "M6 9H4.5a2.5 2.5 0 0 1 0-5H6" }),
375
+ /* @__PURE__ */ jsx("path", { d: "M18 9h1.5a2.5 2.5 0 0 0 0-5H18" }),
376
+ /* @__PURE__ */ jsx("path", { d: "M4 22h16" }),
377
+ /* @__PURE__ */ jsx("path", { d: "M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22" }),
378
+ /* @__PURE__ */ jsx("path", { d: "M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22" }),
379
+ /* @__PURE__ */ jsx("path", { d: "M18 2H6v7a6 6 0 0 0 12 0V2Z" })
380
+ ] }),
381
+ crown: (filled) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: filled ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "m2 4 3 12h14l3-12-6 7-4-7-4 7-6-7zm3 16h14" }) })
382
+ };
383
+ function Rating({
384
+ field,
385
+ value,
386
+ error,
387
+ onChange,
388
+ onNext,
389
+ showQuestionNumber,
390
+ questionNumber
391
+ }) {
392
+ const [hoverValue, setHoverValue] = useState(null);
393
+ const { steps, shape } = field.properties;
394
+ const currentValue = value || 0;
395
+ const displayValue = hoverValue !== null ? hoverValue : currentValue;
396
+ const handleClick = (rating) => {
397
+ onChange(rating);
398
+ onNext(rating);
399
+ };
400
+ const ShapeComponent = SHAPES[shape] || SHAPES.star;
401
+ return /* @__PURE__ */ jsxs("div", { className: "lf-field", children: [
402
+ /* @__PURE__ */ jsx(
403
+ QuestionHeader,
404
+ {
405
+ field,
406
+ questionNumber,
407
+ showQuestionNumber
408
+ }
409
+ ),
410
+ /* @__PURE__ */ jsx(
411
+ "div",
412
+ {
413
+ style: {
414
+ display: "flex",
415
+ gap: "0.5rem"
416
+ },
417
+ onMouseLeave: () => setHoverValue(null),
418
+ children: Array.from({ length: steps }, (_, i) => i + 1).map((rating) => /* @__PURE__ */ jsx(
419
+ "button",
420
+ {
421
+ type: "button",
422
+ onClick: () => handleClick(rating),
423
+ onMouseEnter: () => setHoverValue(rating),
424
+ className: `lf-rating-star ${rating <= displayValue ? "lf-rating-star-active" : ""}`,
425
+ style: {
426
+ background: "none",
427
+ border: "none",
428
+ cursor: "pointer",
429
+ padding: "0.25rem",
430
+ width: "2.5rem",
431
+ height: "2.5rem"
432
+ },
433
+ "aria-label": `Rate ${rating} out of ${steps}`,
434
+ children: ShapeComponent(rating <= displayValue)
435
+ },
436
+ rating
437
+ ))
438
+ }
439
+ ),
440
+ error && /* @__PURE__ */ jsx("p", { id: `${field.ref}-error`, className: "lf-error", children: error })
441
+ ] });
442
+ }
443
+ function Statement({
444
+ field,
445
+ onNext,
446
+ showQuestionNumber,
447
+ questionNumber
448
+ }) {
449
+ return /* @__PURE__ */ jsxs("div", { className: "lf-field", children: [
450
+ /* @__PURE__ */ jsx(
451
+ QuestionHeader,
452
+ {
453
+ field,
454
+ questionNumber,
455
+ showQuestionNumber
456
+ }
457
+ ),
458
+ /* @__PURE__ */ jsx("button", { type: "button", className: "lf-button", onClick: onNext, children: field.properties.buttonText || "Continue" })
459
+ ] });
460
+ }
461
+ function TextField({
462
+ field,
463
+ value,
464
+ error,
465
+ onChange,
466
+ onNext,
467
+ showQuestionNumber,
468
+ questionNumber
469
+ }) {
470
+ const { getInputProps, getTextareaProps } = useField({
471
+ field,
472
+ value,
473
+ error,
474
+ onChange,
475
+ onNext
476
+ });
477
+ const isLongText = field.type === "long_text";
478
+ const inputType = field.type === "email" ? "email" : field.type === "website" ? "url" : "text";
479
+ return /* @__PURE__ */ jsxs("div", { className: "lf-field", children: [
480
+ /* @__PURE__ */ jsx(
481
+ QuestionHeader,
482
+ {
483
+ field,
484
+ questionNumber,
485
+ showQuestionNumber
486
+ }
487
+ ),
488
+ isLongText ? /* @__PURE__ */ jsx(
489
+ "textarea",
490
+ {
491
+ ...getTextareaProps(),
492
+ className: "lf-input",
493
+ rows: 4,
494
+ style: { resize: "vertical", minHeight: "120px" }
495
+ }
496
+ ) : /* @__PURE__ */ jsx("input", { ...getInputProps(), type: inputType, className: "lf-input-underline" }),
497
+ error && /* @__PURE__ */ jsx("p", { id: `${field.ref}-error`, className: "lf-error", children: error })
498
+ ] });
499
+ }
500
+ function YesNo({
501
+ field,
502
+ value,
503
+ error,
504
+ onChange,
505
+ onNext,
506
+ showQuestionNumber,
507
+ questionNumber,
508
+ showKeyHints
509
+ }) {
510
+ const currentValue = value;
511
+ const handleClick = (answer) => {
512
+ onChange(answer);
513
+ onNext(answer);
514
+ };
515
+ React2.useEffect(() => {
516
+ const handleKeyDown = (e) => {
517
+ if (e.key.toLowerCase() === "y") {
518
+ handleClick(true);
519
+ } else if (e.key.toLowerCase() === "n") {
520
+ handleClick(false);
521
+ }
522
+ };
523
+ if (showKeyHints) {
524
+ window.addEventListener("keydown", handleKeyDown);
525
+ return () => window.removeEventListener("keydown", handleKeyDown);
526
+ }
527
+ }, [showKeyHints]);
528
+ return /* @__PURE__ */ jsxs("div", { className: "lf-field", children: [
529
+ /* @__PURE__ */ jsx(
530
+ QuestionHeader,
531
+ {
532
+ field,
533
+ questionNumber,
534
+ showQuestionNumber
535
+ }
536
+ ),
537
+ /* @__PURE__ */ jsxs(
538
+ "div",
539
+ {
540
+ style: {
541
+ display: "flex",
542
+ gap: "var(--lf-spacing-option)"
543
+ },
544
+ children: [
545
+ /* @__PURE__ */ jsxs(
546
+ "button",
547
+ {
548
+ type: "button",
549
+ onClick: () => handleClick(true),
550
+ className: `lf-choice ${currentValue === true ? "lf-choice-selected" : ""}`,
551
+ style: { flex: 1 },
552
+ children: [
553
+ showKeyHints && /* @__PURE__ */ jsx("span", { className: "lf-key-hint", children: "Y" }),
554
+ /* @__PURE__ */ jsx("span", { children: "Yes" }),
555
+ currentValue === true && /* @__PURE__ */ jsx(
556
+ "svg",
557
+ {
558
+ width: "20",
559
+ height: "20",
560
+ viewBox: "0 0 24 24",
561
+ fill: "none",
562
+ stroke: "currentColor",
563
+ strokeWidth: "3",
564
+ style: { color: "var(--lf-color-primary)", marginLeft: "auto" },
565
+ children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
566
+ }
567
+ )
568
+ ]
569
+ }
570
+ ),
571
+ /* @__PURE__ */ jsxs(
572
+ "button",
573
+ {
574
+ type: "button",
575
+ onClick: () => handleClick(false),
576
+ className: `lf-choice ${currentValue === false ? "lf-choice-selected" : ""}`,
577
+ style: { flex: 1 },
578
+ children: [
579
+ showKeyHints && /* @__PURE__ */ jsx("span", { className: "lf-key-hint", children: "N" }),
580
+ /* @__PURE__ */ jsx("span", { children: "No" }),
581
+ currentValue === false && /* @__PURE__ */ jsx(
582
+ "svg",
583
+ {
584
+ width: "20",
585
+ height: "20",
586
+ viewBox: "0 0 24 24",
587
+ fill: "none",
588
+ stroke: "currentColor",
589
+ strokeWidth: "3",
590
+ style: { color: "var(--lf-color-primary)", marginLeft: "auto" },
591
+ children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
592
+ }
593
+ )
594
+ ]
595
+ }
596
+ )
597
+ ]
598
+ }
599
+ ),
600
+ error && /* @__PURE__ */ jsx("p", { id: `${field.ref}-error`, className: "lf-error", children: error })
601
+ ] });
602
+ }
603
+ function FieldRenderer({
604
+ field,
605
+ value,
606
+ error,
607
+ onChange,
608
+ onNext,
609
+ showQuestionNumber,
610
+ questionNumber,
611
+ showKeyHints,
612
+ systemMessages
613
+ }) {
614
+ const commonProps = {
615
+ value,
616
+ error,
617
+ onChange,
618
+ onNext,
619
+ showQuestionNumber,
620
+ questionNumber
621
+ };
622
+ switch (field.type) {
623
+ case "short_text":
624
+ case "long_text":
625
+ case "email":
626
+ case "website":
627
+ return /* @__PURE__ */ jsx(TextField, { field, ...commonProps });
628
+ case "number":
629
+ return /* @__PURE__ */ jsx(NumberField, { field, ...commonProps });
630
+ case "multiple_choice":
631
+ return /* @__PURE__ */ jsx(
632
+ MultipleChoice,
633
+ {
634
+ field,
635
+ ...commonProps,
636
+ showKeyHints,
637
+ systemMessages
638
+ }
639
+ );
640
+ case "rating":
641
+ return /* @__PURE__ */ jsx(Rating, { field, ...commonProps });
642
+ case "opinion_scale":
643
+ return /* @__PURE__ */ jsx(OpinionScale, { field, ...commonProps });
644
+ case "yes_no":
645
+ return /* @__PURE__ */ jsx(YesNo, { field, ...commonProps, showKeyHints });
646
+ case "statement":
647
+ return /* @__PURE__ */ jsx(
648
+ Statement,
649
+ {
650
+ field,
651
+ onNext,
652
+ showQuestionNumber,
653
+ questionNumber
654
+ }
655
+ );
656
+ // For field types not yet implemented, show a basic input
657
+ case "phone_number":
658
+ case "date":
659
+ case "dropdown":
660
+ case "picture_choice":
661
+ case "ranking":
662
+ case "nps":
663
+ case "legal":
664
+ case "file_upload":
665
+ case "contact_info":
666
+ case "payment":
667
+ case "group":
668
+ case "matrix":
669
+ case "address":
670
+ case "slider":
671
+ case "signature":
672
+ default:
673
+ return /* @__PURE__ */ jsxs("div", { className: "lf-field", children: [
674
+ /* @__PURE__ */ jsx(
675
+ QuestionHeader,
676
+ {
677
+ field,
678
+ questionNumber,
679
+ showQuestionNumber
680
+ }
681
+ ),
682
+ /* @__PURE__ */ jsx(
683
+ "input",
684
+ {
685
+ type: "text",
686
+ value: value || "",
687
+ onChange: (e) => onChange(e.target.value),
688
+ onKeyDown: (e) => {
689
+ if (e.key === "Enter") {
690
+ e.preventDefault();
691
+ onNext();
692
+ }
693
+ },
694
+ className: "lf-input-underline",
695
+ placeholder: `Enter your ${field.type.replace(/_/g, " ")}`
696
+ }
697
+ ),
698
+ error && /* @__PURE__ */ jsx("p", { id: `${field.ref}-error`, className: "lf-error", children: error })
699
+ ] });
700
+ }
701
+ }
702
+ function NavigationControls({ onNext, onPrevious }) {
703
+ const { canGoBack, canGoNext, isSubmitting, next, previous, settings } = useFormContext();
704
+ const theme = useThemeContext();
705
+ const navVisibility = theme.componentVariants?.navigationVisibility || "always";
706
+ if (navVisibility === "hidden" || navVisibility === "auto-advance") {
707
+ return null;
708
+ }
709
+ const handleNext = () => onNext ? onNext() : next();
710
+ const handlePrevious = () => onPrevious ? onPrevious() : previous();
711
+ const confirmText = settings.systemMessages?.confirmButtonText || "OK";
712
+ const enterHint = settings.systemMessages?.pressEnterText || "press Enter";
713
+ const isBottomBar = theme.componentVariants?.navigationLayout === "bottom-bar";
714
+ if (isBottomBar) {
715
+ return /* @__PURE__ */ jsxs("div", { className: "lf-navigation-bottom-bar", children: [
716
+ /* @__PURE__ */ jsxs(
717
+ "button",
718
+ {
719
+ type: "button",
720
+ className: "lf-nav-btn-back",
721
+ onClick: handlePrevious,
722
+ disabled: !canGoBack,
723
+ "aria-label": "Previous question",
724
+ children: [
725
+ /* @__PURE__ */ jsx(
726
+ "svg",
727
+ {
728
+ width: "16",
729
+ height: "16",
730
+ viewBox: "0 0 24 24",
731
+ fill: "none",
732
+ stroke: "currentColor",
733
+ strokeWidth: "2",
734
+ strokeLinecap: "round",
735
+ strokeLinejoin: "round",
736
+ children: /* @__PURE__ */ jsx("path", { d: "M19 12H5M12 19l-7-7 7-7" })
737
+ }
738
+ ),
739
+ "Back"
740
+ ]
741
+ }
742
+ ),
743
+ /* @__PURE__ */ jsxs(
744
+ "button",
745
+ {
746
+ type: "button",
747
+ className: "lf-nav-btn-next",
748
+ onClick: handleNext,
749
+ disabled: isSubmitting,
750
+ "aria-label": "Next question",
751
+ children: [
752
+ "Next",
753
+ /* @__PURE__ */ jsx(
754
+ "svg",
755
+ {
756
+ width: "16",
757
+ height: "16",
758
+ viewBox: "0 0 24 24",
759
+ fill: "none",
760
+ stroke: "currentColor",
761
+ strokeWidth: "2",
762
+ strokeLinecap: "round",
763
+ strokeLinejoin: "round",
764
+ children: /* @__PURE__ */ jsx("path", { d: "M5 12h14M12 5l7 7-7 7" })
765
+ }
766
+ )
767
+ ]
768
+ }
769
+ )
770
+ ] });
771
+ }
772
+ return /* @__PURE__ */ jsxs("div", { className: "lf-navigation", children: [
773
+ /* @__PURE__ */ jsx(
774
+ "button",
775
+ {
776
+ type: "button",
777
+ className: "lf-navigation-button",
778
+ onClick: handlePrevious,
779
+ disabled: !canGoBack,
780
+ "aria-label": "Previous question",
781
+ children: /* @__PURE__ */ jsx(
782
+ "svg",
783
+ {
784
+ width: "16",
785
+ height: "16",
786
+ viewBox: "0 0 24 24",
787
+ fill: "none",
788
+ stroke: "currentColor",
789
+ strokeWidth: "2",
790
+ strokeLinecap: "round",
791
+ strokeLinejoin: "round",
792
+ children: /* @__PURE__ */ jsx("path", { d: "M15 18l-6-6 6-6" })
793
+ }
794
+ )
795
+ }
796
+ ),
797
+ /* @__PURE__ */ jsxs(
798
+ "button",
799
+ {
800
+ type: "button",
801
+ className: "lf-button",
802
+ onClick: handleNext,
803
+ disabled: isSubmitting,
804
+ children: [
805
+ confirmText,
806
+ /* @__PURE__ */ jsxs(
807
+ "span",
808
+ {
809
+ style: {
810
+ opacity: 0.7,
811
+ fontSize: "0.75rem",
812
+ marginLeft: "0.5rem"
813
+ },
814
+ children: [
815
+ enterHint,
816
+ " \u21B5"
817
+ ]
818
+ }
819
+ )
820
+ ]
821
+ }
822
+ ),
823
+ /* @__PURE__ */ jsx(
824
+ "button",
825
+ {
826
+ type: "button",
827
+ className: "lf-navigation-button",
828
+ onClick: handleNext,
829
+ disabled: !canGoNext || isSubmitting,
830
+ "aria-label": "Next question",
831
+ children: /* @__PURE__ */ jsx(
832
+ "svg",
833
+ {
834
+ width: "16",
835
+ height: "16",
836
+ viewBox: "0 0 24 24",
837
+ fill: "none",
838
+ stroke: "currentColor",
839
+ strokeWidth: "2",
840
+ strokeLinecap: "round",
841
+ strokeLinejoin: "round",
842
+ children: /* @__PURE__ */ jsx("path", { d: "M9 18l6-6-6-6" })
843
+ }
844
+ )
845
+ }
846
+ )
847
+ ] });
848
+ }
849
+ function ProgressBar() {
850
+ const { content, answers, currentIndex, settings } = useFormContext();
851
+ const theme = useThemeContext();
852
+ const { percentage, segments } = useProgress({ content, answers, currentIndex });
853
+ if (!settings.showProgressBar || !theme.progressBar.enabled) {
854
+ return null;
855
+ }
856
+ const position = theme.progressBar.position === "bottom" ? "bottom" : "top";
857
+ const positionStyle = {
858
+ position: "fixed",
859
+ left: 0,
860
+ right: 0,
861
+ [position]: 0,
862
+ zIndex: 100
863
+ };
864
+ if (theme.progressBar.style === "segmented") {
865
+ return /* @__PURE__ */ jsx(
866
+ "div",
867
+ {
868
+ style: {
869
+ ...positionStyle,
870
+ display: "flex",
871
+ gap: "4px",
872
+ padding: "8px 16px"
873
+ },
874
+ children: segments.map((segment) => /* @__PURE__ */ jsx(
875
+ "div",
876
+ {
877
+ style: {
878
+ flex: 1,
879
+ height: "4px",
880
+ backgroundColor: segment.completed ? "var(--lf-color-progress-fill)" : "var(--lf-color-progress-bg)",
881
+ transition: "background-color var(--lf-transition-duration) var(--lf-transition-easing)"
882
+ }
883
+ },
884
+ segment.index
885
+ ))
886
+ }
887
+ );
888
+ }
889
+ if (theme.progressBar.style === "percentage") {
890
+ return /* @__PURE__ */ jsxs(
891
+ "div",
892
+ {
893
+ style: {
894
+ ...positionStyle,
895
+ padding: "8px 16px",
896
+ textAlign: "center",
897
+ fontSize: "0.875rem",
898
+ fontWeight: 500,
899
+ color: "var(--lf-color-question)"
900
+ },
901
+ children: [
902
+ Math.round(percentage),
903
+ "%"
904
+ ]
905
+ }
906
+ );
907
+ }
908
+ if (theme.progressBar.style === "dots") {
909
+ return /* @__PURE__ */ jsx(
910
+ "div",
911
+ {
912
+ style: {
913
+ ...positionStyle,
914
+ display: "flex",
915
+ justifyContent: "center",
916
+ gap: "8px",
917
+ padding: "8px 16px"
918
+ },
919
+ children: segments.map((segment) => /* @__PURE__ */ jsx(
920
+ "div",
921
+ {
922
+ style: {
923
+ width: segment.current ? "12px" : "8px",
924
+ height: segment.current ? "12px" : "8px",
925
+ borderRadius: "50%",
926
+ backgroundColor: segment.completed || segment.current ? "var(--lf-color-progress-fill)" : "var(--lf-color-progress-bg)",
927
+ transition: "all var(--lf-transition-duration) var(--lf-transition-easing)"
928
+ }
929
+ },
930
+ segment.index
931
+ ))
932
+ }
933
+ );
934
+ }
935
+ return /* @__PURE__ */ jsx("div", { style: { ...positionStyle, padding: "8px 16px" }, children: /* @__PURE__ */ jsx(
936
+ "div",
937
+ {
938
+ className: "lf-progress",
939
+ style: { height: "4px", borderRadius: "2px" },
940
+ children: /* @__PURE__ */ jsx(
941
+ "div",
942
+ {
943
+ className: "lf-progress-fill",
944
+ style: {
945
+ width: `${percentage}%`,
946
+ height: "100%",
947
+ borderRadius: "2px"
948
+ }
949
+ }
950
+ )
951
+ }
952
+ ) });
953
+ }
954
+
955
+ // src/utils/timeEstimate.ts
956
+ var FIELD_TIME_ESTIMATES = {
957
+ // Text fields
958
+ short_text: 15,
959
+ long_text: 45,
960
+ email: 12,
961
+ phone_number: 15,
962
+ website: 12,
963
+ // Selection fields
964
+ multiple_choice: 10,
965
+ picture_choice: 12,
966
+ dropdown: 12,
967
+ yes_no: 5,
968
+ ranking: 20,
969
+ // Scale/rating fields
970
+ rating: 8,
971
+ opinion_scale: 8,
972
+ nps: 8,
973
+ // Complex fields
974
+ address: 60,
975
+ contact_info: 45,
976
+ date: 10,
977
+ number: 10,
978
+ slider: 8,
979
+ signature: 30,
980
+ file_upload: 20,
981
+ payment: 60,
982
+ // Display fields (minimal time)
983
+ statement: 5,
984
+ legal: 15,
985
+ group: 0,
986
+ matrix: 30
987
+ };
988
+ function formatTimeEstimate(seconds) {
989
+ if (seconds < 60) {
990
+ return `About ${Math.round(seconds / 10) * 10} sec`;
991
+ }
992
+ const minutes = Math.round(seconds / 60);
993
+ return `About ${minutes} min`;
994
+ }
995
+ function calculateRemainingTime(content, answeredFieldRefs) {
996
+ let totalSeconds = 0;
997
+ for (const field of content.fields) {
998
+ if (answeredFieldRefs.includes(field.ref)) {
999
+ continue;
1000
+ }
1001
+ const fieldTime = FIELD_TIME_ESTIMATES[field.type] ?? 15;
1002
+ totalSeconds += fieldTime;
1003
+ }
1004
+ return totalSeconds;
1005
+ }
1006
+ function TimeToComplete() {
1007
+ const { content, settings, answers, isStarted, isCompleted } = useFormContext();
1008
+ if (!settings.showTimeToComplete) {
1009
+ return null;
1010
+ }
1011
+ if (isCompleted) {
1012
+ return null;
1013
+ }
1014
+ const answeredFieldRefs = Object.keys(answers);
1015
+ const remainingSeconds = useMemo(
1016
+ () => calculateRemainingTime(content, answeredFieldRefs),
1017
+ [content, answeredFieldRefs]
1018
+ );
1019
+ if (remainingSeconds <= 0) {
1020
+ return null;
1021
+ }
1022
+ const timeText = formatTimeEstimate(remainingSeconds);
1023
+ return /* @__PURE__ */ jsxs(
1024
+ "div",
1025
+ {
1026
+ className: "lf-time-estimate",
1027
+ style: {
1028
+ position: "fixed",
1029
+ top: "1rem",
1030
+ right: "1rem",
1031
+ zIndex: 50,
1032
+ display: "flex",
1033
+ alignItems: "center",
1034
+ gap: "0.5rem",
1035
+ padding: "0.5rem 0.75rem",
1036
+ fontSize: "0.75rem",
1037
+ color: "var(--lf-color-question)",
1038
+ opacity: 0.7
1039
+ },
1040
+ children: [
1041
+ /* @__PURE__ */ jsxs(
1042
+ "svg",
1043
+ {
1044
+ width: "14",
1045
+ height: "14",
1046
+ viewBox: "0 0 24 24",
1047
+ fill: "none",
1048
+ stroke: "currentColor",
1049
+ strokeWidth: "2",
1050
+ strokeLinecap: "round",
1051
+ strokeLinejoin: "round",
1052
+ children: [
1053
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
1054
+ /* @__PURE__ */ jsx("polyline", { points: "12 6 12 12 16 14" })
1055
+ ]
1056
+ }
1057
+ ),
1058
+ /* @__PURE__ */ jsx("span", { children: timeText })
1059
+ ]
1060
+ }
1061
+ );
1062
+ }
1063
+ function CookieConsent({ onAccept }) {
1064
+ const [isVisible, setIsVisible] = useState(false);
1065
+ useEffect(() => {
1066
+ if (!hasCookieConsent()) {
1067
+ setIsVisible(true);
1068
+ }
1069
+ }, []);
1070
+ const handleAccept = () => {
1071
+ setCookieConsent(true);
1072
+ setIsVisible(false);
1073
+ onAccept();
1074
+ };
1075
+ if (!isVisible) {
1076
+ return null;
1077
+ }
1078
+ return /* @__PURE__ */ jsx(
1079
+ "div",
1080
+ {
1081
+ className: "lf-cookie-consent",
1082
+ style: {
1083
+ position: "fixed",
1084
+ inset: 0,
1085
+ zIndex: 1e3,
1086
+ display: "flex",
1087
+ alignItems: "center",
1088
+ justifyContent: "center",
1089
+ backgroundColor: "rgba(0, 0, 0, 0.5)"
1090
+ },
1091
+ children: /* @__PURE__ */ jsxs(
1092
+ "div",
1093
+ {
1094
+ style: {
1095
+ maxWidth: "400px",
1096
+ margin: "1rem",
1097
+ padding: "1.5rem",
1098
+ backgroundColor: "var(--lf-color-surface, #fff)",
1099
+ borderRadius: "var(--lf-border-radius, 8px)",
1100
+ boxShadow: "0 4px 20px rgba(0, 0, 0, 0.15)"
1101
+ },
1102
+ children: [
1103
+ /* @__PURE__ */ jsx(
1104
+ "div",
1105
+ {
1106
+ style: {
1107
+ width: "48px",
1108
+ height: "48px",
1109
+ margin: "0 auto 1rem",
1110
+ display: "flex",
1111
+ alignItems: "center",
1112
+ justifyContent: "center",
1113
+ backgroundColor: "color-mix(in srgb, var(--lf-color-primary) 15%, transparent)",
1114
+ borderRadius: "50%"
1115
+ },
1116
+ children: /* @__PURE__ */ jsxs(
1117
+ "svg",
1118
+ {
1119
+ width: "24",
1120
+ height: "24",
1121
+ viewBox: "0 0 24 24",
1122
+ fill: "none",
1123
+ stroke: "var(--lf-color-primary)",
1124
+ strokeWidth: "2",
1125
+ strokeLinecap: "round",
1126
+ strokeLinejoin: "round",
1127
+ children: [
1128
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
1129
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "9", r: "1", fill: "currentColor" }),
1130
+ /* @__PURE__ */ jsx("circle", { cx: "15", cy: "9", r: "1", fill: "currentColor" }),
1131
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "14", r: "1", fill: "currentColor" }),
1132
+ /* @__PURE__ */ jsx("circle", { cx: "14", cy: "14", r: "1", fill: "currentColor" }),
1133
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1", fill: "currentColor" })
1134
+ ]
1135
+ }
1136
+ )
1137
+ }
1138
+ ),
1139
+ /* @__PURE__ */ jsx(
1140
+ "h2",
1141
+ {
1142
+ className: "lf-question-title",
1143
+ style: {
1144
+ fontSize: "1.25rem",
1145
+ textAlign: "center",
1146
+ marginBottom: "0.75rem"
1147
+ },
1148
+ children: "Cookie consent"
1149
+ }
1150
+ ),
1151
+ /* @__PURE__ */ jsx(
1152
+ "p",
1153
+ {
1154
+ className: "lf-question-description",
1155
+ style: {
1156
+ textAlign: "center",
1157
+ marginBottom: "1.5rem",
1158
+ fontSize: "0.875rem",
1159
+ lineHeight: 1.5
1160
+ },
1161
+ children: "This form uses cookies to save your progress and provide a better experience. By continuing, you agree to the use of cookies."
1162
+ }
1163
+ ),
1164
+ /* @__PURE__ */ jsx(
1165
+ "button",
1166
+ {
1167
+ type: "button",
1168
+ onClick: handleAccept,
1169
+ className: "lf-button",
1170
+ style: {
1171
+ width: "100%",
1172
+ padding: "0.75rem 1.5rem",
1173
+ fontSize: "1rem"
1174
+ },
1175
+ children: "Accept and continue"
1176
+ }
1177
+ )
1178
+ ]
1179
+ }
1180
+ )
1181
+ }
1182
+ );
1183
+ }
1184
+ function Captcha({
1185
+ siteKey,
1186
+ onVerify,
1187
+ onError,
1188
+ onExpired,
1189
+ theme = "auto",
1190
+ size = "normal"
1191
+ }) {
1192
+ const containerRef = useRef(null);
1193
+ const widgetIdRef = useRef(null);
1194
+ const [isLoading, setIsLoading] = useState(true);
1195
+ const [error, setError] = useState(null);
1196
+ const initializeTurnstile = useCallback(() => {
1197
+ if (!containerRef.current || !window.turnstile) return;
1198
+ if (widgetIdRef.current) {
1199
+ try {
1200
+ window.turnstile.remove(widgetIdRef.current);
1201
+ } catch (e) {
1202
+ }
1203
+ }
1204
+ try {
1205
+ widgetIdRef.current = window.turnstile.render(containerRef.current, {
1206
+ sitekey: siteKey,
1207
+ callback: (token) => {
1208
+ setIsLoading(false);
1209
+ onVerify(token);
1210
+ },
1211
+ "error-callback": () => {
1212
+ setIsLoading(false);
1213
+ setError("Verification failed. Please try again.");
1214
+ onError?.();
1215
+ },
1216
+ "expired-callback": () => {
1217
+ setError("Verification expired. Please try again.");
1218
+ onExpired?.();
1219
+ },
1220
+ theme,
1221
+ size
1222
+ });
1223
+ setIsLoading(false);
1224
+ } catch (e) {
1225
+ setIsLoading(false);
1226
+ setError("Failed to initialize CAPTCHA.");
1227
+ onError?.();
1228
+ }
1229
+ }, [siteKey, onVerify, onError, onExpired, theme, size]);
1230
+ useEffect(() => {
1231
+ if (window.turnstile) {
1232
+ initializeTurnstile();
1233
+ return;
1234
+ }
1235
+ const existingScript = document.querySelector(
1236
+ 'script[src*="challenges.cloudflare.com/turnstile"]'
1237
+ );
1238
+ if (existingScript) {
1239
+ existingScript.addEventListener("load", initializeTurnstile);
1240
+ return;
1241
+ }
1242
+ const script = document.createElement("script");
1243
+ script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
1244
+ script.async = true;
1245
+ script.defer = true;
1246
+ script.onload = initializeTurnstile;
1247
+ script.onerror = () => {
1248
+ setIsLoading(false);
1249
+ setError("Failed to load CAPTCHA. Please refresh the page.");
1250
+ onError?.();
1251
+ };
1252
+ document.head.appendChild(script);
1253
+ return () => {
1254
+ if (widgetIdRef.current && window.turnstile) {
1255
+ try {
1256
+ window.turnstile.remove(widgetIdRef.current);
1257
+ } catch (e) {
1258
+ }
1259
+ }
1260
+ };
1261
+ }, [initializeTurnstile, onError]);
1262
+ return /* @__PURE__ */ jsxs(
1263
+ "div",
1264
+ {
1265
+ className: "lf-captcha",
1266
+ style: {
1267
+ display: "flex",
1268
+ flexDirection: "column",
1269
+ alignItems: "center",
1270
+ gap: "0.75rem",
1271
+ padding: "1rem 0"
1272
+ },
1273
+ children: [
1274
+ isLoading && /* @__PURE__ */ jsxs(
1275
+ "div",
1276
+ {
1277
+ style: {
1278
+ display: "flex",
1279
+ alignItems: "center",
1280
+ gap: "0.5rem",
1281
+ color: "var(--lf-color-question)",
1282
+ opacity: 0.7
1283
+ },
1284
+ children: [
1285
+ /* @__PURE__ */ jsxs(
1286
+ "svg",
1287
+ {
1288
+ width: "16",
1289
+ height: "16",
1290
+ viewBox: "0 0 24 24",
1291
+ fill: "none",
1292
+ stroke: "currentColor",
1293
+ strokeWidth: "2",
1294
+ className: "animate-spin",
1295
+ children: [
1296
+ /* @__PURE__ */ jsx(
1297
+ "circle",
1298
+ {
1299
+ cx: "12",
1300
+ cy: "12",
1301
+ r: "10",
1302
+ stroke: "currentColor",
1303
+ strokeWidth: "4",
1304
+ fill: "none",
1305
+ opacity: "0.25"
1306
+ }
1307
+ ),
1308
+ /* @__PURE__ */ jsx("path", { d: "M4 12a8 8 0 0 1 8-8", opacity: "0.75" })
1309
+ ]
1310
+ }
1311
+ ),
1312
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.875rem" }, children: "Loading verification..." })
1313
+ ]
1314
+ }
1315
+ ),
1316
+ /* @__PURE__ */ jsx("div", { ref: containerRef }),
1317
+ error && /* @__PURE__ */ jsx(
1318
+ "p",
1319
+ {
1320
+ style: {
1321
+ color: "var(--lf-color-error, #ef4444)",
1322
+ fontSize: "0.875rem",
1323
+ textAlign: "center"
1324
+ },
1325
+ children: error
1326
+ }
1327
+ )
1328
+ ]
1329
+ }
1330
+ );
1331
+ }
1332
+ function Branding() {
1333
+ const theme = useThemeContext();
1334
+ if (!theme.branding.showPoweredBy) {
1335
+ return null;
1336
+ }
1337
+ return /* @__PURE__ */ jsxs("div", { className: "lf-branding", children: [
1338
+ "Powered by",
1339
+ " ",
1340
+ /* @__PURE__ */ jsx("a", { href: "https://landform.io", target: "_blank", rel: "noopener noreferrer", children: "Landform" })
1341
+ ] });
1342
+ }
1343
+ function Header() {
1344
+ const theme = useThemeContext();
1345
+ const headerLayout = theme.componentVariants?.headerLayout;
1346
+ if (!headerLayout || headerLayout === "none") {
1347
+ return null;
1348
+ }
1349
+ const brandName = theme.branding?.brandName;
1350
+ const logoUrl = theme.branding?.logoUrl;
1351
+ if (headerLayout === "centered-logo") {
1352
+ return /* @__PURE__ */ jsxs("header", { className: "lf-header-centered", children: [
1353
+ logoUrl && /* @__PURE__ */ jsx(
1354
+ "img",
1355
+ {
1356
+ src: logoUrl,
1357
+ alt: brandName || "Logo",
1358
+ className: "lf-header-logo"
1359
+ }
1360
+ ),
1361
+ brandName && !logoUrl && /* @__PURE__ */ jsx("span", { className: "lf-header-brand", style: { fontSize: "1.125rem", fontWeight: 600, color: "var(--lf-color-question)" }, children: brandName })
1362
+ ] });
1363
+ }
1364
+ if (headerLayout === "left-logo") {
1365
+ return /* @__PURE__ */ jsxs("header", { className: "lf-header-left", style: {
1366
+ display: "flex",
1367
+ alignItems: "center",
1368
+ padding: "1rem var(--lf-spacing-container)",
1369
+ gap: "0.75rem"
1370
+ }, children: [
1371
+ logoUrl && /* @__PURE__ */ jsx(
1372
+ "img",
1373
+ {
1374
+ src: logoUrl,
1375
+ alt: brandName || "Logo",
1376
+ className: "lf-header-logo"
1377
+ }
1378
+ ),
1379
+ brandName && /* @__PURE__ */ jsx("span", { style: { fontSize: "1rem", fontWeight: 500, color: "var(--lf-color-question)" }, children: brandName })
1380
+ ] });
1381
+ }
1382
+ return null;
1383
+ }
1384
+ function FormRenderer(props) {
1385
+ const { className, ...formOptions } = props;
1386
+ return /* @__PURE__ */ jsx(FormProvider, { ...formOptions, children: /* @__PURE__ */ jsx(
1387
+ FormRendererInner,
1388
+ {
1389
+ className,
1390
+ theme: formOptions.theme,
1391
+ turnstileSiteKey: formOptions.turnstileSiteKey
1392
+ }
1393
+ ) });
1394
+ }
1395
+ function FormRendererInner({ className, theme, turnstileSiteKey }) {
1396
+ const {
1397
+ currentItem,
1398
+ settings,
1399
+ isStarted,
1400
+ isCompleted,
1401
+ isDuplicateSubmission,
1402
+ showCaptcha,
1403
+ setCaptchaToken,
1404
+ submit,
1405
+ start,
1406
+ next,
1407
+ answers,
1408
+ errors,
1409
+ setAnswer
1410
+ } = useFormContext();
1411
+ const { backgroundStyle } = useTheme({ theme });
1412
+ const [showCookieConsent, setShowCookieConsent] = useState(false);
1413
+ const [hasConsent, setHasConsent] = useState(true);
1414
+ useEffect(() => {
1415
+ if (settings.showCookieConsent && !hasCookieConsent()) {
1416
+ setShowCookieConsent(true);
1417
+ setHasConsent(false);
1418
+ }
1419
+ }, [settings.showCookieConsent]);
1420
+ const handleCookieConsentAccept = () => {
1421
+ setShowCookieConsent(false);
1422
+ setHasConsent(true);
1423
+ };
1424
+ if (isDuplicateSubmission) {
1425
+ return /* @__PURE__ */ jsxs("div", { className: `lf-form ${className || ""}`, style: backgroundStyle, children: [
1426
+ /* @__PURE__ */ jsx("div", { className: "lf-question-container", children: /* @__PURE__ */ jsxs("div", { className: "lf-screen", style: { textAlign: "center" }, children: [
1427
+ /* @__PURE__ */ jsx(
1428
+ "div",
1429
+ {
1430
+ style: {
1431
+ width: "80px",
1432
+ height: "80px",
1433
+ margin: "0 auto 1.5rem",
1434
+ display: "flex",
1435
+ alignItems: "center",
1436
+ justifyContent: "center",
1437
+ backgroundColor: "var(--lf-color-primary)",
1438
+ opacity: 0.1,
1439
+ borderRadius: "50%"
1440
+ },
1441
+ children: /* @__PURE__ */ jsxs(
1442
+ "svg",
1443
+ {
1444
+ width: "40",
1445
+ height: "40",
1446
+ viewBox: "0 0 24 24",
1447
+ fill: "none",
1448
+ stroke: "var(--lf-color-primary)",
1449
+ strokeWidth: "2",
1450
+ strokeLinecap: "round",
1451
+ strokeLinejoin: "round",
1452
+ children: [
1453
+ /* @__PURE__ */ jsx("path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
1454
+ /* @__PURE__ */ jsx("polyline", { points: "22 4 12 14.01 9 11.01" })
1455
+ ]
1456
+ }
1457
+ )
1458
+ }
1459
+ ),
1460
+ /* @__PURE__ */ jsx("h1", { className: "lf-question-title", style: { marginBottom: "0.75rem" }, children: "You've already submitted" }),
1461
+ /* @__PURE__ */ jsx("p", { className: "lf-question-description", children: "You have already submitted a response to this form." })
1462
+ ] }) }),
1463
+ /* @__PURE__ */ jsx(Branding, {})
1464
+ ] });
1465
+ }
1466
+ if (!currentItem) {
1467
+ return /* @__PURE__ */ jsx("div", { className: `lf-form ${className || ""}`, children: /* @__PURE__ */ jsx("div", { className: "lf-screen", children: "Loading..." }) });
1468
+ }
1469
+ return /* @__PURE__ */ jsxs("div", { className: `lf-form ${className || ""}`, style: backgroundStyle, children: [
1470
+ showCookieConsent && /* @__PURE__ */ jsx(CookieConsent, { onAccept: handleCookieConsentAccept }),
1471
+ showCaptcha && turnstileSiteKey && /* @__PURE__ */ jsx(
1472
+ "div",
1473
+ {
1474
+ className: "lf-captcha-modal",
1475
+ style: {
1476
+ position: "fixed",
1477
+ inset: 0,
1478
+ zIndex: 1e3,
1479
+ display: "flex",
1480
+ alignItems: "center",
1481
+ justifyContent: "center",
1482
+ backgroundColor: "rgba(0, 0, 0, 0.5)"
1483
+ },
1484
+ children: /* @__PURE__ */ jsxs(
1485
+ "div",
1486
+ {
1487
+ style: {
1488
+ maxWidth: "400px",
1489
+ margin: "1rem",
1490
+ padding: "1.5rem",
1491
+ backgroundColor: "var(--lf-color-surface, #fff)",
1492
+ borderRadius: "var(--lf-border-radius, 8px)",
1493
+ boxShadow: "0 4px 20px rgba(0, 0, 0, 0.15)",
1494
+ textAlign: "center"
1495
+ },
1496
+ children: [
1497
+ /* @__PURE__ */ jsx(
1498
+ "h2",
1499
+ {
1500
+ className: "lf-question-title",
1501
+ style: { fontSize: "1.25rem", marginBottom: "0.75rem" },
1502
+ children: "Please verify you're human"
1503
+ }
1504
+ ),
1505
+ /* @__PURE__ */ jsx(
1506
+ "p",
1507
+ {
1508
+ className: "lf-question-description",
1509
+ style: { marginBottom: "1rem", fontSize: "0.875rem", opacity: 0.7 },
1510
+ children: "Complete the verification below to submit your response."
1511
+ }
1512
+ ),
1513
+ /* @__PURE__ */ jsx(
1514
+ Captcha,
1515
+ {
1516
+ siteKey: turnstileSiteKey,
1517
+ onVerify: (token) => {
1518
+ setCaptchaToken(token);
1519
+ submit();
1520
+ }
1521
+ }
1522
+ )
1523
+ ]
1524
+ }
1525
+ )
1526
+ }
1527
+ ),
1528
+ isStarted && !isCompleted && /* @__PURE__ */ jsx(ProgressBar, {}),
1529
+ isStarted && !isCompleted && /* @__PURE__ */ jsx(Header, {}),
1530
+ /* @__PURE__ */ jsx(TimeToComplete, {}),
1531
+ /* @__PURE__ */ jsxs(
1532
+ "div",
1533
+ {
1534
+ className: "lf-question-container",
1535
+ style: {
1536
+ ...theme?.componentVariants?.containerMaxWidth && {
1537
+ maxWidth: theme.componentVariants.containerMaxWidth
1538
+ },
1539
+ ...!hasConsent && { opacity: 0.3, pointerEvents: "none" }
1540
+ },
1541
+ children: [
1542
+ currentItem.type === "welcome" && /* @__PURE__ */ jsx(WelcomeScreen, { screen: currentItem.screen, onStart: start }),
1543
+ currentItem.type === "field" && /* @__PURE__ */ jsx(
1544
+ FieldRenderer,
1545
+ {
1546
+ field: currentItem.field,
1547
+ value: answers[currentItem.field.ref],
1548
+ error: errors[currentItem.field.ref] || null,
1549
+ onChange: (value) => setAnswer(currentItem.field.ref, value),
1550
+ onNext: next,
1551
+ questionNumber: currentItem.index + 1,
1552
+ showQuestionNumber: settings.showQuestionNumber,
1553
+ showKeyHints: settings.showKeyHintOnChoices,
1554
+ systemMessages: settings.systemMessages
1555
+ }
1556
+ ),
1557
+ currentItem.type === "thankYou" && /* @__PURE__ */ jsx(ThankYouScreen, { screen: currentItem.screen })
1558
+ ]
1559
+ }
1560
+ ),
1561
+ !settings.hideNavigation && isStarted && currentItem.type === "field" && /* @__PURE__ */ jsx(NavigationControls, {}),
1562
+ /* @__PURE__ */ jsx(Branding, {})
1563
+ ] });
1564
+ }
1565
+ function FormContainer({ children, className }) {
1566
+ return /* @__PURE__ */ jsx("div", { className: `lf-form ${className || ""}`, children });
1567
+ }
1568
+ function QuestionContainer({ children, className }) {
1569
+ return /* @__PURE__ */ jsx("div", { className: `lf-question-container ${className || ""}`, children });
1570
+ }
1571
+ function ClosedScreen({
1572
+ title,
1573
+ message,
1574
+ type = "closed"
1575
+ }) {
1576
+ const defaultTitle = type === "scheduled" ? "Form not available" : type === "limit" ? "Response limit reached" : "Form closed";
1577
+ const defaultMessage = type === "scheduled" ? "This form is not currently accepting responses." : type === "limit" ? "This form has reached its maximum number of responses." : "This form is no longer accepting responses.";
1578
+ const iconPath = type === "limit" ? (
1579
+ // Bar chart icon for limit reached
1580
+ /* @__PURE__ */ jsxs(Fragment, { children: [
1581
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "20", x2: "18", y2: "10" }),
1582
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "20", x2: "12", y2: "4" }),
1583
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "20", x2: "6", y2: "14" })
1584
+ ] })
1585
+ ) : type === "scheduled" ? (
1586
+ // Clock icon for scheduling
1587
+ /* @__PURE__ */ jsxs(Fragment, { children: [
1588
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
1589
+ /* @__PURE__ */ jsx("polyline", { points: "12 6 12 12 16 14" })
1590
+ ] })
1591
+ ) : (
1592
+ // Lock icon for closed
1593
+ /* @__PURE__ */ jsxs(Fragment, { children: [
1594
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }),
1595
+ /* @__PURE__ */ jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })
1596
+ ] })
1597
+ );
1598
+ return /* @__PURE__ */ jsx("div", { className: "lf-form", children: /* @__PURE__ */ jsx("div", { className: "lf-question-container", children: /* @__PURE__ */ jsxs("div", { className: "lf-screen", style: { textAlign: "center" }, children: [
1599
+ /* @__PURE__ */ jsx(
1600
+ "div",
1601
+ {
1602
+ style: {
1603
+ width: "80px",
1604
+ height: "80px",
1605
+ margin: "0 auto 1.5rem",
1606
+ display: "flex",
1607
+ alignItems: "center",
1608
+ justifyContent: "center",
1609
+ backgroundColor: "color-mix(in srgb, var(--lf-color-primary, #6366f1) 15%, transparent)",
1610
+ borderRadius: "50%"
1611
+ },
1612
+ children: /* @__PURE__ */ jsx(
1613
+ "svg",
1614
+ {
1615
+ width: "40",
1616
+ height: "40",
1617
+ viewBox: "0 0 24 24",
1618
+ fill: "none",
1619
+ stroke: "var(--lf-color-primary, #6366f1)",
1620
+ strokeWidth: "2",
1621
+ strokeLinecap: "round",
1622
+ strokeLinejoin: "round",
1623
+ children: iconPath
1624
+ }
1625
+ )
1626
+ }
1627
+ ),
1628
+ /* @__PURE__ */ jsx(
1629
+ "h1",
1630
+ {
1631
+ className: "lf-question-title",
1632
+ style: {
1633
+ fontSize: "2rem",
1634
+ marginBottom: "0.75rem",
1635
+ color: "var(--lf-color-question, #1f2937)"
1636
+ },
1637
+ children: title || defaultTitle
1638
+ }
1639
+ ),
1640
+ /* @__PURE__ */ jsx(
1641
+ "p",
1642
+ {
1643
+ className: "lf-question-description",
1644
+ style: {
1645
+ fontSize: "1.125rem",
1646
+ color: "var(--lf-color-question, #1f2937)",
1647
+ opacity: 0.7
1648
+ },
1649
+ children: message || defaultMessage
1650
+ }
1651
+ )
1652
+ ] }) }) });
1653
+ }
1654
+
1655
+ export { Branding, ClosedScreen, CookieConsent, FieldRenderer, FormContainer, FormProvider, FormRenderer, MultipleChoice, NavigationControls, NumberField, OpinionScale, ProgressBar, QuestionContainer, QuestionHeader, Rating, Statement, TextField, ThankYouScreen, TimeToComplete, WelcomeScreen, YesNo, useFormContext };
1656
+ //# sourceMappingURL=chunk-I6L5OCM3.js.map
1657
+ //# sourceMappingURL=chunk-I6L5OCM3.js.map