najm-kit 2.7.1 → 2.7.3
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/CHANGELOG.md +112 -112
- package/dist/{NajmUIProvider-Dj32bd5d.d.ts → NajmUIProvider-IFU3dFkn.d.ts} +3 -142
- package/dist/adapters/app.d.ts +26 -1
- package/dist/adapters/app.mjs +30 -4
- package/dist/adapters/next.d.ts +2 -1
- package/dist/cardPagination-A6h8vXuk.d.ts +53 -0
- package/dist/chunk-2NX2VKS2.mjs +42 -0
- package/dist/chunk-F5KXJCCJ.mjs +1 -0
- package/dist/chunk-GPHWBOSP.mjs +104 -0
- package/dist/{chunk-5LW62RB6.mjs → chunk-VKQIRB7F.mjs} +53 -2
- package/dist/chunk-XGDMPI5U.mjs +52 -0
- package/dist/format.d.ts +53 -0
- package/dist/format.mjs +2 -0
- package/dist/index.d.ts +93 -3
- package/dist/index.mjs +6 -2
- package/dist/pagination.d.ts +78 -0
- package/dist/pagination.mjs +1 -0
- package/dist/paginationLabels-DgHutNWz.d.ts +143 -0
- package/dist/query.d.ts +301 -0
- package/dist/query.mjs +133 -0
- package/package.json +21 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* How the page controls present position within the result.
|
|
5
|
+
*
|
|
6
|
+
* `numbered` renders a windowed list of page buttons. `compact` renders the
|
|
7
|
+
* `Page X of Y` text with first/previous/next/last controls.
|
|
8
|
+
*
|
|
9
|
+
* `numbered` needs a trustworthy page count. Under `manualPagination` that
|
|
10
|
+
* means the application must pass a `pageCount` derived from a real result
|
|
11
|
+
* total, or — when its endpoint reports no total — pass `hasNextPage` and no
|
|
12
|
+
* `pageCount` at all, which renders the unbounded bar described on the NTable
|
|
13
|
+
* prop. With neither, the bar falls back to `compact` on its own rather than
|
|
14
|
+
* inviting clicks on pages that may not exist.
|
|
15
|
+
*
|
|
16
|
+
* What it must never be handed is a `pageCount` that is really a lower bound,
|
|
17
|
+
* such as `pageIndex + 2`. That reads as a two-page result on page one and a
|
|
18
|
+
* three-page result on page two, so the bar grows a number per click with
|
|
19
|
+
* nothing to say why. NTable warns in development when it catches a count
|
|
20
|
+
* moving in lockstep with the page index.
|
|
21
|
+
*/
|
|
22
|
+
type NTablePaginationVariant = "numbered" | "compact";
|
|
23
|
+
/**
|
|
24
|
+
* Accessible names and visible copy for the page controls.
|
|
25
|
+
*
|
|
26
|
+
* Every field is optional and falls back to English. Supply them to localize —
|
|
27
|
+
* the numbered variant is mostly digits, but its controls still need names.
|
|
28
|
+
*/
|
|
29
|
+
interface NTablePaginationLabels {
|
|
30
|
+
/** Labels the rows-per-page select. Defaults to `"Rows/page"`. */
|
|
31
|
+
rowsPerPage?: string;
|
|
32
|
+
/** Accessible name of the whole page control group. Defaults to `"Pagination"`. */
|
|
33
|
+
pagination?: string;
|
|
34
|
+
/** Accessible name for one page button, given a 1-based page. */
|
|
35
|
+
goToPage?: (page: number) => string;
|
|
36
|
+
/** Accessible name of the current page button, given a 1-based page. */
|
|
37
|
+
currentPage?: (page: number) => string;
|
|
38
|
+
firstPage?: string;
|
|
39
|
+
previousPage?: string;
|
|
40
|
+
nextPage?: string;
|
|
41
|
+
lastPage?: string;
|
|
42
|
+
/** The `compact` variant's position text, given 1-based values. */
|
|
43
|
+
pageOf?: (page: number, pageCount: number) => string;
|
|
44
|
+
/**
|
|
45
|
+
* The position text when the result has no known total, given the 1-based
|
|
46
|
+
* page. Defaults to `"Page X"` — there is no `of Y` to state, and repeating
|
|
47
|
+
* the moving lower bound there would be the same lie the numbered bar avoids.
|
|
48
|
+
*/
|
|
49
|
+
pageOfUnknown?: (page: number) => string;
|
|
50
|
+
/** The selection summary, given selected and total row counts. */
|
|
51
|
+
rowsSelected?: (selected: number, total: number) => string;
|
|
52
|
+
}
|
|
53
|
+
interface NTableLoadMorePagination {
|
|
54
|
+
/** Render the supplied rows as one card list with an explicit continuation control. */
|
|
55
|
+
mode: "load-more";
|
|
56
|
+
/** Whether the owning application has another server page available. */
|
|
57
|
+
hasNextPage: boolean;
|
|
58
|
+
/** True while the owning application is appending the next page. */
|
|
59
|
+
loadingMore?: boolean;
|
|
60
|
+
/** A controlled append error. Existing rows remain rendered and the control becomes Retry. */
|
|
61
|
+
loadMoreError?: ReactNode;
|
|
62
|
+
/** Fetch exactly one additional page. Najm Kit never constructs or owns the request. */
|
|
63
|
+
onLoadMore: () => unknown | Promise<unknown>;
|
|
64
|
+
loadMoreLabel?: string;
|
|
65
|
+
loadingMoreLabel?: string;
|
|
66
|
+
retryLabel?: string;
|
|
67
|
+
endLabel?: string;
|
|
68
|
+
loadMoreErrorLabel?: string;
|
|
69
|
+
/** Localize the polite announcement made after appended rows arrive. */
|
|
70
|
+
itemsLoadedLabel?: (count: number) => string;
|
|
71
|
+
}
|
|
72
|
+
interface NTableInfinitePagination {
|
|
73
|
+
/**
|
|
74
|
+
* Render the supplied rows as one card list that continues automatically when
|
|
75
|
+
* the end of the list scrolls into view. No control and no end-of-list
|
|
76
|
+
* element are rendered while the list is healthy; the continuation button
|
|
77
|
+
* appears only after an append failure, as the retry target.
|
|
78
|
+
*/
|
|
79
|
+
mode: "infinite";
|
|
80
|
+
/** Whether the owning application has another server page available. */
|
|
81
|
+
hasNextPage: boolean;
|
|
82
|
+
/** True while the owning application is appending the next page. */
|
|
83
|
+
loadingMore?: boolean;
|
|
84
|
+
/** A controlled append error. Existing rows remain rendered and Retry appears. */
|
|
85
|
+
loadMoreError?: ReactNode;
|
|
86
|
+
/** Fetch exactly one additional page. Najm Kit never constructs or owns the request. */
|
|
87
|
+
onLoadMore: () => unknown | Promise<unknown>;
|
|
88
|
+
/**
|
|
89
|
+
* Distance ahead of the list end at which the next page is requested.
|
|
90
|
+
* Defaults to `"80px"`.
|
|
91
|
+
*/
|
|
92
|
+
rootMargin?: string;
|
|
93
|
+
loadingMoreLabel?: string;
|
|
94
|
+
retryLabel?: string;
|
|
95
|
+
loadMoreErrorLabel?: string;
|
|
96
|
+
/** Localize the polite announcement made after appended rows arrive. */
|
|
97
|
+
itemsLoadedLabel?: (count: number) => string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Presentation policy used while NTable is actually rendering cards.
|
|
101
|
+
*
|
|
102
|
+
* `paged` preserves the existing page controls. `all` renders every supplied
|
|
103
|
+
* row without a footer, in card and table modes alike. `load-more` renders
|
|
104
|
+
* every supplied row and adds a guarded, accessible continuation control.
|
|
105
|
+
* `infinite` renders every supplied row and continues on scroll instead.
|
|
106
|
+
*
|
|
107
|
+
* Applications remain responsible for fetching, accumulating, filtering,
|
|
108
|
+
* sorting, authorization, and privacy. `all` renders exactly the rows it is
|
|
109
|
+
* given and never fetches, so a caller that has not loaded the whole set must
|
|
110
|
+
* not select it.
|
|
111
|
+
*/
|
|
112
|
+
type NTableCardPagination = {
|
|
113
|
+
mode?: "paged";
|
|
114
|
+
} | {
|
|
115
|
+
mode: "all";
|
|
116
|
+
} | NTableLoadMorePagination | NTableInfinitePagination;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The translator shape `NajmUIProvider` accepts.
|
|
120
|
+
*
|
|
121
|
+
* Deliberately structural rather than an import from `najm-i18n`: the kit
|
|
122
|
+
* depends on no `najm-*` package, and this signature is satisfied by every
|
|
123
|
+
* mainstream i18n library. The application keeps its catalog and its own
|
|
124
|
+
* language provider.
|
|
125
|
+
*/
|
|
126
|
+
type NajmTranslate = (key: string, params?: Record<string, string | number>) => string;
|
|
127
|
+
declare const DEFAULT_PAGINATION_KEY_PREFIX = "common.pagination";
|
|
128
|
+
/**
|
|
129
|
+
* Projects a translator onto the ten pagination labels.
|
|
130
|
+
*
|
|
131
|
+
* Keys are `<prefix>.<field>`, matching the `NTablePaginationLabels` field
|
|
132
|
+
* names one-for-one, so a catalog is readable next to the type. Interpolation
|
|
133
|
+
* params are named for what they are: `page`, `pageCount`, `selected`, `total`.
|
|
134
|
+
*
|
|
135
|
+
* No result is inspected or second-guessed. A translator that echoes missing
|
|
136
|
+
* keys will render those keys — that is the translator's contract to define,
|
|
137
|
+
* and quietly swapping in English would hide the missing entry rather than
|
|
138
|
+
* surface it. Applications that want the packaged English for a given label
|
|
139
|
+
* should omit the key from the prefix and override it via `tableDefaults`.
|
|
140
|
+
*/
|
|
141
|
+
declare function buildPaginationLabels(t: NajmTranslate, prefix?: string): NTablePaginationLabels;
|
|
142
|
+
|
|
143
|
+
export { DEFAULT_PAGINATION_KEY_PREFIX as D, type NajmTranslate as N, type NTableCardPagination as a, type NTablePaginationLabels as b, type NTablePaginationVariant as c, type NTableInfinitePagination as d, type NTableLoadMorePagination as e, buildPaginationLabels as f };
|
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import * as _tanstack_query_core from '@tanstack/query-core';
|
|
2
|
+
import { OffsetPageFetcher, OffsetPage } from './pagination.js';
|
|
3
|
+
export { ApiPage, DEFAULT_MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, OffsetPageOptions, OffsetPagination, QueryValue, cleanQuery, createOffsetPagination, fetchOffsetPage, getPageIndex } from './pagination.js';
|
|
4
|
+
import { L as ListStrategy, R as ResolvedListMode } from './cardPagination-A6h8vXuk.js';
|
|
5
|
+
export { C as CardPaginationLabels, a as CardPaginationState, b as buildCardPaginationLabels, c as createCardPagination } from './cardPagination-A6h8vXuk.js';
|
|
6
|
+
import './paginationLabels-DgHutNWz.js';
|
|
7
|
+
import 'react';
|
|
8
|
+
|
|
9
|
+
interface UseOffsetInfiniteQueryOptions<T> {
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
fetchPage: OffsetPageFetcher<T>;
|
|
12
|
+
queryKey: readonly unknown[];
|
|
13
|
+
/**
|
|
14
|
+
* The size of one *server request*, not of a displayed page.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately decoupled: the display page size is measured from the rendered
|
|
17
|
+
* container and moves as the viewport, column count and card height settle.
|
|
18
|
+
* Tying a request to it would put a network round trip behind every
|
|
19
|
+
* measurement correction, and a second skeleton behind each round trip.
|
|
20
|
+
*/
|
|
21
|
+
windowSize?: number;
|
|
22
|
+
/** The server's `limit` clamp. Defaults to `DEFAULT_MAX_PAGE_SIZE`. */
|
|
23
|
+
maxLimit?: number;
|
|
24
|
+
}
|
|
25
|
+
declare const DEFAULT_ROW_WINDOW_SIZE = 50;
|
|
26
|
+
/**
|
|
27
|
+
* An accumulating row buffer over `fetchOffsetPage`.
|
|
28
|
+
*
|
|
29
|
+
* Every mode of `useResponsiveOffsetList` reads from one of these — numbered
|
|
30
|
+
* pages are a slice of the buffer rather than a request of their own.
|
|
31
|
+
*/
|
|
32
|
+
declare function useOffsetInfiniteQuery<T>({ enabled, fetchPage, queryKey, windowSize, maxLimit, }: UseOffsetInfiniteQueryOptions<T>): {
|
|
33
|
+
rows: T[];
|
|
34
|
+
hasNextPage: boolean;
|
|
35
|
+
total: number;
|
|
36
|
+
data: _tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>;
|
|
37
|
+
error: Error;
|
|
38
|
+
isError: true;
|
|
39
|
+
isPending: false;
|
|
40
|
+
isLoading: false;
|
|
41
|
+
isLoadingError: false;
|
|
42
|
+
isRefetchError: true;
|
|
43
|
+
isSuccess: false;
|
|
44
|
+
isPlaceholderData: false;
|
|
45
|
+
status: "error";
|
|
46
|
+
fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
47
|
+
fetchPreviousPage: (options?: _tanstack_query_core.FetchPreviousPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
48
|
+
hasPreviousPage: boolean;
|
|
49
|
+
isFetchNextPageError: boolean;
|
|
50
|
+
isFetchingNextPage: boolean;
|
|
51
|
+
isFetchPreviousPageError: boolean;
|
|
52
|
+
isFetchingPreviousPage: boolean;
|
|
53
|
+
dataUpdatedAt: number;
|
|
54
|
+
errorUpdatedAt: number;
|
|
55
|
+
failureCount: number;
|
|
56
|
+
failureReason: Error;
|
|
57
|
+
errorUpdateCount: number;
|
|
58
|
+
isFetched: boolean;
|
|
59
|
+
isFetchedAfterMount: boolean;
|
|
60
|
+
isFetching: boolean;
|
|
61
|
+
isInitialLoading: boolean;
|
|
62
|
+
isPaused: boolean;
|
|
63
|
+
isRefetching: boolean;
|
|
64
|
+
isStale: boolean;
|
|
65
|
+
isEnabled: boolean;
|
|
66
|
+
refetch: (options?: _tanstack_query_core.RefetchOptions) => Promise<_tanstack_query_core.QueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
67
|
+
fetchStatus: _tanstack_query_core.FetchStatus;
|
|
68
|
+
promise: Promise<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>>;
|
|
69
|
+
} | {
|
|
70
|
+
rows: T[];
|
|
71
|
+
hasNextPage: boolean;
|
|
72
|
+
total: number;
|
|
73
|
+
data: _tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>;
|
|
74
|
+
error: null;
|
|
75
|
+
isError: false;
|
|
76
|
+
isPending: false;
|
|
77
|
+
isLoading: false;
|
|
78
|
+
isLoadingError: false;
|
|
79
|
+
isRefetchError: false;
|
|
80
|
+
isFetchNextPageError: false;
|
|
81
|
+
isFetchPreviousPageError: false;
|
|
82
|
+
isSuccess: true;
|
|
83
|
+
isPlaceholderData: false;
|
|
84
|
+
status: "success";
|
|
85
|
+
fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
86
|
+
fetchPreviousPage: (options?: _tanstack_query_core.FetchPreviousPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
87
|
+
hasPreviousPage: boolean;
|
|
88
|
+
isFetchingNextPage: boolean;
|
|
89
|
+
isFetchingPreviousPage: boolean;
|
|
90
|
+
dataUpdatedAt: number;
|
|
91
|
+
errorUpdatedAt: number;
|
|
92
|
+
failureCount: number;
|
|
93
|
+
failureReason: Error;
|
|
94
|
+
errorUpdateCount: number;
|
|
95
|
+
isFetched: boolean;
|
|
96
|
+
isFetchedAfterMount: boolean;
|
|
97
|
+
isFetching: boolean;
|
|
98
|
+
isInitialLoading: boolean;
|
|
99
|
+
isPaused: boolean;
|
|
100
|
+
isRefetching: boolean;
|
|
101
|
+
isStale: boolean;
|
|
102
|
+
isEnabled: boolean;
|
|
103
|
+
refetch: (options?: _tanstack_query_core.RefetchOptions) => Promise<_tanstack_query_core.QueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
104
|
+
fetchStatus: _tanstack_query_core.FetchStatus;
|
|
105
|
+
promise: Promise<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>>;
|
|
106
|
+
} | {
|
|
107
|
+
rows: T[];
|
|
108
|
+
hasNextPage: boolean;
|
|
109
|
+
total: number;
|
|
110
|
+
data: undefined;
|
|
111
|
+
error: Error;
|
|
112
|
+
isError: true;
|
|
113
|
+
isPending: false;
|
|
114
|
+
isLoading: false;
|
|
115
|
+
isLoadingError: true;
|
|
116
|
+
isRefetchError: false;
|
|
117
|
+
isFetchNextPageError: false;
|
|
118
|
+
isFetchPreviousPageError: false;
|
|
119
|
+
isSuccess: false;
|
|
120
|
+
isPlaceholderData: false;
|
|
121
|
+
status: "error";
|
|
122
|
+
fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
123
|
+
fetchPreviousPage: (options?: _tanstack_query_core.FetchPreviousPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
124
|
+
hasPreviousPage: boolean;
|
|
125
|
+
isFetchingNextPage: boolean;
|
|
126
|
+
isFetchingPreviousPage: boolean;
|
|
127
|
+
dataUpdatedAt: number;
|
|
128
|
+
errorUpdatedAt: number;
|
|
129
|
+
failureCount: number;
|
|
130
|
+
failureReason: Error;
|
|
131
|
+
errorUpdateCount: number;
|
|
132
|
+
isFetched: boolean;
|
|
133
|
+
isFetchedAfterMount: boolean;
|
|
134
|
+
isFetching: boolean;
|
|
135
|
+
isInitialLoading: boolean;
|
|
136
|
+
isPaused: boolean;
|
|
137
|
+
isRefetching: boolean;
|
|
138
|
+
isStale: boolean;
|
|
139
|
+
isEnabled: boolean;
|
|
140
|
+
refetch: (options?: _tanstack_query_core.RefetchOptions) => Promise<_tanstack_query_core.QueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
141
|
+
fetchStatus: _tanstack_query_core.FetchStatus;
|
|
142
|
+
promise: Promise<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>>;
|
|
143
|
+
} | {
|
|
144
|
+
rows: T[];
|
|
145
|
+
hasNextPage: boolean;
|
|
146
|
+
total: number;
|
|
147
|
+
data: undefined;
|
|
148
|
+
error: null;
|
|
149
|
+
isError: false;
|
|
150
|
+
isPending: true;
|
|
151
|
+
isLoading: true;
|
|
152
|
+
isLoadingError: false;
|
|
153
|
+
isRefetchError: false;
|
|
154
|
+
isFetchNextPageError: false;
|
|
155
|
+
isFetchPreviousPageError: false;
|
|
156
|
+
isSuccess: false;
|
|
157
|
+
isPlaceholderData: false;
|
|
158
|
+
status: "pending";
|
|
159
|
+
fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
160
|
+
fetchPreviousPage: (options?: _tanstack_query_core.FetchPreviousPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
161
|
+
hasPreviousPage: boolean;
|
|
162
|
+
isFetchingNextPage: boolean;
|
|
163
|
+
isFetchingPreviousPage: boolean;
|
|
164
|
+
dataUpdatedAt: number;
|
|
165
|
+
errorUpdatedAt: number;
|
|
166
|
+
failureCount: number;
|
|
167
|
+
failureReason: Error;
|
|
168
|
+
errorUpdateCount: number;
|
|
169
|
+
isFetched: boolean;
|
|
170
|
+
isFetchedAfterMount: boolean;
|
|
171
|
+
isFetching: boolean;
|
|
172
|
+
isInitialLoading: boolean;
|
|
173
|
+
isPaused: boolean;
|
|
174
|
+
isRefetching: boolean;
|
|
175
|
+
isStale: boolean;
|
|
176
|
+
isEnabled: boolean;
|
|
177
|
+
refetch: (options?: _tanstack_query_core.RefetchOptions) => Promise<_tanstack_query_core.QueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
178
|
+
fetchStatus: _tanstack_query_core.FetchStatus;
|
|
179
|
+
promise: Promise<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>>;
|
|
180
|
+
} | {
|
|
181
|
+
rows: T[];
|
|
182
|
+
hasNextPage: boolean;
|
|
183
|
+
total: number;
|
|
184
|
+
data: undefined;
|
|
185
|
+
error: null;
|
|
186
|
+
isError: false;
|
|
187
|
+
isPending: true;
|
|
188
|
+
isLoadingError: false;
|
|
189
|
+
isRefetchError: false;
|
|
190
|
+
isFetchNextPageError: false;
|
|
191
|
+
isFetchPreviousPageError: false;
|
|
192
|
+
isSuccess: false;
|
|
193
|
+
isPlaceholderData: false;
|
|
194
|
+
status: "pending";
|
|
195
|
+
fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
196
|
+
fetchPreviousPage: (options?: _tanstack_query_core.FetchPreviousPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
197
|
+
hasPreviousPage: boolean;
|
|
198
|
+
isFetchingNextPage: boolean;
|
|
199
|
+
isFetchingPreviousPage: boolean;
|
|
200
|
+
dataUpdatedAt: number;
|
|
201
|
+
errorUpdatedAt: number;
|
|
202
|
+
failureCount: number;
|
|
203
|
+
failureReason: Error;
|
|
204
|
+
errorUpdateCount: number;
|
|
205
|
+
isFetched: boolean;
|
|
206
|
+
isFetchedAfterMount: boolean;
|
|
207
|
+
isFetching: boolean;
|
|
208
|
+
isLoading: boolean;
|
|
209
|
+
isInitialLoading: boolean;
|
|
210
|
+
isPaused: boolean;
|
|
211
|
+
isRefetching: boolean;
|
|
212
|
+
isStale: boolean;
|
|
213
|
+
isEnabled: boolean;
|
|
214
|
+
refetch: (options?: _tanstack_query_core.RefetchOptions) => Promise<_tanstack_query_core.QueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
215
|
+
fetchStatus: _tanstack_query_core.FetchStatus;
|
|
216
|
+
promise: Promise<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>>;
|
|
217
|
+
} | {
|
|
218
|
+
rows: T[];
|
|
219
|
+
hasNextPage: boolean;
|
|
220
|
+
total: number;
|
|
221
|
+
data: _tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>;
|
|
222
|
+
isError: false;
|
|
223
|
+
error: null;
|
|
224
|
+
isPending: false;
|
|
225
|
+
isLoading: false;
|
|
226
|
+
isLoadingError: false;
|
|
227
|
+
isRefetchError: false;
|
|
228
|
+
isSuccess: true;
|
|
229
|
+
isPlaceholderData: true;
|
|
230
|
+
isFetchNextPageError: false;
|
|
231
|
+
isFetchPreviousPageError: false;
|
|
232
|
+
status: "success";
|
|
233
|
+
fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
234
|
+
fetchPreviousPage: (options?: _tanstack_query_core.FetchPreviousPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
235
|
+
hasPreviousPage: boolean;
|
|
236
|
+
isFetchingNextPage: boolean;
|
|
237
|
+
isFetchingPreviousPage: boolean;
|
|
238
|
+
dataUpdatedAt: number;
|
|
239
|
+
errorUpdatedAt: number;
|
|
240
|
+
failureCount: number;
|
|
241
|
+
failureReason: Error;
|
|
242
|
+
errorUpdateCount: number;
|
|
243
|
+
isFetched: boolean;
|
|
244
|
+
isFetchedAfterMount: boolean;
|
|
245
|
+
isFetching: boolean;
|
|
246
|
+
isInitialLoading: boolean;
|
|
247
|
+
isPaused: boolean;
|
|
248
|
+
isRefetching: boolean;
|
|
249
|
+
isStale: boolean;
|
|
250
|
+
isEnabled: boolean;
|
|
251
|
+
refetch: (options?: _tanstack_query_core.RefetchOptions) => Promise<_tanstack_query_core.QueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
252
|
+
fetchStatus: _tanstack_query_core.FetchStatus;
|
|
253
|
+
promise: Promise<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>>;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
interface PaginationState {
|
|
257
|
+
pageIndex: number;
|
|
258
|
+
pageSize: number;
|
|
259
|
+
}
|
|
260
|
+
type PaginationUpdater = PaginationState | ((current: PaginationState) => PaginationState);
|
|
261
|
+
interface UseResponsiveOffsetListOptions<T> {
|
|
262
|
+
enabled?: boolean;
|
|
263
|
+
fetchPage: OffsetPageFetcher<T>;
|
|
264
|
+
pageSize?: number;
|
|
265
|
+
queryKey: readonly unknown[];
|
|
266
|
+
/** Defaults to `"paged"`. See `ListStrategy`. */
|
|
267
|
+
strategy?: ListStrategy;
|
|
268
|
+
/** The server's `limit` clamp. Defaults to `DEFAULT_MAX_PAGE_SIZE`. */
|
|
269
|
+
maxLimit?: number;
|
|
270
|
+
/** Below this width the list continues on scroll. Defaults to `lg`. */
|
|
271
|
+
cardBreakpoint?: number;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* One offset-paginated list, in whichever presentation the viewport calls for.
|
|
275
|
+
*
|
|
276
|
+
* Feeds `NTable` directly: the return value carries `pagination`,
|
|
277
|
+
* `onPaginationChange` and `pageCount` for the page controls, and the fields
|
|
278
|
+
* `createCardPagination` reads for scroll continuation.
|
|
279
|
+
*/
|
|
280
|
+
declare function useResponsiveOffsetList<T>({ enabled, fetchPage, pageSize, queryKey, strategy, maxLimit, cardBreakpoint, }: UseResponsiveOffsetListOptions<T>): {
|
|
281
|
+
cardViewport: boolean;
|
|
282
|
+
mode: ResolvedListMode;
|
|
283
|
+
data: T[];
|
|
284
|
+
/** Rows matching the current filters on the server, or `null` if unknown. */
|
|
285
|
+
total: number;
|
|
286
|
+
error: Error;
|
|
287
|
+
hasNextPage: boolean;
|
|
288
|
+
loading: boolean;
|
|
289
|
+
loadingMore: boolean;
|
|
290
|
+
loadMoreError: Error;
|
|
291
|
+
onLoadMore: () => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
292
|
+
onPaginationChange: (updater: PaginationUpdater) => void;
|
|
293
|
+
pageCount: number;
|
|
294
|
+
pagination: {
|
|
295
|
+
pageIndex: number;
|
|
296
|
+
pageSize: number;
|
|
297
|
+
};
|
|
298
|
+
refetch: (options?: _tanstack_query_core.RefetchOptions) => Promise<_tanstack_query_core.QueryObserverResult<_tanstack_query_core.InfiniteData<OffsetPage<T>, unknown>, Error>>;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
export { DEFAULT_ROW_WINDOW_SIZE, ListStrategy, OffsetPage, OffsetPageFetcher, ResolvedListMode, type UseOffsetInfiniteQueryOptions, type UseResponsiveOffsetListOptions, useOffsetInfiniteQuery, useResponsiveOffsetList };
|
package/dist/query.mjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { useCardViewport } from './chunk-XGDMPI5U.mjs';
|
|
2
|
+
export { buildCardPaginationLabels, createCardPagination } from './chunk-XGDMPI5U.mjs';
|
|
3
|
+
import { fetchOffsetPage, DEFAULT_MAX_PAGE_SIZE } from './chunk-2NX2VKS2.mjs';
|
|
4
|
+
export { DEFAULT_MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, cleanQuery, createOffsetPagination, fetchOffsetPage, getPageIndex } from './chunk-2NX2VKS2.mjs';
|
|
5
|
+
import { useInfiniteQuery } from '@tanstack/react-query';
|
|
6
|
+
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
7
|
+
|
|
8
|
+
var DEFAULT_ROW_WINDOW_SIZE = 50;
|
|
9
|
+
function useOffsetInfiniteQuery({
|
|
10
|
+
enabled = true,
|
|
11
|
+
fetchPage,
|
|
12
|
+
queryKey,
|
|
13
|
+
windowSize = DEFAULT_ROW_WINDOW_SIZE,
|
|
14
|
+
maxLimit
|
|
15
|
+
}) {
|
|
16
|
+
const query = useInfiniteQuery({
|
|
17
|
+
enabled,
|
|
18
|
+
initialPageParam: 0,
|
|
19
|
+
queryKey: [...queryKey, "buffer", windowSize],
|
|
20
|
+
queryFn: ({ pageParam }) => fetchOffsetPage(
|
|
21
|
+
fetchPage,
|
|
22
|
+
{ limit: windowSize, offset: Number(pageParam) },
|
|
23
|
+
{ maxLimit }
|
|
24
|
+
),
|
|
25
|
+
getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextOffset : void 0
|
|
26
|
+
});
|
|
27
|
+
const pages = query.data?.pages;
|
|
28
|
+
return {
|
|
29
|
+
...query,
|
|
30
|
+
rows: pages?.flatMap((page) => page.rows) ?? [],
|
|
31
|
+
hasNextPage: Boolean(query.hasNextPage),
|
|
32
|
+
// The newest window carries the freshest count. Reading the first page
|
|
33
|
+
// instead would keep reporting a total from before the latest mutation.
|
|
34
|
+
total: pages?.[pages.length - 1]?.total ?? null
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function useResponsiveOffsetList({
|
|
38
|
+
enabled = true,
|
|
39
|
+
fetchPage,
|
|
40
|
+
pageSize = 25,
|
|
41
|
+
queryKey,
|
|
42
|
+
strategy = "paged",
|
|
43
|
+
maxLimit = DEFAULT_MAX_PAGE_SIZE,
|
|
44
|
+
cardBreakpoint
|
|
45
|
+
}) {
|
|
46
|
+
const cardViewport = useCardViewport(cardBreakpoint);
|
|
47
|
+
const queryIdentity = JSON.stringify(queryKey);
|
|
48
|
+
const [paginationState, setPagination] = useState({
|
|
49
|
+
pageIndex: 0,
|
|
50
|
+
pageSize,
|
|
51
|
+
queryIdentity
|
|
52
|
+
});
|
|
53
|
+
const pagination = paginationState.queryIdentity === queryIdentity ? paginationState : { ...paginationState, pageIndex: 0};
|
|
54
|
+
const wantsAll = strategy === "all";
|
|
55
|
+
const wantsInfinite = strategy === "infinite" || strategy === "paged" && cardViewport;
|
|
56
|
+
const buffer = useOffsetInfiniteQuery({
|
|
57
|
+
enabled,
|
|
58
|
+
fetchPage,
|
|
59
|
+
queryKey,
|
|
60
|
+
maxLimit,
|
|
61
|
+
windowSize: wantsAll ? maxLimit : DEFAULT_ROW_WINDOW_SIZE
|
|
62
|
+
});
|
|
63
|
+
const rows = buffer.rows;
|
|
64
|
+
const allDowngraded = wantsAll && buffer.hasNextPage;
|
|
65
|
+
const warnedRef = useRef(false);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!allDowngraded || warnedRef.current) return;
|
|
68
|
+
warnedRef.current = true;
|
|
69
|
+
if (process.env.NODE_ENV !== "production") {
|
|
70
|
+
console.warn(
|
|
71
|
+
`[najm-kit] List ${queryIdentity} requested strategy "all" but filled the ${maxLimit}-row ceiling, so its bound is not real. Falling back to infinite continuation; give this list a paged or infinite strategy.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}, [allDowngraded, queryIdentity, maxLimit]);
|
|
75
|
+
const mode = wantsAll && !allDowngraded ? "all" : wantsInfinite || allDowngraded ? "infinite" : "paged";
|
|
76
|
+
const start = pagination.pageIndex * pagination.pageSize;
|
|
77
|
+
const data = mode === "paged" ? rows.slice(start, start + pagination.pageSize) : rows;
|
|
78
|
+
const { fetchNextPage, hasNextPage, isFetchingNextPage } = buffer;
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (mode !== "paged") return;
|
|
81
|
+
if (!hasNextPage || isFetchingNextPage) return;
|
|
82
|
+
if (rows.length >= start + pagination.pageSize * 2) return;
|
|
83
|
+
void fetchNextPage();
|
|
84
|
+
}, [
|
|
85
|
+
mode,
|
|
86
|
+
hasNextPage,
|
|
87
|
+
isFetchingNextPage,
|
|
88
|
+
rows.length,
|
|
89
|
+
start,
|
|
90
|
+
pagination.pageSize,
|
|
91
|
+
fetchNextPage
|
|
92
|
+
]);
|
|
93
|
+
const onPaginationChange = useCallback(
|
|
94
|
+
(updater) => {
|
|
95
|
+
setPagination((current) => ({
|
|
96
|
+
...typeof updater === "function" ? updater(
|
|
97
|
+
current.queryIdentity === queryIdentity ? { pageIndex: current.pageIndex, pageSize: current.pageSize } : { pageIndex: 0, pageSize: current.pageSize }
|
|
98
|
+
) : updater,
|
|
99
|
+
queryIdentity
|
|
100
|
+
}));
|
|
101
|
+
},
|
|
102
|
+
[queryIdentity]
|
|
103
|
+
);
|
|
104
|
+
const bufferedPages = Math.max(
|
|
105
|
+
1,
|
|
106
|
+
Math.ceil(rows.length / pagination.pageSize)
|
|
107
|
+
);
|
|
108
|
+
const pageCount = buffer.total !== null ? Math.max(1, Math.ceil(buffer.total / pagination.pageSize)) : buffer.hasNextPage ? bufferedPages + 1 : bufferedPages;
|
|
109
|
+
return {
|
|
110
|
+
cardViewport,
|
|
111
|
+
mode,
|
|
112
|
+
data,
|
|
113
|
+
/** Rows matching the current filters on the server, or `null` if unknown. */
|
|
114
|
+
total: buffer.total,
|
|
115
|
+
error: buffer.error,
|
|
116
|
+
hasNextPage: mode === "infinite" && buffer.hasNextPage,
|
|
117
|
+
// A background window extension is not a load. Paged readers keep the rows
|
|
118
|
+
// they are looking at; only having nothing at all is a loading state.
|
|
119
|
+
loading: buffer.isPending,
|
|
120
|
+
loadingMore: mode === "infinite" && buffer.isFetchingNextPage,
|
|
121
|
+
loadMoreError: buffer.isFetchNextPageError ? buffer.error : null,
|
|
122
|
+
onLoadMore: () => buffer.fetchNextPage(),
|
|
123
|
+
onPaginationChange,
|
|
124
|
+
pageCount,
|
|
125
|
+
pagination: {
|
|
126
|
+
pageIndex: pagination.pageIndex,
|
|
127
|
+
pageSize: pagination.pageSize
|
|
128
|
+
},
|
|
129
|
+
refetch: buffer.refetch
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export { DEFAULT_ROW_WINDOW_SIZE, useOffsetInfiniteQuery, useResponsiveOffsetList };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "najm-kit",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Reusable React UI component package for Najm framework",
|
|
@@ -33,6 +33,21 @@
|
|
|
33
33
|
"default": "./dist/adapters/app.mjs"
|
|
34
34
|
},
|
|
35
35
|
"./package.json": "./package.json",
|
|
36
|
+
"./query": {
|
|
37
|
+
"types": "./dist/query.d.ts",
|
|
38
|
+
"import": "./dist/query.mjs",
|
|
39
|
+
"default": "./dist/query.mjs"
|
|
40
|
+
},
|
|
41
|
+
"./format": {
|
|
42
|
+
"types": "./dist/format.d.ts",
|
|
43
|
+
"import": "./dist/format.mjs",
|
|
44
|
+
"default": "./dist/format.mjs"
|
|
45
|
+
},
|
|
46
|
+
"./pagination": {
|
|
47
|
+
"types": "./dist/pagination.d.ts",
|
|
48
|
+
"import": "./dist/pagination.mjs",
|
|
49
|
+
"default": "./dist/pagination.mjs"
|
|
50
|
+
},
|
|
36
51
|
"./json": {
|
|
37
52
|
"types": "./dist/json.d.ts",
|
|
38
53
|
"import": "./dist/json.mjs",
|
|
@@ -60,6 +75,7 @@
|
|
|
60
75
|
"zod": ">=4",
|
|
61
76
|
"next": ">=14",
|
|
62
77
|
"najm-i18n": ">=2",
|
|
78
|
+
"@tanstack/react-query": "^5",
|
|
63
79
|
"@uiw/react-codemirror": "^4.25.0",
|
|
64
80
|
"@codemirror/state": "^6.6.0",
|
|
65
81
|
"@codemirror/view": "^6.42.0",
|
|
@@ -76,6 +92,9 @@
|
|
|
76
92
|
"najm-i18n": {
|
|
77
93
|
"optional": true
|
|
78
94
|
},
|
|
95
|
+
"@tanstack/react-query": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
79
98
|
"@uiw/react-codemirror": {
|
|
80
99
|
"optional": true
|
|
81
100
|
},
|
|
@@ -147,6 +166,7 @@
|
|
|
147
166
|
"@hookform/resolvers": "^5",
|
|
148
167
|
"@lezer/highlight": "^1.2.0",
|
|
149
168
|
"@tailwindcss/postcss": "^4",
|
|
169
|
+
"@tanstack/react-query": "^5",
|
|
150
170
|
"@testing-library/react": "^16",
|
|
151
171
|
"@testing-library/user-event": "^14.6.1",
|
|
152
172
|
"@types/culori": "^4.0.1",
|