@wordpress/block-editor
Version:
563 lines (532 loc) • 22.3 kB
JavaScript
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
;