@squaredr/fieldcraft-pro 1.8.0 → 1.9.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.
@@ -419,16 +419,20 @@ function deepMerge(base, partial) {
419
419
 
420
420
  // src/theme-editor/palette-generator.ts
421
421
  function generatePalette(baseHex) {
422
+ const cleaned = baseHex.replace("#", "");
423
+ if (!/^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{6}$/.test(cleaned)) {
424
+ return generatePalette("#6B7280");
425
+ }
422
426
  const [h, s, l] = hexToHsl(baseHex);
423
427
  const secH = (h + 180) % 360;
424
428
  const isLightBase = l > 50;
425
429
  return {
426
430
  // Primary
427
431
  primary: hslToHex(h, s, clamp(l, 30, 60)),
428
- primaryForeground: isLightBase ? "#ffffff" : "#ffffff",
432
+ primaryForeground: isLightBase ? "#ffffff" : "#000000",
429
433
  // Secondary (complementary)
430
434
  secondary: hslToHex(secH, Math.max(s - 15, 10), clamp(l, 35, 55)),
431
- secondaryForeground: "#ffffff",
435
+ secondaryForeground: isLightBase ? "#ffffff" : "#000000",
432
436
  // Surfaces
433
437
  surface: hslToHex(h, Math.max(s - 35, 3), 97),
434
438
  background: "#F4F7F8",
@@ -1474,6 +1478,12 @@ function resolveThemeFromDOM() {
1474
1478
  const val = styles.getPropertyValue(prop).trim();
1475
1479
  return val || void 0;
1476
1480
  };
1481
+ const getNumber = (prop) => {
1482
+ const val = get(prop);
1483
+ if (val == null) return void 0;
1484
+ const num = parseFloat(val);
1485
+ return isNaN(num) ? void 0 : num;
1486
+ };
1477
1487
  return {
1478
1488
  colors: {
1479
1489
  primary: get("--primary"),
@@ -1506,6 +1516,17 @@ function resolveThemeFromDOM() {
1506
1516
  inputRadius: get("--fc-radius-input"),
1507
1517
  buttonRadius: get("--fc-radius-button"),
1508
1518
  cardRadius: get("--fc-radius-card")
1519
+ },
1520
+ spacing: {
1521
+ base: getNumber("--fc-spacing-base"),
1522
+ sectionGap: getNumber("--fc-section-gap"),
1523
+ fieldGap: getNumber("--fc-field-gap"),
1524
+ inputPaddingX: getNumber("--fc-input-padding-x"),
1525
+ inputPaddingY: getNumber("--fc-input-padding-y")
1526
+ },
1527
+ layout: {
1528
+ maxWidth: get("--fc-max-width")
1529
+ // alignment, progressPosition, sectionLayout are enums — not resolvable from CSS
1509
1530
  }
1510
1531
  };
1511
1532
  }
@@ -1560,6 +1581,9 @@ function detectPresetFamily(theme) {
1560
1581
  }
1561
1582
  return null;
1562
1583
  }
1584
+ function isValidHex(value) {
1585
+ return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value);
1586
+ }
1563
1587
  var SECTIONS = [
1564
1588
  {
1565
1589
  id: "colors",
@@ -1810,6 +1834,7 @@ function ThemeEditorInner({
1810
1834
  a.click();
1811
1835
  URL.revokeObjectURL(url);
1812
1836
  }, [theme]);
1837
+ const [importError, setImportError] = useState(null);
1813
1838
  const importJson = useCallback(() => {
1814
1839
  const input = document.createElement("input");
1815
1840
  input.type = "file";
@@ -1821,10 +1846,16 @@ function ThemeEditorInner({
1821
1846
  reader.onload = () => {
1822
1847
  try {
1823
1848
  const parsed = JSON.parse(reader.result);
1849
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed) || !("colors" in parsed || "typography" in parsed || "shape" in parsed || "spacing" in parsed || "layout" in parsed)) {
1850
+ setImportError("Invalid theme file: expected an object with colors, typography, shape, spacing, or layout.");
1851
+ return;
1852
+ }
1853
+ setImportError(null);
1824
1854
  setTheme(parsed);
1825
1855
  setPresetKey("custom");
1826
1856
  onChange?.(parsed, "custom");
1827
1857
  } catch {
1858
+ setImportError("Failed to parse JSON file. Please check the file format.");
1828
1859
  }
1829
1860
  };
1830
1861
  reader.readAsText(file);
@@ -1902,6 +1933,10 @@ function ThemeEditorInner({
1902
1933
  onSave && /* @__PURE__ */ jsx("button", { onClick: () => onSave(theme, presetKey), className: "fcte-btn fcte-btn--primary", children: "Save" })
1903
1934
  ] })
1904
1935
  ] }),
1936
+ importError && /* @__PURE__ */ jsxs("div", { className: "fcte-import-error", role: "alert", children: [
1937
+ /* @__PURE__ */ jsx("span", { children: importError }),
1938
+ /* @__PURE__ */ jsx("button", { onClick: () => setImportError(null), className: "fcte-import-error__close", children: "\xD7" })
1939
+ ] }),
1905
1940
  showPalette && /* @__PURE__ */ jsx(
1906
1941
  PaletteGenerator,
1907
1942
  {
@@ -1935,6 +1970,8 @@ function ThemeEditorInner({
1935
1970
  /* @__PURE__ */ jsx("div", { className: "fcte-fields", children: currentSection.fields.map((field) => {
1936
1971
  const val = sectionValues[field.key];
1937
1972
  if (field.kind === "color") {
1973
+ const colorVal = typeof val === "string" ? val : "#000000";
1974
+ const isInvalid = colorVal.length > 0 && !isValidHex(colorVal);
1938
1975
  return /* @__PURE__ */ jsxs("div", { className: "fcte-field", children: [
1939
1976
  /* @__PURE__ */ jsx("label", { className: "fcte-field__label", children: field.label }),
1940
1977
  /* @__PURE__ */ jsxs("div", { className: "fcte-field__color-row", children: [
@@ -1942,7 +1979,7 @@ function ThemeEditorInner({
1942
1979
  "input",
1943
1980
  {
1944
1981
  type: "color",
1945
- value: typeof val === "string" ? val : "#000000",
1982
+ value: isValidHex(colorVal) ? colorVal : "#000000",
1946
1983
  onChange: (e) => updateField(currentSection.themeKey, field.key, e.target.value),
1947
1984
  className: "fcte-field__swatch"
1948
1985
  }
@@ -1951,10 +1988,24 @@ function ThemeEditorInner({
1951
1988
  "input",
1952
1989
  {
1953
1990
  type: "text",
1954
- value: typeof val === "string" ? val : "",
1955
- onChange: (e) => updateField(currentSection.themeKey, field.key, e.target.value),
1956
- className: "fcte-field__text",
1957
- spellCheck: false
1991
+ value: colorVal,
1992
+ onChange: (e) => {
1993
+ const v = e.target.value;
1994
+ if (isValidHex(v)) {
1995
+ updateField(currentSection.themeKey, field.key, v);
1996
+ } else {
1997
+ setTheme((prev) => ({
1998
+ ...prev,
1999
+ [currentSection.themeKey]: {
2000
+ ...prev[currentSection.themeKey],
2001
+ [field.key]: v
2002
+ }
2003
+ }));
2004
+ }
2005
+ },
2006
+ className: `fcte-field__text${isInvalid ? " fcte-field__text--invalid" : ""}`,
2007
+ spellCheck: false,
2008
+ placeholder: "#RRGGBB"
1958
2009
  }
1959
2010
  )
1960
2011
  ] })
@@ -1983,9 +2034,16 @@ function ThemeEditorInner({
1983
2034
  {
1984
2035
  type: "number",
1985
2036
  value: typeof val === "number" ? val : 0,
1986
- onChange: (e) => updateField(currentSection.themeKey, field.key, Number(e.target.value)),
2037
+ onChange: (e) => {
2038
+ let n = Math.round(Number(e.target.value));
2039
+ if (isNaN(n)) n = 0;
2040
+ if (field.min != null) n = Math.max(field.min, n);
2041
+ if (field.max != null) n = Math.min(field.max, n);
2042
+ updateField(currentSection.themeKey, field.key, n);
2043
+ },
1987
2044
  min: field.min,
1988
2045
  max: field.max,
2046
+ step: 1,
1989
2047
  className: "fcte-field__number"
1990
2048
  }
1991
2049
  ),
@@ -2003,7 +2061,8 @@ function ThemeEditorInner({
2003
2061
  onChange: (e) => updateField(currentSection.themeKey, field.key, e.target.value),
2004
2062
  placeholder: field.placeholder,
2005
2063
  className: "fcte-field__text",
2006
- spellCheck: false
2064
+ spellCheck: false,
2065
+ maxLength: 200
2007
2066
  }
2008
2067
  )
2009
2068
  ] }, field.key);
@@ -0,0 +1,174 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { c as FormBuilderProps, F as FormBuilderTheme, e as QuestionTypeInfo, S as SelectedItem, D as DragItem } from './types-BQOeI3LX.js';
4
+ import { FormEngineSchema, Option, Question, Section } from '@squaredr/fieldcraft-core';
5
+ import { ClassValue } from 'clsx';
6
+ import * as _dnd_kit_core from '@dnd-kit/core';
7
+ import { DragStartEvent, DragEndEvent } from '@dnd-kit/core';
8
+
9
+ declare const FormBuilder: react.ComponentType<FormBuilderProps>;
10
+
11
+ declare function useBuilderTheme(): FormBuilderTheme;
12
+ type FormBuilderThemeProviderProps = {
13
+ theme?: FormBuilderTheme;
14
+ children: ReactNode;
15
+ };
16
+ declare function FormBuilderThemeProvider({ theme, children }: FormBuilderThemeProviderProps): react.JSX.Element;
17
+
18
+ /**
19
+ * Metadata for all question types available in the Pro palette.
20
+ * Icons are lucide-react component names (imported dynamically via ICON_MAP).
21
+ *
22
+ * 44 field types — general-purpose form building.
23
+ * Telehealth-specific fields (15) are in @squaredr/fieldcraft-pro-telehealth.
24
+ */
25
+ declare const QUESTION_TYPE_INFO: Record<string, QuestionTypeInfo>;
26
+ /**
27
+ * Default palette layout with categories (44 Pro field types).
28
+ * Telehealth fields can be appended via FormBuilder's `palette` prop.
29
+ */
30
+ declare const DEFAULT_PALETTE: ({
31
+ category: "text";
32
+ label: string;
33
+ types: string[];
34
+ } | {
35
+ category: "numeric";
36
+ label: string;
37
+ types: string[];
38
+ } | {
39
+ category: "selection";
40
+ label: string;
41
+ types: string[];
42
+ } | {
43
+ category: "datetime";
44
+ label: string;
45
+ types: string[];
46
+ } | {
47
+ category: "media";
48
+ label: string;
49
+ types: string[];
50
+ } | {
51
+ category: "content";
52
+ label: string;
53
+ types: string[];
54
+ } | {
55
+ category: "structural";
56
+ label: string;
57
+ types: string[];
58
+ } | {
59
+ category: "advanced";
60
+ label: string;
61
+ types: string[];
62
+ })[];
63
+
64
+ /**
65
+ * Generate unique IDs for sections, questions, and options.
66
+ * Format: {prefix}_{uuid}
67
+ */
68
+ declare function generateId(prefix: string): string;
69
+ declare function generateSectionId(): string;
70
+ declare function generateQuestionId(): string;
71
+ declare function generateOptionId(): string;
72
+
73
+ /**
74
+ * Pure functions for mutating FormEngineSchema.
75
+ * All functions return a new schema object (immutable updates).
76
+ */
77
+
78
+ declare function addSection(schema: FormEngineSchema, section: Section, index: number): FormEngineSchema;
79
+ declare function removeSection(schema: FormEngineSchema, sectionId: string): FormEngineSchema;
80
+ declare function updateSection(schema: FormEngineSchema, sectionId: string, updates: Partial<Section>): FormEngineSchema;
81
+ declare function moveSection(schema: FormEngineSchema, sectionId: string, newIndex: number): FormEngineSchema;
82
+ declare function duplicateSection(schema: FormEngineSchema, sectionId: string): FormEngineSchema;
83
+ declare function addQuestion(schema: FormEngineSchema, sectionId: string, question: Question, index: number): FormEngineSchema;
84
+ declare function removeQuestion(schema: FormEngineSchema, sectionId: string, questionId: string): FormEngineSchema;
85
+ declare function updateQuestion(schema: FormEngineSchema, sectionId: string, questionId: string, updates: Partial<Question>): FormEngineSchema;
86
+ declare function moveQuestion(schema: FormEngineSchema, sectionId: string, questionId: string, targetSectionId: string, newIndex: number): FormEngineSchema;
87
+ declare function duplicateQuestion(schema: FormEngineSchema, sectionId: string, questionId: string): FormEngineSchema;
88
+ declare function addOption(schema: FormEngineSchema, sectionId: string, questionId: string, option: Option, index: number): FormEngineSchema;
89
+ declare function removeOption(schema: FormEngineSchema, sectionId: string, questionId: string, optionIndex: number): FormEngineSchema;
90
+ declare function updateOption(schema: FormEngineSchema, sectionId: string, questionId: string, optionIndex: number, updates: Partial<Option>): FormEngineSchema;
91
+ declare function moveOption(schema: FormEngineSchema, sectionId: string, questionId: string, oldIndex: number, newIndex: number): FormEngineSchema;
92
+ declare function findQuestion(schema: FormEngineSchema, sectionId: string, questionId: string): {
93
+ section: Section;
94
+ question: Question;
95
+ questionIndex: number;
96
+ } | null;
97
+ declare function findSection(schema: FormEngineSchema, sectionId: string): {
98
+ section: Section;
99
+ sectionIndex: number;
100
+ } | null;
101
+
102
+ declare function cn(...inputs: ClassValue[]): string;
103
+
104
+ /**
105
+ * Master state hook for the FormBuilder.
106
+ * Combines schema state, selection, and undo/redo.
107
+ *
108
+ * All mutation callbacks use a ref to read the latest schema,
109
+ * avoiding stale closures when multiple mutations fire in the
110
+ * same render tick.
111
+ */
112
+ declare function useBuilderState(initialSchema: FormEngineSchema): {
113
+ schema: FormEngineSchema;
114
+ selectedItem: SelectedItem | null;
115
+ isDirty: boolean;
116
+ updateSchema: (newSchema: FormEngineSchema) => void;
117
+ addSection: (section: Section, index: number) => void;
118
+ removeSection: (sectionId: string) => void;
119
+ updateSection: (sectionId: string, updates: Partial<Section>) => void;
120
+ moveSection: (sectionId: string, newIndex: number) => void;
121
+ duplicateSection: (sectionId: string) => void;
122
+ addQuestion: (sectionId: string, question: Question, index: number) => void;
123
+ removeQuestion: (sectionId: string, questionId: string) => void;
124
+ updateQuestion: (sectionId: string, questionId: string, updates: Partial<Question>) => void;
125
+ moveQuestion: (sectionId: string, questionId: string, targetSectionId: string, newIndex: number) => void;
126
+ duplicateQuestion: (sectionId: string, questionId: string) => void;
127
+ selectQuestion: (sectionId: string, questionId: string) => void;
128
+ selectSection: (sectionId: string) => void;
129
+ clearSelection: () => void;
130
+ canUndo: boolean;
131
+ canRedo: boolean;
132
+ undo: () => void;
133
+ redo: () => void;
134
+ resetSchema: (newSchema: FormEngineSchema) => void;
135
+ markClean: () => void;
136
+ };
137
+ type BuilderStateHook = ReturnType<typeof useBuilderState>;
138
+
139
+ type UndoRedoState = {
140
+ canUndo: boolean;
141
+ canRedo: boolean;
142
+ undo: () => void;
143
+ redo: () => void;
144
+ push: (schema: FormEngineSchema) => void;
145
+ clear: () => void;
146
+ };
147
+ /**
148
+ * Hook for undo/redo functionality.
149
+ * Maintains a history stack of schemas with max 50 entries.
150
+ * Uses state for the index so canUndo/canRedo trigger re-renders.
151
+ * The history array is kept in a ref since its identity doesn't
152
+ * need to cause re-renders — only the index matters.
153
+ */
154
+ declare function useUndoRedo(currentSchema: FormEngineSchema, setSchema: (schema: FormEngineSchema) => void): UndoRedoState;
155
+
156
+ /**
157
+ * Hook to wire up @dnd-kit drag-and-drop.
158
+ * Returns DndContext props and handlers.
159
+ */
160
+ declare function useDragDrop(builderState: BuilderStateHook): {
161
+ sensors: _dnd_kit_core.SensorDescriptor<_dnd_kit_core.SensorOptions>[];
162
+ handleDragStart: (event: DragStartEvent) => void;
163
+ handleDragEnd: (event: DragEndEvent) => void;
164
+ handleDragCancel: () => void;
165
+ activeDragItem: DragItem | null;
166
+ };
167
+
168
+ /**
169
+ * Default empty schema for new forms.
170
+ * Contains one section with one question to get started.
171
+ */
172
+ declare const DEFAULT_SCHEMA: FormEngineSchema;
173
+
174
+ export { useUndoRedo as A, type BuilderStateHook as B, DEFAULT_PALETTE as D, FormBuilder as F, QUESTION_TYPE_INFO as Q, type UndoRedoState as U, DEFAULT_SCHEMA as a, FormBuilderThemeProvider as b, addOption as c, addQuestion as d, addSection as e, cn as f, duplicateQuestion as g, duplicateSection as h, findQuestion as i, findSection as j, generateId as k, generateOptionId as l, generateQuestionId as m, generateSectionId as n, moveOption as o, moveQuestion as p, moveSection as q, removeOption as r, removeQuestion as s, removeSection as t, updateOption as u, updateQuestion as v, updateSection as w, useBuilderState as x, useBuilderTheme as y, useDragDrop as z };
@@ -0,0 +1,174 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { c as FormBuilderProps, F as FormBuilderTheme, e as QuestionTypeInfo, S as SelectedItem, D as DragItem } from './types-BQOeI3LX.mjs';
4
+ import { FormEngineSchema, Option, Question, Section } from '@squaredr/fieldcraft-core';
5
+ import { ClassValue } from 'clsx';
6
+ import * as _dnd_kit_core from '@dnd-kit/core';
7
+ import { DragStartEvent, DragEndEvent } from '@dnd-kit/core';
8
+
9
+ declare const FormBuilder: react.ComponentType<FormBuilderProps>;
10
+
11
+ declare function useBuilderTheme(): FormBuilderTheme;
12
+ type FormBuilderThemeProviderProps = {
13
+ theme?: FormBuilderTheme;
14
+ children: ReactNode;
15
+ };
16
+ declare function FormBuilderThemeProvider({ theme, children }: FormBuilderThemeProviderProps): react.JSX.Element;
17
+
18
+ /**
19
+ * Metadata for all question types available in the Pro palette.
20
+ * Icons are lucide-react component names (imported dynamically via ICON_MAP).
21
+ *
22
+ * 44 field types — general-purpose form building.
23
+ * Telehealth-specific fields (15) are in @squaredr/fieldcraft-pro-telehealth.
24
+ */
25
+ declare const QUESTION_TYPE_INFO: Record<string, QuestionTypeInfo>;
26
+ /**
27
+ * Default palette layout with categories (44 Pro field types).
28
+ * Telehealth fields can be appended via FormBuilder's `palette` prop.
29
+ */
30
+ declare const DEFAULT_PALETTE: ({
31
+ category: "text";
32
+ label: string;
33
+ types: string[];
34
+ } | {
35
+ category: "numeric";
36
+ label: string;
37
+ types: string[];
38
+ } | {
39
+ category: "selection";
40
+ label: string;
41
+ types: string[];
42
+ } | {
43
+ category: "datetime";
44
+ label: string;
45
+ types: string[];
46
+ } | {
47
+ category: "media";
48
+ label: string;
49
+ types: string[];
50
+ } | {
51
+ category: "content";
52
+ label: string;
53
+ types: string[];
54
+ } | {
55
+ category: "structural";
56
+ label: string;
57
+ types: string[];
58
+ } | {
59
+ category: "advanced";
60
+ label: string;
61
+ types: string[];
62
+ })[];
63
+
64
+ /**
65
+ * Generate unique IDs for sections, questions, and options.
66
+ * Format: {prefix}_{uuid}
67
+ */
68
+ declare function generateId(prefix: string): string;
69
+ declare function generateSectionId(): string;
70
+ declare function generateQuestionId(): string;
71
+ declare function generateOptionId(): string;
72
+
73
+ /**
74
+ * Pure functions for mutating FormEngineSchema.
75
+ * All functions return a new schema object (immutable updates).
76
+ */
77
+
78
+ declare function addSection(schema: FormEngineSchema, section: Section, index: number): FormEngineSchema;
79
+ declare function removeSection(schema: FormEngineSchema, sectionId: string): FormEngineSchema;
80
+ declare function updateSection(schema: FormEngineSchema, sectionId: string, updates: Partial<Section>): FormEngineSchema;
81
+ declare function moveSection(schema: FormEngineSchema, sectionId: string, newIndex: number): FormEngineSchema;
82
+ declare function duplicateSection(schema: FormEngineSchema, sectionId: string): FormEngineSchema;
83
+ declare function addQuestion(schema: FormEngineSchema, sectionId: string, question: Question, index: number): FormEngineSchema;
84
+ declare function removeQuestion(schema: FormEngineSchema, sectionId: string, questionId: string): FormEngineSchema;
85
+ declare function updateQuestion(schema: FormEngineSchema, sectionId: string, questionId: string, updates: Partial<Question>): FormEngineSchema;
86
+ declare function moveQuestion(schema: FormEngineSchema, sectionId: string, questionId: string, targetSectionId: string, newIndex: number): FormEngineSchema;
87
+ declare function duplicateQuestion(schema: FormEngineSchema, sectionId: string, questionId: string): FormEngineSchema;
88
+ declare function addOption(schema: FormEngineSchema, sectionId: string, questionId: string, option: Option, index: number): FormEngineSchema;
89
+ declare function removeOption(schema: FormEngineSchema, sectionId: string, questionId: string, optionIndex: number): FormEngineSchema;
90
+ declare function updateOption(schema: FormEngineSchema, sectionId: string, questionId: string, optionIndex: number, updates: Partial<Option>): FormEngineSchema;
91
+ declare function moveOption(schema: FormEngineSchema, sectionId: string, questionId: string, oldIndex: number, newIndex: number): FormEngineSchema;
92
+ declare function findQuestion(schema: FormEngineSchema, sectionId: string, questionId: string): {
93
+ section: Section;
94
+ question: Question;
95
+ questionIndex: number;
96
+ } | null;
97
+ declare function findSection(schema: FormEngineSchema, sectionId: string): {
98
+ section: Section;
99
+ sectionIndex: number;
100
+ } | null;
101
+
102
+ declare function cn(...inputs: ClassValue[]): string;
103
+
104
+ /**
105
+ * Master state hook for the FormBuilder.
106
+ * Combines schema state, selection, and undo/redo.
107
+ *
108
+ * All mutation callbacks use a ref to read the latest schema,
109
+ * avoiding stale closures when multiple mutations fire in the
110
+ * same render tick.
111
+ */
112
+ declare function useBuilderState(initialSchema: FormEngineSchema): {
113
+ schema: FormEngineSchema;
114
+ selectedItem: SelectedItem | null;
115
+ isDirty: boolean;
116
+ updateSchema: (newSchema: FormEngineSchema) => void;
117
+ addSection: (section: Section, index: number) => void;
118
+ removeSection: (sectionId: string) => void;
119
+ updateSection: (sectionId: string, updates: Partial<Section>) => void;
120
+ moveSection: (sectionId: string, newIndex: number) => void;
121
+ duplicateSection: (sectionId: string) => void;
122
+ addQuestion: (sectionId: string, question: Question, index: number) => void;
123
+ removeQuestion: (sectionId: string, questionId: string) => void;
124
+ updateQuestion: (sectionId: string, questionId: string, updates: Partial<Question>) => void;
125
+ moveQuestion: (sectionId: string, questionId: string, targetSectionId: string, newIndex: number) => void;
126
+ duplicateQuestion: (sectionId: string, questionId: string) => void;
127
+ selectQuestion: (sectionId: string, questionId: string) => void;
128
+ selectSection: (sectionId: string) => void;
129
+ clearSelection: () => void;
130
+ canUndo: boolean;
131
+ canRedo: boolean;
132
+ undo: () => void;
133
+ redo: () => void;
134
+ resetSchema: (newSchema: FormEngineSchema) => void;
135
+ markClean: () => void;
136
+ };
137
+ type BuilderStateHook = ReturnType<typeof useBuilderState>;
138
+
139
+ type UndoRedoState = {
140
+ canUndo: boolean;
141
+ canRedo: boolean;
142
+ undo: () => void;
143
+ redo: () => void;
144
+ push: (schema: FormEngineSchema) => void;
145
+ clear: () => void;
146
+ };
147
+ /**
148
+ * Hook for undo/redo functionality.
149
+ * Maintains a history stack of schemas with max 50 entries.
150
+ * Uses state for the index so canUndo/canRedo trigger re-renders.
151
+ * The history array is kept in a ref since its identity doesn't
152
+ * need to cause re-renders — only the index matters.
153
+ */
154
+ declare function useUndoRedo(currentSchema: FormEngineSchema, setSchema: (schema: FormEngineSchema) => void): UndoRedoState;
155
+
156
+ /**
157
+ * Hook to wire up @dnd-kit drag-and-drop.
158
+ * Returns DndContext props and handlers.
159
+ */
160
+ declare function useDragDrop(builderState: BuilderStateHook): {
161
+ sensors: _dnd_kit_core.SensorDescriptor<_dnd_kit_core.SensorOptions>[];
162
+ handleDragStart: (event: DragStartEvent) => void;
163
+ handleDragEnd: (event: DragEndEvent) => void;
164
+ handleDragCancel: () => void;
165
+ activeDragItem: DragItem | null;
166
+ };
167
+
168
+ /**
169
+ * Default empty schema for new forms.
170
+ * Contains one section with one question to get started.
171
+ */
172
+ declare const DEFAULT_SCHEMA: FormEngineSchema;
173
+
174
+ export { useUndoRedo as A, type BuilderStateHook as B, DEFAULT_PALETTE as D, FormBuilder as F, QUESTION_TYPE_INFO as Q, type UndoRedoState as U, DEFAULT_SCHEMA as a, FormBuilderThemeProvider as b, addOption as c, addQuestion as d, addSection as e, cn as f, duplicateQuestion as g, duplicateSection as h, findQuestion as i, findSection as j, generateId as k, generateOptionId as l, generateQuestionId as m, generateSectionId as n, moveOption as o, moveQuestion as p, moveSection as q, removeOption as r, removeQuestion as s, removeSection as t, updateOption as u, updateQuestion as v, updateSection as w, useBuilderState as x, useBuilderTheme as y, useDragDrop as z };