itmar-block-packages 1.9.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ import {
6
6
  ToggleControl,
7
7
  } from "@wordpress/components";
8
8
  import apiFetch from "@wordpress/api-fetch";
9
+ import { addQueryArgs } from "@wordpress/url";
9
10
 
10
11
  //const _ = require("lodash");
11
12
 
@@ -68,8 +69,9 @@ const ChoiceControl = (props) => {
68
69
  const fetchData = async () => {
69
70
  try {
70
71
  const fetchChoices = await fetchFunction(selectedSlug);
71
-
72
72
  setChoices(fetchChoices);
73
+ //指定の投稿タイプに含まれないフィールドを削除する
74
+ pruneChoiceItemsByObjectKeys(fetchChoices[0], choiceItems);
73
75
  } catch (error) {
74
76
  console.error("Error fetching data:", error.message);
75
77
  }
@@ -90,22 +92,78 @@ const ChoiceControl = (props) => {
90
92
  }
91
93
  return setItems;
92
94
  };
95
+ /**
96
+ * dataObj のキー一覧を「choiceItems と比較する形」に変換して Set で返す
97
+ * - 通常キー: そのまま
98
+ * - acf / meta: 子キーに `${parent}_` を付けたもの(例: acf_relate_url, meta_footnotes)
99
+ */
100
+ function buildComparableKeySet(dataObj) {
101
+ const keySet = new Set();
102
+
103
+ if (!dataObj || typeof dataObj !== "object") return keySet;
104
+
105
+ for (const [key, val] of Object.entries(dataObj)) {
106
+ if (
107
+ (key === "acf" || key === "meta") &&
108
+ val &&
109
+ typeof val === "object" &&
110
+ !Array.isArray(val)
111
+ ) {
112
+ for (const childKey of Object.keys(val)) {
113
+ keySet.add(`${key}_${childKey}`);
114
+ }
115
+ continue;
116
+ }
117
+
118
+ keySet.add(key);
119
+ }
120
+
121
+ return keySet;
122
+ }
123
+
124
+ /**
125
+ * choiceItems を dataObj のキーに合わせて削除する
126
+ * - choiceItems が string 配列でも、{value: "..."} の配列でも動くようにしてあります
127
+ */
128
+ function pruneChoiceItemsByObjectKeys(dataObj, choiceItems) {
129
+ const validKeys = buildComparableKeySet(dataObj);
130
+
131
+ const getItemKey = (item) => {
132
+ if (typeof item === "string") return item;
133
+ if (item && typeof item === "object")
134
+ return item.value ?? item.key ?? item.name ?? "";
135
+ return "";
136
+ };
137
+
138
+ const next = (choiceItems ?? []).filter((item) =>
139
+ validKeys.has(getItemKey(item))
140
+ );
141
+
142
+ // ★ 配列の参照はそのまま、中身だけ置き換える
143
+ choiceItems.splice(0, choiceItems.length, ...next);
144
+
145
+ return choiceItems; // 必要なら返す
146
+ }
93
147
 
94
148
  //階層化されたカスタムフィールドのフィールド名を表示する関数
95
149
  let groupLabel = "";
96
- const dispCustumFields = (obj, prefix = "", onChange) => {
150
+ const dispCustumFields = (obj, prefix = "", isImage = false, onChange) => {
97
151
  return Object.entries(obj).map(([key, value]) => {
98
152
  const fieldName = prefix ? `${prefix}.${key}` : key; //prefixはグループ名
99
153
 
100
154
  const fieldLabel = key.replace(/^(meta_|acf_)/, "");
101
-
102
- if (typeof value === "object" && value !== null) {
155
+ //オブジェクトであって配列でないものがグループと考える
156
+ if (
157
+ typeof value === "object" &&
158
+ !Array.isArray(value) &&
159
+ value !== null
160
+ ) {
103
161
  groupLabel = `${fieldLabel}.`;
104
162
  return (
105
163
  <div className="group_area">
106
164
  <div className="group_label">{fieldLabel}</div>
107
165
  <div key={fieldName} className="field_group">
108
- {dispCustumFields(value, fieldName, onChange)}
166
+ {dispCustumFields(value, fieldName, isImage, onChange)}
109
167
  </div>
110
168
  </div>
111
169
  );
@@ -117,6 +175,7 @@ const ChoiceControl = (props) => {
117
175
  { value: "itmar/design-title", label: "itmar/design-title" },
118
176
  { value: "core/paragraph", label: "core/paragraph" },
119
177
  { value: "core/image", label: "core/image" },
178
+ { value: "itmar/slide-mv", label: "itmar/slide-mv" },
120
179
  ];
121
180
  return (
122
181
  <div className="itmar_custom_field_set">
@@ -136,20 +195,23 @@ const ChoiceControl = (props) => {
136
195
  props.onChange(newChoiceFields);
137
196
  }}
138
197
  />
139
- <ComboboxControl
140
- options={options}
141
- value={
142
- blockMap[`${prefix ? groupLabel : ""}${fieldLabel}`] ||
143
- "itmar/design-title"
144
- }
145
- onChange={(newValue) => {
146
- const fieldKey = prefix
147
- ? `${groupLabel}${fieldLabel}`
148
- : `${fieldLabel}`;
149
- const newBlockMap = { ...blockMap, [fieldKey]: newValue };
150
- props.onBlockMapChange(newBlockMap);
151
- }}
152
- />
198
+ {!isImage && (
199
+ <ComboboxControl
200
+ options={options}
201
+ value={
202
+ //blockMap[`${prefix ? groupLabel : ""}${key}`] || "itmar/design-title"
203
+ blockMap[`${prefix ? prefix + "." : ""}${key}`] ||
204
+ "itmar/design-title"
205
+ }
206
+ onChange={(newValue) => {
207
+ //const fieldKey = prefix ? `${groupLabel}${key}` : `${key}`;
208
+ const fieldKey = prefix ? `${prefix}.${key}` : `${key}`;
209
+
210
+ const newBlockMap = { ...blockMap, [fieldKey]: newValue };
211
+ props.onBlockMapChange(newBlockMap);
212
+ }}
213
+ />
214
+ )}
153
215
  </div>
154
216
  );
155
217
  }
@@ -240,6 +302,23 @@ const ChoiceControl = (props) => {
240
302
  }}
241
303
  />
242
304
  )}
305
+ {choice.content && (
306
+ <ToggleControl
307
+ className="field_choice"
308
+ label={__("Content", "block-collections")}
309
+ checked={choiceItems.some(
310
+ (choiceField) => choiceField === "content"
311
+ )}
312
+ onChange={(checked) => {
313
+ const newChoiceFields = handleChoiceChange(
314
+ checked,
315
+ "content",
316
+ choiceItems
317
+ );
318
+ props.onChange(newChoiceFields);
319
+ }}
320
+ />
321
+ )}
243
322
  {choice.date && (
244
323
  <ToggleControl
245
324
  className="field_choice"
@@ -343,6 +422,7 @@ const ChoiceControl = (props) => {
343
422
  </div>
344
423
  <div className="custom_field_area">
345
424
  {dispCustumFields({
425
+ // meta はそのまま
346
426
  ...Object.entries(choice.meta).reduce(
347
427
  (acc, [key, value]) => ({
348
428
  ...acc,
@@ -350,13 +430,16 @@ const ChoiceControl = (props) => {
350
430
  }),
351
431
  {}
352
432
  ),
353
- ...Object.entries(choice.acf).reduce(
354
- (acc, [key, value]) => ({
355
- ...acc,
356
- [`acf_${key}`]: value,
357
- }),
358
- {}
359
- ),
433
+ // acf は「同名で _source があるもののベース側を除く」
434
+ ...Object.entries(choice.acf)
435
+ .filter(([key]) => !key.endsWith("_source"))
436
+ .reduce(
437
+ (acc, [key, value]) => ({
438
+ ...acc,
439
+ [`acf_${key}`]: value,
440
+ }),
441
+ {}
442
+ ),
360
443
  })}
361
444
  </div>
362
445
  </>
@@ -364,16 +447,124 @@ const ChoiceControl = (props) => {
364
447
  </div>
365
448
  );
366
449
  })}
450
+ {type === "imgField" &&
451
+ choices.map((choice, index) => {
452
+ //metaの対象カスタムフィールドが含まれるかのフラグ
453
+ const metaFlg =
454
+ choice.meta &&
455
+ !Object.keys(choice.meta).every(
456
+ (key) => key === "_acf_changed" || key === "footnotes"
457
+ );
458
+ //acfの対象カスタムフィールドが含まれるかのフラグ
459
+ const acfFlg =
460
+ choice.acf &&
461
+ typeof choice.acf === "object" &&
462
+ !Array.isArray(choice.acf);
463
+
464
+ return (
465
+ <div key={index} className="field_section">
466
+ {choice.content && (
467
+ <ToggleControl
468
+ className="field_choice"
469
+ label={__("Content", "block-collections")}
470
+ checked={choiceItems.some(
471
+ (choiceField) => choiceField === "content"
472
+ )}
473
+ onChange={(checked) => {
474
+ const newChoiceFields = handleChoiceChange(
475
+ checked,
476
+ "content",
477
+ choiceItems
478
+ );
479
+ props.onChange(newChoiceFields);
480
+ }}
481
+ />
482
+ )}
483
+ {(choice.featured_media || choice.featured_media === 0) && (
484
+ <ToggleControl
485
+ className="field_choice"
486
+ label={__("Featured Image", "block-collections")}
487
+ checked={choiceItems.some(
488
+ (choiceField) => choiceField === "featured_media"
489
+ )}
490
+ onChange={(checked) => {
491
+ const newChoiceFields = handleChoiceChange(
492
+ checked,
493
+ "featured_media",
494
+ choiceItems
495
+ );
496
+ props.onChange(newChoiceFields);
497
+ }}
498
+ />
499
+ )}
500
+
501
+ {(metaFlg || acfFlg) && (
502
+ <>
503
+ <div className="custom_field_label">
504
+ {__("Custom Field", "block-collections")}
505
+ </div>
506
+ <div className="custom_field_area">
507
+ {dispCustumFields(
508
+ {
509
+ // meta はそのまま
510
+ ...Object.entries(choice.meta).reduce(
511
+ (acc, [key, value]) => ({
512
+ ...acc,
513
+ [`meta_${key}`]: value,
514
+ }),
515
+ {}
516
+ ),
517
+ // acf は「同名で _source があるもののベース側を除く」
518
+ ...Object.entries(choice.acf)
519
+ .filter(([key]) => !key.endsWith("_source"))
520
+ .reduce(
521
+ (acc, [key, value]) => ({
522
+ ...acc,
523
+ [`acf_${key}`]: value,
524
+ }),
525
+ {}
526
+ ),
527
+ },
528
+ "",
529
+ true
530
+ )}
531
+ </div>
532
+ </>
533
+ )}
534
+ </div>
535
+ );
536
+ })}
367
537
  </div>
368
538
  );
369
539
  };
370
540
 
371
541
  //固定ページ取得RestAPI関数
372
542
  export const fetchPagesOptions = async (home_url) => {
373
- const pages = await apiFetch({ path: "/wp/v2/pages" });
374
- //ページIDが-1である要素をホーム要素として作成
375
- if (pages && !pages.some((page) => page.id === -1)) {
376
- pages.unshift({
543
+ const allPages = [];
544
+ let page = 1;
545
+
546
+ while (true) {
547
+ const items = await apiFetch({
548
+ path: addQueryArgs("/wp/v2/pages", {
549
+ status: "publish", // 公開のみ
550
+ per_page: 100, // 最大100
551
+ page,
552
+ // orderby: "title",
553
+ // order: "asc",
554
+ }),
555
+ });
556
+
557
+ allPages.push(...items);
558
+
559
+ // 100件未満ならこれが最後
560
+ if (!items || items.length < 100) break;
561
+
562
+ page++;
563
+ }
564
+
565
+ // ページIDが-1である要素をホーム要素として作成
566
+ if (!allPages.some((p) => p.id === -1)) {
567
+ allPages.unshift({
377
568
  id: -1,
378
569
  title: { rendered: "ホーム" },
379
570
  link: home_url,
@@ -381,14 +572,13 @@ export const fetchPagesOptions = async (home_url) => {
381
572
  });
382
573
  }
383
574
 
384
- const ret_pages = pages
385
- ? pages.map((page) => ({
386
- value: page.id,
387
- slug: page.slug,
388
- label: page.title.rendered,
389
- link: `${home_url}/${page.slug}`,
390
- }))
391
- : [];
575
+ const ret_pages = allPages.map((p) => ({
576
+ value: p.id,
577
+ slug: p.slug,
578
+ label: p.title?.rendered ?? "",
579
+ // 階層ページでも正しいURLになるようにRESTのlinkを優先
580
+ link: p.link || (p.slug ? `${home_url}/${p.slug}` : home_url),
581
+ }));
392
582
 
393
583
  return ret_pages;
394
584
  };
@@ -483,6 +673,7 @@ export const restFieldes = async (rest_base) => {
483
673
  //投稿データに以下のフィールドが含まれているかを調べる
484
674
  const selectedFields = [
485
675
  "title",
676
+ "content",
486
677
  "date",
487
678
  "excerpt",
488
679
  "featured_media",
@@ -495,6 +686,7 @@ export const restFieldes = async (rest_base) => {
495
686
  const response = await apiFetch({
496
687
  path: `/wp/v2/${rest_base}?_fields=${fieldsParam}&per_page=1&order=desc`,
497
688
  });
689
+
498
690
  return response;
499
691
  };
500
692