{"_id":"@alsocoder/apna-media-library","_rev":"3-7e22ba16490f6b23dd052449392d9651","name":"@alsocoder/apna-media-library","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@alsocoder/apna-media-library","version":"0.1.0","keywords":["react","react-component","media-library","file-upload","image-picker","media-picker","drag-and-drop","ui-component","css-variables","typescript","apna-media-library","alsocoder"],"author":{"url":"https://alsocoder.com","name":"Also Coder"},"license":"MIT","_id":"@alsocoder/apna-media-library@0.1.0","maintainers":[{"name":"alsocoder-org","email":"alsocoder@gmail.com"}],"homepage":"https://github.com/alsocoders/apna-media-library#readme","bugs":{"url":"https://github.com/alsocoders/apna-media-library/issues"},"dist":{"shasum":"cffebfe11b4f4f67d2b321cb72435c662c66d52c","tarball":"https://registry.npmjs.org/@alsocoder/apna-media-library/-/apna-media-library-0.1.0.tgz","fileCount":9,"integrity":"sha512-h7gcDHKjuhasb2i2rcQxl0UChM2Tk/A/IeXKyG1zqdjB+X8AB3/KXGtLSdEXlh7vdoX71Sml6c9pOx4WiwJeLA==","signatures":[{"sig":"MEQCIG0CErzN45ViO0Eqn1qnU6bTPKaJPXDwCKiZJprI4AWNAiAZkleCkK516uqKnirDGzsArj9WpmRKGzhbRV/Pgn90MA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":510635},"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 ../ApnaModel && npm run build --prefix ../ApnaToast","build:local":"npm run build:deps && npm run build","dev:playground":"npm run dev --prefix playground","prepublishOnly":"npm run build"},"_npmUser":{"name":"alsocoder-org","email":"alsocoder@gmail.com"},"repository":{"url":"git+https://github.com/alsocoders/apna-media-library.git","type":"git"},"_npmVersion":"12.0.1","description":"React media library with provider, global drop upload, picker fields, and modal-based browsing.","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-modal":"^0.1.1","@alsocoder/apna-toast":"^0.1.0","@radix-ui/react-dismissable-layer":"^1.1.11"},"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"},"optionalDependencies":{"pdfjs-dist":"^4.10.38"},"_npmOperationalInternal":{"tmp":"tmp/apna-media-library_0.1.0_1785765817082_0.9979758859340948","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@alsocoder/apna-media-library","version":"0.1.1","keywords":["react","react-component","media-library","file-upload","image-picker","media-picker","drag-and-drop","ui-component","css-variables","typescript","apna-media-library","alsocoder"],"author":{"url":"https://alsocoder.com","name":"Also Coder"},"license":"MIT","_id":"@alsocoder/apna-media-library@0.1.1","maintainers":[{"name":"alsocoder-org","email":"alsocoder@gmail.com"}],"homepage":"https://github.com/alsocoders/apna-media-library#readme","bugs":{"url":"https://github.com/alsocoders/apna-media-library/issues"},"dist":{"shasum":"1956ff5adf92ed90d218d5bbfa5db5875c9d65f3","tarball":"https://registry.npmjs.org/@alsocoder/apna-media-library/-/apna-media-library-0.1.1.tgz","fileCount":10,"integrity":"sha512-ELa5wXa0sdvpubhTkbRWzqpr+6906LqLoqTsRTEdZE9YJiDl1RdO6k6fYFojz8v+/h5cYo674wVayikEQaSo7g==","signatures":[{"sig":"MEMCH3/CcfDqwA9ExzGMdu15DtVaFb4rFDBwjlTC64uw4ekCIFuNY41hBrOULBqbNE5VIPUft2FY6YkQsakdIvx36ln/","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":510880},"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 ../ApnaModal && npm run build --prefix ../ApnaToast","build:local":"npm run build:deps && npm run build","dev:playground":"npm run dev --prefix playground","prepublishOnly":"npm run build"},"_npmUser":{"name":"alsocoder-org","email":"alsocoder@gmail.com"},"repository":{"url":"git+https://github.com/alsocoders/apna-media-library.git","type":"git"},"_npmVersion":"12.0.1","description":"React media library with provider, global drop upload, picker fields, and modal-based browsing.","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-modal":"^0.1.1","@alsocoder/apna-toast":"^0.1.0","@radix-ui/react-dismissable-layer":"^1.1.11"},"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"},"optionalDependencies":{"pdfjs-dist":"^4.10.38"},"_npmOperationalInternal":{"tmp":"tmp/apna-media-library_0.1.1_1785770577586_0.33259155420419484","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@alsocoder/apna-media-library","version":"0.2.0","description":"React media library with provider, global drop upload, picker fields, and modal-based browsing.","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 ../ApnaModal && npm run build --prefix ../ApnaToast && npm run build --prefix ../ApnaUpload","build:local":"npm run build:deps && npm run build","build":"tsup","dev":"tsup --watch","dev:playground":"npm run dev --prefix playground","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"keywords":["react","react-component","media-library","file-upload","image-picker","media-picker","drag-and-drop","ui-component","css-variables","typescript","apna-media-library","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-media-library.git"},"bugs":{"url":"https://github.com/alsocoders/apna-media-library/issues"},"homepage":"https://github.com/alsocoders/apna-media-library#readme","publishConfig":{"access":"public"},"engines":{"node":">=18"},"peerDependencies":{"react":">=18","react-dom":">=18"},"dependencies":{"@alsocoder/apna-modal":"^0.1.1","@alsocoder/apna-toast":"^0.1.0","@alsocoder/apna-upload":"^0.1.0","@radix-ui/react-dismissable-layer":"^1.1.11","clsx":"^2.1.1"},"optionalDependencies":{"pdfjs-dist":"^4.10.38"},"overrides":{"@types/react":"^19.1.8","@types/react-dom":"^19.1.6"},"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":"f575efa6c7e463c6f1a7a64e71ca90310d47343b","_id":"@alsocoder/apna-media-library@0.2.0","_nodeVersion":"24.16.0","_npmVersion":"12.0.1","dist":{"integrity":"sha512-IWJUWQLDcCHfTGcLHjhskMHxOSN7N/O6SxQTKpqRhkqNptjmx7r1qyq2LlgRc37ZfFgV4afClVp3zOJJyG5QcA==","shasum":"ff7b8cb7e9b1b3aa5784263f7531fa0deba72195","tarball":"https://registry.npmjs.org/@alsocoder/apna-media-library/-/apna-media-library-0.2.0.tgz","fileCount":10,"unpackedSize":499911,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGF/J+htjF8gVH4dpIWexTN2M9oSDyRBSzFgtnvmL0xaAiAzlFHQhcQV8oWTsap2s8EmlN5iQqu4Mgm2zFrU94+Fjw=="}]},"_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-media-library_0.2.0_1785814195466_0.0742260117822493"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-03T14:03:36.911Z","modified":"2026-08-04T03:29:55.799Z","0.1.0":"2026-08-03T14:03:37.270Z","0.1.1":"2026-08-03T15:22:57.752Z","0.2.0":"2026-08-04T03:29:55.617Z"},"bugs":{"url":"https://github.com/alsocoders/apna-media-library/issues"},"author":{"name":"Also Coder","url":"https://alsocoder.com"},"license":"MIT","homepage":"https://github.com/alsocoders/apna-media-library#readme","keywords":["react","react-component","media-library","file-upload","image-picker","media-picker","drag-and-drop","ui-component","css-variables","typescript","apna-media-library","alsocoder"],"repository":{"type":"git","url":"git+https://github.com/alsocoders/apna-media-library.git"},"description":"React media library with provider, global drop upload, picker fields, and modal-based browsing.","maintainers":[{"name":"alsocoder-org","email":"alsocoder@gmail.com"}],"readme":"# @alsocoder/apna-media-library\r\n\r\nReact media library with provider, global drag-and-drop upload, modal-based browsing, picker fields, and gallery support.\r\n\r\nBuilt for reuse across projects — internally bundles `@alsocoder/apna-modal`, `@alsocoder/apna-toast`, and upload UI via `@alsocoder/apna-upload` (ApnaTable pattern). You only install and import this package.\r\n\r\n## Install\r\n\r\n```bash\r\nnpm install @alsocoder/apna-media-library\r\n```\r\n\r\nPeer dependencies: `react` and `react-dom` (>= 18).\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\nOpens at `http://localhost:5178` with a mock in-memory adapter (no backend required).\r\n\r\nFrom package root:\r\n\r\n```bash\r\nnpm run dev:playground\r\n```\r\n\r\n## CSS import\r\n\r\n```ts\r\nimport \"@alsocoder/apna-media-library/styles.css\"\r\n```\r\n\r\nThis single stylesheet includes modal, toast, upload, and media library styles.\r\n\r\n## Quick start\r\n\r\n```tsx\r\nimport {\r\n  ApnaMediaProvider,\r\n  ApnaMediaPicker,\r\n  type ApnaMediaAdapter,\r\n} from \"@alsocoder/apna-media-library\"\r\nimport \"@alsocoder/apna-media-library/styles.css\"\r\n\r\nconst adapter: ApnaMediaAdapter = {\r\n  async listFolders({ include } = {}) {\r\n    const res = await fetch(`/api/media/folders?include=${include?.join(\",\") ?? \"\"}`)\r\n    const data = await res.json()\r\n    return data.items ?? []\r\n  },\r\n  async listMedia({ folder, page, limit }) {\r\n    const params = new URLSearchParams({ page: String(page), limit: String(limit) })\r\n    if (folder) params.set(\"folder\", folder)\r\n    const res = await fetch(`/api/media?${params}`)\r\n    const data = await res.json()\r\n    return { items: data.items ?? [], pagination: data.pagination }\r\n  },\r\n  async upload(file, folder, onProgress) {\r\n    const formData = new FormData()\r\n    formData.append(\"folder\", folder)\r\n    formData.append(\"file\", file)\r\n    const res = await fetch(\"/api/media/upload\", { method: \"POST\", body: formData })\r\n    onProgress?.(100)\r\n    return res.json()\r\n  },\r\n}\r\n\r\nexport function App() {\r\n  return (\r\n    <ApnaMediaProvider\r\n      adapter={adapter}\r\n      resolveUrl={(path) => `${import.meta.env.VITE_API_URL}${path}`}\r\n    >\r\n      <Form />\r\n    </ApnaMediaProvider>\r\n  )\r\n}\r\n\r\nfunction Form() {\r\n  const [url, setUrl] = useState(\"\")\r\n  return (\r\n    <ApnaMediaPicker\r\n      label=\"Cover image\"\r\n      value={url}\r\n      onChange={setUrl}\r\n      folder=\"blog\"\r\n    />\r\n  )\r\n}\r\n```\r\n\r\n## Provider\r\n\r\n```tsx\r\n<ApnaMediaProvider\r\n  adapter={myAdapter}\r\n  canBrowse={true}\r\n  canUpload={true}\r\n  defaultFolder=\"misc\"\r\n  enableGlobalDrop={true}\r\n  enableToaster={true}\r\n  enablePdfPreview={true}\r\n  resolveUrl={(path) => `${API_BASE}/uploads${path}`}\r\n  onNotify={(msg) => console.log(msg)}\r\n>\r\n  {children}\r\n</ApnaMediaProvider>\r\n```\r\n\r\n### Hook: `useApnaMedia()`\r\n\r\n```ts\r\nconst {\r\n  openLibrary,        // (opts) => Promise<ApnaMediaItem | null>\r\n  openLibraryForDrop, // (files) => void\r\n  openPreview,\r\n  closePreview,\r\n  uploadFile,\r\n  removeItem,\r\n  ensureLoaded,\r\n  listFolderMedia,\r\n  items,\r\n  folders,\r\n  isLibraryOpen,\r\n  canBrowse,\r\n  canUpload,\r\n} = useApnaMedia()\r\n```\r\n\r\n`openLibrary` is promise-based — no manual modal state needed:\r\n\r\n```tsx\r\nconst item = await openLibrary({ folder: \"blog\", currentUrl: value })\r\nif (item) setValue(item.url)\r\n```\r\n\r\n## Components\r\n\r\n| Export | Description |\r\n|--------|-------------|\r\n| `ApnaMediaProvider` | Context + library modal, preview, global drop |\r\n| `useApnaMedia` | Access provider API |\r\n| `ApnaMediaPicker` | Single file field (Browse/Upload → Change → Preview) |\r\n| `ApnaMediaImageField` | Picker preset for images |\r\n| `ApnaMediaGallery` | Multi-image array field |\r\n\r\n## ApnaEditor integration\r\n\r\n```tsx\r\nconst { openLibrary } = useApnaMedia()\r\n\r\n<ApnaEditor\r\n  imageUpload={{\r\n    pick: async () => {\r\n      const item = await openLibrary({ folder: \"blog\" })\r\n      return item ? { url: item.url, alt: item.name } : null\r\n    },\r\n  }}\r\n/>\r\n```\r\n\r\n## Adapter interface\r\n\r\n```ts\r\ntype ApnaMediaAdapter = {\r\n  listFolders: (opts?: { include?: string[] }) => Promise<ApnaMediaFolder[]>\r\n  listMedia: (opts: { folder?: string; page: number; limit: number }) => Promise<{\r\n    items: ApnaMediaItem[]\r\n    pagination: { page: number; limit: number; total: number; totalPages: number }\r\n  }>\r\n  upload: (file: File, folder: string, onProgress?: (pct: number) => void) => Promise<ApnaMediaItem>\r\n  delete?: (id: string) => Promise<void>\r\n}\r\n```\r\n\r\n## Features\r\n\r\n- **Global drop** — drag files anywhere on the page to open upload modal\r\n- **In-modal drop** — drop zone inside library modal\r\n- **Folder tree** — hierarchical folders with search\r\n- **Paginated grid** — infinite scroll (20 items per page)\r\n- **PDF thumbnails** — optional via `pdfjs-dist` dynamic import\r\n- **Toasts** — upload errors, copy path, etc. via bundled ApnaToast\r\n- **Z-index** — uses ApnaModal stack; works with nested Select/DatePicker in modals\r\n\r\n## Customization\r\n\r\nOverride CSS variables (`--apna-media-*`) or use `classNames` prop on provider/picker.\r\n\r\n```tsx\r\nimport { defaultClassNames, mergeClassNames } from \"@alsocoder/apna-media-library\"\r\n```\r\n\r\n## Notes\r\n\r\n- By default `ApnaMediaProvider` includes its own `ApnaModalProvider` — no extra modal setup needed.\r\n- If your app already uses `ApnaModalProvider`, nest media inside it and pass `useParentModalProvider`:\r\n\r\n```tsx\r\n<ApnaModalProvider>\r\n  <ApnaMediaProvider useParentModalProvider adapter={myAdapter}>\r\n    <App />\r\n  </ApnaMediaProvider>\r\n</ApnaModalProvider>\r\n```\r\n\r\n- Set `enableToaster={false}` if you manage toasts yourself.\r\n- Set `enablePdfPreview={false}` to skip PDF.js loading.\r\n","readmeFilename":"README.md"}