@reforgium/statum 2.1.1 → 2.2.0
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 +15 -16
- package/fesm2022/reforgium-statum.mjs +25 -18
- package/package.json +1 -1
- package/types/reforgium-statum.d.ts +10 -15
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @reforgium/statum
|
|
1
|
+
# @reforgium/statum
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@reforgium/statum)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -181,7 +181,7 @@ entities.removeOne(1);
|
|
|
181
181
|
|
|
182
182
|
## PaginatedDataStore
|
|
183
183
|
|
|
184
|
-
Lightweight store for server-side pagination
|
|
184
|
+
Lightweight store for server-side pagination, filtering, and page cache.
|
|
185
185
|
|
|
186
186
|
### When to use
|
|
187
187
|
|
|
@@ -201,23 +201,22 @@ Lightweight store for server-side pagination with sorting, filtering, and page c
|
|
|
201
201
|
| pageSize | `number` | Page size |
|
|
202
202
|
| totalElements | `number` | Total items on server |
|
|
203
203
|
| filters | `Partial<F>` | Active filters |
|
|
204
|
-
| sort | `string \| undefined` | Sort as `"field,asc"` / `"field,desc"` |
|
|
205
204
|
|
|
206
205
|
### Methods
|
|
207
206
|
|
|
208
|
-
| Method | Description
|
|
209
|
-
|
|
210
|
-
| refresh | Repeat request with current params
|
|
211
|
-
| updatePage | Change page (can use cache)
|
|
212
|
-
| updatePageSize | Change page size (can use cache)
|
|
213
|
-
| updateFilters | Merge filters, reset cache, go to page 0
|
|
214
|
-
| setFilters | Replace filters, reset cache
|
|
215
|
-
| updateQuery | Map table events to `page
|
|
216
|
-
| updateRoute | Change endpoint, reset meta/cache
|
|
217
|
-
| setRouteParams | Fill route url with params
|
|
218
|
-
| updateConfig | Patch config and apply presets
|
|
219
|
-
| copy | Copy config/meta from another store
|
|
220
|
-
| destroy | Manual destroying of caches and abort requests
|
|
207
|
+
| Method | Description |
|
|
208
|
+
|----------------|------------------------------------------------|
|
|
209
|
+
| refresh | Repeat request with current params |
|
|
210
|
+
| updatePage | Change page (can use cache) |
|
|
211
|
+
| updatePageSize | Change page size (can use cache) |
|
|
212
|
+
| updateFilters | Merge filters, reset cache, go to page 0 |
|
|
213
|
+
| setFilters | Replace filters, reset cache, go to page 0 |
|
|
214
|
+
| updateQuery | Map table events to `page` |
|
|
215
|
+
| updateRoute | Change endpoint, reset meta/cache |
|
|
216
|
+
| setRouteParams | Fill route url with params |
|
|
217
|
+
| updateConfig | Patch config and apply presets |
|
|
218
|
+
| copy | Copy config/meta from another store |
|
|
219
|
+
| destroy | Manual destroying of caches and abort requests |
|
|
221
220
|
|
|
222
221
|
Example:
|
|
223
222
|
|
|
@@ -1013,7 +1013,14 @@ class ResourceStore {
|
|
|
1013
1013
|
abort(method, args, reason) {
|
|
1014
1014
|
const url = this.buildUrl(method, args);
|
|
1015
1015
|
const key = buildKey(method, url, args);
|
|
1016
|
+
const entry = this.entries.get(key);
|
|
1016
1017
|
this.trace({ type: 'abort', method, key });
|
|
1018
|
+
if (entry) {
|
|
1019
|
+
entry.inflight = undefined;
|
|
1020
|
+
if (entry.status === 'loading' || entry.status === 'stale') {
|
|
1021
|
+
entry.status = 'idle';
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1017
1024
|
this.scheduler.cancel?.(key, reason);
|
|
1018
1025
|
}
|
|
1019
1026
|
/**
|
|
@@ -1023,6 +1030,12 @@ class ResourceStore {
|
|
|
1023
1030
|
*/
|
|
1024
1031
|
abortAll(reason) {
|
|
1025
1032
|
this.trace({ type: 'abort', method: 'GET', key: '*' });
|
|
1033
|
+
this.entries.forEach((entry) => {
|
|
1034
|
+
entry.inflight = undefined;
|
|
1035
|
+
if (entry.status === 'loading' || entry.status === 'stale') {
|
|
1036
|
+
entry.status = 'idle';
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1026
1039
|
this.scheduler.cancelAll?.(reason);
|
|
1027
1040
|
}
|
|
1028
1041
|
ensureEntry(key) {
|
|
@@ -1212,7 +1225,7 @@ class ResourceStore {
|
|
|
1212
1225
|
*
|
|
1213
1226
|
* Provides:
|
|
1214
1227
|
* - reactive signals: `items`, `loading`, `cached`;
|
|
1215
|
-
* - methods to control page/size/filters
|
|
1228
|
+
* - methods to control page/size/filters;
|
|
1216
1229
|
* - optional LRU cache by pages;
|
|
1217
1230
|
* - configurable transport (GET/POST/PATCH/…).
|
|
1218
1231
|
*
|
|
@@ -1237,8 +1250,6 @@ class PaginatedDataStore {
|
|
|
1237
1250
|
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
1238
1251
|
/** Current filters (applied to requests). */
|
|
1239
1252
|
filters = {};
|
|
1240
|
-
/** Current sorting (`field,asc|desc`). */
|
|
1241
|
-
sort;
|
|
1242
1253
|
/** Current page index (0-based). */
|
|
1243
1254
|
page = 0;
|
|
1244
1255
|
/** Default page size. */
|
|
@@ -1259,7 +1270,7 @@ class PaginatedDataStore {
|
|
|
1259
1270
|
this.initTransport();
|
|
1260
1271
|
inject(DestroyRef).onDestroy(() => this.destroy());
|
|
1261
1272
|
}
|
|
1262
|
-
/** Force reload current data (with the same page/filters
|
|
1273
|
+
/** Force reload current data (with the same page/filters). */
|
|
1263
1274
|
refresh() {
|
|
1264
1275
|
return this.#fetchItems({});
|
|
1265
1276
|
}
|
|
@@ -1298,21 +1309,20 @@ class PaginatedDataStore {
|
|
|
1298
1309
|
};
|
|
1299
1310
|
/**
|
|
1300
1311
|
* Update the state from table events (PrimeNG, etc.) and fetch.
|
|
1301
|
-
* Supports `page/first/rows
|
|
1312
|
+
* Supports `page/first/rows`.
|
|
1302
1313
|
*/
|
|
1303
|
-
updateQuery = ({ page: pageNum, first = 0, rows = 0
|
|
1314
|
+
updateQuery = ({ page: pageNum, first = 0, rows = 0 }) => {
|
|
1304
1315
|
const page = (pageNum ?? (first && rows && Math.floor(first / rows))) || 0;
|
|
1305
|
-
|
|
1306
|
-
return this.#fetchItems({ page, sort });
|
|
1316
|
+
return this.#fetchItems({ page });
|
|
1307
1317
|
};
|
|
1308
1318
|
/**
|
|
1309
|
-
* Set filters from scratch (goes to the first page
|
|
1319
|
+
* Set filters from scratch (goes to the first page) and fetch.
|
|
1310
1320
|
* Useful for quick presets.
|
|
1311
1321
|
*/
|
|
1312
1322
|
setFilters = (filters = {}) => {
|
|
1313
1323
|
this.#cache.clear();
|
|
1314
1324
|
this.cached.set([]);
|
|
1315
|
-
return this.#fetchItems({ page: 0, filters
|
|
1325
|
+
return this.#fetchItems({ page: 0, filters });
|
|
1316
1326
|
};
|
|
1317
1327
|
/**
|
|
1318
1328
|
* Change the resource route (resets page, cache, and presets) without fetching.
|
|
@@ -1394,14 +1404,13 @@ class PaginatedDataStore {
|
|
|
1394
1404
|
}
|
|
1395
1405
|
}
|
|
1396
1406
|
}
|
|
1397
|
-
#fetchItems = async ({ page = this.page, size = this.pageSize,
|
|
1398
|
-
const query = this.parseQuery({ page, size,
|
|
1407
|
+
#fetchItems = async ({ page = this.page, size = this.pageSize, filters = this.filters }) => {
|
|
1408
|
+
const query = this.parseQuery({ page, size, filters });
|
|
1399
1409
|
this.loading.set(true);
|
|
1400
1410
|
this.#transport.abortAll();
|
|
1401
1411
|
try {
|
|
1402
1412
|
const response = await this.runTransport(filters, query);
|
|
1403
1413
|
this.page = page;
|
|
1404
|
-
this.sort = sort;
|
|
1405
1414
|
this.filters = filters;
|
|
1406
1415
|
const parsed = await this.parseResponseData(response);
|
|
1407
1416
|
this.pageSize = parsed.pageable?.pageSize || size;
|
|
@@ -1424,7 +1433,7 @@ class PaginatedDataStore {
|
|
|
1424
1433
|
this.#transport = new ResourceStore({ [this.config.method || 'GET']: this.route }, {
|
|
1425
1434
|
delay: this.config.debounceTime,
|
|
1426
1435
|
delayMode: 'debounce',
|
|
1427
|
-
presetQueries: { page: this.page, size: this.pageSize
|
|
1436
|
+
presetQueries: { page: this.page, size: this.pageSize },
|
|
1428
1437
|
presetPayload: this.filters,
|
|
1429
1438
|
});
|
|
1430
1439
|
}
|
|
@@ -1437,10 +1446,10 @@ class PaginatedDataStore {
|
|
|
1437
1446
|
// @ts-ignore
|
|
1438
1447
|
return this.#transport[method]({ query, payload, params }, { promote: false, dedupe: true });
|
|
1439
1448
|
}
|
|
1440
|
-
parseQuery({ page = 0, size,
|
|
1449
|
+
parseQuery({ page = 0, size, filters }) {
|
|
1441
1450
|
const method = this.config.method || 'GET';
|
|
1442
1451
|
const requestPayload = { page, size, ...(method === 'GET' ? filters : {}) };
|
|
1443
|
-
const rawQueries = this.config.parseRequest?.({ ...requestPayload
|
|
1452
|
+
const rawQueries = this.config.parseRequest?.({ ...requestPayload }) || requestPayload;
|
|
1444
1453
|
const queries = {};
|
|
1445
1454
|
Object.entries(rawQueries).forEach(([key, value]) => {
|
|
1446
1455
|
if (Array.isArray(value)) {
|
|
@@ -1450,7 +1459,6 @@ class PaginatedDataStore {
|
|
|
1450
1459
|
queries[key] = value;
|
|
1451
1460
|
}
|
|
1452
1461
|
});
|
|
1453
|
-
sort && (queries['sort'] = sort);
|
|
1454
1462
|
return queries;
|
|
1455
1463
|
}
|
|
1456
1464
|
parseResponseData = async (data) => {
|
|
@@ -1496,7 +1504,6 @@ class PaginatedDataStore {
|
|
|
1496
1504
|
this.filters = this.config.presetFilters || {};
|
|
1497
1505
|
this.pageSize = this.config.presetQuery?.pageSize || 20;
|
|
1498
1506
|
this.page = this.config.presetQuery?.page || 0;
|
|
1499
|
-
this.sort = this.config.presetQuery?.sort;
|
|
1500
1507
|
}
|
|
1501
1508
|
}
|
|
1502
1509
|
|
package/package.json
CHANGED
|
@@ -585,17 +585,16 @@ type PaginatedDataStoreConfig<ItemsType extends object, FilterType = unknown> =
|
|
|
585
585
|
cacheSize?: number;
|
|
586
586
|
/** Debounce delay before request (ms). Useful for frequent filter changes. */
|
|
587
587
|
debounceTime?: number;
|
|
588
|
-
/** Initial pagination
|
|
588
|
+
/** Initial pagination params. `page` is required. */
|
|
589
589
|
presetQuery?: {
|
|
590
590
|
page: number;
|
|
591
591
|
pageSize?: number;
|
|
592
|
-
sort?: string;
|
|
593
592
|
};
|
|
594
593
|
/** Initial filters. Will be sent with the first request. */
|
|
595
594
|
presetFilters?: Partial<FilterType>;
|
|
596
595
|
/**
|
|
597
596
|
* Custom transformation of request data into a query object.
|
|
598
|
-
* Useful for mapping `page/pageSize
|
|
597
|
+
* Useful for mapping `page/pageSize` → API-specific keys.
|
|
599
598
|
*/
|
|
600
599
|
parseRequest?: (data: PageableRequest) => Query;
|
|
601
600
|
/**
|
|
@@ -611,9 +610,9 @@ type PaginatedDataStoreConfig<ItemsType extends object, FilterType = unknown> =
|
|
|
611
610
|
type PaginatedDataStoreProviderConfig = {
|
|
612
611
|
/** Default method for all stores (if not overridden locally). */
|
|
613
612
|
defaultMethod?: RestMethods;
|
|
614
|
-
/** Default base `page/pageSize
|
|
613
|
+
/** Default base `page/pageSize`. */
|
|
615
614
|
defaultQuery?: PaginatedDataStoreConfig<object>['presetQuery'];
|
|
616
|
-
/** Global `parseRequest` — maps pagination
|
|
615
|
+
/** Global `parseRequest` — maps pagination into a query object. */
|
|
617
616
|
defaultParseRequest?: PaginatedDataStoreConfig<object>['parseRequest'];
|
|
618
617
|
/** Whether to enable page cache by default. */
|
|
619
618
|
defaultHasCache?: boolean;
|
|
@@ -625,15 +624,13 @@ type PaginationType = {
|
|
|
625
624
|
page?: number;
|
|
626
625
|
first?: number | null;
|
|
627
626
|
rows?: number | null;
|
|
628
|
-
sortField?: string | string[] | null;
|
|
629
|
-
sortOrder?: number | null;
|
|
630
627
|
};
|
|
631
628
|
/**
|
|
632
629
|
* Store for paginated data (tables/lists) with per-page cache and unified requests.
|
|
633
630
|
*
|
|
634
631
|
* Provides:
|
|
635
632
|
* - reactive signals: `items`, `loading`, `cached`;
|
|
636
|
-
* - methods to control page/size/filters
|
|
633
|
+
* - methods to control page/size/filters;
|
|
637
634
|
* - optional LRU cache by pages;
|
|
638
635
|
* - configurable transport (GET/POST/PATCH/…).
|
|
639
636
|
*
|
|
@@ -657,8 +654,6 @@ declare class PaginatedDataStore<ItemsType extends object, FilterType = unknown>
|
|
|
657
654
|
loading: WritableSignal<boolean>;
|
|
658
655
|
/** Current filters (applied to requests). */
|
|
659
656
|
filters: Partial<FilterType>;
|
|
660
|
-
/** Current sorting (`field,asc|desc`). */
|
|
661
|
-
sort?: string | ReadonlyArray<string>;
|
|
662
657
|
/** Current page index (0-based). */
|
|
663
658
|
page: number;
|
|
664
659
|
/** Default page size. */
|
|
@@ -671,7 +666,7 @@ declare class PaginatedDataStore<ItemsType extends object, FilterType = unknown>
|
|
|
671
666
|
* @param config Store behavior: method, cache, request/response parsers, debounce, presets, etc.
|
|
672
667
|
*/
|
|
673
668
|
constructor(route: string, config?: PaginatedDataStoreConfig<ItemsType, FilterType>);
|
|
674
|
-
/** Force reload current data (with the same page/filters
|
|
669
|
+
/** Force reload current data (with the same page/filters). */
|
|
675
670
|
refresh(): Promise<ItemsType[] | undefined>;
|
|
676
671
|
/**
|
|
677
672
|
* Switch page with a request.
|
|
@@ -692,11 +687,11 @@ declare class PaginatedDataStore<ItemsType extends object, FilterType = unknown>
|
|
|
692
687
|
updateFilters: (filters: Partial<FilterType>) => Promise<ItemsType[] | undefined>;
|
|
693
688
|
/**
|
|
694
689
|
* Update the state from table events (PrimeNG, etc.) and fetch.
|
|
695
|
-
* Supports `page/first/rows
|
|
690
|
+
* Supports `page/first/rows`.
|
|
696
691
|
*/
|
|
697
|
-
updateQuery: ({ page: pageNum, first, rows
|
|
692
|
+
updateQuery: ({ page: pageNum, first, rows }: PaginationType) => Promise<ItemsType[] | undefined>;
|
|
698
693
|
/**
|
|
699
|
-
* Set filters from scratch (goes to the first page
|
|
694
|
+
* Set filters from scratch (goes to the first page) and fetch.
|
|
700
695
|
* Useful for quick presets.
|
|
701
696
|
*/
|
|
702
697
|
setFilters: (filters?: Partial<FilterType>) => Promise<ItemsType[] | undefined>;
|
|
@@ -779,7 +774,7 @@ type DictStoreConfig<ItemsType extends object> = {
|
|
|
779
774
|
/** Autoload data on initialization (`true` by default). */
|
|
780
775
|
autoLoad?: boolean | 'whenEmpty';
|
|
781
776
|
/**
|
|
782
|
-
* Custom mapper of pagination
|
|
777
|
+
* Custom mapper of pagination request into query params.
|
|
783
778
|
* Useful if the API expects non-standard field names.
|
|
784
779
|
*/
|
|
785
780
|
parseRequest?: (data: PageableRequest) => AnyDict;
|