@formique/semantq 1.1.1 → 1.1.2

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/astToFormique.js CHANGED
@@ -642,7 +642,6 @@ getOptionValuesByKey(attributes, targetKey) {
642
642
 
643
643
 
644
644
  buildDynamicSingleSelect(node, rawFieldName) {
645
-
646
645
  const cleanString = this.cleanFieldName(rawFieldName);
647
646
  const fieldName = cleanString.input_name;
648
647
 
@@ -661,7 +660,6 @@ buildDynamicSingleSelect(node, rawFieldName) {
661
660
  inputParams = { validations: {}, attributes: {} }
662
661
  }
663
662
 
664
-
665
663
  validations = inputParams.validations;
666
664
  attributes = inputParams.attributes;
667
665
 
@@ -669,47 +667,37 @@ buildDynamicSingleSelect(node, rawFieldName) {
669
667
  validations['required'] = true;
670
668
  }
671
669
 
672
- // 1. CRITICAL FIX: Extract 'options' (main dropdown values) from attributes and store separately (Index 5).
670
+ // Extract 'options' (main dropdown values)
673
671
  if (attributes.options) {
674
672
  mainSelectOptions = attributes.options;
675
673
  delete attributes.options;
676
674
  }
677
675
 
678
- // Since the AST is now clean, we assume the only remaining attributes are standard HTML attributes (or empty {}).
679
- // The previous workaround for fragmented 'South' and 'Africa' keys is now removed.
680
-
681
- // Push Schema Elements (Index 3 and 4)
682
676
  fieldSchema.push(validations); // Index 3
683
- fieldSchema.push(attributes); // Index 4 (Should be {} if no HTML attributes were defined)
684
-
677
+ fieldSchema.push(attributes); // Index 4
685
678
 
686
- /// NOW BUILD SCENARIO (SUB-OPTIONS) BLOCKS (Index 6)
687
-
688
- // optionValues retrieves all unique keys that are NOT 'options'
679
+ // Build scenario blocks (Index 6)
689
680
  const optionValues = this.extractOptionValues(node.attributes);
690
681
  let scenarioBlocks = [];
691
682
 
692
-
693
683
  if (optionValues.length > 0) {
694
684
  optionValues.forEach(option => {
695
-
696
685
  let schema = {};
697
- const lowerCaseOption = option.toLowerCase();
698
-
699
- // Set the ID/label for the scenario block
700
- schema['id'] = option; //lowerCaseOption;
686
+
687
+ // Use the original option string as the ID
688
+ schema['id'] = option;
701
689
  schema['label'] = option;
702
690
 
703
- // Use the option string directly as the attribute key for lookup (e.g., 'South Africa' -> 'South Africa')
704
- const attributeKey = option;
705
-
706
- /// Add sub-options now
707
- const keyOptions = this.getOptionValuesByKey(node.attributes, attributeKey);
691
+ // Get sub-options for this category
692
+ const keyOptions = this.getOptionValuesByKey(node.attributes, option);
708
693
  let options = [];
709
694
 
710
695
  if (keyOptions.length > 0) {
711
696
  keyOptions.forEach(subOption => {
712
- options.push({ value: subOption.toLowerCase(), label: this.toTitleCase(subOption) })
697
+ options.push({
698
+ value: subOption.toLowerCase(),
699
+ label: this.toTitleCase(subOption)
700
+ });
713
701
  });
714
702
  schema['options'] = options;
715
703
  scenarioBlocks.push(schema);
@@ -717,15 +705,9 @@ buildDynamicSingleSelect(node, rawFieldName) {
717
705
  });
718
706
  }
719
707
 
708
+ fieldSchema.push(mainSelectOptions); // Index 5
709
+ fieldSchema.push(scenarioBlocks); // Index 6
720
710
 
721
- // 3. PUSH MAIN OPTIONS: Add the main select options list (Index 5)
722
- fieldSchema.push(mainSelectOptions);
723
-
724
- // 4. PUSH SCENARIO BLOCKS: Add the list of scenario blocks (Index 6)
725
- fieldSchema.push(scenarioBlocks);
726
-
727
-
728
- // 5. Final push to the form schema
729
711
  this.formSchema.push(fieldSchema);
730
712
  }
731
713
 
@@ -958,6 +940,7 @@ buildField(node) {
958
940
 
959
941
  // 5. Finalize Schema
960
942
  this.formSchema.push(fieldSchema);
943
+ console.log("formSchema", JSON.stringify(this.formSchema,null,2));
961
944
  }
962
945
 
963
946
 
@@ -782,51 +782,47 @@ renderForm() {
782
782
 
783
783
  // renderField method - No change needed here for this issue, but ensure it handles 'submit' type correctly if called directly
784
784
  renderField(type, name, label, validate, attributes, options, subOptions = undefined) {
785
- const fieldRenderMap = {
786
- 'text': this.renderTextField,
787
- 'email': this.renderEmailField,
788
- 'number': this.renderNumberField,
789
- 'password': this.renderPasswordField,
790
- 'textarea': this.renderTextAreaField,
791
- 'tel': this.renderTelField,
792
- 'date': this.renderDateField,
793
- 'time': this.renderTimeField,
794
- 'datetime-local': this.renderDateTimeField,
795
- 'month': this.renderMonthField,
796
- 'week': this.renderWeekField,
797
- 'url': this.renderUrlField,
798
- 'search': this.renderSearchField,
799
- 'color': this.renderColorField,
800
- 'checkbox': this.renderCheckboxField,
801
- 'radio': this.renderRadioField,
802
- 'file': this.renderFileField,
803
- 'hidden': this.renderHiddenField,
804
- 'image': this.renderImageField,
805
- 'singleSelect': this.renderSingleSelectField,
806
- 'multipleSelect': this.renderMultipleSelectField,
807
- 'dynamicSingleSelect': this.renderDynamicSingleSelectField,
808
- 'range': this.renderRangeField,
809
- 'recaptcha': this.renderRecaptchaField,
810
- 'submit': this.renderSubmitButton, // Keep this for completeness, but renderSubmitButtonElement will now handle it
811
- };
812
-
813
- const renderMethod = fieldRenderMap[type];
785
+ const fieldRenderMap = {
786
+ 'text': this.renderTextField,
787
+ 'email': this.renderEmailField,
788
+ 'number': this.renderNumberField,
789
+ 'password': this.renderPasswordField,
790
+ 'textarea': this.renderTextAreaField,
791
+ 'tel': this.renderTelField,
792
+ 'date': this.renderDateField,
793
+ 'time': this.renderTimeField,
794
+ 'datetime-local': this.renderDateTimeField,
795
+ 'month': this.renderMonthField,
796
+ 'week': this.renderWeekField,
797
+ 'url': this.renderUrlField,
798
+ 'search': this.renderSearchField,
799
+ 'color': this.renderColorField,
800
+ 'checkbox': this.renderCheckboxField,
801
+ 'radio': this.renderRadioField,
802
+ 'file': this.renderFileField,
803
+ 'hidden': this.renderHiddenField,
804
+ 'image': this.renderImageField,
805
+ 'singleSelect': this.renderSingleSelectField,
806
+ 'multipleSelect': this.renderMultipleSelectField,
807
+ 'dynamicSingleSelect': this.renderDynamicSingleSelectField,
808
+ 'range': this.renderRangeField,
809
+ 'recaptcha': this.renderRecaptchaField,
810
+ 'submit': this.renderSubmitButton,
811
+ };
814
812
 
815
- if (renderMethod) {
816
- // If the type is 'submit', ensure we use the specific renderSubmitButtonElement
817
- // Although, with the filter in renderForm(), this branch for 'submit' type
818
- // might not be hit in the primary rendering flow, it's good practice.
819
- return renderMethod.call(this, type, name, label, validate, attributes, options,subOptions);
813
+ const renderMethod = fieldRenderMap[type];
820
814
 
821
- if (type === 'submit') {
822
- return this.renderSubmitButton(type, name, label, validate, attributes, options);
823
- }
824
- //return renderMethod.call(this, type, name, label, validate, attributes, options);
825
- } else {
826
- console.warn(`Unsupported field type '${type}' encountered.`);
827
- return '';
828
- }
815
+ if (renderMethod) {
816
+ // IMPORTANT: Pass ALL arguments including subOptions
817
+ return renderMethod.call(this, type, name, label, validate, attributes, options, subOptions);
818
+ } else {
819
+ console.warn(`Unsupported field type '${type}' encountered.`);
820
+ return '';
829
821
  }
822
+ }
823
+
824
+
825
+
830
826
 
831
827
 
832
828
 
@@ -3641,244 +3637,227 @@ renderCheckboxField(type, name, label, validate, attributes, options) {
3641
3637
  /* DYNAMIC SINGLE SELECT BLOCK */
3642
3638
 
3643
3639
  // Function to render the dynamic select field and update based on user selection
3644
- renderDynamicSingleSelectField(type, name, label, validate, attributes, options) {
3645
- //console.log('DEBUG: renderDynamicSingleSelectField called with options:', options);
3646
-
3647
- // Step 1: Extract main categories from options
3648
- const mainCategoryOptions = options.map(item => {
3649
- // Use item.id as the value for the main select
3650
- return {
3651
- value: item.id, // ← FIXED: Use id, not value
3652
- label: item.label,
3653
- // You can add selected logic if needed
3654
- };
3655
- });
3656
-
3657
- // Step 2: The nested options ARE your sub-categories!
3658
- // Transform the structure
3659
- const subCategoriesOptions = options.map(item => ({
3660
- id: item.id, // Same as main category value
3661
- label: item.label + ' Technologies', // Or customize
3662
- options: item.options // The nested options array
3663
- }));
3664
-
3665
- //console.log('Main categories:', mainCategoryOptions);
3666
- //console.log('Sub categories:', subCategoriesOptions);
3667
-
3668
- const mode = 'dynamicSingleSelect';
3669
-
3670
- // Pass both to the renderer
3671
- this.renderSingleSelectField(type, name, label, validate, attributes, mainCategoryOptions, subCategoriesOptions, mode);
3672
- }
3673
-
3674
- renderSingleSelectField(type, name, label, validate, attributes, options, subCategoriesOptions, mode) {
3675
-
3676
- // Define valid validation attributes for select fields
3677
- const selectValidationAttributes = ['required'];
3678
-
3679
- // Construct validation attributes
3680
- let validationAttrs = '';
3681
- // Store original required state for the main select
3682
- let originalRequired = false; // <--- This variable tracks if the main select was originally required
3683
- if (validate) {
3684
- Object.entries(validate).forEach(([key, value]) => {
3685
- if (selectValidationAttributes.includes(key)) {
3686
- if (key === 'required') {
3687
- validationAttrs += `${key} `;
3688
- originalRequired = true; // Mark that it was originally required
3689
- }
3690
- } else {
3691
- // Removed console.warn
3692
- }
3640
+ renderDynamicSingleSelectField(type, name, label, validate, attributes, options, subOptions) {
3641
+ console.log('DEBUG: renderDynamicSingleSelectField called', {
3642
+ type, name, label,
3643
+ options: options ? options.length : 'none',
3644
+ subOptions: subOptions ? subOptions.length : 'none'
3693
3645
  });
3694
- }
3695
-
3696
- // Handle the binding syntax
3697
- let bindingDirective = '';
3698
- if (attributes.binding) {
3699
- if (typeof attributes.binding === 'string' && attributes.binding.startsWith('::')) {
3700
- bindingDirective = ` bind:value="${name}" `;
3646
+
3647
+ // Check if options exist
3648
+ if (!options || !Array.isArray(options)) {
3649
+ console.warn('Dynamic single select field missing options:', name);
3650
+ options = [];
3701
3651
  }
3702
- }
3703
-
3704
- // Define attributes for the select field
3705
- let id = attributes.id || name;
3706
- let dimensionAttrs = ''; // No dimension attributes applicable for select fields
3707
-
3708
- // Handle additional attributes
3709
- let additionalAttrs = '';
3710
- for (const [key, value] of Object.entries(attributes)) {
3711
- if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) {
3712
- if (key.startsWith('on')) {
3713
- // Handle event attributes
3714
- const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
3715
- additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
3716
- } else {
3717
- // Handle boolean attributes
3718
- if (value === true) {
3719
- additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
3720
- } else if (value !== false) {
3721
- // Convert underscores to hyphens and set the attribute
3722
- additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
3652
+
3653
+ // Step 1: Extract main categories from options
3654
+ // Options should already be in the correct format from the parser
3655
+ const mainCategoryOptions = options.map(item => {
3656
+ // Handle both string and object formats
3657
+ if (typeof item === 'string') {
3658
+ return {
3659
+ value: item.toLowerCase().replace(/\s+/g, '-'),
3660
+ label: item
3661
+ };
3662
+ } else {
3663
+ // Already an object with value/label
3664
+ return {
3665
+ value: item.value || item.id || item,
3666
+ label: item.label || item.value || item
3667
+ };
3723
3668
  }
3724
- }
3669
+ });
3670
+
3671
+ // Step 2: Handle subCategoriesOptions (scenario blocks)
3672
+ let subCategoriesOptions = [];
3673
+
3674
+ if (subOptions && Array.isArray(subOptions)) {
3675
+ // Use the subOptions exactly as provided by the parser
3676
+ // They should already have the correct structure
3677
+ subCategoriesOptions = subOptions.map(item => {
3678
+ // Ensure each subCategory has the required structure
3679
+ return {
3680
+ id: item.id || item.value || '',
3681
+ label: item.label || item.id || '',
3682
+ options: Array.isArray(item.options) ? item.options : []
3683
+ };
3684
+ });
3725
3685
  }
3726
- }
3686
+
3687
+ console.log('Main categories:', mainCategoryOptions);
3688
+ console.log('Sub categories:', subCategoriesOptions);
3689
+
3690
+ // Pass both to the renderer with the mode flag
3691
+ this.renderSingleSelectField(
3692
+ type,
3693
+ name,
3694
+ label,
3695
+ validate,
3696
+ attributes,
3697
+ mainCategoryOptions,
3698
+ subCategoriesOptions,
3699
+ 'dynamicSingleSelect'
3700
+ );
3701
+ }
3727
3702
 
3728
- // Construct select options HTML based on options
3729
- let selectHTML = '';
3730
- if (Array.isArray(options)) {
3731
- // Add a default option
3732
- selectHTML += `
3733
- <option value="">Choose an option</option>
3734
- `;
3735
3703
 
3736
- // Add the provided options
3737
- selectHTML += options.map((option) => {
3738
- const isSelected = option.selected ? ' selected' : '';
3739
- return `
3740
- <option value="${option.value}"${isSelected}>${option.label}</option>
3741
- `;
3742
- }).join('');
3743
- }
3744
3704
 
3745
- let inputClass = attributes.class || this.inputClass;
3746
3705
 
3747
- // Remove `onchange` from HTML; it will be handled by JavaScript event listeners
3748
- const onchangeAttr = ''; // <--- Ensure this is an empty string
3749
3706
 
3750
- let labelDisplay;
3751
- let rawLabel;
3707
+ renderSingleSelectField(type, name, label, validate, attributes, options, subCategoriesOptions, mode) {
3708
+ // Define valid validation attributes for select fields
3709
+ const selectValidationAttributes = ['required'];
3752
3710
 
3753
- if (mode === 'dynamicSingleSelect' && subCategoriesOptions) {
3754
- if (label.includes('-')) {
3755
- const [mainCategoryLabel] = label.split('-');
3756
- labelDisplay = mainCategoryLabel;
3757
- rawLabel = label;
3758
- } else {
3759
- labelDisplay = label;
3760
- rawLabel = label;
3711
+ // Construct validation attributes
3712
+ let validationAttrs = '';
3713
+ let originalRequired = false;
3714
+ if (validate) {
3715
+ Object.entries(validate).forEach(([key, value]) => {
3716
+ if (selectValidationAttributes.includes(key)) {
3717
+ if (key === 'required') {
3718
+ validationAttrs += `${key} `;
3719
+ originalRequired = true;
3720
+ }
3721
+ }
3722
+ });
3761
3723
  }
3762
- } else {
3763
- labelDisplay = label;
3764
- }
3765
-
3766
- // Construct the final HTML string for the main select
3767
- let formHTML = `
3768
- <fieldset class="${this.selectGroupClass}" id="${id + '-block'}">
3769
- <legend>${labelDisplay}
3770
- ${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
3771
- </legend>
3772
- <label for="${id}"> Select ${labelDisplay}
3773
- <select name="${name}"
3774
- ${bindingDirective}
3775
- ${dimensionAttrs}
3776
- id="${id}"
3777
- class="${inputClass}"
3778
- ${additionalAttrs}
3779
- ${validationAttrs}
3780
- data-original-required="${originalRequired}" >
3781
- ${selectHTML}
3782
- </select>
3783
- </fieldset>
3784
- `.replace(/^\s*\n/gm, '').trim();
3785
-
3786
- // FIXED: Apply vertical layout to the <select> element and its children
3787
- // Only split on actual attribute boundaries, not within attribute values
3788
- let formattedHtml = formHTML.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g, (match, p1, p2) => {
3789
- // Use regex to match complete attribute="value" pairs
3790
- const attributes = p1.match(/(\w+(?:-\w+)*=("[^"]*"|'[^']*'|\w+)|[^=\s]+(?!\s*=))/g) || [];
3791
- const formattedAttributes = attributes.map(attr => ` ${attr}`).join('\n');
3792
- return `<select\n${formattedAttributes}\n>\n${p2.trim()}\n</select>`;
3793
- });
3794
-
3795
- // Ensure the <fieldset> block starts on a new line and remove extra blank lines
3796
- formattedHtml = formattedHtml.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
3797
- // Ensure <fieldset> starts on a new line
3798
- return `\n${match}\n`;
3799
- }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
3800
-
3801
- this.formMarkUp+=formattedHtml;
3802
-
3803
3724
 
3804
- /* dynamicSingleSelect - Sub-Category Generation Block */
3805
-
3806
- if (mode && mode ==='dynamicSingleSelect' && subCategoriesOptions) {
3807
-
3808
- const categoryId = attributes.id || name; // This is the ID of the main dynamic select ('languages')
3725
+ // Handle the binding syntax
3726
+ let bindingDirective = '';
3727
+ if (attributes.binding) {
3728
+ if (typeof attributes.binding === 'string' && attributes.binding.startsWith('::')) {
3729
+ bindingDirective = ` bind:value="${name}" `;
3730
+ }
3731
+ }
3809
3732
 
3810
- subCategoriesOptions.forEach((subCategory) => {
3811
- const { id, label, options: subOptions } = subCategory; // Renamed 'options' to 'subOptions' to avoid conflict
3733
+ // Define attributes for the select field
3734
+ let id = attributes.id || name;
3735
+ let dimensionAttrs = '';
3812
3736
 
3813
- // IMPORTANT: Sub-category selects are *initially hidden*
3814
- // Therefore, by default, they are NOT required until they are revealed.
3815
- let isSubCategoryRequired = false; // Default to false as they are hidden
3816
- const subCategoryValidationAttrs = ''; // No direct 'required' in HTML initially
3737
+ // Handle additional attributes
3738
+ let additionalAttrs = '';
3739
+ for (const [key, value] of Object.entries(attributes)) {
3740
+ if (key !== 'id' && key !== 'class' && key !== 'dependsOn' && key !== 'dependents' && value !== undefined) {
3741
+ if (key.startsWith('on')) {
3742
+ const eventValue = value.endsWith('()') ? value.slice(0, -2) : value;
3743
+ additionalAttrs += ` @${key.replace(/^on/, '')}={${eventValue}}\n`;
3744
+ } else {
3745
+ if (value === true) {
3746
+ additionalAttrs += ` ${key.replace(/_/g, '-')}\n`;
3747
+ } else if (value !== false) {
3748
+ additionalAttrs += ` ${key.replace(/_/g, '-')}="${value}"\n`;
3749
+ }
3750
+ }
3751
+ }
3752
+ }
3817
3753
 
3818
- // Build the select options HTML for sub-category
3819
- const subSelectHTML = subOptions.map(option => {
3820
- const isSelected = option.selected ? ' selected' : '';
3821
- return `
3822
- <option value="${option.value}"${isSelected}>${option.label}</option>
3754
+ // Construct select options HTML based on options
3755
+ let selectHTML = '';
3756
+ if (Array.isArray(options) && options.length > 0) {
3757
+ // Add a default option
3758
+ selectHTML += `
3759
+ <option value="">Choose an option</option>
3823
3760
  `;
3824
- }).join('');
3825
-
3826
-
3827
- let subCategoryLabel;
3828
-
3829
- if (rawLabel.includes('-')) {
3830
- subCategoryLabel = rawLabel.split('-')?.[1] + ' Options';
3831
- } else {
3832
- subCategoryLabel = 'options';
3833
- }
3834
3761
 
3835
- let optionsLabel;
3836
- if (subCategoryLabel !== 'options') {
3837
- optionsLabel = rawLabel.split('-')?.[1] + ' Option';
3838
- } else {
3839
- optionsLabel = subCategoryLabel;
3840
- }
3762
+ // Add the provided options
3763
+ selectHTML += options.map((option) => {
3764
+ const optionValue = option.value || option;
3765
+ const optionLabel = option.label || option;
3766
+ const isSelected = option.selected ? ' selected' : '';
3767
+ return `
3768
+ <option value="${optionValue}"${isSelected}>${optionLabel}</option>
3769
+ `;
3770
+ }).join('');
3771
+ }
3841
3772
 
3773
+ let inputClass = attributes.class || this.inputClass;
3842
3774
 
3843
- // Create the HTML for the sub-category fieldset and select elements
3844
- // Added a class based on the main select's ID for easy grouping/selection
3845
- let subFormHTML = `
3846
- <fieldset class="${this.selectGroupClass} ${categoryId}" id="${id}" style="display: none;"> <legend>${label} ${subCategoryLabel} ${isSubCategoryRequired && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
3847
- </legend>
3848
- <label for="${id}"> Select ${label} ${optionsLabel}
3849
- </label>
3850
- <select name="${id}"
3775
+ // Construct the final HTML string for the main select
3776
+ let formHTML = `
3777
+ <fieldset class="${this.selectGroupClass}" id="${id + '-block'}">
3778
+ <legend>${label}
3779
+ ${validationAttrs.includes('required') && this.formSettings.requiredFieldIndicator ? this.formSettings.asteriskHtml : ''}
3780
+ </legend>
3781
+ <label for="${id}"> Select ${label}
3782
+ <select name="${name}"
3851
3783
  ${bindingDirective}
3852
3784
  ${dimensionAttrs}
3853
3785
  id="${id}"
3854
3786
  class="${inputClass}"
3855
3787
  ${additionalAttrs}
3856
- ${subCategoryValidationAttrs}
3857
- data-original-required="${isSubCategoryRequired}" >
3858
- <option value="">Choose an option</option>
3859
- ${subSelectHTML}
3860
- </select>
3861
- </fieldset>
3862
- `.replace(/^\s*\n/gm, '').trim();
3863
-
3864
- // FIXED: Apply the same corrected formatting to sub-category selects
3865
- subFormHTML = subFormHTML.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g, (match, p1, p2) => {
3788
+ ${validationAttrs}
3789
+ data-original-required="${originalRequired}" >
3790
+ ${selectHTML}
3791
+ </select>
3792
+ </fieldset>
3793
+ `.replace(/^\s*\n/gm, '').trim();
3794
+
3795
+ // Format the HTML
3796
+ let formattedHtml = formHTML.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g, (match, p1, p2) => {
3866
3797
  const attributes = p1.match(/(\w+(?:-\w+)*=("[^"]*"|'[^']*'|\w+)|[^=\s]+(?!\s*=))/g) || [];
3867
3798
  const formattedAttributes = attributes.map(attr => ` ${attr}`).join('\n');
3868
3799
  return `<select\n${formattedAttributes}\n>\n${p2.trim()}\n</select>`;
3869
- });
3800
+ });
3870
3801
 
3871
- // Ensure the <fieldset> block starts on a new line and remove extra blank lines
3872
- subFormHTML = subFormHTML.replace(/(<fieldset\s+[^>]*>)/g, (match) => {
3873
- return `\n${match}\n`;
3874
- }).replace(/\n\s*\n/g, '\n');
3802
+ this.formMarkUp += formattedHtml;
3875
3803
 
3876
- // Append the generated HTML to formMarkUp
3877
- this.formMarkUp += subFormHTML;
3878
- });
3879
- }
3880
- }
3804
+ /* dynamicSingleSelect - Sub-Category Generation Block */
3805
+ if (mode === 'dynamicSingleSelect' && subCategoriesOptions && Array.isArray(subCategoriesOptions) && subCategoriesOptions.length > 0) {
3806
+ const categoryId = attributes.id || name;
3807
+
3808
+ subCategoriesOptions.forEach((subCategory) => {
3809
+ // Skip invalid subCategories
3810
+ if (!subCategory || !subCategory.id) {
3811
+ console.warn('Invalid subCategory in dynamic select:', subCategory);
3812
+ return;
3813
+ }
3814
+
3815
+ const { id, label: subLabel, options: subOptions } = subCategory;
3816
+
3817
+ // Ensure subOptions is an array
3818
+ const subOptionArray = Array.isArray(subOptions) ? subOptions : [];
3819
+
3820
+ // Build the select options HTML for sub-category
3821
+ const subSelectHTML = subOptionArray.length > 0 ?
3822
+ subOptionArray.map(option => {
3823
+ const optionValue = option.value || option;
3824
+ const optionLabel = option.label || option;
3825
+ const isSelected = option.selected ? ' selected' : '';
3826
+ return `
3827
+ <option value="${optionValue}"${isSelected}>${optionLabel}</option>
3828
+ `;
3829
+ }).join('') :
3830
+ '<option value="">No options available</option>';
3831
+
3832
+ // Create the HTML for the sub-category fieldset
3833
+ let subFormHTML = `
3834
+ <fieldset class="${this.selectGroupClass} ${categoryId}" id="${id}" style="display: none;">
3835
+ <legend>${subLabel || id}</legend>
3836
+ <label for="${id}"> Select ${subLabel || id}</label>
3837
+ <select name="${id}"
3838
+ ${bindingDirective}
3839
+ ${dimensionAttrs}
3840
+ id="${id}"
3841
+ class="${inputClass}"
3842
+ ${additionalAttrs}
3843
+ data-original-required="false">
3844
+ <option value="">Choose an option</option>
3845
+ ${subSelectHTML}
3846
+ </select>
3847
+ </fieldset>
3848
+ `.replace(/^\s*\n/gm, '').trim();
3849
+
3850
+ // Format the HTML
3851
+ subFormHTML = subFormHTML.replace(/<select\s+([^>]*)>([\s\S]*?)<\/select>/g, (match, p1, p2) => {
3852
+ const attributes = p1.match(/(\w+(?:-\w+)*=("[^"]*"|'[^']*'|\w+)|[^=\s]+(?!\s*=))/g) || [];
3853
+ const formattedAttributes = attributes.map(attr => ` ${attr}`).join('\n');
3854
+ return `<select\n${formattedAttributes}\n>\n${p2.trim()}\n</select>`;
3855
+ });
3881
3856
 
3857
+ this.formMarkUp += subFormHTML;
3858
+ });
3859
+ }
3860
+ }
3882
3861
 
3883
3862
 
3884
3863
  renderMultipleSelectField(type, name, label, validate, attributes, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formique/semantq",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Formique is a native form builder for the Semantq JS Framework",
5
5
  "main": "formique-semantq.js",
6
6
  "type": "module",