@enerjisaformlibrary/formbuilder-react 1.0.9 → 1.0.22
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 +265 -843
- package/index.cjs +469 -240
- package/index.cjs.map +1 -1
- package/index.js +469 -240
- package/index.js.map +1 -1
- package/package.json +14 -19
- package/styles.css +1 -1
- package/types/client/src/components/form-builder/CanvasField.d.ts.map +1 -1
- package/types/client/src/components/form-builder/PropertiesPanel.d.ts.map +1 -1
- package/types/client/src/components/form-builder/Toolbar.d.ts.map +1 -1
- package/types/client/src/store/formStore.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
# @enerjisaformlibrary/formbuilder-react
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
- [TypeScript Types](#typescript-types)
|
|
21
|
-
- [Complete JSON Example](#complete-json-example)
|
|
22
|
-
- [Extracting Form Values](#extracting-form-values)
|
|
23
|
-
|
|
24
|
-
---
|
|
3
|
+
Professional drag-and-drop form builder React component with 20+ field types, multi-step wizard support, conditional logic, undo/redo history, and JSON export/import.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **20+ Field Types**: Text, textarea, number, email, phone, URL, password, date, time, date range, dropdown, checkbox, radio, toggle, multi-select, file upload, signature pad, star rating, slider, color picker, rich text editor, autocomplete, QR code, pattern format, and more
|
|
8
|
+
- **Drag & Drop**: Intuitive drag-and-drop interface for building forms
|
|
9
|
+
- **Multi-Step Wizard**: Create multi-step forms with progress indicator
|
|
10
|
+
- **Conditional Logic**: Show/hide/enable/disable fields based on conditions
|
|
11
|
+
- **Undo/Redo**: Full undo/redo support with 50-state history
|
|
12
|
+
- **Grid Layout**: 12-column responsive grid system
|
|
13
|
+
- **Containers**: Nested row/column layouts within containers
|
|
14
|
+
- **JSON Export/Import**: Export forms as JSON and import them back
|
|
15
|
+
- **Custom Styling**: Per-field CSS class customization
|
|
16
|
+
- **Form Versioning**: Save and restore form versions
|
|
17
|
+
- **Tooltips**: Help text support for fields
|
|
18
|
+
- **Actions System**: onClick, onChange, onFocus, onBlur event handlers
|
|
19
|
+
- **Tab Index**: Keyboard navigation order for form fields
|
|
25
20
|
|
|
26
21
|
## Installation
|
|
27
22
|
|
|
@@ -29,700 +24,290 @@ A comprehensive, standalone drag-and-drop form builder React component. All UI c
|
|
|
29
24
|
npm install @enerjisaformlibrary/formbuilder-react
|
|
30
25
|
```
|
|
31
26
|
|
|
32
|
-
**Peer Dependencies:**
|
|
33
|
-
- `react`: ^18.0.0
|
|
34
|
-
- `react-dom`: ^18.0.0
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
27
|
## Quick Start
|
|
39
28
|
|
|
40
29
|
```tsx
|
|
41
|
-
import { FormBuilder
|
|
30
|
+
import { FormBuilder } from '@enerjisaformlibrary/formbuilder-react';
|
|
42
31
|
import '@enerjisaformlibrary/formbuilder-react/styles.css';
|
|
43
32
|
|
|
44
33
|
function App() {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
34
|
+
const handleSave = (formSchema) => {
|
|
35
|
+
console.log('Form saved:', formSchema);
|
|
36
|
+
// Save to your database
|
|
48
37
|
};
|
|
49
38
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
const jsonString = JSON.stringify(form, null, 2);
|
|
53
|
-
saveToDatabase(jsonString);
|
|
39
|
+
const handleChange = (formSchema) => {
|
|
40
|
+
console.log('Form changed:', formSchema);
|
|
54
41
|
};
|
|
55
42
|
|
|
56
43
|
return (
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
onSave={handleSave}
|
|
61
|
-
theme="light"
|
|
62
|
-
showToolbar={true}
|
|
63
|
-
showComponentLibrary={true}
|
|
64
|
-
showPropertiesPanel={true}
|
|
65
|
-
/>
|
|
66
|
-
</div>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Loading Existing Form
|
|
72
|
-
|
|
73
|
-
```tsx
|
|
74
|
-
import { FormBuilder } from '@enerjisaformlibrary/formbuilder-react';
|
|
75
|
-
import '@enerjisaformlibrary/formbuilder-react/styles.css';
|
|
76
|
-
|
|
77
|
-
function EditForm({ existingFormJson }) {
|
|
78
|
-
// Parse JSON string if needed
|
|
79
|
-
const initialForm = typeof existingFormJson === 'string'
|
|
80
|
-
? JSON.parse(existingFormJson)
|
|
81
|
-
: existingFormJson;
|
|
82
|
-
|
|
83
|
-
return (
|
|
84
|
-
<FormBuilder
|
|
85
|
-
initialForm={initialForm}
|
|
86
|
-
onSave={(form) => updateInDatabase(form)}
|
|
44
|
+
<FormBuilder
|
|
45
|
+
onSave={handleSave}
|
|
46
|
+
onChange={handleChange}
|
|
87
47
|
/>
|
|
88
48
|
);
|
|
89
49
|
}
|
|
90
50
|
```
|
|
91
51
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
## FormBuilder Props
|
|
95
|
-
|
|
96
|
-
| Prop | Type | Default | Description |
|
|
97
|
-
|------|------|---------|-------------|
|
|
98
|
-
| `initialForm` | `FormSchema` | `undefined` | Pre-existing form schema to load |
|
|
99
|
-
| `onSave` | `(form: FormSchema) => void` | `undefined` | Callback when save button clicked |
|
|
100
|
-
| `onChange` | `(form: FormSchema) => void` | `undefined` | Callback on every form change |
|
|
101
|
-
| `className` | `string` | `''` | Additional CSS class for container |
|
|
102
|
-
| `theme` | `'light' \| 'dark'` | `'light'` | Color theme |
|
|
103
|
-
| `showToolbar` | `boolean` | `true` | Show/hide top toolbar |
|
|
104
|
-
| `showComponentLibrary` | `boolean` | `true` | Show/hide left component panel |
|
|
105
|
-
| `showPropertiesPanel` | `boolean` | `true` | Show/hide right properties panel |
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## Exported Components
|
|
110
|
-
|
|
111
|
-
```tsx
|
|
112
|
-
// Main wrapper component (recommended)
|
|
113
|
-
import { FormBuilder } from '@enerjisaformlibrary/formbuilder-react';
|
|
114
|
-
|
|
115
|
-
// Individual components for custom layouts
|
|
116
|
-
import {
|
|
117
|
-
FormCanvas, // Central form editing canvas
|
|
118
|
-
PropertiesPanel, // Right-side property editor
|
|
119
|
-
ComponentLibrary, // Left-side component palette
|
|
120
|
-
Toolbar, // Top toolbar with actions
|
|
121
|
-
CanvasField, // Individual field renderer
|
|
122
|
-
DragOverlayContent, // Drag preview overlay
|
|
123
|
-
JsonViewerModal, // JSON preview modal
|
|
124
|
-
} from '@enerjisaformlibrary/formbuilder-react';
|
|
125
|
-
|
|
126
|
-
// Zustand store for state management
|
|
127
|
-
import { useFormStore } from '@enerjisaformlibrary/formbuilder-react';
|
|
128
|
-
|
|
129
|
-
// All TypeScript types and schemas
|
|
130
|
-
import type {
|
|
131
|
-
FormSchema,
|
|
132
|
-
FormField,
|
|
133
|
-
FormRow,
|
|
134
|
-
FormColumn,
|
|
135
|
-
Step,
|
|
136
|
-
FieldProps,
|
|
137
|
-
Validation,
|
|
138
|
-
ConditionalLogic,
|
|
139
|
-
CustomStyle,
|
|
140
|
-
SubmissionConfig,
|
|
141
|
-
} from '@enerjisaformlibrary/formbuilder-react';
|
|
142
|
-
```
|
|
52
|
+
## Props
|
|
143
53
|
|
|
144
|
-
|
|
54
|
+
| Prop | Type | Required | Description |
|
|
55
|
+
|------|------|----------|-------------|
|
|
56
|
+
| `initialSchema` | `FormSchema` | No | Initial form schema to load |
|
|
57
|
+
| `onSave` | `(schema: FormSchema) => void` | No | Called when user clicks Save button |
|
|
58
|
+
| `onChange` | `(schema: FormSchema) => void` | No | Called on every form change |
|
|
145
59
|
|
|
146
60
|
## Form Schema Structure
|
|
147
61
|
|
|
148
|
-
The form is represented as a hierarchical JSON structure:
|
|
149
|
-
|
|
150
62
|
```typescript
|
|
151
63
|
interface FormSchema {
|
|
152
|
-
id: string;
|
|
153
|
-
name: string;
|
|
154
|
-
description?: string;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
settings?: FormSettings;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
createdAt?: string; // ISO date string
|
|
163
|
-
updatedAt?: string; // ISO date string
|
|
64
|
+
id: string;
|
|
65
|
+
name: string;
|
|
66
|
+
description?: string;
|
|
67
|
+
isMultiStep: boolean;
|
|
68
|
+
currentVersion: number;
|
|
69
|
+
rows: FormRow[]; // For single-page forms
|
|
70
|
+
steps?: FormStep[]; // For multi-step forms
|
|
71
|
+
settings?: FormSettings;
|
|
72
|
+
submissionConfig?: SubmissionConfig;
|
|
73
|
+
versions?: FormVersion[];
|
|
164
74
|
}
|
|
165
75
|
|
|
166
76
|
interface FormRow {
|
|
167
77
|
id: string;
|
|
168
|
-
columns: FormColumn[];
|
|
169
|
-
conditionalLogic?: ConditionalLogic;
|
|
170
|
-
fieldSize?: 'compact' | 'normal' | 'comfortable';
|
|
171
|
-
spacing?: number; // 0-16
|
|
172
|
-
padding?: number; // 0-16
|
|
78
|
+
columns: FormColumn[];
|
|
79
|
+
conditionalLogic?: ConditionalLogic;
|
|
173
80
|
}
|
|
174
81
|
|
|
175
82
|
interface FormColumn {
|
|
176
83
|
id: string;
|
|
177
|
-
width: number;
|
|
178
|
-
|
|
179
|
-
|
|
84
|
+
width: number; // 1-12 (grid columns)
|
|
85
|
+
fields: FormField[];
|
|
86
|
+
responsiveWidth?: ResponsiveWidth;
|
|
180
87
|
}
|
|
181
88
|
|
|
182
89
|
interface FormField {
|
|
183
|
-
id: string;
|
|
184
|
-
type:
|
|
185
|
-
props: FieldProps;
|
|
186
|
-
validation?:
|
|
187
|
-
conditionalLogic?: ConditionalLogic;
|
|
188
|
-
customStyle?: CustomStyle;
|
|
189
|
-
localization?: Localization; // Multi-language support
|
|
190
|
-
computed?: ComputedField; // Calculated fields
|
|
191
|
-
responsiveWidth?: ResponsiveWidth; // Responsive breakpoints
|
|
192
|
-
events?: FieldEvents; // Event handlers
|
|
90
|
+
id: string;
|
|
91
|
+
type: FieldType;
|
|
92
|
+
props: FieldProps;
|
|
93
|
+
validation?: FieldValidation;
|
|
94
|
+
conditionalLogic?: ConditionalLogic;
|
|
95
|
+
customStyle?: CustomStyle;
|
|
193
96
|
}
|
|
194
97
|
```
|
|
195
98
|
|
|
196
|
-
---
|
|
197
|
-
|
|
198
99
|
## Field Types
|
|
199
100
|
|
|
200
|
-
### Basic
|
|
101
|
+
### Basic Fields
|
|
102
|
+
- `input` - Single line text input
|
|
103
|
+
- `textarea` - Multi-line text area
|
|
104
|
+
- `number` - Numeric input
|
|
105
|
+
- `email` - Email input with validation
|
|
106
|
+
- `password` - Password input
|
|
107
|
+
- `phone` - Phone number input
|
|
108
|
+
- `url` - URL input
|
|
201
109
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
| `number` | Numeric input | `min`, `max`, `step` |
|
|
207
|
-
| `email` | Email input with validation | `placeholder` |
|
|
208
|
-
| `password` | Password input (masked) | `placeholder` |
|
|
209
|
-
| `phone` | Phone number input | `placeholder` |
|
|
210
|
-
| `url` | URL input with validation | `placeholder` |
|
|
110
|
+
### Date & Time
|
|
111
|
+
- `date` - Date picker
|
|
112
|
+
- `time` - Time picker
|
|
113
|
+
- `daterange` - Date range picker
|
|
211
114
|
|
|
212
115
|
### Selection Fields
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
| `radio` | Radio button group | `options`, `optionsString` |
|
|
219
|
-
| `toggle` | On/off toggle switch | `defaultValue` |
|
|
220
|
-
| `multiselect` | Multi-select with tags | `options`, `multiSelectConfig` |
|
|
221
|
-
|
|
222
|
-
### Date & Time Fields
|
|
223
|
-
|
|
224
|
-
| Type | Description | Key Props |
|
|
225
|
-
|------|-------------|-----------|
|
|
226
|
-
| `date` | Date picker | `defaultValue` |
|
|
227
|
-
| `time` | Time picker | `timeConfig.format` |
|
|
228
|
-
| `daterange` | Date range picker | `dateRangeConfig` |
|
|
116
|
+
- `dropdown` - Single select dropdown
|
|
117
|
+
- `checkbox` - Checkbox field
|
|
118
|
+
- `radio` - Radio button group
|
|
119
|
+
- `toggle` - Toggle switch
|
|
120
|
+
- `multiselect` - Multi-select with tags
|
|
229
121
|
|
|
230
122
|
### Advanced Fields
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
| `image` | Image display | `imageUrl`, `imageWidth`, `imageHeight` |
|
|
256
|
-
| `button` | Action button | `buttonConfig` |
|
|
257
|
-
|
|
258
|
-
### Structure Elements
|
|
259
|
-
|
|
260
|
-
| Type | Description | Key Props |
|
|
261
|
-
|------|-------------|-----------|
|
|
262
|
-
| `container` | Grouping container | `containerConfig` |
|
|
263
|
-
| `row` | Grid row | - |
|
|
264
|
-
|
|
265
|
-
---
|
|
266
|
-
|
|
267
|
-
## Field Props (FieldProps)
|
|
123
|
+
- `file` - File upload with drag-drop
|
|
124
|
+
- `signature` - Signature pad with canvas drawing
|
|
125
|
+
- `rating` - Star rating (configurable max stars)
|
|
126
|
+
- `slider` - Range slider with min/max
|
|
127
|
+
- `color` - Color picker with hex input
|
|
128
|
+
- `richtext` - Rich text editor
|
|
129
|
+
- `autocomplete` - Autocomplete input
|
|
130
|
+
- `pattern` - Pattern format input (phone, credit card, etc.)
|
|
131
|
+
- `qrcode` - Static QR code display
|
|
132
|
+
|
|
133
|
+
### Static Elements
|
|
134
|
+
- `header` - Section header
|
|
135
|
+
- `label` - Static label text
|
|
136
|
+
- `paragraph` - Paragraph text
|
|
137
|
+
- `divider` - Horizontal divider
|
|
138
|
+
- `spacer` - Vertical spacer
|
|
139
|
+
- `alert` - Alert/notification box
|
|
140
|
+
- `image` - Image placeholder
|
|
141
|
+
- `button` - Submit/reset/custom action buttons
|
|
142
|
+
|
|
143
|
+
### Structure
|
|
144
|
+
- `container` - Grouping element with nested rows/columns
|
|
145
|
+
|
|
146
|
+
## Field Props
|
|
268
147
|
|
|
269
148
|
```typescript
|
|
270
149
|
interface FieldProps {
|
|
271
|
-
key: string;
|
|
272
|
-
label
|
|
273
|
-
placeholder?: string;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
helpText?: string; // Help text below field
|
|
277
|
-
tooltip?: string; // Tooltip on hover
|
|
150
|
+
key: string; // Unique field key for form data
|
|
151
|
+
label?: string; // Field label
|
|
152
|
+
placeholder?: string; // Placeholder text
|
|
153
|
+
tooltip?: string; // Help tooltip text
|
|
154
|
+
optionsString?: string; // Options for dropdown/radio/checkbox (one per line)
|
|
278
155
|
size?: 'small' | 'medium' | 'large';
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
hidden?: boolean; // Hide field
|
|
283
|
-
autoFocus?: boolean; // Auto-focus on mount
|
|
284
|
-
tabIndex?: number; // Tab order
|
|
285
|
-
htmlAttributes?: Record<string, string>; // Custom HTML attributes
|
|
156
|
+
autoFocus?: boolean;
|
|
157
|
+
tabIndex?: number; // Keyboard navigation order (positive = order, -1 = skip, 0 = natural order)
|
|
158
|
+
htmlAttributes?: Record<string, string>;
|
|
286
159
|
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
|
|
160
|
+
// Button specific
|
|
161
|
+
buttonConfig?: {
|
|
162
|
+
buttonType: 'submit' | 'reset' | 'button';
|
|
163
|
+
variant: 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive';
|
|
164
|
+
actionType?: 'submit' | 'reset' | 'navigate' | 'custom';
|
|
165
|
+
navigateUrl?: string;
|
|
166
|
+
customAction?: string;
|
|
167
|
+
};
|
|
290
168
|
|
|
291
|
-
//
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
timeConfig?: TimeConfig;
|
|
298
|
-
signatureConfig?: SignatureConfig;
|
|
299
|
-
multiSelectConfig?: MultiSelectConfig;
|
|
300
|
-
patternConfig?: PatternConfig;
|
|
301
|
-
buttonConfig?: ButtonConfig;
|
|
302
|
-
qrCodeConfig?: QRCodeConfig;
|
|
303
|
-
containerConfig?: ContainerConfig;
|
|
304
|
-
tableConfig?: TableConfig;
|
|
169
|
+
// Pattern format specific
|
|
170
|
+
patternConfig?: {
|
|
171
|
+
format: 'phone' | 'creditCard' | 'date' | 'ssn' | 'custom';
|
|
172
|
+
mask?: string;
|
|
173
|
+
customPattern?: string;
|
|
174
|
+
};
|
|
305
175
|
|
|
306
|
-
//
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
min?: number;
|
|
325
|
-
max?: number;
|
|
326
|
-
|
|
327
|
-
errorMessage?: string; // Custom error message
|
|
328
|
-
customValidation?: string; // Custom validation code
|
|
329
|
-
validationType?: 'email' | 'url' | 'phone' | 'creditCard' | 'custom';
|
|
330
|
-
autoValidate?: boolean; // Validate automatically
|
|
331
|
-
validateOnBlur?: boolean; // Validate on blur
|
|
332
|
-
validateOnChange?: boolean; // Validate on change
|
|
333
|
-
}
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
**Example:**
|
|
337
|
-
```json
|
|
338
|
-
{
|
|
339
|
-
"validation": {
|
|
340
|
-
"required": true,
|
|
341
|
-
"minLength": 3,
|
|
342
|
-
"maxLength": 100,
|
|
343
|
-
"pattern": "^[a-zA-Z]+$",
|
|
344
|
-
"errorMessage": "Only letters allowed, 3-100 characters"
|
|
345
|
-
}
|
|
176
|
+
// QR Code specific
|
|
177
|
+
qrCodeConfig?: {
|
|
178
|
+
value: string;
|
|
179
|
+
size: number;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Container specific
|
|
183
|
+
containerConfig?: {
|
|
184
|
+
rows: ContainerRow[];
|
|
185
|
+
gap?: number;
|
|
186
|
+
padding?: number;
|
|
187
|
+
borderStyle?: 'none' | 'solid' | 'dashed';
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// Rating specific
|
|
191
|
+
maxRating?: number;
|
|
192
|
+
|
|
193
|
+
// Slider specific
|
|
194
|
+
min?: number;
|
|
195
|
+
max?: number;
|
|
196
|
+
step?: number;
|
|
346
197
|
}
|
|
347
198
|
```
|
|
348
199
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
## Conditional Logic
|
|
352
|
-
|
|
353
|
-
Show, hide, enable, disable, or require fields based on other field values:
|
|
354
|
-
|
|
355
|
-
```typescript
|
|
356
|
-
interface ConditionalLogic {
|
|
357
|
-
enabled: boolean; // Enable/disable conditional logic
|
|
358
|
-
action: 'show' | 'hide' | 'enable' | 'disable' | 'require';
|
|
359
|
-
logicType: 'all' | 'any'; // Match all or any condition
|
|
360
|
-
conditions: Condition[];
|
|
361
|
-
}
|
|
200
|
+
## Tab Index Usage
|
|
362
201
|
|
|
363
|
-
|
|
364
|
-
fieldKey: string; // Key of field to check
|
|
365
|
-
operator: ConditionOperator; // Comparison operator
|
|
366
|
-
value?: any; // Value to compare against
|
|
367
|
-
}
|
|
202
|
+
The `tabIndex` property controls keyboard navigation order when users press Tab to move between form fields:
|
|
368
203
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
| 'greaterThan'
|
|
375
|
-
| 'lessThan'
|
|
376
|
-
| 'greaterThanOrEqual'
|
|
377
|
-
| 'lessThanOrEqual'
|
|
378
|
-
| 'isEmpty'
|
|
379
|
-
| 'isNotEmpty'
|
|
380
|
-
| 'startsWith'
|
|
381
|
-
| 'endsWith';
|
|
382
|
-
```
|
|
204
|
+
| Value | Behavior |
|
|
205
|
+
|-------|----------|
|
|
206
|
+
| Positive (1, 2, 3...) | Field is focused in order from lowest to highest tabIndex |
|
|
207
|
+
| 0 | Field follows natural DOM order |
|
|
208
|
+
| -1 | Field is skipped during Tab navigation |
|
|
383
209
|
|
|
384
|
-
**Example
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
"action": "show",
|
|
390
|
-
"logicType": "all",
|
|
391
|
-
"conditions": [
|
|
392
|
-
{
|
|
393
|
-
"fieldKey": "has_experience",
|
|
394
|
-
"operator": "equals",
|
|
395
|
-
"value": "yes"
|
|
396
|
-
}
|
|
397
|
-
]
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
```
|
|
210
|
+
**Example:** To create a custom tab order:
|
|
211
|
+
- First Name: tabIndex = 1
|
|
212
|
+
- Email: tabIndex = 2
|
|
213
|
+
- Last Name: tabIndex = 3
|
|
214
|
+
- Phone: tabIndex = 4
|
|
401
215
|
|
|
402
|
-
|
|
216
|
+
When user presses Tab, they'll move: First Name → Email → Last Name → Phone
|
|
403
217
|
|
|
404
|
-
|
|
218
|
+
**Important:** tabIndex only works in **Preview Mode**. In editor mode, fields don't receive focus in the same way.
|
|
405
219
|
|
|
406
|
-
|
|
220
|
+
## Validation
|
|
407
221
|
|
|
408
222
|
```typescript
|
|
409
|
-
interface
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
allowSkip?: boolean; // Allow skipping this step
|
|
418
|
-
};
|
|
223
|
+
interface FieldValidation {
|
|
224
|
+
required?: boolean;
|
|
225
|
+
minLength?: number;
|
|
226
|
+
maxLength?: number;
|
|
227
|
+
min?: number;
|
|
228
|
+
max?: number;
|
|
229
|
+
pattern?: string;
|
|
230
|
+
customMessage?: string;
|
|
419
231
|
}
|
|
420
232
|
```
|
|
421
233
|
|
|
422
|
-
|
|
423
|
-
```json
|
|
424
|
-
{
|
|
425
|
-
"id": "registration-form",
|
|
426
|
-
"name": "User Registration",
|
|
427
|
-
"isMultiStep": true,
|
|
428
|
-
"steps": [
|
|
429
|
-
{
|
|
430
|
-
"id": "step-1",
|
|
431
|
-
"title": "Personal Info",
|
|
432
|
-
"description": "Enter your personal details",
|
|
433
|
-
"rows": [...]
|
|
434
|
-
},
|
|
435
|
-
{
|
|
436
|
-
"id": "step-2",
|
|
437
|
-
"title": "Contact",
|
|
438
|
-
"description": "How can we reach you?",
|
|
439
|
-
"rows": [...]
|
|
440
|
-
},
|
|
441
|
-
{
|
|
442
|
-
"id": "step-3",
|
|
443
|
-
"title": "Preferences",
|
|
444
|
-
"validation": { "allowSkip": true },
|
|
445
|
-
"rows": [...]
|
|
446
|
-
}
|
|
447
|
-
],
|
|
448
|
-
"rows": []
|
|
449
|
-
}
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
---
|
|
453
|
-
|
|
454
|
-
## Container Fields
|
|
455
|
-
|
|
456
|
-
Containers allow nested layouts with their own grid system:
|
|
234
|
+
## Conditional Logic
|
|
457
235
|
|
|
458
236
|
```typescript
|
|
459
|
-
interface
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
border?: boolean; // Show border
|
|
465
|
-
flexWrap?: 'wrap' | 'nowrap';
|
|
466
|
-
justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';
|
|
467
|
-
alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline';
|
|
468
|
-
fieldSize?: 'compact' | 'normal' | 'comfortable';
|
|
469
|
-
rows?: ContainerRow[]; // Nested rows structure
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
interface ContainerRow {
|
|
473
|
-
id: string;
|
|
474
|
-
width: number; // 1-12 grid width
|
|
475
|
-
height?: number; // 40-500 pixels or auto
|
|
476
|
-
columns: ContainerColumn[];
|
|
237
|
+
interface ConditionalLogic {
|
|
238
|
+
enabled: boolean;
|
|
239
|
+
action: 'show' | 'hide' | 'enable' | 'disable' | 'require';
|
|
240
|
+
conditions: Condition[];
|
|
241
|
+
logicOperator: 'and' | 'or';
|
|
477
242
|
}
|
|
478
243
|
|
|
479
|
-
interface
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
244
|
+
interface Condition {
|
|
245
|
+
fieldKey: string;
|
|
246
|
+
operator: 'equals' | 'notEquals' | 'contains' | 'notContains' |
|
|
247
|
+
'greaterThan' | 'lessThan' | 'isEmpty' | 'isNotEmpty';
|
|
248
|
+
value: string;
|
|
484
249
|
}
|
|
485
250
|
```
|
|
486
251
|
|
|
487
|
-
---
|
|
488
|
-
|
|
489
252
|
## Custom Styling
|
|
490
253
|
|
|
491
|
-
Per-field CSS customization:
|
|
492
|
-
|
|
493
254
|
```typescript
|
|
494
255
|
interface CustomStyle {
|
|
495
|
-
|
|
256
|
+
containerClassName?: string; // CSS class for field container
|
|
496
257
|
labelClassName?: string; // CSS class for label
|
|
497
|
-
inputClassName?: string; // CSS class for input
|
|
498
|
-
|
|
499
|
-
css?: string; // Custom inline CSS
|
|
500
|
-
wrapperCss?: string; // Wrapper element CSS
|
|
501
|
-
deviceTarget?: 'any' | 'mobile' | 'tablet' | 'desktop';
|
|
502
|
-
width?: { value?: number; unit?: string };
|
|
503
|
-
height?: string;
|
|
504
|
-
marginTop?: string;
|
|
505
|
-
marginRight?: string;
|
|
506
|
-
marginBottom?: string;
|
|
507
|
-
marginLeft?: string;
|
|
508
|
-
color?: string;
|
|
509
|
-
backgroundColor?: string;
|
|
510
|
-
}
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
**Example:**
|
|
514
|
-
```json
|
|
515
|
-
{
|
|
516
|
-
"customStyle": {
|
|
517
|
-
"containerClassName": "my-field-wrapper",
|
|
518
|
-
"labelClassName": "text-lg font-bold",
|
|
519
|
-
"inputClassName": "border-2 border-blue-500",
|
|
520
|
-
"backgroundColor": "#f0f0f0"
|
|
521
|
-
}
|
|
258
|
+
inputClassName?: string; // CSS class for input element
|
|
259
|
+
css?: string; // Custom CSS (applied inline)
|
|
522
260
|
}
|
|
523
261
|
```
|
|
524
262
|
|
|
525
|
-
|
|
263
|
+
## Actions System
|
|
526
264
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
Define event handlers for field interactions:
|
|
265
|
+
Each field can have event handlers:
|
|
530
266
|
|
|
531
267
|
```typescript
|
|
532
|
-
interface
|
|
533
|
-
onClick?:
|
|
534
|
-
onChange?:
|
|
535
|
-
onFocus?:
|
|
536
|
-
onBlur?:
|
|
537
|
-
onMount?: EventAction[];
|
|
538
|
-
onUnmount?: EventAction[];
|
|
268
|
+
interface FieldActions {
|
|
269
|
+
onClick?: FieldAction;
|
|
270
|
+
onChange?: FieldAction;
|
|
271
|
+
onFocus?: FieldAction;
|
|
272
|
+
onBlur?: FieldAction;
|
|
539
273
|
}
|
|
540
274
|
|
|
541
|
-
interface
|
|
542
|
-
type: '
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
275
|
+
interface FieldAction {
|
|
276
|
+
type: 'showMessage' | 'hideField' | 'showField' | 'clearField' |
|
|
277
|
+
'setFieldValue' | 'focusField' | 'submitForm' | 'custom' | 'code';
|
|
278
|
+
args?: {
|
|
279
|
+
message?: string;
|
|
280
|
+
targetFieldKey?: string;
|
|
281
|
+
value?: string;
|
|
282
|
+
code?: string;
|
|
283
|
+
};
|
|
546
284
|
}
|
|
547
285
|
```
|
|
548
286
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
## Submission Config
|
|
552
|
-
|
|
553
|
-
Configure form submission behavior:
|
|
287
|
+
## Multi-Step Forms
|
|
554
288
|
|
|
555
289
|
```typescript
|
|
556
|
-
interface
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
template?: string;
|
|
566
|
-
};
|
|
567
|
-
apiEndpoint?: {
|
|
568
|
-
url?: string;
|
|
569
|
-
method?: 'POST' | 'PUT' | 'PATCH';
|
|
570
|
-
headers?: Record<string, string>;
|
|
290
|
+
interface FormStep {
|
|
291
|
+
id: string;
|
|
292
|
+
title: string;
|
|
293
|
+
description?: string;
|
|
294
|
+
order: number;
|
|
295
|
+
rows: FormRow[];
|
|
296
|
+
validation?: {
|
|
297
|
+
validateOnNext?: boolean;
|
|
298
|
+
allowSkip?: boolean;
|
|
571
299
|
};
|
|
572
|
-
redirectUrl?: string; // Redirect after submit
|
|
573
|
-
successMessage?: string; // Success message
|
|
574
|
-
errorMessage?: string; // Error message
|
|
575
|
-
}
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
---
|
|
579
|
-
|
|
580
|
-
## Zustand Store API
|
|
581
|
-
|
|
582
|
-
Access and manipulate form state programmatically:
|
|
583
|
-
|
|
584
|
-
```tsx
|
|
585
|
-
import { useFormStore } from '@enerjisaformlibrary/formbuilder-react';
|
|
586
|
-
|
|
587
|
-
function MyComponent() {
|
|
588
|
-
const {
|
|
589
|
-
// State
|
|
590
|
-
form, // Current FormSchema
|
|
591
|
-
selection, // Selected element
|
|
592
|
-
previewMode, // Preview mode state
|
|
593
|
-
formValues, // Form values (in preview)
|
|
594
|
-
|
|
595
|
-
// Form Actions
|
|
596
|
-
loadForm, // Load form schema
|
|
597
|
-
setFormName, // Set form name
|
|
598
|
-
resetForm, // Reset to empty form
|
|
599
|
-
|
|
600
|
-
// Row Actions
|
|
601
|
-
addRow, // Add empty row
|
|
602
|
-
addRowWithField, // Add row with a field
|
|
603
|
-
updateRow, // Update row properties
|
|
604
|
-
deleteRow, // Delete row
|
|
605
|
-
moveRow, // Move/reorder row
|
|
606
|
-
|
|
607
|
-
// Column Actions
|
|
608
|
-
addColumn, // Add column to row
|
|
609
|
-
updateColumn, // Update column properties
|
|
610
|
-
deleteColumn, // Delete column
|
|
611
|
-
|
|
612
|
-
// Field Actions
|
|
613
|
-
addField, // Add field to column
|
|
614
|
-
updateField, // Update field properties
|
|
615
|
-
deleteField, // Delete field
|
|
616
|
-
moveField, // Move field between columns
|
|
617
|
-
|
|
618
|
-
// Container Actions
|
|
619
|
-
addFieldToContainer, // Add field to container column
|
|
620
|
-
addFieldToContainerDirect, // Add field to container
|
|
621
|
-
|
|
622
|
-
// Selection
|
|
623
|
-
setSelection, // Set selected element
|
|
624
|
-
clearSelection, // Clear selection
|
|
625
|
-
|
|
626
|
-
// Preview
|
|
627
|
-
setPreviewMode, // Toggle preview mode
|
|
628
|
-
setFormValue, // Set field value in preview
|
|
629
|
-
|
|
630
|
-
// Multi-step
|
|
631
|
-
addStep, // Add wizard step
|
|
632
|
-
updateStep, // Update step
|
|
633
|
-
deleteStep, // Delete step
|
|
634
|
-
moveStep, // Reorder step
|
|
635
|
-
setCurrentStep, // Navigate to step
|
|
636
|
-
|
|
637
|
-
// Version Control
|
|
638
|
-
saveVersion, // Save form version
|
|
639
|
-
restoreVersion, // Restore previous version
|
|
640
|
-
|
|
641
|
-
// Undo/Redo
|
|
642
|
-
undo, // Undo last action
|
|
643
|
-
redo, // Redo undone action
|
|
644
|
-
canUndo, // Check if undo available
|
|
645
|
-
canRedo, // Check if redo available
|
|
646
|
-
|
|
647
|
-
} = useFormStore();
|
|
648
|
-
|
|
649
|
-
// Example: Get current form JSON
|
|
650
|
-
const formJson = JSON.stringify(form, null, 2);
|
|
651
|
-
|
|
652
|
-
// Example: Load existing form
|
|
653
|
-
useEffect(() => {
|
|
654
|
-
loadForm(existingFormData);
|
|
655
|
-
}, []);
|
|
656
300
|
}
|
|
657
301
|
```
|
|
658
302
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
## TypeScript Types
|
|
662
|
-
|
|
663
|
-
All types are exported for TypeScript usage:
|
|
664
|
-
|
|
665
|
-
```typescript
|
|
666
|
-
import type {
|
|
667
|
-
// Core Types
|
|
668
|
-
FormSchema,
|
|
669
|
-
FormField,
|
|
670
|
-
FormRow,
|
|
671
|
-
FormColumn,
|
|
672
|
-
Step,
|
|
673
|
-
|
|
674
|
-
// Field Configuration
|
|
675
|
-
FieldProps,
|
|
676
|
-
Validation,
|
|
677
|
-
ConditionalLogic,
|
|
678
|
-
Condition,
|
|
679
|
-
ConditionOperator,
|
|
680
|
-
CustomStyle,
|
|
681
|
-
ResponsiveWidth,
|
|
682
|
-
FieldEvents,
|
|
683
|
-
EventAction,
|
|
684
|
-
|
|
685
|
-
// Field-Specific Configs
|
|
686
|
-
FileUploadConfig,
|
|
687
|
-
RatingConfig,
|
|
688
|
-
AutocompleteConfig,
|
|
689
|
-
SignatureConfig,
|
|
690
|
-
PatternConfig,
|
|
691
|
-
ButtonConfig,
|
|
692
|
-
QRCodeConfig,
|
|
693
|
-
ContainerConfig,
|
|
694
|
-
TableConfig,
|
|
695
|
-
|
|
696
|
-
// Form Settings
|
|
697
|
-
FormSettings,
|
|
698
|
-
SubmissionConfig,
|
|
699
|
-
FormVersion,
|
|
700
|
-
Localization,
|
|
701
|
-
ComputedField,
|
|
702
|
-
|
|
703
|
-
// Selection
|
|
704
|
-
Selection,
|
|
705
|
-
SelectionType,
|
|
706
|
-
|
|
707
|
-
// Field Type Constants
|
|
708
|
-
FieldType,
|
|
709
|
-
StructureType,
|
|
710
|
-
StaticType,
|
|
711
|
-
} from '@enerjisaformlibrary/formbuilder-react';
|
|
712
|
-
```
|
|
713
|
-
|
|
714
|
-
---
|
|
715
|
-
|
|
716
|
-
## Complete JSON Example
|
|
717
|
-
|
|
718
|
-
Here's a complete form with various field types:
|
|
303
|
+
## Example: Complete Form Schema
|
|
719
304
|
|
|
720
305
|
```json
|
|
721
306
|
{
|
|
722
|
-
"id": "contact-form
|
|
307
|
+
"id": "contact-form",
|
|
723
308
|
"name": "Contact Form",
|
|
724
|
-
"description": "A sample contact form",
|
|
725
309
|
"isMultiStep": false,
|
|
310
|
+
"currentVersion": 1,
|
|
726
311
|
"rows": [
|
|
727
312
|
{
|
|
728
313
|
"id": "row-1",
|
|
@@ -735,9 +320,10 @@ Here's a complete form with various field types:
|
|
|
735
320
|
"id": "field-1",
|
|
736
321
|
"type": "input",
|
|
737
322
|
"props": {
|
|
738
|
-
"key": "
|
|
323
|
+
"key": "firstName",
|
|
739
324
|
"label": "First Name",
|
|
740
|
-
"placeholder": "Enter your first name"
|
|
325
|
+
"placeholder": "Enter your first name",
|
|
326
|
+
"tabIndex": 1
|
|
741
327
|
},
|
|
742
328
|
"validation": {
|
|
743
329
|
"required": true,
|
|
@@ -754,9 +340,10 @@ Here's a complete form with various field types:
|
|
|
754
340
|
"id": "field-2",
|
|
755
341
|
"type": "input",
|
|
756
342
|
"props": {
|
|
757
|
-
"key": "
|
|
343
|
+
"key": "lastName",
|
|
758
344
|
"label": "Last Name",
|
|
759
|
-
"placeholder": "Enter your last name"
|
|
345
|
+
"placeholder": "Enter your last name",
|
|
346
|
+
"tabIndex": 2
|
|
760
347
|
},
|
|
761
348
|
"validation": {
|
|
762
349
|
"required": true
|
|
@@ -779,11 +366,12 @@ Here's a complete form with various field types:
|
|
|
779
366
|
"props": {
|
|
780
367
|
"key": "email",
|
|
781
368
|
"label": "Email Address",
|
|
782
|
-
"placeholder": "you@example.com"
|
|
369
|
+
"placeholder": "you@example.com",
|
|
370
|
+
"tooltip": "We'll never share your email",
|
|
371
|
+
"tabIndex": 3
|
|
783
372
|
},
|
|
784
373
|
"validation": {
|
|
785
|
-
"required": true
|
|
786
|
-
"validationType": "email"
|
|
374
|
+
"required": true
|
|
787
375
|
}
|
|
788
376
|
}
|
|
789
377
|
]
|
|
@@ -801,17 +389,10 @@ Here's a complete form with various field types:
|
|
|
801
389
|
"id": "field-4",
|
|
802
390
|
"type": "dropdown",
|
|
803
391
|
"props": {
|
|
804
|
-
"key": "
|
|
805
|
-
"label": "
|
|
806
|
-
"
|
|
807
|
-
|
|
808
|
-
{ "label": "Technical Support", "value": "support" },
|
|
809
|
-
{ "label": "Sales", "value": "sales" },
|
|
810
|
-
{ "label": "Other", "value": "other" }
|
|
811
|
-
]
|
|
812
|
-
},
|
|
813
|
-
"validation": {
|
|
814
|
-
"required": true
|
|
392
|
+
"key": "country",
|
|
393
|
+
"label": "Country",
|
|
394
|
+
"optionsString": "USA\nCanada\nUK\nGermany\nFrance\nOther",
|
|
395
|
+
"tabIndex": 4
|
|
815
396
|
}
|
|
816
397
|
}
|
|
817
398
|
]
|
|
@@ -831,23 +412,13 @@ Here's a complete form with various field types:
|
|
|
831
412
|
"props": {
|
|
832
413
|
"key": "message",
|
|
833
414
|
"label": "Message",
|
|
834
|
-
"placeholder": "
|
|
415
|
+
"placeholder": "How can we help you?",
|
|
416
|
+
"tabIndex": 5
|
|
835
417
|
},
|
|
836
418
|
"validation": {
|
|
837
419
|
"required": true,
|
|
838
420
|
"minLength": 10,
|
|
839
|
-
"maxLength":
|
|
840
|
-
},
|
|
841
|
-
"conditionalLogic": {
|
|
842
|
-
"enabled": true,
|
|
843
|
-
"action": "show",
|
|
844
|
-
"logicType": "all",
|
|
845
|
-
"conditions": [
|
|
846
|
-
{
|
|
847
|
-
"fieldKey": "inquiry_type",
|
|
848
|
-
"operator": "isNotEmpty"
|
|
849
|
-
}
|
|
850
|
-
]
|
|
421
|
+
"maxLength": 500
|
|
851
422
|
}
|
|
852
423
|
}
|
|
853
424
|
]
|
|
@@ -863,36 +434,14 @@ Here's a complete form with various field types:
|
|
|
863
434
|
"fields": [
|
|
864
435
|
{
|
|
865
436
|
"id": "field-6",
|
|
866
|
-
"type": "checkbox",
|
|
867
|
-
"props": {
|
|
868
|
-
"key": "agree_terms",
|
|
869
|
-
"label": "I agree to the terms and conditions"
|
|
870
|
-
},
|
|
871
|
-
"validation": {
|
|
872
|
-
"required": true,
|
|
873
|
-
"errorMessage": "You must agree to continue"
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
]
|
|
877
|
-
}
|
|
878
|
-
]
|
|
879
|
-
},
|
|
880
|
-
{
|
|
881
|
-
"id": "row-6",
|
|
882
|
-
"columns": [
|
|
883
|
-
{
|
|
884
|
-
"id": "col-7",
|
|
885
|
-
"width": 12,
|
|
886
|
-
"fields": [
|
|
887
|
-
{
|
|
888
|
-
"id": "field-7",
|
|
889
437
|
"type": "button",
|
|
890
438
|
"props": {
|
|
891
|
-
"key": "
|
|
439
|
+
"key": "submit",
|
|
892
440
|
"label": "Submit",
|
|
893
441
|
"buttonConfig": {
|
|
894
|
-
"
|
|
895
|
-
"variant": "
|
|
442
|
+
"buttonType": "submit",
|
|
443
|
+
"variant": "primary",
|
|
444
|
+
"actionType": "submit"
|
|
896
445
|
}
|
|
897
446
|
}
|
|
898
447
|
}
|
|
@@ -900,192 +449,65 @@ Here's a complete form with various field types:
|
|
|
900
449
|
}
|
|
901
450
|
]
|
|
902
451
|
}
|
|
903
|
-
]
|
|
904
|
-
"submissionConfig": {
|
|
905
|
-
"enabled": true,
|
|
906
|
-
"webhookUrl": "https://api.example.com/forms/submit",
|
|
907
|
-
"webhookMethod": "POST",
|
|
908
|
-
"successMessage": "Thank you! Your message has been sent.",
|
|
909
|
-
"redirectUrl": "/thank-you"
|
|
910
|
-
},
|
|
911
|
-
"settings": {
|
|
912
|
-
"defaultLanguage": "en",
|
|
913
|
-
"layout": {
|
|
914
|
-
"labelPosition": "top",
|
|
915
|
-
"spacing": "normal"
|
|
916
|
-
}
|
|
917
|
-
}
|
|
452
|
+
]
|
|
918
453
|
}
|
|
919
454
|
```
|
|
920
455
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
## Extracting Form Values
|
|
456
|
+
## Integration with JetDesk
|
|
924
457
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
2. **Render fields** based on their `type`
|
|
929
|
-
3. **Collect values** using field `props.key` as the key
|
|
930
|
-
4. **Apply validation** from `validation` object
|
|
931
|
-
5. **Handle conditional logic** to show/hide fields
|
|
458
|
+
```tsx
|
|
459
|
+
import { FormBuilder } from '@enerjisaformlibrary/formbuilder-react';
|
|
460
|
+
import '@enerjisaformlibrary/formbuilder-react/styles.css';
|
|
932
461
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
{
|
|
936
|
-
"first_name": "John",
|
|
937
|
-
"last_name": "Doe",
|
|
938
|
-
"email": "john@example.com",
|
|
939
|
-
"inquiry_type": "support",
|
|
940
|
-
"message": "I need help with...",
|
|
941
|
-
"agree_terms": true
|
|
942
|
-
}
|
|
943
|
-
```
|
|
462
|
+
function FormEditor({ formId }) {
|
|
463
|
+
const [initialSchema, setInitialSchema] = useState(null);
|
|
944
464
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
for (const containerRow of field.props.containerConfig.rows) {
|
|
959
|
-
for (const containerCol of containerRow.columns) {
|
|
960
|
-
if (containerCol.fields) {
|
|
961
|
-
fields.push(...containerCol.fields);
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
}
|
|
465
|
+
useEffect(() => {
|
|
466
|
+
// Load existing form from database
|
|
467
|
+
fetch(`/api/forms/${formId}`)
|
|
468
|
+
.then(res => res.json())
|
|
469
|
+
.then(data => setInitialSchema(data.schema));
|
|
470
|
+
}, [formId]);
|
|
471
|
+
|
|
472
|
+
const handleSave = async (schema) => {
|
|
473
|
+
await fetch(`/api/forms/${formId}`, {
|
|
474
|
+
method: 'PUT',
|
|
475
|
+
headers: { 'Content-Type': 'application/json' },
|
|
476
|
+
body: JSON.stringify({ schema })
|
|
477
|
+
});
|
|
969
478
|
};
|
|
970
|
-
|
|
971
|
-
if (form.isMultiStep && form.steps) {
|
|
972
|
-
for (const step of form.steps) {
|
|
973
|
-
processRows(step.rows);
|
|
974
|
-
}
|
|
975
|
-
} else {
|
|
976
|
-
processRows(form.rows);
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
return fields;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
// Get field keys for form values
|
|
983
|
-
const fields = extractFieldsFromForm(formSchema);
|
|
984
|
-
const fieldKeys = fields
|
|
985
|
-
.filter(f => !['header', 'subheader', 'label', 'paragraph', 'divider', 'spacer', 'image', 'button'].includes(f.type))
|
|
986
|
-
.map(f => f.props.key);
|
|
987
|
-
```
|
|
988
|
-
|
|
989
|
-
---
|
|
990
479
|
|
|
991
|
-
|
|
480
|
+
if (!initialSchema) return <div>Loading...</div>;
|
|
992
481
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
desktop?: number; // 1-12 columns on desktop
|
|
1000
|
-
}
|
|
1001
|
-
```
|
|
1002
|
-
|
|
1003
|
-
**Example:**
|
|
1004
|
-
```json
|
|
1005
|
-
{
|
|
1006
|
-
"responsiveWidth": {
|
|
1007
|
-
"mobile": 12,
|
|
1008
|
-
"tablet": 6,
|
|
1009
|
-
"desktop": 4
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
```
|
|
1013
|
-
|
|
1014
|
-
---
|
|
1015
|
-
|
|
1016
|
-
## Version History
|
|
1017
|
-
|
|
1018
|
-
The form supports versioning:
|
|
1019
|
-
|
|
1020
|
-
```typescript
|
|
1021
|
-
interface FormVersion {
|
|
1022
|
-
id: string;
|
|
1023
|
-
version: number;
|
|
1024
|
-
createdAt: string; // ISO date
|
|
1025
|
-
createdBy?: string; // User identifier
|
|
1026
|
-
changelog?: string; // Description of changes
|
|
1027
|
-
snapshot: FormSchema; // Complete form state
|
|
482
|
+
return (
|
|
483
|
+
<FormBuilder
|
|
484
|
+
initialSchema={initialSchema}
|
|
485
|
+
onSave={handleSave}
|
|
486
|
+
/>
|
|
487
|
+
);
|
|
1028
488
|
}
|
|
1029
489
|
```
|
|
1030
490
|
|
|
1031
|
-
|
|
491
|
+
## Keyboard Shortcuts
|
|
1032
492
|
|
|
1033
|
-
|
|
493
|
+
- **Ctrl/Cmd + Z**: Undo
|
|
494
|
+
- **Ctrl/Cmd + Shift + Z**: Redo
|
|
1034
495
|
|
|
1035
|
-
|
|
496
|
+
## Browser Support
|
|
1036
497
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
placeholder?: string;
|
|
1042
|
-
helpText?: string;
|
|
1043
|
-
errorMessages?: Record<string, string>;
|
|
1044
|
-
}>;
|
|
1045
|
-
}
|
|
1046
|
-
```
|
|
498
|
+
- Chrome (latest)
|
|
499
|
+
- Firefox (latest)
|
|
500
|
+
- Safari (latest)
|
|
501
|
+
- Edge (latest)
|
|
1047
502
|
|
|
1048
|
-
|
|
1049
|
-
```json
|
|
1050
|
-
{
|
|
1051
|
-
"localization": {
|
|
1052
|
-
"translations": {
|
|
1053
|
-
"tr": {
|
|
1054
|
-
"label": "Ad",
|
|
1055
|
-
"placeholder": "Adınızı girin"
|
|
1056
|
-
},
|
|
1057
|
-
"de": {
|
|
1058
|
-
"label": "Vorname",
|
|
1059
|
-
"placeholder": "Geben Sie Ihren Vornamen ein"
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
```
|
|
1065
|
-
|
|
1066
|
-
---
|
|
503
|
+
## Changelog
|
|
1067
504
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
```typescript
|
|
1071
|
-
const SUPPORTED_LANGUAGES = [
|
|
1072
|
-
{ code: 'en', name: 'English' },
|
|
1073
|
-
{ code: 'tr', name: 'Turkish' },
|
|
1074
|
-
{ code: 'de', name: 'German' },
|
|
1075
|
-
{ code: 'fr', name: 'French' },
|
|
1076
|
-
{ code: 'es', name: 'Spanish' },
|
|
1077
|
-
{ code: 'pt', name: 'Portuguese' },
|
|
1078
|
-
{ code: 'it', name: 'Italian' },
|
|
1079
|
-
{ code: 'nl', name: 'Dutch' },
|
|
1080
|
-
{ code: 'ru', name: 'Russian' },
|
|
1081
|
-
{ code: 'zh', name: 'Chinese' },
|
|
1082
|
-
{ code: 'ja', name: 'Japanese' },
|
|
1083
|
-
{ code: 'ko', name: 'Korean' },
|
|
1084
|
-
{ code: 'ar', name: 'Arabic' },
|
|
1085
|
-
];
|
|
1086
|
-
```
|
|
505
|
+
### 1.0.22
|
|
506
|
+
- Fixed: tabIndex now properly applied to all interactive field types (dropdown, checkbox, radio, toggle, slider, autocomplete, daterange)
|
|
1087
507
|
|
|
1088
|
-
|
|
508
|
+
### 1.0.21
|
|
509
|
+
- Added: Comprehensive undo/redo functionality with 50-state history
|
|
510
|
+
- Each form modification creates a separate history entry
|
|
1089
511
|
|
|
1090
512
|
## License
|
|
1091
513
|
|