{"id":51820523,"url":"https://github.com/h-k-dev/angular-file-drop","last_synced_at":"2026-07-24T14:00:37.626Z","repository":{"id":364159620,"uuid":"1266653851","full_name":"h-k-dev/angular-file-drop","owner":"h-k-dev","description":"This is a 2 weekend thing","archived":false,"fork":false,"pushed_at":"2026-06-30T21:44:13.000Z","size":621,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-30T22:14:04.472Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://h-k-dev.github.io/angular-file-drop/angular-file-drop/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/h-k-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-11T20:36:25.000Z","updated_at":"2026-06-30T21:43:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/h-k-dev/angular-file-drop","commit_stats":null,"previous_names":["h-k-dev/angular-file-drop"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/h-k-dev/angular-file-drop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h-k-dev%2Fangular-file-drop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h-k-dev%2Fangular-file-drop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h-k-dev%2Fangular-file-drop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h-k-dev%2Fangular-file-drop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/h-k-dev","download_url":"https://codeload.github.com/h-k-dev/angular-file-drop/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h-k-dev%2Fangular-file-drop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35844578,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-24T02:00:07.870Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-22T05:00:25.535Z","updated_at":"2026-07-24T14:00:37.617Z","avatar_url":"https://github.com/h-k-dev.png","language":"TypeScript","funding_links":[],"categories":["Third Party Components"],"sub_categories":["File Upload"],"readme":"# Angular File Drop\n\n[![npm version](https://img.shields.io/npm/v/@h-k-dev/angular-file-drop.svg)](https://www.npmjs.com/package/@h-k-dev/angular-file-drop)\n[![license](https://img.shields.io/npm/l/@h-k-dev/angular-file-drop.svg)](./LICENSE)\n\nA lightweight, **signal-based** Angular directive for drag-and-drop file and folder uploads — with directory traversal, `accept`-type filtering, and a click-to-open file picker. No stylesheets, no XHR wrappers, no DOM mutation. Just the `File` objects.\n\n\u003e This is the workspace repository. The published package lives in [`projects/angular-file-drop`](./projects/angular-file-drop).\n\n## The Philosophy\n**[Try the live demo →](https://h-k-dev.github.io/angular-file-drop)**\n\nDropzone.js is great, but it often fights against modern Angular architecture by injecting its own CSS, mutating the DOM, and hijacking HTTP requests with its own XHR wrappers.\n\n`angular-file-drop` is the \"Angular-only\" alternative. It does a portion of what Dropzone does, but strictly the Angular way:\n\n- **Native directive** — binds to your existing elements using standard Angular syntax.\n- **Headless** — handles the messy HTML5 drag-and-drop events and hands you raw `File` objects.\n- **Zero network opinions** — you upload with Angular's own `HttpClient`, keeping interceptors and auth intact.\n- **Bring your own UI** — no forced stylesheets. Style your dropzone exactly how your app needs it.\n\n## Features\n\n- Drag-and-drop **and** click-to-open file picker on a single element\n- Recursive **folder/directory traversal** (modern File System Access API, with legacy `webkitGetAsEntry` and plain `FileList` fallbacks)\n- `accept`-style filtering by extension or MIME (`.png`, `image/*`, `application/pdf`, `*/*`)\n- Single- or multiple-file enforcement\n- Automatic hidden-file filtering (`.git`, `.DS_Store`, …)\n- Signal-based drag-over state for easy template styling\n- SSR-safe (guards all browser-only APIs)\n- A ready-made `FILE_TYPES` map of common `accept` strings\n\n## Installation\n\n```bash\nnpm install @h-k-dev/angular-file-drop\n```\n\nRequires Angular `17.3+` (standalone directives + signals).\n\n## Quick Start\n\nThe directive is standalone — import `AngularFileDrop` directly into your component.\n\n```ts\nimport { Component } from '@angular/core';\nimport { AngularFileDrop, FileDropEvent } from '@h-k-dev/angular-file-drop';\n\n@Component({\n  selector: 'app-uploader',\n  imports: [AngularFileDrop],\n  template: `\n    \u003cdiv\n      dropZone\n      [class.is-dragging]=\"zone.isDragOver()\"\n      (fileDrop)=\"onDrop($event)\"\n      #zone=\"dropZone\"\n    \u003e\n      Drag files here, or click to browse\n    \u003c/div\u003e\n  `,\n})\nexport class UploaderComponent {\n  onDrop(event: FileDropEvent) {\n    for (const { file, relativePath } of event.files) {\n      console.log(relativePath, file.size);\n    }\n  }\n}\n```\n\n```css\n.is-dragging {\n  outline: 2px dashed #4f46e5;\n  background: #eef2ff;\n}\n```\n\n## Examples\n\n### Restrict accepted types\n\nPass any `accept`-style string. The `FILE_TYPES` map provides ready-made values.\n\n```ts\nimport { FILE_TYPES } from '@h-k-dev/angular-file-drop';\n\n@Component({\n  imports: [AngularFileDrop],\n  template: `\n    \u003cdiv dropZone [acceptedFiles]=\"accept\" (fileDrop)=\"onDrop($event)\"\u003e\n      Images and PDFs only\n    \u003c/div\u003e\n  `,\n})\nexport class Component {\n  accept = `${FILE_TYPES.ANY_IMAGE},${FILE_TYPES.PDF}`; // \"image/*,.pdf,application/pdf\"\n}\n```\n\nYou can also write the string by hand: `[acceptedFiles]=\"'.png,.jpg,image/*'\"`.\n\n### Single file only\n\n```html\n\u003cdiv dropZone [multiple]=\"false\" (fileDrop)=\"onDrop($event)\"\u003e\n  Drop a single file\n\u003c/div\u003e\n```\n\n### Drop a whole folder\n\n`directory` (on by default) recursively walks dropped folders. Each `DroppedFile` carries a `relativePath` that preserves the folder structure.\n\n```ts\ntemplate: `\u003cdiv dropZone [directory]=\"true\" (fileDrop)=\"onDrop($event)\"\u003eDrop a folder\u003c/div\u003e`;\n\nonDrop(event: FileDropEvent) {\n  // e.g. \"photos/2026/holiday/IMG_001.jpg\"\n  event.files.forEach((f) =\u003e console.log(f.relativePath));\n}\n```\n\n### Open a folder picker on click\n\nSet `directoryPicker` to make the click-to-open dialog a folder chooser instead of a file chooser.\n\n```html\n\u003cdiv dropZone [directoryPicker]=\"true\"\u003eClick to choose a folder\u003c/div\u003e\n```\n\n### React to drag events and style the zone\n\nThe `isDragOver` signal flips while a valid file drag is over the element. Grab the directive instance via the `dropZone` export.\n\n```html\n\u003cdiv\n  dropZone\n  #zone=\"dropZone\"\n  [class.active]=\"zone.isDragOver()\"\n  (dragEnter)=\"onEnter($event)\"\n  (dragLeave)=\"onLeave($event)\"\n\u003e\n  {{ zone.isDragOver() ? 'Release to upload' : 'Drag files here' }}\n\u003c/div\u003e\n```\n\n### Manual activation (custom button)\n\nDisable the built-in click handling with `isManualActivation` and open the picker yourself from a child control.\n\n```ts\n@Component({\n  imports: [AngularFileDrop],\n  template: `\n    \u003cdiv dropZone #zone=\"dropZone\" [isManualActivation]=\"true\" (fileDrop)=\"onDrop($event)\"\u003e\n      \u003cp\u003eDrag files here\u003c/p\u003e\n      \u003cbutton type=\"button\" (click)=\"zone.openFilePicker($event)\"\u003eBrowse files\u003c/button\u003e\n      \u003cbutton type=\"button\" (click)=\"zone.openDirectoryPicker($event)\"\u003eBrowse folder\u003c/button\u003e\n    \u003c/div\u003e\n  `,\n})\nexport class Component {\n  onDrop(event: FileDropEvent) {\n    /* ... */\n  }\n}\n```\n\n### Disable the zone\n\n```html\n\u003cdiv dropZone [disabled]=\"isUploading\" (fileDrop)=\"onDrop($event)\"\u003e…\u003c/div\u003e\n```\n\n### Upload with HttpClient\n\nThe directive stays out of your network layer — wire it up however you like.\n\n```ts\nonDrop(event: FileDropEvent) {\n  const body = new FormData();\n  event.files.forEach((f) =\u003e body.append('files', f.file, f.relativePath));\n  this.http.post('/api/upload', body).subscribe();\n}\n```\n\n## API Reference\n\n### Inputs\n\n| Input               | Type      | Default | Description                                                                                 |\n| ------------------- | --------- | ------- | ------------------------------------------------------------------------------------------- |\n| `multiple`          | `boolean` | `true`  | Allow more than one file. When `false`, only the first file is emitted.                     |\n| `directory`         | `boolean` | `true`  | Recursively traverse dropped folders.                                                       |\n| `directoryPicker`   | `boolean` | `false` | Make the click-to-open dialog a **folder** picker (`webkitdirectory`) rather than files.    |\n| `acceptedFiles`     | `string`  | `''`    | `accept`-style filter, e.g. `.png,image/*,application/pdf`. Empty accepts everything.       |\n| `ignoreHiddenFiles` | `boolean` | `true`  | Drop dotfiles and files inside dot-folders (`.git`, `.DS_Store`, …).                         |\n| `clickable`         | `boolean` | `true`  | Open the file picker when the host element is clicked or activated via keyboard.            |\n| `disabled`          | `boolean` | `false` | Ignore all drops, clicks, and keyboard activation.                                          |\n| `isManualActivation`| `boolean` | `false` | Disable built-in click/keyboard activation so you can call the `open*` methods yourself.    |\n\n### Outputs\n\n| Output      | Payload          | Description                                              |\n| ----------- | ---------------- | ------------------------------------------------------- |\n| `fileDrop`  | `FileDropEvent`  | Emitted after files are dropped or chosen and filtered. |\n| `dragEnter` | `DragEvent`      | A valid file drag entered the element.                  |\n| `dragOver`  | `DragEvent`      | A valid file drag is moving over the element.           |\n| `dragLeave` | `DragEvent`      | A valid file drag left the element.                     |\n\n### Public members\n\n| Member                          | Description                                                        |\n| ------------------------------- | ------------------------------------------------------------------ |\n| `isDragOver: Signal\u003cboolean\u003e`   | `true` while a valid file drag is over the element.                |\n| `openPicker(event?, options?)`  | Open the hidden file input. `options.directory` toggles folder mode. |\n| `openFilePicker(event?)`        | Open a file picker.                                                |\n| `openDirectoryPicker(event?)`   | Open a folder picker.                                              |\n\nAccess these in templates via the `dropZone` export: `#zone=\"dropZone\"`.\n\n### Types\n\n```ts\ninterface DroppedFile {\n  file: File;\n  relativePath: string; // preserves folder structure, e.g. \"docs/report.pdf\"\n}\n\ninterface FileDropEvent {\n  files: DroppedFile[];\n}\n```\n\n### Exported utilities\n\nThe directive's pure helpers are exported for advanced use and testing: `containsFiles`, `setDropEffect`, `isHiddenPath`, `filterHiddenFiles`, `isFileAccepted`, `filterAcceptedFiles`, `enforceMultiple`, `toDroppedFiles`, `readDroppedFiles`, `walkHandles`, `walkEntries`, `createHiddenFileInput`, and the `FILE_TYPES` map.\n\n## Development\n\n```bash\nnpm install\nnpm test          # run the vitest unit suite\nnpm run test:ci   # single run (CI)\nnpm run build     # build the library with ng-packagr\n```\n\n## Browser Support\n\nWorks in all modern browsers. Folder traversal uses the File System Access API where available and falls back to `webkitGetAsEntry` and then a plain `FileList`. Server-side rendering is safe — all browser-only APIs are guarded.\n\n## License\n\nMIT © h-k-dev\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fh-k-dev%2Fangular-file-drop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fh-k-dev%2Fangular-file-drop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fh-k-dev%2Fangular-file-drop/lists"}