@sanity/sdk-react 0.0.0-alpha.29 → 0.0.0-alpha.30
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 +5 -57
- package/dist/index.d.ts +998 -437
- package/dist/index.js +325 -259
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
- package/src/_exports/sdk-react.ts +4 -1
- package/src/components/SDKProvider.tsx +6 -1
- package/src/components/SanityApp.test.tsx +29 -47
- package/src/components/SanityApp.tsx +12 -11
- package/src/components/auth/AuthBoundary.test.tsx +177 -7
- package/src/components/auth/AuthBoundary.tsx +31 -1
- package/src/components/auth/ConfigurationError.ts +22 -0
- package/src/components/auth/LoginError.tsx +9 -3
- package/src/hooks/auth/useVerifyOrgProjects.test.tsx +136 -0
- package/src/hooks/auth/useVerifyOrgProjects.tsx +48 -0
- package/src/hooks/client/useClient.ts +3 -3
- package/src/hooks/comlink/useManageFavorite.test.ts +276 -27
- package/src/hooks/comlink/useManageFavorite.ts +102 -51
- package/src/hooks/comlink/useWindowConnection.ts +3 -2
- package/src/hooks/datasets/useDatasets.test.ts +80 -0
- package/src/hooks/datasets/useDatasets.ts +2 -1
- package/src/hooks/document/useApplyDocumentActions.ts +105 -31
- package/src/hooks/document/useDocument.test.ts +41 -4
- package/src/hooks/document/useDocument.ts +198 -114
- package/src/hooks/document/useDocumentEvent.test.ts +5 -5
- package/src/hooks/document/useDocumentEvent.ts +67 -23
- package/src/hooks/document/useDocumentPermissions.ts +47 -8
- package/src/hooks/document/useDocumentSyncStatus.test.ts +12 -5
- package/src/hooks/document/useDocumentSyncStatus.ts +41 -14
- package/src/hooks/document/useEditDocument.test.ts +24 -6
- package/src/hooks/document/useEditDocument.ts +238 -133
- package/src/hooks/documents/useDocuments.test.tsx +1 -1
- package/src/hooks/documents/useDocuments.ts +153 -44
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +1 -1
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +120 -47
- package/src/hooks/projection/useProjection.ts +134 -46
- package/src/hooks/projects/useProject.test.ts +80 -0
- package/src/hooks/projects/useProjects.test.ts +77 -0
- package/src/hooks/query/useQuery.test.tsx +4 -4
- package/src/hooks/query/useQuery.ts +115 -43
- package/src/hooks/releases/useActiveReleases.test.tsx +84 -0
- package/src/hooks/releases/useActiveReleases.ts +39 -0
- package/src/hooks/releases/usePerspective.test.tsx +120 -0
- package/src/hooks/releases/usePerspective.ts +49 -0
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createGroqSearchFilter,
|
|
3
|
+
type DatasetHandle,
|
|
4
|
+
type DocumentHandle,
|
|
5
|
+
type QueryOptions,
|
|
6
|
+
} from '@sanity/sdk'
|
|
2
7
|
import {type SortOrderingItem} from '@sanity/types'
|
|
3
8
|
import {pick} from 'lodash-es'
|
|
4
9
|
import {useCallback, useEffect, useMemo, useState} from 'react'
|
|
@@ -7,16 +12,6 @@ import {useSanityInstance} from '../context/useSanityInstance'
|
|
|
7
12
|
import {useQuery} from '../query/useQuery'
|
|
8
13
|
|
|
9
14
|
const DEFAULT_BATCH_SIZE = 25
|
|
10
|
-
const DEFAULT_PERSPECTIVE = 'drafts'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Result structure returned from the infinite list query
|
|
14
|
-
* @internal
|
|
15
|
-
*/
|
|
16
|
-
interface UseDocumentsQueryResult {
|
|
17
|
-
count: number
|
|
18
|
-
data: DocumentHandle[]
|
|
19
|
-
}
|
|
20
15
|
|
|
21
16
|
/**
|
|
22
17
|
* Configuration options for the useDocuments hook
|
|
@@ -24,7 +19,16 @@ interface UseDocumentsQueryResult {
|
|
|
24
19
|
* @beta
|
|
25
20
|
* @category Types
|
|
26
21
|
*/
|
|
27
|
-
export interface DocumentsOptions
|
|
22
|
+
export interface DocumentsOptions<
|
|
23
|
+
TDocumentType extends string = string,
|
|
24
|
+
TDataset extends string = string,
|
|
25
|
+
TProjectId extends string = string,
|
|
26
|
+
> extends DatasetHandle<TDataset, TProjectId>,
|
|
27
|
+
Pick<QueryOptions, 'perspective' | 'params'> {
|
|
28
|
+
/**
|
|
29
|
+
* Filter documents by their `_type`. Can be a single type or an array of types.
|
|
30
|
+
*/
|
|
31
|
+
documentType?: TDocumentType | TDocumentType[]
|
|
28
32
|
/**
|
|
29
33
|
* GROQ filter expression to apply to the query
|
|
30
34
|
*/
|
|
@@ -49,11 +53,15 @@ export interface DocumentsOptions extends QueryOptions {
|
|
|
49
53
|
* @beta
|
|
50
54
|
* @category Types
|
|
51
55
|
*/
|
|
52
|
-
export interface DocumentsResponse
|
|
56
|
+
export interface DocumentsResponse<
|
|
57
|
+
TDocumentType extends string = string,
|
|
58
|
+
TDataset extends string = string,
|
|
59
|
+
TProjectId extends string = string,
|
|
60
|
+
> {
|
|
53
61
|
/**
|
|
54
62
|
* Array of document handles for the current batch
|
|
55
63
|
*/
|
|
56
|
-
data: DocumentHandle[]
|
|
64
|
+
data: DocumentHandle<TDocumentType, TDataset, TProjectId>[]
|
|
57
65
|
/**
|
|
58
66
|
* Whether there are more items available to load
|
|
59
67
|
*/
|
|
@@ -89,45 +97,136 @@ export interface DocumentsResponse {
|
|
|
89
97
|
*
|
|
90
98
|
* @example Basic infinite list with loading more
|
|
91
99
|
* ```tsx
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
100
|
+
* import {
|
|
101
|
+
* useDocuments,
|
|
102
|
+
* createDatasetHandle,
|
|
103
|
+
* type DatasetHandle,
|
|
104
|
+
* type DocumentHandle,
|
|
105
|
+
* type SortOrderingItem
|
|
106
|
+
* } from '@sanity/sdk-react'
|
|
107
|
+
* import {Suspense} from 'react'
|
|
108
|
+
*
|
|
109
|
+
* // Define a component to display a single document (using useProjection for efficiency)
|
|
110
|
+
* function MyDocumentComponent({doc}: {doc: DocumentHandle}) {
|
|
111
|
+
* const {data} = useProjection<{title?: string}>({
|
|
112
|
+
* ...doc, // Pass the full handle
|
|
113
|
+
* projection: '{title}'
|
|
114
|
+
* })
|
|
115
|
+
*
|
|
116
|
+
* return <>{data?.title || 'Untitled'}</>
|
|
117
|
+
* }
|
|
118
|
+
*
|
|
119
|
+
* // Define props for the list component
|
|
120
|
+
* interface DocumentListProps {
|
|
121
|
+
* dataset: DatasetHandle
|
|
122
|
+
* documentType: string
|
|
123
|
+
* search?: string
|
|
124
|
+
* }
|
|
125
|
+
*
|
|
126
|
+
* function DocumentList({dataset, documentType, search}: DocumentListProps) {
|
|
127
|
+
* const { data, hasMore, isPending, loadMore, count } = useDocuments({
|
|
128
|
+
* ...dataset,
|
|
129
|
+
* documentType,
|
|
130
|
+
* search,
|
|
131
|
+
* batchSize: 10,
|
|
132
|
+
* orderings: [{field: '_createdAt', direction: 'desc'}],
|
|
133
|
+
* })
|
|
134
|
+
*
|
|
135
|
+
* return (
|
|
136
|
+
* <div>
|
|
137
|
+
* <p>Total documents: {count}</p>
|
|
138
|
+
* <ol>
|
|
139
|
+
* {data.map((docHandle) => (
|
|
140
|
+
* <li key={docHandle.documentId}>
|
|
141
|
+
* <Suspense fallback="Loading…">
|
|
142
|
+
* <MyDocumentComponent docHandle={docHandle} />
|
|
143
|
+
* </Suspense>
|
|
144
|
+
* </li>
|
|
145
|
+
* ))}
|
|
146
|
+
* </ol>
|
|
147
|
+
* {hasMore && (
|
|
148
|
+
* <button onClick={loadMore}>
|
|
149
|
+
* {isPending ? 'Loading...' : 'Load More'}
|
|
150
|
+
* </button>
|
|
151
|
+
* )}
|
|
152
|
+
* </div>
|
|
153
|
+
* )
|
|
154
|
+
* }
|
|
155
|
+
*
|
|
156
|
+
* // Usage:
|
|
157
|
+
* // const myDatasetHandle = createDatasetHandle({ projectId: 'p1', dataset: 'production' })
|
|
158
|
+
* // <DocumentList dataset={myDatasetHandle} documentType="post" search="Sanity" />
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* @example Using `filter` and `params` options for narrowing a collection
|
|
162
|
+
* ```tsx
|
|
163
|
+
* import {useState} from 'react'
|
|
164
|
+
* import {useDocuments} from '@sanity/sdk-react'
|
|
165
|
+
*
|
|
166
|
+
* export default function FilteredAuthors() {
|
|
167
|
+
* const [max, setMax] = useState(2)
|
|
168
|
+
* const {data} = useDocuments({
|
|
169
|
+
* documentType: 'author',
|
|
170
|
+
* filter: 'length(books) <= $max',
|
|
171
|
+
* params: {max},
|
|
172
|
+
* })
|
|
98
173
|
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
174
|
+
* return (
|
|
175
|
+
* <>
|
|
176
|
+
* <input
|
|
177
|
+
* id="maxBooks"
|
|
178
|
+
* type="number"
|
|
179
|
+
* value={max}
|
|
180
|
+
* onChange={e => setMax(e.currentTarget.value)}
|
|
181
|
+
* />
|
|
182
|
+
* {data.map(author => (
|
|
183
|
+
* <Suspense key={author.documentId}>
|
|
184
|
+
* <MyAuthorComponent documentHandle={author} />
|
|
185
|
+
* </Suspense>
|
|
107
186
|
* ))}
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* </button>}
|
|
112
|
-
* </div>
|
|
113
|
-
* )
|
|
187
|
+
* </>
|
|
188
|
+
* )
|
|
189
|
+
* }
|
|
114
190
|
* ```
|
|
115
191
|
*/
|
|
116
|
-
export function useDocuments
|
|
192
|
+
export function useDocuments<
|
|
193
|
+
TDocumentType extends string = string,
|
|
194
|
+
TDataset extends string = string,
|
|
195
|
+
TProjectId extends string = string,
|
|
196
|
+
>({
|
|
117
197
|
batchSize = DEFAULT_BATCH_SIZE,
|
|
118
198
|
params,
|
|
119
199
|
search,
|
|
120
200
|
filter,
|
|
121
201
|
orderings,
|
|
202
|
+
documentType,
|
|
122
203
|
...options
|
|
123
|
-
}: DocumentsOptions): DocumentsResponse
|
|
204
|
+
}: DocumentsOptions<TDocumentType, TDataset, TProjectId>): DocumentsResponse<
|
|
205
|
+
TDocumentType,
|
|
206
|
+
TDataset,
|
|
207
|
+
TProjectId
|
|
208
|
+
> {
|
|
124
209
|
const instance = useSanityInstance(options)
|
|
125
|
-
const perspective = options.perspective ?? DEFAULT_PERSPECTIVE
|
|
126
210
|
const [limit, setLimit] = useState(batchSize)
|
|
211
|
+
const documentTypes = useMemo(
|
|
212
|
+
() =>
|
|
213
|
+
(Array.isArray(documentType) ? documentType : [documentType]).filter(
|
|
214
|
+
(i): i is TDocumentType => typeof i === 'string',
|
|
215
|
+
),
|
|
216
|
+
[documentType],
|
|
217
|
+
)
|
|
127
218
|
|
|
128
219
|
// Reset the limit to the current batchSize whenever any query parameters
|
|
129
220
|
// (filter, search, params, orderings) or batchSize changes
|
|
130
|
-
const key = JSON.stringify({
|
|
221
|
+
const key = JSON.stringify({
|
|
222
|
+
filter,
|
|
223
|
+
search,
|
|
224
|
+
params,
|
|
225
|
+
orderings,
|
|
226
|
+
batchSize,
|
|
227
|
+
types: documentTypes,
|
|
228
|
+
...options,
|
|
229
|
+
})
|
|
131
230
|
useEffect(() => {
|
|
132
231
|
setLimit(batchSize)
|
|
133
232
|
}, [key, batchSize])
|
|
@@ -144,13 +243,18 @@ export function useDocuments({
|
|
|
144
243
|
}
|
|
145
244
|
}
|
|
146
245
|
|
|
246
|
+
// Add type filter if specified
|
|
247
|
+
if (documentTypes?.length) {
|
|
248
|
+
conditions.push(`(_type in $__types)`)
|
|
249
|
+
}
|
|
250
|
+
|
|
147
251
|
// Add additional filter if specified
|
|
148
252
|
if (filter) {
|
|
149
253
|
conditions.push(`(${filter})`)
|
|
150
254
|
}
|
|
151
255
|
|
|
152
256
|
return conditions.length ? `[${conditions.join(' && ')}]` : ''
|
|
153
|
-
}, [filter, search])
|
|
257
|
+
}, [filter, search, documentTypes])
|
|
154
258
|
|
|
155
259
|
const orderClause = orderings
|
|
156
260
|
? `| order(${orderings
|
|
@@ -163,21 +267,26 @@ export function useDocuments({
|
|
|
163
267
|
.join(',')})`
|
|
164
268
|
: ''
|
|
165
269
|
|
|
166
|
-
const dataQuery = `*${filterClause}${orderClause}[0...${limit}]{"documentId":_id,"documentType":_type,...$
|
|
270
|
+
const dataQuery = `*${filterClause}${orderClause}[0...${limit}]{"documentId":_id,"documentType":_type,...$__handle}`
|
|
167
271
|
const countQuery = `count(*${filterClause})`
|
|
168
272
|
|
|
169
273
|
const {
|
|
170
274
|
data: {count, data},
|
|
171
275
|
isPending,
|
|
172
|
-
} = useQuery<
|
|
276
|
+
} = useQuery<{count: number; data: DocumentHandle<TDocumentType, TDataset, TProjectId>[]}>({
|
|
173
277
|
...options,
|
|
278
|
+
query: `{"count":${countQuery},"data":${dataQuery}}`,
|
|
174
279
|
params: {
|
|
175
280
|
...params,
|
|
176
|
-
|
|
281
|
+
__handle: {
|
|
282
|
+
...pick(instance.config, 'projectId', 'dataset', 'perspective'),
|
|
283
|
+
...pick(options, 'projectId', 'dataset', 'perspective'),
|
|
284
|
+
},
|
|
285
|
+
__types: documentTypes,
|
|
177
286
|
},
|
|
178
|
-
perspective,
|
|
179
287
|
})
|
|
180
288
|
|
|
289
|
+
// Now use the correctly typed variables
|
|
181
290
|
const hasMore = data.length < count
|
|
182
291
|
|
|
183
292
|
const loadMore = useCallback(() => {
|
|
@@ -186,6 +295,6 @@ export function useDocuments({
|
|
|
186
295
|
|
|
187
296
|
return useMemo(
|
|
188
297
|
() => ({data, hasMore, count, isPending, loadMore}),
|
|
189
|
-
[data, hasMore,
|
|
298
|
+
[count, data, hasMore, isPending, loadMore],
|
|
190
299
|
)
|
|
191
300
|
}
|
|
@@ -72,7 +72,7 @@ describe('usePaginatedDocuments', () => {
|
|
|
72
72
|
},
|
|
73
73
|
]
|
|
74
74
|
|
|
75
|
-
vi.mocked(useQuery).mockImplementation((query, options) => {
|
|
75
|
+
vi.mocked(useQuery).mockImplementation(({query, ...options}) => {
|
|
76
76
|
const result = evaluateSync(parse(query), {dataset, params: options?.params}).get()
|
|
77
77
|
return {
|
|
78
78
|
data: result,
|
|
@@ -6,15 +6,18 @@ import {useCallback, useEffect, useMemo, useState} from 'react'
|
|
|
6
6
|
import {useSanityInstance} from '../context/useSanityInstance'
|
|
7
7
|
import {useQuery} from '../query/useQuery'
|
|
8
8
|
|
|
9
|
-
const DEFAULT_PERSPECTIVE = 'drafts'
|
|
10
|
-
|
|
11
9
|
/**
|
|
12
10
|
* Configuration options for the usePaginatedDocuments hook
|
|
13
11
|
*
|
|
14
12
|
* @beta
|
|
15
13
|
* @category Types
|
|
16
14
|
*/
|
|
17
|
-
export interface PaginatedDocumentsOptions
|
|
15
|
+
export interface PaginatedDocumentsOptions<
|
|
16
|
+
TDocumentType extends string = string,
|
|
17
|
+
TDataset extends string = string,
|
|
18
|
+
TProjectId extends string = string,
|
|
19
|
+
> extends Omit<QueryOptions<TDocumentType, TDataset, TProjectId>, 'query'> {
|
|
20
|
+
documentType?: TDocumentType | TDocumentType[]
|
|
18
21
|
/**
|
|
19
22
|
* GROQ filter expression to apply to the query
|
|
20
23
|
*/
|
|
@@ -39,11 +42,15 @@ export interface PaginatedDocumentsOptions extends QueryOptions {
|
|
|
39
42
|
* @beta
|
|
40
43
|
* @category Types
|
|
41
44
|
*/
|
|
42
|
-
export interface PaginatedDocumentsResponse
|
|
45
|
+
export interface PaginatedDocumentsResponse<
|
|
46
|
+
TDocumentType extends string = string,
|
|
47
|
+
TDataset extends string = string,
|
|
48
|
+
TProjectId extends string = string,
|
|
49
|
+
> {
|
|
43
50
|
/**
|
|
44
51
|
* Array of document handles for the current page
|
|
45
52
|
*/
|
|
46
|
-
data: DocumentHandle[]
|
|
53
|
+
data: DocumentHandle<TDocumentType, TDataset, TProjectId>[]
|
|
47
54
|
/**
|
|
48
55
|
* Whether a query is currently in progress
|
|
49
56
|
*/
|
|
@@ -126,54 +133,110 @@ export interface PaginatedDocumentsResponse {
|
|
|
126
133
|
* @beta
|
|
127
134
|
* @category Documents
|
|
128
135
|
* @param options - Configuration options for the paginated list
|
|
129
|
-
* @returns An object containing the
|
|
136
|
+
* @returns An object containing the list of document handles, pagination details, and functions to navigate between pages
|
|
130
137
|
*
|
|
131
138
|
* @remarks
|
|
132
139
|
* - The returned document handles include projectId and dataset information from the current Sanity instance
|
|
133
140
|
* - This makes them ready to use with document operations and other document hooks
|
|
134
141
|
* - The hook automatically uses the correct Sanity instance based on the projectId and dataset in the options
|
|
135
142
|
*
|
|
136
|
-
* @example
|
|
143
|
+
* @example Paginated list of documents with navigation
|
|
137
144
|
* ```tsx
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* }
|
|
148
|
-
* filter: '_type == "post"',
|
|
149
|
-
* search: searchTerm,
|
|
150
|
-
* pageSize: 10,
|
|
151
|
-
* orderings: [{field: '_createdAt', direction: 'desc'}]
|
|
152
|
-
* })
|
|
145
|
+
* import {
|
|
146
|
+
* usePaginatedDocuments,
|
|
147
|
+
* createDatasetHandle,
|
|
148
|
+
* type DatasetHandle,
|
|
149
|
+
* type DocumentHandle,
|
|
150
|
+
* type SortOrderingItem,
|
|
151
|
+
* useProjection
|
|
152
|
+
* } from '@sanity/sdk-react'
|
|
153
|
+
* import {Suspense} from 'react'
|
|
154
|
+
* import {ErrorBoundary} from 'react-error-boundary'
|
|
153
155
|
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
156
|
+
* // Define a component to display a single document row
|
|
157
|
+
* function MyTableRowComponent({doc}: {doc: DocumentHandle}) {
|
|
158
|
+
* const {data} = useProjection<{title?: string}>({
|
|
159
|
+
* ...doc,
|
|
160
|
+
* projection: '{title}',
|
|
161
|
+
* })
|
|
162
|
+
*
|
|
163
|
+
* return (
|
|
164
|
+
* <tr>
|
|
165
|
+
* <td>{data?.title ?? 'Untitled'}</td>
|
|
166
|
+
* </tr>
|
|
167
|
+
* )
|
|
168
|
+
* }
|
|
169
|
+
*
|
|
170
|
+
* // Define props for the list component
|
|
171
|
+
* interface PaginatedDocumentListProps {
|
|
172
|
+
* documentType: string
|
|
173
|
+
* dataset?: DatasetHandle
|
|
174
|
+
* }
|
|
175
|
+
*
|
|
176
|
+
* function PaginatedDocumentList({documentType, dataset}: PaginatedDocumentListProps) {
|
|
177
|
+
* const {
|
|
178
|
+
* data,
|
|
179
|
+
* isPending,
|
|
180
|
+
* currentPage,
|
|
181
|
+
* totalPages,
|
|
182
|
+
* nextPage,
|
|
183
|
+
* previousPage,
|
|
184
|
+
* hasNextPage,
|
|
185
|
+
* hasPreviousPage
|
|
186
|
+
* } = usePaginatedDocuments({
|
|
187
|
+
* ...dataset,
|
|
188
|
+
* documentType,
|
|
189
|
+
* pageSize: 10,
|
|
190
|
+
* orderings: [{field: '_createdAt', direction: 'desc'}],
|
|
191
|
+
* })
|
|
192
|
+
*
|
|
193
|
+
* return (
|
|
194
|
+
* <div>
|
|
195
|
+
* <table>
|
|
196
|
+
* <thead>
|
|
197
|
+
* <tr><th>Title</th></tr>
|
|
198
|
+
* </thead>
|
|
199
|
+
* <tbody>
|
|
200
|
+
* {data.map(doc => (
|
|
201
|
+
* <ErrorBoundary key={doc.documentId} fallback={<tr><td>Error loading document</td></tr>}>
|
|
202
|
+
* <Suspense fallback={<tr><td>Loading...</td></tr>}>
|
|
203
|
+
* <MyTableRowComponent doc={doc} />
|
|
204
|
+
* </Suspense>
|
|
205
|
+
* </ErrorBoundary>
|
|
206
|
+
* ))}
|
|
207
|
+
* </tbody>
|
|
208
|
+
* </table>
|
|
209
|
+
* <div style={{opacity: isPending ? 0.5 : 1}}>
|
|
210
|
+
* <button onClick={previousPage} disabled={!hasPreviousPage || isPending}>Previous</button>
|
|
211
|
+
* <span>Page {currentPage} / {totalPages}</span>
|
|
212
|
+
* <button onClick={nextPage} disabled={!hasNextPage || isPending}>Next</button>
|
|
213
|
+
* </div>
|
|
214
|
+
* </div>
|
|
215
|
+
* )
|
|
216
|
+
* }
|
|
167
217
|
*
|
|
218
|
+
* // Usage:
|
|
219
|
+
* // const myDatasetHandle = createDatasetHandle({ projectId: 'p1', dataset: 'production' })
|
|
220
|
+
* // <PaginatedDocumentList dataset={myDatasetHandle} documentType="post" />
|
|
221
|
+
* ```
|
|
168
222
|
*/
|
|
169
|
-
export function usePaginatedDocuments
|
|
223
|
+
export function usePaginatedDocuments<
|
|
224
|
+
TDocumentType extends string = string,
|
|
225
|
+
TDataset extends string = string,
|
|
226
|
+
TProjectId extends string = string,
|
|
227
|
+
>({
|
|
228
|
+
documentType,
|
|
170
229
|
filter = '',
|
|
171
230
|
pageSize = 25,
|
|
172
231
|
params = {},
|
|
173
232
|
orderings,
|
|
174
233
|
search,
|
|
175
234
|
...options
|
|
176
|
-
}: PaginatedDocumentsOptions): PaginatedDocumentsResponse
|
|
235
|
+
}: PaginatedDocumentsOptions<TDocumentType, TDataset, TProjectId>): PaginatedDocumentsResponse<
|
|
236
|
+
TDocumentType,
|
|
237
|
+
TDataset,
|
|
238
|
+
TProjectId
|
|
239
|
+
> {
|
|
177
240
|
const instance = useSanityInstance(options)
|
|
178
241
|
const [pageIndex, setPageIndex] = useState(0)
|
|
179
242
|
const key = JSON.stringify({filter, search, params, orderings, pageSize})
|
|
@@ -185,7 +248,9 @@ export function usePaginatedDocuments({
|
|
|
185
248
|
|
|
186
249
|
const startIndex = pageIndex * pageSize
|
|
187
250
|
const endIndex = (pageIndex + 1) * pageSize
|
|
188
|
-
const
|
|
251
|
+
const documentTypes = (Array.isArray(documentType) ? documentType : [documentType]).filter(
|
|
252
|
+
(i) => typeof i === 'string',
|
|
253
|
+
)
|
|
189
254
|
|
|
190
255
|
const filterClause = useMemo(() => {
|
|
191
256
|
const conditions: string[] = []
|
|
@@ -199,13 +264,17 @@ export function usePaginatedDocuments({
|
|
|
199
264
|
}
|
|
200
265
|
}
|
|
201
266
|
|
|
267
|
+
if (documentTypes?.length) {
|
|
268
|
+
conditions.push(`(_type in $__types)`)
|
|
269
|
+
}
|
|
270
|
+
|
|
202
271
|
// Add additional filter if specified
|
|
203
272
|
if (filter) {
|
|
204
273
|
conditions.push(`(${filter})`)
|
|
205
274
|
}
|
|
206
275
|
|
|
207
276
|
return conditions.length ? `[${conditions.join(' && ')}]` : ''
|
|
208
|
-
}, [filter, search])
|
|
277
|
+
}, [filter, search, documentTypes?.length])
|
|
209
278
|
|
|
210
279
|
const orderClause = orderings
|
|
211
280
|
? `| order(${orderings
|
|
@@ -218,20 +287,24 @@ export function usePaginatedDocuments({
|
|
|
218
287
|
.join(',')})`
|
|
219
288
|
: ''
|
|
220
289
|
|
|
221
|
-
const dataQuery = `*${filterClause}${orderClause}[${startIndex}...${endIndex}]{"documentId":_id,"documentType":_type,...$
|
|
290
|
+
const dataQuery = `*${filterClause}${orderClause}[${startIndex}...${endIndex}]{"documentId":_id,"documentType":_type,...$__handle}`
|
|
222
291
|
const countQuery = `count(*${filterClause})`
|
|
223
292
|
|
|
224
293
|
const {
|
|
225
294
|
data: {data, count},
|
|
226
295
|
isPending,
|
|
227
|
-
} = useQuery<{data: DocumentHandle[]; count: number}>(
|
|
228
|
-
|
|
229
|
-
{
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
296
|
+
} = useQuery<{data: DocumentHandle<TDocumentType, TDataset, TProjectId>[]; count: number}>({
|
|
297
|
+
...options,
|
|
298
|
+
query: `{"data":${dataQuery},"count":${countQuery}}`,
|
|
299
|
+
params: {
|
|
300
|
+
...params,
|
|
301
|
+
__types: documentTypes,
|
|
302
|
+
__handle: {
|
|
303
|
+
...pick(instance.config, 'projectId', 'dataset', 'perspective'),
|
|
304
|
+
...pick(options, 'projectId', 'dataset', 'perspective'),
|
|
305
|
+
},
|
|
233
306
|
},
|
|
234
|
-
)
|
|
307
|
+
})
|
|
235
308
|
|
|
236
309
|
const totalPages = Math.ceil(count / pageSize)
|
|
237
310
|
const currentPage = pageIndex + 1
|