@formique/semantq 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/formique-semantq.js +289 -182
  2. package/package.json +1 -1
@@ -60,6 +60,23 @@ class Formique extends FormBuilder {
60
60
  asteriskHtml: '<span aria-hidden="true" style="color: red;">*</span>',
61
61
  ...formSettings
62
62
  };
63
+
64
+ this.themeColor = formSettings.themeColor || null;
65
+
66
+ this.themeColorMap = {
67
+ 'primary': {
68
+ '--formique-base-bg': '#ffffff',
69
+ '--formique-base-text': '#333333',
70
+ '--formique-base-shadow': '0 10px 30px rgba(0, 0, 0, 0.1)',
71
+ '--formique-base-label': '#555555',
72
+ '--formique-input-border': '#dddddd',
73
+ '--formique-focus-color': null, // Will be set to themeColor
74
+ '--formique-btn-bg': null, // Will be set to themeColor
75
+ '--formique-btn-text': '#ffffff',
76
+ '--formique-btn-shadow': null // Will be calculated from themeColor
77
+ }
78
+ };
79
+
63
80
  this.divClass = 'input-block';
64
81
  this.inputClass = 'form-input';
65
82
  this.radioGroupClass = 'radio-group';
@@ -83,7 +100,8 @@ class Formique extends FormBuilder {
83
100
  this.formiqueEndpoint = "https://formiqueapi.onrender.com/api/send-email";
84
101
 
85
102
  // DISABLE DOM LISTENER
86
- //document.addEventListener('DOMContentLoaded', () => {
103
+
104
+ // document.addEventListener('DOMContentLoaded', () => {
87
105
  // 1. Build the form's HTML in memory
88
106
  this.formMarkUp += this.renderFormElement(); // Adds opening <form> tag and any hidden inputs
89
107
 
@@ -156,17 +174,20 @@ class Formique extends FormBuilder {
156
174
  // Initialize dependency graph and observers after the form is rendered
157
175
  this.initDependencyGraph();
158
176
  this.registerObservers();
177
+ this.attachDynamicSelectListeners();
159
178
 
160
179
  // Apply theme
161
- if (this.formSettings.theme && this.themes.includes(this.formSettings.theme)) {
180
+ if (this.themeColor) {
181
+ this.applyCustomTheme(this.themeColor, this.formContainerId); // <--- NEW: Apply custom theme
182
+ } else if (this.formSettings.theme && this.themes.includes(this.formSettings.theme)) {
162
183
  let theme = this.formSettings.theme;
163
184
  this.applyTheme(theme, this.formContainerId);
164
185
  } else {
165
- this.applyTheme('dark', this.formContainerId);
186
+ // Fallback if no themeColor and no valid theme specified
187
+ this.applyTheme('dark', this.formContainerId); // Default to 'dark'
166
188
  }
167
-
168
-
169
- //DISABLE DOM LISTNER
189
+
190
+ // DISABLE DOM LISTENER
170
191
  //}); // DOM LISTENER WRAPPER
171
192
 
172
193
  // CONSTRUCTOR WRAPPER FOR FORMIQUE CLASS
@@ -180,69 +201,69 @@ generateFormId() {
180
201
 
181
202
 
182
203
  initDependencyGraph() {
183
- this.dependencyGraph = {};
204
+ this.dependencyGraph = {};
205
+
206
+ this.formSchema.forEach((field) => {
207
+ const [type, name, label, validate, attributes = {}] = field;
208
+ const fieldId = attributes.id || name;
209
+
210
+ if (attributes.dependents) {
211
+ // Initialize dependency array for the parent field
212
+ this.dependencyGraph[fieldId] = attributes.dependents.map((dependentName) => {
213
+ const dependentField = this.formSchema.find(
214
+ ([, depName]) => depName === dependentName
215
+ );
216
+
217
+ if (dependentField) {
218
+ const dependentAttributes = dependentField[4] || {};
219
+ const dependentFieldId = dependentAttributes.id || dependentName; // Get dependent field ID
220
+
221
+ return {
222
+ dependent: dependentFieldId,
223
+ condition: dependentAttributes.condition || null,
224
+ };
225
+ } else {
226
+ console.warn(`Dependent field "${dependentName}" not found in schema.`);
227
+ }
228
+ });
184
229
 
185
- this.formSchema.forEach((field) => {
186
- const [type, name, label, validate, attributes = {}] = field;
187
- const fieldId = attributes.id || name;
230
+ // Add state tracking for the parent field
231
+ this.dependencyGraph[fieldId].push({ state: null });
188
232
 
189
- if (attributes.dependents) {
190
- // Initialize dependency array for the parent field
191
- this.dependencyGraph[fieldId] = attributes.dependents.map((dependentName) => {
192
- const dependentField = this.formSchema.find(
193
- ([, depName]) => depName === dependentName
194
- );
195
-
196
- if (dependentField) {
197
- const dependentAttributes = dependentField[4] || {};
198
- const dependentFieldId = dependentAttributes.id || dependentName; // Get dependent field ID
199
-
200
- return {
201
- dependent: dependentFieldId,
202
- condition: dependentAttributes.condition || null,
203
- };
204
- } else {
205
- console.warn(`Dependent field "${dependentName}" not found in schema.`);
233
+ // Attach the input change event listener to the parent field
234
+ this.attachInputChangeListener(fieldId);
206
235
  }
207
- });
208
-
209
- // Add state tracking for the parent field
210
- this.dependencyGraph[fieldId].push({ state: null });
211
-
212
- // console.log("Graph", this.dependencyGraph[fieldId]);
213
-
214
- // Attach the input change event listener to the parent field
215
- this.attachInputChangeListener(fieldId);
216
- }
217
-
218
- // Hide dependent fields initially
219
- if (attributes.dependents) {
220
-
221
- attributes.dependents.forEach((dependentName) => {
222
- const dependentField = this.formSchema.find(
223
- ([, depName]) => depName === dependentName
224
- );
225
- const dependentAttributes = dependentField ? dependentField[4] || {} : {};
226
- const dependentFieldId = dependentAttributes.id || dependentName;
227
236
 
228
- //alert(dependentFieldId);
229
-
230
- const inputBlock = document.querySelector(`#${dependentFieldId}-block`);
231
- //alert(inputBlock);
232
-
233
-
234
- if (inputBlock) {
235
- // alert(dependentName);
236
- inputBlock.style.display = 'none'; // Hide dependent field by default
237
+ // Hide dependent fields initially and set their required state
238
+ if (attributes.dependents) {
239
+ attributes.dependents.forEach((dependentName) => {
240
+ const dependentField = this.formSchema.find(
241
+ ([, depName]) => depName === dependentName
242
+ );
243
+ const dependentAttributes = dependentField ? dependentField[4] || {} : {};
244
+ const dependentFieldId = dependentAttributes.id || dependentName;
245
+
246
+ const inputBlock = document.querySelector(`#${dependentFieldId}-block`);
247
+
248
+ if (inputBlock) {
249
+ inputBlock.style.display = 'none'; // Hide dependent field by default
250
+ // Save original required state and set to false
251
+ const inputs = inputBlock.querySelectorAll('input, select, textarea');
252
+ inputs.forEach((input) => {
253
+ // Check if the input was originally required in the schema
254
+ if (input.hasAttribute('required') && input.required === true) {
255
+ input.setAttribute('data-original-required', 'true'); // Save original required state
256
+ input.required = false; // Remove required attribute when hiding
257
+ } else {
258
+ input.setAttribute('data-original-required', 'false'); // Explicitly mark as not originally required
259
+ }
260
+ });
261
+ }
262
+ });
237
263
  }
238
- });
239
- }
240
- });
241
-
242
- // console.log("Dependency Graph:", this.dependencyGraph);
264
+ });
243
265
  }
244
266
 
245
-
246
267
  // Attach Event Listeners
247
268
  attachInputChangeListener(parentField) {
248
269
  const fieldElement = document.getElementById(parentField);
@@ -344,6 +365,60 @@ registerObservers() {
344
365
  }
345
366
 
346
367
 
368
+ // --- NEW METHOD FOR DYNAMIC SELECT LISTENERS ---
369
+ attachDynamicSelectListeners() {
370
+ this.formSchema.forEach(field => {
371
+ const [type, name, label, validate, attributes = {}] = field;
372
+
373
+ if (type === 'dynamicSingleSelect') {
374
+ const mainSelectId = attributes.id || name;
375
+ const mainSelectElement = document.getElementById(mainSelectId);
376
+
377
+ if (mainSelectElement) {
378
+ mainSelectElement.addEventListener('change', (event) => {
379
+ const selectedCategory = event.target.value; // e.g., 'frontend', 'backend', 'server'
380
+
381
+ // Find all sub-category fieldsets related to this main select
382
+ const subCategoryFieldsets = document.querySelectorAll(`.${mainSelectId}-subcategory-group`);
383
+
384
+ subCategoryFieldsets.forEach(fieldset => {
385
+ const subSelect = fieldset.querySelector('select'); // Get the actual select element
386
+ if (subSelect) {
387
+ // Save original required state (if it was true) then set to false if hidden
388
+ subSelect.setAttribute('data-original-required', subSelect.required.toString());
389
+ subSelect.required = false; // Always set to false when hiding
390
+ }
391
+ fieldset.style.display = 'none'; // Hide all sub-category fieldsets initially
392
+ });
393
+
394
+ // Show the selected sub-category fieldset and manage its required state
395
+ const selectedFieldsetId = selectedCategory + '-options'; // Matches the ID format in renderSingleSelectField
396
+ const selectedFieldset = document.getElementById(selectedFieldsetId);
397
+
398
+ if (selectedFieldset) {
399
+ selectedFieldset.style.display = 'block'; // Show the selected one
400
+ const selectedSubSelect = selectedFieldset.querySelector('select');
401
+ if (selectedSubSelect) {
402
+ // Restore original required state for the visible select
403
+ selectedSubSelect.required = selectedSubSelect.getAttribute('data-original-required') === 'true';
404
+ }
405
+ }
406
+ });
407
+
408
+ // IMPORTANT: Trigger the change listener once on load if a default option is selected
409
+ // This ensures correct initial visibility and required states if there's a pre-selected main category.
410
+ // We do this by dispatching a 'change' event programmatically if the select has a value.
411
+ if (mainSelectElement.value) {
412
+ const event = new Event('change');
413
+ mainSelectElement.dispatchEvent(event);
414
+ }
415
+ } else {
416
+ console.warn(`Main dynamic select element with ID ${mainSelectId} not found.`);
417
+ }
418
+ }
419
+ });
420
+ }
421
+
347
422
  applyTheme(theme, formContainerId) {
348
423
  //const stylesheet = document.querySelector('link[formique-style]');
349
424
 
@@ -398,6 +473,67 @@ applyTheme(theme, formContainerId) {
398
473
  }
399
474
 
400
475
 
476
+
477
+ // New method to apply a custom theme based on a color
478
+ applyCustomTheme(color, formContainerId) {
479
+ const formContainer = document.getElementById(formContainerId);
480
+
481
+ if (!formContainer) {
482
+ console.error(`Form container with ID "${formContainerId}" not found. Cannot apply custom theme.`);
483
+ return;
484
+ }
485
+
486
+ // You can add 'formique' class here as well if not already added
487
+ formContainer.classList.add('formique');
488
+
489
+ // Generate a slightly darker shade for the button shadow if needed
490
+ // This is a simplified example; for robust color manipulation, consider a library
491
+ const darkenColor = (hex, percent) => {
492
+ const f = parseInt(hex.slice(1), 16);
493
+ const t = percent < 0 ? 0 : 255;
494
+ const p = percent < 0 ? percent * -1 : percent;
495
+ const R = f >> 16;
496
+ const G = (f >> 8) & 0x00FF;
497
+ const B = f & 0x0000FF;
498
+ return "#" + (0x1000000 + (Math.round((t - R) * p) + R) * 0x10000 + (Math.round((t - G) * p) + G) * 0x100 + (Math.round((t - B) * p) + B)).toString(16).slice(1);
499
+ };
500
+
501
+ const shadowColor = darkenColor(color, 0.2); // Darken the theme color by 20% for shadow
502
+
503
+ // Define the custom CSS variables, prioritizing the provided color
504
+ const customCssVars = {
505
+ '--formique-base-bg': '#ffffff', // Light theme base background
506
+ '--formique-base-text': '#333333', // Light theme base text
507
+ '--formique-base-shadow': '0 10px 30px rgba(0, 0, 0, 0.1)', // Light theme shadow
508
+ '--formique-base-label': '#555555', // Light theme label
509
+ '--formique-input-border': '#dddddd', // Light theme input border
510
+ '--formique-focus-color': color, // Set to the provided custom color
511
+ '--formique-btn-bg': color, // Set to the provided custom color
512
+ '--formique-btn-text': '#ffffff', // White text for buttons
513
+ '--formique-btn-shadow': `0 2px 10px ${shadowColor || 'rgba(0, 0, 0, 0.1)'}` // Dynamic button shadow
514
+ };
515
+
516
+ let styleContent = '';
517
+ for (const [prop, val] of Object.entries(customCssVars)) {
518
+ styleContent += ` ${prop}: ${val};\n`;
519
+ }
520
+
521
+ // Create a <style> tag for the custom theme
522
+ const styleElement = document.createElement('style');
523
+ styleElement.textContent = `
524
+ #${formContainerId}.formique {
525
+ ${styleContent}
526
+ }
527
+ `;
528
+
529
+ // Insert the style element into the head or before the form container
530
+ formContainer.parentNode.insertBefore(styleElement, formContainer);
531
+
532
+ console.log(`Applied custom theme with color: ${color} to form container: ${formContainerId}`);
533
+ }
534
+
535
+
536
+
401
537
  // renderFormElement method
402
538
  renderFormElement() {
403
539
  let formHTML = '<form';
@@ -3402,17 +3538,20 @@ this.renderSingleSelectField(type, name, label, validate, attributes, mainCatego
3402
3538
 
3403
3539
  renderSingleSelectField(type, name, label, validate, attributes, options, subCategoriesOptions, mode) {
3404
3540
 
3405
- console.log("Within");
3541
+ console.log("Within renderSingleSelectField");
3406
3542
  // Define valid validation attributes for select fields
3407
3543
  const selectValidationAttributes = ['required'];
3408
3544
 
3409
3545
  // Construct validation attributes
3410
3546
  let validationAttrs = '';
3547
+ // Store original required state for the main select
3548
+ let originalRequired = false; // <--- This variable tracks if the main select was originally required
3411
3549
  if (validate) {
3412
3550
  Object.entries(validate).forEach(([key, value]) => {
3413
3551
  if (selectValidationAttributes.includes(key)) {
3414
3552
  if (key === 'required') {
3415
3553
  validationAttrs += `${key} `;
3554
+ originalRequired = true; // Mark that it was originally required
3416
3555
  }
3417
3556
  } else {
3418
3557
  console.warn(`\x1b[31mUnsupported validation attribute '${key}' for field '${name}' of type '${type}'.\x1b[0m`);
@@ -3423,10 +3562,10 @@ console.log("Within");
3423
3562
  // Handle the binding syntax
3424
3563
  let bindingDirective = '';
3425
3564
  if (attributes.binding) {
3426
- if (typeof attributes.binding === 'string' && attributes.binding.startsWith('::')) {
3427
- bindingDirective = ` bind:value="${name}" `;
3565
+ if (typeof attributes.binding === 'string' && attributes.binding.startsWith('::')) {
3566
+ bindingDirective = ` bind:value="${name}" `;
3567
+ }
3428
3568
  }
3429
- }
3430
3569
 
3431
3570
  // Define attributes for the select field
3432
3571
  let id = attributes.id || name;
@@ -3435,7 +3574,8 @@ console.log("Within");
3435
3574
  // Handle additional attributes
3436
3575
  let additionalAttrs = '';
3437
3576
  for (const [key, value] of Object.entries(attributes)) {
3438
- if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) { if (key.startsWith('on')) {
3577
+ if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) {
3578
+ if (key.startsWith('on')) {
3439
3579
  // Handle event attributes
3440
3580
  const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
3441
3581
  additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
@@ -3470,32 +3610,33 @@ console.log("Within");
3470
3610
 
3471
3611
  let inputClass = attributes.class || this.inputClass;
3472
3612
 
3473
- const onchangeAttr = (mode === 'dynamicSingleSelect' && subCategoriesOptions) ? ' onchange="handleDynamicSingleSelect(this.value,id)"' : '';
3474
-
3613
+ // Remove `onchange` from HTML; it will be handled by JavaScript event listeners
3614
+ const onchangeAttr = ''; // <--- Ensure this is an empty string
3615
+
3475
3616
  let labelDisplay;
3476
- let rawLabel;
3617
+ let rawLabel;
3477
3618
 
3478
3619
  if (mode === 'dynamicSingleSelect' && subCategoriesOptions) {
3479
- if (label.includes('-')) {
3480
- const [mainCategoryLabel] = label.split('-');
3481
- labelDisplay = mainCategoryLabel;
3482
- rawLabel = label;
3483
- } else {
3484
- labelDisplay = label;
3485
- rawLabel = label;
3486
- }
3620
+ if (label.includes('-')) {
3621
+ const [mainCategoryLabel] = label.split('-');
3622
+ labelDisplay = mainCategoryLabel;
3623
+ rawLabel = label;
3624
+ } else {
3625
+ labelDisplay = label;
3626
+ rawLabel = label;
3627
+ }
3487
3628
  } else {
3488
- labelDisplay = label;
3629
+ labelDisplay = label;
3489
3630
  }
3490
3631
 
3491
3632
 
3492
- // Construct the final HTML string
3633
+ // Construct the final HTML string for the main select
3493
3634
  let formHTML = `
3494
3635
  <fieldset class="${this.selectGroupClass}" id="${id + '-block'}">
3495
- <legend>${labelDisplay}
3636
+ <legend>${labelDisplay}
3496
3637
  ${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
3497
3638
  </legend>
3498
- <label for="${id}"> Select ${labelDisplay}
3639
+ <label for="${id}"> Select ${labelDisplay}
3499
3640
  <select name="${name}"
3500
3641
  ${bindingDirective}
3501
3642
  ${dimensionAttrs}
@@ -3503,8 +3644,7 @@ console.log("Within");
3503
3644
  class="${inputClass}"
3504
3645
  ${additionalAttrs}
3505
3646
  ${validationAttrs}
3506
- ${onchangeAttr}
3507
- >
3647
+ data-original-required="${originalRequired}" >
3508
3648
  ${selectHTML}
3509
3649
  </select>
3510
3650
  </fieldset>
@@ -3524,123 +3664,90 @@ console.log("Within");
3524
3664
  return `\n${match}\n`;
3525
3665
  }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
3526
3666
 
3527
- //console.log(formattedHtml);
3528
3667
  this.formMarkUp+=formattedHtml;
3529
- //return formattedHtml;
3530
-
3531
3668
 
3532
- /* dynamicSingleSelect */
3533
3669
 
3534
- if (mode && mode ==='dynamicSingleSelect' && subCategoriesOptions) {
3670
+ /* dynamicSingleSelect - Sub-Category Generation Block */
3535
3671
 
3672
+ if (mode && mode ==='dynamicSingleSelect' && subCategoriesOptions) {
3536
3673
 
3537
- // Find the target div with id this.formContainerId
3538
- const targetDiv = document.getElementById(this.formContainerId);
3674
+ const categoryId = attributes.id || name; // This is the ID of the main dynamic select ('languages')
3539
3675
 
3540
- let categoryId = attributes.id || name;
3676
+ subCategoriesOptions.forEach(subCategory => {
3677
+ const { id, label, options: subOptions } = subCategory; // Renamed 'options' to 'subOptions' to avoid conflict
3541
3678
 
3679
+ // IMPORTANT: Sub-category selects are *initially hidden*
3680
+ // Therefore, by default, they are NOT required until they are revealed.
3681
+ // If your schema later allows specific sub-categories to be inherently required
3682
+ // when shown, you'd need to extract that validation from your schema here.
3683
+ // For now, they are considered non-required until JavaScript makes them required.
3684
+ let isSubCategoryRequired = false; // Default to false as they are hidden
3685
+ const subCategoryValidationAttrs = ''; // No direct 'required' in HTML initially
3542
3686
 
3543
- if (targetDiv) {
3544
- // Create a script element
3545
- const scriptElement = document.createElement('script');
3546
- scriptElement.textContent = `
3547
- window.handleDynamicSingleSelect = function(category, fieldsetid) {
3548
- //console.log("HERE", fieldsetid);
3687
+ // Build the select options HTML for sub-category
3688
+ const subSelectHTML = subOptions.map(option => {
3689
+ const isSelected = option.selected ? ' selected' : '';
3690
+ return `
3691
+ <option value="${option.value}"${isSelected}>${option.label}</option>
3692
+ `;
3693
+ }).join('');
3549
3694
 
3550
- // Hide all subcategory fields
3551
- document.querySelectorAll(\`[class*="\${fieldsetid}"]\`).forEach(div => {
3552
- div.style.display = "none";
3553
- });
3554
3695
 
3555
- // Show the selected category
3556
- const selectedCategoryFieldset = document.getElementById(category + '-options');
3557
- if (selectedCategoryFieldset) {
3558
- selectedCategoryFieldset.style.display = "block";
3559
- }
3560
- }
3561
- `;
3562
-
3563
- // Append the script element to the target div
3564
- targetDiv.appendChild(scriptElement);
3565
- } else {
3566
- console.error(`Target div with id "${this.formContainerId}" not found.`);
3567
- }
3696
+ let subCategoryLabel;
3697
+ console.log('Label (rawLabel for sub-category):', rawLabel); // Debug log
3568
3698
 
3569
- subCategoriesOptions.forEach(subCategory => {
3570
- const { id, label, options } = subCategory;
3699
+ if (rawLabel.includes('-')) {
3700
+ subCategoryLabel = rawLabel.split('-')?.[1] + ' Options';
3701
+ } else {
3702
+ subCategoryLabel = 'options';
3703
+ }
3571
3704
 
3572
- // Build the select options HTML
3573
- const selectHTML = options.map(option => {
3574
- const isSelected = option.selected ? ' selected' : '';
3575
- return `
3576
- <option value="${option.value}"${isSelected}>${option.label}</option>
3577
- `;
3578
- }).join('');
3705
+ let optionsLabel;
3706
+ if (subCategoryLabel !== 'options') {
3707
+ optionsLabel = rawLabel.split('-')?.[1] + ' Option';
3708
+ } else {
3709
+ optionsLabel = subCategoryLabel;
3710
+ }
3579
3711
 
3580
3712
 
3581
- let subCategoryLabel;
3582
- console.log('Label:', rawLabel); // Debug log
3713
+ // Create the HTML for the sub-category fieldset and select elements
3714
+ // Added a class based on the main select's ID for easy grouping/selection
3715
+ let subFormHTML = `
3716
+ <fieldset class="${this.selectGroupClass} ${categoryId}-subcategory-group" id="${id}-options" style="display: none;"> <legend>${label} ${subCategoryLabel} ${isSubCategoryRequired && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
3717
+ </legend>
3718
+ <label for="${id}"> Select ${label} ${optionsLabel}
3719
+ </label>
3720
+ <select name="${id}"
3721
+ ${bindingDirective}
3722
+ ${dimensionAttrs}
3723
+ id="${id}"
3724
+ class="${inputClass}"
3725
+ ${additionalAttrs}
3726
+ ${subCategoryValidationAttrs}
3727
+ data-original-required="${isSubCategoryRequired}" >
3728
+ <option value="">Choose an option</option>
3729
+ ${subSelectHTML}
3730
+ </select>
3731
+ </fieldset>
3732
+ `.replace(/^\s*\n/gm, '').trim();
3733
+
3734
+ // Apply vertical layout to the <select> element and its children
3735
+ subFormHTML = subFormHTML.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g, (match, p1, p2) => {
3736
+ const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
3737
+ return `<select\n${attributes}\n>\n${p2.trim()}\n</select>`;
3738
+ });
3583
3739
 
3584
- if (rawLabel.includes('-')) {
3585
- subCategoryLabel = rawLabel.split('-')?.[1] + ' Options';
3586
- } else {
3587
- subCategoryLabel = 'options';
3588
- }
3740
+ // Ensure the <fieldset> block starts on a new line and remove extra blank lines
3741
+ subFormHTML = subFormHTML.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
3742
+ return `\n${match}\n`;
3743
+ }).replace(/\n\s*\n/g, '\n');
3589
3744
 
3590
- let optionsLabel;
3591
- if (subCategoryLabel !== 'options') {
3592
- optionsLabel = rawLabel.split('-')?.[1] + ' Option';
3593
- } else {
3594
- optionsLabel = subCategoryLabel;
3745
+ // Append the generated HTML to formMarkUp
3746
+ this.formMarkUp += subFormHTML;
3747
+ });
3595
3748
  }
3596
-
3597
-
3598
- // Create the HTML for the fieldset and select elements
3599
- let formHTML = `
3600
- <fieldset class="${this.selectGroupClass} ${categoryId}" id="${id}-options" style="display: none;">
3601
- <legend> ${label} ${subCategoryLabel} ${this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
3602
- </legend>
3603
- <label for="${id}"> Select ${label} ${optionsLabel}
3604
- </label>
3605
- <select name="${id}"
3606
- ${bindingDirective}
3607
- ${dimensionAttrs}
3608
- id="${id + '-block'}"
3609
- class="${inputClass}"
3610
- ${additionalAttrs}
3611
- ${validationAttrs}
3612
- >
3613
- <option value="">Choose an option</option>
3614
- ${selectHTML}
3615
- </select>
3616
- </fieldset>
3617
- `.replace(/^\s*\n/gm, '').trim();
3618
-
3619
- // Apply vertical layout to the <select> element and its children
3620
- formHTML = formHTML.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g, (match, p1, p2) => {
3621
- // Reformat attributes into a vertical layout
3622
- const attributes = p1.trim().split(/\s+/).map(attr => ` ${attr}`).join('\n');
3623
- return `<select\n${attributes}\n>\n${p2.trim()}\n</select>`;
3624
- });
3625
-
3626
- // Ensure the <fieldset> block starts on a new line and remove extra blank lines
3627
- formHTML = formHTML.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
3628
- // Ensure <fieldset> starts on a new line
3629
- return `\n${match}\n`;
3630
- }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
3631
-
3632
- // Append the generated HTML to formMarkUp
3633
- this.formMarkUp += formHTML;
3634
-
3635
- //return formHTML;
3636
- });
3637
-
3638
-
3639
- }
3640
3749
  }
3641
3750
 
3642
-
3643
-
3644
3751
  renderMultipleSelectField(type, name, label, validate, attributes, options) {
3645
3752
  // Define valid validation attributes for multiple select fields
3646
3753
  const selectValidationAttributes = ['required', 'minlength', 'maxlength'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formique/semantq",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Formique is a native form builder for the Semantq JS Framework",
5
5
  "main": "formique-semantq.js",
6
6
  "type": "module",