akanjs 3.0.0-alpha.96 → 3.0.0-alpha.97
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/fetch/client/sliceInitHandle.ts +2 -0
- package/fetch/fetchType/appliedReturn.type.ts +2 -0
- package/local/apps/serverLifecycle/serverLifecycle-local.db-shm +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-shm +0 -0
- package/package.json +1 -1
- package/store/action.ts +66 -17
- package/store/liveInsert.ts +9 -2
- package/store/sliceRole.ts +1 -1
- package/store/state.ts +16 -0
- package/store/storeInstance.ts +6 -2
- package/types/fetch/fetchType/appliedReturn.type.d.ts +2 -0
- package/types/store/action.d.ts +4 -2
- package/types/store/baseSt.d.ts +2 -2
- package/types/store/liveInsert.d.ts +5 -1
- package/types/store/sliceRole.d.ts +1 -1
- package/types/store/state.d.ts +5 -1
- package/types/ui/InfiniteScroll.d.ts +3 -8
- package/types/ui/More.d.ts +3 -2
- package/ui/InfiniteScroll.tsx +9 -26
- package/ui/Load/Pagination.tsx +0 -2
- package/ui/Load/Units.tsx +16 -5
- package/ui/More.tsx +9 -16
|
@@ -78,6 +78,8 @@ export class SliceInitHandle {
|
|
|
78
78
|
[`pageOf${capRefName}`]: page,
|
|
79
79
|
[`lastPageOf${capRefName}`]: lastPage,
|
|
80
80
|
[`limitOf${capRefName}`]: limit,
|
|
81
|
+
|
|
82
|
+
[`hasMoreOf${capRefName}`]: !!limit && modelObjList.length >= limit,
|
|
81
83
|
[`queryArgsOf${capRefName}`]: queryArgs,
|
|
82
84
|
[`sortOf${capRefName}`]: sort,
|
|
83
85
|
[`${refName}InitAt`]: new Date(),
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/store/action.ts
CHANGED
|
@@ -177,7 +177,7 @@ export type SliceAction<
|
|
|
177
177
|
} & {
|
|
178
178
|
[K in `setPageOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: (page: number, options?: FetchPolicy) => Promise<void>;
|
|
179
179
|
} & {
|
|
180
|
-
[K in `
|
|
180
|
+
[K in `loadMoreOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: (options?: FetchPolicy) => Promise<void>;
|
|
181
181
|
} & {
|
|
182
182
|
[K in `setLimitOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: (
|
|
183
183
|
limit: number,
|
|
@@ -273,11 +273,14 @@ type DefaultSliceActionFields<
|
|
|
273
273
|
} & {
|
|
274
274
|
[Suffix in _Suffixes as
|
|
275
275
|
| `setPageOf${_CapRef}${StoreSliceSuffixCap<SlceCls, Suffix>}`
|
|
276
|
-
| `addPageOf${_CapRef}${StoreSliceSuffixCap<SlceCls, Suffix>}`
|
|
277
276
|
| `setLimitOf${_CapRef}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: (
|
|
278
277
|
value: number,
|
|
279
278
|
options?: FetchPolicy,
|
|
280
279
|
) => Promise<void>;
|
|
280
|
+
} & {
|
|
281
|
+
[Suffix in _Suffixes as `loadMoreOf${_CapRef}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: (
|
|
282
|
+
options?: FetchPolicy,
|
|
283
|
+
) => Promise<void>;
|
|
281
284
|
} & {
|
|
282
285
|
[Suffix in _Suffixes as `setQueryArgsOf${_CapRef}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: (
|
|
283
286
|
...args:
|
|
@@ -528,7 +531,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
528
531
|
refreshModel: `refresh${className}`,
|
|
529
532
|
selectModel: `select${className}`,
|
|
530
533
|
setPageOfModel: `setPageOf${className}`,
|
|
531
|
-
|
|
534
|
+
loadMoreOfModel: `loadMoreOf${className}`,
|
|
532
535
|
setLimitOfModel: `setLimitOf${className}`,
|
|
533
536
|
setQueryArgsOfModel: `setQueryArgsOf${className}`,
|
|
534
537
|
setSortOfModel: `setSortOf${className}`,
|
|
@@ -537,6 +540,8 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
537
540
|
lastPageOfModel: `lastPageOf${className}`,
|
|
538
541
|
pageOfModel: `pageOf${className}`,
|
|
539
542
|
limitOfModel: `limitOf${className}`,
|
|
543
|
+
hasMoreOfModel: `hasMoreOf${className}`,
|
|
544
|
+
isCumulativeOfModel: `isCumulativeOf${className}`,
|
|
540
545
|
queryArgsOfModel: `queryArgsOf${className}`,
|
|
541
546
|
sortOfModel: `sortOf${className}`,
|
|
542
547
|
};
|
|
@@ -1034,7 +1039,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1034
1039
|
refreshModel: SliceName.replace(names.Model, names.refreshModel),
|
|
1035
1040
|
selectModel: SliceName.replace(names.Model, names.selectModel),
|
|
1036
1041
|
setPageOfModel: SliceName.replace(names.Model, names.setPageOfModel),
|
|
1037
|
-
|
|
1042
|
+
loadMoreOfModel: SliceName.replace(names.Model, names.loadMoreOfModel),
|
|
1038
1043
|
setLimitOfModel: SliceName.replace(names.Model, names.setLimitOfModel),
|
|
1039
1044
|
setQueryArgsOfModel: SliceName.replace(names.Model, names.setQueryArgsOfModel),
|
|
1040
1045
|
setSortOfModel: SliceName.replace(names.Model, names.setSortOfModel),
|
|
@@ -1043,10 +1048,23 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1043
1048
|
lastPageOfModel: SliceName.replace(names.Model, names.lastPageOfModel),
|
|
1044
1049
|
pageOfModel: SliceName.replace(names.Model, names.pageOfModel),
|
|
1045
1050
|
limitOfModel: SliceName.replace(names.Model, names.limitOfModel),
|
|
1051
|
+
hasMoreOfModel: SliceName.replace(names.Model, names.hasMoreOfModel),
|
|
1052
|
+
isCumulativeOfModel: SliceName.replace(names.Model, names.isCumulativeOfModel),
|
|
1046
1053
|
queryArgsOfModel: SliceName.replace(names.Model, names.queryArgsOfModel),
|
|
1047
1054
|
sortOfModel: SliceName.replace(names.Model, names.sortOfModel),
|
|
1048
1055
|
modelSelection: SliceName.replace(names.Model, names.modelSelection),
|
|
1049
1056
|
};
|
|
1057
|
+
/**
|
|
1058
|
+
* Whether the server still holds rows past the ones just returned.
|
|
1059
|
+
*
|
|
1060
|
+
* Read off the batch rather than off `<model>Insight`, because the count is a second query — absent entirely
|
|
1061
|
+
* under `{ insight: false }`, and one live event out of step with the list the rest of the time. A short
|
|
1062
|
+
* batch is the server saying it had nothing more; a full one costs at most one extra request that comes back
|
|
1063
|
+
* empty when the total happens to be a multiple of the limit.
|
|
1064
|
+
*/
|
|
1065
|
+
const hasMoreFrom = (batch: unknown[], askedFor: number) => ({
|
|
1066
|
+
[namesOfSlice.hasMoreOfModel]: batch.length >= askedFor && askedFor > 0,
|
|
1067
|
+
});
|
|
1050
1068
|
const singleSliceAction = {
|
|
1051
1069
|
[namesOfSlice.initModel]: async function (
|
|
1052
1070
|
this: SetGet,
|
|
@@ -1057,7 +1075,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1057
1075
|
FetchPolicy;
|
|
1058
1076
|
const queryArgs = new Array(initArgLength).fill(null).map((_, i) => args[i] as object);
|
|
1059
1077
|
const defaultModel = new cnst.full().set(initForm.default ?? {}) as unknown as Full;
|
|
1060
|
-
this.set({ [names.defaultModel]: defaultModel });
|
|
1078
|
+
this.set({ [names.defaultModel]: defaultModel, [namesOfSlice.isCumulativeOfModel]: false });
|
|
1061
1079
|
await ((this as unknown as DynamicRecord)[namesOfSlice.refreshModel] as (...args: any[]) => Promise<void>)({
|
|
1062
1080
|
...initForm,
|
|
1063
1081
|
queryArgs,
|
|
@@ -1089,6 +1107,10 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1089
1107
|
...fetchPolicy
|
|
1090
1108
|
} = initForm;
|
|
1091
1109
|
const modelOperation = currentState[names.modelOperation] as string;
|
|
1110
|
+
const isCumulative = currentState[namesOfSlice.isCumulativeOfModel] as boolean;
|
|
1111
|
+
const loadedLength = (currentState[namesOfSlice.modelList] as DataList<Light>).length;
|
|
1112
|
+
|
|
1113
|
+
const fetchLimit = isCumulative ? Math.max(limit, loadedLength) : limit;
|
|
1092
1114
|
const queryArgsOfModel = currentState[namesOfSlice.queryArgsOfModel] as object[];
|
|
1093
1115
|
const pageOfModel = currentState[namesOfSlice.pageOfModel] as number;
|
|
1094
1116
|
const limitOfModel = currentState[namesOfSlice.limitOfModel] as number;
|
|
@@ -1112,7 +1134,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1112
1134
|
(fetch[namesOfSlice.modelList] as (...args: any[]) => Promise<Light[]>)(
|
|
1113
1135
|
...fetchQueryArgs,
|
|
1114
1136
|
(page - 1) * limit,
|
|
1115
|
-
|
|
1137
|
+
fetchLimit,
|
|
1116
1138
|
sort,
|
|
1117
1139
|
{ ...fetchPolicy, onError: initForm.onError },
|
|
1118
1140
|
),
|
|
@@ -1134,6 +1156,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1134
1156
|
[namesOfSlice.modelInitList]: modelList,
|
|
1135
1157
|
[namesOfSlice.modelInitAt]: new Date(),
|
|
1136
1158
|
[namesOfSlice.lastPageOfModel]: Math.max(Math.floor((modelInsight.count - 1) / (limit || 20)) + 1, 1),
|
|
1159
|
+
...hasMoreFrom(modelDataList, fetchLimit),
|
|
1137
1160
|
[namesOfSlice.limitOfModel]: limit,
|
|
1138
1161
|
[namesOfSlice.queryArgsOfModel]: queryArgs,
|
|
1139
1162
|
[namesOfSlice.sortOfModel]: sort,
|
|
@@ -1183,35 +1206,51 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1183
1206
|
this.set({
|
|
1184
1207
|
[namesOfSlice.modelList]: new DataList(modelDataList),
|
|
1185
1208
|
[namesOfSlice.pageOfModel]: page,
|
|
1209
|
+
[namesOfSlice.isCumulativeOfModel]: false,
|
|
1210
|
+
...hasMoreFrom(modelDataList, limitOfModel),
|
|
1186
1211
|
});
|
|
1187
1212
|
} finally {
|
|
1188
1213
|
if (requests.isCurrent(ticket)) this.set({ [namesOfSlice.modelListLoading]: false });
|
|
1189
1214
|
}
|
|
1190
1215
|
},
|
|
1191
|
-
|
|
1216
|
+
/**
|
|
1217
|
+
* Appends the rows after the ones already loaded, turning this slice's window into a cumulative list.
|
|
1218
|
+
*
|
|
1219
|
+
* The offset is the list on screen rather than a page number, and that is the whole point. A page number
|
|
1220
|
+
* assumes the server's ordering baseline has not moved, which a live insertion at the front breaks on the
|
|
1221
|
+
* first event; the rows in hand are the first `n` the query returns whatever has been inserted since, so
|
|
1222
|
+
* asking for what comes after them cannot drift from what is displayed. It also leaves `pageOf<Model>` at
|
|
1223
|
+
* 1, which is what keeps live placement — refused anywhere but the first page — working past the first
|
|
1224
|
+
* "more".
|
|
1225
|
+
*/
|
|
1226
|
+
[namesOfSlice.loadMoreOfModel]: async function (this: SetGet, options?: FetchPolicy) {
|
|
1192
1227
|
const currentState = this.get() as { [key: string]: any };
|
|
1228
|
+
|
|
1229
|
+
if (currentState[namesOfSlice.modelListLoading] as boolean) return;
|
|
1230
|
+
if (!(currentState[namesOfSlice.hasMoreOfModel] as boolean)) return;
|
|
1193
1231
|
const modelList = currentState[namesOfSlice.modelList] as DataList<Light>;
|
|
1194
1232
|
const queryArgsOfModel = currentState[namesOfSlice.queryArgsOfModel] as object[];
|
|
1195
1233
|
const pageOfModel = currentState[namesOfSlice.pageOfModel] as number;
|
|
1196
|
-
const limitOfModel = currentState[namesOfSlice.limitOfModel] as number;
|
|
1234
|
+
const limitOfModel = (currentState[namesOfSlice.limitOfModel] as number) || 20;
|
|
1197
1235
|
const sortOfModel = currentState[namesOfSlice.sortOfModel] as Sort;
|
|
1198
|
-
if (pageOfModel === page) return;
|
|
1199
|
-
const addFront = page < pageOfModel;
|
|
1200
1236
|
const ticket = requests.claim();
|
|
1201
1237
|
const fetchQueryArgs = expandQueryArgs(queryArgsOfModel, slice.args);
|
|
1202
1238
|
const modelDataList = await (fetch[namesOfSlice.modelList] as (...args: any[]) => Promise<Light[]>)(
|
|
1203
1239
|
...fetchQueryArgs,
|
|
1204
|
-
(
|
|
1240
|
+
(pageOfModel - 1) * limitOfModel + modelList.length,
|
|
1205
1241
|
limitOfModel,
|
|
1206
1242
|
sortOfModel,
|
|
1207
1243
|
options,
|
|
1208
1244
|
);
|
|
1209
1245
|
|
|
1210
1246
|
if (!requests.isCurrent(ticket)) return;
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1247
|
+
|
|
1248
|
+
const currentModelList = (this.get() as { [key: string]: any })[namesOfSlice.modelList] as DataList<Light>;
|
|
1249
|
+
this.set({
|
|
1250
|
+
[namesOfSlice.modelList]: new DataList([...currentModelList.values, ...modelDataList]),
|
|
1251
|
+
[namesOfSlice.isCumulativeOfModel]: true,
|
|
1252
|
+
...hasMoreFrom(modelDataList, limitOfModel),
|
|
1253
|
+
});
|
|
1215
1254
|
},
|
|
1216
1255
|
[namesOfSlice.setLimitOfModel]: async function (this: SetGet, limit: number, options?: FetchPolicy) {
|
|
1217
1256
|
const currentState = this.get() as { [key: string]: any };
|
|
@@ -1240,6 +1279,8 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1240
1279
|
[namesOfSlice.lastPageOfModel]: Math.max(Math.floor((modelInsight.count - 1) / limit) + 1, 1),
|
|
1241
1280
|
[namesOfSlice.limitOfModel]: limit,
|
|
1242
1281
|
[namesOfSlice.pageOfModel]: page,
|
|
1282
|
+
[namesOfSlice.isCumulativeOfModel]: false,
|
|
1283
|
+
...hasMoreFrom(modelDataList, limit),
|
|
1243
1284
|
});
|
|
1244
1285
|
} finally {
|
|
1245
1286
|
if (requests.isCurrent(ticket)) this.set({ [namesOfSlice.modelListLoading]: false });
|
|
@@ -1291,6 +1332,8 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1291
1332
|
[namesOfSlice.modelInsight]: modelInsight,
|
|
1292
1333
|
[namesOfSlice.lastPageOfModel]: Math.max(Math.floor((modelInsight.count - 1) / limitOfModel) + 1, 1),
|
|
1293
1334
|
[namesOfSlice.pageOfModel]: 1,
|
|
1335
|
+
[namesOfSlice.isCumulativeOfModel]: false,
|
|
1336
|
+
...hasMoreFrom(modelDataList, limitOfModel),
|
|
1294
1337
|
[namesOfSlice.modelSelection]: new Map(),
|
|
1295
1338
|
});
|
|
1296
1339
|
} finally {
|
|
@@ -1319,6 +1362,8 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1319
1362
|
[namesOfSlice.modelList]: new DataList(modelDataList),
|
|
1320
1363
|
[namesOfSlice.sortOfModel]: sort,
|
|
1321
1364
|
[namesOfSlice.pageOfModel]: 1,
|
|
1365
|
+
[namesOfSlice.isCumulativeOfModel]: false,
|
|
1366
|
+
...hasMoreFrom(modelDataList, limitOfModel),
|
|
1322
1367
|
});
|
|
1323
1368
|
} finally {
|
|
1324
1369
|
if (requests.isCurrent(ticket)) this.set({ [namesOfSlice.modelListLoading]: false });
|
|
@@ -1329,7 +1374,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1329
1374
|
*
|
|
1330
1375
|
* The verb already answers membership — the server evaluated this slice's own query against the document
|
|
1331
1376
|
* before and after the write, so arriving here at all is the proof, and nothing is re-checked. What is left
|
|
1332
|
-
* is the window: `<slice>List` is
|
|
1377
|
+
* is the window: `<slice>List` is a prefix of the list, not the list, so an insertion is only ever attempted
|
|
1333
1378
|
* where its position can be known. Everywhere else the list is stamped stale and refetched, which is always
|
|
1334
1379
|
* correct and merely costs a round trip.
|
|
1335
1380
|
*/
|
|
@@ -1344,6 +1389,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1344
1389
|
}
|
|
1345
1390
|
const modelInsight = currentState[namesOfSlice.modelInsight] as Insight & BaseInsight;
|
|
1346
1391
|
const limit = (currentState[namesOfSlice.limitOfModel] as number) || 20;
|
|
1392
|
+
const isCumulative = currentState[namesOfSlice.isCumulativeOfModel] as boolean;
|
|
1347
1393
|
const countedTo = (count: number) => ({
|
|
1348
1394
|
[namesOfSlice.modelInsight]: new cnst.insight().set({ ...modelInsight, count }),
|
|
1349
1395
|
[namesOfSlice.lastPageOfModel]: Math.max(Math.floor((count - 1) / limit) + 1, 1),
|
|
@@ -1372,6 +1418,8 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1372
1418
|
row: light as unknown as LiveSortableRow,
|
|
1373
1419
|
page: currentState[namesOfSlice.pageOfModel] as number,
|
|
1374
1420
|
limit,
|
|
1421
|
+
cumulative: isCumulative,
|
|
1422
|
+
hasMore: currentState[namesOfSlice.hasMoreOfModel] as boolean,
|
|
1375
1423
|
sortKey: String(currentState[namesOfSlice.sortOfModel]),
|
|
1376
1424
|
allowedSorts: slice.live?.sort ?? [],
|
|
1377
1425
|
sorts: fetch.sortValueMap?.get(refName),
|
|
@@ -1382,7 +1430,8 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1382
1430
|
return;
|
|
1383
1431
|
}
|
|
1384
1432
|
|
|
1385
|
-
const
|
|
1433
|
+
const inserted = [...modelList.slice(0, placement), light, ...modelList.slice(placement)];
|
|
1434
|
+
const placed = isCumulative ? inserted : inserted.slice(0, limit);
|
|
1386
1435
|
this.set({
|
|
1387
1436
|
[namesOfSlice.modelList]: new DataList(placed as Light[]),
|
|
1388
1437
|
...countedTo(modelInsight.count + 1),
|
package/store/liveInsert.ts
CHANGED
|
@@ -8,6 +8,10 @@ export interface LivePlacementProps {
|
|
|
8
8
|
/** Which page of the list is on screen. Only the first can place a row. */
|
|
9
9
|
page: number;
|
|
10
10
|
limit: number;
|
|
11
|
+
/** Whether the list is pages `1..N` concatenated rather than one window. */
|
|
12
|
+
cumulative: boolean;
|
|
13
|
+
/** Whether the server still holds rows past the ones in hand. */
|
|
14
|
+
hasMore: boolean;
|
|
11
15
|
/** The sort key the window is ordered by. */
|
|
12
16
|
sortKey: string;
|
|
13
17
|
/** The sort keys the slice declared a subscriber may reproduce. */
|
|
@@ -38,6 +42,8 @@ export const livePlacementIndex = ({
|
|
|
38
42
|
row,
|
|
39
43
|
page,
|
|
40
44
|
limit,
|
|
45
|
+
cumulative,
|
|
46
|
+
hasMore,
|
|
41
47
|
sortKey,
|
|
42
48
|
allowedSorts,
|
|
43
49
|
sorts,
|
|
@@ -49,9 +55,10 @@ export const livePlacementIndex = ({
|
|
|
49
55
|
const paths = Object.entries(sort);
|
|
50
56
|
if (paths.some(([path]) => comparableOf(row[path]) === null)) return null;
|
|
51
57
|
const index = list.findIndex((item) => compareRows(row, item, paths) < 0);
|
|
58
|
+
if (index !== -1) return index;
|
|
52
59
|
|
|
53
|
-
if (
|
|
54
|
-
return
|
|
60
|
+
if (cumulative) return hasMore ? null : list.length;
|
|
61
|
+
return list.length < limit ? list.length : null;
|
|
55
62
|
};
|
|
56
63
|
|
|
57
64
|
const compareRows = (left: LiveSortableRow, right: LiveSortableRow, paths: [string, 1 | -1][]): number => {
|
package/store/sliceRole.ts
CHANGED
package/store/state.ts
CHANGED
|
@@ -49,6 +49,8 @@ export type SliceStateKey =
|
|
|
49
49
|
| "lastPageOfModel"
|
|
50
50
|
| "pageOfModel"
|
|
51
51
|
| "limitOfModel"
|
|
52
|
+
| "hasMoreOfModel"
|
|
53
|
+
| "isCumulativeOfModel"
|
|
52
54
|
| "queryArgsOfModel"
|
|
53
55
|
| "sortOfModel";
|
|
54
56
|
type _SliceMap<S extends SliceCls> = StoreSliceMap<S>;
|
|
@@ -114,6 +116,10 @@ export type SliceState<
|
|
|
114
116
|
[K in `pageOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: number;
|
|
115
117
|
} & {
|
|
116
118
|
[K in `limitOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: number;
|
|
119
|
+
} & {
|
|
120
|
+
[K in
|
|
121
|
+
| `hasMoreOf${_CapitalizedRefName}${_CapitalizedSuffix}`
|
|
122
|
+
| `isCumulativeOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: boolean;
|
|
117
123
|
} & {
|
|
118
124
|
[K in `queryArgsOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: Args;
|
|
119
125
|
} & {
|
|
@@ -150,6 +156,10 @@ type DefaultSliceStateFields<
|
|
|
150
156
|
| `lastPageOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}`
|
|
151
157
|
| `pageOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}`
|
|
152
158
|
| `limitOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: number;
|
|
159
|
+
} & {
|
|
160
|
+
[Suffix in _Suffixes as
|
|
161
|
+
| `hasMoreOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}`
|
|
162
|
+
| `isCumulativeOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: boolean;
|
|
153
163
|
} & {
|
|
154
164
|
[Suffix in _Suffixes as `queryArgsOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: StoreSliceArgs<
|
|
155
165
|
SlceCls,
|
|
@@ -206,6 +216,8 @@ export const createSliceState = (refName: string, slice: { [key: string]: Serial
|
|
|
206
216
|
lastPageOfModel: `lastPageOf${className}`,
|
|
207
217
|
pageOfModel: `pageOf${className}`,
|
|
208
218
|
limitOfModel: `limitOf${className}`,
|
|
219
|
+
hasMoreOfModel: `hasMoreOf${className}`,
|
|
220
|
+
isCumulativeOfModel: `isCumulativeOf${className}`,
|
|
209
221
|
queryArgsOfModel: `queryArgsOf${className}`,
|
|
210
222
|
sortOfModel: `sortOf${className}`,
|
|
211
223
|
};
|
|
@@ -225,6 +237,8 @@ export const createSliceState = (refName: string, slice: { [key: string]: Serial
|
|
|
225
237
|
lastPageOfModel: SliceName.replace(names.Model, names.lastPageOfModel),
|
|
226
238
|
pageOfModel: SliceName.replace(names.Model, names.pageOfModel),
|
|
227
239
|
limitOfModel: SliceName.replace(names.Model, names.limitOfModel),
|
|
240
|
+
hasMoreOfModel: SliceName.replace(names.Model, names.hasMoreOfModel),
|
|
241
|
+
isCumulativeOfModel: SliceName.replace(names.Model, names.isCumulativeOfModel),
|
|
228
242
|
queryArgsOfModel: SliceName.replace(names.Model, names.queryArgsOfModel),
|
|
229
243
|
sortOfModel: SliceName.replace(names.Model, names.sortOfModel),
|
|
230
244
|
};
|
|
@@ -240,6 +254,8 @@ export const createSliceState = (refName: string, slice: { [key: string]: Serial
|
|
|
240
254
|
[namesOfSlice.lastPageOfModel]: 1,
|
|
241
255
|
[namesOfSlice.pageOfModel]: 1,
|
|
242
256
|
[namesOfSlice.limitOfModel]: 20,
|
|
257
|
+
[namesOfSlice.hasMoreOfModel]: false,
|
|
258
|
+
[namesOfSlice.isCumulativeOfModel]: false,
|
|
243
259
|
[namesOfSlice.queryArgsOfModel]: [],
|
|
244
260
|
[namesOfSlice.sortOfModel]: "latest",
|
|
245
261
|
};
|
package/store/storeInstance.ts
CHANGED
|
@@ -433,6 +433,8 @@ export class StoreInstance {
|
|
|
433
433
|
modelStaleAt: `${fieldName}StaleAt`,
|
|
434
434
|
pageOfModel: `pageOf${className}`,
|
|
435
435
|
limitOfModel: `limitOf${className}`,
|
|
436
|
+
hasMoreOfModel: `hasMoreOf${className}`,
|
|
437
|
+
isCumulativeOfModel: `isCumulativeOf${className}`,
|
|
436
438
|
queryArgsOfModel: `queryArgsOf${className}`,
|
|
437
439
|
sortOfModel: `sortOf${className}`,
|
|
438
440
|
modelSelection: `${fieldName}Selection`,
|
|
@@ -440,7 +442,7 @@ export class StoreInstance {
|
|
|
440
442
|
refreshModel: `refresh${className}`,
|
|
441
443
|
selectModel: `select${className}`,
|
|
442
444
|
setPageOfModel: `setPageOf${className}`,
|
|
443
|
-
|
|
445
|
+
loadMoreOfModel: `loadMoreOf${className}`,
|
|
444
446
|
setLimitOfModel: `setLimitOf${className}`,
|
|
445
447
|
setQueryArgsOfModel: `setQueryArgsOf${className}`,
|
|
446
448
|
setSortOfModel: `setSortOf${className}`,
|
|
@@ -460,6 +462,8 @@ export class StoreInstance {
|
|
|
460
462
|
lastPageOfModel: SliceName.replace(names.Model, names.lastPageOfModel),
|
|
461
463
|
pageOfModel: SliceName.replace(names.Model, names.pageOfModel),
|
|
462
464
|
limitOfModel: SliceName.replace(names.Model, names.limitOfModel),
|
|
465
|
+
hasMoreOfModel: SliceName.replace(names.Model, names.hasMoreOfModel),
|
|
466
|
+
isCumulativeOfModel: SliceName.replace(names.Model, names.isCumulativeOfModel),
|
|
463
467
|
queryArgsOfModel: SliceName.replace(names.Model, names.queryArgsOfModel),
|
|
464
468
|
sortOfModel: SliceName.replace(names.Model, names.sortOfModel),
|
|
465
469
|
modelSelection: SliceName.replace(names.Model, names.modelSelection),
|
|
@@ -469,7 +473,7 @@ export class StoreInstance {
|
|
|
469
473
|
refreshModel: SliceName.replace(names.Model, names.refreshModel),
|
|
470
474
|
selectModel: SliceName.replace(names.Model, names.selectModel),
|
|
471
475
|
setPageOfModel: SliceName.replace(names.Model, names.setPageOfModel),
|
|
472
|
-
|
|
476
|
+
loadMoreOfModel: SliceName.replace(names.Model, names.loadMoreOfModel),
|
|
473
477
|
setLimitOfModel: SliceName.replace(names.Model, names.setLimitOfModel),
|
|
474
478
|
setQueryArgsOfModel: SliceName.replace(names.Model, names.setQueryArgsOfModel),
|
|
475
479
|
setSortOfModel: SliceName.replace(names.Model, names.setSortOfModel),
|
|
@@ -32,6 +32,8 @@ type ServerInitShape<RefName extends string, QueryArgs, CapRefName extends strin
|
|
|
32
32
|
[K in `lastPageOf${CapRefName}`]: number;
|
|
33
33
|
} & {
|
|
34
34
|
[K in `limitOf${CapRefName}`]: number;
|
|
35
|
+
} & {
|
|
36
|
+
[K in `hasMoreOf${CapRefName}`]: boolean;
|
|
35
37
|
} & {
|
|
36
38
|
[K in `queryArgsOf${CapRefName}`]: QueryArgs;
|
|
37
39
|
} & {
|
package/types/store/action.d.ts
CHANGED
|
@@ -100,7 +100,7 @@ export type SliceAction<RefName extends string, Suffix extends string, Input, Li
|
|
|
100
100
|
} & {
|
|
101
101
|
[K in `setPageOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: (page: number, options?: FetchPolicy) => Promise<void>;
|
|
102
102
|
} & {
|
|
103
|
-
[K in `
|
|
103
|
+
[K in `loadMoreOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: (options?: FetchPolicy) => Promise<void>;
|
|
104
104
|
} & {
|
|
105
105
|
[K in `setLimitOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: (limit: number, options?: FetchPolicy) => Promise<void>;
|
|
106
106
|
} & {
|
|
@@ -143,7 +143,9 @@ type DefaultSliceActionFields<SlceCls extends SliceCls, _CapRef extends string,
|
|
|
143
143
|
remove?: boolean;
|
|
144
144
|
}) => void;
|
|
145
145
|
} & {
|
|
146
|
-
[Suffix in _Suffixes as `setPageOf${_CapRef}${StoreSliceSuffixCap<SlceCls, Suffix>}` | `
|
|
146
|
+
[Suffix in _Suffixes as `setPageOf${_CapRef}${StoreSliceSuffixCap<SlceCls, Suffix>}` | `setLimitOf${_CapRef}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: (value: number, options?: FetchPolicy) => Promise<void>;
|
|
147
|
+
} & {
|
|
148
|
+
[Suffix in _Suffixes as `loadMoreOf${_CapRef}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: (options?: FetchPolicy) => Promise<void>;
|
|
147
149
|
} & {
|
|
148
150
|
[Suffix in _Suffixes as `setQueryArgsOf${_CapRef}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: (...args: [...args: StoreSliceArgs<SlceCls, Suffix>, options?: FetchPolicy] | [
|
|
149
151
|
setQueryArgs: (...prevQueryArgs: StoreSliceArgs<SlceCls, Suffix>) => StoreSliceArgs<SlceCls, Suffix>,
|
package/types/store/baseSt.d.ts
CHANGED
|
@@ -112,7 +112,7 @@ declare const RootStore_base: import("./rootStore.d.ts").RootStoreCls<"root", im
|
|
|
112
112
|
} & {
|
|
113
113
|
[x: `setPageOf${any}`]: (page: number, options?: import("../common.d.ts").FetchPolicy) => Promise<void>;
|
|
114
114
|
} & {
|
|
115
|
-
[x: `
|
|
115
|
+
[x: `loadMoreOf${any}`]: (options?: import("../common.d.ts").FetchPolicy) => Promise<void>;
|
|
116
116
|
} & {
|
|
117
117
|
[x: `setLimitOf${any}`]: (limit: number, options?: import("../common.d.ts").FetchPolicy) => Promise<void>;
|
|
118
118
|
} & {
|
|
@@ -418,7 +418,7 @@ export declare const st: import("./agentic.d.ts").StAgentic & {
|
|
|
418
418
|
} & {
|
|
419
419
|
[x: `setPageOf${any}`]: (page: number, options?: import("../common.d.ts").FetchPolicy) => Promise<void>;
|
|
420
420
|
} & {
|
|
421
|
-
[x: `
|
|
421
|
+
[x: `loadMoreOf${any}`]: (options?: import("../common.d.ts").FetchPolicy) => Promise<void>;
|
|
422
422
|
} & {
|
|
423
423
|
[x: `setLimitOf${any}`]: (limit: number, options?: import("../common.d.ts").FetchPolicy) => Promise<void>;
|
|
424
424
|
} & {
|
|
@@ -9,6 +9,10 @@ export interface LivePlacementProps {
|
|
|
9
9
|
/** Which page of the list is on screen. Only the first can place a row. */
|
|
10
10
|
page: number;
|
|
11
11
|
limit: number;
|
|
12
|
+
/** Whether the list is pages `1..N` concatenated rather than one window. */
|
|
13
|
+
cumulative: boolean;
|
|
14
|
+
/** Whether the server still holds rows past the ones in hand. */
|
|
15
|
+
hasMore: boolean;
|
|
12
16
|
/** The sort key the window is ordered by. */
|
|
13
17
|
sortKey: string;
|
|
14
18
|
/** The sort keys the slice declared a subscriber may reproduce. */
|
|
@@ -37,4 +41,4 @@ export interface LivePlacementProps {
|
|
|
37
41
|
* And every sorted field has to be on the row, because a comparison against a value that is not there silently
|
|
38
42
|
* places the row at one end.
|
|
39
43
|
*/
|
|
40
|
-
export declare const livePlacementIndex: ({ list, row, page, limit, sortKey, allowedSorts, sorts, }: LivePlacementProps) => number | null;
|
|
44
|
+
export declare const livePlacementIndex: ({ list, row, page, limit, cumulative, hasMore, sortKey, allowedSorts, sorts, }: LivePlacementProps) => number | null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { SerializedArg } from "akanjs/signal";
|
|
2
2
|
import type { SliceStateKey } from "./state.d.ts";
|
|
3
3
|
/** The generated slice actions, named by what they do rather than by the key any one slice publishes them under. */
|
|
4
|
-
export type SliceActionKey = "initModel" | "refreshModel" | "selectModel" | "setPageOfModel" | "
|
|
4
|
+
export type SliceActionKey = "initModel" | "refreshModel" | "selectModel" | "setPageOfModel" | "loadMoreOfModel" | "setLimitOfModel" | "setQueryArgsOfModel" | "setSortOfModel" | "applyLiveModel" | "watchLiveModel";
|
|
5
5
|
/**
|
|
6
6
|
* What a generated key on `st.do` / `st.use` actually is.
|
|
7
7
|
*
|
package/types/store/state.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface DraftState {
|
|
|
25
25
|
/** Set while a draft is the thing in the form. Drives the "continuing, saved n minutes ago" chip. */
|
|
26
26
|
appliedAt: Date | null;
|
|
27
27
|
}
|
|
28
|
-
export type SliceStateKey = "defaultModel" | "modelInsight" | "modelList" | "modelListLoading" | "modelInitList" | "modelInitAt" | "modelStaleAt" | "modelSelection" | "lastPageOfModel" | "pageOfModel" | "limitOfModel" | "queryArgsOfModel" | "sortOfModel";
|
|
28
|
+
export type SliceStateKey = "defaultModel" | "modelInsight" | "modelList" | "modelListLoading" | "modelInitList" | "modelInitAt" | "modelStaleAt" | "modelSelection" | "lastPageOfModel" | "pageOfModel" | "limitOfModel" | "hasMoreOfModel" | "isCumulativeOfModel" | "queryArgsOfModel" | "sortOfModel";
|
|
29
29
|
type _SliceMap<S extends SliceCls> = StoreSliceMap<S>;
|
|
30
30
|
type _StateRefName<S extends SliceCls> = SlceCnstRefName<S>;
|
|
31
31
|
type _StateCap<S extends SliceCls> = SlceCnstCapitalizedRefName<S>;
|
|
@@ -78,6 +78,8 @@ export type SliceState<RefName extends string, Suffix extends string, Full, Ligh
|
|
|
78
78
|
[K in `pageOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: number;
|
|
79
79
|
} & {
|
|
80
80
|
[K in `limitOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: number;
|
|
81
|
+
} & {
|
|
82
|
+
[K in `hasMoreOf${_CapitalizedRefName}${_CapitalizedSuffix}` | `isCumulativeOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: boolean;
|
|
81
83
|
} & {
|
|
82
84
|
[K in `queryArgsOf${_CapitalizedRefName}${_CapitalizedSuffix}`]: Args;
|
|
83
85
|
} & {
|
|
@@ -97,6 +99,8 @@ type DefaultSliceStateFields<SlceCls extends SliceCls, _RefName extends string,
|
|
|
97
99
|
[Suffix in _Suffixes as `${_RefName}Insight${StoreSliceSuffixCap<SlceCls, Suffix>}`]: _Insight;
|
|
98
100
|
} & {
|
|
99
101
|
[Suffix in _Suffixes as `lastPageOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}` | `pageOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}` | `limitOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: number;
|
|
102
|
+
} & {
|
|
103
|
+
[Suffix in _Suffixes as `hasMoreOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}` | `isCumulativeOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: boolean;
|
|
100
104
|
} & {
|
|
101
105
|
[Suffix in _Suffixes as `queryArgsOf${_CapRefName}${StoreSliceSuffixCap<SlceCls, Suffix>}`]: StoreSliceArgs<SlceCls, Suffix>;
|
|
102
106
|
} & {
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
export interface InfiniteScrollProps {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
itemsPerPage: number;
|
|
5
|
-
onAddPage: (page: number) => Promise<void>;
|
|
6
|
-
onPageSelect: (page: number, option?: {
|
|
7
|
-
scrollToTop?: boolean;
|
|
8
|
-
}) => void;
|
|
2
|
+
hasMore: boolean;
|
|
3
|
+
onLoadMore: () => Promise<void>;
|
|
9
4
|
children: React.ReactNode;
|
|
10
5
|
reverse?: boolean;
|
|
11
6
|
}
|
|
12
|
-
export declare const InfiniteScroll: ({
|
|
7
|
+
export declare const InfiniteScroll: ({ hasMore, onLoadMore, children, reverse }: InfiniteScrollProps) => import("react/jsx-runtime").JSX.Element;
|
package/types/ui/More.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ interface MoreProps {
|
|
|
2
2
|
total: number;
|
|
3
3
|
itemsPerPage: number;
|
|
4
4
|
currentPage: number;
|
|
5
|
-
|
|
5
|
+
hasMore: boolean;
|
|
6
|
+
onLoadMore: () => Promise<void>;
|
|
6
7
|
onPageSelect: (page: number, option?: {
|
|
7
8
|
scrollToTop?: boolean;
|
|
8
9
|
}) => void;
|
|
@@ -10,5 +11,5 @@ interface MoreProps {
|
|
|
10
11
|
className?: string;
|
|
11
12
|
reverse?: boolean;
|
|
12
13
|
}
|
|
13
|
-
export declare const More: ({ total, itemsPerPage, currentPage,
|
|
14
|
+
export declare const More: ({ total, itemsPerPage, currentPage, hasMore, onLoadMore, onPageSelect, children, className, reverse, }: MoreProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
15
|
export {};
|
package/ui/InfiniteScroll.tsx
CHANGED
|
@@ -3,29 +3,16 @@ import { useEffect, useRef, useState } from "react";
|
|
|
3
3
|
import { BiLoaderAlt } from "react-icons/bi";
|
|
4
4
|
|
|
5
5
|
export interface InfiniteScrollProps {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
itemsPerPage: number;
|
|
9
|
-
onAddPage: (page: number) => Promise<void>;
|
|
10
|
-
onPageSelect: (page: number, option?: { scrollToTop?: boolean }) => void;
|
|
6
|
+
hasMore: boolean;
|
|
7
|
+
onLoadMore: () => Promise<void>;
|
|
11
8
|
children: React.ReactNode;
|
|
12
9
|
reverse?: boolean;
|
|
13
10
|
}
|
|
14
11
|
|
|
15
|
-
export const InfiniteScroll = ({
|
|
16
|
-
itemsPerPage,
|
|
17
|
-
currentPage,
|
|
18
|
-
total,
|
|
19
|
-
onPageSelect,
|
|
20
|
-
onAddPage,
|
|
21
|
-
children,
|
|
22
|
-
reverse,
|
|
23
|
-
}: InfiniteScrollProps) => {
|
|
12
|
+
export const InfiniteScroll = ({ hasMore, onLoadMore, children, reverse }: InfiniteScrollProps) => {
|
|
24
13
|
const [isFetching, setIsFetching] = useState(false);
|
|
25
14
|
const isFetchingRef = useRef(false);
|
|
26
15
|
const target = useRef<HTMLDivElement>(null);
|
|
27
|
-
const page = useRef<number>(currentPage);
|
|
28
|
-
const totalPages = Math.ceil(total / (itemsPerPage || 1));
|
|
29
16
|
|
|
30
17
|
useEffect(() => {
|
|
31
18
|
const observer = new IntersectionObserver((entries) => {
|
|
@@ -36,12 +23,10 @@ export const InfiniteScroll = ({
|
|
|
36
23
|
return () => {
|
|
37
24
|
observer.disconnect();
|
|
38
25
|
};
|
|
39
|
-
}, []);
|
|
26
|
+
}, [hasMore]);
|
|
40
27
|
|
|
41
28
|
const fetchMoreItems = async () => {
|
|
42
29
|
if (isFetchingRef.current) return;
|
|
43
|
-
const nextPage = page.current + 1;
|
|
44
|
-
if (nextPage > totalPages) return;
|
|
45
30
|
|
|
46
31
|
const scroller = reverse ? document.scrollingElement : null;
|
|
47
32
|
const prevScrollHeight = scroller?.scrollHeight ?? 0;
|
|
@@ -50,9 +35,7 @@ export const InfiniteScroll = ({
|
|
|
50
35
|
isFetchingRef.current = true;
|
|
51
36
|
setIsFetching(true);
|
|
52
37
|
try {
|
|
53
|
-
await
|
|
54
|
-
onPageSelect(nextPage, { scrollToTop: false });
|
|
55
|
-
page.current = nextPage;
|
|
38
|
+
await onLoadMore();
|
|
56
39
|
|
|
57
40
|
const restoreScroll = () => {
|
|
58
41
|
if (scroller) {
|
|
@@ -76,15 +59,15 @@ export const InfiniteScroll = ({
|
|
|
76
59
|
|
|
77
60
|
return (
|
|
78
61
|
<>
|
|
79
|
-
{reverse ? (
|
|
62
|
+
{reverse && hasMore ? (
|
|
80
63
|
<div ref={target} className="flex w-full items-end justify-center">
|
|
81
|
-
{isFetching
|
|
64
|
+
{isFetching ? <BiLoaderAlt className="h-10 animate-spin pb-4 text-2xl" /> : null}
|
|
82
65
|
</div>
|
|
83
66
|
) : null}
|
|
84
67
|
{children}
|
|
85
|
-
{!reverse ? (
|
|
68
|
+
{!reverse && hasMore ? (
|
|
86
69
|
<div ref={target} className="flex h-32 w-full justify-center pt-4">
|
|
87
|
-
{isFetching
|
|
70
|
+
{isFetching ? <BiLoaderAlt className="animate-spin text-2xl" /> : null}
|
|
88
71
|
</div>
|
|
89
72
|
) : null}
|
|
90
73
|
</>
|
package/ui/Load/Pagination.tsx
CHANGED
|
@@ -39,7 +39,6 @@ function Render<RefName extends string, Light>({ className, init, scrollToTop }:
|
|
|
39
39
|
lastPageOfModel: `lastPageOf${ModelName}`,
|
|
40
40
|
limitOfModel: `limitOf${ModelName}`,
|
|
41
41
|
setPageOfModel: `setPageOf${ModelName}`,
|
|
42
|
-
addPageOfModel: `addPageOf${ModelName}`,
|
|
43
42
|
};
|
|
44
43
|
const namesOfSlice = {
|
|
45
44
|
modelInsight: sliceName.replace(names.model, names.modelInsight),
|
|
@@ -47,7 +46,6 @@ function Render<RefName extends string, Light>({ className, init, scrollToTop }:
|
|
|
47
46
|
lastPageOfModel: sliceName.replace(names.model, names.lastPageOfModel),
|
|
48
47
|
pageOfModel: sliceName.replace(names.model, names.pageOfModel),
|
|
49
48
|
setPageOfModel: sliceName.replace(names.model, names.setPageOfModel),
|
|
50
|
-
addPageOfModel: sliceName.replace(names.model, names.addPageOfModel),
|
|
51
49
|
};
|
|
52
50
|
const modelInsight = storeUse[namesOfSlice.modelInsight]() as BaseInsight;
|
|
53
51
|
const limitOfModel = storeUse[namesOfSlice.limitOfModel]() as number;
|
package/ui/Load/Units.tsx
CHANGED
|
@@ -85,10 +85,12 @@ function Render<RefName extends string, Light extends { id: string }>({
|
|
|
85
85
|
pageOfModel: `pageOf${ModelName}`,
|
|
86
86
|
lastPageOfModel: `lastPageOf${ModelName}`,
|
|
87
87
|
limitOfModel: `limitOf${ModelName}`,
|
|
88
|
+
hasMoreOfModel: `hasMoreOf${ModelName}`,
|
|
89
|
+
isCumulativeOfModel: `isCumulativeOf${ModelName}`,
|
|
88
90
|
queryArgsOfModel: `queryArgsOf${ModelName}`,
|
|
89
91
|
sortOfModel: `sortOf${ModelName}`,
|
|
90
92
|
setPageOfModel: `setPageOf${ModelName}`,
|
|
91
|
-
|
|
93
|
+
loadMoreOfModel: `loadMoreOf${ModelName}`,
|
|
92
94
|
refreshModel: `refresh${ModelName}`,
|
|
93
95
|
watchLiveModel: `watchLive${ModelName}`,
|
|
94
96
|
};
|
|
@@ -102,10 +104,12 @@ function Render<RefName extends string, Light extends { id: string }>({
|
|
|
102
104
|
pageOfModel: sliceName.replace(names.model, names.pageOfModel),
|
|
103
105
|
lastPageOfModel: sliceName.replace(names.model, names.lastPageOfModel),
|
|
104
106
|
limitOfModel: sliceName.replace(names.model, names.limitOfModel),
|
|
107
|
+
hasMoreOfModel: sliceName.replace(names.model, names.hasMoreOfModel),
|
|
108
|
+
isCumulativeOfModel: sliceName.replace(names.model, names.isCumulativeOfModel),
|
|
105
109
|
queryArgsOfModel: sliceName.replace(names.model, names.queryArgsOfModel),
|
|
106
110
|
sortOfModel: sliceName.replace(names.model, names.sortOfModel),
|
|
107
111
|
setPageOfModel: sliceName.replace(names.model, names.setPageOfModel),
|
|
108
|
-
|
|
112
|
+
loadMoreOfModel: sliceName.replace(names.model, names.loadMoreOfModel),
|
|
109
113
|
refreshModel: sliceName.replace(names.model, names.refreshModel),
|
|
110
114
|
watchLiveModel: sliceName.replace(names.model, names.watchLiveModel),
|
|
111
115
|
};
|
|
@@ -116,6 +120,7 @@ function Render<RefName extends string, Light extends { id: string }>({
|
|
|
116
120
|
const initModelObjInsight = (init as DynamicRecord)[names.modelObjInsight] as BaseInsight | null;
|
|
117
121
|
const initLimitOfModel = (init as DynamicRecord)[names.limitOfModel] as number;
|
|
118
122
|
const initPageOfModel = (init as DynamicRecord)[names.pageOfModel] as number;
|
|
123
|
+
const initHasMoreOfModel = (init as DynamicRecord)[names.hasMoreOfModel] as boolean;
|
|
119
124
|
const initSignature = JSON.stringify(initQueryArgs);
|
|
120
125
|
|
|
121
126
|
const useCache =
|
|
@@ -155,6 +160,9 @@ function Render<RefName extends string, Light extends { id: string }>({
|
|
|
155
160
|
[namesOfSlice.pageOfModel]: initPageOfModel,
|
|
156
161
|
[namesOfSlice.lastPageOfModel]: initLastPageOfModel,
|
|
157
162
|
[namesOfSlice.limitOfModel]: initLimitOfModel,
|
|
163
|
+
|
|
164
|
+
[namesOfSlice.hasMoreOfModel]: initHasMoreOfModel,
|
|
165
|
+
[namesOfSlice.isCumulativeOfModel]: false,
|
|
158
166
|
[namesOfSlice.queryArgsOfModel]: initQueryArgsOfModel,
|
|
159
167
|
[namesOfSlice.sortOfModel]: initSortOfModel,
|
|
160
168
|
});
|
|
@@ -180,6 +188,7 @@ function Render<RefName extends string, Light extends { id: string }>({
|
|
|
180
188
|
const modelInsight = storeUse[namesOfSlice.modelInsight]() as BaseInsight;
|
|
181
189
|
const limitOfModel = storeUse[namesOfSlice.limitOfModel]() as number;
|
|
182
190
|
const pageOfModel = storeUse[namesOfSlice.pageOfModel]() as number;
|
|
191
|
+
const hasMoreOfModel = storeUse[namesOfSlice.hasMoreOfModel]() as boolean;
|
|
183
192
|
const insight = loaded ? modelInsight : initModelObjInsight;
|
|
184
193
|
const limit = loaded ? limitOfModel : initLimitOfModel;
|
|
185
194
|
const page = loaded ? pageOfModel : initPageOfModel;
|
|
@@ -188,8 +197,9 @@ function Render<RefName extends string, Light extends { id: string }>({
|
|
|
188
197
|
total,
|
|
189
198
|
currentPage: page,
|
|
190
199
|
itemsPerPage: limit || total,
|
|
191
|
-
|
|
192
|
-
|
|
200
|
+
hasMore: loaded ? hasMoreOfModel : initHasMoreOfModel,
|
|
201
|
+
onLoadMore: async () => {
|
|
202
|
+
await storeDo[namesOfSlice.loadMoreOfModel]();
|
|
193
203
|
},
|
|
194
204
|
onPageSelect: (page: number, option?: { scrollToTop?: boolean }) => {
|
|
195
205
|
void storeDo[namesOfSlice.setPageOfModel](page);
|
|
@@ -342,7 +352,8 @@ interface MoreProps {
|
|
|
342
352
|
total: number;
|
|
343
353
|
itemsPerPage: number;
|
|
344
354
|
currentPage: number;
|
|
345
|
-
|
|
355
|
+
hasMore: boolean;
|
|
356
|
+
onLoadMore: () => Promise<void>;
|
|
346
357
|
onPageSelect: (page: number, option?: { scrollToTop?: boolean }) => void;
|
|
347
358
|
children?: React.ReactNode;
|
|
348
359
|
className?: string;
|
package/ui/More.tsx
CHANGED
|
@@ -8,7 +8,8 @@ interface MoreProps {
|
|
|
8
8
|
total: number;
|
|
9
9
|
itemsPerPage: number;
|
|
10
10
|
currentPage: number;
|
|
11
|
-
|
|
11
|
+
hasMore: boolean;
|
|
12
|
+
onLoadMore: () => Promise<void>;
|
|
12
13
|
onPageSelect: (page: number, option?: { scrollToTop?: boolean }) => void;
|
|
13
14
|
children?: React.ReactNode;
|
|
14
15
|
className?: string;
|
|
@@ -19,7 +20,8 @@ export const More = ({
|
|
|
19
20
|
total,
|
|
20
21
|
itemsPerPage,
|
|
21
22
|
currentPage,
|
|
22
|
-
|
|
23
|
+
hasMore,
|
|
24
|
+
onLoadMore,
|
|
23
25
|
onPageSelect,
|
|
24
26
|
children,
|
|
25
27
|
className,
|
|
@@ -31,24 +33,15 @@ export const More = ({
|
|
|
31
33
|
setIsMobile(isMobileDevice());
|
|
32
34
|
}, []);
|
|
33
35
|
|
|
34
|
-
if (
|
|
35
|
-
return <>{children}</>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (isMobile) {
|
|
36
|
+
if (isMobile)
|
|
39
37
|
return (
|
|
40
|
-
<InfiniteScroll
|
|
41
|
-
total={total}
|
|
42
|
-
currentPage={currentPage}
|
|
43
|
-
itemsPerPage={itemsPerPage}
|
|
44
|
-
onAddPage={onAddPage}
|
|
45
|
-
onPageSelect={onPageSelect}
|
|
46
|
-
reverse={reverse}
|
|
47
|
-
>
|
|
38
|
+
<InfiniteScroll hasMore={hasMore} onLoadMore={onLoadMore} reverse={reverse}>
|
|
48
39
|
{children}
|
|
49
40
|
</InfiniteScroll>
|
|
50
41
|
);
|
|
51
|
-
|
|
42
|
+
|
|
43
|
+
if (total <= itemsPerPage) return <>{children}</>;
|
|
44
|
+
|
|
52
45
|
return (
|
|
53
46
|
<>
|
|
54
47
|
{children}
|