Spaces:
Running
Running
| class UploadZone extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.dragCounter = 0; | |
| } | |
| connectedCallback() { | |
| this.innerHTML = ` | |
| <div id="drop-zone" class="relative group"> | |
| <div class="border-2 border-dashed border-slate-700 rounded-2xl p-8 text-center transition-all duration-300 bg-slate-900/50 hover:border-primary-500/50 hover:bg-primary-500/5"> | |
| <!-- Icon --> | |
| <div class="w-16 h-16 mx-auto mb-4 rounded-full bg-slate-800 flex items-center justify-center group-hover:scale-110 transition-transform duration-300"> | |
| <i data-feather="upload-cloud" class="w-8 h-8 text-slate-400 group-hover:text-primary-400 transition-colors"></i> | |
| </div> | |
| <h3 class="text-lg font-semibold text-slate-300 mb-2 group-hover:text-slate-200"> | |
| Arraste arquivos de áudio | |
| </h3> | |
| <p class="text-sm text-slate-500 mb-4"> | |
| ou <span class="text-primary-400 cursor-pointer hover:underline">clique para selecionar</span> | |
| </p> | |
| <div class="flex items-center justify-center gap-2 text-xs text-slate-600"> | |
| <span class="px-2 py-1 rounded bg-slate-800">MP3</span> | |
| <span class="px-2 py-1 rounded bg-slate-800">WAV</span> | |
| <span class="px-2 py-1 rounded bg-slate-800">M4A</span> | |
| <span class="px-2 py-1 rounded bg-slate-800">FLAC</span> | |
| </div> | |
| <!-- Hidden Input --> | |
| <input type="file" id="file-input" multiple accept="audio/*" class="hidden"> | |
| </div> | |
| <!-- Processing Overlay --> | |
| <div id="upload-overlay" class="absolute inset-0 bg-slate-900/90 backdrop-blur-sm rounded-2xl flex items-center justify-center hidden"> | |
| <div class="text-center"> | |
| <div class="w-12 h-12 border-4 border-primary-500/30 border-t-primary-500 rounded-full animate-spin mx-auto mb-3"></div> | |
| <p class="text-sm text-slate-300">Analisando áudio...</p> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| this.setupEventListeners(); | |
| } | |
| setupEventListeners() { | |
| const dropZone = this.querySelector('#drop-zone'); | |
| const fileInput = this.querySelector('#file-input'); | |
| const overlay = this.querySelector('#upload-overlay'); | |
| // Click to select | |
| dropZone.addEventListener('click', (e) => { | |
| if (e.target !== fileInput) { | |
| fileInput.click(); | |
| } | |
| }); | |
| fileInput.addEventListener('change', (e) => { | |
| this.handleFiles(e.target.files); | |
| }); | |
| // Drag and Drop | |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, (e) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| }, false); | |
| }); | |
| ['dragenter', 'dragover'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, () => { | |
| this.dragCounter++; | |
| dropZone.classList.add('drag-over'); | |
| }, false); | |
| }); | |
| ['dragleave', 'drop'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, () => { | |
| this.dragCounter--; | |
| if (this.dragCounter === 0) { | |
| dropZone.classList.remove('drag-over'); | |
| } | |
| }, false); | |
| }); | |
| dropZone.addEventListener('drop', (e) => { | |
| this.dragCounter = 0; | |
| dropZone.classList.remove('drag-over'); | |
| const files = e.dataTransfer.files; | |
| if (files.length > 0) { | |
| // Show overlay briefly for effect | |
| overlay.classList.remove('hidden'); | |
| setTimeout(() => { | |
| overlay.classList.add('hidden'); | |
| this.handleFiles(files); | |
| }, 500); | |
| } | |
| }); | |
| } | |
| handleFiles(files) { | |
| // Dispatch custom event to main app | |
| document.dispatchEvent(new CustomEvent('files-uploaded', { | |
| detail: { files: Array.from(files) } | |
| })); | |
| } | |
| } | |
| customElements.define('upload-zone', UploadZone); |