| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Photo Gallery</title> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| } |
| |
| :root { |
| --primary-color: #4a6fa5; |
| --secondary-color: #166088; |
| --accent-color: #4fc3f7; |
| --light-color: #f8f9fa; |
| --dark-color: #343a40; |
| --shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
| --transition: all 0.3s ease; |
| } |
| |
| body { |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); |
| color: var(--dark-color); |
| line-height: 1.6; |
| min-height: 100vh; |
| } |
| |
| header { |
| background: linear-gradient(to right, var(--primary-color), var(--secondary-color)); |
| color: white; |
| padding: 1.5rem 2rem; |
| box-shadow: var(--shadow); |
| position: sticky; |
| top: 0; |
| z-index: 100; |
| } |
| |
| .header-content { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| max-width: 1200px; |
| margin: 0 auto; |
| } |
| |
| .logo { |
| display: flex; |
| align-items: center; |
| gap: 10px; |
| } |
| |
| .logo i { |
| font-size: 1.8rem; |
| } |
| |
| .logo h1 { |
| font-size: 1.8rem; |
| font-weight: 700; |
| } |
| |
| .anycoder-link { |
| color: white; |
| text-decoration: none; |
| font-size: 0.9rem; |
| opacity: 0.8; |
| transition: var(--transition); |
| } |
| |
| .anycoder-link:hover { |
| opacity: 1; |
| text-decoration: underline; |
| } |
| |
| main { |
| max-width: 1200px; |
| margin: 2rem auto; |
| padding: 0 1.5rem; |
| } |
| |
| .gallery-controls { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 2rem; |
| flex-wrap: wrap; |
| gap: 1rem; |
| } |
| |
| .search-box { |
| display: flex; |
| background: white; |
| border-radius: 30px; |
| padding: 0.5rem 1rem; |
| box-shadow: var(--shadow); |
| flex: 1; |
| max-width: 400px; |
| } |
| |
| .search-box input { |
| border: none; |
| outline: none; |
| padding: 0.5rem; |
| width: 100%; |
| font-size: 1rem; |
| } |
| |
| .search-box button { |
| background: none; |
| border: none; |
| color: var(--primary-color); |
| cursor: pointer; |
| font-size: 1.2rem; |
| } |
| |
| .category-filter { |
| display: flex; |
| gap: 0.5rem; |
| flex-wrap: wrap; |
| } |
| |
| .category-btn { |
| background: white; |
| border: none; |
| padding: 0.5rem 1rem; |
| border-radius: 20px; |
| font-weight: 500; |
| cursor: pointer; |
| transition: var(--transition); |
| box-shadow: var(--shadow); |
| } |
| |
| .category-btn:hover, .category-btn.active { |
| background: var(--primary-color); |
| color: white; |
| } |
| |
| .upload-btn { |
| background: var(--accent-color); |
| color: white; |
| border: none; |
| padding: 0.7rem 1.5rem; |
| border-radius: 30px; |
| font-weight: 600; |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| gap: 0.5rem; |
| transition: var(--transition); |
| box-shadow: var(--shadow); |
| } |
| |
| .upload-btn:hover { |
| background: #03a9f4; |
| transform: translateY(-2px); |
| } |
| |
| .gallery-container { |
| display: grid; |
| grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); |
| gap: 1.5rem; |
| } |
| |
| .photo-card { |
| background: white; |
| border-radius: 12px; |
| overflow: hidden; |
| box-shadow: var(--shadow); |
| transition: var(--transition); |
| cursor: pointer; |
| } |
| |
| .photo-card:hover { |
| transform: translateY(-5px); |
| box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15); |
| } |
| |
| .photo-img { |
| width: 100%; |
| height: 200px; |
| object-fit: cover; |
| display: block; |
| } |
| |
| .photo-details { |
| padding: 1rem; |
| } |
| |
| .photo-title { |
| font-weight: 600; |
| margin-bottom: 0.5rem; |
| font-size: 1.1rem; |
| } |
| |
| .photo-description { |
| color: #666; |
| font-size: 0.9rem; |
| margin-bottom: 1rem; |
| } |
| |
| .photo-meta { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| font-size: 0.8rem; |
| color: #888; |
| } |
| |
| .photo-actions { |
| display: flex; |
| gap: 0.8rem; |
| } |
| |
| .action-btn { |
| background: none; |
| border: none; |
| color: #888; |
| cursor: pointer; |
| transition: var(--transition); |
| } |
| |
| .action-btn:hover { |
| color: var(--primary-color); |
| } |
| |
| .modal { |
| display: none; |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background: rgba(0, 0, 0, 0.8); |
| z-index: 1000; |
| justify-content: center; |
| align-items: center; |
| padding: 1rem; |
| } |
| |
| .modal-content { |
| background: white; |
| border-radius: 12px; |
| max-width: 800px; |
| width: 100%; |
| max-height: 90vh; |
| overflow: hidden; |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); |
| } |
| |
| .modal-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| padding: 1rem 1.5rem; |
| border-bottom: 1px solid #eee; |
| } |
| |
| .modal-title { |
| font-weight: 600; |
| font-size: 1.2rem; |
| } |
| |
| .close-btn { |
| background: none; |
| border: none; |
| font-size: 1.5rem; |
| cursor: pointer; |
| color: #888; |
| } |
| |
| .modal-body { |
| padding: 1.5rem; |
| max-height: calc(90vh - 120px); |
| overflow-y: auto; |
| } |
| |
| .modal-img { |
| width: 100%; |
| border-radius: 8px; |
| margin-bottom: 1rem; |
| } |
| |
| .modal-description { |
| margin-bottom: 1rem; |
| } |
| |
| .modal-meta { |
| display: flex; |
| justify-content: space-between; |
| color: #666; |
| font-size: 0.9rem; |
| } |
| |
| .upload-modal { |
| display: none; |
| } |
| |
| .upload-form { |
| display: flex; |
| flex-direction: column; |
| gap: 1rem; |
| } |
| |
| .form-group { |
| display: flex; |
| flex-direction: column; |
| gap: 0.5rem; |
| } |
| |
| .form-group label { |
| font-weight: 500; |
| } |
| |
| .form-group input, .form-group textarea, .form-group select { |
| padding: 0.8rem; |
| border: 1px solid #ddd; |
| border-radius: 6px; |
| font-family: inherit; |
| } |
| |
| .form-group textarea { |
| min-height: 100px; |
| resize: vertical; |
| } |
| |
| .submit-btn { |
| background: var(--primary-color); |
| color: white; |
| border: none; |
| padding: 0.8rem 1.5rem; |
| border-radius: 6px; |
| font-weight: 600; |
| cursor: pointer; |
| transition: var(--transition); |
| } |
| |
| .submit-btn:hover { |
| background: var(--secondary-color); |
| } |
| |
| .file-input-container { |
| position: relative; |
| display: inline-block; |
| width: 100%; |
| } |
| |
| .file-input { |
| position: absolute; |
| left: 0; |
| top: 0; |
| opacity: 0; |
| width: 100%; |
| height: 100%; |
| cursor: pointer; |
| } |
| |
| .file-input-label { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| padding: 2rem; |
| border: 2px dashed #ddd; |
| border-radius: 6px; |
| cursor: pointer; |
| transition: var(--transition); |
| text-align: center; |
| } |
| |
| .file-input-label:hover { |
| border-color: var(--primary-color); |
| background: rgba(74, 111, 165, 0.05); |
| } |
| |
| .file-input-label i { |
| font-size: 2rem; |
| color: #888; |
| margin-bottom: 0.5rem; |
| } |
| |
| footer { |
| background: var(--dark-color); |
| color: white; |
| text-align: center; |
| padding: 1.5rem; |
| margin-top: 3rem; |
| } |
| |
| @media (max-width: 768px) { |
| .header-content { |
| flex-direction: column; |
| gap: 1rem; |
| text-align: center; |
| } |
| |
| .gallery-controls { |
| flex-direction: column; |
| align-items: stretch; |
| } |
| |
| .search-box { |
| max-width: 100%; |
| } |
| |
| .category-filter { |
| justify-content: center; |
| } |
| |
| .gallery-container { |
| grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); |
| } |
| } |
| |
| @media (max-width: 480px) { |
| .gallery-container { |
| grid-template-columns: 1fr; |
| } |
| |
| .modal-content { |
| margin: 1rem; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <header> |
| <div class="header-content"> |
| <div class="logo"> |
| <i class="fas fa-camera"></i> |
| <h1>Photo Gallery</h1> |
| </div> |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="anycoder-link" target="_blank">Built with anycoder</a> |
| </div> |
| </header> |
|
|
| <main> |
| <div class="gallery-controls"> |
| <div class="search-box"> |
| <input type="text" placeholder="Search photos..."> |
| <button><i class="fas fa-search"></i></button> |
| </div> |
| |
| <div class="category-filter"> |
| <button class="category-btn active">All</button> |
| <button class="category-btn">Nature</button> |
| <button class="category-btn">Portrait</button> |
| <button class="category-btn">Urban</button> |
| <button class="category-btn">Abstract</button> |
| </div> |
| |
| <button class="upload-btn" id="openUploadModal"> |
| <i class="fas fa-cloud-upload-alt"></i> |
| Upload Photo |
| </button> |
| </div> |
|
|
| <div class="gallery-container" id="gallery"> |
| |
| </div> |
| </main> |
|
|
| |
| <div class="modal" id="photoModal"> |
| <div class="modal-content"> |
| <div class="modal-header"> |
| <h2 class="modal-title" id="modalPhotoTitle">Photo Title</h2> |
| <button class="close-btn" id="closeModal">×</button> |
| </div> |
| <div class="modal-body"> |
| <img id="modalPhotoImg" src="" alt="" class="modal-img"> |
| <p class="modal-description" id="modalPhotoDescription">Photo description</p> |
| <div class="modal-meta"> |
| <span id="modalPhotoDate">Date</span> |
| <span id="modalPhotoCategory">Category</span> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="modal upload-modal" id="uploadModal"> |
| <div class="modal-content"> |
| <div class="modal-header"> |
| <h2 class="modal-title">Upload New Photo</h2> |
| <button class="close-btn" id="closeUploadModal">×</button> |
| </div> |
| <div class="modal-body"> |
| <form class="upload-form" id="uploadForm"> |
| <div class="form-group"> |
| <label for="photoTitle">Photo Title</label> |
| <input type="text" id="photoTitle" placeholder="Enter photo title" required> |
| </div> |
| |
| <div class="form-group"> |
| <label for="photoDescription">Description</label> |
| <textarea id="photoDescription" placeholder="Enter photo description"></textarea> |
| </div> |
| |
| <div class="form-group"> |
| <label for="photoCategory">Category</label> |
| <select id="photoCategory"> |
| <option value="nature">Nature</option> |
| <option value="portrait">Portrait</option> |
| <option value="urban">Urban</option> |
| <option value="abstract">Abstract</option> |
| </select> |
| </div> |
| |
| <div class="form-group"> |
| <label>Upload Photo</label> |
| <div class="file-input-container"> |
| <input type="file" id="photoFile" class="file-input" accept="image/*" required> |
| <label for="photoFile" class="file-input-label"> |
| <i class="fas fa-cloud-upload-alt"></i> |
| <span>Click to upload or drag and drop</span> |
| <small>PNG, JPG, GIF up to 5MB</small> |
| </label> |
| </div> |
| </div> |
| |
| <button type="submit" class="submit-btn">Upload Photo</button> |
| </form> |
| </div> |
| </div> |
| </div> |
|
|
| <footer> |
| <p>© 2023 Photo Gallery App. All rights reserved.</p> |
| </footer> |
|
|
| <script> |
| |
| const photos = [ |
| { |
| id: 1, |
| title: "Mountain Landscape", |
| description: "A breathtaking view of snow-capped mountains during sunset.", |
| category: "nature", |
| date: "2023-05-15", |
| imageUrl: "https://picsum.photos/id/1018/600/400", |
| likes: 42, |
| views: 128 |
| }, |
| { |
| id: 2, |
| title: "Urban Architecture", |
| description: "Modern city buildings with unique geometric patterns.", |
| category: "urban", |
| date: "2023-06-22", |
| imageUrl: "https://picsum.photos/id/1015/600/400", |
| likes: 28, |
| views: 95 |
| }, |
| { |
| id: 3, |
| title: "Portrait in Nature", |
| description: "A thoughtful portrait taken in a natural forest setting.", |
| category: "portrait", |
| date: "2023-04-10", |
| imageUrl: "https://picsum.photos/id/1011/600/400", |
| likes: 56, |
| views: 210 |
| }, |
| { |
| id: 4, |
| title: "Abstract Patterns", |
| description: "Colorful abstract patterns created with digital art techniques.", |
| category: "abstract", |
| date: "2023-07-05", |
| imageUrl: "https://picsum.photos/id/1019/600/400", |
| likes: 37, |
| views: 143 |
| }, |
| { |
| id: 5, |
| title: "Coastal View", |
| description: "Serene coastal landscape with rocky shores and crashing waves.", |
| category: "nature", |
| date: "2023-05-30", |
| imageUrl: "https://picsum.photos/id/1005/600/400", |
| likes: 64, |
| views: 187 |
| }, |
| { |
| id: 6, |
| title: "City Lights", |
| description: "Vibrant cityscape at night with illuminated buildings and streets.", |
| category: "urban", |
| date: "2023-06-18", |
| imageUrl: "https://picsum.photos/id/1016/600/400", |
| likes: 49, |
| views: 165 |
| }, |
| { |
| id: 7, |
| title: "Floral Close-up", |
| description: "Macro photography of delicate flowers with intricate details.", |
| category: "nature", |
| date: "2023-04-25", |
| imageUrl: "https://picsum.photos/id/1020/600/400", |
| likes: 71, |
| views: 234 |
| }, |
| { |
| id: 8, |
| title: "Minimalist Portrait", |
| description: "Clean and simple portrait with emphasis on facial expressions.", |
| category: "portrait", |
| date: "2023-07-12", |
| imageUrl: "https://picsum.photos/id/1025/600/400", |
| likes: 33, |
| views: 112 |
| } |
| ]; |
| |
| |
| const galleryContainer = document.getElementById('gallery'); |
| const photoModal = document.getElementById('photoModal'); |
| const uploadModal = document.getElementById('uploadModal'); |
| const closeModalBtn = document.getElementById('closeModal'); |
| const closeUploadModalBtn = document.getElementById('closeUploadModal'); |
| const openUploadModalBtn = document.getElementById('openUploadModal'); |
| const uploadForm = document.getElementById('uploadForm'); |
| const searchInput = document.querySelector('.search-box input'); |
| const categoryButtons = document.querySelectorAll('.category-btn'); |
| |
| |
| function initGallery() { |
| renderPhotos(photos); |
| } |
| |
| |
| function renderPhotos(photosArray) { |
| galleryContainer.innerHTML = ''; |
| |
| photosArray.forEach(photo => { |
| const photoCard = document.createElement('div'); |
| photoCard.className = 'photo-card'; |
| photoCard.innerHTML = ` |
| <img src="${photo.imageUrl}" alt="${photo.title}" class="photo-img"> |
| <div class="photo-details"> |
| <h3 class="photo-title">${photo.title}</h3> |
| <p class="photo-description">${photo.description}</p> |
| <div class="photo-meta"> |
| <span>${photo.date}</span> |
| <div class="photo-actions"> |
| <button class="action-btn"><i class="far fa-heart"></i> ${photo.likes}</button> |
| <button class="action-btn"><i class="far fa-eye"></i> ${photo.views}</button> |
| </div> |
| </div> |
| </div> |
| `; |
| |
| photoCard.addEventListener('click', () => openPhotoModal(photo)); |
| galleryContainer.appendChild(photoCard); |
| }); |
| } |
| |
| |
| function openPhotoModal(photo) { |
| document.getElementById('modalPhotoTitle').textContent = photo.title; |
| document.getElementById('modalPhotoImg').src = photo.imageUrl; |
| document.getElementById('modalPhotoImg').alt = photo.title; |
| document.getElementById('modalPhotoDescription').textContent = photo.description; |
| document.getElementById('modalPhotoDate').textContent = `Uploaded: ${photo.date}`; |
| document.getElementById('modalPhotoCategory').textContent = `Category: ${photo.category.charAt(0).toUpperCase() + photo.category.slice(1)}`; |
| |
| photoModal.style.display = 'flex'; |
| } |
| |
| |
| function openUploadModal() { |
| uploadModal.style.display = 'flex'; |
| } |
| |
| |
| function closeModals() { |
| photoModal.style.display = 'none'; |
| uploadModal.style.display = 'none'; |
| } |
| |
| |
| function filterByCategory(category) { |
| if (category === 'all') { |
| renderPhotos(photos); |
| } else { |
| const filteredPhotos = photos.filter(photo => photo.category === category); |
| renderPhotos(filteredPhotos); |
| } |
| } |
| |
| |
| function searchPhotos(query) { |
| const filteredPhotos = photos.filter(photo => |
| photo.title.toLowerCase().includes(query.toLowerCase()) || |
| photo.description.toLowerCase().includes(query.toLowerCase()) |
| ); |
| renderPhotos(filteredPhotos); |
| } |
| |
| |
| function handlePhotoUpload(event) { |
| event.preventDefault(); |
| |
| const title = document.getElementById('photoTitle').value; |
| const description = document.getElementById('photoDescription').value; |
| const category = document.getElementById('photoCategory').value; |
| const fileInput = document.getElementById('photoFile'); |
| |
| if (fileInput.files.length > 0) { |
| const file = fileInput.files[0]; |
| const reader = new FileReader(); |
| |
| reader.onload = function(e) { |
| const newPhoto = { |
| id: photos.length + 1, |
| title: title, |
| description: description, |
| category: category, |
| date: new Date().toISOString().split('T')[0], |
| imageUrl: e.target.result, |
| likes: 0, |
| views: 0 |
| }; |
| |
| photos.unshift(newPhoto); |
| renderPhotos(photos); |
| closeModals(); |
| uploadForm.reset(); |
| |
| |
| alert('Photo uploaded successfully!'); |
| }; |
| |
| reader.readAsDataURL(file); |
| } |
| } |
| |
| |
| closeModalBtn.addEventListener('click', closeModals); |
| closeUploadModalBtn.addEventListener('click', closeModals); |
| openUploadModalBtn.addEventListener('click', openUploadModal); |
| uploadForm.addEventListener('submit', handlePhotoUpload); |
| |
| |
| window.addEventListener('click', (event) => { |
| if (event.target === photoModal || event.target === uploadModal) { |
| closeModals(); |
| } |
| }); |
| |
| |
| categoryButtons.forEach(button => { |
| button.addEventListener('click', () => { |
| |
| categoryButtons.forEach(btn => btn.classList.remove('active')); |
| |
| button.classList.add('active'); |
| |
| filterByCategory(button.textContent.toLowerCase()); |
| }); |
| }); |
| |
| |
| searchInput.addEventListener('input', (event) => { |
| searchPhotos(event.target.value); |
| }); |
| |
| |
| document.addEventListener('DOMContentLoaded', initGallery); |
| </script> |
| </body> |
| </html> |