Files
wordpress-theme-aksrt/src/AKSRT/Widgets/CategoryTabsWidget.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

202 lines
8.7 KiB
PHP

<?php
/**
* @package AKSRT
*/
namespace AKSRT\Widgets;
defined('ABSPATH') || exit;
/**
* Homepage category tabs widget.
*/
class CategoryTabsWidget extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_category_tabs',
esc_html__('AKSRT 分类 Tabs', 'aksrt'),
[
'classname' => 'widget_aksrt_category_tabs',
'description' => esc_html__('多分类切换内容流,可挂载到首页内容挂载区。', 'aksrt'),
'customize_selective_refresh' => true,
]
);
}
public function widget($args, $instance): void {
$context = aksrt_home_widget_resolve_context($args);
$title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base);
$title = $title !== '' ? $title : __('热门分类', 'aksrt');
$count = max(1, min(12, (int) ($instance['count'] ?? 6)));
$columns = aksrt_home_widget_effective_columns((int) ($instance['columns'] ?? 3), $context);
$layout = aksrt_home_widget_effective_layout(
aksrt_home_widget_resolve_layout($instance['layout'] ?? 'card', 'card'),
$context
);
$show_more = ! empty($instance['show_more']);
$tabs = $this->resolve_tabs($instance);
echo $args['before_widget'];
echo '<section class="aksrt-home-widget aksrt-home-widget--tabs widget-tab-posts" data-aksrt-home-cat-tabs data-aksrt-tabs="1" data-rest="' . esc_url(rest_url('aksrt/v1/widgets/category-tabs')) . '" data-count="' . esc_attr((string) $count) . '" data-layout="' . esc_attr($layout) . '" data-columns="' . esc_attr((string) $columns) . '" data-context="' . esc_attr($context) . '" data-show-more="' . (!empty($show_more) ? '1' : '0') . '">';
if (empty($tabs)) {
echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无可用分类', 'aksrt')) : '<p class="widget-empty">' . esc_html__('暂无可用分类', 'aksrt') . '</p>';
echo '</section>';
echo $args['after_widget'];
return;
}
?>
<header class="aksrt-home-widget__header aksrt-home-widget__header--tabs">
<h3 class="aksrt-home-widget__title"><?php echo esc_html($title); ?></h3>
<ul class="widget-tab-posts__nav aksrt-home-widget__tab-nav" role="tablist">
<?php foreach ($tabs as $index => $tab) : ?>
<li role="presentation">
<button
type="button"
role="tab"
class="widget-tab-posts__tab<?php echo 0 === $index ? ' active' : ''; ?>"
data-tab="cat-<?php echo (int) $tab['id']; ?>"
aria-selected="<?php echo 0 === $index ? 'true' : 'false'; ?>"
>
<?php echo esc_html($tab['name']); ?>
</button>
</li>
<?php endforeach; ?>
</ul>
</header>
<?php
foreach ($tabs as $index => $tab) {
?>
<div class="widget-tab-posts__panel<?php echo 0 === $index ? ' active' : ''; ?>"
data-tab-panel="cat-<?php echo (int) $tab['id']; ?>"
data-loaded="<?php echo 0 === $index ? '1' : '0'; ?>"
data-cat="<?php echo (int) $tab['id']; ?>"
data-more="<?php echo esc_url($tab['link']); ?>"
role="tabpanel"<?php echo 0 === $index ? '' : ' hidden'; ?>>
<?php if (0 === $index) :
$query = new \WP_Query([
'post_type' => aksrt_home_widget_post_types(),
'post_status' => 'publish',
'posts_per_page' => $count,
'ignore_sticky_posts' => true,
'no_found_rows' => true,
'cat' => (int) $tab['id'],
]);
aksrt_home_widget_render_post_grid($query, $layout, $columns, $context);
wp_reset_postdata();
if ($show_more) : ?>
<div class="aksrt-home-widget__footer">
<a class="aksrt-home-widget__more" href="<?php echo esc_url($tab['link']); ?>">
<?php esc_html_e('查看更多', 'aksrt'); ?>
</a>
</div>
<?php endif;
else : ?>
<div class="post_ajax_loader" data-aksrt-ajax-loader>
<?php for ($si = 0; $si < $count; $si++) : ?>
<div class="posts-mini placeholder-row"><div class="placeholder radius thumb-placeholder"></div><div class="flex1"><div class="placeholder k1"></div><div class="placeholder s1 mt6"></div></div></div>
<?php endfor; ?>
</div>
<?php endif; ?>
</div>
<?php
}
echo '</section>';
echo $args['after_widget'];
}
public function form($instance): void {
$title = (string) ($instance['title'] ?? '');
$category_ids = aksrt_home_widget_normalize_category_ids($instance['category_ids'] ?? [], 6);
$count = (int) ($instance['count'] ?? 6);
$columns = (int) ($instance['columns'] ?? 3);
$layout = aksrt_home_widget_resolve_layout($instance['layout'] ?? 'card', 'card');
$show_more = ! empty($instance['show_more']);
$categories = aksrt_home_widget_available_categories();
?>
<p>
<label for="<?php echo esc_attr($this->get_field_id('title')); ?>"><?php esc_html_e('标题:', 'aksrt'); ?></label>
<input class="widefat" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" type="text" value="<?php echo esc_attr($title); ?>">
</p>
<p>
<label for="<?php echo esc_attr($this->get_field_id('category_ids')); ?>"><?php esc_html_e('分类(可多选):', 'aksrt'); ?></label>
<select class="widefat" id="<?php echo esc_attr($this->get_field_id('category_ids')); ?>" name="<?php echo esc_attr($this->get_field_name('category_ids')); ?>[]" multiple size="6">
<?php foreach ($categories as $category) : ?>
<option value="<?php echo (int) $category->term_id; ?>" <?php selected(in_array((int) $category->term_id, $category_ids, true)); ?>>
<?php echo esc_html($category->name); ?>
</option>
<?php endforeach; ?>
</select>
<small><?php esc_html_e('留空时自动选择热门分类。', 'aksrt'); ?></small>
</p>
<p>
<label for="<?php echo esc_attr($this->get_field_id('count')); ?>"><?php esc_html_e('每个 Tab 的文章数量:', 'aksrt'); ?></label>
<input class="tiny-text" id="<?php echo esc_attr($this->get_field_id('count')); ?>" name="<?php echo esc_attr($this->get_field_name('count')); ?>" type="number" min="1" max="12" value="<?php echo (int) $count; ?>">
</p>
<p>
<label for="<?php echo esc_attr($this->get_field_id('columns')); ?>"><?php esc_html_e('桌面列数:', 'aksrt'); ?></label>
<input class="tiny-text" id="<?php echo esc_attr($this->get_field_id('columns')); ?>" name="<?php echo esc_attr($this->get_field_name('columns')); ?>" type="number" min="1" max="4" value="<?php echo (int) $columns; ?>">
</p>
<p>
<label for="<?php echo esc_attr($this->get_field_id('layout')); ?>"><?php esc_html_e('布局:', 'aksrt'); ?></label>
<select class="widefat" id="<?php echo esc_attr($this->get_field_id('layout')); ?>" name="<?php echo esc_attr($this->get_field_name('layout')); ?>">
<?php foreach (aksrt_home_widget_allowed_layouts() as $value => $label) : ?>
<option value="<?php echo esc_attr($value); ?>" <?php selected($layout, $value); ?>>
<?php echo esc_html($label); ?>
</option>
<?php endforeach; ?>
</select>
</p>
<p>
<input class="checkbox" type="checkbox" <?php checked($show_more); ?> id="<?php echo esc_attr($this->get_field_id('show_more')); ?>" name="<?php echo esc_attr($this->get_field_name('show_more')); ?>" value="1">
<label for="<?php echo esc_attr($this->get_field_id('show_more')); ?>"><?php esc_html_e('显示“查看更多”链接', 'aksrt'); ?></label>
</p>
<?php if (function_exists('aksrt_widget_render_location_help')) { aksrt_widget_render_location_help('hybrid'); } ?>
<?php
}
public function update($new_instance, $old_instance): array {
return [
'title' => sanitize_text_field($new_instance['title'] ?? ''),
'category_ids' => aksrt_home_widget_normalize_category_ids($new_instance['category_ids'] ?? [], 6),
'count' => max(1, min(12, absint($new_instance['count'] ?? 6))),
'columns' => max(1, min(4, absint($new_instance['columns'] ?? 3))),
'layout' => aksrt_home_widget_resolve_layout($new_instance['layout'] ?? 'card', 'card'),
'show_more' => ! empty($new_instance['show_more']) ? 1 : 0,
];
}
/**
* Resolve configured category tabs.
*
* @param array $instance Widget instance.
* @return array<int, array{id:int,name:string,link:string}>
*/
private function resolve_tabs(array $instance): array {
$category_ids = aksrt_home_widget_normalize_category_ids($instance['category_ids'] ?? [], 6);
if (empty($category_ids)) {
$category_ids = aksrt_home_widget_fallback_category_ids(4);
}
$tabs = [];
foreach ($category_ids as $category_id) {
$term = get_term($category_id, 'category');
if (! $term || is_wp_error($term)) {
continue;
}
$tabs[] = [
'id' => (int) $category_id,
'name' => $term->name,
'link' => get_category_link($category_id),
];
}
return $tabs;
}
}