@acorex/cdk 20.2.0-next.19 → 20.2.0-next.20
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/common/index.d.ts
CHANGED
|
@@ -210,6 +210,13 @@ declare class AXItemClickEvent<T = any> extends AXClickEvent {
|
|
|
210
210
|
name?: string;
|
|
211
211
|
item: T;
|
|
212
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Fires each time the user select the element.
|
|
215
|
+
* @category Events
|
|
216
|
+
*/
|
|
217
|
+
declare class AXItemSelectedEvent<T = any> extends AXEvent {
|
|
218
|
+
item: T;
|
|
219
|
+
}
|
|
213
220
|
/**
|
|
214
221
|
* Fires each time the component gets focused.
|
|
215
222
|
* @category Events
|
|
@@ -646,6 +653,7 @@ declare function convertArrayToDataSource<T = unknown>(items: T[], options?: {
|
|
|
646
653
|
key: string;
|
|
647
654
|
pageSize: number;
|
|
648
655
|
}): AXDataSource<T>;
|
|
656
|
+
declare function applyDataSourceQuery<T extends Record<string, unknown>>(items: T[], filter: AXDataSourceFilterOption): T[];
|
|
649
657
|
|
|
650
658
|
interface AXDataListQuery {
|
|
651
659
|
take?: number;
|
|
@@ -920,6 +928,10 @@ declare abstract class MXSelectionValueComponent<T = unknown> extends MXValueCom
|
|
|
920
928
|
/** Template string for formatting item display text */
|
|
921
929
|
get textTemplate(): string;
|
|
922
930
|
set textTemplate(value: string);
|
|
931
|
+
private _defaultText;
|
|
932
|
+
/** Text to display when neither textField nor valueField exist on the item */
|
|
933
|
+
get defaultText(): string;
|
|
934
|
+
set defaultText(value: string);
|
|
923
935
|
private _disabledField;
|
|
924
936
|
/** Field name used to determine if an item is disabled */
|
|
925
937
|
get disabledField(): string;
|
|
@@ -930,6 +942,13 @@ declare abstract class MXSelectionValueComponent<T = unknown> extends MXValueCom
|
|
|
930
942
|
set multiple(value: boolean);
|
|
931
943
|
/** Gets the currently selected items */
|
|
932
944
|
get selectedItems(): unknown[];
|
|
945
|
+
onItemSelected: EventEmitter<AXItemSelectedEvent>;
|
|
946
|
+
/**
|
|
947
|
+
* Emitted when an item in the list is clicked.
|
|
948
|
+
*
|
|
949
|
+
* @event
|
|
950
|
+
*/
|
|
951
|
+
onItemClick: EventEmitter<AXItemClickEvent>;
|
|
933
952
|
/**
|
|
934
953
|
* Abstract method to retrieve an item by its key
|
|
935
954
|
* @param key The unique identifier of the item
|
|
@@ -977,8 +996,8 @@ declare abstract class MXSelectionValueComponent<T = unknown> extends MXValueCom
|
|
|
977
996
|
*/
|
|
978
997
|
private createCacheKey;
|
|
979
998
|
/**
|
|
980
|
-
|
|
981
|
-
|
|
999
|
+
* Normalizes currently selected items and updates the data service
|
|
1000
|
+
*/
|
|
982
1001
|
private _normalizeSelectedItems;
|
|
983
1002
|
/**
|
|
984
1003
|
* Unselects the specified items from the selection
|
|
@@ -1007,6 +1026,12 @@ declare abstract class MXSelectionValueComponent<T = unknown> extends MXValueCom
|
|
|
1007
1026
|
* @returns True if the item is disabled
|
|
1008
1027
|
*/
|
|
1009
1028
|
isItemDisabled(item: T): boolean;
|
|
1029
|
+
/**
|
|
1030
|
+
* Checks if an item is currently loading
|
|
1031
|
+
* @param item Item to check
|
|
1032
|
+
* @returns True if the item is loading
|
|
1033
|
+
*/
|
|
1034
|
+
isItemLoading(item: T): boolean;
|
|
1010
1035
|
/**
|
|
1011
1036
|
* Gets the display text for an item using template or text field
|
|
1012
1037
|
* @param item Item to get display text for
|
|
@@ -1027,6 +1052,25 @@ declare abstract class MXSelectionValueComponent<T = unknown> extends MXValueCom
|
|
|
1027
1052
|
* Clears only the cache while preserving selected items
|
|
1028
1053
|
*/
|
|
1029
1054
|
protected softClearSelectionCache(): void;
|
|
1055
|
+
/**
|
|
1056
|
+
* Clears the debouncing cache for specific items to ensure fresh data is displayed
|
|
1057
|
+
*/
|
|
1058
|
+
protected clearItemDebouncingCache(item: T): void;
|
|
1059
|
+
/**
|
|
1060
|
+
* Forces a refresh of the display when items are loaded asynchronously
|
|
1061
|
+
* This should be called when the data source is updated
|
|
1062
|
+
*/
|
|
1063
|
+
protected refreshDisplay(): void;
|
|
1064
|
+
/**
|
|
1065
|
+
* Refreshes the display for all selected items to ensure consistency
|
|
1066
|
+
* This is useful when the data source has been updated
|
|
1067
|
+
*/
|
|
1068
|
+
protected refreshSelectedItemsDisplay(): void;
|
|
1069
|
+
/**
|
|
1070
|
+
* Forces a refresh of all selected items by clearing their debouncing cache
|
|
1071
|
+
* This ensures fresh data is displayed when the data source is updated
|
|
1072
|
+
*/
|
|
1073
|
+
protected forceRefreshSelectedItems(): void;
|
|
1030
1074
|
/**
|
|
1031
1075
|
* Asynchronously loads an item by its key and caches the result
|
|
1032
1076
|
* @param key Key to load item by
|
|
@@ -1110,5 +1154,5 @@ declare class AXHotkeysService {
|
|
|
1110
1154
|
static ɵprov: i0.ɵɵInjectableDeclaration<AXHotkeysService>;
|
|
1111
1155
|
}
|
|
1112
1156
|
|
|
1113
|
-
export { AXAutoFocusDirective, AXAutocompleteParentComponent, AXButtonClickEvent, AXClearableComponent, AXClickEvent, AXClosableComponent, AXCommonModule, AXComponent, AXComponentCloseEvent, AXComponentClosedPromise, AXComponentClosing, AXComponentResult, AXDataSource, AXDomService, AXEvent, AXFocusEvent, AXFocusableComponent, AXHotkeyDirective, AXHotkeysService, AXHtmlEvent, AXInfiniteScrollerDirective, AXInvertedColorDirective, AXItemClickEvent, AXListDataSource, AXNgModelDelayedValueChangedDirective, AXOptionChangedEvent, AXPagedComponent, AXRangeChangedEvent, AXResponsiveDirective, AXRippleDirective, AXSearchableComponent, AXSelectionValueChangedEvent, AXValuableComponent, AXValueChangedEvent, AX_LOCATIONS, AX_PLACEMENT_BOTTOM, AX_PLACEMENT_BOTTOM_END, AX_PLACEMENT_BOTTOM_START, AX_PLACEMENT_END, AX_PLACEMENT_END_BOTTOM, AX_PLACEMENT_END_TOP, AX_PLACEMENT_MAP, AX_PLACEMENT_START, AX_PLACEMENT_START_BOTTOM, AX_PLACEMENT_START_TOP, AX_PLACEMENT_TOP, AX_PLACEMENT_TOP_END, AX_PLACEMENT_TOP_START, AX_SELECTION_DATA_TOKEN, AX_STYLE_COLOR_TYPES, AX_STYLE_LOOK_TYPES, MXBaseComponent, MXButtonBaseComponent, MXColorComponent, MXColorLookComponent, MXInputBaseValueComponent, MXInteractiveComponent, MXLookComponent, MXLookableComponent, MXSelectionBridgeService, MXSelectionValueComponent, MXValueComponent, NXButtonComponent, NXClickEvent, NXColorComponent, NXComponent, NXEvent, NXInteractiveComponent, NXLookComponent, NXNativeEvent, NXValueComponent, TAB_META_KEY, convertArrayToDataSource, convertToPlacement };
|
|
1157
|
+
export { AXAutoFocusDirective, AXAutocompleteParentComponent, AXButtonClickEvent, AXClearableComponent, AXClickEvent, AXClosableComponent, AXCommonModule, AXComponent, AXComponentCloseEvent, AXComponentClosedPromise, AXComponentClosing, AXComponentResult, AXDataSource, AXDomService, AXEvent, AXFocusEvent, AXFocusableComponent, AXHotkeyDirective, AXHotkeysService, AXHtmlEvent, AXInfiniteScrollerDirective, AXInvertedColorDirective, AXItemClickEvent, AXItemSelectedEvent, AXListDataSource, AXNgModelDelayedValueChangedDirective, AXOptionChangedEvent, AXPagedComponent, AXRangeChangedEvent, AXResponsiveDirective, AXRippleDirective, AXSearchableComponent, AXSelectionValueChangedEvent, AXValuableComponent, AXValueChangedEvent, AX_LOCATIONS, AX_PLACEMENT_BOTTOM, AX_PLACEMENT_BOTTOM_END, AX_PLACEMENT_BOTTOM_START, AX_PLACEMENT_END, AX_PLACEMENT_END_BOTTOM, AX_PLACEMENT_END_TOP, AX_PLACEMENT_MAP, AX_PLACEMENT_START, AX_PLACEMENT_START_BOTTOM, AX_PLACEMENT_START_TOP, AX_PLACEMENT_TOP, AX_PLACEMENT_TOP_END, AX_PLACEMENT_TOP_START, AX_SELECTION_DATA_TOKEN, AX_STYLE_COLOR_TYPES, AX_STYLE_LOOK_TYPES, MXBaseComponent, MXButtonBaseComponent, MXColorComponent, MXColorLookComponent, MXInputBaseValueComponent, MXInteractiveComponent, MXLookComponent, MXLookableComponent, MXSelectionBridgeService, MXSelectionValueComponent, MXValueComponent, NXButtonComponent, NXClickEvent, NXColorComponent, NXComponent, NXEvent, NXInteractiveComponent, NXLookComponent, NXNativeEvent, NXValueComponent, TAB_META_KEY, applyDataSourceQuery, convertArrayToDataSource, convertToPlacement };
|
|
1114
1158
|
export type { AXAnimationEasing, AXComponentState, AXConnectedPosition, AXDataListFetchCallbackResult, AXDataListFetchDataCallback, AXDataListItems, AXDataListQuery, AXDataSourceByKeyCallback, AXDataSourceCallbackResult, AXDataSourceChangedEvent, AXDataSourceConfig, AXDataSourceFilterOption, AXDataSourceItemExpandedEvent, AXDataSourceLoadCallback, AXDataSourceOperator, AXDataSourceQuery, AXDataSourceSortOption, AXDirection, AXExpandToggleIcons, AXFilterLogic, AXFormValidationRule, AXHotKeyAction, AXLocation, AXOrientation, AXPagedComponentInterface, AXPlacement, AXPlacementType, AXRange, AXScrollPosition, AXSelectionMode, AXSizeType, AXSortOrder, AXStyleColorType, AXStyleLookType, AXStyleSizeType, AXSurfaceType, DisabledCallback, MXComponentOptionChanged, MXComponentOptionChanging, MXComponentSetOption, NormalizedItem };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { model, output, Component, inject, ElementRef, signal, afterNextRender, Injectable, ChangeDetectorRef, ViewContainerRef, EventEmitter, DOCUMENT, PLATFORM_ID, Renderer2, Input, Directive, NgModule, InjectionToken, Output, input, effect } from '@angular/core';
|
|
3
3
|
import { AXHtmlUtil } from '@acorex/core/utils';
|
|
4
|
-
import { flatten, clone, cloneDeep, isEqual, omit, set } from 'lodash-es';
|
|
4
|
+
import { flatten, clone, cloneDeep, isEqual, omit, set, get } from 'lodash-es';
|
|
5
5
|
import { isBrowser } from '@acorex/core/platform';
|
|
6
6
|
import { AXValidationService } from '@acorex/core/validation';
|
|
7
7
|
import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
|
|
@@ -871,6 +871,12 @@ class AXButtonClickEvent extends AXClickEvent {
|
|
|
871
871
|
*/
|
|
872
872
|
class AXItemClickEvent extends AXClickEvent {
|
|
873
873
|
}
|
|
874
|
+
/**
|
|
875
|
+
* Fires each time the user select the element.
|
|
876
|
+
* @category Events
|
|
877
|
+
*/
|
|
878
|
+
class AXItemSelectedEvent extends AXEvent {
|
|
879
|
+
}
|
|
874
880
|
/**
|
|
875
881
|
* Fires each time the component gets focused.
|
|
876
882
|
* @category Events
|
|
@@ -1167,34 +1173,50 @@ class AXDataSource {
|
|
|
1167
1173
|
}
|
|
1168
1174
|
}
|
|
1169
1175
|
function convertArrayToDataSource(items, options = { key: 'id', pageSize: 100 }) {
|
|
1176
|
+
// Normalize primitives to objects so consumers (e.g., select/list) can rely on
|
|
1177
|
+
// value and text fields without triggering extra byKey lookups.
|
|
1178
|
+
const normalizedItems = items.map((candidate) => {
|
|
1179
|
+
const isObjectItem = candidate != null && typeof candidate === 'object';
|
|
1180
|
+
if (isObjectItem) {
|
|
1181
|
+
return candidate;
|
|
1182
|
+
}
|
|
1183
|
+
// For primitive values, create an object with both key and text
|
|
1184
|
+
return { [options.key]: candidate, text: String(candidate ?? '') };
|
|
1185
|
+
});
|
|
1170
1186
|
const config = {
|
|
1171
1187
|
key: options.key,
|
|
1172
1188
|
pageSize: options.pageSize,
|
|
1173
1189
|
load: async (e) => {
|
|
1174
|
-
const
|
|
1190
|
+
const itemsForFilter = normalizedItems;
|
|
1191
|
+
const resultNormalized = e.filter
|
|
1192
|
+
? applyDataSourceQuery(itemsForFilter, e.filter)
|
|
1193
|
+
: itemsForFilter;
|
|
1194
|
+
const result = resultNormalized;
|
|
1175
1195
|
return {
|
|
1176
1196
|
items: result.slice(e.skip, e.skip + e.take),
|
|
1177
|
-
total:
|
|
1197
|
+
total: resultNormalized.length,
|
|
1178
1198
|
};
|
|
1179
1199
|
},
|
|
1180
|
-
byKey: (v) =>
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
if (!filter?.field || filter.value === undefined || filter.value === null) {
|
|
1186
|
-
return items; // No filter applied
|
|
1187
|
-
}
|
|
1188
|
-
return items.filter((item) => {
|
|
1189
|
-
const itemField = item[filter.field];
|
|
1190
|
-
if (typeof itemField === 'string' && typeof filter.value === 'string') {
|
|
1191
|
-
return itemField.toLowerCase().includes(filter.value.toLowerCase());
|
|
1192
|
-
}
|
|
1193
|
-
return false; // Return false if itemField isn't comparable
|
|
1194
|
-
});
|
|
1200
|
+
byKey: async (v) =>
|
|
1201
|
+
// Search in the normalized list (supports primitive and object arrays)
|
|
1202
|
+
normalizedItems.find((record) => {
|
|
1203
|
+
return record[options.key] == v;
|
|
1204
|
+
}),
|
|
1195
1205
|
};
|
|
1196
1206
|
return new AXDataSource(config);
|
|
1197
1207
|
}
|
|
1208
|
+
function applyDataSourceQuery(items, filter) {
|
|
1209
|
+
if (!filter?.field || filter.value === undefined || filter.value === null) {
|
|
1210
|
+
return items; // No filter applied
|
|
1211
|
+
}
|
|
1212
|
+
return items.filter((item) => {
|
|
1213
|
+
const itemField = item[filter.field];
|
|
1214
|
+
if (typeof itemField === 'string' && typeof filter.value === 'string') {
|
|
1215
|
+
return itemField.toLowerCase().includes(filter.value.toLowerCase());
|
|
1216
|
+
}
|
|
1217
|
+
return false; // Return false if itemField isn't comparable
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1198
1220
|
|
|
1199
1221
|
const AX_STYLE_COLOR_TYPES = [
|
|
1200
1222
|
'primary',
|
|
@@ -1722,8 +1744,18 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1722
1744
|
this._valueField = 'id';
|
|
1723
1745
|
this._textField = 'text';
|
|
1724
1746
|
this._textTemplate = '';
|
|
1747
|
+
this._defaultText = '-';
|
|
1725
1748
|
this._disabledField = 'disabled';
|
|
1726
1749
|
this._multiple = false;
|
|
1750
|
+
// #endregion
|
|
1751
|
+
// #region Events
|
|
1752
|
+
this.onItemSelected = new EventEmitter();
|
|
1753
|
+
/**
|
|
1754
|
+
* Emitted when an item in the list is clicked.
|
|
1755
|
+
*
|
|
1756
|
+
* @event
|
|
1757
|
+
*/
|
|
1758
|
+
this.onItemClick = new EventEmitter();
|
|
1727
1759
|
}
|
|
1728
1760
|
/** Field name used to extract the unique value from items */
|
|
1729
1761
|
get valueField() {
|
|
@@ -1746,6 +1778,13 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1746
1778
|
set textTemplate(value) {
|
|
1747
1779
|
this.setOption({ name: 'textTemplate', value });
|
|
1748
1780
|
}
|
|
1781
|
+
/** Text to display when neither textField nor valueField exist on the item */
|
|
1782
|
+
get defaultText() {
|
|
1783
|
+
return this._defaultText;
|
|
1784
|
+
}
|
|
1785
|
+
set defaultText(value) {
|
|
1786
|
+
this.setOption({ name: 'defaultText', value });
|
|
1787
|
+
}
|
|
1749
1788
|
/** Field name used to determine if an item is disabled */
|
|
1750
1789
|
get disabledField() {
|
|
1751
1790
|
return this._disabledField;
|
|
@@ -1783,16 +1822,22 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1783
1822
|
if (value == null || (isArray && value.length === 0)) {
|
|
1784
1823
|
return this.multiple ? [] : null;
|
|
1785
1824
|
}
|
|
1786
|
-
//
|
|
1825
|
+
// Check if items already have text properties to avoid unnecessary findByKey calls
|
|
1787
1826
|
const itemsToNormalize = isArray ? value : [value];
|
|
1788
|
-
const
|
|
1827
|
+
const needsFindByKey = itemsToNormalize.some(item => {
|
|
1828
|
+
if (typeof item === 'object' && item !== null) {
|
|
1829
|
+
return get(item, this.textField) == null;
|
|
1830
|
+
}
|
|
1831
|
+
return true; // Primitive values need findByKey
|
|
1832
|
+
});
|
|
1833
|
+
const normalizedItems = this.normalizeItemsList(itemsToNormalize, needsFindByKey);
|
|
1789
1834
|
if (normalizedItems.length === 0) {
|
|
1790
1835
|
return this.multiple ? [] : null;
|
|
1791
1836
|
}
|
|
1792
1837
|
// Extract values based on selection mode
|
|
1793
1838
|
return this.multiple
|
|
1794
|
-
? normalizedItems.map(item => item
|
|
1795
|
-
: normalizedItems[0]
|
|
1839
|
+
? normalizedItems.map(item => get(item, this.valueField))
|
|
1840
|
+
: get(normalizedItems[0], this.valueField) ?? null;
|
|
1796
1841
|
}
|
|
1797
1842
|
/**
|
|
1798
1843
|
* Override to normalize selected items when value changes
|
|
@@ -1825,22 +1870,24 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1825
1870
|
normalizeItem(item, findByKey = false) {
|
|
1826
1871
|
const isComplexObject = typeof item === 'object' && item !== null;
|
|
1827
1872
|
const itemRecord = item;
|
|
1828
|
-
const
|
|
1873
|
+
const valueKey = isComplexObject ? get(itemRecord, this.valueField) : item;
|
|
1874
|
+
const key = valueKey != null ? String(valueKey) : String(item);
|
|
1829
1875
|
const cacheKey = this.createCacheKey(key);
|
|
1830
1876
|
// Return cached item if available and has text
|
|
1831
1877
|
const cachedItem = this.dataService.cacheList[cacheKey];
|
|
1832
|
-
if (cachedItem && cachedItem
|
|
1878
|
+
if (cachedItem && get(cachedItem, this.textField) != null) {
|
|
1833
1879
|
return cachedItem;
|
|
1834
1880
|
}
|
|
1835
|
-
const hasTextProperty = !isComplexObject || itemRecord
|
|
1881
|
+
const hasTextProperty = !isComplexObject || get(itemRecord, this.textField) != null;
|
|
1836
1882
|
const normalizedObj = {};
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1883
|
+
// If findByKey is true, we need to resolve the item to get its text
|
|
1884
|
+
// This is needed for string values like 'ir', 'us' that don't have text
|
|
1885
|
+
if (findByKey) {
|
|
1886
|
+
this.handleItemNormalization(normalizedObj, item, key, true, cacheKey);
|
|
1840
1887
|
}
|
|
1841
1888
|
else {
|
|
1842
|
-
//
|
|
1843
|
-
|
|
1889
|
+
// Item already has all required properties, just copy them
|
|
1890
|
+
Object.assign(normalizedObj, item);
|
|
1844
1891
|
}
|
|
1845
1892
|
this.dataService.cacheList[cacheKey] = normalizedObj;
|
|
1846
1893
|
return normalizedObj;
|
|
@@ -1867,7 +1914,7 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1867
1914
|
// Set loading state
|
|
1868
1915
|
obj[this.valueField] = key;
|
|
1869
1916
|
obj['isLoading'] = true;
|
|
1870
|
-
obj[this.textField] = 'Loading';
|
|
1917
|
+
obj[this.textField] = 'Loading...';
|
|
1871
1918
|
promise
|
|
1872
1919
|
.then(result => {
|
|
1873
1920
|
if (typeof result === 'object' && result) {
|
|
@@ -1877,11 +1924,23 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1877
1924
|
obj[this.valueField] = result || key;
|
|
1878
1925
|
obj[this.textField] = result;
|
|
1879
1926
|
}
|
|
1927
|
+
})
|
|
1928
|
+
.catch(error => {
|
|
1929
|
+
console.warn('Failed to load item by key:', key, error);
|
|
1930
|
+
// On error, keep the original value but mark as failed
|
|
1931
|
+
obj[this.textField] = this.defaultText;
|
|
1880
1932
|
})
|
|
1881
1933
|
.finally(() => {
|
|
1882
1934
|
delete obj['isLoading'];
|
|
1935
|
+
delete obj['_displayTextLoading'];
|
|
1936
|
+
delete obj['_loadingTriggered'];
|
|
1883
1937
|
this.dataService.cacheList[cacheKey] = obj;
|
|
1884
|
-
|
|
1938
|
+
// Use setTimeout to defer change detection and prevent immediate re-rendering
|
|
1939
|
+
setTimeout(() => {
|
|
1940
|
+
if (this.cdr) {
|
|
1941
|
+
this.cdr.markForCheck();
|
|
1942
|
+
}
|
|
1943
|
+
}, 0);
|
|
1885
1944
|
});
|
|
1886
1945
|
}
|
|
1887
1946
|
/**
|
|
@@ -1891,12 +1950,16 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1891
1950
|
const isSourceObject = typeof sourceItem === 'object' && sourceItem !== null;
|
|
1892
1951
|
const sourceRecord = sourceItem;
|
|
1893
1952
|
if (isComplexSource && isSourceObject) {
|
|
1894
|
-
|
|
1895
|
-
|
|
1953
|
+
const valueFromSource = get(sourceRecord, this.valueField);
|
|
1954
|
+
const textFromSource = get(sourceRecord, this.textField);
|
|
1955
|
+
obj[this.valueField] = (valueFromSource ?? textFromSource);
|
|
1956
|
+
obj[this.textField] = (textFromSource ?? valueFromSource ?? this.defaultText);
|
|
1896
1957
|
}
|
|
1897
1958
|
else {
|
|
1898
|
-
|
|
1899
|
-
|
|
1959
|
+
const valueFromSource = isSourceObject ? get(sourceRecord, this.valueField) : sourceItem;
|
|
1960
|
+
const textFromSource = isSourceObject ? get(sourceRecord, this.textField) : sourceItem;
|
|
1961
|
+
obj[this.valueField] = (valueFromSource ?? textFromSource);
|
|
1962
|
+
obj[this.textField] = (textFromSource ?? valueFromSource ?? this.defaultText);
|
|
1900
1963
|
}
|
|
1901
1964
|
}
|
|
1902
1965
|
/**
|
|
@@ -1906,13 +1969,19 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1906
1969
|
return `k-${key}`;
|
|
1907
1970
|
}
|
|
1908
1971
|
/**
|
|
1909
|
-
|
|
1910
|
-
|
|
1972
|
+
* Normalizes currently selected items and updates the data service
|
|
1973
|
+
*/
|
|
1911
1974
|
_normalizeSelectedItems() {
|
|
1912
1975
|
const values = Array.isArray(this.value)
|
|
1913
1976
|
? this.value
|
|
1914
1977
|
: this.value != null ? [this.value] : [];
|
|
1915
|
-
|
|
1978
|
+
// Only call normalizeItem with findByKey=true if the item doesn't have text
|
|
1979
|
+
// This prevents unnecessary API calls for items that already have text
|
|
1980
|
+
this.dataService.selectedItems = values.map(value => {
|
|
1981
|
+
const hasText = typeof value === 'object' && value !== null &&
|
|
1982
|
+
get(value, this.textField) != null;
|
|
1983
|
+
return this.normalizeItem(value, !hasText);
|
|
1984
|
+
});
|
|
1916
1985
|
}
|
|
1917
1986
|
// #endregion
|
|
1918
1987
|
// #region Public Selection Methods
|
|
@@ -1926,7 +1995,7 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1926
1995
|
return;
|
|
1927
1996
|
}
|
|
1928
1997
|
const normalizedItems = this.normalizeItemsList(items);
|
|
1929
|
-
const newSelectedItems = this.selectedItems.filter(selectedItem => !normalizedItems.some(normalizedItem => normalizedItem
|
|
1998
|
+
const newSelectedItems = this.selectedItems.filter(selectedItem => !normalizedItems.some(normalizedItem => get(normalizedItem, this.valueField) === get(selectedItem, this.valueField)));
|
|
1930
1999
|
this.commitValue(newSelectedItems, true);
|
|
1931
2000
|
}
|
|
1932
2001
|
/**
|
|
@@ -1934,13 +2003,20 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1934
2003
|
* @param items Items to select
|
|
1935
2004
|
*/
|
|
1936
2005
|
selectItems(...items) {
|
|
1937
|
-
if (!items?.length)
|
|
2006
|
+
if (!items?.length) {
|
|
1938
2007
|
return;
|
|
2008
|
+
}
|
|
1939
2009
|
const currentValue = Array.isArray(this.value) ? this.value : [this.value];
|
|
1940
2010
|
const normalizedItems = this.normalizeItemsList(items);
|
|
1941
2011
|
const newSelectedItems = this.multiple
|
|
1942
2012
|
? [...currentValue, ...normalizedItems]
|
|
1943
2013
|
: normalizedItems;
|
|
2014
|
+
this.onItemSelected.emit({
|
|
2015
|
+
component: this,
|
|
2016
|
+
isUserInteraction: true,
|
|
2017
|
+
item: newSelectedItems[0],
|
|
2018
|
+
htmlElement: this.getHostElement()
|
|
2019
|
+
});
|
|
1944
2020
|
this.commitValue(newSelectedItems, true);
|
|
1945
2021
|
}
|
|
1946
2022
|
/**
|
|
@@ -1968,7 +2044,8 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1968
2044
|
*/
|
|
1969
2045
|
isItemSelected(item) {
|
|
1970
2046
|
const normalizedItem = this.normalizeItem(item);
|
|
1971
|
-
|
|
2047
|
+
const normalizedValue = get(normalizedItem, this.valueField);
|
|
2048
|
+
return this.selectedItems.some(selectedItem => get(selectedItem, this.valueField) === normalizedValue);
|
|
1972
2049
|
}
|
|
1973
2050
|
/**
|
|
1974
2051
|
* Checks if an item is disabled
|
|
@@ -1978,9 +2055,18 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1978
2055
|
isItemDisabled(item) {
|
|
1979
2056
|
const itemRecord = item;
|
|
1980
2057
|
return (this.disabled ||
|
|
1981
|
-
coerceBooleanProperty(itemRecord
|
|
2058
|
+
coerceBooleanProperty(get(itemRecord, this.disabledField)) === true ||
|
|
1982
2059
|
(this.disabledCallback?.({ item, index: -1 }) ?? false));
|
|
1983
2060
|
}
|
|
2061
|
+
/**
|
|
2062
|
+
* Checks if an item is currently loading
|
|
2063
|
+
* @param item Item to check
|
|
2064
|
+
* @returns True if the item is loading
|
|
2065
|
+
*/
|
|
2066
|
+
isItemLoading(item) {
|
|
2067
|
+
const normalizedItem = this.normalizeItem(item);
|
|
2068
|
+
return normalizedItem['isLoading'] === true;
|
|
2069
|
+
}
|
|
1984
2070
|
// #endregion
|
|
1985
2071
|
// #region Protected Utility Methods
|
|
1986
2072
|
/**
|
|
@@ -1989,22 +2075,108 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1989
2075
|
* @returns Formatted display text
|
|
1990
2076
|
*/
|
|
1991
2077
|
getDisplayText(item) {
|
|
1992
|
-
|
|
2078
|
+
// Prevent recursive calls that could cause infinite loops
|
|
2079
|
+
if (item && typeof item === 'object' && item['_displayTextLoading']) {
|
|
2080
|
+
return 'Loading...';
|
|
2081
|
+
}
|
|
2082
|
+
// Add timestamp-based debouncing to prevent excessive calls for ALL item types
|
|
2083
|
+
const now = Date.now();
|
|
2084
|
+
const itemKey = typeof item === 'string' ? item : get(item, this.valueField);
|
|
2085
|
+
const cacheKey = this.createCacheKey(String(itemKey));
|
|
2086
|
+
// Check if we have a recent result in the cache to prevent duplicate processing
|
|
2087
|
+
const cachedResult = this.dataService.cacheList[cacheKey];
|
|
2088
|
+
if (cachedResult && cachedResult['_lastDisplayTextCall']) {
|
|
2089
|
+
const timeSinceLastCall = now - cachedResult['_lastDisplayTextCall'];
|
|
2090
|
+
if (timeSinceLastCall < 50) { // 50ms debounce
|
|
2091
|
+
return cachedResult['_lastDisplayTextResult'] || String(itemKey || '');
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
// For string values, we need to normalize with findByKey=true to trigger async loading
|
|
2095
|
+
const isStringValue = typeof item === 'string';
|
|
2096
|
+
const normalizedItem = this.normalizeItem(item, isStringValue);
|
|
2097
|
+
// Check if this item is already in the cache with proper text
|
|
2098
|
+
const itemValue = get(normalizedItem, this.valueField);
|
|
2099
|
+
if (itemValue != null && itemValue !== '') {
|
|
2100
|
+
const cacheKey = this.createCacheKey(String(itemValue));
|
|
2101
|
+
const cachedItem = this.dataService.cacheList[cacheKey];
|
|
2102
|
+
if (cachedItem && get(cachedItem, this.textField) != null) {
|
|
2103
|
+
// Use cached text if available
|
|
2104
|
+
const cachedText = get(cachedItem, this.textField);
|
|
2105
|
+
if (cachedText && cachedText !== 'Loading...') {
|
|
2106
|
+
const result = String(cachedText);
|
|
2107
|
+
// Cache the timestamp and result for debouncing in the cache (works for all item types)
|
|
2108
|
+
if (cachedItem) {
|
|
2109
|
+
cachedItem['_lastDisplayTextCall'] = now;
|
|
2110
|
+
cachedItem['_lastDisplayTextResult'] = result;
|
|
2111
|
+
}
|
|
2112
|
+
return result;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
1993
2116
|
// Try template formatting first
|
|
1994
2117
|
if (this.textTemplate) {
|
|
1995
2118
|
const formattedTemplate = this.formatService.format(this.textTemplate, 'string', normalizedItem);
|
|
1996
2119
|
if (formattedTemplate !== this.textTemplate) {
|
|
1997
|
-
|
|
2120
|
+
const result = formattedTemplate;
|
|
2121
|
+
// Cache the timestamp and result for debouncing in the cache
|
|
2122
|
+
if (normalizedItem) {
|
|
2123
|
+
normalizedItem['_lastDisplayTextCall'] = now;
|
|
2124
|
+
normalizedItem['_lastDisplayTextResult'] = result;
|
|
2125
|
+
}
|
|
2126
|
+
return result;
|
|
1998
2127
|
}
|
|
1999
2128
|
}
|
|
2000
2129
|
// Use text field or fallback to value field
|
|
2001
|
-
|
|
2002
|
-
|
|
2130
|
+
const textValue = get(normalizedItem, this.textField);
|
|
2131
|
+
if (textValue != null && textValue !== '') {
|
|
2132
|
+
const result = String(textValue);
|
|
2133
|
+
// Cache the timestamp and result for debouncing in the cache
|
|
2134
|
+
if (normalizedItem) {
|
|
2135
|
+
normalizedItem['_lastDisplayTextCall'] = now;
|
|
2136
|
+
normalizedItem['_lastDisplayTextResult'] = result;
|
|
2137
|
+
}
|
|
2138
|
+
return result;
|
|
2139
|
+
}
|
|
2140
|
+
// If text is missing and item is loading, show loading state
|
|
2141
|
+
if (normalizedItem['isLoading']) {
|
|
2142
|
+
const result = 'Loading...';
|
|
2143
|
+
// Cache the timestamp and result for debouncing in the cache
|
|
2144
|
+
if (normalizedItem) {
|
|
2145
|
+
normalizedItem['_lastDisplayTextCall'] = now;
|
|
2146
|
+
normalizedItem['_lastDisplayTextResult'] = result;
|
|
2147
|
+
}
|
|
2148
|
+
return result;
|
|
2003
2149
|
}
|
|
2004
2150
|
// Attempt to load item by key if text is missing
|
|
2005
|
-
const value = normalizedItem
|
|
2006
|
-
|
|
2007
|
-
|
|
2151
|
+
const value = get(normalizedItem, this.valueField);
|
|
2152
|
+
if (value == null || value === '') {
|
|
2153
|
+
const result = this.defaultText;
|
|
2154
|
+
// Cache the timestamp and result for debouncing in the cache
|
|
2155
|
+
if (normalizedItem) {
|
|
2156
|
+
normalizedItem['_lastDisplayTextCall'] = now;
|
|
2157
|
+
normalizedItem['_lastDisplayTextResult'] = result;
|
|
2158
|
+
}
|
|
2159
|
+
return result;
|
|
2160
|
+
}
|
|
2161
|
+
// If we have a value but no text, trigger async loading only once
|
|
2162
|
+
// Use a flag to prevent multiple calls
|
|
2163
|
+
if (!normalizedItem['_loadingTriggered'] && !normalizedItem[this.textField]) {
|
|
2164
|
+
normalizedItem['_loadingTriggered'] = true;
|
|
2165
|
+
normalizedItem['_displayTextLoading'] = true;
|
|
2166
|
+
// Use setTimeout to defer the async loading and prevent immediate change detection
|
|
2167
|
+
setTimeout(() => {
|
|
2168
|
+
this.loadAndCacheItemByKey(value);
|
|
2169
|
+
}, 0);
|
|
2170
|
+
}
|
|
2171
|
+
// Return the value itself as a fallback
|
|
2172
|
+
// This will be updated once the async loading completes
|
|
2173
|
+
const result = String(value);
|
|
2174
|
+
// Cache the timestamp and result for debouncing in the cache
|
|
2175
|
+
if (normalizedItem) {
|
|
2176
|
+
normalizedItem['_lastDisplayTextCall'] = now;
|
|
2177
|
+
normalizedItem['_lastDisplayTextResult'] = result;
|
|
2178
|
+
}
|
|
2179
|
+
return result;
|
|
2008
2180
|
}
|
|
2009
2181
|
/**
|
|
2010
2182
|
* Gets the value of an item
|
|
@@ -2013,7 +2185,7 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
2013
2185
|
*/
|
|
2014
2186
|
getValue(item) {
|
|
2015
2187
|
const normalizedItem = this.normalizeItem(item);
|
|
2016
|
-
return normalizedItem
|
|
2188
|
+
return get(normalizedItem, this.valueField);
|
|
2017
2189
|
}
|
|
2018
2190
|
/**
|
|
2019
2191
|
* Clears the selection cache and selected items
|
|
@@ -2030,6 +2202,52 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
2030
2202
|
this.dataService.cacheList = {};
|
|
2031
2203
|
this.cdr.markForCheck();
|
|
2032
2204
|
}
|
|
2205
|
+
/**
|
|
2206
|
+
* Clears the debouncing cache for specific items to ensure fresh data is displayed
|
|
2207
|
+
*/
|
|
2208
|
+
clearItemDebouncingCache(item) {
|
|
2209
|
+
// Get the cache key for this item
|
|
2210
|
+
const itemKey = typeof item === 'string' ? item : get(item, this.valueField);
|
|
2211
|
+
if (itemKey != null) {
|
|
2212
|
+
const cacheKey = this.createCacheKey(String(itemKey));
|
|
2213
|
+
const cachedItem = this.dataService.cacheList[cacheKey];
|
|
2214
|
+
if (cachedItem) {
|
|
2215
|
+
delete cachedItem['_lastDisplayTextCall'];
|
|
2216
|
+
delete cachedItem['_lastDisplayTextResult'];
|
|
2217
|
+
delete cachedItem['_displayTextLoading'];
|
|
2218
|
+
delete cachedItem['_loadingTriggered'];
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
/**
|
|
2223
|
+
* Forces a refresh of the display when items are loaded asynchronously
|
|
2224
|
+
* This should be called when the data source is updated
|
|
2225
|
+
*/
|
|
2226
|
+
refreshDisplay() {
|
|
2227
|
+
this.cdr.markForCheck();
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* Refreshes the display for all selected items to ensure consistency
|
|
2231
|
+
* This is useful when the data source has been updated
|
|
2232
|
+
*/
|
|
2233
|
+
refreshSelectedItemsDisplay() {
|
|
2234
|
+
// Force a refresh of the display without re-normalizing to avoid infinite loops
|
|
2235
|
+
if (this.selectedItems.length > 0) {
|
|
2236
|
+
this.cdr.markForCheck();
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* Forces a refresh of all selected items by clearing their debouncing cache
|
|
2241
|
+
* This ensures fresh data is displayed when the data source is updated
|
|
2242
|
+
*/
|
|
2243
|
+
forceRefreshSelectedItems() {
|
|
2244
|
+
if (this.selectedItems.length > 0) {
|
|
2245
|
+
this.selectedItems.forEach(item => {
|
|
2246
|
+
this.clearItemDebouncingCache(item);
|
|
2247
|
+
});
|
|
2248
|
+
this.cdr.markForCheck();
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2033
2251
|
// #endregion
|
|
2034
2252
|
// #region Private Helper Methods
|
|
2035
2253
|
/**
|
|
@@ -2042,12 +2260,29 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
2042
2260
|
if (item) {
|
|
2043
2261
|
const normalizedItem = this.normalizeItem(item);
|
|
2044
2262
|
const cacheKey = this.createCacheKey(String(normalizedItem[this.valueField]));
|
|
2263
|
+
// Update the cache with the loaded item
|
|
2045
2264
|
this.dataService.cacheList[cacheKey] = normalizedItem;
|
|
2046
|
-
|
|
2265
|
+
// Clear loading flags
|
|
2266
|
+
if (this.dataService.cacheList[cacheKey]) {
|
|
2267
|
+
delete this.dataService.cacheList[cacheKey]['_displayTextLoading'];
|
|
2268
|
+
delete this.dataService.cacheList[cacheKey]['_loadingTriggered'];
|
|
2269
|
+
}
|
|
2270
|
+
// Use setTimeout to defer change detection and prevent immediate re-rendering
|
|
2271
|
+
setTimeout(() => {
|
|
2272
|
+
if (this.cdr) {
|
|
2273
|
+
this.cdr.markForCheck();
|
|
2274
|
+
}
|
|
2275
|
+
}, 0);
|
|
2047
2276
|
}
|
|
2048
2277
|
}
|
|
2049
2278
|
catch (error) {
|
|
2050
2279
|
console.warn('Failed to load item by key:', key, error);
|
|
2280
|
+
// Clear loading flags on error too
|
|
2281
|
+
const cacheKey = this.createCacheKey(String(key));
|
|
2282
|
+
if (this.dataService.cacheList[cacheKey]) {
|
|
2283
|
+
delete this.dataService.cacheList[cacheKey]['_displayTextLoading'];
|
|
2284
|
+
delete this.dataService.cacheList[cacheKey]['_loadingTriggered'];
|
|
2285
|
+
}
|
|
2051
2286
|
}
|
|
2052
2287
|
}
|
|
2053
2288
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: MXSelectionValueComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
@@ -2215,5 +2450,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImpor
|
|
|
2215
2450
|
* Generated bundle index. Do not edit.
|
|
2216
2451
|
*/
|
|
2217
2452
|
|
|
2218
|
-
export { AXAutoFocusDirective, AXAutocompleteParentComponent, AXButtonClickEvent, AXClearableComponent, AXClickEvent, AXClosableComponent, AXCommonModule, AXComponent, AXComponentCloseEvent, AXComponentClosedPromise, AXComponentClosing, AXComponentResult, AXDataSource, AXDomService, AXEvent, AXFocusEvent, AXFocusableComponent, AXHotkeyDirective, AXHotkeysService, AXHtmlEvent, AXInfiniteScrollerDirective, AXInvertedColorDirective, AXItemClickEvent, AXListDataSource, AXNgModelDelayedValueChangedDirective, AXOptionChangedEvent, AXPagedComponent, AXRangeChangedEvent, AXResponsiveDirective, AXRippleDirective, AXSearchableComponent, AXSelectionValueChangedEvent, AXValuableComponent, AXValueChangedEvent, AX_LOCATIONS, AX_PLACEMENT_BOTTOM, AX_PLACEMENT_BOTTOM_END, AX_PLACEMENT_BOTTOM_START, AX_PLACEMENT_END, AX_PLACEMENT_END_BOTTOM, AX_PLACEMENT_END_TOP, AX_PLACEMENT_MAP, AX_PLACEMENT_START, AX_PLACEMENT_START_BOTTOM, AX_PLACEMENT_START_TOP, AX_PLACEMENT_TOP, AX_PLACEMENT_TOP_END, AX_PLACEMENT_TOP_START, AX_SELECTION_DATA_TOKEN, AX_STYLE_COLOR_TYPES, AX_STYLE_LOOK_TYPES, MXBaseComponent, MXButtonBaseComponent, MXColorComponent, MXColorLookComponent, MXInputBaseValueComponent, MXInteractiveComponent, MXLookComponent, MXLookableComponent, MXSelectionBridgeService, MXSelectionValueComponent, MXValueComponent, NXButtonComponent, NXClickEvent, NXColorComponent, NXComponent, NXEvent, NXInteractiveComponent, NXLookComponent, NXNativeEvent, NXValueComponent, TAB_META_KEY, convertArrayToDataSource, convertToPlacement };
|
|
2453
|
+
export { AXAutoFocusDirective, AXAutocompleteParentComponent, AXButtonClickEvent, AXClearableComponent, AXClickEvent, AXClosableComponent, AXCommonModule, AXComponent, AXComponentCloseEvent, AXComponentClosedPromise, AXComponentClosing, AXComponentResult, AXDataSource, AXDomService, AXEvent, AXFocusEvent, AXFocusableComponent, AXHotkeyDirective, AXHotkeysService, AXHtmlEvent, AXInfiniteScrollerDirective, AXInvertedColorDirective, AXItemClickEvent, AXItemSelectedEvent, AXListDataSource, AXNgModelDelayedValueChangedDirective, AXOptionChangedEvent, AXPagedComponent, AXRangeChangedEvent, AXResponsiveDirective, AXRippleDirective, AXSearchableComponent, AXSelectionValueChangedEvent, AXValuableComponent, AXValueChangedEvent, AX_LOCATIONS, AX_PLACEMENT_BOTTOM, AX_PLACEMENT_BOTTOM_END, AX_PLACEMENT_BOTTOM_START, AX_PLACEMENT_END, AX_PLACEMENT_END_BOTTOM, AX_PLACEMENT_END_TOP, AX_PLACEMENT_MAP, AX_PLACEMENT_START, AX_PLACEMENT_START_BOTTOM, AX_PLACEMENT_START_TOP, AX_PLACEMENT_TOP, AX_PLACEMENT_TOP_END, AX_PLACEMENT_TOP_START, AX_SELECTION_DATA_TOKEN, AX_STYLE_COLOR_TYPES, AX_STYLE_LOOK_TYPES, MXBaseComponent, MXButtonBaseComponent, MXColorComponent, MXColorLookComponent, MXInputBaseValueComponent, MXInteractiveComponent, MXLookComponent, MXLookableComponent, MXSelectionBridgeService, MXSelectionValueComponent, MXValueComponent, NXButtonComponent, NXClickEvent, NXColorComponent, NXComponent, NXEvent, NXInteractiveComponent, NXLookComponent, NXNativeEvent, NXValueComponent, TAB_META_KEY, applyDataSourceQuery, convertArrayToDataSource, convertToPlacement };
|
|
2219
2454
|
//# sourceMappingURL=acorex-cdk-common.mjs.map
|