@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.
Files changed (35) hide show
  1. package/dist/_chunks-es/SanityInstanceContext.js +6 -0
  2. package/dist/_chunks-es/SanityInstanceContext.js.map +1 -0
  3. package/dist/_chunks-es/useLogOut.js +28 -15
  4. package/dist/_chunks-es/useLogOut.js.map +1 -1
  5. package/dist/components.js +170 -94
  6. package/dist/components.js.map +1 -1
  7. package/dist/context.js +11 -1
  8. package/dist/context.js.map +1 -1
  9. package/dist/hooks.d.ts +161 -58
  10. package/dist/hooks.js +537 -290
  11. package/dist/hooks.js.map +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/index.js.map +1 -1
  14. package/package.json +7 -4
  15. package/src/_exports/hooks.ts +3 -1
  16. package/src/components/SanityApp.tsx +3 -16
  17. package/src/context/SanityInstanceContext.ts +4 -0
  18. package/src/context/SanityProvider.tsx +3 -3
  19. package/src/hooks/comlink/useManageFavorite.test.ts +106 -0
  20. package/src/hooks/comlink/useManageFavorite.ts +98 -0
  21. package/src/hooks/comlink/useRecordDocumentHistoryEvent.test.ts +77 -0
  22. package/src/hooks/comlink/useRecordDocumentHistoryEvent.ts +75 -0
  23. package/src/hooks/context/useSanityInstance.ts +1 -1
  24. package/src/hooks/document/useApplyActions.test.ts +5 -4
  25. package/src/hooks/document/useApplyActions.ts +10 -4
  26. package/src/hooks/document/useEditDocument.ts +29 -32
  27. package/src/hooks/helpers/createCallbackHook.tsx +3 -2
  28. package/src/hooks/helpers/createStateSourceHook.tsx +2 -6
  29. package/src/hooks/infiniteList/useInfiniteList.test.tsx +10 -10
  30. package/src/hooks/infiniteList/useInfiniteList.ts +39 -28
  31. package/src/hooks/paginatedList/usePaginatedList.ts +25 -13
  32. package/src/hooks/projection/useProjection.ts +3 -3
  33. package/src/hooks/query/useQuery.ts +25 -34
  34. package/dist/_chunks-es/context.js +0 -8
  35. 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 DEFAULT_PAGE_SIZE = 25
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 page (defaults to 25)
31
+ * Number of items to load per batch (defaults to 25)
31
32
  */
32
- pageSize?: number
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 page
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 page of results
68
+ * Function to load the next batch of results
67
69
  */
68
70
  loadMore: () => void
69
71
  }
70
72
 
71
73
  /**
72
- * React hook for paginated document queries with infinite scrolling support
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
- * This hook provides a convenient way to implement infinite scrolling lists of documents
75
- * with support for filtering, searching, and custom ordering. It handles pagination
76
- * automatically and provides a simple API for loading more results.
77
- *
78
- * The hook constructs and executes GROQ queries based on the provided options,
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
- * pageSize: 10,
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
- pageSize = DEFAULT_PAGE_SIZE,
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(pageSize)
116
+ const [limit, setLimit] = useState(batchSize)
106
117
 
107
- // Reset the limit to the current pageSize whenever any query parameters
108
- // (filter, search, params, orderings) or pageSize changes
109
- const key = JSON.stringify({filter, search, params, orderings, pageSize})
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(pageSize)
112
- }, [key, pageSize])
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 + pageSize, count))
157
- }, [count, pageSize])
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
- * React hook for paginated document queries with traditional pagination controls
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
- * This hook provides a convenient way to implement paginated lists of documents
121
- * with support for filtering, searching, and custom ordering. It handles pagination
122
- * automatically and provides a comprehensive API for navigating between pages.
123
- *
124
- * The hook constructs and executes GROQ queries based on the provided options,
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 useDocuments to render a collection with specific fields
56
+ * @example Combining with useInfiniteList to render a collection with specific fields
57
57
  * ```
58
58
  * // DocumentList.jsx
59
- * const { results, isPending } = useDocuments({ filter: '_type == "article"' })
59
+ * const { data } = useInfiniteList({ filter: '_type == "article"' })
60
60
  * return (
61
61
  * <div>
62
62
  * <h1>Articles</h1>
63
63
  * <ul>
64
- * {isPending ? 'Loading…' : results.map(article => (
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, useRef, useState, useSyncExternalStore, useTransition} from 'react'
8
+ import {useEffect, useMemo, useState, useSyncExternalStore, useTransition} from 'react'
9
9
 
10
10
  import {useSanityInstance} from '../context/useSanityInstance'
11
11
 
12
12
  /**
13
- * React hook for executing GROQ queries against your Sanity dataset.
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. It integrates with React's Suspense and Transitions APIs
17
- * to provide a smooth user experience.
16
+ * for your Sanity content. Changes made to the dataset’s content will trigger
17
+ * automatic updates.
18
18
  *
19
- * Features:
20
- * - Suspense integration: The hook will suspend rendering until data is available
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
- * @example
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
- * // With loading state for transitions
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 = useRef<AbortController | null>(null)
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.current && !ref.current.signal.aborted) {
87
- ref.current.abort()
88
- ref.current = new AbortController()
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.current.signal})
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;"}