feat(frontend): replace article system with Zibll 9.0

This commit is contained in:
2026-08-18 16:06:47 +08:00
parent 2881c69e9c
commit 0f2ac6c34a
69 changed files with 2603 additions and 4163 deletions
+5 -110
View File
@@ -1,7 +1,7 @@
/**
* Homepage Fixed - Interactive Layer (v2.0 Zibll-aligned)
*
* Unified AJAX protocol for homepage tabs, sort, pagination, and view toggle.
* Unified AJAX protocol for homepage tabs, sort, and pagination.
* Uses Zibll-style ajaxpager DOM conventions and custom event flow:
* post_ajax.before → fetch → post_ajax.ed → aksrt:posts:rendered
*
@@ -11,34 +11,18 @@
(function () {
'use strict';
var STORAGE_KEY = 'aksrt_homepage_view_mode';
var SORT_QUERY_KEY = 'home_sort';
var DEFAULT_SORT = 'modified';
var DEFAULT_VIEW = 'list';
var REST_PATH = '/wp-json/aksrt/v1/homepage/tab-posts';
/* ============================================================
* Utilities
* ============================================================ */
function safeLocalStorage() {
try { return window.localStorage; } catch (e) { return null; }
}
function getViewMode() {
var store = safeLocalStorage();
if (store) {
var saved = store.getItem(STORAGE_KEY);
if (saved === 'card' || saved === 'list') return saved;
}
return getDefaultView();
}
function setViewMode(mode) {
var store = safeLocalStorage();
if (store) { try { store.setItem(STORAGE_KEY, mode); } catch (e) { /* */ } }
}
function getRenderedPostCount(scope) {
if (!scope) return 0;
return scope.querySelectorAll('.posts-item, .posts-mini').length;
@@ -63,8 +47,8 @@
}
function getDefaultView() {
var configured = window.aksrtData && aksrtData.homepageDefaultView;
return configured === 'card' || configured === 'list' ? configured : DEFAULT_VIEW;
var panel = document.querySelector('[data-home-tab-panel][data-view]');
return panel && panel.getAttribute('data-view') === 'card' ? 'card' : 'list';
}
function isSortAjaxEnabled(sortbar) {
@@ -119,8 +103,8 @@
}
function getStreamViewMode(stream) {
var activeBtn = stream.querySelector('.aksrt-homepage__view-btn.is-active');
return (activeBtn && activeBtn.getAttribute('data-view-mode')) || getViewMode();
var panel = getActivePanel(stream);
return panel && panel.getAttribute('data-view') === 'card' ? 'card' : 'list';
}
function getActivePanel(stream) {
@@ -153,7 +137,6 @@
'&page=' + encodeURIComponent(options.page || 1) +
'&per_page=' + encodeURIComponent(options.perPage || 10) +
'&sort=' + encodeURIComponent(options.sort || getDefaultSort()) +
'&view_mode=' + encodeURIComponent(options.viewMode || getDefaultView()) +
'&page_url=' + encodeURIComponent(options.pageUrl || window.location.href) +
'&panel=' + encodeURIComponent(options.panel || 'latest') +
'&source=' + encodeURIComponent(options.source || 'home') +
@@ -731,93 +714,6 @@
});
}
/* ============================================================
* View Mode Toggle
* ============================================================ */
function applyViewMode(buttons, postsContainers, mode) {
buttons.forEach(function (btn) {
var btnMode = btn.getAttribute('data-view-mode');
var isActive = btnMode === mode;
btn.classList.toggle('is-active', isActive);
btn.setAttribute('aria-pressed', isActive ? 'true' : 'false');
});
Array.prototype.forEach.call(postsContainers, function (container) {
container.setAttribute('data-view', mode);
container.querySelectorAll('.posts-item').forEach(function (item) {
item.classList.toggle('card', mode === 'card');
item.classList.toggle('list', mode === 'list');
});
});
}
function initViewToggle() {
var toggles = document.querySelectorAll('[data-aksrt-view-toggle]');
toggles.forEach(function (toggle) {
var buttons = toggle.querySelectorAll('.aksrt-homepage__view-btn');
var stream = toggle.closest('.aksrt-homepage__stream');
if (!stream) return;
function getPostsContainers() {
return stream.querySelectorAll('[data-home-tab-panel]');
}
function getActiveMode() {
var activeBtn = toggle.querySelector('.aksrt-homepage__view-btn.is-active');
return (activeBtn && activeBtn.getAttribute('data-view-mode')) || getViewMode();
}
var initialMode = getViewMode();
var activePanel = getActivePanel(stream);
var renderedMode = activePanel ? activePanel.getAttribute('data-view') : '';
applyViewMode(buttons, getPostsContainers(), initialMode);
// The persisted preference may differ from the server-rendered mode.
// Class toggling alone leaves list-only markup and its `.flex` utility in
// place, which forces a supposed card to occupy a full row. Fetch the
// matching renderer immediately so DOM and visual mode stay atomic.
if (activePanel && renderedMode !== initialMode) {
requestHomepagePanel(activePanel, {
termId: getPanelTermId(activePanel),
page: 1,
perPage: getHomepagePerPage(activePanel),
sort: getCurrentSort(stream),
viewMode: initialMode,
panel: activePanel.getAttribute('data-home-tab-panel') || 'latest',
source: 'home',
tabOrderby: getPanelTabOrderby(activePanel)
});
}
buttons.forEach(function (btn) {
btn.addEventListener('click', function () {
var mode = btn.getAttribute('data-view-mode') || getDefaultView();
var activePanel = getActivePanel(stream);
if (!activePanel || mode === getActiveMode()) return;
setViewMode(mode);
applyViewMode(buttons, getPostsContainers(), mode);
requestHomepagePanel(activePanel, {
termId: getPanelTermId(activePanel),
page: 1,
perPage: getHomepagePerPage(activePanel),
sort: getCurrentSort(stream),
viewMode: mode,
panel: activePanel.getAttribute('data-home-tab-panel') || 'latest',
source: 'home',
tabOrderby: getPanelTabOrderby(activePanel)
});
});
});
// Re-apply view mode after AJAX content renders
stream.addEventListener('aksrt:posts:rendered', function () {
applyViewMode(buttons, getPostsContainers(), getActiveMode());
});
});
}
/* ============================================================
* Init
* ============================================================ */
@@ -827,7 +723,6 @@
initSortbarDropdown();
initSortbar();
initStickyTabs();
initViewToggle();
}
if (document.readyState === 'loading') {