@@ -0,0 +1,241 @@
< ? php
/**
* Simple image slider widget (zibll widget_ui_slider lite).
*
* @package AKSRT
*/
namespace AKSRT\Widgets ;
defined ( 'ABSPATH' ) || exit ;
class WidgetSlider extends \WP_Widget {
public function __construct () {
parent :: __construct (
'aksrt_slider' ,
esc_html__ ( 'AKSRT 幻灯片' , 'aksrt' ),
[
'description' => esc_html__ ( '多图轮播,支持链接与标题。' , 'aksrt' ),
'classname' => 'widget_aksrt_slider' ,
]
);
}
public function widget ( $args , $instance ) : void {
$slides = $this -> normalize_slides ( $instance [ 'slides' ] ? ? []);
if ( ! $slides ) {
return ;
}
$interval = max ( 2 , min ( 20 , ( int ) ( $instance [ 'interval' ] ? ? 4 )));
$loop = ! empty ( $instance [ 'loop' ]);
$dots = ! empty ( $instance [ 'pagination' ]);
$nav = ! empty ( $instance [ 'button' ]);
$blank = ! empty ( $instance [ 'blank' ]);
$height = max ( 80 , min ( 800 , ( int ) ( $instance [ 'height' ] ? ? 220 )));
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
}
$uid = 'aksrt-slider-' . esc_attr ( $this -> id );
printf (
'<div class="aksrt-widget-slider" id="%s" data-interval="%d" data-loop="%s" style="--aksrt-slider-h:%dpx">' ,
$uid ,
$interval * 1000 ,
$loop ? '1' : '0' ,
$height
);
echo '<div class="aksrt-widget-slider__track">' ;
foreach ( $slides as $i => $slide ) {
$active = $i === 0 ? ' is-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" />' ;
if ( $slide [ 'href' ] !== '' ) {
printf (
'<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 {
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>' ;
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).
?>
<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 {
$title = (string) ($instance['title'] ?? '');
$interval = (int) ($instance['interval'] ?? 4);
$height = (int) ($instance['height'] ?? 220);
$slides = $this->normalize_slides($instance['slides'] ?? []);
if (!$slides) {
$slides = [
['image' => '', 'href' => '', 'title' => '', 'desc' => ''],
['image' => '', 'href' => '', 'title' => '', 'desc' => ''],
];
}
?>
<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($title); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('切换间隔(秒)', 'aksrt'); ?>
<input class="tiny-text" type="number" min="2" max="20" name="<?php echo esc_attr($this->get_field_name('interval')); ?>" value="<?php echo esc_attr((string) $interval); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('高度(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); ?>">
</label>
</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('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('pagination')); ?>" value="1" <?php checked(!empty($instance['pagination']) || !isset($instance['pagination'])); ?>> <?php esc_html_e('指示点', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('blank')); ?>" value="1" <?php checked(!empty($instance['blank'])); ?>> <?php esc_html_e('新窗口打开', 'aksrt'); ?></label>
</p>
<?php foreach ($slides as $i => $slide) : ?>
<p style="border-top:1px solid #ddd;padding-top:8px;">
<strong><?php echo esc_html(sprintf(__('幻灯片 %d', 'aksrt'), $i + 1)); ?></strong><br>
<label><?php esc_html_e('图片 URL', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][image]"); ?>" value="<?php echo esc_attr($slide['image']); ?>">
</label>
<label><?php esc_html_e('链接', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][href]"); ?>" value="<?php echo esc_attr($slide['href']); ?>">
</label>
<label><?php esc_html_e('标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][title]"); ?>" value="<?php echo esc_attr($slide['title']); ?>">
</label>
<label><?php esc_html_e('简介', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][desc]"); ?>" value="<?php echo esc_attr($slide['desc']); ?>">
</label>
</p>
<?php endforeach; ?>
<p class="description"><?php esc_html_e('保存后可继续编辑;需要更多张可先填满再在下次编辑时追加(当前表单固定 2–6 槽)。', 'aksrt'); ?></p>
<?php
// Ensure at least 4 slots for editing convenience.
$extra = max(0, 4 - count($slides));
for ($j = 0; $j < $extra; $j++) {
$i = count($slides) + $j;
?>
<p style="border-top:1px dashed #eee;padding-top:8px;">
<strong><?php echo esc_html(sprintf(__('空槽 %d', 'aksrt'), $i + 1)); ?></strong>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][image]"); ?>" placeholder="<?php esc_attr_e('图片 URL', 'aksrt'); ?>">
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][href]"); ?>" placeholder="<?php esc_attr_e('链接', 'aksrt'); ?>">
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][title]"); ?>" placeholder="<?php esc_attr_e('标题', 'aksrt'); ?>">
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('slides') . "[{$i}][desc]"); ?>" placeholder="<?php esc_attr_e('简介', 'aksrt'); ?>">
</p>
<?php
}
}
public function update($new_instance, $old_instance): array {
$slides = $this->normalize_slides($new_instance['slides'] ?? []);
return [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'interval' => max(2, min(20, (int) ($new_instance['interval'] ?? 4))),
'height' => max(80, min(800, (int) ($new_instance['height'] ?? 220))),
'loop' => !empty($new_instance['loop']) ? 1 : 0,
'button' => !empty($new_instance['button']) ? 1 : 0,
'pagination' => !empty($new_instance['pagination']) ? 1 : 0,
'blank' => !empty($new_instance['blank']) ? 1 : 0,
'slides' => $slides,
];
}
/**
* @param mixed $raw
* @return array<int, array{image:string,href:string,title:string,desc:string}>
*/
private function normalize_slides($raw): array {
if (!is_array($raw)) {
return [];
}
$out = [];
foreach ($raw as $row) {
if (!is_array($row)) {
continue;
}
$image = esc_url_raw(trim((string) ($row['image'] ?? $row['link'] ?? '')));
if ($image === '') {
continue;
}
$out[] = [
'image' => $image,
'href' => esc_url_raw(trim((string) ($row['href'] ?? ''))),
'title' => sanitize_text_field((string) ($row['title'] ?? '')),
'desc' => sanitize_text_field((string) ($row['desc'] ?? $row['dec'] ?? '')),
];
if (count($out) >= 8) {
break;
}
}
return $out;
}
}