@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/LowCodeParser.js +327 -150
- package/astToFormique.js +15 -32
- package/formique-semantq.js +229 -250
- package/package.json +1 -1
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
|
-
//
|
|
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
|
|
684
|
-
|
|
677
|
+
fieldSchema.push(attributes); // Index 4
|
|
685
678
|
|
|
686
|
-
|
|
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
|
-
|
|
698
|
-
|
|
699
|
-
|
|
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
|
-
//
|
|
704
|
-
const
|
|
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({
|
|
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
|
|
package/formique-semantq.js
CHANGED
|
@@ -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
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
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
|
-
|
|
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
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
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
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
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
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
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
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
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
|
-
|
|
3751
|
-
|
|
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
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
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
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
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
|
-
|
|
3811
|
-
|
|
3733
|
+
// Define attributes for the select field
|
|
3734
|
+
let id = attributes.id || name;
|
|
3735
|
+
let dimensionAttrs = '';
|
|
3812
3736
|
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
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
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
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
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
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
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
<
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
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
|
-
${
|
|
3857
|
-
data-original-required="${
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3877
|
-
|
|
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) {
|