{"_id":"@eldersoon/antd-paginated-select","_rev":"4-030bf964df8c7082760f24b2bc173c7a","name":"@eldersoon/antd-paginated-select","dist-tags":{"latest":"1.1.1"},"versions":{"1.0.5":{"name":"@eldersoon/antd-paginated-select","version":"1.0.5","keywords":["react","antd","ant-design","select","pagination","infinite-scroll","server-side"],"author":{"name":"Elderson Gamma","email":"eldersondagama@gmail.com"},"license":"MIT","_id":"@eldersoon/antd-paginated-select@1.0.5","maintainers":[{"name":"eldersoon","email":"eldersondagama@gmail.com"}],"homepage":"https://github.com/eldersongamma/antd-paginated-select#readme","bugs":{"url":"https://github.com/eldersongamma/antd-paginated-select/issues"},"dist":{"shasum":"332d4572580b0e160e84e4ea52872e6cd4aad3ff","tarball":"https://registry.npmjs.org/@eldersoon/antd-paginated-select/-/antd-paginated-select-1.0.5.tgz","fileCount":3,"integrity":"sha512-ktIz5F3X/zzJSwFEjnmSiuzTWL3qMezRhq4WC7ScbE5xV/1TyBYBCdWSomBUnFNjLdIbHPzDqNe7FwJbJBqDqw==","signatures":[{"sig":"MEUCIC0SzfRDCeCvac9l75L9UASLnpm0AP+/mq1f3R9vr2RpAiEAs46MCyVe6B3GupRb5F4AUoa5xK+buJFBxhL0zvK3QDg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":16976},"main":"./dist/index.js","_from":"file:eldersoon-antd-paginated-select-1.0.5.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"private":false,"scripts":{"dev":"tsup --watch","lint":"eslint src --ext .ts,.tsx","build":"tsup","clean":"rm -rf dist","type-check":"tsc --noEmit"},"_npmUser":{"name":"eldersoon","email":"eldersondagama@gmail.com"},"_resolved":"/tmp/c24bef039669944553a0eac480b052b2/eldersoon-antd-paginated-select-1.0.5.tgz","_integrity":"sha512-ktIz5F3X/zzJSwFEjnmSiuzTWL3qMezRhq4WC7ScbE5xV/1TyBYBCdWSomBUnFNjLdIbHPzDqNe7FwJbJBqDqw==","repository":{"url":"git+https://github.com/eldersongamma/antd-paginated-select.git","type":"git","directory":"packages/paginated-select"},"_npmVersion":"10.8.2","description":"A paginated select component for Ant Design with server-side data fetching","directories":{},"sideEffects":false,"_nodeVersion":"20.19.4","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","eslint":"^8.0.0","typescript":"^5.0.0","@types/react":"^18.2.0","@types/react-dom":"^18.2.0"},"peerDependencies":{"antd":">=5.0.0","react":">=16.8.0","react-dom":">=16.8.0","@tanstack/react-query":">=4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/antd-paginated-select_1.0.5_1755378873461_0.4951150685935941","host":"s3://npm-registry-packages-npm-production"}},"1.0.6":{"name":"@eldersoon/antd-paginated-select","version":"1.0.6","keywords":["react","antd","ant-design","select","pagination","infinite-scroll","server-side"],"author":{"name":"Elderson Gamma","email":"eldersondagama@gmail.com"},"license":"MIT","_id":"@eldersoon/antd-paginated-select@1.0.6","maintainers":[{"name":"eldersoon","email":"eldersondagama@gmail.com"}],"homepage":"https://github.com/eldersoon/antd-paginated-select#readme","bugs":{"url":"https://github.com/eldersoon/antd-paginated-select/issues"},"dist":{"shasum":"502cecd372e79169b3b0ff399d8172e9a030caea","tarball":"https://registry.npmjs.org/@eldersoon/antd-paginated-select/-/antd-paginated-select-1.0.6.tgz","fileCount":3,"integrity":"sha512-oHF0Ys9xtS840DZQ1nOjrD1Ab2Zlx7pfHZGEf82PToKWCd6OQqkUh0NQBZowwB89ovAvo4u7S1LpblXhBTcPDA==","signatures":[{"sig":"MEUCIFMd6oYDVwkh2mQood5THaPL62rXCZNEfoCJmiVl8VmaAiEA+vB/0H4y7nSQdBhrYo7HjeitRqtBWNGSchxG7Xu9gVo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":16964},"main":"./dist/index.js","_from":"file:eldersoon-antd-paginated-select-1.0.6.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"private":false,"scripts":{"dev":"tsup --watch","lint":"eslint src --ext .ts,.tsx","build":"tsup","clean":"rm -rf dist","type-check":"tsc --noEmit"},"_npmUser":{"name":"eldersoon","email":"eldersondagama@gmail.com"},"_resolved":"/tmp/a57e43261acd544939aa9e2d8a9becc6/eldersoon-antd-paginated-select-1.0.6.tgz","_integrity":"sha512-oHF0Ys9xtS840DZQ1nOjrD1Ab2Zlx7pfHZGEf82PToKWCd6OQqkUh0NQBZowwB89ovAvo4u7S1LpblXhBTcPDA==","repository":{"url":"git+https://github.com/eldersoon/antd-paginated-select.git","type":"git","directory":"packages/paginated-select"},"_npmVersion":"10.8.2","description":"A paginated select component for Ant Design with server-side data fetching","directories":{},"sideEffects":false,"_nodeVersion":"20.19.4","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","eslint":"^8.0.0","typescript":"^5.0.0","@types/react":"^18.2.0","@types/react-dom":"^18.2.0"},"peerDependencies":{"antd":">=5.0.0","react":">=16.8.0","react-dom":">=16.8.0","@tanstack/react-query":">=4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/antd-paginated-select_1.0.6_1755380130521_0.815733492549773","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@eldersoon/antd-paginated-select","version":"1.1.0","keywords":["react","antd","ant-design","select","pagination","infinite-scroll","server-side"],"author":{"name":"Elderson Gamma","email":"eldersondagama@gmail.com"},"license":"MIT","_id":"@eldersoon/antd-paginated-select@1.1.0","maintainers":[{"name":"eldersoon","email":"eldersondagama@gmail.com"}],"homepage":"https://github.com/eldersoon/antd-paginated-select#readme","bugs":{"url":"https://github.com/eldersoon/antd-paginated-select/issues"},"dist":{"shasum":"e46f2546f3affa43856eaa8b714afafe20b47332","tarball":"https://registry.npmjs.org/@eldersoon/antd-paginated-select/-/antd-paginated-select-1.1.0.tgz","fileCount":9,"integrity":"sha512-J1/Ex2/mn5pQPOm4+wOsBurbimo0q2wxzxD5VahaCq1MqxEHYjVgax4JgGvzbSMgOmSfiM+We70T8Vd4aIKJuQ==","signatures":[{"sig":"MEUCIQCw/7mEORze9r//Jmqo1ScCcx3Cykb6psZXkrOKQePkkAIgWauWLuwWfiImnp/7YdL9y4J+ySwLdWkepB13CNV1VOk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":70366},"main":"./dist/index.js","_from":"file:eldersoon-antd-paginated-select-1.1.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"private":false,"scripts":{"dev":"tsup --watch","lint":"eslint src --ext .ts,.tsx","build":"tsup","clean":"rm -rf dist","type-check":"tsc --noEmit"},"_npmUser":{"name":"eldersoon","email":"eldersondagama@gmail.com"},"_resolved":"/tmp/9b7626de0bd7d754d82659fd59625532/eldersoon-antd-paginated-select-1.1.0.tgz","_integrity":"sha512-J1/Ex2/mn5pQPOm4+wOsBurbimo0q2wxzxD5VahaCq1MqxEHYjVgax4JgGvzbSMgOmSfiM+We70T8Vd4aIKJuQ==","repository":{"url":"git+https://github.com/eldersoon/antd-paginated-select.git","type":"git","directory":"packages/paginated-select"},"_npmVersion":"10.8.2","description":"A paginated select component for Ant Design with server-side data fetching","directories":{},"sideEffects":false,"_nodeVersion":"20.19.4","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","eslint":"^8.0.0","typescript":"^5.0.0","@types/react":"^18.2.0","@types/react-dom":"^18.2.0"},"peerDependencies":{"antd":">=5.0.0","react":">=16.8.0","react-dom":">=16.8.0","@tanstack/react-query":">=4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/antd-paginated-select_1.1.0_1755381375942_0.8532939625821263","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@eldersoon/antd-paginated-select","version":"1.1.1","description":"A paginated select component for Ant Design with server-side data fetching","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"private":false,"sideEffects":false,"keywords":["react","antd","ant-design","select","pagination","infinite-scroll","server-side"],"author":{"name":"Elderson Gamma","email":"eldersondagama@gmail.com"},"license":"MIT","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0","antd":">=5.0.0","@tanstack/react-query":">=4.0.0"},"devDependencies":{"@types/react":"^18.2.0","@types/react-dom":"^18.2.0","eslint":"^8.0.0","tsup":"^8.0.0","typescript":"^5.0.0"},"repository":{"type":"git","url":"git+https://github.com/eldersoon/antd-paginated-select.git","directory":"packages/paginated-select"},"homepage":"https://github.com/eldersoon/antd-paginated-select#readme","bugs":{"url":"https://github.com/eldersoon/antd-paginated-select/issues"},"scripts":{"build":"tsup","dev":"tsup --watch","clean":"rm -rf dist","type-check":"tsc --noEmit","lint":"eslint src --ext .ts,.tsx"},"_id":"@eldersoon/antd-paginated-select@1.1.1","_integrity":"sha512-6icmOw81yMfC76gTOB0KyG49trhzyGBy2/nuDJ4qSfUglrY3UoBMoEAnFfJBb2azTSX+1EV2YlSWP5VXnZmpMg==","_resolved":"/tmp/d84e3cfe04ed32f08e946c1f529b250a/eldersoon-antd-paginated-select-1.1.1.tgz","_from":"file:eldersoon-antd-paginated-select-1.1.1.tgz","_nodeVersion":"20.19.4","_npmVersion":"10.8.2","dist":{"integrity":"sha512-6icmOw81yMfC76gTOB0KyG49trhzyGBy2/nuDJ4qSfUglrY3UoBMoEAnFfJBb2azTSX+1EV2YlSWP5VXnZmpMg==","shasum":"4266d087233ee7d8ded786a641dc05c2e11217df","tarball":"https://registry.npmjs.org/@eldersoon/antd-paginated-select/-/antd-paginated-select-1.1.1.tgz","fileCount":9,"unpackedSize":70385,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDSoid6JLWi099Q1BK3fwOsnsoX4k14DKjmnd5iRU5h+AiBThr+D43C8zCSFhBZIRmmfyjMlLyRa34kfOBxmJUJpkw=="}]},"_npmUser":{"name":"eldersoon","email":"eldersondagama@gmail.com"},"directories":{},"maintainers":[{"name":"eldersoon","email":"eldersondagama@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/antd-paginated-select_1.1.1_1755382751324_0.02261590872964825"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-16T21:14:33.367Z","modified":"2025-08-16T22:19:11.702Z","1.0.5":"2025-08-16T21:14:33.638Z","1.0.6":"2025-08-16T21:35:30.701Z","1.1.0":"2025-08-16T21:56:16.113Z","1.1.1":"2025-08-16T22:19:11.533Z"},"bugs":{"url":"https://github.com/eldersoon/antd-paginated-select/issues"},"author":{"name":"Elderson Gamma","email":"eldersondagama@gmail.com"},"license":"MIT","homepage":"https://github.com/eldersoon/antd-paginated-select#readme","keywords":["react","antd","ant-design","select","pagination","infinite-scroll","server-side"],"repository":{"type":"git","url":"git+https://github.com/eldersoon/antd-paginated-select.git","directory":"packages/paginated-select"},"description":"A paginated select component for Ant Design with server-side data fetching","maintainers":[{"name":"eldersoon","email":"eldersondagama@gmail.com"}],"readme":"# @eldersoon/antd-paginated-select\n\nA high-performance React component that provides server-side paginated select functionality for Ant Design, built with TypeScript and TanStack Query.\n\n## Features\n\n- 🚀 **Server-side pagination** - Efficiently handle massive datasets (1M+ records)\n- 🔍 **Intelligent search** - Real-time search with 300ms debounce and server-side filtering\n- 📦 **Full TypeScript support** - Complete type safety with strongly-typed generic adapters\n- 🎯 **Flexible data adapters** - Work with any API structure (REST, GraphQL, custom)\n- 🔄 **Infinite scroll** - Seamless pagination through smooth scrolling\n- 💾 **Smart caching & optimization** - Built on TanStack Query with automatic caching, deduplication, and background updates\n- 🎨 **Native Ant Design integration** - Perfect integration with your existing Ant Design theme and components\n- ⚡ **High performance** - Virtualized rendering and optimized re-renders for large lists\n- 🔧 **Highly customizable** - Support for single/multiple selection, custom formatting, and advanced filtering\n- 🌐 **SSR compatible** - Works seamlessly with Next.js and other SSR frameworks\n\n## Installation\n\n```bash\nnpm install @eldersoon/antd-paginated-select\n# or\nyarn add @eldersoon/antd-paginated-select\n# or\npnpm add @eldersoon/antd-paginated-select\n```\n\n### Peer Dependencies\n\nMake sure you have these peer dependencies installed:\n\n```bash\nnpm install react react-dom antd @tanstack/react-query\n```\n\n## Quick Start\n\n### 1. Setup Query Client (Required)\n\n```tsx\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\n\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      staleTime: 60 * 1000, // 1 minute\n      refetchOnWindowFocus: false,\n    },\n  },\n});\n\nfunction App() {\n  return (\n    <QueryClientProvider client={queryClient}>\n      {/* Your app components */}\n    </QueryClientProvider>\n  );\n}\n```\n\n### 2. Basic Usage\n\n```tsx\nimport { PaginatedSelect, makeAdapter } from \"@eldersoon/antd-paginated-select\";\n\n// Define your data type\ntype User = {\n  id: string;\n  name: string;\n  email: string;\n  department: string;\n};\n\n// Create a strongly-typed data adapter\nconst userAdapter = makeAdapter<User>()({\n  // Required: Fetch paginated data\n  list: async ({ page, pageSize, search, params }) => {\n    const response = await fetch(\n      `/api/users?page=${page}&limit=${pageSize}&search=${\n        search || \"\"\n      }&department=${params?.department || \"\"}`\n    );\n    const data = await response.json();\n\n    return {\n      items: data.users,\n      total: data.total, // OR use { items, hasMore: boolean }\n    };\n  },\n\n  // Optional: Fetch specific items by ID (for controlled values)\n  getById: async (id: string) => {\n    const response = await fetch(`/api/users/${id}`);\n    return response.json();\n  },\n\n  // Optional: Bulk fetch multiple items (more efficient)\n  getByIds: async (ids: string[]) => {\n    const response = await fetch(`/api/users/bulk`, {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify({ ids }),\n    });\n    return response.json();\n  },\n\n  // Required: How to display each item\n  getLabel: (user) => `${user.name} (${user.email})`,\n\n  // Required: Unique identifier for each item\n  getValue: (user) => user.id,\n});\n\n// Use the component\nfunction UserSelect() {\n  const [selectedUser, setSelectedUser] = useState<string>();\n\n  return (\n    <PaginatedSelect\n      dataAdapter={userAdapter}\n      value={selectedUser}\n      onChange={(value) => setSelectedUser(value)}\n      placeholder=\"Search and select a user...\"\n      pageSize={20}\n      params={{ department: \"engineering\" }} // Optional: additional filters\n    />\n  );\n}\n```\n\n## API Reference\n\n### PaginatedSelect Props\n\n| Prop            | Type                  | Default | Description                                                      |\n| --------------- | --------------------- | ------- | ---------------------------------------------------------------- |\n| `dataAdapter`   | `DataAdapter<T>`      | -       | **Required.** Adapter that defines how to fetch and display data |\n| `multiple`      | `boolean`             | `false` | Enable multiple selection                                        |\n| `pageSize`      | `number`              | `10`    | Number of items to fetch per page                                |\n| `params`        | `Record<string, any>` | -       | Additional parameters to pass to the list function               |\n| `dependencyKey` | `unknown`             | -       | Key that triggers data refresh when changed                      |\n| `debug`         | `boolean`             | `false` | Enable debug logging                                             |\n| `...restProps`  | `SelectProps`         | -       | All other Ant Design Select props are supported                  |\n\n### DataAdapter Interface\n\n```tsx\ntype DataAdapter<T> = {\n  list: (args: ListArgs) => Promise<ListResponse<T>>;\n  getById?: (id: string) => Promise<T | null>;\n  getByIds?: (ids: string[]) => Promise<T[]>;\n  getLabel: (item: T) => string;\n  getValue: (item: T) => string;\n};\n\ntype ListArgs = {\n  page: number;\n  pageSize: number;\n  search?: string;\n  params?: Record<string, any>;\n};\n\ntype ListResponse<T> =\n  | { items: T[]; total: number } // For total-based pagination\n  | { items: T[]; hasMore: boolean }; // For cursor-based pagination\n```\n\n## Advanced Examples\n\n### Multiple Selection with Custom Formatting\n\n```tsx\nfunction MultiUserSelect() {\n  const [selectedUsers, setSelectedUsers] = useState<string[]>([]);\n\n  return (\n    <PaginatedSelect\n      dataAdapter={userAdapter}\n      multiple\n      value={selectedUsers}\n      onChange={(values) => setSelectedUsers(values)}\n      placeholder=\"Select multiple users...\"\n      maxTagCount={3}\n      maxTagPlaceholder={(omittedValues) => `+${omittedValues.length} more`}\n    />\n  );\n}\n```\n\n### Conditional Data Loading with Dependencies\n\n```tsx\nfunction DepartmentUserSelect({ departmentId }: { departmentId?: string }) {\n  const [selectedUser, setSelectedUser] = useState<string>();\n\n  const departmentUserAdapter = makeAdapter<User>()({\n    list: async ({ page, pageSize, search }) => {\n      if (!departmentId) return { items: [], total: 0 };\n\n      const response = await fetch(\n        `/api/departments/${departmentId}/users?page=${page}&limit=${pageSize}&search=${\n          search || \"\"\n        }`\n      );\n      return response.json();\n    },\n    getLabel: (user) => user.name,\n    getValue: (user) => user.id,\n  });\n\n  return (\n    <PaginatedSelect\n      dataAdapter={departmentUserAdapter}\n      value={selectedUser}\n      onChange={setSelectedUser}\n      dependencyKey={departmentId} // Reload when department changes\n      disabled={!departmentId}\n      placeholder={\n        departmentId\n          ? \"Select a user from this department...\"\n          : \"Please select a department first\"\n      }\n    />\n  );\n}\n```\n\n### Custom Response Format (Cursor-based Pagination)\n\n```tsx\nconst cursorBasedAdapter = makeAdapter<Product>()({\n  list: async ({ page, pageSize, search, params }) => {\n    const response = await api.getProducts({\n      cursor: params?.cursor,\n      limit: pageSize,\n      search,\n      category: params?.category,\n    });\n\n    return {\n      items: response.products,\n      hasMore: !!response.nextCursor,\n      // Store cursor for next request\n      meta: { nextCursor: response.nextCursor },\n    };\n  },\n  getLabel: (product) => `${product.name} - $${product.price.toFixed(2)}`,\n  getValue: (product) => product.id,\n});\n```\n\n### GraphQL Integration\n\n```tsx\nimport { gql, request } from \"graphql-request\";\n\nconst graphqlAdapter = makeAdapter<User>()({\n  list: async ({ page, pageSize, search, params }) => {\n    const query = gql`\n      query GetUsers(\n        $first: Int!\n        $offset: Int!\n        $search: String\n        $role: String\n      ) {\n        users(first: $first, offset: $offset, search: $search, role: $role) {\n          nodes {\n            id\n            name\n            email\n            role\n          }\n          totalCount\n        }\n      }\n    `;\n\n    const data = await request(\"/graphql\", query, {\n      first: pageSize,\n      offset: (page - 1) * pageSize,\n      search,\n      role: params?.role,\n    });\n\n    return {\n      items: data.users.nodes,\n      total: data.users.totalCount,\n    };\n  },\n  getLabel: (user) => user.name,\n  getValue: (user) => user.id,\n});\n```\n\n### Async Validation and Custom Styling\n\n```tsx\nfunction AsyncUserSelect() {\n  const [selectedUser, setSelectedUser] = useState<string>();\n  const [isValidating, setIsValidating] = useState(false);\n\n  const handleChange = async (value: string) => {\n    setIsValidating(true);\n\n    try {\n      // Validate selection server-side\n      await fetch(`/api/users/${value}/validate`, { method: \"POST\" });\n      setSelectedUser(value);\n    } catch (error) {\n      message.error(\"This user cannot be selected\");\n    } finally {\n      setIsValidating(false);\n    }\n  };\n\n  return (\n    <PaginatedSelect\n      dataAdapter={userAdapter}\n      value={selectedUser}\n      onChange={handleChange}\n      loading={isValidating}\n      placeholder=\"Select and validate user...\"\n      style={{ width: 300 }}\n      dropdownStyle={{ minWidth: 400 }}\n      size=\"large\"\n    />\n  );\n}\n```\n\n## TypeScript Support\n\nThe component provides comprehensive TypeScript support with automatic type inference:\n\n```tsx\n// 1. Define your data structure\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  role: \"admin\" | \"user\" | \"guest\";\n  avatar?: string;\n}\n\n// 2. Create type-safe adapter\nconst userAdapter = makeAdapter<User>()({\n  list: async (args) => {\n    // args is automatically typed as ListArgs\n    // Your IDE will provide full autocomplete\n    const { page, pageSize, search, params } = args;\n\n    // Return type is validated against ListResponse<User>\n    return {\n      items: [], // User[]\n      total: 0, // number\n    };\n  },\n  getLabel: (user) => {\n    // user parameter is typed as User\n    return user.name; // Full autocomplete available\n  },\n  getValue: (user) => user.id, // Typed as User\n});\n\n// 3. Component usage with full type safety\nfunction TypeSafeUserSelect() {\n  // Single selection: string | undefined\n  const [singleUser, setSingleUser] = useState<string>();\n\n  // Multiple selection: string[]\n  const [multipleUsers, setMultipleUsers] = useState<string[]>([]);\n\n  return (\n    <>\n      {/* Single selection - value and onChange are typed correctly */}\n      <PaginatedSelect\n        dataAdapter={userAdapter}\n        value={singleUser}\n        onChange={(value) => setSingleUser(value)} // value is string\n      />\n\n      {/* Multiple selection - type is inferred from multiple prop */}\n      <PaginatedSelect\n        dataAdapter={userAdapter}\n        multiple\n        value={multipleUsers}\n        onChange={(values) => setMultipleUsers(values)} // values is string[]\n      />\n    </>\n  );\n}\n```\n\n### Advanced Type Features\n\n```tsx\n// Generic constraint for specific data types\nfunction createUserAdapter<T extends { id: string; name: string }>() {\n  return makeAdapter<T>()({\n    // Full type safety with constraints\n    getValue: (item) => item.id,\n    getLabel: (item) => item.name,\n    // ... other methods\n  });\n}\n\n// Utility types for complex scenarios\ntype SelectValue<T extends boolean> = T extends true ? string[] : string;\ntype SelectHandler<T extends boolean> = (value: SelectValue<T>) => void;\n```\n\n## Performance Optimization\n\n### Best Practices\n\n1. **Optimize `pageSize`** - Balance between UX and network efficiency:\n\n   ```tsx\n   // Good for most cases\n   <PaginatedSelect pageSize={20} />\n\n   // For mobile or slow networks\n   <PaginatedSelect pageSize={10} />\n\n   // For desktop with fast networks\n   <PaginatedSelect pageSize={50} />\n   ```\n\n2. **Use `dependencyKey` for data invalidation**:\n\n   ```tsx\n   <PaginatedSelect\n     dataAdapter={userAdapter}\n     dependencyKey={[currentTenant.id, selectedDepartment]}\n     // Data reloads automatically when dependencies change\n   />\n   ```\n\n3. **Implement efficient bulk fetching**:\n\n   ```tsx\n   const optimizedAdapter = makeAdapter<User>()({\n     // Bulk fetch for better performance\n     getByIds: async (ids) => {\n       const response = await fetch(\"/api/users/bulk\", {\n         method: \"POST\",\n         body: JSON.stringify({ ids }),\n       });\n       return response.json();\n     },\n     // Single fetch fallback\n     getById: async (id) => {\n       const response = await fetch(`/api/users/${id}`);\n       return response.json();\n     },\n   });\n   ```\n\n4. **Memoize adapters to prevent unnecessary re-renders**:\n\n   ```tsx\n   function UserSelect({ departmentId }: { departmentId: string }) {\n     const userAdapter = useMemo(\n       () =>\n         makeAdapter<User>()({\n           list: async (args) => {\n             // Include departmentId in the request\n             return fetchUsers({ ...args, departmentId });\n           },\n           getLabel: (user) => user.name,\n           getValue: (user) => user.id,\n         }),\n       [departmentId] // Only recreate when departmentId changes\n     );\n\n     return <PaginatedSelect dataAdapter={userAdapter} />;\n   }\n   ```\n\n### Query Configuration\n\n```tsx\n// Configure TanStack Query for optimal performance\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      staleTime: 5 * 60 * 1000, // 5 minutes - reduce API calls\n      cacheTime: 10 * 60 * 1000, // 10 minutes - keep in memory\n      refetchOnWindowFocus: false, // Prevent unnecessary refetches\n      retry: 2, // Retry failed requests\n    },\n  },\n});\n```\n\n## Troubleshooting\n\n### Common Issues\n\n1. **\"Cannot find module\" errors**: Ensure peer dependencies are installed\n2. **Infinite loading**: Check your API response format matches `ListResponse<T>`\n3. **Selected values not showing**: Implement `getById` or `getByIds` methods\n4. **Performance issues**: Reduce `pageSize` or implement `getByIds` for bulk operations\n\n### Debug Mode\n\nEnable debug logging to troubleshoot issues:\n\n```tsx\n<PaginatedSelect\n  dataAdapter={userAdapter}\n  debug={process.env.NODE_ENV === \"development\"}\n  // Check browser console for detailed logs\n/>\n```\n\n## License\n\nMIT - see [LICENSE](LICENSE) file for details.\n","readmeFilename":"README.md"}