initial commit
This commit is contained in:
233
layouts/_default/list.html
Normal file
233
layouts/_default/list.html
Normal file
@@ -0,0 +1,233 @@
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ if .Description }}
|
||||
<p class="lead">{{ .Description }}</p>
|
||||
{{ end }}
|
||||
|
||||
{{ .Content }}
|
||||
|
||||
{{ if .Pages }}
|
||||
<div class="filter-controls">
|
||||
<div class="filter-row">
|
||||
<input type="text" id="searchInput" placeholder="Search titles and descriptions..." class="search-input">
|
||||
<select id="tagFilter" class="tag-filter">
|
||||
<option value="">All tags</option>
|
||||
</select>
|
||||
<select id="sortBy" class="sort-select">
|
||||
<option value="title">Sort by Title</option>
|
||||
<option value="date">Sort by Date</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid" id="cardGrid">
|
||||
{{ range .Pages }}
|
||||
<div class="card"
|
||||
data-title="{{ .Title }}"
|
||||
data-description="{{ if .Description }}{{ .Description }}{{ else if .Summary }}{{ .Summary | plainify | replaceRE "^### \\*\\*Summary\\*\\*\\s*" "" | truncate 200 }}{{ else }}{{ .Content | plainify | replaceRE "^### \\*\\*Summary\\*\\*\\s*" "" | truncate 200 }}{{ end }}"
|
||||
data-tags="{{ if .Params.tags }}{{ delimit .Params.tags "," }}{{ end }}"
|
||||
data-date="{{ .Date.Format "2006-01-02" }}">
|
||||
{{ if .Params.image }}
|
||||
<img src="{{ .Params.image }}" alt="{{ .Title }} logo" class="case-logo" />
|
||||
{{ end }}
|
||||
<h3><a href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
|
||||
{{ if .Description }}
|
||||
<p>{{ .Description }}</p>
|
||||
{{ else if .Summary }}
|
||||
<p>{{ .Summary | plainify | replaceRE "^### \\*\\*Summary\\*\\*\\s*" "" | truncate 200 }}</p>
|
||||
{{ else }}
|
||||
<p>{{ .Content | plainify | replaceRE "^### \\*\\*Summary\\*\\*\\s*" "" | truncate 200 }}</p>
|
||||
{{ end }}
|
||||
{{ if .Params.tags }}
|
||||
<div class="tags">
|
||||
{{ range .Params.tags }}
|
||||
<span class="tag">{{ . }}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
<a href="{{ .RelPermalink }}" class="btn">Read More</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<style>
|
||||
.lead {
|
||||
font-size: 1.125rem;
|
||||
color: #666;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.filter-controls {
|
||||
margin-bottom: 48px;
|
||||
padding: 24px;
|
||||
background: var(--card-background);
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 4px 12px var(--shadow);
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-input, .tag-filter, .sort-select {
|
||||
font-family: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
padding: 10px 16px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 24px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(8px);
|
||||
transition: all 0.2s ease;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
min-width: 280px;
|
||||
}
|
||||
|
||||
.search-input:focus, .tag-filter:focus, .sort-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--e2c-yellow);
|
||||
background: var(--e2c-yellow);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: var(--text-secondary);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.tag-filter, .sort-select {
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
.tags {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
background: #e9ecef;
|
||||
color: #495057;
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin: 0.25rem 0.25rem 0 0;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.card.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.filter-controls {
|
||||
margin-bottom: 32px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.search-input, .tag-filter, .sort-select {
|
||||
width: 100%;
|
||||
min-width: unset;
|
||||
padding: 8px 14px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const tagFilter = document.getElementById('tagFilter');
|
||||
const sortBy = document.getElementById('sortBy');
|
||||
const cardGrid = document.getElementById('cardGrid');
|
||||
const cards = Array.from(cardGrid.querySelectorAll('.card'));
|
||||
|
||||
// Collect all unique tags
|
||||
const allTags = new Set();
|
||||
cards.forEach(card => {
|
||||
const tags = card.dataset.tags;
|
||||
if (tags) {
|
||||
tags.split(',').forEach(tag => allTags.add(tag.trim()));
|
||||
}
|
||||
});
|
||||
|
||||
// Populate tag filter dropdown
|
||||
Array.from(allTags).sort().forEach(tag => {
|
||||
const option = document.createElement('option');
|
||||
option.value = tag;
|
||||
option.textContent = tag;
|
||||
tagFilter.appendChild(option);
|
||||
});
|
||||
|
||||
function filterAndSort() {
|
||||
const searchTerm = searchInput.value.toLowerCase();
|
||||
const selectedTag = tagFilter.value;
|
||||
const sortCriteria = sortBy.value;
|
||||
|
||||
// Filter cards
|
||||
const filteredCards = cards.filter(card => {
|
||||
const title = card.dataset.title.toLowerCase();
|
||||
const description = card.dataset.description.toLowerCase();
|
||||
const tags = card.dataset.tags;
|
||||
|
||||
// Text search
|
||||
const matchesSearch = !searchTerm ||
|
||||
title.includes(searchTerm) ||
|
||||
description.includes(searchTerm);
|
||||
|
||||
// Tag filter
|
||||
const matchesTag = !selectedTag ||
|
||||
(tags && tags.split(',').map(t => t.trim()).includes(selectedTag));
|
||||
|
||||
return matchesSearch && matchesTag;
|
||||
});
|
||||
|
||||
// Sort filtered cards
|
||||
filteredCards.sort((a, b) => {
|
||||
if (sortCriteria === 'title') {
|
||||
return a.dataset.title.localeCompare(b.dataset.title);
|
||||
} else if (sortCriteria === 'date') {
|
||||
const dateA = new Date(a.dataset.date || '1970-01-01');
|
||||
const dateB = new Date(b.dataset.date || '1970-01-01');
|
||||
return dateB - dateA; // Most recent first
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
// Hide all cards first
|
||||
cards.forEach(card => card.classList.add('hidden'));
|
||||
|
||||
// Show and reorder filtered cards
|
||||
filteredCards.forEach((card, index) => {
|
||||
card.classList.remove('hidden');
|
||||
card.style.order = index;
|
||||
});
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
searchInput.addEventListener('input', filterAndSort);
|
||||
tagFilter.addEventListener('change', filterAndSort);
|
||||
sortBy.addEventListener('change', filterAndSort);
|
||||
|
||||
// Initial sort by title
|
||||
filterAndSort();
|
||||
});
|
||||
</script>
|
||||
{{ end }}
|
Reference in New Issue
Block a user