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