{"version":3,"file":"fg-file-grab.mjs","sources":["../../../projects/file-grab/src/lib/file-grab.component.ts","../../../projects/file-grab/src/fg-file-grab.ts"],"sourcesContent":["import { Component, EventEmitter, Input, Output } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\nconst DEFAULT_ACCEPT = [\n  'image/jpg', 'image/jpeg', 'image/png',\n  'application/pdf',\n  'application/vnd.ms-excel',\n  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n  'text/csv',\n  '.xls', '.xlsx', '.csv', '.pdf', '.jpg', '.jpeg', '.png'\n].join(',');\n\nexport type FileGrabItem = {\n  file: File;\n  url: string; // object url for preview/open\n};\n\n@Component({\n  selector: 'file-grab',\n  standalone: true,\n  imports: [CommonModule],\n  template: `\n    <div class=\"fg-container\">\n      <div class=\"fg-dropzone\"\n           [class.fg-over]=\"dragOver\"\n           (dragover)=\"onDragOver($event)\"\n           (dragleave)=\"onDragLeave($event)\"\n           (drop)=\"onDrop($event)\"\n           (click)=\"fileInput.click()\">\n        <input #fileInput type=\"file\" class=\"fg-hidden\" [accept]=\"accept || defaultAccept\" [multiple]=\"multiple\"\n               (change)=\"onFileInput($event)\" />\n        <div class=\"fg-dropzone-text\">\n          <strong>{{ uploadHereLabel || 'Drop your file or click here' }}</strong>\n          <div class=\"fg-desc\" *ngIf=\"uploadDescLabel\">{{ uploadDescLabel }}</div>\n        </div>\n      </div>\n\n      <div class=\"fg-list\" *ngIf=\"items.length\">\n        <div class=\"fg-item\" *ngFor=\"let it of items; let i = index\">\n          <a class=\"fg-icon-wrap\" [href]=\"it.url\" target=\"_blank\" rel=\"noopener noreferrer\">\n            <ng-container [ngSwitch]=\"ext(it.file)\">\n              <div *ngSwitchCase=\"'pdf'\" class=\"fg-icon fg-icon-pdf\">PDF</div>\n              <div *ngSwitchCase=\"'xls'\" class=\"fg-icon fg-icon-xls\"></div>\n              <div *ngSwitchCase=\"'xlsx'\" class=\"fg-icon fg-icon-xls\"></div>\n              <div *ngSwitchCase=\"'csv'\" class=\"fg-icon fg-icon-xls\"></div>\n              <div *ngSwitchDefault class=\"fg-icon fg-icon-generic\"></div>\n            </ng-container>\n          </a>\n          <div class=\"fg-name\" [title]=\"it.file.name\">{{ it.file.name }}</div>\n          <button type=\"button\" class=\"fg-remove\" (click)=\"remove(i)\" aria-label=\"Remove\">×</button>\n        </div>\n      </div>\n    </div>\n  `,\n  styles: [`\n    :host { display: block; }\n    .fg-container { width: 100%; }\n    .fg-dropzone {\n      border: 2px dashed #CBD5E1; /* slate-300 */\n      border-radius: 6px;\n      padding: 24px;\n      text-align: center;\n      color: #0F172A; /* slate-900 */\n      background: #F8FAFC; /* slate-50 */\n      cursor: pointer;\n      transition: border-color .15s ease, background .15s ease;\n    }\n    .fg-over { border-color: #10B981; background: #ECFDF5; } /* emerald */\n    .fg-dropzone-text strong { font-weight: 600; }\n    .fg-desc { color: #64748B; font-size: 12px; margin-top: 4px; }\n    .fg-hidden { display: none; }\n\n    .fg-list { display: flex; gap: 24px; margin-top: 12px; flex-wrap: wrap; }\n    .fg-item { display: flex; flex-direction: column; align-items: center; position: relative; }\n    .fg-name { margin-top: 6px; max-width: 140px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; font-size: 12px; }\n    .fg-remove { position: absolute; top: -6px; right: -6px; background: #EF4444; color: #fff; border: none; border-radius: 999px; width: 20px; height: 20px; line-height: 18px; cursor: pointer; }\n\n    .fg-icon { width: 56px; height: 72px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-weight: 700; color: white; }\n    .fg-icon-pdf { background: #EF4444; }\n    .fg-icon-xls { background: #16A34A; position: relative; }\n    .fg-icon-xls::before { content: ''; width: 28px; height: 28px; background: repeating-linear-gradient(90deg, #fff 0 6px, transparent 6px 8px), repeating-linear-gradient(0deg, #fff 0 6px, transparent 6px 8px); border-radius: 4px; }\n    .fg-icon-generic { background: #64748B; position: relative; }\n    .fg-icon-generic::before { content: ''; width: 28px; height: 36px; background: #fff; border-radius: 3px; }\n  `]\n})\nexport class FileGrabComponent {\n  @Input() uploadHereLabel?: string;\n  @Input() uploadDescLabel?: string;\n  @Input() accept?: string;\n  @Input() multiple = false;\n\n  @Output() fileSelected = new EventEmitter<File>();\n  @Output() filesChanged = new EventEmitter<File[]>();\n\n  protected defaultAccept = DEFAULT_ACCEPT;\n  protected dragOver = false;\n  protected items: FileGrabItem[] = [];\n\n  private addFiles(files: FileList | File[]) {\n    const arr = Array.from(files);\n    const added: File[] = [];\n    for (const f of arr) {\n      // Validate accept if provided\n      if (this.accept && !this.isAccepted(f)) continue;\n      // prevent duplicates by name + size\n      if (this.items.some(x => x.file.name === f.name && x.file.size === f.size)) continue;\n      const url = URL.createObjectURL(f);\n      this.items.push({ file: f, url });\n      added.push(f);\n      this.fileSelected.emit(f);\n    }\n    if (added.length) this.filesChanged.emit(this.items.map(i => i.file));\n  }\n\n  protected onFileInput(e: Event) {\n    const input = e.target as HTMLInputElement;\n    if (!input.files) return;\n    this.addFiles(input.files);\n    input.value = '';\n  }\n\n  protected onDragOver(e: DragEvent) {\n    e.preventDefault();\n    this.dragOver = true;\n  }\n\n  protected onDragLeave(_: DragEvent) {\n    this.dragOver = false;\n  }\n\n  protected onDrop(e: DragEvent) {\n    e.preventDefault();\n    this.dragOver = false;\n    if (e.dataTransfer?.files?.length) {\n      this.addFiles(e.dataTransfer.files);\n    }\n  }\n\n  remove(index: number) {\n    const [removed] = this.items.splice(index, 1);\n    if (removed) URL.revokeObjectURL(removed.url);\n    this.filesChanged.emit(this.items.map(i => i.file));\n  }\n\n  ext(file: File): string {\n    const n = file.name.toLowerCase();\n    const dot = n.lastIndexOf('.')\n    return dot >= 0 ? n.substring(dot + 1) : (file.type.split('/')[1] || '').toLowerCase();\n  }\n\n  private isAccepted(file: File): boolean {\n    const accepts = (this.accept || this.defaultAccept).split(',').map(s => s.trim().toLowerCase());\n    const name = file.name.toLowerCase();\n    const mime = file.type.toLowerCase();\n    return accepts.some(a => {\n      if (!a) return false;\n      if (a.startsWith('.')) return name.endsWith(a);\n      return mime === a;\n    });\n  }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}