- rewrite schemas for posts, tab_posts, oneline_posts, hot_posts, term_cards, term_aggregation, media_cards, combo_info, partners, recent_comments, posts_toc and slider_media_combo to mirror the modern reference modules field by field (options, defaults, dependencies, min/max/step, subheadings) - port reference renderers: hot ranking with TOP badges, category aggregation card, graphic cover cards, partners CSS marquee, combo info cards with auto band palette, recent comment list - rename 分类与标签聚合卡片 to 分类聚合卡片 and restrict it to categories (no tags/topics branch in this project) - drop topics filter fields and the topics query branch; align orderby value domain with supported post meta - add searchable sortable terms admin field type and spinner unit suffix rendering - unify widget pagination and lazy loading on the REST client with the runtime-owned ias reveal trigger; remove the legacy post_ajax_trigger dual path - align section-navs TOC with the reference contract (wznav ids, 3-heading minimum, zib-widget box)
108 lines
4.6 KiB
PHP
108 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace AKSRT\WidgetSystem;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
/**
|
|
* Shared list/card/mini markup and pagination payloads (reference parity).
|
|
*/
|
|
final class PostRenderer {
|
|
public static function payload(array $instance, string $slug, int $page = 1, string $segment = ''): array {
|
|
$segment_config = $slug === 'tab_posts' ? PostQuery::segment($instance, $segment) : [];
|
|
$query = PostQuery::build($instance, $page, $segment_config);
|
|
$html = self::items($query, $instance, $slug, $page);
|
|
$max_pages = max(1, (int) $query->max_num_pages);
|
|
$total = (int) $query->found_posts;
|
|
return [
|
|
'html' => $html,
|
|
'paginate_html' => self::pagination($instance, $page, $max_pages),
|
|
'has_more' => $page < $max_pages,
|
|
'page' => $page,
|
|
'max_pages' => $max_pages,
|
|
'total' => $total,
|
|
];
|
|
}
|
|
|
|
public static function items(\WP_Query $query, array $instance, string $slug, int $page = 1): string {
|
|
if (!$query->have_posts()) {
|
|
return function_exists('aksrt_widget_ajax_null') ? aksrt_widget_ajax_null() : RendererHelpers::empty_state(__('暂无内容', 'aksrt'));
|
|
}
|
|
$style = (string) ($instance['style'] ?? 'card');
|
|
$mode = in_array($style, ['list', 'card', 'mini'], true) ? $style : 'card';
|
|
$mini_opt = (array) ($instance['mini_opt'] ?? []);
|
|
$config = [
|
|
'type' => $mode === 'card' ? 'card' : 'list',
|
|
'show_number' => in_array('show_number', $mini_opt, true),
|
|
'show_thumb' => in_array('show_thumb', $mini_opt, true),
|
|
'show_meta' => in_array('show_meta', $mini_opt, true),
|
|
'number_offset' => ($page - 1) * max(1, (int) ($instance['count'] ?? 8)),
|
|
'widget_style' => $mode,
|
|
];
|
|
if ($slug === 'oneline_posts') {
|
|
return self::oneline($query, $config, $mode);
|
|
}
|
|
if ($mode === 'mini') {
|
|
return aksrt_render_mini_list($query, array_merge($config, ['class' => 'ajax-item']), false);
|
|
}
|
|
// The .widget-ajaxpager container rendered by the caller is the ajax
|
|
// target, so item markup goes in without the page-level list wrapper.
|
|
return aksrt_render_post_list(array_merge($config, ['skip_wrapper' => true]), $query, false);
|
|
}
|
|
|
|
private static function oneline(\WP_Query $query, array $config, string $style): string {
|
|
$items = '';
|
|
while ($query->have_posts()) {
|
|
$query->the_post();
|
|
if ($style === 'mini') {
|
|
$data = aksrt_renderer_collect_post_data($config);
|
|
$items .= $data ? aksrt_renderer_mini($data, array_merge($config, [
|
|
'class' => 'posts-item',
|
|
'show_thumb' => true,
|
|
'show_meta' => true,
|
|
'show_number' => false,
|
|
])) : '';
|
|
} else {
|
|
$items .= aksrt_render_post(array_merge($config, [
|
|
'type' => $style === 'card' ? 'card' : 'list',
|
|
'is_mult_thumb' => 'disable',
|
|
'is_no_thumb' => 'disable',
|
|
]));
|
|
}
|
|
}
|
|
wp_reset_postdata();
|
|
return '<div class="widget-main-post mb20 style-' . esc_attr($style) . '"><div class="swiper-container swiper-scroll" data-slideClass="posts-item"><div class="swiper-wrapper">' . $items . '</div><div class="swiper-button-prev"></div><div class="swiper-button-next"></div></div></div>';
|
|
}
|
|
|
|
/**
|
|
* Pagination contract: '' keeps the hidden stub, ajax renders the load
|
|
* more trigger, number renders numbered page links; all targets carry the
|
|
* `.widget-ajaxpager` reference attribute. Interactive controls stay clear
|
|
* of the runtime's legacy `.ajax-next` HTML pipeline — the widget system
|
|
* owns the REST client in frontend.js.
|
|
*/
|
|
private static function pagination(array $instance, int $page, int $max_pages): string {
|
|
$mode = (string) ($instance['paginate'] ?? '');
|
|
if ($mode === '' || $mode === 'none') {
|
|
return '<div class="ajax-pag hide"><div class="next-page"><a href="#"></a></div></div>';
|
|
}
|
|
if ($max_pages <= 1) {
|
|
return '';
|
|
}
|
|
if ($mode === 'number' || $mode === 'numbered') {
|
|
$links = '';
|
|
$start = max(1, min($page - 2, $max_pages - 4));
|
|
$end = min($max_pages, max($start + 4, $page + 2));
|
|
for ($number = $start; $number <= $end; $number++) {
|
|
if ($number === $page) {
|
|
$links .= '<span class="page-numbers current">' . $number . '</span>';
|
|
continue;
|
|
}
|
|
$links .= '<a class="next-page page-numbers" ajaxpager-target=".widget-ajaxpager" ajax-replace="true" data-widget-page="' . $number . '" role="button" tabindex="0" href="#page-' . $number . '">' . $number . '</a>';
|
|
}
|
|
return '<div class="pagenav ajax-pag" role="navigation" aria-label="' . esc_attr__('文章分页', 'aksrt') . '">' . $links . '</div>';
|
|
}
|
|
return '<div class="text-center theme-pagination ajax-pag"><div no-scroll="true" ajaxpager-target=".widget-ajaxpager" class="next-page"><a role="button" tabindex="0" href="#next" data-next-page="' . (int) ($page + 1) . '">' . esc_html__('加载更多', 'aksrt') . '</a></div></div>';
|
|
}
|
|
}
|