fix(widgets): restore single-card reference UI for search and quote widgets

The 搜索框 and 一言 sidebar widgets fell into the generic renderer branch, so
they rendered a .jio-panel card wrapping a second bespoke card
(.jio-widget-search / .jio-widget-quote). In-article 一言 also used
divergent modifier classes and an extra hover rule.

- search: render through the shared jio_get_search_box() (legacy keyword,
  history and category pickers) inside one reference card, mirroring the
  reference widget's markup and title default 「搜索」
- quote: render the reference .yiyan-box card itself as the click root; the
  shell now emits only the reference widget-container/title wrapper
- article 一言: adopt the reference class lists for header, footer and the
  standalone after-article box; drop the invented yiyan--* modifiers and
  the duplicate hover/opacity rules
- client: fill .cn/.en in the reference order (first line .cn, second .en)
  and drop the stale window.JioYiyan bridge plus the dead qv-yiyan.php AJAX
  path in runtime main.js
- schema: replace the ad-hoc search_style radio with the reference switches
  (keywords, history, input category, more_cats) and default title
- assets: enqueue assets/js/yiyan.js through the quote widget's asset list

Tests: widget-system suite 820/821 (the one failure is the pre-existing
frontend-lifecycle theme-init stub); new search/quote card contracts pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Opus 5 committed 2026-11-03 15:56:52 +08:00
1 parent 1549d5b25d
commit f80ca1e27d
19 files changed
+276 -182

No files matched your search

+21 -7
View File
@@ -29,6 +29,10 @@ final class FrontendRenderer {
echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return;
}
if (in_array($definition->slug(), ['search', 'quote'], true)) {
self::render_reference_widget($definition, $instance, $widget, $content, true);
return;
}
if (in_array($definition->slug(), ['user_card', 'author_card', 'tab_posts', 'posts'], true)) {
self::render_reference_widget($definition, $instance, $widget, $content);
return;
@@ -90,7 +94,13 @@ final class FrontendRenderer {
echo (string) ($args['after_widget'] ?? '</section>'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
private static function render_reference_widget(WidgetDefinition $definition, array $instance, SchemaWidget $widget, string $content): void {
/**
* 参考主题侧栏结构:widget-container > 标题 + 内容。
*
* 搜索框 / 一言自带卡片(.zib-widget.widget-search / .yiyan-box),
* 此时不再套 .widget-content 与第二层卡片;其余模块保持 .widget-content 结构层。
*/
private static function render_reference_widget(WidgetDefinition $definition, array $instance, SchemaWidget $widget, string $content, bool $own_card = false): void {
$classes = ['jio-widget-box-wrap', 'jio-' . str_replace('_', '-', $definition->slug()) . '-wrap'];
if (in_array($definition->slug(), ['user_card', 'author_card'], true)) {
$classes[] = 'jio-user-widget-wrap';
@@ -103,12 +113,12 @@ final class FrontendRenderer {
$classes[] = 'jio-widget--affix';
}
$content_classes = ['widget-content'];
$animation = '';
if (!empty($instance['animation_in'])) {
$content_classes[] = 'obs-animate ani-' . sanitize_html_class((string) $instance['animation_in']);
}
if (!empty($instance['animation_repeat'])) {
$content_classes[] = 'obs-animate-repeat';
$animation = ' obs-animate ani-' . sanitize_html_class((string) $instance['animation_in']);
if (!empty($instance['animation_repeat'])) {
$animation .= ' obs-animate-repeat';
}
}
$style = self::style($instance);
@@ -116,7 +126,11 @@ final class FrontendRenderer {
echo '<div class="' . esc_attr(implode(' ', $classes)) . '" data-jio-widget="' . esc_attr($definition->slug()) . '" data-widget-id="' . esc_attr((string) $widget->id) . '"' . $affix_attr . ($style !== '' ? ' style="' . esc_attr($style) . '"' : '') . '>';
echo '<div class="widget-container"><div>';
echo self::reference_title($instance); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="' . esc_attr(implode(' ', $content_classes)) . '">' . $content . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
if ($own_card) {
echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo '<div class="widget-content' . esc_attr($animation) . '">' . $content . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
echo '</div></div></div>';
}