formique 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,17 @@
1
1
  # Formique
2
2
 
3
- <!-- <img src="https://github.com/thincmedia/anyGridJs/blob/main/images/anyGridJs_Example.png" alt="anyGridJs Example"> -->
3
+ <img src="https://github.com/Gugulethu-Nyoni/formique/blob/main/images/formique-js-form-builder-anyframework.png" alt="Formique JS Form Builder Example">
4
4
 
5
5
 
6
- Formique: A lightweight, declarative JavaScript syntax library for generating WCAG acceAccessibility compliant forms. Suited for vanilla js and Semantq JS framework. Formique simplifies the process of creating and managing forms with a variety of input types, built-in validation, and customizable attributes. Open-source &amp; MIT licensed.
6
+ Formique is a robust and elegant WCAG and ARIA compliant form-building library tailored for JavaScript enthusiasts. It supports a wide array of input types, features JS-driven themes, and offers advanced functionalities like nested conditional logic and dynamic dropdowns. Highly customizable and extensible, Formique is built for the Semantq JS Framework but seamlessly integrates with Vanilla JS, React, Vue, Angular, and Svelte.
7
7
 
8
8
 
9
- ## Accessibility Compliance
9
+ Formique is Open-source &amp; MIT licensed.
10
10
 
11
- Formique is designed with a laser focus on usability and accessibility, ensuring that the generated form HTML markup meets the highest standards of web accessibility, such as the Web Content Accessibility Guidelines (WCAG) 2.1.
11
+
12
+ ## Accessibility Compliance -
13
+
14
+ Formique is designed with a laser focus on WAI-ARIA and WCAG usability and accessibility, ensuring that the generated form HTML markup meets the highest standards of web accessibility.
12
15
 
13
16
  With minimal declarative form input definitions, Formique takes care of the rest to ensure the final markup is comprehensive enough to meet [official](https://www.w3.org/WAI/tutorials/forms/) usability and accessibility standards.
14
17
 
@@ -17,20 +20,30 @@ For more information on the Web Content (Forms) Accessibility Guidelines (WCAG),
17
20
 
18
21
  ## Key Features
19
22
 
20
- - Declarative Syntax: Define forms using a simple and intuitive schema.
21
- - Wide Range of Inputs: Supports text, email, number, password, date, time, file uploads, and more.
22
- - Validation and Attributes: Easily specify validation rules and attributes for each form field.
23
- - Dynamic Form Generation: Generate forms dynamically based on your schema.
24
- - Framework Agnostic: Currently works Semantq and vanilla JS.
25
-
26
-
23
+ - **Declarative Syntax:** Define forms using a simple and intuitive schema.
24
+ - **Wide Range of Inputs:** Supports text, email, number, password, date, time, file uploads, and more.
25
+ - **Validation and Attributes:** Easily specify validation rules and attributes for each form field.
26
+ - **Dynamic Form Generation:** Generate forms dynamically based on your schema.
27
+ - **Framework Agnostic:** Currently works with Semantq and vanilla JS. (More frameworks to be added)
28
+ - **Accessibility and Usability Compliant:** Formique yields form markup compliant with WCAG.
29
+ - **Mobile Responsive:** Forms are mobile responsive out of the box.
30
+ - **Nested Dynamic Conditional Logic:** Implement complex conditional logic to show or hide form fields based on user input.
31
+ - **Dynamic Dropdowns:** Create dropdowns whose options change dynamically based on other field selections.
32
+ - **JavaScript-Driven Themes:** Apply themes dynamically using JavaScript for a customizable user interface.
33
+ - **WAI-ARIA and WCAG-Compliant HTML:** Ensure all form elements are accessible and meet WCAG standards.
34
+ - **Progressive Enhancement:** Forms function with or without JavaScript, ensuring accessibility and functionality across all environments.
27
35
 
28
36
  ## Why Choose Formique?
29
37
 
30
- - Vanilla JS: No dependencies; works seamlessly with vanilla JS and Semantq JS framework.(More frameworks to be added)
31
- - Lightweight: Minimal footprint optimized for performance.
32
- - Customizable: Adapt the library to fit your project's unique needs.
33
- - Declarative: Write your forms in JavaScript and define forms with a concise schema for better readability and maintainability.
38
+ - **Vanilla JS:** No dependencies; works seamlessly with vanilla JS and Semantq JS framework. (More frameworks to be added)
39
+ - **Lightweight:** Minimal footprint optimized for performance.
40
+ - **Customizable:** Adapt the library to fit your project's unique needs for functionality and style.
41
+ - **Declarative:** Write your forms in JavaScript and define forms with a concise schema for better readability and maintainability.
42
+ - **Usability and Accessibility Compliant:** You just need to focus on defining form fields data. Formique handles WCAG compliance for you.
43
+ - **Mobile Responsive:** Formique forms are mobile responsive out of the box.
44
+ - **Dynamic Features:** Implement nested conditional logic, dynamic dropdowns, and JavaScript-driven themes to enhance user experience.
45
+ - **Progressive Enhancement:** Forms are designed to function with or without JavaScript, ensuring broad compatibility.
46
+
34
47
 
35
48
 
36
49
  ## Form Input Types Covered
@@ -55,6 +68,7 @@ For more information on the Web Content (Forms) Accessibility Guidelines (WCAG),
55
68
  - Radio: ```html <input type="radio"> ```
56
69
  - Checkbox: ```html <input type="checkbox"> ```
57
70
  - Select (Single & Multiple): ```html <select> ```
71
+ - Dynamic Single Select: Displays a single-select dropdown of subcategories based on a selected category. For example, it dynamically shows a dropdown of states when a country is selected from a list of countries: ```html <select> ```
58
72
  - Submit: ```html <input type="submit"> ```
59
73
 
60
74
 
@@ -65,8 +79,7 @@ For more information on the Web Content (Forms) Accessibility Guidelines (WCAG),
65
79
  The form schema is an array of field definitions. Each field is defined by an array containing:
66
80
  - Input definition (required)
67
81
  - Input validation (optional)
68
- - Input attributes (optional)
69
- - Binding syntax (optional)
82
+ - Input attributes (optional, including binding attribute (optional)
70
83
  - Options (applicable to single select, multiple select, radio and check box inputs)
71
84
 
72
85
  ## Input Definition
@@ -77,27 +90,43 @@ The form schema is an array of field definitions. Each field is defined by an ar
77
90
  You don't need to use the type, name and label keys to define these parameters.
78
91
  **Example Input Definition:**
79
92
 
80
- `{ 'text', 'firstname', 'First Name' }`
93
+ `['text', 'firstname', 'First Name' ]`
81
94
 
82
95
  In the example above:
83
- - The first item (text) defines the type of the input - this will yield: `<input type="text"`
84
- - The second item (firstname) defines the name value of the input - this will yield: `<input name="firstname"`
96
+ - The first item (text) defines the type of the input - this will yield: `<input type="text" ...`
97
+ - The second item (firstname) defines the name value of the input - this will yield: `<input name="firstname" ...`
85
98
  - The third item (First Name) defines the Label value- this will yield: `<label for="firstname">First Name</label>`
99
+ - Final html output will be:
86
100
 
101
+ ```html
102
+ <div class="input-block" id="firstname-block">
103
+ <label for="firstname">First Name</label>
104
+ <input type="text" name="firstname" id="firstname" class="form-input" placeholder="First
105
+ Name">
106
+ </div>
107
+ ```
87
108
 
88
109
 
89
110
  ## Input Validation
90
111
  - **Validation**: Object specifying validation rules. This can include:
91
112
  - **Required**: Boolean to specify if the field is mandatory.
92
113
  - Example: `required: true`
93
- - **MinLength**: Specifies the minimum number of characters allowed.
114
+ - **Minlength**: Specifies the minimum number of characters allowed.
94
115
  - Example: `minlength: 5`
95
- - **MaxLength**: Specifies the maximum number of characters allowed.
116
+ - **Maxlength**: Specifies the maximum number of characters allowed.
96
117
  - Example: `maxlength: 50`
97
118
  - **Pattern**: A regex pattern the input must match.
98
119
  - Example: `pattern: "/^[A-Za-z0-9]+$/"`
99
120
 
100
- **Formique will filter out any invalid validation defined and throw warnings on the browser console.E.g. you define min and max validations for a text field Formique will filter these out.**
121
+ ### Number Field Specific Validation:
122
+ - **Min**: Specifies the minimum numeric value allowed.
123
+ - Example: `min: 1`
124
+ - **Max**: Specifies the maximum numeric value allowed.
125
+ - Example: `max: 100`
126
+ - **Step**: Specifies the increment step for numeric values.
127
+ - Example: `step: 0.01` (for decimal increments)
128
+
129
+ **Formique will filter out any invalid validation defined and throw warnings on the browser console.E.g. when you define min and max validations (instead of minlength and maxlength) for a text field, Formique will filter these out.**
101
130
 
102
131
 
103
132
  ## Input Attributes
@@ -105,8 +134,12 @@ In the example above:
105
134
  - Example: `{ id: 'username', class: 'form-input', style: 'font-size: 13px;' }`
106
135
 
107
136
  ## Binding
108
- - **Binding**: Optional binding syntax for dynamic data. It can use `bind:value` or `::inputName`.
109
- - Example: `'bind:value'` or `'::inputName'` - inputName must be the value defined as the input name (second item) in the input definition object.
137
+
138
+ - **Binding:** Optional binding syntax for dynamic data. The binding object has been moved to the attributes object, allowing for inclusion directly within the attributes. Two syntax formats are supported:
139
+ - `binding: '::nameOfField'`
140
+ - `binding: 'bind-value'`
141
+ - Example: `'::inputName'` or `'bind-value'`
142
+ *Note:* `inputName` must be the value defined as the input name (the second item) in the input definition object.
110
143
 
111
144
  ## Options
112
145
  - **Options**: For singleSelect,, multipleSelect, radio, and checkbox inputs. This is an array of options, each with a `value` and `label`.
@@ -122,90 +155,110 @@ In the example above:
122
155
 
123
156
  ## Installation
124
157
 
125
- There are two ways to install and use Formique in your project:
158
+ There are two primary ways to install and use Formique in your project:
126
159
 
127
- ## Option A: Use Formique as a Universal Module Definition (UMD) Module
160
+ ### Option A: Use Formique in a Browser Context (No Bundler Required)
128
161
 
129
- 1. Include the CSS and JavaScript in the head section of your HTML file:
162
+ 1. **Include the CSS** in the head section of your HTML file:
130
163
 
131
164
  ```html
132
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/formique-css@1.0.1/formique.min.css">
165
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/formique-css@1.0.1/formique.min.css" formique-style>
133
166
  ```
134
167
 
135
- 2. Insert the js script tag just before the closing tag ```html </body> ``` of your html file.
168
+ 2. **Include the JavaScript** before the closing `</body>` tag of your HTML file:
136
169
 
137
170
  ```html
138
- <script src="https://cdn.jsdelivr.net/npm/formique@1.0.1/formique.umd.js"></script>
171
+ <script src="https://cdn.jsdelivr.net/npm/formique@1.0.1/formique.mjs.js"></script>
139
172
  ```
140
- ## Usage Example
141
173
 
142
- 1. Define the form container somewhere in the html body:
174
+ ### Usage Example:
143
175
 
144
- ```html
145
- <div id="formique"></div>
146
- ```
176
+ 1. Define the form container somewhere in the HTML body:
147
177
 
178
+ ```html
179
+ <div id="formique"></div>
180
+ ```
148
181
 
149
- 2. Define your form parameters (formParams), form schema (formSchema) and then initialize Formique in script which should go below this script tag:
182
+ Alternatively, you can use a different container ID by setting `containerId: 'someelementid'` in the `formSettings` object.
150
183
 
184
+ 2. Define your `formParams`, `formSchema`, and initialize Formique in a `<script>` block (placed below the previous script tag):
151
185
 
152
- ```html
153
- <script src="https://cdn.jsdelivr.net/npm/formique@1.0.1/formique.umd.js"></script>
154
-
186
+ ```html
155
187
  <script>
188
+ const formSchema = [
189
+ ['text', 'name', 'Name', { required: true }, {}],
190
+ ['email', 'email', 'Email', { required: true }, {}],
191
+ ['singleSelect', 'diet', 'Dietary Requirements', { required: true }, {}, [
192
+ { value: 'gluten-free', label: 'Gluten-free' },
193
+ { value: 'vegetarian', label: 'Vegetarian' },
194
+ // Additional options here...
195
+ ]],
196
+ ['submit', 'submitButton', 'Submit']
197
+ ];
198
+
156
199
  const formParams = {
157
200
  method: 'post',
158
201
  action: 'submit.js',
159
202
  id: 'myForm',
160
203
  class: 'form',
161
- semantq: true,
162
204
  style: 'width: 100%; font-size: 14px;'
163
205
  };
164
206
 
165
- const formSchema = [
166
- ['text', 'name', 'Name', { required: true }, {}, ''],
167
- ['email', 'email', 'Email', { required: true }, {}, ''],
168
- [
169
- 'singleSelect', 'diet', 'Dietary Requirements', {required: true}, {}, '',
170
- [
171
- {value: 'gluten-free', label: 'Gluten-free'},
172
- {value: 'dairy-free', label: 'Dairy-free'},
173
- {value: 'keto', label: 'Ketogenic'},
174
- {value: 'low-carb', label: 'Low-carb'},
175
- {value: 'pescatarian', label: 'Pescatarian'},
176
- {value: 'halal', label: 'Halal'},
177
- {value: 'kosher', label: 'Kosher'},
178
- {value: 'vegetarian', label: 'Vegetarian'},
179
- {value: 'lacto-ovo-vegetarian', label: 'Lacto-ovo-vegetarian'},
180
- {value: 'raw-food', label: 'Raw food'},
181
- {value: 'macrobiotic', label: 'Macrobiotic'},
182
- {value: 'flexitarian', label: 'Flexitarian'}
183
- ]
184
- ],
185
- ['submit', 'submitButton', 'Submit', {}, {}, '']
186
- ];
207
+ const formSettings = {
208
+ requiredFieldIndicator: true,
209
+ framework: 'semantq',
210
+ placeholders: true,
211
+ containerid: 'form-div'
212
+ };
187
213
 
214
+ // Initialize the form
215
+ const form = new Formique(formSchema, formParams, formSettings);
216
+ </script>
217
+ ```
188
218
 
189
- (function(formParams, formSchema) {
190
- const form = new Formique(formParams, formSchema);
191
- const formHTML = form.renderFormHTML();
192
- })(formParams, formSchema);
219
+ **Note:** You can also use this instantiation with just the `formSchema`, leaving out the `formParams` and `formSettings`. This will apply the default dark theme and render the form inputs without the surrounding `<form>` element.
193
220
 
194
- </script>
195
- ```
221
+ ---
196
222
 
197
- ## Option B: Use Formique as an ESM Module
223
+ ### Option B: Use Formique in a Node.js (Bundler) Environment
198
224
 
199
- 1. Install Formique via npm:
225
+ 1. **Install Formique via npm:**
200
226
 
201
227
  ```bash
202
228
  npm install formique
203
229
  ```
204
230
 
231
+ 2. **Import and Use Formique in Your JavaScript File:**
232
+
233
+ ```javascript
234
+ import Formique from 'formique';
235
+
236
+ const formSchema = [
237
+ // Define your schema as shown above...
238
+ ];
239
+
240
+ const formParams = {
241
+ // Optional parameters...
242
+ };
243
+
244
+ const formSettings = {
245
+ // Optional settings...
246
+ };
247
+
248
+ const form = new Formique(formSchema, formParams, formSettings);
249
+ ```
250
+
251
+ ---
252
+
253
+ ### Other Formats
254
+
255
+ Formique is also available in additional formats like **ESM (ES Modules)** and **IIFE (Immediately Invoked Function Expression)** for specific use cases. For most projects, we recommend using **UMD** for browser contexts and **ESM** for Node.js environments. Refer to the Formique CDN for all available formats.
256
+
257
+
205
258
  2. Include the CSS and import Formique in the head section of your HTML file:
206
259
 
207
260
  ```html
208
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/formique-css@1.0.1/formique.min.css">
261
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/formique-css@1.0.1/formique.min.css" formique-style>
209
262
  ```
210
263
  3. Define form container somewhere in the html body:
211
264
 
@@ -229,10 +282,10 @@ There are two ways to install and use Formique in your project:
229
282
  };
230
283
 
231
284
  const formSchema = [
232
- ['text', 'name', 'Name', { required: true }, {}, ''],
233
- ['email', 'email', 'Email', { required: true }, {}, ''],
285
+ ['text', 'name', 'Name', { required: true }],
286
+ ['email', 'email', 'Email', { required: true }],
234
287
  [
235
- 'singleSelect', 'diet', 'Dietary Requirements', {required: true}, {}, '',
288
+ 'singleSelect', 'diet', 'Dietary Requirements', {required: true}, {},
236
289
  [
237
290
  {value: 'gluten-free', label: 'Gluten-free'},
238
291
  {value: 'dairy-free', label: 'Dairy-free'},
@@ -248,11 +301,11 @@ There are two ways to install and use Formique in your project:
248
301
  {value: 'flexitarian', label: 'Flexitarian'}
249
302
  ]
250
303
  ],
251
- ['submit', 'submitButton', 'Submit', {}, {}, ''],
304
+ ['submit', 'submitButton', 'Submit'],
252
305
  ];
253
306
 
254
307
  const form = new Formique(formParams, formSchema);
255
- const formHTML = form.renderFormHTML();
308
+
256
309
 
257
310
  </script>
258
311
  ```
@@ -273,7 +326,6 @@ const formSchema = [
273
326
  'First Name',
274
327
  { minlength: 2, maxlength: 5, required: true, disabled: true }, // Validation options
275
328
  { value: "John", id: 'firstNameInput', class: 'form-input', style: 'width: 100%;', oninput: "incrementer()" }, // Attributes
276
- '::firstName' // Binding syntax
277
329
  ],
278
330
 
279
331
  // URL Input Field
@@ -282,8 +334,7 @@ const formSchema = [
282
334
  'websiteUrl',
283
335
  'Website URL',
284
336
  { required: true }, // Validation options
285
- { id: 'websiteUrlInput', class: 'form-control', style: 'width: 100%;' }, // Attributes
286
- 'bind:value' // Binding syntax
337
+ { id: 'websiteUrlInput', class: 'form-control', style: 'width: 100%;', binding: 'bind:value' }
287
338
  ],
288
339
 
289
340
  // Radio Input Field
@@ -292,8 +343,7 @@ const formSchema = [
292
343
  'gender',
293
344
  'Gender',
294
345
  { required: true }, // Validation options
295
- { id: 'genderRadio', class: 'form-radio-input', style: 'margin-left: 1rem;', onchange: 'actioner()' }, // Attributes
296
- '::gender', // Binding syntax
346
+ { id: 'genderRadio', class: 'form-radio-input', style: 'margin-left: 1rem;', onchange: 'actionFunction()' }
297
347
  [
298
348
  { value: 'male', label: 'Male' }, // Options
299
349
  { value: 'female', label: 'Female' },
@@ -307,8 +357,7 @@ const formSchema = [
307
357
  'preferences',
308
358
  'Preferences',
309
359
  { required: true }, // Validation options
310
- { id: 'preferencesCheckbox', class: 'form-checkbox-input', style: 'margin-left: 1rem;', onchange: 'submit' }, // Attributes
311
- '::preferences', // Binding syntax
360
+ { id: 'preferencesCheckbox', binding: '::preferences', class: 'form-checkbox-input', style: 'margin-left: 1rem;', onchange: 'submit' }
312
361
  [
313
362
  { value: 'news', label: 'Newsletter' }, // Options
314
363
  { value: 'updates', label: 'Product Updates' },
@@ -322,8 +371,7 @@ const formSchema = [
322
371
  'colors',
323
372
  'Colors',
324
373
  { required: true }, // Validation options
325
- { id: 'colorsSelect', class: 'form-select-input', style: 'margin-left: 1rem;', onchange: 'trigger' }, // Attributes
326
- '::colors', // Binding syntax
374
+ { id: 'colorsSelect', class: 'form-select-input', style: 'margin-left: 1rem;', onchange: 'trigger' }, // the onchange: 'trigger' format works with (framework: semantq) set in your formSettings object so that the syntax can be transformed as per framework specs
327
375
  [
328
376
  { value: 'red', label: 'Red' }, // Options
329
377
  { value: 'green', label: 'Green' },
@@ -337,8 +385,7 @@ const formSchema = [
337
385
  'colors', // Name/identifier of the field
338
386
  'Colors', // Label of the field
339
387
  { required: true, min: 2, max: 3 }, // Validation options
340
- { id: 'colorsSelect', class: 'form-select-input', style: 'margin-left: 1rem;', onchange: 'alerter' }, // Attributes
341
- '::colors', // Binding syntax
388
+ { id: 'colorsSelect', class: 'form-select-input', style: 'margin-left: 1rem;', onchange: 'alerter' }
342
389
  [
343
390
  { value: 'red', label: 'Red' }, // Options
344
391
  { value: 'green', label: 'Green' },
@@ -352,8 +399,8 @@ const formSchema = [
352
399
  'submit',
353
400
  'submitButton',
354
401
  'Submit',
355
- { required: true }, // Validation options
356
- { id: 'submitBtn', class: 'form-submit-btn', style: 'margin-top: 1rem;' } // Attributes
402
+ {}, // Validation options
403
+ { id: 'submitBtn', class: 'form-submit-btn', style: 'margin-top: 1rem; width: 100%;' }
357
404
  ]
358
405
  ];
359
406
 
@@ -391,7 +438,7 @@ const formParams = {
391
438
  enctype: 'multipart/form-data', // Encoding type for file uploads
392
439
  target: '_blank', // Where to open the form result (e.g., '_self', '_blank')
393
440
  novalidate: true, // Disable form validation
394
- accept_charset: 'UTF-8' // this will be transformed to: accept-charset: 'UTF-8' Character set for form data
441
+ accept_charset: 'UTF-8' // this will be transformed to: accept-charset: 'UTF-8' Character set for form data
395
442
  };
396
443
  ```
397
444
 
@@ -584,6 +631,108 @@ By customizing these parameters, you can control various aspects of the form's b
584
631
  </div>
585
632
  ```
586
633
 
634
+
635
+ ## Dynamic Drop-Down Schema
636
+
637
+ The `dynamicSingleSelect` input type in Formique is used for generating dynamic dropdowns based on predefined options. It allows you to define multi-level select options (e.g., categories and subcategories) that change based on user selections. Below is an example of the schema format for implementing dynamic drop-downs.
638
+
639
+
640
+ ### Common Use Cases:
641
+
642
+ - **Country-State Dropdowns**: A common implementation where selecting a country dynamically updates the list of states/provinces.
643
+ - **Product Categories**: A dropdown where the first selection (e.g., a product category) updates the second dropdown to show relevant product subcategories.
644
+ - **Job Roles & Departments**: Selecting a department dynamically shows job roles related to that department (e.g., selecting "IT" shows job roles like "Software Developer", "Network Engineer", etc.).
645
+ - **Course & Subjects**: In educational systems, choosing a course can display relevant subjects or modules available for that course.
646
+ - **Location-Based Services**: When selecting a country or city, a second dropdown can list local services or offices relevant to the location chosen.
647
+
648
+
649
+ ### Dynamic Drop Downs Schema Definition:
650
+
651
+ The code below goes into your
652
+ ```javascript
653
+ [
654
+ 'dynamicSingleSelect', // Input type (required)
655
+ 'languages', // Field name (required)
656
+ 'Programming Scope-Programming Languages', // Labels for both primary drop down and secondary (dynamic) drop down seperated by a hyphen - e.g. Country-States
657
+ { required: true }, // Validation rules (optional) but the curly braces {} must always be included
658
+ {}, // Field attributes (optional) but the curly braces {} must always be included
659
+
660
+ // Dropdown Options
661
+ [
662
+ {
663
+ id: 'frontend', // Option group ID (required)
664
+ label: 'Front End', // Option group label (required)
665
+ options: [ // List of options (required)
666
+ { value: 'javascript', label: 'JavaScript' },
667
+ { value: 'html', label: 'HTML' },
668
+ { value: 'css', label: 'CSS' },
669
+ { value: 'typescript', label: 'TypeScript' },
670
+ { value: 'semantq', label: 'Semantq' },
671
+ { value: 'svelte', label: 'Svelte' },
672
+ { value: 'vue', label: 'Vue' },
673
+ { value: 'react', label: 'React' },
674
+ { value: 'angular', label: 'Angular' },
675
+ ]
676
+ },
677
+ {
678
+ id: 'backend', // Option group ID (required)
679
+ label: 'Back End', // Option group label (required)
680
+ options: [ // List of options (required)
681
+ { value: 'nodejs', label: 'Node.js' },
682
+ { value: 'python', label: 'Python' },
683
+ { value: 'java', label: 'Java' },
684
+ { value: 'php', label: 'PHP' },
685
+ { value: 'ruby', label: 'Ruby' },
686
+ { value: 'csharp', label: 'C#' },
687
+ { value: 'golang', label: 'Go' }
688
+ ]
689
+ }
690
+ ]
691
+ ]
692
+ ```
693
+
694
+
695
+ ## Nested Conditionality Logic - Schema Definition
696
+
697
+ Formique allows for dynamic form generation with powerful conditional logic based on the values of other form fields. This allows you to create forms where the options in one field depend on the selection made in another field.
698
+
699
+ ### Key Features:
700
+ - **Dynamic Field Display**: Show or hide fields based on previous selections.
701
+ - **Conditional Logic**: Use conditions such as specific values in other fields to control the availability of options or visibility of fields.
702
+ - **Multiple Dependencies**: Fields can have more than one dependents
703
+
704
+ ### Schema Definition Example:
705
+
706
+ The following schema demonstrates how to implement dynamic dropdowns with nested conditional logic in Formique.
707
+
708
+ ```js
709
+ [
710
+ // Role field - Single Select (required)
711
+ ['singleSelect', 'role', 'Role', { required: true }, { dependents: ['topic', 'mode'] },
712
+ // in the attributes object of the parent field add dependents (array) by field names to the dependents: item
713
+ [
714
+ { value: 'conference attendee', label: 'Conference Attendee' },
715
+ { value: 'conference presenter', label: 'Conference Presenter' }
716
+ ]
717
+ ],
718
+
719
+ // Topic field - Text input (dependent on 'role' being 'conference presenter')
720
+ ['text', 'topic', 'Topic', {}, { dependsOn: 'role', condition: 'conference presenter' }],
721
+ // in the attributes object of the child field add the dependsOn: 'role' item where the key is dependsOn: and the value is name of the parent field: e.g. 'role in this case'
722
+ // also add the condition this way: condition: 'conference presenter'
723
+ // you can use the string 'conference presenter' as the condition to be met
724
+ // so this if the user select Conference Presenter in the role field - then dependents of that field (mode and topic) will be displayed. If the selected is changed to something else that doesn't meet the defined condtion - the dependents will be hidden.
725
+
726
+ // Mode field - Single Select (required, dependent on 'role' being 'conference presenter')
727
+ ['singleSelect', 'mode', 'Mode', { required: true }, { dependsOn: 'role', condition: (value) => value === 'conference presenter' }, // you can use an arrow function to evaluate the condition - this is useful for more comprex evaluations
728
+ [
729
+ { value: 'physical', label: 'Physical' },
730
+ { value: 'virtual', label: 'Virtual' }
731
+ ]
732
+ ]
733
+ ]
734
+ ```
735
+
587
736
  ## Styling the Form
588
737
 
589
738
  Formique provides a set of CSS classes to facilitate the styling of various form elements. The default class names for different form components are as follows:
@@ -0,0 +1,371 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap');
2
+
3
+ .dark-theme {
4
+ --background-dark: #0A0A0A;
5
+ --background-light: #1A1A1A;
6
+ --text-light: #E5E5E5;
7
+ --border-color: #333333;
8
+ --input-background: #1C1C1C;
9
+ --input-background-disabled: #2A2A2A;
10
+ --label-color: #999999;
11
+ --radio-checkbox-accent: #4A90E2;
12
+ --button-background: #444444;
13
+ --button-background-hover: #555555;
14
+ --button-text-color: #E5E5E5;
15
+ --input-border-color: #444444;
16
+ --input-border-color-focus: #666666;
17
+ --secondary-text-color: #B3B3B3;
18
+ }
19
+
20
+
21
+
22
+ .light-theme {
23
+ --background-light: #ffffff;
24
+ --background-dark: #1a1a1a;
25
+ --text-light: #333333;
26
+ --border-color: #cccccc;
27
+ --input-background: #ffffff;
28
+ --input-background-disabled: #e0e0e0;
29
+ --label-color: #555555;
30
+ --radio-checkbox-accent: #4A90E2;
31
+ --button-background: #888888;
32
+ --button-background-hover: #666666;
33
+ --button-text-color: #ffffff;
34
+ --input-border-color: #cccccc;
35
+ --input-border-color-focus: #4A90E2;
36
+ --secondary-text-color: #777777;
37
+ }
38
+
39
+ .pink-theme {
40
+ --background-dark: red;
41
+ --background-light: #05051e;
42
+ --text-light: #fe8bbb;
43
+ --border-color: pink;
44
+ --input-background: #ffffff;
45
+ --input-background-disabled: #f1f8e9;
46
+ --label-color: #fe8bbb; /* #c2185b; */
47
+ --radio-checkbox-accent: #c2185b;
48
+ --button-background: #c2185b; /*#e91e63;*/
49
+ --button-background-hover: #f01ff0;
50
+ --button-text-color: #ffffff;
51
+ --input-border-color: #05051e;
52
+ --input-border-color-focus: #c2185b;
53
+ --secondary-text-color: #880e4f;
54
+ }
55
+
56
+
57
+ .light-blue-theme {
58
+ --background-light: #f0f8ff;
59
+ --background-dark: #003b5c;
60
+ --text-light: #fff;
61
+ --text-dark: #000;
62
+ --border-color: #0077b6;
63
+ --input-background: #ffffff;
64
+ --input-background-disabled: #d1e4f4;
65
+ --label-color: #4a6572;
66
+ --radio-checkbox-accent: #0091d3;
67
+ --button-background: #1e3a8a;
68
+ --button-background-hover: #0f2a5f;
69
+ --button-text-color: #ffffff;
70
+ --input-border-color: #0077b6;
71
+ --input-border-color-focus: #00aaff;
72
+ --secondary-text-color: #4f5b62;
73
+ --dropdown-option-text-color: #003b5c;
74
+ --legend-text-color: #1d3557;
75
+
76
+ }
77
+
78
+
79
+ .indigo-theme {
80
+ --background-dark: #2c2c2c;
81
+ --background-light: #3d3d3d;
82
+ --text-light: #e0e0e0;
83
+ --border-color: #444;
84
+ --input-background: #333;
85
+ --input-background-disabled: #555;
86
+ --label-color: #7986cb;
87
+ --radio-checkbox-accent: #3f51b5;
88
+ --button-background: #3f51b5;
89
+ --button-background-hover: #303f9f;
90
+ --button-text-color: #ffffff;
91
+ --input-border-color: #3f51b5;
92
+ --input-border-color-focus: #303f9f;
93
+ --secondary-text-color: #b3b3b3;
94
+ }
95
+
96
+
97
+
98
+
99
+ .dark-blue-theme {
100
+ --background-dark: #0a0a0a;
101
+ --background-light: #1a1a1a;
102
+ --text-light: #e0e0e0;
103
+ --border-color: #333;
104
+ --input-background: #222;
105
+ --input-background-disabled: #444;
106
+ --label-color: #81d4fa;
107
+ --radio-checkbox-accent: #2196f3;
108
+ --button-background: #2196f3;
109
+ --button-background-hover: #1976d2;
110
+ --button-text-color: #ffffff;
111
+ --input-border-color: #2196f3;
112
+ --input-border-color-focus: #1976d2;
113
+ --secondary-text-color: #b3b3b3;
114
+ }
115
+
116
+
117
+
118
+
119
+ .dark-orange-theme {
120
+ --background-dark: #121212;
121
+ --background-light: #1e1e1e;
122
+ --text-light: #e0e0e0;
123
+ --border-color: #333;
124
+ --input-background: #333;
125
+ --input-background-disabled: #444;
126
+ --label-color: #f96743;
127
+ --radio-checkbox-accent: #f96743;
128
+ --button-background: #f96743;
129
+ --button-background-hover: #f24e2e;
130
+ --button-text-color: #ffffff;
131
+ --input-border-color: #f96743;
132
+ --input-border-color-focus: #f24e2e;
133
+ --secondary-text-color: #b3b3b3;
134
+ }
135
+
136
+
137
+ .green-theme {
138
+ --background-dark: #001700;
139
+ --background-light: #011f01;
140
+ --text-light: #e0e0e0;
141
+ --border-color: #333;
142
+ --input-background: #333;
143
+ --input-background-disabled: #444;
144
+ --label-color: #4caf50;
145
+ --radio-checkbox-accent: #4caf50;
146
+ --button-background: #4caf50;
147
+ --button-background-hover: #388e3c;
148
+ --button-text-color: #ffffff;
149
+ --input-border-color: #4caf50;
150
+ --input-border-color-focus: #388e3c;
151
+ --secondary-text-color: #b3b3b3;
152
+ }
153
+
154
+
155
+ .purple-theme {
156
+ --background-dark: #121212;
157
+ --background-light: #f3e5f5;
158
+ --text-light: #4a148c;
159
+ --border-color: #333;
160
+ --input-background: #ffffff;
161
+ --input-background-disabled: #f1f8e9;
162
+ --label-color: #9c27b0;
163
+ --radio-checkbox-accent: #9c27b0;
164
+ --button-background: #9c27b0;
165
+ --button-background-hover: #7b1fa2;
166
+ --button-text-color: #ffffff;
167
+ --input-border-color: #9c27b0;
168
+ --input-border-color-focus: #7b1fa2;
169
+ --secondary-text-color: #880e4f;
170
+ }
171
+
172
+ .midnight-blush-theme {
173
+ --background-light: #05051e;
174
+ --background-dark: #05051e;
175
+ --text-light: #ffbd7a;
176
+ --text-dark: #ffffff;
177
+ --border-color: #ffbd7a; /* #9e7aff;*/
178
+ --input-background: #ffffff;
179
+ --input-background-disabled: #d9d9d9;
180
+ --label-color: #9e7aff;
181
+ --radio-checkbox-accent: #fe8bbb;
182
+ --button-background: #ffbd7a;
183
+ --button-background-hover: #9e7aff; /*#fe8bbb*/;
184
+ --button-text-color: #05051e;
185
+ --input-border-color: #9e7aff;
186
+ --input-border-color-focus: #fe8bbb;
187
+ --secondary-text-color: #ffbd7a;
188
+ --dropdown-option-text-color: #05051e;
189
+ --legend-text-color: #9e7aff;
190
+ }
191
+
192
+
193
+
194
+
195
+
196
+ /* Formique Container */
197
+ .formique {
198
+ max-width: 600px;
199
+ margin: auto;
200
+ padding: 1rem;
201
+ background: var(--background-light);
202
+ border-radius: 8px;
203
+ color: var(--text-light);
204
+ /*font-family: 'Montserrat', sans-serif;*/
205
+ font-family: 'Lato', sans-serif;
206
+ margin-bottom: 200px;
207
+ border: 1px solid var(--border-color);
208
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.7);
209
+ }
210
+
211
+ /* Input Block Styling */
212
+ .formique .input-block {
213
+ margin-bottom: 1rem;
214
+ position: relative;
215
+ }
216
+
217
+ .formique .input-block label {
218
+ display: block;
219
+ margin-bottom: 0.5rem;
220
+ color: var(--label-color);
221
+ }
222
+
223
+ .formique .input-block .form-input,
224
+ .formique .input-block .form-control {
225
+ width: 100%;
226
+ padding: 0.75rem 1rem 0.75rem 2.5rem;
227
+ border: none;
228
+ border-bottom: 1px solid var(--border-color);
229
+ border-radius: 0;
230
+ background-color: var(--background-light);
231
+ color: var(--text-light);
232
+ box-sizing: border-box;
233
+ font-size: 1rem;
234
+ }
235
+
236
+
237
+ .formique .input-block .form-input:focus {
238
+ outline: none;
239
+ border-bottom: 1px solid var(--button-background);
240
+ }
241
+
242
+
243
+ .formique .input-block .form-input-icon {
244
+ position: absolute;
245
+ left: 1rem;
246
+ top: 50%;
247
+ transform: translateY(-50%);
248
+ color: var(--label-color);
249
+ }
250
+
251
+ /* Input Disabled */
252
+ .formique .input-block .form-input:disabled {
253
+ background-color: var(--input-background-disabled);
254
+ cursor: not-allowed;
255
+ border-bottom: 1px solid var(--label-color);
256
+ }
257
+
258
+ /* Fieldset Styling for Radio, Checkbox, and Select Groups */
259
+ .formique .radio-group,
260
+ .formique .checkbox-group,
261
+ .formique .form-select {
262
+ margin-bottom: 1rem;
263
+ padding: 1rem 0;
264
+ border-radius: 8px;
265
+ background-color: transparent;
266
+ border: 1px solid var(--border-color);
267
+ }
268
+
269
+ /* Radio and Checkbox Group Styling */
270
+ .formique .radio-group legend,
271
+ .formique .checkbox-group legend {
272
+ margin-bottom: 0.5rem;
273
+ font-weight: bold;
274
+ color: var(--label-color);
275
+ }
276
+
277
+ .formique .radio-group div,
278
+ .formique .checkbox-group div {
279
+ margin-bottom: 0.5rem;
280
+ display: flex;
281
+ align-items: center;
282
+ }
283
+
284
+ .formique .radio-group .form-radio-input,
285
+ .formique .checkbox-group .form-checkbox-input {
286
+ margin-right: 0.5rem;
287
+ accent-color: var(--radio-checkbox-accent);
288
+ }
289
+
290
+ /* Select Styling */
291
+ .formique .form-select {
292
+ margin-bottom: 1rem;
293
+ padding: 1rem 2rem;
294
+ border-radius: 8px;
295
+ background-color: transparent;
296
+ border: 1px solid var(--border-color); /* Reduced thickness */
297
+ }
298
+
299
+ .formique .form-select legend {
300
+ color: var(--legend-text-color);
301
+ }
302
+
303
+ .formique .form-select select option {
304
+ background-color: #000!important;
305
+ }
306
+
307
+ /* Label for Select */
308
+ .formique .form-select label {
309
+ display: block;
310
+ margin-bottom: 0.5rem;
311
+ color: var(--label-color);
312
+ }
313
+
314
+ /* The Select Input Styling */
315
+ .formique .form-select select {
316
+ width: 100%;
317
+ padding: 0.75rem 1rem 0.75rem 2.5rem;
318
+ border: none;
319
+ background-color: transparent;
320
+ color: var(--legend-text-color);
321
+ font-size: 1rem;
322
+ box-sizing: border-box;
323
+ }
324
+
325
+ /* Focus Effect for Select */
326
+ .formique .form-select select:focus {
327
+ outline: none;
328
+ }
329
+
330
+ /* Icon for Select, Radio, and Checkbox Inputs */
331
+ .formique .radio-group .form-radio-input + label,
332
+ .formique .checkbox-group .form-checkbox-input + label {
333
+ margin-left: 0.5rem;
334
+ }
335
+
336
+ .formique .radio-group label,
337
+ .formique .checkbox-group label {
338
+ color: var(--label-color);
339
+ font-weight: normal;
340
+ }
341
+
342
+ .formique .form-select .form-select-icon {
343
+ position: absolute;
344
+ left: 1rem;
345
+ top: 50%;
346
+ transform: translateY(-50%);
347
+ color: var(--label-color);
348
+ }
349
+
350
+ .formique .radio-group div,
351
+ .formique .checkbox-group div {
352
+ padding-left: 2.5rem;
353
+ }
354
+
355
+
356
+ .formique .form-submit-btn {
357
+ padding: 0.75rem 1.5rem;
358
+ border: 1px solid var(--background-light);
359
+ border-radius: 4px;
360
+ background-color: var(--button-background);
361
+ color: var(--button-text-color);
362
+ font-size: 1rem;
363
+ cursor: pointer;
364
+ transition: background-color 0.3s;
365
+ }
366
+
367
+ .formique .form-submit-btn:hover {
368
+ background-color: var(--button-background-hover);
369
+ color: var(--button-text-color);
370
+ }
371
+
@@ -0,0 +1 @@
1
+ !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e="undefined"!=typeof globalThis?globalThis:e||self).Formique=n()}(this,(function(){"use strict";class e{renderField(e,n,t,i,s,l){throw new Error("Method renderField must be implemented")}}return class extends e{constructor(e,n={},t={}){super(),this.formSchema=e,this.divClass="input-block",this.inputClass="form-input",this.radioGroupClass="radio-group",this.checkboxGroupClass="checkbox-group",this.selectGroupClass="form-select",this.submitButtonClass="form-submit-btn",this.formParams=n,this.formContainerId=t.formContainerId||"formique",this.formAction=n.action||"https://httpbin.org/post",this.method=n.method.toUpperCase()||"POST",this.formMarkUp="",this.dependencyGraph={},this.themes=["dark","light","pink","light","indigo","dark-blue","light-blue","dark-orange","green","purple","midnight-blush"],document.addEventListener("DOMContentLoaded",(()=>{if(this.renderFormHTML(),this.initDependencyGraph(),this.registerObservers(),this.formSettings.theme&&this.themes.includes(this.formSettings.theme)){let e=this.formSettings.theme;this.applyTheme(e,this.formContainerId)}else this.applyTheme("dark",this.formContainerId);document.getElementById(`${this.formParams.id}`).addEventListener("submit",(e=>{this.formSettings.submitOnPage&&(e.preventDefault(),this.handleOnPageFormSubmission(this.formParams.id))}))})),this.formSettings={requiredFieldIndicator:!0,placeholders:!0,asteriskHtml:'<span aria-hidden="true" style="color: red;">*</span>',...t},Object.keys(this.formParams).length>0&&(this.formMarkUp+=this.renderFormElement()),this.renderForm()}initDependencyGraph(){this.dependencyGraph={},this.formSchema.forEach((e=>{const[n,t,i,s,l={}]=e,r=l.id||t;l.dependents&&(this.dependencyGraph[r]=l.dependents.map((e=>{const n=this.formSchema.find((([,n])=>n===e));if(n){const t=n[4]||{};return{dependent:t.id||e,condition:t.condition||null}}console.warn(`Dependent field "${e}" not found in schema.`)})),this.dependencyGraph[r].push({state:null}),this.attachInputChangeListener(r)),l.dependents&&l.dependents.forEach((e=>{const n=this.formSchema.find((([,n])=>n===e)),t=(n&&n[4]||{}).id||e,i=document.querySelector(`#${t}-block`);i&&(i.style.display="none")}))}))}attachInputChangeListener(e){const n=document.getElementById(e);n&&n.addEventListener("input",(n=>{const t=n.target.value;this.handleParentFieldChange(e,t)}))}handleParentFieldChange(e,n){const t=this.dependencyGraph[e];t&&(this.dependencyGraph[e].forEach((e=>{void 0!==e.state&&(e.state=n)})),t.forEach((e=>{if(e.dependent){const t=e.dependent+"-block",i=document.getElementById(t);if(i){const t="function"==typeof e.condition?e.condition(n):n===e.condition;i.style.display=t?"block":"none";i.querySelectorAll("input, select, textarea").forEach((e=>{t?e.required="true"===e.getAttribute("data-original-required"):(e.setAttribute("data-original-required",e.required),e.required=!1)}))}else console.warn(`Wrapper block with ID ${t} not found.`)}})))}registerObservers(){this.formSchema.forEach((e=>{const[n,t,i,s,l={}]=e,r=l.id||t;l.dependents&&l.dependents.forEach((e=>{if(this.dependencyGraph[r]){const n=this.formSchema.find((([,n])=>n===e));if(n){const t=n[4]?.id||e;this.dependencyGraph[r].forEach((n=>{n.dependent===e&&(n.observers||(n.observers=[]),n.observers.push(t))}))}}}))}))}applyTheme(e,n){const t=document.querySelector("link[formique-style]");t?fetch(t.href).then((e=>e.text())).then((t=>{const i=t.match(new RegExp(`\\.${e}-theme\\s*{([^}]*)}`,"i"));if(!i)return void console.error(`Theme rules for ${e} not found in the stylesheet.`);const s=i[1].trim(),l=document.getElementById(n);if(l){l.classList.add(`${e}-theme`,"formique");const t=document.createElement("style");t.textContent=`\n #${n} {\n ${s}\n }\n `,l.parentNode.insertBefore(t,l)}else console.error(`Form container with ID ${n} not found.`)})).catch((e=>{console.error("Error loading the stylesheet:",e)})):console.error("Stylesheet with 'formique-style' not found!")}renderFormElement(){let e="<form\n";const n=this.formParams||{};for(const[t,i]of Object.entries(n))if(null!=i)if("boolean"==typeof i)i&&(e+=` ${t}\n`);else{e+=` ${"accept_charset"===t?"accept-charset":t.replace(/_/g,"-")}="${i}"\n`}if(e+=">\n",n.laravel){e+=`<input type="hidden" name="_token" value="${document.querySelector('meta[name="csrf-token"]').getAttribute("content")}">`}return e=e.replace(/\n\s*$/,"\n"),e}renderForm(){const e=this.formSchema.map((e=>{const[n,t,i,s,l={},r]=e;return this.renderField(n,t,i,s,l,r)})).join("");this.formMarkUp+=e}renderField(e,n,t,i,s,l){const r={text:this.renderTextField,email:this.renderEmailField,number:this.renderNumberField,password:this.renderPasswordField,password:this.renderTextAreaField,tel:this.renderTelField,date:this.renderDateField,time:this.renderTimeField,"datetime-local":this.renderDateTimeField,month:this.renderMonthField,week:this.renderWeekField,url:this.renderUrlField,search:this.renderSearchField,color:this.renderColorField,checkbox:this.renderCheckboxField,radio:this.renderRadioField,file:this.renderFileField,hidden:this.renderHiddenField,image:this.renderImageField,textarea:this.renderTextAreaField,singleSelect:this.renderSingleSelectField,multipleSelect:this.renderMultipleSelectField,dynamicSingleSelect:this.renderDynamicSingleSelectField,submit:this.renderSubmitButton}[e];return r?r.call(this,e,n,t,i,s,l):(console.warn(`Unsupported field type '${e}' encountered.`),"")}handleOnPageFormSubmission(e){const n=document.getElementById(e);if(n){const e=new FormData(n);fetch(this.formAction,{method:this.method,body:e}).then((e=>e.json())).then((e=>{console.log("Success:",e);const n=document.getElementById(this.formContainerId);if(n){const e=document.createElement("div");e.classList.add("success-message","message-container"),e.innerHTML=this.formSettings.successMessage||"Your details have been successfully submitted!",n.innerHTML="",n.appendChild(e)}})).catch((e=>{console.error("Error:",e);const n=document.getElementById(this.formContainerId);if(n){let t=n.querySelector(".error-message");t&&t.remove();const i=document.createElement("div");i.classList.add("error-message","message-container");let s=this.formSettings.errorMessage||"An error occurred while submitting the form. Please try again.";s=`${s}<br/>Details: ${e.message}`,i.innerHTML=s,n.appendChild(i)}}))}}renderTextField(e,n,t,i,s){const l=["required","minlength","maxlength","pattern"];let r="";i&&Object.entries(i).forEach((([e,t])=>{if(l.includes(e))if("boolean"==typeof t&&t)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${t}"\n`;break;default:l.includes(e)||console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'number'.`)}else console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'text'.`)}));let a="";if(s.binding&&("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n))return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d=s.id||n;const o=this.formSettings?.framework||!1;let c,$="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on"))if("semantq"===o){const t=n.endsWith("()")?n.slice(0,-2):n;$+=` @${e.replace(/^on/,"")}={${t}}\n`}else{$+=` ${e}="${n.endsWith("()")?n:`${n}()`}"\n`}else!0===n?$+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&($+=` ${e.replace(/_/g,"-")}="${n}"\n`);c="class"in s?s.class:this.inputClass;let p=`\n <div class="${this.divClass}" id="${d+"-block"}">\n <label for="${d}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${d}"\n class="${c}"\n ${$}\n ${r}\n ${$.includes("placeholder")?"":this.formSettings.placeholders?`placeholder="${t}"`:""} />\n </div>\n`.replace(/^\s*\n/gm,"").trim();p=p.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),this.formMarkUp+=p}renderEmailField(e,n,t,i,s){const l=["required","pattern","minlength","maxlength","multiple"];let r="";i&&Object.entries(i).forEach((([e,t])=>{if(l.includes(e))if("boolean"==typeof t&&t)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${t}"\n`;break;default:l.includes(e)||console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'number'.`)}else console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'email'.`)}));let a="";if(s.binding&&("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n))return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}"> \n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n ${c.includes("placeholder")?"":this.formSettings.placeholders?`placeholder="${t}"`:""}\n\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderNumberField(e,n,t,i,s){const l=["required","min","max","step"];let r="";i&&Object.entries(i).forEach((([e,t])=>{if(l.includes(e))if("boolean"==typeof t&&t)r+=` ${e}\n`;else switch(e){case"min":case"max":case"step":r+=` ${e}="${t}"\n`;break;default:l.includes(e)||console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'number'.`)}else console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'number'.`)}));let a="";if(s.binding&&("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n))return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}"> \n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderPasswordField(e,n,t,i,s){const l=["required","minlength","maxlength","pattern"];let r="";i&&Object.entries(i).forEach((([e,t])=>{if(l.includes(e))if("boolean"==typeof t&&t)r+=` ${e}\n`;else switch(e){case"minlength":case"maxlength":case"pattern":r+=` ${e}="${t}"\n`;break;default:l.includes(e)||console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'password'.`)}else console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'password'.`)}));let a="";if(s.binding&&("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n))return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}"> \n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderTextAreaField(e,n,t,i,s){const l=["required","minlength","maxlength","pattern"];let r="";i&&Object.entries(i).forEach((([e,t])=>{if(l.includes(e))if("boolean"==typeof t&&t)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${t}"\n`;break;default:l.includes(e)||console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'number'.`)}else console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'text'.`)}));let a="";if(s.binding&&("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n))return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d=s.id||n;const o=this.formSettings?.framework||!1;let c,$="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on"))if("semantq"===o){const t=n.endsWith("()")?n.slice(0,-2):n;$+=` @${e.replace(/^on/,"")}={${t}}\n`}else{$+=` ${e}="${n.endsWith("()")?n:`${n}()`}"\n`}else!0===n?$+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&($+=` ${e.replace(/_/g,"-")}="${n}"\n`);c="class"in s?s.class:this.inputClass;let p=`\n <div class="${this.divClass}" id="${d+"-block"}">\n <label for="${d}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </label>\n <textarea \n name="${n}"\n ${a}\n id="${d}"\n class="${c}"\n ${$}\n ${r}\n ${$.includes("placeholder")?"":this.formSettings.placeholders?`placeholder="${t}"`:""}>\n </textarea>\n </div>\n`.replace(/^\s*\n/gm,"").trim();p=p.replace(/<textarea\s+([^>]*)>\s*<\/textarea>/,((e,n)=>`<textarea\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n></textarea>`)),this.formMarkUp+=p}renderTelField(e,n,t,i,s){const l=["required","pattern","minlength","maxlength"];let r="";i&&Object.entries(i).forEach((([e,t])=>{if(l.includes(e))if("boolean"==typeof t&&t)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${t}"\n`;break;default:l.includes(e)||console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'tel'.`)}else console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'tel'.`)}));let a="";if("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}">\n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();return $=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),$}renderDateField(e,n,t,i,s){const l=["required","min","max","step","placeholder","readonly","disabled","autocomplete","spellcheck","inputmode","title"];let r="";i&&Object.entries(i).forEach((([e,t])=>{if(l.includes(e))if("boolean"==typeof t&&t)r+=` ${e}\n`;else switch(e){case"min":case"max":case"step":r+=` ${e}="${t}"\n`;break;default:l.includes(e)||console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'date'.`)}else console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'date'.`)}));let a="";if("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}"> \n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderTimeField(e,n,t,i,s){const l=["required","min","max","step","readonly","disabled","autocomplete","spellcheck","inputmode","title"];let r="";i&&Object.entries(i).forEach((([t,i])=>{if(l.includes(t))if("boolean"==typeof i&&i)r+=` ${t}\n`;else switch(t){case"min":case"max":case"step":r+=` ${t}="${i}"\n`;break;default:l.includes(t)||console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}else console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let a="";if("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}"> \n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderDateTimeField(e,n,t,i,s){const l=["required","min","max","step","readonly","disabled","autocomplete","spellcheck","inputmode","title"];let r="";i&&Object.entries(i).forEach((([t,i])=>{if(l.includes(t))if("boolean"==typeof i&&i)r+=` ${t}\n`;else switch(t){case"min":case"max":case"step":r+=` ${t}="${i}"\n`;break;default:l.includes(t)||console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}else console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let a="";if(s.binding&&("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n))return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}"> \n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderMonthField(e,n,t,i,s){const l=["required","min","max","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";i&&Object.entries(i).forEach((([e,t])=>{if(l.includes(e))if("boolean"==typeof t&&t)r+=` ${e}\n`;else switch(e){case"min":case"max":case"pattern":r+=` ${e}="${t}"\n`;break;default:l.includes(e)&&console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'month'.`)}else console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'month'.`)}));let a="";if("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}">\n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderWeekField(e,n,t,i,s){const l=["required","min","max","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";i&&Object.entries(i).forEach((([e,t])=>{if(l.includes(e))if("boolean"==typeof t&&t)r+=` ${e}\n`;else switch(e){case"min":case"max":case"pattern":r+=` ${e}="${t}"\n`;break;default:l.includes(e)&&console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'week'.`)}else console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'week'.`)}));let a="";if("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}">\n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderUrlField(e,n,t,i,s){const l=["required","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";i&&Object.entries(i).forEach((([t,i])=>{if(l.includes(t))if("boolean"==typeof i&&i)r+=` ${t}\n`;else if("pattern"===t)r+=` ${t}="${i}"\n`;else l.includes(t)||console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`);else console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let a="";if("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}">\n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderSearchField(e,n,t,i,s){const l=["required","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";i&&Object.entries(i).forEach((([t,i])=>{if(l.includes(t))if("boolean"==typeof i&&i)r+=` ${t}\n`;else if("pattern"===t)r+=` ${t}="${i}"\n`;else l.includes(t)||console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`);else console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let a="";if("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}">\n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderColorField(e,n,t,i,s){const l=["required","readonly","disabled","autocomplete","inputmode","title"];let r="";i&&Object.entries(i).forEach((([t,i])=>{l.includes(t)?"boolean"==typeof i&&i?r+=` ${t}\n`:l.includes(t)||console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`):console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let a="";if(("bind:value"===s.binding||s.binding.startsWith("::")&&n)&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}">\n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderFileField(e,n,t,i,s){const l=["required","accept","multiple","disabled","title"];let r="";i&&Object.entries(i).forEach((([t,i])=>{l.includes(t)?"boolean"==typeof i&&i?r+=` ${t}\n`:l.includes(t)||console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`):console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let a="";if("bind:value"===s.binding&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}">\n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderHiddenField(e,n,t,i,s){const l=["type","name","value","id","class","style","required","readonly","disabled","tabindex"];let r="";i&&Object.entries(i).forEach((([t,i])=>{l.includes(t)&&"boolean"==typeof i&&i?r+=` ${t}\n`:console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let a="";if("bind:value"===s.binding&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}">\n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderImageField(e,n,t,i,s){const l=["accept","required","minwidth","maxwidth","minheight","maxheight"];let r="";i&&Object.entries(i).forEach((([t,i])=>{l.includes(t)?"accept"===t?r+=`accept="${i}"\n`:["required","minwidth","maxwidth","minheight","maxheight"].includes(t)?r+=`${t}="${i}"\n`:console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`):console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let a="";("bind:value"===s.binding||s.binding.startsWith("::"))&&(a=` bind:value="${n}"`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}">\n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();$=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=$}renderImageField(e,n,t,i,s){const l=["accept","required","minwidth","maxwidth","minheight","maxheight"];let r="";i&&Object.entries(i).forEach((([t,i])=>{l.includes(t)?r+=`${t}="${i}"\n`:console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let a="";("bind:value"===s.binding||bindingSyntax.startsWith("::"))&&(a=`bind:value="${n}"\n`);let d,o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);d="class"in s?s.class:this.inputClass;let $=`\n <div class="${this.divClass}" id="${o+"-block"}">\n <label for="${o}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <input \n type="${e}"\n name="${n}"\n ${a}\n id="${o}"\n class="${d}"\n ${c}\n ${r}\n />\n </div>\n `.replace(/^\s*\n/gm,"").trim();return $=$.replace(/<input\s+([^>]*)\/>/,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),$=$.replace(/(<div\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),$}renderTextAreaField(e,n,t,i,s){const l=["required","minlength","maxlength"];let r="";i&&Object.entries(i).forEach((([t,i])=>{l.includes(t)?r+="boolean"==typeof i&&i?` ${t}\n`:` ${t}="${i}"\n`:console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let a="";if(s.binding&&("bind:value"===s.binding&&n&&(a=`bind:value="${n}"\n`),s.binding.startsWith("::")&&n&&(a=`bind:value="${n}"\n`),s.binding&&!n))return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let d=s.id||n,o="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){o+=` ${e}="${n.endsWith("()")?n:`${n}()`}"\n`}else!0===n?o+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(o+=` ${e.replace(/_/g,"-")}="${n}"\n`);let c=s.class||this.inputClass,$=`\n <div class="${this.divClass}" id="${d+"-block"}">\n <label for="${d}">${t}\n ${r.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </label>\n <textarea \n name="${n}"\n ${a}\n id="${d}"\n class="${c}"\n ${o}\n ${r}\n ${o.includes("placeholder")?"":this.formSettings.placeholders?`placeholder="${t}"`:""}>\n </textarea>\n </div>\n`.replace(/^\s*\n/gm,"").trim();$=$.replace(/<textarea\s+([^>]*)>\s*<\/textarea>/,((e,n)=>`<textarea\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n></textarea>`)),this.formMarkUp+=$}renderRadioField(e,n,t,i,s,l){const r=["required"];let a="";i&&Object.entries(i).forEach((([e,t])=>{if(r.includes(e))if("boolean"==typeof t&&t)a+=` ${e}\n`;else if("required"===e)a+=` ${e}\n`;else r.includes(e)||console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'radio'.`);else console.warn(`Unsupported validation attribute '${e}' for field '${n}' of type 'radio'.`)}));let d="";if(s.binding)if("bind:value"===s.binding&&n)d=` bind:value="${n}"\n`;else if(s.binding.startsWith("::")&&n)d=` bind:value="${n}"\n`;else if(s.binding&&!n)return void console.log("%s",`You cannot set binding value when there is no name attribute defined in ${n} ${e} field.`);let o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);let $=s.class||this.inputClass,p="";l&&l.length&&(p=l.map((t=>`\n <div>\n <input \n type="${e}" \n name="${n}" \n value="${t.value}"\n ${d} \n ${c}\n ${s.id,`id="${o}-${t.value}"`}\n class="${$}"\n ${a}\n />\n <label \n for="${s.id,`${o}-${t.value}`}">\n ${t.label}\n </label>\n </div>\n `)).join(""));let m=`\n <fieldset class="${this.radioGroupClass}" id="${o+"-block"}">\n <legend>\n ${t} \n ${a.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </legend>\n ${p}\n </fieldset>\n `.replace(/^\s*\n/gm,"").trim().replace(/<input\s+([^>]*)\/>/g,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`));m=m.replace(/(<fieldset\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=m}renderCheckboxField(e,n,t,i,s,l){const r=["required"];let a="";i&&Object.entries(i).forEach((([t,i])=>{r.includes(t)?"required"===t&&(a+=`${t}\n`):console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let d="";s.binding&&("bind:checked"===s.binding||s.binding.startsWith("::"))&&(d=` bind:checked="${n}"\n`);let o,c=s.id||n,$="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;$+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?$+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&($+=` ${e.replace(/_/g,"-")}="${n}"\n`);o="class"in s?s.class:this.inputClass;let p="";Array.isArray(l)&&(p=l.map((e=>{const t=`${c}-${e.value}`;return`\n <div>\n <input \n type="checkbox" \n name="${n}" \n value="${e.value}"${d} ${$}\n ${s.id,`id="${t}"`}\n class="${o}"\n />\n <label \n for="${t}">\n ${e.label}\n </label>\n </div>\n `})).join(""));let m=`\n <fieldset class="${this.checkboxGroupClass}" id="${c+"-block"}">\n <legend>\n ${t} ${a.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </legend>\n ${p}\n </fieldset>\n `.replace(/^\s*\n/gm,"").trim();m=m.replace(/<input\s+([^>]*)\/>/g,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),m=m.replace(/(<fieldset\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=m}renderDynamicSingleSelectField(e,n,t,i,s,l){const r=l.flat().map((e=>{const n=e.options.some((e=>!0===e.selected));return{value:e.id,label:e.label,...n&&{selected:!0}}})),a=l;this.renderSingleSelectField(e,n,t,i,s,r,a,"dynamicSingleSelect")}renderSingleSelectField(e,n,t,i,s,l,r,a){const d=["required"];let o="";i&&Object.entries(i).forEach((([t,i])=>{d.includes(t)?"required"===t&&(o+=`${t} `):console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let c="";s.binding&&"string"==typeof s.binding&&s.binding.startsWith("::")&&(c=` bind:value="${n}" `);let $=s.id||n,p="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;p+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?p+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(p+=` ${e.replace(/_/g,"-")}="${n}"\n`);let m="";Array.isArray(l)&&(m+='\n <option value="">Choose an option</option>\n ',m+=l.map((e=>{const n=e.selected?" selected":"";return`\n <option value="${e.value}"${n}>${e.label}</option>\n `})).join(""));let u=s.class||this.inputClass;const f="dynamicSingleSelect"===a&&r?' onchange="handleDynamicSingleSelect(this.value,id)"':"";let h,b;if("dynamicSingleSelect"===a&&r)if(t.includes("-")){const[e]=t.split("-");h=e,b=t}else h=t,b=t;else h=t;let g=`\n <fieldset class="${this.selectGroupClass}" id="${$+"-block"}">\n <legend>${h} \n ${o.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </legend>\n <label for="${$}"> Select ${h} \n <select name="${n}"\n ${c}\n \n id="${$}"\n class="${u}"\n ${p}\n ${o}\n ${f} \n >\n ${m}\n </select>\n </fieldset>\n`.replace(/^\s*\n/gm,"").trim().replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g,((e,n,t)=>`<select\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n>\n${t.trim()}\n</select>`));if(g=g.replace(/(<fieldset\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=g,a&&"dynamicSingleSelect"===a&&r){const e=document.getElementById(this.formContainerId);let t=s.id||n;if(e){const n=document.createElement("script");n.textContent='\n window.handleDynamicSingleSelect = function(category, fieldsetid) {\n //console.log("HERE", fieldsetid);\n\n // Hide all subcategory fields\n document.querySelectorAll(`[class*="${fieldsetid}"]`).forEach(div => {\n div.style.display = "none";\n });\n\n // Show the selected category\n const selectedCategoryFieldset = document.getElementById(category + \'-options\');\n if (selectedCategoryFieldset) {\n selectedCategoryFieldset.style.display = "block";\n }\n }\n',e.appendChild(n)}else console.error(`Target div with id "${this.formContainerId}" not found.`);r.forEach((e=>{const{id:n,label:i,options:s}=e,l=s.map((e=>{const n=e.selected?" selected":"";return`\n <option value="${e.value}"${n}>${e.label}</option>\n `})).join("");let r,a;console.log("Label:",b),r=b.includes("-")?b.split("-")?.[1]+" Options":"options",a="options"!==r?b.split("-")?.[1]+" Option":r;let d=`\n <fieldset class="${this.selectGroupClass} ${t}" id="${n}-options" style="display: none;">\n <legend> ${i} ${r} ${this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </legend>\n <label for="${n}"> Select ${i} ${a} \n </label>\n <select name="${n}"\n ${c}\n \n id="${n+"-block"}"\n class="${u}"\n ${p}\n ${o}\n >\n <option value="">Choose an option</option>\n ${l}\n </select>\n </fieldset>\n `.replace(/^\s*\n/gm,"").trim();d=d.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g,((e,n,t)=>`<select\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n>\n${t.trim()}\n</select>`)),d=d.replace(/(<fieldset\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=d}))}}renderMultipleSelectField(e,n,t,i,s,l){const r=["required","minlength","maxlength"];let a="";i&&Object.entries(i).forEach((([t,i])=>{r.includes(t)?"required"===t?a+=`${t} `:"minlength"===t?a+=`minlength="${i}" `:"maxlength"===t&&(a+=`maxlength="${i}" `):console.warn(`Unsupported validation attribute '${t}' for field '${n}' of type '${e}'.`)}));let d="";s.binding&&"string"==typeof s.binding&&s.binding.startsWith("::")&&(d=` bind:value="${n}" `);let o=s.id||n,c="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){const t=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${t}}\n`}else!0===n?c+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(c+=` ${e.replace(/_/g,"-")}="${n}"\n`);let $="";Array.isArray(l)&&($=l.map((e=>{const n=e.selected?" selected":"";return`\n <option value="${e.value}"${n}>${e.label}</option>\n `})).join(""));let p;p="class"in s?s.class:this.inputClass;let m=`\n <fieldset class="${this.selectGroupClass}" id="${o+"-block"}">\n <label for="${o}">${t}\n ${a.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n</label>\n <select name="${n}"\n ${d}\n \n id="${o}"\n class="${p}"\n ${c}\n ${a}\n multiple\n >\n ${$}\n </select>\n </fieldset>\n `.replace(/^\s*\n/gm,"").trim();m=m.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g,((e,n,t)=>`<select\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n>\n${t.trim()}\n</select>`)),m=m.replace(/(<fieldset\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=m}renderSubmitButton(e,n,t,i,s){const l=s.id||n;let r,a="";for(const[e,n]of Object.entries(s))if("id"!==e&&"class"!==e&&"dependsOn"!==e&&"dependents"!==e&&void 0!==n)if(e.startsWith("on")){a+=` ${e}="${n.endsWith("()")?n.slice(0,-2):n}"`}else!0===n?a+=` ${e.replace(/_/g,"-")}`:!1!==n&&(a+=` ${e.replace(/_/g,"-")}="${n}"`);r="class"in s?s.class:this.submitButtonClass;let d=`\n <input type="${e}"\n id="${l+"-block"}"\n class="${r}"\n value="${t}"\n ${a}\n />\n `.replace(/^\s*\n/gm,"").trim();this.formMarkUp+=d}renderFormHTML(){this.formMarkUp+="</form>";const e=document.getElementById(this.formContainerId);e?e.innerHTML=this.formMarkUp:console.error(`Error: formContainer not found. Please ensure an element with id ${this.formContainerId} exists in the HTML.`)}}}));
package/package.json CHANGED
@@ -1,17 +1,24 @@
1
1
  {
2
2
  "name": "formique",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Formique Declarative Form Syntax JavaScript Library",
5
- "main": "dist/formique.umd.js",
6
- "module": "dist/formique.esm.js",
5
+ "main": "formique.umd.js",
6
+ "module": "formique.mjs",
7
+ "browser": "formique.umd.js",
7
8
  "exports": {
8
- ".": "./dist/formique.esm.js"
9
+ ".": {
10
+ "require": "./formique.cjs.js",
11
+ "import": "./formique.mjs",
12
+ "default": "./formique.umd.js"
13
+ }
9
14
  },
10
15
  "files": [
11
16
  "dist/*",
12
17
  "README.md"
13
18
  ],
14
- "keywords": [],
15
- "author": "",
16
- "license": "ISC"
19
+ "keywords": ["form", "javascript", "forms", "library"],
20
+ "author": "Gugulethu Nyoni",
21
+ "license": "MIT",
22
+ "dependencies": {},
23
+ "devDependencies": {}
17
24
  }