feat(phase2): shared common-form layer + CommonWidgetFields trait for all widgets
- Extract Zibll CSF_Widget::params() common-field parity into inc/modules/widget-common.php (required by widgets-register.php to keep both files <1000 lines): subtitle, title_style(simple/big), title_highlight +color, title_link x3, title_link_style colors, sidebar_affix, show_type (all/only_pc/only_sm), show_id_type(show/hide), show_ids, animation_in, animation_repeat, layout_pin_pc/m, layout_bg(white/dark). Adds normalize_link/normalize_bg helpers, affix_attrs, common_wrapper_attr, render_title, animation_class. Back-compat normalizes legacy tokens (in_affix->sidebar_affix, pc/only_pc, include/exclude->show/hide). - Add src/AKSRT/Widgets/Traits/CommonWidgetFields.php trait (render_common_form_section + update_common_fields). - Wire all 57 widgets to use the trait; migrate the 16 hand-rolled in_affix implementations to shared sidebar_affix (fixes 6 latent bugs where in_affix was never persisted). SearchWidget intentionally skips the shared form section (its business show_type collides) but adopts the affix helper. - widget_display_callback parity verified via tmp assertion script. Refs .specs/plans/zibll-widgets-1to1.md Phase 2 (US1/US5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
require_once AKSRT_DIR . '/inc/modules/widget-common.php';
|
||||
|
||||
/**
|
||||
* Register custom widgets.
|
||||
*/
|
||||
@@ -307,208 +309,6 @@ if (!function_exists('aksrt_widget_get_area_context')) {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Shared Widget Form Helper — 公共表单字段与显示规则
|
||||
// ============================================================
|
||||
|
||||
if (!function_exists('aksrt_widget_common_defaults')) {
|
||||
/**
|
||||
* Default values for shared widget instance fields.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
function aksrt_widget_common_defaults(): array {
|
||||
return [
|
||||
'subtitle' => '',
|
||||
'title_link' => '',
|
||||
'show_type' => 'all',
|
||||
'show_id_type' => '',
|
||||
'show_ids' => '',
|
||||
'sidebar_affix' => false,
|
||||
'animation_in' => '',
|
||||
'animation_repeat'=> false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('aksrt_widget_normalize_common')) {
|
||||
/**
|
||||
* Normalize common widget instance fields, filling defaults for legacy instances.
|
||||
*
|
||||
* @param array<string, mixed> $instance
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
function aksrt_widget_normalize_common(array $instance): array {
|
||||
$defaults = aksrt_widget_common_defaults();
|
||||
$result = array_merge($defaults, $instance);
|
||||
|
||||
$result['subtitle'] = sanitize_text_field($result['subtitle'] ?? '');
|
||||
$result['title_link'] = esc_url_raw($result['title_link'] ?? '');
|
||||
$result['show_type'] = in_array($result['show_type'] ?? '', ['all', 'pc', 'mobile'], true) ? $result['show_type'] : 'all';
|
||||
$result['show_id_type'] = in_array($result['show_id_type'] ?? '', ['', 'include', 'exclude'], true) ? $result['show_id_type'] : '';
|
||||
$result['show_ids'] = sanitize_text_field($result['show_ids'] ?? '');
|
||||
$result['sidebar_affix'] = !empty($result['sidebar_affix']);
|
||||
$result['animation_in'] = sanitize_key($result['animation_in'] ?? '');
|
||||
$result['animation_repeat']= !empty($result['animation_repeat']);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('aksrt_widget_render_common_form')) {
|
||||
/**
|
||||
* Render shared widget admin form fields.
|
||||
*
|
||||
* Call this from your widget's form() method after rendering custom fields.
|
||||
*
|
||||
* @param array<string, mixed> $instance Current widget instance.
|
||||
*/
|
||||
function aksrt_widget_render_common_form(array $instance): void {
|
||||
$instance = aksrt_widget_normalize_common($instance);
|
||||
?>
|
||||
<hr style="margin:16px 0 12px;border-color:#e0e0e0;">
|
||||
<p style="font-size:12px;color:#666;margin-bottom:8px;font-weight:600;"><?php esc_html_e('显示规则', 'aksrt'); ?></p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr($instance['show_type_id'] ?? ''); ?>"><?php esc_html_e('显示设备', 'aksrt'); ?></label><br>
|
||||
<select name="<?php echo esc_attr($instance['show_type_name'] ?? 'show_type'); ?>" id="<?php echo esc_attr($instance['show_type_id'] ?? ''); ?>" class="widefat">
|
||||
<option value="all" <?php selected($instance['show_type'], 'all'); ?>><?php esc_html_e('全部', 'aksrt'); ?></option>
|
||||
<option value="pc" <?php selected($instance['show_type'], 'pc'); ?>><?php esc_html_e('仅 PC', 'aksrt'); ?></option>
|
||||
<option value="mobile" <?php selected($instance['show_type'], 'mobile'); ?>><?php esc_html_e('仅移动端', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr($instance['show_id_type_id'] ?? ''); ?>"><?php esc_html_e('ID 显示规则', 'aksrt'); ?></label><br>
|
||||
<select name="<?php echo esc_attr($instance['show_id_type_name'] ?? 'show_id_type'); ?>" id="<?php echo esc_attr($instance['show_id_type_id'] ?? ''); ?>" class="widefat">
|
||||
<option value="" <?php selected($instance['show_id_type'], ''); ?>><?php esc_html_e('不限制', 'aksrt'); ?></option>
|
||||
<option value="include" <?php selected($instance['show_id_type'], 'include'); ?>><?php esc_html_e('仅指定 ID 显示', 'aksrt'); ?></option>
|
||||
<option value="exclude" <?php selected($instance['show_id_type'], 'exclude'); ?>><?php esc_html_e('指定 ID 隐藏', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr($instance['show_ids_id'] ?? ''); ?>"><?php esc_html_e('文章/页面 ID', 'aksrt'); ?></label><br>
|
||||
<input type="text" name="<?php echo esc_attr($instance['show_ids_name'] ?? 'show_ids'); ?>" id="<?php echo esc_attr($instance['show_ids_id'] ?? ''); ?>" class="widefat" value="<?php echo esc_attr($instance['show_ids']); ?>" placeholder="<?php esc_attr_e('逗号分隔,如: 1,2,3', 'aksrt'); ?>">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>
|
||||
<input type="checkbox" name="<?php echo esc_attr($instance['sidebar_affix_name'] ?? 'sidebar_affix'); ?>" value="1" <?php checked($instance['sidebar_affix']); ?>>
|
||||
<?php esc_html_e('侧栏吸顶', 'aksrt'); ?>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo esc_attr($instance['animation_in_id'] ?? ''); ?>"><?php esc_html_e('入场动画', 'aksrt'); ?></label><br>
|
||||
<select name="<?php echo esc_attr($instance['animation_in_name'] ?? 'animation_in'); ?>" id="<?php echo esc_attr($instance['animation_in_id'] ?? ''); ?>" class="widefat">
|
||||
<option value="" <?php selected($instance['animation_in'], ''); ?>><?php esc_html_e('无', 'aksrt'); ?></option>
|
||||
<option value="fadeIn" <?php selected($instance['animation_in'], 'fadeIn'); ?>><?php esc_html_e('淡入', 'aksrt'); ?></option>
|
||||
<option value="fadeInUp" <?php selected($instance['animation_in'], 'fadeInUp'); ?>><?php esc_html_e('上移淡入', 'aksrt'); ?></option>
|
||||
<option value="fadeInDown" <?php selected($instance['animation_in'], 'fadeInDown'); ?>><?php esc_html_e('下移淡入', 'aksrt'); ?></option>
|
||||
<option value="fadeInLeft" <?php selected($instance['animation_in'], 'fadeInLeft'); ?>><?php esc_html_e('左移淡入', 'aksrt'); ?></option>
|
||||
<option value="fadeInRight" <?php selected($instance['animation_in'], 'fadeInRight'); ?>><?php esc_html_e('右移淡入', 'aksrt'); ?></option>
|
||||
<option value="zoomIn" <?php selected($instance['animation_in'], 'zoomIn'); ?>><?php esc_html_e('缩放进入', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>
|
||||
<input type="checkbox" name="<?php echo esc_attr($instance['animation_repeat_name'] ?? 'animation_repeat'); ?>" value="1" <?php checked($instance['animation_repeat']); ?>>
|
||||
<?php esc_html_e('动画重复播放', 'aksrt'); ?>
|
||||
</label>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('aksrt_widget_update_common')) {
|
||||
/**
|
||||
* Process shared widget form fields in update().
|
||||
*
|
||||
* @param array<string, mixed> $new_instance
|
||||
* @param array<string, mixed> $old_instance
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
function aksrt_widget_update_common(array $new_instance, array $old_instance): array {
|
||||
$merged = array_merge($old_instance, $new_instance);
|
||||
return aksrt_widget_normalize_common($merged);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('aksrt_widget_render_common_before')) {
|
||||
/**
|
||||
* Render common wrapper attributes and classes before widget content.
|
||||
*
|
||||
* Outputs CSS classes for animation, affix, show_type, etc.
|
||||
* Call this at the start of widget() before rendering content.
|
||||
*
|
||||
* @param array $args Sidebar args.
|
||||
* @param array $instance Widget instance.
|
||||
* @return array Modified args with extra classes.
|
||||
*/
|
||||
function aksrt_widget_get_common_wrapper_classes(array $args, array $instance): array {
|
||||
$instance = aksrt_widget_normalize_common($instance);
|
||||
$classes = [];
|
||||
|
||||
// Show type: PC or mobile
|
||||
if ($instance['show_type'] === 'pc') {
|
||||
$classes[] = 'aksrt-widget--pc-only';
|
||||
} elseif ($instance['show_type'] === 'mobile') {
|
||||
$classes[] = 'aksrt-widget--mobile-only';
|
||||
}
|
||||
|
||||
// Sidebar affix
|
||||
if ($instance['sidebar_affix']) {
|
||||
$classes[] = 'aksrt-widget--affix';
|
||||
}
|
||||
|
||||
// Animation
|
||||
if ($instance['animation_in'] !== '') {
|
||||
$classes[] = 'aksrt-widget--animated';
|
||||
$classes[] = 'aksrt-anim-' . $instance['animation_in'];
|
||||
if ($instance['animation_repeat']) {
|
||||
$classes[] = 'aksrt-anim-repeat';
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('aksrt_widget_should_display_by_ids')) {
|
||||
/**
|
||||
* Check if the current post/page should display the widget based on show_id_type and show_ids.
|
||||
*
|
||||
* @param array $instance Widget instance.
|
||||
* @return bool
|
||||
*/
|
||||
function aksrt_widget_should_display_by_ids(array $instance): bool {
|
||||
$instance = aksrt_widget_normalize_common($instance);
|
||||
|
||||
if ($instance['show_id_type'] === '' || $instance['show_ids'] === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
$current_id = get_the_ID();
|
||||
if (!$current_id) {
|
||||
return true; // Not on a singular page, allow display
|
||||
}
|
||||
|
||||
$ids = array_filter(array_map('absint', explode(',', $instance['show_ids'])));
|
||||
|
||||
if ($instance['show_id_type'] === 'include') {
|
||||
return in_array($current_id, $ids, true);
|
||||
}
|
||||
|
||||
if ($instance['show_id_type'] === 'exclude') {
|
||||
return !in_array($current_id, $ids, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('aksrt_widget_render_location_help')) {
|
||||
/**
|
||||
* Render placement hint inside widget admin forms.
|
||||
@@ -559,11 +359,11 @@ if (!function_exists('aksrt_filter_widget_display_by_area')) {
|
||||
if (is_array($instance) && function_exists('aksrt_widget_normalize_common')) {
|
||||
$normalized = aksrt_widget_normalize_common($instance);
|
||||
|
||||
// Show type: PC / mobile filter
|
||||
if ($normalized['show_type'] === 'pc' && wp_is_mobile()) {
|
||||
// Show type: PC / mobile filter (Zibll only_pc / only_sm).
|
||||
if ($normalized['show_type'] === 'only_pc' && wp_is_mobile()) {
|
||||
return false;
|
||||
}
|
||||
if ($normalized['show_type'] === 'mobile' && !wp_is_mobile()) {
|
||||
if ($normalized['show_type'] === 'only_sm' && !wp_is_mobile()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user