engmsilva commited on
Commit
e592b04
·
verified ·
1 Parent(s): 741d18a

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +5 -3
  2. index.html +1349 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Form Builder
3
- emoji: 🐢
4
  colorFrom: pink
5
  colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: form-builder
3
+ emoji: 🐳
4
  colorFrom: pink
5
  colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1349 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Advanced Form Builder with Layout</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .form-builder-container {
11
+ display: grid;
12
+ grid-template-columns: 250px 1fr 300px;
13
+ gap: 1rem;
14
+ height: calc(100vh - 80px);
15
+ }
16
+ .field-list {
17
+ background-color: #f8fafc;
18
+ border-right: 1px solid #e2e8f0;
19
+ overflow-y: auto;
20
+ }
21
+ .form-canvas {
22
+ background-color: #ffffff;
23
+ overflow-y: auto;
24
+ padding: 1rem;
25
+ min-height: 100%;
26
+ }
27
+ .field-settings {
28
+ background-color: #f8fafc;
29
+ border-left: 1px solid #e2e8f0;
30
+ overflow-y: auto;
31
+ }
32
+ .draggable-field {
33
+ cursor: grab;
34
+ transition: all 0.2s;
35
+ }
36
+ .draggable-field:active {
37
+ cursor: grabbing;
38
+ }
39
+ .form-field {
40
+ position: relative;
41
+ transition: all 0.2s;
42
+ }
43
+ .form-field:hover {
44
+ box-shadow: 0 0 0 2px #3b82f6;
45
+ }
46
+ .form-field.selected {
47
+ box-shadow: 0 0 0 2px #3b82f6;
48
+ background-color: #eff6ff;
49
+ }
50
+ .field-actions {
51
+ position: absolute;
52
+ right: 0;
53
+ top: 0;
54
+ display: none;
55
+ }
56
+ .form-field:hover .field-actions {
57
+ display: flex;
58
+ }
59
+ .form-field.selected .field-actions {
60
+ display: flex;
61
+ }
62
+ .empty-state {
63
+ border: 2px dashed #cbd5e1;
64
+ border-radius: 0.5rem;
65
+ padding: 2rem;
66
+ text-align: center;
67
+ color: #64748b;
68
+ }
69
+ #formPreviewModal {
70
+ transition: opacity 0.3s ease;
71
+ }
72
+ .form-preview {
73
+ max-width: 600px;
74
+ margin: 0 auto;
75
+ background: white;
76
+ border-radius: 0.5rem;
77
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
78
+ }
79
+ .layout-container {
80
+ border: 2px dashed #cbd5e1;
81
+ border-radius: 0.5rem;
82
+ padding: 1rem;
83
+ margin-bottom: 1rem;
84
+ background-color: #f8fafc;
85
+ }
86
+ .layout-row {
87
+ border: 2px dashed #a5b4fc;
88
+ border-radius: 0.5rem;
89
+ padding: 0.75rem;
90
+ margin-bottom: 0.5rem;
91
+ background-color: #eef2ff;
92
+ }
93
+ .layout-column {
94
+ border: 2px dashed #93c5fd;
95
+ border-radius: 0.5rem;
96
+ padding: 0.5rem;
97
+ margin-bottom: 0.25rem;
98
+ background-color: #eff6ff;
99
+ }
100
+ .layout-placeholder {
101
+ border: 2px dashed #d1d5db;
102
+ border-radius: 0.25rem;
103
+ padding: 1rem;
104
+ text-align: center;
105
+ color: #6b7280;
106
+ background-color: #f9fafb;
107
+ margin-bottom: 0.5rem;
108
+ }
109
+ .layout-actions {
110
+ position: absolute;
111
+ right: 0;
112
+ top: 0;
113
+ display: none;
114
+ }
115
+ .layout-container:hover .layout-actions,
116
+ .layout-row:hover .layout-actions,
117
+ .layout-column:hover .layout-actions {
118
+ display: flex;
119
+ }
120
+ .grid-resize-handle {
121
+ position: absolute;
122
+ right: 5px;
123
+ bottom: 5px;
124
+ width: 12px;
125
+ height: 12px;
126
+ cursor: nwse-resize;
127
+ opacity: 0;
128
+ transition: opacity 0.2s;
129
+ }
130
+ .layout-column:hover .grid-resize-handle {
131
+ opacity: 1;
132
+ }
133
+ .drop-zone {
134
+ min-height: 50px;
135
+ transition: background-color 0.2s;
136
+ }
137
+ .drop-zone.active {
138
+ background-color: #dbeafe;
139
+ }
140
+ </style>
141
+ </head>
142
+ <body class="bg-gray-50">
143
+ <header class="bg-white shadow-sm">
144
+ <div class="max-w-7xl mx-auto px-4 py-4 sm:px-6 lg:px-8 flex justify-between items-center">
145
+ <h1 class="text-2xl font-bold text-gray-900">Form Builder Pro</h1>
146
+ <div class="flex space-x-3">
147
+ <button id="previewBtn" class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
148
+ <i class="fas fa-eye mr-2"></i> Preview
149
+ </button>
150
+ <button id="saveBtn" class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
151
+ <i class="fas fa-save mr-2"></i> Save Form
152
+ </button>
153
+ </div>
154
+ </div>
155
+ </header>
156
+
157
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
158
+ <div class="form-builder-container">
159
+ <!-- Field Library -->
160
+ <div class="field-list p-4">
161
+ <h2 class="text-lg font-semibold mb-4 text-gray-800">Field Library</h2>
162
+ <div class="space-y-2">
163
+ <div class="mb-4">
164
+ <h3 class="text-sm font-medium text-gray-500 uppercase tracking-wider mb-2">Layout Elements</h3>
165
+ <div class="space-y-2">
166
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="container">
167
+ <div class="flex items-center">
168
+ <i class="fas fa-square-full text-indigo-500 mr-2"></i>
169
+ <span>Container</span>
170
+ </div>
171
+ </div>
172
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="row">
173
+ <div class="flex items-center">
174
+ <i class="fas fa-grip-lines text-indigo-500 mr-2"></i>
175
+ <span>Row</span>
176
+ </div>
177
+ </div>
178
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="column">
179
+ <div class="flex items-center">
180
+ <i class="fas fa-columns text-indigo-500 mr-2"></i>
181
+ <span>Column</span>
182
+ </div>
183
+ </div>
184
+ </div>
185
+ </div>
186
+
187
+ <div>
188
+ <h3 class="text-sm font-medium text-gray-500 uppercase tracking-wider mb-2">Form Fields</h3>
189
+ <div class="space-y-2">
190
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="text">
191
+ <div class="flex items-center">
192
+ <i class="fas fa-font text-blue-500 mr-2"></i>
193
+ <span>Text Input</span>
194
+ </div>
195
+ </div>
196
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="email">
197
+ <div class="flex items-center">
198
+ <i class="fas fa-envelope text-blue-500 mr-2"></i>
199
+ <span>Email Input</span>
200
+ </div>
201
+ </div>
202
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="number">
203
+ <div class="flex items-center">
204
+ <i class="fas fa-hashtag text-blue-500 mr-2"></i>
205
+ <span>Number Input</span>
206
+ </div>
207
+ </div>
208
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="textarea">
209
+ <div class="flex items-center">
210
+ <i class="fas fa-align-left text-blue-500 mr-2"></i>
211
+ <span>Text Area</span>
212
+ </div>
213
+ </div>
214
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="select">
215
+ <div class="flex items-center">
216
+ <i class="fas fa-list-ul text-blue-500 mr-2"></i>
217
+ <span>Dropdown</span>
218
+ </div>
219
+ </div>
220
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="checkbox">
221
+ <div class="flex items-center">
222
+ <i class="fas fa-check-square text-blue-500 mr-2"></i>
223
+ <span>Checkbox</span>
224
+ </div>
225
+ </div>
226
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="radio">
227
+ <div class="flex items-center">
228
+ <i class="fas fa-dot-circle text-blue-500 mr-2"></i>
229
+ <span>Radio Buttons</span>
230
+ </div>
231
+ </div>
232
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="date">
233
+ <div class="flex items-center">
234
+ <i class="fas fa-calendar-alt text-blue-500 mr-2"></i>
235
+ <span>Date Picker</span>
236
+ </div>
237
+ </div>
238
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="file">
239
+ <div class="flex items-center">
240
+ <i class="fas fa-file-upload text-blue-500 mr-2"></i>
241
+ <span>File Upload</span>
242
+ </div>
243
+ </div>
244
+ <div draggable="true" class="draggable-field p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="section">
245
+ <div class="flex items-center">
246
+ <i class="fas fa-square-full text-blue-500 mr-2"></i>
247
+ <span>Section Header</span>
248
+ </div>
249
+ </div>
250
+ </div>
251
+ </div>
252
+ </div>
253
+ </div>
254
+
255
+ <!-- Form Canvas -->
256
+ <div class="form-canvas" id="formCanvas">
257
+ <div class="mb-6">
258
+ <input type="text" id="formTitle" placeholder="Form Title" class="w-full text-2xl font-bold border-0 focus:ring-0 focus:border-blue-500 border-b-2 border-gray-200 pb-2 outline-none">
259
+ <input type="text" id="formDescription" placeholder="Form Description" class="w-full text-gray-500 border-0 focus:ring-0 focus:border-blue-500 border-b border-gray-200 pb-1 outline-none mt-1">
260
+ </div>
261
+
262
+ <div id="formFieldsContainer" class="drop-zone">
263
+ <div class="empty-state">
264
+ <i class="fas fa-mouse-pointer text-4xl mb-3 text-gray-400"></i>
265
+ <h3 class="font-medium text-gray-500">Drag and drop fields here</h3>
266
+ <p class="text-sm text-gray-400 mt-1">Start building your form by dragging fields from the left panel</p>
267
+ </div>
268
+ </div>
269
+ </div>
270
+
271
+ <!-- Field Settings -->
272
+ <div class="field-settings p-4">
273
+ <h2 class="text-lg font-semibold mb-4 text-gray-800">Field Settings</h2>
274
+ <div id="fieldSettings" class="text-gray-500 italic">
275
+ <p>Select a field to edit its properties</p>
276
+ </div>
277
+ </div>
278
+ </div>
279
+ </div>
280
+
281
+ <!-- Form Preview Modal -->
282
+ <div id="formPreviewModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
283
+ <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
284
+ <div class="fixed inset-0 transition-opacity" aria-hidden="true">
285
+ <div class="absolute inset-0 bg-gray-500 opacity-75"></div>
286
+ </div>
287
+ <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
288
+ <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-2xl sm:w-full">
289
+ <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
290
+ <div class="sm:flex sm:items-start">
291
+ <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
292
+ <h3 class="text-lg leading-6 font-medium text-gray-900 mb-4">Form Preview</h3>
293
+ <div class="form-preview p-6">
294
+ <h2 id="previewFormTitle" class="text-2xl font-bold mb-2">Form Title</h2>
295
+ <p id="previewFormDescription" class="text-gray-500 mb-6">Form description goes here</p>
296
+ <div id="previewFormFields"></div>
297
+ <div class="mt-6">
298
+ <button type="button" class="w-full px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
299
+ Submit Form
300
+ </button>
301
+ </div>
302
+ </div>
303
+ </div>
304
+ </div>
305
+ </div>
306
+ <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
307
+ <button type="button" id="closePreview" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
308
+ Close
309
+ </button>
310
+ </div>
311
+ </div>
312
+ </div>
313
+ </div>
314
+
315
+ <script>
316
+ document.addEventListener('DOMContentLoaded', function() {
317
+ // Form data structure
318
+ let formData = {
319
+ title: '',
320
+ description: '',
321
+ fields: []
322
+ };
323
+
324
+ let selectedField = null;
325
+ let nextFieldId = 1;
326
+ let isResizing = false;
327
+ let currentResizeColumn = null;
328
+ let startX, startWidth;
329
+ let draggedElement = null;
330
+
331
+ // Drag and drop functionality
332
+ const draggableFields = document.querySelectorAll('.draggable-field');
333
+ const formCanvas = document.getElementById('formCanvas');
334
+ const formFieldsContainer = document.getElementById('formFieldsContainer');
335
+ const fieldSettings = document.getElementById('fieldSettings');
336
+ const formTitle = document.getElementById('formTitle');
337
+ const formDescription = document.getElementById('formDescription');
338
+ const previewFormTitle = document.getElementById('previewFormTitle');
339
+ const previewFormDescription = document.getElementById('previewFormDescription');
340
+ const previewFormFields = document.getElementById('previewFormFields');
341
+ const previewBtn = document.getElementById('previewBtn');
342
+ const saveBtn = document.getElementById('saveBtn');
343
+ const formPreviewModal = document.getElementById('formPreviewModal');
344
+ const closePreview = document.getElementById('closePreview');
345
+
346
+ // Set up drag events for all draggable fields
347
+ draggableFields.forEach(field => {
348
+ field.addEventListener('dragstart', function(e) {
349
+ e.dataTransfer.setData('text/plain', field.dataset.type);
350
+ draggedElement = field;
351
+ e.dataTransfer.effectAllowed = 'copy';
352
+ });
353
+ });
354
+
355
+ // Set up drop zones for different layout elements
356
+ function setupDropZones() {
357
+ // Main container drop zone
358
+ formFieldsContainer.addEventListener('dragover', function(e) {
359
+ e.preventDefault();
360
+ e.dataTransfer.dropEffect = 'copy';
361
+ this.classList.add('active');
362
+ });
363
+
364
+ formFieldsContainer.addEventListener('dragleave', function() {
365
+ this.classList.remove('active');
366
+ });
367
+
368
+ formFieldsContainer.addEventListener('drop', function(e) {
369
+ e.preventDefault();
370
+ this.classList.remove('active');
371
+
372
+ const fieldType = e.dataTransfer.getData('text/plain');
373
+ addFieldToForm(fieldType, this);
374
+ });
375
+
376
+ // Container drop zones (can accept rows or fields)
377
+ document.querySelectorAll('.layout-container').forEach(container => {
378
+ container.addEventListener('dragover', function(e) {
379
+ e.preventDefault();
380
+ e.dataTransfer.dropEffect = 'copy';
381
+ this.classList.add('active');
382
+ });
383
+
384
+ container.addEventListener('dragleave', function() {
385
+ this.classList.remove('active');
386
+ });
387
+
388
+ container.addEventListener('drop', function(e) {
389
+ e.preventDefault();
390
+ this.classList.remove('active');
391
+
392
+ const fieldType = e.dataTransfer.getData('text/plain');
393
+ // Containers can accept rows or fields directly
394
+ if (fieldType === 'row' || fieldType === 'container' ||
395
+ ['text', 'email', 'number', 'textarea', 'select', 'checkbox',
396
+ 'radio', 'date', 'file', 'section'].includes(fieldType)) {
397
+ addFieldToForm(fieldType, this);
398
+ }
399
+ });
400
+ });
401
+
402
+ // Row drop zones (can accept columns)
403
+ document.querySelectorAll('.layout-row').forEach(row => {
404
+ row.addEventListener('dragover', function(e) {
405
+ e.preventDefault();
406
+ e.dataTransfer.dropEffect = 'copy';
407
+ this.classList.add('active');
408
+ });
409
+
410
+ row.addEventListener('dragleave', function() {
411
+ this.classList.remove('active');
412
+ });
413
+
414
+ row.addEventListener('drop', function(e) {
415
+ e.preventDefault();
416
+ this.classList.remove('active');
417
+
418
+ const fieldType = e.dataTransfer.getData('text/plain');
419
+ // Rows can only accept columns
420
+ if (fieldType === 'column') {
421
+ addFieldToForm(fieldType, this);
422
+ }
423
+ });
424
+ });
425
+
426
+ // Column drop zones (can accept fields)
427
+ document.querySelectorAll('.layout-column').forEach(column => {
428
+ column.addEventListener('dragover', function(e) {
429
+ e.preventDefault();
430
+ e.dataTransfer.dropEffect = 'copy';
431
+ this.classList.add('active');
432
+ });
433
+
434
+ column.addEventListener('dragleave', function() {
435
+ this.classList.remove('active');
436
+ });
437
+
438
+ column.addEventListener('drop', function(e) {
439
+ e.preventDefault();
440
+ this.classList.remove('active');
441
+
442
+ const fieldType = e.dataTransfer.getData('text/plain');
443
+ // Columns can accept fields but not layout elements
444
+ if (['text', 'email', 'number', 'textarea', 'select', 'checkbox',
445
+ 'radio', 'date', 'file', 'section'].includes(fieldType)) {
446
+ addFieldToForm(fieldType, this);
447
+ }
448
+ });
449
+ });
450
+ }
451
+
452
+ // Add a new field to the form at a specific drop zone
453
+ function addFieldToForm(type, dropZone) {
454
+ // Remove empty state if it exists
455
+ const emptyState = dropZone.querySelector('.empty-state');
456
+ if (emptyState) {
457
+ emptyState.remove();
458
+ }
459
+
460
+ const fieldId = `field-${nextFieldId++}`;
461
+ let fieldHtml = '';
462
+ let defaultSettings = {};
463
+
464
+ switch(type) {
465
+ case 'container':
466
+ fieldHtml = `
467
+ <div class="form-field layout-container mb-4 relative drop-zone" data-id="${fieldId}" data-type="container">
468
+ <div class="layout-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
469
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
470
+ <i class="fas fa-pencil-alt text-xs"></i>
471
+ </button>
472
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
473
+ <i class="fas fa-trash-alt text-xs"></i>
474
+ </button>
475
+ </div>
476
+ <div class="layout-placeholder">
477
+ <i class="fas fa-square-full text-indigo-300 text-xl mb-2"></i>
478
+ <p class="text-sm">Drop rows or fields here</p>
479
+ </div>
480
+ </div>
481
+ `;
482
+ defaultSettings = {
483
+ padding: '1rem',
484
+ margin: '0 0 1rem 0'
485
+ };
486
+ break;
487
+
488
+ case 'row':
489
+ fieldHtml = `
490
+ <div class="form-field layout-row mb-2 relative drop-zone" data-id="${fieldId}" data-type="row">
491
+ <div class="layout-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
492
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
493
+ <i class="fas fa-pencil-alt text-xs"></i>
494
+ </button>
495
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
496
+ <i class="fas fa-trash-alt text-xs"></i>
497
+ </button>
498
+ </div>
499
+ <div class="layout-placeholder">
500
+ <i class="fas fa-grip-lines text-indigo-300 text-xl mb-2"></i>
501
+ <p class="text-sm">Drop columns here</p>
502
+ </div>
503
+ </div>
504
+ `;
505
+ defaultSettings = {
506
+ columns: 1,
507
+ gap: '0.5rem'
508
+ };
509
+ break;
510
+
511
+ case 'column':
512
+ fieldHtml = `
513
+ <div class="form-field layout-column relative drop-zone" data-id="${fieldId}" data-type="column">
514
+ <div class="layout-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
515
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
516
+ <i class="fas fa-pencil-alt text-xs"></i>
517
+ </button>
518
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
519
+ <i class="fas fa-trash-alt text-xs"></i>
520
+ </button>
521
+ </div>
522
+ <div class="grid-resize-handle">
523
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-4 h-4 text-gray-400">
524
+ <path fill-rule="evenodd" d="M12 1.586l-4 4v3.586l4-4V1.586zm4 4l-4 4h3.586l4-4V5.586zm-4 9.414l4-4v3.586l-4 4v-3.586zm-4-4l4-4H8.414l-4 4v3.586z" clip-rule="evenodd" />
525
+ </svg>
526
+ </div>
527
+ <div class="layout-placeholder">
528
+ <i class="fas fa-columns text-indigo-300 text-xl mb-2"></i>
529
+ <p class="text-sm">Drop fields here</p>
530
+ </div>
531
+ </div>
532
+ `;
533
+ defaultSettings = {
534
+ width: '100%',
535
+ minWidth: '200px'
536
+ };
537
+ break;
538
+
539
+ case 'text':
540
+ fieldHtml = `
541
+ <div class="form-field mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${fieldId}" data-type="text">
542
+ <div class="field-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
543
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
544
+ <i class="fas fa-pencil-alt text-xs"></i>
545
+ </button>
546
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
547
+ <i class="fas fa-trash-alt text-xs"></i>
548
+ </button>
549
+ </div>
550
+ <label class="block text-sm font-medium text-gray-700 mb-1">Text Field</label>
551
+ <input type="text" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" placeholder="Enter text">
552
+ </div>
553
+ `;
554
+ defaultSettings = {
555
+ label: 'Text Field',
556
+ placeholder: 'Enter text',
557
+ required: false
558
+ };
559
+ break;
560
+
561
+ case 'email':
562
+ fieldHtml = `
563
+ <div class="form-field mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative, data-id="${fieldId}" data-type="email">
564
+ <div class="field-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
565
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
566
+ <i class="fas fa-pencil-alt text-xs"></i>
567
+ </button>
568
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
569
+ <i class="fas fa-trash-alt text-xs"></i>
570
+ </button>
571
+ </div>
572
+ <label class="block text-sm font-medium text-gray-700 mb-1">Email Field</label>
573
+ <input type="email" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" placeholder="Enter email">
574
+ </div>
575
+ `;
576
+ defaultSettings = {
577
+ label: 'Email Field',
578
+ placeholder: 'Enter email',
579
+ required: false
580
+ };
581
+ break;
582
+
583
+ case 'number':
584
+ fieldHtml = `
585
+ <div class="form-field mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${fieldId}" data-type="number">
586
+ <div class="field-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
587
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
588
+ <i class="fas fa-pencil-alt text-xs"></i>
589
+ </button>
590
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
591
+ <i class="fas fa-trash-alt text-xs"></i>
592
+ </button>
593
+ </div>
594
+ <label class="block text-sm font-medium text-gray-700 mb-1">Number Field</label>
595
+ <input type="number" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" placeholder="Enter number">
596
+ </div>
597
+ `;
598
+ defaultSettings = {
599
+ label: 'Number Field',
600
+ placeholder: 'Enter number',
601
+ required: false
602
+ };
603
+ break;
604
+
605
+ case 'textarea':
606
+ fieldHtml = `
607
+ <div class="form-field mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${fieldId}" data-type="textarea">
608
+ <div class="field-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
609
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
610
+ <i class="fas fa-pencil-alt text-xs"></i>
611
+ </button>
612
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
613
+ <i class="fas fa-trash-alt text-xs"></i>
614
+ </button>
615
+ </div>
616
+ <label class="block text-sm font-medium text-gray-700 mb-1">Text Area</label>
617
+ <textarea rows="3" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" placeholder="Enter text"></textarea>
618
+ </div>
619
+ `;
620
+ defaultSettings = {
621
+ label: 'Text Area',
622
+ placeholder: 'Enter text',
623
+ required: false,
624
+ rows: 3
625
+ };
626
+ break;
627
+
628
+ case 'select':
629
+ fieldHtml = `
630
+ <div class="form-field mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${fieldId}" data-type="select">
631
+ <div class="field-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
632
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
633
+ <i class="fas fa-pencil-alt text-xs"></i>
634
+ </button>
635
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
636
+ <i class="fas fa-trash-alt text-xs"></i>
637
+ </button>
638
+ </div>
639
+ <label class="block text-sm font-medium text-gray-700 mb-1">Dropdown</label>
640
+ <select class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md">
641
+ <option>Option 1</option>
642
+ <option>Option 2</option>
643
+ <option>Option 3</option>
644
+ </select>
645
+ </div>
646
+ `;
647
+ defaultSettings = {
648
+ label: 'Dropdown',
649
+ options: ['Option 1', 'Option 2', 'Option 3'],
650
+ required: false
651
+ };
652
+ break;
653
+
654
+ case 'checkbox':
655
+ fieldHtml = `
656
+ <div class="form-field mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${fieldId}" data-type="checkbox">
657
+ <div class="field-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
658
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
659
+ <i class="fas fa-pencil-alt text-xs"></i>
660
+ </button>
661
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
662
+ <i class="fas fa-trash-alt text-xs"></i>
663
+ </button>
664
+ </div>
665
+ <div class="flex items-start">
666
+ <div class="flex items-center h-5">
667
+ <input type="checkbox" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded">
668
+ </div>
669
+ <div class="ml-3 text-sm">
670
+ <label class="font-medium text-gray-700">Checkbox</label>
671
+ <p class="text-gray-500">Checkbox description</p>
672
+ </div>
673
+ </div>
674
+ </div>
675
+ `;
676
+ defaultSettings = {
677
+ label: 'Checkbox',
678
+ description: 'Checkbox description',
679
+ required: false
680
+ };
681
+ break;
682
+
683
+ case 'radio':
684
+ fieldHtml = `
685
+ <div class="form-field mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${fieldId}" data-type="radio">
686
+ <div class="field-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
687
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
688
+ <i class="fas fa-pencil-alt text-xs"></i>
689
+ </button>
690
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
691
+ <i class="fas fa-trash-alt text-xs"></i>
692
+ </button>
693
+ </div>
694
+ <label class="block text-sm font-medium text-gray-700 mb-1">Radio Buttons</label>
695
+ <div class="mt-2 space-y-2">
696
+ <div class="flex items-center">
697
+ <input type="radio" name="radio-group" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300">
698
+ <label class="ml-3 block text-sm font-medium text-gray-700">Option 1</label>
699
+ </div>
700
+ <div class="flex items-center">
701
+ <input type="radio" name="radio-group" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300">
702
+ <label class="ml-3 block text-sm font-medium text-gray-700">Option 2</label>
703
+ </div>
704
+ <div class="flex items-center">
705
+ <input type="radio, name="radio-group" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300">
706
+ <label class="ml-3 block text-sm font-medium text-gray-700">Option 3</label>
707
+ </div>
708
+ </div>
709
+ </div>
710
+ `;
711
+ defaultSettings = {
712
+ label: 'Radio Buttons',
713
+ options: ['Option 1', 'Option 2', 'Option 3'],
714
+ required: false
715
+ };
716
+ break;
717
+
718
+ case 'date':
719
+ fieldHtml = `
720
+ <div class="form-field mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${fieldId}" data-type="date">
721
+ <div class="field-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
722
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
723
+ <i class="fas fa-pencil-alt text-xs"></i>
724
+ </button>
725
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
726
+ <i class="fas fa-trash-alt text-xs"></i>
727
+ </button>
728
+ </div>
729
+ <label class="block text-sm font-medium text-gray-700 mb-1">Date Picker</label>
730
+ <input type="date" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md">
731
+ </div>
732
+ `;
733
+ defaultSettings = {
734
+ label: 'Date Picker',
735
+ required: false
736
+ };
737
+ break;
738
+
739
+ case 'file':
740
+ fieldHtml = `
741
+ <div class="form-field mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${fieldId}" data-type="file">
742
+ <div class="field-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
743
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
744
+ <i class="fas fa-pencil-alt text-xs"></i>
745
+ </button>
746
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
747
+ <i class="fas fa-trash-alt text-xs"></i>
748
+ </button>
749
+ </div>
750
+ <label class="block text-sm font-medium text-gray-700 mb-1">File Upload</label>
751
+ <div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
752
+ <div class="space-y-1 text-center">
753
+ <svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48" aria-hidden="true">
754
+ <path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
755
+ </svg>
756
+ <div class="flex text-sm text-gray-600">
757
+ <label class="relative cursor-pointer bg-white rounded-md font-medium text-blue-600 hover:text-blue-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-blue-500">
758
+ <span>Upload a file</span>
759
+ <input type="file" class="sr-only">
760
+ </label>
761
+ <p class="pl-1">or drag and drop</p>
762
+ </div>
763
+ <p class="text-xs text-gray-500">PNG, JPG, GIF up to 10MB</p>
764
+ </div>
765
+ </div>
766
+ </div>
767
+ `;
768
+ defaultSettings = {
769
+ label: 'File Upload',
770
+ required: false,
771
+ fileTypes: 'PNG, JPG, GIF',
772
+ maxSize: '10MB'
773
+ };
774
+ break;
775
+
776
+ case 'section':
777
+ fieldHtml = `
778
+ <div class="form-field mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${fieldId}" data-type="section">
779
+ <div class="field-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
780
+ <button class="edit-field p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
781
+ <i class="fas fa-pencil-alt text-xs"></i>
782
+ </button>
783
+ <button class="delete-field p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
784
+ <i class="fas fa-trash-alt text-xs"></i>
785
+ </button>
786
+ </div>
787
+ <h3 class="text-lg font-medium text-gray-900">Section Title</h3>
788
+ <p class="mt-1 text-sm text-gray-500">Section description goes here</p>
789
+ </div>
790
+ `;
791
+ defaultSettings = {
792
+ title: 'Section Title',
793
+ description: 'Section description goes here'
794
+ };
795
+ break;
796
+ }
797
+
798
+ // Create a temporary element to parse the HTML
799
+ const tempDiv = document.createElement('div');
800
+ tempDiv.innerHTML = fieldHtml.trim();
801
+ const newField = tempDiv.firstChild;
802
+
803
+ // Add to form
804
+ if (dropZone === formFieldsContainer) {
805
+ // Adding to main container
806
+ dropZone.appendChild(newField);
807
+ } else {
808
+ // Adding to a layout element (container, row, or column)
809
+ const placeholder = dropZone.querySelector('.layout-placeholder');
810
+ if (placeholder) {
811
+ dropZone.insertBefore(newField, placeholder);
812
+ } else {
813
+ dropZone.appendChild(newField);
814
+ }
815
+ }
816
+
817
+ // Add to form data
818
+ formData.fields.push({
819
+ id: fieldId,
820
+ type: type,
821
+ settings: defaultSettings,
822
+ parentId: dropZone !== formFieldsContainer ? dropZone.dataset.id : null
823
+ });
824
+
825
+ // Set up field interactions
826
+ setupFieldInteractions(newField);
827
+
828
+ // Set up drop zones for the new element if it's a layout element
829
+ if (['container', 'row', 'column'].includes(type)) {
830
+ setupDropZones();
831
+ }
832
+
833
+ // If it's a column, set up resize handle
834
+ if (type === 'column') {
835
+ setupColumnResize(newField);
836
+ }
837
+
838
+ // Select the new field
839
+ selectField(newField);
840
+ }
841
+
842
+ // Set up column resize functionality
843
+ function setupColumnResize(columnElement) {
844
+ const resizeHandle = columnElement.querySelector('.grid-resize-handle');
845
+
846
+ resizeHandle.addEventListener('mousedown', function(e) {
847
+ e.preventDefault();
848
+ isResizing = true;
849
+ currentResizeColumn = columnElement;
850
+ startX = e.clientX;
851
+ startWidth = parseInt(document.defaultView.getComputedStyle(columnElement).width, 10);
852
+
853
+ document.documentElement.style.cursor = 'nwse-resize';
854
+ document.addEventListener('mousemove', handleResize);
855
+ document.addEventListener('mouseup', stopResize);
856
+ });
857
+
858
+ function handleResize(e) {
859
+ if (!isResizing) return;
860
+
861
+ const width = startWidth + e.clientX - startX;
862
+ currentResizeColumn.style.width = `${width}px`;
863
+ currentResizeColumn.style.minWidth = `${width}px`;
864
+
865
+ // Update form data
866
+ const fieldId = currentResizeColumn.dataset.id;
867
+ const fieldData = formData.fields.find(field => field.id === fieldId);
868
+ if (fieldData) {
869
+ fieldData.settings.width = `${width}px`;
870
+ fieldData.settings.minWidth = `${width}px`;
871
+ }
872
+ }
873
+
874
+ function stopResize() {
875
+ isResizing = false;
876
+ currentResizeColumn = null;
877
+ document.documentElement.style.cursor = '';
878
+ document.removeEventListener('mousemove', handleResize);
879
+ document.removeEventListener('mouseup', stopResize);
880
+ }
881
+ }
882
+
883
+ // Set up field interactions (click, delete, etc.)
884
+ function setupFieldInteractions(fieldElement) {
885
+ // Select field on click
886
+ fieldElement.addEventListener('click', function(e) {
887
+ if (!e.target.closest('.field-actions') && !e.target.closest('.layout-actions')) {
888
+ selectField(fieldElement);
889
+ }
890
+ });
891
+
892
+ // Delete field
893
+ const deleteBtn = fieldElement.querySelector('.delete-field');
894
+ if (deleteBtn) {
895
+ deleteBtn.addEventListener('click', function() {
896
+ const fieldId = fieldElement.dataset.id;
897
+
898
+ // Remove from DOM
899
+ fieldElement.remove();
900
+
901
+ // Remove from form data
902
+ formData.fields = formData.fields.filter(field => field.id !== fieldId);
903
+
904
+ // Clear selection
905
+ selectedField = null;
906
+ renderFieldSettings(null);
907
+
908
+ // Show empty state if no fields left in container
909
+ const containers = document.querySelectorAll('.layout-container, .layout-row, .layout-column');
910
+ containers.forEach(container => {
911
+ if (container.children.length === 1 && container.querySelector('.layout-placeholder')) {
912
+ container.innerHTML = `
913
+ <div class="layout-placeholder">
914
+ <i class="fas fa-square-full text-indigo-300 text-xl mb-2"></i>
915
+ <p class="text-sm">Drop ${container.classList.contains('layout-container') ? 'rows or fields' :
916
+ container.classList.contains('layout-row') ? 'columns' : 'fields'} here</p>
917
+ </div>
918
+ `;
919
+ }
920
+ });
921
+
922
+ // Show empty state if no fields left in main container
923
+ if (formFieldsContainer.children.length === 0) {
924
+ formFieldsContainer.innerHTML = `
925
+ <div class="empty-state">
926
+ <i class="fas fa-mouse-pointer text-4xl mb-3 text-gray-400"></i>
927
+ <h3 class="font-medium text-gray-500">Drag and drop fields here</h3>
928
+ <p class="text-sm text-gray-400 mt-1">Start building your form by dragging fields from the left panel</p>
929
+ </div>
930
+ `;
931
+ }
932
+ });
933
+ }
934
+
935
+ // Edit field (same as select for now)
936
+ const editBtn = fieldElement.querySelector('.edit-field');
937
+ if (editBtn) {
938
+ editBtn.addEventListener('click', function() {
939
+ selectField(fieldElement);
940
+ });
941
+ }
942
+ }
943
+
944
+ // Select a field and show its settings
945
+ function selectField(fieldElement) {
946
+ // Deselect previously selected field
947
+ if (selectedField) {
948
+ selectedField.classList.remove('selected');
949
+ }
950
+
951
+ // Select new field
952
+ selectedField = fieldElement;
953
+ if (selectedField) {
954
+ selectedField.classList.add('selected');
955
+
956
+ // Scroll to show the selected field
957
+ selectedField.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
958
+
959
+ // Find the field in form data
960
+ const fieldId = selectedField.dataset.id;
961
+ const fieldData = formData.fields.find(field => field.id === fieldId);
962
+
963
+ // Render field settings
964
+ renderFieldSettings(fieldData);
965
+ } else {
966
+ renderFieldSettings(null);
967
+ }
968
+ }
969
+
970
+ // Render field settings panel
971
+ function renderFieldSettings(fieldData) {
972
+ if (!fieldData) {
973
+ fieldSettings.innerHTML = '<p class="text-gray-500 italic">Select a field to edit its properties</p>';
974
+ return;
975
+ }
976
+
977
+ let settingsHtml = '';
978
+
979
+ switch(fieldData.type) {
980
+ case 'container':
981
+ settingsHtml = `
982
+ <div class="space-y-4">
983
+ <div>
984
+ <label class="block text-sm font-medium text-gray-700 mb-1">Padding</label>
985
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="padding" value="${fieldData.settings.padding}">
986
+ </div>
987
+ <div>
988
+ <label class="block text-sm font-medium text-gray-700 mb-1">Margin</label>
989
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="margin" value="${fieldData.settings.margin}">
990
+ </div>
991
+ </div>
992
+ `;
993
+ break;
994
+
995
+ case 'row':
996
+ settingsHtml = `
997
+ <div class="space-y-4">
998
+ <div>
999
+ <label class="block text-sm font-medium text-gray-700 mb-1">Number of Columns</label>
1000
+ <select class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="columns">
1001
+ <option value="1" ${fieldData.settings.columns === 1 ? 'selected' : ''}>1 Column</option>
1002
+ <option value="2" ${fieldData.settings.columns === 2 ? 'selected' : ''}>2 Columns</option>
1003
+ <option value="3" ${fieldData.settings.columns === 3 ? 'selected' : ''}>3 Columns</option>
1004
+ <option value="4" ${fieldData.settings.columns === 4 ? 'selected' : ''}>4 Columns</option>
1005
+ </select>
1006
+ </div>
1007
+ <div>
1008
+ <label class="block text-sm font-medium text-gray-700 mb-1">Gap Between Columns</label>
1009
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="gap" value="${fieldData.settings.gap}">
1010
+ </div>
1011
+ </div>
1012
+ `;
1013
+ break;
1014
+
1015
+ case 'column':
1016
+ settingsHtml = `
1017
+ <div class="space-y-4">
1018
+ <div>
1019
+ <label class="block text-sm font-medium text-gray-700 mb-1">Width</label>
1020
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="width, value="${fieldData.settings.width}">
1021
+ </div>
1022
+ <div>
1023
+ <label class="block text-sm font-medium text-gray-700 mb-1">Minimum Width</label>
1024
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="minWidth" value="${fieldData.settings.minWidth}">
1025
+ </div>
1026
+ </div>
1027
+ `;
1028
+ break;
1029
+
1030
+ case 'text':
1031
+ case 'email':
1032
+ case 'number':
1033
+ case 'date':
1034
+ settingsHtml = `
1035
+ <div class="space-y-4">
1036
+ <div>
1037
+ <label class="block text-sm font-medium text-gray-700 mb-1">Label</label>
1038
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="label" value="${fieldData.settings.label}">
1039
+ </div>
1040
+ <div>
1041
+ <label class="block text-sm font-medium text-gray-700 mb-1">Placeholder</label>
1042
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="placeholder" value="${fieldData.settings.placeholder || ''}">
1043
+ </div>
1044
+ <div class="flex items-center">
1045
+ <input type="checkbox" class="field-setting h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" data-setting="required" ${fieldData.settings.required ? 'checked' : ''}>
1046
+ <label class="ml-2 block text-sm text-gray-700">Required</label>
1047
+ </div>
1048
+ </div>
1049
+ `;
1050
+ break;
1051
+
1052
+ case 'textarea':
1053
+ settingsHtml = `
1054
+ <div class="space-y-4">
1055
+ <div>
1056
+ <label class="block text-sm font-medium text-gray-700 mb-1">Label</label>
1057
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="label" value="${fieldData.settings.label}">
1058
+ </div>
1059
+ <div>
1060
+ <label class="block text-sm font-medium text-gray-700 mb-1">Placeholder</label>
1061
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="placeholder" value="${fieldData.settings.placeholder || ''}">
1062
+ </div>
1063
+ <div>
1064
+ <label class="block text-sm font-medium text-gray-700 mb-1">Rows</label>
1065
+ <input type="number" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="rows" value="${fieldData.settings.rows || 3}">
1066
+ </div>
1067
+ <div class="flex items-center">
1068
+ <input type="checkbox" class="field-setting h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" data-setting="required" ${fieldData.settings.required ? 'checked' : ''}>
1069
+ <label class="ml-2 block text-sm text-gray-700">Required</label>
1070
+ </div>
1071
+ </div>
1072
+ `;
1073
+ break;
1074
+
1075
+ case 'select':
1076
+ case 'radio':
1077
+ settingsHtml = `
1078
+ <div class="space-y-4">
1079
+ <div>
1080
+ <label class="block text-sm font-medium text-gray-700 mb-1">Label</label>
1081
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="label" value="${fieldData.settings.label}">
1082
+ </div>
1083
+ <div>
1084
+ <label class="block text-sm font-medium text-gray-700 mb-1">Options (one per line)</label>
1085
+ <textarea class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="options" rows="4">${fieldData.settings.options.join('\n')}</textarea>
1086
+ </div>
1087
+ <div class="flex items-center">
1088
+ <input type="checkbox" class="field-setting h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" data-setting="required" ${fieldData.settings.required ? 'checked' : ''}>
1089
+ <label class="ml-2 block text-sm text-gray-700">Required</label>
1090
+ </div>
1091
+ </div>
1092
+ `;
1093
+ break;
1094
+
1095
+ case 'checkbox':
1096
+ settingsHtml = `
1097
+ <div class="space-y-4">
1098
+ <div>
1099
+ <label class="block text-sm font-medium text-gray-700 mb-1">Label</label>
1100
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="label" value="${fieldData.settings.label}">
1101
+ </div>
1102
+ <div>
1103
+ <label class="block text-sm font-medium text-gray-700 mb-1">Description</label>
1104
+ <input type="text, class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="description" value="${fieldData.settings.description || ''}">
1105
+ </div>
1106
+ <div class="flex items-center">
1107
+ <input type="checkbox" class="field-setting h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" data-setting="required" ${fieldData.settings.required ? 'checked' : ''}>
1108
+ <label class="ml-2 block text-sm text-gray-700">Required</label>
1109
+ </div>
1110
+ </div>
1111
+ `;
1112
+ break;
1113
+
1114
+ case 'file':
1115
+ settingsHtml = `
1116
+ <div class="space-y-4">
1117
+ <div>
1118
+ <label class="block text-sm font-medium text-gray-700 mb-1">Label</label>
1119
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="label" value="${fieldData.settings.label}">
1120
+ </div>
1121
+ <div>
1122
+ <label class="block text-sm font-medium text-gray-700 mb-1">Accepted File Types</label>
1123
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="fileTypes" value="${fieldData.settings.fileTypes || ''}">
1124
+ </div>
1125
+ <div>
1126
+ <label class="block text-sm font-medium text-gray-700 mb-1">Maximum File Size</label>
1127
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="maxSize" value="${fieldData.settings.maxSize || ''}">
1128
+ </div>
1129
+ <div class="flex items-center">
1130
+ <input type="checkbox" class="field-setting h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" data-setting="required" ${fieldData.settings.required ? 'checked' : ''}>
1131
+ <label class="ml-2 block text-sm text-gray-700">Required</label>
1132
+ </div>
1133
+ </div>
1134
+ `;
1135
+ break;
1136
+
1137
+ case 'section':
1138
+ settingsHtml = `
1139
+ <div class="space-y-4">
1140
+ <div>
1141
+ <label class="block text-sm font-medium text-gray-700 mb-1">Title</label>
1142
+ <input type="text" class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="title" value="${fieldData.settings.title}">
1143
+ </div>
1144
+ <div>
1145
+ <label class="block text-sm font-medium text-gray-700 mb-1">Description</label>
1146
+ <textarea class="field-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="description">${fieldData.settings.description || ''}</textarea>
1147
+ </div>
1148
+ </div>
1149
+ `;
1150
+ break;
1151
+ }
1152
+
1153
+ fieldSettings.innerHTML = settingsHtml;
1154
+
1155
+ // Set up event listeners for settings changes
1156
+ const settingInputs = fieldSettings.querySelectorAll('.field-setting');
1157
+ settingInputs.forEach(input => {
1158
+ if (input.type === 'checkbox') {
1159
+ input.addEventListener('change', function() {
1160
+ updateFieldSetting(fieldData.id, input.dataset.setting, input.checked);
1161
+ });
1162
+ } else {
1163
+ input.addEventListener('change', function() {
1164
+ updateFieldSetting(fieldData.id, input.dataset.setting, input.value);
1165
+ });
1166
+
1167
+ input.addEventListener('keyup', function() {
1168
+ updateFieldSetting(fieldData.id, input.dataset.setting, input.value);
1169
+ });
1170
+ }
1171
+ });
1172
+ }
1173
+
1174
+ // Update a field setting and refresh the field display
1175
+ function updateFieldSetting(fieldId, settingName, settingValue) {
1176
+ // Find the field in form data
1177
+ const fieldData = formData.fields.find(field => field.id === fieldId);
1178
+ if (!fieldData) return;
1179
+
1180
+ // Update the setting
1181
+ if (settingName === 'options') {
1182
+ // For options, split by newline
1183
+ fieldData.settings[settingName] = settingValue.split('\n').filter(opt => opt.trim() !== '');
1184
+ } else if (settingName === 'required' || settingName === 'rows' || settingName === 'columns') {
1185
+ // For checkboxes and numbers, convert to boolean/number
1186
+ fieldData.settings[settingName] = settingName === 'required'
1187
+ ? settingValue
1188
+ : parseInt(settingValue);
1189
+ } else {
1190
+ // For everything else, just set the value
1191
+ fieldData.settings[settingName] = settingValue;
1192
+ }
1193
+
1194
+ // Refresh the field display
1195
+ refreshFieldDisplay(fieldId);
1196
+ }
1197
+
1198
+ // Refresh the display of a field based on its current settings
1199
+ function refreshFieldDisplay(fieldId) {
1200
+ const fieldElement = document.querySelector(`.form-field[data-id="${fieldId}"]`);
1201
+ if (!fieldElement) return;
1202
+
1203
+ const fieldData = formData.fields.find(field => field.id === fieldId);
1204
+ if (!fieldData) return;
1205
+
1206
+ switch(fieldData.type) {
1207
+ case 'container':
1208
+ fieldElement.style.padding = fieldData.settings.padding;
1209
+ fieldElement.style.margin = fieldData.settings.margin;
1210
+ break;
1211
+
1212
+ case 'row':
1213
+ // Update row columns if needed
1214
+ if (fieldData.settings.columns) {
1215
+ // In a real implementation, you would adjust the columns here
1216
+ }
1217
+ break;
1218
+
1219
+ case 'column':
1220
+ fieldElement.style.width = fieldData.settings.width;
1221
+ fieldElement.style.minWidth = fieldData.settings.minWidth;
1222
+ break;
1223
+
1224
+ case 'text':
1225
+ case 'email':
1226
+ case 'number':
1227
+ case 'date':
1228
+ fieldElement.querySelector('label').textContent = fieldData.settings.label;
1229
+ fieldElement.querySelector('input').placeholder = fieldData.settings.placeholder || '';
1230
+ if (fieldData.settings.required) {
1231
+ fieldElement.querySelector('label').innerHTML += ' <span class="text-red-500">*</span>';
1232
+ }
1233
+ break;
1234
+
1235
+ case 'textarea':
1236
+ fieldElement.querySelector('label').textContent = fieldData.settings.label;
1237
+ fieldElement.querySelector('textarea').placeholder = fieldData.settings.placeholder || '';
1238
+ fieldElement.querySelector('textarea').rows = fieldData.settings.rows || 3;
1239
+ if (fieldData.settings.required) {
1240
+ fieldElement.querySelector('label').innerHTML += ' <span class="text-red-500">*</span>';
1241
+ }
1242
+ break;
1243
+
1244
+ case 'select':
1245
+ fieldElement.querySelector('label').textContent = fieldData.settings.label;
1246
+ if (fieldData.settings.required) {
1247
+ fieldElement.querySelector('label').innerHTML += ' <span class="text-red-500">*</span>';
1248
+ }
1249
+
1250
+ const select = fieldElement.querySelector('select');
1251
+ select.innerHTML = '';
1252
+ fieldData.settings.options.forEach(option => {
1253
+ const optionElement = document.createElement('option');
1254
+ optionElement.textContent = option;
1255
+ select.appendChild(optionElement);
1256
+ });
1257
+ break;
1258
+
1259
+ case 'checkbox':
1260
+ fieldElement.querySelector('label').textContent = fieldData.settings.label;
1261
+ const description = fieldElement.querySelector('p.text-gray-500');
1262
+ if (description) {
1263
+ description.textContent = fieldData.settings.description || '';
1264
+ }
1265
+ break;
1266
+
1267
+ case 'radio':
1268
+ fieldElement.querySelector('label').textContent = fieldData.settings.label;
1269
+ if (fieldData.settings.required) {
1270
+ fieldElement.querySelector('label').innerHTML += ' <span class="text-red-500">*</span>';
1271
+ }
1272
+
1273
+ const radioContainer = fieldElement.querySelector('.space-y-2');
1274
+ radioContainer.innerHTML = '';
1275
+ fieldData.settings.options.forEach((option, index) => {
1276
+ const radioDiv = document.createElement('div');
1277
+ radioDiv.className = 'flex items-center';
1278
+ radioDiv.innerHTML = `
1279
+ <input type="radio" name="radio-group" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300">
1280
+ <label class="ml-3 block text-sm font-medium text-gray-700">${option}</label>
1281
+ `;
1282
+ radioContainer.appendChild(radioDiv);
1283
+ });
1284
+ break;
1285
+
1286
+ case 'file':
1287
+ fieldElement.querySelector('label').textContent = fieldData.settings.label;
1288
+ const fileDesc = fieldElement.querySelector('p.text-xs.text-gray-500');
1289
+ if (fileDesc) {
1290
+ fileDesc.textContent = `${fieldData.settings.fileTypes || 'Any file'} up to ${fieldData.settings.maxSize || '10MB'}`;
1291
+ }
1292
+ break;
1293
+
1294
+ case 'section':
1295
+ fieldElement.querySelector('h3').textContent = fieldData.settings.title;
1296
+ fieldElement.querySelector('p').textContent = fieldData.settings.description || '';
1297
+ break;
1298
+ }
1299
+ }
1300
+
1301
+ // Form title and description updates
1302
+ formTitle.addEventListener('input', function() {
1303
+ formData.title = this.value;
1304
+ });
1305
+
1306
+ formDescription.addEventListener('input', function() {
1307
+ formData.description = this.value;
1308
+ });
1309
+
1310
+ // Preview button
1311
+ previewBtn.addEventListener('click', function() {
1312
+ previewFormTitle.textContent = formData.title || 'Form Title';
1313
+ previewFormDescription.textContent = formData.description || 'Form description goes here';
1314
+
1315
+ // Generate preview fields
1316
+ previewFormFields.innerHTML = '';
1317
+
1318
+ // Helper function to recursively render fields with their hierarchy
1319
+ function renderFields(fields, parentId = null) {
1320
+ fields.filter(field => field.parentId === parentId).forEach(field => {
1321
+ let fieldHtml = '';
1322
+
1323
+ switch(field.type) {
1324
+ case 'container':
1325
+ fieldHtml = `<div class="mb-4 p-4 bg-gray-50 rounded" style="padding: ${field.settings.padding}; margin: ${field.settings.margin}">`;
1326
+ fieldHtml += renderFields(formData.fields, field.id);
1327
+ fieldHtml += `</div>`;
1328
+ break;
1329
+
1330
+ case 'row':
1331
+ fieldHtml = `<div class="grid grid-cols-${field.settings.columns || 1} gap-${field.settings.gap || '0.5rem'} mb-4">`;
1332
+ fieldHtml += renderFields(formData.fields, field.id);
1333
+ fieldHtml += `</div>`;
1334
+ break;
1335
+
1336
+ case 'column':
1337
+ fieldHtml = `<div style="width: ${field.settings.width || '100%'}; min-width: ${field.settings.minWidth || '200px'}">`;
1338
+ fieldHtml += renderFields(formData.fields, field.id);
1339
+ fieldHtml += `</div>`;
1340
+ break;
1341
+
1342
+ case 'text':
1343
+ case 'email':
1344
+ case 'number':
1345
+ case 'date':
1346
+ fieldHtml = `
1347
+ <div class="mb-4">
1348
+ <label class="block text-sm font-medium text-gray-700 mb-1">${field.settings.label}
1349
+ </html>