formique 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/formique.js +2936 -0
  2. package/package.json +12 -0
package/formique.js ADDED
@@ -0,0 +1,2936 @@
1
+ import pretty from 'pretty';
2
+
3
+ // Base class for form rendering
4
+ class FormBuilder
5
+ {
6
+
7
+
8
+ renderField(type, name, label, validate, attributes, bindingSyntax, options) {
9
+ throw new Error('Method renderField must be implemented');
10
+ }
11
+
12
+
13
+
14
+
15
+ }
16
+
17
+
18
+
19
+ // Extended class for specific form rendering methods
20
+ class Formique extends FormBuilder {
21
+
22
+ constructor (formParams = {}, formSchema) {
23
+ super();
24
+ this.formSchema=formSchema;
25
+ this.divClass='input-block';
26
+ this.inputClass='form-input';
27
+ this.radioGroupClass='radio-group';
28
+ this.checkboxGroupClass='checkbox-group';
29
+ this.selectGroupClass='form-select';
30
+ this.formParams=formParams;
31
+ this.formMarkUp='';
32
+
33
+ if (this.formParams) {
34
+ this.formMarkUp += this.renderFormElement();
35
+ }
36
+
37
+ this.renderForm();
38
+
39
+
40
+ }
41
+
42
+
43
+
44
+
45
+ // renderFormElement method
46
+ renderFormElement() {
47
+ let formHTML = '<form\n';
48
+ // Use this.formParams directly
49
+ const paramsToUse = this.formParams || {};
50
+
51
+ // Dynamically add attributes if they are present in the parameters
52
+ for (const [key, value] of Object.entries(paramsToUse)) {
53
+ if (value !== undefined && value !== null) {
54
+ // Handle boolean attributes
55
+ if (typeof value === 'boolean') {
56
+ if (value) {
57
+ formHTML += ` ${key}\n`;
58
+ }
59
+ } else {
60
+ // Handle other attributes
61
+ const formattedKey = key === 'accept_charset' ? 'accept-charset' : key.replace(/_/g, '-');
62
+ formHTML += ` ${formattedKey}="${value}"\n`;
63
+ }
64
+ }
65
+ }
66
+
67
+ // Close the <form> tag
68
+ formHTML += '>\n';
69
+
70
+ // Manually ensure vertical formatting of the HTML string
71
+ formHTML = formHTML.replace(/\n\s*$/, '\n'); // Remove trailing whitespace/newline if necessary
72
+ return formHTML;
73
+ }
74
+
75
+
76
+ // Main renderForm method
77
+ renderForm() {
78
+ // Process each field synchronously
79
+ const formHTML = this.formSchema.map(field => {
80
+ const [type, name, label, validate, attributes = {}, bindingSyntax, options] = field;
81
+ return this.renderField(type, name, label, validate, attributes, bindingSyntax, options);
82
+ }).join('');
83
+
84
+ //return formHTML;
85
+ //return this.formMarkUp;
86
+
87
+ this.formMarkUp += formHTML;
88
+ //console.log("here", this.formMarkUp);
89
+ }
90
+
91
+
92
+ renderField(type, name, label, validate, attributes, bindingSyntax, options) {
93
+ switch (type) {
94
+ case 'text':
95
+ return this.renderTextField(type, name, label, validate, attributes, bindingSyntax);
96
+ case 'email':
97
+ return this.renderEmailField(type, name, label, validate, attributes, bindingSyntax);
98
+ case 'number':
99
+ return this.renderNumberField(type, name, label, validate, attributes, bindingSyntax);
100
+ case 'password':
101
+ return this.renderPasswordField(type, name, label, validate, attributes, bindingSyntax);
102
+ case 'tel': // New case for tel field
103
+ return this.renderTelField(type, name, label, validate, attributes, bindingSyntax);
104
+ case 'date':
105
+ return this.renderDateField(type, name, label, validate, attributes, bindingSyntax);
106
+ case 'time':
107
+ return this.renderTimeField(type, name, label, validate, attributes, bindingSyntax);
108
+ case 'datetime-local':
109
+ return this.renderDateTimeField(type, name, label, validate, attributes, bindingSyntax);
110
+ case 'month':
111
+ return this.renderMonthField(type, name, label, validate, attributes, bindingSyntax);
112
+ case 'week':
113
+ return this.renderWeekField(type, name, label, validate, attributes, bindingSyntax);
114
+ case 'url':
115
+ return this.renderUrlField(type, name, label, validate, attributes, bindingSyntax);
116
+ case 'search':
117
+ return this.renderSearchField(type, name, label, validate, attributes, bindingSyntax);
118
+ case 'color':
119
+ return this.renderColorField(type, name, label, validate, attributes, bindingSyntax);
120
+ case 'checkbox':
121
+ return this.renderCheckboxField(type, name, label, validate, attributes, bindingSyntax, options);
122
+ case 'radio':
123
+ return this.renderRadioField(type, name, label, validate, attributes, bindingSyntax, options);
124
+ case 'file':
125
+ return this.renderFileField(type, name, label, validate, attributes, bindingSyntax);
126
+ case 'hidden':
127
+ return this.renderHiddenField(type, name, label, validate, attributes, bindingSyntax);
128
+ case 'image':
129
+ return this.renderImageField(type, name, label, validate, attributes, bindingSyntax);
130
+ case 'textarea':
131
+ return this.renderTextareaField(type, name, label, validate, attributes, bindingSyntax);
132
+ case 'singleSelect':
133
+ return this.renderSingleSelectField(type, name, label, validate, attributes, bindingSyntax, options);
134
+ case 'multipleSelect':
135
+ return this.renderMultipleSelectField(type, name, label, validate, attributes, bindingSyntax, options);
136
+ case 'submit':
137
+ return this.renderSubmitButton(type, name, label, attributes);
138
+ default:
139
+ console.warn(`Unsupported field type '${type}' encountered.`);
140
+ return ''; // or handle gracefully
141
+ }
142
+
143
+
144
+
145
+ }
146
+
147
+
148
+
149
+
150
+
151
+ // Specific rendering methods for each field type
152
+ renderTextField(type, name, label, validate, attributes, bindingSyntax) {
153
+
154
+ //console.log("here");
155
+ // Define valid attributes for different input types
156
+ const textInputAttributes = [
157
+ 'required',
158
+ 'minlength',
159
+ 'maxlength',
160
+ 'pattern',
161
+ 'placeholder',
162
+ 'readonly',
163
+ 'disabled',
164
+ 'size',
165
+ 'autocomplete',
166
+ 'spellcheck',
167
+ 'inputmode',
168
+ 'title',
169
+ ];
170
+
171
+ // Construct validation attributes
172
+ let validationAttrs = '';
173
+ if (validate) {
174
+ Object.entries(validate).forEach(([key, value]) => {
175
+ if (textInputAttributes.includes(key)) {
176
+ if (typeof value === 'boolean' && value) {
177
+ validationAttrs += ` ${key}\n`;
178
+ } else {
179
+ switch (key) {
180
+ case 'minlength':
181
+ case 'maxlength':
182
+ validationAttrs += ` ${key}="${value}"\n`;
183
+ break;
184
+ default:
185
+ if (!numberInputAttributes.includes(key)) {
186
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'number'.\x1b[0m`);
187
+ }
188
+ break;
189
+ }
190
+ }
191
+ } else {
192
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'text'.\x1b[0m`);
193
+ }
194
+ });
195
+ }
196
+
197
+ // Handle the binding syntax
198
+ let bindingDirective = '';
199
+ if (bindingSyntax === 'bind:value' && name) {
200
+ bindingDirective = ` bind:value="${name}"\n`;
201
+ }
202
+ if (bindingSyntax.startsWith('::') && name) {
203
+ bindingDirective = ` bind:value="${name}"\n`;
204
+ }
205
+ if (bindingSyntax && !name) {
206
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
207
+ return;
208
+ }
209
+
210
+
211
+ // Get the id from attributes or fall back to name
212
+ let id = attributes.id || name;
213
+
214
+ // Determine if semantiq is true based on formParams
215
+ const semantq = this.formParams?.semantq || false;
216
+
217
+ // Construct additional attributes dynamically
218
+ let additionalAttrs = '';
219
+ for (const [key, value] of Object.entries(attributes)) {
220
+ if (key !== 'id' && value !== undefined) {
221
+ if (key.startsWith('on')) {
222
+ // Handle event attributes
223
+ if (semantq) {
224
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
225
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
226
+ } else {
227
+ // Add parentheses if not present
228
+ const eventValue = value.endsWith('()') ? value : `${value}()`;
229
+ additionalAttrs += ` ${key}="${eventValue}"\n`;
230
+ }
231
+ } else {
232
+ // Handle boolean attributes
233
+ if (value === true) {
234
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
235
+ } else if (value !== false) {
236
+ // Convert underscores to hyphens and set the attribute
237
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
238
+ }
239
+ }
240
+ }
241
+ }
242
+
243
+ // Construct the final HTML string
244
+ let formHTML = `
245
+ <div class="${this.divClass}">
246
+ <label for="${id}">${label}</label>
247
+ <input
248
+ type="${type}"
249
+ name="${name}"
250
+ ${bindingDirective}
251
+ id="${id}"
252
+ ${additionalAttrs}
253
+ ${validationAttrs}
254
+ />
255
+ </div>
256
+ `.replace(/^\s*\n/gm, '').trim();
257
+
258
+ // Format the entire HTML using pretty
259
+ let formattedHtml = pretty(formHTML, {
260
+ indent_size: 2,
261
+ wrap_line_length: 0,
262
+ preserve_newlines: true, // Preserve existing newlines
263
+ });
264
+
265
+ // Apply vertical layout to the <input> element only
266
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
267
+ // Reformat attributes into a vertical layout
268
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
269
+ return `<input\n${attributes}\n/>`;
270
+ });
271
+
272
+ // Ensure the <div> block starts on a new line
273
+ /*
274
+ formattedHtml = formattedHtml.replace(/<div\s+([^>]*)>/, (match) => {
275
+ // Ensure <div> starts on a new line
276
+ return `\n${match}\n`;
277
+ });
278
+ */
279
+
280
+
281
+ //this.formMarkUp += formattedHtml;
282
+ //console.log("HR",this.formMarkUp);
283
+ return formattedHtml;
284
+ }
285
+
286
+
287
+
288
+
289
+ // Specific rendering methods for each field type
290
+ renderEmailField(type, name, label, validate, attributes, bindingSyntax) {
291
+ // Define valid attributes for the email input type
292
+ const emailInputAttributes = [
293
+ 'required',
294
+ 'minlength',
295
+ 'maxlength',
296
+ 'pattern',
297
+ 'placeholder',
298
+ 'readonly',
299
+ 'disabled',
300
+ 'size',
301
+ 'autocomplete',
302
+ 'spellcheck',
303
+ 'inputmode',
304
+ 'title',
305
+ ];
306
+
307
+ // Construct validation attributes
308
+ let validationAttrs = '';
309
+ if (validate) {
310
+ Object.entries(validate).forEach(([key, value]) => {
311
+ if (emailInputAttributes.includes(key)) {
312
+ if (typeof value === 'boolean' && value) {
313
+ validationAttrs += ` ${key}\n`;
314
+ } else {
315
+ switch (key) {
316
+ case 'minlength':
317
+ case 'maxlength':
318
+ validationAttrs += ` ${key}="${value}"\n`;
319
+ break;
320
+ case 'pattern':
321
+ validationAttrs += ` ${key}="${value}"\n`;
322
+ break;
323
+ default:
324
+ if (!numberInputAttributes.includes(key)) {
325
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'number'.\x1b[0m`);
326
+ }
327
+ break;
328
+ }
329
+ }
330
+ } else {
331
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'email'.\x1b[0m`);
332
+ }
333
+ });
334
+ }
335
+
336
+ // Handle the binding syntax
337
+ let bindingDirective = '';
338
+ if (bindingSyntax === 'bind:value' && name) {
339
+ bindingDirective = ` bind:value="${name}"\n`;
340
+ }
341
+ if (bindingSyntax.startsWith('::') && name) {
342
+ bindingDirective = ` bind:value="${name}"\n`;
343
+ }
344
+ if (bindingSyntax && !name) {
345
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
346
+ return;
347
+ }
348
+
349
+ // Get the id from attributes or fall back to name
350
+ let id = attributes.id || name;
351
+
352
+ // Construct additional attributes dynamically
353
+ let additionalAttrs = '';
354
+ for (const [key, value] of Object.entries(attributes)) {
355
+ if (key !== 'id' && value !== undefined) {
356
+ if (key.startsWith('on')) {
357
+ // Handle event attributes
358
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
359
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
360
+ } else {
361
+ // Handle boolean attributes
362
+ if (value === true) {
363
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
364
+ } else if (value !== false) {
365
+ // Convert underscores to hyphens and set the attribute
366
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
367
+ }
368
+ }
369
+ }
370
+ }
371
+
372
+ // Construct the final HTML string
373
+ let formHTML = `
374
+ <div class="${this.divClass}">
375
+ <label for="${id}">${label}</label>
376
+ <input
377
+ type="${type}"
378
+ name="${name}"
379
+ ${bindingDirective}
380
+ id="${id}"
381
+ ${additionalAttrs}
382
+ ${validationAttrs}
383
+ />
384
+ </div>
385
+ `.replace(/^\s*\n/gm, '').trim();
386
+
387
+ // Format the entire HTML using pretty
388
+ let formattedHtml = pretty(formHTML, {
389
+ indent_size: 2,
390
+ wrap_line_length: 0,
391
+ preserve_newlines: true, // Preserve existing newlines
392
+ });
393
+
394
+ // Apply vertical layout to the <input> element only
395
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
396
+ // Reformat attributes into a vertical layout
397
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
398
+ return `<input\n${attributes}\n/>`;
399
+ });
400
+
401
+ // Ensure the <div> block starts on a new line and remove extra blank lines
402
+
403
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
404
+ // Ensure <div> starts on a new line
405
+ return `\n${match}\n`;
406
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
407
+
408
+
409
+ //this.formMarkUp += formattedHtml;
410
+
411
+ return formattedHtml;
412
+ //return this.formMarkUp;
413
+ //console.log(this.formMarkUp);
414
+ }
415
+
416
+
417
+
418
+ renderNumberField(type, name, label, validate, attributes, bindingSyntax) {
419
+ // Define valid attributes for the number input type
420
+ const numberInputAttributes = [
421
+ 'required',
422
+ 'min',
423
+ 'max',
424
+ 'step',
425
+ 'placeholder',
426
+ 'readonly',
427
+ 'disabled',
428
+ 'size',
429
+ 'autocomplete',
430
+ 'inputmode',
431
+ 'title',
432
+ ];
433
+
434
+ // Construct validation attributes
435
+ let validationAttrs = '';
436
+ if (validate) {
437
+ Object.entries(validate).forEach(([key, value]) => {
438
+ if (numberInputAttributes.includes(key)) {
439
+ if (typeof value === 'boolean' && value) {
440
+ validationAttrs += ` ${key}\n`;
441
+ } else {
442
+ switch (key) {
443
+ case 'min':
444
+ case 'max':
445
+ validationAttrs += ` ${key}="${value}"\n`;
446
+ break;
447
+ case 'step':
448
+ validationAttrs += ` ${key}="${value}"\n`;
449
+ break;
450
+ default:
451
+ if (!numberInputAttributes.includes(key)) {
452
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'number'.\x1b[0m`);
453
+ }
454
+ break;
455
+ }
456
+ }
457
+ } else {
458
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'number'.\x1b[0m`);
459
+ }
460
+ });
461
+ }
462
+
463
+ // Handle the binding syntax
464
+ let bindingDirective = '';
465
+ if (bindingSyntax === 'bind:value' && name) {
466
+ bindingDirective = ` bind:value="${name}"\n`;
467
+ }
468
+ if (bindingSyntax.startsWith('::') && name) {
469
+ bindingDirective = ` bind:value="${name}"\n`;
470
+ }
471
+ if (bindingSyntax && !name) {
472
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
473
+ return;
474
+ }
475
+
476
+
477
+ // Get the id from attributes or fall back to name
478
+ let id = attributes.id || name;
479
+
480
+ // Construct additional attributes dynamically
481
+ let additionalAttrs = '';
482
+ for (const [key, value] of Object.entries(attributes)) {
483
+ if (key !== 'id' && value !== undefined) {
484
+ if (key.startsWith('on')) {
485
+ // Handle event attributes
486
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
487
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
488
+ } else {
489
+ // Handle boolean attributes
490
+ if (value === true) {
491
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
492
+ } else if (value !== false) {
493
+ // Convert underscores to hyphens and set the attribute
494
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
495
+ }
496
+ }
497
+ }
498
+ }
499
+
500
+ // Construct the final HTML string
501
+ let formHTML = `
502
+ <div class="${this.divClass}">
503
+ <label for="${id}">${label}</label>
504
+ <input
505
+ type="${type}"
506
+ name="${name}"
507
+ ${bindingDirective}
508
+ id="${id}"
509
+ ${additionalAttrs}
510
+ ${validationAttrs}
511
+ />
512
+ </div>
513
+ `.replace(/^\s*\n/gm, '').trim();
514
+
515
+ // Format the entire HTML using pretty
516
+ let formattedHtml = pretty(formHTML, {
517
+ indent_size: 2,
518
+ wrap_line_length: 0,
519
+ preserve_newlines: true, // Preserve existing newlines
520
+ });
521
+
522
+ // Apply vertical layout to the <input> element only
523
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
524
+ // Reformat attributes into a vertical layout
525
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
526
+ return `<input\n${attributes}\n/>`;
527
+ });
528
+
529
+ // Ensure the <div> block starts on a new line and remove extra blank lines
530
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
531
+ // Ensure <div> starts on a new line
532
+ return `\n${match}\n`;
533
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
534
+
535
+ return formattedHtml;
536
+ }
537
+
538
+
539
+
540
+ // New method for rendering password fields
541
+ renderPasswordField(type, name, label, validate, attributes, bindingSyntax) {
542
+ // Define valid attributes for the password input type
543
+ const passwordInputAttributes = [
544
+ 'required',
545
+ 'minlength',
546
+ 'maxlength',
547
+ 'pattern',
548
+ 'placeholder',
549
+ 'readonly',
550
+ 'disabled',
551
+ 'size',
552
+ 'autocomplete',
553
+ 'spellcheck',
554
+ 'inputmode',
555
+ 'title',
556
+ ];
557
+
558
+ // Construct validation attributes
559
+ let validationAttrs = '';
560
+ if (validate) {
561
+ Object.entries(validate).forEach(([key, value]) => {
562
+ if (passwordInputAttributes.includes(key)) {
563
+ if (typeof value === 'boolean' && value) {
564
+ validationAttrs += ` ${key}\n`;
565
+ } else {
566
+ switch (key) {
567
+ case 'minlength':
568
+ case 'maxlength':
569
+ case 'pattern':
570
+ validationAttrs += ` ${key}="${value}"\n`;
571
+ break;
572
+ default:
573
+ if (!passwordInputAttributes.includes(key)) {
574
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'password'.\x1b[0m`);
575
+ }
576
+ break;
577
+ }
578
+ }
579
+ } else {
580
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'password'.\x1b[0m`);
581
+ }
582
+ });
583
+ }
584
+
585
+ // Handle the binding syntax
586
+ let bindingDirective = '';
587
+ if (bindingSyntax === 'bind:value' && name) {
588
+ bindingDirective = ` bind:value="${name}"\n`;
589
+ }
590
+ if (bindingSyntax.startsWith('::') && name) {
591
+ bindingDirective = ` bind:value="${name}"\n`;
592
+ }
593
+ if (bindingSyntax && !name) {
594
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
595
+ return;
596
+ }
597
+
598
+ // Get the id from attributes or fall back to name
599
+ let id = attributes.id || name;
600
+
601
+ // Construct additional attributes dynamically
602
+ let additionalAttrs = '';
603
+ for (const [key, value] of Object.entries(attributes)) {
604
+ if (key !== 'id' && value !== undefined) {
605
+ if (key.startsWith('on')) {
606
+ // Handle event attributes
607
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
608
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
609
+ } else {
610
+ // Handle boolean attributes
611
+ if (value === true) {
612
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
613
+ } else if (value !== false) {
614
+ // Convert underscores to hyphens and set the attribute
615
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
616
+ }
617
+ }
618
+ }
619
+ }
620
+
621
+ // Construct the final HTML string
622
+ let formHTML = `
623
+ <div class="${this.divClass}">
624
+ <label for="${id}">${label}</label>
625
+ <input
626
+ type="${type}"
627
+ name="${name}"
628
+ ${bindingDirective}
629
+ id="${id}"
630
+ ${additionalAttrs}
631
+ ${validationAttrs}
632
+ />
633
+ </div>
634
+ `.replace(/^\s*\n/gm, '').trim();
635
+
636
+ // Format the entire HTML using pretty
637
+ let formattedHtml = pretty(formHTML, {
638
+ indent_size: 2,
639
+ wrap_line_length: 0,
640
+ preserve_newlines: true, // Preserve existing newlines
641
+ });
642
+
643
+ // Apply vertical layout to the <input> element only
644
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
645
+ // Reformat attributes into a vertical layout
646
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
647
+ return `<input\n${attributes}\n/>`;
648
+ });
649
+
650
+ // Ensure the <div> block starts on a new line and remove extra blank lines
651
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
652
+ // Ensure <div> starts on a new line
653
+ return `\n${match}\n`;
654
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
655
+
656
+ return formattedHtml;
657
+ }
658
+
659
+
660
+
661
+
662
+
663
+
664
+ // New method for rendering tel fields
665
+ renderTelField(type, name, label, validate, attributes, bindingSyntax) {
666
+ // Define valid attributes for the tel input type
667
+ const telInputAttributes = [
668
+ 'required',
669
+ 'pattern',
670
+ 'placeholder',
671
+ 'readonly',
672
+ 'disabled',
673
+ 'size',
674
+ 'autocomplete',
675
+ 'spellcheck',
676
+ 'inputmode',
677
+ 'title',
678
+ 'minlength',
679
+ 'maxlength',
680
+ ];
681
+
682
+
683
+ // Construct validation attributes
684
+ let validationAttrs = '';
685
+ if (validate) {
686
+ Object.entries(validate).forEach(([key, value]) => {
687
+ if (telInputAttributes.includes(key)) {
688
+ if (typeof value === 'boolean' && value) {
689
+ validationAttrs += ` ${key}\n`;
690
+ } else {
691
+ switch (key) {
692
+ case 'pattern':
693
+ validationAttrs += ` ${key}="${value}"\n`;
694
+ break;
695
+ default:
696
+ if (!telInputAttributes.includes(key)) {
697
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'tel'.\x1b[0m`);
698
+ }
699
+ break;
700
+ }
701
+ }
702
+ } else {
703
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'tel'.\x1b[0m`);
704
+ }
705
+ });
706
+ }
707
+
708
+ // Handle the binding syntax
709
+ let bindingDirective = '';
710
+ if (bindingSyntax === 'bind:value' && name) {
711
+ bindingDirective = ` bind:value="${name}"\n`;
712
+ }
713
+ if (bindingSyntax.startsWith('::') && name) {
714
+ bindingDirective = ` bind:value="${name}"\n`;
715
+ }
716
+ if (bindingSyntax && !name) {
717
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
718
+ return;
719
+ }
720
+
721
+ // Get the id from attributes or fall back to name
722
+ let id = attributes.id || name;
723
+
724
+ // Construct additional attributes dynamically
725
+ let additionalAttrs = '';
726
+ for (const [key, value] of Object.entries(attributes)) {
727
+ if (key !== 'id' && value !== undefined) {
728
+ if (key.startsWith('on')) {
729
+ // Handle event attributes
730
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
731
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
732
+ } else {
733
+ // Handle boolean attributes
734
+ if (value === true) {
735
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
736
+ } else if (value !== false) {
737
+ // Convert underscores to hyphens and set the attribute
738
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
739
+ }
740
+ }
741
+ }
742
+ }
743
+
744
+ // Construct the final HTML string
745
+ let formHTML = `
746
+ <div class="${this.divClass}">
747
+ <label for="${id}">${label}</label>
748
+ <input
749
+ type="${type}"
750
+ name="${name}"
751
+ ${bindingDirective}
752
+ id="${id}"
753
+ ${additionalAttrs}
754
+ ${validationAttrs}
755
+ />
756
+ </div>
757
+ `.replace(/^\s*\n/gm, '').trim();
758
+
759
+ // Format the entire HTML using pretty
760
+ let formattedHtml = pretty(formHTML, {
761
+ indent_size: 2,
762
+ wrap_line_length: 0,
763
+ preserve_newlines: true, // Preserve existing newlines
764
+ });
765
+
766
+ // Apply vertical layout to the <input> element only
767
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
768
+ // Reformat attributes into a vertical layout
769
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
770
+ return `<input\n${attributes}\n/>`;
771
+ });
772
+
773
+ // Ensure the <div> block starts on a new line and remove extra blank lines
774
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
775
+ // Ensure <div> starts on a new line
776
+ return `\n${match}\n`;
777
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
778
+
779
+ return formattedHtml;
780
+ }
781
+
782
+
783
+
784
+ renderDateField(type, name, label, validate, attributes, bindingSyntax) {
785
+ // Define valid attributes for the date input type
786
+ const dateInputAttributes = [
787
+ 'required',
788
+ 'min',
789
+ 'max',
790
+ 'step',
791
+ 'placeholder',
792
+ 'readonly',
793
+ 'disabled',
794
+ 'autocomplete',
795
+ 'spellcheck',
796
+ 'inputmode',
797
+ 'title',
798
+ ];
799
+
800
+ // Construct validation attributes
801
+ let validationAttrs = '';
802
+ if (validate) {
803
+ Object.entries(validate).forEach(([key, value]) => {
804
+ if (dateInputAttributes.includes(key)) {
805
+ if (typeof value === 'boolean' && value) {
806
+ validationAttrs += ` ${key}\n`;
807
+ } else {
808
+ switch (key) {
809
+ case 'min':
810
+ case 'max':
811
+ case 'step':
812
+ validationAttrs += ` ${key}="${value}"\n`;
813
+ break;
814
+ default:
815
+ if (!dateInputAttributes.includes(key)) {
816
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'date'.\x1b[0m`);
817
+ }
818
+ break;
819
+ }
820
+ }
821
+ } else {
822
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'date'.\x1b[0m`);
823
+ }
824
+ });
825
+ }
826
+
827
+ // Handle the binding syntax
828
+ let bindingDirective = '';
829
+ if (bindingSyntax === 'bind:value' && name) {
830
+ bindingDirective = ` bind:value="${name}"\n`;
831
+ }
832
+ if (bindingSyntax.startsWith('::') && name) {
833
+ bindingDirective = ` bind:value="${name}"\n`;
834
+ }
835
+ if (bindingSyntax && !name) {
836
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
837
+ return;
838
+ }
839
+
840
+ // Get the id from attributes or fall back to name
841
+ let id = attributes.id || name;
842
+
843
+ // Construct additional attributes dynamically
844
+ let additionalAttrs = '';
845
+ for (const [key, value] of Object.entries(attributes)) {
846
+ if (key !== 'id' && value !== undefined) {
847
+ if (key.startsWith('on')) {
848
+ // Handle event attributes
849
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
850
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
851
+ } else {
852
+ // Handle boolean attributes
853
+ if (value === true) {
854
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
855
+ } else if (value !== false) {
856
+ // Convert underscores to hyphens and set the attribute
857
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
858
+ }
859
+ }
860
+ }
861
+ }
862
+
863
+ // Construct the final HTML string
864
+ let formHTML = `
865
+ <div class="${this.divClass}">
866
+ <label for="${id}">${label}</label>
867
+ <input
868
+ type="${type}"
869
+ name="${name}"
870
+ ${bindingDirective}
871
+ id="${id}"
872
+ ${additionalAttrs}
873
+ ${validationAttrs}
874
+ />
875
+ </div>
876
+ `.replace(/^\s*\n/gm, '').trim();
877
+
878
+ // Format the entire HTML using pretty
879
+ let formattedHtml = pretty(formHTML, {
880
+ indent_size: 2,
881
+ wrap_line_length: 0,
882
+ preserve_newlines: true, // Preserve existing newlines
883
+ });
884
+
885
+ // Apply vertical layout to the <input> element only
886
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
887
+ // Reformat attributes into a vertical layout
888
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
889
+ return `<input\n${attributes}\n/>`;
890
+ });
891
+
892
+ // Ensure the <div> block starts on a new line and remove extra blank lines
893
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
894
+ // Ensure <div> starts on a new line
895
+ return `\n${match}\n`;
896
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
897
+
898
+ return formattedHtml;
899
+ }
900
+
901
+
902
+
903
+ renderTimeField(type, name, label, validate, attributes, bindingSyntax) {
904
+ // Define valid attributes for the time input type
905
+ const timeInputAttributes = [
906
+ 'required',
907
+ 'min',
908
+ 'max',
909
+ 'step',
910
+ 'readonly',
911
+ 'disabled',
912
+ 'autocomplete',
913
+ 'spellcheck',
914
+ 'inputmode',
915
+ 'title',
916
+ ];
917
+
918
+ // Construct validation attributes
919
+ let validationAttrs = '';
920
+ if (validate) {
921
+ Object.entries(validate).forEach(([key, value]) => {
922
+ if (timeInputAttributes.includes(key)) {
923
+ if (typeof value === 'boolean' && value) {
924
+ validationAttrs += ` ${key}\n`;
925
+ } else {
926
+ switch (key) {
927
+ case 'min':
928
+ case 'max':
929
+ case 'step':
930
+ validationAttrs += ` ${key}="${value}"\n`;
931
+ break;
932
+ default:
933
+ if (!timeInputAttributes.includes(key)) {
934
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
935
+ }
936
+ break;
937
+ }
938
+ }
939
+ } else {
940
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
941
+ }
942
+ });
943
+ }
944
+
945
+ // Handle the binding syntax
946
+ let bindingDirective = '';
947
+ if (bindingSyntax === 'bind:value' && name) {
948
+ bindingDirective = ` bind:value="${name}"\n`;
949
+ }
950
+ if (bindingSyntax.startsWith('::') && name) {
951
+ bindingDirective = ` bind:value="${name}"\n`;
952
+ }
953
+ if (bindingSyntax && !name) {
954
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
955
+ return;
956
+ }
957
+
958
+ // Get the id from attributes or fall back to name
959
+ let id = attributes.id || name;
960
+
961
+ // Construct additional attributes dynamically
962
+ let additionalAttrs = '';
963
+ for (const [key, value] of Object.entries(attributes)) {
964
+ if (key !== 'id' && value !== undefined) {
965
+ if (key.startsWith('on')) {
966
+ // Handle event attributes
967
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
968
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
969
+ } else {
970
+ // Handle boolean attributes
971
+ if (value === true) {
972
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
973
+ } else if (value !== false) {
974
+ // Convert underscores to hyphens and set the attribute
975
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
976
+ }
977
+ }
978
+ }
979
+ }
980
+
981
+ // Construct the final HTML string
982
+ let formHTML = `
983
+ <div class="${this.divClass}">
984
+ <label for="${id}">${label}</label>
985
+ <input
986
+ type="${type}"
987
+ name="${name}"
988
+ ${bindingDirective}
989
+ id="${id}"
990
+ ${additionalAttrs}
991
+ ${validationAttrs}
992
+ />
993
+ </div>
994
+ `.replace(/^\s*\n/gm, '').trim();
995
+
996
+ // Format the entire HTML using pretty
997
+ let formattedHtml = pretty(formHTML, {
998
+ indent_size: 2,
999
+ wrap_line_length: 0,
1000
+ preserve_newlines: true, // Preserve existing newlines
1001
+ });
1002
+
1003
+ // Apply vertical layout to the <input> element only
1004
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
1005
+ // Reformat attributes into a vertical layout
1006
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
1007
+ return `<input\n${attributes}\n/>`;
1008
+ });
1009
+
1010
+ // Ensure the <div> block starts on a new line and remove extra blank lines
1011
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
1012
+ // Ensure <div> starts on a new line
1013
+ return `\n${match}\n`;
1014
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
1015
+
1016
+ return formattedHtml;
1017
+ }
1018
+
1019
+
1020
+
1021
+
1022
+ renderDateTimeField(type, name, label, validate, attributes, bindingSyntax) {
1023
+ // Define valid attributes for the datetime input type
1024
+ const dateTimeInputAttributes = [
1025
+ 'required',
1026
+ 'min',
1027
+ 'max',
1028
+ 'step',
1029
+ 'readonly',
1030
+ 'disabled',
1031
+ 'autocomplete',
1032
+ 'spellcheck',
1033
+ 'inputmode',
1034
+ 'title',
1035
+ ];
1036
+
1037
+ // Construct validation attributes
1038
+ let validationAttrs = '';
1039
+ if (validate) {
1040
+ Object.entries(validate).forEach(([key, value]) => {
1041
+ if (dateTimeInputAttributes.includes(key)) {
1042
+ if (typeof value === 'boolean' && value) {
1043
+ validationAttrs += ` ${key}\n`;
1044
+ } else {
1045
+ switch (key) {
1046
+ case 'min':
1047
+ case 'max':
1048
+ case 'step':
1049
+ validationAttrs += ` ${key}="${value}"\n`;
1050
+ break;
1051
+ default:
1052
+ if (!dateTimeInputAttributes.includes(key)) {
1053
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1054
+ }
1055
+ break;
1056
+ }
1057
+ }
1058
+ } else {
1059
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1060
+ }
1061
+ });
1062
+ }
1063
+
1064
+ // Handle the binding syntax
1065
+ let bindingDirective = '';
1066
+ if (bindingSyntax === 'bind:value' && name) {
1067
+ bindingDirective = ` bind:value="${name}"\n`;
1068
+ } if (bindingSyntax.startsWith('::') && name) {
1069
+ bindingDirective = ` bind:value="${name}"\n`;
1070
+ }
1071
+ if (bindingSyntax && !name) {
1072
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
1073
+ return;
1074
+ }
1075
+
1076
+ // Get the id from attributes or fall back to name
1077
+ let id = attributes.id || name;
1078
+
1079
+ // Construct additional attributes dynamically
1080
+ let additionalAttrs = '';
1081
+ for (const [key, value] of Object.entries(attributes)) {
1082
+ if (key !== 'id' && value !== undefined) {
1083
+ if (key.startsWith('on')) {
1084
+ // Handle event attributes
1085
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
1086
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
1087
+ } else {
1088
+ // Handle boolean attributes
1089
+ if (value === true) {
1090
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
1091
+ } else if (value !== false) {
1092
+ // Convert underscores to hyphens and set the attribute
1093
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
1094
+ }
1095
+ }
1096
+ }
1097
+ }
1098
+
1099
+ // Construct the final HTML string
1100
+ let formHTML = `
1101
+ <div class="${this.divClass}">
1102
+ <label for="${id}">${label}</label>
1103
+ <input
1104
+ type="${type}"
1105
+ name="${name}"
1106
+ ${bindingDirective}
1107
+ id="${id}"
1108
+ ${additionalAttrs}
1109
+ ${validationAttrs}
1110
+ />
1111
+ </div>
1112
+ `.replace(/^\s*\n/gm, '').trim();
1113
+
1114
+ // Format the entire HTML using pretty
1115
+ let formattedHtml = pretty(formHTML, {
1116
+ indent_size: 2,
1117
+ wrap_line_length: 0,
1118
+ preserve_newlines: true, // Preserve existing newlines
1119
+ });
1120
+
1121
+ // Apply vertical layout to the <input> element only
1122
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
1123
+ // Reformat attributes into a vertical layout
1124
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
1125
+ return `<input\n${attributes}\n/>`;
1126
+ });
1127
+
1128
+ // Ensure the <div> block starts on a new line and remove extra blank lines
1129
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
1130
+ // Ensure <div> starts on a new line
1131
+ return `\n${match}\n`;
1132
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
1133
+
1134
+ return formattedHtml;
1135
+ }
1136
+
1137
+
1138
+
1139
+
1140
+
1141
+
1142
+
1143
+ renderMonthField(type, name, label, validate, attributes, bindingSyntax) {
1144
+ // Define valid attributes for the month input type
1145
+ const monthInputAttributes = [
1146
+ 'required',
1147
+ 'min',
1148
+ 'max',
1149
+ 'pattern',
1150
+ 'placeholder',
1151
+ 'readonly',
1152
+ 'disabled',
1153
+ 'size',
1154
+ 'autocomplete',
1155
+ 'spellcheck',
1156
+ 'inputmode',
1157
+ 'title',
1158
+ ];
1159
+
1160
+ // Construct validation attributes
1161
+ let validationAttrs = '';
1162
+ if (validate) {
1163
+ Object.entries(validate).forEach(([key, value]) => {
1164
+ if (monthInputAttributes.includes(key)) {
1165
+ if (typeof value === 'boolean' && value) {
1166
+ validationAttrs += ` ${key}\n`;
1167
+ } else {
1168
+ switch (key) {
1169
+ case 'min':
1170
+ case 'max':
1171
+ case 'pattern':
1172
+ validationAttrs += ` ${key}="${value}"\n`;
1173
+ break;
1174
+ default:
1175
+ if (monthInputAttributes.includes(key)) {
1176
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'month'.\x1b[0m`);
1177
+ }
1178
+ break;
1179
+ }
1180
+ }
1181
+ } else {
1182
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'month'.\x1b[0m`);
1183
+ }
1184
+ });
1185
+ }
1186
+
1187
+ // Handle the binding syntax
1188
+ let bindingDirective = '';
1189
+ if (bindingSyntax === 'bind:value' && name) {
1190
+ bindingDirective = ` bind:value="${name}"\n`;
1191
+ } if (bindingSyntax.startsWith('::') && name) {
1192
+ bindingDirective = ` bind:value="${name}"\n`;
1193
+ } if (bindingSyntax && !name) {
1194
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
1195
+ return;
1196
+ }
1197
+
1198
+ // Get the id from attributes or fall back to name
1199
+ let id = attributes.id || name;
1200
+
1201
+ // Construct additional attributes dynamically
1202
+ let additionalAttrs = '';
1203
+ for (const [key, value] of Object.entries(attributes)) {
1204
+ if (key !== 'id' && value !== undefined) {
1205
+ if (key.startsWith('on')) {
1206
+ // Handle event attributes
1207
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
1208
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
1209
+ } else {
1210
+ // Handle boolean attributes
1211
+ if (value === true) {
1212
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
1213
+ } else if (value !== false) {
1214
+ // Convert underscores to hyphens and set the attribute
1215
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
1216
+ }
1217
+ }
1218
+ }
1219
+ }
1220
+
1221
+ // Construct the final HTML string
1222
+ let formHTML = `
1223
+ <div class="${this.divClass}">
1224
+ <label for="${id}">${label}</label>
1225
+ <input
1226
+ type="${type}"
1227
+ name="${name}"
1228
+ ${bindingDirective}
1229
+ id="${id}"
1230
+ ${additionalAttrs}
1231
+ ${validationAttrs}
1232
+ />
1233
+ </div>
1234
+ `.replace(/^\s*\n/gm, '').trim();
1235
+
1236
+ // Format the entire HTML using pretty
1237
+ let formattedHtml = pretty(formHTML, {
1238
+ indent_size: 2,
1239
+ wrap_line_length: 0,
1240
+ preserve_newlines: true, // Preserve existing newlines
1241
+ });
1242
+
1243
+ // Apply vertical layout to the <input> element only
1244
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
1245
+ // Reformat attributes into a vertical layout
1246
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
1247
+ return `<input\n${attributes}\n/>`;
1248
+ });
1249
+
1250
+ // Ensure the <div> block starts on a new line and remove extra blank lines
1251
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
1252
+ // Ensure <div> starts on a new line
1253
+ return `\n${match}\n`;
1254
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
1255
+
1256
+ return formattedHtml;
1257
+ }
1258
+
1259
+
1260
+
1261
+ renderWeekField(type, name, label, validate, attributes, bindingSyntax) {
1262
+ // Define valid attributes for the week input type
1263
+ const weekInputAttributes = [
1264
+ 'required',
1265
+ 'min',
1266
+ 'max',
1267
+ 'pattern',
1268
+ 'placeholder',
1269
+ 'readonly',
1270
+ 'disabled',
1271
+ 'size',
1272
+ 'autocomplete',
1273
+ 'spellcheck',
1274
+ 'inputmode',
1275
+ 'title',
1276
+ ];
1277
+
1278
+ // Construct validation attributes
1279
+ let validationAttrs = '';
1280
+ if (validate) {
1281
+ Object.entries(validate).forEach(([key, value]) => {
1282
+ if (weekInputAttributes.includes(key)) {
1283
+ if (typeof value === 'boolean' && value) {
1284
+ validationAttrs += ` ${key}\n`;
1285
+ } else {
1286
+ switch (key) {
1287
+ case 'min':
1288
+ case 'max':
1289
+ case 'pattern':
1290
+ validationAttrs += ` ${key}="${value}"\n`;
1291
+ break;
1292
+ default:
1293
+ if (weekInputAttributes.includes(key)) {
1294
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'week'.\x1b[0m`);
1295
+ }
1296
+ break;
1297
+ }
1298
+ }
1299
+ } else {
1300
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'week'.\x1b[0m`);
1301
+ }
1302
+ });
1303
+ }
1304
+
1305
+ // Handle the binding syntax
1306
+ let bindingDirective = '';
1307
+ if (bindingSyntax === 'bind:value' && name) {
1308
+ bindingDirective = ` bind:value="${name}"\n`;
1309
+ } if (bindingSyntax.startsWith('::') && name) {
1310
+ bindingDirective = ` bind:value="${name}"\n`;
1311
+ } if (bindingSyntax && !name) {
1312
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
1313
+ return;
1314
+ }
1315
+
1316
+ // Get the id from attributes or fall back to name
1317
+ let id = attributes.id || name;
1318
+
1319
+ // Construct additional attributes dynamically
1320
+ let additionalAttrs = '';
1321
+ for (const [key, value] of Object.entries(attributes)) {
1322
+ if (key !== 'id' && value !== undefined) {
1323
+ if (key.startsWith('on')) {
1324
+ // Handle event attributes
1325
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
1326
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
1327
+ } else {
1328
+ // Handle boolean attributes
1329
+ if (value === true) {
1330
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
1331
+ } else if (value !== false) {
1332
+ // Convert underscores to hyphens and set the attribute
1333
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
1334
+ }
1335
+ }
1336
+ }
1337
+ }
1338
+
1339
+ // Construct the final HTML string
1340
+ let formHTML = `
1341
+ <div class="${this.divClass}">
1342
+ <label for="${id}">${label}</label>
1343
+ <input
1344
+ type="${type}"
1345
+ name="${name}"
1346
+ ${bindingDirective}
1347
+ id="${id}"
1348
+ ${additionalAttrs}
1349
+ ${validationAttrs}
1350
+ />
1351
+ </div>
1352
+ `.replace(/^\s*\n/gm, '').trim();
1353
+
1354
+ // Format the entire HTML using pretty
1355
+ let formattedHtml = pretty(formHTML, {
1356
+ indent_size: 2,
1357
+ wrap_line_length: 0,
1358
+ preserve_newlines: true, // Preserve existing newlines
1359
+ });
1360
+
1361
+ // Apply vertical layout to the <input> element only
1362
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
1363
+ // Reformat attributes into a vertical layout
1364
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
1365
+ return `<input\n${attributes}\n/>`;
1366
+ });
1367
+
1368
+ // Ensure the <div> block starts on a new line and remove extra blank lines
1369
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
1370
+ // Ensure <div> starts on a new line
1371
+ return `\n${match}\n`;
1372
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
1373
+
1374
+ return formattedHtml;
1375
+ }
1376
+
1377
+
1378
+
1379
+ renderUrlField(type, name, label, validate, attributes, bindingSyntax) {
1380
+ // Define valid attributes for the URL input type
1381
+ const urlInputAttributes = [
1382
+ 'required',
1383
+ 'pattern',
1384
+ 'placeholder',
1385
+ 'readonly',
1386
+ 'disabled',
1387
+ 'size',
1388
+ 'autocomplete',
1389
+ 'spellcheck',
1390
+ 'inputmode',
1391
+ 'title',
1392
+ ];
1393
+
1394
+ // Construct validation attributes
1395
+ let validationAttrs = '';
1396
+ if (validate) {
1397
+ Object.entries(validate).forEach(([key, value]) => {
1398
+ if (urlInputAttributes.includes(key)) {
1399
+ if (typeof value === 'boolean' && value) {
1400
+ validationAttrs += ` ${key}\n`;
1401
+ } else {
1402
+ switch (key) {
1403
+ case 'pattern':
1404
+ validationAttrs += ` ${key}="${value}"\n`;
1405
+ break;
1406
+ default:
1407
+ if (!urlInputAttributes.includes(key)) {
1408
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1409
+ }
1410
+ break;
1411
+ }
1412
+ }
1413
+ } else {
1414
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1415
+ }
1416
+ });
1417
+ }
1418
+
1419
+ // Handle the binding syntax
1420
+ let bindingDirective = '';
1421
+ if (bindingSyntax === 'bind:value' && name) {
1422
+ bindingDirective = ` bind:value="${name}"\n`;
1423
+ } if (bindingSyntax.startsWith('::') && name) {
1424
+ bindingDirective = ` bind:value="${name}"\n`;
1425
+ } if (bindingSyntax && !name) {
1426
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
1427
+ return;
1428
+ }
1429
+
1430
+ // Get the id from attributes or fall back to name
1431
+ let id = attributes.id || name;
1432
+
1433
+ // Construct additional attributes dynamically
1434
+ let additionalAttrs = '';
1435
+ for (const [key, value] of Object.entries(attributes)) {
1436
+ if (key !== 'id' && value !== undefined) {
1437
+ if (key.startsWith('on')) {
1438
+ // Handle event attributes
1439
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
1440
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
1441
+ } else {
1442
+ // Handle boolean attributes
1443
+ if (value === true) {
1444
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
1445
+ } else if (value !== false) {
1446
+ // Convert underscores to hyphens and set the attribute
1447
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
1448
+ }
1449
+ }
1450
+ }
1451
+ }
1452
+
1453
+ // Construct the final HTML string
1454
+ let formHTML = `
1455
+ <div class="${this.divClass}">
1456
+ <label for="${id}">${label}</label>
1457
+ <input
1458
+ type="${type}"
1459
+ name="${name}"
1460
+ ${bindingDirective}
1461
+ id="${id}"
1462
+ ${additionalAttrs}
1463
+ ${validationAttrs}
1464
+ />
1465
+ </div>
1466
+ `.replace(/^\s*\n/gm, '').trim();
1467
+
1468
+ // Format the entire HTML using pretty
1469
+ let formattedHtml = pretty(formHTML, {
1470
+ indent_size: 2,
1471
+ wrap_line_length: 0,
1472
+ preserve_newlines: true, // Preserve existing newlines
1473
+ });
1474
+
1475
+ // Apply vertical layout to the <input> element only
1476
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
1477
+ // Reformat attributes into a vertical layout
1478
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
1479
+ return `<input\n${attributes}\n/>`;
1480
+ });
1481
+
1482
+ // Ensure the <div> block starts on a new line and remove extra blank lines
1483
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
1484
+ // Ensure <div> starts on a new line
1485
+ return `\n${match}\n`;
1486
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
1487
+
1488
+ return formattedHtml;
1489
+ }
1490
+
1491
+
1492
+ renderSearchField(type, name, label, validate, attributes, bindingSyntax) {
1493
+ // Define valid attributes for the search input type
1494
+ const searchInputAttributes = [
1495
+ 'required',
1496
+ 'pattern',
1497
+ 'placeholder',
1498
+ 'readonly',
1499
+ 'disabled',
1500
+ 'size',
1501
+ 'autocomplete',
1502
+ 'spellcheck',
1503
+ 'inputmode',
1504
+ 'title',
1505
+ ];
1506
+
1507
+ // Construct validation attributes
1508
+ let validationAttrs = '';
1509
+ if (validate) {
1510
+ Object.entries(validate).forEach(([key, value]) => {
1511
+ if (searchInputAttributes.includes(key)) {
1512
+ if (typeof value === 'boolean' && value) {
1513
+ validationAttrs += ` ${key}\n`;
1514
+ } else {
1515
+ switch (key) {
1516
+ case 'pattern':
1517
+ validationAttrs += ` ${key}="${value}"\n`;
1518
+ break;
1519
+ default:
1520
+ if (!searchInputAttributes.includes(key)) {
1521
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1522
+ }
1523
+ break;
1524
+ }
1525
+ }
1526
+ } else {
1527
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1528
+ }
1529
+ });
1530
+ }
1531
+
1532
+ // Handle the binding syntax
1533
+ let bindingDirective = '';
1534
+ if (bindingSyntax === 'bind:value' && name) {
1535
+ bindingDirective = ` bind:value="${name}"\n`;
1536
+ } if (bindingSyntax.startsWith('::') && name) {
1537
+ bindingDirective = ` bind:value="${name}"\n`;
1538
+ } if (bindingSyntax && !name) {
1539
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
1540
+ return;
1541
+ }
1542
+
1543
+ // Get the id from attributes or fall back to name
1544
+ let id = attributes.id || name;
1545
+
1546
+ // Construct additional attributes dynamically
1547
+ let additionalAttrs = '';
1548
+ for (const [key, value] of Object.entries(attributes)) {
1549
+ if (key !== 'id' && value !== undefined) {
1550
+ if (key.startsWith('on')) {
1551
+ // Handle event attributes
1552
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
1553
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
1554
+ } else {
1555
+ // Handle boolean attributes
1556
+ if (value === true) {
1557
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
1558
+ } else if (value !== false) {
1559
+ // Convert underscores to hyphens and set the attribute
1560
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
1561
+ }
1562
+ }
1563
+ }
1564
+ }
1565
+
1566
+ // Construct the final HTML string
1567
+ let formHTML = `
1568
+ <div class="${this.divClass}">
1569
+ <label for="${id}">${label}</label>
1570
+ <input
1571
+ type="${type}"
1572
+ name="${name}"
1573
+ ${bindingDirective}
1574
+ id="${id}"
1575
+ ${additionalAttrs}
1576
+ ${validationAttrs}
1577
+ />
1578
+ </div>
1579
+ `.replace(/^\s*\n/gm, '').trim();
1580
+
1581
+ // Format the entire HTML using pretty
1582
+ let formattedHtml = pretty(formHTML, {
1583
+ indent_size: 2,
1584
+ wrap_line_length: 0,
1585
+ preserve_newlines: true, // Preserve existing newlines
1586
+ });
1587
+
1588
+ // Apply vertical layout to the <input> element only
1589
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
1590
+ // Reformat attributes into a vertical layout
1591
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
1592
+ return `<input\n${attributes}\n/>`;
1593
+ });
1594
+
1595
+ // Ensure the <div> block starts on a new line and remove extra blank lines
1596
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
1597
+ // Ensure <div> starts on a new line
1598
+ return `\n${match}\n`;
1599
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
1600
+
1601
+ return formattedHtml;
1602
+ }
1603
+
1604
+
1605
+ renderColorField(type, name, label, validate, attributes, bindingSyntax) {
1606
+ // Define valid attributes for the color input type
1607
+ const colorInputAttributes = [
1608
+ 'required',
1609
+ 'readonly',
1610
+ 'disabled',
1611
+ 'autocomplete',
1612
+ 'inputmode',
1613
+ 'title',
1614
+ ];
1615
+
1616
+ // Construct validation attributes
1617
+ let validationAttrs = '';
1618
+ if (validate) {
1619
+ Object.entries(validate).forEach(([key, value]) => {
1620
+ if (colorInputAttributes.includes(key)) {
1621
+ if (typeof value === 'boolean' && value) {
1622
+ validationAttrs += ` ${key}\n`;
1623
+ } else {
1624
+ switch (key) {
1625
+ default:
1626
+ if (!colorInputAttributes.includes(key)) {
1627
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1628
+ }
1629
+ break;
1630
+ }
1631
+ }
1632
+ } else {
1633
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1634
+ }
1635
+ });
1636
+ }
1637
+
1638
+ // Handle the binding syntax
1639
+ let bindingDirective = '';
1640
+ if (bindingSyntax === 'bind:value') {
1641
+ bindingDirective = ` bind:value="${name}"\n`;
1642
+ } else if (bindingSyntax.startsWith('::') && name) {
1643
+ bindingDirective = ` bind:value="${name}"\n`;
1644
+ }
1645
+ if (bindingSyntax && !name) {
1646
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
1647
+ return;
1648
+ }
1649
+
1650
+ // Get the id from attributes or fall back to name
1651
+ let id = attributes.id || name;
1652
+
1653
+ // Construct additional attributes dynamically
1654
+ let additionalAttrs = '';
1655
+ for (const [key, value] of Object.entries(attributes)) {
1656
+ if (key !== 'id' && value !== undefined) {
1657
+ if (key.startsWith('on')) {
1658
+ // Handle event attributes
1659
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
1660
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
1661
+ } else {
1662
+ // Handle boolean attributes
1663
+ if (value === true) {
1664
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
1665
+ } else if (value !== false) {
1666
+ // Convert underscores to hyphens and set the attribute
1667
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
1668
+ }
1669
+ }
1670
+ }
1671
+ }
1672
+
1673
+ // Construct the final HTML string
1674
+ let formHTML = `
1675
+ <div class="${this.divClass}">
1676
+ <label for="${id}">${label}</label>
1677
+ <input
1678
+ type="${type}"
1679
+ name="${name}"
1680
+ ${bindingDirective}
1681
+ id="${id}"
1682
+ ${additionalAttrs}
1683
+ ${validationAttrs}
1684
+ />
1685
+ </div>
1686
+ `.replace(/^\s*\n/gm, '').trim();
1687
+
1688
+ // Format the entire HTML using pretty
1689
+ let formattedHtml = pretty(formHTML, {
1690
+ indent_size: 2,
1691
+ wrap_line_length: 0,
1692
+ preserve_newlines: true, // Preserve existing newlines
1693
+ });
1694
+
1695
+ // Apply vertical layout to the <input> element only
1696
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
1697
+ // Reformat attributes into a vertical layout
1698
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
1699
+ return `<input\n${attributes}\n/>`;
1700
+ });
1701
+
1702
+ // Ensure the <div> block starts on a new line and remove extra blank lines
1703
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
1704
+ // Ensure <div> starts on a new line
1705
+ return `\n${match}\n`;
1706
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
1707
+
1708
+ return formattedHtml;
1709
+ }
1710
+
1711
+
1712
+
1713
+
1714
+
1715
+
1716
+ renderFileField(type, name, label, validate, attributes, bindingSyntax) {
1717
+ // Define valid attributes for the file input type
1718
+ const fileInputAttributes = [
1719
+ 'required',
1720
+ 'accept',
1721
+ 'multiple',
1722
+ 'disabled',
1723
+ 'title',
1724
+ ];
1725
+
1726
+ // Construct validation attributes
1727
+ let validationAttrs = '';
1728
+ if (validate) {
1729
+ Object.entries(validate).forEach(([key, value]) => {
1730
+ if (fileInputAttributes.includes(key)) {
1731
+ if (typeof value === 'boolean' && value) {
1732
+ validationAttrs += ` ${key}\n`;
1733
+ } else {
1734
+ switch (key) {
1735
+ default:
1736
+ if (!fileInputAttributes.includes(key)) {
1737
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1738
+ }
1739
+ break;
1740
+ }
1741
+ }
1742
+ } else {
1743
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1744
+ }
1745
+ });
1746
+ }
1747
+
1748
+ // Handle the binding syntax
1749
+ let bindingDirective = '';
1750
+ if (bindingSyntax === 'bind:value') {
1751
+ bindingDirective = ` bind:value="${name}"\n`;
1752
+ } if (bindingSyntax.startsWith('::') && name) {
1753
+ bindingDirective = ` bind:value="${name}"\n`;
1754
+ }
1755
+ if (bindingSyntax && !name) {
1756
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
1757
+ return;
1758
+ }
1759
+
1760
+ // Get the id from attributes or fall back to name
1761
+ let id = attributes.id || name;
1762
+
1763
+ // Construct additional attributes dynamically
1764
+ let additionalAttrs = '';
1765
+ for (const [key, value] of Object.entries(attributes)) {
1766
+ if (key !== 'id' && value !== undefined) {
1767
+ if (key.startsWith('on')) {
1768
+ // Handle event attributes
1769
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
1770
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
1771
+ } else {
1772
+ // Handle boolean attributes
1773
+ if (value === true) {
1774
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
1775
+ } else if (value !== false) {
1776
+ // Convert underscores to hyphens and set the attribute
1777
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
1778
+ }
1779
+ }
1780
+ }
1781
+ }
1782
+
1783
+ // Construct the final HTML string
1784
+ let formHTML = `
1785
+ <div class="${this.divClass}">
1786
+ <label for="${id}">${label}</label>
1787
+ <input
1788
+ type="${type}"
1789
+ name="${name}"
1790
+ ${bindingDirective}
1791
+ id="${id}"
1792
+ ${additionalAttrs}
1793
+ ${validationAttrs}
1794
+ />
1795
+ </div>
1796
+ `.replace(/^\s*\n/gm, '').trim();
1797
+
1798
+ // Format the entire HTML using pretty
1799
+ let formattedHtml = pretty(formHTML, {
1800
+ indent_size: 2,
1801
+ wrap_line_length: 0,
1802
+ preserve_newlines: true, // Preserve existing newlines
1803
+ });
1804
+
1805
+ // Apply vertical layout to the <input> element only
1806
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
1807
+ // Reformat attributes into a vertical layout
1808
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
1809
+ return `<input\n${attributes}\n/>`;
1810
+ });
1811
+
1812
+ // Ensure the <div> block starts on a new line and remove extra blank lines
1813
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
1814
+ // Ensure <div> starts on a new line
1815
+ return `\n${match}\n`;
1816
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
1817
+
1818
+ return formattedHtml;
1819
+ }
1820
+
1821
+
1822
+
1823
+
1824
+
1825
+ renderHiddenField(type, name, label, validate, attributes, bindingSyntax) {
1826
+ // Define valid attributes for the hidden input type
1827
+ const validAttributes = [
1828
+ 'type',
1829
+ 'name',
1830
+ 'value',
1831
+ 'id',
1832
+ 'class',
1833
+ 'style',
1834
+ 'required',
1835
+ 'readonly',
1836
+ 'disabled',
1837
+ 'tabindex',
1838
+ ];
1839
+
1840
+ // Construct validation attributes
1841
+ let validationAttrs = '';
1842
+ if (validate) {
1843
+ Object.entries(validate).forEach(([key, value]) => {
1844
+ if (validAttributes.includes(key)) {
1845
+ if (typeof value === 'boolean' && value) {
1846
+ validationAttrs += ` ${key}\n`;
1847
+ } else {
1848
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1849
+ }
1850
+ } else {
1851
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1852
+ }
1853
+ });
1854
+ }
1855
+
1856
+ // Handle the binding syntax
1857
+ let bindingDirective = '';
1858
+ if (bindingSyntax === 'bind:value') {
1859
+ bindingDirective = ` bind:value="${name}"\n`;
1860
+ } if (bindingSyntax.startsWith('::') && name) {
1861
+ bindingDirective = ` bind:value="${name}"\n`;
1862
+ }
1863
+ if (bindingSyntax && !name) {
1864
+ console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
1865
+ return;
1866
+ }
1867
+
1868
+
1869
+ // Get the id from attributes or fall back to name
1870
+ let id = attributes.id || name;
1871
+
1872
+ // Construct additional attributes dynamically
1873
+ let additionalAttrs = '';
1874
+ for (const [key, value] of Object.entries(attributes)) {
1875
+ if (key !== 'id' && value !== undefined) {
1876
+ if (key.startsWith('on')) {
1877
+ // Handle event attributes
1878
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
1879
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
1880
+ } else {
1881
+ // Handle boolean attributes
1882
+ if (value === true) {
1883
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
1884
+ } else if (value !== false) {
1885
+ // Convert underscores to hyphens and set the attribute
1886
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
1887
+ }
1888
+ }
1889
+ }
1890
+ }
1891
+
1892
+ // Construct the final HTML string
1893
+ let formHTML = `
1894
+ <div class="${this.divClass}">
1895
+ <label for="${id}">${label}</label>
1896
+ <input
1897
+ type="${type}"
1898
+ name="${name}"
1899
+ ${bindingDirective}
1900
+ id="${id}"
1901
+ ${additionalAttrs}
1902
+ ${validationAttrs}
1903
+ />
1904
+ </div>
1905
+ `.replace(/^\s*\n/gm, '').trim();
1906
+
1907
+ // Format the entire HTML using pretty
1908
+ let formattedHtml = pretty(formHTML, {
1909
+ indent_size: 2,
1910
+ wrap_line_length: 0,
1911
+ preserve_newlines: true, // Preserve existing newlines
1912
+ });
1913
+
1914
+ // Apply vertical layout to the <input> element only
1915
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
1916
+ // Reformat attributes into a vertical layout
1917
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
1918
+ return `<input\n${attributes}\n/>`;
1919
+ });
1920
+
1921
+ // Ensure the <div> block starts on a new line and remove extra blank lines
1922
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
1923
+ // Ensure <div> starts on a new line
1924
+ return `\n${match}\n`;
1925
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
1926
+
1927
+ return formattedHtml;
1928
+ }
1929
+
1930
+
1931
+
1932
+ renderImageField(type, name, label, validate, attributes, bindingSyntax) {
1933
+ // Define valid validation attributes for image upload
1934
+ const imageUploadValidationAttributes = [
1935
+ 'accept',
1936
+ 'required',
1937
+ 'minwidth',
1938
+ 'maxwidth',
1939
+ 'minheight',
1940
+ 'maxheight',
1941
+ ];
1942
+
1943
+ // Construct validation attributes
1944
+ let validationAttrs = '';
1945
+ if (validate) {
1946
+ Object.entries(validate).forEach(([key, value]) => {
1947
+ if (imageUploadValidationAttributes.includes(key)) {
1948
+ if (key === 'accept') {
1949
+ validationAttrs += `accept="${value}"\n`;
1950
+ } else if (['required', 'minwidth', 'maxwidth', 'minheight', 'maxheight'].includes(key)) {
1951
+ validationAttrs += `${key}="${value}"\n`;
1952
+ } else {
1953
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1954
+ }
1955
+ } else {
1956
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
1957
+ }
1958
+ });
1959
+ }
1960
+
1961
+ // Handle the binding syntax
1962
+ let bindingDirective = '';
1963
+ if (bindingSyntax === 'bind:value') {
1964
+ bindingDirective = ` bind:value="${name}"`;
1965
+ } else if (bindingSyntax.startsWith('::')) {
1966
+ bindingDirective = ` bind:value="${name}"`;
1967
+ }
1968
+
1969
+ // Get the id from attributes or fall back to name
1970
+ let id = attributes.id || name;
1971
+
1972
+ // Construct additional attributes dynamically
1973
+ let additionalAttrs = '';
1974
+ for (const [key, value] of Object.entries(attributes)) {
1975
+ if (key !== 'id' && value !== undefined) {
1976
+ if (key.startsWith('on')) {
1977
+ // Handle event attributes
1978
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
1979
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
1980
+ } else {
1981
+ // Handle boolean attributes
1982
+ if (value === true) {
1983
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
1984
+ } else if (value !== false) {
1985
+ // Convert underscores to hyphens and set the attribute
1986
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
1987
+ }
1988
+ }
1989
+ }
1990
+ }
1991
+
1992
+ // Construct the final HTML string
1993
+ let formHTML = `
1994
+ <div class="${this.divClass}">
1995
+ <label for="${id}">${label}</label>
1996
+ <input
1997
+ type="${type}"
1998
+ name="${name}"
1999
+ ${bindingDirective}
2000
+ id="${id}"
2001
+ ${additionalAttrs}
2002
+ ${validationAttrs}
2003
+ />
2004
+ </div>
2005
+ `.replace(/^\s*\n/gm, '').trim();
2006
+
2007
+ // Format the entire HTML using pretty
2008
+ let formattedHtml = pretty(formHTML, {
2009
+ indent_size: 2,
2010
+ wrap_line_length: 0,
2011
+ preserve_newlines: true, // Preserve existing newlines
2012
+ });
2013
+
2014
+ // Apply vertical layout to the <input> element only
2015
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
2016
+ // Reformat attributes into a vertical layout
2017
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
2018
+ return `<input\n${attributes}\n/>`;
2019
+ });
2020
+
2021
+ // Ensure the <div> block starts on a new line and remove extra blank lines
2022
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
2023
+ // Ensure <div> starts on a new line
2024
+ return `\n${match}\n`;
2025
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
2026
+
2027
+ return formattedHtml;
2028
+ }
2029
+
2030
+
2031
+
2032
+
2033
+ renderImageField(type, name, label, validate, attributes, bindingSyntax) {
2034
+ // Define valid validation attributes for image upload
2035
+ const imageUploadValidationAttributes = [
2036
+ 'accept',
2037
+ 'required',
2038
+ 'minwidth',
2039
+ 'maxwidth',
2040
+ 'minheight',
2041
+ 'maxheight',
2042
+ ];
2043
+
2044
+ // Construct validation attributes
2045
+ let validationAttrs = '';
2046
+ if (validate) {
2047
+ Object.entries(validate).forEach(([key, value]) => {
2048
+ if (imageUploadValidationAttributes.includes(key)) {
2049
+ validationAttrs += `${key}="${value}"\n`;
2050
+ } else {
2051
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
2052
+ }
2053
+ });
2054
+ }
2055
+
2056
+ // Handle the binding syntax
2057
+ let bindingDirective = '';
2058
+ if (bindingSyntax === 'bind:value' || bindingSyntax.startsWith('::')) {
2059
+ bindingDirective = `bind:value="${name}"\n`;
2060
+ }
2061
+
2062
+ // Get the id from attributes or fall back to name
2063
+ let id = attributes.id || name;
2064
+
2065
+ // Construct additional attributes dynamically
2066
+ let additionalAttrs = '';
2067
+ for (const [key, value] of Object.entries(attributes)) {
2068
+ if (key !== 'id' && value !== undefined) {
2069
+ if (key.startsWith('on')) {
2070
+ // Handle event attributes
2071
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
2072
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
2073
+ } else {
2074
+ // Handle boolean attributes
2075
+ if (value === true) {
2076
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
2077
+ } else if (value !== false) {
2078
+ // Convert underscores to hyphens and set the attribute
2079
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
2080
+ }
2081
+ }
2082
+ }
2083
+ }
2084
+
2085
+ // Construct the final HTML string
2086
+ let formHTML = `
2087
+ <div class="${this.divClass}">
2088
+ <label for="${id}">${label}</label>
2089
+ <input
2090
+ type="${type}"
2091
+ name="${name}"
2092
+ ${bindingDirective}
2093
+ id="${id}"
2094
+ ${additionalAttrs}
2095
+ ${validationAttrs}
2096
+ />
2097
+ </div>
2098
+ `.replace(/^\s*\n/gm, '').trim();
2099
+
2100
+ // Format the entire HTML using pretty
2101
+ let formattedHtml = pretty(formHTML, {
2102
+ indent_size: 2,
2103
+ wrap_line_length: 0,
2104
+ preserve_newlines: true, // Preserve existing newlines
2105
+ });
2106
+
2107
+ // Apply vertical layout to the <input> element only
2108
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
2109
+ // Reformat attributes into a vertical layout
2110
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
2111
+ return `<input\n${attributes}\n/>`;
2112
+ });
2113
+
2114
+ // Ensure the <div> block starts on a new line and remove extra blank lines
2115
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
2116
+ // Ensure <div> starts on a new line
2117
+ return `\n${match}\n`;
2118
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
2119
+
2120
+ return formattedHtml;
2121
+ }
2122
+
2123
+ renderTextareaField(type, name, label, validate, attributes, bindingSyntax) {
2124
+ // Define valid validation attributes for textarea
2125
+ const textareaValidationAttributes = [
2126
+ 'required',
2127
+ 'minlength',
2128
+ 'maxlength',
2129
+ 'rows',
2130
+ 'cols',
2131
+ ];
2132
+
2133
+ // Construct validation and dimension attributes
2134
+ let validationAttrs = '';
2135
+ let dimensionAttrs = '';
2136
+
2137
+ if (validate) {
2138
+ Object.entries(validate).forEach(([key, value]) => {
2139
+ if (textareaValidationAttributes.includes(key)) {
2140
+ if (key === 'required') {
2141
+ validationAttrs += `required\n`;
2142
+ } else if (['minlength', 'maxlength'].includes(key)) {
2143
+ validationAttrs += `${key}="${value}"\n`;
2144
+ } else if (['rows', 'cols'].includes(key)) {
2145
+ dimensionAttrs += `${key}="${value}"\n`;
2146
+ }
2147
+ } else {
2148
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
2149
+ }
2150
+ });
2151
+ }
2152
+
2153
+ // Handle the binding syntax
2154
+ let bindingDirective = '';
2155
+ if (bindingSyntax === 'bind:value' || bindingSyntax.startsWith('::')) {
2156
+ bindingDirective = `bind:value="${name}"\n`;
2157
+ }
2158
+
2159
+ // Get the id from attributes or fall back to name
2160
+ let id = attributes.id || name;
2161
+
2162
+ // Construct additional attributes dynamically
2163
+ let additionalAttrs = '';
2164
+ for (const [key, value] of Object.entries(attributes)) {
2165
+ if (key !== 'id' && value !== undefined) {
2166
+ if (key.startsWith('on')) {
2167
+ // Handle event attributes
2168
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
2169
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
2170
+ } else {
2171
+ // Handle boolean attributes
2172
+ if (value === true) {
2173
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
2174
+ } else if (value !== false) {
2175
+ // Convert underscores to hyphens and set the attribute
2176
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
2177
+ }
2178
+ }
2179
+ }
2180
+ }
2181
+
2182
+ // Construct the final HTML string
2183
+ let formHTML = `
2184
+ <div class="${this.divClass}">
2185
+ <label for="${id}">${label}</label>
2186
+ <textarea
2187
+ name="${name}"
2188
+ ${bindingDirective}
2189
+ ${dimensionAttrs}
2190
+ id="${id}"
2191
+ ${additionalAttrs}
2192
+ ${validationAttrs}
2193
+ ></textarea>
2194
+ </div>
2195
+ `.replace(/^\s*\n/gm, '').trim();
2196
+
2197
+ // Format the entire HTML using pretty
2198
+ let formattedHtml = pretty(formHTML, {
2199
+ indent_size: 2,
2200
+ wrap_line_length: 0,
2201
+ preserve_newlines: true, // Preserve existing newlines
2202
+ });
2203
+
2204
+ // Apply vertical layout to the <textarea> element only
2205
+ formattedHtml = formattedHtml.replace(/<textarea\s+([^>]*)<\/textarea>/, (match, p1) => {
2206
+ // Reformat attributes into a vertical layout
2207
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
2208
+ return `<textarea\n${attributes}\n></textarea>`;
2209
+ });
2210
+
2211
+ // Ensure the <div> block starts on a new line and remove extra blank lines
2212
+ formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
2213
+ // Ensure <div> starts on a new line
2214
+ return `\n${match}\n`;
2215
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
2216
+
2217
+ return formattedHtml;
2218
+ }
2219
+
2220
+
2221
+
2222
+
2223
+ renderRadioField(type, name, label, validate, attributes, bindingSyntax, options) {
2224
+ // Define valid validation attributes for radio fields
2225
+ const radioValidationAttributes = ['required'];
2226
+
2227
+ // Construct validation attributes
2228
+ let validationAttrs = '';
2229
+ if (validate) {
2230
+ Object.entries(validate).forEach(([key, value]) => {
2231
+ if (radioValidationAttributes.includes(key)) {
2232
+ if (key === 'required') {
2233
+ validationAttrs += `required\n`;
2234
+ }
2235
+ } else {
2236
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
2237
+ }
2238
+ });
2239
+ }
2240
+
2241
+ // Handle the binding syntax
2242
+ let bindingDirective = '';
2243
+ if (bindingSyntax === 'bind:value') {
2244
+ bindingDirective = ` bind:value="${name}"\n`;
2245
+ } else if (bindingSyntax.startsWith('::')) {
2246
+ bindingDirective = ` bind:value="${name}"\n`;
2247
+ }
2248
+
2249
+ // Define attributes for the radio inputs
2250
+ let id = attributes.id || name;
2251
+
2252
+ // Handle additional attributes
2253
+ let additionalAttrs = '';
2254
+ for (const [key, value] of Object.entries(attributes)) {
2255
+ if (key !== 'id' && value !== undefined) {
2256
+ if (key.startsWith('on')) {
2257
+ // Handle event attributes
2258
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
2259
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
2260
+ } else {
2261
+ // Handle boolean attributes
2262
+ if (value === true) {
2263
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
2264
+ } else if (value !== false) {
2265
+ // Convert underscores to hyphens and set the attribute
2266
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
2267
+ }
2268
+ }
2269
+ }
2270
+ }
2271
+
2272
+ // Construct radio button HTML based on options
2273
+ let optionsHTML = '';
2274
+ if (options && options.length) {
2275
+ optionsHTML = options.map((option) => {
2276
+ return `
2277
+ <div>
2278
+ <input type="radio" name="${name}" value="${option.value}"${bindingDirective} ${additionalAttrs}
2279
+ ${attributes.id ? `id="${id}-${option.value}"` : `id="${id}-${option.value}"`}
2280
+ />
2281
+ <label for="${attributes.id ? `${id}-${option.value}` : `${id}-${option.value}`}">${option.label}</label>
2282
+ </div>
2283
+ `;
2284
+ }).join('');
2285
+ }
2286
+
2287
+ // Construct the final HTML string
2288
+ let formHTML = `
2289
+ <fieldset class="${this.radioGroupClass}">
2290
+ <legend>${label}</legend>
2291
+ ${optionsHTML}
2292
+ </fieldset>
2293
+ `.replace(/^\s*\n/gm, '').trim();
2294
+
2295
+ // Format the entire HTML using pretty
2296
+ let formattedHtml = pretty(formHTML, {
2297
+ indent_size: 2,
2298
+ wrap_line_length: 0,
2299
+ preserve_newlines: true, // Preserve existing newlines
2300
+ });
2301
+
2302
+ // Apply vertical layout to the <input> elements only
2303
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/g, (match, p1) => {
2304
+ // Reformat attributes into a vertical layout
2305
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
2306
+ return `<input\n${attributes}\n/>`;
2307
+ });
2308
+
2309
+ // Ensure the <fieldset> block starts on a new line and remove extra blank lines
2310
+ formattedHtml = formattedHtml.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
2311
+ // Ensure <fieldset> starts on a new line
2312
+ return `\n${match}\n`;
2313
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
2314
+
2315
+ return formattedHtml;
2316
+ }
2317
+
2318
+
2319
+ renderCheckboxField(type, name, label, validate, attributes, bindingSyntax, options) {
2320
+ // Define valid validation attributes for checkbox fields
2321
+ const checkboxValidationAttributes = ['required'];
2322
+
2323
+ // Construct validation attributes
2324
+ let validationAttrs = '';
2325
+ if (validate) {
2326
+ Object.entries(validate).forEach(([key, value]) => {
2327
+ if (checkboxValidationAttributes.includes(key)) {
2328
+ if (key === 'required') {
2329
+ validationAttrs += `${key}\n`;
2330
+ }
2331
+ } else {
2332
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
2333
+ }
2334
+ });
2335
+ }
2336
+
2337
+ // Handle the binding syntax
2338
+ let bindingDirective = '';
2339
+ if (bindingSyntax === 'bind:checked') {
2340
+ bindingDirective = ` bind:checked="${name}"\n`;
2341
+ } else if (bindingSyntax.startsWith('::')) {
2342
+ bindingDirective = ` bind:checked="${name}"\n`;
2343
+ }
2344
+
2345
+ // Define attributes for the checkbox inputs
2346
+ let id = attributes.id || name;
2347
+
2348
+ // Handle additional attributes
2349
+ let additionalAttrs = '';
2350
+ for (const [key, value] of Object.entries(attributes)) {
2351
+ if (key !== 'id' && value !== undefined) {
2352
+ if (key.startsWith('on')) {
2353
+ // Handle event attributes
2354
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
2355
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
2356
+ } else {
2357
+ // Handle boolean attributes
2358
+ if (value === true) {
2359
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
2360
+ } else if (value !== false) {
2361
+ // Convert underscores to hyphens and set the attribute
2362
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
2363
+ }
2364
+ }
2365
+ }
2366
+ }
2367
+
2368
+ // Construct checkbox HTML based on options
2369
+ let optionsHTML = '';
2370
+ if (Array.isArray(options)) {
2371
+ optionsHTML = options.map((option) => {
2372
+ const optionId = `${id}-${option.value}`;
2373
+ return `
2374
+ <div>
2375
+ <input type="checkbox" name="${name}" value="${option.value}"${bindingDirective} ${additionalAttrs}
2376
+ ${attributes.id ? `id="${optionId}"` : `id="${optionId}"`}
2377
+ />
2378
+ <label for="${optionId}">${option.label}</label>
2379
+ </div>
2380
+ `;
2381
+ }).join('');
2382
+ }
2383
+
2384
+ // Construct the final HTML string
2385
+ let formHTML = `
2386
+ <fieldset class="${this.checkboxGroupClass}">
2387
+ <legend>${label}</legend>
2388
+ ${optionsHTML}
2389
+ </fieldset>
2390
+ `.replace(/^\s*\n/gm, '').trim();
2391
+
2392
+ // Format the entire HTML using pretty
2393
+ let formattedHtml = pretty(formHTML, {
2394
+ indent_size: 2,
2395
+ wrap_line_length: 0,
2396
+ preserve_newlines: true, // Preserve existing newlines
2397
+ });
2398
+
2399
+ // Apply vertical layout to the <input> elements only
2400
+ formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/g, (match, p1) => {
2401
+ // Reformat attributes into a vertical layout
2402
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
2403
+ return `<input\n${attributes}\n/>`;
2404
+ });
2405
+
2406
+ // Ensure the <fieldset> block starts on a new line and remove extra blank lines
2407
+ formattedHtml = formattedHtml.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
2408
+ // Ensure <fieldset> starts on a new line
2409
+ return `\n${match}\n`;
2410
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
2411
+
2412
+ return formattedHtml;
2413
+ }
2414
+
2415
+
2416
+
2417
+ renderSingleSelectField(type, name, label, validate, attributes, bindingSyntax, options) {
2418
+ // Define valid validation attributes for select fields
2419
+ const selectValidationAttributes = ['required'];
2420
+
2421
+ // Construct validation attributes
2422
+ let validationAttrs = '';
2423
+ if (validate) {
2424
+ Object.entries(validate).forEach(([key, value]) => {
2425
+ if (selectValidationAttributes.includes(key)) {
2426
+ if (key === 'required') {
2427
+ validationAttrs += `${key} `;
2428
+ }
2429
+ } else {
2430
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
2431
+ }
2432
+ });
2433
+ }
2434
+
2435
+ // Handle the binding syntax
2436
+ let bindingDirective = '';
2437
+ if (typeof bindingSyntax === 'string' && bindingSyntax.startsWith('::')) {
2438
+ bindingDirective = ` bind:value="${name}" `;
2439
+ }
2440
+
2441
+ // Define attributes for the select field
2442
+ let id = attributes.id || name;
2443
+ let dimensionAttrs = ''; // No dimension attributes applicable for select fields
2444
+
2445
+ // Handle additional attributes
2446
+ let additionalAttrs = '';
2447
+ for (const [key, value] of Object.entries(attributes)) {
2448
+ if (key !== 'id' && value !== undefined) {
2449
+ if (key.startsWith('on')) {
2450
+ // Handle event attributes
2451
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
2452
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
2453
+ } else {
2454
+ // Handle boolean attributes
2455
+ if (value === true) {
2456
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
2457
+ } else if (value !== false) {
2458
+ // Convert underscores to hyphens and set the attribute
2459
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
2460
+ }
2461
+ }
2462
+ }
2463
+ }
2464
+
2465
+ // Construct select options HTML based on options
2466
+ let selectHTML = '';
2467
+ if (Array.isArray(options)) {
2468
+ // Add a default option
2469
+ selectHTML += `
2470
+ <option value="">Choose an option</option>
2471
+ `;
2472
+
2473
+ // Add the provided options
2474
+ selectHTML += options.map((option) => {
2475
+ const isSelected = option.selected ? ' selected' : '';
2476
+ return `
2477
+ <option value="${option.value}"${isSelected}>${option.label}</option>
2478
+ `;
2479
+ }).join('');
2480
+ }
2481
+
2482
+ // Construct the final HTML string
2483
+ let formHTML = `
2484
+ <fieldset class="${this.selectGroupClass}">
2485
+ <label for="${id}">${label}</label>
2486
+ <select name="${name}"
2487
+ ${bindingDirective}
2488
+ ${dimensionAttrs}
2489
+ id="${id}"
2490
+ ${additionalAttrs}
2491
+ ${validationAttrs}
2492
+ >
2493
+ ${selectHTML}
2494
+ </select>
2495
+ </fieldset>
2496
+ `.replace(/^\s*\n/gm, '').trim();
2497
+
2498
+ // Format the entire HTML using pretty
2499
+ let formattedHtml = pretty(formHTML, {
2500
+ indent_size: 2,
2501
+ wrap_line_length: 0,
2502
+ preserve_newlines: true, // Preserve existing newlines
2503
+ });
2504
+
2505
+ // Apply vertical layout to the <select> element and its children
2506
+ formattedHtml = formattedHtml.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g, (match, p1, p2) => {
2507
+ // Reformat attributes into a vertical layout
2508
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
2509
+ return `<select\n${attributes}\n>\n${p2.trim()}\n</select>`;
2510
+ });
2511
+
2512
+ // Ensure the <fieldset> block starts on a new line and remove extra blank lines
2513
+ formattedHtml = formattedHtml.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
2514
+ // Ensure <fieldset> starts on a new line
2515
+ return `\n${match}\n`;
2516
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
2517
+
2518
+ return formattedHtml;
2519
+ }
2520
+
2521
+
2522
+
2523
+
2524
+ renderMultipleSelectField(type, name, label, validate, attributes, bindingSyntax, options) {
2525
+ // Define valid validation attributes for multiple select fields
2526
+ const selectValidationAttributes = ['required', 'minlength', 'maxlength'];
2527
+
2528
+ // Construct validation attributes
2529
+ let validationAttrs = '';
2530
+ if (validate) {
2531
+ Object.entries(validate).forEach(([key, value]) => {
2532
+ if (selectValidationAttributes.includes(key)) {
2533
+ if (key === 'required') {
2534
+ validationAttrs += `${key} `;
2535
+ } else if (key === 'minlength') {
2536
+ validationAttrs += `minlength="${value}" `;
2537
+ } else if (key === 'maxlength') {
2538
+ validationAttrs += `maxlength="${value}" `;
2539
+ }
2540
+ } else {
2541
+ console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
2542
+ }
2543
+ });
2544
+ }
2545
+
2546
+ // Handle the binding syntax
2547
+ let bindingDirective = '';
2548
+ if (typeof bindingSyntax === 'string' && bindingSyntax.startsWith('::')) {
2549
+ bindingDirective = ` bind:value="${name}" `;
2550
+ }
2551
+
2552
+ // Define attributes for the select field
2553
+ let id = attributes.id || name;
2554
+ let dimensionAttrs = ''; // No dimension attributes applicable for select fields
2555
+
2556
+ // Handle additional attributes
2557
+ let additionalAttrs = '';
2558
+ for (const [key, value] of Object.entries(attributes)) {
2559
+ if (key !== 'id' && value !== undefined) {
2560
+ if (key.startsWith('on')) {
2561
+ // Handle event attributes
2562
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
2563
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
2564
+ } else {
2565
+ // Handle boolean attributes
2566
+ if (value === true) {
2567
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
2568
+ } else if (value !== false) {
2569
+ // Convert underscores to hyphens and set the attribute
2570
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
2571
+ }
2572
+ }
2573
+ }
2574
+ }
2575
+
2576
+ // Construct select options HTML based on options
2577
+ let selectHTML = '';
2578
+ if (Array.isArray(options)) {
2579
+ selectHTML = options.map((option) => {
2580
+ const isSelected = option.selected ? ' selected' : '';
2581
+ return `
2582
+ <option value="${option.value}"${isSelected}>${option.label}</option>
2583
+ `;
2584
+ }).join('');
2585
+ }
2586
+
2587
+ // Define multiple attribute for multi-select
2588
+ const multipleAttr = 'multiple';
2589
+
2590
+ // Construct the final HTML string
2591
+ let formHTML = `
2592
+ <fieldset class="${this.selectGroupClass}">
2593
+ <label for="${id}">${label}</label>
2594
+ <select name="${name}"
2595
+ ${bindingDirective}
2596
+ ${dimensionAttrs}
2597
+ id="${id}"
2598
+ ${additionalAttrs}
2599
+ ${validationAttrs}
2600
+ ${multipleAttr}
2601
+ >
2602
+ ${selectHTML}
2603
+ </select>
2604
+ </fieldset>
2605
+ `.replace(/^\s*\n/gm, '').trim();
2606
+
2607
+ // Format the entire HTML using pretty
2608
+ let formattedHtml = pretty(formHTML, {
2609
+ indent_size: 2,
2610
+ wrap_line_length: 0,
2611
+ preserve_newlines: true, // Preserve existing newlines
2612
+ });
2613
+
2614
+ // Apply vertical layout to the <select> element and its children
2615
+ formattedHtml = formattedHtml.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g, (match, p1, p2) => {
2616
+ // Reformat attributes into a vertical layout
2617
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
2618
+ return `<select\n${attributes}\n>\n${p2.trim()}\n</select>`;
2619
+ });
2620
+
2621
+ // Ensure the <fieldset> block starts on a new line and remove extra blank lines
2622
+ formattedHtml = formattedHtml.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
2623
+ // Ensure <fieldset> starts on a new line
2624
+ return `\n${match}\n`;
2625
+ }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
2626
+
2627
+ return formattedHtml;
2628
+ }
2629
+
2630
+
2631
+
2632
+ renderSubmitButton(type, name, label, attributes) {
2633
+ // Define id attribute or fallback to name
2634
+ const id = attributes.id || name;
2635
+
2636
+ // Handle additional attributes
2637
+ let additionalAttrs = '';
2638
+ for (const [key, value] of Object.entries(attributes)) {
2639
+ if (key !== 'id' && value !== undefined) {
2640
+ if (key.startsWith('on')) {
2641
+ // Handle event attributes
2642
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
2643
+ additionalAttrs += ` ${key}="${eventValue}"`;
2644
+ } else {
2645
+ // Handle boolean attributes
2646
+ if (value === true) {
2647
+ additionalAttrs += ` ${key.replace(/_/g, '-')}`;
2648
+ } else if (value !== false) {
2649
+ // Convert underscores to hyphens and set the attribute
2650
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"`;
2651
+ }
2652
+ }
2653
+ }
2654
+ }
2655
+
2656
+ // Construct the final HTML string
2657
+ const formHTML = `
2658
+ <input type="${type}"
2659
+ id="${id}"
2660
+ value="${label}"
2661
+ ${additionalAttrs}
2662
+ />
2663
+ `.replace(/^\s*\n/gm, '').trim();
2664
+
2665
+ // Format the entire HTML using pretty
2666
+ const formattedHtml = pretty(formHTML, {
2667
+ indent_size: 2,
2668
+ wrap_line_length: 0,
2669
+ preserve_newlines: true, // Preserve existing newlines
2670
+ });
2671
+
2672
+ return formattedHtml;
2673
+ }
2674
+
2675
+
2676
+
2677
+
2678
+
2679
+ renderFormHTML () {
2680
+
2681
+ this.formMarkUp+= '</form>';
2682
+ const formContainer = document.getElementById('formique');
2683
+ formContainer.innerHTML = this.formMarkUp;
2684
+ //return this.formMarkUp;
2685
+
2686
+
2687
+ }
2688
+
2689
+
2690
+
2691
+
2692
+ // no renderMethod below here
2693
+ }
2694
+
2695
+
2696
+
2697
+
2698
+
2699
+
2700
+
2701
+
2702
+
2703
+
2704
+
2705
+
2706
+
2707
+
2708
+
2709
+ /*
2710
+
2711
+
2712
+ const formSchema = [
2713
+ ['text', 'firstName', 'First Name', { minlength: 2, maxlength: 5, required: true, disabled: true}, { value: "John", id: 'firstNameInput', class: 'form-input', style: 'width: 100%;', oninput: "incrementer()"}, '::firstName'],
2714
+ ['email', 'email', 'Email', { required: true}, { class: 'form-input', style: 'width: 100%;'}, '::emailValue'],
2715
+ ['number', 'age', 'Your Age', {required: false}, { id: 'age12'}, '::age'],
2716
+ ['password', 'password', 'Password', { minlength: 8, required: true }, { class: 'form-control', style: 'width: 100%;' }, '::passwordValue'],
2717
+ ['tel', 'phoneNumber', 'Phone Number', { required: true }, { class: 'form-control', style: 'width: 100%;' }, '::telValue'],
2718
+ ['date', 'birthdate', 'Birth Date', { required: true }, { id: 'birthdateInput', class: 'form-control', style: 'width: 100%;' }, '::date'],
2719
+ ['time', 'meetingTime', 'Meeting Time', { required: true }, { id: 'meetingTimeInput', class: 'form-control', style: 'width: 100%;' }, '::time'],
2720
+ ['datetime-local', 'meetingDateTime', 'Meeting Date & Time', { required: true }, { id: 'meetingDateTimeInput', class: 'form-control', style: 'width: 100%;' }, '::meetingDateTime'],
2721
+ ['month', 'eventMonth', 'Event Month', { required: true }, { id: 'eventMonthInput', class: 'form-control', style: 'width: 100%;' }, '::eventMonth'],
2722
+ ['week', 'eventWeek', 'Event Week', { required: true }, { id: 'eventWeekInput', class: 'form-control', style: 'width: 100%;' }, '::eventWeek'],
2723
+ ['url', 'websiteUrl', 'Website URL', { required: true}, { id: 'websiteUrlInput', class: 'form-control', style: 'width: 100%;' }, 'bind:value'],
2724
+ ['search', 'searchQuery', 'Search', { required: true }, { id: 'searchQueryInput', class: 'form-control', style: 'width: 100%;' }, '::searchQuery'],
2725
+ ['color', 'colorPicker', 'Pick a Color', { required: true }, { id: 'colorPickerInput', class: 'form-control', style: 'width: 100%;' }, '::colorValue'],
2726
+ ['file', 'terms', 'Upload File', { required: true }, { id: 'my-file', class: 'form-control', style: 'width: 100%;' }, 'bind:value'],
2727
+ ['hidden', 'user_id', '', { required: true }, {}, '::user_id'],
2728
+ ['image','profilePicture','Profile Picture', { required: true, accept: 'image/*' },
2729
+ { id: 'profilePictureInput', class: 'form-control', style: 'width: 100%;' },
2730
+ 'bind:value'],
2731
+ ['textarea', 'comments', 'Comments',
2732
+ { required: true, minlength: 10, maxlength: 200, rows: 4, cols: 50 },
2733
+ { id: 'commentsTextarea', class: 'form-control', style: 'width: 100%; height: 100px;' },
2734
+ '::comments'],
2735
+
2736
+
2737
+ [
2738
+ 'radio',
2739
+ 'gender',
2740
+ 'Gender',
2741
+ { required: true },
2742
+ { id: 'genderRadio', class: 'form-radio-input', style: 'margin-left: 1rem;', onchange: 'actioner()' },
2743
+ '::gender',
2744
+ [
2745
+ { value: 'male', label: 'Male' },
2746
+ { value: 'female', label: 'Female' },
2747
+ { value: 'other', label: 'Other' },
2748
+ ]
2749
+ ],
2750
+
2751
+
2752
+ [
2753
+ 'checkbox',
2754
+ 'preferences',
2755
+ 'Preferences',
2756
+ { required: true },
2757
+ { id: 'preferencesCheckbox', class: 'form-checkbox-input', style: 'margin-left: 1rem;', onchange: 'submit' },
2758
+ '::preferences',
2759
+ [
2760
+ { value: 'news', label: 'Newsletter' },
2761
+ { value: 'updates', label: 'Product Updates' },
2762
+ { value: 'offers', label: 'Special Offers' },
2763
+ ]
2764
+ ],
2765
+
2766
+
2767
+
2768
+ [
2769
+ 'singleSelect',
2770
+ 'colors',
2771
+ 'Colors',
2772
+ { required: true }, // Validation options
2773
+ { id: 'colorsSelect', class: 'form-select-input', style: 'margin-left: 1rem;', onchange: 'trigger' }, // Attributes
2774
+ '::colors', // Binding syntax
2775
+ [
2776
+ { value: 'red', label: 'Red', selected: false },
2777
+ { value: 'green', label: 'Green'},
2778
+ { value: 'blue', label: 'Blue', selected: true},
2779
+ ] // Options
2780
+ ],
2781
+
2782
+
2783
+
2784
+ [
2785
+ 'multipleSelect', // Type of field
2786
+ 'colors', // Name/identifier of the field
2787
+ 'Colors', // Label of the field
2788
+ { required: true, min: 2, max: 3 }, // Validation options
2789
+ { id: 'colorsSelect', class: 'form-select-input', style: 'margin-left: 1rem;', onchange: 'alerter' }, // Attributes
2790
+ '::colors', // Binding syntax
2791
+ [
2792
+ { value: 'red', label: 'Red' },
2793
+ { value: 'green', label: 'Green' },
2794
+ { value: 'blue', label: 'Blue', selected: false },
2795
+ { value: 'yellow', label: 'Yellow', selected: false },
2796
+ ] // Options
2797
+ ],
2798
+
2799
+
2800
+ [
2801
+ 'submit',
2802
+ 'submitButton',
2803
+ 'Submit',
2804
+ { required: true },
2805
+ { id: 'submitBtn', class: 'form-submit-btn', style: 'margin-top: 1rem;' }
2806
+ ],
2807
+
2808
+
2809
+
2810
+
2811
+ ];
2812
+
2813
+
2814
+ */
2815
+
2816
+ const formSchema = [
2817
+ // Text Input Field
2818
+ [
2819
+ 'text',
2820
+ 'firstName',
2821
+ 'First Name',
2822
+ { minlength: 2, maxlength: 5, required: true, disabled: true }, // Validation options
2823
+ { value: "John", id: 'firstNameInput', class: 'form-input', style: 'width: 100%;', oninput: "incrementer()" }, // Attributes
2824
+ '::firstName' // Binding syntax
2825
+ ],
2826
+
2827
+ // URL Input Field
2828
+ [
2829
+ 'url',
2830
+ 'websiteUrl',
2831
+ 'Website URL',
2832
+ { required: true }, // Validation options
2833
+ { id: 'websiteUrlInput', class: 'form-control', style: 'width: 100%;' }, // Attributes
2834
+ 'bind:value' // Binding syntax
2835
+ ],
2836
+
2837
+ // Radio Input Field
2838
+ [
2839
+ 'radio',
2840
+ 'gender',
2841
+ 'Gender',
2842
+ { required: true }, // Validation options
2843
+ { id: 'genderRadio', class: 'form-radio-input', style: 'margin-left: 1rem;', onchange: 'actioner()' }, // Attributes
2844
+ '::gender', // Binding syntax
2845
+ [
2846
+ { value: 'male', label: 'Male' }, // Options
2847
+ { value: 'female', label: 'Female' },
2848
+ { value: 'other', label: 'Other' }
2849
+ ]
2850
+ ],
2851
+
2852
+ // Checkbox Input Field
2853
+ [
2854
+ 'checkbox',
2855
+ 'preferences',
2856
+ 'Preferences',
2857
+ { required: true }, // Validation options
2858
+ { id: 'preferencesCheckbox', class: 'form-checkbox-input', style: 'margin-left: 1rem;', onchange: 'submit' }, // Attributes
2859
+ '::preferences', // Binding syntax
2860
+ [
2861
+ { value: 'news', label: 'Newsletter' }, // Options
2862
+ { value: 'updates', label: 'Product Updates' },
2863
+ { value: 'offers', label: 'Special Offers' }
2864
+ ]
2865
+ ],
2866
+
2867
+ // Single Select Input Field
2868
+ [
2869
+ 'singleSelect',
2870
+ 'colors',
2871
+ 'Colors',
2872
+ { required: true }, // Validation options
2873
+ { id: 'colorsSelect', class: 'form-select-input', style: 'margin-left: 1rem;', onchange: 'trigger' }, // Attributes
2874
+ '::colors', // Binding syntax
2875
+ [
2876
+ { value: 'red', label: 'Red' }, // Options
2877
+ { value: 'green', label: 'Green' },
2878
+ { value: 'blue', label: 'Blue', selected: true }
2879
+ ]
2880
+ ],
2881
+
2882
+ // Multiple Select Input Field
2883
+ [
2884
+ 'multipleSelect', // Type of field
2885
+ 'colors', // Name/identifier of the field
2886
+ 'Colors', // Label of the field
2887
+ { required: true, min: 2, max: 3 }, // Validation options
2888
+ { id: 'colorsSelect', class: 'form-select-input', style: 'margin-left: 1rem;', onchange: 'alerter' }, // Attributes
2889
+ '::colors', // Binding syntax
2890
+ [
2891
+ { value: 'red', label: 'Red' }, // Options
2892
+ { value: 'green', label: 'Green' },
2893
+ { value: 'blue', label: 'Blue' },
2894
+ { value: 'yellow', label: 'Yellow' }
2895
+ ]
2896
+ ],
2897
+
2898
+ // Submit Button
2899
+ [
2900
+ 'submit',
2901
+ 'submitButton',
2902
+ 'Submit',
2903
+ { required: true }, // Validation options
2904
+ { id: 'submitBtn', class: 'form-submit-btn', style: 'margin-top: 1rem;' } // Attributes
2905
+ ]
2906
+ ];
2907
+
2908
+
2909
+
2910
+ const formParams= {
2911
+ method: 'post',
2912
+ action: 'submit.js',
2913
+ id: 'myForm',
2914
+ class: 'form',
2915
+ semantq: true,
2916
+ style: 'width: 100%; font-size: 14px;',
2917
+ //enctype: 'multipart/form-data',
2918
+ //target: '_blank',
2919
+ //nonvalidate: true,
2920
+ //accept_charset: 'UTF-8',
2921
+ };
2922
+
2923
+
2924
+ // Instantiate the form
2925
+ const form = new Formique(formParams, formSchema);
2926
+ const formHTML = form.renderFormHTML();
2927
+ console.log(formHTML);
2928
+
2929
+
2930
+
2931
+
2932
+
2933
+
2934
+
2935
+
2936
+