UNPKG

@gechiui/block-editor

Version:
370 lines (336 loc) 11.2 kB
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