UNPKG

@wordpress/block-library

Version:
338 lines (316 loc) 10.5 kB
/** * External dependencies */ import { get } from 'lodash'; /** * WordPress dependencies */ import { useSelect } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; import { store as coreStore } from '@wordpress/core-data'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { decodeEntities } from '@wordpress/html-entities'; import { cloneBlock, store as blocksStore } from '@wordpress/blocks'; /** * Internal dependencies */ const { name: queryLoopName } = { $schema: "https://schemas.wp.org/trunk/block.json", apiVersion: 2, name: "core/query", title: "Query Loop", category: "theme", description: "An advanced block that allows displaying post types based on different query parameters and visual configurations.", textdomain: "default", attributes: { queryId: { type: "number" }, query: { type: "object", "default": { perPage: null, pages: 0, offset: 0, postType: "post", order: "desc", orderBy: "date", author: "", search: "", exclude: [], sticky: "", inherit: true, taxQuery: null, parents: [] } }, tagName: { type: "string", "default": "div" }, displayLayout: { type: "object", "default": { type: "list" } }, namespace: { type: "string" } }, providesContext: { queryId: "queryId", query: "query", displayLayout: "displayLayout" }, supports: { align: ["wide", "full"], html: false, color: { gradients: true, link: true, __experimentalDefaultControls: { background: true, text: true } }, __experimentalLayout: true }, editorStyle: "wp-block-query-editor" }; /** * @typedef IHasNameAndId * @property {string|number} id The entity's id. * @property {string} name The entity's name. */ /** * The object used in Query block that contains info and helper mappings * from an array of IHasNameAndId objects. * * @typedef {Object} QueryEntitiesInfo * @property {IHasNameAndId[]} entities The array of entities. * @property {Object<string, IHasNameAndId>} mapById Object mapping with the id as key and the entity as value. * @property {Object<string, IHasNameAndId>} mapByName Object mapping with the name as key and the entity as value. * @property {string[]} names Array with the entities' names. */ /** * Returns a helper object with mapping from Objects that implement * the `IHasNameAndId` interface. The returned object is used for * integration with `FormTokenField` component. * * @param {IHasNameAndId[]} entities The entities to extract of helper object. * @return {QueryEntitiesInfo} The object with the entities information. */ export const getEntitiesInfo = entities => { const mapping = entities === null || entities === void 0 ? void 0 : entities.reduce((accumulator, entity) => { const { mapById, mapByName, names } = accumulator; mapById[entity.id] = entity; mapByName[entity.name] = entity; names.push(entity.name); return accumulator; }, { mapById: {}, mapByName: {}, names: [] }); return { entities, ...mapping }; }; /** * Helper util to map records to add a `name` prop from a * provided path, in order to handle all entities in the same * fashion(implementing`IHasNameAndId` interface). * * @param {Object[]} entities The array of entities. * @param {string} path The path to map a `name` property from the entity. * @return {IHasNameAndId[]} An array of enitities that now implement the `IHasNameAndId` interface. */ export const mapToIHasNameAndId = (entities, path) => { return (entities || []).map(entity => ({ ...entity, name: decodeEntities(get(entity, path)) })); }; /** * Returns a helper object that contains: * 1. An `options` object from the available post types, to be passed to a `SelectControl`. * 2. A helper map with available taxonomies per post type. * * @return {Object} The helper object related to post types. */ export const usePostTypes = () => { const postTypes = useSelect(select => { var _getPostTypes; const { getPostTypes } = select(coreStore); const excludedPostTypes = ['attachment']; const filteredPostTypes = (_getPostTypes = getPostTypes({ per_page: -1 })) === null || _getPostTypes === void 0 ? void 0 : _getPostTypes.filter(_ref => { let { viewable, slug } = _ref; return viewable && !excludedPostTypes.includes(slug); }); return filteredPostTypes; }, []); const postTypesTaxonomiesMap = useMemo(() => { if (!(postTypes !== null && postTypes !== void 0 && postTypes.length)) return; return postTypes.reduce((accumulator, type) => { accumulator[type.slug] = type.taxonomies; return accumulator; }, {}); }, [postTypes]); const postTypesSelectOptions = useMemo(() => (postTypes || []).map(_ref2 => { let { labels, slug } = _ref2; return { label: labels.singular_name, value: slug }; }), [postTypes]); return { postTypesTaxonomiesMap, postTypesSelectOptions }; }; /** * Hook that returns the taxonomies associated with a specific post type. * * @param {string} postType The post type from which to retrieve the associated taxonomies. * @return {Object[]} An array of the associated taxonomies. */ export const useTaxonomies = postType => { const taxonomies = useSelect(select => { const { getTaxonomies } = select(coreStore); const filteredTaxonomies = getTaxonomies({ type: postType, per_page: -1, context: 'view' }); return filteredTaxonomies; }, [postType]); return taxonomies; }; /** * Hook that returns whether a specific post type is hierarchical. * * @param {string} postType The post type to check. * @return {boolean} Whether a specific post type is hierarchical. */ export function useIsPostTypeHierarchical(postType) { return useSelect(select => { const type = select(coreStore).getPostType(postType); return (type === null || type === void 0 ? void 0 : type.viewable) && (type === null || type === void 0 ? void 0 : type.hierarchical); }, [postType]); } /** * Hook that returns the query properties' names defined by the active * block variation, to determine which block's filters to show. * * @param {Object} attributes Block attributes. * @return {string[]} An array of the query attributes. */ export function useAllowedControls(attributes) { return useSelect(select => { var _select$getActiveBloc; return (_select$getActiveBloc = select(blocksStore).getActiveBlockVariation(queryLoopName, attributes)) === null || _select$getActiveBloc === void 0 ? void 0 : _select$getActiveBloc.allowControls; }, [attributes]); } export function isControlAllowed(allowedControls, key) { // Every controls is allowed if the list is not defined. if (!allowedControls) { return true; } return allowedControls.includes(key); } /** * Clones a pattern's blocks and then recurses over that list of blocks, * transforming them to retain some `query` attribute properties. * For now we retain the `postType` and `inherit` properties as they are * fundamental for the expected functionality of the block and don't affect * its design and presentation. * * Returns the cloned/transformed blocks and array of existing Query Loop * client ids for further manipulation, in order to avoid multiple recursions. * * @param {WPBlock[]} blocks The list of blocks to look through and transform(mutate). * @param {Record<string,*>} queryBlockAttributes The existing Query Loop's attributes. * @return {{ newBlocks: WPBlock[], queryClientIds: string[] }} An object with the cloned/transformed blocks and all the Query Loop clients from these blocks. */ export const getTransformedBlocksFromPattern = (blocks, queryBlockAttributes) => { const { query: { postType, inherit } } = queryBlockAttributes; const clonedBlocks = blocks.map(block => cloneBlock(block)); const queryClientIds = []; const blocksQueue = [...clonedBlocks]; while (blocksQueue.length > 0) { var _block$innerBlocks; const block = blocksQueue.shift(); if (block.name === 'core/query') { block.attributes.query = { ...block.attributes.query, postType, inherit }; queryClientIds.push(block.clientId); } (_block$innerBlocks = block.innerBlocks) === null || _block$innerBlocks === void 0 ? void 0 : _block$innerBlocks.forEach(innerBlock => { blocksQueue.push(innerBlock); }); } return { newBlocks: clonedBlocks, queryClientIds }; }; /** * Helper hook that determines if there is an active variation of the block * and if there are available specific patterns for this variation. * If there are, these patterns are going to be the only ones suggested to * the user in setup and replace flow, without including the default ones * for Query Loop. * * If there are no such patterns, the default ones for Query Loop are going * to be suggested. * * @param {string} clientId The block's client ID. * @param {Object} attributes The block's attributes. * @return {string} The block name to be used in the patterns suggestions. */ export function useBlockNameForPatterns(clientId, attributes) { const activeVariationName = useSelect(select => { var _select$getActiveBloc2; return (_select$getActiveBloc2 = select(blocksStore).getActiveBlockVariation(queryLoopName, attributes)) === null || _select$getActiveBloc2 === void 0 ? void 0 : _select$getActiveBloc2.name; }, [attributes]); const blockName = `${queryLoopName}/${activeVariationName}`; const activeVariationPatterns = useSelect(select => { if (!activeVariationName) { return; } const { getBlockRootClientId, __experimentalGetPatternsByBlockTypes } = select(blockEditorStore); const rootClientId = getBlockRootClientId(clientId); return __experimentalGetPatternsByBlockTypes(blockName, rootClientId); }, [clientId, activeVariationName]); return activeVariationPatterns !== null && activeVariationPatterns !== void 0 && activeVariationPatterns.length ? blockName : queryLoopName; } //# sourceMappingURL=utils.js.map