@sanity/sdk-react 0.0.0-alpha.16 → 0.0.0-alpha.18
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/_chunks-es/SanityInstanceContext.js +6 -0
- package/dist/_chunks-es/SanityInstanceContext.js.map +1 -0
- package/dist/_chunks-es/useLogOut.js +28 -15
- package/dist/_chunks-es/useLogOut.js.map +1 -1
- package/dist/components.js +170 -94
- package/dist/components.js.map +1 -1
- package/dist/context.js +11 -1
- package/dist/context.js.map +1 -1
- package/dist/hooks.d.ts +161 -58
- package/dist/hooks.js +537 -290
- package/dist/hooks.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
- package/src/_exports/hooks.ts +3 -1
- package/src/components/SanityApp.tsx +3 -16
- package/src/context/SanityInstanceContext.ts +4 -0
- package/src/context/SanityProvider.tsx +3 -3
- package/src/hooks/comlink/useManageFavorite.test.ts +106 -0
- package/src/hooks/comlink/useManageFavorite.ts +98 -0
- package/src/hooks/comlink/useRecordDocumentHistoryEvent.test.ts +77 -0
- package/src/hooks/comlink/useRecordDocumentHistoryEvent.ts +75 -0
- package/src/hooks/context/useSanityInstance.ts +1 -1
- package/src/hooks/document/useApplyActions.test.ts +5 -4
- package/src/hooks/document/useApplyActions.ts +10 -4
- package/src/hooks/document/useEditDocument.ts +29 -32
- package/src/hooks/helpers/createCallbackHook.tsx +3 -2
- package/src/hooks/helpers/createStateSourceHook.tsx +2 -6
- package/src/hooks/infiniteList/useInfiniteList.test.tsx +10 -10
- package/src/hooks/infiniteList/useInfiniteList.ts +39 -28
- package/src/hooks/paginatedList/usePaginatedList.ts +25 -13
- package/src/hooks/projection/useProjection.ts +3 -3
- package/src/hooks/query/useQuery.ts +25 -34
- package/dist/_chunks-es/context.js +0 -8
- package/dist/_chunks-es/context.js.map +0 -1
|
@@ -4,7 +4,7 @@ import {useCallback, useEffect, useMemo, useState} from 'react'
|
|
|
4
4
|
|
|
5
5
|
import {useQuery} from '../query/useQuery'
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const DEFAULT_BATCH_SIZE = 25
|
|
8
8
|
const DEFAULT_PERSPECTIVE = 'drafts'
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -20,6 +20,7 @@ interface InfiniteListQueryResult {
|
|
|
20
20
|
* Configuration options for the useInfiniteList hook
|
|
21
21
|
*
|
|
22
22
|
* @beta
|
|
23
|
+
* @category Types
|
|
23
24
|
*/
|
|
24
25
|
export interface InfiniteListOptions extends QueryOptions {
|
|
25
26
|
/**
|
|
@@ -27,9 +28,9 @@ export interface InfiniteListOptions extends QueryOptions {
|
|
|
27
28
|
*/
|
|
28
29
|
filter?: string
|
|
29
30
|
/**
|
|
30
|
-
* Number of items to load per
|
|
31
|
+
* Number of items to load per batch (defaults to 25)
|
|
31
32
|
*/
|
|
32
|
-
|
|
33
|
+
batchSize?: number
|
|
33
34
|
/**
|
|
34
35
|
* Sorting configuration for the results
|
|
35
36
|
*/
|
|
@@ -44,10 +45,11 @@ export interface InfiniteListOptions extends QueryOptions {
|
|
|
44
45
|
* Return value from the useInfiniteList hook
|
|
45
46
|
*
|
|
46
47
|
* @beta
|
|
48
|
+
* @category Types
|
|
47
49
|
*/
|
|
48
50
|
export interface InfiniteList {
|
|
49
51
|
/**
|
|
50
|
-
* Array of document handles for the current
|
|
52
|
+
* Array of document handles for the current batch
|
|
51
53
|
*/
|
|
52
54
|
data: DocumentHandle[]
|
|
53
55
|
/**
|
|
@@ -63,38 +65,47 @@ export interface InfiniteList {
|
|
|
63
65
|
*/
|
|
64
66
|
isPending: boolean
|
|
65
67
|
/**
|
|
66
|
-
* Function to load the next
|
|
68
|
+
* Function to load the next batch of results
|
|
67
69
|
*/
|
|
68
70
|
loadMore: () => void
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
72
|
-
*
|
|
74
|
+
* Retrieves batches of {@link DocumentHandle}s, narrowed by optional filters, text searches, and custom ordering,
|
|
75
|
+
* with infinite scrolling support. The number of document handles returned per batch is customizable,
|
|
76
|
+
* and additional batches can be loaded using the supplied `loadMore` function.
|
|
73
77
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* combining search terms, filters, and ordering specifications. It maintains the
|
|
80
|
-
* current page size internally and exposes a function to load additional items.
|
|
81
|
-
*
|
|
82
|
-
* Usage example:
|
|
78
|
+
* @beta
|
|
79
|
+
* @category Documents
|
|
80
|
+
* @param options - Configuration options for the infinite list
|
|
81
|
+
* @returns An object containing the list of document handles, the loading state, the total count of retrived document handles, and a function to load more
|
|
82
|
+
* @example
|
|
83
83
|
* ```tsx
|
|
84
84
|
* const {data, hasMore, isPending, loadMore} = useInfiniteList({
|
|
85
85
|
* filter: '_type == "post"',
|
|
86
86
|
* search: searchTerm,
|
|
87
|
-
*
|
|
87
|
+
* batchSize: 10,
|
|
88
88
|
* orderings: [{field: '_createdAt', direction: 'desc'}]
|
|
89
89
|
* })
|
|
90
|
+
*
|
|
91
|
+
* return (
|
|
92
|
+
* <div>
|
|
93
|
+
* Total documents: {count}
|
|
94
|
+
* <ol>
|
|
95
|
+
* {data.map((doc) => (
|
|
96
|
+
* <li key={doc._id}>
|
|
97
|
+
* <MyDocumentComponent doc={doc} />
|
|
98
|
+
* </li>
|
|
99
|
+
* ))}
|
|
100
|
+
* </ol>
|
|
101
|
+
* {hasMore && <button onClick={loadMore}>Load More</button>}
|
|
102
|
+
* </div>
|
|
103
|
+
* )
|
|
90
104
|
* ```
|
|
91
105
|
*
|
|
92
|
-
* @beta
|
|
93
|
-
* @param options - Configuration options for the infinite list
|
|
94
|
-
* @returns An object containing the current data, loading state, and functions to load more
|
|
95
106
|
*/
|
|
96
107
|
export function useInfiniteList({
|
|
97
|
-
|
|
108
|
+
batchSize = DEFAULT_BATCH_SIZE,
|
|
98
109
|
params,
|
|
99
110
|
search,
|
|
100
111
|
filter,
|
|
@@ -102,14 +113,14 @@ export function useInfiniteList({
|
|
|
102
113
|
...options
|
|
103
114
|
}: InfiniteListOptions): InfiniteList {
|
|
104
115
|
const perspective = options.perspective ?? DEFAULT_PERSPECTIVE
|
|
105
|
-
const [limit, setLimit] = useState(
|
|
116
|
+
const [limit, setLimit] = useState(batchSize)
|
|
106
117
|
|
|
107
|
-
// Reset the limit to the current
|
|
108
|
-
// (filter, search, params, orderings) or
|
|
109
|
-
const key = JSON.stringify({filter, search, params, orderings,
|
|
118
|
+
// Reset the limit to the current batchSize whenever any query parameters
|
|
119
|
+
// (filter, search, params, orderings) or batchSize changes
|
|
120
|
+
const key = JSON.stringify({filter, search, params, orderings, batchSize})
|
|
110
121
|
useEffect(() => {
|
|
111
|
-
setLimit(
|
|
112
|
-
}, [key,
|
|
122
|
+
setLimit(batchSize)
|
|
123
|
+
}, [key, batchSize])
|
|
113
124
|
|
|
114
125
|
const filterClause = useMemo(() => {
|
|
115
126
|
const conditions: string[] = []
|
|
@@ -153,8 +164,8 @@ export function useInfiniteList({
|
|
|
153
164
|
const hasMore = data.length < count
|
|
154
165
|
|
|
155
166
|
const loadMore = useCallback(() => {
|
|
156
|
-
setLimit((prev) => Math.min(prev +
|
|
157
|
-
}, [count,
|
|
167
|
+
setLimit((prev) => Math.min(prev + batchSize, count))
|
|
168
|
+
}, [count, batchSize])
|
|
158
169
|
|
|
159
170
|
return useMemo(
|
|
160
171
|
() => ({data, hasMore, count, isPending, loadMore}),
|
|
@@ -10,6 +10,7 @@ const DEFAULT_PERSPECTIVE = 'drafts'
|
|
|
10
10
|
* Configuration options for the usePaginatedList hook
|
|
11
11
|
*
|
|
12
12
|
* @beta
|
|
13
|
+
* @category Types
|
|
13
14
|
*/
|
|
14
15
|
export interface PaginatedListOptions extends QueryOptions {
|
|
15
16
|
/**
|
|
@@ -34,6 +35,7 @@ export interface PaginatedListOptions extends QueryOptions {
|
|
|
34
35
|
* Return value from the usePaginatedList hook
|
|
35
36
|
*
|
|
36
37
|
* @beta
|
|
38
|
+
* @category Types
|
|
37
39
|
*/
|
|
38
40
|
export interface PaginatedList {
|
|
39
41
|
/**
|
|
@@ -115,17 +117,15 @@ export interface PaginatedList {
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
/**
|
|
118
|
-
*
|
|
120
|
+
* Retrieves pages of {@link DocumentHandle}s, narrowed by optional filters, text searches, and custom ordering,
|
|
121
|
+
* with support for traditional paginated interfaces. The number of document handles returned per page is customizable,
|
|
122
|
+
* while page navigation is handled via the included navigation functions.
|
|
119
123
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* combining search terms, filters, and ordering specifications. It maintains the
|
|
126
|
-
* current page state internally and exposes functions to navigate between pages.
|
|
127
|
-
*
|
|
128
|
-
* Usage example:
|
|
124
|
+
* @beta
|
|
125
|
+
* @category Documents
|
|
126
|
+
* @param options - Configuration options for the paginated list
|
|
127
|
+
* @returns An object containing the current page of document handles, the loading and pagination state, and navigation functions
|
|
128
|
+
* @example
|
|
129
129
|
* ```tsx
|
|
130
130
|
* const {
|
|
131
131
|
* data,
|
|
@@ -142,11 +142,23 @@ export interface PaginatedList {
|
|
|
142
142
|
* pageSize: 10,
|
|
143
143
|
* orderings: [{field: '_createdAt', direction: 'desc'}]
|
|
144
144
|
* })
|
|
145
|
+
*
|
|
146
|
+
* return (
|
|
147
|
+
* <>
|
|
148
|
+
* <table>
|
|
149
|
+
* {data.map(doc => (
|
|
150
|
+
* <MyTableRowComponent key={doc._id} doc={doc} />
|
|
151
|
+
* ))}
|
|
152
|
+
* </table>
|
|
153
|
+
* <>
|
|
154
|
+
* {hasPreviousPage && <button onClick={previousPage}>Previous</button>}
|
|
155
|
+
* {currentPage} / {totalPages}
|
|
156
|
+
* {hasNextPage && <button onClick={nextPage}>Next</button>}
|
|
157
|
+
* </>
|
|
158
|
+
* </>
|
|
159
|
+
* )
|
|
145
160
|
* ```
|
|
146
161
|
*
|
|
147
|
-
* @beta
|
|
148
|
-
* @param options - Configuration options for the paginated list
|
|
149
|
-
* @returns An object containing the current data, pagination state, and navigation functions
|
|
150
162
|
*/
|
|
151
163
|
export function usePaginatedList({
|
|
152
164
|
filter = '',
|
|
@@ -53,15 +53,15 @@ interface UseProjectionResults<TResult extends object> {
|
|
|
53
53
|
* }
|
|
54
54
|
* ```
|
|
55
55
|
*
|
|
56
|
-
* @example Combining with
|
|
56
|
+
* @example Combining with useInfiniteList to render a collection with specific fields
|
|
57
57
|
* ```
|
|
58
58
|
* // DocumentList.jsx
|
|
59
|
-
* const {
|
|
59
|
+
* const { data } = useInfiniteList({ filter: '_type == "article"' })
|
|
60
60
|
* return (
|
|
61
61
|
* <div>
|
|
62
62
|
* <h1>Articles</h1>
|
|
63
63
|
* <ul>
|
|
64
|
-
* {
|
|
64
|
+
* {data.map(article => (
|
|
65
65
|
* <li key={article._id}>
|
|
66
66
|
* <Suspense fallback='Loading…'>
|
|
67
67
|
* <ProjectionComponent
|
|
@@ -5,43 +5,42 @@ import {
|
|
|
5
5
|
type QueryOptions,
|
|
6
6
|
resolveQuery,
|
|
7
7
|
} from '@sanity/sdk'
|
|
8
|
-
import {useEffect, useMemo,
|
|
8
|
+
import {useEffect, useMemo, useState, useSyncExternalStore, useTransition} from 'react'
|
|
9
9
|
|
|
10
10
|
import {useSanityInstance} from '../context/useSanityInstance'
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Executes GROQ queries against a Sanity dataset.
|
|
14
14
|
*
|
|
15
15
|
* This hook provides a convenient way to fetch and subscribe to real-time updates
|
|
16
|
-
* for your Sanity content.
|
|
17
|
-
*
|
|
16
|
+
* for your Sanity content. Changes made to the dataset’s content will trigger
|
|
17
|
+
* automatic updates.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* - Real-time updates: Automatically subscribes to live updates when content changes
|
|
22
|
-
* - Transition support: Uses React's useTransition to avoid UI jank when queries change
|
|
23
|
-
* - Automatic cleanup: Properly manages subscriptions and aborts in-flight requests
|
|
24
|
-
*
|
|
25
|
-
* When the query or options change, the hook will:
|
|
26
|
-
* 1. Abort any in-flight requests for the previous query
|
|
27
|
-
* 2. Start a new transition to update the query
|
|
28
|
-
* 3. Suspend rendering until the new data is available
|
|
29
|
-
* 4. Return the new data once it's ready
|
|
30
|
-
*
|
|
31
|
-
* The returned `isPending` flag indicates when a transition is in progress,
|
|
19
|
+
* @remarks
|
|
20
|
+
* The returned `isPending` flag indicates when a React transition is in progress,
|
|
32
21
|
* which can be used to show loading states for query changes.
|
|
33
22
|
*
|
|
34
|
-
* @
|
|
23
|
+
* @beta
|
|
24
|
+
* @category GROQ
|
|
25
|
+
* @param query - GROQ query string to execute
|
|
26
|
+
* @param options - Optional configuration for the query
|
|
27
|
+
* @returns Object containing the query result and a pending state flag
|
|
28
|
+
*
|
|
29
|
+
* @example Basic usage
|
|
35
30
|
* ```tsx
|
|
36
|
-
* // Basic usage
|
|
37
31
|
* const {data, isPending} = useQuery<Movie[]>('*[_type == "movie"]')
|
|
32
|
+
* ```
|
|
38
33
|
*
|
|
34
|
+
* @example Using parameters
|
|
35
|
+
* ```tsx
|
|
39
36
|
* // With parameters
|
|
40
37
|
* const {data} = useQuery<Movie>('*[_type == "movie" && _id == $id][0]', {
|
|
41
38
|
* params: { id: 'movie-123' }
|
|
42
39
|
* })
|
|
40
|
+
* ```
|
|
43
41
|
*
|
|
44
|
-
*
|
|
42
|
+
* @example With a loading state for transitions
|
|
43
|
+
* ```tsx
|
|
45
44
|
* const {data, isPending} = useQuery<Movie[]>('*[_type == "movie"]')
|
|
46
45
|
* return (
|
|
47
46
|
* <div>
|
|
@@ -53,11 +52,6 @@ import {useSanityInstance} from '../context/useSanityInstance'
|
|
|
53
52
|
* )
|
|
54
53
|
* ```
|
|
55
54
|
*
|
|
56
|
-
* @param query - GROQ query string to execute
|
|
57
|
-
* @param options - Optional configuration for the query
|
|
58
|
-
* @returns Object containing the query result and a pending state flag
|
|
59
|
-
*
|
|
60
|
-
* @beta
|
|
61
55
|
*/
|
|
62
56
|
export function useQuery<T>(query: string, options?: QueryOptions): {data: T; isPending: boolean} {
|
|
63
57
|
const instance = useSanityInstance(options?.resourceId)
|
|
@@ -72,10 +66,7 @@ export function useQuery<T>(query: string, options?: QueryOptions): {data: T; is
|
|
|
72
66
|
const deferred = useMemo(() => parseQueryKey(deferredQueryKey), [deferredQueryKey])
|
|
73
67
|
|
|
74
68
|
// Create an AbortController to cancel in-flight requests when needed
|
|
75
|
-
const ref =
|
|
76
|
-
if (ref.current === null) {
|
|
77
|
-
ref.current = new AbortController()
|
|
78
|
-
}
|
|
69
|
+
const [ref, setRef] = useState<AbortController>(new AbortController())
|
|
79
70
|
|
|
80
71
|
// When the query or options change, start a transition to update the query
|
|
81
72
|
useEffect(() => {
|
|
@@ -83,14 +74,14 @@ export function useQuery<T>(query: string, options?: QueryOptions): {data: T; is
|
|
|
83
74
|
|
|
84
75
|
startTransition(() => {
|
|
85
76
|
// Abort any in-flight requests for the previous query
|
|
86
|
-
if (ref
|
|
87
|
-
ref.
|
|
88
|
-
|
|
77
|
+
if (ref && !ref.signal.aborted) {
|
|
78
|
+
ref.abort()
|
|
79
|
+
setRef(new AbortController())
|
|
89
80
|
}
|
|
90
81
|
|
|
91
82
|
setDeferredQueryKey(queryKey)
|
|
92
83
|
})
|
|
93
|
-
}, [deferredQueryKey, queryKey])
|
|
84
|
+
}, [deferredQueryKey, queryKey, ref])
|
|
94
85
|
|
|
95
86
|
// Get the state source for this query from the query store
|
|
96
87
|
const {getCurrent, subscribe} = useMemo(
|
|
@@ -102,7 +93,7 @@ export function useQuery<T>(query: string, options?: QueryOptions): {data: T; is
|
|
|
102
93
|
// This is the React Suspense integration - throwing a promise
|
|
103
94
|
// will cause React to show the nearest Suspense fallback
|
|
104
95
|
if (getCurrent() === undefined) {
|
|
105
|
-
throw resolveQuery(instance, deferred.query, {...deferred.options, signal: ref.
|
|
96
|
+
throw resolveQuery(instance, deferred.query, {...deferred.options, signal: ref.signal})
|
|
106
97
|
}
|
|
107
98
|
|
|
108
99
|
// Subscribe to updates and get the current data
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext } from "react";
|
|
3
|
-
const SanityInstanceContext = createContext(null), SanityProvider = ({ children, sanityInstances }) => /* @__PURE__ */ jsx(SanityInstanceContext.Provider, { value: sanityInstances, children });
|
|
4
|
-
export {
|
|
5
|
-
SanityInstanceContext,
|
|
6
|
-
SanityProvider
|
|
7
|
-
};
|
|
8
|
-
//# sourceMappingURL=context.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sources":["../../src/context/SanityProvider.tsx"],"sourcesContent":["import {type SanityInstance} from '@sanity/sdk'\nimport {createContext, type ReactElement} from 'react'\n\n/**\n * @internal\n */\nexport interface SanityProviderProps {\n children: React.ReactNode\n sanityInstances: SanityInstance[]\n}\n\nexport const SanityInstanceContext = createContext<SanityInstance[] | null>(null)\n\n/**\n * @internal\n *\n * Top-level context provider that provides access to the Sanity configuration instance.\n * This must wrap any components making use of the Sanity SDK React hooks.\n *\n * @remarks In most cases, SanityApp should be used rather than SanityProvider directly; SanityApp bundles both SanityProvider and an authentication layer.\n * @param props - Sanity project and dataset configuration\n * @returns Rendered component\n * @example\n * ```tsx\n * import {createSanityInstance} from '@sanity/sdk'\n * import {SanityProvider} from '@sanity/sdk-react'\n *\n * import MyAppRoot from './Root'\n *\n * const sanityInstance = createSanityInstance({\n * projectId: 'your-project-id',\n * dataset: 'production',\n * })\n *\n * export default function MyApp() {\n * return (\n * <SanityProvider sanityInstance={sanityInstance}>\n * <MyAppRoot />\n * </SanityProvider>\n * )\n * }\n * ```\n */\nexport const SanityProvider = ({children, sanityInstances}: SanityProviderProps): ReactElement => {\n return (\n <SanityInstanceContext.Provider value={sanityInstances}>\n {children}\n </SanityInstanceContext.Provider>\n )\n}\n"],"names":[],"mappings":";;AAWO,MAAM,wBAAwB,cAAuC,IAAI,GAgCnE,iBAAiB,CAAC,EAAC,UAAU,gBAAe,0BAEpD,sBAAsB,UAAtB,EAA+B,OAAO,iBACpC,SACH,CAAA;"}
|