I am creating a Query Loop block that retrieves post type, similar to Gutenberg's, but for various reasons I need to create one.
Currently the code I am using is as follows and it works, but I would prefer the various internal blocks to use their own edit.js file.
Currently they use what is given in this file instead.
edit.js
/**
* WordPress dependencies
*/
import { __ } from "@wordpress/i18n";
import {
useBlockProps,
useInnerBlocksProps,
store as blockEditorStore,
} from "@wordpress/block-editor";
import { useSelect } from "@wordpress/data";
import { store as coreStore } from "@wordpress/core-data";
import QueryInspectorControls from "./inspector-controls";
import React from "react";
/**
* Edit component
*
* @param {Object} props
* @param {Object} props.attributes
* @param {Object} props.setAttributes
* @param {string} props.clientId
*/
export default function Edit({ attributes, setAttributes, clientId }) {
const { query } = attributes;
const blockProps = useBlockProps();
// Get Posts
const { posts, isResolving, templateBlock } = useSelect(
(select) => {
const { getEntityRecords, isResolving } = select(coreStore);
const { getBlocks } = select(blockEditorStore);
// Find block
const innerBlocks = getBlocks(clientId);
const template = innerBlocks.find(
(block) => block.name === "prismatic/post-type-template",
);
return {
posts: getEntityRecords("postType", query.postType, {
per_page: query.perPage,
offset: query.offset,
orderby: query.orderBy,
order: query.order,
_embed: true,
...(query.authors?.length && {
author: query.authors.join(","),
}),
...(query.search?.length && {
search: query.search.join(" "),
}),
...(query.postType === "page" &&
query.parents?.length && {
parent: query.parents[0],
}),
...(query.taxQuery && {
tax_query: Object.entries(query.taxQuery)
.filter(([key]) => !key.includes("_relation"))
.map(([taxonomy, terms]) => ({
taxonomy,
terms,
operator:
query.taxQuery[`${taxonomy}_relation`] === "AND"
? "AND"
: "IN",
})),
}),
}),
isResolving: isResolving("getEntityRecords", [
"postType",
query.postType,
]),
templateBlock: template,
};
},
[query, clientId],
);
// Template
const innerBlocksProps = useInnerBlocksProps(
{
style: { display: "none" },
},
{
template: [["prismatic/post-type-template"]],
allowedBlocks: ["prismatic/post-type-template"],
},
);
// Item rendering
function QueryPostTypeItem({ post, templateBlock }) {
if (!templateBlock || !post) return null;
return (
<div
className={templateBlock.attributes.className || ""}
data-id={post.id}
>
{templateBlock.innerBlocks.map((block) => {
if (block.name === "prismatic/post-type-featured-image") {
const imageUrl =
post._embedded?.["wp:featuredmedia"]?.[0]?.source_url;
const imageAlt =
post._embedded?.["wp:featuredmedia"]?.[0]?.alt_text || "";
if (!imageUrl) return null;
return (
<img
key={block.clientId}
src={imageUrl}
alt={imageAlt}
className={block.attributes.className || ""}
/>
);
}
if (block.name === "prismatic/post-type-title") {
const TagName = block.attributes.tagName || "p";
return (
<TagName
key={block.clientId}
className={block.attributes.className || ""}
>
{post.title.rendered}
</TagName>
);
}
if (block.name === "prismatic/post-type-excerpt") {
const excerpt = post.excerpt?.rendered || "";
const words = block.attributes.words || 25;
const strippedExcerpt = excerpt.replace(/<\/?[^>]+(>|$)/g, "");
const trimmedExcerpt =
strippedExcerpt.split(" ").slice(0, words).join(" ") + "...";
return (
<p
key={block.clientId}
className={block.attributes.className || ""}
dangerouslySetInnerHTML={{ __html: trimmedExcerpt }}
/>
);
}
if (block.name === "prismatic/post-type-button") {
const text =
block.attributes.text || __("Leggi tutto", "prismatic");
return (
<a
key={block.clientId}
className={block.attributes.className || ""}
href={post.link}
>
{text}
</a>
);
}
return null;
})}
</div>
);
}
// Loop
function QueryPostTypeLoop({ posts, templateBlock }) {
if (!posts) return null;
return (
<>
{posts.map((post) => (
<QueryPostTypeItem
key={post.id}
post={post}
templateBlock={templateBlock}
/>
))}
</>
);
}
return (
<>
<QueryInspectorControls
attributes={attributes}
setAttributes={setAttributes}
/>
<div {...blockProps}>
{isResolving ? (
<p>{__("Loading...", "prismatic")}</p>
) : !posts?.length ? (
<p>{__("No content.", "prismatic")}</p>
) : (
<>
<div {...innerBlocksProps} />
<QueryPostTypeLoop posts={posts} templateBlock={templateBlock} />
</>
)}
</div>
</>
);
}