@gechiui/block-editor
Version:
309 lines (277 loc) • 8.99 kB
JavaScript
import { createElement, Fragment } from "@gechiui/element";
/**
* External dependencies
*/
import classnames from 'classnames';
import { omit } from 'lodash';
/**
* GeChiUI dependencies
*/
import { createContext, useMemo, useCallback, RawHTML } from '@gechiui/element';
import { getBlockType, getSaveContent, isUnmodifiedDefaultBlock } from '@gechiui/blocks';
import { withFilters } from '@gechiui/components';
import { withDispatch, withSelect, useDispatch } from '@gechiui/data';
import { compose, pure, ifCondition } from '@gechiui/compose';
import { safeHTML } from '@gechiui/dom';
/**
* Internal dependencies
*/
import BlockEdit from '../block-edit';
import BlockInvalidWarning from './block-invalid-warning';
import BlockCrashWarning from './block-crash-warning';
import BlockCrashBoundary from './block-crash-boundary';
import BlockHtml from './block-html';
import { useBlockProps } from './use-block-props';
import { store as blockEditorStore } from '../../store';
export const BlockListBlockContext = createContext();
/**
* Merges wrapper props with special handling for classNames and styles.
*
* @param {Object} propsA
* @param {Object} propsB
*
* @return {Object} Merged props.
*/
function mergeWrapperProps(propsA, propsB) {
const newProps = { ...propsA,
...propsB
};
if (propsA && propsB && propsA.className && propsB.className) {
newProps.className = classnames(propsA.className, propsB.className);
}
if (propsA && propsB && propsA.style && propsB.style) {
newProps.style = { ...propsA.style,
...propsB.style
};
}
return newProps;
}
function Block(_ref) {
let {
children,
isHtml,
...props
} = _ref;
return createElement("div", useBlockProps(props, {
__unstableIsHtml: isHtml
}), children);
}
function BlockListBlock(_ref2) {
let {
mode,
isLocked,
canRemove,
clientId,
isSelected,
isSelectionEnabled,
className,
name,
isValid,
attributes,
wrapperProps,
setAttributes,
onReplace,
onInsertBlocksAfter,
onMerge,
toggleSelection
} = _ref2;
const {
removeBlock
} = useDispatch(blockEditorStore);
const onRemove = useCallback(() => removeBlock(clientId), [clientId]); // We wrap the BlockEdit component in a div that hides it when editing in
// HTML mode. This allows us to render all of the ancillary pieces
// (InspectorControls, etc.) which are inside `BlockEdit` but not
// `BlockHTML`, even in HTML mode.
let blockEdit = createElement(BlockEdit, {
name: name,
isSelected: isSelected,
attributes: attributes,
setAttributes: setAttributes,
insertBlocksAfter: isLocked ? undefined : onInsertBlocksAfter,
onReplace: canRemove ? onReplace : undefined,
onRemove: canRemove ? onRemove : undefined,
mergeBlocks: canRemove ? onMerge : undefined,
clientId: clientId,
isSelectionEnabled: isSelectionEnabled,
toggleSelection: toggleSelection
});
const blockType = getBlockType(name); // Determine whether the block has props to apply to the wrapper.
if (blockType !== null && blockType !== void 0 && blockType.getEditWrapperProps) {
wrapperProps = mergeWrapperProps(wrapperProps, blockType.getEditWrapperProps(attributes));
}
const isAligned = wrapperProps && !!wrapperProps['data-align']; // For aligned blocks, provide a wrapper element so the block can be
// positioned relative to the block column.
if (isAligned) {
blockEdit = createElement("div", {
className: "gc-block",
"data-align": wrapperProps['data-align']
}, blockEdit);
}
let block;
if (!isValid) {
const saveContent = getSaveContent(blockType, attributes);
block = createElement(Block, {
className: "has-warning"
}, createElement(BlockInvalidWarning, {
clientId: clientId
}), createElement(RawHTML, null, safeHTML(saveContent)));
} else if (mode === 'html') {
// Render blockEdit so the inspector controls don't disappear.
// See #8969.
block = createElement(Fragment, null, createElement("div", {
style: {
display: 'none'
}
}, blockEdit), createElement(Block, {
isHtml: true
}, createElement(BlockHtml, {
clientId: clientId
})));
} else if ((blockType === null || blockType === void 0 ? void 0 : blockType.apiVersion) > 1) {
block = blockEdit;
} else {
block = createElement(Block, wrapperProps, blockEdit);
}
const value = {
clientId,
className,
wrapperProps: omit(wrapperProps, ['data-align']),
isAligned
};
const memoizedValue = useMemo(() => value, Object.values(value));
return createElement(BlockListBlockContext.Provider, {
value: memoizedValue
}, createElement(BlockCrashBoundary, {
fallback: createElement(Block, {
className: "has-warning"
}, createElement(BlockCrashWarning, null))
}, block));
}
const applyWithSelect = withSelect((select, _ref3) => {
let {
clientId,
rootClientId
} = _ref3;
const {
isBlockSelected,
getBlockMode,
isSelectionEnabled,
getTemplateLock,
__unstableGetBlockWithoutInnerBlocks,
canRemoveBlock,
canMoveBlock
} = select(blockEditorStore);
const block = __unstableGetBlockWithoutInnerBlocks(clientId);
const isSelected = isBlockSelected(clientId);
const templateLock = getTemplateLock(rootClientId);
const canRemove = canRemoveBlock(clientId, rootClientId);
const canMove = canMoveBlock(clientId, rootClientId); // The fallback to `{}` is a temporary fix.
// This function should never be called when a block is not present in
// the state. It happens now because the order in withSelect rendering
// is not correct.
const {
name,
attributes,
isValid
} = block || {}; // Do not add new properties here, use `useSelect` instead to avoid
// leaking new props to the public API (editor.BlockListBlock filter).
return {
mode: getBlockMode(clientId),
isSelectionEnabled: isSelectionEnabled(),
isLocked: !!templateLock,
canRemove,
canMove,
// Users of the editor.BlockListBlock filter used to be able to
// access the block prop.
// Ideally these blocks would rely on the clientId prop only.
// This is kept for backward compatibility reasons.
block,
name,
attributes,
isValid,
isSelected
};
});
const applyWithDispatch = withDispatch((dispatch, ownProps, _ref4) => {
let {
select
} = _ref4;
const {
updateBlockAttributes,
insertBlocks,
mergeBlocks,
replaceBlocks,
toggleSelection,
__unstableMarkLastChangeAsPersistent
} = dispatch(blockEditorStore); // Do not add new properties here, use `useDispatch` instead to avoid
// leaking new props to the public API (editor.BlockListBlock filter).
return {
setAttributes(newAttributes) {
const {
getMultiSelectedBlockClientIds
} = select(blockEditorStore);
const multiSelectedBlockClientIds = getMultiSelectedBlockClientIds();
const {
clientId
} = ownProps;
const clientIds = multiSelectedBlockClientIds.length ? multiSelectedBlockClientIds : [clientId];
updateBlockAttributes(clientIds, newAttributes);
},
onInsertBlocks(blocks, index) {
const {
rootClientId
} = ownProps;
insertBlocks(blocks, index, rootClientId);
},
onInsertBlocksAfter(blocks) {
const {
clientId,
rootClientId
} = ownProps;
const {
getBlockIndex
} = select(blockEditorStore);
const index = getBlockIndex(clientId);
insertBlocks(blocks, index + 1, rootClientId);
},
onMerge(forward) {
const {
clientId
} = ownProps;
const {
getPreviousBlockClientId,
getNextBlockClientId
} = select(blockEditorStore);
if (forward) {
const nextBlockClientId = getNextBlockClientId(clientId);
if (nextBlockClientId) {
mergeBlocks(clientId, nextBlockClientId);
}
} else {
const previousBlockClientId = getPreviousBlockClientId(clientId);
if (previousBlockClientId) {
mergeBlocks(previousBlockClientId, clientId);
}
}
},
onReplace(blocks, indexToSelect, initialPosition) {
if (blocks.length && !isUnmodifiedDefaultBlock(blocks[blocks.length - 1])) {
__unstableMarkLastChangeAsPersistent();
}
replaceBlocks([ownProps.clientId], blocks, indexToSelect, initialPosition);
},
toggleSelection(selectionEnabled) {
toggleSelection(selectionEnabled);
}
};
});
export default compose(pure, applyWithSelect, applyWithDispatch, // block is sometimes not mounted at the right time, causing it be undefined
// see issue for more info
// https://github.com/GeChiUI/gutenberg/issues/17013
ifCondition(_ref5 => {
let {
block
} = _ref5;
return !!block;
}), withFilters('editor.BlockListBlock'))(BlockListBlock);
//# sourceMappingURL=block.js.map