@formique/semantq 1.0.3 → 1.0.5
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 +254 -17
- package/formique-semantq.js +4030 -0
- package/package.json +2 -2
- package/formique.mjs +0 -1
|
@@ -0,0 +1,4030 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* Formique Semantq Class Library
|
|
4
|
+
*
|
|
5
|
+
* This library provides an extension of the FormBuilder class, allowing for dynamic form rendering, theming,
|
|
6
|
+
* and dependency management. The key functionalities include:
|
|
7
|
+
*
|
|
8
|
+
* - Dynamic form rendering based on a provided schema (`formSchema`).
|
|
9
|
+
* - Theming support with predefined themes that can be applied to the form container.
|
|
10
|
+
* - Dependency management to show/hide fields based on parent field values.
|
|
11
|
+
* - Initialization of event listeners to handle form input changes.
|
|
12
|
+
* - **Dynamic dropdowns**: Automatically populate dropdown fields based on other form inputs.
|
|
13
|
+
* - **ARIA labels and WCAG compliance**: Generates forms with accessibility features, including ARIA labels for improved accessibility and compliance with Web Content Accessibility Guidelines (WCAG).
|
|
14
|
+
*
|
|
15
|
+
* Key Methods:
|
|
16
|
+
* - `constructor(formParams, formSchema, formSettings)`: Initializes the form with the provided parameters, schema, and settings.
|
|
17
|
+
* - `renderForm()`: Renders the form using the schema and appends it to the DOM.
|
|
18
|
+
* - `initDependencyGraph()`: Sets up the dependency graph for managing field visibility based on dependencies.
|
|
19
|
+
* - `attachInputChangeListener(parentField)`: Attaches input change listeners to parent fields for dependency management.
|
|
20
|
+
* - `handleParentFieldChange(parentFieldId, value)`: Handles changes in parent fields and updates dependent fields.
|
|
21
|
+
* - `registerObservers()`: Registers observers for dependent fields to manage their state based on parent field values.
|
|
22
|
+
* - `applyTheme(theme, formContainerId)`: Applies a specified theme to the form container.
|
|
23
|
+
* - `renderFormElement()`: Renders the form element with the necessary attributes and CSRF token if applicable.
|
|
24
|
+
* - `renderField(type, name, label, validate, attributes, options)`: Renders individual form fields based on type and attributes, including dynamic dropdowns and ARIA attributes.
|
|
25
|
+
*
|
|
26
|
+
* Dependencies:
|
|
27
|
+
* - The library depends on a DOM structure to initialize and manipulate form elements.
|
|
28
|
+
* - Requires a CSS stylesheet with theme definitions.- there are plans to internalise css themes within js
|
|
29
|
+
*
|
|
30
|
+
* Example Usage:
|
|
31
|
+
* const form = new Formique(formSchema,formParams,formSettings);
|
|
32
|
+
* - formParams and formSettings parameters are optional
|
|
33
|
+
*
|
|
34
|
+
* This package is suited for Vanilla Js implementations. Formique has different versions
|
|
35
|
+
* applicable to these frameworks: Svelte, Vue JS, React and Angular.
|
|
36
|
+
*
|
|
37
|
+
* Author: Gugulethu Nyoni
|
|
38
|
+
* Version: 1.0.8
|
|
39
|
+
* License: Open-source & MIT licensed.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class FormBuilder
|
|
44
|
+
{
|
|
45
|
+
renderField(type, name, label, validate, attributes, options) {
|
|
46
|
+
throw new Error('Method renderField must be implemented');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Extended class for specific form rendering methods
|
|
52
|
+
class Formique extends FormBuilder {
|
|
53
|
+
constructor(formSchema, formSettings = {}, formParams = {}) {
|
|
54
|
+
super();
|
|
55
|
+
this.formSchema = formSchema;
|
|
56
|
+
this.formParams = formParams;
|
|
57
|
+
this.formSettings = {
|
|
58
|
+
requiredFieldIndicator: true,
|
|
59
|
+
placeholders: true,
|
|
60
|
+
asteriskHtml: '<span aria-hidden="true" style="color: red;">*</span>',
|
|
61
|
+
...formSettings
|
|
62
|
+
};
|
|
63
|
+
this.divClass = 'input-block';
|
|
64
|
+
this.inputClass = 'form-input';
|
|
65
|
+
this.radioGroupClass = 'radio-group';
|
|
66
|
+
this.checkboxGroupClass = 'checkbox-group';
|
|
67
|
+
this.selectGroupClass = 'form-select';
|
|
68
|
+
this.submitButtonClass = 'form-submit-btn';
|
|
69
|
+
this.formContainerId = formSettings?.formContainerId || 'formique';
|
|
70
|
+
// Ensure formParams.id is used if provided, otherwise generate a new ID
|
|
71
|
+
this.formId = this.formParams?.id || this.generateFormId();
|
|
72
|
+
this.formAction = formParams?.action || 'https://httpbin.org/post';
|
|
73
|
+
this.method = 'POST';
|
|
74
|
+
this.formMarkUp = '';
|
|
75
|
+
this.dependencyGraph = {};
|
|
76
|
+
this.redirect = formSettings?.redirect || '';
|
|
77
|
+
this.redirectURL = formSettings?.redirectURL || '';
|
|
78
|
+
this.themes = [
|
|
79
|
+
"dark", "light", "pink", "indigo", "dark-blue", "light-blue",
|
|
80
|
+
"dark-orange", "bright-yellow", "green", "purple", "midnight-blush",
|
|
81
|
+
"deep-blue", "blue", "brown", "orange"
|
|
82
|
+
];
|
|
83
|
+
this.formiqueEndpoint = "https://formiqueapi.onrender.com/api/send-email";
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
87
|
+
// 1. Build the form's HTML in memory
|
|
88
|
+
this.formMarkUp += this.renderFormElement(); // Adds opening <form> tag and any hidden inputs
|
|
89
|
+
|
|
90
|
+
// Filter out 'submit' field for rendering, and render all other fields
|
|
91
|
+
const nonSubmitFieldsHtml = this.formSchema
|
|
92
|
+
.filter(field => field[0] !== 'submit')
|
|
93
|
+
.map(field => {
|
|
94
|
+
const [type, name, label, validate, attributes = {}, options] = field;
|
|
95
|
+
return this.renderField(type, name, label, validate, attributes, options);
|
|
96
|
+
}).join('');
|
|
97
|
+
this.formMarkUp += nonSubmitFieldsHtml;
|
|
98
|
+
|
|
99
|
+
// Find and render the submit button separately, at the very end of the form content
|
|
100
|
+
const submitField = this.formSchema.find(field => field[0] === 'submit');
|
|
101
|
+
if (submitField) {
|
|
102
|
+
const [type, name, label, validate, attributes = {}] = submitField;
|
|
103
|
+
const id = attributes.id || name;
|
|
104
|
+
let buttonClass = this.submitButtonClass;
|
|
105
|
+
if ('class' in attributes) {
|
|
106
|
+
buttonClass = attributes.class;
|
|
107
|
+
}
|
|
108
|
+
let additionalAttrs = '';
|
|
109
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
110
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) {
|
|
111
|
+
if (key.startsWith('on')) {
|
|
112
|
+
const eventValue = value.endsWith('()') ? value : `${value}()`;
|
|
113
|
+
additionalAttrs += ` ${key}="${eventValue}"`;
|
|
114
|
+
} else {
|
|
115
|
+
if (value === true) {
|
|
116
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}`;
|
|
117
|
+
} else if (value !== false) {
|
|
118
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"`;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
this.formMarkUp += `
|
|
124
|
+
<div id="formiqueSpinner" style="display: flex; align-items: center; gap: 1rem; font-family: sans-serif; display:none;">
|
|
125
|
+
<div class="formique-spinner"></div>
|
|
126
|
+
<p class="message">Hang in tight, we are submitting your details…</p>
|
|
127
|
+
</div>
|
|
128
|
+
<input type="submit" id="${id}" class="${buttonClass}" value="${label}"${additionalAttrs}>
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
// 2. Inject the complete form HTML into the DOM
|
|
134
|
+
this.renderFormHTML(); // This puts the form element into the document!
|
|
135
|
+
|
|
136
|
+
// 3. Now that the form is in the DOM, attach event listeners
|
|
137
|
+
const formElement = document.getElementById(`${this.formId}`);
|
|
138
|
+
if (formElement) { // Add a check here just in case, although it should now exist
|
|
139
|
+
formElement.addEventListener('submit', function(event) {
|
|
140
|
+
if (this.formSettings.submitMode === 'email') {
|
|
141
|
+
event.preventDefault();
|
|
142
|
+
document.getElementById("formiqueSpinner").style.display = "block";
|
|
143
|
+
this.handleEmailSubmission(this.formId);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (this.formSettings.submitOnPage) {
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
document.getElementById("formiqueSpinner").style.display = "block";
|
|
149
|
+
this.handleOnPageFormSubmission(this.formId);
|
|
150
|
+
}
|
|
151
|
+
}.bind(this));
|
|
152
|
+
} else {
|
|
153
|
+
console.error(`Form with ID ${this.formId} not found after rendering. Event listener could not be attached.`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Initialize dependency graph and observers after the form is rendered
|
|
157
|
+
this.initDependencyGraph();
|
|
158
|
+
this.registerObservers();
|
|
159
|
+
|
|
160
|
+
// Apply theme
|
|
161
|
+
if (this.formSettings.theme && this.themes.includes(this.formSettings.theme)) {
|
|
162
|
+
let theme = this.formSettings.theme;
|
|
163
|
+
this.applyTheme(theme, this.formContainerId);
|
|
164
|
+
} else {
|
|
165
|
+
this.applyTheme('dark', this.formContainerId);
|
|
166
|
+
}
|
|
167
|
+
}); // DOM LISTENER WRAPPER
|
|
168
|
+
|
|
169
|
+
// CONSTRUCTOR WRAPPER FOR FORMIQUE CLASS
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
generateFormId() {
|
|
174
|
+
return `fmq-${Math.random().toString(36).substr(2, 10)}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
initDependencyGraph() {
|
|
180
|
+
this.dependencyGraph = {};
|
|
181
|
+
|
|
182
|
+
this.formSchema.forEach((field) => {
|
|
183
|
+
const [type, name, label, validate, attributes = {}] = field;
|
|
184
|
+
const fieldId = attributes.id || name;
|
|
185
|
+
|
|
186
|
+
if (attributes.dependents) {
|
|
187
|
+
// Initialize dependency array for the parent field
|
|
188
|
+
this.dependencyGraph[fieldId] = attributes.dependents.map((dependentName) => {
|
|
189
|
+
const dependentField = this.formSchema.find(
|
|
190
|
+
([, depName]) => depName === dependentName
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
if (dependentField) {
|
|
194
|
+
const dependentAttributes = dependentField[4] || {};
|
|
195
|
+
const dependentFieldId = dependentAttributes.id || dependentName; // Get dependent field ID
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
dependent: dependentFieldId,
|
|
199
|
+
condition: dependentAttributes.condition || null,
|
|
200
|
+
};
|
|
201
|
+
} else {
|
|
202
|
+
console.warn(`Dependent field "${dependentName}" not found in schema.`);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Add state tracking for the parent field
|
|
207
|
+
this.dependencyGraph[fieldId].push({ state: null });
|
|
208
|
+
|
|
209
|
+
// console.log("Graph", this.dependencyGraph[fieldId]);
|
|
210
|
+
|
|
211
|
+
// Attach the input change event listener to the parent field
|
|
212
|
+
this.attachInputChangeListener(fieldId);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Hide dependent fields initially
|
|
216
|
+
if (attributes.dependents) {
|
|
217
|
+
|
|
218
|
+
attributes.dependents.forEach((dependentName) => {
|
|
219
|
+
const dependentField = this.formSchema.find(
|
|
220
|
+
([, depName]) => depName === dependentName
|
|
221
|
+
);
|
|
222
|
+
const dependentAttributes = dependentField ? dependentField[4] || {} : {};
|
|
223
|
+
const dependentFieldId = dependentAttributes.id || dependentName;
|
|
224
|
+
|
|
225
|
+
//alert(dependentFieldId);
|
|
226
|
+
|
|
227
|
+
const inputBlock = document.querySelector(`#${dependentFieldId}-block`);
|
|
228
|
+
//alert(inputBlock);
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
if (inputBlock) {
|
|
232
|
+
// alert(dependentName);
|
|
233
|
+
inputBlock.style.display = 'none'; // Hide dependent field by default
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// console.log("Dependency Graph:", this.dependencyGraph);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
// Attach Event Listeners
|
|
244
|
+
attachInputChangeListener(parentField) {
|
|
245
|
+
const fieldElement = document.getElementById(parentField);
|
|
246
|
+
//alert(parentField);
|
|
247
|
+
|
|
248
|
+
if (fieldElement) {
|
|
249
|
+
fieldElement.addEventListener('input', (event) => {
|
|
250
|
+
const value = event.target.value;
|
|
251
|
+
this.handleParentFieldChange(parentField, value);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
handleParentFieldChange(parentFieldId, value) {
|
|
258
|
+
const dependencies = this.dependencyGraph[parentFieldId];
|
|
259
|
+
|
|
260
|
+
if (dependencies) {
|
|
261
|
+
// Update the state of the parent field
|
|
262
|
+
this.dependencyGraph[parentFieldId].forEach((dep) => {
|
|
263
|
+
if (dep.state !== undefined) {
|
|
264
|
+
dep.state = value; // Set state to the selected value
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Log the updated dependency graph for the parent field
|
|
269
|
+
// console.log(`Updated Dependency Graph for ${parentFieldId}:`, this.dependencyGraph[parentFieldId]);
|
|
270
|
+
|
|
271
|
+
// Notify all observers (dependent fields)
|
|
272
|
+
dependencies.forEach((dependency) => {
|
|
273
|
+
if (dependency.dependent) {
|
|
274
|
+
const observerId = dependency.dependent + "-block"; // Ensure we're targeting the wrapper
|
|
275
|
+
const inputBlock = document.getElementById(observerId); // Find the wrapper element
|
|
276
|
+
|
|
277
|
+
if (inputBlock) {
|
|
278
|
+
// Check if the condition for the observer is satisfied
|
|
279
|
+
const conditionMet = typeof dependency.condition === 'function'
|
|
280
|
+
? dependency.condition(value)
|
|
281
|
+
: value === dependency.condition;
|
|
282
|
+
|
|
283
|
+
// Debug the condition evaluation
|
|
284
|
+
// console.log(`Checking condition for ${observerId}: `, value, "==", dependency.condition, "Result:", conditionMet);
|
|
285
|
+
|
|
286
|
+
// Toggle visibility based on the condition
|
|
287
|
+
inputBlock.style.display = conditionMet ? 'block' : 'none';
|
|
288
|
+
|
|
289
|
+
// Adjust the 'required' attribute for all inputs within the block based on visibility
|
|
290
|
+
const inputs = inputBlock.querySelectorAll('input, select, textarea');
|
|
291
|
+
inputs.forEach((input) => {
|
|
292
|
+
if (conditionMet) {
|
|
293
|
+
input.required = input.getAttribute('data-original-required') === 'true'; // Restore original required state
|
|
294
|
+
} else {
|
|
295
|
+
input.setAttribute('data-original-required', input.required); // Save original required state
|
|
296
|
+
input.required = false; // Remove required attribute when hiding
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
} else {
|
|
300
|
+
console.warn(`Wrapper block with ID ${observerId} not found.`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Register observers for each dependent field
|
|
308
|
+
registerObservers() {
|
|
309
|
+
this.formSchema.forEach((field) => {
|
|
310
|
+
const [type, name, label, validate, attributes = {}] = field;
|
|
311
|
+
const fieldId = attributes.id || name;
|
|
312
|
+
|
|
313
|
+
if (attributes.dependents) {
|
|
314
|
+
attributes.dependents.forEach((dependentName) => {
|
|
315
|
+
// Ensure the dependency graph exists for the parent field
|
|
316
|
+
if (this.dependencyGraph[fieldId]) {
|
|
317
|
+
// Find the dependent field in the form schema
|
|
318
|
+
const dependentField = this.formSchema.find(
|
|
319
|
+
([, depName]) => depName === dependentName
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// If the dependent field exists, register it as an observer
|
|
323
|
+
if (dependentField) {
|
|
324
|
+
const dependentFieldId = dependentField[4]?.id || dependentName;
|
|
325
|
+
this.dependencyGraph[fieldId].forEach((dependency) => {
|
|
326
|
+
if (dependency.dependent === dependentName) {
|
|
327
|
+
// Store the dependent as an observer for this parent field
|
|
328
|
+
if (!dependency.observers) {
|
|
329
|
+
dependency.observers = [];
|
|
330
|
+
}
|
|
331
|
+
dependency.observers.push(dependentFieldId);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// console.log("Observers Registered:", JSON.stringify(this.dependencyGraph,null,2));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
applyTheme(theme, formContainerId) {
|
|
345
|
+
//const stylesheet = document.querySelector('link[formique-style]');
|
|
346
|
+
|
|
347
|
+
const stylesheet = document.querySelector('link[href*="formique-css"]');
|
|
348
|
+
|
|
349
|
+
if (!stylesheet) {
|
|
350
|
+
console.error("Stylesheet with 'formique-css' in the name not found!");
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
fetch(stylesheet.href)
|
|
355
|
+
.then(response => response.text())
|
|
356
|
+
.then(cssText => {
|
|
357
|
+
// Extract theme-specific CSS rules
|
|
358
|
+
const themeRules = cssText.match(new RegExp(`\\.${theme}-theme\\s*{([^}]*)}`, 'i'));
|
|
359
|
+
|
|
360
|
+
if (!themeRules) {
|
|
361
|
+
console.error(`Theme rules for ${theme} not found in the stylesheet.`);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Extract CSS rules for the theme
|
|
366
|
+
const themeCSS = themeRules[1].trim();
|
|
367
|
+
|
|
368
|
+
// Find the form container element
|
|
369
|
+
const formContainer = document.getElementById(formContainerId);
|
|
370
|
+
|
|
371
|
+
if (formContainer) {
|
|
372
|
+
// Append the theme class to the form container
|
|
373
|
+
formContainer.classList.add(`${theme}-theme`, 'formique');
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
// Create a <style> tag with the extracted theme styles
|
|
377
|
+
const clonedStyle = document.createElement('style');
|
|
378
|
+
clonedStyle.textContent = `
|
|
379
|
+
#${formContainerId} {
|
|
380
|
+
${themeCSS}
|
|
381
|
+
}
|
|
382
|
+
`;
|
|
383
|
+
|
|
384
|
+
// Insert the <style> tag above the form container
|
|
385
|
+
formContainer.parentNode.insertBefore(clonedStyle, formContainer);
|
|
386
|
+
|
|
387
|
+
// console.log(`Applied ${theme} theme to form container: ${formContainerId}`);
|
|
388
|
+
} else {
|
|
389
|
+
console.error(`Form container with ID ${formContainerId} not found.`);
|
|
390
|
+
}
|
|
391
|
+
})
|
|
392
|
+
.catch(error => {
|
|
393
|
+
console.error('Error loading the stylesheet:', error);
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
// renderFormElement method
|
|
399
|
+
renderFormElement() {
|
|
400
|
+
let formHTML = '<form';
|
|
401
|
+
|
|
402
|
+
// Ensure `this.formParams` is being passed in as the source of form attributes
|
|
403
|
+
const paramsToUse = this.formParams || {};
|
|
404
|
+
//console.log(paramsToUse);
|
|
405
|
+
|
|
406
|
+
if (!paramsToUse.id) {
|
|
407
|
+
paramsToUse.id = this.formId;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
// Dynamically add attributes if they are present in the parameters
|
|
412
|
+
Object.keys(paramsToUse).forEach(key => {
|
|
413
|
+
const value = paramsToUse[key];
|
|
414
|
+
if (value !== undefined && value !== null) {
|
|
415
|
+
// Handle boolean attributes (without values, just their presence)
|
|
416
|
+
if (typeof value === 'boolean') {
|
|
417
|
+
if (value) {
|
|
418
|
+
formHTML += ` ${key}`; // Simply add the key as the attribute
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
// Handle other attributes (key-value pairs)
|
|
422
|
+
const formattedKey = key === 'accept_charset' ? 'accept-charset' : key.replace(/_/g, '-');
|
|
423
|
+
formHTML += ` ${formattedKey}="${value}"`;
|
|
424
|
+
//console.log("HERE",formHTML);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// Conditionally add CSRF token if 'laravel' is true
|
|
430
|
+
if (paramsToUse.laravel) {
|
|
431
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
|
432
|
+
if (csrfToken) {
|
|
433
|
+
formHTML += `<input type="hidden" name="_token" value="${csrfToken}">`;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Close the <form> tag
|
|
438
|
+
formHTML += '>\n';
|
|
439
|
+
|
|
440
|
+
// Return the generated form HTML
|
|
441
|
+
return formHTML;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
// Main renderForm method
|
|
448
|
+
/*
|
|
449
|
+
renderForm() {
|
|
450
|
+
// Process each field synchronously
|
|
451
|
+
const formHTML = this.formSchema.map(field => {
|
|
452
|
+
const [type, name, label, validate, attributes = {},options] = field;
|
|
453
|
+
return this.renderField(type, name, label, validate, attributes, options);
|
|
454
|
+
}).join('');
|
|
455
|
+
this.formMarkUp += formHTML;
|
|
456
|
+
}
|
|
457
|
+
*/
|
|
458
|
+
|
|
459
|
+
renderForm() {
|
|
460
|
+
// Filter out the 'submit' type before mapping
|
|
461
|
+
const formHTML = this.formSchema
|
|
462
|
+
.filter(field => field[0] !== 'submit') // Exclude submit button from this loop
|
|
463
|
+
.map(field => {
|
|
464
|
+
const [type, name, label, validate, attributes = {}, options] = field;
|
|
465
|
+
return this.renderField(type, name, label, validate, attributes, options);
|
|
466
|
+
}).join('');
|
|
467
|
+
this.formMarkUp += formHTML;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
// New method to render the submit button specifically
|
|
473
|
+
renderSubmitButtonElement() {
|
|
474
|
+
const submitField = this.formSchema.find(field => field[0] === 'submit');
|
|
475
|
+
if (submitField) {
|
|
476
|
+
const [type, name, label, validate, attributes = {}] = submitField;
|
|
477
|
+
const id = attributes.id || name;
|
|
478
|
+
let buttonClass = this.submitButtonClass;
|
|
479
|
+
if ('class' in attributes) {
|
|
480
|
+
buttonClass = attributes.class;
|
|
481
|
+
}
|
|
482
|
+
let additionalAttrs = '';
|
|
483
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
484
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) {
|
|
485
|
+
if (key.startsWith('on')) {
|
|
486
|
+
const eventValue = value.endsWith('()') ? value : `${value}()`;
|
|
487
|
+
additionalAttrs += ` ${key}="${eventValue}"`;
|
|
488
|
+
} else {
|
|
489
|
+
if (value === true) {
|
|
490
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}`;
|
|
491
|
+
} else if (value !== false) {
|
|
492
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"`;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Include the spinner div before the submit button
|
|
499
|
+
return `
|
|
500
|
+
<div id="formiqueSpinner" style="display: flex; align-items: center; gap: 1rem; font-family: sans-serif; display:none;">
|
|
501
|
+
<div class="formique-spinner"></div>
|
|
502
|
+
<p class="message">Hang in tight, we are submitting your details…</p>
|
|
503
|
+
</div>
|
|
504
|
+
<input type="submit" id="${id}" class="${buttonClass}" value="${label}"${additionalAttrs}>
|
|
505
|
+
`.trim();
|
|
506
|
+
}
|
|
507
|
+
return ''; // Return empty string if no submit button is found in schema
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
/*
|
|
512
|
+
renderField(type, name, label, validate, attributes, options) {
|
|
513
|
+
const fieldRenderMap = {
|
|
514
|
+
'text': this.renderTextField,
|
|
515
|
+
'email': this.renderEmailField,
|
|
516
|
+
'number': this.renderNumberField,
|
|
517
|
+
'password': this.renderPasswordField,
|
|
518
|
+
'textarea': this.renderTextAreaField,
|
|
519
|
+
'tel': this.renderTelField,
|
|
520
|
+
'date': this.renderDateField,
|
|
521
|
+
'time': this.renderTimeField,
|
|
522
|
+
'datetime-local': this.renderDateTimeField,
|
|
523
|
+
'month': this.renderMonthField,
|
|
524
|
+
'week': this.renderWeekField,
|
|
525
|
+
'url': this.renderUrlField,
|
|
526
|
+
'search': this.renderSearchField,
|
|
527
|
+
'color': this.renderColorField,
|
|
528
|
+
'checkbox': this.renderCheckboxField,
|
|
529
|
+
'radio': this.renderRadioField,
|
|
530
|
+
'file': this.renderFileField,
|
|
531
|
+
'hidden': this.renderHiddenField,
|
|
532
|
+
'image': this.renderImageField,
|
|
533
|
+
'textarea': this.renderTextAreaField,
|
|
534
|
+
'singleSelect': this.renderSingleSelectField,
|
|
535
|
+
'multipleSelect': this.renderMultipleSelectField,
|
|
536
|
+
'dynamicSingleSelect': this.renderDynamicSingleSelectField,
|
|
537
|
+
'range': this.renderRangeField,
|
|
538
|
+
'submit': this.renderSubmitButton,
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const renderMethod = fieldRenderMap[type];
|
|
542
|
+
|
|
543
|
+
if (renderMethod) {
|
|
544
|
+
return renderMethod.call(this, type, name, label, validate, attributes, options);
|
|
545
|
+
} else {
|
|
546
|
+
console.warn(`Unsupported field type '${type}' encountered.`);
|
|
547
|
+
return ''; // or handle gracefully
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
*/
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
// renderField method - No change needed here for this issue, but ensure it handles 'submit' type correctly if called directly
|
|
555
|
+
renderField(type, name, label, validate, attributes, options) {
|
|
556
|
+
const fieldRenderMap = {
|
|
557
|
+
'text': this.renderTextField,
|
|
558
|
+
'email': this.renderEmailField,
|
|
559
|
+
'number': this.renderNumberField,
|
|
560
|
+
'password': this.renderPasswordField,
|
|
561
|
+
'textarea': this.renderTextAreaField,
|
|
562
|
+
'tel': this.renderTelField,
|
|
563
|
+
'date': this.renderDateField,
|
|
564
|
+
'time': this.renderTimeField,
|
|
565
|
+
'datetime-local': this.renderDateTimeField,
|
|
566
|
+
'month': this.renderMonthField,
|
|
567
|
+
'week': this.renderWeekField,
|
|
568
|
+
'url': this.renderUrlField,
|
|
569
|
+
'search': this.renderSearchField,
|
|
570
|
+
'color': this.renderColorField,
|
|
571
|
+
'checkbox': this.renderCheckboxField,
|
|
572
|
+
'radio': this.renderRadioField,
|
|
573
|
+
'file': this.renderFileField,
|
|
574
|
+
'hidden': this.renderHiddenField,
|
|
575
|
+
'image': this.renderImageField,
|
|
576
|
+
'singleSelect': this.renderSingleSelectField,
|
|
577
|
+
'multipleSelect': this.renderMultipleSelectField,
|
|
578
|
+
'dynamicSingleSelect': this.renderDynamicSingleSelectField,
|
|
579
|
+
'range': this.renderRangeField,
|
|
580
|
+
'submit': this.renderSubmitButton, // Keep this for completeness, but renderSubmitButtonElement will now handle it
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const renderMethod = fieldRenderMap[type];
|
|
584
|
+
|
|
585
|
+
if (renderMethod) {
|
|
586
|
+
// If the type is 'submit', ensure we use the specific renderSubmitButtonElement
|
|
587
|
+
// Although, with the filter in renderForm(), this branch for 'submit' type
|
|
588
|
+
// might not be hit in the primary rendering flow, it's good practice.
|
|
589
|
+
return renderMethod.call(this, type, name, label, validate, attributes, options);
|
|
590
|
+
|
|
591
|
+
if (type === 'submit') {
|
|
592
|
+
return this.renderSubmitButton(type, name, label, validate, attributes, options);
|
|
593
|
+
}
|
|
594
|
+
//return renderMethod.call(this, type, name, label, validate, attributes, options);
|
|
595
|
+
} else {
|
|
596
|
+
console.warn(`Unsupported field type '${type}' encountered.`);
|
|
597
|
+
return '';
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
renderSubmitButton(type, name, label, validate, attributes) {
|
|
604
|
+
// This method can simply call the dedicated submit button renderer if it's kept separate.
|
|
605
|
+
// Or, if renderField is only used for non-submit fields, this method might not be strictly necessary
|
|
606
|
+
// to be called from renderField's map, but it needs to exist if mapped.
|
|
607
|
+
// For simplicity, I'll make it consistent with the new separation.
|
|
608
|
+
const id = attributes.id || name;
|
|
609
|
+
let buttonClass = this.submitButtonClass;
|
|
610
|
+
if ('class' in attributes) {
|
|
611
|
+
buttonClass = attributes.class;
|
|
612
|
+
}
|
|
613
|
+
let additionalAttrs = '';
|
|
614
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
615
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) {
|
|
616
|
+
if (key.startsWith('on')) {
|
|
617
|
+
const eventValue = value.endsWith('()') ? value : `${value}()`;
|
|
618
|
+
additionalAttrs += ` ${key}="${eventValue}"`;
|
|
619
|
+
} else {
|
|
620
|
+
if (value === true) {
|
|
621
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}`;
|
|
622
|
+
} else if (value !== false) {
|
|
623
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"`;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
// No spinner div here, as that's added once by renderSubmitButtonElement
|
|
629
|
+
return `<input type="${type}" id="${id}" class="${buttonClass}" value="${label}"${additionalAttrs}>`;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
// Show success/error messages (externalizable)
|
|
634
|
+
showSuccessMessage(message) {
|
|
635
|
+
const container = document.getElementById(this.formContainerId);
|
|
636
|
+
container.innerHTML = `
|
|
637
|
+
<div class="formique-success"> ${message}</div>
|
|
638
|
+
${this.formSettings.redirectURL
|
|
639
|
+
? `<meta http-equiv="refresh" content="2;url=${this.formSettings.redirectURL}">`
|
|
640
|
+
: ""}
|
|
641
|
+
`;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
showErrorMessage(message) {
|
|
645
|
+
const container = document.getElementById(this.formContainerId);
|
|
646
|
+
const errorDiv = document.createElement("div");
|
|
647
|
+
errorDiv.className = "formique-error";
|
|
648
|
+
errorDiv.textContent = `${message}`;
|
|
649
|
+
container.prepend(errorDiv);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Check if form has file inputs
|
|
653
|
+
hasFileInputs(form) {
|
|
654
|
+
return Boolean(form.querySelector('input[type="file"]'));
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
async handleEmailSubmission(formId) {
|
|
662
|
+
console.log(`Starting email submission for form ID: ${formId}`);
|
|
663
|
+
|
|
664
|
+
const form = document.getElementById(formId);
|
|
665
|
+
if (!form) {
|
|
666
|
+
console.error(`Form with ID ${formId} not found`);
|
|
667
|
+
throw new Error(`Form with ID ${formId} not found`);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// Validate required settings - now checks if sendTo is array with at least one item
|
|
671
|
+
if (!Array.isArray(this.formSettings?.sendTo) || this.formSettings.sendTo.length === 0) {
|
|
672
|
+
console.error('formSettings.sendTo must be an array with at least one recipient email');
|
|
673
|
+
throw new Error('formSettings.sendTo must be an array with at least one recipient email');
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Serialize form data
|
|
677
|
+
const payload = {
|
|
678
|
+
formData: {},
|
|
679
|
+
metadata: {
|
|
680
|
+
recipients: this.formSettings.sendTo, // Now sending array
|
|
681
|
+
timestamp: new Date().toISOString()
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
let senderName = '';
|
|
686
|
+
let senderEmail = '';
|
|
687
|
+
let formSubject = '';
|
|
688
|
+
|
|
689
|
+
console.log('Initial payload structure:', JSON.parse(JSON.stringify(payload)));
|
|
690
|
+
|
|
691
|
+
// Process form fields (unchanged)
|
|
692
|
+
new FormData(form).forEach((value, key) => {
|
|
693
|
+
console.log(`Processing form field - Key: ${key}, Value: ${value}`);
|
|
694
|
+
payload.formData[key] = value;
|
|
695
|
+
|
|
696
|
+
const lowerKey = key.toLowerCase();
|
|
697
|
+
if ((lowerKey === 'email' || lowerKey.includes('email'))) {
|
|
698
|
+
senderEmail = value;
|
|
699
|
+
}
|
|
700
|
+
if ((lowerKey === 'name' || lowerKey.includes('name'))) {
|
|
701
|
+
senderName = value;
|
|
702
|
+
}
|
|
703
|
+
if ((lowerKey === 'subject' || lowerKey.includes('subject'))) {
|
|
704
|
+
formSubject = value;
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
// Determine the email subject with fallback logic
|
|
709
|
+
payload.metadata.subject = formSubject ||
|
|
710
|
+
this.formSettings.subject ||
|
|
711
|
+
'Message From Contact Form';
|
|
712
|
+
|
|
713
|
+
console.log('Determined email subject:', payload.metadata.subject);
|
|
714
|
+
|
|
715
|
+
// Add sender information to metadata
|
|
716
|
+
if (senderEmail) {
|
|
717
|
+
payload.metadata.sender = senderEmail;
|
|
718
|
+
payload.metadata.replyTo = senderName
|
|
719
|
+
? `${senderName} <${senderEmail}>`
|
|
720
|
+
: senderEmail;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
console.log('Payload after form processing:', JSON.parse(JSON.stringify(payload)));
|
|
724
|
+
|
|
725
|
+
try {
|
|
726
|
+
const endpoint = this.formiqueEndpoint || this.formAction;
|
|
727
|
+
const method = this.method || 'POST';
|
|
728
|
+
|
|
729
|
+
console.log(`Preparing to send request to: ${endpoint}`);
|
|
730
|
+
console.log(`Request method: ${method}`);
|
|
731
|
+
console.log('Final payload being sent:', payload);
|
|
732
|
+
|
|
733
|
+
const response = await fetch(endpoint, {
|
|
734
|
+
method: method,
|
|
735
|
+
headers: {
|
|
736
|
+
'Content-Type': 'application/json',
|
|
737
|
+
'X-Formique-Version': '1.0'
|
|
738
|
+
},
|
|
739
|
+
body: JSON.stringify(payload)
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
console.log(`Received response with status: ${response.status}`);
|
|
743
|
+
|
|
744
|
+
if (!response.ok) {
|
|
745
|
+
const errorData = await response.json().catch(() => ({}));
|
|
746
|
+
console.error('API Error Response:', errorData);
|
|
747
|
+
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
|
|
748
|
+
document.getElementById("formiqueSpinner").style.display = "none";
|
|
749
|
+
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const data = await response.json();
|
|
753
|
+
console.log('API Success Response:', data);
|
|
754
|
+
|
|
755
|
+
const successMessage = this.formSettings.successMessage ||
|
|
756
|
+
data.message ||
|
|
757
|
+
'Your message has been sent successfully!';
|
|
758
|
+
console.log(`Showing success message: ${successMessage}`);
|
|
759
|
+
|
|
760
|
+
this.showSuccessMessage(successMessage);
|
|
761
|
+
|
|
762
|
+
} catch (error) {
|
|
763
|
+
console.error('Email submission failed:', error);
|
|
764
|
+
const errorMessage = this.formSettings.errorMessage ||
|
|
765
|
+
error.message ||
|
|
766
|
+
'Failed to send message. Please try again later.';
|
|
767
|
+
console.log(`Showing error message: ${errorMessage}`);
|
|
768
|
+
this.showErrorMessage(errorMessage);
|
|
769
|
+
document.getElementById("formiqueSpinner").style.display = "none";
|
|
770
|
+
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
// Email validation helper
|
|
777
|
+
validateEmail(email) {
|
|
778
|
+
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
779
|
+
console.log(`Validating email ${email}: ${isValid ? 'valid' : 'invalid'}`); // Debug log
|
|
780
|
+
return isValid;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
// Method to handle on-page form submissions
|
|
786
|
+
handleOnPageFormSubmission(formId) {
|
|
787
|
+
const formElement = document.getElementById(formId);
|
|
788
|
+
//console.warn("handler fired also",formId,this.method,this.formAction);
|
|
789
|
+
|
|
790
|
+
if (formElement) {
|
|
791
|
+
// Gather form data
|
|
792
|
+
const formData = new FormData(formElement);
|
|
793
|
+
|
|
794
|
+
// Submit form data using fetch to a test endpoint
|
|
795
|
+
fetch(this.formAction, {
|
|
796
|
+
method: this.method,
|
|
797
|
+
body: formData
|
|
798
|
+
})
|
|
799
|
+
.then(response => response.json())
|
|
800
|
+
.then(data => {
|
|
801
|
+
console.log('Success:', data);
|
|
802
|
+
// Handle the response data here, e.g., show a success message
|
|
803
|
+
|
|
804
|
+
// Get the form container element
|
|
805
|
+
const formContainer = document.getElementById(this.formContainerId);
|
|
806
|
+
|
|
807
|
+
if (this.redirect && this.redirectURL) {
|
|
808
|
+
window.location.href = this.redirectURL;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
if (formContainer) {
|
|
813
|
+
// Create a new div element for the success message
|
|
814
|
+
const successMessageDiv = document.createElement('div');
|
|
815
|
+
|
|
816
|
+
// Add custom classes for styling the success message
|
|
817
|
+
successMessageDiv.classList.add('success-message', 'message-container');
|
|
818
|
+
|
|
819
|
+
// Set the success message text
|
|
820
|
+
successMessageDiv.innerHTML = this.formSettings.successMessage || 'Your details have been successfully submitted!';
|
|
821
|
+
|
|
822
|
+
// Replace the content of the form container with the success message div
|
|
823
|
+
formContainer.innerHTML = ''; // Clear existing content
|
|
824
|
+
formContainer.appendChild(successMessageDiv); // Append the new success message div
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
})
|
|
829
|
+
.catch(error => {
|
|
830
|
+
console.error('Error:', error);
|
|
831
|
+
|
|
832
|
+
const formContainer = document.getElementById(this.formContainerId);
|
|
833
|
+
if (formContainer) {
|
|
834
|
+
// Check if an error message div already exists and remove it
|
|
835
|
+
let existingErrorDiv = formContainer.querySelector('.error-message');
|
|
836
|
+
if (existingErrorDiv) {
|
|
837
|
+
existingErrorDiv.remove();
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Create a new div element for the error message
|
|
841
|
+
const errorMessageDiv = document.createElement('div');
|
|
842
|
+
|
|
843
|
+
// Add custom classes for styling the error message
|
|
844
|
+
errorMessageDiv.classList.add('error-message', 'message-container');
|
|
845
|
+
|
|
846
|
+
// Set the error message text
|
|
847
|
+
let err = this.formSettings.errorMessage || 'An error occurred while submitting the form. Please try again.';
|
|
848
|
+
err = `${err}<br/>Details: ${error.message}`;
|
|
849
|
+
errorMessageDiv.innerHTML = err;
|
|
850
|
+
|
|
851
|
+
// Append the new error message div to the form container
|
|
852
|
+
formContainer.appendChild(errorMessageDiv);
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
// text field rendering
|
|
863
|
+
renderTextField(type, name, label, validate, attributes) {
|
|
864
|
+
const textInputValidationAttributes = [
|
|
865
|
+
'required',
|
|
866
|
+
'minlength',
|
|
867
|
+
'maxlength',
|
|
868
|
+
'pattern',
|
|
869
|
+
];
|
|
870
|
+
|
|
871
|
+
// Construct validation attributes
|
|
872
|
+
let validationAttrs = '';
|
|
873
|
+
if (validate) {
|
|
874
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
875
|
+
if (textInputValidationAttributes.includes(key)) {
|
|
876
|
+
if (typeof value === 'boolean' && value) {
|
|
877
|
+
validationAttrs += ` ${key}\n`;
|
|
878
|
+
} else {
|
|
879
|
+
switch (key) {
|
|
880
|
+
case 'pattern':
|
|
881
|
+
case 'minlength':
|
|
882
|
+
case 'maxlength':
|
|
883
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
884
|
+
break;
|
|
885
|
+
default:
|
|
886
|
+
if (!textInputValidationAttributes.includes(key)) {
|
|
887
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'number'.\x1b[0m`);
|
|
888
|
+
}
|
|
889
|
+
break;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
} else {
|
|
893
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'text'.\x1b[0m`);
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Handle the binding syntax
|
|
899
|
+
let bindingDirective = '';
|
|
900
|
+
if (attributes.binding) {
|
|
901
|
+
if (attributes.binding === 'bind:value' && name) {
|
|
902
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
903
|
+
}
|
|
904
|
+
if (attributes.binding.startsWith('::') && name) {
|
|
905
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
906
|
+
}
|
|
907
|
+
if (attributes.binding && !name) {
|
|
908
|
+
console.log(`\x1b[31m%s\x1b[0m`,`You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
// Get the id from attributes or fall back to name
|
|
916
|
+
let id = attributes.id || name;
|
|
917
|
+
// Determine if semanti is true based on formSettings
|
|
918
|
+
const framework = this.formSettings?.framework || false;
|
|
919
|
+
|
|
920
|
+
// Construct additional attributes dynamically
|
|
921
|
+
let additionalAttrs = '';
|
|
922
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
923
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
924
|
+
// Handle event attributes
|
|
925
|
+
if (framework === 'semantq') {
|
|
926
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
927
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
928
|
+
} else {
|
|
929
|
+
// Add parentheses if not present
|
|
930
|
+
const eventValue = value.endsWith('()') ? value : `${value}()`;
|
|
931
|
+
additionalAttrs += ` ${key}="${eventValue}"\n`;
|
|
932
|
+
}
|
|
933
|
+
} else {
|
|
934
|
+
// Handle boolean attributes
|
|
935
|
+
if (value === true) {
|
|
936
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
937
|
+
} else if (value !== false) {
|
|
938
|
+
// Convert underscores to hyphens and set the attribute
|
|
939
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
|
|
947
|
+
let inputClass;
|
|
948
|
+
if ('class' in attributes) {
|
|
949
|
+
inputClass = attributes.class;
|
|
950
|
+
} else {
|
|
951
|
+
inputClass = this.inputClass;
|
|
952
|
+
}
|
|
953
|
+
// Construct the final HTML string
|
|
954
|
+
let formHTML = `
|
|
955
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
956
|
+
<label for="${id}">${label}
|
|
957
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
958
|
+
</label>
|
|
959
|
+
<input
|
|
960
|
+
type="${type}"
|
|
961
|
+
name="${name}"
|
|
962
|
+
${bindingDirective}
|
|
963
|
+
id="${id}"
|
|
964
|
+
class="${inputClass}"
|
|
965
|
+
${additionalAttrs}
|
|
966
|
+
${validationAttrs}
|
|
967
|
+
${additionalAttrs.includes('placeholder') ? '' : (this.formSettings.placeholders ? `placeholder="${label}"` : '')} />
|
|
968
|
+
</div>
|
|
969
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
970
|
+
|
|
971
|
+
let formattedHtml = formHTML;
|
|
972
|
+
|
|
973
|
+
// Apply vertical layout to the <input> element only
|
|
974
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
975
|
+
// Reformat attributes into a vertical layout
|
|
976
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
977
|
+
return `<input\n${attributes}\n/>`;
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
this.formMarkUp +=formattedHtml;
|
|
982
|
+
//return formattedHtml;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
// Specific rendering method for rendering the email field
|
|
989
|
+
renderEmailField(type, name, label, validate, attributes) {
|
|
990
|
+
// Define valid attributes for the email input type
|
|
991
|
+
|
|
992
|
+
const emailInputValidationAttributes = [
|
|
993
|
+
'required',
|
|
994
|
+
'pattern',
|
|
995
|
+
'minlength',
|
|
996
|
+
'maxlength',
|
|
997
|
+
'multiple'
|
|
998
|
+
];
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
// Construct validation attributes
|
|
1002
|
+
let validationAttrs = '';
|
|
1003
|
+
if (validate) {
|
|
1004
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
1005
|
+
if (emailInputValidationAttributes.includes(key)) {
|
|
1006
|
+
if (typeof value === 'boolean' && value) {
|
|
1007
|
+
validationAttrs += ` ${key}\n`;
|
|
1008
|
+
} else {
|
|
1009
|
+
switch (key) {
|
|
1010
|
+
case 'pattern':
|
|
1011
|
+
case 'minlength':
|
|
1012
|
+
case 'maxlength':
|
|
1013
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
1014
|
+
break;
|
|
1015
|
+
default:
|
|
1016
|
+
if (!emailInputValidationAttributes.includes(key)) {
|
|
1017
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'number'.\x1b[0m`);
|
|
1018
|
+
}
|
|
1019
|
+
break;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
} else {
|
|
1023
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'email'.\x1b[0m`);
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Handle the binding syntax
|
|
1029
|
+
let bindingDirective = '';
|
|
1030
|
+
if (attributes.binding) {
|
|
1031
|
+
if (attributes.binding === 'bind:value' && name) {
|
|
1032
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1033
|
+
}
|
|
1034
|
+
if (attributes.binding.startsWith('::') && name) {
|
|
1035
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1036
|
+
}
|
|
1037
|
+
if (attributes.binding && !name) {
|
|
1038
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
|
|
1044
|
+
// Get the id from attributes or fall back to name
|
|
1045
|
+
let id = attributes.id || name;
|
|
1046
|
+
|
|
1047
|
+
// Construct additional attributes dynamically
|
|
1048
|
+
let additionalAttrs = '';
|
|
1049
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1050
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
1051
|
+
// Handle event attributes
|
|
1052
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
1053
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
1054
|
+
} else {
|
|
1055
|
+
// Handle boolean attributes
|
|
1056
|
+
if (value === true) {
|
|
1057
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
1058
|
+
} else if (value !== false) {
|
|
1059
|
+
// Convert underscores to hyphens and set the attribute
|
|
1060
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
let inputClass;
|
|
1068
|
+
if ('class' in attributes) {
|
|
1069
|
+
inputClass = attributes.class;
|
|
1070
|
+
} else {
|
|
1071
|
+
inputClass = this.inputClass;
|
|
1072
|
+
}
|
|
1073
|
+
// Construct the final HTML string
|
|
1074
|
+
let formHTML = `
|
|
1075
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
1076
|
+
<label for="${id}">${label}
|
|
1077
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
1078
|
+
</label>
|
|
1079
|
+
<input
|
|
1080
|
+
type="${type}"
|
|
1081
|
+
name="${name}"
|
|
1082
|
+
${bindingDirective}
|
|
1083
|
+
id="${id}"
|
|
1084
|
+
class="${inputClass}"
|
|
1085
|
+
${additionalAttrs}
|
|
1086
|
+
${validationAttrs}
|
|
1087
|
+
${additionalAttrs.includes('placeholder') ? '' : (this.formSettings.placeholders ? `placeholder="${label}"` : '')}
|
|
1088
|
+
|
|
1089
|
+
/>
|
|
1090
|
+
</div>
|
|
1091
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
1092
|
+
|
|
1093
|
+
let formattedHtml = formHTML;
|
|
1094
|
+
|
|
1095
|
+
// Apply vertical layout to the <input> element only
|
|
1096
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
1097
|
+
// Reformat attributes into a vertical layout
|
|
1098
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
1099
|
+
return `<input\n${attributes}\n/>`;
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
1103
|
+
|
|
1104
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
1105
|
+
// Ensure <div> starts on a new line
|
|
1106
|
+
return `\n${match}\n`;
|
|
1107
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
this.formMarkUp += formattedHtml;
|
|
1111
|
+
|
|
1112
|
+
//return formattedHtml;
|
|
1113
|
+
//return this.formMarkUp;
|
|
1114
|
+
//console.log(this.formMarkUp);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
renderNumberField(type, name, label, validate, attributes) {
|
|
1120
|
+
// Define valid attributes for the number input type
|
|
1121
|
+
|
|
1122
|
+
const numberInputValidationAttributes = [
|
|
1123
|
+
'required',
|
|
1124
|
+
'min',
|
|
1125
|
+
'max',
|
|
1126
|
+
'step',
|
|
1127
|
+
];
|
|
1128
|
+
|
|
1129
|
+
// Construct validation attributes
|
|
1130
|
+
let validationAttrs = '';
|
|
1131
|
+
if (validate) {
|
|
1132
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
1133
|
+
if (numberInputValidationAttributes.includes(key)) {
|
|
1134
|
+
if (typeof value === 'boolean' && value) {
|
|
1135
|
+
validationAttrs += ` ${key}\n`;
|
|
1136
|
+
} else {
|
|
1137
|
+
switch (key) {
|
|
1138
|
+
case 'min':
|
|
1139
|
+
case 'max':
|
|
1140
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
1141
|
+
break;
|
|
1142
|
+
case 'step':
|
|
1143
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
1144
|
+
break;
|
|
1145
|
+
default:
|
|
1146
|
+
if (!numberInputValidationAttributes.includes(key)) {
|
|
1147
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'number'.\x1b[0m`);
|
|
1148
|
+
}
|
|
1149
|
+
break;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
} else {
|
|
1153
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'number'.\x1b[0m`);
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// Handle the binding syntax
|
|
1159
|
+
let bindingDirective = '';
|
|
1160
|
+
if (attributes.binding) {
|
|
1161
|
+
if (attributes.binding === 'bind:value' && name) {
|
|
1162
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1163
|
+
}
|
|
1164
|
+
if (attributes.binding.startsWith('::') && name) {
|
|
1165
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1166
|
+
}
|
|
1167
|
+
if (attributes.binding && !name) {
|
|
1168
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
|
|
1174
|
+
|
|
1175
|
+
// Get the id from attributes or fall back to name
|
|
1176
|
+
let id = attributes.id || name;
|
|
1177
|
+
|
|
1178
|
+
// Construct additional attributes dynamically
|
|
1179
|
+
let additionalAttrs = '';
|
|
1180
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1181
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
1182
|
+
// Handle event attributes
|
|
1183
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
1184
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
1185
|
+
} else {
|
|
1186
|
+
// Handle boolean attributes
|
|
1187
|
+
if (value === true) {
|
|
1188
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
1189
|
+
} else if (value !== false) {
|
|
1190
|
+
// Convert underscores to hyphens and set the attribute
|
|
1191
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
let inputClass;
|
|
1198
|
+
if ('class' in attributes) {
|
|
1199
|
+
inputClass = attributes.class;
|
|
1200
|
+
} else {
|
|
1201
|
+
inputClass = this.inputClass;
|
|
1202
|
+
}
|
|
1203
|
+
// Construct the final HTML string
|
|
1204
|
+
let formHTML = `
|
|
1205
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
1206
|
+
<label for="${id}">${label}
|
|
1207
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
1208
|
+
</label>
|
|
1209
|
+
<input
|
|
1210
|
+
type="${type}"
|
|
1211
|
+
name="${name}"
|
|
1212
|
+
${bindingDirective}
|
|
1213
|
+
id="${id}"
|
|
1214
|
+
class="${inputClass}"
|
|
1215
|
+
${additionalAttrs}
|
|
1216
|
+
${validationAttrs}
|
|
1217
|
+
/>
|
|
1218
|
+
</div>
|
|
1219
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
1220
|
+
|
|
1221
|
+
let formattedHtml = formHTML;
|
|
1222
|
+
|
|
1223
|
+
// Apply vertical layout to the <input> element only
|
|
1224
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
1225
|
+
// Reformat attributes into a vertical layout
|
|
1226
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
1227
|
+
return `<input\n${attributes}\n/>`;
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
1231
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
1232
|
+
// Ensure <div> starts on a new line
|
|
1233
|
+
return `\n${match}\n`;
|
|
1234
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
1235
|
+
|
|
1236
|
+
//return formattedHtml;
|
|
1237
|
+
this.formMarkUp +=formattedHtml;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
// New method for rendering password fields
|
|
1243
|
+
renderPasswordField(type, name, label, validate, attributes) {
|
|
1244
|
+
// Define valid attributes for the password input type
|
|
1245
|
+
|
|
1246
|
+
|
|
1247
|
+
const passwordInputValidationAttributes = [
|
|
1248
|
+
'required',
|
|
1249
|
+
'minlength',
|
|
1250
|
+
'maxlength',
|
|
1251
|
+
'pattern',
|
|
1252
|
+
];
|
|
1253
|
+
|
|
1254
|
+
// Construct validation attributes
|
|
1255
|
+
let validationAttrs = '';
|
|
1256
|
+
if (validate) {
|
|
1257
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
1258
|
+
if (passwordInputValidationAttributes.includes(key)) {
|
|
1259
|
+
if (typeof value === 'boolean' && value) {
|
|
1260
|
+
validationAttrs += ` ${key}\n`;
|
|
1261
|
+
} else {
|
|
1262
|
+
switch (key) {
|
|
1263
|
+
case 'minlength':
|
|
1264
|
+
case 'maxlength':
|
|
1265
|
+
case 'pattern':
|
|
1266
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
1267
|
+
break;
|
|
1268
|
+
default:
|
|
1269
|
+
if (!passwordInputValidationAttributes.includes(key)) {
|
|
1270
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'password'.\x1b[0m`);
|
|
1271
|
+
}
|
|
1272
|
+
break;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
} else {
|
|
1276
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'password'.\x1b[0m`);
|
|
1277
|
+
}
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// Handle the binding syntax
|
|
1282
|
+
// Handle the binding syntax
|
|
1283
|
+
let bindingDirective = '';
|
|
1284
|
+
if (attributes.binding) {
|
|
1285
|
+
if (attributes.binding === 'bind:value' && name) {
|
|
1286
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1287
|
+
}
|
|
1288
|
+
if (attributes.binding.startsWith('::') && name) {
|
|
1289
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1290
|
+
}
|
|
1291
|
+
if (attributes.binding && !name) {
|
|
1292
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
|
|
1300
|
+
// Get the id from attributes or fall back to name
|
|
1301
|
+
let id = attributes.id || name;
|
|
1302
|
+
|
|
1303
|
+
// Construct additional attributes dynamically
|
|
1304
|
+
let additionalAttrs = '';
|
|
1305
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1306
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
1307
|
+
// Handle event attributes
|
|
1308
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
1309
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
1310
|
+
} else {
|
|
1311
|
+
// Handle boolean attributes
|
|
1312
|
+
if (value === true) {
|
|
1313
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
1314
|
+
} else if (value !== false) {
|
|
1315
|
+
// Convert underscores to hyphens and set the attribute
|
|
1316
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
let inputClass;
|
|
1323
|
+
if ('class' in attributes) {
|
|
1324
|
+
inputClass = attributes.class;
|
|
1325
|
+
} else {
|
|
1326
|
+
inputClass = this.inputClass;
|
|
1327
|
+
}
|
|
1328
|
+
// Construct the final HTML string
|
|
1329
|
+
let formHTML = `
|
|
1330
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
1331
|
+
<label for="${id}">${label}
|
|
1332
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
1333
|
+
</label>
|
|
1334
|
+
<input
|
|
1335
|
+
type="${type}"
|
|
1336
|
+
name="${name}"
|
|
1337
|
+
${bindingDirective}
|
|
1338
|
+
id="${id}"
|
|
1339
|
+
class="${inputClass}"
|
|
1340
|
+
${additionalAttrs}
|
|
1341
|
+
${validationAttrs}
|
|
1342
|
+
/>
|
|
1343
|
+
</div>
|
|
1344
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
1345
|
+
|
|
1346
|
+
let formattedHtml = formHTML;
|
|
1347
|
+
|
|
1348
|
+
// Apply vertical layout to the <input> element only
|
|
1349
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
1350
|
+
// Reformat attributes into a vertical layout
|
|
1351
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
1352
|
+
return `<input\n${attributes}\n/>`;
|
|
1353
|
+
});
|
|
1354
|
+
|
|
1355
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
1356
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
1357
|
+
// Ensure <div> starts on a new line
|
|
1358
|
+
return `\n${match}\n`;
|
|
1359
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
1360
|
+
|
|
1361
|
+
//return formattedHtml;
|
|
1362
|
+
this.formMarkUp +=formattedHtml;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
// Textarea field rendering
|
|
1367
|
+
|
|
1368
|
+
renderTextAreaField(type, name, label, validate, attributes) {
|
|
1369
|
+
const textInputValidationAttributes = [
|
|
1370
|
+
'required',
|
|
1371
|
+
'minlength',
|
|
1372
|
+
'maxlength',
|
|
1373
|
+
'pattern',
|
|
1374
|
+
];
|
|
1375
|
+
|
|
1376
|
+
// Construct validation attributes
|
|
1377
|
+
let validationAttrs = '';
|
|
1378
|
+
if (validate) {
|
|
1379
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
1380
|
+
if (textInputValidationAttributes.includes(key)) {
|
|
1381
|
+
if (typeof value === 'boolean' && value) {
|
|
1382
|
+
validationAttrs += ` ${key}\n`;
|
|
1383
|
+
} else {
|
|
1384
|
+
switch (key) {
|
|
1385
|
+
case 'pattern':
|
|
1386
|
+
case 'minlength':
|
|
1387
|
+
case 'maxlength':
|
|
1388
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
1389
|
+
break;
|
|
1390
|
+
default:
|
|
1391
|
+
if (!textInputValidationAttributes.includes(key)) {
|
|
1392
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'number'.\x1b[0m`);
|
|
1393
|
+
}
|
|
1394
|
+
break;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
} else {
|
|
1398
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'text'.\x1b[0m`);
|
|
1399
|
+
}
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
|
|
1404
|
+
|
|
1405
|
+
// Handle the binding syntax
|
|
1406
|
+
let bindingDirective = '';
|
|
1407
|
+
if (attributes.binding) {
|
|
1408
|
+
if (attributes.binding === 'bind:value' && name) {
|
|
1409
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1410
|
+
}
|
|
1411
|
+
if (attributes.binding.startsWith('::') && name) {
|
|
1412
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1413
|
+
}
|
|
1414
|
+
if (attributes.binding && !name) {
|
|
1415
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
|
|
1421
|
+
|
|
1422
|
+
// Get the id from attributes or fall back to name
|
|
1423
|
+
let id = attributes.id || name;
|
|
1424
|
+
// Determine if semanti is true based on formSettings
|
|
1425
|
+
const framework = this.formSettings?.framework || false;
|
|
1426
|
+
|
|
1427
|
+
// Construct additional attributes dynamically
|
|
1428
|
+
let additionalAttrs = '';
|
|
1429
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1430
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
1431
|
+
// Handle event attributes
|
|
1432
|
+
if (framework === 'semantq') {
|
|
1433
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
1434
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
1435
|
+
} else {
|
|
1436
|
+
// Add parentheses if not present
|
|
1437
|
+
const eventValue = value.endsWith('()') ? value : `${value}()`;
|
|
1438
|
+
additionalAttrs += ` ${key}="${eventValue}"\n`;
|
|
1439
|
+
}
|
|
1440
|
+
} else {
|
|
1441
|
+
// Handle boolean attributes
|
|
1442
|
+
if (value === true) {
|
|
1443
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
1444
|
+
} else if (value !== false) {
|
|
1445
|
+
// Convert underscores to hyphens and set the attribute
|
|
1446
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
|
|
1453
|
+
|
|
1454
|
+
let inputClass;
|
|
1455
|
+
if ('class' in attributes) {
|
|
1456
|
+
inputClass = attributes.class;
|
|
1457
|
+
} else {
|
|
1458
|
+
inputClass = this.inputClass;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
// Construct the final HTML string for textarea
|
|
1462
|
+
let formHTML = `
|
|
1463
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
1464
|
+
<label for="${id}">${label}
|
|
1465
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
1466
|
+
</label>
|
|
1467
|
+
<textarea
|
|
1468
|
+
name="${name}"
|
|
1469
|
+
${bindingDirective}
|
|
1470
|
+
id="${id}"
|
|
1471
|
+
class="${inputClass}"
|
|
1472
|
+
${additionalAttrs}
|
|
1473
|
+
${validationAttrs}
|
|
1474
|
+
${additionalAttrs.includes('placeholder') ? '' : (this.formSettings.placeholders ? `placeholder="${label}"` : '')}>
|
|
1475
|
+
</textarea>
|
|
1476
|
+
</div>
|
|
1477
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
1478
|
+
|
|
1479
|
+
let formattedHtml = formHTML;
|
|
1480
|
+
|
|
1481
|
+
// Apply vertical layout to the <textarea> element only
|
|
1482
|
+
formattedHtml = formattedHtml.replace(/<textarea\s+([^>]*)>\s*<\/textarea>/, (match, p1) => {
|
|
1483
|
+
// Reformat attributes into a vertical layout
|
|
1484
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
1485
|
+
return `<textarea\n${attributes}\n></textarea>`;
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
this.formMarkUp += formattedHtml;
|
|
1489
|
+
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
// New method for rendering tel fields
|
|
1494
|
+
renderTelField(type, name, label, validate, attributes) {
|
|
1495
|
+
|
|
1496
|
+
const telInputValidationAttributes = [
|
|
1497
|
+
'required',
|
|
1498
|
+
'pattern',
|
|
1499
|
+
'minlength',
|
|
1500
|
+
'maxlength',
|
|
1501
|
+
];
|
|
1502
|
+
|
|
1503
|
+
|
|
1504
|
+
// Construct validation attributes
|
|
1505
|
+
let validationAttrs = '';
|
|
1506
|
+
if (validate) {
|
|
1507
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
1508
|
+
if (telInputValidationAttributes.includes(key)) {
|
|
1509
|
+
if (typeof value === 'boolean' && value) {
|
|
1510
|
+
validationAttrs += ` ${key}\n`;
|
|
1511
|
+
} else {
|
|
1512
|
+
switch (key) {
|
|
1513
|
+
case 'pattern':
|
|
1514
|
+
case 'minlength':
|
|
1515
|
+
case 'maxlength':
|
|
1516
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
1517
|
+
break;
|
|
1518
|
+
default:
|
|
1519
|
+
if (!telInputValidationAttributes.includes(key)) {
|
|
1520
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'tel'.\x1b[0m`);
|
|
1521
|
+
}
|
|
1522
|
+
break;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
} else {
|
|
1526
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'tel'.\x1b[0m`);
|
|
1527
|
+
}
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// Handle the binding syntax
|
|
1532
|
+
// Handle the binding syntax
|
|
1533
|
+
let bindingDirective = '';
|
|
1534
|
+
if (attributes?.binding === 'bind:value' && name) {
|
|
1535
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1536
|
+
}
|
|
1537
|
+
if (attributes?.binding?.startsWith('::') && name) {
|
|
1538
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1539
|
+
}
|
|
1540
|
+
if (attributes?.binding && !name) {
|
|
1541
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// Get the id from attributes or fall back to name
|
|
1546
|
+
let id = attributes.id || name;
|
|
1547
|
+
|
|
1548
|
+
// Construct additional attributes dynamically
|
|
1549
|
+
let additionalAttrs = '';
|
|
1550
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1551
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
1552
|
+
// Handle event attributes
|
|
1553
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
1554
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
1555
|
+
} else {
|
|
1556
|
+
// Handle boolean attributes
|
|
1557
|
+
if (value === true) {
|
|
1558
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
1559
|
+
} else if (value !== false) {
|
|
1560
|
+
// Convert underscores to hyphens and set the attribute
|
|
1561
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
let inputClass;
|
|
1568
|
+
if ('class' in attributes) {
|
|
1569
|
+
inputClass = attributes.class;
|
|
1570
|
+
} else {
|
|
1571
|
+
inputClass = this.inputClass;
|
|
1572
|
+
}
|
|
1573
|
+
// Construct the final HTML string
|
|
1574
|
+
let formHTML = `
|
|
1575
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
1576
|
+
<label for="${id}">${label}
|
|
1577
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
1578
|
+
</label>
|
|
1579
|
+
<input
|
|
1580
|
+
type="${type}"
|
|
1581
|
+
name="${name}"
|
|
1582
|
+
${bindingDirective}
|
|
1583
|
+
id="${id}"
|
|
1584
|
+
class="${inputClass}"
|
|
1585
|
+
${additionalAttrs}
|
|
1586
|
+
${validationAttrs}
|
|
1587
|
+
/>
|
|
1588
|
+
</div>
|
|
1589
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
1590
|
+
|
|
1591
|
+
let formattedHtml = formHTML;
|
|
1592
|
+
|
|
1593
|
+
// Apply vertical layout to the <input> element only
|
|
1594
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
1595
|
+
// Reformat attributes into a vertical layout
|
|
1596
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
1597
|
+
return `<input\n${attributes}\n/>`;
|
|
1598
|
+
});
|
|
1599
|
+
|
|
1600
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
1601
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
1602
|
+
// Ensure <div> starts on a new line
|
|
1603
|
+
return `\n${match}\n`;
|
|
1604
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
1605
|
+
|
|
1606
|
+
return formattedHtml;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
renderDateField(type, name, label, validate, attributes) {
|
|
1610
|
+
// Define valid attributes for the date input type
|
|
1611
|
+
const dateInputAttributes = [
|
|
1612
|
+
'required',
|
|
1613
|
+
'min',
|
|
1614
|
+
'max',
|
|
1615
|
+
'step',
|
|
1616
|
+
'placeholder',
|
|
1617
|
+
'readonly',
|
|
1618
|
+
'disabled',
|
|
1619
|
+
'autocomplete',
|
|
1620
|
+
'spellcheck',
|
|
1621
|
+
'inputmode',
|
|
1622
|
+
'title',
|
|
1623
|
+
];
|
|
1624
|
+
|
|
1625
|
+
// Construct validation attributes
|
|
1626
|
+
let validationAttrs = '';
|
|
1627
|
+
if (validate) {
|
|
1628
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
1629
|
+
if (dateInputAttributes.includes(key)) {
|
|
1630
|
+
if (typeof value === 'boolean' && value) {
|
|
1631
|
+
validationAttrs += ` ${key}\n`;
|
|
1632
|
+
} else {
|
|
1633
|
+
switch (key) {
|
|
1634
|
+
case 'min':
|
|
1635
|
+
case 'max':
|
|
1636
|
+
case 'step':
|
|
1637
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
1638
|
+
break;
|
|
1639
|
+
default:
|
|
1640
|
+
if (!dateInputAttributes.includes(key)) {
|
|
1641
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'date'.\x1b[0m`);
|
|
1642
|
+
}
|
|
1643
|
+
break;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
} else {
|
|
1647
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'date'.\x1b[0m`);
|
|
1648
|
+
}
|
|
1649
|
+
});
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
// Handle the binding syntax
|
|
1653
|
+
let bindingDirective = '';
|
|
1654
|
+
if (attributes?.binding === 'bind:value' && name) {
|
|
1655
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1656
|
+
}
|
|
1657
|
+
if (attributes?.binding?.startsWith('::') && name) {
|
|
1658
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1659
|
+
}
|
|
1660
|
+
if (attributes?.binding && !name) {
|
|
1661
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
// Get the id from attributes or fall back to name
|
|
1666
|
+
let id = attributes.id || name;
|
|
1667
|
+
|
|
1668
|
+
// Construct additional attributes dynamically
|
|
1669
|
+
let additionalAttrs = '';
|
|
1670
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1671
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
1672
|
+
// Handle event attributes
|
|
1673
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
1674
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
1675
|
+
} else {
|
|
1676
|
+
// Handle boolean attributes
|
|
1677
|
+
if (value === true) {
|
|
1678
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
1679
|
+
} else if (value !== false) {
|
|
1680
|
+
// Convert underscores to hyphens and set the attribute
|
|
1681
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
let inputClass;
|
|
1688
|
+
if ('class' in attributes) {
|
|
1689
|
+
inputClass = attributes.class;
|
|
1690
|
+
} else {
|
|
1691
|
+
inputClass = this.inputClass;
|
|
1692
|
+
}
|
|
1693
|
+
// Construct the final HTML string
|
|
1694
|
+
let formHTML = `
|
|
1695
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
1696
|
+
<label for="${id}">${label}
|
|
1697
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
1698
|
+
</label>
|
|
1699
|
+
<input
|
|
1700
|
+
type="${type}"
|
|
1701
|
+
name="${name}"
|
|
1702
|
+
${bindingDirective}
|
|
1703
|
+
id="${id}"
|
|
1704
|
+
class="${inputClass}"
|
|
1705
|
+
${additionalAttrs}
|
|
1706
|
+
${validationAttrs}
|
|
1707
|
+
/>
|
|
1708
|
+
</div>
|
|
1709
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
1710
|
+
|
|
1711
|
+
let formattedHtml = formHTML;
|
|
1712
|
+
|
|
1713
|
+
// Apply vertical layout to the <input> element only
|
|
1714
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
1715
|
+
// Reformat attributes into a vertical layout
|
|
1716
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
1717
|
+
return `<input\n${attributes}\n/>`;
|
|
1718
|
+
});
|
|
1719
|
+
|
|
1720
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
1721
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
1722
|
+
// Ensure <div> starts on a new line
|
|
1723
|
+
return `\n${match}\n`;
|
|
1724
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
1725
|
+
|
|
1726
|
+
//return formattedHtml;
|
|
1727
|
+
this.formMarkUp +=formattedHtml;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
|
|
1731
|
+
|
|
1732
|
+
renderTimeField(type, name, label, validate, attributes) {
|
|
1733
|
+
// Define valid attributes for the time input type
|
|
1734
|
+
const timeInputAttributes = [
|
|
1735
|
+
'required',
|
|
1736
|
+
'min',
|
|
1737
|
+
'max',
|
|
1738
|
+
'step',
|
|
1739
|
+
'readonly',
|
|
1740
|
+
'disabled',
|
|
1741
|
+
'autocomplete',
|
|
1742
|
+
'spellcheck',
|
|
1743
|
+
'inputmode',
|
|
1744
|
+
'title',
|
|
1745
|
+
];
|
|
1746
|
+
|
|
1747
|
+
// Construct validation attributes
|
|
1748
|
+
let validationAttrs = '';
|
|
1749
|
+
if (validate) {
|
|
1750
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
1751
|
+
if (timeInputAttributes.includes(key)) {
|
|
1752
|
+
if (typeof value === 'boolean' && value) {
|
|
1753
|
+
validationAttrs += ` ${key}\n`;
|
|
1754
|
+
} else {
|
|
1755
|
+
switch (key) {
|
|
1756
|
+
case 'min':
|
|
1757
|
+
case 'max':
|
|
1758
|
+
case 'step':
|
|
1759
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
1760
|
+
break;
|
|
1761
|
+
default:
|
|
1762
|
+
if (!timeInputAttributes.includes(key)) {
|
|
1763
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
1764
|
+
}
|
|
1765
|
+
break;
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
} else {
|
|
1769
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
// Handle the binding syntax
|
|
1775
|
+
// Handle the binding syntax
|
|
1776
|
+
let bindingDirective = '';
|
|
1777
|
+
if (attributes?.binding === 'bind:value' && name) {
|
|
1778
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1779
|
+
}
|
|
1780
|
+
if (attributes?.binding?.startsWith('::') && name) {
|
|
1781
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1782
|
+
}
|
|
1783
|
+
if (attributes?.binding && !name) {
|
|
1784
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
// Get the id from attributes or fall back to name
|
|
1789
|
+
let id = attributes.id || name;
|
|
1790
|
+
|
|
1791
|
+
// Construct additional attributes dynamically
|
|
1792
|
+
let additionalAttrs = '';
|
|
1793
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1794
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
1795
|
+
// Handle event attributes
|
|
1796
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
1797
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
1798
|
+
} else {
|
|
1799
|
+
// Handle boolean attributes
|
|
1800
|
+
if (value === true) {
|
|
1801
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
1802
|
+
} else if (value !== false) {
|
|
1803
|
+
// Convert underscores to hyphens and set the attribute
|
|
1804
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
let inputClass;
|
|
1811
|
+
if ('class' in attributes) {
|
|
1812
|
+
inputClass = attributes.class;
|
|
1813
|
+
} else {
|
|
1814
|
+
inputClass = this.inputClass;
|
|
1815
|
+
}
|
|
1816
|
+
// Construct the final HTML string
|
|
1817
|
+
let formHTML = `
|
|
1818
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
1819
|
+
<label for="${id}">${label}
|
|
1820
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
1821
|
+
</label>
|
|
1822
|
+
<input
|
|
1823
|
+
type="${type}"
|
|
1824
|
+
name="${name}"
|
|
1825
|
+
${bindingDirective}
|
|
1826
|
+
id="${id}"
|
|
1827
|
+
class="${inputClass}"
|
|
1828
|
+
${additionalAttrs}
|
|
1829
|
+
${validationAttrs}
|
|
1830
|
+
/>
|
|
1831
|
+
</div>
|
|
1832
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
1833
|
+
|
|
1834
|
+
let formattedHtml = formHTML;
|
|
1835
|
+
|
|
1836
|
+
// Apply vertical layout to the <input> element only
|
|
1837
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
1838
|
+
// Reformat attributes into a vertical layout
|
|
1839
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
1840
|
+
return `<input\n${attributes}\n/>`;
|
|
1841
|
+
});
|
|
1842
|
+
|
|
1843
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
1844
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
1845
|
+
// Ensure <div> starts on a new line
|
|
1846
|
+
return `\n${match}\n`;
|
|
1847
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
1848
|
+
|
|
1849
|
+
//return formattedHtml;
|
|
1850
|
+
this.formMarkUp +=formattedHtml;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
|
|
1854
|
+
|
|
1855
|
+
|
|
1856
|
+
renderDateTimeField(type, name, label, validate, attributes) {
|
|
1857
|
+
// Define valid attributes for the datetime input type
|
|
1858
|
+
const dateTimeInputAttributes = [
|
|
1859
|
+
'required',
|
|
1860
|
+
'min',
|
|
1861
|
+
'max',
|
|
1862
|
+
'step',
|
|
1863
|
+
'readonly',
|
|
1864
|
+
'disabled',
|
|
1865
|
+
'autocomplete',
|
|
1866
|
+
'spellcheck',
|
|
1867
|
+
'inputmode',
|
|
1868
|
+
'title',
|
|
1869
|
+
];
|
|
1870
|
+
|
|
1871
|
+
// Construct validation attributes
|
|
1872
|
+
let validationAttrs = '';
|
|
1873
|
+
if (validate) {
|
|
1874
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
1875
|
+
if (dateTimeInputAttributes.includes(key)) {
|
|
1876
|
+
if (typeof value === 'boolean' && value) {
|
|
1877
|
+
validationAttrs += ` ${key}\n`;
|
|
1878
|
+
} else {
|
|
1879
|
+
switch (key) {
|
|
1880
|
+
case 'min':
|
|
1881
|
+
case 'max':
|
|
1882
|
+
case 'step':
|
|
1883
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
1884
|
+
break;
|
|
1885
|
+
default:
|
|
1886
|
+
if (!dateTimeInputAttributes.includes(key)) {
|
|
1887
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
1888
|
+
}
|
|
1889
|
+
break;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
} else {
|
|
1893
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
1894
|
+
}
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
// Handle the binding syntax
|
|
1899
|
+
let bindingDirective = '';
|
|
1900
|
+
if (attributes.binding) {
|
|
1901
|
+
if (attributes.binding === 'bind:value' && name) {
|
|
1902
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1903
|
+
}
|
|
1904
|
+
if (attributes.binding.startsWith('::') && name) {
|
|
1905
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
1906
|
+
}
|
|
1907
|
+
if (attributes.binding && !name) {
|
|
1908
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
|
|
1914
|
+
|
|
1915
|
+
|
|
1916
|
+
// Get the id from attributes or fall back to name
|
|
1917
|
+
let id = attributes.id || name;
|
|
1918
|
+
|
|
1919
|
+
// Construct additional attributes dynamically
|
|
1920
|
+
let additionalAttrs = '';
|
|
1921
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1922
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
1923
|
+
// Handle event attributes
|
|
1924
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
1925
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
1926
|
+
} else {
|
|
1927
|
+
// Handle boolean attributes
|
|
1928
|
+
if (value === true) {
|
|
1929
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
1930
|
+
} else if (value !== false) {
|
|
1931
|
+
// Convert underscores to hyphens and set the attribute
|
|
1932
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
let inputClass;
|
|
1939
|
+
if ('class' in attributes) {
|
|
1940
|
+
inputClass = attributes.class;
|
|
1941
|
+
} else {
|
|
1942
|
+
inputClass = this.inputClass;
|
|
1943
|
+
}
|
|
1944
|
+
// Construct the final HTML string
|
|
1945
|
+
let formHTML = `
|
|
1946
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
1947
|
+
<label for="${id}">${label}
|
|
1948
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
1949
|
+
</label>
|
|
1950
|
+
<input
|
|
1951
|
+
type="${type}"
|
|
1952
|
+
name="${name}"
|
|
1953
|
+
${bindingDirective}
|
|
1954
|
+
id="${id}"
|
|
1955
|
+
class="${inputClass}"
|
|
1956
|
+
${additionalAttrs}
|
|
1957
|
+
${validationAttrs}
|
|
1958
|
+
/>
|
|
1959
|
+
</div>
|
|
1960
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
1961
|
+
|
|
1962
|
+
let formattedHtml = formHTML;
|
|
1963
|
+
|
|
1964
|
+
// Apply vertical layout to the <input> element only
|
|
1965
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
1966
|
+
// Reformat attributes into a vertical layout
|
|
1967
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
1968
|
+
return `<input\n${attributes}\n/>`;
|
|
1969
|
+
});
|
|
1970
|
+
|
|
1971
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
1972
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
1973
|
+
// Ensure <div> starts on a new line
|
|
1974
|
+
return `\n${match}\n`;
|
|
1975
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
1976
|
+
|
|
1977
|
+
//return formattedHtml;
|
|
1978
|
+
this.formMarkUp +=formattedHtml;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
|
|
1982
|
+
renderMonthField(type, name, label, validate, attributes) {
|
|
1983
|
+
// Define valid attributes for the month input type
|
|
1984
|
+
const monthInputAttributes = [
|
|
1985
|
+
'required',
|
|
1986
|
+
'min',
|
|
1987
|
+
'max',
|
|
1988
|
+
'pattern',
|
|
1989
|
+
'placeholder',
|
|
1990
|
+
'readonly',
|
|
1991
|
+
'disabled',
|
|
1992
|
+
'size',
|
|
1993
|
+
'autocomplete',
|
|
1994
|
+
'spellcheck',
|
|
1995
|
+
'inputmode',
|
|
1996
|
+
'title',
|
|
1997
|
+
];
|
|
1998
|
+
|
|
1999
|
+
// Construct validation attributes
|
|
2000
|
+
let validationAttrs = '';
|
|
2001
|
+
if (validate) {
|
|
2002
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
2003
|
+
if (monthInputAttributes.includes(key)) {
|
|
2004
|
+
if (typeof value === 'boolean' && value) {
|
|
2005
|
+
validationAttrs += ` ${key}\n`;
|
|
2006
|
+
} else {
|
|
2007
|
+
switch (key) {
|
|
2008
|
+
case 'min':
|
|
2009
|
+
case 'max':
|
|
2010
|
+
case 'pattern':
|
|
2011
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
2012
|
+
break;
|
|
2013
|
+
default:
|
|
2014
|
+
if (monthInputAttributes.includes(key)) {
|
|
2015
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'month'.\x1b[0m`);
|
|
2016
|
+
}
|
|
2017
|
+
break;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
} else {
|
|
2021
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'month'.\x1b[0m`);
|
|
2022
|
+
}
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
// Handle the binding syntax
|
|
2027
|
+
let bindingDirective = '';
|
|
2028
|
+
if (attributes?.binding === 'bind:value' && name) {
|
|
2029
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2030
|
+
}
|
|
2031
|
+
if (attributes?.binding?.startsWith('::') && name) {
|
|
2032
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2033
|
+
}
|
|
2034
|
+
if (attributes?.binding && !name) {
|
|
2035
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
|
|
2040
|
+
// Get the id from attributes or fall back to name
|
|
2041
|
+
let id = attributes.id || name;
|
|
2042
|
+
|
|
2043
|
+
// Construct additional attributes dynamically
|
|
2044
|
+
let additionalAttrs = '';
|
|
2045
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
2046
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
2047
|
+
// Handle event attributes
|
|
2048
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
2049
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
2050
|
+
} else {
|
|
2051
|
+
// Handle boolean attributes
|
|
2052
|
+
if (value === true) {
|
|
2053
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
2054
|
+
} else if (value !== false) {
|
|
2055
|
+
// Convert underscores to hyphens and set the attribute
|
|
2056
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
let inputClass;
|
|
2063
|
+
if ('class' in attributes) {
|
|
2064
|
+
inputClass = attributes.class;
|
|
2065
|
+
} else {
|
|
2066
|
+
inputClass = this.inputClass;
|
|
2067
|
+
}
|
|
2068
|
+
// Construct the final HTML string
|
|
2069
|
+
let formHTML = `
|
|
2070
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
2071
|
+
<label for="${id}">${label}
|
|
2072
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
2073
|
+
</label>
|
|
2074
|
+
<input
|
|
2075
|
+
type="${type}"
|
|
2076
|
+
name="${name}"
|
|
2077
|
+
${bindingDirective}
|
|
2078
|
+
id="${id}"
|
|
2079
|
+
class="${inputClass}"
|
|
2080
|
+
${additionalAttrs}
|
|
2081
|
+
${validationAttrs}
|
|
2082
|
+
/>
|
|
2083
|
+
</div>
|
|
2084
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
2085
|
+
|
|
2086
|
+
let formattedHtml = formHTML;
|
|
2087
|
+
|
|
2088
|
+
// Apply vertical layout to the <input> element only
|
|
2089
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
2090
|
+
// Reformat attributes into a vertical layout
|
|
2091
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
2092
|
+
return `<input\n${attributes}\n/>`;
|
|
2093
|
+
});
|
|
2094
|
+
|
|
2095
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
2096
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
2097
|
+
// Ensure <div> starts on a new line
|
|
2098
|
+
return `\n${match}\n`;
|
|
2099
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
2100
|
+
|
|
2101
|
+
//return formattedHtml;
|
|
2102
|
+
this.formMarkUp +=formattedHtml;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
|
|
2106
|
+
|
|
2107
|
+
renderWeekField(type, name, label, validate, attributes) {
|
|
2108
|
+
// Define valid attributes for the week input type
|
|
2109
|
+
const weekInputAttributes = [
|
|
2110
|
+
'required',
|
|
2111
|
+
'min',
|
|
2112
|
+
'max',
|
|
2113
|
+
'pattern',
|
|
2114
|
+
'placeholder',
|
|
2115
|
+
'readonly',
|
|
2116
|
+
'disabled',
|
|
2117
|
+
'size',
|
|
2118
|
+
'autocomplete',
|
|
2119
|
+
'spellcheck',
|
|
2120
|
+
'inputmode',
|
|
2121
|
+
'title',
|
|
2122
|
+
];
|
|
2123
|
+
|
|
2124
|
+
// Construct validation attributes
|
|
2125
|
+
let validationAttrs = '';
|
|
2126
|
+
if (validate) {
|
|
2127
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
2128
|
+
if (weekInputAttributes.includes(key)) {
|
|
2129
|
+
if (typeof value === 'boolean' && value) {
|
|
2130
|
+
validationAttrs += ` ${key}\n`;
|
|
2131
|
+
} else {
|
|
2132
|
+
switch (key) {
|
|
2133
|
+
case 'min':
|
|
2134
|
+
case 'max':
|
|
2135
|
+
case 'pattern':
|
|
2136
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
2137
|
+
break;
|
|
2138
|
+
default:
|
|
2139
|
+
if (weekInputAttributes.includes(key)) {
|
|
2140
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'week'.\x1b[0m`);
|
|
2141
|
+
}
|
|
2142
|
+
break;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
} else {
|
|
2146
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'week'.\x1b[0m`);
|
|
2147
|
+
}
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
// Handle the binding syntax
|
|
2152
|
+
let bindingDirective = '';
|
|
2153
|
+
if (attributes?.binding === 'bind:value' && name) {
|
|
2154
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2155
|
+
}
|
|
2156
|
+
if (attributes?.binding?.startsWith('::') && name) {
|
|
2157
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2158
|
+
}
|
|
2159
|
+
if (attributes?.binding && !name) {
|
|
2160
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
// Get the id from attributes or fall back to name
|
|
2165
|
+
let id = attributes.id || name;
|
|
2166
|
+
|
|
2167
|
+
// Construct additional attributes dynamically
|
|
2168
|
+
let additionalAttrs = '';
|
|
2169
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
2170
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
2171
|
+
// Handle event attributes
|
|
2172
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
2173
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
2174
|
+
} else {
|
|
2175
|
+
// Handle boolean attributes
|
|
2176
|
+
if (value === true) {
|
|
2177
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
2178
|
+
} else if (value !== false) {
|
|
2179
|
+
// Convert underscores to hyphens and set the attribute
|
|
2180
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
let inputClass;
|
|
2187
|
+
if ('class' in attributes) {
|
|
2188
|
+
inputClass = attributes.class;
|
|
2189
|
+
} else {
|
|
2190
|
+
inputClass = this.inputClass;
|
|
2191
|
+
}
|
|
2192
|
+
// Construct the final HTML string
|
|
2193
|
+
let formHTML = `
|
|
2194
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
2195
|
+
<label for="${id}">${label}
|
|
2196
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
2197
|
+
</label>
|
|
2198
|
+
<input
|
|
2199
|
+
type="${type}"
|
|
2200
|
+
name="${name}"
|
|
2201
|
+
${bindingDirective}
|
|
2202
|
+
id="${id}"
|
|
2203
|
+
class="${inputClass}"
|
|
2204
|
+
${additionalAttrs}
|
|
2205
|
+
${validationAttrs}
|
|
2206
|
+
/>
|
|
2207
|
+
</div>
|
|
2208
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
2209
|
+
|
|
2210
|
+
let formattedHtml = formHTML;
|
|
2211
|
+
|
|
2212
|
+
// Apply vertical layout to the <input> element only
|
|
2213
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
2214
|
+
// Reformat attributes into a vertical layout
|
|
2215
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
2216
|
+
return `<input\n${attributes}\n/>`;
|
|
2217
|
+
});
|
|
2218
|
+
|
|
2219
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
2220
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
2221
|
+
// Ensure <div> starts on a new line
|
|
2222
|
+
return `\n${match}\n`;
|
|
2223
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
2224
|
+
|
|
2225
|
+
//return formattedHtml;
|
|
2226
|
+
this.formMarkUp +=formattedHtml;
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
|
|
2230
|
+
|
|
2231
|
+
renderUrlField(type, name, label, validate, attributes) {
|
|
2232
|
+
// Define valid attributes for the URL input type
|
|
2233
|
+
const urlInputAttributes = [
|
|
2234
|
+
'required',
|
|
2235
|
+
'pattern',
|
|
2236
|
+
'placeholder',
|
|
2237
|
+
'readonly',
|
|
2238
|
+
'disabled',
|
|
2239
|
+
'size',
|
|
2240
|
+
'autocomplete',
|
|
2241
|
+
'spellcheck',
|
|
2242
|
+
'inputmode',
|
|
2243
|
+
'title',
|
|
2244
|
+
];
|
|
2245
|
+
|
|
2246
|
+
// Construct validation attributes
|
|
2247
|
+
let validationAttrs = '';
|
|
2248
|
+
if (validate) {
|
|
2249
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
2250
|
+
if (urlInputAttributes.includes(key)) {
|
|
2251
|
+
if (typeof value === 'boolean' && value) {
|
|
2252
|
+
validationAttrs += ` ${key}\n`;
|
|
2253
|
+
} else {
|
|
2254
|
+
switch (key) {
|
|
2255
|
+
case 'pattern':
|
|
2256
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
2257
|
+
break;
|
|
2258
|
+
default:
|
|
2259
|
+
if (!urlInputAttributes.includes(key)) {
|
|
2260
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2261
|
+
}
|
|
2262
|
+
break;
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
} else {
|
|
2266
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2267
|
+
}
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
// Handle the binding syntax
|
|
2272
|
+
// Handle the binding syntax
|
|
2273
|
+
let bindingDirective = '';
|
|
2274
|
+
if (attributes?.binding === 'bind:value' && name) {
|
|
2275
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2276
|
+
}
|
|
2277
|
+
if (attributes?.binding?.startsWith('::') && name) {
|
|
2278
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2279
|
+
}
|
|
2280
|
+
if (attributes?.binding && !name) {
|
|
2281
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
// Get the id from attributes or fall back to name
|
|
2286
|
+
let id = attributes.id || name;
|
|
2287
|
+
|
|
2288
|
+
// Construct additional attributes dynamically
|
|
2289
|
+
let additionalAttrs = '';
|
|
2290
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
2291
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
2292
|
+
// Handle event attributes
|
|
2293
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
2294
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
2295
|
+
} else {
|
|
2296
|
+
// Handle boolean attributes
|
|
2297
|
+
if (value === true) {
|
|
2298
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
2299
|
+
} else if (value !== false) {
|
|
2300
|
+
// Convert underscores to hyphens and set the attribute
|
|
2301
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
let inputClass;
|
|
2308
|
+
if ('class' in attributes) {
|
|
2309
|
+
inputClass = attributes.class;
|
|
2310
|
+
} else {
|
|
2311
|
+
inputClass = this.inputClass;
|
|
2312
|
+
}
|
|
2313
|
+
// Construct the final HTML string
|
|
2314
|
+
let formHTML = `
|
|
2315
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
2316
|
+
<label for="${id}">${label}
|
|
2317
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
2318
|
+
</label>
|
|
2319
|
+
<input
|
|
2320
|
+
type="${type}"
|
|
2321
|
+
name="${name}"
|
|
2322
|
+
${bindingDirective}
|
|
2323
|
+
id="${id}"
|
|
2324
|
+
class="${inputClass}"
|
|
2325
|
+
${additionalAttrs}
|
|
2326
|
+
${validationAttrs}
|
|
2327
|
+
/>
|
|
2328
|
+
</div>
|
|
2329
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
2330
|
+
|
|
2331
|
+
let formattedHtml = formHTML;
|
|
2332
|
+
|
|
2333
|
+
// Apply vertical layout to the <input> element only
|
|
2334
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
2335
|
+
// Reformat attributes into a vertical layout
|
|
2336
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
2337
|
+
return `<input\n${attributes}\n/>`;
|
|
2338
|
+
});
|
|
2339
|
+
|
|
2340
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
2341
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
2342
|
+
// Ensure <div> starts on a new line
|
|
2343
|
+
return `\n${match}\n`;
|
|
2344
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
2345
|
+
|
|
2346
|
+
//return formattedHtml;
|
|
2347
|
+
this.formMarkUp +=formattedHtml;
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
|
|
2351
|
+
renderSearchField(type, name, label, validate, attributes) {
|
|
2352
|
+
// Define valid attributes for the search input type
|
|
2353
|
+
const searchInputAttributes = [
|
|
2354
|
+
'required',
|
|
2355
|
+
'pattern',
|
|
2356
|
+
'placeholder',
|
|
2357
|
+
'readonly',
|
|
2358
|
+
'disabled',
|
|
2359
|
+
'size',
|
|
2360
|
+
'autocomplete',
|
|
2361
|
+
'spellcheck',
|
|
2362
|
+
'inputmode',
|
|
2363
|
+
'title',
|
|
2364
|
+
];
|
|
2365
|
+
|
|
2366
|
+
// Construct validation attributes
|
|
2367
|
+
let validationAttrs = '';
|
|
2368
|
+
if (validate) {
|
|
2369
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
2370
|
+
if (searchInputAttributes.includes(key)) {
|
|
2371
|
+
if (typeof value === 'boolean' && value) {
|
|
2372
|
+
validationAttrs += ` ${key}\n`;
|
|
2373
|
+
} else {
|
|
2374
|
+
switch (key) {
|
|
2375
|
+
case 'pattern':
|
|
2376
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
2377
|
+
break;
|
|
2378
|
+
default:
|
|
2379
|
+
if (!searchInputAttributes.includes(key)) {
|
|
2380
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2381
|
+
}
|
|
2382
|
+
break;
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
} else {
|
|
2386
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2387
|
+
}
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
// Handle the binding syntax
|
|
2392
|
+
let bindingDirective = '';
|
|
2393
|
+
if (attributes?.binding === 'bind:value' && name) {
|
|
2394
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2395
|
+
}
|
|
2396
|
+
if (attributes?.binding?.startsWith('::') && name) {
|
|
2397
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2398
|
+
}
|
|
2399
|
+
if (attributes?.binding && !name) {
|
|
2400
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
// Get the id from attributes or fall back to name
|
|
2405
|
+
let id = attributes.id || name;
|
|
2406
|
+
|
|
2407
|
+
// Construct additional attributes dynamically
|
|
2408
|
+
let additionalAttrs = '';
|
|
2409
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
2410
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
2411
|
+
// Handle event attributes
|
|
2412
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
2413
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
2414
|
+
} else {
|
|
2415
|
+
// Handle boolean attributes
|
|
2416
|
+
if (value === true) {
|
|
2417
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
2418
|
+
} else if (value !== false) {
|
|
2419
|
+
// Convert underscores to hyphens and set the attribute
|
|
2420
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
let inputClass;
|
|
2427
|
+
if ('class' in attributes) {
|
|
2428
|
+
inputClass = attributes.class;
|
|
2429
|
+
} else {
|
|
2430
|
+
inputClass = this.inputClass;
|
|
2431
|
+
}
|
|
2432
|
+
// Construct the final HTML string
|
|
2433
|
+
let formHTML = `
|
|
2434
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
2435
|
+
<label for="${id}">${label}
|
|
2436
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
2437
|
+
</label>
|
|
2438
|
+
<input
|
|
2439
|
+
type="${type}"
|
|
2440
|
+
name="${name}"
|
|
2441
|
+
${bindingDirective}
|
|
2442
|
+
id="${id}"
|
|
2443
|
+
class="${inputClass}"
|
|
2444
|
+
${additionalAttrs}
|
|
2445
|
+
${validationAttrs}
|
|
2446
|
+
/>
|
|
2447
|
+
</div>
|
|
2448
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
2449
|
+
|
|
2450
|
+
let formattedHtml = formHTML;
|
|
2451
|
+
|
|
2452
|
+
// Apply vertical layout to the <input> element only
|
|
2453
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
2454
|
+
// Reformat attributes into a vertical layout
|
|
2455
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
2456
|
+
return `<input\n${attributes}\n/>`;
|
|
2457
|
+
});
|
|
2458
|
+
|
|
2459
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
2460
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
2461
|
+
// Ensure <div> starts on a new line
|
|
2462
|
+
return `\n${match}\n`;
|
|
2463
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
2464
|
+
|
|
2465
|
+
//return formattedHtml;
|
|
2466
|
+
this.formMarkUp +=formattedHtml;
|
|
2467
|
+
}
|
|
2468
|
+
|
|
2469
|
+
|
|
2470
|
+
renderColorField(type, name, label, validate, attributes) {
|
|
2471
|
+
// Define valid attributes for the color input type
|
|
2472
|
+
const colorInputAttributes = [
|
|
2473
|
+
'required',
|
|
2474
|
+
'readonly',
|
|
2475
|
+
'disabled',
|
|
2476
|
+
'autocomplete',
|
|
2477
|
+
'inputmode',
|
|
2478
|
+
'title',
|
|
2479
|
+
];
|
|
2480
|
+
|
|
2481
|
+
// Construct validation attributes
|
|
2482
|
+
let validationAttrs = '';
|
|
2483
|
+
if (validate) {
|
|
2484
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
2485
|
+
if (colorInputAttributes.includes(key)) {
|
|
2486
|
+
if (typeof value === 'boolean' && value) {
|
|
2487
|
+
validationAttrs += ` ${key}\n`;
|
|
2488
|
+
} else {
|
|
2489
|
+
switch (key) {
|
|
2490
|
+
default:
|
|
2491
|
+
if (!colorInputAttributes.includes(key)) {
|
|
2492
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2493
|
+
}
|
|
2494
|
+
break;
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
} else {
|
|
2498
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2499
|
+
}
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
// Handle the binding syntax
|
|
2504
|
+
// Handle the binding syntax
|
|
2505
|
+
let bindingDirective = '';
|
|
2506
|
+
if (attributes?.binding === 'bind:value' && name) {
|
|
2507
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2508
|
+
}
|
|
2509
|
+
if (attributes?.binding?.startsWith('::') && name) {
|
|
2510
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2511
|
+
}
|
|
2512
|
+
if (attributes?.binding && !name) {
|
|
2513
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
2514
|
+
return;
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
// Get the id from attributes or fall back to name
|
|
2518
|
+
let id = attributes.id || name;
|
|
2519
|
+
|
|
2520
|
+
// Construct additional attributes dynamically
|
|
2521
|
+
let additionalAttrs = '';
|
|
2522
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
2523
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
2524
|
+
// Handle event attributes
|
|
2525
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
2526
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
2527
|
+
} else {
|
|
2528
|
+
// Handle boolean attributes
|
|
2529
|
+
if (value === true) {
|
|
2530
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
2531
|
+
} else if (value !== false) {
|
|
2532
|
+
// Convert underscores to hyphens and set the attribute
|
|
2533
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
let inputClass;
|
|
2540
|
+
if ('class' in attributes) {
|
|
2541
|
+
inputClass = attributes.class;
|
|
2542
|
+
} else {
|
|
2543
|
+
// inputClass = this.inputClass;
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
if (type === 'color') {
|
|
2547
|
+
inputClass += ' form-color-input'; // Add the new specific class for color inputs
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
// Construct the final HTML string
|
|
2551
|
+
let formHTML = `
|
|
2552
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
2553
|
+
<label for="${id}">${label}
|
|
2554
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
2555
|
+
</label>
|
|
2556
|
+
<input
|
|
2557
|
+
type="${type}"
|
|
2558
|
+
name="${name}"
|
|
2559
|
+
${bindingDirective}
|
|
2560
|
+
id="${id}"
|
|
2561
|
+
class="${inputClass}"
|
|
2562
|
+
${additionalAttrs}
|
|
2563
|
+
${validationAttrs}
|
|
2564
|
+
/>
|
|
2565
|
+
</div>
|
|
2566
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
2567
|
+
|
|
2568
|
+
let formattedHtml = formHTML;
|
|
2569
|
+
|
|
2570
|
+
// Apply vertical layout to the <input> element only
|
|
2571
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
2572
|
+
// Reformat attributes into a vertical layout
|
|
2573
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
2574
|
+
return `<input\n${attributes}\n/>`;
|
|
2575
|
+
});
|
|
2576
|
+
|
|
2577
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
2578
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
2579
|
+
// Ensure <div> starts on a new line
|
|
2580
|
+
return `\n${match}\n`;
|
|
2581
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
2582
|
+
|
|
2583
|
+
//return formattedHtml;
|
|
2584
|
+
this.formMarkUp +=formattedHtml;
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
|
|
2588
|
+
|
|
2589
|
+
renderFileField(type, name, label, validate, attributes) {
|
|
2590
|
+
// Define valid attributes for the file input type
|
|
2591
|
+
const fileInputAttributes = [
|
|
2592
|
+
'required',
|
|
2593
|
+
'accept',
|
|
2594
|
+
'multiple',
|
|
2595
|
+
'disabled',
|
|
2596
|
+
'title',
|
|
2597
|
+
];
|
|
2598
|
+
|
|
2599
|
+
// Construct validation attributes
|
|
2600
|
+
let validationAttrs = '';
|
|
2601
|
+
if (validate) {
|
|
2602
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
2603
|
+
if (fileInputAttributes.includes(key)) {
|
|
2604
|
+
if (typeof value === 'boolean' && value) {
|
|
2605
|
+
validationAttrs += ` ${key}\n`;
|
|
2606
|
+
} else {
|
|
2607
|
+
switch (key) {
|
|
2608
|
+
default:
|
|
2609
|
+
if (!fileInputAttributes.includes(key)) {
|
|
2610
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2611
|
+
}
|
|
2612
|
+
break;
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
} else {
|
|
2616
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2617
|
+
}
|
|
2618
|
+
});
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
// Handle the binding syntax
|
|
2622
|
+
let bindingDirective = '';
|
|
2623
|
+
if (attributes?.binding === 'bind:value' && name) {
|
|
2624
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2625
|
+
}
|
|
2626
|
+
if (attributes?.binding?.startsWith('::') && name) {
|
|
2627
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2628
|
+
}
|
|
2629
|
+
if (attributes?.binding && !name) {
|
|
2630
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
// Get the id from attributes or fall back to name
|
|
2635
|
+
let id = attributes.id || name;
|
|
2636
|
+
|
|
2637
|
+
// Construct additional attributes dynamically
|
|
2638
|
+
let additionalAttrs = '';
|
|
2639
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
2640
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
2641
|
+
// Handle event attributes
|
|
2642
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
2643
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
2644
|
+
} else {
|
|
2645
|
+
// Handle boolean attributes
|
|
2646
|
+
if (value === true) {
|
|
2647
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
2648
|
+
} else if (value !== false) {
|
|
2649
|
+
// Convert underscores to hyphens and set the attribute
|
|
2650
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
|
|
2656
|
+
let inputClass;
|
|
2657
|
+
if ('class' in attributes) {
|
|
2658
|
+
inputClass = attributes.class;
|
|
2659
|
+
} else {
|
|
2660
|
+
inputClass = this.inputClass;
|
|
2661
|
+
}
|
|
2662
|
+
// Construct the final HTML string
|
|
2663
|
+
let formHTML = `
|
|
2664
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
2665
|
+
<label for="${id}">${label}
|
|
2666
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
2667
|
+
</label>
|
|
2668
|
+
<input
|
|
2669
|
+
type="${type}"
|
|
2670
|
+
name="${name}"
|
|
2671
|
+
${bindingDirective}
|
|
2672
|
+
id="${id}"
|
|
2673
|
+
class="${inputClass}"
|
|
2674
|
+
${additionalAttrs}
|
|
2675
|
+
${validationAttrs}
|
|
2676
|
+
/>
|
|
2677
|
+
</div>
|
|
2678
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
2679
|
+
|
|
2680
|
+
let formattedHtml = formHTML;
|
|
2681
|
+
|
|
2682
|
+
// Apply vertical layout to the <input> element only
|
|
2683
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
2684
|
+
// Reformat attributes into a vertical layout
|
|
2685
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
2686
|
+
return `<input\n${attributes}\n/>`;
|
|
2687
|
+
});
|
|
2688
|
+
|
|
2689
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
2690
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
2691
|
+
// Ensure <div> starts on a new line
|
|
2692
|
+
return `\n${match}\n`;
|
|
2693
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
2694
|
+
|
|
2695
|
+
//return formattedHtml;
|
|
2696
|
+
this.formMarkUp +=formattedHtml;
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
|
|
2700
|
+
|
|
2701
|
+
|
|
2702
|
+
|
|
2703
|
+
renderHiddenField(type, name, label, validate, attributes) {
|
|
2704
|
+
// Define valid attributes for the hidden input type
|
|
2705
|
+
const validAttributes = [
|
|
2706
|
+
'type',
|
|
2707
|
+
'name',
|
|
2708
|
+
'value',
|
|
2709
|
+
'id',
|
|
2710
|
+
'class',
|
|
2711
|
+
'style',
|
|
2712
|
+
'required',
|
|
2713
|
+
'readonly',
|
|
2714
|
+
'disabled',
|
|
2715
|
+
'tabindex',
|
|
2716
|
+
];
|
|
2717
|
+
|
|
2718
|
+
// Construct validation attributes
|
|
2719
|
+
let validationAttrs = '';
|
|
2720
|
+
if (validate) {
|
|
2721
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
2722
|
+
if (validAttributes.includes(key)) {
|
|
2723
|
+
if (typeof value === 'boolean' && value) {
|
|
2724
|
+
validationAttrs += ` ${key}\n`;
|
|
2725
|
+
} else {
|
|
2726
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2727
|
+
}
|
|
2728
|
+
} else {
|
|
2729
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2730
|
+
}
|
|
2731
|
+
});
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
// Handle the binding syntax
|
|
2735
|
+
let bindingDirective = '';
|
|
2736
|
+
if (attributes?.binding === 'bind:value' && name) {
|
|
2737
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2738
|
+
}
|
|
2739
|
+
if (attributes?.binding?.startsWith('::') && name) {
|
|
2740
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2741
|
+
}
|
|
2742
|
+
if (attributes?.binding && !name) {
|
|
2743
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2747
|
+
|
|
2748
|
+
// Get the id from attributes or fall back to name
|
|
2749
|
+
let id = attributes.id || name;
|
|
2750
|
+
|
|
2751
|
+
// Construct additional attributes dynamically
|
|
2752
|
+
let additionalAttrs = '';
|
|
2753
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
2754
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
2755
|
+
// Handle event attributes
|
|
2756
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
2757
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
2758
|
+
} else {
|
|
2759
|
+
// Handle boolean attributes
|
|
2760
|
+
if (value === true) {
|
|
2761
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
2762
|
+
} else if (value !== false) {
|
|
2763
|
+
// Convert underscores to hyphens and set the attribute
|
|
2764
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
let inputClass;
|
|
2771
|
+
if ('class' in attributes) {
|
|
2772
|
+
inputClass = attributes.class;
|
|
2773
|
+
} else {
|
|
2774
|
+
inputClass = this.inputClass;
|
|
2775
|
+
}
|
|
2776
|
+
// Construct the final HTML string
|
|
2777
|
+
let formHTML = `
|
|
2778
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
2779
|
+
<!--<label for="${id}">${label}
|
|
2780
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
2781
|
+
</label> -->
|
|
2782
|
+
<input
|
|
2783
|
+
type="${type}"
|
|
2784
|
+
name="${name}"
|
|
2785
|
+
${bindingDirective}
|
|
2786
|
+
id="${id}"
|
|
2787
|
+
class="${inputClass}"
|
|
2788
|
+
${additionalAttrs}
|
|
2789
|
+
${validationAttrs}
|
|
2790
|
+
/>
|
|
2791
|
+
</div>
|
|
2792
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
2793
|
+
|
|
2794
|
+
let formattedHtml = formHTML;
|
|
2795
|
+
|
|
2796
|
+
// Apply vertical layout to the <input> element only
|
|
2797
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
2798
|
+
// Reformat attributes into a vertical layout
|
|
2799
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
2800
|
+
return `<input\n${attributes}\n/>`;
|
|
2801
|
+
});
|
|
2802
|
+
|
|
2803
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
2804
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
2805
|
+
// Ensure <div> starts on a new line
|
|
2806
|
+
return `\n${match}\n`;
|
|
2807
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
2808
|
+
|
|
2809
|
+
//return formattedHtml;
|
|
2810
|
+
this.formMarkUp +=formattedHtml;
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2813
|
+
|
|
2814
|
+
/*
|
|
2815
|
+
renderImageField(type, name, label, validate, attributes) {
|
|
2816
|
+
// Define valid validation attributes for image upload
|
|
2817
|
+
const imageUploadValidationAttributes = [
|
|
2818
|
+
'accept',
|
|
2819
|
+
'required',
|
|
2820
|
+
'minwidth',
|
|
2821
|
+
'maxwidth',
|
|
2822
|
+
'minheight',
|
|
2823
|
+
'maxheight',
|
|
2824
|
+
];
|
|
2825
|
+
|
|
2826
|
+
// Construct validation attributes
|
|
2827
|
+
let validationAttrs = '';
|
|
2828
|
+
if (validate) {
|
|
2829
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
2830
|
+
if (imageUploadValidationAttributes.includes(key)) {
|
|
2831
|
+
if (key === 'accept') {
|
|
2832
|
+
validationAttrs += `accept="${value}"\n`;
|
|
2833
|
+
} else if (['required', 'minwidth', 'maxwidth', 'minheight', 'maxheight'].includes(key)) {
|
|
2834
|
+
validationAttrs += `${key}="${value}"\n`;
|
|
2835
|
+
} else {
|
|
2836
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2837
|
+
}
|
|
2838
|
+
} else {
|
|
2839
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2840
|
+
}
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
// Handle the binding syntax
|
|
2845
|
+
// Handle the binding syntax
|
|
2846
|
+
let bindingDirective = '';
|
|
2847
|
+
if (attributes?.binding === 'bind:value' && name) {
|
|
2848
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2849
|
+
}
|
|
2850
|
+
if (attributes?.binding?.startsWith('::') && name) {
|
|
2851
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2852
|
+
}
|
|
2853
|
+
if (attributes?.binding && !name) {
|
|
2854
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
2855
|
+
return;
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
// Get the id from attributes or fall back to name
|
|
2859
|
+
let id = attributes.id || name;
|
|
2860
|
+
|
|
2861
|
+
// Construct additional attributes dynamically
|
|
2862
|
+
let additionalAttrs = '';
|
|
2863
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
2864
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
2865
|
+
// Handle event attributes
|
|
2866
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
2867
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
2868
|
+
} else {
|
|
2869
|
+
// Handle boolean attributes
|
|
2870
|
+
if (value === true) {
|
|
2871
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
2872
|
+
} else if (value !== false) {
|
|
2873
|
+
// Convert underscores to hyphens and set the attribute
|
|
2874
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
let inputClass;
|
|
2881
|
+
if ('class' in attributes) {
|
|
2882
|
+
inputClass = attributes.class;
|
|
2883
|
+
} else {
|
|
2884
|
+
inputClass = this.inputClass;
|
|
2885
|
+
}
|
|
2886
|
+
// Construct the final HTML string
|
|
2887
|
+
let formHTML = `
|
|
2888
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
2889
|
+
<label for="${id}">${label}
|
|
2890
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
2891
|
+
</label>
|
|
2892
|
+
<input
|
|
2893
|
+
type="${type}"
|
|
2894
|
+
name="${name}"
|
|
2895
|
+
${bindingDirective}
|
|
2896
|
+
id="${id}"
|
|
2897
|
+
class="${inputClass}"
|
|
2898
|
+
${additionalAttrs}
|
|
2899
|
+
${validationAttrs}
|
|
2900
|
+
/>
|
|
2901
|
+
</div>
|
|
2902
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
2903
|
+
|
|
2904
|
+
let formattedHtml = formHTML;
|
|
2905
|
+
|
|
2906
|
+
// Apply vertical layout to the <input> element only
|
|
2907
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
2908
|
+
// Reformat attributes into a vertical layout
|
|
2909
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
2910
|
+
return `<input\n${attributes}\n/>`;
|
|
2911
|
+
});
|
|
2912
|
+
|
|
2913
|
+
// Ensure the <div> block starts on a new line and remove extra blank lines
|
|
2914
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
2915
|
+
// Ensure <div> starts on a new line
|
|
2916
|
+
return `\n${match}\n`;
|
|
2917
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
2918
|
+
|
|
2919
|
+
//return formattedHtml;
|
|
2920
|
+
this.formMarkUp +=formattedHtml;
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
*/
|
|
2924
|
+
|
|
2925
|
+
renderImageField(type, name, label, validate, attributes) {
|
|
2926
|
+
// Define valid validation attributes for image input
|
|
2927
|
+
const imageValidationAttributes = [
|
|
2928
|
+
'accept',
|
|
2929
|
+
'required',
|
|
2930
|
+
'minwidth',
|
|
2931
|
+
'maxwidth',
|
|
2932
|
+
'minheight',
|
|
2933
|
+
'maxheight',
|
|
2934
|
+
'src',
|
|
2935
|
+
'alt',
|
|
2936
|
+
'width',
|
|
2937
|
+
'height'
|
|
2938
|
+
];
|
|
2939
|
+
|
|
2940
|
+
// Construct validation attributes
|
|
2941
|
+
let validationAttrs = '';
|
|
2942
|
+
if (validate) {
|
|
2943
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
2944
|
+
if (imageValidationAttributes.includes(key)) {
|
|
2945
|
+
if (typeof value === 'boolean' && value) {
|
|
2946
|
+
validationAttrs += ` ${key}\n`;
|
|
2947
|
+
} else {
|
|
2948
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
2949
|
+
}
|
|
2950
|
+
} else {
|
|
2951
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
2952
|
+
}
|
|
2953
|
+
});
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
// Handle the binding syntax
|
|
2957
|
+
let bindingDirective = '';
|
|
2958
|
+
const bindingValue = attributes?.binding;
|
|
2959
|
+
if (bindingValue === 'bind:value' && name) {
|
|
2960
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2961
|
+
}
|
|
2962
|
+
if (typeof bindingValue === 'string' && bindingValue.startsWith('::') && name) {
|
|
2963
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
2964
|
+
}
|
|
2965
|
+
if (bindingValue && !name) {
|
|
2966
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
2967
|
+
return;
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
// Get the id from attributes or fall back to name
|
|
2971
|
+
let id = attributes.id || name;
|
|
2972
|
+
|
|
2973
|
+
// Construct additional attributes dynamically
|
|
2974
|
+
let additionalAttrs = '';
|
|
2975
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
2976
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) {
|
|
2977
|
+
if (key.startsWith('on')) {
|
|
2978
|
+
// Handle event attributes
|
|
2979
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
2980
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
2981
|
+
} else {
|
|
2982
|
+
// Handle boolean attributes
|
|
2983
|
+
if (value === true) {
|
|
2984
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
2985
|
+
} else if (value !== false) {
|
|
2986
|
+
// Convert underscores to hyphens and set the attribute
|
|
2987
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
// Special handling for image submit button
|
|
2994
|
+
let inputElement;
|
|
2995
|
+
if (type === 'image' && name === 'submit') {
|
|
2996
|
+
inputElement = `
|
|
2997
|
+
<input
|
|
2998
|
+
type="image"
|
|
2999
|
+
name="${name}"
|
|
3000
|
+
${bindingDirective}
|
|
3001
|
+
id="${id}"
|
|
3002
|
+
class="${attributes.class || this.inputClass}"
|
|
3003
|
+
src="${attributes.src || 'img_submit.gif'}"
|
|
3004
|
+
alt="${attributes.alt || 'Submit'}"
|
|
3005
|
+
width="${attributes.width || '48'}"
|
|
3006
|
+
height="${attributes.height || '48'}"
|
|
3007
|
+
${additionalAttrs}
|
|
3008
|
+
${validationAttrs}
|
|
3009
|
+
/>`;
|
|
3010
|
+
} else {
|
|
3011
|
+
// Regular image input field
|
|
3012
|
+
inputElement = `
|
|
3013
|
+
<input
|
|
3014
|
+
type="${type}"
|
|
3015
|
+
name="${name}"
|
|
3016
|
+
${bindingDirective}
|
|
3017
|
+
id="${id}"
|
|
3018
|
+
class="${attributes.class || this.inputClass}"
|
|
3019
|
+
${additionalAttrs}
|
|
3020
|
+
${validationAttrs}
|
|
3021
|
+
/>`;
|
|
3022
|
+
}
|
|
3023
|
+
|
|
3024
|
+
// Construct the final HTML string
|
|
3025
|
+
let formHTML = `
|
|
3026
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
3027
|
+
${type === 'image' && name === 'submit' ? '' : `<label for="${id}">${label}
|
|
3028
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
3029
|
+
</label>`}
|
|
3030
|
+
${inputElement}
|
|
3031
|
+
</div>
|
|
3032
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
3033
|
+
|
|
3034
|
+
// Format the HTML
|
|
3035
|
+
let formattedHtml = formHTML.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
3036
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
3037
|
+
return `<input\n${attributes}\n/>`;
|
|
3038
|
+
});
|
|
3039
|
+
|
|
3040
|
+
formattedHtml = formattedHtml.replace(/(<div\s+[^>]*>)/g, (match) => {
|
|
3041
|
+
return `\n${match}\n`;
|
|
3042
|
+
}).replace(/\n\s*\n/g, '\n');
|
|
3043
|
+
|
|
3044
|
+
return formattedHtml;
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
|
|
3048
|
+
|
|
3049
|
+
|
|
3050
|
+
// Textarea field rendering
|
|
3051
|
+
renderTextAreaField(type, name, label, validate, attributes) {
|
|
3052
|
+
const textAreaValidationAttributes = [
|
|
3053
|
+
'required',
|
|
3054
|
+
'minlength',
|
|
3055
|
+
'maxlength'
|
|
3056
|
+
];
|
|
3057
|
+
|
|
3058
|
+
// Construct validation attributes
|
|
3059
|
+
let validationAttrs = '';
|
|
3060
|
+
if (validate) {
|
|
3061
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
3062
|
+
if (textAreaValidationAttributes.includes(key)) {
|
|
3063
|
+
if (typeof value === 'boolean' && value) {
|
|
3064
|
+
validationAttrs += ` ${key}\n`;
|
|
3065
|
+
} else {
|
|
3066
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
3067
|
+
}
|
|
3068
|
+
} else {
|
|
3069
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
3070
|
+
}
|
|
3071
|
+
});
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
// Handle the binding syntax
|
|
3075
|
+
let bindingDirective = '';
|
|
3076
|
+
if (attributes.binding) {
|
|
3077
|
+
if (attributes.binding === 'bind:value' && name) {
|
|
3078
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
3079
|
+
}
|
|
3080
|
+
if (attributes.binding.startsWith('::') && name) {
|
|
3081
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
3082
|
+
}
|
|
3083
|
+
if (attributes.binding && !name) {
|
|
3084
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
3085
|
+
return;
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
// Get the id from attributes or fall back to name
|
|
3090
|
+
let id = attributes.id || name;
|
|
3091
|
+
|
|
3092
|
+
// Construct additional attributes dynamically
|
|
3093
|
+
let additionalAttrs = '';
|
|
3094
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
3095
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) {
|
|
3096
|
+
if (key.startsWith('on')) {
|
|
3097
|
+
const eventValue = value.endsWith('()') ? value : `${value}()`;
|
|
3098
|
+
additionalAttrs += ` ${key}="${eventValue}"\n`;
|
|
3099
|
+
} else {
|
|
3100
|
+
if (value === true) {
|
|
3101
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
3102
|
+
} else if (value !== false) {
|
|
3103
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
let inputClass = attributes.class || this.inputClass;
|
|
3110
|
+
|
|
3111
|
+
// Construct the final HTML string
|
|
3112
|
+
let formHTML = `
|
|
3113
|
+
<div class="${this.divClass}" id="${id + '-block'}">
|
|
3114
|
+
<label for="${id}">${label}
|
|
3115
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
3116
|
+
</label>
|
|
3117
|
+
<textarea
|
|
3118
|
+
name="${name}"
|
|
3119
|
+
${bindingDirective}
|
|
3120
|
+
id="${id}"
|
|
3121
|
+
class="${inputClass}"
|
|
3122
|
+
${additionalAttrs}
|
|
3123
|
+
${validationAttrs}
|
|
3124
|
+
${additionalAttrs.includes('placeholder') ? '' : (this.formSettings.placeholders ? `placeholder="${label}"` : '')}>
|
|
3125
|
+
</textarea>
|
|
3126
|
+
</div>
|
|
3127
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
3128
|
+
|
|
3129
|
+
let formattedHtml = formHTML;
|
|
3130
|
+
|
|
3131
|
+
// Apply vertical layout to the <textarea> element only
|
|
3132
|
+
formattedHtml = formattedHtml.replace(/<textarea\s+([^>]*)>\s*<\/textarea>/, (match, p1) => {
|
|
3133
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
3134
|
+
return `<textarea\n${attributes}\n></textarea>`;
|
|
3135
|
+
});
|
|
3136
|
+
|
|
3137
|
+
this.formMarkUp += formattedHtml;
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
|
|
3141
|
+
|
|
3142
|
+
renderRadioField(type, name, label, validate, attributes, options) {
|
|
3143
|
+
// Define valid validation attributes for radio fields
|
|
3144
|
+
const radioValidationAttributes = ['required'];
|
|
3145
|
+
|
|
3146
|
+
// Construct validation attributes
|
|
3147
|
+
let validationAttrs = '';
|
|
3148
|
+
if (validate) {
|
|
3149
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
3150
|
+
if (radioValidationAttributes.includes(key)) {
|
|
3151
|
+
if (typeof value === 'boolean' && value) {
|
|
3152
|
+
validationAttrs += ` ${key}\n`;
|
|
3153
|
+
} else {
|
|
3154
|
+
// Handle specific validation attributes
|
|
3155
|
+
switch (key) {
|
|
3156
|
+
case 'required':
|
|
3157
|
+
validationAttrs += ` ${key}\n`;
|
|
3158
|
+
break;
|
|
3159
|
+
default:
|
|
3160
|
+
if (!radioValidationAttributes.includes(key)) {
|
|
3161
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'radio'.\x1b[0m`);
|
|
3162
|
+
}
|
|
3163
|
+
break;
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
} else {
|
|
3167
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type 'radio'.\x1b[0m`);
|
|
3168
|
+
}
|
|
3169
|
+
});
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
// Handle the binding syntax
|
|
3173
|
+
let bindingDirective = '';
|
|
3174
|
+
if (attributes.binding) {
|
|
3175
|
+
if (attributes.binding === 'bind:value' && name) {
|
|
3176
|
+
bindingDirective = ` bind:value="${name}"\n`;
|
|
3177
|
+
} else if (attributes.binding.startsWith('::') && name) {
|
|
3178
|
+
bindingDirective = ` bind:value="${name}"\n`;
|
|
3179
|
+
} else if (attributes.binding && !name) {
|
|
3180
|
+
console.log(`\x1b[31m%s\x1b[0m`, `You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
3181
|
+
return;
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
// Define attributes for the radio inputs
|
|
3186
|
+
let id = attributes.id || name;
|
|
3187
|
+
|
|
3188
|
+
// Construct additional attributes dynamically
|
|
3189
|
+
let additionalAttrs = '';
|
|
3190
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
3191
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
3192
|
+
// Handle event attributes
|
|
3193
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
3194
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
3195
|
+
} else {
|
|
3196
|
+
// Handle boolean attributes
|
|
3197
|
+
if (value === true) {
|
|
3198
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
3199
|
+
} else if (value !== false) {
|
|
3200
|
+
// Convert underscores to hyphens and set the attribute
|
|
3201
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
|
|
3207
|
+
let inputClass = attributes.class || this.inputClass;
|
|
3208
|
+
|
|
3209
|
+
// Construct radio button HTML based on options
|
|
3210
|
+
let optionsHTML = '';
|
|
3211
|
+
if (options && options.length) {
|
|
3212
|
+
optionsHTML = options.map((option) => {
|
|
3213
|
+
return `
|
|
3214
|
+
<div>
|
|
3215
|
+
<input
|
|
3216
|
+
type="${type}"
|
|
3217
|
+
name="${name}"
|
|
3218
|
+
value="${option.value}"
|
|
3219
|
+
${bindingDirective}
|
|
3220
|
+
${additionalAttrs}
|
|
3221
|
+
${attributes.id ? `id="${id}-${option.value}"` : `id="${id}-${option.value}"`}
|
|
3222
|
+
class="${inputClass}"
|
|
3223
|
+
${validationAttrs}
|
|
3224
|
+
/>
|
|
3225
|
+
<label
|
|
3226
|
+
for="${attributes.id ? `${id}-${option.value}` : `${id}-${option.value}`}">
|
|
3227
|
+
${option.label}
|
|
3228
|
+
</label>
|
|
3229
|
+
</div>
|
|
3230
|
+
`;
|
|
3231
|
+
}).join('');
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
// Construct the final HTML string
|
|
3235
|
+
let formHTML = `
|
|
3236
|
+
<fieldset class="${this.radioGroupClass}" id="${id + '-block'}">
|
|
3237
|
+
<legend>
|
|
3238
|
+
${label}
|
|
3239
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
3240
|
+
</legend>
|
|
3241
|
+
${optionsHTML}
|
|
3242
|
+
</fieldset>
|
|
3243
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
3244
|
+
|
|
3245
|
+
// Apply vertical layout to the <input> elements only
|
|
3246
|
+
let formattedHtml = formHTML.replace(/<input\s+([^>]*)\/>/g, (match, p1) => {
|
|
3247
|
+
// Reformat attributes into a vertical layout
|
|
3248
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
3249
|
+
return `<input\n${attributes}\n/>`;
|
|
3250
|
+
});
|
|
3251
|
+
|
|
3252
|
+
// Ensure the <fieldset> block starts on a new line and remove extra blank lines
|
|
3253
|
+
formattedHtml = formattedHtml.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
|
|
3254
|
+
// Ensure <fieldset> starts on a new line
|
|
3255
|
+
return `\n${match}\n`;
|
|
3256
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
3257
|
+
|
|
3258
|
+
//return formattedHtml;
|
|
3259
|
+
this.formMarkUp +=formattedHtml;
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3262
|
+
|
|
3263
|
+
renderCheckboxField(type, name, label, validate, attributes, options) {
|
|
3264
|
+
// Define valid validation attributes for checkbox fields
|
|
3265
|
+
const checkboxValidationAttributes = ['required'];
|
|
3266
|
+
|
|
3267
|
+
// Construct validation attributes
|
|
3268
|
+
let validationAttrs = '';
|
|
3269
|
+
if (validate) {
|
|
3270
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
3271
|
+
if (checkboxValidationAttributes.includes(key)) {
|
|
3272
|
+
if (key === 'required') {
|
|
3273
|
+
validationAttrs += `${key}\n`;
|
|
3274
|
+
}
|
|
3275
|
+
} else {
|
|
3276
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
3277
|
+
}
|
|
3278
|
+
});
|
|
3279
|
+
}
|
|
3280
|
+
|
|
3281
|
+
// Handle the binding syntax
|
|
3282
|
+
let bindingDirective = '';
|
|
3283
|
+
if (attributes.binding) {
|
|
3284
|
+
if (attributes.binding === 'bind:checked') {
|
|
3285
|
+
bindingDirective = ` bind:checked="${name}"\n`;
|
|
3286
|
+
} else if (attributes.binding.startsWith('::')) {
|
|
3287
|
+
bindingDirective = ` bind:checked="${name}"\n`;
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
// Define attributes for the checkbox inputs
|
|
3292
|
+
let id = attributes.id || name;
|
|
3293
|
+
|
|
3294
|
+
// Handle additional attributes
|
|
3295
|
+
let additionalAttrs = '';
|
|
3296
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
3297
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
3298
|
+
// Handle event attributes
|
|
3299
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
3300
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
3301
|
+
} else {
|
|
3302
|
+
// Handle boolean attributes
|
|
3303
|
+
if (value === true) {
|
|
3304
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
3305
|
+
} else if (value !== false) {
|
|
3306
|
+
// Convert underscores to hyphens and set the attribute
|
|
3307
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
|
|
3313
|
+
|
|
3314
|
+
let inputClass;
|
|
3315
|
+
if ('class' in attributes) {
|
|
3316
|
+
inputClass = attributes.class;
|
|
3317
|
+
} else {
|
|
3318
|
+
inputClass = this.inputClass;
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3321
|
+
// Construct checkbox HTML based on options
|
|
3322
|
+
let optionsHTML = '';
|
|
3323
|
+
if (Array.isArray(options)) {
|
|
3324
|
+
optionsHTML = options.map((option) => {
|
|
3325
|
+
const optionId = `${id}-${option.value}`;
|
|
3326
|
+
return `
|
|
3327
|
+
<div>
|
|
3328
|
+
<input
|
|
3329
|
+
type="checkbox"
|
|
3330
|
+
name="${name}"
|
|
3331
|
+
value="${option.value}"${bindingDirective} ${additionalAttrs}
|
|
3332
|
+
${attributes.id ? `id="${optionId}"` : `id="${optionId}"`}
|
|
3333
|
+
class="${inputClass}"
|
|
3334
|
+
/>
|
|
3335
|
+
<label
|
|
3336
|
+
for="${optionId}">
|
|
3337
|
+
${option.label}
|
|
3338
|
+
</label>
|
|
3339
|
+
</div>
|
|
3340
|
+
`;
|
|
3341
|
+
}).join('');
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
// Construct the final HTML string
|
|
3345
|
+
let formHTML = `
|
|
3346
|
+
<fieldset class="${this.checkboxGroupClass}" id="${id + '-block'}">
|
|
3347
|
+
<legend>
|
|
3348
|
+
${label} ${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
3349
|
+
</legend>
|
|
3350
|
+
${optionsHTML}
|
|
3351
|
+
</fieldset>
|
|
3352
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
3353
|
+
|
|
3354
|
+
let formattedHtml = formHTML;
|
|
3355
|
+
|
|
3356
|
+
// Apply vertical layout to the <input> elements only
|
|
3357
|
+
formattedHtml = formattedHtml.replace(/<input\s+([^>]*)\/>/g, (match, p1) => {
|
|
3358
|
+
// Reformat attributes into a vertical layout
|
|
3359
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
3360
|
+
return `<input\n${attributes}\n/>`;
|
|
3361
|
+
});
|
|
3362
|
+
|
|
3363
|
+
// Ensure the <fieldset> block starts on a new line and remove extra blank lines
|
|
3364
|
+
formattedHtml = formattedHtml.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
|
|
3365
|
+
// Ensure <fieldset> starts on a new line
|
|
3366
|
+
return `\n${match}\n`;
|
|
3367
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
3368
|
+
|
|
3369
|
+
//return formattedHtml;
|
|
3370
|
+
this.formMarkUp +=formattedHtml;
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
|
|
3374
|
+
|
|
3375
|
+
/* DYNAMIC SINGLE SELECT BLOCK */
|
|
3376
|
+
|
|
3377
|
+
// Function to render the dynamic select field and update based on user selection
|
|
3378
|
+
renderDynamicSingleSelectField(type, name, label, validate, attributes, options) {
|
|
3379
|
+
|
|
3380
|
+
// Step 1: Transform the data into an array of objects
|
|
3381
|
+
const mainCategoryOptions = options.flat().map(item => {
|
|
3382
|
+
// Check if any option has selected: true
|
|
3383
|
+
const selected = item.options.some(option => option.selected === true);
|
|
3384
|
+
|
|
3385
|
+
// Create a transformed object
|
|
3386
|
+
return {
|
|
3387
|
+
value: item.id,
|
|
3388
|
+
label: item.label,
|
|
3389
|
+
...(selected && { selected: true }) // Conditionally add selected: true
|
|
3390
|
+
};
|
|
3391
|
+
});
|
|
3392
|
+
|
|
3393
|
+
const subCategoriesOptions=options;
|
|
3394
|
+
const mode='dynamicSingleSelect';
|
|
3395
|
+
this.renderSingleSelectField(type, name, label, validate, attributes, mainCategoryOptions, subCategoriesOptions, mode);
|
|
3396
|
+
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3399
|
+
|
|
3400
|
+
renderSingleSelectField(type, name, label, validate, attributes, options, subCategoriesOptions, mode) {
|
|
3401
|
+
|
|
3402
|
+
console.log("Within");
|
|
3403
|
+
// Define valid validation attributes for select fields
|
|
3404
|
+
const selectValidationAttributes = ['required'];
|
|
3405
|
+
|
|
3406
|
+
// Construct validation attributes
|
|
3407
|
+
let validationAttrs = '';
|
|
3408
|
+
if (validate) {
|
|
3409
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
3410
|
+
if (selectValidationAttributes.includes(key)) {
|
|
3411
|
+
if (key === 'required') {
|
|
3412
|
+
validationAttrs += `${key} `;
|
|
3413
|
+
}
|
|
3414
|
+
} else {
|
|
3415
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
3416
|
+
}
|
|
3417
|
+
});
|
|
3418
|
+
}
|
|
3419
|
+
|
|
3420
|
+
// Handle the binding syntax
|
|
3421
|
+
let bindingDirective = '';
|
|
3422
|
+
if (attributes.binding) {
|
|
3423
|
+
if (typeof attributes.binding === 'string' && attributes.binding.startsWith('::')) {
|
|
3424
|
+
bindingDirective = ` bind:value="${name}" `;
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
// Define attributes for the select field
|
|
3429
|
+
let id = attributes.id || name;
|
|
3430
|
+
let dimensionAttrs = ''; // No dimension attributes applicable for select fields
|
|
3431
|
+
|
|
3432
|
+
// Handle additional attributes
|
|
3433
|
+
let additionalAttrs = '';
|
|
3434
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
3435
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
3436
|
+
// Handle event attributes
|
|
3437
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
3438
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
3439
|
+
} else {
|
|
3440
|
+
// Handle boolean attributes
|
|
3441
|
+
if (value === true) {
|
|
3442
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
3443
|
+
} else if (value !== false) {
|
|
3444
|
+
// Convert underscores to hyphens and set the attribute
|
|
3445
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
|
|
3451
|
+
// Construct select options HTML based on options
|
|
3452
|
+
let selectHTML = '';
|
|
3453
|
+
if (Array.isArray(options)) {
|
|
3454
|
+
// Add a default option
|
|
3455
|
+
selectHTML += `
|
|
3456
|
+
<option value="">Choose an option</option>
|
|
3457
|
+
`;
|
|
3458
|
+
|
|
3459
|
+
// Add the provided options
|
|
3460
|
+
selectHTML += options.map((option) => {
|
|
3461
|
+
const isSelected = option.selected ? ' selected' : '';
|
|
3462
|
+
return `
|
|
3463
|
+
<option value="${option.value}"${isSelected}>${option.label}</option>
|
|
3464
|
+
`;
|
|
3465
|
+
}).join('');
|
|
3466
|
+
}
|
|
3467
|
+
|
|
3468
|
+
let inputClass = attributes.class || this.inputClass;
|
|
3469
|
+
|
|
3470
|
+
const onchangeAttr = (mode === 'dynamicSingleSelect' && subCategoriesOptions) ? ' onchange="handleDynamicSingleSelect(this.value,id)"' : '';
|
|
3471
|
+
|
|
3472
|
+
let labelDisplay;
|
|
3473
|
+
let rawLabel;
|
|
3474
|
+
|
|
3475
|
+
if (mode === 'dynamicSingleSelect' && subCategoriesOptions) {
|
|
3476
|
+
if (label.includes('-')) {
|
|
3477
|
+
const [mainCategoryLabel] = label.split('-');
|
|
3478
|
+
labelDisplay = mainCategoryLabel;
|
|
3479
|
+
rawLabel = label;
|
|
3480
|
+
} else {
|
|
3481
|
+
labelDisplay = label;
|
|
3482
|
+
rawLabel = label;
|
|
3483
|
+
}
|
|
3484
|
+
} else {
|
|
3485
|
+
labelDisplay = label;
|
|
3486
|
+
}
|
|
3487
|
+
|
|
3488
|
+
|
|
3489
|
+
// Construct the final HTML string
|
|
3490
|
+
let formHTML = `
|
|
3491
|
+
<fieldset class="${this.selectGroupClass}" id="${id + '-block'}">
|
|
3492
|
+
<legend>${labelDisplay}
|
|
3493
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
3494
|
+
</legend>
|
|
3495
|
+
<label for="${id}"> Select ${labelDisplay}
|
|
3496
|
+
<select name="${name}"
|
|
3497
|
+
${bindingDirective}
|
|
3498
|
+
${dimensionAttrs}
|
|
3499
|
+
id="${id}"
|
|
3500
|
+
class="${inputClass}"
|
|
3501
|
+
${additionalAttrs}
|
|
3502
|
+
${validationAttrs}
|
|
3503
|
+
${onchangeAttr}
|
|
3504
|
+
>
|
|
3505
|
+
${selectHTML}
|
|
3506
|
+
</select>
|
|
3507
|
+
</fieldset>
|
|
3508
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
3509
|
+
|
|
3510
|
+
|
|
3511
|
+
// Apply vertical layout to the <select> element and its children
|
|
3512
|
+
let formattedHtml = formHTML.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g, (match, p1, p2) => {
|
|
3513
|
+
// Reformat attributes into a vertical layout
|
|
3514
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
3515
|
+
return `<select\n${attributes}\n>\n${p2.trim()}\n</select>`;
|
|
3516
|
+
});
|
|
3517
|
+
|
|
3518
|
+
// Ensure the <fieldset> block starts on a new line and remove extra blank lines
|
|
3519
|
+
formattedHtml = formattedHtml.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
|
|
3520
|
+
// Ensure <fieldset> starts on a new line
|
|
3521
|
+
return `\n${match}\n`;
|
|
3522
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
3523
|
+
|
|
3524
|
+
//console.log(formattedHtml);
|
|
3525
|
+
this.formMarkUp+=formattedHtml;
|
|
3526
|
+
//return formattedHtml;
|
|
3527
|
+
|
|
3528
|
+
|
|
3529
|
+
/* dynamicSingleSelect */
|
|
3530
|
+
|
|
3531
|
+
if (mode && mode ==='dynamicSingleSelect' && subCategoriesOptions) {
|
|
3532
|
+
|
|
3533
|
+
|
|
3534
|
+
// Find the target div with id this.formContainerId
|
|
3535
|
+
const targetDiv = document.getElementById(this.formContainerId);
|
|
3536
|
+
|
|
3537
|
+
let categoryId = attributes.id || name;
|
|
3538
|
+
|
|
3539
|
+
|
|
3540
|
+
if (targetDiv) {
|
|
3541
|
+
// Create a script element
|
|
3542
|
+
const scriptElement = document.createElement('script');
|
|
3543
|
+
scriptElement.textContent = `
|
|
3544
|
+
window.handleDynamicSingleSelect = function(category, fieldsetid) {
|
|
3545
|
+
//console.log("HERE", fieldsetid);
|
|
3546
|
+
|
|
3547
|
+
// Hide all subcategory fields
|
|
3548
|
+
document.querySelectorAll(\`[class*="\${fieldsetid}"]\`).forEach(div => {
|
|
3549
|
+
div.style.display = "none";
|
|
3550
|
+
});
|
|
3551
|
+
|
|
3552
|
+
// Show the selected category
|
|
3553
|
+
const selectedCategoryFieldset = document.getElementById(category + '-options');
|
|
3554
|
+
if (selectedCategoryFieldset) {
|
|
3555
|
+
selectedCategoryFieldset.style.display = "block";
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
`;
|
|
3559
|
+
|
|
3560
|
+
// Append the script element to the target div
|
|
3561
|
+
targetDiv.appendChild(scriptElement);
|
|
3562
|
+
} else {
|
|
3563
|
+
console.error(`Target div with id "${this.formContainerId}" not found.`);
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
subCategoriesOptions.forEach(subCategory => {
|
|
3567
|
+
const { id, label, options } = subCategory;
|
|
3568
|
+
|
|
3569
|
+
// Build the select options HTML
|
|
3570
|
+
const selectHTML = options.map(option => {
|
|
3571
|
+
const isSelected = option.selected ? ' selected' : '';
|
|
3572
|
+
return `
|
|
3573
|
+
<option value="${option.value}"${isSelected}>${option.label}</option>
|
|
3574
|
+
`;
|
|
3575
|
+
}).join('');
|
|
3576
|
+
|
|
3577
|
+
|
|
3578
|
+
let subCategoryLabel;
|
|
3579
|
+
console.log('Label:', rawLabel); // Debug log
|
|
3580
|
+
|
|
3581
|
+
if (rawLabel.includes('-')) {
|
|
3582
|
+
subCategoryLabel = rawLabel.split('-')?.[1] + ' Options';
|
|
3583
|
+
} else {
|
|
3584
|
+
subCategoryLabel = 'options';
|
|
3585
|
+
}
|
|
3586
|
+
|
|
3587
|
+
let optionsLabel;
|
|
3588
|
+
if (subCategoryLabel !== 'options') {
|
|
3589
|
+
optionsLabel = rawLabel.split('-')?.[1] + ' Option';
|
|
3590
|
+
} else {
|
|
3591
|
+
optionsLabel = subCategoryLabel;
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
|
|
3595
|
+
// Create the HTML for the fieldset and select elements
|
|
3596
|
+
let formHTML = `
|
|
3597
|
+
<fieldset class="${this.selectGroupClass} ${categoryId}" id="${id}-options" style="display: none;">
|
|
3598
|
+
<legend> ${label} ${subCategoryLabel} ${this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
3599
|
+
</legend>
|
|
3600
|
+
<label for="${id}"> Select ${label} ${optionsLabel}
|
|
3601
|
+
</label>
|
|
3602
|
+
<select name="${id}"
|
|
3603
|
+
${bindingDirective}
|
|
3604
|
+
${dimensionAttrs}
|
|
3605
|
+
id="${id + '-block'}"
|
|
3606
|
+
class="${inputClass}"
|
|
3607
|
+
${additionalAttrs}
|
|
3608
|
+
${validationAttrs}
|
|
3609
|
+
>
|
|
3610
|
+
<option value="">Choose an option</option>
|
|
3611
|
+
${selectHTML}
|
|
3612
|
+
</select>
|
|
3613
|
+
</fieldset>
|
|
3614
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
3615
|
+
|
|
3616
|
+
// Apply vertical layout to the <select> element and its children
|
|
3617
|
+
formHTML = formHTML.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g, (match, p1, p2) => {
|
|
3618
|
+
// Reformat attributes into a vertical layout
|
|
3619
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
3620
|
+
return `<select\n${attributes}\n>\n${p2.trim()}\n</select>`;
|
|
3621
|
+
});
|
|
3622
|
+
|
|
3623
|
+
// Ensure the <fieldset> block starts on a new line and remove extra blank lines
|
|
3624
|
+
formHTML = formHTML.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
|
|
3625
|
+
// Ensure <fieldset> starts on a new line
|
|
3626
|
+
return `\n${match}\n`;
|
|
3627
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
3628
|
+
|
|
3629
|
+
// Append the generated HTML to formMarkUp
|
|
3630
|
+
this.formMarkUp += formHTML;
|
|
3631
|
+
|
|
3632
|
+
//return formHTML;
|
|
3633
|
+
});
|
|
3634
|
+
|
|
3635
|
+
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
|
|
3639
|
+
|
|
3640
|
+
|
|
3641
|
+
renderMultipleSelectField(type, name, label, validate, attributes, options) {
|
|
3642
|
+
// Define valid validation attributes for multiple select fields
|
|
3643
|
+
const selectValidationAttributes = ['required', 'minlength', 'maxlength'];
|
|
3644
|
+
|
|
3645
|
+
// Construct validation attributes
|
|
3646
|
+
let validationAttrs = '';
|
|
3647
|
+
if (validate) {
|
|
3648
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
3649
|
+
if (selectValidationAttributes.includes(key)) {
|
|
3650
|
+
if (key === 'required') {
|
|
3651
|
+
validationAttrs += `${key} `;
|
|
3652
|
+
} else if (key === 'minlength') {
|
|
3653
|
+
validationAttrs += `minlength="${value}" `;
|
|
3654
|
+
} else if (key === 'maxlength') {
|
|
3655
|
+
validationAttrs += `maxlength="${value}" `;
|
|
3656
|
+
}
|
|
3657
|
+
} else {
|
|
3658
|
+
console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
|
|
3659
|
+
}
|
|
3660
|
+
});
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
// Handle the binding syntax
|
|
3664
|
+
let bindingDirective = '';
|
|
3665
|
+
if (attributes.binding) {
|
|
3666
|
+
if (typeof attributes.binding === 'string' && attributes.binding.startsWith('::')) {
|
|
3667
|
+
bindingDirective = ` bind:value="${name}" `;
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3671
|
+
// Define attributes for the select field
|
|
3672
|
+
let id = attributes.id || name;
|
|
3673
|
+
let dimensionAttrs = ''; // No dimension attributes applicable for select fields
|
|
3674
|
+
|
|
3675
|
+
// Handle additional attributes
|
|
3676
|
+
let additionalAttrs = '';
|
|
3677
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
3678
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
3679
|
+
// Handle event attributes
|
|
3680
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
3681
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
3682
|
+
} else {
|
|
3683
|
+
// Handle boolean attributes
|
|
3684
|
+
if (value === true) {
|
|
3685
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
3686
|
+
} else if (value !== false) {
|
|
3687
|
+
// Convert underscores to hyphens and set the attribute
|
|
3688
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
|
|
3694
|
+
// Construct select options HTML based on options
|
|
3695
|
+
let selectHTML = '';
|
|
3696
|
+
if (Array.isArray(options)) {
|
|
3697
|
+
selectHTML = options.map((option) => {
|
|
3698
|
+
const isSelected = option.selected ? ' selected' : '';
|
|
3699
|
+
return `
|
|
3700
|
+
<option value="${option.value}"${isSelected}>${option.label}</option>
|
|
3701
|
+
`;
|
|
3702
|
+
}).join('');
|
|
3703
|
+
}
|
|
3704
|
+
|
|
3705
|
+
// Define multiple attribute for multi-select
|
|
3706
|
+
const multipleAttr = 'multiple';
|
|
3707
|
+
|
|
3708
|
+
let inputClass;
|
|
3709
|
+
if ('class' in attributes) {
|
|
3710
|
+
inputClass = attributes.class;
|
|
3711
|
+
} else {
|
|
3712
|
+
inputClass = this.inputClass;
|
|
3713
|
+
}
|
|
3714
|
+
// Construct the final HTML string
|
|
3715
|
+
let formHTML = `
|
|
3716
|
+
<fieldset class="${this.selectGroupClass}" id="${id + '-block'}">
|
|
3717
|
+
<label for="${id}">${label}
|
|
3718
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
3719
|
+
</label>
|
|
3720
|
+
<select name="${name}"
|
|
3721
|
+
${bindingDirective}
|
|
3722
|
+
${dimensionAttrs}
|
|
3723
|
+
id="${id}"
|
|
3724
|
+
class="${inputClass}"
|
|
3725
|
+
${additionalAttrs}
|
|
3726
|
+
${validationAttrs}
|
|
3727
|
+
${multipleAttr}
|
|
3728
|
+
>
|
|
3729
|
+
${selectHTML}
|
|
3730
|
+
</select>
|
|
3731
|
+
</fieldset>
|
|
3732
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
3733
|
+
|
|
3734
|
+
let formattedHtml = formHTML;
|
|
3735
|
+
|
|
3736
|
+
// Apply vertical layout to the <select> element and its children
|
|
3737
|
+
formattedHtml = formattedHtml.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g, (match, p1, p2) => {
|
|
3738
|
+
// Reformat attributes into a vertical layout
|
|
3739
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
3740
|
+
return `<select\n${attributes}\n>\n${p2.trim()}\n</select>`;
|
|
3741
|
+
});
|
|
3742
|
+
|
|
3743
|
+
// Ensure the <fieldset> block starts on a new line and remove extra blank lines
|
|
3744
|
+
formattedHtml = formattedHtml.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
|
|
3745
|
+
// Ensure <fieldset> starts on a new line
|
|
3746
|
+
return `\n${match}\n`;
|
|
3747
|
+
}).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
|
|
3748
|
+
|
|
3749
|
+
//return formattedHtml;
|
|
3750
|
+
this.formMarkUp +=formattedHtml;
|
|
3751
|
+
}
|
|
3752
|
+
|
|
3753
|
+
|
|
3754
|
+
renderRangeField(type, name, label, validate, attributes) {
|
|
3755
|
+
const rangeValidationAttributes = ['required', 'min', 'max', 'step'];
|
|
3756
|
+
|
|
3757
|
+
// Construct validation attributes
|
|
3758
|
+
let validationAttrs = '';
|
|
3759
|
+
if (validate) {
|
|
3760
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
3761
|
+
if (rangeValidationAttributes.includes(key)) {
|
|
3762
|
+
if (typeof value === 'boolean' && value) {
|
|
3763
|
+
validationAttrs += ` ${key}\n`;
|
|
3764
|
+
} else {
|
|
3765
|
+
validationAttrs += ` ${key}="${value}"\n`;
|
|
3766
|
+
}
|
|
3767
|
+
} else {
|
|
3768
|
+
console.warn(`Unsupported validation attribute '${key}' for field '${name}' of type 'range'.`);
|
|
3769
|
+
}
|
|
3770
|
+
});
|
|
3771
|
+
}
|
|
3772
|
+
|
|
3773
|
+
// Handle the binding syntax
|
|
3774
|
+
let bindingDirective = '';
|
|
3775
|
+
if (attributes.binding) {
|
|
3776
|
+
if (attributes.binding === 'bind:value' && name) {
|
|
3777
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
3778
|
+
} else if (attributes.binding.startsWith('::') && name) {
|
|
3779
|
+
bindingDirective = `bind:value="${name}"\n`;
|
|
3780
|
+
} else if (attributes.binding && !name) {
|
|
3781
|
+
console.log(`You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
3782
|
+
return;
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3786
|
+
// Get the id from attributes or fall back to name
|
|
3787
|
+
let id = attributes.id || name;
|
|
3788
|
+
|
|
3789
|
+
// Construct additional attributes dynamically
|
|
3790
|
+
let additionalAttrs = '';
|
|
3791
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
3792
|
+
if (key !== 'id' && key !== 'class' && value !== undefined) {
|
|
3793
|
+
if (key.startsWith('on')) {
|
|
3794
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
3795
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
|
|
3796
|
+
} else {
|
|
3797
|
+
if (value === true) {
|
|
3798
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
|
|
3799
|
+
} else if (value !== false) {
|
|
3800
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
// Handle class attribute
|
|
3807
|
+
let inputClass = attributes.class || this.inputClass;
|
|
3808
|
+
|
|
3809
|
+
// Construct the final HTML string
|
|
3810
|
+
let formHTML = `
|
|
3811
|
+
<div class="${this.divClass}" id="${id}-block">
|
|
3812
|
+
<label for="${id}">${label}
|
|
3813
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
3814
|
+
</label>
|
|
3815
|
+
<input
|
|
3816
|
+
type="${type}"
|
|
3817
|
+
name="${name}"
|
|
3818
|
+
${bindingDirective}
|
|
3819
|
+
id="${id}"
|
|
3820
|
+
class="${inputClass}"
|
|
3821
|
+
${additionalAttrs}
|
|
3822
|
+
${validationAttrs}
|
|
3823
|
+
${additionalAttrs.includes('placeholder') ? '' : (this.formSettings.placeholders ? `placeholder="${label}"` : '')}
|
|
3824
|
+
/>
|
|
3825
|
+
<span id="${id}-value">50</span> <!-- Displays the range value dynamically -->
|
|
3826
|
+
</div>
|
|
3827
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
3828
|
+
|
|
3829
|
+
// Apply vertical layout to the <input> element only
|
|
3830
|
+
formHTML = formHTML.replace(/<input\s+([^>]*)\/>/, (match, p1) => {
|
|
3831
|
+
const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
|
|
3832
|
+
return `<input\n${attributes}\n/>`;
|
|
3833
|
+
});
|
|
3834
|
+
|
|
3835
|
+
this.formMarkUp += formHTML;
|
|
3836
|
+
}
|
|
3837
|
+
|
|
3838
|
+
|
|
3839
|
+
|
|
3840
|
+
/*
|
|
3841
|
+
renderRangeField(type, name, label, validate, attributes) {
|
|
3842
|
+
const rangeValidationAttributes = ['required', 'min', 'max', 'step'];
|
|
3843
|
+
|
|
3844
|
+
// Validate required parameters
|
|
3845
|
+
if (!type || !name || !label) {
|
|
3846
|
+
throw new Error('Missing required parameters: type, name, or label.');
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3849
|
+
// Construct validation attributes
|
|
3850
|
+
let validationAttrs = '';
|
|
3851
|
+
if (validate) {
|
|
3852
|
+
Object.entries(validate).forEach(([key, value]) => {
|
|
3853
|
+
if (rangeValidationAttributes.includes(key)) {
|
|
3854
|
+
if (typeof value === 'boolean' && value) {
|
|
3855
|
+
validationAttrs += ` ${key}`;
|
|
3856
|
+
} else {
|
|
3857
|
+
validationAttrs += ` ${key}="${value}"`;
|
|
3858
|
+
}
|
|
3859
|
+
} else {
|
|
3860
|
+
console.warn(`Unsupported validation attribute '${key}' for field '${name}' of type 'range'.`);
|
|
3861
|
+
}
|
|
3862
|
+
});
|
|
3863
|
+
}
|
|
3864
|
+
|
|
3865
|
+
// Handle the binding syntax
|
|
3866
|
+
let bindingDirective = '';
|
|
3867
|
+
if (attributes.binding) {
|
|
3868
|
+
if (attributes.binding === 'bind:value' && name) {
|
|
3869
|
+
bindingDirective = `bind:value="${name}"`;
|
|
3870
|
+
} else if (attributes.binding.startsWith('::') && name) {
|
|
3871
|
+
bindingDirective = `bind:value="${name}"`;
|
|
3872
|
+
} else if (attributes.binding && !name) {
|
|
3873
|
+
console.error(`You cannot set binding value when there is no name attribute defined in ${name} ${type} field.`);
|
|
3874
|
+
return;
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
// Get the id from attributes or fall back to name
|
|
3879
|
+
let id = attributes.id || name;
|
|
3880
|
+
|
|
3881
|
+
// Construct additional attributes dynamically
|
|
3882
|
+
let additionalAttrs = '';
|
|
3883
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
3884
|
+
if (key !== 'id' && key !== 'class' && value !== undefined) {
|
|
3885
|
+
if (key.startsWith('on')) {
|
|
3886
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
3887
|
+
additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}`;
|
|
3888
|
+
} else {
|
|
3889
|
+
if (value === true) {
|
|
3890
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}`;
|
|
3891
|
+
} else if (value !== false) {
|
|
3892
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"`;
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
|
|
3898
|
+
// Handle class attribute
|
|
3899
|
+
let inputClass = attributes.class || this.inputClass;
|
|
3900
|
+
|
|
3901
|
+
// Construct the final HTML string
|
|
3902
|
+
let formHTML = `
|
|
3903
|
+
<div class="${this.divClass}" id="${id}-block">
|
|
3904
|
+
<label for="${id}">${label}
|
|
3905
|
+
${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
|
|
3906
|
+
</label>
|
|
3907
|
+
<input
|
|
3908
|
+
type="${type}"
|
|
3909
|
+
name="${name}"
|
|
3910
|
+
${bindingDirective}
|
|
3911
|
+
id="${id}"
|
|
3912
|
+
class="${inputClass}"
|
|
3913
|
+
${additionalAttrs}
|
|
3914
|
+
${validationAttrs}
|
|
3915
|
+
${this.formSettings.placeholders ? `placeholder="${label}"` : ''}
|
|
3916
|
+
/>
|
|
3917
|
+
<span id="${id}-value">50</span> <!-- Displays the range value dynamically -->
|
|
3918
|
+
</div>
|
|
3919
|
+
`;
|
|
3920
|
+
|
|
3921
|
+
this.formMarkUp += formHTML;
|
|
3922
|
+
}
|
|
3923
|
+
|
|
3924
|
+
*/
|
|
3925
|
+
|
|
3926
|
+
|
|
3927
|
+
/* DYNAMIC SINGLE SELECT BLOCK */
|
|
3928
|
+
|
|
3929
|
+
|
|
3930
|
+
|
|
3931
|
+
|
|
3932
|
+
|
|
3933
|
+
|
|
3934
|
+
/* END DYNAMIC SINGLE SELECT BLOCK */
|
|
3935
|
+
|
|
3936
|
+
|
|
3937
|
+
|
|
3938
|
+
renderSubmitButton(type, name, label, validate, attributes) {
|
|
3939
|
+
// Define id attribute or fallback to name
|
|
3940
|
+
const id = attributes.id || name;
|
|
3941
|
+
|
|
3942
|
+
// Handle additional attributes§
|
|
3943
|
+
let additionalAttrs = '';
|
|
3944
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
3945
|
+
if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
|
|
3946
|
+
// Handle event attributes
|
|
3947
|
+
const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
|
|
3948
|
+
additionalAttrs += ` ${key}="${eventValue}"`;
|
|
3949
|
+
} else {
|
|
3950
|
+
// Handle boolean attributes
|
|
3951
|
+
if (value === true) {
|
|
3952
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}`;
|
|
3953
|
+
} else if (value !== false) {
|
|
3954
|
+
// Convert underscores to hyphens and set the attribute
|
|
3955
|
+
additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"`;
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
|
|
3961
|
+
let submitButtonClass;
|
|
3962
|
+
if ('class' in attributes) {
|
|
3963
|
+
submitButtonClass=attributes.class;
|
|
3964
|
+
} else {
|
|
3965
|
+
submitButtonClass=this.submitButtonClass;
|
|
3966
|
+
}
|
|
3967
|
+
|
|
3968
|
+
|
|
3969
|
+
const spinner = `<div id="formiqueSpinner" style="display: flex; align-items: center; gap: 1rem; font-family: sans-serif; display:none;">
|
|
3970
|
+
<div class="formique-spinner"></div>
|
|
3971
|
+
<p class="message">Hang in tight, we are submitting your details…</p>
|
|
3972
|
+
</div>
|
|
3973
|
+
`;
|
|
3974
|
+
// Construct the final HTML string
|
|
3975
|
+
|
|
3976
|
+
const formHTML = `
|
|
3977
|
+
${spinner}
|
|
3978
|
+
<input type="${type}"
|
|
3979
|
+
id="${id + '-block'}"
|
|
3980
|
+
class="${submitButtonClass}"
|
|
3981
|
+
value="${label}"
|
|
3982
|
+
${additionalAttrs}
|
|
3983
|
+
/>
|
|
3984
|
+
`.replace(/^\s*\n/gm, '').trim();
|
|
3985
|
+
|
|
3986
|
+
let formattedHtml = formHTML;
|
|
3987
|
+
|
|
3988
|
+
//return formattedHtml;
|
|
3989
|
+
this.formMarkUp +=formattedHtml;
|
|
3990
|
+
}
|
|
3991
|
+
|
|
3992
|
+
|
|
3993
|
+
|
|
3994
|
+
|
|
3995
|
+
|
|
3996
|
+
renderFormHTML() {
|
|
3997
|
+
this.formMarkUp += '</form>';
|
|
3998
|
+
const formContainer = document.getElementById(this.formContainerId);
|
|
3999
|
+
if (!formContainer) {
|
|
4000
|
+
console.error(`Error: form container with ID ${this.formContainerId} not found. Please ensure an element with id ${this.formContainerId} exists in the HTML.`);
|
|
4001
|
+
} else {
|
|
4002
|
+
formContainer.innerHTML = this.formMarkUp;
|
|
4003
|
+
}
|
|
4004
|
+
|
|
4005
|
+
|
|
4006
|
+
//return this.formMarkUp;
|
|
4007
|
+
|
|
4008
|
+
|
|
4009
|
+
}
|
|
4010
|
+
|
|
4011
|
+
|
|
4012
|
+
|
|
4013
|
+
|
|
4014
|
+
// no renderMethod below here
|
|
4015
|
+
}
|
|
4016
|
+
|
|
4017
|
+
|
|
4018
|
+
|
|
4019
|
+
export default Formique;
|
|
4020
|
+
|
|
4021
|
+
|
|
4022
|
+
|
|
4023
|
+
|
|
4024
|
+
|
|
4025
|
+
|
|
4026
|
+
|
|
4027
|
+
|
|
4028
|
+
|
|
4029
|
+
|
|
4030
|
+
|