mirror of
https://github.com/supabase/supabase.git
synced 2026-10-12 12:55:06 +03:00
* docs: ui library password auth vue * docs: ui library password auth nuxt * Fix a pnpm-lock conflict. * Adjust paths * Fix import path * Rebuild * docs: remove shadcn-vue package * Fix the pnpm-lock. --------- Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com> Co-authored-by: Terry Sutton <saltcod@gmail.com>
19 lines
6.7 KiB
JSON
19 lines
6.7 KiB
JSON
{
|
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
"name": "infinite-query-hook",
|
|
"type": "registry:block",
|
|
"title": "Infinite Query Hook",
|
|
"description": "React hook for infinite lists, fetching data from Supabase.",
|
|
"dependencies": [
|
|
"@supabase/supabase-js",
|
|
"@supabase/postgrest-js@*"
|
|
],
|
|
"registryDependencies": [],
|
|
"files": [
|
|
{
|
|
"path": "registry/default/blocks/infinite-query-hook/hooks/use-infinite-query.ts",
|
|
"content": "'use client'\n\nimport { createClient } from '@/registry/default/fixtures/lib/supabase/client'\nimport { PostgrestQueryBuilder, type PostgrestClientOptions } from '@supabase/postgrest-js'\nimport { type SupabaseClient } from '@supabase/supabase-js'\nimport { useEffect, useRef, useSyncExternalStore } from 'react'\n\nconst supabase = createClient()\n\n// The following types are used to make the hook type-safe. It extracts the database type from the supabase client.\ntype SupabaseClientType = typeof supabase\n\n// Utility type to check if the type is any\ntype IfAny<T, Y, N> = 0 extends 1 & T ? Y : N\n\n// Extracts the database type from the supabase client. If the supabase client doesn't have a type, it will fallback properly.\ntype Database =\n SupabaseClientType extends SupabaseClient<infer U>\n ? IfAny<\n U,\n {\n public: {\n Tables: Record<string, any>\n Views: Record<string, any>\n Functions: Record<string, any>\n }\n },\n U\n >\n : {\n public: {\n Tables: Record<string, any>\n Views: Record<string, any>\n Functions: Record<string, any>\n }\n }\n\n// Change this to the database schema you want to use\ntype DatabaseSchema = Database['public']\n\n// Extracts the table names from the database type\ntype SupabaseTableName = keyof DatabaseSchema['Tables']\n\n// Extracts the table definition from the database type\ntype SupabaseTableData<T extends SupabaseTableName> = DatabaseSchema['Tables'][T]['Row']\n\n// Default client options for PostgrestQueryBuilder\ntype DefaultClientOptions = PostgrestClientOptions\n\ntype SupabaseSelectBuilder<T extends SupabaseTableName> = ReturnType<\n PostgrestQueryBuilder<\n DefaultClientOptions,\n DatabaseSchema,\n DatabaseSchema['Tables'][T],\n T\n >['select']\n>\n\n// A function that modifies the query. Can be used to sort, filter, etc. If .range is used, it will be overwritten.\ntype SupabaseQueryHandler<T extends SupabaseTableName> = (\n query: SupabaseSelectBuilder<T>\n) => SupabaseSelectBuilder<T>\n\ninterface UseInfiniteQueryProps<T extends SupabaseTableName, Query extends string = '*'> {\n // The table name to query\n tableName: T\n // The columns to select, defaults to `*`\n columns?: string\n // The number of items to fetch per page, defaults to `20`\n pageSize?: number\n // A function that modifies the query. Can be used to sort, filter, etc. If .range is used, it will be overwritten.\n trailingQuery?: SupabaseQueryHandler<T>\n}\n\ninterface StoreState<TData> {\n data: TData[]\n count: number\n isSuccess: boolean\n isLoading: boolean\n isFetching: boolean\n error: Error | null\n hasInitialFetch: boolean\n}\n\ntype Listener = () => void\n\nfunction createStore<TData extends SupabaseTableData<T>, T extends SupabaseTableName>(\n props: UseInfiniteQueryProps<T>\n) {\n const { tableName, columns = '*', pageSize = 20, trailingQuery } = props\n\n let state: StoreState<TData> = {\n data: [],\n count: 0,\n isSuccess: false,\n isLoading: false,\n isFetching: false,\n error: null,\n hasInitialFetch: false,\n }\n\n const listeners = new Set<Listener>()\n\n const notify = () => {\n listeners.forEach((listener) => listener())\n }\n\n const setState = (newState: Partial<StoreState<TData>>) => {\n state = { ...state, ...newState }\n notify()\n }\n\n const fetchPage = async (skip: number) => {\n if (state.hasInitialFetch && (state.isFetching || state.count <= state.data.length)) return\n\n setState({ isFetching: true })\n\n let query = supabase\n .from(tableName)\n .select(columns, { count: 'exact' }) as unknown as SupabaseSelectBuilder<T>\n\n if (trailingQuery) {\n query = trailingQuery(query)\n }\n const { data: newData, count, error } = await query.range(skip, skip + pageSize - 1)\n\n if (error) {\n console.error('An unexpected error occurred:', error)\n setState({ error })\n } else {\n setState({\n data: [...state.data, ...(newData as TData[])],\n count: count || 0,\n isSuccess: true,\n error: null,\n })\n }\n setState({ isFetching: false })\n }\n\n const fetchNextPage = async () => {\n if (state.isFetching) return\n await fetchPage(state.data.length)\n }\n\n const initialize = async () => {\n setState({ isLoading: true, isSuccess: false, data: [] })\n await fetchNextPage()\n setState({ isLoading: false, hasInitialFetch: true })\n }\n\n return {\n getState: () => state,\n subscribe: (listener: Listener) => {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n fetchNextPage,\n initialize,\n }\n}\n\n// Empty initial state to avoid hydration errors.\nconst initialState: any = {\n data: [],\n count: 0,\n isSuccess: false,\n isLoading: false,\n isFetching: false,\n error: null,\n hasInitialFetch: false,\n}\n\nfunction useInfiniteQuery<\n TData extends SupabaseTableData<T>,\n T extends SupabaseTableName = SupabaseTableName,\n>(props: UseInfiniteQueryProps<T>) {\n const storeRef = useRef(createStore<TData, T>(props))\n\n const state = useSyncExternalStore(\n storeRef.current.subscribe,\n () => storeRef.current.getState(),\n () => initialState as StoreState<TData>\n )\n\n useEffect(() => {\n // Recreate store if props change\n if (\n storeRef.current.getState().hasInitialFetch &&\n (props.tableName !== props.tableName ||\n props.columns !== props.columns ||\n props.pageSize !== props.pageSize)\n ) {\n storeRef.current = createStore<TData, T>(props)\n }\n\n if (!state.hasInitialFetch && typeof window !== 'undefined') {\n storeRef.current.initialize()\n }\n }, [props.tableName, props.columns, props.pageSize, state.hasInitialFetch])\n\n return {\n data: state.data,\n count: state.count,\n isSuccess: state.isSuccess,\n isLoading: state.isLoading,\n isFetching: state.isFetching,\n error: state.error,\n hasMore: state.count > state.data.length,\n fetchNextPage: storeRef.current.fetchNextPage,\n }\n}\n\nexport {\n useInfiniteQuery,\n type SupabaseQueryHandler,\n type SupabaseTableData,\n type SupabaseTableName,\n type UseInfiniteQueryProps,\n}\n",
|
|
"type": "registry:hook"
|
|
}
|
|
]
|
|
} |