From 05200ce7818810285a08d8eed9c1e1f85fb2c865 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 19 Jul 2026 01:22:36 +0800 Subject: [PATCH] feat(widgets): deepen categories, tag cloud, vertical icon cards Add hierarchy/include-exclude for categories; multi-taxonomy colored tag cloud; vertical icon-card layout with go_link and mobile columns. --- src/AKSRT/Widgets/CategoriesWidget.php | 168 +++++++++++++++----- src/AKSRT/Widgets/IconCardsWidget.php | 207 +++++++++++++++---------- src/AKSRT/Widgets/TagCloudWidget.php | 152 +++++++++++++----- 3 files changed, 374 insertions(+), 153 deletions(-) diff --git a/src/AKSRT/Widgets/CategoriesWidget.php b/src/AKSRT/Widgets/CategoriesWidget.php index df7a969e..fe2362ce 100644 --- a/src/AKSRT/Widgets/CategoriesWidget.php +++ b/src/AKSRT/Widgets/CategoriesWidget.php @@ -1,5 +1,7 @@ esc_html__('以列表形式展示文章分类', 'aksrt'), + [ + 'description' => esc_html__('以列表形式展示文章分类', 'aksrt'), 'customize_selective_refresh' => true, - ) + ] ); } - public function widget($args, $instance) { - echo $args['before_widget']; + public function widget($args, $instance): void { + echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - $title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base); + $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 + } - if ($title): ?> -

- parse_ids((string) ($instance['exclude'] ?? '')); + $include = $this->parse_ids((string) ($instance['include'] ?? '')); - $count = absint($instance['count'] ?: 8); - $cats = get_categories(array('number' => $count, 'orderby' => 'count', 'order' => 'DESC')); - ?> - - 'category', + 'number' => $count, + 'orderby' => $orderby, + 'order' => $order, + 'hide_empty' => $hide_empty, + 'hierarchical' => $hierarchical, + ]; + if ($parent > 0) { + $term_args['parent'] = $parent; + } + if ($include) { + $term_args['include'] = $include; + $term_args['orderby'] = 'include'; + unset($term_args['number']); + } + if ($exclude) { + $term_args['exclude'] = $exclude; + } + + $cats = get_categories($term_args); + if (!$cats) { + echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + return; + } + + echo ''; + + echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } - public function form($instance) { - $title = $instance['title'] ?? __('分类目录', 'aksrt'); - $count = isset($instance['count']) ? (int)$instance['count'] : 8; + /** @return array */ + private function parse_ids(string $raw): array { + if ($raw === '') { + return []; + } + $ids = []; + foreach (preg_split('/[\s,]+/', $raw) ?: [] as $p) { + $id = (int) $p; + if ($id > 0) { + $ids[] = $id; + } + } + return array_values(array_unique($ids)); + } + + public function form($instance): void { ?>

- - +

- - + +

+

+ + +

+

+ +

+

+ +

+

+ +

+

+
+
+

sanitize_text_field($new['title']), - 'count' => absint($new['count']), + 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')), + 'count' => max(1, min(50, (int) ($new_instance['count'] ?? 8))), + 'orderby' => $orderby, + 'order' => strtoupper((string) ($new_instance['order'] ?? 'DESC')) === 'ASC' ? 'ASC' : 'DESC', + 'parent' => max(0, (int) ($new_instance['parent'] ?? 0)), + 'include' => sanitize_text_field((string) ($new_instance['include'] ?? '')), + 'exclude' => sanitize_text_field((string) ($new_instance['exclude'] ?? '')), + 'show_count' => !empty($new_instance['show_count']) ? 1 : 0, + 'hide_empty' => !empty($new_instance['hide_empty']) ? 1 : 0, + 'hierarchical' => !empty($new_instance['hierarchical']) ? 1 : 0, ]; } } diff --git a/src/AKSRT/Widgets/IconCardsWidget.php b/src/AKSRT/Widgets/IconCardsWidget.php index 84e422c8..1237a020 100644 --- a/src/AKSRT/Widgets/IconCardsWidget.php +++ b/src/AKSRT/Widgets/IconCardsWidget.php @@ -1,5 +1,7 @@ esc_html__('显示图标+文字卡片网格', 'aksrt'), - ]); - } + public function __construct() { + parent::__construct('aksrt_icon_cards', esc_html__('AKSRT 图标卡片', 'aksrt'), [ + 'description' => esc_html__('图标在上、文案在下的竖版图标卡片网格。', 'aksrt'), + ]); + } - public function widget($args, $instance): void { - echo $args['before_widget']; - $title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base); - $cards = isset($instance['cards']) && is_array($instance['cards']) ? $instance['cards'] : []; - $columns = max(1, min(4, (int) ($instance['columns'] ?? 2))); + public function widget($args, $instance): void { + $cards = isset($instance['cards']) && is_array($instance['cards']) ? $instance['cards'] : []; + $columns = max(1, min(4, (int) ($instance['columns'] ?? $instance['pc_row'] ?? 2))); + $m_cols = max(1, min(3, (int) ($instance['m_row'] ?? 2))); + $go_link = !empty($instance['go_link']); + $show_bg = !isset($instance['show_widget_bg']) || !empty($instance['show_widget_bg']); + $icon_size = max(18, min(48, (int) ($instance['icon_size'] ?? 28))); - if ($title) echo $args['before_title'] . esc_html($title) . $args['after_title']; + $valid = []; + foreach ($cards as $card) { + if (!is_array($card)) { + continue; + } + $title = sanitize_text_field((string) ($card['title'] ?? '')); + if ($title === '') { + continue; + } + $url = esc_url_raw(trim((string) ($card['link'] ?? $card['url'] ?? ''))); + if ($url !== '' && $go_link && function_exists('aksrt_go_link')) { + $url = aksrt_go_link($url, true); + } + $valid[] = [ + 'title' => $title, + 'icon' => sanitize_text_field((string) ($card['icon'] ?? '')), + 'desc' => sanitize_text_field((string) ($card['description'] ?? $card['desc'] ?? '')), + 'url' => $url, + ]; + } + if (!$valid) { + return; + } - if (!empty($cards)) : ?> -
- - < class="widget-icon-cards__item"> - -
- -
-
- -
- -
- > - -
- id_base); + if ($title !== '') { + echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } - echo $args['after_widget']; - } + $style = sprintf('--ic-columns:%d;--ic-m-columns:%d;--ic-icon:%dpx;', $columns, $m_cols, $icon_size); + echo '
'; + foreach ($valid as $card) { + $tag = $card['url'] !== '' ? 'a' : 'div'; + $extra = $card['url'] !== '' ? ' href="' . esc_url($card['url']) . '" target="_blank" rel="noopener noreferrer"' : ''; + echo '<' . $tag . ' class="widget-icon-cards__item is-vertical"' . $extra . '>'; + if ($card['icon'] !== '') { + echo '
'; + } + echo '
'; + echo '
' . esc_html($card['title']) . '
'; + if ($card['desc'] !== '') { + echo '
' . esc_html($card['desc']) . '
'; + } + echo '
'; + } + echo '
'; - public function form($instance): void { - $cards = isset($instance['cards']) && is_array($instance['cards']) ? $instance['cards'] : []; - while (count($cards) < 4) { - $cards[] = ['title' => '', 'icon' => '', 'link' => '', 'description' => '']; - } - ?> -

- - -

-

- - -

-
- $card) : ?> -
-

" value="" placeholder="">

-

" value="" placeholder="">

-

" value="" placeholder="">

-

" value="" placeholder="">

-
- sanitize_text_field($card['title'] ?? ''), - 'icon' => sanitize_text_field($card['icon'] ?? ''), - 'link' => esc_url_raw($card['link'] ?? ''), - 'description' => sanitize_text_field($card['description'] ?? ''), - ]; - } - } - return [ - 'title' => sanitize_text_field($new_instance['title'] ?? ''), - 'columns' => max(1, min(4, (int) ($new_instance['columns'] ?? 2))), - 'cards' => $cards, - ]; - } + public function form($instance): void { + $cards = isset($instance['cards']) && is_array($instance['cards']) ? $instance['cards'] : []; + while (count($cards) < 4) { + $cards[] = ['title' => '', 'icon' => '', 'link' => '', 'description' => '']; + } + ?> +

+ +

+

+ + +

+

+ + +

+
+ $card) : ?> +
+ " value="" placeholder=""> + " value="" placeholder=""> + " value="" placeholder=""> + " value="" placeholder=""> +
+ $title, + 'icon' => sanitize_text_field((string) ($card['icon'] ?? '')), + 'link' => esc_url_raw(trim((string) ($card['link'] ?? ''))), + 'description' => sanitize_text_field((string) ($card['description'] ?? '')), + ]; + if (count($cards) >= 12) { + break; + } + } + } + return [ + 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')), + 'columns' => max(1, min(4, (int) ($new_instance['columns'] ?? 2))), + 'm_row' => max(1, min(3, (int) ($new_instance['m_row'] ?? 2))), + 'go_link' => !empty($new_instance['go_link']) ? 1 : 0, + 'show_widget_bg' => !empty($new_instance['show_widget_bg']) ? 1 : 0, + 'cards' => $cards, + ]; + } } diff --git a/src/AKSRT/Widgets/TagCloudWidget.php b/src/AKSRT/Widgets/TagCloudWidget.php index 22eeb779..53044419 100644 --- a/src/AKSRT/Widgets/TagCloudWidget.php +++ b/src/AKSRT/Widgets/TagCloudWidget.php @@ -1,5 +1,7 @@ esc_html__('展示文章标签云', 'aksrt'), + [ + 'description' => esc_html__('展示文章标签/分类云', 'aksrt'), 'customize_selective_refresh' => true, - ) + ] ); } - public function widget($args, $instance) { - echo $args['before_widget']; + public function widget($args, $instance): void { + 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 + } - $title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base); - $count = absint($instance['count'] ?: 15); + $taxonomy = sanitize_key((string) ($instance['taxonomy'] ?? 'post_tag')); + if (!taxonomy_exists($taxonomy)) { + $taxonomy = 'post_tag'; + } + $number = max(1, min(80, (int) ($instance['count'] ?? $instance['number'] ?? 15))); + $orderby = sanitize_key((string) ($instance['orderby'] ?? 'count')); + if (!in_array($orderby, ['count', 'name', 'id'], true)) { + $orderby = 'count'; + } + $show_count = !empty($instance['show_count']); + $blank = !empty($instance['blank']); + $color = sanitize_html_class((string) ($instance['color'] ?? 'rand')); + $fixed_width = !empty($instance['fixed_width']); - if ($title): ?> -

- + $tags = get_terms([ + 'taxonomy' => $taxonomy, + 'orderby' => $orderby, + 'order' => 'DESC', + 'number' => $number, + 'hide_empty' => !empty($instance['hide_empty']), + ]); + if (is_wp_error($tags) || !$tags) { + echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + return; + } -
- $count, 'orderby' => 'count', 'order' => 'DESC']); - foreach ($tags as $tag): - $size = 12 + min(4, (int)($tag->count / 5)); - ?> - #name); ?> - -
- '; + foreach ($tags as $tag) { + $url = get_term_link($tag); + if (is_wp_error($url)) { + continue; + } + $cls = 'but'; + if ($color === 'rand') { + if ($i > 10) { + $i = 0; + } + $cls .= ' ' . $palette[$i]; + $i++; + } elseif ($color !== '') { + $cls .= ' ' . $color; + } + $tgt = $blank ? ' target="_blank" rel="noopener noreferrer"' : ''; + $count = $show_count ? ' (' . (int) $tag->count . ')' : ''; + echo '' . esc_html($tag->name) . $count . ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + echo ''; + + echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } - public function form($instance) { - $title = $instance['title'] ?? __('标签云', 'aksrt'); - $count = isset($instance['count']) ? (int)$instance['count'] : 15; + public function form($instance): void { + $taxs = get_taxonomies(['public' => true], 'objects'); ?>

- - +

- - + +

+

+ +

+

+ +

+

+ +

+

+
+
+
+

sanitize_text_field($new['title']), - 'count' => absint($new['count']), + 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')), + 'taxonomy' => sanitize_key((string) ($new_instance['taxonomy'] ?? 'post_tag')), + 'count' => max(1, min(80, (int) ($new_instance['count'] ?? 15))), + 'orderby' => in_array(($new_instance['orderby'] ?? ''), ['count', 'name', 'id'], true) ? $new_instance['orderby'] : 'count', + 'color' => sanitize_html_class((string) ($new_instance['color'] ?? 'rand')), + 'show_count' => !empty($new_instance['show_count']) ? 1 : 0, + 'blank' => !empty($new_instance['blank']) ? 1 : 0, + 'fixed_width' => !empty($new_instance['fixed_width']) ? 1 : 0, + 'hide_empty' => !empty($new_instance['hide_empty']) ? 1 : 0, ]; } }