@wordpress/block-editor
Version:
1,540 lines (1,539 loc) • 53.8 kB
JavaScript
// packages/block-editor/src/store/selectors.js
import {
getBlockType,
getBlockTypes,
getBlockVariations,
hasBlockSupport,
getPossibleBlockTransformations,
switchToBlockType,
store as blocksStore,
privateApis as blocksPrivateApis
} from "@wordpress/blocks";
import { Platform } from "@wordpress/element";
import { applyFilters } from "@wordpress/hooks";
import { symbol } from "@wordpress/icons";
import { create, remove, toHTMLString } from "@wordpress/rich-text";
import deprecated from "@wordpress/deprecated";
import { createSelector, createRegistrySelector } from "@wordpress/data";
import {
isFiltered,
checkAllowListRecursive,
checkAllowList,
getAllPatternsDependants,
getInsertBlockTypeDependants,
getParsedPattern,
getGrammar,
mapUserPattern
} from "./utils";
import { orderBy } from "../utils/sorting";
import { STORE_NAME } from "./constants";
import { unlock } from "../lock-unlock";
import {
getContentLockingParent,
getEditedContentOnlySection,
getSectionRootClientId,
isSectionBlock,
getParentSectionBlock,
isZoomOut,
isContainerInsertableToInContentOnlyMode
} from "./private-selectors";
var { isContentBlock } = unlock(blocksPrivateApis);
var MILLISECONDS_PER_HOUR = 3600 * 1e3;
var MILLISECONDS_PER_DAY = 24 * 3600 * 1e3;
var MILLISECONDS_PER_WEEK = 7 * 24 * 3600 * 1e3;
var EMPTY_ARRAY = [];
var EMPTY_SET = /* @__PURE__ */ new Set();
var DEFAULT_INSERTER_OPTIONS = {
[isFiltered]: true
};
function getBlockName(state, clientId) {
const block = state.blocks.byClientId.get(clientId);
const socialLinkName = "core/social-link";
if (Platform.OS !== "web" && block?.name === socialLinkName) {
const attributes = state.blocks.attributes.get(clientId);
const { service } = attributes ?? {};
return service ? `${socialLinkName}-${service}` : socialLinkName;
}
return block ? block.name : null;
}
function isBlockValid(state, clientId) {
const block = state.blocks.byClientId.get(clientId);
return !!block && block.isValid;
}
function getBlockAttributes(state, clientId) {
const block = state.blocks.byClientId.get(clientId);
if (!block) {
return null;
}
return state.blocks.attributes.get(clientId);
}
function getBlock(state, clientId) {
if (!state.blocks.byClientId.has(clientId)) {
return null;
}
return state.blocks.tree.get(clientId);
}
var __unstableGetBlockWithoutInnerBlocks = createSelector(
(state, clientId) => {
const block = state.blocks.byClientId.get(clientId);
if (!block) {
return null;
}
return {
...block,
attributes: getBlockAttributes(state, clientId)
};
},
(state, clientId) => [
state.blocks.byClientId.get(clientId),
state.blocks.attributes.get(clientId)
]
);
function getBlocks(state, rootClientId) {
const treeKey = !rootClientId || !areInnerBlocksControlled(state, rootClientId) ? rootClientId || "" : "controlled||" + rootClientId;
return state.blocks.tree.get(treeKey)?.innerBlocks || EMPTY_ARRAY;
}
var __unstableGetClientIdWithClientIdsTree = createSelector(
(state, clientId) => {
deprecated(
"wp.data.select( 'core/block-editor' ).__unstableGetClientIdWithClientIdsTree",
{
since: "6.3",
version: "6.5"
}
);
return {
clientId,
innerBlocks: __unstableGetClientIdsTree(state, clientId)
};
},
(state) => [state.blocks.order]
);
var __unstableGetClientIdsTree = createSelector(
(state, rootClientId = "") => {
deprecated(
"wp.data.select( 'core/block-editor' ).__unstableGetClientIdsTree",
{
since: "6.3",
version: "6.5"
}
);
return getBlockOrder(state, rootClientId).map(
(clientId) => __unstableGetClientIdWithClientIdsTree(state, clientId)
);
},
(state) => [state.blocks.order]
);
var getClientIdsOfDescendants = createSelector(
(state, rootIds) => {
rootIds = Array.isArray(rootIds) ? [...rootIds] : [rootIds];
const ids = [];
for (const rootId of rootIds) {
const order = state.blocks.order.get(rootId);
if (order) {
ids.push(...order);
}
}
let index = 0;
while (index < ids.length) {
const id = ids[index];
const order = state.blocks.order.get(id);
if (order) {
ids.splice(index + 1, 0, ...order);
}
index++;
}
return ids;
},
(state) => [state.blocks.order]
);
var getClientIdsWithDescendants = (state) => getClientIdsOfDescendants(state, "");
var getGlobalBlockCount = createSelector(
(state, blockName) => {
const clientIds = getClientIdsWithDescendants(state);
if (!blockName) {
return clientIds.length;
}
let count = 0;
for (const clientId of clientIds) {
const block = state.blocks.byClientId.get(clientId);
if (block.name === blockName) {
count++;
}
}
return count;
},
(state) => [state.blocks.order, state.blocks.byClientId]
);
var getBlocksByName = createSelector(
(state, blockName) => {
if (!blockName) {
return EMPTY_ARRAY;
}
const blockNames = Array.isArray(blockName) ? blockName : [blockName];
const clientIds = getClientIdsWithDescendants(state);
const foundBlocks = clientIds.filter((clientId) => {
const block = state.blocks.byClientId.get(clientId);
return blockNames.includes(block.name);
});
return foundBlocks.length > 0 ? foundBlocks : EMPTY_ARRAY;
},
(state) => [state.blocks.order, state.blocks.byClientId]
);
function __experimentalGetGlobalBlocksByName(state, blockName) {
deprecated(
"wp.data.select( 'core/block-editor' ).__experimentalGetGlobalBlocksByName",
{
since: "6.5",
alternative: `wp.data.select( 'core/block-editor' ).getBlocksByName`
}
);
return getBlocksByName(state, blockName);
}
var getBlocksByClientId = createSelector(
(state, clientIds) => (Array.isArray(clientIds) ? clientIds : [clientIds]).map(
(clientId) => getBlock(state, clientId)
),
(state, clientIds) => (Array.isArray(clientIds) ? clientIds : [clientIds]).map(
(clientId) => state.blocks.tree.get(clientId)
)
);
var getBlockNamesByClientId = createSelector(
(state, clientIds) => getBlocksByClientId(state, clientIds).filter(Boolean).map((block) => block.name),
(state, clientIds) => getBlocksByClientId(state, clientIds)
);
function getBlockCount(state, rootClientId) {
return getBlockOrder(state, rootClientId).length;
}
function getSelectionStart(state) {
return state.selection.selectionStart;
}
function getSelectionEnd(state) {
return state.selection.selectionEnd;
}
function getBlockSelectionStart(state) {
return state.selection.selectionStart.clientId;
}
function getBlockSelectionEnd(state) {
return state.selection.selectionEnd.clientId;
}
function getSelectedBlockCount(state) {
const multiSelectedBlockCount = getMultiSelectedBlockClientIds(state).length;
if (multiSelectedBlockCount) {
return multiSelectedBlockCount;
}
return state.selection.selectionStart.clientId ? 1 : 0;
}
function hasSelectedBlock(state) {
const { selectionStart, selectionEnd } = state.selection;
return !!selectionStart.clientId && selectionStart.clientId === selectionEnd.clientId;
}
function getSelectedBlockClientId(state) {
const { selectionStart, selectionEnd } = state.selection;
const { clientId } = selectionStart;
if (!clientId || clientId !== selectionEnd.clientId) {
return null;
}
return clientId;
}
function getSelectedBlock(state) {
const clientId = getSelectedBlockClientId(state);
return clientId ? getBlock(state, clientId) : null;
}
function getBlockRootClientId(state, clientId) {
return state.blocks.parents.get(clientId) ?? null;
}
var getBlockParents = createSelector(
(state, clientId, ascending = false) => {
const parents = [];
let current = clientId;
while (current = state.blocks.parents.get(current)) {
parents.push(current);
}
if (!parents.length) {
return EMPTY_ARRAY;
}
return ascending ? parents : parents.reverse();
},
(state) => [state.blocks.parents]
);
var getBlockParentsByBlockName = createSelector(
(state, clientId, blockName, ascending = false) => {
const parents = getBlockParents(state, clientId, ascending);
const hasName = Array.isArray(blockName) ? (name) => blockName.includes(name) : (name) => blockName === name;
return parents.filter((id) => hasName(getBlockName(state, id)));
},
(state) => [state.blocks.parents]
);
function getBlockHierarchyRootClientId(state, clientId) {
let current = clientId;
let parent;
do {
parent = current;
current = state.blocks.parents.get(current);
} while (current);
return parent;
}
function getLowestCommonAncestorWithSelectedBlock(state, clientId) {
const selectedId = getSelectedBlockClientId(state);
const clientParents = [...getBlockParents(state, clientId), clientId];
const selectedParents = [
...getBlockParents(state, selectedId),
selectedId
];
let lowestCommonAncestor;
const maxDepth = Math.min(clientParents.length, selectedParents.length);
for (let index = 0; index < maxDepth; index++) {
if (clientParents[index] === selectedParents[index]) {
lowestCommonAncestor = clientParents[index];
} else {
break;
}
}
return lowestCommonAncestor;
}
function getAdjacentBlockClientId(state, startClientId, modifier = 1) {
if (startClientId === void 0) {
startClientId = getSelectedBlockClientId(state);
}
if (startClientId === void 0) {
if (modifier < 0) {
startClientId = getFirstMultiSelectedBlockClientId(state);
} else {
startClientId = getLastMultiSelectedBlockClientId(state);
}
}
if (!startClientId) {
return null;
}
const rootClientId = getBlockRootClientId(state, startClientId);
if (rootClientId === null) {
return null;
}
const { order } = state.blocks;
const orderSet = order.get(rootClientId);
const index = orderSet.indexOf(startClientId);
const nextIndex = index + 1 * modifier;
if (nextIndex < 0) {
return null;
}
if (nextIndex === orderSet.length) {
return null;
}
return orderSet[nextIndex];
}
function getPreviousBlockClientId(state, startClientId) {
return getAdjacentBlockClientId(state, startClientId, -1);
}
function getNextBlockClientId(state, startClientId) {
return getAdjacentBlockClientId(state, startClientId, 1);
}
function getSelectedBlocksInitialCaretPosition(state) {
return state.initialPosition;
}
var getSelectedBlockClientIds = createSelector(
(state) => {
const { selectionStart, selectionEnd } = state.selection;
if (!selectionStart.clientId || !selectionEnd.clientId) {
return EMPTY_ARRAY;
}
if (selectionStart.clientId === selectionEnd.clientId) {
return [selectionStart.clientId];
}
const rootClientId = getBlockRootClientId(
state,
selectionStart.clientId
);
if (rootClientId === null) {
return EMPTY_ARRAY;
}
const blockOrder = getBlockOrder(state, rootClientId);
const startIndex = blockOrder.indexOf(selectionStart.clientId);
const endIndex = blockOrder.indexOf(selectionEnd.clientId);
if (startIndex > endIndex) {
return blockOrder.slice(endIndex, startIndex + 1);
}
return blockOrder.slice(startIndex, endIndex + 1);
},
(state) => [
state.blocks.order,
state.selection.selectionStart.clientId,
state.selection.selectionEnd.clientId
]
);
function getMultiSelectedBlockClientIds(state) {
const { selectionStart, selectionEnd } = state.selection;
if (selectionStart.clientId === selectionEnd.clientId) {
return EMPTY_ARRAY;
}
return getSelectedBlockClientIds(state);
}
var getMultiSelectedBlocks = createSelector(
(state) => {
const multiSelectedBlockClientIds = getMultiSelectedBlockClientIds(state);
if (!multiSelectedBlockClientIds.length) {
return EMPTY_ARRAY;
}
return multiSelectedBlockClientIds.map(
(clientId) => getBlock(state, clientId)
);
},
(state) => [
...getSelectedBlockClientIds.getDependants(state),
state.blocks.byClientId,
state.blocks.order,
state.blocks.attributes
]
);
function getFirstMultiSelectedBlockClientId(state) {
return getMultiSelectedBlockClientIds(state)[0] || null;
}
function getLastMultiSelectedBlockClientId(state) {
const selectedClientIds = getMultiSelectedBlockClientIds(state);
return selectedClientIds[selectedClientIds.length - 1] || null;
}
function isFirstMultiSelectedBlock(state, clientId) {
return getFirstMultiSelectedBlockClientId(state) === clientId;
}
function isBlockMultiSelected(state, clientId) {
return getMultiSelectedBlockClientIds(state).indexOf(clientId) !== -1;
}
var isAncestorMultiSelected = createSelector(
(state, clientId) => {
let ancestorClientId = clientId;
let isMultiSelected = false;
while (ancestorClientId && !isMultiSelected) {
ancestorClientId = getBlockRootClientId(state, ancestorClientId);
isMultiSelected = isBlockMultiSelected(state, ancestorClientId);
}
return isMultiSelected;
},
(state) => [
state.blocks.order,
state.selection.selectionStart.clientId,
state.selection.selectionEnd.clientId
]
);
function getMultiSelectedBlocksStartClientId(state) {
const { selectionStart, selectionEnd } = state.selection;
if (selectionStart.clientId === selectionEnd.clientId) {
return null;
}
return selectionStart.clientId || null;
}
function getMultiSelectedBlocksEndClientId(state) {
const { selectionStart, selectionEnd } = state.selection;
if (selectionStart.clientId === selectionEnd.clientId) {
return null;
}
return selectionEnd.clientId || null;
}
function __unstableIsFullySelected(state) {
const selectionAnchor = getSelectionStart(state);
const selectionFocus = getSelectionEnd(state);
return !selectionAnchor.attributeKey && !selectionFocus.attributeKey && typeof selectionAnchor.offset === "undefined" && typeof selectionFocus.offset === "undefined";
}
function __unstableIsSelectionCollapsed(state) {
const selectionAnchor = getSelectionStart(state);
const selectionFocus = getSelectionEnd(state);
return !!selectionAnchor && !!selectionFocus && selectionAnchor.clientId === selectionFocus.clientId && selectionAnchor.attributeKey === selectionFocus.attributeKey && selectionAnchor.offset === selectionFocus.offset;
}
function __unstableSelectionHasUnmergeableBlock(state) {
return getSelectedBlockClientIds(state).some((clientId) => {
const blockName = getBlockName(state, clientId);
const blockType = getBlockType(blockName);
return !blockType.merge;
});
}
function __unstableIsSelectionMergeable(state, isForward) {
const selectionAnchor = getSelectionStart(state);
const selectionFocus = getSelectionEnd(state);
if (selectionAnchor.clientId === selectionFocus.clientId) {
return false;
}
if (!selectionAnchor.attributeKey || !selectionFocus.attributeKey || typeof selectionAnchor.offset === "undefined" || typeof selectionFocus.offset === "undefined") {
return false;
}
const anchorRootClientId = getBlockRootClientId(
state,
selectionAnchor.clientId
);
const focusRootClientId = getBlockRootClientId(
state,
selectionFocus.clientId
);
if (anchorRootClientId !== focusRootClientId) {
return false;
}
const blockOrder = getBlockOrder(state, anchorRootClientId);
const anchorIndex = blockOrder.indexOf(selectionAnchor.clientId);
const focusIndex = blockOrder.indexOf(selectionFocus.clientId);
let selectionStart, selectionEnd;
if (anchorIndex > focusIndex) {
selectionStart = selectionFocus;
selectionEnd = selectionAnchor;
} else {
selectionStart = selectionAnchor;
selectionEnd = selectionFocus;
}
const targetBlockClientId = isForward ? selectionEnd.clientId : selectionStart.clientId;
const blockToMergeClientId = isForward ? selectionStart.clientId : selectionEnd.clientId;
const targetBlockName = getBlockName(state, targetBlockClientId);
const targetBlockType = getBlockType(targetBlockName);
if (!targetBlockType.merge) {
return false;
}
const blockToMerge = getBlock(state, blockToMergeClientId);
if (blockToMerge.name === targetBlockName) {
return true;
}
const blocksToMerge = switchToBlockType(blockToMerge, targetBlockName);
return blocksToMerge && blocksToMerge.length;
}
var __unstableGetSelectedBlocksWithPartialSelection = (state) => {
const selectionAnchor = getSelectionStart(state);
const selectionFocus = getSelectionEnd(state);
if (selectionAnchor.clientId === selectionFocus.clientId) {
return EMPTY_ARRAY;
}
if (!selectionAnchor.attributeKey || !selectionFocus.attributeKey || typeof selectionAnchor.offset === "undefined" || typeof selectionFocus.offset === "undefined") {
return EMPTY_ARRAY;
}
const anchorRootClientId = getBlockRootClientId(
state,
selectionAnchor.clientId
);
const focusRootClientId = getBlockRootClientId(
state,
selectionFocus.clientId
);
if (anchorRootClientId !== focusRootClientId) {
return EMPTY_ARRAY;
}
const blockOrder = getBlockOrder(state, anchorRootClientId);
const anchorIndex = blockOrder.indexOf(selectionAnchor.clientId);
const focusIndex = blockOrder.indexOf(selectionFocus.clientId);
const [selectionStart, selectionEnd] = anchorIndex > focusIndex ? [selectionFocus, selectionAnchor] : [selectionAnchor, selectionFocus];
const blockA = getBlock(state, selectionStart.clientId);
const blockB = getBlock(state, selectionEnd.clientId);
const htmlA = blockA.attributes[selectionStart.attributeKey];
const htmlB = blockB.attributes[selectionEnd.attributeKey];
let valueA = create({ html: htmlA });
let valueB = create({ html: htmlB });
valueA = remove(valueA, 0, selectionStart.offset);
valueB = remove(valueB, selectionEnd.offset, valueB.text.length);
return [
{
...blockA,
attributes: {
...blockA.attributes,
[selectionStart.attributeKey]: toHTMLString({
value: valueA
})
}
},
{
...blockB,
attributes: {
...blockB.attributes,
[selectionEnd.attributeKey]: toHTMLString({
value: valueB
})
}
}
];
};
function getBlockOrder(state, rootClientId) {
return state.blocks.order.get(rootClientId || "") || EMPTY_ARRAY;
}
function getBlockIndex(state, clientId) {
const rootClientId = getBlockRootClientId(state, clientId);
return getBlockOrder(state, rootClientId).indexOf(clientId);
}
function isBlockSelected(state, clientId) {
const { selectionStart, selectionEnd } = state.selection;
if (selectionStart.clientId !== selectionEnd.clientId) {
return false;
}
return selectionStart.clientId === clientId;
}
function hasSelectedInnerBlock(state, clientId, deep = false) {
const selectedBlockClientIds = getSelectedBlockClientIds(state);
if (!selectedBlockClientIds.length) {
return false;
}
if (deep) {
return selectedBlockClientIds.some(
(id) => (
// Pass true because we don't care about order and it's more
// performant.
getBlockParents(state, id, true).includes(clientId)
)
);
}
return selectedBlockClientIds.some(
(id) => getBlockRootClientId(state, id) === clientId
);
}
function hasDraggedInnerBlock(state, clientId, deep = false) {
return getBlockOrder(state, clientId).some(
(innerClientId) => isBlockBeingDragged(state, innerClientId) || deep && hasDraggedInnerBlock(state, innerClientId, deep)
);
}
function isBlockWithinSelection(state, clientId) {
if (!clientId) {
return false;
}
const clientIds = getMultiSelectedBlockClientIds(state);
const index = clientIds.indexOf(clientId);
return index > -1 && index < clientIds.length - 1;
}
function hasMultiSelection(state) {
const { selectionStart, selectionEnd } = state.selection;
return selectionStart.clientId !== selectionEnd.clientId;
}
function isMultiSelecting(state) {
return state.isMultiSelecting;
}
function isSelectionEnabled(state) {
return state.isSelectionEnabled;
}
function getBlockMode(state, clientId) {
return state.blocksMode[clientId] || "visual";
}
function isTyping(state) {
return state.isTyping;
}
function isDraggingBlocks(state) {
return !!state.draggedBlocks.length;
}
function getDraggedBlockClientIds(state) {
return state.draggedBlocks;
}
function isBlockBeingDragged(state, clientId) {
return state.draggedBlocks.includes(clientId);
}
function isAncestorBeingDragged(state, clientId) {
if (!isDraggingBlocks(state)) {
return false;
}
const parents = getBlockParents(state, clientId);
return parents.some(
(parentClientId) => isBlockBeingDragged(state, parentClientId)
);
}
function isCaretWithinFormattedText() {
deprecated(
'wp.data.select( "core/block-editor" ).isCaretWithinFormattedText',
{
since: "6.1",
version: "6.3"
}
);
return false;
}
var getBlockInsertionPoint = createSelector(
(state) => {
let rootClientId, index;
const {
insertionCue,
selection: { selectionEnd }
} = state;
if (insertionCue !== null) {
return insertionCue;
}
const { clientId } = selectionEnd;
if (clientId) {
rootClientId = getBlockRootClientId(state, clientId) || void 0;
index = getBlockIndex(state, selectionEnd.clientId) + 1;
} else {
index = getBlockOrder(state).length;
}
return { rootClientId, index };
},
(state) => [
state.insertionCue,
state.selection.selectionEnd.clientId,
state.blocks.parents,
state.blocks.order
]
);
function isBlockInsertionPointVisible(state) {
return state.insertionCue !== null;
}
function isValidTemplate(state) {
return state.template.isValid;
}
function getTemplate(state) {
return state.settings.template;
}
function getTemplateLock(state, rootClientId) {
if (!rootClientId) {
return state.settings.templateLock ?? false;
}
const blockListTemplateLock = getBlockListSettings(
state,
rootClientId
)?.templateLock;
if (blockListTemplateLock === "contentOnly" && state.editedContentOnlySection === rootClientId) {
return false;
}
return blockListTemplateLock ?? false;
}
var isBlockVisibleInTheInserter = (state, blockNameOrType, rootClientId = null) => {
let blockType;
let blockName;
if (blockNameOrType && "object" === typeof blockNameOrType) {
blockType = blockNameOrType;
blockName = blockNameOrType.name;
} else {
blockType = getBlockType(blockNameOrType);
blockName = blockNameOrType;
}
if (!blockType) {
return false;
}
const { allowedBlockTypes } = getSettings(state);
const isBlockAllowedInEditor = checkAllowList(
allowedBlockTypes,
blockName,
true
);
if (!isBlockAllowedInEditor) {
return false;
}
const parents = (Array.isArray(blockType.parent) ? blockType.parent : []).concat(Array.isArray(blockType.ancestor) ? blockType.ancestor : []);
if (parents.length > 0) {
if (parents.includes("core/post-content")) {
return true;
}
let current = rootClientId;
let hasParent = false;
do {
if (parents.includes(getBlockName(state, current))) {
hasParent = true;
break;
}
current = state.blocks.parents.get(current);
} while (current);
return hasParent;
}
return true;
};
var canInsertBlockTypeUnmemoized = (state, blockName, rootClientId = null) => {
if (!isBlockVisibleInTheInserter(state, blockName, rootClientId)) {
return false;
}
let blockType;
if (blockName && "object" === typeof blockName) {
blockType = blockName;
blockName = blockType.name;
} else {
blockType = getBlockType(blockName);
}
const rootTemplateLock = getTemplateLock(state, rootClientId);
if (rootTemplateLock && rootTemplateLock !== "contentOnly") {
return false;
}
const blockEditingMode = getBlockEditingMode(state, rootClientId ?? "");
if (blockEditingMode === "disabled") {
return false;
}
const parentBlockListSettings = getBlockListSettings(state, rootClientId);
if (rootClientId && parentBlockListSettings === void 0) {
return false;
}
const isContentRoleBlock = isContentBlock(blockName);
const isParentSectionBlock = !!isSectionBlock(state, rootClientId);
const isBlockWithinSection = !!getParentSectionBlock(
state,
rootClientId
);
if ((isParentSectionBlock || isBlockWithinSection) && !isContentRoleBlock) {
return false;
}
if ((isParentSectionBlock || blockEditingMode === "contentOnly") && !isContainerInsertableToInContentOnlyMode(
state,
blockName,
rootClientId
)) {
return false;
}
const parentName = getBlockName(state, rootClientId);
const parentBlockType = getBlockType(parentName);
const parentAllowedChildBlocks = parentBlockType?.allowedBlocks;
let hasParentAllowedBlock = checkAllowList(
parentAllowedChildBlocks,
blockName
);
if (hasParentAllowedBlock !== false) {
const parentAllowedBlocks = parentBlockListSettings?.allowedBlocks;
const hasParentListAllowedBlock = checkAllowList(
parentAllowedBlocks,
blockName
);
if (hasParentListAllowedBlock !== null) {
hasParentAllowedBlock = hasParentListAllowedBlock;
}
}
const blockAllowedParentBlocks = blockType.parent;
const hasBlockAllowedParent = checkAllowList(
blockAllowedParentBlocks,
parentName
);
let hasBlockAllowedAncestor = true;
const blockAllowedAncestorBlocks = blockType.ancestor;
if (blockAllowedAncestorBlocks) {
const ancestors = [
rootClientId,
...getBlockParents(state, rootClientId)
];
hasBlockAllowedAncestor = ancestors.some(
(ancestorClientId) => checkAllowList(
blockAllowedAncestorBlocks,
getBlockName(state, ancestorClientId)
)
);
}
const canInsert = hasBlockAllowedAncestor && (hasParentAllowedBlock === null && hasBlockAllowedParent === null || hasParentAllowedBlock === true || hasBlockAllowedParent === true);
if (!canInsert) {
return canInsert;
}
return applyFilters(
"blockEditor.__unstableCanInsertBlockType",
canInsert,
blockType,
rootClientId,
{
// Pass bound selectors of the current registry. If we're in a nested
// context, the data will differ from the one selected from the root
// registry.
getBlock: getBlock.bind(null, state),
getBlockParentsByBlockName: getBlockParentsByBlockName.bind(
null,
state
)
}
);
};
var canInsertBlockType = createRegistrySelector(
(select) => createSelector(
canInsertBlockTypeUnmemoized,
(state, blockName, rootClientId) => getInsertBlockTypeDependants(select)(state, rootClientId)
)
);
function canInsertBlocks(state, clientIds, rootClientId = null) {
return clientIds.every(
(id) => canInsertBlockType(state, getBlockName(state, id), rootClientId)
);
}
function canRemoveBlock(state, clientId) {
const attributes = getBlockAttributes(state, clientId);
if (attributes === null) {
return true;
}
if (attributes.lock?.remove !== void 0) {
return !attributes.lock.remove;
}
const rootClientId = getBlockRootClientId(state, clientId);
const rootTemplateLock = getTemplateLock(state, rootClientId);
if (rootTemplateLock && rootTemplateLock !== "contentOnly") {
return false;
}
const isBlockWithinSection = !!getParentSectionBlock(state, clientId);
const isContentRoleBlock = isContentBlock(
getBlockName(state, clientId)
);
if (isBlockWithinSection && !isContentRoleBlock) {
return false;
}
const isParentSectionBlock = !!isSectionBlock(state, rootClientId);
const rootBlockEditingMode = getBlockEditingMode(state, rootClientId);
if ((isParentSectionBlock || rootBlockEditingMode === "contentOnly") && !isContainerInsertableToInContentOnlyMode(
state,
getBlockName(state, clientId),
rootClientId
)) {
return false;
}
return rootBlockEditingMode !== "disabled";
}
function canRemoveBlocks(state, clientIds) {
return clientIds.every((clientId) => canRemoveBlock(state, clientId));
}
function canMoveBlock(state, clientId) {
const attributes = getBlockAttributes(state, clientId);
if (attributes === null) {
return true;
}
if (attributes.lock?.move !== void 0) {
return !attributes.lock.move;
}
const rootClientId = getBlockRootClientId(state, clientId);
const rootTemplateLock = getTemplateLock(state, rootClientId);
if (rootTemplateLock === "all") {
return false;
}
const isBlockWithinSection = !!getParentSectionBlock(state, clientId);
const isContentRoleBlock = isContentBlock(
getBlockName(state, clientId)
);
if (isBlockWithinSection && !isContentRoleBlock) {
return false;
}
const isParentSectionBlock = !!isSectionBlock(state, rootClientId);
const rootBlockEditingMode = getBlockEditingMode(state, rootClientId);
if ((isParentSectionBlock || rootBlockEditingMode === "contentOnly") && !isContainerInsertableToInContentOnlyMode(
state,
getBlockName(state, clientId),
rootClientId
)) {
return false;
}
return getBlockEditingMode(state, rootClientId) !== "disabled";
}
function canMoveBlocks(state, clientIds) {
return clientIds.every((clientId) => canMoveBlock(state, clientId));
}
function canEditBlock(state, clientId) {
const attributes = getBlockAttributes(state, clientId);
if (attributes === null) {
return true;
}
const { lock } = attributes;
return !lock?.edit;
}
function canLockBlockType(state, nameOrType) {
if (!hasBlockSupport(nameOrType, "lock", true)) {
return false;
}
return !!state.settings?.canLockBlocks;
}
function getInsertUsage(state, id) {
return state.preferences.insertUsage?.[id] ?? null;
}
var canIncludeBlockTypeInInserter = (state, blockType, rootClientId) => {
if (!hasBlockSupport(blockType, "inserter", true)) {
return false;
}
return canInsertBlockTypeUnmemoized(state, blockType.name, rootClientId);
};
var getItemFromVariation = (state, item) => (variation) => {
const variationId = `${item.id}/${variation.name}`;
const { time, count = 0 } = getInsertUsage(state, variationId) || {};
return {
...item,
id: variationId,
icon: variation.icon || item.icon,
title: variation.title || item.title,
description: variation.description || item.description,
category: variation.category || item.category,
// If `example` is explicitly undefined for the variation, the preview will not be shown.
example: variation.hasOwnProperty("example") ? variation.example : item.example,
initialAttributes: {
...item.initialAttributes,
...variation.attributes
},
innerBlocks: variation.innerBlocks,
keywords: variation.keywords || item.keywords,
frecency: calculateFrecency(time, count)
};
};
var calculateFrecency = (time, count) => {
if (!time) {
return count;
}
const duration = Date.now() - time;
switch (true) {
case duration < MILLISECONDS_PER_HOUR:
return count * 4;
case duration < MILLISECONDS_PER_DAY:
return count * 2;
case duration < MILLISECONDS_PER_WEEK:
return count / 2;
default:
return count / 4;
}
};
var buildBlockTypeItem = (state, { buildScope = "inserter" }) => (blockType) => {
const id = blockType.name;
let isDisabled = false;
if (!hasBlockSupport(blockType.name, "multiple", true)) {
isDisabled = getBlocksByClientId(
state,
getClientIdsWithDescendants(state)
).some(({ name }) => name === blockType.name);
}
const { time, count = 0 } = getInsertUsage(state, id) || {};
const blockItemBase = {
id,
name: blockType.name,
title: blockType.title,
icon: blockType.icon,
isDisabled,
frecency: calculateFrecency(time, count)
};
if (buildScope === "transform") {
return blockItemBase;
}
const inserterVariations = getBlockVariations(
blockType.name,
"inserter"
);
return {
...blockItemBase,
initialAttributes: {},
description: blockType.description,
category: blockType.category,
keywords: blockType.keywords,
parent: blockType.parent,
ancestor: blockType.ancestor,
variations: inserterVariations,
example: blockType.example,
utility: 1
// Deprecated.
};
};
var getInserterItems = createRegistrySelector(
(select) => createSelector(
(state, rootClientId = null, options = DEFAULT_INSERTER_OPTIONS) => {
const buildReusableBlockInserterItem = (reusableBlock) => {
const icon = !reusableBlock.wp_pattern_sync_status ? {
src: symbol,
foreground: "var(--wp-block-synced-color)"
} : symbol;
const userPattern = mapUserPattern(reusableBlock);
const { time, count = 0 } = getInsertUsage(state, userPattern.name) || {};
const frecency = calculateFrecency(time, count);
return {
id: userPattern.name,
name: "core/block",
initialAttributes: { ref: reusableBlock.id },
title: userPattern.title,
icon,
category: "reusable",
keywords: ["reusable"],
isDisabled: false,
utility: 1,
// Deprecated.
frecency,
content: userPattern.content,
get blocks() {
return getParsedPattern(userPattern).blocks;
},
syncStatus: userPattern.syncStatus
};
};
const patternInserterItems = canInsertBlockTypeUnmemoized(
state,
"core/block",
rootClientId
) ? unlock(select(STORE_NAME)).getReusableBlocks().map(buildReusableBlockInserterItem) : [];
const buildBlockTypeInserterItem = buildBlockTypeItem(state, {
buildScope: "inserter"
});
let blockTypeInserterItems = getBlockTypes().filter(
(blockType) => hasBlockSupport(blockType, "inserter", true)
).map(buildBlockTypeInserterItem);
if (options[isFiltered] !== false) {
blockTypeInserterItems = blockTypeInserterItems.filter(
(blockType) => canIncludeBlockTypeInInserter(
state,
blockType,
rootClientId
)
);
} else {
blockTypeInserterItems = blockTypeInserterItems.filter(
(blockType) => isBlockVisibleInTheInserter(
state,
blockType,
rootClientId
)
).map((blockType) => ({
...blockType,
isAllowedInCurrentRoot: canIncludeBlockTypeInInserter(
state,
blockType,
rootClientId
)
}));
}
const stretchVariations = [];
const items = blockTypeInserterItems.reduce(
(accumulator, item) => {
const { variations = [] } = item;
if (!variations.some(({ isDefault }) => isDefault)) {
accumulator.push(item);
}
if (variations.length) {
const variationMapper = getItemFromVariation(
state,
item
);
variations.map(variationMapper).forEach((variation) => {
if (variation.id === "core/paragraph/stretchy-paragraph" || variation.id === "core/heading/stretchy-heading") {
stretchVariations.push(variation);
} else {
accumulator.push(variation);
}
});
}
return accumulator;
},
[]
);
items.push(...stretchVariations);
const groupByType = (blocks, block) => {
const { core, noncore } = blocks;
const type = block.name.startsWith("core/") ? core : noncore;
type.push(block);
return blocks;
};
const { core: coreItems, noncore: nonCoreItems } = items.reduce(
groupByType,
{ core: [], noncore: [] }
);
const sortedBlockTypes = [...coreItems, ...nonCoreItems];
return [...sortedBlockTypes, ...patternInserterItems];
},
(state, rootClientId) => [
getBlockTypes(),
unlock(select(STORE_NAME)).getReusableBlocks(),
state.blocks.order,
state.preferences.insertUsage,
...getInsertBlockTypeDependants(select)(state, rootClientId)
]
)
);
var getBlockTransformItems = createRegistrySelector(
(select) => createSelector(
(state, blocks, rootClientId = null) => {
const normalizedBlocks = Array.isArray(blocks) ? blocks : [blocks];
const buildBlockTypeTransformItem = buildBlockTypeItem(state, {
buildScope: "transform"
});
const blockTypeTransformItems = getBlockTypes().filter(
(blockType) => canIncludeBlockTypeInInserter(
state,
blockType,
rootClientId
)
).map(buildBlockTypeTransformItem);
const itemsByName = Object.fromEntries(
Object.entries(blockTypeTransformItems).map(
([, value]) => [value.name, value]
)
);
const possibleTransforms = getPossibleBlockTransformations(
normalizedBlocks
).reduce((accumulator, block) => {
if (itemsByName[block?.name]) {
accumulator.push(itemsByName[block.name]);
}
return accumulator;
}, []);
return orderBy(
possibleTransforms,
(block) => itemsByName[block.name].frecency,
"desc"
);
},
(state, blocks, rootClientId) => [
getBlockTypes(),
state.preferences.insertUsage,
...getInsertBlockTypeDependants(select)(state, rootClientId)
]
)
);
var hasInserterItems = (state, rootClientId = null) => {
const hasBlockType = getBlockTypes().some(
(blockType) => canIncludeBlockTypeInInserter(state, blockType, rootClientId)
);
if (hasBlockType) {
return true;
}
const hasReusableBlock = canInsertBlockTypeUnmemoized(
state,
"core/block",
rootClientId
);
return hasReusableBlock;
};
var getAllowedBlocks = createRegistrySelector(
(select) => createSelector(
(state, rootClientId = null) => {
if (!rootClientId) {
return;
}
const blockTypes = getBlockTypes().filter(
(blockType) => canIncludeBlockTypeInInserter(state, blockType, rootClientId)
);
const hasReusableBlock = canInsertBlockTypeUnmemoized(
state,
"core/block",
rootClientId
);
if (hasReusableBlock) {
blockTypes.push("core/block");
}
return blockTypes;
},
(state, rootClientId) => [
getBlockTypes(),
...getInsertBlockTypeDependants(select)(state, rootClientId)
]
)
);
var __experimentalGetAllowedBlocks = createSelector(
(state, rootClientId = null) => {
deprecated(
'wp.data.select( "core/block-editor" ).__experimentalGetAllowedBlocks',
{
alternative: 'wp.data.select( "core/block-editor" ).getAllowedBlocks',
since: "6.2",
version: "6.4"
}
);
return getAllowedBlocks(state, rootClientId);
},
(state, rootClientId) => getAllowedBlocks.getDependants(state, rootClientId)
);
function getDirectInsertBlock(state, rootClientId = null) {
if (!rootClientId) {
return;
}
const { defaultBlock, directInsert } = state.blockListSettings[rootClientId] ?? {};
if (!defaultBlock || !directInsert) {
return;
}
return defaultBlock;
}
function __experimentalGetDirectInsertBlock(state, rootClientId = null) {
deprecated(
'wp.data.select( "core/block-editor" ).__experimentalGetDirectInsertBlock',
{
alternative: 'wp.data.select( "core/block-editor" ).getDirectInsertBlock',
since: "6.3",
version: "6.4"
}
);
return getDirectInsertBlock(state, rootClientId);
}
var __experimentalGetParsedPattern = createRegistrySelector(
(select) => (state, patternName) => {
const pattern = unlock(select(STORE_NAME)).getPatternBySlug(
patternName
);
return pattern ? getParsedPattern(pattern) : null;
}
);
var getAllowedPatternsDependants = (select) => (state, rootClientId) => [
...getAllPatternsDependants(select)(state),
...getInsertBlockTypeDependants(select)(state, rootClientId)
];
var patternsWithParsedBlocks = /* @__PURE__ */ new WeakMap();
function enhancePatternWithParsedBlocks(pattern) {
let enhancedPattern = patternsWithParsedBlocks.get(pattern);
if (!enhancedPattern) {
enhancedPattern = {
...pattern,
get blocks() {
return getParsedPattern(pattern).blocks;
}
};
patternsWithParsedBlocks.set(pattern, enhancedPattern);
}
return enhancedPattern;
}
var __experimentalGetAllowedPatterns = createRegistrySelector(
(select) => {
return createSelector(
(state, rootClientId = null, options = DEFAULT_INSERTER_OPTIONS) => {
const { getAllPatterns } = unlock(select(STORE_NAME));
const patterns = getAllPatterns();
const { allowedBlockTypes } = getSettings(state);
const parsedPatterns = patterns.filter(({ inserter = true }) => !!inserter).map(enhancePatternWithParsedBlocks);
const availableParsedPatterns = parsedPatterns.filter(
(pattern) => checkAllowListRecursive(
getGrammar(pattern),
allowedBlockTypes
)
);
const patternsAllowed = availableParsedPatterns.filter(
(pattern) => getGrammar(pattern).every(
({ blockName: name }) => options[isFiltered] !== false ? canInsertBlockType(
state,
name,
rootClientId
) : isBlockVisibleInTheInserter(
state,
name,
rootClientId
)
)
);
return patternsAllowed;
},
getAllowedPatternsDependants(select)
);
}
);
var getPatternsByBlockTypes = createRegistrySelector(
(select) => createSelector(
(state, blockNames, rootClientId = null) => {
if (!blockNames) {
return EMPTY_ARRAY;
}
const patterns = select(STORE_NAME).__experimentalGetAllowedPatterns(
rootClientId
);
const normalizedBlockNames = Array.isArray(blockNames) ? blockNames : [blockNames];
const filteredPatterns = patterns.filter(
(pattern) => pattern?.blockTypes?.some?.(
(blockName) => normalizedBlockNames.includes(blockName)
)
);
if (filteredPatterns.length === 0) {
return EMPTY_ARRAY;
}
return filteredPatterns;
},
(state, blockNames, rootClientId) => getAllowedPatternsDependants(select)(state, rootClientId)
)
);
var __experimentalGetPatternsByBlockTypes = createRegistrySelector(
(select) => {
deprecated(
'wp.data.select( "core/block-editor" ).__experimentalGetPatternsByBlockTypes',
{
alternative: 'wp.data.select( "core/block-editor" ).getPatternsByBlockTypes',
since: "6.2",
version: "6.4"
}
);
return select(STORE_NAME).getPatternsByBlockTypes;
}
);
var __experimentalGetPatternTransformItems = createRegistrySelector(
(select) => createSelector(
(state, blocks, rootClientId = null) => {
if (!blocks) {
return EMPTY_ARRAY;
}
if (blocks.some(
({ clientId, innerBlocks }) => innerBlocks.length || areInnerBlocksControlled(state, clientId)
)) {
return EMPTY_ARRAY;
}
const selectedBlockNames = Array.from(
new Set(blocks.map(({ name }) => name))
);
return select(STORE_NAME).getPatternsByBlockTypes(
selectedBlockNames,
rootClientId
);
},
(state, blocks, rootClientId) => getAllowedPatternsDependants(select)(state, rootClientId)
)
);
function getBlockListSettings(state, clientId) {
return state.blockListSettings[clientId];
}
function getSettings(state) {
return state.settings;
}
function isLastBlockChangePersistent(state) {
return state.blocks.isPersistentChange;
}
var __experimentalGetBlockListSettingsForBlocks = createSelector(
(state, clientIds = []) => {
return clientIds.reduce((blockListSettingsForBlocks, clientId) => {
if (!state.blockListSettings[clientId]) {
return blockListSettingsForBlocks;
}
return {
...blockListSettingsForBlocks,
[clientId]: state.blockListSettings[clientId]
};
}, {});
},
(state) => [state.blockListSettings]
);
var __experimentalGetReusableBlockTitle = createRegistrySelector(
(select) => createSelector(
(state, ref) => {
deprecated(
"wp.data.select( 'core/block-editor' ).__experimentalGetReusableBlockTitle",
{
since: "6.6",
version: "6.8"
}
);
const reusableBlock = unlock(select(STORE_NAME)).getReusableBlocks().find((block) => block.id === ref);
if (!reusableBlock) {
return null;
}
return reusableBlock.title?.raw;
},
() => [unlock(select(STORE_NAME)).getReusableBlocks()]
)
);
function __unstableIsLastBlockChangeIgnored(state) {
return state.blocks.isIgnoredChange;
}
function __experimentalGetLastBlockAttributeChanges(state) {
return state.lastBlockAttributesChange;
}
function hasBlockMovingClientId() {
deprecated(
'wp.data.select( "core/block-editor" ).hasBlockMovingClientId',
{
since: "6.7",
hint: "Block moving mode feature has been removed"
}
);
return false;
}
function didAutomaticChange(state) {
return !!state.automaticChangeStatus;
}
function isBlockHighlighted(state, clientId) {
return state.highlightedBlock === clientId;
}
function areInnerBlocksControlled(state, clientId) {
return !!state.blocks.controlledInnerBlocks[clientId];
}
var __experimentalGetActiveBlockIdByBlockNames = createSelector(
(state, validBlockNames) => {
if (!validBlockNames.length) {
return null;
}
const selectedBlockClientId = getSelectedBlockClientId(state);
if (validBlockNames.includes(
getBlockName(state, selectedBlockClientId)
)) {
return selectedBlockClientId;
}
const multiSelectedBlockClientIds = getMultiSelectedBlockClientIds(state);
const entityAreaParents = getBlockParentsByBlockName(
state,
selectedBlockClientId || multiSelectedBlockClientIds[0],
validBlockNames
);
if (entityAreaParents) {
return entityAreaParents[entityAreaParents.length - 1];
}
return null;
},
(state, validBlockNames) => [
state.selection.selectionStart.clientId,
state.selection.selectionEnd.clientId,
validBlockNames
]
);
function wasBlockJustInserted(state, clientId, source) {
const { lastBlockInserted } = state;
return lastBlockInserted.clientIds?.includes(clientId) && lastBlockInserted.source === source;
}
function isBlockVisible(state, clientId) {
return state.blockVisibility?.[clientId] ?? true;
}
function getHoveredBlockClientId() {
deprecated(
"wp.data.select( 'core/block-editor' ).getHoveredBlockClientId",
{
since: "6.9",
version: "7.1"
}
);
return void 0;
}
var __unstableGetVisibleBlocks = createSelector(
(state) => {
const visibleBlocks = new Set(
Object.keys(state.blockVisibility).filter(
(key) => state.blockVisibility[key]
)
);
if (visibleBlocks.size === 0) {
return EMPTY_SET;
}
return visibleBlocks;
},
(state) => [state.blockVisibility]
);
function __unstableHasActiveBlockOverlayActive(state, clientId) {
if (getBlockEditingMode(state, clientId) !== "default") {
return false;
}
if (!canEditBlock(state, clientId)) {
return true;
}
if (isZoomOut(state)) {
const sectionRootClientId = getSectionRootClientId(state);
if (sectionRootClientId) {
const sectionClientIds = getBlockOrder(
state,
sectionRootClientId
);
if (sectionClientIds?.includes(clientId)) {
return true;
}
} else if (clientId && !getBlockRootClientId(state, clientId)) {
return true;
}
}
const blockSupportDisable = hasBlockSupport(
getBlockName(state, clientId),
"__experimentalDisableBlockOverlay",
false
);
const shouldEnableIfUnselected = blockSupportDisable ? false : areInnerBlocksControlled(state, clientId);
return shouldEnableIfUnselected && !isBlockSelected(state, clientId) && !hasSelectedInnerBlock(state, clientId, true);
}
function __unstableIsWithinBlockOverlay(state, clientId) {
let parent = state.blocks.parents.get(clientId);
while (!!parent) {
if (__unstableHasActiveBlockOverlayActive(state, parent)) {
return true;
}
parent = state.blocks.parents.get(parent);
}
return false;
}
function getBlockEditingMode(state, clientId = "") {
if (clientId === null) {
clientId = "";
}
if (state.derivedBlockEditingModes?.has(clientId)) {
return state.derivedBlockEditingModes.get(clientId);
}
if (state.blockEditingModes.has(clientId)) {
return state.blockEditingModes.get(clientId);
}
return "default";
}
var isUngroupable = createRegistrySelector(
(select) => (state, clientId = "") => {
const _clientId = clientId || getSelectedBlockClientId(state);
if (!_clientId) {
return false;
}
if (isSectionBlock(state, _clientId)) {
return false;
}
const { getGroupingBlockName } = select(blocksStore);
const block = getBlock(state, _clientId);
const groupingBlockName = getGroupingBlockName();
const _isUngroupable = block && (block.name === groupingBlockName || getBlockType(block.name)?.transforms?.ungroup) && !!block.innerBlocks.length;
return _isUngroupable && canRemoveBlock(state, _clientId);
}
);
var isGroupable = createRegistrySelector(
(select) => (state, clientIds = EMPTY_ARRAY) => {
const { getGroupingBlockName } = select(blocksStore);
const groupingBlockName = getGroupingBlockName();
const _clientIds = clientIds?.length ? clientIds : getSelectedBlockClientIds(state);
const rootClientId = _clientIds?.length ? getBlockRootClientId(state, _clientIds[0]) : void 0;
const groupingBlockAvailable = canInsertBlockType(