Files
wordpress-theme-aksrt/templates/parts/home-modules/module-hero.php
T
kate522 96bd9d949d fix(header): full zibll 1:1 for top multifunc search/cards/admin
Wire hero search through aksrt_get_search_box, device-gated cards/blur (zib_m_pc_is_show), expanded admin schema, and keyword/type UI styles matching zibll header-slider.
2026-07-24 10:43:29 +08:00

412 lines
21 KiB
PHP

<?php
/**
* Home module: header components hero + floating search + icon cards.
*
* DOM mirrors zibll header-slider:
* container > slider | search overlay (before + form + after) | cards
*
* @package AKSRT
* @version 4.2.0
*/
defined('ABSPATH') || exit;
// Visibility (page type + device) is decided by aksrt_header_slide_is_show().
// This partial only renders content when already gated by aksrt_header_slide().
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) {
$cfg = [];
}
$hero_banners = function_exists('aksrt_get_banners') ? aksrt_get_banners(8, 'hero') : [];
$settings = function_exists('aksrt_get_all_settings') ? aksrt_get_all_settings() : [];
$search_cfg = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_header_components_search_config() : [
'pc' => false,
'mobile' => false,
'show' => false,
'show_keywords' => false,
'popular_limit' => 6,
'show_input_cat' => false,
'show_type' => false,
'in_cat' => '',
'in_type' => 'post',
'more_cats' => [],
'before_html' => '',
'after_html' => '',
];
// zibll zib_m_pc_is_show: checkbox arrays with pc_s / m_s.
$is_mobile_req = function_exists('wp_is_mobile') && wp_is_mobile();
$m_pc_show = static function ($opt) use ($is_mobile_req): bool {
$opt = (array) $opt;
return ($is_mobile_req && in_array('m_s', $opt, true)) || (!$is_mobile_req && in_array('pc_s', $opt, true));
};
// Search device gate (zibll header_slider_search_s).
$search_opt = [];
if (!empty($search_cfg['pc'])) {
$search_opt[] = 'pc_s';
}
if (!empty($search_cfg['mobile'])) {
$search_opt[] = 'm_s';
}
// Fallback: master enabled without explicit devices = both (legacy).
if (!$search_opt && !empty($search_cfg['show'])) {
$search_opt = ['pc_s', 'm_s'];
}
$show_search = $m_pc_show($search_opt) || !empty($cfg['with_search']);
// Cards device gate (zibll header_slider_card_s).
$cards_master = !empty($settings['header_components_cards_enabled']);
$card_show_devices = isset($settings['header_components_card_show_devices'])
? (array) $settings['header_components_card_show_devices']
: ['pc', 'mobile'];
$card_opt = [];
if (in_array('pc', $card_show_devices, true)) {
$card_opt[] = 'pc_s';
}
if (in_array('mobile', $card_show_devices, true)) {
$card_opt[] = 'm_s';
}
if (!$card_opt && $cards_master) {
$card_opt = ['pc_s', 'm_s'];
}
$show_cards = $cards_master && $m_pc_show($card_opt);
$icon_cards = isset($settings['header_components_icon_cards']) ? (array) $settings['header_components_icon_cards'] : [];
$card_bg_mode = !empty($settings['header_components_card_shared_bg']) ? 'shared' : 'individual';
$card_icon_shape = $settings['header_components_card_icon_shape'] ?? 'circle';
// Filter blur (zibll header_slider_filter_blur) — independent of cards.
$filter_blur_devices = isset($settings['header_components_filter_blur_devices'])
? (array) $settings['header_components_filter_blur_devices']
: [];
// Legacy: header_components_card_blur true without devices → both.
if (!$filter_blur_devices && !empty($settings['header_components_card_blur'])) {
$filter_blur_devices = ['pc', 'mobile'];
}
$filter_opt = [];
if (in_array('pc', $filter_blur_devices, true)) {
$filter_opt[] = 'pc_s';
}
if (in_array('mobile', $filter_blur_devices, true)) {
$filter_opt[] = 'm_s';
}
$filter_blur_on = $m_pc_show($filter_opt);
$slide_count = count($hero_banners);
// card-N only counts cards actually rendered on this device (zibll parity).
$visible_card_count = 0;
if ($show_cards) {
foreach ($icon_cards as $_c) {
if (is_array($_c) && (trim((string) ($_c['icon'] ?? '')) !== '' || trim((string) ($_c['custom_icon'] ?? '')) !== '' || trim((string) ($_c['customize_icon'] ?? '')) !== '')) {
$visible_card_count++;
} elseif (is_array($_c) && trim((string) ($_c['title'] ?? '')) !== '') {
// Count entries even if icon missing so layout still allocates.
$visible_card_count++;
}
}
}
$container_classes = ['header-slider-container', 'header-components-container', 'relative', 'mb20', 'card-' . $visible_card_count];
if ($filter_blur_on) {
$container_classes[] = 'filter-blur';
}
if (in_array('pc', $filter_blur_devices, true)) {
$container_classes[] = 'has-blur-pc';
}
if (in_array('mobile', $filter_blur_devices, true)) {
$container_classes[] = 'has-blur-mobile';
}
/**
* Search overlay = zibll .header-slider-search.abs-center
* Uses aksrt_get_search_box() (zib_get_search_box parity) for form / keywords / type / cat.
* Title/subtitle live in before_html (same flow stack as the form).
*/
$render_hero_search = static function () use ($search_cfg, $is_mobile_req): void {
$_before = isset($search_cfg['before_html']) ? (string) $search_cfg['before_html'] : '';
$_after = isset($search_cfg['after_html']) ? (string) $search_cfg['after_html'] : '';
$search_args = [
'class' => '',
'show_keywords' => !empty($search_cfg['show_keywords']),
'popular_limit' => max(0, (int) ($search_cfg['popular_limit'] ?? 6)),
'show_history' => false,
'show_posts' => false,
'show_input_cat' => !empty($search_cfg['show_input_cat']),
'more_cats' => !empty($search_cfg['more_cats']) ? (array) $search_cfg['more_cats'] : [],
'in_cat' => (string) ($search_cfg['in_cat'] ?? ''),
'show_type' => !empty($search_cfg['show_type']),
'in_type' => (string) ($search_cfg['in_type'] ?? 'post'),
'in_user' => '',
's' => is_search() ? (string) get_search_query() : '',
];
// Mobile: never show hot keywords (zibll).
if ($is_mobile_req) {
$search_args['show_keywords'] = false;
}
$search_html = function_exists('aksrt_get_search_box')
? (string) aksrt_get_search_box($search_args)
: '';
$before_html = trim($_before) !== ''
? '<div class="header-slider-search-more text-center before">' . wp_kses_post($_before) . '</div>'
: '';
$after_html = trim($_after) !== ''
? '<div class="header-slider-search-more after">' . wp_kses_post($_after) . '</div>'
: '';
echo '<div class="header-slider-search header-components-search abs-center">'
. $before_html
. $search_html
. $after_html
. '</div>';
};
$render_icon_cards = static function () use ($show_cards, $icon_cards, $card_bg_mode, $card_icon_shape): void {
if (!$show_cards || empty($icon_cards)) {
return;
}
/**
* Overlay icon cards — 1:1 zibll zib_header_slide() card module.
*
* show_widget_bg (shared) → outer .zib-widget.padding-10 + each card class=padding-10 in .relative-h
* individual (default) → outer empty div + each card class=zib-widget in .flex1
* Row: .flex.jse.flex-row.gutters-5.flex-col-sm-2
*/
$show_widget_bg = ($card_bg_mode === 'shared');
$icon_radius4 = ($card_icon_shape === 'square');
$slider_card_html = '';
foreach ($icon_cards as $raw) {
if (!is_array($raw)) {
continue;
}
$card = $raw;
$card['class'] = $show_widget_bg ? 'padding-10' : 'zib-widget';
$extra = trim((string) ($card['icon_class'] ?? ''));
if ($icon_radius4 && !str_contains($extra, 'radius4')) {
$extra = trim($extra . ' radius4');
}
$card['icon_class'] = $extra;
unset($card['icon_shape']);
$wrap_class = $show_widget_bg ? 'relative-h' : 'flex1';
$inner = function_exists('aksrt_icon_cover_card')
? aksrt_icon_cover_card($card)
: (function_exists('aksrt_render_icon_cover_card') ? aksrt_render_icon_cover_card($card) : '');
if ($inner === '') {
continue;
}
$slider_card_html .= '<div class="' . esc_attr($wrap_class) . '">' . $inner . '</div>';
}
if ($slider_card_html === '') {
return;
}
?>
<div class="header-slider-card header-components-card container">
<?php if ($show_widget_bg) : ?>
<div class="zib-widget padding-10">
<?php else : ?>
<div class="">
<?php endif; ?>
<div class="flex jse flex-row gutters-5 flex-col-sm-2">
<?php echo $slider_card_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
</div>
</div>
<?php
};
if (!empty($hero_banners)) :
$slider_cfg = function_exists('aksrt_get_slider_config') ? aksrt_get_slider_config() : [];
$_nav_color_for_header = sanitize_hex_color((string) ($slider_cfg['nav_color'] ?? '')) ?: '';
if ($_nav_color_for_header !== '' && strtolower($_nav_color_for_header) !== '#ffffff') {
echo '<style id="aksrt-header-slider-nav-color">body.nav-fixed:not(.body-scroll):not(.mobile-navbar-show) .header.show-slide{--header-color:' . esc_attr($_nav_color_for_header) . ';}</style>';
}
$swiper_config = [
'effect' => $slider_cfg['effect'] ?? 'fade',
'loop' => $slide_count > 1 && ($slider_cfg['loop'] ?? true),
'speed' => 600,
'autoplay' => $slide_count > 1 ? ['delay' => ($slider_cfg['autoplay_interval'] ?? 5000), 'disableOnInteraction' => false] : false,
'keyboard' => ['enabled' => true],
];
if (!empty($slider_cfg['pagination'])) {
$swiper_config['pagination'] = ['clickable' => true];
}
if (!empty($slider_cfg['navigation'])) {
$swiper_config['navigation'] = true;
}
$pc_height = $slider_cfg['pc_height'] ?? '';
$mobile_height = $slider_cfg['mobile_height'] ?? '';
$height_style = '';
if ($pc_height !== '') {
$height_style .= '--pc-height:' . esc_attr($pc_height) . ';';
}
if ($mobile_height !== '') {
$height_style .= '--m-height:' . esc_attr($mobile_height) . ';';
}
?>
<div class="<?php echo esc_attr(implode(' ', $container_classes)); ?>" id="aksrt-header-components"<?php if ($height_style !== '') : ?> style="<?php echo $height_style; ?>"<?php endif; ?>>
<div class="relative zib-slider">
<div class="swiper new-swiper header-components slide-index slide-header" data-swiper-config="<?php echo esc_attr(wp_json_encode($swiper_config)); ?>">
<div class="swiper-wrapper">
<?php foreach ($hero_banners as $index => $slide) :
$device_vis = $slide['device_visibility'] ?? 'all';
$slide_classes = 'swiper-slide';
if ($device_vis === 'pc') {
$slide_classes .= ' hero-slide--pc-only';
} elseif ($device_vis === 'mobile') {
$slide_classes .= ' hero-slide--mobile-only';
}
if (!empty($slide['video_url'])) {
$slide_classes .= ' hero-slide--has-video';
}
$link_target = !empty($slide['link_target']) ? $slide['link_target'] : '_self';
$has_layers = !empty($slide['layers']) && is_array($slide['layers']);
$text_parallax = !empty($slide['text_parallax']);
// Align CSS vars with zibll --text-size-pc / --text-size-m.
$text_style = '';
if (!empty($slide['text_size_pc'])) {
$text_style .= '--text-size-pc:' . esc_attr((int) $slide['text_size_pc']) . 'px;';
$text_style .= '--hero-text-size-pc:' . esc_attr($slide['text_size_pc']) . ';';
}
if (!empty($slide['text_size_m'])) {
$text_style .= '--text-size-m:' . esc_attr((int) $slide['text_size_m']) . 'px;';
$text_style .= '--hero-text-size-m:' . esc_attr($slide['text_size_m']) . ';';
}
if ($text_parallax) {
$text_style .= '--hero-text-parallax:1;';
}
?>
<div class="<?php echo esc_attr($slide_classes); ?>"<?php if (!empty($slide['link'])) : ?> data-link="<?php echo esc_url($slide['link']); ?>" data-target="<?php echo esc_attr($link_target); ?>"<?php endif; ?>>
<div class="header-components__slide-bg">
<?php if (!empty($slide['video_url'])) : ?>
<video class="header-components__video" autoplay muted loop playsinline preload="metadata">
<source src="<?php echo esc_url($slide['video_url']); ?>" type="video/mp4">
</video>
<?php endif; ?>
<?php if (!empty($slide['image'])) : ?>
<img
src="<?php echo esc_url($slide['image']); ?>"
alt="<?php echo esc_attr($slide['title'] ?: get_bloginfo('name')); ?>"
loading="<?php echo 0 === $index ? 'eager' : 'lazy'; ?>"
decoding="async"
class="fit-cover"
>
<?php elseif (empty($slide['video_url'])) : ?>
<div class="header-components__slide-fallback"></div>
<?php endif; ?>
<?php if ($has_layers) : ?>
<?php foreach ($slide['layers'] as $layer) :
if (empty($layer['image'])) continue;
$layer_classes = 'header-components__layer';
if (!empty($layer['parallax'])) $layer_classes .= ' header-components__layer--parallax';
if (!empty($layer['free_size'])) $layer_classes .= ' header-components__layer--free-size';
$layer_style = '';
if (!empty($layer['align']) && $layer['align'] !== 'center') {
$layer_style .= 'background-position:' . esc_attr($layer['align']) . ';';
}
if (!empty($layer['parallax'])) {
$pscale = isset($layer['parallax_scale']) ? (float) $layer['parallax_scale'] : 0.3;
$popacity = isset($layer['parallax_opacity']) ? (float) $layer['parallax_opacity'] : 0.6;
$layer_style .= '--layer-parallax-scale:' . esc_attr($pscale) . ';';
$layer_style .= '--layer-parallax-opacity:' . esc_attr($popacity) . ';';
}
?>
<div class="<?php echo esc_attr($layer_classes); ?>" style="background-image:url('<?php echo esc_url($layer['image']); ?>')<?php echo $layer_style ? ';' . $layer_style : ''; ?>"></div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div class="header-components__slide-overlay"></div>
<?php
// Per-slide decorative text only (zibll .abs-center.slide-text).
// Hero main title/subtitle must live in search before_html.
$show_text = !isset($slide['show_text']) || $slide['show_text'];
$has_title = !empty($slide['title']);
$has_subtitle = !empty($slide['subtitle']);
$has_description = !empty($slide['description']);
$text_align = $slide['text_align'] ?? '';
$text_align_class = '';
if ($text_align !== '' && $text_align !== 'left') {
// zibll uses conter-bottom / left-bottom etc.; keep text-* for existing data.
$text_align_class = ' text-' . sanitize_html_class($text_align);
if (in_array($text_align, ['center', 'conter-conter', 'conter-bottom'], true)) {
$text_align_class .= ' conter-conter';
} elseif ($text_align === 'left-bottom') {
$text_align_class .= ' left-bottom';
} elseif ($text_align === 'right-bottom') {
$text_align_class .= ' right-bottom';
}
}
?>
<?php if ($show_text && ($has_title || $has_subtitle || $has_description)) : ?>
<div class="abs-center slide-text header-components__content<?php echo esc_attr($text_align_class); ?><?php echo $text_parallax ? ' has-text-parallax' : ''; ?>"<?php echo $text_style ? ' style="' . $text_style . '"' : ''; ?>>
<?php if ($has_title) : ?>
<div class="header-components__title slide-title"><?php echo esc_html($slide['title']); ?></div>
<?php endif; ?>
<?php if ($has_subtitle) : ?>
<div class="header-components__subtitle slide-desc"><?php echo esc_html($slide['subtitle']); ?></div>
<?php endif; ?>
<?php if ($has_description) : ?>
<div class="header-components__description"><?php echo esc_html($slide['description']); ?></div>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<?php if ($show_search) : ?>
<?php $render_hero_search(); ?>
<?php endif; ?>
<?php $render_icon_cards(); ?>
<?php if (function_exists('aksrt_render_admin_quick_link')) : ?>
<div class="home-module__admin-tools">
<?php aksrt_render_admin_quick_link('page-display/hero', __('Hero 轮播管理', 'aksrt')); ?>
<?php aksrt_render_admin_quick_link('page-display/header-components', __('顶部多功能组件', 'aksrt')); ?>
</div>
<?php endif; ?>
</div>
<?php else : ?>
<?php
$is_admin_user = current_user_can('manage_options');
?>
<div class="<?php echo esc_attr(implode(' ', $container_classes)); ?>" id="aksrt-header-components">
<div class="header-components slide-header header-components--gradient">
<?php if (!$show_search) : ?>
<div class="abs-center slide-text header-components__content text-center conter-conter">
<div class="header-components__title slide-title"><?php bloginfo('name'); ?></div>
<div class="header-components__subtitle slide-desc"><?php echo esc_html(get_bloginfo('description', 'display')); ?></div>
<?php if ($is_admin_user) : ?>
<p class="header-components__admin-hint" role="note">
<?php esc_html_e('No Hero slides found. Showing fallback site info.', 'aksrt'); ?>
</p>
<?php endif; ?>
</div>
<?php elseif ($is_admin_user) : ?>
<div class="header-components__admin-hint" role="note" style="position:absolute;bottom:12px;left:50%;transform:translateX(-50%);z-index:3;">
<?php esc_html_e('No Hero slides found. Configure slides or search before_html in admin.', 'aksrt'); ?>
</div>
<?php endif; ?>
</div>
<?php if ($show_search) : ?>
<?php $render_hero_search(); ?>
<?php endif; ?>
<?php $render_icon_cards(); ?>
<?php if (function_exists('aksrt_render_admin_quick_link')) : ?>
<div class="home-module__admin-tools">
<?php aksrt_render_admin_quick_link('page-display/hero', __('Hero 轮播管理', 'aksrt')); ?>
<?php aksrt_render_admin_quick_link('page-display/header-components', __('顶部多功能组件', 'aksrt')); ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>