@wordpress/block-library
Version:
Block library for the WordPress editor.
286 lines (281 loc) • 9.24 kB
JavaScript
/**
* External dependencies
*/
import clsx from 'clsx';
/**
* WordPress dependencies
*/
import { memo, useMemo, useState } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { __, _x } from '@wordpress/i18n';
import { BlockControls, BlockContextProvider, __experimentalUseBlockPreview as useBlockPreview, useBlockProps, useInnerBlocksProps, store as blockEditorStore } from '@wordpress/block-editor';
import { Spinner, ToolbarGroup } from '@wordpress/components';
import { store as coreStore } from '@wordpress/core-data';
import { list, grid } from '@wordpress/icons';
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
const TEMPLATE = [['core/post-title'], ['core/post-date'], ['core/post-excerpt']];
function PostTemplateInnerBlocks({
classList
}) {
const innerBlocksProps = useInnerBlocksProps({
className: clsx('wp-block-post', classList)
}, {
template: TEMPLATE,
__unstableDisableLayoutClassNames: true
});
return /*#__PURE__*/_jsx("li", {
...innerBlocksProps
});
}
function PostTemplateBlockPreview({
blocks,
blockContextId,
classList,
isHidden,
setActiveBlockContextId
}) {
const blockPreviewProps = useBlockPreview({
blocks,
props: {
className: clsx('wp-block-post', classList)
}
});
const handleOnClick = () => {
setActiveBlockContextId(blockContextId);
};
const style = {
display: isHidden ? 'none' : undefined
};
return /*#__PURE__*/_jsx("li", {
...blockPreviewProps,
tabIndex: 0
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-to-interactive-role
,
role: "button",
onClick: handleOnClick,
onKeyPress: handleOnClick,
style: style
});
}
const MemoizedPostTemplateBlockPreview = memo(PostTemplateBlockPreview);
export default function PostTemplateEdit({
setAttributes,
clientId,
context: {
query: {
perPage,
offset = 0,
postType,
order,
orderBy,
author,
search,
exclude,
sticky,
inherit,
taxQuery,
parents,
pages,
format,
// We gather extra query args to pass to the REST API call.
// This way extenders of Query Loop can add their own query args,
// and have accurate previews in the editor.
// Noting though that these args should either be supported by the
// REST API or be handled by custom REST filters like `rest_{$this->post_type}_query`.
...restQueryArgs
} = {},
templateSlug,
previewPostType
},
attributes: {
layout
},
__unstableLayoutClassNames
}) {
const {
type: layoutType,
columnCount = 3
} = layout || {};
const [activeBlockContextId, setActiveBlockContextId] = useState();
const {
posts,
blocks
} = useSelect(select => {
const {
getEntityRecords,
getTaxonomies
} = select(coreStore);
const {
getBlocks
} = select(blockEditorStore);
const templateCategory = inherit && templateSlug?.startsWith('category-') && getEntityRecords('taxonomy', 'category', {
context: 'view',
per_page: 1,
_fields: ['id'],
slug: templateSlug.replace('category-', '')
});
const templateTag = inherit && templateSlug?.startsWith('tag-') && getEntityRecords('taxonomy', 'post_tag', {
context: 'view',
per_page: 1,
_fields: ['id'],
slug: templateSlug.replace('tag-', '')
});
const query = {
offset: offset || 0,
order,
orderby: orderBy
};
// There is no need to build the taxQuery if we inherit.
if (taxQuery && !inherit) {
const taxonomies = getTaxonomies({
type: postType,
per_page: -1,
context: 'view'
});
// We have to build the tax query for the REST API and use as
// keys the taxonomies `rest_base` with the `term ids` as values.
const builtTaxQuery = Object.entries(taxQuery).reduce((accumulator, [taxonomySlug, terms]) => {
const taxonomy = taxonomies?.find(({
slug
}) => slug === taxonomySlug);
if (taxonomy?.rest_base) {
accumulator[taxonomy?.rest_base] = terms;
}
return accumulator;
}, {});
if (!!Object.keys(builtTaxQuery).length) {
Object.assign(query, builtTaxQuery);
}
}
if (perPage) {
query.per_page = perPage;
}
if (author) {
query.author = author;
}
if (search) {
query.search = search;
}
if (exclude?.length) {
query.exclude = exclude;
}
if (parents?.length) {
query.parent = parents;
}
if (format?.length) {
query.format = format;
}
/*
* Handle cases where sticky is set to `exclude` or `only`.
* Which works as a `post__in/post__not_in` query for sticky posts.
*/
if (['exclude', 'only'].includes(sticky)) {
query.sticky = sticky === 'only';
}
// Empty string represents the default behavior of including sticky posts.
if (['', 'ignore'].includes(sticky)) {
// Remove any leftover sticky query parameter.
delete query.sticky;
query.ignore_sticky = sticky === 'ignore';
}
// If `inherit` is truthy, adjust conditionally the query to create a better preview.
let currentPostType = postType;
if (inherit) {
// Change the post-type if needed.
if (templateSlug?.startsWith('archive-')) {
query.postType = templateSlug.replace('archive-', '');
currentPostType = query.postType;
} else if (templateCategory) {
query.categories = templateCategory[0]?.id;
} else if (templateTag) {
query.tags = templateTag[0]?.id;
} else if (templateSlug?.startsWith('taxonomy-post_format')) {
// Get the post format slug from the template slug by removing the prefix.
query.format = templateSlug.replace('taxonomy-post_format-post-format-', '');
}
}
// When we preview Query Loop blocks we should prefer the current
// block's postType, which is passed through block context.
const usedPostType = previewPostType || currentPostType;
return {
posts: getEntityRecords('postType', usedPostType, {
...query,
...restQueryArgs
}),
blocks: getBlocks(clientId)
};
}, [perPage, offset, order, orderBy, clientId, author, search, postType, exclude, sticky, inherit, templateSlug, taxQuery, parents, format, restQueryArgs, previewPostType]);
const blockContexts = useMemo(() => posts?.map(post => {
var _post$class_list;
return {
postType: post.type,
postId: post.id,
classList: (_post$class_list = post.class_list) !== null && _post$class_list !== void 0 ? _post$class_list : ''
};
}), [posts]);
const blockProps = useBlockProps({
className: clsx(__unstableLayoutClassNames, {
[`columns-${columnCount}`]: layoutType === 'grid' && columnCount // Ensure column count is flagged via classname for backwards compatibility.
})
});
if (!posts) {
return /*#__PURE__*/_jsx("p", {
...blockProps,
children: /*#__PURE__*/_jsx(Spinner, {})
});
}
if (!posts.length) {
return /*#__PURE__*/_jsxs("p", {
...blockProps,
children: [" ", __('No results found.')]
});
}
const setDisplayLayout = newDisplayLayout => setAttributes({
layout: {
...layout,
...newDisplayLayout
}
});
const displayLayoutControls = [{
icon: list,
title: _x('List view', 'Post template block display setting'),
onClick: () => setDisplayLayout({
type: 'default'
}),
isActive: layoutType === 'default' || layoutType === 'constrained'
}, {
icon: grid,
title: _x('Grid view', 'Post template block display setting'),
onClick: () => setDisplayLayout({
type: 'grid',
columnCount
}),
isActive: layoutType === 'grid'
}];
// To avoid flicker when switching active block contexts, a preview is rendered
// for each block context, but the preview for the active block context is hidden.
// This ensures that when it is displayed again, the cached rendering of the
// block preview is used, instead of having to re-render the preview from scratch.
return /*#__PURE__*/_jsxs(_Fragment, {
children: [/*#__PURE__*/_jsx(BlockControls, {
children: /*#__PURE__*/_jsx(ToolbarGroup, {
controls: displayLayoutControls
})
}), /*#__PURE__*/_jsx("ul", {
...blockProps,
children: blockContexts && blockContexts.map(blockContext => /*#__PURE__*/_jsxs(BlockContextProvider, {
value: blockContext,
children: [blockContext.postId === (activeBlockContextId || blockContexts[0]?.postId) ? /*#__PURE__*/_jsx(PostTemplateInnerBlocks, {
classList: blockContext.classList
}) : null, /*#__PURE__*/_jsx(MemoizedPostTemplateBlockPreview, {
blocks: blocks,
blockContextId: blockContext.postId,
classList: blockContext.classList,
setActiveBlockContextId: setActiveBlockContextId,
isHidden: blockContext.postId === (activeBlockContextId || blockContexts[0]?.postId)
})]
}, blockContext.postId))
})]
});
}
//# sourceMappingURL=edit.js.map