mirror of
https://github.com/svemagie/blog-eleventy-indiekit.git
synced 2026-04-02 16:44:56 +02:00
Add color-coded left borders to post cards on all blog listing and category pages, and make sidebar widgets collapsible with localStorage persistence on both listing and single-post sidebars.
240 lines
16 KiB
Plaintext
240 lines
16 KiB
Plaintext
{# Blog Sidebar - Shown on individual post pages #}
|
|
{# Data-driven when homepageConfig.blogPostSidebar is configured, otherwise falls back to default widgets #}
|
|
{# Each widget is wrapped in a collapsible container with localStorage persistence #}
|
|
|
|
{% if homepageConfig and homepageConfig.blogPostSidebar and homepageConfig.blogPostSidebar.length %}
|
|
{# === Data-driven mode: render configured widgets === #}
|
|
{% for widget in homepageConfig.blogPostSidebar %}
|
|
|
|
{# Resolve widget title #}
|
|
{% if widget.type == "search" %}{% set widgetTitle = "Search" %}
|
|
{% elif widget.type == "social-activity" %}{% set widgetTitle = "Social Activity" %}
|
|
{% elif widget.type == "github-repos" %}{% set widgetTitle = "GitHub" %}
|
|
{% elif widget.type == "funkwhale" %}{% set widgetTitle = "Listening" %}
|
|
{% elif widget.type == "recent-posts" %}{% set widgetTitle = "Recent Posts" %}
|
|
{% elif widget.type == "blogroll" %}{% set widgetTitle = "Blogroll" %}
|
|
{% elif widget.type == "feedland" %}{% set widgetTitle = "FeedLand" %}
|
|
{% elif widget.type == "categories" %}{% set widgetTitle = "Categories" %}
|
|
{% elif widget.type == "webmentions" %}{% set widgetTitle = "Webmentions" %}
|
|
{% elif widget.type == "recent-comments" %}{% set widgetTitle = "Recent Comments" %}
|
|
{% elif widget.type == "fediverse-follow" %}{% set widgetTitle = "Fediverse" %}
|
|
{% elif widget.type == "author-card" %}{% set widgetTitle = "Author" %}
|
|
{% elif widget.type == "author-card-compact" %}{% set widgetTitle = "Author" %}
|
|
{% elif widget.type == "subscribe" %}{% set widgetTitle = "Subscribe" %}
|
|
{% elif widget.type == "toc" %}{% set widgetTitle = "Table of Contents" %}
|
|
{% elif widget.type == "post-categories" %}{% set widgetTitle = "Categories" %}
|
|
{% elif widget.type == "share" %}{% set widgetTitle = "Share" %}
|
|
{% elif widget.type == "custom-html" %}{% set widgetTitle = (widget.config.title if widget.config and widget.config.title) or "Custom" %}
|
|
{% else %}{% set widgetTitle = widget.type %}
|
|
{% endif %}
|
|
|
|
{% set widgetKey = "post-widget-" + widget.type + "-" + loop.index0 %}
|
|
{% set defaultOpen = "true" if loop.index0 < 3 else "false" %}
|
|
|
|
{# Collapsible wrapper — Alpine.js handles toggle, localStorage persists state #}
|
|
<div
|
|
class="widget-collapsible mb-4"
|
|
x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : {{ defaultOpen }} }"
|
|
>
|
|
<div class="bg-white dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
|
|
<button
|
|
class="widget-header w-full p-4"
|
|
@click="open = !open; localStorage.setItem('{{ widgetKey }}', open)"
|
|
:aria-expanded="open ? 'true' : 'false'"
|
|
>
|
|
<h3 class="widget-title font-bold text-lg">{{ widgetTitle }}</h3>
|
|
<svg
|
|
class="widget-chevron"
|
|
:class="open && 'rotate-180'"
|
|
fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
|
>
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
</button>
|
|
|
|
<div
|
|
x-show="open"
|
|
x-transition:enter="transition ease-out duration-150"
|
|
x-transition:enter-start="opacity-0"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in duration-100"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
x-cloak
|
|
>
|
|
{# Widget content — inner .widget provides padding, inner title hidden by CSS #}
|
|
{% if widget.type == "author-card-compact" %}
|
|
{% include "components/widgets/author-card-compact.njk" %}
|
|
{% elif widget.type == "author-card" %}
|
|
{% include "components/widgets/author-card.njk" %}
|
|
{% elif widget.type == "toc" %}
|
|
{% include "components/widgets/toc.njk" %}
|
|
{% elif widget.type == "post-categories" %}
|
|
{% include "components/widgets/post-categories.njk" %}
|
|
{% elif widget.type == "recent-posts" %}
|
|
{% include "components/widgets/recent-posts-blog.njk" %}
|
|
{% elif widget.type == "webmentions" %}
|
|
{% include "components/widgets/webmentions.njk" %}
|
|
{% elif widget.type == "share" %}
|
|
{% include "components/widgets/share.njk" %}
|
|
{% elif widget.type == "subscribe" %}
|
|
{% include "components/widgets/subscribe.njk" %}
|
|
{% elif widget.type == "social-activity" %}
|
|
{% include "components/widgets/social-activity.njk" %}
|
|
{% elif widget.type == "github-repos" %}
|
|
{% include "components/widgets/github-repos.njk" %}
|
|
{% elif widget.type == "funkwhale" %}
|
|
{% include "components/widgets/funkwhale.njk" %}
|
|
{% elif widget.type == "blogroll" %}
|
|
{% include "components/widgets/blogroll.njk" %}
|
|
{% elif widget.type == "feedland" %}
|
|
{% include "components/widgets/feedland.njk" %}
|
|
{% elif widget.type == "categories" %}
|
|
{% include "components/widgets/categories.njk" %}
|
|
{% elif widget.type == "recent-comments" %}
|
|
{% include "components/widgets/recent-comments.njk" %}
|
|
{% elif widget.type == "search" %}
|
|
<is-land on:visible>
|
|
<div class="widget">
|
|
<div id="blog-sidebar-search"></div>
|
|
<script>initPagefind("#blog-sidebar-search");</script>
|
|
</div>
|
|
</is-land>
|
|
{% elif widget.type == "fediverse-follow" %}
|
|
{% include "components/widgets/fediverse-follow.njk" %}
|
|
{% elif widget.type == "custom-html" %}
|
|
{% set wConfig = widget.config or {} %}
|
|
<is-land on:visible>
|
|
<div class="widget">
|
|
{% if wConfig.content %}
|
|
<div class="prose dark:prose-invert prose-sm max-w-none">
|
|
{{ wConfig.content | safe }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</is-land>
|
|
{% else %}
|
|
<!-- Unknown widget type: {{ widget.type }} -->
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% endfor %}
|
|
{% else %}
|
|
{# === Fallback: default blog post sidebar (backward compatibility) === #}
|
|
{# Each widget wrapped in collapsible container #}
|
|
|
|
{# Author Card Compact #}
|
|
{% set widgetKey = "post-fb-author-card-compact" %}
|
|
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : true }">
|
|
<div class="bg-white dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
|
|
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
|
|
<h3 class="widget-title font-bold text-lg">Author</h3>
|
|
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
|
|
</button>
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
|
|
{% include "components/widgets/author-card-compact.njk" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# Table of Contents #}
|
|
{% set widgetKey = "post-fb-toc" %}
|
|
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : true }">
|
|
<div class="bg-white dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
|
|
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
|
|
<h3 class="widget-title font-bold text-lg">Table of Contents</h3>
|
|
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
|
|
</button>
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
|
|
{% include "components/widgets/toc.njk" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# Post Categories #}
|
|
{% set widgetKey = "post-fb-post-categories" %}
|
|
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : true }">
|
|
<div class="bg-white dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
|
|
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
|
|
<h3 class="widget-title font-bold text-lg">Categories</h3>
|
|
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
|
|
</button>
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
|
|
{% include "components/widgets/post-categories.njk" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# Recent Posts #}
|
|
{% set widgetKey = "post-fb-recent-posts" %}
|
|
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
|
|
<div class="bg-white dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
|
|
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
|
|
<h3 class="widget-title font-bold text-lg">Recent Posts</h3>
|
|
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
|
|
</button>
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
|
|
{% include "components/widgets/recent-posts-blog.njk" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# Webmentions #}
|
|
{% set widgetKey = "post-fb-webmentions" %}
|
|
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
|
|
<div class="bg-white dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
|
|
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
|
|
<h3 class="widget-title font-bold text-lg">Webmentions</h3>
|
|
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
|
|
</button>
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
|
|
{% include "components/widgets/webmentions.njk" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# Share #}
|
|
{% set widgetKey = "post-fb-share" %}
|
|
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
|
|
<div class="bg-white dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
|
|
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
|
|
<h3 class="widget-title font-bold text-lg">Share</h3>
|
|
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
|
|
</button>
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
|
|
{% include "components/widgets/share.njk" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# Subscribe #}
|
|
{% set widgetKey = "post-fb-subscribe" %}
|
|
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
|
|
<div class="bg-white dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
|
|
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
|
|
<h3 class="widget-title font-bold text-lg">Subscribe</h3>
|
|
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
|
|
</button>
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
|
|
{% include "components/widgets/subscribe.njk" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# Recent Comments #}
|
|
{% set widgetKey = "post-fb-recent-comments" %}
|
|
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
|
|
<div class="bg-white dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
|
|
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
|
|
<h3 class="widget-title font-bold text-lg">Recent Comments</h3>
|
|
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
|
|
</button>
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
|
|
{% include "components/widgets/recent-comments.njk" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|