@pinhadev/pinha-quiz-builder-pkg 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # pinha-quiz-builder
2
+
3
+ Quiz and QuizBuilder components for Next.js projects.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install pinha-quiz-builder
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### QuizBuilder
14
+
15
+ Build a quiz and get JSON. Use `onChange` to capture the quiz data.
16
+
17
+ ```tsx
18
+ import { QuizBuilder } from "pinha-quiz-builder";
19
+
20
+ export default function BuilderPage() {
21
+ const handleChange = (data) => {
22
+ console.log(data);
23
+ // Save to file, API, or state
24
+ };
25
+ return <QuizBuilder onChange={handleChange} />;
26
+ }
27
+ ```
28
+
29
+ ### Quiz
30
+
31
+ Render a quiz from JSON produced by QuizBuilder.
32
+
33
+ ```tsx
34
+ import { Quiz } from "pinha-quiz-builder";
35
+ import quizData from "./my-quiz.json";
36
+
37
+ export default function QuizPage() {
38
+ return (
39
+ <Quiz
40
+ data={quizData}
41
+ onSubmit={(answers) => console.log(answers)}
42
+ />
43
+ );
44
+ }
45
+ ```
46
+
47
+ ## Quiz JSON shape
48
+
49
+ ```json
50
+ {
51
+ "id": "uuid",
52
+ "config": {},
53
+ "quizes": [
54
+ {
55
+ "id": 1,
56
+ "title": "Question title",
57
+ "subtitle": "Optional subtitle",
58
+ "type": "basic-numeric"
59
+ },
60
+ {
61
+ "id": 2,
62
+ "title": "Slider question",
63
+ "subtitle": "subtitle",
64
+ "type": "basic-slider",
65
+ "sliderParams": {
66
+ "min": 0,
67
+ "max": 100,
68
+ "inputType": "int",
69
+ "prefix": "Rp.",
70
+ "suffix": "/unit"
71
+ }
72
+ }
73
+ ]
74
+ }
75
+ ```
76
+
77
+ Question types: `basic-numeric`, `basic-text`, `basic-options`, `basic-slider`.
78
+
79
+ ## Tailwind CSS
80
+
81
+ The package uses **Tailwind utility classes** for styling. It does **not** bundle Tailwind or list it as a dependency.
82
+
83
+ - **Your app** must have Tailwind installed and configured.
84
+ - **Include the package in Tailwind’s `content`** so the classes used by the package are generated:
85
+
86
+ ```js
87
+ // tailwind.config.js
88
+ module.exports = {
89
+ content: [
90
+ "./app/**/*.{js,ts,jsx,tsx}",
91
+ "./node_modules/pinha-quiz-builder/dist/**/*.{js,mjs}",
92
+ ],
93
+ theme: { extend: {} },
94
+ plugins: [],
95
+ };
96
+ ```
97
+
98
+ The package “inherits” your project’s Tailwind version; no separate Tailwind dependency is needed in the package.
99
+
100
+ ## Build
101
+
102
+ ```bash
103
+ cd pinha-quiz-builder
104
+ npm install
105
+ npm run build
106
+ ```
@@ -0,0 +1,538 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+
4
+ type QuestionType = "numeric" | "text" | "options" | "slider" | "basicInfo";
5
+ type DesignType = "default" | "card" | "compact";
6
+ type ProgressBarColorPreset = "green" | "blue" | "orange" | "white" | "black" | "custom";
7
+ type ButtonStyle = "primary" | "secondary" | "outline";
8
+ interface SliderParams {
9
+ min: number;
10
+ max: number;
11
+ inputType: "int" | "float";
12
+ prefix?: string;
13
+ suffix?: string;
14
+ }
15
+ /** HTML input type for text questions (email, password, etc.). */
16
+ type TextInputFormat = "text" | "email" | "password" | "tel" | "url" | "search";
17
+ interface TextParams {
18
+ placeholder?: string;
19
+ defaultValue?: string;
20
+ required?: boolean;
21
+ /** Input type for the field (email, password, etc.). */
22
+ inputFormat?: TextInputFormat;
23
+ }
24
+ interface NumericParams {
25
+ placeholder?: string;
26
+ defaultValue?: number;
27
+ required?: boolean;
28
+ }
29
+ /** Single option: label and optional image URL (for card-style display). */
30
+ interface OptionItem {
31
+ /** Stable id for React keys + reordering. */
32
+ id: string;
33
+ label: string;
34
+ imageUrl?: string;
35
+ }
36
+ type OptionsLayout = "list" | "grid" | "masonry";
37
+ type OptionsImageFit = "contain" | "cover";
38
+ interface OptionsParams {
39
+ options: OptionItem[];
40
+ /** How to render options in the quiz UI. */
41
+ layout?: OptionsLayout;
42
+ /** How option images should fit inside the image frame. */
43
+ imageFit?: OptionsImageFit;
44
+ }
45
+ interface QuizItemBase {
46
+ id: string;
47
+ title: string;
48
+ subtitle?: string;
49
+ designType?: DesignType;
50
+ /** When true, this question must be answered before continuing/submitting. */
51
+ required?: boolean;
52
+ /** @deprecated Prefer submitButtonConfig for full per-block button settings. */
53
+ buttonStyle?: ButtonStyle;
54
+ /** Per-block submit button overrides. Use override: true to enable; then other keys (position, size, etc.) apply. Merged with global when override is true. */
55
+ submitButtonConfig?: Record<string, unknown>;
56
+ }
57
+ interface QuizItemNumeric extends QuizItemBase {
58
+ type: "numeric";
59
+ numericParams?: NumericParams;
60
+ }
61
+ interface QuizItemText extends QuizItemBase {
62
+ type: "text";
63
+ textParams?: TextParams;
64
+ }
65
+ interface QuizItemOptions extends QuizItemBase {
66
+ type: "options";
67
+ optionsParams?: OptionsParams;
68
+ }
69
+ interface QuizItemSlider extends QuizItemBase {
70
+ type: "slider";
71
+ sliderParams: SliderParams;
72
+ }
73
+ /** Params for Basic Information Form (name, phone, optional OTP, privacy policy). */
74
+ interface BasicInfoParams {
75
+ /** When true, show phone verification with "Send Verification Code" and OTP flow. */
76
+ withOtp?: boolean;
77
+ /** URL for privacy policy link. */
78
+ privacyPolicyUrl?: string;
79
+ /** Label for privacy agreement (e.g. "I agree to the Privacy Policy"). */
80
+ privacyPolicyLabel?: string;
81
+ }
82
+ interface QuizItemBasicInfo extends QuizItemBase {
83
+ type: "basicInfo";
84
+ basicInfoParams?: BasicInfoParams;
85
+ }
86
+ type QuizItem = QuizItemNumeric | QuizItemText | QuizItemOptions | QuizItemSlider | QuizItemBasicInfo;
87
+ interface QuizData {
88
+ id: string;
89
+ config: Record<string, unknown>;
90
+ quizes: QuizItem[];
91
+ }
92
+
93
+ interface QuizProps {
94
+ data: QuizData;
95
+ /** When set, quiz shows only this step (builder preview). No prev/next. */
96
+ previewStep?: number;
97
+ onSubmit?: (answers: Record<string, string | number>) => void;
98
+ /** Called on "Continue" (non-final step) with current answers + next step index. */
99
+ onCheckpoint?: (answers: Record<string, string | number>, nextStepIndex: number) => void;
100
+ /** Called when the user navigates to a different question (Continue/Back). Passes the new step index. */
101
+ onPageChanged?: (stepIndex: number) => void;
102
+ }
103
+ declare function Quiz({ data, previewStep, onSubmit, onCheckpoint, onPageChanged }: QuizProps): react_jsx_runtime.JSX.Element;
104
+
105
+ interface QuizBuilderProps {
106
+ initialData?: QuizData | null;
107
+ onChange?: (data: QuizData) => void;
108
+ onSubmit?: (answers: Record<string, string | number>) => void;
109
+ }
110
+ declare function QuizBuilder({ initialData, onChange, onSubmit }: QuizBuilderProps): react_jsx_runtime.JSX.Element;
111
+
112
+ interface LazyImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, "loading"> {
113
+ /** Image URL */
114
+ src: string;
115
+ /** Alt text (required for accessibility) */
116
+ alt: string;
117
+ /** Optional class for placeholder shown while loading */
118
+ placeholderClassName?: string;
119
+ }
120
+ /**
121
+ * Lazy-loading image component (native loading="lazy").
122
+ * Shows an optional placeholder until the image has loaded.
123
+ * Shared by v1 and v2.
124
+ */
125
+ declare function LazyImage({ src, alt, className, placeholderClassName, onLoad, onError, ...rest }: LazyImageProps): react_jsx_runtime.JSX.Element | null;
126
+
127
+ type GradientDirection = "to right" | "to bottom" | "to bottom right" | "to bottom left" | "135deg" | "180deg";
128
+ type ColorPickerFieldProps = {
129
+ /** Current value (hex string). Result of selection is always a string. */
130
+ value: string;
131
+ onChange: (hex: string) => void;
132
+ /** Default hex when value is empty or when opening the custom picker. */
133
+ defaultColor: string;
134
+ /** Optional label above the field (e.g. "Background color", "Color"). */
135
+ label?: string;
136
+ /**
137
+ * Preset options: array of hex strings and "custom".
138
+ * Defaults to [PRESET_HEX.green, PRESET_HEX.blue, PRESET_HEX.orange, "custom"].
139
+ */
140
+ customColorSelectionOptions?: (string | "custom")[];
141
+ labelClass?: string;
142
+ inputClass?: string;
143
+ containerClass?: string;
144
+ /**
145
+ * When true, show Solid | Gradient toggle inside the Color section.
146
+ * When gradient is selected, show second color + direction.
147
+ */
148
+ allowGradient?: boolean;
149
+ gradientType?: "solid" | "gradient";
150
+ onGradientTypeChange?: (type: "solid" | "gradient") => void;
151
+ value2?: string;
152
+ defaultColor2?: string;
153
+ onValue2Change?: (hex: string) => void;
154
+ gradientDirection?: GradientDirection;
155
+ onGradientDirectionChange?: (direction: GradientDirection) => void;
156
+ };
157
+ /**
158
+ * Color picker with optional preset chips and optional gradient (Solid | Gradient).
159
+ */
160
+ declare function ColorPickerField({ value, onChange, defaultColor, label, customColorSelectionOptions, labelClass, inputClass, containerClass, allowGradient, gradientType, onGradientTypeChange, value2, defaultColor2, onValue2Change, gradientDirection, onGradientDirectionChange, }: ColorPickerFieldProps): react_jsx_runtime.JSX.Element;
161
+
162
+ /** "colors" = Green/Blue/Orange/Custom; "text" = White/Black/Custom */
163
+ type PresetSetKind = "colors" | "text";
164
+ interface PresetOrCustomColorFieldProps {
165
+ label: string;
166
+ defaultCustomColor: string;
167
+ presetSet?: PresetSetKind;
168
+ preset: ProgressBarColorPreset;
169
+ customHex: string;
170
+ onPresetChange: (preset: ProgressBarColorPreset) => void;
171
+ onCustomChange: (hex: string) => void;
172
+ fieldClass?: string;
173
+ labelClass?: string;
174
+ inputClass?: string;
175
+ }
176
+ /** Preset chips + custom color picker when Custom is selected. */
177
+ declare function PresetOrCustomColorField({ label, defaultCustomColor, presetSet, preset, customHex, onPresetChange, onCustomChange, fieldClass, labelClass, inputClass, }: PresetOrCustomColorFieldProps): react_jsx_runtime.JSX.Element;
178
+
179
+ interface BasicInfoValue {
180
+ firstName: string;
181
+ lastName: string;
182
+ phone: string;
183
+ otpCode: string;
184
+ privacyAccepted: boolean;
185
+ }
186
+
187
+ /**
188
+ * V2 types – no dependency on v1.
189
+ */
190
+ /** Spacing in px. All optional; omit or use 0 for default. */
191
+ interface Spacing {
192
+ marginTop?: number;
193
+ marginRight?: number;
194
+ marginBottom?: number;
195
+ marginLeft?: number;
196
+ paddingTop?: number;
197
+ paddingRight?: number;
198
+ paddingBottom?: number;
199
+ paddingLeft?: number;
200
+ }
201
+ type BorderPreset = "none" | "thin" | "medium" | "thick" | "dashed" | "dotted";
202
+ type BoxShadowPreset = "none" | "sm" | "md" | "lg" | "xl" | "inner";
203
+ type BorderRadiusPreset = "none" | "sm" | "md" | "lg" | "xl" | "full" | "custom";
204
+ /** Background styling options. All optional. */
205
+ interface BackgroundStyle {
206
+ /** Background color (hex or CSS color). */
207
+ backgroundColor?: string;
208
+ /** Border preset. */
209
+ borderPreset?: BorderPreset;
210
+ /** Custom border color (hex or CSS color). Only used if borderPreset is not "none". */
211
+ borderColor?: string;
212
+ /** Border radius preset. */
213
+ borderRadiusPreset?: BorderRadiusPreset;
214
+ /** Custom border radius (px). Only used if borderRadiusPreset is "custom". */
215
+ customBorderRadiusPx?: number;
216
+ /** Box shadow preset. */
217
+ boxShadowPreset?: BoxShadowPreset;
218
+ }
219
+ type SectionType = "container" | "input" | "button" | "text" | "image" | "divider" | "basic";
220
+ interface SectionBase {
221
+ id: string;
222
+ type: SectionType;
223
+ /** Optional display name to identify the section in the builder. */
224
+ name?: string;
225
+ spacing?: Spacing;
226
+ /** Flex value for this section when it's a child of a flex container. */
227
+ flex?: number;
228
+ /** Background styling options. */
229
+ backgroundStyle?: BackgroundStyle;
230
+ }
231
+ /** Flex layout options for container sections. Default: flex, column, gap 16px. */
232
+ interface ContainerLayout {
233
+ /** flex-direction */
234
+ flexDirection?: "row" | "column";
235
+ /** Gap between children (px). Tailwind gap-4 = 16. */
236
+ gap?: number;
237
+ /** align-items */
238
+ alignItems?: "flex-start" | "center" | "flex-end" | "stretch";
239
+ /** justify-content */
240
+ justifyContent?: "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | "space-evenly";
241
+ /** flex-wrap */
242
+ flexWrap?: "nowrap" | "wrap";
243
+ }
244
+ interface ContainerSection extends SectionBase {
245
+ type: "container";
246
+ /** Child section ids (order preserved). */
247
+ children?: string[];
248
+ /** Flex layout. Omitted keys use default (flex, column, gap 16). */
249
+ layout?: ContainerLayout;
250
+ /**
251
+ * Container width preset or custom.
252
+ * - "quarter" = 25%
253
+ * - "half" = 50%
254
+ * - "threeQuarter" = 75%
255
+ * - "full" = 100%
256
+ * - "custom" uses customWidthPx
257
+ */
258
+ width?: "quarter" | "half" | "threeQuarter" | "full" | "auto" | "custom";
259
+ /** Used when width is "custom" (px). */
260
+ customWidthPx?: number;
261
+ /** Center horizontally with auto margins (supersedes marginLeft and marginRight from spacing). */
262
+ centerHorizontal?: boolean;
263
+ }
264
+ type InputFieldType = "text" | "number" | "phone" | "select" | "slider" | "checkbox";
265
+ type InputValidationType = "none" | "email" | "url" | "number" | "phone" | "password" | "zipcode";
266
+ interface SelectOption {
267
+ value: string;
268
+ label: string;
269
+ /** Optional image URL to display alongside the option. */
270
+ imageUrl?: string;
271
+ /** Optional SVG content or image URL for icon-card styling. */
272
+ iconValue?: string;
273
+ /** Whether the iconValue is an SVG string or a URL. */
274
+ iconType?: "svg" | "url";
275
+ /** Optional action to perform when this option is selected. */
276
+ onClick?: ButtonAction;
277
+ }
278
+ interface InputSection extends SectionBase {
279
+ type: "input";
280
+ /** Field type - determines which UI control is rendered. */
281
+ fieldType?: InputFieldType;
282
+ /** Unique field name for form data collection. */
283
+ fieldName?: string;
284
+ placeholder?: string;
285
+ label?: string;
286
+ /** For select: array of options. */
287
+ options?: SelectOption[];
288
+ /** For select: UI type (dropdown, multiple choice with optional images, or card layout). */
289
+ selectUIType?: "dropdown" | "multipleChoice" | "card";
290
+ /** For card UI type: visual style of the card. */
291
+ cardStyling?: "default" | "icon";
292
+ /** For icon-card styling: background color of the icon box. */
293
+ iconBackgroundColor?: string;
294
+ /** For card UI type: card size preset or custom. */
295
+ cardSize?: "small" | "medium" | "large" | "custom";
296
+ /** Used when cardSize is "custom" (px). */
297
+ customCardSizePx?: number;
298
+ /** For select with images: how the images fit within their box (card and multipleChoice UI types). */
299
+ imageObjectFit?: ImageObjectFit;
300
+ /** For slider: min value. */
301
+ sliderMin?: number;
302
+ /** For slider: max value. */
303
+ sliderMax?: number;
304
+ /** For slider: step value. */
305
+ sliderStep?: number;
306
+ /** For checkbox: the text displayed next to the checkbox. */
307
+ checkboxLabel?: string;
308
+ /** Whether this field is required. */
309
+ required?: boolean;
310
+ /** Validation type for the input. */
311
+ validationType?: InputValidationType;
312
+ /** Custom validation error message. */
313
+ validationMessage?: string;
314
+ /**
315
+ * Input width preset or custom.
316
+ * - "quarter" = 25%
317
+ * - "half" = 50%
318
+ * - "threeQuarter" = 75%
319
+ * - "full" = 100%
320
+ * - "custom" uses customWidthPx
321
+ */
322
+ width?: "quarter" | "half" | "threeQuarter" | "full" | "auto" | "custom";
323
+ /** Used when width is "custom" (px). */
324
+ customWidthPx?: number;
325
+ /** Horizontal position of the input within its section. */
326
+ position?: "left" | "center" | "right";
327
+ /** Alignment of the label text. */
328
+ labelAlignment?: "left" | "center" | "right";
329
+ /** Font size of the label (px). */
330
+ labelFontSize?: number;
331
+ /** Letter spacing of the label (em). */
332
+ labelLetterSpacing?: number;
333
+ /** Alignment of the input text. */
334
+ inputAlignment?: "left" | "center" | "right";
335
+ }
336
+ type ButtonActionType = "none" | "goToPage" | "toThankYouPage" | "toRedirectionPage" | "submit";
337
+ interface ButtonAction {
338
+ type: ButtonActionType;
339
+ /** Target page index (0-based) when type is "goToPage". */
340
+ targetPageIndex?: number;
341
+ }
342
+ interface ButtonSection extends SectionBase {
343
+ type: "button";
344
+ label: string;
345
+ /** Button size preset (controls padding + font size). */
346
+ size?: "small" | "medium" | "large";
347
+ /**
348
+ * Button width preset or custom.
349
+ * - "quarter" = 25%
350
+ * - "half" = 50%
351
+ * - "threeQuarter" = 75%
352
+ * - "custom" uses customWidthPx
353
+ */
354
+ width?: "quarter" | "half" | "threeQuarter" | "custom";
355
+ /** Used when width is "custom" (px). */
356
+ customWidthPx?: number;
357
+ /** Border radius preset or custom. */
358
+ borderRadius?: "small" | "medium" | "custom";
359
+ /** Used when borderRadius is "custom" (px). */
360
+ customBorderRadiusPx?: number;
361
+ /** Horizontal position of the button within its section. */
362
+ position?: "left" | "center" | "right";
363
+ /** Text alignment inside the button. */
364
+ textPosition?: "left" | "center" | "right";
365
+ /** Button background color (hex or CSS color). */
366
+ buttonColor?: string;
367
+ /** Button text color (hex or CSS color). */
368
+ textColor?: string;
369
+ /** Action to perform when button is clicked. */
370
+ onClick?: ButtonAction;
371
+ }
372
+ type TextFontSize = "small" | "medium" | "large" | "custom";
373
+ type TextAlignment = "left" | "center" | "right";
374
+ type TextTag = "p" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
375
+ interface TextSection extends SectionBase {
376
+ type: "text";
377
+ content: string;
378
+ /** Which HTML tag to render this text as. Default: "p". */
379
+ tag?: TextTag;
380
+ /** Font size preset or custom. */
381
+ font?: TextFontSize;
382
+ /** Used when font is "custom" (px). */
383
+ customFontSizePx?: number;
384
+ /** Text color (hex or CSS color). */
385
+ color?: string;
386
+ /** Text alignment. */
387
+ alignment?: TextAlignment;
388
+ /** Whether to render italic. */
389
+ italic?: boolean;
390
+ /** Whether to render bold. */
391
+ bold?: boolean;
392
+ /** Whether to underline. */
393
+ underline?: boolean;
394
+ }
395
+ type ImageSize = "small" | "medium" | "large" | "custom";
396
+ type ImagePosition = "left" | "center" | "right";
397
+ type ImageObjectFit = "cover" | "contain";
398
+ interface ImageSection extends SectionBase {
399
+ type: "image";
400
+ src: string;
401
+ alt: string;
402
+ /** Image display size preset or custom. */
403
+ size?: ImageSize;
404
+ /** Used when size is "custom" – max-width in px. */
405
+ customSizePx?: number;
406
+ /** Horizontal position of the image. */
407
+ position?: ImagePosition;
408
+ /** How the image fits within its box. */
409
+ objectFit?: ImageObjectFit;
410
+ /** Optional SVG content to render instead of src. */
411
+ svgContent?: string;
412
+ }
413
+ interface DividerSection extends SectionBase {
414
+ type: "divider";
415
+ /** Hex or CSS color for the line. */
416
+ color?: string;
417
+ /** Thickness of the line in px. */
418
+ thickness?: number;
419
+ /** Border style like solid, dashed, dotted. */
420
+ lineStyle?: "solid" | "dashed" | "dotted";
421
+ /** Width preset or custom. */
422
+ width?: "quarter" | "half" | "threeQuarter" | "full" | "custom";
423
+ /** Used when width is "custom" (px). */
424
+ customWidthPx?: number;
425
+ /** Horizontal position of the line. */
426
+ position?: "left" | "center" | "right";
427
+ }
428
+ type BasicComponentType = "progressBar";
429
+ interface BasicComponentSection extends SectionBase {
430
+ type: "basic";
431
+ /** Component type to render. */
432
+ componentType?: BasicComponentType;
433
+ /** Progress bar height in px. */
434
+ progressBarHeight?: number;
435
+ /** Progress bar background color. */
436
+ progressBarBgColor?: string;
437
+ /** Progress bar fill color. */
438
+ progressBarFillColor?: string;
439
+ /** Border radius for progress bar (px). */
440
+ progressBarRadius?: number;
441
+ /** Whether to show percentage text. */
442
+ showPercentage?: boolean;
443
+ }
444
+ type Section = ContainerSection | InputSection | ButtonSection | TextSection | ImageSection | DividerSection | BasicComponentSection;
445
+ /** Page-level layout: which sections render in header/footer. Main content is the page's sectionIds. */
446
+ interface QuizV2Layout {
447
+ id: string;
448
+ /** Section ids to render in the header slot (order preserved). */
449
+ headerSectionIds?: string[];
450
+ /** Section ids to render in the footer slot (order preserved). */
451
+ footerSectionIds?: string[];
452
+ }
453
+ /** One page of the quiz: root section ids for this page (order preserved). */
454
+ type QuizV2PageType = "regular" | "redirection" | "thankYou";
455
+ interface QuizV2Page {
456
+ id: string;
457
+ /** Page type: regular (default), redirection (countdown), or thankYou (auto-submit). */
458
+ type?: QuizV2PageType;
459
+ /** Root section ids on this page – rendered inside main-content-generator (order preserved). */
460
+ sectionIds: string[];
461
+ /** Layout id for this page. If empty, use first layout. */
462
+ layoutId?: string | null;
463
+ /** For redirection page: URL to redirect to after countdown. */
464
+ redirectUrl?: string;
465
+ /** For redirection page: countdown duration in seconds (default: 5). */
466
+ redirectCountdownSeconds?: number;
467
+ }
468
+ interface QuizV2Data {
469
+ id: string;
470
+ /** Layout-level config applied to every page of this instance. */
471
+ config: Record<string, unknown>;
472
+ /** All sections (can be referenced by pages and layouts). */
473
+ sections: Section[];
474
+ /** Page-level layouts (header/main/footer). Each page can pick one or use first. */
475
+ layouts: QuizV2Layout[];
476
+ /** Quiz pages (page 1, page 2, …). */
477
+ pages: QuizV2Page[];
478
+ }
479
+
480
+ interface QuizV2Props {
481
+ /** Quiz ID to load from storage (takes priority over data) */
482
+ id?: string;
483
+ /** Quiz data (used if id is not provided) */
484
+ data?: QuizV2Data;
485
+ onSubmit?: (answers: unknown) => void;
486
+ /** Optional controlled current page index */
487
+ currentPageIndex?: number;
488
+ /** Optional callback when page changes */
489
+ onPageChange?: (index: number) => void;
490
+ /** Whether to enable default tracking (defaults to true) */
491
+ withDefaultTracking?: boolean;
492
+ }
493
+ /**
494
+ * Quiz V2 – renders by page inside layout-container (header-generator, main-content-generator, footer-generator).
495
+ * Layout-level config and page layout (header/footer sections) applied. Main content = current page sections.
496
+ *
497
+ * Accepts either an `id` to load from storage or `data` directly. If both are provided, `id` takes priority.
498
+ */
499
+ declare function QuizV2({ id, data: propData, onSubmit, currentPageIndex, onPageChange, withDefaultTracking }: QuizV2Props): react_jsx_runtime.JSX.Element;
500
+
501
+ interface QuizBuilderV2Props {
502
+ /** Initial data (e.g. from localStorage or getDefaultQuizV2Data()). */
503
+ initialData?: QuizV2Data | null;
504
+ /** Last saved data – used to compute dirty state and which sections changed. */
505
+ lastSavedData?: QuizV2Data | null;
506
+ onChange?: (data: QuizV2Data) => void;
507
+ /** Called when user clicks Save. Consumer should persist and pass new lastSavedData. */
508
+ onSave?: (data: QuizV2Data) => void;
509
+ onSubmit?: (answers: unknown) => void;
510
+ }
511
+ /**
512
+ * QuizBuilder V2 – global config, default sections on first load, save button, unsaved warning, visual for changed sections.
513
+ */
514
+ declare function QuizBuilderV2({ initialData, lastSavedData, onChange, onSave, onSubmit, }: QuizBuilderV2Props): react_jsx_runtime.JSX.Element;
515
+
516
+ /**
517
+ * Default spacing per section type (Tailwind-like in px). Used when creating sections and when
518
+ * resolving missing keys. Fallback when type is unknown.
519
+ */
520
+ declare const DEFAULT_SPACING: Spacing;
521
+ /** Default page layout: empty header and footer. */
522
+ declare function getDefaultQuizV2Layout(): QuizV2Layout;
523
+ /**
524
+ * Resolve layout for a page: page.layoutId ?? first layout. Returns layout or null.
525
+ */
526
+ declare function resolveLayoutForPage(layouts: QuizV2Layout[] | undefined, page: QuizV2Page | null): QuizV2Layout | null;
527
+ /**
528
+ * Creates default quiz data with one page and one of each basic section type.
529
+ * Used when user first loads the builder (nothing in storage).
530
+ */
531
+ declare function getDefaultQuizV2Data(): QuizV2Data;
532
+
533
+ /**
534
+ * Display label for a section: uses section.name when set, otherwise type-specific fallback.
535
+ */
536
+ declare function getSectionLabel(s: Section): string;
537
+
538
+ export { type BasicInfoParams, type BasicInfoValue, type ButtonStyle, ColorPickerField, type ColorPickerFieldProps, type ContainerLayout, DEFAULT_SPACING, type DesignType, type GradientDirection, type ImageObjectFit, type ImagePosition, type ImageSize, LazyImage, type LazyImageProps, type OptionItem, type OptionsParams, PresetOrCustomColorField, type PresetOrCustomColorFieldProps, type PresetSetKind, type ProgressBarColorPreset, type QuestionType, Quiz, QuizBuilder, type QuizBuilderProps, QuizBuilderV2, type QuizBuilderV2Props, type QuizData, type QuizItem, type QuizProps, QuizV2, type QuizV2Data, type QuizV2Layout, type QuizV2Page, type QuizV2Props, type Section, type SectionType, type SliderParams, type Spacing, type TextAlignment, type TextFontSize, getDefaultQuizV2Data, getDefaultQuizV2Layout, getSectionLabel, resolveLayoutForPage };