feat(mobile): add infinite scroll and hide pagination on mobile
Add IntersectionObserver-based infinite scroll for mobile viewports that fetches next pages as the user scrolls. Uses MutationObserver to re-attach after DOM replacements. Hide pagination controls on screens narrower than 768px.
This commit is contained in:
parent
8e92385c56
commit
39bad42f9c
3 changed files with 81 additions and 1 deletions
|
|
@ -11,6 +11,82 @@
|
|||
src="https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.6/bundles/datastar.js"
|
||||
></script>
|
||||
{% block head %}{% endblock %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var mobileQuery = window.matchMedia("(max-width: 767px)");
|
||||
if (!mobileQuery.matches) return;
|
||||
|
||||
function setupInfiniteScroll() {
|
||||
document.querySelectorAll("[data-infinite-scroll]").forEach(function (container) {
|
||||
var sentinel = container.querySelector(".infinite-scroll-sentinel");
|
||||
if (!sentinel || sentinel.dataset.observed) return;
|
||||
sentinel.dataset.observed = "true";
|
||||
|
||||
var observer = new IntersectionObserver(
|
||||
function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (!entry.isIntersecting) return;
|
||||
|
||||
var nextUrl = container.getAttribute("data-next-url");
|
||||
var targetSelector = container.getAttribute("data-target");
|
||||
if (!nextUrl || !targetSelector) {
|
||||
observer.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
observer.disconnect();
|
||||
sentinel.remove();
|
||||
|
||||
fetch(nextUrl, {
|
||||
headers: { "datastar-request": "true" },
|
||||
})
|
||||
.then(function (res) { return res.text(); })
|
||||
.then(function (html) {
|
||||
var doc = new DOMParser().parseFromString(html, "text/html");
|
||||
var newTarget = doc.querySelector(targetSelector);
|
||||
if (!newTarget) return;
|
||||
|
||||
// Append new rows to existing tbody
|
||||
var existingTbody = container.querySelector("tbody");
|
||||
var newTbody = newTarget.querySelector("tbody");
|
||||
if (existingTbody && newTbody) {
|
||||
Array.from(newTbody.children).forEach(function (row) {
|
||||
existingTbody.appendChild(document.adoptNode(row));
|
||||
});
|
||||
}
|
||||
|
||||
// Check if there are more pages
|
||||
var newContainer = newTarget.querySelector("[data-infinite-scroll]");
|
||||
var newNextUrl = newContainer && newContainer.getAttribute("data-next-url");
|
||||
if (newNextUrl) {
|
||||
container.setAttribute("data-next-url", newNextUrl);
|
||||
var newSentinel = document.createElement("div");
|
||||
newSentinel.className = "infinite-scroll-sentinel h-4";
|
||||
newSentinel.setAttribute("aria-hidden", "true");
|
||||
container.appendChild(newSentinel);
|
||||
setupInfiniteScroll();
|
||||
} else {
|
||||
container.removeAttribute("data-next-url");
|
||||
container.removeAttribute("data-infinite-scroll");
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
{ rootMargin: "200px" }
|
||||
);
|
||||
|
||||
observer.observe(sentinel);
|
||||
});
|
||||
}
|
||||
|
||||
setupInfiniteScroll();
|
||||
|
||||
var bodyObserver = new MutationObserver(function () {
|
||||
setupInfiniteScroll();
|
||||
});
|
||||
bodyObserver.observe(document.body, { childList: true, subtree: true });
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body class="min-h-screen bg-amber-50 text-stone-900">
|
||||
<main class="mx-auto flex w-full max-w-5xl flex-col gap-6 px-6 py-10">
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
{% macro pagination_header(items, navigator, target_selector) %}
|
||||
<div
|
||||
class="pagination-controls flex flex-wrap items-center justify-between gap-x-3 border-t border-amber-200/70 px-4 py-1.5 text-xs leading-none text-stone-600"
|
||||
class="pagination-controls hidden md:flex flex-wrap items-center justify-between gap-x-3 border-t border-amber-200/70 px-4 py-1.5 text-xs leading-none text-stone-600"
|
||||
>
|
||||
<span>
|
||||
Showing {{ items.start_index() }}–{{ items.end_index() }} of {{ items.total }}
|
||||
|
|
|
|||
|
|
@ -147,6 +147,10 @@ a {
|
|||
.responsive-table td.mobile-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pagination-controls {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop: alternating cards with central line */
|
||||
|
|
|
|||
Loading…
Reference in a new issue