Merge branch 'v4' into chl/xeno_grant

This commit is contained in:
vintro 2024-12-18 10:45:44 -05:00
commit d6bcf44d83
No known key found for this signature in database
3 changed files with 259 additions and 32 deletions

View File

@ -40,9 +40,12 @@ export const PageList: QuartzComponent = ({ cfg, fileData, allFiles, limit }: Pr
{list.map((page) => { {list.map((page) => {
const title = page.frontmatter?.title const title = page.frontmatter?.title
const tags = page.frontmatter?.tags ?? [] const tags = page.frontmatter?.tags ?? []
const tagsStr = JSON.stringify(tags)
const href = resolveRelative(fileData.slug!, page.slug!)
return ( return (
<li class="section-li"> <li class="section-li" data-tags={tagsStr}>
<a href={href} class="section-link">
<div class="section"> <div class="section">
{page.dates && ( {page.dates && (
<p class="meta"> <p class="meta">
@ -50,25 +53,10 @@ export const PageList: QuartzComponent = ({ cfg, fileData, allFiles, limit }: Pr
</p> </p>
)} )}
<div class="desc"> <div class="desc">
<h3> <h3>{title}</h3>
<a href={resolveRelative(fileData.slug!, page.slug!)} class="internal">
{title}
</a>
</h3>
</div> </div>
<ul class="tags">
{tags.map((tag) => (
<li>
<a
class="internal tag-link"
href={resolveRelative(fileData.slug!, `tags/${tag}` as FullSlug)}
>
{tag}
</a>
</li>
))}
</ul>
</div> </div>
</a>
</li> </li>
) )
})} })}
@ -80,8 +68,4 @@ PageList.css = `
.section h3 { .section h3 {
margin: 0; margin: 0;
} }
.section > .tags {
margin: 0;
}
` `

View File

@ -33,8 +33,17 @@ export default ((opts?: Partial<FolderContentOptions>) => {
const isDirectChild = fileParts.length === folderParts.length + 1 const isDirectChild = fileParts.length === folderParts.length + 1
return prefixed && isDirectChild return prefixed && isDirectChild
}) })
// Get all unique tags
const allTags = new Set<string>()
allPagesInFolder.forEach(file => {
const tags = file.frontmatter?.tags ?? []
tags.forEach(tag => allTags.add(tag))
})
const sortedTags = Array.from(allTags).sort()
const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? [] const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? []
const classes = ["popover-hint", ...cssClasses].join(" ") const classes = ["popover-hint", "disable-card-preview", ...cssClasses].join(" ")
const listProps = { const listProps = {
...props, ...props,
allFiles: allPagesInFolder, allFiles: allPagesInFolder,
@ -45,14 +54,103 @@ export default ((opts?: Partial<FolderContentOptions>) => {
? fileData.description ? fileData.description
: htmlToJsx(fileData.filePath!, tree) : htmlToJsx(fileData.filePath!, tree)
// Add client-side filtering script
const filterScript = `
let isInitialized = false;
function initializeTagFiltering() {
// Prevent double initialization
if (isInitialized) {
return;
}
const selectedTags = new Set()
const cards = document.querySelectorAll('.section-li')
const countEl = document.querySelector('.folder-count')
const tagButtons = document.querySelectorAll('.tag-filter-btn')
if (!cards.length || !tagButtons.length) {
return; // Don't initialize if elements aren't present
}
isInitialized = true;
function updateVisibility() {
let visibleCount = 0
cards.forEach(card => {
try {
const tagsAttr = card.getAttribute('data-tags')
const tags = JSON.parse(tagsAttr || '[]')
const shouldShow = selectedTags.size === 0 ||
tags.some(tag => selectedTags.has(tag))
card.style.display = shouldShow ? '' : 'none'
if (shouldShow) visibleCount++
} catch (e) {
console.error('Error processing card:', e)
}
})
if (countEl) {
const countText = ${JSON.stringify(i18n(cfg.locale).pages.folderContent.itemsUnderFolder({count: 0}))}
countEl.textContent = countText.replace('0', visibleCount.toString())
}
}
tagButtons.forEach(btn => {
// Remove any existing listeners first
const newBtn = btn.cloneNode(true)
btn.parentNode.replaceChild(newBtn, btn)
newBtn.addEventListener('click', () => {
const tag = newBtn.getAttribute('data-tag')
if (selectedTags.has(tag)) {
selectedTags.delete(tag)
newBtn.classList.remove('selected')
} else {
selectedTags.add(tag)
newBtn.classList.add('selected')
}
updateVisibility()
})
})
}
// Initialize on first load
initializeTagFiltering()
// Handle client-side navigation
document.addEventListener('nav', () => {
isInitialized = false;
setTimeout(initializeTagFiltering, 0)
})
// Backup initialization for edge cases
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeTagFiltering)
} else {
setTimeout(initializeTagFiltering, 0)
}
`
return ( return (
<div class={classes}> <div class={classes}>
<article> <article>
<p>{content}</p> <p>{content}</p>
</article> </article>
<div class="page-listing"> <div class="page-listing">
<div class="tag-filter">
{sortedTags.map(tag => (
<button
data-tag={tag}
class="tag-filter-btn"
type="button"
>
{tag}
</button>
))}
</div>
{options.showFolderCount && ( {options.showFolderCount && (
<p> <p class="folder-count">
{i18n(cfg.locale).pages.folderContent.itemsUnderFolder({ {i18n(cfg.locale).pages.folderContent.itemsUnderFolder({
count: allPagesInFolder.length, count: allPagesInFolder.length,
})} })}
@ -62,6 +160,7 @@ export default ((opts?: Partial<FolderContentOptions>) => {
<PageList {...listProps} /> <PageList {...listProps} />
</div> </div>
</div> </div>
<script dangerouslySetInnerHTML={{ __html: filterScript }} />
</div> </div>
) )
} }

View File

@ -545,3 +545,147 @@ input {
padding: 0.5rem; padding: 0.5rem;
} }
// Blog listing styles
.page-listing {
margin-top: 2rem;
.tag-filter {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 2rem;
.tag-filter-btn {
padding: 0.4rem 0.8rem;
border-radius: 20px;
border: 1px solid var(--lightgray);
background: var(--light);
color: var(--gray);
font-size: 0.9rem;
font-family: var(--bodyFont);
cursor: pointer;
transition: all 0.2s ease;
&:hover {
border-color: var(--secondary);
color: var(--tertiary);
background-color: var(--highlight);
}
&.selected {
background: var(--secondary);
color: var(--light);
border-color: var(--secondary);
}
&::before {
content: "#";
opacity: 0.7;
margin-right: 0.2rem;
}
}
}
.section-ul {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
margin-top: 1.5rem;
}
.section-li {
margin: 0;
transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
border-radius: 12px;
background: var(--light);
border: 1px solid var(--lightgray);
overflow: hidden;
position: relative;
.section-link {
text-decoration: none;
color: var(--darkgray);
background: none !important;
display: block;
height: 100%;
&:hover {
background: none !important;
color: var(--darkgray);
}
}
&:hover {
transform: translateY(-3px);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
background-color: var(--highlight);
.section {
.desc {
h3 {
color: var(--tertiary);
}
}
}
}
.section {
display: flex;
flex-direction: column;
height: 100%;
padding: 1.25rem;
gap: 0.7rem;
.meta {
order: -1;
font-size: 0.9rem;
color: var(--gray);
}
.desc {
flex: 1;
h3 {
margin: 0;
font-size: 1.3rem;
line-height: 1.4;
transition: color 0.2s ease;
color: var(--secondary);
}
}
}
.internal {
&[href*=".html"],
&:not([href*="."]) {
&::before, &::after {
display: none !important;
}
}
// Keep the hover effect
&:hover {
color: var(--tertiary);
}
}
}
}
// Hide tags in tag page cards
[data-slug^="tags/"] .section-li .section .tags {
display: none;
}
// Disable preview popups for folder listing cards
.disable-card-preview {
.popover {
display: none !important;
}
.internal {
&::before, &::after {
display: none !important;
}
}
}