@wordpress/block-editor
Version:
320 lines (319 loc) • 9.85 kB
JavaScript
// packages/block-editor/src/components/inserter/index.js
import clsx from "clsx";
import { speak } from "@wordpress/a11y";
import { __, _x, sprintf } from "@wordpress/i18n";
import { Dropdown, Button } from "@wordpress/components";
import { Component } from "@wordpress/element";
import { withDispatch, withSelect } from "@wordpress/data";
import { compose, ifCondition } from "@wordpress/compose";
import { createBlock, store as blocksStore } from "@wordpress/blocks";
import { plus } from "@wordpress/icons";
import InserterMenu from "./menu";
import QuickInserter from "./quick-inserter";
import { store as blockEditorStore } from "../../store";
import { jsx } from "react/jsx-runtime";
var defaultRenderToggle = ({
onToggle,
disabled,
isOpen,
blockTitle,
hasSingleBlockType,
toggleProps = {}
}) => {
const {
as: Wrapper = Button,
label: labelProp,
onClick,
...rest
} = toggleProps;
let label = labelProp;
if (!label && hasSingleBlockType) {
label = sprintf(
// translators: %s: the name of the block when there is only one
_x("Add %s", "directly add the only allowed block"),
blockTitle
);
} else if (!label) {
label = _x("Add block", "Generic label for block inserter button");
}
function handleClick(event) {
if (onToggle) {
onToggle(event);
}
if (onClick) {
onClick(event);
}
}
return /* @__PURE__ */ jsx(
Wrapper,
{
__next40pxDefaultSize: toggleProps.as ? void 0 : true,
icon: plus,
label,
tooltipPosition: "bottom",
onClick: handleClick,
className: "block-editor-inserter__toggle",
"aria-haspopup": !hasSingleBlockType ? "true" : false,
"aria-expanded": !hasSingleBlockType ? isOpen : false,
disabled,
...rest
}
);
};
var Inserter = class extends Component {
constructor() {
super(...arguments);
this.onToggle = this.onToggle.bind(this);
this.renderToggle = this.renderToggle.bind(this);
this.renderContent = this.renderContent.bind(this);
}
onToggle(isOpen) {
const { onToggle } = this.props;
if (onToggle) {
onToggle(isOpen);
}
}
/**
* Render callback to display Dropdown toggle element.
*
* @param {Object} options
* @param {Function} options.onToggle Callback to invoke when toggle is
* pressed.
* @param {boolean} options.isOpen Whether dropdown is currently open.
*
* @return {Element} Dropdown toggle element.
*/
renderToggle({ onToggle, isOpen }) {
const {
disabled,
blockTitle,
hasSingleBlockType,
directInsertBlock,
toggleProps,
hasItems,
renderToggle = defaultRenderToggle
} = this.props;
return renderToggle({
onToggle,
isOpen,
disabled: disabled || !hasItems,
blockTitle,
hasSingleBlockType,
directInsertBlock,
toggleProps
});
}
/**
* Render callback to display Dropdown content element.
*
* @param {Object} options
* @param {Function} options.onClose Callback to invoke when dropdown is
* closed.
*
* @return {Element} Dropdown content element.
*/
renderContent({ onClose }) {
const {
rootClientId,
clientId,
isAppender,
showInserterHelpPanel,
// This prop is experimental to give some time for the quick inserter to mature
// Feel free to make them stable after a few releases.
__experimentalIsQuick: isQuick,
onSelectOrClose,
selectBlockOnInsert
} = this.props;
if (isQuick) {
return /* @__PURE__ */ jsx(
QuickInserter,
{
onSelect: (blocks) => {
const firstBlock = Array.isArray(blocks) && blocks?.length ? blocks[0] : blocks;
if (onSelectOrClose && typeof onSelectOrClose === "function") {
onSelectOrClose(firstBlock);
}
onClose();
},
rootClientId,
clientId,
isAppender,
selectBlockOnInsert
}
);
}
return /* @__PURE__ */ jsx(
InserterMenu,
{
onSelect: () => {
onClose();
},
rootClientId,
clientId,
isAppender,
showInserterHelpPanel
}
);
}
render() {
const {
position,
hasSingleBlockType,
directInsertBlock,
insertOnlyAllowedBlock,
__experimentalIsQuick: isQuick,
onSelectOrClose
} = this.props;
if (hasSingleBlockType || directInsertBlock) {
return this.renderToggle({ onToggle: insertOnlyAllowedBlock });
}
return /* @__PURE__ */ jsx(
Dropdown,
{
className: "block-editor-inserter",
contentClassName: clsx("block-editor-inserter__popover", {
"is-quick": isQuick
}),
popoverProps: { position, shift: true },
onToggle: this.onToggle,
expandOnMobile: true,
headerTitle: __("Add a block"),
renderToggle: this.renderToggle,
renderContent: this.renderContent,
onClose: onSelectOrClose
}
);
}
};
var inserter_default = compose([
withSelect(
(select, { clientId, rootClientId, shouldDirectInsert = true }) => {
const {
getBlockRootClientId,
hasInserterItems,
getAllowedBlocks,
getDirectInsertBlock
} = select(blockEditorStore);
const { getBlockVariations } = select(blocksStore);
rootClientId = rootClientId || getBlockRootClientId(clientId) || void 0;
const allowedBlocks = getAllowedBlocks(rootClientId);
const directInsertBlock = shouldDirectInsert && getDirectInsertBlock(rootClientId);
const hasSingleBlockType = allowedBlocks?.length === 1 && getBlockVariations(allowedBlocks[0].name, "inserter")?.length === 0;
let allowedBlockType = false;
if (hasSingleBlockType) {
allowedBlockType = allowedBlocks[0];
}
return {
hasItems: hasInserterItems(rootClientId),
hasSingleBlockType,
blockTitle: allowedBlockType ? allowedBlockType.title : "",
allowedBlockType,
directInsertBlock,
rootClientId
};
}
),
withDispatch((dispatch, ownProps, { select }) => {
return {
insertOnlyAllowedBlock() {
const {
rootClientId,
clientId,
isAppender,
hasSingleBlockType,
allowedBlockType,
directInsertBlock,
onSelectOrClose,
selectBlockOnInsert
} = ownProps;
if (!hasSingleBlockType && !directInsertBlock) {
return;
}
function getAdjacentBlockAttributes(attributesToCopy) {
const { getBlock, getPreviousBlockClientId } = select(blockEditorStore);
if (!attributesToCopy || !clientId && !rootClientId) {
return {};
}
const result = {};
let adjacentAttributes = {};
if (!clientId) {
const parentBlock = getBlock(rootClientId);
if (parentBlock?.innerBlocks?.length) {
const lastInnerBlock = parentBlock.innerBlocks[parentBlock.innerBlocks.length - 1];
if (directInsertBlock && directInsertBlock?.name === lastInnerBlock.name) {
adjacentAttributes = lastInnerBlock.attributes;
}
}
} else {
const currentBlock = getBlock(clientId);
const previousBlock = getBlock(
getPreviousBlockClientId(clientId)
);
if (currentBlock?.name === previousBlock?.name) {
adjacentAttributes = previousBlock?.attributes || {};
}
}
attributesToCopy.forEach((attribute) => {
if (adjacentAttributes.hasOwnProperty(attribute)) {
result[attribute] = adjacentAttributes[attribute];
}
});
return result;
}
function getInsertionIndex() {
const {
getBlockIndex,
getBlockSelectionEnd,
getBlockOrder,
getBlockRootClientId
} = select(blockEditorStore);
if (clientId) {
return getBlockIndex(clientId);
}
const end = getBlockSelectionEnd();
if (!isAppender && end && getBlockRootClientId(end) === rootClientId) {
return getBlockIndex(end) + 1;
}
return getBlockOrder(rootClientId).length;
}
const { insertBlock } = dispatch(blockEditorStore);
let blockToInsert;
if (directInsertBlock) {
const newAttributes = getAdjacentBlockAttributes(
directInsertBlock.attributesToCopy
);
blockToInsert = createBlock(directInsertBlock.name, {
...directInsertBlock.attributes || {},
...newAttributes
});
} else {
blockToInsert = createBlock(allowedBlockType.name);
}
insertBlock(
blockToInsert,
getInsertionIndex(),
rootClientId,
selectBlockOnInsert
);
if (onSelectOrClose) {
onSelectOrClose(blockToInsert);
}
const message = sprintf(
// translators: %s: the name of the block that has been added
__("%s block added"),
allowedBlockType.title
);
speak(message);
}
};
}),
// The global inserter should always be visible, we are using ( ! isAppender && ! rootClientId && ! clientId ) as
// a way to detect the global Inserter.
ifCondition(
({ hasItems, isAppender, rootClientId, clientId }) => hasItems || !isAppender && !rootClientId && !clientId
)
])(Inserter);
export {
inserter_default as default
};
//# sourceMappingURL=index.js.map