@wordpress/block-editor
Version:
359 lines (333 loc) • 11.9 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import { createElement, Fragment } from "@wordpress/element";
/**
* External dependencies
*/
import { View, Platform, TouchableWithoutFeedback } from 'react-native';
/**
* WordPress dependencies
*/
import { useRef, useState } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { createBlock } from '@wordpress/blocks';
import { KeyboardAwareFlatList, WIDE_ALIGNMENTS, alignmentHelpers } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import styles from './style.scss';
import BlockListAppender from '../block-list-appender';
import BlockListItem from './block-list-item';
import BlockListItemCell from './block-list-item-cell';
import { BlockListProvider, DEFAULT_BLOCK_LIST_CONTEXT } from './block-list-context';
import { BlockDraggableWrapper } from '../block-draggable';
import { useEditorWrapperStyles } from '../../hooks/use-editor-wrapper-styles';
import { store as blockEditorStore } from '../../store';
const identity = x => x;
const stylesMemo = {};
const getStyles = (isStackedHorizontally, horizontalAlignment) => {
const styleName = `${isStackedHorizontally}-${horizontalAlignment}`;
if (stylesMemo[styleName]) {
return stylesMemo[styleName];
}
const computedStyles = [isStackedHorizontally && styles.horizontal, horizontalAlignment && styles[`is-aligned-${horizontalAlignment}`], styles.overflowVisible];
stylesMemo[styleName] = computedStyles;
return computedStyles;
};
export default function BlockList({
blockWidth: initialBlockWidth,
contentResizeMode,
contentStyle,
filterInnerBlocks,
gridProperties,
header,
horizontal,
horizontalAlignment,
marginHorizontal = styles.defaultBlock.marginLeft,
marginVertical = styles.defaultBlock.marginTop,
onAddBlock,
onDeleteBlock,
orientation,
parentWidth,
renderAppender,
renderFooterAppender,
rootClientId,
withFooter = true
}) {
const {
blockClientIds,
blockCount,
blockInsertionPointIsVisible,
isReadOnly,
isRootList,
isFloatingToolbarVisible,
isStackedHorizontally,
maxWidth,
isRTL
} = useSelect(select => {
const {
getBlockCount,
getBlockHierarchyRootClientId,
getBlockOrder,
getSelectedBlockClientId,
isBlockInsertionPointVisible,
getSettings
} = select(blockEditorStore);
const selectedBlockClientId = getSelectedBlockClientId();
const rootBlockId = getBlockHierarchyRootClientId(selectedBlockClientId);
let blockOrder = getBlockOrder(rootClientId); // Display only block which fulfill the condition in passed `filterInnerBlocks` function.
if (filterInnerBlocks) {
blockOrder = filterInnerBlocks(blockOrder);
}
const {
isRTL: isRTLSetting,
maxWidth: maxWidthSetting,
readOnly
} = getSettings();
return {
blockClientIds: blockOrder,
blockCount: getBlockCount(),
blockInsertionPointIsVisible: Platform.OS === 'ios' && isBlockInsertionPointVisible(),
isReadOnly: readOnly,
isRootList: rootClientId === undefined,
isFloatingToolbarVisible: !!selectedBlockClientId && !!getBlockCount(rootBlockId),
isStackedHorizontally: orientation === 'horizontal',
maxWidth: maxWidthSetting,
isRTL: isRTLSetting
};
}, [filterInnerBlocks, orientation, rootClientId]);
const {
insertBlock,
clearSelectedBlock
} = useDispatch(blockEditorStore);
const extraData = useRef({
parentWidth,
renderFooterAppender,
renderAppender,
onDeleteBlock,
contentStyle
});
const [blockWidth, setBlockWidth] = useState(initialBlockWidth || 0);
const addBlockToEndOfPost = newBlock => {
insertBlock(newBlock, blockCount);
};
const scrollViewRef = useRef(null);
const shouldFlatListPreventAutomaticScroll = () => blockInsertionPointIsVisible;
const shouldShowInnerBlockAppender = () => renderAppender && blockClientIds.length > 0;
const getExtraData = () => {
if (extraData.current.parentWidth !== parentWidth || extraData.current.renderFooterAppender !== renderFooterAppender || extraData.current.onDeleteBlock !== onDeleteBlock || extraData.current.contentStyle !== contentStyle || extraData.current.renderAppender !== renderAppender || extraData.current.blockWidth !== blockWidth || extraData.current.gridProperties !== gridProperties) {
extraData.current = {
parentWidth,
renderFooterAppender,
onDeleteBlock,
contentStyle,
renderAppender,
blockWidth,
gridProperties
};
}
return extraData.current;
};
const onLayout = ({
nativeEvent
}) => {
const {
layout
} = nativeEvent;
const layoutWidth = Math.floor(layout.width);
if (isRootList && blockWidth !== layoutWidth) {
setBlockWidth(Math.min(layoutWidth, maxWidth));
} else if (!isRootList && !blockWidth) {
setBlockWidth(Math.min(layoutWidth, maxWidth));
}
};
const renderItem = ({
item: clientId,
index
}) => {
// Extracting the grid item properties here to avoid
// re-renders in the blockListItem component.
const isGridItem = !!gridProperties;
const gridItemProps = gridProperties && {
numOfColumns: gridProperties.numColumns,
tileCount: blockClientIds.length,
tileIndex: blockClientIds.indexOf(clientId)
};
return createElement(BlockListItem, _extends({
index: index,
isStackedHorizontally: isStackedHorizontally,
rootClientId: rootClientId,
clientId: clientId,
parentWidth: parentWidth,
contentResizeMode: contentResizeMode,
contentStyle: contentStyle,
onAddBlock: onAddBlock,
marginVertical: marginVertical,
marginHorizontal: marginHorizontal,
onDeleteBlock: onDeleteBlock,
shouldShowInnerBlockAppender: shouldShowInnerBlockAppender,
blockWidth: blockWidth,
isGridItem: isGridItem
}, gridItemProps));
};
const {
blockToolbar,
headerToolbar,
floatingToolbar
} = styles;
const containerStyle = {
flex: isRootList ? 1 : 0,
// We set negative margin in the parent to remove the edge spacing between parent block and child block in ineer blocks.
marginVertical: isRootList ? 0 : -marginVertical,
marginHorizontal: isRootList ? 0 : -marginHorizontal
};
const isContentStretch = contentResizeMode === 'stretch';
const isMultiBlocks = blockClientIds.length > 1;
const {
isWider
} = alignmentHelpers;
const extraScrollHeight = headerToolbar.height + blockToolbar.height + (isFloatingToolbarVisible ? floatingToolbar.height : 0);
return createElement(View, {
style: containerStyle,
onAccessibilityEscape: clearSelectedBlock,
onLayout: onLayout,
testID: "block-list-wrapper"
}, isRootList ? createElement(BlockListProvider, {
value: { ...DEFAULT_BLOCK_LIST_CONTEXT,
scrollRef: scrollViewRef.current
}
}, createElement(BlockDraggableWrapper, {
isRTL: isRTL
}, ({
onScroll
}) => createElement(KeyboardAwareFlatList, _extends({}, Platform.OS === 'android' ? {
removeClippedSubviews: false
} : {}, {
// Disable clipping on Android to fix focus losing. See https://github.com/wordpress-mobile/gutenberg-mobile/pull/741#issuecomment-472746541
accessibilityLabel: "block-list",
innerRef: ref => {
scrollViewRef.current = ref;
},
extraScrollHeight: extraScrollHeight,
keyboardShouldPersistTaps: "always",
scrollViewStyle: {
flex: 1
},
extraData: getExtraData(),
scrollEnabled: isRootList,
contentContainerStyle: [horizontal && styles.horizontalContentContainer, isWider(blockWidth, 'medium') && (isContentStretch && isMultiBlocks ? styles.horizontalContentContainerStretch : styles.horizontalContentContainerCenter)],
data: blockClientIds,
keyExtractor: identity,
renderItem: renderItem,
CellRendererComponent: BlockListItemCell,
shouldPreventAutomaticScroll: shouldFlatListPreventAutomaticScroll,
ListHeaderComponent: header,
ListEmptyComponent: !isReadOnly && createElement(EmptyList, {
orientation: orientation,
rootClientId: rootClientId,
renderAppender: renderAppender,
renderFooterAppender: renderFooterAppender
}),
ListFooterComponent: createElement(Footer, {
addBlockToEndOfPost: addBlockToEndOfPost,
isReadOnly: isReadOnly,
renderFooterAppender: renderFooterAppender,
withFooter: withFooter
}),
onScroll: onScroll
})))) : createElement(Fragment, null, blockClientIds.length > 0 ? createElement(View, {
style: [{
flex: 0
}, styles.overflowVisible]
}, createElement(View, {
style: [...getStyles(isStackedHorizontally, horizontalAlignment), horizontal && styles.horizontalContentContainer]
}, blockClientIds.map((currentClientId, index) => {
return createElement(View, {
key: currentClientId
}, renderItem({
item: currentClientId,
index
}));
}), createElement(Footer, {
addBlockToEndOfPost: addBlockToEndOfPost,
isReadOnly: isReadOnly,
renderFooterAppender: renderFooterAppender,
withFooter: withFooter
}))) : createElement(EmptyList, {
orientation: orientation,
rootClientId: rootClientId,
renderAppender: renderAppender,
renderFooterAppender: renderFooterAppender
})), shouldShowInnerBlockAppender() && createElement(View, {
style: {
marginHorizontal: marginHorizontal - styles.innerAppender.marginLeft
}
}, createElement(BlockListAppender, {
rootClientId: rootClientId,
renderAppender: renderAppender,
showSeparator: true
})));
}
function Footer({
addBlockToEndOfPost,
isReadOnly,
renderFooterAppender,
withFooter
}) {
if (!isReadOnly && withFooter) {
return createElement(Fragment, null, createElement(TouchableWithoutFeedback, {
accessibilityLabel: __('Add paragraph block'),
testID: __('Add paragraph block'),
onPress: () => {
const paragraphBlock = createBlock('core/paragraph');
addBlockToEndOfPost(paragraphBlock);
}
}, createElement(View, {
style: styles.blockListFooter
})));
} else if (renderFooterAppender) {
return createElement(View, null, renderFooterAppender());
}
return null;
}
function EmptyList({
orientation,
renderAppender,
renderFooterAppender,
rootClientId
}) {
const {
shouldShowInsertionPoint
} = useSelect(select => {
const {
getBlockOrder,
getBlockInsertionPoint,
isBlockInsertionPointVisible
} = select(blockEditorStore);
const isStackedHorizontally = orientation === 'horizontal';
const blockClientIds = getBlockOrder(rootClientId);
const insertionPoint = getBlockInsertionPoint();
const blockInsertionPointIsVisible = isBlockInsertionPointVisible();
return {
shouldShowInsertionPoint: !isStackedHorizontally && blockInsertionPointIsVisible && insertionPoint.rootClientId === rootClientId && ( // If list is empty, show the insertion point (via the default appender)
blockClientIds.length === 0 || // Or if the insertion point is right before the denoted block.
!blockClientIds[insertionPoint.index])
};
});
const align = renderAppender ? WIDE_ALIGNMENTS.alignments.full : undefined;
const [wrapperStyles] = useEditorWrapperStyles({
align
});
if (renderFooterAppender || renderAppender === false) {
return null;
}
const containerStyles = [styles.defaultAppender, wrapperStyles];
return createElement(View, {
style: containerStyles
}, createElement(BlockListAppender, {
rootClientId: rootClientId,
renderAppender: renderAppender,
showSeparator: shouldShowInsertionPoint
}));
}
//# sourceMappingURL=index.native.js.map