@liedekef/ftable 1.1.22 → 1.1.24
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/ftable.esm.js +225 -136
- package/ftable.js +225 -136
- package/ftable.min.js +2 -2
- package/ftable.umd.js +225 -136
- package/package.json +1 -1
- package/themes/basic/ftable_basic.css +1 -1
- package/themes/basic/ftable_basic.min.css +1 -1
- package/themes/ftable_theme_base.less +1 -1
- package/themes/lightcolor/blue/ftable.css +1 -1
- package/themes/lightcolor/blue/ftable.min.css +1 -1
- package/themes/lightcolor/gray/ftable.css +1 -1
- package/themes/lightcolor/gray/ftable.min.css +1 -1
- package/themes/lightcolor/green/ftable.css +1 -1
- package/themes/lightcolor/green/ftable.min.css +1 -1
- package/themes/lightcolor/orange/ftable.css +1 -1
- package/themes/lightcolor/orange/ftable.min.css +1 -1
- package/themes/lightcolor/red/ftable.css +1 -1
- package/themes/lightcolor/red/ftable.min.css +1 -1
- package/themes/metro/blue/ftable.css +1 -1
- package/themes/metro/blue/ftable.min.css +1 -1
- package/themes/metro/brown/ftable.css +1 -1
- package/themes/metro/brown/ftable.min.css +1 -1
- package/themes/metro/crimson/ftable.css +1 -1
- package/themes/metro/crimson/ftable.min.css +1 -1
- package/themes/metro/darkgray/ftable.css +1 -1
- package/themes/metro/darkgray/ftable.min.css +1 -1
- package/themes/metro/darkorange/ftable.css +1 -1
- package/themes/metro/darkorange/ftable.min.css +1 -1
- package/themes/metro/green/ftable.css +1 -1
- package/themes/metro/green/ftable.min.css +1 -1
- package/themes/metro/lightgray/ftable.css +1 -1
- package/themes/metro/lightgray/ftable.min.css +1 -1
- package/themes/metro/pink/ftable.css +1 -1
- package/themes/metro/pink/ftable.min.css +1 -1
- package/themes/metro/purple/ftable.css +1 -1
- package/themes/metro/purple/ftable.min.css +1 -1
- package/themes/metro/red/ftable.css +1 -1
- package/themes/metro/red/ftable.min.css +1 -1
package/ftable.js
CHANGED
|
@@ -550,17 +550,86 @@ class FTableFormBuilder {
|
|
|
550
550
|
this.dependencies = new Map(); // Track field dependencies
|
|
551
551
|
this.optionsCache = new FTableOptionsCache();
|
|
552
552
|
this.originalFieldOptions = new Map(); // Store original field.options
|
|
553
|
+
this.resolvedFieldOptions = new Map(); // Store resolved options per context
|
|
554
|
+
|
|
555
|
+
// Initialize with empty cache objects
|
|
556
|
+
Object.keys(this.options.fields || {}).forEach(fieldName => {
|
|
557
|
+
this.resolvedFieldOptions.set(fieldName, {});
|
|
558
|
+
});
|
|
559
|
+
Object.entries(this.options.fields).forEach(([fieldName, field]) => {
|
|
560
|
+
this.originalFieldOptions.set(fieldName, field.options);
|
|
561
|
+
});
|
|
553
562
|
}
|
|
554
563
|
|
|
555
|
-
//
|
|
556
|
-
|
|
557
|
-
|
|
564
|
+
// Get options for specific context
|
|
565
|
+
async getFieldOptions(fieldName, context = 'table', params = {}) {
|
|
566
|
+
const field = this.options.fields[fieldName];
|
|
567
|
+
const originalOptions = this.originalFieldOptions.get(fieldName);
|
|
568
|
+
|
|
569
|
+
// If no options or already resolved for this context with same params, return cached
|
|
570
|
+
if (!originalOptions) {
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Determine if we should skip caching for this specific context
|
|
575
|
+
const shouldSkipCache = this.shouldSkipCachingForContext(field, context, params);
|
|
576
|
+
const cacheKey = this.generateOptionsCacheKey(context, params);
|
|
577
|
+
// Skip cache if configured or forceRefresh requested
|
|
578
|
+
if (!shouldSkipCache && !params.forceRefresh) {
|
|
579
|
+
const cached = this.resolvedFieldOptions.get(fieldName)[cacheKey];
|
|
580
|
+
if (cached) return cached;
|
|
581
|
+
}
|
|
558
582
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
583
|
+
try {
|
|
584
|
+
// Create temp field with original options for resolution
|
|
585
|
+
const tempField = { ...field, options: originalOptions };
|
|
586
|
+
const resolved = await this.resolveOptions(tempField, {
|
|
587
|
+
...params
|
|
588
|
+
}, context, shouldSkipCache);
|
|
589
|
+
|
|
590
|
+
// Only cache if noCache is not enabled
|
|
591
|
+
if (!shouldSkipCache) {
|
|
592
|
+
this.resolvedFieldOptions.get(fieldName)[cacheKey] = resolved;
|
|
562
593
|
}
|
|
563
|
-
|
|
594
|
+
return resolved;
|
|
595
|
+
} catch (err) {
|
|
596
|
+
console.error(`Failed to resolve options for ${fieldName} (${context}):`, err);
|
|
597
|
+
return originalOptions;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Helper method to determine caching behavior
|
|
602
|
+
shouldSkipCachingForContext(field, context, params) {
|
|
603
|
+
if (!field.noCache) return false;
|
|
604
|
+
|
|
605
|
+
if (typeof field.noCache === 'boolean') {
|
|
606
|
+
return field.noCache; // true = skip all contexts
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (typeof field.noCache === 'function') {
|
|
610
|
+
return field.noCache({ context, ...params });
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (typeof field.noCache === 'object') {
|
|
614
|
+
// Check if this specific context should skip cache
|
|
615
|
+
return field.noCache[context] === true;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return false; // Default to caching
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
generateOptionsCacheKey(context, params) {
|
|
622
|
+
// Create a unique key based on context and dependency values
|
|
623
|
+
const keyParts = [context];
|
|
624
|
+
|
|
625
|
+
if (params.dependedValues) {
|
|
626
|
+
// Include relevant dependency values in the cache key
|
|
627
|
+
Object.keys(params.dependedValues).sort().forEach(key => {
|
|
628
|
+
keyParts.push(`${key}=${params.dependedValues[key]}`);
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
return keyParts.join('|');
|
|
564
633
|
}
|
|
565
634
|
|
|
566
635
|
shouldIncludeField(field, formType) {
|
|
@@ -573,6 +642,7 @@ class FTableFormBuilder {
|
|
|
573
642
|
}
|
|
574
643
|
|
|
575
644
|
createFieldContainer(fieldName, field, record, formType) {
|
|
645
|
+
// in this function, field.options already contains the resolved values
|
|
576
646
|
const container = FTableDOMHelper.create('div', {
|
|
577
647
|
className: 'ftable-input-field-container',
|
|
578
648
|
attributes: {
|
|
@@ -594,66 +664,12 @@ class FTableFormBuilder {
|
|
|
594
664
|
return container;
|
|
595
665
|
}
|
|
596
666
|
|
|
597
|
-
/*async resolveAllFieldOptions(fieldValues) {
|
|
598
|
-
// Store original options before first resolution
|
|
599
|
-
this.storeOriginalFieldOptions();
|
|
600
|
-
|
|
601
|
-
const promises = Object.entries(this.options.fields).map(async ([fieldName, field]) => {
|
|
602
|
-
// Use original options if we have them, otherwise use current field.options
|
|
603
|
-
const originalOptions = this.originalFieldOptions.get(fieldName) || field.options;
|
|
604
|
-
|
|
605
|
-
if (originalOptions && (typeof originalOptions === 'function' || typeof originalOptions === 'string')) {
|
|
606
|
-
try {
|
|
607
|
-
// Pass fieldValues as dependedValues for dependency resolution
|
|
608
|
-
const params = { dependedValues: fieldValues };
|
|
609
|
-
|
|
610
|
-
// Resolve using original options, not the possibly already-resolved ones
|
|
611
|
-
const tempField = { ...field, options: originalOptions };
|
|
612
|
-
const resolved = await this.resolveOptions(tempField, params);
|
|
613
|
-
field.options = resolved; // Replace with resolved data
|
|
614
|
-
} catch (err) {
|
|
615
|
-
console.error(`Failed to resolve options for ${fieldName}:`, err);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
});
|
|
619
|
-
await Promise.all(promises);
|
|
620
|
-
}*/
|
|
621
|
-
|
|
622
|
-
async resolveNonDependantFieldOptions(fieldValues) {
|
|
623
|
-
// Store original options before first resolution
|
|
624
|
-
this.storeOriginalFieldOptions();
|
|
625
|
-
|
|
626
|
-
const promises = Object.entries(this.options.fields).map(async ([fieldName, field]) => {
|
|
627
|
-
// Use original options if we have them, otherwise use current field.options
|
|
628
|
-
if (field.dependsOn) {
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
|
-
const originalOptions = this.originalFieldOptions.get(fieldName) || field.options;
|
|
632
|
-
|
|
633
|
-
if (originalOptions && (typeof originalOptions === 'function' || typeof originalOptions === 'string')) {
|
|
634
|
-
try {
|
|
635
|
-
// Pass fieldValues as dependedValues for dependency resolution
|
|
636
|
-
const params = { dependedValues: fieldValues };
|
|
637
|
-
|
|
638
|
-
// Resolve using original options, not the possibly already-resolved ones
|
|
639
|
-
const tempField = { ...field, options: originalOptions };
|
|
640
|
-
const resolved = await this.resolveOptions(tempField, params);
|
|
641
|
-
field.options = resolved; // Replace with resolved data
|
|
642
|
-
} catch (err) {
|
|
643
|
-
console.error(`Failed to resolve options for ${fieldName}:`, err);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
});
|
|
647
|
-
await Promise.all(promises);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
667
|
async createForm(formType = 'create', record = {}) {
|
|
651
668
|
|
|
652
669
|
this.currentFormRecord = record;
|
|
653
670
|
|
|
654
671
|
// Pre-resolve all options for fields depending on nothing, the others are handled down the road when dependancies are calculated
|
|
655
|
-
|
|
656
|
-
await this.resolveNonDependantFieldOptions(record);
|
|
672
|
+
await this.resolveFormFieldOptions(record, formType);
|
|
657
673
|
|
|
658
674
|
const form = FTableDOMHelper.create('form', {
|
|
659
675
|
className: `ftable-dialog-form ftable-${formType}-form`
|
|
@@ -662,12 +678,27 @@ class FTableFormBuilder {
|
|
|
662
678
|
// Build dependency map first
|
|
663
679
|
this.buildDependencyMap();
|
|
664
680
|
|
|
665
|
-
|
|
681
|
+
// Create form fields using for...of instead of forEach, this allows the await to work
|
|
682
|
+
for (const [fieldName, field] of Object.entries(this.options.fields)) {
|
|
666
683
|
if (this.shouldIncludeField(field, formType)) {
|
|
667
|
-
|
|
684
|
+
let fieldWithOptions = { ...field };
|
|
685
|
+
if (!field.dependsOn) {
|
|
686
|
+
const contextOptions = await this.getFieldOptions(fieldName, formType, {
|
|
687
|
+
record,
|
|
688
|
+
source: formType
|
|
689
|
+
});
|
|
690
|
+
fieldWithOptions.options = contextOptions;
|
|
691
|
+
} else {
|
|
692
|
+
// For dependent fields, use placeholder or original options
|
|
693
|
+
// They will be resolved when dependencies change
|
|
694
|
+
fieldWithOptions.options = field.options;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
const fieldContainer = this.createFieldContainer(fieldName, fieldWithOptions, record, formType);
|
|
668
699
|
form.appendChild(fieldContainer);
|
|
669
700
|
}
|
|
670
|
-
}
|
|
701
|
+
}
|
|
671
702
|
|
|
672
703
|
// Set up dependency listeners after all fields are created
|
|
673
704
|
this.setupDependencyListeners(form);
|
|
@@ -675,6 +706,35 @@ class FTableFormBuilder {
|
|
|
675
706
|
return form;
|
|
676
707
|
}
|
|
677
708
|
|
|
709
|
+
async resolveFormFieldOptions(record, formType) {
|
|
710
|
+
const promises = Object.entries(this.options.fields).map(async ([fieldName, field]) => {
|
|
711
|
+
if (field.dependsOn) {
|
|
712
|
+
// Dependent fields will be resolved when dependencies change
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (this.shouldResolveOptions(field.options)) {
|
|
717
|
+
try {
|
|
718
|
+
await this.getFieldOptions(fieldName, formType, {
|
|
719
|
+
record,
|
|
720
|
+
source: formType
|
|
721
|
+
});
|
|
722
|
+
} catch (err) {
|
|
723
|
+
console.error(`Failed to resolve form options for ${fieldName}:`, err);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
await Promise.all(promises);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
shouldResolveOptions(options) {
|
|
732
|
+
return options &&
|
|
733
|
+
(typeof options === 'function' || typeof options === 'string') &&
|
|
734
|
+
!Array.isArray(options) &&
|
|
735
|
+
!(typeof options === 'object' && !Array.isArray(options) && Object.keys(options).length > 0);
|
|
736
|
+
}
|
|
737
|
+
|
|
678
738
|
buildDependencyMap() {
|
|
679
739
|
this.dependencies.clear();
|
|
680
740
|
|
|
@@ -722,7 +782,7 @@ class FTableFormBuilder {
|
|
|
722
782
|
this.handleDependencyChange(form);
|
|
723
783
|
}
|
|
724
784
|
|
|
725
|
-
async resolveOptions(field, params = {}) {
|
|
785
|
+
async resolveOptions(field, params = {}, source = '', noCache = false) {
|
|
726
786
|
if (!field.options) return [];
|
|
727
787
|
|
|
728
788
|
// Case 1: Direct options (array or object)
|
|
@@ -731,13 +791,16 @@ class FTableFormBuilder {
|
|
|
731
791
|
}
|
|
732
792
|
|
|
733
793
|
let result;
|
|
734
|
-
// Create a mutable flag for cache clearing
|
|
735
|
-
let noCache = false;
|
|
736
794
|
|
|
737
795
|
// Enhance params with clearCache() method
|
|
738
796
|
const enhancedParams = {
|
|
739
797
|
...params,
|
|
740
|
-
|
|
798
|
+
source: source,
|
|
799
|
+
clearCache: () => {
|
|
800
|
+
noCache = true;
|
|
801
|
+
// Also update the field's noCache setting for future calls
|
|
802
|
+
this.updateFieldCacheSetting(field, source, true);
|
|
803
|
+
}
|
|
741
804
|
};
|
|
742
805
|
|
|
743
806
|
if (typeof field.options === 'function') {
|
|
@@ -780,41 +843,81 @@ class FTableFormBuilder {
|
|
|
780
843
|
}
|
|
781
844
|
}
|
|
782
845
|
|
|
846
|
+
updateFieldCacheSetting(field, context, skipCache) {
|
|
847
|
+
if (!field.noCache) {
|
|
848
|
+
// Initialize noCache as object for this context
|
|
849
|
+
field.noCache = { [context]: skipCache };
|
|
850
|
+
} else if (typeof field.noCache === 'boolean') {
|
|
851
|
+
// Convert boolean to object, preserving existing behavior for other contexts
|
|
852
|
+
field.noCache = {
|
|
853
|
+
'table': field.noCache,
|
|
854
|
+
'create': field.noCache,
|
|
855
|
+
'edit': field.noCache,
|
|
856
|
+
[context]: skipCache // Override for this context
|
|
857
|
+
};
|
|
858
|
+
} else if (typeof field.noCache === 'object') {
|
|
859
|
+
// Update specific context
|
|
860
|
+
field.noCache[context] = skipCache;
|
|
861
|
+
}
|
|
862
|
+
// Function-based noCache remains unchanged (runtime decision)
|
|
863
|
+
}
|
|
864
|
+
|
|
783
865
|
clearOptionsCache(url = null, params = null) {
|
|
784
866
|
this.optionsCache.clear(url, params);
|
|
785
867
|
}
|
|
786
868
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
const dependedValues = {};
|
|
869
|
+
getFormValues(form) {
|
|
870
|
+
const values = {};
|
|
790
871
|
|
|
791
|
-
// Get all
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
872
|
+
// Get all form elements
|
|
873
|
+
const elements = form.elements;
|
|
874
|
+
|
|
875
|
+
for (let i = 0; i < elements.length; i++) {
|
|
876
|
+
const element = elements[i];
|
|
877
|
+
const name = element.name;
|
|
878
|
+
|
|
879
|
+
if (!name || element.disabled) continue;
|
|
880
|
+
|
|
881
|
+
switch (element.type) {
|
|
882
|
+
case 'checkbox':
|
|
883
|
+
values[name] = element.checked ? element.value || '1' : '0';
|
|
884
|
+
break;
|
|
885
|
+
|
|
886
|
+
case 'radio':
|
|
887
|
+
if (element.checked) {
|
|
888
|
+
values[name] = element.value;
|
|
889
|
+
}
|
|
890
|
+
break;
|
|
891
|
+
|
|
892
|
+
case 'select-multiple':
|
|
893
|
+
values[name] = Array.from(element.selectedOptions).map(option => option.value);
|
|
894
|
+
break;
|
|
895
|
+
|
|
896
|
+
default:
|
|
897
|
+
values[name] = element.value;
|
|
898
|
+
break;
|
|
800
899
|
}
|
|
801
900
|
}
|
|
802
901
|
|
|
803
|
-
|
|
902
|
+
return values;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
async handleDependencyChange(form, changedFieldname = '') {
|
|
906
|
+
// Build dependedValues: { field1: value1, field2: value2 }
|
|
907
|
+
const dependedValues = this.getFormValues(form);
|
|
804
908
|
const formType = form.classList.contains('ftable-create-form') ? 'create' : 'edit';
|
|
805
909
|
const record = this.currentFormRecord || {};
|
|
806
910
|
|
|
807
|
-
// Prepare base params for options function
|
|
808
911
|
const baseParams = {
|
|
809
912
|
record,
|
|
810
913
|
source: formType,
|
|
811
|
-
form,
|
|
914
|
+
form,
|
|
812
915
|
dependedValues
|
|
813
916
|
};
|
|
814
917
|
|
|
815
|
-
// Update each dependent field
|
|
816
918
|
for (const [fieldName, field] of Object.entries(this.options.fields)) {
|
|
817
919
|
if (!field.dependsOn) continue;
|
|
920
|
+
|
|
818
921
|
if (changedFieldname !== '') {
|
|
819
922
|
let dependsOnFields = field.dependsOn
|
|
820
923
|
.split(',')
|
|
@@ -837,31 +940,22 @@ class FTableFormBuilder {
|
|
|
837
940
|
if (datalist) datalist.innerHTML = '';
|
|
838
941
|
}
|
|
839
942
|
|
|
840
|
-
//
|
|
943
|
+
// Resolve options with current context
|
|
841
944
|
const params = {
|
|
842
945
|
...baseParams,
|
|
843
|
-
// Specific for this field
|
|
844
946
|
dependsOnField: field.dependsOn,
|
|
845
947
|
dependsOnValue: dependedValues[field.dependsOn]
|
|
846
948
|
};
|
|
847
949
|
|
|
848
|
-
|
|
849
|
-
const originalOptions = this.originalFieldOptions.get(fieldName) || field.options;
|
|
850
|
-
const tempField = { ...field, options: originalOptions };
|
|
851
|
-
|
|
852
|
-
// Resolve options with full context using original options
|
|
853
|
-
const newOptions = await this.resolveOptions(tempField, params);
|
|
950
|
+
const newOptions = await this.getFieldOptions(fieldName, formType, params);
|
|
854
951
|
|
|
855
|
-
// Populate
|
|
952
|
+
// Populate the input
|
|
856
953
|
if (input.tagName === 'SELECT') {
|
|
857
954
|
this.populateSelectOptions(input, newOptions, '');
|
|
858
955
|
} else if (input.tagName === 'INPUT' && input.list) {
|
|
859
956
|
this.populateDatalistOptions(input.list, newOptions);
|
|
860
957
|
}
|
|
861
958
|
|
|
862
|
-
// at the end of the event chain: trigger change so other depending fields are notified too
|
|
863
|
-
// we don't do this without setTimeout so it triggers after the current loop is finished
|
|
864
|
-
// otherwise the change might trigger too soon
|
|
865
959
|
setTimeout(() => {
|
|
866
960
|
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
867
961
|
}, 0);
|
|
@@ -1181,7 +1275,7 @@ class FTableFormBuilder {
|
|
|
1181
1275
|
const select = FTableDOMHelper.create('select', { attributes });
|
|
1182
1276
|
|
|
1183
1277
|
if (field.options) {
|
|
1184
|
-
//
|
|
1278
|
+
// the field options are already the resolved ones
|
|
1185
1279
|
this.populateSelectOptions(select, field.options, value);
|
|
1186
1280
|
}
|
|
1187
1281
|
|
|
@@ -1741,53 +1835,45 @@ class FTable extends FTableEventEmitter {
|
|
|
1741
1835
|
}
|
|
1742
1836
|
|
|
1743
1837
|
async resolveAsyncFieldOptions() {
|
|
1744
|
-
|
|
1745
|
-
this.formBuilder.storeOriginalFieldOptions();
|
|
1746
|
-
|
|
1747
|
-
for (const fieldName of this.columnList) {
|
|
1838
|
+
const promises = this.columnList.map(async (fieldName) => {
|
|
1748
1839
|
const field = this.options.fields[fieldName];
|
|
1840
|
+
const originalOptions = this.formBuilder.originalFieldOptions.get(fieldName);
|
|
1749
1841
|
|
|
1750
|
-
|
|
1751
|
-
const originalOptions = this.formBuilder.originalFieldOptions.get(fieldName) || field.options;
|
|
1752
|
-
|
|
1753
|
-
if (originalOptions &&
|
|
1754
|
-
(typeof originalOptions === 'function' || typeof originalOptions === 'string') &&
|
|
1755
|
-
!Array.isArray(originalOptions) &&
|
|
1756
|
-
!(typeof originalOptions === 'object' && !Array.isArray(originalOptions) && Object.keys(originalOptions).length > 0)
|
|
1757
|
-
) {
|
|
1842
|
+
if (this.formBuilder.shouldResolveOptions(originalOptions)) {
|
|
1758
1843
|
try {
|
|
1759
|
-
//
|
|
1760
|
-
const
|
|
1761
|
-
|
|
1762
|
-
|
|
1844
|
+
// Check if already resolved to avoid duplicate work
|
|
1845
|
+
const cacheKey = this.formBuilder.generateOptionsCacheKey('table', {});
|
|
1846
|
+
if (!this.formBuilder.resolvedFieldOptions.get(fieldName)?.[cacheKey]) {
|
|
1847
|
+
await this.formBuilder.getFieldOptions(fieldName, 'table');
|
|
1848
|
+
}
|
|
1763
1849
|
} catch (err) {
|
|
1764
|
-
console.error(`Failed to resolve options for ${fieldName}:`, err);
|
|
1850
|
+
console.error(`Failed to resolve table options for ${fieldName}:`, err);
|
|
1765
1851
|
}
|
|
1766
1852
|
}
|
|
1767
|
-
}
|
|
1853
|
+
});
|
|
1854
|
+
|
|
1855
|
+
await Promise.all(promises);
|
|
1856
|
+
// DON'T call refreshDisplayValues() here - let renderTableData do it
|
|
1768
1857
|
}
|
|
1769
1858
|
|
|
1770
|
-
refreshDisplayValues() {
|
|
1859
|
+
async refreshDisplayValues() {
|
|
1771
1860
|
const rows = this.elements.tableBody.querySelectorAll('.ftable-data-row');
|
|
1772
1861
|
if (rows.length === 0) return;
|
|
1773
1862
|
|
|
1774
|
-
|
|
1775
|
-
this.columnList
|
|
1863
|
+
for (const row of rows) {
|
|
1864
|
+
for (const fieldName of this.columnList) {
|
|
1776
1865
|
const field = this.options.fields[fieldName];
|
|
1777
|
-
if (!field.options)
|
|
1778
|
-
|
|
1779
|
-
// Check if options are now resolved (was a function/string before)
|
|
1780
|
-
if (typeof field.options === 'function' || typeof field.options === 'string') {
|
|
1781
|
-
return; // Still unresolved
|
|
1782
|
-
}
|
|
1866
|
+
if (!field.options) continue;
|
|
1783
1867
|
|
|
1784
1868
|
const cell = row.querySelector(`td[data-field-name="${fieldName}"]`);
|
|
1785
|
-
if (!cell)
|
|
1869
|
+
if (!cell) continue;
|
|
1786
1870
|
|
|
1787
|
-
|
|
1871
|
+
// Get table-specific options
|
|
1872
|
+
const options = await this.formBuilder.getFieldOptions(fieldName, 'table');
|
|
1873
|
+
const value = this.getDisplayText(row.recordData, fieldName, options);
|
|
1788
1874
|
cell.innerHTML = field.listEscapeHTML ? FTableDOMHelper.escapeHtml(value) : value;
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1791
1877
|
}
|
|
1792
1878
|
|
|
1793
1879
|
createMainStructure() {
|
|
@@ -1990,7 +2076,7 @@ class FTable extends FTableEventEmitter {
|
|
|
1990
2076
|
case 'datetime-local':
|
|
1991
2077
|
if (typeof FDatepicker !== 'undefined') {
|
|
1992
2078
|
const dateFormat = field.dateFormat || this.options.defaultDateFormat;
|
|
1993
|
-
|
|
2079
|
+
const containerDiv = document.createElement('div');
|
|
1994
2080
|
// Create hidden input
|
|
1995
2081
|
const hiddenInput = FTableDOMHelper.create('input', {
|
|
1996
2082
|
className: 'ftable-toolbarsearch-extra',
|
|
@@ -2011,8 +2097,8 @@ class FTable extends FTableEventEmitter {
|
|
|
2011
2097
|
}
|
|
2012
2098
|
});
|
|
2013
2099
|
// Append both inputs
|
|
2014
|
-
|
|
2015
|
-
|
|
2100
|
+
containerDiv.appendChild(hiddenInput);
|
|
2101
|
+
containerDiv.appendChild(visibleInput);
|
|
2016
2102
|
|
|
2017
2103
|
// Apply FDatepicker
|
|
2018
2104
|
const picker = new FDatepicker(visibleInput, {
|
|
@@ -2021,6 +2107,8 @@ class FTable extends FTableEventEmitter {
|
|
|
2021
2107
|
altFormat: 'Y-m-d'
|
|
2022
2108
|
});
|
|
2023
2109
|
|
|
2110
|
+
input = containerDiv;
|
|
2111
|
+
|
|
2024
2112
|
} else {
|
|
2025
2113
|
input = FTableDOMHelper.create('input', {
|
|
2026
2114
|
className: 'ftable-toolbarsearch',
|
|
@@ -2141,7 +2229,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2141
2229
|
DisplayText: displayText
|
|
2142
2230
|
}));
|
|
2143
2231
|
} else if (field.options) {
|
|
2144
|
-
optionsSource = await this.formBuilder.resolveOptions(field);
|
|
2232
|
+
optionsSource = await this.formBuilder.resolveOptions(field, {}, 'search');
|
|
2145
2233
|
}
|
|
2146
2234
|
|
|
2147
2235
|
// Add empty option only if first option is not already empty
|
|
@@ -3180,9 +3268,10 @@ class FTable extends FTableEventEmitter {
|
|
|
3180
3268
|
});
|
|
3181
3269
|
}
|
|
3182
3270
|
|
|
3183
|
-
getDisplayText(record, fieldName) {
|
|
3271
|
+
getDisplayText(record, fieldName, customOptions = null) {
|
|
3184
3272
|
const field = this.options.fields[fieldName];
|
|
3185
3273
|
const value = record[fieldName];
|
|
3274
|
+
const options = customOptions || field.options;
|
|
3186
3275
|
|
|
3187
3276
|
if (field.display && typeof field.display === 'function') {
|
|
3188
3277
|
return field.display({ record, value });
|
|
@@ -3208,8 +3297,8 @@ class FTable extends FTableEventEmitter {
|
|
|
3208
3297
|
return this.getCheckboxText(fieldName, value);
|
|
3209
3298
|
}
|
|
3210
3299
|
|
|
3211
|
-
if (
|
|
3212
|
-
const option = this.findOptionByValue(
|
|
3300
|
+
if (options) {
|
|
3301
|
+
const option = this.findOptionByValue(options, value);
|
|
3213
3302
|
return option ? option.DisplayText || option.text || option : value;
|
|
3214
3303
|
}
|
|
3215
3304
|
|