Files
wordpress-theme-aksrt/src/AKSRT/Widgets/WidgetForumTabTopics.php
T
kate522 aed056152c feat(widgets): shared tab JS + main posts ajax load_mode
Move category/forum/product tab lazy load into widget-set.js with
skeletons; MainPosts gains load_mode=ajax first-paint placeholders.
2026-07-21 12:42:58 +08:00

184 lines
6.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Multi-tab forum topic lists (zibll zib_bbs_widget_ui_tab_posts + shared mini rows).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
defined('ABSPATH') || exit;
class WidgetForumTabTopics extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_forum_tab_topics',
esc_html__('论坛 - 多栏目帖子', 'aksrt'),
[
'description' => esc_html__('热门/最新/精华等栏目切换的帖子列表(共享迷你行 + 懒加载)。', 'aksrt'),
'classname' => 'widget_aksrt_forum_tab_topics',
]
);
}
public function widget($args, $instance): void {
if (!post_type_exists('aksrt_forum_topic')) {
return;
}
$tabs = $this->normalize_tabs($instance['tabs'] ?? []);
if (!$tabs) {
$tabs = [
['title' => __('最新', 'aksrt'), 'orderby' => 'date'],
['title' => __('热门', 'aksrt'), 'orderby' => 'views'],
['title' => __('精华', 'aksrt'), 'orderby' => 'essence'],
];
}
$count = max(1, min(20, (int) ($instance['count'] ?? 6)));
$style = sanitize_key((string) ($instance['style'] ?? 'mini'));
if (!in_array($style, ['mini', 'rank'], true)) {
$style = 'mini';
}
$plate = max(0, (int) ($instance['plate'] ?? 0));
$uid = 'aksrt-ftab-' . esc_attr($this->id);
// Prefer WidgetAjax forum-topics (HotTopics payload with style/plate).
$rest = rest_url('aksrt/v1/widgets/forum-topics');
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
echo '<div class="aksrt-ftab widget-tab-posts" id="' . $uid . '" data-aksrt-tabs="1" data-aksrt-ftab="1" data-rest="' . esc_url($rest) . '" data-count="' . esc_attr((string) $count) . '" data-style="' . esc_attr($style) . '" data-plate="' . esc_attr((string) $plate) . '">';
echo '<ul class="aksrt-ftab__nav widget-tab-posts__nav" role="tablist">';
foreach ($tabs as $i => $tab) {
$cls = $i === 0 ? ' is-active' : '';
echo '<li role="presentation"><button type="button" role="tab" class="aksrt-ftab__btn widget-tab-posts__tab' . esc_attr($cls) . '" data-index="' . (int) $i . '" data-orderby="' . esc_attr($tab['orderby']) . '" aria-selected="' . ($i === 0 ? 'true' : 'false') . '">' . esc_html($tab['title']) . '</button></li>';
}
echo '</ul>';
foreach ($tabs as $i => $tab) {
$cls = $i === 0 ? ' is-active' : '';
$loaded = $i === 0 ? '1' : '0';
echo '<div class="aksrt-ftab__panel widget-tab-posts__panel' . esc_attr($cls) . '" data-index="' . (int) $i . '" data-loaded="' . esc_attr($loaded) . '"' . ($i === 0 ? '' : ' hidden') . ' role="tabpanel">';
if ($i === 0) {
$payload = WidgetForumHotTopics::query_payload([
'count' => $count,
'orderby' => $tab['orderby'],
'period' => 'all',
'style' => $style,
'plate' => $plate,
'paginate' => 'none',
], 1);
echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo '<div class="post_ajax_loader" data-aksrt-ajax-loader></div>';
}
echo '</div>';
}
echo '</div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* @param mixed $raw
* @return list<array{title:string,orderby:string}>
*/
private function normalize_tabs($raw): array {
$out = [];
if (is_string($raw)) {
foreach (array_filter(explode("\n", $raw)) as $line) {
$parts = array_map('trim', explode('|', $line));
if ($parts[0] === '') {
continue;
}
$out[] = [
'title' => $parts[0],
'orderby' => sanitize_key($parts[1] ?? 'date'),
];
}
return $out;
}
if (is_array($raw)) {
foreach ($raw as $tab) {
if (!is_array($tab)) {
continue;
}
$title = trim((string) ($tab['title'] ?? ''));
if ($title === '') {
continue;
}
$out[] = [
'title' => $title,
'orderby' => sanitize_key((string) ($tab['orderby'] ?? 'date')),
];
}
}
return $out;
}
public function form($instance): void {
$defaults = [
'title' => __('论坛栏目', 'aksrt'),
'count' => 6,
'style' => 'mini',
'plate' => 0,
'tabs' => "最新|date\n热门|views\n精华|essence",
];
$instance = wp_parse_args($instance, $defaults);
$tabs_val = $instance['tabs'];
if (is_array($tabs_val)) {
$lines = [];
foreach ($tabs_val as $t) {
$lines[] = ($t['title'] ?? '') . '|' . ($t['orderby'] ?? 'date');
}
$tabs_val = implode("\n", $lines);
}
?>
<p>
<label><?php esc_html_e('标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) $instance['title']); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('每栏数量', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="20" name="<?php echo esc_attr($this->get_field_name('count')); ?>" value="<?php echo esc_attr((string) $instance['count']); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('样式', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('style')); ?>">
<option value="mini" <?php selected($instance['style'], 'mini'); ?>><?php esc_html_e('迷你信息行', 'aksrt'); ?></option>
<option value="rank" <?php selected($instance['style'], 'rank'); ?>><?php esc_html_e('排行榜', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('版块 ID(可选)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="0" name="<?php echo esc_attr($this->get_field_name('plate')); ?>" value="<?php echo esc_attr((string) ((int) $instance['plate'])); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('栏目(每行: 标题|排序 date/views/essence/comments)', 'aksrt'); ?>
<textarea class="widefat" rows="4" name="<?php echo esc_attr($this->get_field_name('tabs')); ?>"><?php echo esc_textarea((string) $tabs_val); ?></textarea>
</label>
</p>
<?php
}
public function update($new_instance, $old_instance): array {
$style = sanitize_key((string) ($new_instance['style'] ?? 'mini'));
if (!in_array($style, ['mini', 'rank'], true)) {
$style = 'mini';
}
return [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 6))),
'style' => $style,
'plate' => max(0, (int) ($new_instance['plate'] ?? 0)),
'tabs' => sanitize_textarea_field((string) ($new_instance['tabs'] ?? '')),
];
}
}