{"_id":"@alsocoder/apna-table","_rev":"3-c699bf9ba1768a32f81edb01f044bfc6","name":"@alsocoder/apna-table","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@alsocoder/apna-table","version":"0.1.0","keywords":["react","react-component","table","data-table","datagrid","pagination","sorting","filtering","server-side","client-side","bulk-actions","ui-component","css-variables","accessible","a11y","typescript","apna-table","alsocoder"],"author":{"url":"https://alsocoder.com","name":"Also Coder"},"license":"MIT","_id":"@alsocoder/apna-table@0.1.0","maintainers":[{"name":"alsocoder-org","email":"alsocoder@gmail.com"}],"homepage":"https://github.com/alsocoders/apna-table#readme","bugs":{"url":"https://github.com/alsocoders/apna-table/issues"},"dist":{"shasum":"8659e18d1a01c9999fdb6ec73249e84582264414","tarball":"https://registry.npmjs.org/@alsocoder/apna-table/-/apna-table-0.1.0.tgz","fileCount":10,"integrity":"sha512-5MLymqIVd7DMLxiBQy1tAcNTJIsUlu+/UdkOR07TtkIkRwubP/CRxzfas1Wwz2QSS9MWhMl1stC4tJFFJsimGw==","signatures":[{"sig":"MEUCIDVAZHkmKrktVRiHDYYtKVArl2D7lHgWYuIhp3QyIYN2AiEA4xufSW0Wr4SV7RcKDLZuylyCq0GwWiXTPxP4+KxSbPs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":377464},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./styles.css":"./dist/styles.css"},"scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit","build:deps":"npm run build --prefix ../ApnaInput && npm run build --prefix ../ApnaSelect && npm run build --prefix ../ApnaDatePicker","build:local":"npm run build:deps && tsup","prepublishOnly":"npm run build"},"_npmUser":{"name":"alsocoder-org","email":"alsocoder@gmail.com"},"repository":{"url":"git+https://github.com/alsocoders/apna-table.git","type":"git"},"_npmVersion":"12.0.1","description":"A flexible React data table with client/server modes, sorting, filters, pagination, row selection, and bulk actions.","directories":{},"sideEffects":["*.css"],"_nodeVersion":"24.16.0","browserslist":["Chrome >= 90","Firefox >= 90","Safari >= 14","Edge >= 90","iOS >= 14","Samsung >= 15","not dead"],"dependencies":{"clsx":"^2.1.1","@alsocoder/apna-input":"^0.1.0","@alsocoder/apna-select":"^0.2.2","@radix-ui/react-checkbox":"^1.3.2","@alsocoder/apna-date-picker":"^0.1.2","@radix-ui/react-dropdown-menu":"^2.1.15"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","react":"^19.1.0","react-dom":"^19.1.0","typescript":"^5.9.2","@types/react":"^19.1.8","@types/react-dom":"^19.1.6"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/apna-table_0.1.0_1785728021449_0.36877802829093165","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@alsocoder/apna-table","version":"0.1.1","keywords":["react","react-component","table","data-table","datagrid","pagination","sorting","filtering","server-side","client-side","bulk-actions","ui-component","css-variables","accessible","a11y","typescript","apna-table","alsocoder"],"author":{"url":"https://alsocoder.com","name":"Also Coder"},"license":"MIT","_id":"@alsocoder/apna-table@0.1.1","maintainers":[{"name":"alsocoder-org","email":"alsocoder@gmail.com"}],"homepage":"https://github.com/alsocoders/apna-table#readme","bugs":{"url":"https://github.com/alsocoders/apna-table/issues"},"dist":{"shasum":"f7c258d7bfd20116aa20d723f2394cddd1b75e3a","tarball":"https://registry.npmjs.org/@alsocoder/apna-table/-/apna-table-0.1.1.tgz","fileCount":10,"integrity":"sha512-K74dMBA7lQ0dbKnYNImK6kwq9vVj3wAiQMaofnPnJPJ5sr0PSnYOCNBBIs66zT0hOftQdFUW4dKy1HPHzlZHnA==","signatures":[{"sig":"MEUCIQD5jl/2mt0DtwTN0mHu6Hoq2JFlEXQUKsna29kT4KwRWQIgAs6Ty0UT/NotwSFzctXUH7MwWEOQ9o0A/ageVgGgv5w=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":377904},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./styles.css":"./dist/styles.css"},"gitHead":"22d0f830f317dbed476136e3fe1f5ac5bcab7faa","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit","build:deps":"npm run build --prefix ../ApnaInput && npm run build --prefix ../ApnaSelect && npm run build --prefix ../ApnaDatePicker","build:local":"npm run build:deps && tsup","prepublishOnly":"npm run build"},"_npmUser":{"name":"alsocoder-org","email":"alsocoder@gmail.com"},"repository":{"url":"git+https://github.com/alsocoders/apna-table.git","type":"git"},"_npmVersion":"12.0.1","description":"A flexible React data table with client/server modes, sorting, filters, pagination, row selection, and bulk actions.","directories":{},"sideEffects":["*.css"],"_nodeVersion":"24.16.0","browserslist":["Chrome >= 90","Firefox >= 90","Safari >= 14","Edge >= 90","iOS >= 14","Samsung >= 15","not dead"],"dependencies":{"clsx":"^2.1.1","@alsocoder/apna-input":"^0.1.3","@alsocoder/apna-select":"^0.2.2","@radix-ui/react-checkbox":"^1.3.2","@alsocoder/apna-date-picker":"^0.1.2","@radix-ui/react-dropdown-menu":"^2.1.15"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","react":"^19.1.0","react-dom":"^19.1.0","typescript":"^5.9.2","@types/react":"^19.1.8","@types/react-dom":"^19.1.6"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/apna-table_0.1.1_1785804490103_0.7449503756408016","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@alsocoder/apna-table","version":"0.1.2","description":"A flexible React data table with client/server modes, sorting, filters, pagination, row selection, and bulk actions.","license":"MIT","author":{"name":"Also Coder","url":"https://alsocoder.com"},"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./styles.css":"./dist/styles.css"},"sideEffects":["*.css"],"scripts":{"build:deps":"npm run build --prefix ../ApnaInput && npm run build --prefix ../ApnaSelect && npm run build --prefix ../ApnaDatePicker","build:local":"npm run build:deps && tsup","build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"keywords":["react","react-component","table","data-table","datagrid","pagination","sorting","filtering","server-side","client-side","bulk-actions","ui-component","css-variables","accessible","a11y","typescript","apna-table","alsocoder"],"browserslist":["Chrome >= 90","Firefox >= 90","Safari >= 14","Edge >= 90","iOS >= 14","Samsung >= 15","not dead"],"repository":{"type":"git","url":"git+https://github.com/alsocoders/apna-table.git"},"bugs":{"url":"https://github.com/alsocoders/apna-table/issues"},"homepage":"https://github.com/alsocoders/apna-table#readme","publishConfig":{"access":"public"},"engines":{"node":">=18"},"peerDependencies":{"react":">=18","react-dom":">=18"},"dependencies":{"@alsocoder/apna-date-picker":"^0.1.2","@alsocoder/apna-input":"^0.1.3","@alsocoder/apna-select":"file:../ApnaSelect","@radix-ui/react-checkbox":"^1.3.2","@radix-ui/react-dropdown-menu":"^2.1.15","clsx":"^2.1.1"},"devDependencies":{"@types/react":"^19.1.8","@types/react-dom":"^19.1.6","react":"^19.1.0","react-dom":"^19.1.0","tsup":"^8.5.0","typescript":"^5.9.2"},"gitHead":"a131a41be4ab0df4c609378eded566f632f496aa","_id":"@alsocoder/apna-table@0.1.2","_nodeVersion":"24.16.0","_npmVersion":"12.0.1","dist":{"integrity":"sha512-YAEtXMQmSgVbSO+ALiEL9T66xTPSSy0E8JQO+nUKEOX+U7T03SdTAdj87uaHLbAEryrfLak0NX0Rh7/JqZxLsw==","shasum":"33ac7bfd5f4b26a28bb0757c5452113cb4caf9a5","tarball":"https://registry.npmjs.org/@alsocoder/apna-table/-/apna-table-0.1.2.tgz","fileCount":11,"unpackedSize":383124,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDlsgj0hgbGaamuONNGuM43wK5gpJdRuRqsTR8UYewUtAIgZ2GGtts65GQU25LadAS4BLJjPCW8RlAmWWdTjI1kNEg="}]},"_npmUser":{"name":"alsocoder-org","email":"alsocoder@gmail.com"},"directories":{},"maintainers":[{"name":"alsocoder-org","email":"alsocoder@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/apna-table_0.1.2_1786262389411_0.7050029505909032"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-03T03:33:41.264Z","modified":"2026-08-09T07:59:49.734Z","0.1.0":"2026-08-03T03:33:41.589Z","0.1.1":"2026-08-04T00:48:10.255Z","0.1.2":"2026-08-09T07:59:49.579Z"},"bugs":{"url":"https://github.com/alsocoders/apna-table/issues"},"author":{"name":"Also Coder","url":"https://alsocoder.com"},"license":"MIT","homepage":"https://github.com/alsocoders/apna-table#readme","keywords":["react","react-component","table","data-table","datagrid","pagination","sorting","filtering","server-side","client-side","bulk-actions","ui-component","css-variables","accessible","a11y","typescript","apna-table","alsocoder"],"repository":{"type":"git","url":"git+https://github.com/alsocoders/apna-table.git"},"description":"A flexible React data table with client/server modes, sorting, filters, pagination, row selection, and bulk actions.","maintainers":[{"name":"alsocoder-org","email":"alsocoder@gmail.com"}],"readme":"# @alsocoder/apna-table\r\n\r\nA flexible React data table with **client** and **server** modes, multi-column sorting, filters (ApnaInput / ApnaSelect / ApnaDatePicker), pagination, row selection, bulk actions, skeleton loading, and declarative row actions.\r\n\r\n## Install\r\n\r\n```bash\r\nnpm install @alsocoder/apna-table\r\n```\r\n\r\nFilter/pagination UI internally uses ApnaInput, ApnaSelect, and ApnaDatePicker — you do **not** need to install or import them separately.\r\n\r\n## CSS import\r\n\r\n```ts\r\nimport \"@alsocoder/apna-table/styles.css\"\r\n```\r\n\r\nThis single stylesheet includes table styles plus the bundled filter/pagination component styles.\r\n\r\n## Quick start\r\n\r\n```tsx\r\nimport { ApnaTable, type ApnaTableColumn } from \"@alsocoder/apna-table\"\r\nimport \"@alsocoder/apna-table/styles.css\"\r\n\r\ntype User = { id: string; name: string; email: string }\r\n\r\nconst columns: ApnaTableColumn<User>[] = [\r\n  { id: \"name\", header: \"Name\", accessorKey: \"name\", sortable: true },\r\n  { id: \"email\", header: \"Email\", accessorKey: \"email\", sortable: true },\r\n]\r\n\r\n<ApnaTable\r\n  mode=\"client\"\r\n  title=\"Users\"\r\n  description=\"Manage users\"\r\n  columns={columns}\r\n  rowKey=\"id\"\r\n  data={users}\r\n  showSerialNumber\r\n  selectable\r\n  filters={[\r\n    {\r\n      key: \"search\",\r\n      type: \"input\",\r\n      placeholder: \"Search…\",\r\n      value: search,\r\n      onValueChange: setSearch,\r\n    },\r\n  ]}\r\n  actions={{\r\n    variant: \"dropdown\",\r\n    position: \"end\",\r\n    items: [\r\n      { key: \"view\", label: \"View\", icon: <EyeIcon />, onAction: (row) => view(row) },\r\n    ],\r\n  }}\r\n  headerActions={<button>Add user</button>}\r\n/>\r\n```\r\n\r\n## Modes\r\n\r\n### Client mode\r\n\r\nPass `data` once. The table handles local pagination and sorting.\r\n\r\n```tsx\r\n<ApnaTable mode=\"client\" data={items} columns={columns} rowKey=\"id\" />\r\n```\r\n\r\n### Server mode\r\n\r\nPass `fetchData`. The table sends `page`, `pageSize`, `sort`, and debounced `filters`.\r\n\r\n```tsx\r\n<ApnaTable\r\n  mode=\"server\"\r\n  fetchData={async ({ page, pageSize, sort, filters, signal }) => {\r\n    const res = await listUsers({ page, pageSize, sort, ...filters }, { signal })\r\n    return { items: res.items, total: res.total }\r\n  }}\r\n  columns={columns}\r\n  rowKey=\"id\"\r\n/>\r\n```\r\n\r\n## Columns\r\n\r\n```ts\r\ntype ApnaTableColumn<T> = {\r\n  id: string\r\n  header: ReactNode\r\n  accessorKey?: keyof T\r\n  cell?: (ctx: { row: T; index: number; serial: number }) => ReactNode\r\n  sortable?: boolean\r\n  sortKey?: string\r\n  hidden?: boolean | \"sm\" | \"md\" | \"lg\"\r\n  align?: \"left\" | \"center\" | \"right\"\r\n}\r\n```\r\n\r\nColumn order:\r\n\r\n| Feature | Position |\r\n|---------|----------|\r\n| S.No | First (optional) |\r\n| Checkbox | After S.No (optional) |\r\n| Actions (`position: \"start\"`) | After checkbox |\r\n| User columns | Middle |\r\n| Actions (`position: \"end\"`) | Last (default) |\r\n\r\n## Filters\r\n\r\nSupported filter types:\r\n\r\n| type | Component |\r\n|------|-----------|\r\n| `input` | ApnaInput |\r\n| `select` | ApnaSelect |\r\n| `date` | ApnaDatePicker |\r\n| `dateRange` | ApnaDateRangePicker |\r\n\r\nFirst filter stays visible. Extra filters open via **Show filters**.\r\n\r\n## Row actions\r\n\r\n### Dropdown (ellipsis menu)\r\n\r\n```tsx\r\nactions={{\r\n  variant: \"dropdown\",\r\n  position: \"end\",\r\n  items: [\r\n    { key: \"edit\", label: \"Edit\", icon: <EditIcon />, onAction: editRow },\r\n    { key: \"delete\", label: \"Delete\", icon: <TrashIcon />, destructive: true, onAction: deleteRow },\r\n  ],\r\n}}\r\n```\r\n\r\n### Inline (side-by-side buttons)\r\n\r\n```tsx\r\nactions={{\r\n  variant: \"inline\",\r\n  position: \"start\",\r\n  items: [\r\n    { key: \"edit\", label: \"Edit\", icon: <EditIcon />, iconOnly: true, onAction: editRow },\r\n  ],\r\n}}\r\n```\r\n\r\n- `show: false` hides the actions column\r\n- Each action supports `label` + optional `icon` (consumer-provided ReactNode)\r\n\r\n## Bulk actions\r\n\r\nShown in the toolbar when rows are selected:\r\n\r\n```tsx\r\nbulkActions={[\r\n  {\r\n    key: \"archive\",\r\n    label: \"Archive selected\",\r\n    icon: <ArchiveIcon />,\r\n    onAction: (rows, keys) => archiveMany(rows),\r\n  },\r\n]}\r\n```\r\n\r\n## Sorting\r\n\r\n```ts\r\ntype ApnaTableSort = { field: string; direction: \"asc\" | \"desc\" }\r\n```\r\n\r\n- Click a sortable header to cycle `asc → desc → none`\r\n- `multiSort` enables multiple sort columns\r\n- Server mode sends `sort=createdAt:desc,title:asc`\r\n\r\n## Skeleton loading\r\n\r\n- Server mode: skeleton shows while `fetchData` is in flight (after filter debounce)\r\n- Client mode: brief skeleton while filters/sort/page apply\r\n- `skeletonRows` controls placeholder row count (defaults to `pageSize`)\r\n- Pagination is disabled while `loading` is true\r\n\r\n## Backend API contract\r\n\r\n### Request query params\r\n\r\n| Param | Example |\r\n|-------|---------|\r\n| `page` | `1` |\r\n| `pageSize` | `25` |\r\n| `sort` | `createdAt:desc,title:asc` |\r\n| filter keys | `search=foo&status=active&dateFrom=2026-01-01&dateTo=2026-01-31` |\r\n\r\nDate range filters are sent as `{key}From` and `{key}To`.\r\n\r\n### Response shape\r\n\r\n```json\r\n{\r\n  \"success\": true,\r\n  \"data\": [],\r\n  \"meta\": { \"page\": 1, \"pageSize\": 25, \"total\": 142, \"totalPages\": 6 }\r\n}\r\n```\r\n\r\n### Node/Express + MongoDB example\r\n\r\n```ts\r\nfunction parseSort(sort?: string): Record<string, 1 | -1> {\r\n  if (!sort) return { createdAt: -1 }\r\n  return Object.fromEntries(\r\n    sort.split(\",\").map((part) => {\r\n      const [field, dir] = part.split(\":\")\r\n      return [field, dir === \"asc\" ? 1 : -1]\r\n    })\r\n  )\r\n}\r\n\r\nexport async function listUsers({ page, pageSize, search, status, sort }) {\r\n  const filter: Record<string, unknown> = {}\r\n  if (search) filter.$or = [{ name: new RegExp(search, \"i\") }, { email: new RegExp(search, \"i\") }]\r\n  if (status && status !== \"all\") filter.status = status\r\n\r\n  const skip = (page - 1) * pageSize\r\n  const [total, items] = await Promise.all([\r\n    User.countDocuments(filter),\r\n    User.find(filter).sort(parseSort(sort)).skip(skip).limit(pageSize),\r\n  ])\r\n\r\n  return { items, total, page, pageSize }\r\n}\r\n```\r\n\r\n## Hooks\r\n\r\n```ts\r\nimport { useApnaTable, useApnaTablePagination, buildSortQuery } from \"@alsocoder/apna-table\"\r\n\r\nconst table = useApnaTable({ mode: \"server\", fetchData, columns, rowKey: \"id\" })\r\n// rows, loading, pagination, sort, selection, refetch\r\n```\r\n\r\n## Customization\r\n\r\n- Vanilla CSS with `--apna-table-*` variables\r\n- Falls back to shadcn/Tailwind tokens (`--border`, `--foreground`, `--primary`, etc.)\r\n- `classNames` object + `className` / `tableClassName` / `headerClassName` shortcuts\r\n- `icons` prop to override built-in SVG icons\r\n\r\n## Playground\r\n\r\n```bash\r\ncd playground\r\nnpm install\r\nnpm run dev\r\n```\r\n\r\n## Mahima migration\r\n\r\n**Before:**\r\n\r\n```tsx\r\n<DataTable title=\"Services\" filters={filters} pagination={pagination}>\r\n  <Table>...</Table>\r\n</DataTable>\r\n```\r\n\r\n**After:**\r\n\r\n```tsx\r\n<ApnaTable\r\n  mode=\"server\"\r\n  title=\"Services\"\r\n  columns={columns}\r\n  fetchData={listServices}\r\n  filters={filters}\r\n  showSerialNumber\r\n  actions={{ variant: \"dropdown\", items: [...] }}\r\n  headerActions={<Button>Add service</Button>}\r\n/>\r\n```\r\n\r\n## License\r\n\r\nMIT\r\n","readmeFilename":"README.md"}