359 lines
12 KiB
PHP
359 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* Shared post renderer helpers.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
if (!function_exists('aksrt_renderer_list')) {
|
|
/**
|
|
* Render a horizontal list card.
|
|
*
|
|
* @param array<string,mixed> $data Collected post data.
|
|
* @param array<string,mixed> $cfg Renderer config.
|
|
* @return string
|
|
*/
|
|
function aksrt_renderer_list(array $data, array $cfg): string {
|
|
$classes = ['posts-item', 'list', 'ajax-item', 'flex', 'posts-item--' . ($cfg['card_style'] ?: 'standard')];
|
|
|
|
if ($cfg['style'] !== '' && $cfg['style'] !== 'default') {
|
|
$classes[] = $cfg['style'];
|
|
}
|
|
if ($cfg['style'] === 'style2') {
|
|
$classes[] = 'xx';
|
|
}
|
|
if (!empty($data['is_mult_thumb'])) {
|
|
$classes[] = 'mult-thumb';
|
|
}
|
|
if ($cfg['context'] === 'home') {
|
|
$classes[] = 'posts-item--home-list';
|
|
}
|
|
if (empty($data['has_img'])) {
|
|
$classes[] = 'no-thumb';
|
|
}
|
|
if (($cfg['list_show_type'] ?? 'separate') === 'no_margin') {
|
|
$classes[] = 'no-margin';
|
|
$classes[] = 'no_margin';
|
|
}
|
|
|
|
$thumb = aksrt_renderer_thumb($data, $cfg);
|
|
$mult = aksrt_renderer_mult_thumb($data);
|
|
if (!empty($data['is_mult_thumb'])) {
|
|
// Zibll's multi-image mode replaces the primary graphic; both must
|
|
// never be emitted for the same list item.
|
|
$thumb = '';
|
|
}
|
|
|
|
if ($cfg['card_style'] === 'text' || ($cfg['card_style'] === 'thumb_if_has' && empty($data['has_img']))) {
|
|
$thumb = '';
|
|
$mult = '';
|
|
$classes[] = 'no-thumb';
|
|
$classes[] = 'posts-item--text';
|
|
}
|
|
$title_markup = aksrt_renderer_title_markup($data, $cfg);
|
|
$excerpt_markup = aksrt_renderer_excerpt_markup($data, $cfg);
|
|
$badges = aksrt_renderer_badges($data, $cfg);
|
|
$meta = aksrt_renderer_meta($data, $cfg);
|
|
|
|
$target_attr = !empty($cfg['target_blank']) ? ' target="_blank"' : '';
|
|
$heading_cls = !empty($cfg['heading_bold']) ? 'item-heading item-heading--bold' : 'item-heading';
|
|
$title_html = '<h2 class="' . $heading_cls . '"><a href="' . esc_url($data['permalink']) . '" rel="bookmark"' . $target_attr . '>' . $title_markup . '</a></h2>';
|
|
$excerpt_html = $cfg['show_excerpt'] && $excerpt_markup !== ''
|
|
? '<p class="item-excerpt">' . $excerpt_markup . '</p>'
|
|
: '';
|
|
|
|
$html = '<article ' . aksrt_renderer_article_attrs($classes, $data) . '>';
|
|
|
|
if ($cfg['style'] === 'style2') {
|
|
$html .= $title_html;
|
|
$html .= '<div class="flex">';
|
|
$html .= $thumb;
|
|
$html .= '<div class="item-body flex xx flex1 jsb">';
|
|
$html .= $mult ?: $excerpt_html;
|
|
$html .= $badges;
|
|
$html .= $meta;
|
|
$html .= '</div></div>';
|
|
} else {
|
|
$html .= $thumb;
|
|
$html .= '<div class="item-body flex xx flex1 jsb">';
|
|
$html .= $title_html;
|
|
$html .= $mult ?: $excerpt_html;
|
|
$html .= '<div>' . $badges . $meta . '</div>';
|
|
$html .= '</div>';
|
|
}
|
|
|
|
$html .= '</article>';
|
|
return $html;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_renderer_card')) {
|
|
/**
|
|
* Render a vertical card or CMS resource card.
|
|
*
|
|
* @param array<string,mixed> $data Collected post data.
|
|
* @param array<string,mixed> $cfg Renderer config.
|
|
* @return string
|
|
*/
|
|
function aksrt_renderer_card(array $data, array $cfg): string {
|
|
$classes = ['posts-item', 'card', 'ajax-item', 'posts-item--' . ($cfg['card_style'] ?: 'standard')];
|
|
|
|
if ($cfg['style'] !== '' && $cfg['style'] !== 'default') {
|
|
$classes[] = $cfg['style'];
|
|
}
|
|
if ($cfg['context'] === 'home') {
|
|
$classes[] = 'posts-item--home';
|
|
}
|
|
if ($cfg['mode'] === 'grid-cms') {
|
|
$classes[] = 'posts-item--cms';
|
|
}
|
|
if (!empty($data['is_mult_thumb'])) {
|
|
$classes[] = 'mult-thumb';
|
|
}
|
|
if (empty($data['has_img'])) {
|
|
$classes[] = 'no-thumb';
|
|
}
|
|
|
|
$thumb = aksrt_renderer_thumb($data, $cfg);
|
|
$badges = aksrt_renderer_badges($data, $cfg);
|
|
$resource_strip = aksrt_renderer_resource_strip($data);
|
|
$meta = aksrt_renderer_meta($data, $cfg);
|
|
$title_markup = aksrt_renderer_title_markup($data, $cfg);
|
|
$html = '<article ' . aksrt_renderer_article_attrs($classes, $data) . '>';
|
|
$html .= $thumb;
|
|
$html .= '<div class="item-body">';
|
|
$target_attr = !empty($cfg['target_blank']) ? ' target="_blank"' : '';
|
|
$heading_cls = !empty($cfg['heading_bold']) ? 'item-heading item-heading--bold' : 'item-heading';
|
|
$html .= '<h2 class="' . $heading_cls . '"><a href="' . esc_url($data['permalink']) . '" rel="bookmark"' . $target_attr . '>' . $title_markup . '</a></h2>';
|
|
|
|
$html .= $badges;
|
|
|
|
if ($cfg['mode'] === 'grid-cms') {
|
|
$html .= $resource_strip;
|
|
}
|
|
|
|
$html .= $meta;
|
|
$html .= '</div></article>';
|
|
|
|
return $html;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_renderer_mini')) {
|
|
/**
|
|
* Render a compact mini card.
|
|
*
|
|
* @param array<string,mixed> $data Collected post data.
|
|
* @param array<string,mixed> $cfg Renderer config.
|
|
* @return string
|
|
*/
|
|
function aksrt_renderer_mini(array $data, array $cfg): string {
|
|
$classes = ['posts-mini'];
|
|
if (!empty($cfg['extra_class'])) {
|
|
$classes[] = $cfg['extra_class'];
|
|
}
|
|
if (empty($data['has_img'])) {
|
|
$classes[] = 'posts-mini--no-thumb';
|
|
}
|
|
|
|
$number_html = '';
|
|
if ($cfg['show_number'] && $cfg['number_offset'] > 0) {
|
|
$badge_colors = ['c-red', 'c-yellow', 'c-orange', 'c-blue', 'c-green'];
|
|
$index = $cfg['number_offset'] - 1;
|
|
$color = $badge_colors[$index] ?? '';
|
|
$number_html = '<span class="badg badg-sm mr3 ' . esc_attr($color) . '">' . (int) $cfg['number_offset'] . '</span>';
|
|
}
|
|
|
|
$thumb_html = '';
|
|
$target_attr = !empty($cfg['target_blank']) ? ' target="_blank"' : '';
|
|
if ($cfg['show_thumb'] && !empty($data['has_img'])) {
|
|
$fit_class = !empty($cfg['thumb_fit']) && $cfg['thumb_fit'] === 'contain' ? 'fit-contain' : 'fit-cover';
|
|
$thumb_attr = aksrt_get_lazy_attr('lazy_posts_thumb', (string) $data['img_url'], $fit_class);
|
|
$thumb_img = '<img' . $thumb_attr . ' alt="' . esc_attr($data['title']) . '">';
|
|
|
|
$thumb_html = '<a href="' . esc_url($data['permalink']) . '" class="item-thumbnail" aria-label="' . esc_attr($data['title']) . '"' . $target_attr . '>' . $thumb_img . '</a>';
|
|
}
|
|
|
|
$heading_class = 'item-heading' . (!empty($cfg['heading_bold']) ? ' item-heading--bold' : '') . (!empty($data['has_img']) ? ' text-ellipsis-2' : ' text-ellipsis');
|
|
$title_html = '<h2 class="' . esc_attr($heading_class) . '">' . $number_html . '<a href="' . esc_url($data['permalink']) . '"' . $target_attr . '>' . aksrt_renderer_title_markup($data, $cfg) . '</a></h2>';
|
|
|
|
$html = '<div class="' . esc_attr(implode(' ', array_unique($classes))) . '">';
|
|
$html .= $thumb_html;
|
|
$html .= '<div class="posts-mini-con flex xx flex1 jsb">';
|
|
$html .= $title_html;
|
|
$html .= aksrt_renderer_meta($data, $cfg);
|
|
$html .= '</div></div>';
|
|
|
|
return $html;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_render_post')) {
|
|
/**
|
|
* Render one post item inside a loop.
|
|
*
|
|
* @param array<string,mixed> $config Renderer config overrides.
|
|
* @return string
|
|
*/
|
|
function aksrt_render_post(array $config = []): string {
|
|
$cfg = aksrt_renderer_config($config);
|
|
$data = aksrt_renderer_collect_post_data($cfg);
|
|
|
|
if (empty($data)) {
|
|
return '';
|
|
}
|
|
|
|
switch ($cfg['mode']) {
|
|
case 'card':
|
|
case 'grid-cms':
|
|
return aksrt_renderer_card($data, $cfg);
|
|
case 'mini':
|
|
return aksrt_renderer_mini($data, $cfg);
|
|
case 'list':
|
|
default:
|
|
return aksrt_renderer_list($data, $cfg);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_render_post_list')) {
|
|
/**
|
|
* Render a query loop with the unified renderer.
|
|
*
|
|
* @param WP_Query|null $query Posts query. Falls back to global loop.
|
|
* @param array<string,mixed> $config Renderer config overrides.
|
|
* @param bool $echo Whether to echo output.
|
|
* @return string
|
|
*/
|
|
function aksrt_render_post_list(?WP_Query $query = null, array $config = [], bool $echo = true): string {
|
|
$cfg = aksrt_renderer_config($config);
|
|
$wrapper_classes = ['posts-row', 'ajaxpager'];
|
|
|
|
if ($cfg['context'] === 'home') {
|
|
$wrapper_classes[] = 'aksrt-homepage__posts';
|
|
$wrapper_classes[] = 'post-grid';
|
|
$wrapper_classes[] = 'post-grid--home';
|
|
} elseif ($cfg['mode'] !== 'mini') {
|
|
$wrapper_classes[] = 'post-grid';
|
|
$wrapper_classes[] = 'post-grid--' . $cfg['mode'];
|
|
}
|
|
|
|
if (!empty($config['wrapper_class']) && is_string($config['wrapper_class'])) {
|
|
foreach (preg_split('/\s+/', trim($config['wrapper_class'])) as $extra_class) {
|
|
$extra_class = sanitize_html_class($extra_class);
|
|
if ($extra_class !== '') {
|
|
$wrapper_classes[] = $extra_class;
|
|
}
|
|
}
|
|
}
|
|
|
|
$wrapper_attr = 'class="' . esc_attr(implode(' ', array_unique($wrapper_classes))) . '"';
|
|
if ($cfg['columns'] > 1) {
|
|
$wrapper_attr .= ' style="--aksrt-widget-columns:' . (int) $cfg['columns'] . '"';
|
|
}
|
|
|
|
$data_view = in_array($cfg['mode'], ['card', 'grid-cms'], true) ? 'card' : $cfg['mode'];
|
|
// Homepage panel already provides .posts-row.ajaxpager — allow callers to skip nesting.
|
|
$skip_wrapper = !empty($config['skip_wrapper']);
|
|
$html = '';
|
|
|
|
if ($query instanceof WP_Query) {
|
|
if (!$query->have_posts()) {
|
|
$html = '<div class="no-results"><p>' . esc_html__('暂无文章', 'aksrt') . '</p></div>';
|
|
} else {
|
|
if (!$skip_wrapper) {
|
|
$html .= '<div ' . $wrapper_attr . ' data-view="' . esc_attr($data_view) . '">';
|
|
}
|
|
$number = $cfg['number_offset'];
|
|
while ($query->have_posts()) {
|
|
$query->the_post();
|
|
if ($cfg['mode'] === 'mini' && $cfg['show_number']) {
|
|
$number++;
|
|
$config['number_offset'] = $number;
|
|
$cfg = aksrt_renderer_config($config);
|
|
}
|
|
$html .= aksrt_render_post($cfg);
|
|
}
|
|
if (!$skip_wrapper) {
|
|
$html .= '</div>';
|
|
}
|
|
}
|
|
wp_reset_postdata();
|
|
} else {
|
|
if (!have_posts()) {
|
|
$html = '<div class="no-results"><p>' . esc_html__('暂无文章', 'aksrt') . '</p></div>';
|
|
} else {
|
|
if (!$skip_wrapper) {
|
|
$html .= '<div ' . $wrapper_attr . ' data-view="' . esc_attr($data_view) . '">';
|
|
}
|
|
while (have_posts()) {
|
|
the_post();
|
|
$html .= aksrt_render_post($cfg);
|
|
}
|
|
if (!$skip_wrapper) {
|
|
$html .= '</div>';
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($echo) {
|
|
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
|
return '';
|
|
}
|
|
|
|
return $html;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_render_mini_list')) {
|
|
/**
|
|
* Convenience wrapper for mini lists.
|
|
*
|
|
* @param WP_Query $query Posts query.
|
|
* @param array<string,mixed> $config Renderer config overrides.
|
|
* @param bool $echo Whether to echo output.
|
|
* @return string
|
|
*/
|
|
function aksrt_render_mini_list(WP_Query $query, array $config = [], bool $echo = true): string {
|
|
$config['mode'] = 'mini';
|
|
return aksrt_render_post_list($query, $config, $echo);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_render_placeholder')) {
|
|
/**
|
|
* Render skeleton placeholders for loading states.
|
|
*
|
|
* @param string $mode list|card|mini.
|
|
* @param int $count Number of placeholders.
|
|
* @return string
|
|
*/
|
|
function aksrt_render_placeholder(string $mode = 'list', int $count = 3): string {
|
|
$count = max(1, min(12, $count));
|
|
$html = '';
|
|
|
|
if ($mode === 'card') {
|
|
$item = '<div class="posts-item card"><div class="item-thumbnail"><div class="radius8 item-thumbnail placeholder"></div></div>'
|
|
. '<div class="item-body"><h2 class="item-excerpt placeholder t1 item-heading"></h2>'
|
|
. '<p class="mt10 placeholder k2"></p>'
|
|
. '<i class="flex jsb"><i class="placeholder s1"></i><i class="placeholder s1 ml10"></i></i></div></div>';
|
|
$html = str_repeat($item, $count);
|
|
} elseif ($mode === 'mini') {
|
|
$item = '<div class="posts-mini"><div class="item-thumbnail"><div class="radius8 placeholder"></div></div>'
|
|
. '<div class="posts-mini-con"><div class="placeholder t1"></div>'
|
|
. '<div class="placeholder k2 mt6"></div></div></div>';
|
|
$html = str_repeat($item, $count);
|
|
} else {
|
|
$item = '<div class="posts-item list flex"><div class="post-graphic"><div class="radius8 item-thumbnail placeholder"></div></div>'
|
|
. '<div class="item-body flex xx flex1 jsb"><p class="placeholder t1"></p>'
|
|
. '<h4 class="item-excerpt placeholder k1"></h4><p class="placeholder k2"></p>'
|
|
. '<i><i class="placeholder s1"></i><i class="placeholder s1 ml10"></i></i></div></div>';
|
|
$html = str_repeat($item, $count);
|
|
}
|
|
|
|
return $html;
|
|
}
|
|
}
|