itmar-block-packages
Version:
Common React components for WordPress custom blocks, now fully TypeScript-ready.
243 lines (239 loc) • 11.2 kB
JavaScript
;
var blockStore = require('./blockStore.js');
var blocks = require('@wordpress/blocks');
var element = require('@wordpress/element');
var data = require('@wordpress/data');
const createBlockAttr = (selectedField, pluginOption) => {
// 3. blockAttributes を Record<string, any> とすることで、
// ブロックごとに異なるプロパティ(className, url, slideBlocksなど)を持てるようにします
let blockAttributes = {};
const pluginUrl = window[pluginOption]?.plugin_url || "";
switch (selectedField.block) {
case "itmar/design-title":
const block_class = selectedField.key.startsWith("tax_")
? selectedField.key
: `sp_field_${selectedField.key}`;
blockAttributes = {
className: block_class,
headingContent: `[${selectedField.label}]`,
};
break;
case "core/paragraph":
blockAttributes = {
className: `itmar_ex_block sp_field_${selectedField.key}`,
content: `[${selectedField.label}]`,
};
break;
case "core/image":
blockAttributes = {
className: `itmar_ex_block sp_field_${selectedField.key}`,
url: `${pluginUrl}/assets/image/main_sample.png`,
};
break;
case "itmar/slide-mv":
const spaceAttributes = {
margin_val: {
type: "object",
default: {
top: "0em",
left: "0em",
bottom: "0em",
right: "0em",
},
},
padding_val: {
type: "object",
default: {
top: "0em",
left: "0em",
bottom: "0em",
right: "0em",
},
},
};
const imageBlock = blocks.createBlock("core/image", {
className: "itmar_ex_block",
url: `${pluginUrl}/assets/image/slide_sample.png`,
...spaceAttributes,
});
//Design Blockの初期設定を取得
const blockType = blocks.getBlockType("itmar/design-group");
const defaultValBase = blockType?.attributes?.default_val?.default ?? {};
const mobileValBase = blockType?.attributes?.mobile_val?.default ?? {};
const slideBlock = blocks.createBlock("itmar/design-group", {
default_val: {
...defaultValBase,
width_val: "fit",
},
mobile_val: {
...mobileValBase,
width_val: "fit",
},
}, [imageBlock]);
//slideBlock をシリアライズ
const serializedSlide = blockStore.serializeBlockTree(slideBlock);
// 同じスライドブロックを5つ複製(独立したブロックとして)
const slideBlocks = Array.from({ length: 5 }, () => blockStore.createBlockTree(serializedSlide));
//子ブロック付きで返す
blockAttributes = {
attributes: { className: `sp_field_${selectedField.key}` },
slideBlocks: slideBlocks,
};
break;
default:
blockAttributes = {
className: `sp_field_${selectedField.key}`,
headingContent: `[${selectedField.label}]`,
};
}
return blockAttributes;
};
const normalizeDispAttributeArray = (items = [], length) => {
const next = [...items];
if (next.length < length) {
return [
...next,
...Array.from({ length: length - next.length }, () => ({})),
];
}
return next.slice(0, length);
};
const normalizeBlockSnapshot = (value) => {
if (Array.isArray(value)) {
return value.map(normalizeBlockSnapshot);
}
if (!value || typeof value !== "object") {
return value;
}
const { clientId, isValid, originalContent, validationIssues, ...rest } = value;
return Object.fromEntries(Object.entries(rest).map(([key, entryValue]) => [
key,
normalizeBlockSnapshot(entryValue),
]));
};
const getBlocksSnapshot = (blocks = []) => JSON.stringify(blocks.map((block) => normalizeBlockSnapshot(blockStore.serializeBlockTree(block))));
//表示フィールド変更によるインナーブロックの再構成
const useRebuildChangeField = (dispAttributeArray, // 外部から渡される配列
selectedFields, pickupType, dispTaxonomies, sectionCount, domType, clientId, insertId, pluginOption, onRebuildBlocks) => {
// dispatch関数を取得
const { replaceInnerBlocks } = data.useDispatch("core/block-editor");
const pickupBlock = data.useSelect((select) => select("core/block-editor").getBlock(clientId), [clientId]);
const insertBlock = data.useSelect((select) => select("core/block-editor").getBlock(insertId), [insertId]);
element.useEffect(() => {
//dispAttributeArray の個数調整
const normalizedDispAttributeArray = normalizeDispAttributeArray(dispAttributeArray, sectionCount);
// インナーブロックに差し込むブロック配列を生成
const blocksArray = normalizedDispAttributeArray.map((dispAttribute, unit_index) => {
// blocksAttributesArray属性で登録されたブロックのclassName一覧(sp_field_xxx を拾う)
const allBlocks = Array.isArray(dispAttribute.innerBlocks)
? blockStore.flattenBlocks(dispAttribute.innerBlocks) //階層になったブロックを平坦化
: [];
const existingKeys = allBlocks
.map((block) => block.attributes?.className)
.filter(Boolean)
.map((cls) => {
// sp_field_◯◯ か tax_◯◯ のどちらかにマッチ
//const match = cls.match(/sp_field_([\w-]+)|(tax_[\w-]+)/);
const match = cls.match(/sp_field_([^\s]+)|(tax_[^\s]+)/);
if (!match)
return null;
// match[1] があれば sp_field_ のほう → プレフィックス除去
// match[2] があれば tax_ のほう → そのまま返す
return match[1] ?? match[2];
})
.filter(Boolean);
// dispTaxonomies からオブジェクトを作って selectedFieldsに加えてnewSelectedFieldsを生成
const newSelectedFields = [
...selectedFields,
...dispTaxonomies.map((tax) => ({
key: `tax_${tax}`, // 例: "tax_category"
label: tax, // 例: "category"
block: "itmar/design-title",
})),
];
// newSelectedFieldsのうち未挿入のものだけ追加
const autoGeneratedBlocks = newSelectedFields
.filter((field) => !existingKeys.includes(field.key))
.map((field) => {
const attr = createBlockAttr(field, pluginOption);
const blockName = field.block;
const blockAttributes = attr?.attributes ?? attr;
const innerBlocks = Array.isArray(attr?.slideBlocks)
? attr.slideBlocks
: [];
return blocks.createBlock(blockName, blockAttributes, innerBlocks);
});
// blocksAttributesArray属性で登録されたブロックの再構築
const selectedKeys = selectedFields.map((f) => f.key);
const filterBlocksRecursively = (blocks) => {
return blocks
.map((block) => {
const className = block.attributes?.className || "";
// 1. まず sp_field_ のパターンを探す
//let match = className.match(/sp_field_([a-zA-Z0-9_]+)/);
let match = className.match(/sp_field_([^\s]+)/);
// 2. 見つからなければ tax_○○ をチェック
if (className.startsWith("tax_")) {
const name = className.slice("tax_".length); // "tax_category" → "category"
// dispTaxonomies に含まれないものだけ有効とする
if (!dispTaxonomies.includes(name)) {
// 正規表現の match 結果っぽい形の配列を自分で作る
// [0] に全体一致, [1] にグループ1 というイメージ
match = [`tax_${name}`, name];
}
}
// 再帰的に innerBlocks をフィルタ
const filteredInner = block.innerBlocks
? filterBlocksRecursively(block.innerBlocks)
: [];
const isAutoGenerated = !!match;
const keep = !isAutoGenerated || selectedKeys.includes(match[1]);
if (!keep)
return null;
// 構造を復元して返す
return {
...block,
innerBlocks: filteredInner,
};
})
.filter(Boolean); // null を除去
};
const userBlocks = Array.isArray(dispAttribute.innerBlocks)
? filterBlocksRecursively(dispAttribute.innerBlocks).map(blockStore.createBlockTree)
: [];
// autoGenerated(selectedFields) + userBlocks を合成
const innerBlocks = [...userBlocks, ...autoGeneratedBlocks];
const ret = blocks.createBlock("itmar/design-group", {
...dispAttribute.attributes,
className: `unit_design_${unit_index}`,
domType: domType,
}, innerBlocks);
return ret;
});
//挿入するブロックと自身のブロックが異なる場合(slide-mvにデータを入れる場合)
if (insertId !== clientId && pickupBlock) {
blocksArray.push(pickupBlock);
}
const currentBlocks = insertBlock?.innerBlocks || [];
const currentSnapshot = getBlocksSnapshot(currentBlocks);
const nextSnapshot = getBlocksSnapshot(blocksArray);
if (currentSnapshot === nextSnapshot) {
return;
}
onRebuildBlocks?.(blocksArray);
// 既存のインナーブロックを一括置換
replaceInnerBlocks(insertId, blocksArray, false);
}, [
selectedFields,
pickupType,
dispTaxonomies,
sectionCount,
domType,
clientId,
insertId,
pluginOption,
replaceInnerBlocks,
]);
};
exports.useRebuildChangeField = useRebuildChangeField;
//# sourceMappingURL=BrockInserter.js.map