From 66ee624cb99ff6095bcd6bafaced9f01cc68dfd1 Mon Sep 17 00:00:00 2001
From: unknown
Date: Tue, 21 Jul 2026 09:22:01 +0800
Subject: [PATCH] feat(widgets): buttons/text-title/yiyan depth + batch15 CSS
Buttons gain zibll but/hollow/color + obs animation; text-title supports
per-char entrance; yiyan wraps main-bg shell with affix/animate options.
---
assets/css/frontend/widget-batch5.css | 184 ++++++++++++++++++++++++++
src/AKSRT/Widgets/ButtonsWidget.php | 182 +++++++++++++++++--------
src/AKSRT/Widgets/WidgetTextTitle.php | 104 ++++++++++++---
src/AKSRT/Widgets/WidgetYiyan.php | 85 +++++++++---
4 files changed, 457 insertions(+), 98 deletions(-)
diff --git a/assets/css/frontend/widget-batch5.css b/assets/css/frontend/widget-batch5.css
index 61d5b6d6..45c11701 100644
--- a/assets/css/frontend/widget-batch5.css
+++ b/assets/css/frontend/widget-batch5.css
@@ -1825,3 +1825,187 @@
font-weight: var(--font-weight-sm, 600);
}
}
+
+/* ===== Batch15: icon-card / icon-cover / buttons / yiyan / text-title ===== */
+
+.icon-card {
+ text-align: center;
+ padding: 16px 10px;
+}
+.icon-card .card-icon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ line-height: 1;
+ color: var(--theme-color, var(--focus-color, #f04494));
+}
+.icon-card .card-icon.toggle-radius {
+ width: 3.2em;
+ height: 3.2em;
+ border-radius: 50%;
+ background: rgba(240, 68, 148, 0.1);
+}
+.icon-card .card-icon img {
+ width: 1em;
+ height: 1em;
+ object-fit: contain;
+}
+.icon-card .main-color {
+ color: inherit;
+ text-decoration: none;
+}
+.icon-card .main-color:hover {
+ color: var(--theme-color, var(--focus-color, #f04494));
+}
+
+.icon-cover-card {
+ display: flex;
+ align-items: center;
+ gap: 0;
+ padding: 12px;
+ border-radius: 8px;
+ background: var(--surface-1, #fff);
+ border: 1px solid var(--color-line, #e5e7eb);
+ transition: box-shadow .2s ease, transform .2s ease;
+}
+a.main-color > .icon-cover-card:hover,
+.icon-cover-card:hover {
+ box-shadow: 0 6px 18px rgba(0,0,0,.06);
+ transform: translateY(-1px);
+}
+.icon-cover-icon.badg.cir {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 2.4em;
+ height: 2.4em;
+ min-width: 2.4em;
+ border-radius: 50%;
+ background: rgba(59, 130, 246, 0.12);
+ color: #2563eb;
+ flex-shrink: 0;
+}
+.icon-cover-icon.radius4 {
+ border-radius: 10px !important;
+}
+.icon-cover-icon img {
+ width: 70%;
+ height: 70%;
+ object-fit: contain;
+}
+.icon-cover-desc {
+ min-width: 0;
+}
+.icon-cover-desc .font-bold {
+ font-weight: 700;
+}
+
+/* Buttons wrap (zibll flex hh) */
+.widget-buttons .widget-buttons-wrap {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--pc-gap, 12px);
+}
+.widget-buttons .widget-buttons-wrap.align-center { justify-content: center; }
+.widget-buttons .widget-buttons-wrap.align-left { justify-content: flex-start; }
+.widget-buttons .widget-buttons-wrap.align-right { justify-content: flex-end; }
+.widget-buttons .widget-buttons-wrap .but {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 4px;
+ padding: 8px 16px;
+ border-radius: 6px;
+ text-decoration: none;
+ font-size: 14px;
+ line-height: 1.3;
+ background: var(--theme-color, #3b82f6);
+ color: #fff;
+ border: 1px solid transparent;
+ min-width: var(--pc-width, auto);
+}
+.widget-buttons .widget-buttons-wrap .but.radius {
+ border-radius: 999px;
+}
+.widget-buttons .widget-buttons-wrap .but.hollow {
+ background: transparent;
+ color: var(--theme-color, #3b82f6);
+ border-color: currentColor;
+}
+.widget-buttons .widget-buttons-wrap .but.c-blue { background: #3b82f6; }
+.widget-buttons .widget-buttons-wrap .but.c-green { background: #22c55e; }
+.widget-buttons .widget-buttons-wrap .but.c-yellow { background: #eab308; color: #1a1a1a; }
+.widget-buttons .widget-buttons-wrap .but.c-red { background: #ef4444; }
+.widget-buttons .widget-buttons-wrap .but.c-purple { background: #a855f7; }
+.widget-buttons .widget-buttons-wrap .but.hollow.c-blue { color: #3b82f6; background: transparent; }
+.widget-buttons .widget-buttons-wrap .but.hollow.c-green { color: #22c55e; background: transparent; }
+.widget-buttons .widget-buttons-wrap .but.hollow.c-red { color: #ef4444; background: transparent; }
+.widget-buttons .widget-buttons-wrap .but.hollow.c-purple { color: #a855f7; background: transparent; }
+.widget-buttons .widget-buttons-wrap .but.hollow.c-yellow { color: #ca8a04; background: transparent; }
+.widget-buttons.pc-size-small .but { padding: 4px 10px; font-size: 12px; }
+.widget-buttons.pc-size-medium .but { padding: 6px 14px; font-size: 13px; }
+.widget-buttons.pc-size-large .but { padding: 10px 18px; font-size: 15px; }
+@media (max-width: 767px) {
+ .widget-buttons .widget-buttons-wrap {
+ gap: var(--m-gap, 6px);
+ }
+ .widget-buttons .widget-buttons-wrap .but {
+ min-width: var(--m-width, auto);
+ }
+ .widget-buttons.m-size-small .but { padding: 4px 10px; font-size: 12px; }
+ .widget-buttons.m-size-medium .but { padding: 6px 12px; font-size: 13px; }
+ .widget-buttons.m-size-large .but { padding: 8px 14px; font-size: 14px; }
+}
+
+/* Yiyan shell */
+.yiyan-box.main-bg,
+.yiyan-box-wrap .yiyan-box {
+ background: var(--surface-1, #fff);
+ border-radius: 8px;
+ box-shadow: 0 0 10px var(--main-shadow, rgba(116,116,116,.08));
+ padding: 18px 16px;
+ text-align: center;
+}
+.yiyan-box .aksrt-yiyan,
+.yiyan-box .yiyan {
+ margin: 0;
+}
+
+/* Text title char animation inherits data-aksrt-animate */
+.aksrt-text-title__main > span[data-aksrt-animate] {
+ display: inline-block;
+}
+
+/* Shared icon grid columns (reuse term-cards pattern) */
+.widget_aksrt_icon_cards .row.gutters-5,
+.widget_aksrt_icon_cover_cards .row.gutters-5 {
+ display: flex;
+ flex-wrap: wrap;
+ margin: 0 -4px;
+}
+.widget_aksrt_icon_cards .row.gutters-5 > [class*="col-"],
+.widget_aksrt_icon_cover_cards .row.gutters-5 > [class*="col-"] {
+ padding: 0 4px;
+ box-sizing: border-box;
+}
+.widget_aksrt_icon_cards .col-sm-12,
+.widget_aksrt_icon_cover_cards .col-sm-12 { width: 100%; }
+.widget_aksrt_icon_cards .col-sm-6,
+.widget_aksrt_icon_cover_cards .col-sm-6 { width: 50%; }
+.widget_aksrt_icon_cards .col-sm-4,
+.widget_aksrt_icon_cover_cards .col-sm-4 { width: 33.333%; }
+.widget_aksrt_icon_cards .col-sm-3,
+.widget_aksrt_icon_cover_cards .col-sm-3 { width: 25%; }
+.widget_aksrt_icon_cards .col-sm-2,
+.widget_aksrt_icon_cover_cards .col-sm-2 { width: 16.666%; }
+@media (max-width: 767px) {
+ .widget_aksrt_icon_cards .col-xs-12,
+ .widget_aksrt_icon_cover_cards .col-xs-12 { width: 100%; }
+ .widget_aksrt_icon_cards .col-xs-6,
+ .widget_aksrt_icon_cover_cards .col-xs-6 { width: 50%; }
+ .widget_aksrt_icon_cards .col-xs-4,
+ .widget_aksrt_icon_cover_cards .col-xs-4 { width: 33.333%; }
+ .widget_aksrt_icon_cards .col-xs-3,
+ .widget_aksrt_icon_cover_cards .col-xs-3 { width: 25%; }
+}
diff --git a/src/AKSRT/Widgets/ButtonsWidget.php b/src/AKSRT/Widgets/ButtonsWidget.php
index 20eb79b2..b28a6bb3 100644
--- a/src/AKSRT/Widgets/ButtonsWidget.php
+++ b/src/AKSRT/Widgets/ButtonsWidget.php
@@ -1,6 +1,6 @@
esc_html__('显示一组自定义按钮,支持外链跳转与样式。', 'aksrt'),
+ 'description' => esc_html__('显示一组自定义按钮,支持外链跳转、色系与入场动画。', 'aksrt'),
+ 'classname' => 'widget_aksrt_buttons',
]);
}
+ /**
+ * @param array $args
+ * @param array $instance
+ */
public function widget($args, $instance): void {
$buttons = isset($instance['buttons']) && is_array($instance['buttons']) ? $instance['buttons'] : [];
- $layout = sanitize_key((string) ($instance['layout'] ?? 'horizontal'));
- if (!in_array($layout, ['vertical', 'horizontal'], true)) {
- $layout = 'horizontal';
+ // Also accept zibll-style `items`.
+ if (!$buttons && isset($instance['items']) && is_array($instance['items'])) {
+ $buttons = $instance['items'];
}
+
$align = sanitize_key((string) ($instance['align'] ?? 'center'));
if (!in_array($align, ['left', 'center', 'right'], true)) {
$align = 'center';
@@ -30,31 +36,94 @@ class ButtonsWidget extends \WP_Widget {
$radius = !empty($instance['radius']);
$hollow = !empty($instance['hollow']);
$go_link = !empty($instance['go_link']);
- $gap = max(4, min(30, (int) ($instance['gap'] ?? 10)));
+ $pc_gap = max(0, min(40, (int) ($instance['pc_gap'] ?? $instance['gap'] ?? 12)));
+ $m_gap = max(0, min(30, (int) ($instance['m_gap'] ?? 6)));
+ $pc_pad = sanitize_key((string) ($instance['pc_padding'] ?? 'large'));
+ $m_pad = sanitize_key((string) ($instance['m_padding'] ?? 'medium'));
+ if (!in_array($pc_pad, ['small', 'medium', 'large'], true)) {
+ $pc_pad = 'large';
+ }
+ if (!in_array($m_pad, ['small', 'medium', 'large'], true)) {
+ $m_pad = 'medium';
+ }
+ $anim = sanitize_key((string) ($instance['obs_animation'] ?? ''));
+ $anim_repeat = !empty($instance['animation_repeat']);
+ $pc_width = max(0, min(400, (int) ($instance['pc_width'] ?? 0)));
+ $m_width = max(0, min(400, (int) ($instance['m_width'] ?? 0)));
- $items = [];
+ $but_class = 'but';
+ if ($radius) {
+ $but_class .= ' radius';
+ }
+ if ($hollow) {
+ $but_class .= ' hollow';
+ }
+
+ $buts = '';
+ $key = 0;
foreach ($buttons as $btn) {
if (!is_array($btn)) {
continue;
}
$text = sanitize_text_field((string) ($btn['text'] ?? ''));
- $url = esc_url_raw(trim((string) ($btn['url'] ?? '')));
if ($text === '') {
continue;
}
+ $url = '';
+ $target = '';
+ if (is_array($btn['link'] ?? null)) {
+ $url = esc_url_raw(trim((string) ($btn['link']['url'] ?? '')));
+ $target = sanitize_text_field((string) ($btn['link']['target'] ?? ''));
+ } else {
+ $url = esc_url_raw(trim((string) ($btn['url'] ?? '')));
+ $target = !empty($btn['new_window']) || !empty($btn['target']) ? '_blank' : '';
+ }
if ($url !== '' && $go_link && function_exists('aksrt_go_link')) {
$url = aksrt_go_link($url, true);
}
- $items[] = [
- 'text' => $text,
- 'url' => $url,
- 'style' => sanitize_key((string) ($btn['style'] ?? 'primary')),
- 'icon' => sanitize_text_field((string) ($btn['icon'] ?? $btn['icon_left'] ?? '')),
- 'icon_right' => sanitize_text_field((string) ($btn['icon_right'] ?? '')),
- 'new_window' => !empty($btn['new_window']) || !empty($btn['target']),
+ if ($url === '') {
+ $url = 'javascript:void(0)';
+ }
+
+ $color = sanitize_html_class((string) ($btn['color'] ?? $btn['style'] ?? ''));
+ // Map legacy style keys to color classes.
+ $style_map = [
+ 'primary' => 'c-blue',
+ 'secondary' => 'c-blue-2',
+ 'outline' => '',
+ 'text' => '',
+ 'pink' => 'c-red',
+ 'blue' => 'c-blue',
+ 'green' => 'c-green',
+ 'yellow' => 'c-yellow',
+ 'purple' => 'c-purple',
];
+ if (isset($style_map[$color])) {
+ $color = $style_map[$color];
+ }
+ $item_class = trim($but_class . ' ' . $color);
+
+ $icon_left = sanitize_text_field((string) ($btn['icon'] ?? $btn['icon_left'] ?? ''));
+ $icon_right = sanitize_text_field((string) ($btn['icon_right'] ?? ''));
+ $left_html = $icon_left !== '' ? ' ' : '';
+ $right_html = $icon_right !== '' ? ' ' : '';
+
+ $anim_pack = function_exists('aksrt_obs_animate_attrs')
+ ? aksrt_obs_animate_attrs($anim, $anim_repeat, $key * 0.15)
+ : ['class' => '', 'attr' => ''];
+
+ $tgt = $target !== '' && strpos($url, 'javascript:') !== 0
+ ? ' target="' . esc_attr($target) . '" rel="noopener noreferrer"'
+ : '';
+
+ $buts .= '';
+ $key++;
}
- if (!$items) {
+
+ if ($buts === '') {
return;
}
@@ -64,36 +133,26 @@ class ButtonsWidget extends \WP_Widget {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
- $wrap = 'widget-buttons widget-buttons--' . $layout . ' align-' . $align;
- if ($radius) {
- $wrap .= ' is-radius';
+ $style_attr = '';
+ if ($pc_width > 0) {
+ $style_attr .= '--pc-width:' . $pc_width . 'px;';
}
- if ($hollow) {
- $wrap .= ' is-hollow';
+ if ($m_width > 0) {
+ $style_attr .= '--m-width:' . $m_width . 'px;';
}
- echo '';
- foreach ($items as $btn) {
- $style = in_array($btn['style'], ['primary', 'secondary', 'outline', 'text', 'pink', 'blue', 'green', 'yellow', 'purple'], true)
- ? $btn['style']
- : 'primary';
- $cls = 'widget-buttons__btn widget-buttons__btn--' . $style;
- $href = $btn['url'] !== '' ? $btn['url'] : 'javascript:void(0)';
- $tgt = $btn['new_window'] && $btn['url'] !== '' ? ' target="_blank" rel="noopener noreferrer"' : '';
- echo '
';
- if ($btn['icon'] !== '') {
- echo ' ';
- }
- echo esc_html($btn['text']);
- if ($btn['icon_right'] !== '') {
- echo ' ';
- }
- echo '';
- }
- echo '
';
+ $style_attr .= '--pc-gap:' . $pc_gap . 'px;--m-gap:' . $m_gap . 'px;--btn-gap:' . $pc_gap . 'px;';
+
+ echo '';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
+ /**
+ * @param array $instance
+ */
public function form($instance): void {
$buttons = isset($instance['buttons']) && is_array($instance['buttons']) ? $instance['buttons'] : [];
while (count($buttons) < 4) {
@@ -106,12 +165,6 @@ class ButtonsWidget extends \WP_Widget {
-
+
+
+
+
+
$btn) : ?>
" value="" placeholder="">
@@ -132,7 +196,7 @@ class ButtonsWidget extends \WP_Widget {
" value="" placeholder="">
" value="" placeholder="">
@@ -141,6 +205,11 @@ class ButtonsWidget extends \WP_Widget {
$new_instance
+ * @param array
$old_instance
+ * @return array
+ */
public function update($new_instance, $old_instance): array {
$buttons = [];
if (isset($new_instance['buttons']) && is_array($new_instance['buttons'])) {
@@ -166,14 +235,17 @@ class ButtonsWidget extends \WP_Widget {
}
}
return [
- 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
- 'layout' => ($new_instance['layout'] ?? '') === 'vertical' ? 'vertical' : 'horizontal',
- 'align' => in_array(($new_instance['align'] ?? ''), ['left', 'center', 'right'], true) ? $new_instance['align'] : 'center',
- 'radius' => !empty($new_instance['radius']) ? 1 : 0,
- 'hollow' => !empty($new_instance['hollow']) ? 1 : 0,
- 'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
- 'gap' => max(4, min(30, (int) ($new_instance['gap'] ?? 10))),
- 'buttons' => $buttons,
+ 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
+ 'align' => in_array(($new_instance['align'] ?? ''), ['left', 'center', 'right'], true) ? $new_instance['align'] : 'center',
+ 'radius' => !empty($new_instance['radius']) ? 1 : 0,
+ 'hollow' => !empty($new_instance['hollow']) ? 1 : 0,
+ 'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
+ 'gap' => max(4, min(30, (int) ($new_instance['gap'] ?? 12))),
+ 'pc_gap' => max(0, min(40, (int) ($new_instance['pc_gap'] ?? $new_instance['gap'] ?? 12))),
+ 'm_gap' => max(0, min(30, (int) ($new_instance['m_gap'] ?? 6))),
+ 'obs_animation' => sanitize_key((string) ($new_instance['obs_animation'] ?? '')),
+ 'animation_repeat'=> !empty($new_instance['animation_repeat']) ? 1 : 0,
+ 'buttons' => $buttons,
];
}
}
diff --git a/src/AKSRT/Widgets/WidgetTextTitle.php b/src/AKSRT/Widgets/WidgetTextTitle.php
index 76a64bac..5b208df7 100644
--- a/src/AKSRT/Widgets/WidgetTextTitle.php
+++ b/src/AKSRT/Widgets/WidgetTextTitle.php
@@ -1,6 +1,6 @@
esc_html__('自定义标题 + 副标题区块,用于模块分隔。', 'aksrt'),
+ 'description' => esc_html__('自定义标题 + 副标题区块,支持逐字入场动画。', 'aksrt'),
'classname' => 'widget_aksrt_text_title',
]
);
}
+ /**
+ * @param array $args
+ * @param array $instance
+ */
public function widget($args, $instance): void {
$title = trim((string) ($instance['title'] ?? ''));
$desc = trim((string) ($instance['desc'] ?? ''));
@@ -39,6 +43,9 @@ class WidgetTextTitle extends \WP_Widget {
$desc_size = max(11, min(24, (int) ($instance['desc_size'] ?? 14)));
$title_color = sanitize_hex_color((string) ($instance['title_color'] ?? '')) ?: '';
$desc_color = sanitize_hex_color((string) ($instance['desc_color'] ?? '')) ?: '';
+ $anim = sanitize_key((string) ($instance['obs_animation'] ?? ''));
+ $anim_repeat = !empty($instance['animation_repeat']);
+ $icon_width = max(0, min(64, (int) ($instance['title_icon_width'] ?? 0)));
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -50,26 +57,63 @@ class WidgetTextTitle extends \WP_Widget {
$desc_color !== '' ? '--aksrt-tt-desc-color:' . $desc_color . ';' : ''
);
- echo '';
- if ($title !== '') {
- echo '
';
- if ($icon_left !== '') {
- echo '';
+ $icon_w_attr = $icon_width > 0 ? ' style="--icon-width:' . (int) $icon_width . 'px;"' : '';
+ $left_html = $icon_left !== ''
+ ? ''
+ : '';
+ $right_html = $icon_right !== ''
+ ? ''
+ : '';
+
+ $title_html = '';
+ if ($title !== '' && $anim !== '' && function_exists('aksrt_obs_animate_attrs')) {
+ // Split title into per-char spans with staggered delay (zibll).
+ $chars = preg_split('//u', $title, -1, PREG_SPLIT_NO_EMPTY) ?: [];
+ $pieces = [];
+ if ($left_html !== '') {
+ $pieces[] = $left_html;
}
- echo '' . esc_html($title) . '';
- if ($icon_right !== '') {
- echo '';
+ foreach ($chars as $ch) {
+ $pieces[] = $ch;
}
- echo '
';
+ if ($right_html !== '') {
+ $pieces[] = $right_html;
+ }
+ foreach ($pieces as $i => $piece) {
+ $pack = aksrt_obs_animate_attrs($anim, $anim_repeat, $i * 0.05);
+ $is_html = str_starts_with((string) $piece, '<');
+ $inner = $is_html ? $piece : esc_html((string) $piece);
+ $title_html .= '
' . $inner . '';
+ }
+ } elseif ($title !== '') {
+ $title_html = $left_html . esc_html($title) . $right_html;
}
+
+ $desc_html = '';
if ($desc !== '') {
- echo '
' . esc_html($desc) . '
';
+ if ($anim !== '' && function_exists('aksrt_obs_animate_attrs')) {
+ $pack = aksrt_obs_animate_attrs($anim, $anim_repeat, 0.6);
+ $desc_html = '
' . esc_html($desc) . '
';
+ } else {
+ $desc_html = '
' . esc_html($desc) . '
';
+ }
}
+
+ echo '
';
+ if ($title_html !== '') {
+ echo '
' . $title_html . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
+ echo $desc_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '
';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
+ /**
+ * @param array
$instance
+ */
public function form($instance): void {
?>
@@ -109,6 +153,16 @@ class WidgetTextTitle extends \WP_Widget {
+
+
+
+
+
@@ -122,17 +176,25 @@ class WidgetTextTitle extends \WP_Widget {
$new_instance
+ * @param array $old_instance
+ * @return array
+ */
public function update($new_instance, $old_instance): array {
return [
- 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
- 'desc' => sanitize_text_field((string) ($new_instance['desc'] ?? '')),
- 'align' => in_array(($new_instance['align'] ?? ''), ['left', 'center', 'right'], true) ? $new_instance['align'] : 'center',
- 'title_icon_left' => sanitize_text_field((string) ($new_instance['title_icon_left'] ?? '')),
- 'title_icon_right' => sanitize_text_field((string) ($new_instance['title_icon_right'] ?? '')),
- 'title_size' => max(12, min(48, (int) ($new_instance['title_size'] ?? 22))),
- 'desc_size' => max(11, min(24, (int) ($new_instance['desc_size'] ?? 14))),
- 'title_color' => sanitize_hex_color((string) ($new_instance['title_color'] ?? '')) ?: '',
- 'desc_color' => sanitize_hex_color((string) ($new_instance['desc_color'] ?? '')) ?: '',
+ 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
+ 'desc' => sanitize_text_field((string) ($new_instance['desc'] ?? '')),
+ 'align' => in_array(($new_instance['align'] ?? ''), ['left', 'center', 'right'], true) ? $new_instance['align'] : 'center',
+ 'title_icon_left' => sanitize_text_field((string) ($new_instance['title_icon_left'] ?? '')),
+ 'title_icon_right' => sanitize_text_field((string) ($new_instance['title_icon_right'] ?? '')),
+ 'title_size' => max(12, min(48, (int) ($new_instance['title_size'] ?? 22))),
+ 'desc_size' => max(11, min(24, (int) ($new_instance['desc_size'] ?? 14))),
+ 'title_color' => sanitize_hex_color((string) ($new_instance['title_color'] ?? '')) ?: '',
+ 'desc_color' => sanitize_hex_color((string) ($new_instance['desc_color'] ?? '')) ?: '',
+ 'obs_animation' => sanitize_key((string) ($new_instance['obs_animation'] ?? '')),
+ 'animation_repeat' => !empty($new_instance['animation_repeat']) ? 1 : 0,
+ 'title_icon_width' => max(0, min(64, (int) ($new_instance['title_icon_width'] ?? 0))),
];
}
}
diff --git a/src/AKSRT/Widgets/WidgetYiyan.php b/src/AKSRT/Widgets/WidgetYiyan.php
index 5952a5fa..0a610553 100644
--- a/src/AKSRT/Widgets/WidgetYiyan.php
+++ b/src/AKSRT/Widgets/WidgetYiyan.php
@@ -1,5 +1,7 @@
*/
private static function type_labels(): array {
@@ -32,9 +32,6 @@ class WidgetYiyan extends \WP_Widget {
];
}
- /**
- * Sanitize stored type to a valid Hitokoto category letter or empty.
- */
private static function sanitize_type(string $type): string {
$type = strtolower(trim($type));
return preg_match('/^[a-l]$/', $type) ? $type : '';
@@ -44,35 +41,54 @@ class WidgetYiyan extends \WP_Widget {
parent::__construct(
'aksrt_yiyan',
__('一言轮播', 'aksrt'),
- ['description' => __('显示随机一言/每日语录,支持自动轮播。', 'aksrt')]
+ [
+ 'description' => __('显示随机一言/每日语录,支持自动轮播。', 'aksrt'),
+ 'classname' => 'widget_aksrt_yiyan yiyan-box',
+ ]
);
}
+ /**
+ * @param array $args
+ * @param array $instance
+ */
public function widget($args, $instance): void {
- $title = !empty($instance['title']) ? $instance['title'] : '';
+ $title = !empty($instance['title']) ? (string) $instance['title'] : '';
$count = !empty($instance['count']) ? max(1, min(10, (int) $instance['count'])) : 5;
$interval = !empty($instance['interval']) ? max(3, min(30, (int) $instance['interval'])) : 5;
$type = self::sanitize_type((string) ($instance['type'] ?? ''));
- $icon = !empty($instance['icon']) ? $instance['icon'] : 'fas fa-quote-left';
+ $icon = !empty($instance['icon']) ? (string) $instance['icon'] : 'fas fa-quote-left';
+ $in_affix = !empty($instance['in_affix']);
+ $anim = sanitize_key((string) ($instance['obs_animation'] ?? ''));
+ $anim_repeat = !empty($instance['animation_repeat']);
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
- if ($title) {
+ if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
+ // zibll shell: theme-box + yiyan-box main-bg text-center box-body radius8 main-shadow.
+ $pack = function_exists('aksrt_obs_animate_attrs')
+ ? aksrt_obs_animate_attrs($anim, $anim_repeat, 0)
+ : ['class' => '', 'attr' => ''];
+ $affix = $in_affix ? ' data-affix="true"' : '';
+ $box_class = 'theme-box yiyan-box-wrap' . $pack['class'];
+
+ echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ echo '
';
+
if (function_exists('aksrt_render_yiyan_block')) {
aksrt_render_yiyan_block([
- 'class' => 'aksrt-yiyan',
+ 'class' => 'aksrt-yiyan yiyan',
'type' => $type,
'count' => $count,
'interval' => $interval,
'icon' => $icon,
]);
} else {
- // Fallback if single-render helpers are not loaded.
?>
-
';
+
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
+ /**
+ * @param array
$instance
+ */
public function form($instance): void {
- $title = !empty($instance['title']) ? $instance['title'] : '一言';
+ $title = !empty($instance['title']) ? (string) $instance['title'] : '一言';
$count = !empty($instance['count']) ? (int) $instance['count'] : 5;
$interval = !empty($instance['interval']) ? (int) $instance['interval'] : 5;
$type = self::sanitize_type((string) ($instance['type'] ?? ''));
- $icon = !empty($instance['icon']) ? $instance['icon'] : 'fas fa-quote-left';
+ $icon = !empty($instance['icon']) ? (string) $instance['icon'] : 'fas fa-quote-left';
$labels = self::type_labels();
- // Curated subset for the form (full map still used for sanitization).
$form_types = ['', 'a', 'd', 'i', 'k', 'h', 'j'];
?>
@@ -135,16 +155,37 @@ class WidgetYiyan extends \WP_Widget {
name="get_field_name('icon')); ?>" type="text"
value="">
+
+ >
+
+
+
+
+
+
$new_instance
+ * @param array $old_instance
+ * @return array
+ */
public function update($new_instance, $old_instance): array {
- $instance = [];
- $instance['title'] = sanitize_text_field($new_instance['title'] ?? '');
- $instance['count'] = max(1, min(10, absint($new_instance['count'] ?? 5)));
- $instance['interval'] = max(3, min(30, absint($new_instance['interval'] ?? 5)));
- $instance['type'] = self::sanitize_type((string) ($new_instance['type'] ?? ''));
- $instance['icon'] = sanitize_text_field($new_instance['icon'] ?? 'fas fa-quote-left');
- return $instance;
+ return [
+ 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
+ 'count' => max(1, min(10, absint($new_instance['count'] ?? 5))),
+ 'interval' => max(3, min(30, absint($new_instance['interval'] ?? 5))),
+ 'type' => self::sanitize_type((string) ($new_instance['type'] ?? '')),
+ 'icon' => sanitize_text_field((string) ($new_instance['icon'] ?? 'fas fa-quote-left')),
+ 'in_affix' => !empty($new_instance['in_affix']) ? 1 : 0,
+ 'obs_animation' => sanitize_key((string) ($new_instance['obs_animation'] ?? '')),
+ 'animation_repeat' => !empty($new_instance['animation_repeat']) ? 1 : 0,
+ ];
}
}