formique 1.0.4 → 1.0.6
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 +241 -92
- package/dist/css/formique.css +250 -0
- package/formique.cjs.js +1 -0
- package/formique.umd.js +1 -0
- package/package.json +16 -6
package/README.md
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
# Formique
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
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
|
-
|
|
9
|
+
Formique is Open-source & MIT licensed.
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
`
|
|
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
|
-
- **
|
|
114
|
+
- **Minlength**: Specifies the minimum number of characters allowed.
|
|
94
115
|
- Example: `minlength: 5`
|
|
95
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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
|
-
|
|
160
|
+
### Option A: Use Formique in a Browser Context (No Bundler Required)
|
|
128
161
|
|
|
129
|
-
1. Include the CSS
|
|
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.
|
|
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.
|
|
171
|
+
<script src="https://cdn.jsdelivr.net/npm/formique@1.0.1/formique.mjs.js"></script>
|
|
139
172
|
```
|
|
140
|
-
## Usage Example
|
|
141
173
|
|
|
142
|
-
|
|
174
|
+
### Usage Example:
|
|
143
175
|
|
|
144
|
-
|
|
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
|
-
|
|
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
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
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
|
-
|
|
195
|
-
```
|
|
221
|
+
---
|
|
196
222
|
|
|
197
|
-
|
|
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
|
-
|
|
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%;'
|
|
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: '
|
|
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' }
|
|
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' }, //
|
|
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' }
|
|
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
|
-
{
|
|
356
|
-
{ id: 'submitBtn', class: 'form-submit-btn', style: 'margin-top: 1rem;' }
|
|
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'
|
|
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:
|
|
@@ -616,4 +765,4 @@ Formique is licensed under the MIT License.
|
|
|
616
765
|
|
|
617
766
|
## Keywords
|
|
618
767
|
|
|
619
|
-
Javascript forms, declarative form syntax, js form library, formique
|
|
768
|
+
Javascript forms, declarative form syntax, js form library, formique
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600&display=swap');
|
|
2
|
+
|
|
3
|
+
/* Formique CSS */
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/* Dark Theme
|
|
7
|
+
|
|
8
|
+
:root {
|
|
9
|
+
--background-dark: #1e1e1e;
|
|
10
|
+
--background-light: #2e2e2e;
|
|
11
|
+
--text-light: #e0e0e0;
|
|
12
|
+
--border-color: #444;
|
|
13
|
+
--input-background: #333;
|
|
14
|
+
--input-background-disabled: #444;
|
|
15
|
+
--label-color: #b0b0b0;
|
|
16
|
+
--radio-checkbox-accent: #b0b0b0;
|
|
17
|
+
--button-background: #b0b0b0;
|
|
18
|
+
--button-background-hover: #9e9e9e;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Pink Theme
|
|
22
|
+
:root {
|
|
23
|
+
--background-dark: #2c2c2c;
|
|
24
|
+
--background-light: #3d3d3d;
|
|
25
|
+
--text-light: #f1f1f1;
|
|
26
|
+
--border-color: #555;
|
|
27
|
+
--input-background: #444;
|
|
28
|
+
--input-background-disabled: #666;
|
|
29
|
+
--label-color: #ff4081;
|
|
30
|
+
--radio-checkbox-accent: #ff4081;
|
|
31
|
+
--button-background: #ff4081;
|
|
32
|
+
--button-background-hover: #e91e63;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Deep Blue, Purple, and Pink Theme */
|
|
36
|
+
:root {
|
|
37
|
+
--background-dark: #1a1a40;
|
|
38
|
+
--background-light: #2b2b73;
|
|
39
|
+
--text-light: #f1f1f1;
|
|
40
|
+
--border-color: #4a4a91;
|
|
41
|
+
--input-background: #3a3a6b;
|
|
42
|
+
--input-background-disabled: #555587;
|
|
43
|
+
--label-color: #ff66b3;
|
|
44
|
+
--radio-checkbox-accent: #ff66b3;
|
|
45
|
+
--button-background: linear-gradient(45deg, #6a4fbf, #ff66b3);
|
|
46
|
+
--button-background-hover: linear-gradient(45deg, #4e2e8b, #ff4081);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
/* Indigo Theme
|
|
51
|
+
:root {
|
|
52
|
+
--background-dark: #2c2c2c;
|
|
53
|
+
--background-light: #3d3d3d;
|
|
54
|
+
--text-light: #e0e0e0;
|
|
55
|
+
--border-color: #444;
|
|
56
|
+
--input-background: #333;
|
|
57
|
+
--input-background-disabled: #555;
|
|
58
|
+
--label-color: #3f51b5;
|
|
59
|
+
--radio-checkbox-accent: #3f51b5;
|
|
60
|
+
--button-background: #3f51b5;
|
|
61
|
+
--button-background-hover: #303f9f;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Blue Theme
|
|
65
|
+
:root {
|
|
66
|
+
--background-dark: #0a0a0a;
|
|
67
|
+
--background-light: #1a1a1a;
|
|
68
|
+
--text-light: #e0e0e0;
|
|
69
|
+
--border-color: #333;
|
|
70
|
+
--input-background: #222;
|
|
71
|
+
--input-background-disabled: #444;
|
|
72
|
+
--label-color: #2196f3;
|
|
73
|
+
--radio-checkbox-accent: #2196f3;
|
|
74
|
+
--button-background: #2196f3;
|
|
75
|
+
--button-background-hover: #1976d2;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
/* Dark and Orange
|
|
81
|
+
|
|
82
|
+
:root {
|
|
83
|
+
--background-dark: #121212;
|
|
84
|
+
--background-light: #1e1e1e;
|
|
85
|
+
--text-light: #e0e0e0;
|
|
86
|
+
--border-color: #333;
|
|
87
|
+
--input-background: #333;
|
|
88
|
+
--input-background-disabled: #444;
|
|
89
|
+
--label-color: #ff9800;
|
|
90
|
+
--radio-checkbox-accent: #ff9800;
|
|
91
|
+
--button-background: #ff9800;
|
|
92
|
+
--button-background-hover: #e68900;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
|
|
101
|
+
#formique {
|
|
102
|
+
max-width: 600px;
|
|
103
|
+
margin: auto;
|
|
104
|
+
padding: 1rem;
|
|
105
|
+
background-color: var(--background-light);
|
|
106
|
+
border-radius: 8px;
|
|
107
|
+
color: var(--text-light);
|
|
108
|
+
font-family: 'Montserrat', sans-serif;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
#formique {
|
|
116
|
+
max-width: 600px;
|
|
117
|
+
margin: auto;
|
|
118
|
+
padding: 1rem;
|
|
119
|
+
background: linear-gradient(45deg, #1a1a40, #6a4fbf, #ff66b3); /* Blue, purple, pink gradient */
|
|
120
|
+
border-radius: 8px;
|
|
121
|
+
color: #ffffff; /* White text color */
|
|
122
|
+
font-family: 'Montserrat', sans-serif;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
/* Input Block Styling */
|
|
129
|
+
#formique .input-block {
|
|
130
|
+
margin-bottom: 1rem;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#formique .input-block label {
|
|
134
|
+
display: block;
|
|
135
|
+
margin-bottom: 0.5rem;
|
|
136
|
+
color: var(--label-color); /* Orange color for labels */
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#formique .input-block .form-input,
|
|
140
|
+
#formique .input-block .form-control {
|
|
141
|
+
width: 100%;
|
|
142
|
+
padding: 0.75rem;
|
|
143
|
+
border: 1px solid var(--border-color);
|
|
144
|
+
border-radius: 4px;
|
|
145
|
+
background-color: var(--input-background); /* Background for inputs */
|
|
146
|
+
color: var(--text-light);
|
|
147
|
+
box-sizing: border-box;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
#formique .input-block .form-input:disabled {
|
|
151
|
+
background-color: var(--input-background-disabled); /* Background for disabled inputs */
|
|
152
|
+
cursor: not-allowed;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* Radio Group Styling */
|
|
156
|
+
#formique .radio-group {
|
|
157
|
+
margin-bottom: 1rem;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
#formique .radio-group legend {
|
|
161
|
+
margin-bottom: 0.5rem;
|
|
162
|
+
font-weight: bold;
|
|
163
|
+
color: var(--label-color); /* Orange color for legend */
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#formique .radio-group div {
|
|
167
|
+
margin-bottom: 0.5rem;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
#formique .radio-group .form-radio-input {
|
|
171
|
+
margin-right: 0.5rem;
|
|
172
|
+
accent-color: var(--radio-checkbox-accent); /* Orange color for radio buttons */
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* Checkbox Group Styling */
|
|
176
|
+
#formique .checkbox-group {
|
|
177
|
+
margin-bottom: 1rem;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#formique .checkbox-group legend {
|
|
181
|
+
margin-bottom: 0.5rem;
|
|
182
|
+
font-weight: bold;
|
|
183
|
+
color: var(--label-color); /* Orange color for legend */
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
#formique .checkbox-group div {
|
|
187
|
+
margin-bottom: 0.5rem;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
#formique .checkbox-group .form-checkbox-input {
|
|
191
|
+
margin-right: 0.5rem;
|
|
192
|
+
accent-color: var(--radio-checkbox-accent); /* Orange color for checkboxes */
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Select Styling */
|
|
196
|
+
#formique .form-select {
|
|
197
|
+
margin-bottom: 1rem;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
#formique .form-select label {
|
|
201
|
+
display: block;
|
|
202
|
+
margin-bottom: 0.5rem;
|
|
203
|
+
color: var(--label-color); /* Orange color for labels */
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#formique .form-select legend {
|
|
207
|
+
margin-bottom: 0.5rem;
|
|
208
|
+
font-weight: bold;
|
|
209
|
+
color: var(--label-color); /* Orange color for legend */
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
#formique .form-select .form-select-input {
|
|
213
|
+
width: 100%;
|
|
214
|
+
padding: 0.75rem;
|
|
215
|
+
border: 1px solid var(--border-color);
|
|
216
|
+
border-radius: 4px;
|
|
217
|
+
background-color: var(--input-background); /* Background for selects */
|
|
218
|
+
color: var(--text-light);
|
|
219
|
+
box-sizing: border-box;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* Submit Button Styling */
|
|
223
|
+
#formique .form-submit-btn {
|
|
224
|
+
padding: 0.75rem 1.5rem;
|
|
225
|
+
border: 1px solid var(--text-light);
|
|
226
|
+
border-radius: 4px;
|
|
227
|
+
background-color: var(--button-background); /* Orange background */
|
|
228
|
+
color: var(--text-light);
|
|
229
|
+
font-size: 1rem;
|
|
230
|
+
cursor: pointer;
|
|
231
|
+
transition: background-color 0.3s;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
#formique .form-submit-btn:hover {
|
|
235
|
+
background-color: var(--button-background-hover); /* Darker orange on hover */
|
|
236
|
+
color: var(--text-light);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* General Styles */
|
|
240
|
+
|
|
241
|
+
/*
|
|
242
|
+
body {
|
|
243
|
+
background-color: var(--background-dark);
|
|
244
|
+
color: var(--text-light);
|
|
245
|
+
font-family: Arial, sans-serif;
|
|
246
|
+
margin: 0;
|
|
247
|
+
padding: 0;
|
|
248
|
+
}
|
|
249
|
+
*/
|
|
250
|
+
|
package/formique.cjs.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";class e{renderField(e,n,i,t,s,l){throw new Error("Method renderField must be implemented")}}module.exports=class extends e{constructor(e,n={},i={}){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=i.formContainerId||"formique",this.formMarkUp="",this.dependencyGraph={},this.themes=["dark","light","pink","light","indigo","dark-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)})),this.formSettings={requiredFieldIndicator:!0,placeholders:!0,asteriskHtml:'<span aria-hidden="true" style="color: red;">*</span>',...i},Object.keys(this.formParams).length>0&&(this.formMarkUp+=this.renderFormElement()),this.renderForm()}initDependencyGraph(){this.dependencyGraph={},this.formSchema.forEach((e=>{const[n,i,t,s,l={}]=e,r=l.id||i;l.dependents&&(this.dependencyGraph[r]=l.dependents.map((e=>{const n=this.formSchema.find((([,n])=>n===e));if(n){const i=n[4]||{};return{dependent:i.id||e,condition:i.condition||null}}console.warn(`Dependent field "${e}" not found in schema.`)})),this.dependencyGraph[r].push({state:null}),console.log("Graph",this.dependencyGraph[r]),this.attachInputChangeListener(r)),l.dependents&&l.dependents.forEach((e=>{const n=this.formSchema.find((([,n])=>n===e)),i=(n&&n[4]||{}).id||e,t=document.querySelector(`#${i}-block`);t&&(t.style.display="none")}))})),console.log("Dependency Graph:",this.dependencyGraph)}attachInputChangeListener(e){const n=document.getElementById(e);n&&n.addEventListener("input",(n=>{const i=n.target.value;this.handleParentFieldChange(e,i)}))}handleParentFieldChange(e,n){const i=this.dependencyGraph[e];i&&(this.dependencyGraph[e].forEach((e=>{void 0!==e.state&&(e.state=n)})),console.log(`Updated Dependency Graph for ${e}:`,this.dependencyGraph[e]),i.forEach((e=>{if(e.dependent){const i=e.dependent+"-block",t=document.getElementById(i);if(t){const s="function"==typeof e.condition?e.condition(n):n===e.condition;console.log(`Checking condition for ${i}: `,n,"==",e.condition,"Result:",s),t.style.display=s?"block":"none";t.querySelectorAll("input, select, textarea").forEach((e=>{s?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 ${i} not found.`)}})))}registerObservers(){this.formSchema.forEach((e=>{const[n,i,t,s,l={}]=e,r=l.id||i;l.dependents&&l.dependents.forEach((e=>{if(this.dependencyGraph[r]){const n=this.formSchema.find((([,n])=>n===e));if(n){const i=n[4]?.id||e;this.dependencyGraph[r].forEach((n=>{n.dependent===e&&(n.observers||(n.observers=[]),n.observers.push(i))}))}}}))})),console.log("Observers Registered:",JSON.stringify(this.dependencyGraph,null,2))}applyTheme(e,n){const i=document.querySelector("link[formique-style]");i?fetch(i.href).then((e=>e.text())).then((i=>{const t=i.match(new RegExp(`\\.${e}-theme\\s*{([^}]*)}`,"i"));if(!t)return void console.error(`Theme rules for ${e} not found in the stylesheet.`);const s=t[1].trim(),l=document.getElementById(n);if(l){l.classList.add(`${e}-theme`,"formique");const i=document.createElement("style");i.textContent=`\n #${n} {\n ${s}\n }\n `,l.parentNode.insertBefore(i,l),console.log(`Applied ${e} theme to form container: ${n}`)}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[i,t]of Object.entries(n))if(null!=t)if("boolean"==typeof t)t&&(e+=` ${i}\n`);else{e+=` ${"accept_charset"===i?"accept-charset":i.replace(/_/g,"-")}="${t}"\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,i,t,s,l={},r]=e;return this.renderField(n,i,t,s,l,r)})).join("");this.formMarkUp+=e}renderField(e,n,i,t,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,i,t,s,l):(console.warn(`Unsupported field type '${e}' encountered.`),"")}renderTextField(e,n,i,t,s){const l=["required","minlength","maxlength","pattern"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'number'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'text'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;$+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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="${i}"`:""} />\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,i,t,s){const l=["required","pattern","minlength","maxlength","multiple"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'number'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'email'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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="${i}"`:""}\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,i,t,s){const l=["required","min","max","step"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"min":case"max":case"step":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'number'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'number'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","minlength","maxlength","pattern"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"minlength":case"maxlength":case"pattern":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'password'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'password'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","minlength","maxlength","pattern"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'number'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'text'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;$+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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="${i}"`:""}>\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,i,t,s){const l=["required","pattern","minlength","maxlength"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'tel'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'tel'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","min","max","step","placeholder","readonly","disabled","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"min":case"max":case"step":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'date'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'date'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","min","max","step","readonly","disabled","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{if(l.includes(i))if("boolean"==typeof t&&t)r+=` ${i}\n`;else switch(i){case"min":case"max":case"step":r+=` ${i}="${t}"\n`;break;default:l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","min","max","step","readonly","disabled","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{if(l.includes(i))if("boolean"==typeof t&&t)r+=` ${i}\n`;else switch(i){case"min":case"max":case"step":r+=` ${i}="${t}"\n`;break;default:l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","min","max","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"min":case"max":case"pattern":r+=` ${e}="${i}"\n`;break;default:l.includes(e)&&console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'month'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'month'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","min","max","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"min":case"max":case"pattern":r+=` ${e}="${i}"\n`;break;default:l.includes(e)&&console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'week'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'week'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{if(l.includes(i))if("boolean"==typeof t&&t)r+=` ${i}\n`;else if("pattern"===i)r+=` ${i}="${t}"\n`;else l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`);else console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{if(l.includes(i))if("boolean"==typeof t&&t)r+=` ${i}\n`;else if("pattern"===i)r+=` ${i}="${t}"\n`;else l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`);else console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","readonly","disabled","autocomplete","inputmode","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)?"boolean"==typeof t&&t?r+=` ${i}\n`:l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));let a="";if(("bind:value"===s.binding||s.binding.startsWith("::")&&n)&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","accept","multiple","disabled","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)?"boolean"==typeof t&&t?r+=` ${i}\n`:l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["type","name","value","id","class","style","required","readonly","disabled","tabindex"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)&&"boolean"==typeof t&&t?r+=` ${i}\n`:console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["accept","required","minwidth","maxwidth","minheight","maxheight"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)?"accept"===i?r+=`accept="${t}"\n`:["required","minwidth","maxwidth","minheight","maxheight"].includes(i)?r+=`${i}="${t}"\n`:console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["accept","required","minwidth","maxwidth","minheight","maxheight"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)?r+=`${i}="${t}"\n`:console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","minlength","maxlength"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)?r+="boolean"==typeof t&&t?` ${i}\n`:` ${i}="${t}"\n`:console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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}">${i}\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="${i}"`:""}>\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,i,t,s,l){const r=["required"];let a="";t&&Object.entries(t).forEach((([e,i])=>{if(r.includes(e))if("boolean"==typeof i&&i)a+=` ${e}\n`;else if("required"===e)a+=` ${e}\n`;else r.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'radio'.[0m`);else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'radio'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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((i=>`\n <div>\n <input \n type="${e}" \n name="${n}" \n value="${i.value}"\n ${d} \n ${c}\n ${s.id,`id="${o}-${i.value}"`}\n class="${$}"\n ${a}\n />\n <label \n for="${s.id,`${o}-${i.value}`}">\n ${i.label}\n </label>\n </div>\n `)).join(""));let u=`\n <fieldset class="${this.radioGroupClass}" id="${o+"-block"}">\n <legend>\n ${i} \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/>`));u=u.replace(/(<fieldset\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=u}renderCheckboxField(e,n,i,t,s,l){const r=["required"];let a="";t&&Object.entries(t).forEach((([i,t])=>{r.includes(i)?"required"===i&&(a+=`${i}\n`):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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 i=n.endsWith("()")?n.slice(0,-2):n;$+=` @${e.replace(/^on/,"")}={${i}}\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 i=`${c}-${e.value}`;return`\n <div>\n <input \n type="checkbox" \n name="${n}" \n value="${e.value}"${d} ${$}\n ${s.id,`id="${i}"`}\n class="${o}"\n />\n <label \n for="${i}">\n ${e.label}\n </label>\n </div>\n `})).join(""));let u=`\n <fieldset class="${this.checkboxGroupClass}" id="${c+"-block"}">\n <legend>\n ${i} ${a.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </legend>\n ${p}\n </fieldset>\n `.replace(/^\s*\n/gm,"").trim();u=u.replace(/<input\s+([^>]*)\/>/g,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),u=u.replace(/(<fieldset\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=u}renderDynamicSingleSelectField(e,n,i,t,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,i,t,s,r,a,"dynamicSingleSelect")}renderSingleSelectField(e,n,i,t,s,l,r,a){const d=["required"];let o="";t&&Object.entries(t).forEach((([i,t])=>{d.includes(i)?"required"===i&&(o+=`${i} `):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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 i=n.endsWith("()")?n.slice(0,-2):n;p+=` @${e.replace(/^on/,"")}={${i}}\n`}else!0===n?p+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(p+=` ${e.replace(/_/g,"-")}="${n}"\n`);let u="";Array.isArray(l)&&(u+='\n <option value="">Choose an option</option>\n ',u+=l.map((e=>{const n=e.selected?" selected":"";return`\n <option value="${e.value}"${n}>${e.label}</option>\n `})).join(""));let m=s.class||this.inputClass;const f="dynamicSingleSelect"===a&&r?' onchange="handleDynamicSingleSelect(this.value,id)"':"";let h,b;if("dynamicSingleSelect"===a&&r)if(i.includes("-")){const[e]=i.split("-");h=e,b=i}else h=i,b=i;else h=i;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="${m}"\n ${p}\n ${o}\n ${f} \n >\n ${u}\n </select>\n </fieldset>\n`.replace(/^\s*\n/gm,"").trim().replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g,((e,n,i)=>`<select\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n>\n${i.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 i=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:t,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} ${i}" id="${n}-options" style="display: none;">\n <legend> ${t} ${r} ${this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </legend>\n <label for="${n}"> Select ${t} ${a} \n </label>\n <select name="${n}"\n ${c}\n \n id="${n+"-block"}"\n class="${m}"\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,i)=>`<select\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n>\n${i.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,i,t,s,l){const r=["required","minlength","maxlength"];let a="";t&&Object.entries(t).forEach((([i,t])=>{r.includes(i)?"required"===i?a+=`${i} `:"minlength"===i?a+=`minlength="${t}" `:"maxlength"===i&&(a+=`maxlength="${t}" `):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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 u=`\n <fieldset class="${this.selectGroupClass}" id="${o+"-block"}">\n <label for="${o}">${i}\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();u=u.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g,((e,n,i)=>`<select\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n>\n${i.trim()}\n</select>`)),u=u.replace(/(<fieldset\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=u}renderSubmitButton(e,n,i,t,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="${i}"\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/formique.umd.js
ADDED
|
@@ -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,i,t,s,l){throw new Error("Method renderField must be implemented")}}return class extends e{constructor(e,n={},i={}){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=i.formContainerId||"formique",this.formMarkUp="",this.dependencyGraph={},this.themes=["dark","light","pink","light","indigo","dark-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)})),this.formSettings={requiredFieldIndicator:!0,placeholders:!0,asteriskHtml:'<span aria-hidden="true" style="color: red;">*</span>',...i},Object.keys(this.formParams).length>0&&(this.formMarkUp+=this.renderFormElement()),this.renderForm()}initDependencyGraph(){this.dependencyGraph={},this.formSchema.forEach((e=>{const[n,i,t,s,l={}]=e,r=l.id||i;l.dependents&&(this.dependencyGraph[r]=l.dependents.map((e=>{const n=this.formSchema.find((([,n])=>n===e));if(n){const i=n[4]||{};return{dependent:i.id||e,condition:i.condition||null}}console.warn(`Dependent field "${e}" not found in schema.`)})),this.dependencyGraph[r].push({state:null}),console.log("Graph",this.dependencyGraph[r]),this.attachInputChangeListener(r)),l.dependents&&l.dependents.forEach((e=>{const n=this.formSchema.find((([,n])=>n===e)),i=(n&&n[4]||{}).id||e,t=document.querySelector(`#${i}-block`);t&&(t.style.display="none")}))})),console.log("Dependency Graph:",this.dependencyGraph)}attachInputChangeListener(e){const n=document.getElementById(e);n&&n.addEventListener("input",(n=>{const i=n.target.value;this.handleParentFieldChange(e,i)}))}handleParentFieldChange(e,n){const i=this.dependencyGraph[e];i&&(this.dependencyGraph[e].forEach((e=>{void 0!==e.state&&(e.state=n)})),console.log(`Updated Dependency Graph for ${e}:`,this.dependencyGraph[e]),i.forEach((e=>{if(e.dependent){const i=e.dependent+"-block",t=document.getElementById(i);if(t){const s="function"==typeof e.condition?e.condition(n):n===e.condition;console.log(`Checking condition for ${i}: `,n,"==",e.condition,"Result:",s),t.style.display=s?"block":"none";t.querySelectorAll("input, select, textarea").forEach((e=>{s?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 ${i} not found.`)}})))}registerObservers(){this.formSchema.forEach((e=>{const[n,i,t,s,l={}]=e,r=l.id||i;l.dependents&&l.dependents.forEach((e=>{if(this.dependencyGraph[r]){const n=this.formSchema.find((([,n])=>n===e));if(n){const i=n[4]?.id||e;this.dependencyGraph[r].forEach((n=>{n.dependent===e&&(n.observers||(n.observers=[]),n.observers.push(i))}))}}}))})),console.log("Observers Registered:",JSON.stringify(this.dependencyGraph,null,2))}applyTheme(e,n){const i=document.querySelector("link[formique-style]");i?fetch(i.href).then((e=>e.text())).then((i=>{const t=i.match(new RegExp(`\\.${e}-theme\\s*{([^}]*)}`,"i"));if(!t)return void console.error(`Theme rules for ${e} not found in the stylesheet.`);const s=t[1].trim(),l=document.getElementById(n);if(l){l.classList.add(`${e}-theme`,"formique");const i=document.createElement("style");i.textContent=`\n #${n} {\n ${s}\n }\n `,l.parentNode.insertBefore(i,l),console.log(`Applied ${e} theme to form container: ${n}`)}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[i,t]of Object.entries(n))if(null!=t)if("boolean"==typeof t)t&&(e+=` ${i}\n`);else{e+=` ${"accept_charset"===i?"accept-charset":i.replace(/_/g,"-")}="${t}"\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,i,t,s,l={},r]=e;return this.renderField(n,i,t,s,l,r)})).join("");this.formMarkUp+=e}renderField(e,n,i,t,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,i,t,s,l):(console.warn(`Unsupported field type '${e}' encountered.`),"")}renderTextField(e,n,i,t,s){const l=["required","minlength","maxlength","pattern"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'number'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'text'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;$+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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="${i}"`:""} />\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,i,t,s){const l=["required","pattern","minlength","maxlength","multiple"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'number'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'email'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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="${i}"`:""}\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,i,t,s){const l=["required","min","max","step"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"min":case"max":case"step":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'number'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'number'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","minlength","maxlength","pattern"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"minlength":case"maxlength":case"pattern":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'password'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'password'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","minlength","maxlength","pattern"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'number'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'text'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;$+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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="${i}"`:""}>\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,i,t,s){const l=["required","pattern","minlength","maxlength"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"pattern":case"minlength":case"maxlength":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'tel'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'tel'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","min","max","step","placeholder","readonly","disabled","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"min":case"max":case"step":r+=` ${e}="${i}"\n`;break;default:l.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'date'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'date'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","min","max","step","readonly","disabled","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{if(l.includes(i))if("boolean"==typeof t&&t)r+=` ${i}\n`;else switch(i){case"min":case"max":case"step":r+=` ${i}="${t}"\n`;break;default:l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","min","max","step","readonly","disabled","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{if(l.includes(i))if("boolean"==typeof t&&t)r+=` ${i}\n`;else switch(i){case"min":case"max":case"step":r+=` ${i}="${t}"\n`;break;default:l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","min","max","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"min":case"max":case"pattern":r+=` ${e}="${i}"\n`;break;default:l.includes(e)&&console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'month'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'month'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","min","max","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([e,i])=>{if(l.includes(e))if("boolean"==typeof i&&i)r+=` ${e}\n`;else switch(e){case"min":case"max":case"pattern":r+=` ${e}="${i}"\n`;break;default:l.includes(e)&&console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'week'.[0m`)}else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'week'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{if(l.includes(i))if("boolean"==typeof t&&t)r+=` ${i}\n`;else if("pattern"===i)r+=` ${i}="${t}"\n`;else l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`);else console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","pattern","placeholder","readonly","disabled","size","autocomplete","spellcheck","inputmode","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{if(l.includes(i))if("boolean"==typeof t&&t)r+=` ${i}\n`;else if("pattern"===i)r+=` ${i}="${t}"\n`;else l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`);else console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","readonly","disabled","autocomplete","inputmode","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)?"boolean"==typeof t&&t?r+=` ${i}\n`:l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));let a="";if(("bind:value"===s.binding||s.binding.startsWith("::")&&n)&&(a=`bind:value="${n}"\n`),s.binding&&!n)return void console.log("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","accept","multiple","disabled","title"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)?"boolean"==typeof t&&t?r+=` ${i}\n`:l.includes(i)||console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["type","name","value","id","class","style","required","readonly","disabled","tabindex"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)&&"boolean"==typeof t&&t?r+=` ${i}\n`:console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["accept","required","minwidth","maxwidth","minheight","maxheight"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)?"accept"===i?r+=`accept="${t}"\n`:["required","minwidth","maxwidth","minheight","maxheight"].includes(i)?r+=`${i}="${t}"\n`:console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["accept","required","minwidth","maxwidth","minheight","maxheight"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)?r+=`${i}="${t}"\n`:console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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}">${i}\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,i,t,s){const l=["required","minlength","maxlength"];let r="";t&&Object.entries(t).forEach((([i,t])=>{l.includes(i)?r+="boolean"==typeof t&&t?` ${i}\n`:` ${i}="${t}"\n`:console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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("[31m%s[0m",`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}">${i}\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="${i}"`:""}>\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,i,t,s,l){const r=["required"];let a="";t&&Object.entries(t).forEach((([e,i])=>{if(r.includes(e))if("boolean"==typeof i&&i)a+=` ${e}\n`;else if("required"===e)a+=` ${e}\n`;else r.includes(e)||console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'radio'.[0m`);else console.warn(`[31mUnsupported validation attribute '${e}' for field '${n}' of type 'radio'.[0m`)}));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("[31m%s[0m",`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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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((i=>`\n <div>\n <input \n type="${e}" \n name="${n}" \n value="${i.value}"\n ${d} \n ${c}\n ${s.id,`id="${o}-${i.value}"`}\n class="${$}"\n ${a}\n />\n <label \n for="${s.id,`${o}-${i.value}`}">\n ${i.label}\n </label>\n </div>\n `)).join(""));let u=`\n <fieldset class="${this.radioGroupClass}" id="${o+"-block"}">\n <legend>\n ${i} \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/>`));u=u.replace(/(<fieldset\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=u}renderCheckboxField(e,n,i,t,s,l){const r=["required"];let a="";t&&Object.entries(t).forEach((([i,t])=>{r.includes(i)?"required"===i&&(a+=`${i}\n`):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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 i=n.endsWith("()")?n.slice(0,-2):n;$+=` @${e.replace(/^on/,"")}={${i}}\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 i=`${c}-${e.value}`;return`\n <div>\n <input \n type="checkbox" \n name="${n}" \n value="${e.value}"${d} ${$}\n ${s.id,`id="${i}"`}\n class="${o}"\n />\n <label \n for="${i}">\n ${e.label}\n </label>\n </div>\n `})).join(""));let u=`\n <fieldset class="${this.checkboxGroupClass}" id="${c+"-block"}">\n <legend>\n ${i} ${a.includes("required")&&this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </legend>\n ${p}\n </fieldset>\n `.replace(/^\s*\n/gm,"").trim();u=u.replace(/<input\s+([^>]*)\/>/g,((e,n)=>`<input\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n/>`)),u=u.replace(/(<fieldset\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=u}renderDynamicSingleSelectField(e,n,i,t,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,i,t,s,r,a,"dynamicSingleSelect")}renderSingleSelectField(e,n,i,t,s,l,r,a){const d=["required"];let o="";t&&Object.entries(t).forEach((([i,t])=>{d.includes(i)?"required"===i&&(o+=`${i} `):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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 i=n.endsWith("()")?n.slice(0,-2):n;p+=` @${e.replace(/^on/,"")}={${i}}\n`}else!0===n?p+=` ${e.replace(/_/g,"-")}\n`:!1!==n&&(p+=` ${e.replace(/_/g,"-")}="${n}"\n`);let u="";Array.isArray(l)&&(u+='\n <option value="">Choose an option</option>\n ',u+=l.map((e=>{const n=e.selected?" selected":"";return`\n <option value="${e.value}"${n}>${e.label}</option>\n `})).join(""));let m=s.class||this.inputClass;const f="dynamicSingleSelect"===a&&r?' onchange="handleDynamicSingleSelect(this.value,id)"':"";let h,b;if("dynamicSingleSelect"===a&&r)if(i.includes("-")){const[e]=i.split("-");h=e,b=i}else h=i,b=i;else h=i;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="${m}"\n ${p}\n ${o}\n ${f} \n >\n ${u}\n </select>\n </fieldset>\n`.replace(/^\s*\n/gm,"").trim().replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g,((e,n,i)=>`<select\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n>\n${i.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 i=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:t,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} ${i}" id="${n}-options" style="display: none;">\n <legend> ${t} ${r} ${this.formSettings.requiredFieldIndicator?this.formSettings.asteriskHtml:""}\n </legend>\n <label for="${n}"> Select ${t} ${a} \n </label>\n <select name="${n}"\n ${c}\n \n id="${n+"-block"}"\n class="${m}"\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,i)=>`<select\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n>\n${i.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,i,t,s,l){const r=["required","minlength","maxlength"];let a="";t&&Object.entries(t).forEach((([i,t])=>{r.includes(i)?"required"===i?a+=`${i} `:"minlength"===i?a+=`minlength="${t}" `:"maxlength"===i&&(a+=`maxlength="${t}" `):console.warn(`[31mUnsupported validation attribute '${i}' for field '${n}' of type '${e}'.[0m`)}));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 i=n.endsWith("()")?n.slice(0,-2):n;c+=` @${e.replace(/^on/,"")}={${i}}\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 u=`\n <fieldset class="${this.selectGroupClass}" id="${o+"-block"}">\n <label for="${o}">${i}\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();u=u.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g,((e,n,i)=>`<select\n${n.trim().split(/\s+/).map((e=>` ${e}`)).join("\n")}\n>\n${i.trim()}\n</select>`)),u=u.replace(/(<fieldset\s+[^>]*>)/g,(e=>`\n${e}\n`)).replace(/\n\s*\n/g,"\n"),this.formMarkUp+=u}renderSubmitButton(e,n,i,t,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="${i}"\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,14 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "formique",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Formique Declarative Form Syntax JavaScript Library",
|
|
5
|
-
"main": "
|
|
6
|
-
"module": "
|
|
5
|
+
"main": "formique.cjs.js",
|
|
6
|
+
"module": "formique.mjs",
|
|
7
|
+
"browser": "formique.umd.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"require": "./formique.cjs.js",
|
|
11
|
+
"import": "./formique.mjs",
|
|
12
|
+
"default": "./formique.umd.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
7
15
|
"files": [
|
|
8
16
|
"dist/*",
|
|
9
17
|
"README.md"
|
|
10
18
|
],
|
|
11
|
-
"keywords": [],
|
|
12
|
-
"author": "",
|
|
13
|
-
"license": "
|
|
19
|
+
"keywords": ["form", "javascript", "forms", "library"],
|
|
20
|
+
"author": "Gugulethu Nyoni",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"dependencies": {},
|
|
23
|
+
"devDependencies": {}
|
|
14
24
|
}
|