mirror of
https://github.com/usetrmnl/tailor-designs.git
synced 2026-04-29 13:45:22 -07:00
gallery tweaks
This commit is contained in:
+365
-35
@@ -11,8 +11,9 @@
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#3b82f6',
|
||||
secondary: '#1e40af',
|
||||
primary: '#3D3D3E',
|
||||
accent: '#F8654B',
|
||||
secondary: '#E7E7E7',
|
||||
dark: '#0f172a',
|
||||
light: '#f8fafc'
|
||||
}
|
||||
@@ -21,24 +22,145 @@
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.gallery-item {
|
||||
.gallery-item {
|
||||
transition: all 0.3s ease;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.gallery-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.filter-btn.active {
|
||||
background-color: #3b82f6;
|
||||
background-color: #3D3D3E;
|
||||
color: white;
|
||||
}
|
||||
.splash-filter {
|
||||
background-color: #3D3D3E;
|
||||
color: white;
|
||||
}
|
||||
.loading-filter {
|
||||
background-color: #F8654B;
|
||||
color: white;
|
||||
}
|
||||
.screen-image {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
.screen-container {
|
||||
aspect-ratio: 16/9;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
.modal-content {
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
}
|
||||
.credit-text {
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
.density-btn {
|
||||
background-color: #E7E7E7;
|
||||
border: 1px solid #3D3D3E;
|
||||
}
|
||||
.density-btn.active {
|
||||
background-color: #3D3D3E;
|
||||
color: white;
|
||||
}
|
||||
.gallery-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.filter-btn.active {
|
||||
background-color: #3D3D3E;
|
||||
color: white;
|
||||
}
|
||||
.splash-filter {
|
||||
background-color: #3D3D3E;
|
||||
color: white;
|
||||
}
|
||||
.loading-filter {
|
||||
background-color: #F8654B;
|
||||
color: white;
|
||||
}
|
||||
.screen-image {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
.screen-container {
|
||||
aspect-ratio: 16/9;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.modal-content {
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
}
|
||||
.credit-text {
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
.density-btn {
|
||||
background-color: #E7E7E7;
|
||||
border: 1px solid #3D3D3E;
|
||||
}
|
||||
.density-btn.active {
|
||||
background-color: #3D3D3E;
|
||||
color: white;
|
||||
}
|
||||
.gallery-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.filter-btn.active {
|
||||
background-color: #3D3D3E;
|
||||
color: white;
|
||||
}
|
||||
.splash-filter {
|
||||
background-color: #3D3D3E;
|
||||
color: white;
|
||||
}
|
||||
.loading-filter {
|
||||
background-color: #F8654B;
|
||||
color: white;
|
||||
}
|
||||
.screen-image {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
.screen-container {
|
||||
aspect-ratio: 16/9;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.modal-content {
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
}
|
||||
.credit-text {
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
.density-btn {
|
||||
background-color: #E7E7E7;
|
||||
border: 1px solid #3D3D3E;
|
||||
}
|
||||
.density-btn.active {
|
||||
background-color: #3D3D3E;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -51,15 +173,45 @@
|
||||
</header>
|
||||
|
||||
<main class="container mx-auto px-4 py-8">
|
||||
<!-- Filters -->
|
||||
<div class="mb-8 bg-white rounded-lg shadow-sm p-6">
|
||||
<div class="flex flex-wrap gap-4 items-center justify-between">
|
||||
<h2 class="text-xl font-semibold text-gray-900">Filter Screens</h2>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button id="all-filter" class="filter-btn px-4 py-2 rounded-lg bg-gray-100 hover:bg-blue-100 text-gray-800 hover:text-blue-700 transition-colors">All</button>
|
||||
<button id="splash-filter" class="filter-btn px-4 py-2 rounded-lg bg-gray-100 hover:bg-blue-100 text-gray-800 hover:text-blue-700 transition-colors">Splash</button>
|
||||
<button id="loading-filter" class="filter-btn px-4 py-2 rounded-lg bg-gray-100 hover:bg-blue-100 text-gray-800 hover:text-blue-700 transition-colors">Loading</button>
|
||||
<!-- Stats and Controls -->
|
||||
<div class="mb-6 bg-white rounded-lg shadow-sm p-4 flex flex-wrap justify-between items-center gap-4">
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<div class="bg-gray-100 px-3 py-1 rounded-lg">
|
||||
<span class="text-sm text-gray-600">Total Screens:</span>
|
||||
<span id="total-count" class="font-semibold ml-1">0</span>
|
||||
</div>
|
||||
<div class="bg-blue-100 px-3 py-1 rounded-lg">
|
||||
<span class="text-sm text-blue-800">Splash:</span>
|
||||
<span id="splash-count" class="font-semibold ml-1">0</span>
|
||||
</div>
|
||||
<div class="bg-orange-100 px-3 py-1 rounded-lg">
|
||||
<span class="text-sm text-orange-800">Loading:</span>
|
||||
<span id="loading-count" class="font-semibold ml-1">0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- Density Toggle -->
|
||||
<div class="flex items-center gap-1">
|
||||
<button id="density-1" class="density-btn px-3 py-1 rounded-lg text-sm" title="Dense">
|
||||
<i class="fas fa-th"></i>
|
||||
</button>
|
||||
<button id="density-2" class="density-btn px-3 py-1 rounded-lg text-sm" title="Medium">
|
||||
<i class="fas fa-th-large"></i>
|
||||
</button>
|
||||
<button id="density-3" class="density-btn px-3 py-1 rounded-lg text-sm" title="Large">
|
||||
<i class="fas fa-expand-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Filter Buttons -->
|
||||
<div class="flex gap-2">
|
||||
<button id="all-filter" class="filter-btn px-4 py-2 rounded-lg border border-gray-300 text-gray-700 hover:bg-blue-100 hover:text-blue-700 transition-colors">All</button>
|
||||
<button id="splash-filter" class="filter-btn px-4 py-2 rounded-lg border border-blue-500 text-blue-600 hover:bg-blue-100 transition-colors">Splash</button>
|
||||
<button id="loading-filter" class="filter-btn px-4 py-2 rounded-lg border border-orange-500 text-orange-600 hover:bg-orange-100 transition-colors">Loading</button>
|
||||
</div>
|
||||
|
||||
<!-- Search -->
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="text" id="search-input" placeholder="Search screens..." class="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
|
||||
<button id="clear-search" class="px-3 py-2 text-gray-500 hover:text-gray-700">
|
||||
@@ -70,7 +222,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Screen Grid -->
|
||||
<div id="gallery-container" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
<div id="gallery-container" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
<!-- Screens will be dynamically loaded here -->
|
||||
</div>
|
||||
|
||||
@@ -82,6 +234,26 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Modal for full-size view -->
|
||||
<div id="image-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center p-4 z-50 hidden">
|
||||
<div class="modal-content bg-white rounded-lg overflow-hidden max-w-4xl max-h-full">
|
||||
<div class="p-4 border-b flex justify-between items-center">
|
||||
<h3 id="modal-title" class="text-xl font-semibold"></h3>
|
||||
<button id="close-modal" class="text-gray-500 hover:text-gray-700">
|
||||
<i class="fas fa-times text-2xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4 flex justify-center">
|
||||
<img id="modal-image" src="" alt="" class="max-h-[70vh] max-w-full object-contain">
|
||||
</div>
|
||||
<div class="p-4 border-t flex justify-end gap-2">
|
||||
<button id="download-modal" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2">
|
||||
<i class="fas fa-download"></i> Download
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="bg-white border-t mt-12">
|
||||
<div class="container mx-auto px-4 py-6 text-center text-gray-500">
|
||||
<p>Screen Gallery - A collection of screen designs</p>
|
||||
@@ -128,19 +300,38 @@
|
||||
|
||||
const [resolution, type, ...nameParts] = parts;
|
||||
const [width, height] = resolution.split('x').map(Number);
|
||||
const name = nameParts.join('_').split('.')[0];
|
||||
|
||||
// Check if there's a credit part (last part before extension)
|
||||
let name = nameParts.join('_').split('.')[0];
|
||||
let credit = null;
|
||||
|
||||
// If the last part is not an extension, it might be credit
|
||||
const namePartsArray = nameParts.join('_').split('.');
|
||||
if (namePartsArray.length > 1) {
|
||||
const ext = namePartsArray[namePartsArray.length - 1];
|
||||
if (ext !== 'png') {
|
||||
// This means there's a credit part
|
||||
const fullName = nameParts.join('_');
|
||||
const lastHyphenIndex = fullName.lastIndexOf('-');
|
||||
if (lastHyphenIndex > 0) {
|
||||
name = fullName.substring(0, lastHyphenIndex);
|
||||
credit = fullName.substring(lastHyphenIndex + 1, fullName.length - 4); // Remove .png
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
filename,
|
||||
width,
|
||||
height,
|
||||
type: type.toLowerCase(),
|
||||
name
|
||||
name,
|
||||
credit
|
||||
};
|
||||
}
|
||||
|
||||
// Function to render screens
|
||||
function renderScreens(screensToRender) {
|
||||
function renderScreens(screensToRender, density = '2') {
|
||||
const container = document.getElementById('gallery-container');
|
||||
const emptyState = document.getElementById('empty-state');
|
||||
|
||||
@@ -151,25 +342,101 @@
|
||||
}
|
||||
|
||||
emptyState.classList.add('hidden');
|
||||
container.innerHTML = screensToRender.map(screen => `
|
||||
<div class="gallery-item bg-white rounded-lg shadow-sm overflow-hidden">
|
||||
<div class="screen-container flex items-center justify-center p-4 bg-gray-100">
|
||||
<img src="screens/${screen.filename}"
|
||||
alt="${screen.name}"
|
||||
class="screen-image max-h-64 object-contain"
|
||||
onerror="this.src='https://placehold.co/300x200?text=Image+Not+Found'">
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<h3 class="font-semibold text-gray-900 truncate">${screen.name.replace(/_/g, ' ')}</h3>
|
||||
<div class="flex justify-between items-center mt-2">
|
||||
<span class="text-sm text-gray-500">${screen.width}×${screen.height}</span>
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 capitalize">
|
||||
${screen.type}
|
||||
</span>
|
||||
|
||||
// Determine grid classes based on density
|
||||
let gridClass = 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4';
|
||||
let imageHeight = 'h-full';
|
||||
let cardPadding = 'p-3';
|
||||
let titleSize = 'text-base';
|
||||
|
||||
if (density === '1') { // Dense
|
||||
gridClass = 'grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5';
|
||||
imageHeight = 'h-full';
|
||||
cardPadding = 'p-1';
|
||||
titleSize = 'text-xs';
|
||||
} else if (density === '2') { // Medium
|
||||
gridClass = 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4';
|
||||
imageHeight = 'h-full';
|
||||
cardPadding = 'p-2';
|
||||
titleSize = 'text-sm';
|
||||
} else if (density === '3') { // Large
|
||||
gridClass = 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3';
|
||||
imageHeight = 'h-full';
|
||||
cardPadding = 'p-4';
|
||||
titleSize = 'text-lg';
|
||||
}
|
||||
|
||||
container.className = `grid ${gridClass} gap-2`;
|
||||
|
||||
container.innerHTML = screensToRender.map(screen => {
|
||||
const displayName = screen.name.replace(/_/g, ' ');
|
||||
|
||||
return `
|
||||
<div class="gallery-item bg-white rounded-lg shadow-sm overflow-hidden cursor-pointer" data-filename="${screen.filename}" data-name="${displayName}" data-credit="${screen.credit || ''}">
|
||||
<div class="screen-container bg-gray-100">
|
||||
<img src="screens/${screen.filename}"
|
||||
alt="${screen.name}"
|
||||
class="screen-image"
|
||||
onerror="this.src='https://placehold.co/300x200?text=Image+Not+Found'">
|
||||
</div>
|
||||
<div class="${cardPadding}">
|
||||
<h3 class="${titleSize} font-semibold text-gray-900 truncate">${displayName}</h3>
|
||||
<div class="flex justify-between items-center mt-1">
|
||||
<span class="text-xs text-gray-500">${screen.width}×${screen.height}</span>
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium
|
||||
${screen.type === 'splash' ? 'bg-blue-100 text-blue-800' : 'bg-orange-100 text-orange-800'} capitalize">
|
||||
${screen.type}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-end mt-2">
|
||||
<button class="text-xs text-green-600 hover:text-green-800 flex items-center gap-1 download-btn" data-filename="${screen.filename}">
|
||||
<i class="fas fa-download"></i> Download
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// Function to update counts
|
||||
function updateCounts(screens) {
|
||||
const totalCount = screens.length;
|
||||
const splashCount = screens.filter(s => s.type === 'splash').length;
|
||||
const loadingCount = screens.filter(s => s.type === 'loading').length;
|
||||
|
||||
document.getElementById('total-count').textContent = totalCount;
|
||||
document.getElementById('splash-count').textContent = splashCount;
|
||||
document.getElementById('loading-count').textContent = loadingCount;
|
||||
}
|
||||
|
||||
// Modal functions
|
||||
function openModal(filename, name, credit) {
|
||||
const modal = document.getElementById('image-modal');
|
||||
const modalImage = document.getElementById('modal-image');
|
||||
const modalTitle = document.getElementById('modal-title');
|
||||
const downloadBtn = document.getElementById('download-modal');
|
||||
|
||||
modalImage.src = `screens/${filename}`;
|
||||
modalTitle.textContent = name;
|
||||
|
||||
// Set download button to use the correct filename
|
||||
downloadBtn.onclick = function() {
|
||||
// Create a temporary link to trigger download
|
||||
const link = document.createElement('a');
|
||||
link.href = `screens/${filename}`;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
|
||||
modal.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
const modal = document.getElementById('image-modal');
|
||||
modal.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Filter and search functionality
|
||||
@@ -190,7 +457,8 @@
|
||||
if (searchTerm) {
|
||||
filteredScreens = filteredScreens.filter(screen =>
|
||||
screen.name.toLowerCase().includes(searchTerm) ||
|
||||
`${screen.width}×${screen.height}`.toLowerCase().includes(searchTerm)
|
||||
`${screen.width}×${screen.height}`.toLowerCase().includes(searchTerm) ||
|
||||
(screen.credit && screen.credit.toLowerCase().includes(searchTerm))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -202,7 +470,7 @@
|
||||
// Parse filenames to get metadata
|
||||
const parsedScreens = screens.map(screen => parseFilename(screen.filename));
|
||||
|
||||
// Set up filter buttons
|
||||
// Set up filter buttons - single select with outlined style
|
||||
document.querySelectorAll('.filter-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
// Remove active class from all buttons
|
||||
@@ -216,6 +484,31 @@
|
||||
});
|
||||
});
|
||||
|
||||
// Set up density toggle buttons
|
||||
document.getElementById('density-1').addEventListener('click', function() {
|
||||
document.querySelectorAll('.density-btn').forEach(btn => btn.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
renderScreens(parsedScreens, '1');
|
||||
});
|
||||
|
||||
document.getElementById('density-2').addEventListener('click', function() {
|
||||
document.querySelectorAll('.density-btn').forEach(btn => btn.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
renderScreens(parsedScreens, '2');
|
||||
});
|
||||
|
||||
document.getElementById('density-3').addEventListener('click', function() {
|
||||
document.querySelectorAll('.density-btn').forEach(btn => btn.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
renderScreens(parsedScreens, '3');
|
||||
});
|
||||
|
||||
// Set initial active state for density buttons
|
||||
document.getElementById('density-2').classList.add('active');
|
||||
|
||||
// Set initial active filter to ALL
|
||||
document.getElementById('all-filter').classList.add('active');
|
||||
|
||||
// Set up search functionality
|
||||
const searchInput = document.getElementById('search-input');
|
||||
searchInput.addEventListener('input', filterScreens);
|
||||
@@ -226,7 +519,44 @@
|
||||
filterScreens();
|
||||
});
|
||||
|
||||
// Close modal
|
||||
document.getElementById('close-modal').addEventListener('click', closeModal);
|
||||
|
||||
// Close modal when clicking outside
|
||||
document.getElementById('image-modal').addEventListener('click', function(e) {
|
||||
if (e.target === this) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Set up download buttons
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.classList.contains('download-btn') || e.target.closest('.download-btn')) {
|
||||
const btn = e.target.classList.contains('download-btn') ? e.target : e.target.closest('.download-btn');
|
||||
const filename = btn.getAttribute('data-filename');
|
||||
// Create a temporary link to trigger download
|
||||
const link = document.createElement('a');
|
||||
link.href = `screens/${filename}`;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
});
|
||||
|
||||
// Set up thumbnail click to open modal
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.closest('.gallery-item') && !e.target.classList.contains('download-btn')) {
|
||||
const item = e.target.closest('.gallery-item');
|
||||
const filename = item.getAttribute('data-filename');
|
||||
const name = item.getAttribute('data-name');
|
||||
const credit = item.getAttribute('data-credit');
|
||||
openModal(filename, name, credit);
|
||||
}
|
||||
});
|
||||
|
||||
// Initial render
|
||||
updateCounts(parsedScreens);
|
||||
renderScreens(parsedScreens);
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user