<!DOCTYPE html> <html lang="fr" data-theme="dark"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="icon" type="image/png" href="favicon.png"> <title>Esenjin | FTéxPlorateur</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"></script> <style> :root[data-theme="dark"] { --primary-color: #64B5F6; --hover-color: #42A5F5; --bg-color: #1a1a1a; --container-bg: #2d2d2d; --text-color: #e0e0e0; --border-color: #404040; --meta-color: #909090; --input-bg: #3d3d3d; } :root[data-theme="light"] { --primary-color: #4a90e2; --hover-color: #357abd; --bg-color: #f5f6fa; --container-bg: white; --text-color: #333; --border-color: #eee; --meta-color: #666; --input-bg: white; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: var(--bg-color); color: var(--text-color); } .theme-toggle { position: absolute; top: 20px; right: 20px; padding: 10px; border-radius: 5px; background: var(--input-bg); border: 1px solid var(--border-color); color: var(--text-color); cursor: pointer; } .container { max-width: 1200px; margin: 0 auto; background: var(--container-bg); border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); padding: 20px; } h1 { color: var(--primary-color); margin-bottom: 30px; padding-bottom: 10px; border-bottom: 2px solid var(--border-color); } .file-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; padding: 20px; } .file-item { background: var(--container-bg); border: 1px solid var(--border-color); border-radius: 8px; padding: 15px; transition: all 0.3s ease; cursor: pointer; text-decoration: none; color: var(--text-color); display: flex; flex-direction: column; align-items: center; text-align: center; } .file-item:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.2); border-color: var(--primary-color); } .file-icon { font-size: 2em; margin-bottom: 10px; color: var(--primary-color); } .file-name { word-break: break-word; margin-top: 8px; font-size: 0.9em; } .file-meta { font-size: 0.8em; color: var(--meta-color); margin-top: 5px; } .search-bar { width: 100%; max-width: 500px; margin: 0 auto 20px; display: flex; gap: 10px; } .search-input { flex: 1; padding: 10px 15px; border: 2px solid var(--border-color); border-radius: 5px; font-size: 1em; background: var(--input-bg); color: var(--text-color); transition: border-color 0.3s ease; } .search-input:focus { outline: none; border-color: var(--primary-color); } .sort-select { padding: 10px; border: 2px solid var(--border-color); border-radius: 5px; background: var(--input-bg); color: var(--text-color); cursor: pointer; } .sort-select:focus { outline: none; border-color: var(--primary-color); } @media (max-width: 768px) { .file-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); } } /* Styles pour la modal */ .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.9); z-index: 1000; overflow: auto; } .modal-content { position: relative; margin: auto; padding: 20px; width: 90%; max-width: 1200px; min-height: 200px; top: 50%; transform: translateY(-50%); } .modal-close { position: absolute; right: 25px; top: 25px; color: var(--text-color); font-size: 24px; cursor: pointer; z-index: 1001; background: rgba(0, 0, 0, 0.5); width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; } .preview-container { width: 100%; height: 80vh; display: flex; align-items: center; justify-content: center; } .preview-container img { max-width: 100%; max-height: 100%; object-fit: contain; } .preview-container video, .preview-container audio { width: 100%; max-width: 800px; } .preview-container iframe { width: 100%; height: 100%; border: none; } .loading { color: var(--text-color); text-align: center; padding: 20px; } @media (max-width: 768px) { .modal-content { width: 95%; padding: 10px; } .preview-container { height: 60vh; } } </style> </head> <body> <button class="theme-toggle" id="themeToggle"> <i class="fas fa-moon"></i> </button> <div class="container"> <h1>De bric et de broc...</h1> <div class="search-bar"> <input type="text" class="search-input" placeholder="Rechercher des fichiers..." id="searchInput"> <select class="sort-select" id="sortSelect"> <option value="name">Nom</option> <option value="date">Date</option> <option value="size">Taille</option> </select> </div> <div id="previewModal" class="modal"> <span class="modal-close" id="modalClose">×</span> <div class="modal-content"> <div id="previewContainer" class="preview-container"> <div class="loading">Chargement...</div> </div> </div> </div> <div class="file-grid" id="fileGrid"> <!-- Les fichiers seront ajoutés ici dynamiquement --> </div> </div> <script> // Toggle thème const themeToggle = document.getElementById('themeToggle'); const html = document.documentElement; themeToggle.addEventListener('click', () => { const currentTheme = html.getAttribute('data-theme'); const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; html.setAttribute('data-theme', newTheme); themeToggle.innerHTML = newTheme === 'dark' ? '<i class="fas fa-moon"></i>' : '<i class="fas fa-sun"></i>'; }); // Fonction pour déterminer l'icône function getFileIcon(filename) { const ext = filename.split('.').pop().toLowerCase(); const iconMap = { pdf: 'file-pdf', doc: 'file-word', docx: 'file-word', xls: 'file-excel', xlsx: 'file-excel', jpg: 'file-image', jpeg: 'file-image', png: 'file-image', gif: 'file-image', mp3: 'file-audio', wav: 'file-audio', mp4: 'file-video', zip: 'file-archive', rar: 'file-archive' }; return iconMap[ext] || 'file'; } // Fonction pour formater la taille function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } function createFileElement(file) { const fileItem = document.createElement('a'); fileItem.className = 'file-item'; fileItem.href = '#'; const icon = getFileIcon(file.name); fileItem.innerHTML = ` <i class="fa-solid fa-${icon} file-icon"></i> <div class="file-name">${file.name}</div> <div class="file-meta">${formatFileSize(file.size)}</div> <div class="file-meta">${file.date}</div> `; // Gestion du clic pour la prévisualisation fileItem.addEventListener('click', (e) => { e.preventDefault(); previewFile(file); }); return fileItem; } // Gestion de la prévisualisation const modal = document.getElementById('previewModal'); const modalClose = document.getElementById('modalClose'); const previewContainer = document.getElementById('previewContainer'); modalClose.addEventListener('click', () => { modal.style.display = 'none'; previewContainer.innerHTML = '<div class="loading">Chargement...</div>'; }); window.addEventListener('click', (e) => { if (e.target === modal) { modal.style.display = 'none'; previewContainer.innerHTML = '<div class="loading">Chargement...</div>'; } }); function getFileType(filename) { const ext = filename.split('.').pop().toLowerCase(); const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp']; const videoTypes = ['mp4', 'webm', 'ogg']; const audioTypes = ['mp3', 'wav', 'ogg']; const documentTypes = ['pdf']; if (imageTypes.includes(ext)) return 'image'; if (videoTypes.includes(ext)) return 'video'; if (audioTypes.includes(ext)) return 'audio'; if (documentTypes.includes(ext)) return 'pdf'; return 'other'; } async function previewFile(file) { modal.style.display = 'block'; const fileType = getFileType(file.name); const fileUrl = file.path; switch (fileType) { case 'image': previewContainer.innerHTML = ` <img src="${fileUrl}" alt="${file.name}" /> `; break; case 'video': previewContainer.innerHTML = ` <video controls> <source src="${fileUrl}" type="video/${file.name.split('.').pop()}"> Votre navigateur ne supporte pas la lecture vidéo. </video> `; break; case 'audio': previewContainer.innerHTML = ` <audio controls> <source src="${fileUrl}" type="audio/${file.name.split('.').pop()}"> Votre navigateur ne supporte pas la lecture audio. </audio> `; break; case 'pdf': // Utilisation de PDF.js pour les PDF previewContainer.innerHTML = ` <iframe src="${fileUrl}" type="application/pdf"></iframe> `; break; default: previewContainer.innerHTML = ` <div class="loading"> Ce type de fichier ne peut pas être prévisualisé.<br> <a href="${fileUrl}" target="_blank" style="color: var(--primary-color);"> Télécharger le fichier </a> </div> `; } } // Charger les fichiers depuis l'API async function loadFiles() { try { const response = await fetch('list-files.php'); const files = await response.json(); return files; } catch (error) { console.error('Erreur lors du chargement des fichiers:', error); return []; } } // Initialisation let allFiles = []; async function initializeFiles() { allFiles = await loadFiles(); displayFiles(allFiles); } function displayFiles(files) { const fileGrid = document.getElementById('fileGrid'); fileGrid.innerHTML = ''; files.forEach(file => { fileGrid.appendChild(createFileElement(file)); }); } // Recherche const searchInput = document.getElementById('searchInput'); searchInput.addEventListener('input', (e) => { const searchTerm = e.target.value.toLowerCase(); const filteredFiles = allFiles.filter(file => file.name.toLowerCase().includes(searchTerm) ); displayFiles(filteredFiles); }); // Tri const sortSelect = document.getElementById('sortSelect'); sortSelect.addEventListener('change', (e) => { const sortBy = e.target.value; const sortedFiles = [...allFiles].sort((a, b) => { switch(sortBy) { case 'name': return a.name.localeCompare(b.name); case 'date': return new Date(b.date) - new Date(a.date); case 'size': return b.size - a.size; default: return 0; } }); displayFiles(sortedFiles); }); // Initialiser l'application initializeFiles(); </script> </body> </html>