@contember/bindx-dataview 0.1.51 → 0.1.53
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/dist/DataGrid.d.ts.map +1 -1
- package/dist/DataViewContext.d.ts +2 -2
- package/dist/DataViewContext.d.ts.map +1 -1
- package/dist/HasManyDataGrid.d.ts.map +1 -1
- package/dist/columnLeaf.d.ts +32 -1
- package/dist/columnLeaf.d.ts.map +1 -1
- package/dist/columns.d.ts +20 -8
- package/dist/columns.d.ts.map +1 -1
- package/dist/contextHooks.d.ts.map +1 -1
- package/dist/createColumn.d.ts +14 -4
- package/dist/createColumn.d.ts.map +1 -1
- package/dist/createRelationColumn.d.ts +7 -1
- package/dist/createRelationColumn.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/select/SelectDataView.d.ts.map +1 -1
- package/dist/useDataGridSetup.d.ts +2 -2
- package/dist/useDataGridSetup.d.ts.map +1 -1
- package/dist/useDataViewState.d.ts +7 -1
- package/dist/useDataViewState.d.ts.map +1 -1
- package/dist/usePagingTotalCount.d.ts +19 -0
- package/dist/usePagingTotalCount.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/DataGrid.tsx +8 -11
- package/src/DataViewContext.tsx +2 -2
- package/src/HasManyDataGrid.tsx +28 -17
- package/src/columnLeaf.ts +56 -1
- package/src/columns.tsx +20 -4
- package/src/contextHooks.ts +16 -7
- package/src/createColumn.ts +19 -7
- package/src/createRelationColumn.tsx +10 -2
- package/src/export.tsx +2 -2
- package/src/index.ts +2 -0
- package/src/select/SelectDataView.tsx +8 -9
- package/src/useDataGridSetup.ts +6 -6
- package/src/useDataViewState.ts +38 -15
- package/src/usePagingTotalCount.ts +90 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react'
|
|
2
|
+
import type { PagingStateResult } from './useDataViewState.js'
|
|
3
|
+
|
|
4
|
+
export interface PagingTotalSource {
|
|
5
|
+
/** Total reported by the data source, or null when it reports none. */
|
|
6
|
+
readonly countedTotal: number | null
|
|
7
|
+
/** Identifies the counted row set (the filter); bounds learned for another row set are dropped. */
|
|
8
|
+
readonly countKey: string
|
|
9
|
+
/** Whether the loaded rows answer the current query: filter, order, limit and offset. */
|
|
10
|
+
readonly isPageCurrent: boolean
|
|
11
|
+
readonly pageItemCount: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** What the loaded pages prove about the total: `atLeast <= total <= atMost`. */
|
|
15
|
+
interface TotalBounds {
|
|
16
|
+
readonly atLeast: number
|
|
17
|
+
readonly atMost: number | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface LoadedPage {
|
|
21
|
+
readonly offset: number
|
|
22
|
+
readonly limit: number
|
|
23
|
+
readonly itemCount: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type PageOutcome =
|
|
27
|
+
| { readonly type: 'total'; readonly totalCount: number }
|
|
28
|
+
| { readonly type: 'probe'; readonly bounds: TotalBounds; readonly pageIndex: number }
|
|
29
|
+
| { readonly type: 'unknown'; readonly bounds: TotalBounds }
|
|
30
|
+
|
|
31
|
+
const NO_BOUNDS: TotalBounds = { atLeast: 0, atMost: null }
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Feeds the paging state its total: the counted total when the data source reports one,
|
|
35
|
+
* otherwise the total the loaded pages prove. Only a page that answers the current query counts.
|
|
36
|
+
*
|
|
37
|
+
* A page shorter than the limit ends the list. An empty page past offset 0 only bounds the total
|
|
38
|
+
* from above, so the grid bisects between the bounds, one page per query, until a page ends the list.
|
|
39
|
+
*/
|
|
40
|
+
export function usePagingTotalCount(paging: PagingStateResult, source: PagingTotalSource): void {
|
|
41
|
+
const { setTotalCount, goTo, queryLimit, queryOffset } = paging
|
|
42
|
+
const { countedTotal, countKey, isPageCurrent, pageItemCount } = source
|
|
43
|
+
const boundsRef = useRef<{ countKey: string; bounds: TotalBounds }>({ countKey, bounds: NO_BOUNDS })
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (boundsRef.current.countKey !== countKey || countedTotal !== null) {
|
|
47
|
+
boundsRef.current = { countKey, bounds: NO_BOUNDS }
|
|
48
|
+
}
|
|
49
|
+
if (countedTotal !== null) {
|
|
50
|
+
setTotalCount(countedTotal)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
if (!isPageCurrent || queryLimit === undefined || queryOffset === undefined) {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const outcome = inferTotalFromPage(boundsRef.current.bounds, { offset: queryOffset, limit: queryLimit, itemCount: pageItemCount })
|
|
58
|
+
if (outcome.type === 'total') {
|
|
59
|
+
boundsRef.current = { countKey, bounds: NO_BOUNDS }
|
|
60
|
+
setTotalCount(outcome.totalCount)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
boundsRef.current = { countKey, bounds: outcome.bounds }
|
|
64
|
+
if (outcome.type === 'probe') {
|
|
65
|
+
goTo(outcome.pageIndex)
|
|
66
|
+
}
|
|
67
|
+
}, [countedTotal, countKey, isPageCurrent, pageItemCount, queryLimit, queryOffset, setTotalCount, goTo])
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function inferTotalFromPage(bounds: TotalBounds, { offset, limit, itemCount }: LoadedPage): PageOutcome {
|
|
71
|
+
const isEmptyPastStart = itemCount === 0 && offset > 0
|
|
72
|
+
if (itemCount < limit && !isEmptyPastStart) {
|
|
73
|
+
return { type: 'total', totalCount: offset + itemCount }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const narrowed: TotalBounds = isEmptyPastStart
|
|
77
|
+
? { atLeast: bounds.atLeast, atMost: Math.min(bounds.atMost ?? offset, offset) }
|
|
78
|
+
: { atLeast: Math.max(bounds.atLeast, offset + limit), atMost: bounds.atMost }
|
|
79
|
+
|
|
80
|
+
if (narrowed.atMost === null) {
|
|
81
|
+
return { type: 'unknown', bounds: narrowed }
|
|
82
|
+
}
|
|
83
|
+
if (narrowed.atLeast >= narrowed.atMost) {
|
|
84
|
+
return { type: 'total', totalCount: narrowed.atMost }
|
|
85
|
+
}
|
|
86
|
+
// The page holding the middle row starts below `atMost` and ends above `atLeast`,
|
|
87
|
+
// so whatever it returns halves the interval.
|
|
88
|
+
const middleRow = Math.floor((narrowed.atLeast + narrowed.atMost) / 2)
|
|
89
|
+
return { type: 'probe', bounds: narrowed, pageIndex: Math.floor(middleRow / limit) }
|
|
90
|
+
}
|