945 lines
34 KiB
HTML
945 lines
34 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Glass Morphism File Manager</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 25%, #f093fb 50%, #f5576c 75%, #4facfe 100%);
|
||
background-size: 400% 400%;
|
||
animation: gradientShift 15s ease infinite;
|
||
min-height: 100vh;
|
||
padding: 2rem;
|
||
color: #fff;
|
||
}
|
||
|
||
@keyframes gradientShift {
|
||
0% { background-position: 0% 50%; }
|
||
50% { background-position: 100% 50%; }
|
||
100% { background-position: 0% 50%; }
|
||
}
|
||
|
||
main {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
h1 {
|
||
text-align: center;
|
||
font-size: 2.5rem;
|
||
font-weight: 300;
|
||
margin-bottom: 2rem;
|
||
text-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||
background: linear-gradient(45deg, #ffffff, #f0f0f0, #ffffff);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
}
|
||
|
||
.hybrid-component {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
grid-template-rows: auto auto 1fr;
|
||
gap: 1.5rem;
|
||
height: 80vh;
|
||
}
|
||
|
||
/* Glass Morphism Base Styles */
|
||
.glass-panel {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
backdrop-filter: blur(20px);
|
||
-webkit-backdrop-filter: blur(20px);
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
border-radius: 20px;
|
||
padding: 2rem;
|
||
box-shadow:
|
||
0 8px 32px rgba(0, 0, 0, 0.1),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.2),
|
||
inset 0 -1px 0 rgba(255, 255, 255, 0.1);
|
||
position: relative;
|
||
overflow: hidden;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
.glass-panel::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: -100%;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
|
||
transition: left 0.6s;
|
||
}
|
||
|
||
.glass-panel:hover::before {
|
||
left: 100%;
|
||
}
|
||
|
||
.glass-panel:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow:
|
||
0 12px 40px rgba(0, 0, 0, 0.15),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.3),
|
||
inset 0 -1px 0 rgba(255, 255, 255, 0.2);
|
||
}
|
||
|
||
/* Upload Area */
|
||
.upload-zone {
|
||
grid-column: 1 / -1;
|
||
border: 2px dashed rgba(255, 255, 255, 0.3);
|
||
border-radius: 20px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
position: relative;
|
||
min-height: 200px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.upload-zone.dragover {
|
||
border-color: rgba(255, 255, 255, 0.6);
|
||
background: rgba(255, 255, 255, 0.05);
|
||
transform: scale(1.02);
|
||
}
|
||
|
||
.upload-icon {
|
||
font-size: 3rem;
|
||
margin-bottom: 1rem;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.upload-text {
|
||
font-size: 1.2rem;
|
||
font-weight: 300;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.upload-hint {
|
||
font-size: 0.9rem;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
/* Progress Tracking */
|
||
.progress-panel {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.progress-item {
|
||
background: rgba(255, 255, 255, 0.05);
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
border-radius: 12px;
|
||
padding: 1rem;
|
||
backdrop-filter: blur(10px);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.progress-item:hover {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
}
|
||
|
||
.progress-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.filename {
|
||
font-weight: 500;
|
||
truncate: ellipsis;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.file-size {
|
||
font-size: 0.8rem;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.progress-bar {
|
||
height: 4px;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 2px;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #00f5ff, #0080ff);
|
||
border-radius: 2px;
|
||
transition: width 0.3s ease;
|
||
position: relative;
|
||
}
|
||
|
||
.progress-fill::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||
animation: shimmer 2s infinite;
|
||
}
|
||
|
||
@keyframes shimmer {
|
||
0% { transform: translateX(-100%); }
|
||
100% { transform: translateX(100%); }
|
||
}
|
||
|
||
/* File Browser */
|
||
.browser-panel {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.browser-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.search-glass {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
border-radius: 12px;
|
||
padding: 0.5rem 1rem;
|
||
color: #fff;
|
||
backdrop-filter: blur(10px);
|
||
width: 200px;
|
||
}
|
||
|
||
.search-glass::placeholder {
|
||
color: rgba(255, 255, 255, 0.6);
|
||
}
|
||
|
||
.search-glass:focus {
|
||
outline: none;
|
||
border-color: rgba(255, 255, 255, 0.4);
|
||
background: rgba(255, 255, 255, 0.15);
|
||
}
|
||
|
||
.file-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||
gap: 1rem;
|
||
overflow-y: auto;
|
||
max-height: 400px;
|
||
}
|
||
|
||
.file-card {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||
border-radius: 16px;
|
||
padding: 1rem;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
backdrop-filter: blur(15px);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.file-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -50%;
|
||
left: -50%;
|
||
width: 200%;
|
||
height: 200%;
|
||
background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.file-card:hover::before {
|
||
opacity: 1;
|
||
}
|
||
|
||
.file-card:hover {
|
||
transform: translateY(-4px) scale(1.05);
|
||
background: rgba(255, 255, 255, 0.12);
|
||
border-color: rgba(255, 255, 255, 0.3);
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
.file-icon {
|
||
font-size: 2rem;
|
||
margin-bottom: 0.5rem;
|
||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
|
||
}
|
||
|
||
.file-name {
|
||
font-size: 0.8rem;
|
||
font-weight: 500;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Preview Modal */
|
||
.preview-modal {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
backdrop-filter: blur(10px);
|
||
display: none;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 1000;
|
||
animation: fadeIn 0.3s ease;
|
||
}
|
||
|
||
.preview-modal.active {
|
||
display: flex;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
.preview-content {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
backdrop-filter: blur(30px);
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
border-radius: 24px;
|
||
padding: 2rem;
|
||
max-width: 80vw;
|
||
max-height: 80vh;
|
||
overflow: auto;
|
||
position: relative;
|
||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.preview-close {
|
||
position: absolute;
|
||
top: 1rem;
|
||
right: 1rem;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
border-radius: 50%;
|
||
width: 40px;
|
||
height: 40px;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
cursor: pointer;
|
||
color: #fff;
|
||
font-size: 1.2rem;
|
||
transition: all 0.3s ease;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.preview-close:hover {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
/* Validation System */
|
||
.validation-panel {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.validation-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
padding: 1rem;
|
||
background: rgba(255, 255, 255, 0.05);
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
border-radius: 12px;
|
||
backdrop-filter: blur(10px);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.validation-icon {
|
||
font-size: 1.5rem;
|
||
min-width: 24px;
|
||
}
|
||
|
||
.validation-icon.success { color: #00ff88; }
|
||
.validation-icon.warning { color: #ffaa00; }
|
||
.validation-icon.error { color: #ff4444; }
|
||
|
||
.validation-text {
|
||
flex: 1;
|
||
}
|
||
|
||
.validation-title {
|
||
font-weight: 500;
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
|
||
.validation-desc {
|
||
font-size: 0.8rem;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
/* Responsive Design */
|
||
@media (max-width: 768px) {
|
||
.hybrid-component {
|
||
grid-template-columns: 1fr;
|
||
gap: 1rem;
|
||
}
|
||
|
||
h1 {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
.glass-panel {
|
||
padding: 1.5rem;
|
||
}
|
||
|
||
.file-grid {
|
||
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
||
}
|
||
}
|
||
|
||
/* Accessibility */
|
||
.sr-only {
|
||
position: absolute;
|
||
width: 1px;
|
||
height: 1px;
|
||
padding: 0;
|
||
margin: -1px;
|
||
overflow: hidden;
|
||
clip: rect(0, 0, 0, 0);
|
||
white-space: nowrap;
|
||
border: 0;
|
||
}
|
||
|
||
/* Focus States */
|
||
.file-card:focus,
|
||
.upload-zone:focus {
|
||
outline: 2px solid rgba(255, 255, 255, 0.5);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* Animation Classes */
|
||
.animate-in {
|
||
animation: slideInUp 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
@keyframes slideInUp {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(20px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<main>
|
||
<h1>File Manager - Glass Morphism Theme</h1>
|
||
<div class="hybrid-component">
|
||
<!-- Upload Zone -->
|
||
<div class="upload-zone glass-panel" id="uploadZone" tabindex="0" role="button" aria-label="File upload area">
|
||
<div class="upload-icon">☁️</div>
|
||
<div class="upload-text">Drop files here or click to browse</div>
|
||
<div class="upload-hint">Supports images, documents, and archives up to 50MB</div>
|
||
<input type="file" id="fileInput" multiple style="display: none;" accept="image/*,application/pdf,.doc,.docx,.txt,.zip,.rar">
|
||
</div>
|
||
|
||
<!-- Progress Tracking -->
|
||
<div class="progress-panel glass-panel">
|
||
<h3>📊 Upload Progress</h3>
|
||
<div id="progressContainer">
|
||
<div class="validation-item">
|
||
<div class="validation-icon">💫</div>
|
||
<div class="validation-text">
|
||
<div class="validation-title">Ready to upload</div>
|
||
<div class="validation-desc">Select files to see progress here</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- File Browser -->
|
||
<div class="browser-panel glass-panel">
|
||
<div class="browser-header">
|
||
<h3>📁 File Browser</h3>
|
||
<input type="text" class="search-glass" placeholder="Search files..." id="searchInput">
|
||
</div>
|
||
<div class="file-grid" id="fileGrid">
|
||
<!-- Sample files -->
|
||
<div class="file-card" data-filename="document.pdf" data-type="pdf" tabindex="0">
|
||
<div class="file-icon">📄</div>
|
||
<div class="file-name">document.pdf</div>
|
||
</div>
|
||
<div class="file-card" data-filename="image.jpg" data-type="image" tabindex="0">
|
||
<div class="file-icon">🖼️</div>
|
||
<div class="file-name">image.jpg</div>
|
||
</div>
|
||
<div class="file-card" data-filename="archive.zip" data-type="archive" tabindex="0">
|
||
<div class="file-icon">📦</div>
|
||
<div class="file-name">archive.zip</div>
|
||
</div>
|
||
<div class="file-card" data-filename="presentation.pptx" data-type="presentation" tabindex="0">
|
||
<div class="file-icon">📊</div>
|
||
<div class="file-name">presentation.pptx</div>
|
||
</div>
|
||
<div class="file-card" data-filename="spreadsheet.xlsx" data-type="spreadsheet" tabindex="0">
|
||
<div class="file-icon">📈</div>
|
||
<div class="file-name">spreadsheet.xlsx</div>
|
||
</div>
|
||
<div class="file-card" data-filename="video.mp4" data-type="video" tabindex="0">
|
||
<div class="file-icon">🎬</div>
|
||
<div class="file-name">video.mp4</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Validation System -->
|
||
<div class="validation-panel glass-panel">
|
||
<h3>✅ File Validation</h3>
|
||
<div id="validationContainer">
|
||
<div class="validation-item">
|
||
<div class="validation-icon success">✓</div>
|
||
<div class="validation-text">
|
||
<div class="validation-title">System Ready</div>
|
||
<div class="validation-desc">All validation checks passed</div>
|
||
</div>
|
||
</div>
|
||
<div class="validation-item">
|
||
<div class="validation-icon success">✓</div>
|
||
<div class="validation-text">
|
||
<div class="validation-title">Storage Available</div>
|
||
<div class="validation-desc">2.4GB remaining space</div>
|
||
</div>
|
||
</div>
|
||
<div class="validation-item">
|
||
<div class="validation-icon success">✓</div>
|
||
<div class="validation-text">
|
||
<div class="validation-title">Security Scan</div>
|
||
<div class="validation-desc">Malware protection active</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Preview Modal -->
|
||
<div class="preview-modal" id="previewModal" role="dialog" aria-labelledby="previewTitle" aria-hidden="true">
|
||
<div class="preview-content">
|
||
<button class="preview-close" id="previewClose" aria-label="Close preview">×</button>
|
||
<h3 id="previewTitle">File Preview</h3>
|
||
<div id="previewBody">
|
||
<!-- Preview content will be inserted here -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<script>
|
||
class GlassFileManager {
|
||
constructor() {
|
||
this.uploadZone = document.getElementById('uploadZone');
|
||
this.fileInput = document.getElementById('fileInput');
|
||
this.progressContainer = document.getElementById('progressContainer');
|
||
this.fileGrid = document.getElementById('fileGrid');
|
||
this.searchInput = document.getElementById('searchInput');
|
||
this.validationContainer = document.getElementById('validationContainer');
|
||
this.previewModal = document.getElementById('previewModal');
|
||
this.previewClose = document.getElementById('previewClose');
|
||
this.previewBody = document.getElementById('previewBody');
|
||
|
||
this.uploadedFiles = [];
|
||
this.isUploading = false;
|
||
|
||
this.initializeEventListeners();
|
||
this.initializeAnimation();
|
||
}
|
||
|
||
initializeEventListeners() {
|
||
// Upload zone events
|
||
this.uploadZone.addEventListener('click', () => this.fileInput.click());
|
||
this.uploadZone.addEventListener('dragover', this.handleDragOver.bind(this));
|
||
this.uploadZone.addEventListener('dragleave', this.handleDragLeave.bind(this));
|
||
this.uploadZone.addEventListener('drop', this.handleDrop.bind(this));
|
||
|
||
// File input change
|
||
this.fileInput.addEventListener('change', this.handleFileSelect.bind(this));
|
||
|
||
// Search functionality
|
||
this.searchInput.addEventListener('input', this.handleSearch.bind(this));
|
||
|
||
// File preview
|
||
this.fileGrid.addEventListener('click', this.handleFilePreview.bind(this));
|
||
this.previewClose.addEventListener('click', this.closePreview.bind(this));
|
||
this.previewModal.addEventListener('click', (e) => {
|
||
if (e.target === this.previewModal) this.closePreview();
|
||
});
|
||
|
||
// Keyboard navigation
|
||
document.addEventListener('keydown', this.handleKeyboard.bind(this));
|
||
}
|
||
|
||
initializeAnimation() {
|
||
// Animate panels on load
|
||
const panels = document.querySelectorAll('.glass-panel');
|
||
panels.forEach((panel, index) => {
|
||
setTimeout(() => {
|
||
panel.classList.add('animate-in');
|
||
}, index * 200);
|
||
});
|
||
}
|
||
|
||
handleDragOver(e) {
|
||
e.preventDefault();
|
||
this.uploadZone.classList.add('dragover');
|
||
}
|
||
|
||
handleDragLeave(e) {
|
||
e.preventDefault();
|
||
this.uploadZone.classList.remove('dragover');
|
||
}
|
||
|
||
handleDrop(e) {
|
||
e.preventDefault();
|
||
this.uploadZone.classList.remove('dragover');
|
||
|
||
const files = Array.from(e.dataTransfer.files);
|
||
this.processFiles(files);
|
||
}
|
||
|
||
handleFileSelect(e) {
|
||
const files = Array.from(e.target.files);
|
||
this.processFiles(files);
|
||
}
|
||
|
||
processFiles(files) {
|
||
if (this.isUploading) return;
|
||
|
||
// Validate files
|
||
const validatedFiles = this.validateFiles(files);
|
||
|
||
if (validatedFiles.length > 0) {
|
||
this.uploadFiles(validatedFiles);
|
||
}
|
||
}
|
||
|
||
validateFiles(files) {
|
||
const validFiles = [];
|
||
const validationResults = [];
|
||
|
||
files.forEach(file => {
|
||
const validation = this.validateSingleFile(file);
|
||
if (validation.valid) {
|
||
validFiles.push(file);
|
||
validationResults.push({
|
||
type: 'success',
|
||
icon: '✓',
|
||
title: `${file.name} validated`,
|
||
desc: `${this.formatFileSize(file.size)} - Ready to upload`
|
||
});
|
||
} else {
|
||
validationResults.push({
|
||
type: 'error',
|
||
icon: '✗',
|
||
title: `${file.name} rejected`,
|
||
desc: validation.reason
|
||
});
|
||
}
|
||
});
|
||
|
||
this.updateValidationDisplay(validationResults);
|
||
return validFiles;
|
||
}
|
||
|
||
validateSingleFile(file) {
|
||
const maxSize = 50 * 1024 * 1024; // 50MB
|
||
const allowedTypes = [
|
||
'image/jpeg', 'image/png', 'image/gif', 'image/webp',
|
||
'application/pdf', 'text/plain',
|
||
'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||
'application/zip', 'application/x-rar-compressed'
|
||
];
|
||
|
||
if (file.size > maxSize) {
|
||
return { valid: false, reason: 'File size exceeds 50MB limit' };
|
||
}
|
||
|
||
if (!allowedTypes.includes(file.type) && !this.isValidExtension(file.name)) {
|
||
return { valid: false, reason: 'File type not supported' };
|
||
}
|
||
|
||
return { valid: true };
|
||
}
|
||
|
||
isValidExtension(filename) {
|
||
const validExtensions = ['.txt', '.doc', '.docx', '.pdf', '.jpg', '.jpeg', '.png', '.gif', '.webp', '.zip', '.rar'];
|
||
return validExtensions.some(ext => filename.toLowerCase().endsWith(ext));
|
||
}
|
||
|
||
uploadFiles(files) {
|
||
this.isUploading = true;
|
||
this.progressContainer.innerHTML = '';
|
||
|
||
files.forEach((file, index) => {
|
||
this.uploadSingleFile(file, index);
|
||
});
|
||
}
|
||
|
||
uploadSingleFile(file, index) {
|
||
const progressItem = this.createProgressItem(file);
|
||
this.progressContainer.appendChild(progressItem);
|
||
|
||
// Simulate upload progress
|
||
let progress = 0;
|
||
const progressBar = progressItem.querySelector('.progress-fill');
|
||
|
||
const interval = setInterval(() => {
|
||
progress += Math.random() * 15;
|
||
if (progress >= 100) {
|
||
progress = 100;
|
||
clearInterval(interval);
|
||
this.completeUpload(file, progressItem);
|
||
}
|
||
|
||
progressBar.style.width = `${progress}%`;
|
||
}, 200);
|
||
}
|
||
|
||
createProgressItem(file) {
|
||
const item = document.createElement('div');
|
||
item.className = 'progress-item animate-in';
|
||
item.innerHTML = `
|
||
<div class="progress-header">
|
||
<span class="filename">${file.name}</span>
|
||
<span class="file-size">${this.formatFileSize(file.size)}</span>
|
||
</div>
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" style="width: 0%"></div>
|
||
</div>
|
||
`;
|
||
return item;
|
||
}
|
||
|
||
completeUpload(file, progressItem) {
|
||
this.uploadedFiles.push(file);
|
||
|
||
// Add to file browser
|
||
this.addToFileBrowser(file);
|
||
|
||
// Update validation
|
||
this.updateValidationDisplay([{
|
||
type: 'success',
|
||
icon: '✓',
|
||
title: 'Upload completed',
|
||
desc: `${file.name} successfully uploaded`
|
||
}]);
|
||
|
||
// Update progress item
|
||
setTimeout(() => {
|
||
progressItem.style.transform = 'scale(0.95)';
|
||
progressItem.style.opacity = '0.7';
|
||
}, 1000);
|
||
}
|
||
|
||
addToFileBrowser(file) {
|
||
const fileCard = document.createElement('div');
|
||
fileCard.className = 'file-card animate-in';
|
||
fileCard.setAttribute('data-filename', file.name);
|
||
fileCard.setAttribute('data-type', this.getFileType(file));
|
||
fileCard.setAttribute('tabindex', '0');
|
||
|
||
const icon = this.getFileIcon(file);
|
||
fileCard.innerHTML = `
|
||
<div class="file-icon">${icon}</div>
|
||
<div class="file-name">${file.name}</div>
|
||
`;
|
||
|
||
this.fileGrid.insertBefore(fileCard, this.fileGrid.firstChild);
|
||
}
|
||
|
||
getFileType(file) {
|
||
if (file.type.startsWith('image/')) return 'image';
|
||
if (file.type === 'application/pdf') return 'pdf';
|
||
if (file.type.includes('word')) return 'document';
|
||
if (file.type.includes('zip') || file.type.includes('rar')) return 'archive';
|
||
return 'file';
|
||
}
|
||
|
||
getFileIcon(file) {
|
||
const type = this.getFileType(file);
|
||
const icons = {
|
||
'image': '🖼️',
|
||
'pdf': '📄',
|
||
'document': '📝',
|
||
'archive': '📦',
|
||
'file': '📄'
|
||
};
|
||
return icons[type] || '📄';
|
||
}
|
||
|
||
handleSearch(e) {
|
||
const query = e.target.value.toLowerCase();
|
||
const fileCards = this.fileGrid.querySelectorAll('.file-card');
|
||
|
||
fileCards.forEach(card => {
|
||
const filename = card.getAttribute('data-filename').toLowerCase();
|
||
const visible = filename.includes(query);
|
||
card.style.display = visible ? 'block' : 'none';
|
||
|
||
if (visible && query) {
|
||
card.style.animation = 'none';
|
||
card.offsetHeight; // Trigger reflow
|
||
card.style.animation = 'slideInUp 0.3s ease';
|
||
}
|
||
});
|
||
}
|
||
|
||
handleFilePreview(e) {
|
||
const fileCard = e.target.closest('.file-card');
|
||
if (!fileCard) return;
|
||
|
||
const filename = fileCard.getAttribute('data-filename');
|
||
const filetype = fileCard.getAttribute('data-type');
|
||
|
||
this.showPreview(filename, filetype);
|
||
}
|
||
|
||
showPreview(filename, filetype) {
|
||
const previewTitle = document.getElementById('previewTitle');
|
||
previewTitle.textContent = `Preview: ${filename}`;
|
||
|
||
let content = '';
|
||
|
||
switch (filetype) {
|
||
case 'image':
|
||
content = `
|
||
<div style="text-align: center;">
|
||
<div style="width: 400px; height: 300px; background: rgba(255,255,255,0.1); border-radius: 16px; display: flex; align-items: center; justify-content: center; margin: 0 auto;">
|
||
<span style="font-size: 4rem;">🖼️</span>
|
||
</div>
|
||
<p style="margin-top: 1rem; opacity: 0.8;">Image preview would appear here</p>
|
||
</div>
|
||
`;
|
||
break;
|
||
case 'pdf':
|
||
content = `
|
||
<div style="text-align: center;">
|
||
<div style="width: 400px; height: 500px; background: rgba(255,255,255,0.1); border-radius: 16px; display: flex; align-items: center; justify-content: center; margin: 0 auto;">
|
||
<span style="font-size: 4rem;">📄</span>
|
||
</div>
|
||
<p style="margin-top: 1rem; opacity: 0.8;">PDF preview would appear here</p>
|
||
</div>
|
||
`;
|
||
break;
|
||
default:
|
||
content = `
|
||
<div style="text-align: center; padding: 2rem;">
|
||
<div style="font-size: 4rem; margin-bottom: 1rem;">📄</div>
|
||
<h4 style="margin-bottom: 1rem;">${filename}</h4>
|
||
<p style="opacity: 0.8;">Preview not available for this file type</p>
|
||
<p style="opacity: 0.6; font-size: 0.9rem; margin-top: 1rem;">File type: ${filetype}</p>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
this.previewBody.innerHTML = content;
|
||
this.previewModal.classList.add('active');
|
||
this.previewModal.setAttribute('aria-hidden', 'false');
|
||
|
||
// Focus management
|
||
this.previewClose.focus();
|
||
}
|
||
|
||
closePreview() {
|
||
this.previewModal.classList.remove('active');
|
||
this.previewModal.setAttribute('aria-hidden', 'true');
|
||
}
|
||
|
||
updateValidationDisplay(results) {
|
||
if (results.length === 0) return;
|
||
|
||
this.validationContainer.innerHTML = '';
|
||
|
||
results.forEach(result => {
|
||
const item = document.createElement('div');
|
||
item.className = 'validation-item animate-in';
|
||
item.innerHTML = `
|
||
<div class="validation-icon ${result.type}">${result.icon}</div>
|
||
<div class="validation-text">
|
||
<div class="validation-title">${result.title}</div>
|
||
<div class="validation-desc">${result.desc}</div>
|
||
</div>
|
||
`;
|
||
this.validationContainer.appendChild(item);
|
||
});
|
||
}
|
||
|
||
handleKeyboard(e) {
|
||
// Escape key closes preview
|
||
if (e.key === 'Escape' && this.previewModal.classList.contains('active')) {
|
||
this.closePreview();
|
||
}
|
||
|
||
// Enter key on file cards opens preview
|
||
if (e.key === 'Enter' && e.target.classList.contains('file-card')) {
|
||
this.handleFilePreview(e);
|
||
}
|
||
|
||
// Enter key on upload zone opens file picker
|
||
if (e.key === 'Enter' && e.target === this.uploadZone) {
|
||
this.fileInput.click();
|
||
}
|
||
}
|
||
|
||
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];
|
||
}
|
||
}
|
||
|
||
// Initialize the file manager when DOM is loaded
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
new GlassFileManager();
|
||
});
|
||
|
||
// Add some dynamic glass effects
|
||
document.addEventListener('mousemove', (e) => {
|
||
const panels = document.querySelectorAll('.glass-panel');
|
||
panels.forEach(panel => {
|
||
const rect = panel.getBoundingClientRect();
|
||
const x = ((e.clientX - rect.left) / rect.width) * 100;
|
||
const y = ((e.clientY - rect.top) / rect.height) * 100;
|
||
|
||
panel.style.setProperty('--mouse-x', `${x}%`);
|
||
panel.style.setProperty('--mouse-y', `${y}%`);
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |