@wordpress/block-editor
Version:
1,606 lines (1,604 loc) • 61.8 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// packages/block-editor/src/store/reducer.js
var reducer_exports = {};
__export(reducer_exports, {
blockEditingModes: () => blockEditingModes,
blockListSettings: () => blockListSettings,
blockVisibility: () => blockVisibility,
blocks: () => blocks,
blocksMode: () => blocksMode,
default: () => reducer_default,
draggedBlocks: () => draggedBlocks,
editedContentOnlySection: () => editedContentOnlySection,
expandedBlock: () => expandedBlock,
hasBlockSpotlight: () => hasBlockSpotlight,
hasSameKeys: () => hasSameKeys,
highlightedBlock: () => highlightedBlock,
initialPosition: () => initialPosition,
insertionCue: () => insertionCue,
insertionPoint: () => insertionPoint,
isBlockInterfaceHidden: () => isBlockInterfaceHidden,
isDragging: () => isDragging,
isMultiSelecting: () => isMultiSelecting,
isSelectionEnabled: () => isSelectionEnabled,
isTyping: () => isTyping,
isUpdatingSameBlockAttribute: () => isUpdatingSameBlockAttribute,
lastBlockAttributesChange: () => lastBlockAttributesChange,
lastBlockInserted: () => lastBlockInserted,
lastFocus: () => lastFocus,
openedBlockSettingsMenu: () => openedBlockSettingsMenu,
preferences: () => preferences,
registeredInserterMediaCategories: () => registeredInserterMediaCategories,
selection: () => selection,
settings: () => settings,
styleOverrides: () => styleOverrides,
template: () => template,
withDerivedBlockEditingModes: () => withDerivedBlockEditingModes,
zoomLevel: () => zoomLevel
});
module.exports = __toCommonJS(reducer_exports);
var import_es6 = __toESM(require("fast-deep-equal/es6"));
var import_compose = require("@wordpress/compose");
var import_data = require("@wordpress/data");
var import_deprecated = __toESM(require("@wordpress/deprecated"));
var import_blocks = require("@wordpress/blocks");
var import_defaults = require("./defaults");
var import_array = require("./array");
var import_private_keys = require("./private-keys");
var import_lock_unlock = require("../lock-unlock");
var { isContentBlock } = (0, import_lock_unlock.unlock)(import_blocks.privateApis);
var identity = (x) => x;
function mapBlockOrder(blocks2, rootClientId = "") {
const result = /* @__PURE__ */ new Map();
const current = [];
result.set(rootClientId, current);
blocks2.forEach((block) => {
const { clientId, innerBlocks } = block;
current.push(clientId);
mapBlockOrder(innerBlocks, clientId).forEach(
(order, subClientId) => {
result.set(subClientId, order);
}
);
});
return result;
}
function mapBlockParents(blocks2, rootClientId = "") {
const result = [];
const stack = [[rootClientId, blocks2]];
while (stack.length) {
const [parent, currentBlocks] = stack.shift();
currentBlocks.forEach(({ innerBlocks, ...block }) => {
result.push([block.clientId, parent]);
if (innerBlocks?.length) {
stack.push([block.clientId, innerBlocks]);
}
});
}
return result;
}
function flattenBlocks(blocks2, transform = identity) {
const result = [];
const stack = [...blocks2];
while (stack.length) {
const { innerBlocks, ...block } = stack.shift();
stack.push(...innerBlocks);
result.push([block.clientId, transform(block)]);
}
return result;
}
function getFlattenedClientIds(blocks2) {
const result = {};
const stack = [...blocks2];
while (stack.length) {
const { innerBlocks, ...block } = stack.shift();
stack.push(...innerBlocks);
result[block.clientId] = true;
}
return result;
}
function getFlattenedBlocksWithoutAttributes(blocks2) {
return flattenBlocks(blocks2, (block) => {
const { attributes, ...restBlock } = block;
return restBlock;
});
}
function getFlattenedBlockAttributes(blocks2) {
return flattenBlocks(blocks2, (block) => block.attributes);
}
function hasSameKeys(a, b) {
return (0, import_es6.default)(Object.keys(a), Object.keys(b));
}
function isUpdatingSameBlockAttribute(action, lastAction) {
return action.type === "UPDATE_BLOCK_ATTRIBUTES" && lastAction !== void 0 && lastAction.type === "UPDATE_BLOCK_ATTRIBUTES" && (0, import_es6.default)(action.clientIds, lastAction.clientIds) && hasSameKeys(action.attributes, lastAction.attributes);
}
function updateBlockTreeForBlocks(state, blocks2) {
const treeToUpdate = state.tree;
const stack = [...blocks2];
const flattenedBlocks = [...blocks2];
while (stack.length) {
const block = stack.shift();
stack.push(...block.innerBlocks);
flattenedBlocks.push(...block.innerBlocks);
}
for (const block of flattenedBlocks) {
treeToUpdate.set(block.clientId, {});
}
for (const block of flattenedBlocks) {
treeToUpdate.set(
block.clientId,
Object.assign(treeToUpdate.get(block.clientId), {
...state.byClientId.get(block.clientId),
attributes: state.attributes.get(block.clientId),
innerBlocks: block.innerBlocks.map(
(subBlock) => treeToUpdate.get(subBlock.clientId)
)
})
);
}
}
function updateParentInnerBlocksInTree(state, updatedClientIds, updateChildrenOfUpdatedClientIds = false) {
const treeToUpdate = state.tree;
const uncontrolledParents = /* @__PURE__ */ new Set([]);
const controlledParents = /* @__PURE__ */ new Set();
for (const clientId of updatedClientIds) {
let current = updateChildrenOfUpdatedClientIds ? clientId : state.parents.get(clientId);
do {
if (state.controlledInnerBlocks[current]) {
controlledParents.add(current);
break;
} else {
uncontrolledParents.add(current);
current = state.parents.get(current);
}
} while (current !== void 0);
}
for (const clientId of uncontrolledParents) {
treeToUpdate.set(clientId, { ...treeToUpdate.get(clientId) });
}
for (const clientId of uncontrolledParents) {
treeToUpdate.get(clientId).innerBlocks = (state.order.get(clientId) || []).map((subClientId) => treeToUpdate.get(subClientId));
}
for (const clientId of controlledParents) {
treeToUpdate.set("controlled||" + clientId, {
innerBlocks: (state.order.get(clientId) || []).map(
(subClientId) => treeToUpdate.get(subClientId)
)
});
}
}
var withBlockTree = (reducer) => (state = {}, action) => {
const newState = reducer(state, action);
if (newState === state) {
return state;
}
newState.tree = state.tree ? state.tree : /* @__PURE__ */ new Map();
switch (action.type) {
case "RECEIVE_BLOCKS":
case "INSERT_BLOCKS": {
newState.tree = new Map(newState.tree);
updateBlockTreeForBlocks(newState, action.blocks);
updateParentInnerBlocksInTree(
newState,
action.rootClientId ? [action.rootClientId] : [""],
true
);
break;
}
case "UPDATE_BLOCK":
newState.tree = new Map(newState.tree);
newState.tree.set(action.clientId, {
...newState.tree.get(action.clientId),
...newState.byClientId.get(action.clientId),
attributes: newState.attributes.get(action.clientId)
});
updateParentInnerBlocksInTree(
newState,
[action.clientId],
false
);
break;
case "SYNC_DERIVED_BLOCK_ATTRIBUTES":
case "UPDATE_BLOCK_ATTRIBUTES": {
newState.tree = new Map(newState.tree);
action.clientIds.forEach((clientId) => {
newState.tree.set(clientId, {
...newState.tree.get(clientId),
attributes: newState.attributes.get(clientId)
});
});
updateParentInnerBlocksInTree(
newState,
action.clientIds,
false
);
break;
}
case "REPLACE_BLOCKS_AUGMENTED_WITH_CHILDREN": {
const inserterClientIds = getFlattenedClientIds(
action.blocks
);
newState.tree = new Map(newState.tree);
action.replacedClientIds.forEach((clientId) => {
newState.tree.delete(clientId);
if (!inserterClientIds[clientId]) {
newState.tree.delete("controlled||" + clientId);
}
});
updateBlockTreeForBlocks(newState, action.blocks);
updateParentInnerBlocksInTree(
newState,
action.blocks.map((b) => b.clientId),
false
);
const parentsOfRemovedBlocks2 = [];
for (const clientId of action.clientIds) {
const parentId = state.parents.get(clientId);
if (parentId !== void 0 && (parentId === "" || newState.byClientId.get(parentId))) {
parentsOfRemovedBlocks2.push(parentId);
}
}
updateParentInnerBlocksInTree(
newState,
parentsOfRemovedBlocks2,
true
);
break;
}
case "REMOVE_BLOCKS_AUGMENTED_WITH_CHILDREN":
const parentsOfRemovedBlocks = [];
for (const clientId of action.clientIds) {
const parentId = state.parents.get(clientId);
if (parentId !== void 0 && (parentId === "" || newState.byClientId.get(parentId))) {
parentsOfRemovedBlocks.push(parentId);
}
}
newState.tree = new Map(newState.tree);
action.removedClientIds.forEach((clientId) => {
newState.tree.delete(clientId);
newState.tree.delete("controlled||" + clientId);
});
updateParentInnerBlocksInTree(
newState,
parentsOfRemovedBlocks,
true
);
break;
case "MOVE_BLOCKS_TO_POSITION": {
const updatedBlockUids = [];
if (action.fromRootClientId) {
updatedBlockUids.push(action.fromRootClientId);
} else {
updatedBlockUids.push("");
}
if (action.toRootClientId) {
updatedBlockUids.push(action.toRootClientId);
}
newState.tree = new Map(newState.tree);
updateParentInnerBlocksInTree(
newState,
updatedBlockUids,
true
);
break;
}
case "MOVE_BLOCKS_UP":
case "MOVE_BLOCKS_DOWN": {
const updatedBlockUids = [
action.rootClientId ? action.rootClientId : ""
];
newState.tree = new Map(newState.tree);
updateParentInnerBlocksInTree(
newState,
updatedBlockUids,
true
);
break;
}
case "SAVE_REUSABLE_BLOCK_SUCCESS": {
const updatedBlockUids = [];
newState.attributes.forEach((attributes, clientId) => {
if (newState.byClientId.get(clientId).name === "core/block" && attributes.ref === action.updatedId) {
updatedBlockUids.push(clientId);
}
});
newState.tree = new Map(newState.tree);
updatedBlockUids.forEach((clientId) => {
newState.tree.set(clientId, {
...newState.byClientId.get(clientId),
attributes: newState.attributes.get(clientId),
innerBlocks: newState.tree.get(clientId).innerBlocks
});
});
updateParentInnerBlocksInTree(
newState,
updatedBlockUids,
false
);
}
}
return newState;
};
function withPersistentBlockChange(reducer) {
let lastAction;
let markNextChangeAsNotPersistent = false;
let explicitPersistent;
return (state, action) => {
let nextState = reducer(state, action);
let nextIsPersistentChange;
if (action.type === "SET_EXPLICIT_PERSISTENT") {
explicitPersistent = action.isPersistentChange;
nextIsPersistentChange = state.isPersistentChange ?? true;
}
if (explicitPersistent !== void 0) {
nextIsPersistentChange = explicitPersistent;
return nextIsPersistentChange === nextState.isPersistentChange ? nextState : {
...nextState,
isPersistentChange: nextIsPersistentChange
};
}
const isExplicitPersistentChange = action.type === "MARK_LAST_CHANGE_AS_PERSISTENT" || markNextChangeAsNotPersistent;
if (state === nextState && !isExplicitPersistentChange) {
markNextChangeAsNotPersistent = action.type === "MARK_NEXT_CHANGE_AS_NOT_PERSISTENT";
nextIsPersistentChange = state?.isPersistentChange ?? true;
if (state.isPersistentChange === nextIsPersistentChange) {
return state;
}
return {
...nextState,
isPersistentChange: nextIsPersistentChange
};
}
nextState = {
...nextState,
isPersistentChange: isExplicitPersistentChange ? !markNextChangeAsNotPersistent : !isUpdatingSameBlockAttribute(action, lastAction)
};
lastAction = action;
markNextChangeAsNotPersistent = action.type === "MARK_NEXT_CHANGE_AS_NOT_PERSISTENT";
return nextState;
};
}
function withIgnoredBlockChange(reducer) {
const IGNORED_ACTION_TYPES = /* @__PURE__ */ new Set(["RECEIVE_BLOCKS"]);
return (state, action) => {
const nextState = reducer(state, action);
if (nextState !== state) {
nextState.isIgnoredChange = IGNORED_ACTION_TYPES.has(action.type);
}
return nextState;
};
}
var withInnerBlocksRemoveCascade = (reducer) => (state, action) => {
const getAllChildren = (clientIds) => {
let result = clientIds;
for (let i = 0; i < result.length; i++) {
if (!state.order.get(result[i]) || action.keepControlledInnerBlocks && action.keepControlledInnerBlocks[result[i]]) {
continue;
}
if (result === clientIds) {
result = [...result];
}
result.push(...state.order.get(result[i]));
}
return result;
};
if (state) {
switch (action.type) {
case "REMOVE_BLOCKS":
action = {
...action,
type: "REMOVE_BLOCKS_AUGMENTED_WITH_CHILDREN",
removedClientIds: getAllChildren(action.clientIds)
};
break;
case "REPLACE_BLOCKS":
action = {
...action,
type: "REPLACE_BLOCKS_AUGMENTED_WITH_CHILDREN",
replacedClientIds: getAllChildren(action.clientIds)
};
break;
}
}
return reducer(state, action);
};
var withBlockReset = (reducer) => (state, action) => {
if (action.type === "RESET_BLOCKS") {
const newState = {
...state,
byClientId: new Map(
getFlattenedBlocksWithoutAttributes(action.blocks)
),
attributes: new Map(getFlattenedBlockAttributes(action.blocks)),
order: mapBlockOrder(action.blocks),
parents: new Map(mapBlockParents(action.blocks)),
controlledInnerBlocks: {}
};
newState.tree = new Map(state?.tree);
updateBlockTreeForBlocks(newState, action.blocks);
newState.tree.set("", {
innerBlocks: action.blocks.map(
(subBlock) => newState.tree.get(subBlock.clientId)
)
});
return newState;
}
return reducer(state, action);
};
var withReplaceInnerBlocks = (reducer) => (state, action) => {
if (action.type !== "REPLACE_INNER_BLOCKS") {
return reducer(state, action);
}
const nestedControllers = {};
if (Object.keys(state.controlledInnerBlocks).length) {
const stack = [...action.blocks];
while (stack.length) {
const { innerBlocks, ...block } = stack.shift();
stack.push(...innerBlocks);
if (!!state.controlledInnerBlocks[block.clientId]) {
nestedControllers[block.clientId] = true;
}
}
}
let stateAfterBlocksRemoval = state;
if (state.order.get(action.rootClientId)) {
stateAfterBlocksRemoval = reducer(stateAfterBlocksRemoval, {
type: "REMOVE_BLOCKS",
keepControlledInnerBlocks: nestedControllers,
clientIds: state.order.get(action.rootClientId)
});
}
let stateAfterInsert = stateAfterBlocksRemoval;
if (action.blocks.length) {
stateAfterInsert = reducer(stateAfterInsert, {
...action,
type: "INSERT_BLOCKS",
index: 0
});
const stateAfterInsertOrder = new Map(stateAfterInsert.order);
Object.keys(nestedControllers).forEach((key) => {
if (state.order.get(key)) {
stateAfterInsertOrder.set(key, state.order.get(key));
}
});
stateAfterInsert.order = stateAfterInsertOrder;
stateAfterInsert.tree = new Map(stateAfterInsert.tree);
Object.keys(nestedControllers).forEach((_key) => {
const key = `controlled||${_key}`;
if (state.tree.has(key)) {
stateAfterInsert.tree.set(key, state.tree.get(key));
}
});
}
return stateAfterInsert;
};
var withSaveReusableBlock = (reducer) => (state, action) => {
if (state && action.type === "SAVE_REUSABLE_BLOCK_SUCCESS") {
const { id, updatedId } = action;
if (id === updatedId) {
return state;
}
state = { ...state };
state.attributes = new Map(state.attributes);
state.attributes.forEach((attributes, clientId) => {
const { name } = state.byClientId.get(clientId);
if (name === "core/block" && attributes.ref === id) {
state.attributes.set(clientId, {
...attributes,
ref: updatedId
});
}
});
}
return reducer(state, action);
};
var withResetControlledBlocks = (reducer) => (state, action) => {
if (action.type === "SET_HAS_CONTROLLED_INNER_BLOCKS") {
const tempState = reducer(state, {
type: "REPLACE_INNER_BLOCKS",
rootClientId: action.clientId,
blocks: []
});
return reducer(tempState, action);
}
return reducer(state, action);
};
var blocks = (0, import_compose.pipe)(
import_data.combineReducers,
withSaveReusableBlock,
// Needs to be before withBlockCache.
withBlockTree,
// Needs to be before withInnerBlocksRemoveCascade.
withInnerBlocksRemoveCascade,
withReplaceInnerBlocks,
// Needs to be after withInnerBlocksRemoveCascade.
withBlockReset,
withPersistentBlockChange,
withIgnoredBlockChange,
withResetControlledBlocks
)({
// The state is using a Map instead of a plain object for performance reasons.
// You can run the "./test/performance.js" unit test to check the impact
// code changes can have on this reducer.
byClientId(state = /* @__PURE__ */ new Map(), action) {
switch (action.type) {
case "RECEIVE_BLOCKS":
case "INSERT_BLOCKS": {
const newState = new Map(state);
getFlattenedBlocksWithoutAttributes(action.blocks).forEach(
([key, value]) => {
newState.set(key, value);
}
);
return newState;
}
case "UPDATE_BLOCK": {
if (!state.has(action.clientId)) {
return state;
}
const { attributes, ...changes } = action.updates;
if (Object.values(changes).length === 0) {
return state;
}
const newState = new Map(state);
newState.set(action.clientId, {
...state.get(action.clientId),
...changes
});
return newState;
}
case "REPLACE_BLOCKS_AUGMENTED_WITH_CHILDREN": {
if (!action.blocks) {
return state;
}
const newState = new Map(state);
action.replacedClientIds.forEach((clientId) => {
newState.delete(clientId);
});
getFlattenedBlocksWithoutAttributes(action.blocks).forEach(
([key, value]) => {
newState.set(key, value);
}
);
return newState;
}
case "REMOVE_BLOCKS_AUGMENTED_WITH_CHILDREN": {
const newState = new Map(state);
action.removedClientIds.forEach((clientId) => {
newState.delete(clientId);
});
return newState;
}
}
return state;
},
// The state is using a Map instead of a plain object for performance reasons.
// You can run the "./test/performance.js" unit test to check the impact
// code changes can have on this reducer.
attributes(state = /* @__PURE__ */ new Map(), action) {
switch (action.type) {
case "RECEIVE_BLOCKS":
case "INSERT_BLOCKS": {
const newState = new Map(state);
getFlattenedBlockAttributes(action.blocks).forEach(
([key, value]) => {
newState.set(key, value);
}
);
return newState;
}
case "UPDATE_BLOCK": {
if (!state.get(action.clientId) || !action.updates.attributes) {
return state;
}
const newState = new Map(state);
newState.set(action.clientId, {
...state.get(action.clientId),
...action.updates.attributes
});
return newState;
}
case "SYNC_DERIVED_BLOCK_ATTRIBUTES":
case "UPDATE_BLOCK_ATTRIBUTES": {
if (action.clientIds.every((id) => !state.get(id))) {
return state;
}
let hasChange = false;
const newState = new Map(state);
for (const clientId of action.clientIds) {
const updatedAttributeEntries = Object.entries(
!!action.options?.uniqueByBlock ? action.attributes[clientId] : action.attributes ?? {}
);
if (updatedAttributeEntries.length === 0) {
continue;
}
let hasUpdatedAttributes = false;
const existingAttributes = state.get(clientId);
const newAttributes = {};
updatedAttributeEntries.forEach(([key, value]) => {
if (existingAttributes[key] !== value) {
hasUpdatedAttributes = true;
newAttributes[key] = value;
}
});
hasChange = hasChange || hasUpdatedAttributes;
if (hasUpdatedAttributes) {
newState.set(clientId, {
...existingAttributes,
...newAttributes
});
}
}
return hasChange ? newState : state;
}
case "REPLACE_BLOCKS_AUGMENTED_WITH_CHILDREN": {
if (!action.blocks) {
return state;
}
const newState = new Map(state);
action.replacedClientIds.forEach((clientId) => {
newState.delete(clientId);
});
getFlattenedBlockAttributes(action.blocks).forEach(
([key, value]) => {
newState.set(key, value);
}
);
return newState;
}
case "REMOVE_BLOCKS_AUGMENTED_WITH_CHILDREN": {
const newState = new Map(state);
action.removedClientIds.forEach((clientId) => {
newState.delete(clientId);
});
return newState;
}
}
return state;
},
// The state is using a Map instead of a plain object for performance reasons.
// You can run the "./test/performance.js" unit test to check the impact
// code changes can have on this reducer.
order(state = /* @__PURE__ */ new Map(), action) {
switch (action.type) {
case "RECEIVE_BLOCKS": {
const blockOrder = mapBlockOrder(action.blocks);
const newState = new Map(state);
blockOrder.forEach((order, clientId) => {
if (clientId !== "") {
newState.set(clientId, order);
}
});
newState.set(
"",
(state.get("") ?? []).concat(blockOrder[""])
);
return newState;
}
case "INSERT_BLOCKS": {
const { rootClientId = "" } = action;
const subState = state.get(rootClientId) || [];
const mappedBlocks = mapBlockOrder(
action.blocks,
rootClientId
);
const { index = subState.length } = action;
const newState = new Map(state);
mappedBlocks.forEach((order, clientId) => {
newState.set(clientId, order);
});
newState.set(
rootClientId,
(0, import_array.insertAt)(
subState,
mappedBlocks.get(rootClientId),
index
)
);
return newState;
}
case "MOVE_BLOCKS_TO_POSITION": {
const {
fromRootClientId = "",
toRootClientId = "",
clientIds
} = action;
const { index = state.get(toRootClientId).length } = action;
if (fromRootClientId === toRootClientId) {
const subState = state.get(toRootClientId);
const fromIndex = subState.indexOf(clientIds[0]);
const newState2 = new Map(state);
newState2.set(
toRootClientId,
(0, import_array.moveTo)(
state.get(toRootClientId),
fromIndex,
index,
clientIds.length
)
);
return newState2;
}
const newState = new Map(state);
newState.set(
fromRootClientId,
state.get(fromRootClientId)?.filter((id) => !clientIds.includes(id)) ?? []
);
newState.set(
toRootClientId,
(0, import_array.insertAt)(state.get(toRootClientId), clientIds, index)
);
return newState;
}
case "MOVE_BLOCKS_UP": {
const { clientIds, rootClientId = "" } = action;
const firstClientId = clientIds[0];
const subState = state.get(rootClientId);
if (!subState.length || firstClientId === subState[0]) {
return state;
}
const firstIndex = subState.indexOf(firstClientId);
const newState = new Map(state);
newState.set(
rootClientId,
(0, import_array.moveTo)(
subState,
firstIndex,
firstIndex - 1,
clientIds.length
)
);
return newState;
}
case "MOVE_BLOCKS_DOWN": {
const { clientIds, rootClientId = "" } = action;
const firstClientId = clientIds[0];
const lastClientId = clientIds[clientIds.length - 1];
const subState = state.get(rootClientId);
if (!subState.length || lastClientId === subState[subState.length - 1]) {
return state;
}
const firstIndex = subState.indexOf(firstClientId);
const newState = new Map(state);
newState.set(
rootClientId,
(0, import_array.moveTo)(
subState,
firstIndex,
firstIndex + 1,
clientIds.length
)
);
return newState;
}
case "REPLACE_BLOCKS_AUGMENTED_WITH_CHILDREN": {
const { clientIds } = action;
if (!action.blocks) {
return state;
}
const mappedBlocks = mapBlockOrder(action.blocks);
const newState = new Map(state);
action.replacedClientIds.forEach((clientId) => {
newState.delete(clientId);
});
mappedBlocks.forEach((order, clientId) => {
if (clientId !== "") {
newState.set(clientId, order);
}
});
newState.forEach((order, clientId) => {
const newSubOrder = Object.values(order).reduce(
(result, subClientId) => {
if (subClientId === clientIds[0]) {
return [...result, ...mappedBlocks.get("")];
}
if (clientIds.indexOf(subClientId) === -1) {
result.push(subClientId);
}
return result;
},
[]
);
newState.set(clientId, newSubOrder);
});
return newState;
}
case "REMOVE_BLOCKS_AUGMENTED_WITH_CHILDREN": {
const newState = new Map(state);
action.removedClientIds.forEach((clientId) => {
newState.delete(clientId);
});
newState.forEach((order, clientId) => {
const newSubOrder = order?.filter(
(id) => !action.removedClientIds.includes(id)
) ?? [];
if (newSubOrder.length !== order.length) {
newState.set(clientId, newSubOrder);
}
});
return newState;
}
}
return state;
},
// While technically redundant data as the inverse of `order`, it serves as
// an optimization for the selectors which derive the ancestry of a block.
parents(state = /* @__PURE__ */ new Map(), action) {
switch (action.type) {
case "RECEIVE_BLOCKS": {
const newState = new Map(state);
mapBlockParents(action.blocks).forEach(
([key, value]) => {
newState.set(key, value);
}
);
return newState;
}
case "INSERT_BLOCKS": {
const newState = new Map(state);
mapBlockParents(
action.blocks,
action.rootClientId || ""
).forEach(([key, value]) => {
newState.set(key, value);
});
return newState;
}
case "MOVE_BLOCKS_TO_POSITION": {
const newState = new Map(state);
action.clientIds.forEach((id) => {
newState.set(id, action.toRootClientId || "");
});
return newState;
}
case "REPLACE_BLOCKS_AUGMENTED_WITH_CHILDREN": {
const newState = new Map(state);
action.replacedClientIds.forEach((clientId) => {
newState.delete(clientId);
});
mapBlockParents(
action.blocks,
state.get(action.clientIds[0])
).forEach(([key, value]) => {
newState.set(key, value);
});
return newState;
}
case "REMOVE_BLOCKS_AUGMENTED_WITH_CHILDREN": {
const newState = new Map(state);
action.removedClientIds.forEach((clientId) => {
newState.delete(clientId);
});
return newState;
}
}
return state;
},
controlledInnerBlocks(state = {}, { type, clientId, hasControlledInnerBlocks }) {
if (type === "SET_HAS_CONTROLLED_INNER_BLOCKS") {
return {
...state,
[clientId]: hasControlledInnerBlocks
};
}
return state;
}
});
function isBlockInterfaceHidden(state = false, action) {
switch (action.type) {
case "HIDE_BLOCK_INTERFACE":
return true;
case "SHOW_BLOCK_INTERFACE":
return false;
}
return state;
}
function isTyping(state = false, action) {
switch (action.type) {
case "START_TYPING":
return true;
case "STOP_TYPING":
return false;
}
return state;
}
function isDragging(state = false, action) {
switch (action.type) {
case "START_DRAGGING":
return true;
case "STOP_DRAGGING":
return false;
}
return state;
}
function draggedBlocks(state = [], action) {
switch (action.type) {
case "START_DRAGGING_BLOCKS":
return action.clientIds;
case "STOP_DRAGGING_BLOCKS":
return [];
}
return state;
}
function blockVisibility(state = {}, action) {
if (action.type === "SET_BLOCK_VISIBILITY") {
return {
...state,
...action.updates
};
}
return state;
}
function selectionHelper(state = {}, action) {
switch (action.type) {
case "CLEAR_SELECTED_BLOCK": {
if (state.clientId) {
return {};
}
return state;
}
case "SELECT_BLOCK":
if (action.clientId === state.clientId) {
return state;
}
return { clientId: action.clientId };
case "REPLACE_INNER_BLOCKS":
case "INSERT_BLOCKS": {
if (!action.updateSelection || !action.blocks.length) {
return state;
}
return { clientId: action.blocks[0].clientId };
}
case "REMOVE_BLOCKS":
if (!action.clientIds || !action.clientIds.length || action.clientIds.indexOf(state.clientId) === -1) {
return state;
}
return {};
case "REPLACE_BLOCKS": {
if (action.clientIds.indexOf(state.clientId) === -1) {
return state;
}
const blockToSelect = action.blocks[action.indexToSelect] || action.blocks[action.blocks.length - 1];
if (!blockToSelect) {
return {};
}
if (blockToSelect.clientId === state.clientId) {
return state;
}
return { clientId: blockToSelect.clientId };
}
}
return state;
}
function selection(state = {}, action) {
switch (action.type) {
case "SELECTION_CHANGE":
if (action.clientId) {
return {
selectionStart: {
clientId: action.clientId,
attributeKey: action.attributeKey,
offset: action.startOffset
},
selectionEnd: {
clientId: action.clientId,
attributeKey: action.attributeKey,
offset: action.endOffset
}
};
}
return {
selectionStart: action.start || state.selectionStart,
selectionEnd: action.end || state.selectionEnd
};
case "RESET_SELECTION":
const { selectionStart: selectionStart2, selectionEnd: selectionEnd2 } = action;
return {
selectionStart: selectionStart2,
selectionEnd: selectionEnd2
};
case "MULTI_SELECT":
const { start, end } = action;
if (start === state.selectionStart?.clientId && end === state.selectionEnd?.clientId) {
return state;
}
return {
selectionStart: { clientId: start },
selectionEnd: { clientId: end }
};
case "RESET_BLOCKS":
const startClientId = state?.selectionStart?.clientId;
const endClientId = state?.selectionEnd?.clientId;
if (!startClientId && !endClientId) {
return state;
}
if (!action.blocks.some(
(block) => block.clientId === startClientId
)) {
return {
selectionStart: {},
selectionEnd: {}
};
}
if (!action.blocks.some(
(block) => block.clientId === endClientId
)) {
return {
...state,
selectionEnd: state.selectionStart
};
}
}
const selectionStart = selectionHelper(state.selectionStart, action);
const selectionEnd = selectionHelper(state.selectionEnd, action);
if (selectionStart === state.selectionStart && selectionEnd === state.selectionEnd) {
return state;
}
return {
selectionStart,
selectionEnd
};
}
function isMultiSelecting(state = false, action) {
switch (action.type) {
case "START_MULTI_SELECT":
return true;
case "STOP_MULTI_SELECT":
return false;
}
return state;
}
function isSelectionEnabled(state = true, action) {
switch (action.type) {
case "TOGGLE_SELECTION":
return action.isSelectionEnabled;
}
return state;
}
function removalPromptData(state = false, action) {
switch (action.type) {
case "DISPLAY_BLOCK_REMOVAL_PROMPT":
const { clientIds, selectPrevious, message } = action;
return {
clientIds,
selectPrevious,
message
};
case "CLEAR_BLOCK_REMOVAL_PROMPT":
return false;
}
return state;
}
function blockRemovalRules(state = false, action) {
switch (action.type) {
case "SET_BLOCK_REMOVAL_RULES":
return action.rules;
}
return state;
}
function initialPosition(state = null, action) {
if (action.type === "REPLACE_BLOCKS" && action.initialPosition !== void 0) {
return action.initialPosition;
} else if ([
"MULTI_SELECT",
"SELECT_BLOCK",
"RESET_SELECTION",
"INSERT_BLOCKS",
"REPLACE_INNER_BLOCKS"
].includes(action.type)) {
return action.initialPosition;
}
return state;
}
function blocksMode(state = {}, action) {
if (action.type === "TOGGLE_BLOCK_MODE") {
const { clientId } = action;
return {
...state,
[clientId]: state[clientId] && state[clientId] === "html" ? "visual" : "html"
};
}
return state;
}
function insertionCue(state = null, action) {
switch (action.type) {
case "SHOW_INSERTION_POINT": {
const {
rootClientId,
index,
__unstableWithInserter,
operation,
nearestSide
} = action;
const nextState = {
rootClientId,
index,
__unstableWithInserter,
operation,
nearestSide
};
return (0, import_es6.default)(state, nextState) ? state : nextState;
}
case "HIDE_INSERTION_POINT":
return null;
}
return state;
}
function template(state = { isValid: true }, action) {
switch (action.type) {
case "SET_TEMPLATE_VALIDITY":
return {
...state,
isValid: action.isValid
};
}
return state;
}
function settings(state = import_defaults.SETTINGS_DEFAULTS, action) {
switch (action.type) {
case "UPDATE_SETTINGS": {
const updatedSettings = action.reset ? {
...import_defaults.SETTINGS_DEFAULTS,
...action.settings
} : {
...state,
...action.settings
};
Object.defineProperty(updatedSettings, "__unstableIsPreviewMode", {
get() {
(0, import_deprecated.default)("__unstableIsPreviewMode", {
since: "6.8",
alternative: "isPreviewMode"
});
return this.isPreviewMode;
}
});
return updatedSettings;
}
}
return state;
}
function preferences(state = import_defaults.PREFERENCES_DEFAULTS, action) {
switch (action.type) {
case "INSERT_BLOCKS":
case "REPLACE_BLOCKS": {
const nextInsertUsage = action.blocks.reduce(
(prevUsage, block) => {
const { attributes, name: blockName } = block;
let id = blockName;
const match = (0, import_data.select)(import_blocks.store).getActiveBlockVariation(
blockName,
attributes
);
if (match?.name) {
id += "/" + match.name;
}
if (blockName === "core/block") {
id += "/" + attributes.ref;
}
return {
...prevUsage,
[id]: {
time: action.time,
count: prevUsage[id] ? prevUsage[id].count + 1 : 1
}
};
},
state.insertUsage
);
return {
...state,
insertUsage: nextInsertUsage
};
}
}
return state;
}
var blockListSettings = (state = {}, action) => {
switch (action.type) {
// Even if the replaced blocks have the same client ID, our logic
// should correct the state.
case "REPLACE_BLOCKS":
case "REMOVE_BLOCKS": {
return Object.fromEntries(
Object.entries(state).filter(
([id]) => !action.clientIds.includes(id)
)
);
}
case "UPDATE_BLOCK_LIST_SETTINGS": {
const updates = typeof action.clientId === "string" ? { [action.clientId]: action.settings } : action.clientId;
for (const clientId in updates) {
if (!updates[clientId]) {
if (!state[clientId]) {
delete updates[clientId];
}
} else if ((0, import_es6.default)(state[clientId], updates[clientId])) {
delete updates[clientId];
}
}
if (Object.keys(updates).length === 0) {
return state;
}
const merged = { ...state, ...updates };
for (const clientId in updates) {
if (!updates[clientId]) {
delete merged[clientId];
}
}
return merged;
}
}
return state;
};
function lastBlockAttributesChange(state = null, action) {
switch (action.type) {
case "UPDATE_BLOCK":
if (!action.updates.attributes) {
break;
}
return { [action.clientId]: action.updates.attributes };
case "UPDATE_BLOCK_ATTRIBUTES":
return action.clientIds.reduce(
(accumulator, id) => ({
...accumulator,
[id]: !!action.options?.uniqueByBlock ? action.attributes[id] : action.attributes
}),
{}
);
}
return state;
}
function highlightedBlock(state, action) {
switch (action.type) {
case "TOGGLE_BLOCK_HIGHLIGHT":
const { clientId, isHighlighted } = action;
if (isHighlighted) {
return clientId;
} else if (state === clientId) {
return null;
}
return state;
case "SELECT_BLOCK":
if (action.clientId !== state) {
return null;
}
}
return state;
}
function hasBlockSpotlight(state, action) {
switch (action.type) {
case "TOGGLE_BLOCK_SPOTLIGHT":
const { clientId, hasBlockSpotlight: _hasBlockSpotlight } = action;
if (_hasBlockSpotlight) {
return clientId;
} else if (state === clientId) {
return null;
}
return state;
case "SELECT_BLOCK":
if (action.clientId !== state) {
return null;
}
return state;
case "SELECTION_CHANGE":
if (action.start?.clientId !== state || action.end?.clientId !== state) {
return null;
}
return state;
case "CLEAR_SELECTED_BLOCK":
return null;
}
return state;
}
function expandedBlock(state = null, action) {
switch (action.type) {
case "SET_BLOCK_EXPANDED_IN_LIST_VIEW":
return action.clientId;
case "SELECT_BLOCK":
if (action.clientId !== state) {
return null;
}
}
return state;
}
function lastBlockInserted(state = {}, action) {
switch (action.type) {
case "INSERT_BLOCKS":
case "REPLACE_BLOCKS":
if (!action.blocks.length) {
return state;
}
const clientIds = action.blocks.map((block) => {
return block.clientId;
});
const source = action.meta?.source;
return { clientIds, source };
case "RESET_BLOCKS":
return {};
}
return state;
}
function editedContentOnlySection(state, action) {
if (action.type === "EDIT_CONTENT_ONLY_SECTION") {
return action.clientId;
}
return state;
}
function blockEditingModes(state = /* @__PURE__ */ new Map(), action) {
switch (action.type) {
case "SET_BLOCK_EDITING_MODE":
if (state.get(action.clientId) === action.mode) {
return state;
}
return new Map(state).set(action.clientId, action.mode);
case "UNSET_BLOCK_EDITING_MODE": {
if (!state.has(action.clientId)) {
return state;
}
const newState = new Map(state);
newState.delete(action.clientId);
return newState;
}
case "RESET_BLOCKS": {
return state.has("") ? (/* @__PURE__ */ new Map()).set("", state.get("")) : state;
}
}
return state;
}
function openedBlockSettingsMenu(state = null, action) {
if ("SET_OPENED_BLOCK_SETTINGS_MENU" === action.type) {
return action?.clientId ?? null;
}
return state;
}
function styleOverrides(state = /* @__PURE__ */ new Map(), action) {
switch (action.type) {
case "SET_STYLE_OVERRIDE":
return new Map(state).set(action.id, action.style);
case "DELETE_STYLE_OVERRIDE": {
const newState = new Map(state);
newState.delete(action.id);
return newState;
}
}
return state;
}
function registeredInserterMediaCategories(state = [], action) {
switch (action.type) {
case "REGISTER_INSERTER_MEDIA_CATEGORY":
return [...state, action.category];
}
return state;
}
function lastFocus(state = false, action) {
switch (action.type) {
case "LAST_FOCUS":
return action.lastFocus;
}
return state;
}
function zoomLevel(state = 100, action) {
switch (action.type) {
case "SET_ZOOM_LEVEL":
return action.zoom;
case "RESET_ZOOM_LEVEL":
return 100;
}
return state;
}
function insertionPoint(state = null, action) {
switch (action.type) {
case "SET_INSERTION_POINT":
return action.value;
case "SELECT_BLOCK":
return null;
}
return state;
}
var combinedReducers = (0, import_data.combineReducers)({
blocks,
isDragging,
isTyping,
isBlockInterfaceHidden,
draggedBlocks,
selection,
isMultiSelecting,
isSelectionEnabled,
initialPosition,
blocksMode,
blockListSettings,
insertionPoint,
insertionCue,
template,
settings,
preferences,
lastBlockAttributesChange,
lastFocus,
expandedBlock,
highlightedBlock,
lastBlockInserted,
editedContentOnlySection,
blockVisibility,
blockEditingModes,
styleOverrides,
removalPromptData,
blockRemovalRules,
openedBlockSettingsMenu,
registeredInserterMediaCategories,
zoomLevel,
hasBlockSpotlight
});
function getBlockTreeBlock(state, clientId) {
if (clientId === "") {
const rootBlock = state.blocks.tree.get(clientId);
if (!rootBlock) {
return;
}
return {
clientId: "",
...rootBlock
};
}
if (!state.blocks.controlledInnerBlocks[clientId]) {
return state.blocks.tree.get(clientId);
}
const controlledTree = state.blocks.tree.get(`controlled||${clientId}`);
const regularTree = state.blocks.tree.get(clientId);
return {
...regularTree,
innerBlocks: controlledTree?.innerBlocks
};
}
function traverseBlockTree(state, clientId, callback) {
const tree = getBlockTreeBlock(state, clientId);
if (!tree) {
return;
}
callback(tree);
if (!tree?.innerBlocks?.length) {
return;
}
for (const innerBlock of tree?.innerBlocks) {
traverseBlockTree(state, innerBlock.clientId, callback);
}
}
function findParentInClientIdsList(state, clientId, clientIds) {
if (!clientIds.length) {
return;
}
let parent = state.blocks.parents.get(clientId);
while (parent !== void 0) {
if (clientIds.includes(parent)) {
return parent;
}
parent = state.blocks.parents.get(parent);
}
}
function hasBindings(block) {
return block?.attributes?.metadata?.bindings && Object.keys(block?.attributes?.metadata?.bindings).length;
}
function getDerivedBlockEditingModesForTree(state, treeClientId = "") {
const isZoomedOut = state?.zoomLevel < 100 || state?.zoomLevel === "auto-scaled";
const derivedBlockEditingModes = /* @__PURE__ */ new Map();
const sectionRootClientId = state.settings?.[import_private_keys.sectionRootClientIdKey];
const sectionClientIds = state.blocks.order.get(sectionRootClientId);
const hasDisabledBlocks = Array.from(state.blockEditingModes).some(
([, mode]) => mode === "disabled"
);
const templatePartClientIds = [];
const syncedPatternClientIds = [];
Object.keys(state.blocks.controlledInnerBlocks).forEach((clientId) => {
const block = state.blocks.byClientId?.get(clientId);
if (block?.name === "core/template-part") {
templatePartClientIds.push(clientId);
}
if (block?.name === "core/block") {
syncedPatternClientIds.push(clientId);
}
});
const contentOnlyTemplateLockedClientIds = Object.keys(
state.blockListSettings
).filter(
(clientId) => state.blockListSettings[clientId]?.templateLock === "contentOnly"
);
const isIsolatedEditor = state.settings?.[import_private_keys.isIsolatedEditorKey];
const unsyncedPatternClientIds = !!window?.__experimentalContentOnlyPatternInsertion && !isIsolatedEditor ? Array.from(state.blocks.attributes.keys()).filter(
(clientId) => state.blocks.attributes.get(clientId)?.metadata?.patternName
) : [];
const contentOnlyParents = [
...contentOnlyTemplateLockedClientIds,
...unsyncedPatternClientIds,
...window?.__experimentalContentOnlyPatternInsertion && !isIsolatedEditor ? templatePartClientIds : []
];
traverseBlockTree(state, treeClientId, (block) => {
const { clientId, name: blockName } = block;
if (state.editedContentOnlySection) {
if (state.editedContentOnlySection === clientId) {
derivedBlockEditingModes.set(clientId, "default");
return;
}
const parentTempEditedClientId = findParentInClientIdsList(
state,
clientId,
[state.editedContentOnlySection]
);
if (parentTempEditedClientId) {
derivedBlockEditingModes.set(clientId, "default");
return;
}
if (window?.__experimentalContentOnlyPatternInsertion) {
derivedBlockEditingModes.set(clientId, "disabled");
return;
}
}
if (state.blockEditingModes.has(clientId)) {
return;
}
if (hasDisabledBlocks) {
let ancestorBlockEditingMode;
let parent = state.blocks.parents.get(clientId);
while (parent !== void 0) {
if (state.blockEditingModes.has(parent)) {
ancestorBlockEditingMode = state.blockEditingModes.get(parent);
}
if (ancestorBlockEditingMode) {
break;
}
parent = state.blocks.parents.get(parent);
}
if (ancestorBlockEditingMode === "disabled") {
derivedBlockEditingModes.set(clientId, "disabled");
return;
}
}
if (isZoomedOut) {
if (clientId === sectionRootClientId) {
derivedBlockEditingModes.set(clientId, "contentOnly");
return;
}
if (!sectionClientIds?.length) {
derivedBlockEditingModes.set(clientId, "disabled");
return;
}
if (sectionClientIds.includes(clientId)) {
derivedBlockEditingModes.set(clientId, "contentOnly");
return;
}
derivedBlockEditingModes.set(clientId, "disabled");
return;
}
if (syncedPatternClientIds.length) {
if (syncedPatternClientIds.includes(clientId)) {
if (findParentInClientIdsList(
state,
clientId,
syncedPatternClientIds
)) {