UNPKG

@wordpress/block-editor

Version:
563 lines (532 loc) 22.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAllPatterns = void 0; exports.getBlockRemovalRules = getBlockRemovalRules; Object.defineProperty(exports, "getBlockSettings", { enumerable: true, get: function () { return _getBlockSettings.getBlockSettings; } }); exports.getBlockStyles = void 0; exports.getBlockWithoutAttributes = getBlockWithoutAttributes; exports.getClosestAllowedInsertionPoint = getClosestAllowedInsertionPoint; exports.getClosestAllowedInsertionPointForPattern = getClosestAllowedInsertionPointForPattern; exports.getEnabledClientIdsTree = exports.getEnabledBlockParents = exports.getContentLockingParent = void 0; exports.getExpandedBlock = getExpandedBlock; exports.getInserterMediaCategories = void 0; exports.getInsertionPoint = getInsertionPoint; exports.getLastFocus = getLastFocus; exports.getLastInsertedBlocksClientIds = getLastInsertedBlocksClientIds; exports.getOpenedBlockSettingsMenu = getOpenedBlockSettingsMenu; exports.getPatternBySlug = exports.getParentSectionBlock = void 0; exports.getRegisteredInserterMediaCategories = getRegisteredInserterMediaCategories; exports.getRemovalPromptData = getRemovalPromptData; exports.getReusableBlocks = void 0; exports.getSectionRootClientId = getSectionRootClientId; exports.getStyleOverrides = void 0; exports.getTemporarilyEditingAsBlocks = getTemporarilyEditingAsBlocks; exports.getTemporarilyEditingFocusModeToRevert = getTemporarilyEditingFocusModeToRevert; exports.getZoomLevel = getZoomLevel; exports.hasAllowedPatterns = void 0; exports.isBlockInterfaceHidden = isBlockInterfaceHidden; exports.isBlockSubtreeDisabled = void 0; exports.isDragging = isDragging; exports.isSectionBlock = isSectionBlock; exports.isZoomOut = isZoomOut; var _data = require("@wordpress/data"); var _selectors = require("./selectors"); var _utils = require("./utils"); var _utils2 = require("../components/inserter/block-patterns-tab/utils"); var _constants = require("./constants"); var _lockUnlock = require("../lock-unlock"); var _privateKeys = require("./private-keys"); var _getBlockSettings = require("./get-block-settings"); /** * WordPress dependencies */ /** * Internal dependencies */ /** * Returns true if the block interface is hidden, or false otherwise. * * @param {Object} state Global application state. * * @return {boolean} Whether the block toolbar is hidden. */ function isBlockInterfaceHidden(state) { return state.isBlockInterfaceHidden; } /** * Gets the client ids of the last inserted blocks. * * @param {Object} state Global application state. * @return {Array|undefined} Client Ids of the last inserted block(s). */ function getLastInsertedBlocksClientIds(state) { return state?.lastBlockInserted?.clientIds; } function getBlockWithoutAttributes(state, clientId) { return state.blocks.byClientId.get(clientId); } /** * Returns true if all of the descendants of a block with the given client ID * have an editing mode of 'disabled', or false otherwise. * * @param {Object} state Global application state. * @param {string} clientId The block client ID. * * @return {boolean} Whether the block descendants are disabled. */ const isBlockSubtreeDisabled = (state, clientId) => { const isChildSubtreeDisabled = childClientId => { return (0, _selectors.getBlockEditingMode)(state, childClientId) === 'disabled' && (0, _selectors.getBlockOrder)(state, childClientId).every(isChildSubtreeDisabled); }; return (0, _selectors.getBlockOrder)(state, clientId).every(isChildSubtreeDisabled); }; exports.isBlockSubtreeDisabled = isBlockSubtreeDisabled; function getEnabledClientIdsTreeUnmemoized(state, rootClientId) { const blockOrder = (0, _selectors.getBlockOrder)(state, rootClientId); const result = []; for (const clientId of blockOrder) { const innerBlocks = getEnabledClientIdsTreeUnmemoized(state, clientId); if ((0, _selectors.getBlockEditingMode)(state, clientId) !== 'disabled') { result.push({ clientId, innerBlocks }); } else { result.push(...innerBlocks); } } return result; } /** * Returns a tree of block objects with only clientID and innerBlocks set. * Blocks with a 'disabled' editing mode are not included. * * @param {Object} state Global application state. * @param {?string} rootClientId Optional root client ID of block list. * * @return {Object[]} Tree of block objects with only clientID and innerBlocks set. */ const getEnabledClientIdsTree = exports.getEnabledClientIdsTree = (0, _data.createRegistrySelector)(select => (0, _data.createSelector)(getEnabledClientIdsTreeUnmemoized, state => [state.blocks.order, state.derivedBlockEditingModes, state.derivedNavModeBlockEditingModes, state.blockEditingModes, state.settings.templateLock, state.blockListSettings, select(_constants.STORE_NAME).__unstableGetEditorMode(state)])); /** * Returns a list of a given block's ancestors, from top to bottom. Blocks with * a 'disabled' editing mode are excluded. * * @see getBlockParents * * @param {Object} state Global application state. * @param {string} clientId The block client ID. * @param {boolean} ascending Order results from bottom to top (true) or top * to bottom (false). */ const getEnabledBlockParents = exports.getEnabledBlockParents = (0, _data.createSelector)((state, clientId, ascending = false) => { return (0, _selectors.getBlockParents)(state, clientId, ascending).filter(parent => (0, _selectors.getBlockEditingMode)(state, parent) !== 'disabled'); }, state => [state.blocks.parents, state.blockEditingModes, state.settings.templateLock, state.blockListSettings]); /** * Selector that returns the data needed to display a prompt when certain * blocks are removed, or `false` if no such prompt is requested. * * @param {Object} state Global application state. * * @return {Object|false} Data for removal prompt display, if any. */ function getRemovalPromptData(state) { return state.removalPromptData; } /** * Returns true if removal prompt exists, or false otherwise. * * @param {Object} state Global application state. * * @return {boolean} Whether removal prompt exists. */ function getBlockRemovalRules(state) { return state.blockRemovalRules; } /** * Returns the client ID of the block settings menu that is currently open. * * @param {Object} state Global application state. * @return {string|null} The client ID of the block menu that is currently open. */ function getOpenedBlockSettingsMenu(state) { return state.openedBlockSettingsMenu; } /** * Returns all style overrides, intended to be merged with global editor styles. * * Overrides are sorted to match the order of the blocks they relate to. This * is useful to maintain correct CSS cascade order. * * @param {Object} state Global application state. * * @return {Array} An array of style ID to style override pairs. */ const getStyleOverrides = exports.getStyleOverrides = (0, _data.createSelector)(state => { const clientIds = (0, _selectors.getClientIdsWithDescendants)(state); const clientIdMap = clientIds.reduce((acc, clientId, index) => { acc[clientId] = index; return acc; }, {}); return [...state.styleOverrides].sort((overrideA, overrideB) => { var _clientIdMap$clientId, _clientIdMap$clientId2; // Once the overrides Map is spread to an array, the first element // is the key, while the second is the override itself including // the clientId to sort by. const [, { clientId: clientIdA }] = overrideA; const [, { clientId: clientIdB }] = overrideB; const aIndex = (_clientIdMap$clientId = clientIdMap[clientIdA]) !== null && _clientIdMap$clientId !== void 0 ? _clientIdMap$clientId : -1; const bIndex = (_clientIdMap$clientId2 = clientIdMap[clientIdB]) !== null && _clientIdMap$clientId2 !== void 0 ? _clientIdMap$clientId2 : -1; return aIndex - bIndex; }); }, state => [state.blocks.order, state.styleOverrides]); /** @typedef {import('./actions').InserterMediaCategory} InserterMediaCategory */ /** * Returns the registered inserter media categories through the public API. * * @param {Object} state Editor state. * * @return {InserterMediaCategory[]} Inserter media categories. */ function getRegisteredInserterMediaCategories(state) { return state.registeredInserterMediaCategories; } /** * Returns an array containing the allowed inserter media categories. * It merges the registered media categories from extenders with the * core ones. It also takes into account the allowed `mime_types`, which * can be altered by `upload_mimes` filter and restrict some of them. * * @param {Object} state Global application state. * * @return {InserterMediaCategory[]} Client IDs of descendants. */ const getInserterMediaCategories = exports.getInserterMediaCategories = (0, _data.createSelector)(state => { const { settings: { inserterMediaCategories, allowedMimeTypes, enableOpenverseMediaCategory }, registeredInserterMediaCategories } = state; // The allowed `mime_types` can be altered by `upload_mimes` filter and restrict // some of them. In this case we shouldn't add the category to the available media // categories list in the inserter. if (!inserterMediaCategories && !registeredInserterMediaCategories.length || !allowedMimeTypes) { return; } const coreInserterMediaCategoriesNames = inserterMediaCategories?.map(({ name }) => name) || []; const mergedCategories = [...(inserterMediaCategories || []), ...(registeredInserterMediaCategories || []).filter(({ name }) => !coreInserterMediaCategoriesNames.includes(name))]; return mergedCategories.filter(category => { // Check if Openverse category is enabled. if (!enableOpenverseMediaCategory && category.name === 'openverse') { return false; } return Object.values(allowedMimeTypes).some(mimeType => mimeType.startsWith(`${category.mediaType}/`)); }); }, state => [state.settings.inserterMediaCategories, state.settings.allowedMimeTypes, state.settings.enableOpenverseMediaCategory, state.registeredInserterMediaCategories]); /** * Returns whether there is at least one allowed pattern for inner blocks children. * This is useful for deferring the parsing of all patterns until needed. * * @param {Object} state Editor state. * @param {string} [rootClientId=null] Target root client ID. * * @return {boolean} If there is at least one allowed pattern. */ const hasAllowedPatterns = exports.hasAllowedPatterns = (0, _data.createRegistrySelector)(select => (0, _data.createSelector)((state, rootClientId = null) => { const { getAllPatterns } = (0, _lockUnlock.unlock)(select(_constants.STORE_NAME)); const patterns = getAllPatterns(); const { allowedBlockTypes } = (0, _selectors.getSettings)(state); return patterns.some(pattern => { const { inserter = true } = pattern; if (!inserter) { return false; } const grammar = (0, _utils.getGrammar)(pattern); return (0, _utils.checkAllowListRecursive)(grammar, allowedBlockTypes) && grammar.every(({ name: blockName }) => (0, _selectors.canInsertBlockType)(state, blockName, rootClientId)); }); }, (state, rootClientId) => [...(0, _utils.getAllPatternsDependants)(select)(state), ...(0, _utils.getInsertBlockTypeDependants)(select)(state, rootClientId)])); function mapUserPattern(userPattern, __experimentalUserPatternCategories = []) { return { name: `core/block/${userPattern.id}`, id: userPattern.id, type: _utils2.INSERTER_PATTERN_TYPES.user, title: userPattern.title.raw, categories: userPattern.wp_pattern_category?.map(catId => { const category = __experimentalUserPatternCategories.find(({ id }) => id === catId); return category ? category.slug : catId; }), content: userPattern.content.raw, syncStatus: userPattern.wp_pattern_sync_status }; } const getPatternBySlug = exports.getPatternBySlug = (0, _data.createRegistrySelector)(select => (0, _data.createSelector)((state, patternName) => { var _state$settings$__exp, _state$settings$selec; // Only fetch reusable blocks if we know we need them. To do: maybe // use the entity record API to retrieve the block by slug. if (patternName?.startsWith('core/block/')) { const _id = parseInt(patternName.slice('core/block/'.length), 10); const block = (0, _lockUnlock.unlock)(select(_constants.STORE_NAME)).getReusableBlocks().find(({ id }) => id === _id); if (!block) { return null; } return mapUserPattern(block, state.settings.__experimentalUserPatternCategories); } return [ // This setting is left for back compat. ...((_state$settings$__exp = state.settings.__experimentalBlockPatterns) !== null && _state$settings$__exp !== void 0 ? _state$settings$__exp : []), ...((_state$settings$selec = state.settings[_privateKeys.selectBlockPatternsKey]?.(select)) !== null && _state$settings$selec !== void 0 ? _state$settings$selec : [])].find(({ name }) => name === patternName); }, (state, patternName) => patternName?.startsWith('core/block/') ? [(0, _lockUnlock.unlock)(select(_constants.STORE_NAME)).getReusableBlocks(), state.settings.__experimentalReusableBlocks] : [state.settings.__experimentalBlockPatterns, state.settings[_privateKeys.selectBlockPatternsKey]?.(select)])); const getAllPatterns = exports.getAllPatterns = (0, _data.createRegistrySelector)(select => (0, _data.createSelector)(state => { var _state$settings$__exp2, _state$settings$selec2; return [...(0, _lockUnlock.unlock)(select(_constants.STORE_NAME)).getReusableBlocks().map(userPattern => mapUserPattern(userPattern, state.settings.__experimentalUserPatternCategories)), // This setting is left for back compat. ...((_state$settings$__exp2 = state.settings.__experimentalBlockPatterns) !== null && _state$settings$__exp2 !== void 0 ? _state$settings$__exp2 : []), ...((_state$settings$selec2 = state.settings[_privateKeys.selectBlockPatternsKey]?.(select)) !== null && _state$settings$selec2 !== void 0 ? _state$settings$selec2 : [])].filter((x, index, arr) => index === arr.findIndex(y => x.name === y.name)); }, (0, _utils.getAllPatternsDependants)(select))); const EMPTY_ARRAY = []; const getReusableBlocks = exports.getReusableBlocks = (0, _data.createRegistrySelector)(select => state => { var _ref; const reusableBlocksSelect = state.settings[_privateKeys.reusableBlocksSelectKey]; return (_ref = reusableBlocksSelect ? reusableBlocksSelect(select) : state.settings.__experimentalReusableBlocks) !== null && _ref !== void 0 ? _ref : EMPTY_ARRAY; }); /** * Returns the element of the last element that had focus when focus left the editor canvas. * * @param {Object} state Block editor state. * * @return {Object} Element. */ function getLastFocus(state) { return state.lastFocus; } /** * Returns true if the user is dragging anything, or false otherwise. It is possible for a * user to be dragging data from outside of the editor, so this selector is separate from * the `isDraggingBlocks` selector which only returns true if the user is dragging blocks. * * @param {Object} state Global application state. * * @return {boolean} Whether user is dragging. */ function isDragging(state) { return state.isDragging; } /** * Retrieves the expanded block from the state. * * @param {Object} state Block editor state. * * @return {string|null} The client ID of the expanded block, if set. */ function getExpandedBlock(state) { return state.expandedBlock; } /** * Retrieves the client ID of the ancestor block that is content locking the block * with the provided client ID. * * @param {Object} state Global application state. * @param {string} clientId Client Id of the block. * * @return {?string} Client ID of the ancestor block that is content locking the block. */ const getContentLockingParent = (state, clientId) => { let current = clientId; let result; while (!result && (current = state.blocks.parents.get(current))) { if ((0, _selectors.getTemplateLock)(state, current) === 'contentOnly') { result = current; } } return result; }; /** * Retrieves the client ID of the parent section block. * * @param {Object} state Global application state. * @param {string} clientId Client Id of the block. * * @return {?string} Client ID of the ancestor block that is content locking the block. */ exports.getContentLockingParent = getContentLockingParent; const getParentSectionBlock = (state, clientId) => { let current = clientId; let result; while (!result && (current = state.blocks.parents.get(current))) { if (isSectionBlock(state, current)) { result = current; } } return result; }; /** * Retrieves the client ID is a content locking parent * * @param {Object} state Global application state. * @param {string} clientId Client Id of the block. * * @return {boolean} Whether the block is a content locking parent. */ exports.getParentSectionBlock = getParentSectionBlock; function isSectionBlock(state, clientId) { const blockName = (0, _selectors.getBlockName)(state, clientId); if (blockName === 'core/block' || (0, _selectors.getTemplateLock)(state, clientId) === 'contentOnly') { return true; } // Template parts become sections in navigation mode. const _isNavigationMode = (0, _selectors.isNavigationMode)(state); if (_isNavigationMode && blockName === 'core/template-part') { return true; } const sectionRootClientId = getSectionRootClientId(state); const sectionClientIds = (0, _selectors.getBlockOrder)(state, sectionRootClientId); return _isNavigationMode && sectionClientIds.includes(clientId); } /** * Retrieves the client ID of the block that is content locked but is * currently being temporarily edited as a non-locked block. * * @param {Object} state Global application state. * * @return {?string} The client ID of the block being temporarily edited as a non-locked block. */ function getTemporarilyEditingAsBlocks(state) { return state.temporarilyEditingAsBlocks; } /** * Returns the focus mode that should be reapplied when the user stops editing * a content locked blocks as a block without locking. * * @param {Object} state Global application state. * * @return {?string} The focus mode that should be re-set when temporarily editing as blocks stops. */ function getTemporarilyEditingFocusModeToRevert(state) { return state.temporarilyEditingFocusModeRevert; } /** * Returns the style attributes of multiple blocks. * * @param {Object} state Global application state. * @param {string[]} clientIds An array of block client IDs. * * @return {Object} An object where keys are client IDs and values are the corresponding block styles or undefined. */ const getBlockStyles = exports.getBlockStyles = (0, _data.createSelector)((state, clientIds) => clientIds.reduce((styles, clientId) => { styles[clientId] = state.blocks.attributes.get(clientId)?.style; return styles; }, {}), (state, clientIds) => [...clientIds.map(clientId => state.blocks.attributes.get(clientId)?.style)]); /** * Retrieves the client ID of the block which contains the blocks * acting as "sections" in the editor. This is typically the "main content" * of the template/post. * * @param {Object} state Editor state. * * @return {string|undefined} The section root client ID or undefined if not set. */ function getSectionRootClientId(state) { return state.settings?.[_privateKeys.sectionRootClientIdKey]; } /** * Returns whether the editor is considered zoomed out. * * @param {Object} state Global application state. * @return {boolean} Whether the editor is zoomed. */ function isZoomOut(state) { return state.zoomLevel === 'auto-scaled' || state.zoomLevel < 100; } /** * Returns whether the zoom level. * * @param {Object} state Global application state. * @return {number|"auto-scaled"} Zoom level. */ function getZoomLevel(state) { return state.zoomLevel; } /** * Finds the closest block where the block is allowed to be inserted. * * @param {Object} state Editor state. * @param {string[] | string} name Block name or names. * @param {string} clientId Default insertion point. * * @return {string} clientID of the closest container when the block name can be inserted. */ function getClosestAllowedInsertionPoint(state, name, clientId = '') { const blockNames = Array.isArray(name) ? name : [name]; const areBlockNamesAllowedInClientId = id => blockNames.every(currentName => (0, _selectors.canInsertBlockType)(state, currentName, id)); // If we're trying to insert at the root level and it's not allowed // Try the section root instead. if (!clientId) { if (areBlockNamesAllowedInClientId(clientId)) { return clientId; } const sectionRootClientId = getSectionRootClientId(state); if (sectionRootClientId && areBlockNamesAllowedInClientId(sectionRootClientId)) { return sectionRootClientId; } return null; } // Traverse the block tree up until we find a place where we can insert. let current = clientId; while (current !== null && !areBlockNamesAllowedInClientId(current)) { const parentClientId = (0, _selectors.getBlockRootClientId)(state, current); current = parentClientId; } return current; } function getClosestAllowedInsertionPointForPattern(state, pattern, clientId) { const { allowedBlockTypes } = (0, _selectors.getSettings)(state); const isAllowed = (0, _utils.checkAllowListRecursive)((0, _utils.getGrammar)(pattern), allowedBlockTypes); if (!isAllowed) { return null; } const names = (0, _utils.getGrammar)(pattern).map(({ blockName: name }) => name); return getClosestAllowedInsertionPoint(state, names, clientId); } /** * Where the point where the next block will be inserted into. * * @param {Object} state * @return {Object} where the insertion point in the block editor is or null if none is set. */ function getInsertionPoint(state) { return state.insertionPoint; } //# sourceMappingURL=private-selectors.js.map