mirror of
https://github.com/svemagie/blog-eleventy-indiekit.git
synced 2026-04-02 08:44:56 +02:00
fix: pagination scrambling and scroll + feat: excludePostTypes filter
- Fix duplicate x-for keys causing scrambled pagination numbers on
interactions page (two '…' entries shared the same key)
- Fix scroll target in goToPage — was using dead closest('[x-show]')
selector, now scrolls to #webmentions-list
- Add flex-wrap to pagination-links for mobile overflow
- Add excludePostTypes Eleventy filter to exclude post types from
collections by detecting type from frontmatter properties
- Wire excludePostTypes into recent-posts section via sectionConfig
- Add error/stale data banner to changelog page
This commit is contained in:
@@ -7,15 +7,17 @@
|
||||
{% set sectionConfig = section.config or {} %}
|
||||
{% set maxItems = sectionConfig.maxItems or 5 %}
|
||||
{% set showSummary = sectionConfig.showSummary if sectionConfig.showSummary is defined else true %}
|
||||
{% set excludeTypes = sectionConfig.excludeTypes or [] %}
|
||||
{% set recentPosts = collections.posts | excludePostTypes(excludeTypes) | head(maxItems) %}
|
||||
|
||||
{% if collections.posts and collections.posts.length %}
|
||||
{% if recentPosts and recentPosts.length %}
|
||||
<section class="mb-8 sm:mb-12">
|
||||
<h2 class="text-xl sm:text-2xl font-bold text-surface-900 dark:text-surface-100 mb-4 sm:mb-6">
|
||||
{{ sectionConfig.title or "Recent Posts" }}
|
||||
</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
{% for post in collections.posts | head(maxItems) %}
|
||||
{% for post in recentPosts %}
|
||||
{# Detect post type from frontmatter properties #}
|
||||
{% set likedUrl = post.data.likeOf or post.data.like_of %}
|
||||
{% set bookmarkedUrl = post.data.bookmarkOf or post.data.bookmark_of %}
|
||||
|
||||
@@ -59,6 +59,14 @@ withSidebar: false
|
||||
<span class="ml-3 text-surface-600 dark:text-surface-400">Loading changelog...</span>
|
||||
</div>
|
||||
|
||||
{# Error/stale data banner #}
|
||||
<div x-show="apiError && !loading" x-cloak class="mb-6 p-4 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg">
|
||||
<p class="text-amber-800 dark:text-amber-200 text-sm">
|
||||
<span class="font-medium">Note:</span>
|
||||
<span x-text="commits.length > 0 ? 'Showing cached data — GitHub API is temporarily unavailable.' : apiError"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{# Commit list #}
|
||||
<div x-show="!loading" x-cloak>
|
||||
<template x-if="filteredCommits().length === 0">
|
||||
@@ -129,6 +137,7 @@ function changelogApp() {
|
||||
viewMode: 'repo',
|
||||
loading: true,
|
||||
loadingMore: false,
|
||||
apiError: null,
|
||||
commits: [],
|
||||
categories: {},
|
||||
commitCategories: {},
|
||||
@@ -239,14 +248,16 @@ function changelogApp() {
|
||||
async fetchChangelog(days) {
|
||||
try {
|
||||
const response = await fetch('/githubapi/api/changelog?days=' + days);
|
||||
if (!response.ok) throw new Error('Failed to fetch');
|
||||
if (!response.ok) throw new Error('Failed to fetch changelog (HTTP ' + response.status + ')');
|
||||
const data = await response.json();
|
||||
this.commits = data.commits || [];
|
||||
this.categories = data.categories || {};
|
||||
this.commitCategories = data.commitCategories || {};
|
||||
this.currentDays = data.days;
|
||||
this.apiError = data.error || null;
|
||||
} catch (err) {
|
||||
console.error('Changelog error:', err);
|
||||
this.apiError = err.message || 'Failed to load changelog';
|
||||
} finally {
|
||||
this.loading = false;
|
||||
this.loadingMore = false;
|
||||
|
||||
@@ -655,6 +655,25 @@ export default function (eleventyConfig) {
|
||||
return array.slice(0, n);
|
||||
});
|
||||
|
||||
// Exclude post types from a collection by detecting type from frontmatter properties
|
||||
// Usage: collections.posts | excludePostTypes(["reply", "like"])
|
||||
// Supported types: reply, like, bookmark, repost, photo, article, note
|
||||
eleventyConfig.addFilter("excludePostTypes", (posts, excludeTypes) => {
|
||||
if (!Array.isArray(posts) || !Array.isArray(excludeTypes) || !excludeTypes.length) return posts;
|
||||
return posts.filter((post) => {
|
||||
const d = post.data || {};
|
||||
let type;
|
||||
if (d.inReplyTo || d.in_reply_to) type = "reply";
|
||||
else if (d.likeOf || d.like_of) type = "like";
|
||||
else if (d.bookmarkOf || d.bookmark_of) type = "bookmark";
|
||||
else if (d.repostOf || d.repost_of) type = "repost";
|
||||
else if (d.photo && d.photo.length) type = "photo";
|
||||
else if (d.title) type = "article";
|
||||
else type = "note";
|
||||
return !excludeTypes.includes(type);
|
||||
});
|
||||
});
|
||||
|
||||
// Slugify filter
|
||||
eleventyConfig.addFilter("slugify", (str) => {
|
||||
if (!str) return "";
|
||||
|
||||
@@ -216,7 +216,7 @@ permalink: /interactions/
|
||||
</div>
|
||||
|
||||
{# Webmentions list #}
|
||||
<div x-show="!notConfigured && (!loading || webmentions.length)" class="space-y-4">
|
||||
<div id="webmentions-list" x-show="!notConfigured && (!loading || webmentions.length)" class="space-y-4">
|
||||
<template x-for="wm in paginatedWebmentions" :key="wm['wm-id']">
|
||||
<div class="p-4 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm">
|
||||
<div class="flex gap-3">
|
||||
@@ -297,7 +297,7 @@ permalink: /interactions/
|
||||
Page <span x-text="currentPage"></span> of <span x-text="totalPages"></span>
|
||||
<span class="text-surface-600 dark:text-surface-400 ml-1">(<span x-text="filteredWebmentions.length"></span> total)</span>
|
||||
</div>
|
||||
<div class="pagination-links">
|
||||
<div class="pagination-links flex-wrap">
|
||||
<button
|
||||
@click="goToPage(currentPage - 1)"
|
||||
:disabled="currentPage <= 1"
|
||||
@@ -307,7 +307,7 @@ permalink: /interactions/
|
||||
Previous
|
||||
</button>
|
||||
|
||||
<template x-for="p in pageNumbers" :key="p">
|
||||
<template x-for="(p, idx) in pageNumbers" :key="'pg-' + idx">
|
||||
<button
|
||||
@click="typeof p === 'number' && goToPage(p)"
|
||||
:disabled="p === '…'"
|
||||
@@ -413,8 +413,7 @@ function interactionsApp() {
|
||||
goToPage(page) {
|
||||
if (page < 1 || page > this.totalPages) return;
|
||||
this.currentPage = page;
|
||||
// Scroll to top of inbound tab
|
||||
this.$el.closest('[x-show]')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
document.getElementById('webmentions-list')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
},
|
||||
|
||||
async init() {
|
||||
|
||||
Reference in New Issue
Block a user