feat: implement responsive card layout with category filtering and consumption status
This commit is contained in:
parent
40ed683c5f
commit
e80e9a54fe
@ -8,15 +8,162 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<style>
|
||||
/* Sidebar Menu Styles */
|
||||
.sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #fff;
|
||||
overflow-x: hidden;
|
||||
transition: 0.3s;
|
||||
padding-top: 60px;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
.sidebar.open {
|
||||
width: 250px;
|
||||
}
|
||||
.sidebar .close-btn {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 20px;
|
||||
font-size: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.sidebar-heading {
|
||||
padding: 8px 16px 20px 16px;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 500;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.sidebar-menu {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.sidebar-menu li {
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
.sidebar-menu li:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.sidebar-menu li.active {
|
||||
background-color: #e3f2fd;
|
||||
color: #1976d2;
|
||||
font-weight: 500;
|
||||
}
|
||||
.sidebar-section {
|
||||
padding: 12px 16px 8px;
|
||||
font-weight: 500;
|
||||
color: #757575;
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
z-index: 99;
|
||||
}
|
||||
.overlay.visible {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="sidebar" class="sidebar">
|
||||
<span class="close-btn">×</span>
|
||||
<div class="sidebar-heading">My Favorite Stuff</div>
|
||||
<div class="sidebar-section">View Options</div>
|
||||
<ul class="sidebar-menu" id="view-options">
|
||||
<li data-filter="consumed" class="active">Consumed Items</li>
|
||||
<li data-filter="not-consumed">Not Consumed Yet</li>
|
||||
<li data-filter="all">Show All Items</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="overlay" class="overlay"></div>
|
||||
|
||||
<header>
|
||||
<span style="font-size:1.7rem;cursor:pointer;margin-right:18px;">☰</span>
|
||||
<span id="menu-toggle" style="font-size:1.7rem;cursor:pointer;margin-right:18px;">☰</span>
|
||||
<h1 style="margin:0;font-size:2rem;font-weight:500;">My Favorite Stuff</h1>
|
||||
</header>
|
||||
<main>
|
||||
{{ block "main" . }}{{ end }}
|
||||
</main>
|
||||
<div class="fab">+</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const menuToggle = document.getElementById('menu-toggle');
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
const closeBtn = document.querySelector('.close-btn');
|
||||
const overlay = document.getElementById('overlay');
|
||||
const viewOptions = document.querySelectorAll('#view-options li');
|
||||
|
||||
// Set the initial filter based on the selected sidebar option
|
||||
let currentConsumedFilter = 'consumed';
|
||||
|
||||
// Functions to open and close the sidebar
|
||||
function openSidebar() {
|
||||
sidebar.classList.add('open');
|
||||
overlay.classList.add('visible');
|
||||
}
|
||||
|
||||
function closeSidebar() {
|
||||
sidebar.classList.remove('open');
|
||||
overlay.classList.remove('visible');
|
||||
}
|
||||
|
||||
// Toggle sidebar when hamburger is clicked
|
||||
menuToggle.addEventListener('click', openSidebar);
|
||||
|
||||
// Close sidebar when X is clicked
|
||||
closeBtn.addEventListener('click', closeSidebar);
|
||||
|
||||
// Close sidebar when clicking outside
|
||||
overlay.addEventListener('click', closeSidebar);
|
||||
|
||||
// Handle view option clicks
|
||||
viewOptions.forEach(option => {
|
||||
option.addEventListener('click', function() {
|
||||
// Update active class
|
||||
viewOptions.forEach(opt => opt.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
// Set filter and apply
|
||||
currentConsumedFilter = this.getAttribute('data-filter');
|
||||
applyConsumedFilter(currentConsumedFilter);
|
||||
closeSidebar();
|
||||
});
|
||||
});
|
||||
|
||||
// Function to apply the filter - will be called by list.html and index.html scripts
|
||||
window.applyConsumedFilter = function(filterType) {
|
||||
// Make this function available to other scripts
|
||||
window.currentConsumedFilter = filterType;
|
||||
|
||||
// Dispatch a custom event that list.html and index.html can listen for
|
||||
document.dispatchEvent(new CustomEvent('consumedFilterChanged', {
|
||||
detail: { filterType: filterType }
|
||||
}));
|
||||
};
|
||||
|
||||
// Initialize with the default filter
|
||||
window.currentConsumedFilter = 'consumed';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -59,6 +59,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
console.log('FUNC activateTabAndFilter (index.html): Active states updated for tabs.');
|
||||
|
||||
// Get the consumed filter status from window global
|
||||
const consumedFilter = window.currentConsumedFilter || 'consumed';
|
||||
console.log('FUNC activateTabAndFilter (index.html): Current consumed filter is:', consumedFilter);
|
||||
|
||||
let visibleCount = 0;
|
||||
allCards.forEach(cardLinkElement => {
|
||||
const cardDiv = cardLinkElement.querySelector('.card');
|
||||
@ -68,23 +72,45 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check category visibility first
|
||||
let categoryVisible = false;
|
||||
if (selectedCategory === 'all') {
|
||||
cardLinkElement.style.display = '';
|
||||
visibleCount++;
|
||||
categoryVisible = true;
|
||||
} else {
|
||||
const cardCategoryAttr = cardDiv.getAttribute('data-category');
|
||||
const sCat = selectedCategory ? selectedCategory : 'null_or_empty';
|
||||
const cCat = cardCategoryAttr ? cardCategoryAttr : 'null_or_empty';
|
||||
console.log(
|
||||
`FUNC activateTabAndFilter (index.html): Comparing: cardCategory='${cCat}' (length ${cCat.length})` +
|
||||
` vs selectedCategory='${sCat}' (length ${sCat.length})` +
|
||||
` for card titled: '${cardDiv.querySelector('.card-title') ? cardDiv.querySelector('.card-title').textContent.trim() : 'N/A'}'`
|
||||
);
|
||||
const isVisible = (cardCategoryAttr === selectedCategory);
|
||||
cardLinkElement.style.display = isVisible ? '' : 'none';
|
||||
if (isVisible) visibleCount++;
|
||||
categoryVisible = (cardCategoryAttr === selectedCategory);
|
||||
}
|
||||
|
||||
// Now check consumed status
|
||||
const cardData = cardLinkElement.querySelector('meta[name="card-data"]');
|
||||
let consumedStatus;
|
||||
if (cardData) {
|
||||
try {
|
||||
const cardDataObj = JSON.parse(cardData.getAttribute('content'));
|
||||
consumedStatus = cardDataObj.consumed;
|
||||
} catch (e) {
|
||||
console.warn('FUNC activateTabAndFilter (index.html): Error parsing card data:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine visibility based on consumed filter
|
||||
let consumedVisible = true; // Default to true if no consumed status is specified
|
||||
|
||||
if (consumedFilter === 'consumed') {
|
||||
// Show only consumed=true or consumed not specified
|
||||
consumedVisible = (consumedStatus !== false);
|
||||
} else if (consumedFilter === 'not-consumed') {
|
||||
// Show only consumed=false
|
||||
consumedVisible = (consumedStatus === false);
|
||||
}
|
||||
// If consumedFilter is 'all', then consumedVisible remains true
|
||||
|
||||
// Final visibility is the combination of both filters
|
||||
const isVisible = categoryVisible && consumedVisible;
|
||||
cardLinkElement.style.display = isVisible ? '' : 'none';
|
||||
if (isVisible) visibleCount++;
|
||||
});
|
||||
|
||||
console.log('FUNC activateTabAndFilter (index.html): Visible cards after filtering:', visibleCount);
|
||||
}
|
||||
|
||||
@ -114,6 +140,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
activateTabAndFilter(initialCategoryToDisplay);
|
||||
console.log('SCRIPT (index.html): Initial page load filtering complete.');
|
||||
|
||||
// Listen for consumed filter changes from the sidebar
|
||||
document.addEventListener('consumedFilterChanged', function(event) {
|
||||
console.log('EVENT (index.html): consumedFilterChanged received with filterType:', event.detail.filterType);
|
||||
// Re-apply the current category filter when the consumed filter changes
|
||||
const currentCategory = document.querySelector('#tabs .tab.active').getAttribute('data-tab');
|
||||
activateTabAndFilter(currentCategory);
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
{{ end }}
|
||||
|
||||
@ -89,6 +89,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
console.log('FUNC activateTabAndFilter: Active states updated for tabs.');
|
||||
|
||||
// Get the consumed filter status from window global
|
||||
const consumedFilter = window.currentConsumedFilter || 'consumed';
|
||||
console.log('FUNC activateTabAndFilter: Current consumed filter is:', consumedFilter);
|
||||
|
||||
let visibleCount = 0;
|
||||
allCards.forEach(cardLinkElement => {
|
||||
const cardDiv = cardLinkElement.querySelector('.card');
|
||||
@ -98,23 +102,45 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check category visibility first
|
||||
let categoryVisible = false;
|
||||
if (selectedCategory === 'all') {
|
||||
cardLinkElement.style.display = '';
|
||||
visibleCount++;
|
||||
categoryVisible = true;
|
||||
} else {
|
||||
const cardCategoryAttr = cardDiv.getAttribute('data-category');
|
||||
const sCat = selectedCategory ? selectedCategory : 'null_or_empty';
|
||||
const cCat = cardCategoryAttr ? cardCategoryAttr : 'null_or_empty';
|
||||
console.log(
|
||||
`FUNC activateTabAndFilter: Comparing: cardCategory='${cCat}' (length ${cCat.length})` +
|
||||
` vs selectedCategory='${sCat}' (length ${sCat.length})` +
|
||||
` for card titled: '${cardDiv.querySelector('.card-title') ? cardDiv.querySelector('.card-title').textContent.trim() : 'N/A'}'`
|
||||
);
|
||||
const isVisible = (cardCategoryAttr === selectedCategory);
|
||||
cardLinkElement.style.display = isVisible ? '' : 'none';
|
||||
if (isVisible) visibleCount++;
|
||||
categoryVisible = (cardCategoryAttr === selectedCategory);
|
||||
}
|
||||
|
||||
// Now check consumed status
|
||||
const cardData = cardLinkElement.querySelector('meta[name="card-data"]');
|
||||
let consumedStatus;
|
||||
if (cardData) {
|
||||
try {
|
||||
const cardDataObj = JSON.parse(cardData.getAttribute('content'));
|
||||
consumedStatus = cardDataObj.consumed;
|
||||
} catch (e) {
|
||||
console.warn('FUNC activateTabAndFilter: Error parsing card data:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine visibility based on consumed filter
|
||||
let consumedVisible = true; // Default to true if no consumed status is specified
|
||||
|
||||
if (consumedFilter === 'consumed') {
|
||||
// Show only consumed=true or consumed not specified
|
||||
consumedVisible = (consumedStatus !== false);
|
||||
} else if (consumedFilter === 'not-consumed') {
|
||||
// Show only consumed=false
|
||||
consumedVisible = (consumedStatus === false);
|
||||
}
|
||||
// If consumedFilter is 'all', then consumedVisible remains true
|
||||
|
||||
// Final visibility is the combination of both filters
|
||||
const isVisible = categoryVisible && consumedVisible;
|
||||
cardLinkElement.style.display = isVisible ? '' : 'none';
|
||||
if (isVisible) visibleCount++;
|
||||
});
|
||||
|
||||
console.log('FUNC activateTabAndFilter: Visible cards after filtering:', visibleCount);
|
||||
}
|
||||
|
||||
@ -146,6 +172,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
activateTabAndFilter(initialCategoryToDisplay);
|
||||
console.log('SCRIPT: Initial page load filtering complete.');
|
||||
|
||||
// Listen for consumed filter changes from the sidebar
|
||||
document.addEventListener('consumedFilterChanged', function(event) {
|
||||
console.log('EVENT: consumedFilterChanged received in list.html with filterType:', event.detail.filterType);
|
||||
// Re-apply the current category filter when the consumed filter changes
|
||||
const currentCategory = document.querySelector('#tabs .tab.active').getAttribute('data-tab');
|
||||
activateTabAndFilter(currentCategory);
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
{{ end }}
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
<a class="card-link-outer" href="{{ .RelPermalink }}">
|
||||
<meta name="card-data" content='{{ jsonify (dict "consumed" .Params.consumed) }}'>
|
||||
<div class="card" data-category="{{ .Params.category }}">
|
||||
{{ with .Params.image }}
|
||||
<img src="{{ . }}" alt="{{ $.Title }} cover">
|
||||
{{ else }}
|
||||
<img src="/img/no-cover-available-yet.png" alt="{{ $.Title }} - No cover available">
|
||||
{{ end }}
|
||||
{{ if eq .Params.consumed false }}
|
||||
<div class="not-consumed-badge">Not {{ if eq .Params.category "Books" }}Read{{ else if eq .Params.category "Movies" }}Watched{{ else if eq .Params.category "TV Series" }}Watched{{ else if eq .Params.category "Restaurants" }}Visited{{ else if eq .Params.category "Recipes" }}Made{{ else if eq .Params.category "Concerts" }}Attended{{ else }}Experienced{{ end }} Yet</div>
|
||||
{{ end }}
|
||||
<div class="card-title">{{ .Title }}</div>
|
||||
{{ with .Params.rating }}<div class="card-rating">{{ partial "stars.html" (dict "rating" .) }} {{ . }}</div>{{ end }}
|
||||
{{ with .Params.likes }}<div class="card-likes">👍 {{ . }}</div>{{ end }}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user