{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "paginated-list",
  "type": "registry:component",
  "title": "Paginated List",
  "description": "A reusable paginated list component that fetches and displays items in pages, with support for loading states and end-of-list indicators, based on Paginated and Cursor from the Lens React SDK.",
  "dependencies": ["@lens-protocol/react@0.0.0-canary-20250820102520", "react-virtuoso@4.13.0", "lucide-react"],
  "files": [
    {
      "path": "registry/new-york/components/common/paginated-list.tsx",
      "content": "import { Cursor, Paginated } from \"@lens-protocol/react\";\nimport { ReactNode, useCallback, useEffect, useState } from \"react\";\nimport { AlertTriangle, Loader } from \"lucide-react\";\nimport { Virtuoso } from \"react-virtuoso\";\n\ntype PaginationListProps<Type> = {\n  className?: string;\n  data: Paginated<Type> | undefined;\n  loading: boolean;\n  setCursor: (cursor: Cursor | null) => void;\n  renderItem: (item: Type, index: number) => ReactNode;\n  error?: Error | null | undefined;\n  cursor?: Cursor | null;\n  renderHeader?: () => ReactNode;\n  renderFooter?: () => ReactNode;\n  renderSkeleton?: () => ReactNode;\n  emptyMessage?: string;\n  errorMessage?: string;\n  filterFn?: (item: Type) => boolean;\n  sortFn?: (a: Type, b: Type) => number;\n};\n\nexport const PaginatedList = <Type extends any>(props: PaginationListProps<Type>) => {\n  const [items, setItems] = useState<Type[]>([]);\n\n  const DefaultHeader = (): ReactNode => {\n    return <div className=\"h-2\"></div>;\n  };\n\n  const DefaultFooter = (): ReactNode => {\n    if (!loading || !items.length) return <div className=\"h-2\"></div>;\n    return (\n      <div className=\"w-full h-16 flex items-center justify-center\">\n        <Loader className=\"animate-spin flex-none opacity-45 w-6 h-6\" />\n      </div>\n    );\n  };\n\n  const {\n    className,\n    data,\n    loading,\n    error,\n    filterFn,\n    cursor,\n    setCursor,\n    renderItem,\n    renderSkeleton,\n    renderHeader = DefaultHeader,\n    renderFooter = DefaultFooter,\n    emptyMessage,\n    errorMessage,\n    sortFn,\n  } = props;\n\n  useEffect(() => {\n    if (data?.items) {\n      const items = filterFn ? data.items.filter(filterFn) : data.items;\n      const sortedItems = sortFn ? items.toSorted(sortFn) : items;\n      setItems(prevItems => {\n        return cursor ? [...prevItems, ...sortedItems] : [...sortedItems];\n      });\n    }\n  }, [data]);\n\n  const loadMore = useCallback(() => {\n    if (!data || !data.pageInfo.next) return;\n    setCursor(data.pageInfo.next as Cursor);\n  }, [data, setCursor]);\n\n  const Skeleton = (): ReactNode => {\n    return renderSkeleton ? (\n      renderSkeleton()\n    ) : (\n      <div className=\"w-full h-full flex items-center justify-center\">\n        <Loader className=\"animate-spin flex-none opacity-45 w-6 h-6\" />\n      </div>\n    );\n  };\n\n  const Header = renderHeader;\n  const Footer = renderFooter;\n\n  return (\n    <div className=\"h-full\">\n      {error ? (\n        <div className=\"py-4 flex gap-4 items-center\">\n          <AlertTriangle className=\"w-8 h-8\" />\n          {errorMessage ?? \"Error loading data\"}\n        </div>\n      ) : !items.length && loading ? (\n        <div className={\"h-full\" + (className ? ` ${className}` : \"\")}>\n          <Header />\n          <Skeleton />\n          <Footer />\n        </div>\n      ) : (\n        <div className={\"h-full\" + (className ? ` ${className}` : \"\")}>\n          {items.length === 0 && (\n            <div className=\"py-4 h-full flex items-center justify-center text-sm\">\n              {emptyMessage ?? \"Nothing to show\"}\n            </div>\n          )}\n          <Virtuoso\n            className=\"h-full\"\n            data={items}\n            endReached={loadMore}\n            itemContent={(index, item) => renderItem(item, index)}\n            components={{ Header, Footer }}\n          />\n        </div>\n      )}\n    </div>\n  );\n};\n",
      "type": "registry:component"
    }
  ]
}
