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 sectionConfig = section.config or {} %}
|
||||||
{% set maxItems = sectionConfig.maxItems or 5 %}
|
{% set maxItems = sectionConfig.maxItems or 5 %}
|
||||||
{% set showSummary = sectionConfig.showSummary if sectionConfig.showSummary is defined else true %}
|
{% 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">
|
<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">
|
<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" }}
|
{{ sectionConfig.title or "Recent Posts" }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
{% for post in collections.posts | head(maxItems) %}
|
{% for post in recentPosts %}
|
||||||
{# Detect post type from frontmatter properties #}
|
{# Detect post type from frontmatter properties #}
|
||||||
{% set likedUrl = post.data.likeOf or post.data.like_of %}
|
{% set likedUrl = post.data.likeOf or post.data.like_of %}
|
||||||
{% set bookmarkedUrl = post.data.bookmarkOf or post.data.bookmark_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>
|
<span class="ml-3 text-surface-600 dark:text-surface-400">Loading changelog...</span>
|
||||||
</div>
|
</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 #}
|
{# Commit list #}
|
||||||
<div x-show="!loading" x-cloak>
|
<div x-show="!loading" x-cloak>
|
||||||
<template x-if="filteredCommits().length === 0">
|
<template x-if="filteredCommits().length === 0">
|
||||||
@@ -129,6 +137,7 @@ function changelogApp() {
|
|||||||
viewMode: 'repo',
|
viewMode: 'repo',
|
||||||
loading: true,
|
loading: true,
|
||||||
loadingMore: false,
|
loadingMore: false,
|
||||||
|
apiError: null,
|
||||||
commits: [],
|
commits: [],
|
||||||
categories: {},
|
categories: {},
|
||||||
commitCategories: {},
|
commitCategories: {},
|
||||||
@@ -239,14 +248,16 @@ function changelogApp() {
|
|||||||
async fetchChangelog(days) {
|
async fetchChangelog(days) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/githubapi/api/changelog?days=' + days);
|
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();
|
const data = await response.json();
|
||||||
this.commits = data.commits || [];
|
this.commits = data.commits || [];
|
||||||
this.categories = data.categories || {};
|
this.categories = data.categories || {};
|
||||||
this.commitCategories = data.commitCategories || {};
|
this.commitCategories = data.commitCategories || {};
|
||||||
this.currentDays = data.days;
|
this.currentDays = data.days;
|
||||||
|
this.apiError = data.error || null;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Changelog error:', err);
|
console.error('Changelog error:', err);
|
||||||
|
this.apiError = err.message || 'Failed to load changelog';
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.loadingMore = false;
|
this.loadingMore = false;
|
||||||
|
|||||||
@@ -655,6 +655,25 @@ export default function (eleventyConfig) {
|
|||||||
return array.slice(0, n);
|
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
|
// Slugify filter
|
||||||
eleventyConfig.addFilter("slugify", (str) => {
|
eleventyConfig.addFilter("slugify", (str) => {
|
||||||
if (!str) return "";
|
if (!str) return "";
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ permalink: /interactions/
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Webmentions list #}
|
{# 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']">
|
<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="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">
|
<div class="flex gap-3">
|
||||||
@@ -297,7 +297,7 @@ permalink: /interactions/
|
|||||||
Page <span x-text="currentPage"></span> of <span x-text="totalPages"></span>
|
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>
|
<span class="text-surface-600 dark:text-surface-400 ml-1">(<span x-text="filteredWebmentions.length"></span> total)</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="pagination-links">
|
<div class="pagination-links flex-wrap">
|
||||||
<button
|
<button
|
||||||
@click="goToPage(currentPage - 1)"
|
@click="goToPage(currentPage - 1)"
|
||||||
:disabled="currentPage <= 1"
|
:disabled="currentPage <= 1"
|
||||||
@@ -307,7 +307,7 @@ permalink: /interactions/
|
|||||||
Previous
|
Previous
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<template x-for="p in pageNumbers" :key="p">
|
<template x-for="(p, idx) in pageNumbers" :key="'pg-' + idx">
|
||||||
<button
|
<button
|
||||||
@click="typeof p === 'number' && goToPage(p)"
|
@click="typeof p === 'number' && goToPage(p)"
|
||||||
:disabled="p === '…'"
|
:disabled="p === '…'"
|
||||||
@@ -413,8 +413,7 @@ function interactionsApp() {
|
|||||||
goToPage(page) {
|
goToPage(page) {
|
||||||
if (page < 1 || page > this.totalPages) return;
|
if (page < 1 || page > this.totalPages) return;
|
||||||
this.currentPage = page;
|
this.currentPage = page;
|
||||||
// Scroll to top of inbound tab
|
document.getElementById('webmentions-list')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
this.$el.closest('[x-show]')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
|||||||
Reference in New Issue
Block a user