@gechiui/block-editor
Version:
370 lines (336 loc) • 11.2 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import { createElement } from "@gechiui/element";
/**
* External dependencies
*/
import { View, Text, TouchableWithoutFeedback, Dimensions } from 'react-native';
import { pick } from 'lodash';
/**
* GeChiUI dependencies
*/
import { Component, createRef, useMemo } from '@gechiui/element';
import { GlobalStylesContext, getMergedGlobalStyles, alignmentHelpers, useGlobalStyles } from '@gechiui/components';
import { withDispatch, withSelect } from '@gechiui/data';
import { compose, withPreferredColorScheme } from '@gechiui/compose';
import { getBlockType, __experimentalGetAccessibleBlockLabel as getAccessibleBlockLabel } from '@gechiui/blocks';
import { useSetting } from '@gechiui/block-editor';
/**
* Internal dependencies
*/
import styles from './block.scss';
import BlockEdit from '../block-edit';
import BlockInvalidWarning from './block-invalid-warning';
import BlockMobileToolbar from '../block-mobile-toolbar';
import { store as blockEditorStore } from '../../store';
const emptyArray = [];
function BlockForType(_ref) {
let {
attributes,
clientId,
contentStyle,
getBlockWidth,
insertBlocksAfter,
isSelected,
mergeBlocks,
name,
onBlockFocus,
onChange,
onDeleteBlock,
onReplace,
parentWidth,
wrapperProps,
blockWidth,
baseGlobalStyles
} = _ref;
const defaultColors = useSetting('color.palette') || emptyArray;
const fontSizes = useSetting('typography.fontSizes') || emptyArray;
const globalStyle = useGlobalStyles();
const mergedStyle = useMemo(() => {
return getMergedGlobalStyles(baseGlobalStyles, globalStyle, wrapperProps.style, attributes, defaultColors, name, fontSizes);
}, [defaultColors, globalStyle, // I couldn't simply use attributes and wrapperProps.styles as a dependency because they are almost always a new reference.
// Thanks to the JSON.stringify we check if the value is the same instead of reference.
JSON.stringify(wrapperProps.style), JSON.stringify(pick(attributes, GlobalStylesContext.BLOCK_STYLE_ATTRIBUTES))]);
return createElement(GlobalStylesContext.Provider, {
value: mergedStyle
}, createElement(BlockEdit, {
name: name,
isSelected: isSelected,
attributes: attributes,
setAttributes: onChange,
onFocus: onBlockFocus,
onReplace: onReplace,
insertBlocksAfter: insertBlocksAfter,
mergeBlocks: mergeBlocks // Block level styles
,
wrapperProps: wrapperProps // inherited styles merged with block level styles
,
style: mergedStyle,
clientId: clientId,
parentWidth: parentWidth,
contentStyle: contentStyle,
onDeleteBlock: onDeleteBlock,
blockWidth: blockWidth
}), createElement(View, {
onLayout: getBlockWidth
}));
}
class BlockListBlock extends Component {
constructor() {
super(...arguments);
this.insertBlocksAfter = this.insertBlocksAfter.bind(this);
this.onFocus = this.onFocus.bind(this);
this.getBlockWidth = this.getBlockWidth.bind(this);
this.state = {
blockWidth: this.props.blockWidth - 2 * this.props.marginHorizontal
};
this.anchorNodeRef = createRef();
}
onFocus() {
const {
firstToSelectId,
isSelected,
onSelect
} = this.props;
if (!isSelected) {
onSelect(firstToSelectId);
}
}
insertBlocksAfter(blocks) {
this.props.onInsertBlocks(blocks, this.props.order + 1);
if (blocks[0]) {
// focus on the first block inserted
this.props.onSelect(blocks[0].clientId);
}
}
getBlockWidth(_ref2) {
let {
nativeEvent
} = _ref2;
const {
layout
} = nativeEvent;
const {
blockWidth
} = this.state;
const layoutWidth = Math.floor(layout.width);
if (!blockWidth || !layoutWidth) {
return;
}
if (blockWidth !== layoutWidth) {
this.setState({
blockWidth: layoutWidth
});
}
}
getBlockForType() {
const {
blockWidth
} = this.state;
return createElement(BlockForType, _extends({}, this.props, {
onBlockFocus: this.onFocus,
insertBlocksAfter: this.insertBlocksAfter,
getBlockWidth: this.getBlockWidth,
blockWidth: blockWidth
}));
}
renderBlockTitle() {
return createElement(View, {
style: styles.blockTitle
}, createElement(Text, null, "BlockType: ", this.props.name));
}
render() {
const {
attributes,
blockType,
clientId,
icon,
isSelected,
isValid,
order,
title,
isDimmed,
isTouchable,
onDeleteBlock,
isStackedHorizontally,
isParentSelected,
getStylesFromColorScheme,
marginVertical,
marginHorizontal,
isInnerBlockSelected,
name
} = this.props;
if (!attributes || !blockType) {
return null;
}
const {
blockWidth
} = this.state;
const {
align
} = attributes;
const accessibilityLabel = getAccessibleBlockLabel(blockType, attributes, order + 1);
const {
isFullWidth,
isContainerRelated
} = alignmentHelpers;
const accessible = !(isSelected || isInnerBlockSelected);
const screenWidth = Math.floor(Dimensions.get('window').width);
const isScreenWidthEqual = blockWidth === screenWidth;
const isScreenWidthWider = blockWidth < screenWidth;
const isFullWidthToolbar = isFullWidth(align) || isScreenWidthEqual;
return createElement(TouchableWithoutFeedback, {
onPress: this.onFocus,
accessible: accessible,
accessibilityRole: 'button'
}, createElement(View, {
style: {
flex: 1
},
accessibilityLabel: accessibilityLabel
}, createElement(View, {
pointerEvents: isTouchable ? 'auto' : 'box-only',
accessibilityLabel: accessibilityLabel,
style: [{
marginVertical,
marginHorizontal,
flex: 1
}, isDimmed && styles.dimmed]
}, isSelected && createElement(View, {
pointerEvents: "box-none",
style: [styles.solidBorder, isFullWidth(align) && isScreenWidthWider && styles.borderFullWidth, isFullWidth(align) && isContainerRelated(name) && isScreenWidthWider && styles.containerBorderFullWidth, getStylesFromColorScheme(styles.solidBorderColor, styles.solidBorderColorDark)]
}), isParentSelected && createElement(View, {
style: [styles.dashedBorder, getStylesFromColorScheme(styles.dashedBorderColor, styles.dashedBorderColorDark)]
}), isValid ? this.getBlockForType() : createElement(BlockInvalidWarning, {
blockTitle: title,
icon: icon
}), createElement(View, {
style: styles.neutralToolbar,
ref: this.anchorNodeRef
}, isSelected && createElement(BlockMobileToolbar, {
clientId: clientId,
onDelete: onDeleteBlock,
isStackedHorizontally: isStackedHorizontally,
blockWidth: blockWidth,
anchorNodeRef: this.anchorNodeRef.current,
isFullWidth: isFullWidthToolbar
})))));
}
} // Helper function to memoize the wrapperProps since getEditWrapperProps always returns a new reference
const wrapperPropsCache = new WeakMap();
const emptyObj = {};
function getWrapperProps(value, getWrapperPropsFunction) {
if (!getWrapperPropsFunction) {
return emptyObj;
}
const cachedValue = wrapperPropsCache.get(value);
if (!cachedValue) {
const wrapperProps = getWrapperPropsFunction(value);
wrapperPropsCache.set(value, wrapperProps);
return wrapperProps;
}
return cachedValue;
}
export default compose([withSelect((select, _ref3) => {
var _getSettings;
let {
clientId
} = _ref3;
const {
getBlockIndex,
getSettings,
isBlockSelected,
getBlock,
getSelectedBlockClientId,
getLowestCommonAncestorWithSelectedBlock,
getBlockParents,
hasSelectedInnerBlock
} = select(blockEditorStore);
const order = getBlockIndex(clientId);
const isSelected = isBlockSelected(clientId);
const isInnerBlockSelected = hasSelectedInnerBlock(clientId);
const block = getBlock(clientId);
const {
name,
attributes,
isValid
} = block || {};
const blockType = getBlockType(name || 'core/missing');
const title = blockType === null || blockType === void 0 ? void 0 : blockType.title;
const icon = blockType === null || blockType === void 0 ? void 0 : blockType.icon;
const parents = getBlockParents(clientId, true);
const parentId = parents[0] || '';
const selectedBlockClientId = getSelectedBlockClientId();
const commonAncestor = getLowestCommonAncestorWithSelectedBlock(clientId);
const commonAncestorIndex = parents.indexOf(commonAncestor) - 1;
const firstToSelectId = commonAncestor ? parents[commonAncestorIndex] : parents[parents.length - 1];
const isParentSelected = // set false as a default value to prevent re-render when it's changed from null to false
(selectedBlockClientId || false) && selectedBlockClientId === parentId;
const selectedParents = selectedBlockClientId ? getBlockParents(selectedBlockClientId) : [];
const isDescendantOfParentSelected = selectedParents.includes(parentId);
const isTouchable = isSelected || isDescendantOfParentSelected || isParentSelected || parentId === '';
const baseGlobalStyles = (_getSettings = getSettings()) === null || _getSettings === void 0 ? void 0 : _getSettings.__experimentalGlobalStylesBaseStyles;
return {
icon,
name: name || 'core/missing',
order,
title,
attributes,
blockType,
isSelected,
isInnerBlockSelected,
isValid,
isParentSelected,
firstToSelectId,
isTouchable,
baseGlobalStyles,
wrapperProps: getWrapperProps(attributes, blockType.getEditWrapperProps)
};
}), withDispatch((dispatch, ownProps, _ref4) => {
let {
select
} = _ref4;
const {
insertBlocks,
mergeBlocks,
replaceBlocks,
selectBlock,
updateBlockAttributes
} = dispatch(blockEditorStore);
return {
mergeBlocks(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);
}
}
},
onInsertBlocks(blocks, index) {
insertBlocks(blocks, index, ownProps.rootClientId);
},
onSelect() {
let clientId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ownProps.clientId;
let initialPosition = arguments.length > 1 ? arguments[1] : undefined;
selectBlock(clientId, initialPosition);
},
onChange: attributes => {
updateBlockAttributes(ownProps.clientId, attributes);
},
onReplace(blocks, indexToSelect) {
replaceBlocks([ownProps.clientId], blocks, indexToSelect);
}
};
}), withPreferredColorScheme])(BlockListBlock);
//# sourceMappingURL=block.native.js.map