@reforgium/statum 1.1.0 → 2.0.1
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/README.md +57 -55
- package/fesm2022/reforgium-statum.mjs +173 -50
- package/fesm2022/reforgium-statum.mjs.map +1 -1
- package/package.json +1 -1
- package/types/reforgium-statum.d.ts +137 -26
- package/types/reforgium-statum.d.ts.map +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { formatDate, isNullable, isDatePeriod, parseToDate, makeQuery, isNumber, isObject, parseToDatePeriod, parseQueryArray, fillUrlWithParams, deepEqual, concatArray, debounceSignal } from '@reforgium/internal';
|
|
2
|
-
import { InjectionToken, inject, signal, computed, DestroyRef, effect, untracked } from '@angular/core';
|
|
3
2
|
import { HttpClient } from '@angular/common/http';
|
|
3
|
+
import { InjectionToken, inject, signal, computed, DestroyRef, effect, untracked } from '@angular/core';
|
|
4
4
|
import { Subject, filter, timer, merge, map } from 'rxjs';
|
|
5
5
|
import { debounce, tap, throttle, finalize } from 'rxjs/operators';
|
|
6
6
|
|
|
@@ -182,7 +182,7 @@ class Serializer {
|
|
|
182
182
|
return;
|
|
183
183
|
}
|
|
184
184
|
if (fields?.type === 'nullable' || isNullable(value, this.config.mapNullable?.includeEmptyString)) {
|
|
185
|
-
const nullableVal = value
|
|
185
|
+
const nullableVal = value ?? null;
|
|
186
186
|
return this.config.mapNullable.format?.(nullableVal) || this.config.mapNullable.replaceWith || nullableVal;
|
|
187
187
|
}
|
|
188
188
|
if (fields?.type === 'string') {
|
|
@@ -247,8 +247,7 @@ class Serializer {
|
|
|
247
247
|
if (field?.type === 'object') {
|
|
248
248
|
try {
|
|
249
249
|
if (this.config.mapObject.deep) {
|
|
250
|
-
|
|
251
|
-
return this.deserializeElement(value);
|
|
250
|
+
return isObject(value) ? this.deserialize(value) : value;
|
|
252
251
|
}
|
|
253
252
|
else {
|
|
254
253
|
// @ts-ignore
|
|
@@ -283,7 +282,11 @@ class Serializer {
|
|
|
283
282
|
return this.config.mapNumber.parse?.(value) || Number(String(value).trim());
|
|
284
283
|
}
|
|
285
284
|
if (field?.type === 'string' || typeof value === 'string') {
|
|
286
|
-
|
|
285
|
+
const parsed = this.config.mapString.parse?.(value);
|
|
286
|
+
if (parsed !== undefined) {
|
|
287
|
+
return parsed;
|
|
288
|
+
}
|
|
289
|
+
return this.config.mapString.trim ? String(value).trim() : value;
|
|
287
290
|
}
|
|
288
291
|
return value;
|
|
289
292
|
}
|
|
@@ -311,8 +314,6 @@ class Serializer {
|
|
|
311
314
|
}
|
|
312
315
|
}
|
|
313
316
|
|
|
314
|
-
const SERIALIZER_CONFIG = new InjectionToken('SERIALIZER_CONFIG');
|
|
315
|
-
|
|
316
317
|
class LruCache {
|
|
317
318
|
limit;
|
|
318
319
|
map = new Map();
|
|
@@ -356,6 +357,9 @@ class LruCache {
|
|
|
356
357
|
values() {
|
|
357
358
|
return Array.from(this.map.values());
|
|
358
359
|
}
|
|
360
|
+
entries() {
|
|
361
|
+
return Array.from(this.map.entries());
|
|
362
|
+
}
|
|
359
363
|
toArray() {
|
|
360
364
|
return Array.from(this.map.values());
|
|
361
365
|
}
|
|
@@ -368,23 +372,31 @@ class LruCache {
|
|
|
368
372
|
}
|
|
369
373
|
|
|
370
374
|
class LocalStorage {
|
|
375
|
+
prefix;
|
|
376
|
+
constructor(prefix = 're') {
|
|
377
|
+
this.prefix = prefix;
|
|
378
|
+
}
|
|
371
379
|
get length() {
|
|
372
|
-
return localStorage.length;
|
|
380
|
+
return Object.keys(localStorage).filter((key) => key.startsWith(this.prefix || '')).length;
|
|
373
381
|
}
|
|
374
382
|
get(key) {
|
|
375
|
-
const raw = localStorage.getItem(
|
|
383
|
+
const raw = localStorage.getItem(this.getSafePrefix(key));
|
|
376
384
|
const parsed = JSON.parse(raw ?? 'null');
|
|
377
385
|
return parsed ?? null;
|
|
378
386
|
}
|
|
379
387
|
set(key, value) {
|
|
380
388
|
const str = JSON.stringify(value);
|
|
381
|
-
localStorage.setItem(
|
|
389
|
+
localStorage.setItem(this.getSafePrefix(key), str);
|
|
382
390
|
}
|
|
383
391
|
remove(key) {
|
|
384
|
-
return localStorage.removeItem(
|
|
392
|
+
return localStorage.removeItem(this.getSafePrefix(key));
|
|
385
393
|
}
|
|
386
394
|
clear() {
|
|
387
|
-
|
|
395
|
+
const keys = Object.keys(localStorage).filter((key) => key.startsWith(this.prefix || ''));
|
|
396
|
+
keys.forEach((key) => localStorage.removeItem(key));
|
|
397
|
+
}
|
|
398
|
+
getSafePrefix(key) {
|
|
399
|
+
return this.prefix ? `${this.prefix}:${key}` : String(key);
|
|
388
400
|
}
|
|
389
401
|
}
|
|
390
402
|
|
|
@@ -408,23 +420,31 @@ class MemoryStorage {
|
|
|
408
420
|
}
|
|
409
421
|
|
|
410
422
|
class SessionStorage {
|
|
423
|
+
prefix;
|
|
424
|
+
constructor(prefix = 're') {
|
|
425
|
+
this.prefix = prefix;
|
|
426
|
+
}
|
|
411
427
|
get length() {
|
|
412
|
-
return sessionStorage.length;
|
|
428
|
+
return Object.keys(sessionStorage).filter((key) => key.startsWith(this.prefix || '')).length;
|
|
413
429
|
}
|
|
414
430
|
get(key) {
|
|
415
|
-
const raw = sessionStorage.getItem(
|
|
431
|
+
const raw = sessionStorage.getItem(this.getSafePrefix(key));
|
|
416
432
|
const parsed = JSON.parse(raw ?? 'null');
|
|
417
433
|
return parsed ?? null;
|
|
418
434
|
}
|
|
419
435
|
set(key, value) {
|
|
420
436
|
const str = JSON.stringify(value);
|
|
421
|
-
sessionStorage.setItem(
|
|
437
|
+
sessionStorage.setItem(this.getSafePrefix(key), str);
|
|
422
438
|
}
|
|
423
439
|
remove(key) {
|
|
424
|
-
return sessionStorage.removeItem(
|
|
440
|
+
return sessionStorage.removeItem(this.getSafePrefix(key));
|
|
425
441
|
}
|
|
426
442
|
clear() {
|
|
427
|
-
|
|
443
|
+
const keys = Object.keys(sessionStorage).filter((key) => key.startsWith(this.prefix || ''));
|
|
444
|
+
keys.forEach((key) => sessionStorage.removeItem(key));
|
|
445
|
+
}
|
|
446
|
+
getSafePrefix(key) {
|
|
447
|
+
return this.prefix ? `${this.prefix}:${key}` : String(key);
|
|
428
448
|
}
|
|
429
449
|
}
|
|
430
450
|
|
|
@@ -453,6 +473,9 @@ const storageStrategy = (strategy) => {
|
|
|
453
473
|
return fabrics[strategy]();
|
|
454
474
|
};
|
|
455
475
|
|
|
476
|
+
// noinspection ES6PreferShortImport
|
|
477
|
+
const STATUM_CONFIG = new InjectionToken('RE_STATUM_CONFIG');
|
|
478
|
+
|
|
456
479
|
/**
|
|
457
480
|
* Error thrown when requested data is missing in the cache.
|
|
458
481
|
*
|
|
@@ -502,13 +525,32 @@ function joinUrl(base, path) {
|
|
|
502
525
|
return base ? `${base.replace(/\/$/, '')}/${path.replace(/^\//, '')}` : path;
|
|
503
526
|
}
|
|
504
527
|
function buildKey(method, path, args) {
|
|
505
|
-
const params =
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
528
|
+
const params = stableStringify(args.params || {});
|
|
529
|
+
const query = stableStringify(args.query || {});
|
|
530
|
+
const payload = stableStringify(args.payload || {});
|
|
531
|
+
return `${method}|${path}|${params}|${query}|${payload}`;
|
|
532
|
+
}
|
|
533
|
+
function stableStringify(value) {
|
|
534
|
+
if (value === null || value === undefined) {
|
|
535
|
+
return '';
|
|
536
|
+
}
|
|
537
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
538
|
+
return String(value);
|
|
539
|
+
}
|
|
540
|
+
if (value instanceof Date) {
|
|
541
|
+
return value.toISOString();
|
|
542
|
+
}
|
|
543
|
+
if (Array.isArray(value)) {
|
|
544
|
+
return `[${value.map((entry) => stableStringify(entry)).join(',')}]`;
|
|
545
|
+
}
|
|
546
|
+
if (typeof value === 'object') {
|
|
547
|
+
const entries = Object.entries(value)
|
|
548
|
+
.filter(([, v]) => v !== undefined)
|
|
549
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
550
|
+
.map(([k, v]) => `${k}:${stableStringify(v)}`);
|
|
551
|
+
return `{${entries.join(',')}}`;
|
|
552
|
+
}
|
|
553
|
+
return String(value);
|
|
512
554
|
}
|
|
513
555
|
|
|
514
556
|
/**
|
|
@@ -647,7 +689,7 @@ class KeyedScheduler {
|
|
|
647
689
|
*/
|
|
648
690
|
class ResourceStore {
|
|
649
691
|
http = inject(HttpClient);
|
|
650
|
-
serializer = new Serializer(inject(
|
|
692
|
+
serializer = new Serializer(inject(STATUM_CONFIG, { optional: true })?.serializer ?? {});
|
|
651
693
|
#value = signal(null, ...(ngDevMode ? [{ debugName: "#value" }] : []));
|
|
652
694
|
#status = signal('idle', ...(ngDevMode ? [{ debugName: "#status" }] : []));
|
|
653
695
|
#error = signal(null, ...(ngDevMode ? [{ debugName: "#error" }] : []));
|
|
@@ -696,6 +738,7 @@ class ResourceStore {
|
|
|
696
738
|
const url = this.buildUrl('GET', args);
|
|
697
739
|
const key = buildKey('GET', url, args);
|
|
698
740
|
const query = this.prepareQuery(args);
|
|
741
|
+
const responseType = cfg.responseType ?? 'json';
|
|
699
742
|
const entry = this.ensureEntry(key);
|
|
700
743
|
const strategy = cfg.strategy ?? 'network-first';
|
|
701
744
|
const ttlMs = cfg.ttlMs ?? this.opts.ttlMs ?? 0;
|
|
@@ -720,15 +763,15 @@ class ResourceStore {
|
|
|
720
763
|
entry.status = isSWR ? 'stale' : 'loading';
|
|
721
764
|
this.promoteCurrent(entry, cfg.promote);
|
|
722
765
|
const task = this.scheduler.schedule(key, mode, delay, this.exec$({
|
|
723
|
-
req$: this.http.get(url, { params: query }),
|
|
766
|
+
req$: this.http.get(url, { params: query, responseType: responseType }),
|
|
724
767
|
entry,
|
|
725
768
|
promote: cfg.promote,
|
|
726
769
|
parseFn: cfg.parseResponse,
|
|
727
770
|
}));
|
|
728
771
|
cfg.dedupe && (entry.inflight = task);
|
|
729
772
|
void task.catch((e) => {
|
|
730
|
-
if (
|
|
731
|
-
|
|
773
|
+
if (isAbort(e)) {
|
|
774
|
+
return;
|
|
732
775
|
}
|
|
733
776
|
});
|
|
734
777
|
return task;
|
|
@@ -807,6 +850,7 @@ class ResourceStore {
|
|
|
807
850
|
const query = this.prepareQuery(args);
|
|
808
851
|
const payload = { ...(this.opts.presetPayload || {}), ...(args.payload || {}) };
|
|
809
852
|
const serializedPayload = this.serializer.serialize(payload);
|
|
853
|
+
const responseType = config.responseType ?? 'json';
|
|
810
854
|
const entry = this.ensureEntry(key);
|
|
811
855
|
if (config.dedupe && entry.inflight) {
|
|
812
856
|
return entry.inflight;
|
|
@@ -817,17 +861,24 @@ class ResourceStore {
|
|
|
817
861
|
this.promoteCurrent(entry, config.promote);
|
|
818
862
|
let req$;
|
|
819
863
|
if (method === 'DELETE') {
|
|
820
|
-
req$ = this.http.delete(url, {
|
|
864
|
+
req$ = this.http.delete(url, {
|
|
865
|
+
body: serializedPayload,
|
|
866
|
+
params: query,
|
|
867
|
+
responseType: responseType,
|
|
868
|
+
});
|
|
821
869
|
}
|
|
822
870
|
else {
|
|
823
871
|
// @ts-ignore
|
|
824
|
-
req$ = this.http[method.toLowerCase()](url, serializedPayload, {
|
|
872
|
+
req$ = this.http[method.toLowerCase()](url, serializedPayload, {
|
|
873
|
+
params: query,
|
|
874
|
+
responseType: responseType,
|
|
875
|
+
});
|
|
825
876
|
}
|
|
826
877
|
const task = this.scheduler.schedule(key, mode, delay, this.exec$({ req$, entry, promote: config.promote, parseFn: config.parseResponse }));
|
|
827
878
|
config.dedupe && (entry.inflight = task);
|
|
828
879
|
void task.catch((e) => {
|
|
829
|
-
if (
|
|
830
|
-
|
|
880
|
+
if (isAbort(e)) {
|
|
881
|
+
return;
|
|
831
882
|
}
|
|
832
883
|
});
|
|
833
884
|
return task;
|
|
@@ -875,8 +926,6 @@ class ResourceStore {
|
|
|
875
926
|
}
|
|
876
927
|
}
|
|
877
928
|
|
|
878
|
-
const PDS_CONFIG = new InjectionToken('RE_PDS_CONFIG');
|
|
879
|
-
|
|
880
929
|
// noinspection ES6PreferShortImport
|
|
881
930
|
/**
|
|
882
931
|
* Store for paginated data (tables/lists) with per-page cache and unified requests.
|
|
@@ -897,7 +946,7 @@ const PDS_CONFIG = new InjectionToken('RE_PDS_CONFIG');
|
|
|
897
946
|
class PaginatedDataStore {
|
|
898
947
|
route;
|
|
899
948
|
config;
|
|
900
|
-
defaultConfig = inject(
|
|
949
|
+
defaultConfig = inject(STATUM_CONFIG, { optional: true })?.paginatedData || {};
|
|
901
950
|
#transport;
|
|
902
951
|
#cache;
|
|
903
952
|
/** Current page data (reactive). */
|
|
@@ -924,8 +973,8 @@ class PaginatedDataStore {
|
|
|
924
973
|
constructor(route, config = {}) {
|
|
925
974
|
this.route = route;
|
|
926
975
|
this.config = config;
|
|
927
|
-
this.#cache = new LruCache(config.cacheSize);
|
|
928
976
|
this.applyConfig(config);
|
|
977
|
+
this.#cache = new LruCache(this.config.cacheSize);
|
|
929
978
|
this.applyPresetMeta();
|
|
930
979
|
this.initTransport();
|
|
931
980
|
inject(DestroyRef).onDestroy(() => this.destroy());
|
|
@@ -1004,7 +1053,7 @@ class PaginatedDataStore {
|
|
|
1004
1053
|
*
|
|
1005
1054
|
* @param params Dictionary of route parameters (e.g., `{ id: '123' }`)
|
|
1006
1055
|
* @param opts Options object
|
|
1007
|
-
* @param opts.reset If `true
|
|
1056
|
+
* @param opts.reset If `true`, resets page to 0, clears cache, total elements count, and items
|
|
1008
1057
|
* @param opts.abort If `true`, aborts all active transport requests and sets loading to false
|
|
1009
1058
|
*
|
|
1010
1059
|
* @example
|
|
@@ -1044,6 +1093,7 @@ class PaginatedDataStore {
|
|
|
1044
1093
|
*/
|
|
1045
1094
|
updateConfig = (config) => {
|
|
1046
1095
|
this.config = { ...this.config, ...config };
|
|
1096
|
+
this.#cache.limit = this.config.cacheSize || this.defaultConfig.defaultCacheSize || 5;
|
|
1047
1097
|
this.applyPresetMeta();
|
|
1048
1098
|
};
|
|
1049
1099
|
/**
|
|
@@ -1152,6 +1202,9 @@ class PaginatedDataStore {
|
|
|
1152
1202
|
hasCache: config.hasCache === undefined ? this.defaultConfig.defaultHasCache : config.hasCache,
|
|
1153
1203
|
cacheSize: config.cacheSize || this.defaultConfig.defaultCacheSize || 5,
|
|
1154
1204
|
};
|
|
1205
|
+
if (this.#cache) {
|
|
1206
|
+
this.#cache.limit = this.config.cacheSize;
|
|
1207
|
+
}
|
|
1155
1208
|
}
|
|
1156
1209
|
applyPresetMeta() {
|
|
1157
1210
|
this.filters = this.config.presetFilters || {};
|
|
@@ -1182,6 +1235,12 @@ class DictStore {
|
|
|
1182
1235
|
apiUrl;
|
|
1183
1236
|
storageKey;
|
|
1184
1237
|
static MAX_CACHE_SIZE = 400;
|
|
1238
|
+
/**
|
|
1239
|
+
* Default dictionary configuration resolved from {@link STATUM_CONFIG}.
|
|
1240
|
+
*
|
|
1241
|
+
* Used as a global fallback when local configuration does not explicitly
|
|
1242
|
+
*/
|
|
1243
|
+
defaultConfig = inject(STATUM_CONFIG, { optional: true })?.dict || {};
|
|
1185
1244
|
#helper;
|
|
1186
1245
|
#lru = new LruCache(_a.MAX_CACHE_SIZE);
|
|
1187
1246
|
#effectRefs = [];
|
|
@@ -1195,7 +1254,7 @@ class DictStore {
|
|
|
1195
1254
|
* With `fixed: true` filters the local cache; with `fixed: false` triggers server search.
|
|
1196
1255
|
*/
|
|
1197
1256
|
searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : []));
|
|
1198
|
-
debouncedSearchText
|
|
1257
|
+
debouncedSearchText;
|
|
1199
1258
|
/**
|
|
1200
1259
|
* Additional filters for server request (or presets).
|
|
1201
1260
|
*/
|
|
@@ -1228,9 +1287,11 @@ class DictStore {
|
|
|
1228
1287
|
* @param storageKey key for saving cache in the selected strategy
|
|
1229
1288
|
* @param cfg behavior (fixed/search, parsers, label/value keys, cache strategy, etc.)
|
|
1230
1289
|
*/
|
|
1231
|
-
constructor(apiUrl, storageKey, {
|
|
1290
|
+
constructor(apiUrl, storageKey, { autoLoad = true, method = this.defaultConfig.defaultRestMethod, presetFilters = this.defaultConfig.defaultPresetFilters, parseResponse, parseRequest, debounceTime = this.defaultConfig.defaultDebounceTime, fixed = true, maxOptionsSize = this.defaultConfig.defaultMaxOptionsSize, labelKey = this.defaultConfig.defaultLabelKey || 'name', valueKey = this.defaultConfig.defaultValueKey || 'code', keyPrefix = this.defaultConfig.defaultPrefix || 're', cacheStrategy = this.defaultConfig.defaultCacheStrategy || 'persist', }) {
|
|
1232
1291
|
this.apiUrl = apiUrl;
|
|
1233
1292
|
this.storageKey = storageKey;
|
|
1293
|
+
const searchDebounce = debounceTime ?? 300;
|
|
1294
|
+
this.debouncedSearchText = debounceSignal(this.searchText, searchDebounce);
|
|
1234
1295
|
this.#helper = new PaginatedDataStore(this.apiUrl, {
|
|
1235
1296
|
method: method,
|
|
1236
1297
|
hasCache: false,
|
|
@@ -1243,10 +1304,11 @@ class DictStore {
|
|
|
1243
1304
|
this.labelKey = labelKey;
|
|
1244
1305
|
this.valueKey = valueKey;
|
|
1245
1306
|
this.maxOptionsSize = maxOptionsSize;
|
|
1246
|
-
this._armed.set(autoLoad);
|
|
1247
1307
|
if (cacheStrategy !== 'memory') {
|
|
1248
1308
|
this.storage = storageStrategy(cacheStrategy);
|
|
1309
|
+
this.storage.prefix = keyPrefix;
|
|
1249
1310
|
}
|
|
1311
|
+
this.setAutoload(autoLoad);
|
|
1250
1312
|
this.restoreFromStorage();
|
|
1251
1313
|
this.#effectRefs.push(effect(() => {
|
|
1252
1314
|
const incoming = this.#helper.items();
|
|
@@ -1377,7 +1439,9 @@ class DictStore {
|
|
|
1377
1439
|
const out = [];
|
|
1378
1440
|
for (let i = 0; i < list.length; i++) {
|
|
1379
1441
|
const val = list[i][key];
|
|
1380
|
-
val
|
|
1442
|
+
String(val ?? '')
|
|
1443
|
+
.toLowerCase()
|
|
1444
|
+
.includes(query) && out.push(list[i]);
|
|
1381
1445
|
}
|
|
1382
1446
|
return out;
|
|
1383
1447
|
}
|
|
@@ -1388,15 +1452,41 @@ class DictStore {
|
|
|
1388
1452
|
}
|
|
1389
1453
|
return typeof raw === 'string' ? raw : String(raw);
|
|
1390
1454
|
}
|
|
1455
|
+
setAutoload(autoLoad) {
|
|
1456
|
+
if (autoLoad === 'whenEmpty') {
|
|
1457
|
+
const isEmpty = !this.storage?.get(this.storageKey)?.length;
|
|
1458
|
+
this._armed.set(isEmpty);
|
|
1459
|
+
}
|
|
1460
|
+
else {
|
|
1461
|
+
this._armed.set(autoLoad);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1391
1464
|
}
|
|
1392
1465
|
_a = DictStore;
|
|
1393
1466
|
|
|
1394
1467
|
class DictLocalStore {
|
|
1468
|
+
/**
|
|
1469
|
+
* Default dictionary configuration resolved from {@link STATUM_CONFIG}.
|
|
1470
|
+
*
|
|
1471
|
+
* Used as a global fallback when local configuration does not explicitly
|
|
1472
|
+
* define dictionary keys.
|
|
1473
|
+
*/
|
|
1474
|
+
defaultConfig = inject(STATUM_CONFIG, { optional: true })?.dict || {};
|
|
1475
|
+
/**
|
|
1476
|
+
* Source dictionary items.
|
|
1477
|
+
*
|
|
1478
|
+
* Represents the full, unfiltered list of dictionary entries.
|
|
1479
|
+
* Used as the base data set for search and option generation.
|
|
1480
|
+
*/
|
|
1395
1481
|
items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
1396
1482
|
#draftItems = signal([], ...(ngDevMode ? [{ debugName: "#draftItems" }] : []));
|
|
1397
1483
|
/**
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1484
|
+
* Computed list of options in `{ label, value }` format.
|
|
1485
|
+
*
|
|
1486
|
+
* Based on the current draft items (after search/filtering).
|
|
1487
|
+
* Respects `maxOptionsSize` to limit the number of generated options.
|
|
1488
|
+
*
|
|
1489
|
+
* Intended for direct use in dropdowns and select-like components.
|
|
1400
1490
|
*/
|
|
1401
1491
|
options = computed(() => {
|
|
1402
1492
|
const options = this.#draftItems().map((it) => ({
|
|
@@ -1409,19 +1499,30 @@ class DictLocalStore {
|
|
|
1409
1499
|
valueKey;
|
|
1410
1500
|
maxOptionsSize;
|
|
1411
1501
|
constructor(items, config) {
|
|
1412
|
-
this.labelKey = config?.labelKey ?? '
|
|
1413
|
-
this.valueKey = config?.valueKey ?? '
|
|
1414
|
-
this.maxOptionsSize = config?.maxOptionsSize ?? 1000;
|
|
1502
|
+
this.labelKey = config?.labelKey ?? (this.defaultConfig.defaultLabelKey || 'name');
|
|
1503
|
+
this.valueKey = config?.valueKey ?? (this.defaultConfig.defaultValueKey || 'code');
|
|
1504
|
+
this.maxOptionsSize = config?.maxOptionsSize ?? this.defaultConfig.defaultMaxOptionsSize ?? 1000;
|
|
1415
1505
|
this.items.set(items);
|
|
1416
1506
|
this.#draftItems.set(items);
|
|
1417
1507
|
}
|
|
1508
|
+
// noinspection JSUnusedGlobalSymbols
|
|
1418
1509
|
/**
|
|
1419
|
-
*
|
|
1510
|
+
* Restore cached dictionary state.
|
|
1511
|
+
*
|
|
1512
|
+
* This implementation is a no-op and exists only for API compatibility
|
|
1513
|
+
* with {@link DictStore}.
|
|
1420
1514
|
*/
|
|
1421
1515
|
restoreCache() { }
|
|
1422
1516
|
/**
|
|
1423
|
-
*
|
|
1424
|
-
*
|
|
1517
|
+
* Resolve display label by value.
|
|
1518
|
+
*
|
|
1519
|
+
* Performs a linear search over dictionary items and returns
|
|
1520
|
+
* the corresponding label for the given value.
|
|
1521
|
+
*
|
|
1522
|
+
* Commonly used for reverse binding (value → label).
|
|
1523
|
+
*
|
|
1524
|
+
* @param value Dictionary value to look up
|
|
1525
|
+
* @returns Resolved label string, or `undefined` if not found
|
|
1425
1526
|
*/
|
|
1426
1527
|
findLabel = (value) => {
|
|
1427
1528
|
for (const item of this.items()) {
|
|
@@ -1431,6 +1532,16 @@ class DictLocalStore {
|
|
|
1431
1532
|
}
|
|
1432
1533
|
return undefined;
|
|
1433
1534
|
};
|
|
1535
|
+
/**
|
|
1536
|
+
* Filter dictionary items by label.
|
|
1537
|
+
*
|
|
1538
|
+
* Applies a case-insensitive substring match against the label field
|
|
1539
|
+
* and updates the internal draft items used for option generation.
|
|
1540
|
+
*
|
|
1541
|
+
* Passing an empty string resets the filter and restores all items.
|
|
1542
|
+
*
|
|
1543
|
+
* @param name Search query string
|
|
1544
|
+
*/
|
|
1434
1545
|
search = (name = '') => {
|
|
1435
1546
|
const items = this.items();
|
|
1436
1547
|
if (name?.length) {
|
|
@@ -1440,6 +1551,18 @@ class DictLocalStore {
|
|
|
1440
1551
|
this.#draftItems.set(items);
|
|
1441
1552
|
}
|
|
1442
1553
|
};
|
|
1554
|
+
/**
|
|
1555
|
+
* Preload additional dictionary items.
|
|
1556
|
+
*
|
|
1557
|
+
* Can either replace the current items completely or append
|
|
1558
|
+
* new entries to the existing list.
|
|
1559
|
+
*
|
|
1560
|
+
* Updates both the source items and the current draft items.
|
|
1561
|
+
*
|
|
1562
|
+
* @param items Items to preload
|
|
1563
|
+
* @param opts Preload options
|
|
1564
|
+
* @param opts.replace When `true`, replaces existing items instead of appending
|
|
1565
|
+
*/
|
|
1443
1566
|
preload = (items, opts) => {
|
|
1444
1567
|
if (opts?.replace) {
|
|
1445
1568
|
this.items.set(items);
|
|
@@ -1457,5 +1580,5 @@ class DictLocalStore {
|
|
|
1457
1580
|
* Generated bundle index. Do not edit.
|
|
1458
1581
|
*/
|
|
1459
1582
|
|
|
1460
|
-
export { AbortError, CacheMissError, DictLocalStore, DictStore, LruCache,
|
|
1583
|
+
export { AbortError, CacheMissError, DictLocalStore, DictStore, LruCache, PaginatedDataStore, ResourceStore, STATUM_CONFIG, Serializer, isAbort, storageStrategy };
|
|
1461
1584
|
//# sourceMappingURL=reforgium-statum.mjs.map
|