feat(widgets): route WidgetSlider through shared aksrt_render_slider

Replace one-off fade/carousel inline script with .new-swiper stack, dual
height/effect options, and admin empty-state for misconfigured slides.
This commit is contained in:
kate522 committed 2026-07-21 08:04:03 +08:00
1 parent 93fd6a93c2
commit 96bd4c8423
1 file changed
+80 -83
+80 -83
View File
@@ -1,6 +1,6 @@
<?php <?php
/** /**
* Simple image slider widget (zibll widget_ui_slider lite). * Simple image slider widget (zibll widget_ui_slider → aksrt_render_slider / new-swiper).
* *
* @package AKSRT * @package AKSRT
*/ */
@@ -16,8 +16,8 @@ class WidgetSlider extends \WP_Widget {
'aksrt_slider', 'aksrt_slider',
esc_html__('AKSRT 幻灯片', 'aksrt'), esc_html__('AKSRT 幻灯片', 'aksrt'),
[ [
'description' => esc_html__('多图轮播,支持链接与标题。', 'aksrt'), 'description' => esc_html__('多图轮播(共享 new-swiper / Swiper 栈),支持链接与标题。', 'aksrt'),
'classname' => 'widget_aksrt_slider', 'classname' => 'widget_aksrt_slider slide-widget',
] ]
); );
} }
@@ -25,14 +25,38 @@ class WidgetSlider extends \WP_Widget {
public function widget($args, $instance): void { public function widget($args, $instance): void {
$slides = $this->normalize_slides($instance['slides'] ?? []); $slides = $this->normalize_slides($instance['slides'] ?? []);
if (!$slides) { if (!$slides) {
if (current_user_can('manage_options')) {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="c-red muted-box mb20"><b>' . esc_html__('幻灯片模块:', 'aksrt') . '</b>' .
esc_html__('当前配置下没有可显示的内容,请正确配置或移出该模块', 'aksrt') . '</div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
return; return;
} }
$interval = max(2, min(20, (int) ($instance['interval'] ?? 4))); $interval = max(2, min(20, (int) ($instance['interval'] ?? 4)));
$loop = !empty($instance['loop']); $loop = !isset($instance['loop']) || !empty($instance['loop']);
$dots = !empty($instance['pagination']); $dots = !isset($instance['pagination']) || !empty($instance['pagination']);
$nav = !empty($instance['button']); $nav = !isset($instance['button']) || !empty($instance['button']);
$blank = !empty($instance['blank']); $blank = !empty($instance['blank']);
$height = max(80, min(800, (int) ($instance['height'] ?? 220))); $height = max(80, min(800, (int) ($instance['height'] ?? 220)));
$m_height = max(60, min(800, (int) ($instance['m_height'] ?? $height)));
$effect = sanitize_key((string) ($instance['effect'] ?? 'slide'));
if (!in_array($effect, ['slide', 'fade', 'cube', 'coverflow', 'flip'], true)) {
$effect = 'slide';
}
$slide_payload = [];
foreach ($slides as $slide) {
$slide_payload[] = [
'image' => $slide['image'],
'link' => $slide['href'],
'title' => $slide['title'],
'desc' => $slide['desc'],
'alt' => $slide['title'],
'target' => $blank ? '_blank' : '_self',
];
}
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base); $title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
@@ -40,97 +64,47 @@ class WidgetSlider extends \WP_Widget {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} }
$uid = 'aksrt-slider-' . esc_attr($this->id); if (function_exists('aksrt_render_slider')) {
printf( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'<div class="aksrt-widget-slider" id="%s" data-interval="%d" data-loop="%s" style="--aksrt-slider-h:%dpx">', echo aksrt_render_slider([
$uid, 'slides' => $slide_payload,
$interval * 1000, 'effect' => $effect,
$loop ? '1' : '0', 'loop' => $loop,
$height 'autoplay' => true,
); 'interval' => $interval * 1000,
echo '<div class="aksrt-widget-slider__track">'; 'pc_height' => $height . 'px',
foreach ($slides as $i => $slide) { 'm_height' => $m_height . 'px',
'show_nav' => $nav,
'show_pagination' => $dots,
'slider_class' => 'slide-widget',
'wrapper_class' => 'aksrt-widget-slider-wrap',
]);
} else {
// Minimal fallback without helper.
echo '<div class="aksrt-widget-slider" style="--aksrt-slider-h:' . (int) $height . 'px">';
foreach ($slide_payload as $i => $slide) {
$active = $i === 0 ? ' is-active' : ''; $active = $i === 0 ? ' is-active' : '';
echo '<div class="aksrt-widget-slider__slide' . esc_attr($active) . '">'; echo '<div class="aksrt-widget-slider__slide' . esc_attr($active) . '">';
$img = '<img src="' . esc_url($slide['image']) . '" alt="' . esc_attr($slide['title']) . '" loading="lazy" />'; $img = '<img src="' . esc_url($slide['image']) . '" alt="' . esc_attr($slide['title']) . '" loading="lazy" />';
if ($slide['href'] !== '') { if ($slide['link'] !== '') {
printf( echo '<a href="' . esc_url($slide['link']) . '"' . ($blank ? ' target="_blank" rel="noopener noreferrer"' : '') . '>' . $img . '</a>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'<a href="%s"%s class="aksrt-widget-slider__link">%s</a>',
esc_url($slide['href']),
$blank ? ' target="_blank" rel="noopener noreferrer"' : '',
$img // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
);
} else { } else {
echo $img; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $img; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} }
if ($slide['title'] !== '' || $slide['desc'] !== '') {
echo '<div class="aksrt-widget-slider__caption">';
if ($slide['title'] !== '') {
echo '<strong>' . esc_html($slide['title']) . '</strong>';
}
if ($slide['desc'] !== '') {
echo '<span>' . esc_html($slide['desc']) . '</span>';
}
echo '</div>'; echo '</div>';
} }
echo '</div>'; echo '</div>';
} }
echo '</div>';
if ($nav) {
echo '<button type="button" class="aksrt-widget-slider__prev" aria-label="' . esc_attr__('上一张', 'aksrt') . '">‹</button>';
echo '<button type="button" class="aksrt-widget-slider__next" aria-label="' . esc_attr__('下一张', 'aksrt') . '">›</button>';
}
if ($dots) {
echo '<div class="aksrt-widget-slider__dots" role="tablist">';
foreach ($slides as $i => $slide) {
printf(
'<button type="button" class="aksrt-widget-slider__dot%s" data-index="%d" aria-label="%s"></button>',
$i === 0 ? ' is-active' : '',
(int) $i,
esc_attr(sprintf(__('第 %d 张', 'aksrt'), $i + 1))
);
}
echo '</div>';
}
echo '</div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
// Inline init once per widget (no extra enqueue dependency). echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
?>
<script>
(function(){
var root=document.getElementById(<?php echo wp_json_encode($uid); ?>);
if(!root||root._aksrtSlider)return; root._aksrtSlider=1;
var slides=[].slice.call(root.querySelectorAll('.aksrt-widget-slider__slide'));
var dots=[].slice.call(root.querySelectorAll('.aksrt-widget-slider__dot'));
var i=0, timer=null, ms=parseInt(root.getAttribute('data-interval')||'4000',10)||4000;
var loop=root.getAttribute('data-loop')==='1';
function go(n){
if(!slides.length)return;
i=(n+slides.length)%slides.length;
slides.forEach(function(s,idx){s.classList.toggle('is-active',idx===i);});
dots.forEach(function(d,idx){d.classList.toggle('is-active',idx===i);});
}
function next(){ if(!loop && i>=slides.length-1){go(0);return;} go(i+1); }
function start(){ stop(); if(slides.length>1) timer=setInterval(next, ms); }
function stop(){ if(timer){clearInterval(timer);timer=null;} }
var prev=root.querySelector('.aksrt-widget-slider__prev');
var nxt=root.querySelector('.aksrt-widget-slider__next');
if(prev) prev.addEventListener('click', function(){ go(i-1); start(); });
if(nxt) nxt.addEventListener('click', function(){ go(i+1); start(); });
dots.forEach(function(d){ d.addEventListener('click', function(){ go(parseInt(d.getAttribute('data-index')||'0',10)); start(); }); });
root.addEventListener('mouseenter', stop);
root.addEventListener('mouseleave', start);
start();
})();
</script>
<?php
} }
public function form($instance): void { public function form($instance): void {
$title = (string) ($instance['title'] ?? ''); $title = (string) ($instance['title'] ?? '');
$interval = (int) ($instance['interval'] ?? 4); $interval = (int) ($instance['interval'] ?? 4);
$height = (int) ($instance['height'] ?? 220); $height = (int) ($instance['height'] ?? 220);
$m_height = (int) ($instance['m_height'] ?? $height);
$effect = sanitize_key((string) ($instance['effect'] ?? 'slide'));
$slides = $this->normalize_slides($instance['slides'] ?? []); $slides = $this->normalize_slides($instance['slides'] ?? []);
if (!$slides) { if (!$slides) {
$slides = [ $slides = [
@@ -150,10 +124,26 @@ class WidgetSlider extends \WP_Widget {
</label> </label>
</p> </p>
<p> <p>
<label><?php esc_html_e('高度(px)', 'aksrt'); ?> <label><?php esc_html_e('PC 高度(px)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="80" max="800" name="<?php echo esc_attr($this->get_field_name('height')); ?>" value="<?php echo esc_attr((string) $height); ?>"> <input class="tiny-text" type="number" min="80" max="800" name="<?php echo esc_attr($this->get_field_name('height')); ?>" value="<?php echo esc_attr((string) $height); ?>">
</label> </label>
</p> </p>
<p>
<label><?php esc_html_e('移动端高度(px)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="60" max="800" name="<?php echo esc_attr($this->get_field_name('m_height')); ?>" value="<?php echo esc_attr((string) $m_height); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('切换效果', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('effect')); ?>">
<option value="slide" <?php selected($effect, 'slide'); ?>><?php esc_html_e('滑动', 'aksrt'); ?></option>
<option value="fade" <?php selected($effect, 'fade'); ?>><?php esc_html_e('淡入', 'aksrt'); ?></option>
<option value="coverflow" <?php selected($effect, 'coverflow'); ?>><?php esc_html_e('Coverflow', 'aksrt'); ?></option>
<option value="cube" <?php selected($effect, 'cube'); ?>><?php esc_html_e('Cube', 'aksrt'); ?></option>
<option value="flip" <?php selected($effect, 'flip'); ?>><?php esc_html_e('Flip', 'aksrt'); ?></option>
</select>
</label>
</p>
<p> <p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('loop')); ?>" value="1" <?php checked(!empty($instance['loop']) || !isset($instance['loop'])); ?>> <?php esc_html_e('循环播放', 'aksrt'); ?></label><br> <label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('loop')); ?>" value="1" <?php checked(!empty($instance['loop']) || !isset($instance['loop'])); ?>> <?php esc_html_e('循环播放', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('button')); ?>" value="1" <?php checked(!empty($instance['button']) || !isset($instance['button'])); ?>> <?php esc_html_e('翻页按钮', 'aksrt'); ?></label><br> <label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('button')); ?>" value="1" <?php checked(!empty($instance['button']) || !isset($instance['button'])); ?>> <?php esc_html_e('翻页按钮', 'aksrt'); ?></label><br>
@@ -177,9 +167,7 @@ class WidgetSlider extends \WP_Widget {
</label> </label>
</p> </p>
<?php endforeach; ?> <?php endforeach; ?>
<p class="description"><?php esc_html_e('保存后可继续编辑;需要更多张可先填满再在下次编辑时追加(当前表单固定 2–6 槽)。', 'aksrt'); ?></p>
<?php <?php
// Ensure at least 4 slots for editing convenience.
$extra = max(0, 4 - count($slides)); $extra = max(0, 4 - count($slides));
for ($j = 0; $j < $extra; $j++) { for ($j = 0; $j < $extra; $j++) {
$i = count($slides) + $j; $i = count($slides) + $j;
@@ -193,14 +181,23 @@ class WidgetSlider extends \WP_Widget {
</p> </p>
<?php <?php
} }
?>
<p class="description"><?php esc_html_e('使用主题共享 Swiper(.new-swiper)。图片 URL 必填;仅 1 张时不滑动。', 'aksrt'); ?></p>
<?php
} }
public function update($new_instance, $old_instance): array { public function update($new_instance, $old_instance): array {
$slides = $this->normalize_slides($new_instance['slides'] ?? []); $slides = $this->normalize_slides($new_instance['slides'] ?? []);
$effect = sanitize_key((string) ($new_instance['effect'] ?? 'slide'));
if (!in_array($effect, ['slide', 'fade', 'cube', 'coverflow', 'flip'], true)) {
$effect = 'slide';
}
return [ return [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')), 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'interval' => max(2, min(20, (int) ($new_instance['interval'] ?? 4))), 'interval' => max(2, min(20, (int) ($new_instance['interval'] ?? 4))),
'height' => max(80, min(800, (int) ($new_instance['height'] ?? 220))), 'height' => max(80, min(800, (int) ($new_instance['height'] ?? 220))),
'm_height' => max(60, min(800, (int) ($new_instance['m_height'] ?? ($new_instance['height'] ?? 220)))),
'effect' => $effect,
'loop' => !empty($new_instance['loop']) ? 1 : 0, 'loop' => !empty($new_instance['loop']) ? 1 : 0,
'button' => !empty($new_instance['button']) ? 1 : 0, 'button' => !empty($new_instance['button']) ? 1 : 0,
'pagination' => !empty($new_instance['pagination']) ? 1 : 0, 'pagination' => !empty($new_instance['pagination']) ? 1 : 0,