de-bric-et-de-broc/index.html
2024-12-24 20:45:34 +00:00

483 lines
15 KiB
HTML

<!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="shortcut icon" href="favicon.ico">
<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">&times;</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>