| ---
|
| import type { CollectionEntry } from "astro:content";
|
| import { getPostUrlBySlug } from "@utils/url-utils";
|
| import PostCard from "@/components/layout/PostCard.astro";
|
| import { sidebarLayoutConfig, siteConfig } from "@/config";
|
|
|
| const { page } = Astro.props;
|
|
|
| let delay = 0;
|
| const interval = 50;
|
|
|
|
|
| type PostEntry = CollectionEntry<"posts">;
|
|
|
|
|
| const isBothSidebars = sidebarLayoutConfig.position === "both";
|
| const masonryEnabled = siteConfig.postListLayout.grid.masonry;
|
| const gridColumns = siteConfig.postListLayout.grid.columns || 2;
|
|
|
|
|
| const defaultLayout = siteConfig.postListLayout.defaultMode || "list";
|
| const gridCols =
|
| !isBothSidebars && gridColumns === 3
|
| ? "md:grid-cols-2 lg:grid-cols-3"
|
| : "md:grid-cols-2";
|
| const initialLayoutClass =
|
| defaultLayout === "grid"
|
| ? `grid grid-cols-1 ${gridCols} gap-4 grid-mode`
|
| : "flex flex-col gap-4 md:gap-4 list-mode";
|
| ---
|
|
|
| <div
|
| id="post-list-container"
|
| class={`transition-all duration-500 ease-in-out mb-4 ${initialLayoutClass}`}
|
| data-default-layout={defaultLayout}
|
| data-both-sidebars={isBothSidebars}
|
| data-masonry-enabled={masonryEnabled}
|
| data-grid-columns={gridColumns}
|
| >
|
| {
|
| page.data.map((entry: PostEntry, index: number) => (
|
| <PostCard
|
| entry={entry}
|
| title={entry.data.title}
|
| tags={entry.data.tags}
|
| category={entry.data.category}
|
| published={entry.data.published}
|
| updated={entry.data.updated}
|
| url={getPostUrlBySlug(entry.id)}
|
| image={entry.data.image}
|
| description={entry.data.description}
|
| draft={entry.data.draft}
|
| pinned={entry.data.pinned}
|
| loading={index < 2 ? "eager" : "lazy"}
|
| class:list="onload-animation post-card-item"
|
| style={`animation-delay: calc(var(--content-delay) + ${delay++ * interval}ms);`}
|
| />
|
| ))
|
| }
|
| </div>
|
|
|
| <!-- 立即执行脚本:防止刷新时的布局闪烁 -->
|
| <script is:inline define:vars={{ defaultLayout, isBothSidebars, gridColumns }}>
|
| (function() {
|
| const savedLayout = localStorage.getItem('postListLayout');
|
|
|
|
|
| if (savedLayout && savedLayout !== defaultLayout) {
|
| const container = document.getElementById('post-list-container');
|
|
|
| if (container) {
|
|
|
| container.style.transition = 'none';
|
|
|
|
|
| container.classList.remove('list-mode', 'grid-mode', 'flex', 'flex-col', 'grid', 'grid-cols-1', 'md:grid-cols-2', 'lg:grid-cols-3', 'gap-4', 'md:gap-4');
|
|
|
| if (savedLayout === 'grid') {
|
| container.classList.add('grid-mode', 'grid', 'grid-cols-1', 'md:grid-cols-2', 'gap-4');
|
| if (!isBothSidebars && gridColumns === 3) {
|
| container.classList.add('lg:grid-cols-3');
|
| }
|
| } else {
|
| container.classList.add('list-mode', 'flex', 'flex-col', 'gap-4', 'md:gap-4');
|
| }
|
|
|
|
|
| container.offsetHeight;
|
| container.style.transition = '';
|
| }
|
| }
|
| })();
|
| </script>
|
|
|
| <script>
|
|
|
| function initLayout() {
|
| const postListContainer = document.getElementById("post-list-container");
|
| if (!postListContainer) return;
|
|
|
|
|
| const screenWidth = window.innerWidth;
|
| const isSmallScreen = screenWidth < 1200;
|
|
|
|
|
| const savedLayout = localStorage.getItem("postListLayout");
|
| const defaultLayout =
|
| postListContainer.getAttribute("data-default-layout") || "list";
|
| let currentLayout = savedLayout || defaultLayout;
|
|
|
|
|
| if (isSmallScreen) {
|
| currentLayout = "list";
|
| }
|
|
|
|
|
| updatePostListLayout(currentLayout);
|
| }
|
|
|
| function updatePostListLayout(layout: string) {
|
| const postListContainer = document.getElementById("post-list-container");
|
| if (!postListContainer) return;
|
|
|
|
|
| const isCurrentGrid = postListContainer.classList.contains("grid-mode");
|
| const isCurrentList = postListContainer.classList.contains("list-mode");
|
| const currentLayout = isCurrentGrid ? "grid" : (isCurrentList ? "list" : null);
|
|
|
|
|
| const isBothSidebars = postListContainer.getAttribute("data-both-sidebars") === "true";
|
| const masonryEnabled = postListContainer.getAttribute("data-masonry-enabled") === "true";
|
| const gridColumns = parseInt(postListContainer.getAttribute("data-grid-columns") || "2");
|
|
|
|
|
| const applyClasses = () => {
|
| postListContainer.classList.remove("list-mode", "grid-mode");
|
| if (layout === "grid") {
|
| postListContainer.classList.add("grid-mode");
|
| postListContainer.classList.remove("flex", "flex-col");
|
| if (masonryEnabled) {
|
| postListContainer.classList.remove("grid", "grid-cols-1", "md:grid-cols-2", "lg:grid-cols-3", "gap-4");
|
| applyMasonryLayout();
|
| } else {
|
| postListContainer.classList.add("grid", "grid-cols-1", "md:grid-cols-2", "gap-4");
|
| if (!isBothSidebars && gridColumns === 3) {
|
| postListContainer.classList.add("lg:grid-cols-3");
|
| }
|
| resetMasonryLayout();
|
| }
|
| } else {
|
| postListContainer.classList.add("list-mode");
|
| postListContainer.classList.add("flex", "flex-col", "gap-4", "md:gap-4");
|
| postListContainer.classList.remove("grid", "grid-cols-1", "md:grid-cols-2", "lg:grid-cols-3");
|
| resetMasonryLayout();
|
| }
|
| };
|
|
|
|
|
| if (!currentLayout) {
|
| applyClasses();
|
| return;
|
| }
|
|
|
|
|
| if (currentLayout === layout) {
|
| if (layout === "grid" && masonryEnabled) {
|
| applyMasonryLayout();
|
| }
|
| return;
|
| }
|
|
|
|
|
|
|
| postListContainer.classList.add("layout-switching");
|
|
|
|
|
| setTimeout(() => {
|
| applyClasses();
|
|
|
|
|
| requestAnimationFrame(() => {
|
| postListContainer.classList.remove("layout-switching");
|
| });
|
| }, 200);
|
| }
|
|
|
| function resetMasonryLayout() {
|
| const container = document.getElementById("post-list-container");
|
| if (!container) return;
|
|
|
| container.style.height = "";
|
| container.style.position = "";
|
| container.style.display = "";
|
|
|
| const items = container.querySelectorAll(".post-card-item");
|
| items.forEach((item) => {
|
|
|
| item.style.position = "";
|
|
|
| item.style.top = "";
|
|
|
| item.style.left = "";
|
|
|
| item.style.width = "";
|
| });
|
| }
|
|
|
| function applyMasonryLayout() {
|
| const container = document.getElementById("post-list-container");
|
| if (!container) return;
|
|
|
| const masonryEnabled = container.getAttribute("data-masonry-enabled") === "true";
|
| if (!masonryEnabled) return;
|
|
|
|
|
| if (!container.classList.contains("grid-mode")) return;
|
|
|
| const items = Array.from(container.querySelectorAll(".post-card-item"));
|
| if (items.length === 0) return;
|
|
|
|
|
| const gap = 16;
|
| const isBothSidebars = container.getAttribute("data-both-sidebars") === "true";
|
| const gridColumns = parseInt(container.getAttribute("data-grid-columns") || "2");
|
| let colCount = 2;
|
| if (!isBothSidebars && gridColumns === 3 && window.innerWidth >= 1024) {
|
| colCount = 3;
|
| }
|
|
|
|
|
| container.style.position = "relative";
|
| container.style.display = "block";
|
|
|
|
|
| const containerWidth = container.offsetWidth;
|
| const itemWidth = (containerWidth - (colCount - 1) * gap) / colCount;
|
|
|
| const colHeights = new Array(colCount).fill(0);
|
|
|
| items.forEach((item, index) => {
|
| const colIndex = index % colCount;
|
|
|
|
|
| item.style.position = "absolute";
|
|
|
| item.style.width = `${itemWidth}px`;
|
|
|
| item.style.setProperty('height', 'auto', 'important');
|
|
|
|
|
|
|
| const height = item.offsetHeight;
|
|
|
| const top = colHeights[colIndex];
|
| const left = colIndex * (itemWidth + gap);
|
|
|
|
|
| item.style.top = `${top}px`;
|
|
|
| item.style.left = `${left}px`;
|
|
|
| colHeights[colIndex] += height + gap;
|
| });
|
|
|
| container.style.height = `${Math.max(...colHeights)}px`;
|
| }
|
|
|
|
|
| document.addEventListener("DOMContentLoaded", function () {
|
|
|
| setTimeout(initLayout, 50);
|
|
|
|
|
| const imgs = document.querySelectorAll('#post-list-container img');
|
| imgs.forEach(img => {
|
|
|
| if(img.complete) return;
|
| img.addEventListener('load', () => {
|
| applyMasonryLayout();
|
| });
|
| });
|
| });
|
|
|
|
|
| document.addEventListener("visibilitychange", function () {
|
| if (!document.hidden) {
|
| setTimeout(initLayout, 100);
|
| }
|
| });
|
|
|
|
|
| window.addEventListener("layoutChange", function (event) {
|
|
|
| const newLayout = event.detail.layout;
|
| const postListContainer = document.getElementById("post-list-container");
|
| if (!postListContainer) return;
|
|
|
|
|
| const screenWidth = window.innerWidth;
|
| const isSmallScreen = screenWidth < 1200;
|
|
|
| if (isSmallScreen) {
|
| updatePostListLayout("list");
|
| } else {
|
| updatePostListLayout(newLayout);
|
| }
|
| });
|
|
|
|
|
| let resizeTimeout: any;
|
| window.addEventListener("resize", function () {
|
| clearTimeout(resizeTimeout);
|
| resizeTimeout = setTimeout(function () {
|
| initLayout();
|
| }, 250);
|
| });
|
|
|
|
|
| document.addEventListener("astro:page-load", function () {
|
| setTimeout(initLayout, 50);
|
|
|
|
|
| const imgs = document.querySelectorAll('#post-list-container img');
|
| imgs.forEach(img => {
|
|
|
| if(img.complete) return;
|
| img.addEventListener('load', () => {
|
| applyMasonryLayout();
|
| });
|
| });
|
| });
|
|
|
| document.addEventListener("astro:after-swap", function () {
|
| setTimeout(initLayout, 50);
|
| });
|
|
|
|
|
| setTimeout(initLayout, 0);
|
| </script>
|
|
|
| <style>
|
|
|
| #post-list-container {
|
|
|
| transition: opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
| transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
| }
|
|
|
|
|
| #post-list-container > :global(*) {
|
| transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1),
|
| transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
| }
|
|
|
|
|
| #post-list-container {
|
| transition: opacity 0.2s ease-out, transform 0.2s ease-out;
|
| }
|
|
|
| #post-list-container.layout-switching {
|
| opacity: 0;
|
| transform: translateY(10px);
|
| }
|
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
|
|
| #post-list-container.list-mode :global(.post-card-title) {
|
| font-size: 1.5rem !important;
|
| line-height: 2rem !important;
|
| }
|
|
|
| #post-list-container.list-mode :global(.post-card-title::before) {
|
| top: 2.25rem !important;
|
| height: 1rem !important;
|
| }
|
|
|
|
|
| #post-list-container.list-mode :global(.has-cover .description) {
|
| display: -webkit-box !important;
|
| -webkit-box-orient: vertical !important;
|
| overflow: hidden !important;
|
| -webkit-line-clamp: 2 !important;
|
| line-clamp: 2 !important;
|
| }
|
|
|
|
|
| #post-list-container.grid-mode :global(.post-card-wrapper) {
|
| flex-direction: column-reverse !important;
|
| }
|
|
|
| #post-list-container.grid-mode :global(.post-card-image) {
|
| width: 100% !important;
|
| position: relative !important;
|
| top: auto !important;
|
| right: auto !important;
|
| bottom: auto !important;
|
| border-radius: var(--radius-large) var(--radius-large) 0 0 !important;
|
|
|
| }
|
|
|
| #post-list-container.grid-mode :global(.post-card-content) {
|
| width: 100% !important;
|
| padding: 1rem !important;
|
| }
|
|
|
| #post-list-container.grid-mode :global(.no-cover .post-card-content) {
|
| padding-right: 4.5rem !important;
|
| }
|
|
|
| #post-list-container.grid-mode :global(.post-card-title) {
|
| font-size: 1.125rem !important;
|
| line-height: 1.75rem !important;
|
| margin-bottom: 0.5rem !important;
|
| }
|
|
|
| #post-list-container.grid-mode :global(.post-card-title::before) {
|
| display: none !important;
|
| }
|
|
|
| #post-list-container.grid-mode :global(.description) {
|
| font-size: 0.875rem !important;
|
| margin-bottom: 0.75rem !important;
|
| padding-right: 0 !important;
|
| }
|
|
|
| #post-list-container.grid-mode :global(.has-cover .description) {
|
| display: -webkit-box !important;
|
| -webkit-box-orient: vertical !important;
|
| overflow: hidden !important;
|
| -webkit-line-clamp: 3 !important;
|
| line-clamp: 3 !important;
|
| }
|
|
|
| #post-list-container.grid-mode :global(.post-meta) {
|
| margin-bottom: 0.5rem !important;
|
| gap: 0.5rem !important;
|
| }
|
|
|
| #post-list-container.grid-mode :global(.post-meta .text-xl) {
|
| font-size: 1rem !important;
|
| line-height: 1.25rem !important;
|
| }
|
|
|
| #post-list-container.grid-mode :global(.meta-icon) {
|
| width: 1.5rem !important;
|
| height: 1.5rem !important;
|
| margin-right: 0.25rem !important;
|
| }
|
|
|
| #post-list-container.grid-mode :global(.post-meta .text-sm) {
|
| font-size: 0.75rem !important;
|
| line-height: 1rem !important;
|
| }
|
|
|
| #post-list-container.grid-mode :global(.post-meta .pinned-btn) {
|
| padding: 0.25rem 0.375rem !important;
|
| }
|
|
|
| </style>
|
|
|