PluginProbe ʕ •ᴥ•ʔ
VK Blocks / 1.126.2
VK Blocks v1.126.2
1.126.4 1.126.3 1.126.1 1.126.2 1.126.0 1.125.0 1.124.0 1.123.0 1.122.0 1.121.0 1.120.0 1.119.2 1.119.1 1.119.0 1.118.7 1.82.0.0 1.82.0.1 1.83.0.0 1.83.0.1 1.84.0.0 1.84.0.1 1.85.0.2 1.85.0.3 1.85.1.0 1.85.1.1 1.86.0.0 1.86.0.1 1.86.1.0 1.87.0.0 1.87.0.1 1.88.0.1 1.88.0.2 1.89.0.0 1.9.2 1.90.0.0 1.90.0.1 1.90.1.1 1.91.0.0 1.91.1.1 1.92.0.0 1.92.0.1 1.92.1.0 1.92.1.1 1.93.0.0 trunk 1.93.0.1 0.1.1 1.93.1.0 0.10.3 1.93.1.1 0.11.0 1.94.0.0 0.12.6 1.94.0.1 0.15.1 1.94.1.0 0.16.2 1.94.2.1 0.17.6 1.94.2.2 0.2.2 1.95.0.3 0.22.4 1.95.0.4 0.26.7 1.96.2.0 0.3.2 1.96.2.1 0.31.0 1.97.0.1 0.35.5 1.97.0.2 0.37.5 1.99.0.0 0.38.8 1.99.0.1 0.39.4 0.4.7 0.41.0 0.42.0 0.44.13 0.45.2 0.46.1 0.47.0 0.48.0 0.49.8 0.5.2 0.50.3 0.51.0 0.52.2 0.53.2 0.54.0 0.55.0 0.56.2 0.56.3 0.57.6 0.58.10 0.59.0 0.6.0 0.60.1 0.61.2 0.7.1 0.8.2 1.0.16 1.10.0 1.100.0.0 1.100.0.1 1.102.0.0 1.102.0.1 1.103.0.0 1.104.0.0 1.104.0.1 1.105.1.0 1.105.1.1 1.106.0.0 1.106.0.1 1.107.0.0 1.107.0.1 1.107.0.2 1.108.0.0 1.108.0.1 1.109.0.0 1.109.0.1 1.11.4 1.110.0.0 1.110.0.1 1.111.0.0 1.111.0.1 1.111.0.2 1.112.0.0 1.112.0.1 1.113.0.0 1.113.0.1 1.114.0.0 1.114.0.1 1.114.2.0 1.114.2.1 1.115.0.0 1.115.0.1 1.115.1.0 1.115.1.1 1.115.2.0 1.115.2.1 1.116.0.0 1.116.0.1 1.116.1.0 1.116.1.1 1.116.2.0 1.117.0.0 1.117.0.1 1.117.1.0 1.118.2 1.118.5 1.12.0 1.13.2 1.14.1 1.15.1 1.16.11 1.17.0 1.18.6 1.19.0 1.19.1 1.2.3 1.20.7 1.21.0 1.22.4 1.23.0 1.24.5 1.25.1 1.26.2 1.27.7.2 1.28.0.1 1.29.2.0 1.3.9 1.30.0.1 1.31.0.1 1.32.0.2 1.33.2.1 1.36.1.5 1.37.0.0 1.39.2.1 1.4.6 1.40.0.1 1.40.1.0 1.40.1.1 1.41.0.0 1.41.0.1 1.41.2.2 1.41.2.3 1.43.0.0 1.43.0.1 1.43.0.2 1.44.0.0 1.44.0.1 1.45.0.0 1.45.0.1 1.46.0.0 1.46.0.1 1.47.0.0 1.47.0.1 1.47.1.0 1.48.0.0 1.48.0.1 1.48.0.2 1.48.1.0 1.48.1.1 1.5.0 1.50.0.0 1.50.0.1 1.51.0.0 1.51.0.1 1.52.0.0 1.52.0.1 1.53.0.0 1.53.0.1 1.54.0.0 1.54.0.1 1.55.0.0 1.55.0.1 1.56.0.0 1.56.0.1 1.57.0.0 1.57.0.2 1.57.0.3 1.57.0.4 1.57.0.5 1.57.1.0 1.57.1.1 1.57.1.2 1.58.0.0 1.58.0.1 1.59.0.0 1.59.0.1 1.6.0 1.60.0.0 1.60.0.1 1.63.0.0 1.63.0.1 1.64.0.0 1.64.0.1 1.64.1.0 1.64.1.2 1.67.0.0 1.67.0.1 1.68.0.0 1.68.0.1 1.69.0.0 1.69.0.1 1.69.1.1 1.69.1.2 1.7.1 1.70.0.0 1.70.0.1 1.71.0.0 1.71.0.1 1.72.0.0 1.72.1.0 1.72.1.1 1.73.0.0 1.73.0.1 1.74.0.0 1.74.0.1 1.75.0.0 1.75.1.0 1.75.1.1 1.76.0.0 1.76.0.1 1.76.1.0 1.76.1.1 1.76.2.0 1.76.2.1 1.77.0.0 1.77.0.1 1.78.0.0 1.78.0.1 1.79.0.0 1.79.0.1 1.79.0.2 1.79.0.3 1.79.1.0 1.79.1.1 1.8.2 1.80.1.0 1.80.1.1 1.80.1.2 1.81.0.1 1.81.0.2
vk-blocks / src / extensions / core / query / style.js
vk-blocks / src / extensions / core / query Last commit date
style.js 1 week ago
style.js
429 lines
1 /**
2 * WordPress dependencies
3 */
4 import { addFilter } from '@wordpress/hooks';
5 import { createHigherOrderComponent } from '@wordpress/compose';
6 import { InspectorControls } from '@wordpress/block-editor';
7 import {
8 PanelBody,
9 SelectControl,
10 ToggleControl,
11 Notice,
12 } from '@wordpress/components';
13 import { registerBlockVariation } from '@wordpress/blocks';
14 import { __ } from '@wordpress/i18n';
15 import { speak } from '@wordpress/a11y';
16 import apiFetch from '@wordpress/api-fetch';
17 import { select, useSelect } from '@wordpress/data';
18
19 /**
20 * Internal dependencies
21 */
22 import { VkPanelIcon } from '@vkblocks/components/vk-icon';
23
24 const RELATED_POSTS_QUERY_TYPE = 'relatedPosts';
25
26 /**
27 * Structural Site Editor entities (templates, template parts, navigation)
28 * are not real content; core/editor's getCurrentPostType()/getCurrentPostId()
29 * report the entity itself (e.g. postId "themeSlug//templateSlug", not a
30 * number) while editing them, so callers must not treat those as a post.
31 * サイトエディタの構造的エンティティ(テンプレート等)は実コンテンツではない。
32 * 編集中は core/editor の getCurrentPostType()/getCurrentPostId() がその
33 * エンティティ自身を返す(postId も数値ではなく "テーマ//テンプレート" 形式の
34 * 文字列になる)ため、投稿として扱ってはならない。
35 */
36 const NON_CONTENT_ENTITY_POST_TYPES = [
37 'wp_template',
38 'wp_template_part',
39 'wp_navigation',
40 ];
41
42 /**
43 * Attach the edited post ID to related-posts REST previews (request-time only).
44 * 関連記事 REST プレビューへ編集中投稿 ID をリクエスト時だけ付与する。
45 */
46 apiFetch.use((options, next) => {
47 if (typeof options?.path !== 'string') {
48 return next(options);
49 }
50 if (
51 !options.path.includes(
52 `vkBlocksProQueryType=${RELATED_POSTS_QUERY_TYPE}`
53 ) ||
54 options.path.includes('vkBlocksProRelatedPostId=')
55 ) {
56 return next(options);
57 }
58
59 let postId;
60 try {
61 const editor = select('core/editor');
62 // Editing a template/template-part directly (no post-in-progress):
63 // skip getCurrentPostId(), which would return the entity's own
64 // string ID and fail the REST integer param validation (400).
65 // テンプレート等を直接編集中は投稿が存在しないため getCurrentPostId()
66 // を使わない(エンティティ自身の文字列IDが返り、REST の integer 型
67 // バリデーションで 400 になるため)。
68 if (
69 !NON_CONTENT_ENTITY_POST_TYPES.includes(
70 editor?.getCurrentPostType?.()
71 )
72 ) {
73 postId = editor?.getCurrentPostId?.();
74 }
75 } catch (e) {
76 // editor store may be unavailable outside the post editor.
77 }
78 if (!postId) {
79 try {
80 postId = select('core/edit-site')?.getEditedPostContext?.()?.postId;
81 } catch (e) {
82 // ignore
83 }
84 }
85 if (!postId) {
86 return next(options);
87 }
88
89 const separator = options.path.includes('?') ? '&' : '?';
90 return next({
91 ...options,
92 path: `${options.path}${separator}vkBlocksProRelatedPostId=${postId}`,
93 });
94 });
95
96 /**
97 * Whether related-posts mode is on (vkBlocksProQueryType).
98 * Matches PHP vk_blocks_is_related_posts_query_loop.
99 * 関連記事モード判定(PHP 側と同じ)。
100 *
101 * @param {Object} blockAttributes Block attributes.
102 * @return {boolean} True when related-posts mode is active.
103 */
104 const isRelatedPostsQueryAttributes = (blockAttributes) =>
105 blockAttributes?.query?.vkBlocksProQueryType === RELATED_POSTS_QUERY_TYPE;
106
107 /**
108 * Hidden variation for allowedControls only. Title stays "Query Loop" so the
109 * block is never renamed; no icon / description / attributes (avoids sticky ON).
110 * allowedControls 絞り込み専用。タイトルはクエリーループのまま。
111 */
112 registerBlockVariation('core/query', {
113 name: 'vk-blocks-related-posts',
114 // Intentionally no text domain: reuse WordPress core's "Query Loop"
115 // translation so this hidden variation title always matches the core block.
116 // テキストドメインは意図的に付けない。コアの「Query Loop」翻訳を流用し、
117 // 隠しバリエーションのタイトルをコアのブロック名と常に一致させるため。
118 title: __('Query Loop'),
119 // postCount + order (tiebreak). Other controls hide in related mode.
120 // 件数と並び順(tiebreak)のみ。関連�
121 �は現在の投稿に固定。
122 allowedControls: ['postCount', 'order'],
123 scope: [],
124 isActive: (blockAttributes) =>
125 isRelatedPostsQueryAttributes(blockAttributes),
126 });
127
128 /**
129 * VK Query Extension panel: related-posts toggle + modified-date order.
130 * 「VK クエリー拡張」: 関連記事トグルと更新日順。
131 */
132 const withQueryOrderExtension = createHigherOrderComponent(
133 (BlockEdit) => (props) => {
134 const { name, attributes, setAttributes } = props;
135
136 if (name !== 'core/query') {
137 return <BlockEdit {...props} />;
138 }
139
140 const { query } = attributes;
141 const { orderBy = 'date', order = 'desc' } = query || {};
142 const isRelatedPostsQuery = isRelatedPostsQueryAttributes(attributes);
143
144 // Disable (gray out) only when the edited entity is clearly not a post.
145 // Site editor / unknown context stays enabled; help text covers the caveat.
146 // 編集中が post 以外と分かるときだけ無効化(グレー)。テンプレ等は有効のまま。
147 const editedPostType = useSelect((sel) => {
148 // Site Editor: templates preview a sample post via edited-post
149 // context. core/editor's getCurrentPostType() would return the
150 // template's own type (e.g. wp_template), not the previewed
151 // post's type, so prefer the preview context here.
152 // サイトエディタはテンプレート�
153 でサンプル投稿をプレビューする。
154 // core/editor の getCurrentPostType() はテンプレート自身の型
155 // (wp_template 等)を返してしまうため、プレビュー中の投稿の型を
156 // 優�
157 �して使う。
158 try {
159 const templateContext =
160 sel('core/edit-site')?.getEditedPostContext?.();
161 if (templateContext?.postType) {
162 return templateContext.postType;
163 }
164 } catch (e) {
165 // edit-site store may be unavailable outside the site editor.
166 }
167 try {
168 const currentPostType =
169 sel('core/editor')?.getCurrentPostType?.();
170 // Structural entities (templates, template parts, nav) are not
171 // real content types; treat them as unknown rather than
172 // unsupported so template editing does not get blocked.
173 // テンプレート等の構造的エンティティは実コンテンツの投稿タイプ
174 // ではないため「不明」扱いにし、テンプレ編集を誤遮断しない。
175 if (
176 currentPostType &&
177 !NON_CONTENT_ENTITY_POST_TYPES.includes(currentPostType)
178 ) {
179 return currentPostType;
180 }
181 } catch (e) {
182 // editor store may be unavailable outside the post editor.
183 }
184 return null;
185 }, []);
186 const isUnsupportedPostType =
187 typeof editedPostType === 'string' && editedPostType !== 'post';
188 // Keep the control usable to turn OFF if it was already on.
189 // 既に ON のときは OFF に戻せるようにする。
190 const isRelatedPostsToggleDisabled =
191 isUnsupportedPostType && !isRelatedPostsQuery;
192
193 // Each sentence is wrapped in its own __() (one sentence per call).
194 // 翻訳関数は1文につき1つ(1関数=1文ルール)。
195 // When disabled, drop the toggle help and explain via the warning Notice
196 // below so the reason reaches assistive tech without focusing the control.
197 // disabled 時はトグルの help を外し、下の warning Notice で理由を伝える
198 // (フォーカス非依存で AT に届くようにするため)。
199 const relatedPostsHelp = isRelatedPostsToggleDisabled
200 ? undefined
201 : __(
202 'Related posts are matched by the tags on the current post.',
203 'vk-blocks'
204 ) +
205 ' ' +
206 __(
207 'The list may be empty on pages or other post types, or until a new post is saved.',
208 'vk-blocks'
209 );
210
211 /**
212 * Switch data source only (keep layout / perPage / order).
213 * ON: inherit=false (required for build_query_vars_from_query_block).
214 * OFF: clear vkBlocksProQueryType, then restore inherit.
215 * データソースだけ切替。ON 時 inherit=false。
216 * OFF 時はフラグを消し、inherit を復�
217 �する。
218 *
219 * vkBlocksProHadInherit / vkBlocksProInheritBackup are stored on the query
220 * attribute on purpose so OFF can restore the original inherit even after a
221 * reload; they persist in saved content but are harmless (unknown query keys
222 * are ignored on the front end). Moving them to useRef breaks that restore.
223 * これらの退避キーは意図的に query 属性へ保存する(リロード後も OFF で�
224 �の
225 * inherit を復�
226 �するため)。保存�
227 容に残るが無害。useRef 化は復�
228 �を壊す。
229 *
230 * @param {boolean} nextIsOn Whether related posts mode is being enabled.
231 */
232 const toggleRelatedPosts = (nextIsOn) => {
233 const nextQuery = { ...(query || {}) };
234 const nextAttributes = { query: nextQuery };
235 const inheritBackupKey = 'vkBlocksProInheritBackup';
236 const inheritHadKey = 'vkBlocksProHadInherit';
237
238 if (nextIsOn) {
239 nextQuery.vkBlocksProQueryType = RELATED_POSTS_QUERY_TYPE;
240 // Remember inherit only on first switch into related mode.
241 // 関連モードへ�
242 �る初回だけ inherit を退避する。
243 if (
244 !Object.prototype.hasOwnProperty.call(
245 nextQuery,
246 inheritHadKey
247 )
248 ) {
249 const hadInherit = Object.prototype.hasOwnProperty.call(
250 nextQuery,
251 'inherit'
252 );
253 nextQuery[inheritHadKey] = hadInherit;
254 nextQuery[inheritBackupKey] = hadInherit
255 ? nextQuery.inherit
256 : undefined;
257 }
258 nextQuery.inherit = false;
259 } else {
260 delete nextQuery.vkBlocksProQueryType;
261 if (
262 Object.prototype.hasOwnProperty.call(
263 nextQuery,
264 inheritHadKey
265 )
266 ) {
267 const hadInherit = nextQuery[inheritHadKey];
268 const backup = nextQuery[inheritBackupKey];
269 delete nextQuery[inheritHadKey];
270 delete nextQuery[inheritBackupKey];
271 if (hadInherit) {
272 nextQuery.inherit = backup;
273 } else {
274 delete nextQuery.inherit;
275 }
276 }
277 }
278
279 setAttributes(nextAttributes);
280 // ON: the Notice rendered while related mode is on announces itself on
281 // mount, so calling speak() here would double-announce to screen readers.
282 // OFF: no Notice is rendered, so announce the change here.
283 // ON時は Notice が mount 時に自動読み上げするため speak しない(重複回避)。
284 // OFF時は Notice が無いのでここで通知する。
285 if (!nextIsOn) {
286 speak(__('Related posts turned off.', 'vk-blocks'), 'polite');
287 }
288 };
289
290 const getSelectValue = () => {
291 if (orderBy === 'modified') {
292 return order === 'desc' ? 'modified_desc' : 'modified_asc';
293 }
294 return '';
295 };
296
297 return (
298 <>
299 <BlockEdit {...props} />
300 <InspectorControls>
301 <PanelBody
302 title={__('VK Query Extension', 'vk-blocks')}
303 icon={
304 <VkPanelIcon
305 isActive={
306 isRelatedPostsQuery ||
307 orderBy === 'modified'
308 }
309 />
310 }
311 initialOpen={isRelatedPostsQuery}
312 >
313 <ToggleControl
314 label={__('Show related posts', 'vk-blocks')}
315 help={relatedPostsHelp}
316 checked={isRelatedPostsQuery}
317 disabled={isRelatedPostsToggleDisabled}
318 onChange={toggleRelatedPosts}
319 />
320 {isRelatedPostsToggleDisabled && (
321 <Notice status="warning" isDismissible={false}>
322 {__(
323 'Related posts are unavailable for this content type.',
324 'vk-blocks'
325 ) +
326 ' ' +
327 __(
328 'Switch to editing a post that has tags to use this feature.',
329 'vk-blocks'
330 )}
331 </Notice>
332 )}
333 {isRelatedPostsQuery && (
334 <Notice status="info" isDismissible={false}>
335 {__(
336 'Related posts are ranked by how many tags each post shares with the post you are editing, showing the most related first.',
337 'vk-blocks'
338 ) +
339 ' ' +
340 __(
341 'Order settings only affect posts that share the same number of tags.',
342 'vk-blocks'
343 ) +
344 ' ' +
345 __(
346 'Use the "Items per page" setting to change how many related posts appear.',
347 'vk-blocks'
348 )}
349 </Notice>
350 )}
351 <SelectControl
352 label={__('Order by', 'vk-blocks')}
353 help={
354 isRelatedPostsQuery
355 ? __(
356 'Order for posts that share the same number of tags.',
357 'vk-blocks'
358 )
359 : __(
360 'This setting will override the standard order setting.',
361 'vk-blocks'
362 )
363 }
364 value={getSelectValue()}
365 onChange={(value) => {
366 if (value === 'modified_desc') {
367 setAttributes({
368 query: {
369 ...query,
370 orderBy: 'modified',
371 order: 'desc',
372 },
373 });
374 } else if (value === 'modified_asc') {
375 setAttributes({
376 query: {
377 ...query,
378 orderBy: 'modified',
379 order: 'asc',
380 },
381 });
382 } else if (orderBy === 'modified') {
383 setAttributes({
384 query: {
385 ...query,
386 orderBy: 'date',
387 order: 'desc',
388 },
389 });
390 }
391 }}
392 options={[
393 {
394 value: '',
395 label: __(
396 'Use standard options',
397 'vk-blocks'
398 ),
399 },
400 {
401 value: 'modified_desc',
402 label: __(
403 'Modified (Newest to oldest)',
404 'vk-blocks'
405 ),
406 },
407 {
408 value: 'modified_asc',
409 label: __(
410 'Modified (Oldest to newest)',
411 'vk-blocks'
412 ),
413 },
414 ]}
415 />
416 </PanelBody>
417 </InspectorControls>
418 </>
419 );
420 },
421 'withQueryOrderExtension'
422 );
423
424 addFilter(
425 'editor.BlockEdit',
426 'vk-blocks/query-order-extension',
427 withQueryOrderExtension
428 );
429