对小工具进行了一些修复

This commit is contained in:
2026-08-26 08:41:24 +08:00
parent e877ceb560
commit bb9413cba6
15 changed files with 463 additions and 136 deletions
+23 -5
View File
@@ -104,20 +104,38 @@ final class ContentRenderer {
}
private static function tab_posts(array $instance, array $context): string {
$tabs = array_values((array) $instance['tabs']);
$style = in_array(($instance['style'] ?? 'mini'), ['list', 'card', 'mini'], true) ? (string) $instance['style'] : 'mini';
$tabs = array_values((array) ($instance['tabs'] ?? []));
if (!$tabs) {
return RendererHelpers::empty_state(__('请配置文章栏目', 'aksrt'));
}
$buttons = '';
$key = sanitize_html_class((string) ($context['widget_id'] ?? 'preview')) ?: 'preview';
$panel_id = 'aksrt-tab-panel-' . $key;
$ajax_url = rest_url('aksrt/v1/widgets/tab_posts/items');
$first_tab_id = 'aksrt-tab-' . $key . '-0';
$placeholder_type = $style === 'card' ? 'card' : 'lists';
$placeholder = $style === 'mini'
? str_repeat('<div class="posts-mini"><div class="placeholder k1"></div></div>', max(1, (int) ($instance['count'] ?? 6)))
: aksrt_render_placeholder($placeholder_type, max(1, (int) ($instance['count'] ?? 6)));
$panels = '';
$visible_index = 0;
foreach ($tabs as $index => $tab) {
$tab_id = 'aksrt-tab-' . $key . '-' . $index;
$buttons .= '<button type="button" role="tab" id="' . esc_attr($tab_id) . '" aria-controls="' . esc_attr($panel_id) . '" aria-selected="' . ($index === 0 ? 'true' : 'false') . '" tabindex="' . ($index === 0 ? '0' : '-1') . '" data-segment="' . $index . '">' . esc_html((string) ($tab['title'] ?? sprintf(__('栏目 %d', 'aksrt'), $index + 1))) . '</button>';
if (!is_array($tab) || trim((string) ($tab['title'] ?? '')) === '') continue;
$tab_id = 'aksrt-tab-' . $key . '-' . $visible_index;
$selected = $visible_index === 0;
$buttons .= '<li class="' . ($selected ? 'active' : '') . '"><a' . ($selected ? '' : ' data-ajax') . ' data-toggle="tab" href="#' . esc_attr($panel_id . '-' . $visible_index) . '" role="tab" id="' . esc_attr($tab_id) . '" aria-controls="' . esc_attr($panel_id . '-' . $visible_index) . '" aria-selected="' . ($selected ? 'true' : 'false') . '" data-segment="' . (int) $index . '">' . wp_kses_post((string) $tab['title']) . '</a></li>';
if ($selected) {
$payload = PostRenderer::payload($instance, 'tab_posts', 1, (string) $index);
$body = $payload['html'] . $payload['paginate_html'] . '<div class="post_ajax_loader" style="display:none;">' . $placeholder . '</div>';
} else {
$body = '<span class="post_ajax_trigger hide"><a ajaxpager-target=".widget-ajaxpager" class="ajax_load ajax-next ajax-open" data-aksrt-load="1" no-scroll="true" href="' . esc_url(add_query_arg(['widget' => 'tab_posts', 'instance' => (string) ($context['widget_id'] ?? ''), 'page' => 1, 'segment' => $index], $ajax_url)) . '"></a></span><div class="post_ajax_loader" style="display:none;">' . $placeholder . '</div>';
}
$panels .= '<div class="tab-pane fade' . ($selected ? ' active in' : '') . '" id="' . esc_attr($panel_id . '-' . $visible_index) . '" role="tabpanel" aria-labelledby="' . esc_attr($tab_id) . '"><div class="widget-ajaxpager" data-widget-items data-widget="tab_posts" data-instance="' . esc_attr((string) ($context['widget_id'] ?? '')) . '" data-segment="' . (int) $index . '" aria-live="polite" aria-busy="false">' . $body . '</div></div>';
$visible_index++;
}
$payload = PostRenderer::payload($instance, 'tab_posts', 1, '0');
return '<div class="aksrt-widget-tabs" data-widget="tab_posts" data-instance="' . esc_attr((string) ($context['widget_id'] ?? '')) . '"><div class="aksrt-widget-tabs__nav" role="tablist" aria-label="' . esc_attr__('文章栏目', 'aksrt') . '">' . $buttons . '</div><div class="aksrt-widget-tabs__panel" role="tabpanel" id="' . esc_attr($panel_id) . '" aria-labelledby="' . esc_attr($first_tab_id) . '" tabindex="0" data-widget-items data-segment="0" aria-live="polite" aria-busy="false">' . $payload['html'] . $payload['paginate_html'] . '</div></div>';
if ($buttons === '') return RendererHelpers::empty_state(__('请配置文章栏目', 'aksrt'));
return '<div class="widget-tab-post style-' . esc_attr($style) . ($style === 'mini' ? ' posts-mini-lists zib-widget' : ' index-tab') . '" data-widget="tab_posts" data-instance="' . esc_attr((string) ($context['widget_id'] ?? '')) . '"><div class="relative' . ($style === 'card' ? ' mb20' : '') . '"><ul class="list-inline scroll-x no-scrollbar' . ($style === 'mini' ? ' tab-nav-theme' : '') . '" role="tablist">' . $buttons . '</ul></div><div class="tab-content">' . $panels . '</div></div>';
}
private static function post_pay(array $instance): string {
+77 -43
View File
@@ -6,56 +6,90 @@ use AKSRT\WidgetSystem\RendererHelpers;
defined('ABSPATH') || exit;
/** Render full sliders and the slider/media combination. */
/** Reference-compatible renderer for the modern Zibll slider widgets. */
final class SliderRenderer {
public static function render(string $slug, array $instance, array $context = []): string {
unset($context);
$slides = '';
foreach ((array) $instance['slides'] as $slide) {
if (!is_array($slide) || (empty($slide['background']) && empty($slide['background_video']))) {
continue;
}
$link = RendererHelpers::link((array) ($slide['link'] ?? []));
if (!empty($slide['background_video'])) {
$video = esc_url((string) $slide['background_video']);
$video_attr = aksrt_is_lazy('lazy_sider')
? ' class="lazyload" src="" data-src="' . $video . '"'
: ' src="' . $video . '"';
$media = '<video' . $video_attr . ' muted loop playsinline preload="metadata"></video>';
} else {
$media = RendererHelpers::image((string) $slide['background'], (string) ($slide['title'] ?? ''), '', 'lazy_sider', aksrt_get_lazy_thumb('lg'));
}
return $slug === 'slider_media_combo' ? self::combo($instance) : self::slider((array) ($instance['slides'] ?? []), (array) ($instance['option'] ?? []), true);
}
private static function slider(array $slides, array $option, bool $with_margin = false): string {
$option = array_merge(['direction' => 'horizontal', 'loop' => true, 'button' => true, 'pagination' => true, 'effect' => 'slide', 'scale_height' => true, 'scale' => 35, 'auto_height' => false, 'max_height' => 500, 'min_height' => 180, 'pc_height' => 400, 'm_height' => 200, 'spacebetween' => 15, 'speed' => 0, 'autoplay' => true, 'interval' => 4], $option);
$is_mobile = function_exists('wp_is_mobile') && wp_is_mobile();
$items = ''; $count = 0;
foreach ($slides as $slide) {
if (!is_array($slide)) continue;
$hide = (string) ($slide['hide'] ?? '');
if (($is_mobile && $hide === 'm') || (!$is_mobile && $hide === 'pc')) continue;
$background = esc_url((string) ($slide['background'] ?? '')); $video = esc_url((string) ($slide['background_video'] ?? ''));
if ($background === '' && ($video === '' || $is_mobile)) continue;
$alt = (string) (($slide['text']['title'] ?? '') ?: __('幻灯片', 'aksrt'));
$slide_class = '';
if ($video !== '' && !$is_mobile) { $slide_class = ' slide-background-video'; $media = '<video autoplay loop muted playsinline class="fit-cover radius8" src="' . $video . '"' . ($background !== '' ? ' poster="' . $background . '"' : '') . '></video><div class="absolute"></div>'; }
else $media = '<img class="radius8 swiper-lazy" src="' . esc_url(defined('AKSRT_URI') ? AKSRT_URI . '/assets/img/thumbnail-lg.svg' : $background) . '" data-src="' . $background . '" alt="' . esc_attr($alt) . '">';
$layers = '';
foreach ((array) ($slide['layers'] ?? []) as $layer) {
if (!empty($layer['image'])) {
$layers .= '<img' . aksrt_get_lazy_attr('lazy_sider', (string) $layer['image'], 'aksrt-slide-layer', aksrt_get_lazy_thumb('lg')) . ' alt="" data-swiper-parallax="' . (int) ($layer['parallax'] ?? 0) . '" data-swiper-parallax-scale="' . esc_attr((string) ((float) ($layer['scale'] ?? 100) / 100)) . '">';
}
foreach ((array) ($slide['image_layer'] ?? []) as $layer) {
if (!is_array($layer) || empty($layer['image'])) continue;
$attrs = ''; $parallax = (int) ($layer['parallax'] ?? 0); $scale = (float) ($layer['parallax_scale'] ?? 100) / 100; $opacity = (float) ($layer['parallax_opacity'] ?? 100) / 100;
if ($parallax) $attrs .= ' data-swiper-parallax="' . $parallax . '%"'; if ($scale !== 1.0) $attrs .= ' data-swiper-parallax-scale="' . esc_attr((string) $scale) . '"'; if ($opacity !== 1.0) $attrs .= ' data-swiper-parallax-opacity="' . esc_attr((string) $opacity) . '"';
$class = !empty($layer['free_size']) ? ' slide-layer text-' . esc_attr((string) ($layer['align'] ?? 'center')) : '';
$layers .= '<div class="absolute' . $class . '"' . $attrs . '><img class="radius8 swiper-lazy" src="' . esc_url(defined('AKSRT_URI') ? AKSRT_URI . '/assets/img/thumbnail-lg.svg' : (string) $layer['image']) . '" data-src="' . esc_url((string) $layer['image']) . '" alt="' . esc_attr($alt) . '"></div>';
}
$copy = '<div class="aksrt-slide-copy is-' . esc_attr((string) ($slide['text_align'] ?? 'left-bottom')) . '"><h3>' . esc_html((string) ($slide['title'] ?? '')) . '</h3><p>' . esc_html((string) ($slide['description'] ?? '')) . '</p></div>';
$body = $media . $layers . $copy;
if ($link['url']) {
$body = '<a href="' . $link['url'] . '"' . RendererHelpers::link_attrs($link) . '>' . $body . '</a>';
}
$slides .= '<div class="swiper-slide' . (!empty($slide['hide_mobile']) ? ' aksrt-slide--hide-mobile' : '') . '">' . $body . '</div>';
$text = (array) ($slide['text'] ?? []); $copy = '';
if (!empty($text['title'])) { $style = ''; if (!empty($text['text_size_pc'])) $style .= '--text-size-pc:' . (int) $text['text_size_pc'] . 'px;'; if (!empty($text['text_size_m'])) $style .= '--text-size-m:' . (int) $text['text_size_m'] . 'px;'; $copy = '<div class="abs-center slide-text ' . esc_attr((string) ($text['text_align'] ?? 'left-bottom')) . '"' . ($style ? ' style="' . esc_attr($style) . '"' : '') . '><div class="slide-title">' . wp_kses_post((string) $text['title']) . '</div>' . (!empty($text['desc']) ? '<div class="slide-desc">' . wp_kses_post((string) $text['desc']) . '</div>' : '') . '</div>'; if (!empty($text['parallax'])) $copy = '<div class="absolute" data-swiper-parallax="' . (int) $text['parallax'] . '">' . $copy . '</div>'; }
$body = $media . $layers . $copy; $link = RendererHelpers::link((array) ($slide['link'] ?? [])); if ($link['url'] !== '') $body = '<a href="' . $link['url'] . '"' . RendererHelpers::link_attrs($link) . '>' . $body . '</a>';
$items .= '<div class="swiper-slide' . esc_attr($slide_class) . '">' . $body . '</div>'; $count++;
}
if ($slides === '') {
return RendererHelpers::empty_state(__('请配置幻灯片', 'aksrt'));
if (!$count) return RendererHelpers::empty_state(__('请配置幻灯片', 'aksrt'));
$class = 'new-swiper slide-widget' . ($with_margin ? ' mb20' : '') . ($option['scale_height'] && $option['direction'] !== 'vertical' ? ' scale-height' : '') . ($count === 1 ? ' swiper-container-initialized' : ''); $attrs = ' data-direction="' . esc_attr((string) $option['direction']) . '" data-effect="' . esc_attr((string) $option['effect']) . '"';
if (!empty($option['loop']) && $count > 1) $attrs .= ' data-loop="true"'; if (!empty($option['autoplay']) && $count > 1) $attrs .= ' data-autoplay="1" data-interval="' . ((int) $option['interval'] * 1000) . '"'; if ((int) $option['speed'] > 0) $attrs .= ' data-speed="' . (int) $option['speed'] . '"'; $attrs .= ' data-spaceBetween="' . (int) $option['spacebetween'] . '"';
if ($option['auto_height']) $attrs .= ' auto-height="true" style="--max-height:' . (int) $option['max_height'] . 'px;--min-height:' . (int) $option['min_height'] . 'px;"'; else $attrs .= ' style="' . esc_attr($option['scale_height'] && $option['direction'] !== 'vertical' ? '--scale-height:' . (int) $option['scale'] . '%;' : '--pc-height:' . (int) $option['pc_height'] . 'px;--m-height:' . (int) $option['m_height'] . 'px;') . '"';
$html = '<div class="' . esc_attr($class) . '"' . $attrs . '><div class="swiper-wrapper">' . $items . '</div>'; if (!empty($option['button']) && $count > 1) $html .= '<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>'; if (!empty($option['pagination']) && $count > 1) $html .= '<div class="swiper-pagination kaoyou"></div>'; return '<div class="relative zib-slider">' . $html . '</div>';
}
private static function combo(array $instance): string {
$option = (array) ($instance['option'] ?? []); $option['direction'] = 'horizontal'; $option['scale_height'] = true; $option['scale'] = (int) ($instance['slide_scale'] ?? 50);
$slide_list = (array) ($instance['slides'] ?? []);
$slider = !empty($slide_list[0]) ? self::slider($slide_list, $option) : '';
$covers = '';
$is_mobile = function_exists('wp_is_mobile') && wp_is_mobile();
$cover_list = array_values((array) ($instance['covers'] ?? []));
$is_row = count($cover_list) > 1;
$pc_row = max(1, min(3, (int) ($instance['pc_row'] ?? 2)));
$m_row = max(1, min(3, (int) ($instance['m_row'] ?? 2)));
$row_class = 'col-sm-' . (int) (12 / $pc_row) . ($m_row > 1 ? ' col-xs-' . (int) (12 / $m_row) : '');
foreach ($cover_list as $cover) {
if (!is_array($cover)) continue;
$hide = (string) ($cover['hide'] ?? '');
if (($is_mobile && $hide === 'm') || (!$is_mobile && $hide === 'pc')) continue;
$image = esc_url((string) ($cover['image'] ?? ''));
$video = esc_url((string) ($cover['video'] ?? ''));
if ($image === '' && ($video === '' || $is_mobile)) continue;
$media = $video !== '' && !$is_mobile ? '<video autoplay loop muted playsinline class="fit-cover" src="' . $video . '"' . ($image !== '' ? ' poster="' . $image . '"' : '') . '></video>' : '<img class="fit-cover" src="' . $image . '" alt="' . esc_attr(wp_strip_all_tags((string) ($cover['title'] ?? ''))) . '">';
$mask = max(0, min(90, (int) ($instance['mask_opacity'] ?? 0))) / 100;
$card = $media . ($mask ? '<i class="graphic-mask" style="opacity:' . esc_attr((string) $mask) . '"></i>' : '') . (!empty($cover['title']) ? '<div class="abs-center text-center graphic-text this-font">' . wp_kses_post((string) $cover['title']) . '</div>' : '');
$link = RendererHelpers::link((array) ($cover['link'] ?? []));
$card = $link['url'] !== '' ? '<a href="' . $link['url'] . '"' . RendererHelpers::link_attrs($link) . '>' . $card . '</a>' : '<span>' . $card . '</span>';
$covers .= '<div class="' . ($is_row ? esc_attr($row_class) : '') . '"><div class="graphic-cover-item noshadow mb10">' . $card . '</div></div>';
}
$slider = '<div class="new-swiper aksrt-widget-swiper aksrt-widget-slider" data-aksrt-swiper="slider" data-direction="' . esc_attr((string) $instance['direction']) . '" data-effect="' . esc_attr((string) $instance['effect']) . '"' . (!empty($instance['loop']) ? ' data-loop="true"' : '') . (!empty($instance['autoplay']) ? ' data-autoplay="1"' : '') . ' data-interval="' . ((int) $instance['interval'] * 1000) . '" data-speed="' . (int) $instance['speed'] . '" data-spaceBetween="' . (int) $instance['spacebetween'] . '" style="--aksrt-slider-height:' . (int) $instance['pc_height'] . 'px;--aksrt-slider-height-m:' . (int) $instance['m_height'] . 'px"><div class="swiper-wrapper">' . $slides . '</div>';
if (!empty($instance['button'])) {
$slider .= '<button type="button" class="swiper-button-prev" aria-label="' . esc_attr__('上一张', 'aksrt') . '"></button><button type="button" class="swiper-button-next" aria-label="' . esc_attr__('下一张', 'aksrt') . '"></button>';
if ($covers !== '') {
$cover_style = '--font-size:' . (int) ($instance['font_size_pc'] ?? 16) . 'px;--font-size-sm:' . (int) ($instance['font_size_m'] ?? 14) . 'px;';
if (!empty($instance['font_bold'])) $cover_style .= '--font-weight:bold;--font-weight-sm:bold;';
if (!empty($instance['font_color'])) $cover_style .= '--color:' . $instance['font_color'] . ';--color-sm:' . $instance['font_color'] . ';';
$covers = '<div class="widget-graphic-cover" style="' . esc_attr($cover_style) . '">' . ($is_row ? '<div class="row gutters-5">' : '') . $covers . ($is_row ? '</div>' : '') . '</div>';
}
if (!empty($instance['pagination'])) {
$slider .= '<div class="swiper-pagination"></div>';
}
$slider .= '</div>';
if ($slug !== 'slider_media_combo') {
return $slider;
}
$cards = '';
foreach ((array) ($instance['side_cards'] ?? []) as $card) {
$cards .= RendererHelpers::card_media((array) $card);
}
return '<div class="aksrt-slider-combo"><div class="aksrt-slider-combo__main">' . $slider . '</div><aside class="aksrt-slider-combo__side">' . $cards . '</aside></div>';
if ($slider === '' && $covers === '') return RendererHelpers::empty_state(__('请配置幻灯片或卡片', 'aksrt'));
$layout = max(0, min(100, (int) ($instance['layout_ratio'] ?? 60)));
$slide_scale = max(0, (int) ($instance['slide_scale'] ?? 50));
$rows = max(1, (int) ($instance['pc_row'] ?? 2));
$columns = max(1, (int) ($instance['pc_col'] ?? 2));
$base = $layout < 100 ? (($layout * $slide_scale) / (100 - $layout)) : 0;
$cover_ratio = (($base - (($columns - 1) * 3)) / $columns) * $rows;
$style = '--layout-ratio:' . $layout . '%;--cover-ratio:' . $cover_ratio . '%;--m-cover-ratio:' . (int) ($instance['m_cover_ratio'] ?? 45) . '%;--cover-cols:' . $rows . ';';
$class = 'flex widget-slider-graphic m-' . sanitize_html_class((string) ($instance['m_layout_type'] ?? 'vertical'));
$html = '<div class="' . esc_attr($class) . '" style="' . esc_attr($style) . '" data-row="' . $rows . '" data-col="' . $columns . '">';
if ($slider !== '') $html .= '<div class="slider-col mb20">' . $slider . '</div>';
if ($covers !== '') $html .= '<div class="cover-col mb10">' . $covers . '</div>';
return $html . '</div>';
}
}