PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
jetpack / jetpack_vendor / automattic / jetpack-search / src / search-blocks / blocks / search-input / render.php

render.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3, at jetpack_vendor/automattic/jetpack-search/src/search-blocks/blocks/search-input/render.php

194 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Search Input block render.
4 *
5 * WordPress passes $attributes, $content, $block to render.php at runtime;
6 * VariableAnalysis can't see that, so the sniff is disabled here.
7 *
8 * @package automattic/jetpack-search
9 */
10
11 namespace Automattic\Jetpack\Search;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit( 0 );
15 }
16
17 // phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable
18
19 // Trim so a whitespace-only placeholder still falls back to the translated
20 // default — block.json's static default can't be localized, so the empty/
21 // whitespace case resolves here.
22 $placeholder = trim( (string) ( $attributes['placeholder'] ?? '' ) );
23 if ( '' === $placeholder ) {
24 $placeholder = __( 'Search…', 'jetpack-search-pkg' );
25 }
26 $show_icon = (bool) ( $attributes['showIcon'] ?? true );
27 $submit_only = ! empty( $attributes['submitOnly'] );
28 // Sanitize the saved `suggestionTypes` array down to a known-good subset of
29 // the enum, in the canonical render order, deduplicated. Mirrors the
30 // enum in `block.json::attributes.suggestionTypes.items.enum` and the
31 // `TYPE_ORDER` constant in `suggestion-rows.js`. Authors can save anything
32 // the editor lets them — and a future block-version migration could leave
33 // stale strings here — so the front end stays defensive.
34 $allowed_suggestion_types = array( 'query', 'taxonomy', 'post' );
35 $raw_suggestion_types = $attributes['suggestionTypes'] ?? $allowed_suggestion_types;
36 if ( ! is_array( $raw_suggestion_types ) ) {
37 $raw_suggestion_types = $allowed_suggestion_types;
38 }
39 $suggestion_types = array_values(
40 array_intersect( $allowed_suggestion_types, array_map( 'strval', $raw_suggestion_types ) )
41 );
42 // `enableSuggestions` is the author's master kill switch; an empty
43 // `suggestionTypes` array collapses to the same outcome (no dropdown). The
44 // combined gate keeps the rest of the render path branch-free — no listbox,
45 // no combobox attributes, no `data-wp-context` seeded when either says off.
46 $enable_suggestions = ! empty( $attributes['enableSuggestions'] ) && ! empty( $suggestion_types );
47 // Read the URL-derived query through the shared helper so the SSR
48 // `value=` matches the Interactivity store's seeded `searchQuery`.
49 // The helper picks `s` vs `q` based on `is_search()` and applies the
50 // same sanitize_text_field + trim WP would have applied to `s`.
51 $initial_query = Search_Blocks::parse_url_search_query();
52 $input_id = wp_unique_id( 'jetpack-search-input-' );
53 // A separate id pair is generated for the listbox + status region so the
54 // input's `aria-controls` / `aria-activedescendant` can resolve to stable
55 // DOM ids. Only generated (and only emitted) when suggestions are on, to
56 // keep the default DOM untouched for authors who haven't opted in.
57 $listbox_id = $enable_suggestions ? wp_unique_id( 'jetpack-search-suggestions-' ) : '';
58 // `data-wp-context` seeds the per-instance Interactivity state for the
59 // suggestions dropdown. Keeping it per-block (rather than on the shared
60 // `jetpack-search` store) means a header + sidebar Search Input on the
61 // same page never share a dropdown's `showSuggestions` / `activeIndex` —
62 // each instance owns its own UI state while still pulling the query from
63 // the shared `state.searchQuery`. Post-type scope lives on the parent
64 // `search-results` block, not here — inputs are entry points, not
65 // boundaries.
66 $context = $enable_suggestions
67 ? array(
68 'showSuggestions' => false,
69 'activeIndex' => -1,
70 'activeOptionId' => '',
71 'rows' => array(),
72 'listboxId' => $listbox_id,
73 // `suggestionTypes` is seeded so the view bundle can filter
74 // rows client-side without re-reading the block attribute via
75 // a roundtrip. Per-instance because two Search Input blocks
76 // on the same page could pick different shapes.
77 'suggestionTypes' => $suggestion_types,
78 )
79 : array();
80 $emit_context = ! empty( $context );
81 $context_json = $emit_context
82 ? wp_json_encode( $context, JSON_HEX_AMP | JSON_UNESCAPED_SLASHES )
83 : '';
84
85 // Mirrors `render_block_core_search()`'s width handling: both halves of the
86 // (value, unit) pair must be present, then emit `width: <n><unit>;` on the
87 // wrapper. The inside-wrapper's `display:flex` + `min-width:0` on the field
88 // shrinks the visible text area in step; icon + clear stay at natural width.
89 // `widthUnit` is allowlisted against the same units the editor offers —
90 // `block.json` only types it as a free-form string, so a REST write or a
91 // future migration can't smuggle an arbitrary unit into the inline style.
92 $wrapper_extra_attrs = array();
93 $allowed_width_units = array( 'px', '%' );
94 $raw_width_unit = (string) ( $attributes['widthUnit'] ?? '' );
95 $width_unit = in_array( $raw_width_unit, $allowed_width_units, true ) ? $raw_width_unit : '';
96 $has_width = isset( $attributes['width'] ) && '' !== $attributes['width'] && '' !== $width_unit;
97 if ( $has_width ) {
98 $wrapper_extra_attrs['style'] = sprintf(
99 'width:%d%s;',
100 (int) $attributes['width'],
101 $width_unit
102 );
103 }
104 ?>
105 <div
106 <?php echo wp_kses_data( get_block_wrapper_attributes( $wrapper_extra_attrs ) ); ?>
107 data-wp-interactive="jetpack-search"
108 <?php if ( $emit_context ) : ?>
109 data-wp-context='<?php echo esc_attr( $context_json ); ?>'
110 <?php endif; ?>
111 >
112 <label class="jetpack-search-input__label screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>">
113 <?php esc_html_e( 'Search', 'jetpack-search-pkg' ); ?>
114 </label>
115 <div class="jetpack-search-input__inside-wrapper">
116 <?php if ( $show_icon ) : ?>
117 <svg class="jetpack-search-input__icon" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
118 <path d="M13 5c-3.3 0-6 2.7-6 6 0 1.4.5 2.7 1.3 3.7l-3.8 3.8 1.1 1.1 3.8-3.8c1 .8 2.3 1.3 3.7 1.3 3.3 0 6-2.7 6-6s-2.7-6-6-6zm0 10.5c-2.5 0-4.5-2-4.5-4.5s2-4.5 4.5-4.5 4.5 2 4.5 4.5-2 4.5-4.5 4.5z" />
119 </svg>
120 <?php endif; ?>
121 <input
122 id="<?php echo esc_attr( $input_id ); ?>"
123 type="search"
124 class="jetpack-search-input__field"
125 placeholder="<?php echo esc_attr( $placeholder ); ?>"
126 value="<?php echo esc_attr( $initial_query ); ?>"
127 <?php if ( $submit_only ) : ?>
128 data-submit-only="true"
129 <?php endif; ?>
130 <?php if ( $enable_suggestions ) : ?>
131 role="combobox"
132 aria-autocomplete="list"
133 aria-haspopup="listbox"
134 aria-controls="<?php echo esc_attr( $listbox_id ); ?>"
135 data-suggestions-enabled="true"
136 data-wp-bind--aria-expanded="context.showSuggestions"
137 data-wp-bind--aria-activedescendant="context.activeOptionId"
138 data-wp-on--focus="actions.onSearchFocus"
139 data-wp-on--blur="actions.onSearchBlur"
140 <?php endif; ?>
141 data-wp-bind--value="state.searchQuery"
142 data-wp-on--input="actions.onSearchInput"
143 data-wp-on--keydown="actions.onSearchKeydown"
144 data-wp-init="callbacks.initFocusInputIfHasQuery"
145 />
146 <button
147 type="button"
148 class="jetpack-search-input__clear"
149 data-wp-bind--hidden="!state.searchQuery"
150 data-wp-on--click="actions.clearSearch"
151 >
152 <?php
153 /* translators: Button is used to clear the search input query. */
154 echo esc_html__( 'clear', 'jetpack-search-pkg' );
155 ?>
156 </button>
157 <?php if ( $enable_suggestions ) : ?>
158 <ul
159 id="<?php echo esc_attr( $listbox_id ); ?>"
160 class="jetpack-search-input__suggestions"
161 role="listbox"
162 aria-label="<?php echo esc_attr__( 'Search suggestions', 'jetpack-search-pkg' ); ?>"
163 data-wp-bind--hidden="!context.showSuggestions"
164 hidden
165 >
166 <template
167 data-wp-each--row="context.rows"
168 data-wp-each-key="context.row.key"
169 >
170 <li
171 class="jetpack-search-input__suggestions-label"
172 role="presentation"
173 data-wp-bind--hidden="!context.row.isHeader"
174 data-wp-text="context.row.label"
175 ></li>
176 <li
177 class="jetpack-search-input__suggestions-option"
178 role="option"
179 data-wp-bind--hidden="context.row.isHeader"
180 data-wp-bind--id="context.row.optionId"
181 data-wp-bind--aria-selected="state.isRowActive"
182 data-wp-class--is-active="state.isRowActive"
183 data-wp-on--mousedown="actions.onSuggestionMousedown"
184 data-wp-on--click="actions.onSuggestionClick"
185 tabindex="-1"
186 >
187 <span data-wp-text="context.row.text"></span>
188 </li>
189 </template>
190 </ul>
191 <?php endif; ?>
192 </div>
193 </div>
194