PluginProbe ʕ •ᴥ•ʔ
VK Blocks / trunk
VK Blocks vtrunk
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 / group / view.js
vk-blocks / src / extensions / core / group Last commit date
deprecated 2 weeks ago style.js 2 months ago style.scss 2 months ago view.js 2 weeks ago
view.js
760 lines
1 /**
2 * Group Block Scrollable Extension - Frontend View
3 *
4 * @package
5 */
6
7 (function () {
8 'use strict';
9
10 // 重複読み込み防止フラグ(より�
11 牢な初期化ガード)
12 if (window.__vkGroupScrollableInit) {
13 return;
14 }
15 window.__vkGroupScrollableInit = true;
16
17 // 更新中フラグ(無限ループを防ぐ)
18 let isUpdating = false;
19
20 // 測定用コンテナ(1回だけ作成して再利用、パフォーマンス向上)
21 let measurementContainer = null;
22
23 /**
24 * 測定用のコンテナを取得(再利用)
25 * @return {HTMLElement} 測定用コンテナ要素
26 */
27 function getMeasurementContainer() {
28 if (!measurementContainer) {
29 measurementContainer = document.createElement('div');
30 // 測定用コンテナを非表示にする(レイアウトシフトを防ぐ)
31 measurementContainer.style.cssText =
32 'position: absolute; visibility: hidden; top: -9999px; left: -9999px;';
33 document.body.appendChild(measurementContainer);
34 }
35 return measurementContainer;
36 }
37
38 /**
39 * カラムの�
40 を測定(バッチ処理でパフォーマンス向上)
41 * @param {HTMLElement[]} columns - 測定対象のカラム要素の�
42 �列
43 * @param {HTMLElement} tempContainer - 測定用コンテナ
44 * @return {number} 最大�
45
46 */
47 function measureColumnsWidth(columns, tempContainer) {
48 const clones = [];
49 let maxWidth = 0;
50
51 // バッチ処理:すべてのクローンを一度に作成
52 columns.forEach((column) => {
53 if (!column) {
54 return;
55 }
56
57 try {
58 const columnClone = column.cloneNode(true);
59 const computedStyle = window.getComputedStyle(column);
60
61 // クローンに�
62 要なスタイルを適用
63 columnClone.style.cssText = `
64 position: absolute;
65 visibility: hidden;
66 top: -9999px;
67 left: -9999px;
68 width: auto;
69 max-width: none;
70 min-width: auto;
71 padding: ${computedStyle.padding};
72 margin: ${computedStyle.margin};
73 border: ${computedStyle.border};
74 box-sizing: ${computedStyle.boxSizing};
75 font-size: ${computedStyle.fontSize};
76 font-family: ${computedStyle.fontFamily};
77 `;
78
79 tempContainer.appendChild(columnClone);
80 clones.push(columnClone);
81 } catch (error) {
82 // エラーが発生した場合はスキップ
83 }
84 });
85
86 // バッチ処理:すべてのクローンの�
87 を一度に測定
88 clones.forEach((clone) => {
89 const contentWidth = clone.scrollWidth || clone.offsetWidth;
90 if (contentWidth > maxWidth) {
91 maxWidth = contentWidth;
92 }
93 });
94
95 // バッチ処理:すべてのクローンを一度に削除
96 clones.forEach((clone) => {
97 try {
98 tempContainer.removeChild(clone);
99 } catch (error) {
100 // エラーが発生した場合はスキップ
101 }
102 });
103
104 return maxWidth;
105 }
106
107 /**
108 * ブレークポイントに応じてテーブルモードを適用するかどうかを判定
109 * @param {HTMLElement} group - グループブロック要素
110 * @return {boolean} テーブルモードを適用するかどうか
111 */
112 function shouldApplyTableMode(group) {
113 const breakpoint = group.getAttribute('data-scroll-breakpoint');
114 if (!breakpoint) {
115 return false;
116 }
117
118 const windowWidth = window.innerWidth;
119 const mobileBreakpoint = 575.98;
120 const tabletBreakpoint = 991.98;
121
122 // PCブレークポイントの場合:常に適用
123 if (breakpoint === 'group-scrollable-pc') {
124 return true;
125 }
126
127 // Tabletブレークポイントの場合:991.98px以下の場合のみ適用
128 if (breakpoint === 'group-scrollable-tablet') {
129 return windowWidth <= tabletBreakpoint;
130 }
131
132 // Mobileブレークポイントの場合:575.98px以下の場合のみ適用
133 if (breakpoint === 'group-scrollable-mobile') {
134 return windowWidth <= mobileBreakpoint;
135 }
136
137 return false;
138 }
139
140 /**
141 * スクロール可能なグループブロック�
142 のカラムブロックの�
143 を統一する
144 * テーブルのように複数の行(.wp-block-columns)がある場合、同じ列位置のカラムを統一する
145 * リサイズ時やカスタムイベント時に使用(data-table-mode="true"のもののみ処理)
146 */
147 function equalizeColumnWidths() {
148 // テーブルモードが有効なスクロール可能なグループブロックを取得
149 const scrollableGroups = document.querySelectorAll(
150 '.wp-block-group.is-style-vk-group-scrollable[data-table-mode="true"]'
151 );
152
153 // 新しく追加されたグループブロックを監視対象に追加
154 scrollableGroups.forEach((group) => {
155 observeGroup(group);
156 });
157
158 // 各グループブロックを個別に処理(パフォーマンス向上)
159 scrollableGroups.forEach((group) => {
160 // ブレークポイントに応じてテーブルモードを適用するかどうかを判定
161 if (shouldApplyTableMode(group)) {
162 processGroupBlock(group);
163 } else {
164 // テーブルモードを適用しない場合は、min-widthを削除
165 removeMinWidthFromColumns(group);
166 }
167 });
168 }
169
170 // 監視中のグループブロックを記録(重複監視を防ぐ)
171 const observedGroups = new WeakSet();
172 // Intersection Observerで監視中のグループブロックを記録
173 const intersectionObservedGroups = new WeakSet();
174
175 // MutationObserverで動的に追加された要素にも対応
176 // attributesの変更(minWidthの設定)は無視する
177 const observer = new MutationObserver((mutations) => {
178 // 更新中は無視
179 if (isUpdating) {
180 return;
181 }
182
183 // attributesの変更は無視(minWidthの設定による再実行を防ぐ)
184 const hasRelevantChanges = mutations.some((mutation) => {
185 return (
186 mutation.type === 'childList' &&
187 (mutation.addedNodes.length > 0 ||
188 mutation.removedNodes.length > 0)
189 );
190 });
191
192 if (hasRelevantChanges) {
193 // 変更があったグループブロックのみを処理(パフォーマンス向上)
194 const groupsToProcess = new Set();
195 const groupsToClean = new Set();
196 mutations.forEach((mutation) => {
197 let group = mutation.target;
198 // 親要素を遡ってグループブロックを探す
199 while (
200 group &&
201 (!group.classList ||
202 !group.classList.contains('wp-block-group') ||
203 !group.classList.contains(
204 'is-style-vk-group-scrollable'
205 ))
206 ) {
207 group = group.parentElement;
208 }
209 if (group) {
210 // data-table-modeがtrueの場合は処理対象に追加
211 if (group.getAttribute('data-table-mode') === 'true') {
212 // ブレークポイントに応じてテーブルモードを適用するかどうかを判定
213 if (shouldApplyTableMode(group)) {
214 groupsToProcess.add(group);
215 } else {
216 // テーブルモードを適用しない場合はmin-width削除対象に追加
217 groupsToClean.add(group);
218 }
219 } else {
220 // data-table-modeがtrueでない場合はmin-width削除対象に追加
221 groupsToClean.add(group);
222 }
223 }
224 });
225 // 変更があったグループブロックを処理(コンテンツ変更時は再処理が�
226 要)
227 groupsToProcess.forEach((group) => {
228 // 非同期処理でブラウザをブロックしない
229 requestAnimationFrame(() => {
230 processGroupBlock(group);
231 });
232 });
233 // data-table-modeがtrueでないグループブロックからmin-widthを削除
234 groupsToClean.forEach((group) => {
235 requestAnimationFrame(() => {
236 removeMinWidthFromColumns(group);
237 });
238 });
239 }
240 });
241
242 /**
243 * グループブロックを監視対象に追加
244 * @param {HTMLElement} group - 監視対象のグループブロック要素
245 */
246 function observeGroup(group) {
247 if (!observedGroups.has(group)) {
248 observer.observe(group, {
249 childList: true,
250 subtree: true,
251 attributes: false, // attributesの変更は無視
252 });
253 observedGroups.add(group);
254 }
255 }
256 // 処理済みのグループブロックを記録(Intersection Observerで初回表示時のみ処理)
257 const processedGroups = new WeakSet();
258
259 // Intersection Observerで動的に追加されたグループブロックを自動検出
260 // 表示領域に�
261 �った時に自動的に処理される
262 const intersectionObserver = new IntersectionObserver(
263 (entries) => {
264 entries.forEach((entry) => {
265 if (
266 entry.isIntersecting &&
267 !processedGroups.has(entry.target)
268 ) {
269 // 表示領域に�
270 �ったグループブロックを処理(同期的に処理)
271 // ブレークポイントに応じてテーブルモードを適用するかどうかを判定
272 if (shouldApplyTableMode(entry.target)) {
273 processGroupBlock(entry.target);
274 } else {
275 // テーブルモードを適用しない場合は、min-widthを削除
276 removeMinWidthFromColumns(entry.target);
277 }
278 processedGroups.add(entry.target);
279 // 一度処理したら監視を解除(パフォーマンス向上)
280 intersectionObserver.unobserve(entry.target);
281 }
282 });
283 },
284 {
285 // 少し早めに検出(表示領域の10%が見えた時点)
286 rootMargin: '10%',
287 }
288 );
289
290 /**
291 * テーブルモードが無効なグループブロックからmin-widthを削除
292 * @param {HTMLElement} group - 処理対象のグループブロック要素
293 */
294 function removeMinWidthFromColumns(group) {
295 // 一度にすべてのカラムを取得(min-widthが設定されているもののみ)
296 const columns = group.querySelectorAll(
297 '.wp-block-columns .wp-block-column[style*="min-width"]'
298 );
299
300 // requestAnimationFrameでまとめて処理
301 if (columns.length > 0) {
302 requestAnimationFrame(() => {
303 columns.forEach((column) => {
304 column.style.minWidth = '';
305 });
306 });
307 }
308 }
309
310 /**
311 * 単一のグループブロックを処理(パフォーマンス向上のため個別処理)
312 * @param {HTMLElement} group - 処理対象のグループブロック要素(data-table-mode="true"のもののみ)
313 */
314 function processGroupBlock(group) {
315 // 更新中フラグを設定
316 isUpdating = true;
317
318 // グループ�
319 のすべてのカラムブロック(行)を取得
320 const columnsContainers = Array.from(
321 group.querySelectorAll('.wp-block-columns')
322 );
323 if (columnsContainers.length === 0) {
324 isUpdating = false;
325 return;
326 }
327
328 // 測定用コンテナを取得(再利用)
329 const tempContainer = getMeasurementContainer();
330
331 // 最大列数を取得
332 let maxColumnCount = 0;
333 columnsContainers.forEach((container) => {
334 const columnCount =
335 container.querySelectorAll('.wp-block-column').length;
336 if (columnCount > maxColumnCount) {
337 maxColumnCount = columnCount;
338 }
339 });
340
341 // 各列位置ごとに最大�
342 を計算(バッチ処理でパフォーマンス向上)
343 const columnMaxWidths = [];
344
345 for (let colIndex = 0; colIndex < maxColumnCount; colIndex++) {
346 // 同じ列位置にあるすべてのカラムを収集
347 const columns = [];
348 columnsContainers.forEach((container) => {
349 const containerColumns = Array.from(
350 container.querySelectorAll('.wp-block-column')
351 );
352 if (containerColumns[colIndex]) {
353 columns.push(containerColumns[colIndex]);
354 }
355 });
356
357 // バッチ処理で一度に測定
358 columnMaxWidths[colIndex] = measureColumnsWidth(
359 columns,
360 tempContainer
361 );
362 }
363
364 // 各列位置のカラムに同じ最小�
365 を設定
366 columnsContainers.forEach((container) => {
367 const columns = Array.from(
368 container.querySelectorAll('.wp-block-column')
369 );
370 columns.forEach((column, colIndex) => {
371 if (columnMaxWidths[colIndex] > 0) {
372 column.style.minWidth = `${columnMaxWidths[colIndex]}px`;
373 }
374 });
375 });
376
377 // 更新中フラグを解除
378 isUpdating = false;
379 }
380
381 /**
382 * 新しく追加されたグループブロックをIntersection Observerで監視
383 * @param {Node} node - チェック対象のノード
384 */
385 function observeNewGroup(node) {
386 // 追加されたノード自体がグループブロックか確認(data-table-mode="true"のもののみ)
387 if (
388 node.nodeType === Node.ELEMENT_NODE &&
389 node.classList &&
390 node.classList.contains('wp-block-group') &&
391 node.classList.contains('is-style-vk-group-scrollable') &&
392 node.getAttribute('data-table-mode') === 'true'
393 ) {
394 if (!intersectionObservedGroups.has(node)) {
395 intersectionObserver.observe(node);
396 intersectionObservedGroups.add(node);
397 }
398 }
399
400 if (
401 node.nodeType === Node.ELEMENT_NODE &&
402 node.classList &&
403 node.classList.contains('wp-block-group') &&
404 node.classList.contains('is-style-vk-group-scrollable')
405 ) {
406 applyNestedGridScrollableStyle(node);
407 }
408
409 // 追加されたノードの子要素にグループブロックが含まれているか確認
410 if (node.nodeType === Node.ELEMENT_NODE) {
411 const nestedScrollableGroups = node.querySelectorAll(
412 '.wp-block-group.is-style-vk-group-scrollable'
413 );
414 nestedScrollableGroups.forEach((group) => {
415 applyNestedGridScrollableStyle(group);
416 });
417
418 // data-table-mode="true"のもののみIntersection Observerに追加
419 const nestedGroups = node.querySelectorAll(
420 '.wp-block-group.is-style-vk-group-scrollable[data-table-mode="true"]'
421 );
422 nestedGroups.forEach((group) => {
423 if (!intersectionObservedGroups.has(group)) {
424 intersectionObserver.observe(group);
425 intersectionObservedGroups.add(group);
426 }
427 });
428 }
429 }
430
431 // DOM変更を監視して、新しく追加されたグループブロックを検出(軽量版)
432 // 追加されたノードのみをチェックして、パフォーマンスを最適化
433 const checkNewGroupsObserver = new MutationObserver((mutations) => {
434 mutations.forEach((mutation) => {
435 if (
436 mutation.type === 'childList' &&
437 mutation.addedNodes.length > 0
438 ) {
439 mutation.addedNodes.forEach((node) => {
440 observeNewGroup(node);
441 });
442 }
443 });
444 });
445
446 /**
447 * 親要素のgrid-template-columnsからminimumColumnWidthを取得
448 * @param {HTMLElement} group - グループブロック要素
449 * @return {{kind: string, value: string}|null} minimumColumnWidthの値
450 */
451 function getMinimumColumnWidth(group) {
452 const extractMinimumColumnWidth = (gridTemplateColumns) => {
453 if (!gridTemplateColumns) {
454 return null;
455 }
456 // grid-template-columns: repeat(3, minmax(0, 1fr))
457 const repeatMinmaxMatch = gridTemplateColumns.match(
458 /repeat\(\s*(\d+)\s*,\s*minmax\(\s*0(?:px)?\s*,\s*1fr\s*\)\s*\)/i
459 );
460 if (repeatMinmaxMatch && repeatMinmaxMatch[1]) {
461 return {
462 kind: 'repeat-fr',
463 value: repeatMinmaxMatch[1].trim(),
464 };
465 }
466 // grid-template-columns: repeat(3, 1fr)
467 const repeatFrMatch = gridTemplateColumns.match(
468 /repeat\(\s*(\d+)\s*,\s*1fr\s*\)/i
469 );
470 if (repeatFrMatch && repeatFrMatch[1]) {
471 return { kind: 'repeat-fr', value: repeatFrMatch[1].trim() };
472 }
473 // grid-template-columns: repeat(auto-fill, minmax(min(XXXpx, 100%), 1fr))
474 const minMatch = gridTemplateColumns.match(
475 /min\(([^,]+),\s*100%\)/i
476 );
477 if (minMatch && minMatch[1]) {
478 return { kind: 'min', value: minMatch[1].trim() };
479 }
480 // grid-template-columns: repeat(3, minmax(0, 1fr))
481 const minmaxMatch = gridTemplateColumns.match(
482 /minmax\(\s*([^,]+)\s*,\s*([^)]+)\)/i
483 );
484 if (minmaxMatch && minmaxMatch[1] && minmaxMatch[2]) {
485 const minValue = minmaxMatch[1].trim();
486 const maxValue = minmaxMatch[2].trim();
487 return {
488 kind: 'minmax',
489 value: `minmax(${minValue}, ${maxValue})`,
490 };
491 }
492 return null;
493 };
494 // wp-container-core-group-is-layout-* クラスを持つ要素を検索
495 // まずグループブロック自体をチェック
496 let containerElement = null;
497 let containerClassName = null;
498 const groupClasses = Array.from(group.classList);
499 const containerClass = groupClasses.find((cls) =>
500 cls.includes('wp-container-core-group-is-layout-')
501 );
502
503 if (containerClass) {
504 containerElement = group;
505 containerClassName = containerClass;
506 } else {
507 // グループブロックの子要素を検索
508 containerElement = group.querySelector(
509 '[class*="wp-container-core-group-is-layout-"]'
510 );
511 if (containerElement) {
512 const containerClasses = Array.from(containerElement.classList);
513 containerClassName = containerClasses.find((cls) =>
514 cls.includes('wp-container-core-group-is-layout-')
515 );
516 }
517 }
518
519 // スタイルシートから直接値を取得
520 if (containerClassName) {
521 // すべてのスタイルシートを検索
522 for (let i = 0; i < document.styleSheets.length; i++) {
523 try {
524 const styleSheet = document.styleSheets[i];
525 const rules = styleSheet.cssRules || styleSheet.rules;
526 if (!rules) {
527 continue;
528 }
529
530 for (let j = 0; j < rules.length; j++) {
531 const rule = rules[j];
532 if (
533 rule.selectorText &&
534 rule.selectorText.includes(containerClassName)
535 ) {
536 const gridTemplateColumns =
537 rule.style.gridTemplateColumns;
538
539 const extracted =
540 extractMinimumColumnWidth(gridTemplateColumns);
541 if (extracted) {
542 return extracted;
543 }
544 }
545 }
546 } catch (e) {
547 // クロスオリジンのスタイルシートなどでエラーが発生する可能性がある
548 // エラーは無視して次のスタイルシートをチェック
549 }
550 }
551 }
552
553 // デフォルト値(WordPressのデフォルトは12rem)
554 return { kind: 'min', value: '12rem' };
555 }
556
557 /**
558 * ブレークポイントに応じてグリッドレイアウトの横スクロールを適用するかどうかを判定
559 * @param {string} breakpoint ブレークポイント
560 * @return {boolean} グリッドレイアウトの横スクロールを適用するかどうか
561 */
562 function shouldApplyGridScrollableByBreakpoint(breakpoint) {
563 if (!breakpoint) {
564 return false;
565 }
566
567 const windowWidth = window.innerWidth;
568 const mobileBreakpoint = 575.98;
569 const tabletBreakpoint = 991.98;
570
571 // PCブレークポイントの場合:常に適用
572 if (breakpoint === 'group-scrollable-pc') {
573 return true;
574 }
575
576 // Tabletブレークポイントの場合:991.98px以下の場合のみ適用
577 if (breakpoint === 'group-scrollable-tablet') {
578 return windowWidth <= tabletBreakpoint;
579 }
580
581 // Mobileブレークポイントの場合:575.98px以下の場合のみ適用
582 if (breakpoint === 'group-scrollable-mobile') {
583 return windowWidth <= mobileBreakpoint;
584 }
585
586 return false;
587 }
588
589 /**
590 * グリッドレイアウトの横スクロール設定を適用
591 * @param {HTMLElement} group グループブロック要素
592 * @param {string} breakpoint ブレークポイント
593 */
594 function applyGridScrollableStyleWithBreakpoint(group, breakpoint) {
595 if (!group.classList.contains('is-layout-grid')) {
596 return;
597 }
598
599 // ブレークポイントに応じて適用するかどうかを判定
600 if (!shouldApplyGridScrollableByBreakpoint(breakpoint)) {
601 // 適用しない場合は、設定を削除
602 group.style.gridTemplateColumns = '';
603 group.style.gridAutoColumns = '';
604 return;
605 }
606
607 const minimumColumnWidth = getMinimumColumnWidth(group);
608 if (minimumColumnWidth) {
609 // grid-template-columnsをnoneにして、grid-auto-columnsを設定
610 group.style.gridTemplateColumns = 'none';
611 if (minimumColumnWidth.kind === 'repeat-fr') {
612 group.style.gridAutoColumns = `calc(100% / ${minimumColumnWidth.value})`;
613 } else if (minimumColumnWidth.kind === 'minmax') {
614 group.style.gridAutoColumns = minimumColumnWidth.value;
615 } else {
616 group.style.gridAutoColumns = `minmax(min(${minimumColumnWidth.value}, 100%), max-content)`;
617 }
618 } else {
619 group.style.gridTemplateColumns = '';
620 group.style.gridAutoColumns = '';
621 }
622 }
623
624 function applyNestedGridScrollableStyle(group) {
625 if (
626 !group.classList.contains('wp-block-group') ||
627 !group.classList.contains('is-style-vk-group-scrollable')
628 ) {
629 return;
630 }
631 const breakpoint = group.getAttribute('data-scroll-breakpoint');
632 if (!breakpoint) {
633 return;
634 }
635 Array.from(group.children).forEach((child) => {
636 if (
637 child.classList &&
638 child.classList.contains('wp-block-group') &&
639 child.classList.contains('is-layout-grid')
640 ) {
641 applyGridScrollableStyleWithBreakpoint(child, breakpoint);
642 }
643 });
644 }
645
646 /**
647 * 初期化処理
648 */
649 function init() {
650 const scrollableGroups = document.querySelectorAll(
651 '.wp-block-group.is-style-vk-group-scrollable'
652 );
653 scrollableGroups.forEach((group) => {
654 applyNestedGridScrollableStyle(group);
655 });
656
657 // 既存のグループブロックをIntersection Observerで監視(表示領域に�
658 �った時だけ処理)
659 // data-table-mode="true"のもののみ処理することでパフォーマンスを向上
660 const existingGroups = document.querySelectorAll(
661 '.wp-block-group.is-style-vk-group-scrollable[data-table-mode="true"]'
662 );
663 existingGroups.forEach((group) => {
664 if (!intersectionObservedGroups.has(group)) {
665 intersectionObserver.observe(group);
666 intersectionObservedGroups.add(group);
667 }
668 });
669
670 // data-table-modeがtrueでないスクロール可能なグループブロックからmin-widthを削除
671 const nonTableModeGroups = document.querySelectorAll(
672 '.wp-block-group.is-style-vk-group-scrollable:not([data-table-mode="true"])'
673 );
674 nonTableModeGroups.forEach((group) => {
675 removeMinWidthFromColumns(group);
676 });
677
678 // document.bodyを監視して、新しく追加されたグループブロックを検出
679 checkNewGroupsObserver.observe(document.body, {
680 childList: true,
681 subtree: true,
682 });
683 }
684
685 // DOMContentLoaded時に初期化
686 if (document.readyState === 'loading') {
687 document.addEventListener('DOMContentLoaded', init);
688 } else {
689 init();
690 }
691
692 /**
693 * 要素が可視領域�
694 にあるかチェック
695 * @param {HTMLElement} element - チェック対象の要素
696 * @return {boolean} 可視領域�
697 にある場合true
698 */
699 function isElementVisible(element) {
700 const rect = element.getBoundingClientRect();
701 return (
702 rect.top < window.innerHeight &&
703 rect.bottom > 0 &&
704 rect.left < window.innerWidth &&
705 rect.right > 0
706 );
707 }
708
709 // リサイズ時にも再計算(可視領域�
710 のみ、パフォーマンス向上)
711 let resizeTimer;
712 window.addEventListener('resize', () => {
713 clearTimeout(resizeTimer);
714 resizeTimer = setTimeout(() => {
715 const scrollableGroups = document.querySelectorAll(
716 '.wp-block-group.is-style-vk-group-scrollable'
717 );
718 scrollableGroups.forEach((group) => {
719 if (isElementVisible(group)) {
720 applyNestedGridScrollableStyle(group);
721 }
722 });
723
724 // 可視領域�
725 のテーブルモードが有効なグループブロックのみを処理
726 const scrollableTableGroups = document.querySelectorAll(
727 '.wp-block-group.is-style-vk-group-scrollable[data-table-mode="true"]'
728 );
729 scrollableTableGroups.forEach((group) => {
730 if (isElementVisible(group)) {
731 requestAnimationFrame(() => {
732 // ブレークポイントに応じてテーブルモードを適用するかどうかを判定
733 if (shouldApplyTableMode(group)) {
734 processGroupBlock(group);
735 } else {
736 // テーブルモードを適用しない場合は、min-widthを削除
737 removeMinWidthFromColumns(group);
738 }
739 });
740 }
741 });
742
743 // data-table-modeがtrueでないスクロール可能なグループブロックからmin-widthを削除
744 const nonTableModeGroups = document.querySelectorAll(
745 '.wp-block-group.is-style-vk-group-scrollable:not([data-table-mode="true"])'
746 );
747 nonTableModeGroups.forEach((group) => {
748 if (isElementVisible(group)) {
749 removeMinWidthFromColumns(group);
750 }
751 });
752 }, 250);
753 });
754
755 // カスタムイベントもサポート(明示的に呼び出したい場合、同期的に処理)
756 window.addEventListener('vk-blocks-group-scrollable-update', () => {
757 equalizeColumnWidths();
758 });
759 })();
760