@wordpress/block-editor
Version:
149 lines (139 loc) • 3.83 kB
JavaScript
/**
* External dependencies
*/
import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { AsyncModeProvider, useSelect } from '@wordpress/data';
import { useViewportMatch, useMergeRefs } from '@wordpress/compose';
/**
* Internal dependencies
*/
import BlockListBlock from './block';
import BlockListAppender from '../block-list-appender';
import useBlockDropZone from '../use-block-drop-zone';
import { useInBetweenInserter } from './use-in-between-inserter';
import BlockTools from '../block-tools';
import { store as blockEditorStore } from '../../store';
import { usePreParsePatterns } from '../../utils/pre-parse-patterns';
import { LayoutProvider, defaultLayout } from './layout';
function Root( { className, children } ) {
const isLargeViewport = useViewportMatch( 'medium' );
const {
isTyping,
isOutlineMode,
isFocusMode,
isNavigationMode,
} = useSelect( ( select ) => {
const {
isTyping: _isTyping,
getSettings,
isNavigationMode: _isNavigationMode,
} = select( blockEditorStore );
const { outlineMode, focusMode } = getSettings();
return {
isTyping: _isTyping(),
isOutlineMode: outlineMode,
isFocusMode: focusMode,
isNavigationMode: _isNavigationMode(),
};
}, [] );
return (
<div
ref={ useMergeRefs( [
useBlockDropZone(),
useInBetweenInserter(),
] ) }
className={ classnames(
'block-editor-block-list__layout is-root-container',
className,
{
'is-typing': isTyping,
'is-outline-mode': isOutlineMode,
'is-focus-mode': isFocusMode && isLargeViewport,
'is-navigate-mode': isNavigationMode,
}
) }
>
{ children }
</div>
);
}
export default function BlockList( { className, __experimentalLayout } ) {
usePreParsePatterns();
return (
<BlockTools>
<Root className={ className }>
<BlockListItems __experimentalLayout={ __experimentalLayout } />
</Root>
</BlockTools>
);
}
function Items( {
placeholder,
rootClientId,
renderAppender,
__experimentalAppenderTagName,
__experimentalLayout: layout = defaultLayout,
} ) {
function selector( select ) {
const {
getBlockOrder,
getSelectedBlockClientId,
getMultiSelectedBlockClientIds,
hasMultiSelection,
} = select( blockEditorStore );
return {
blockClientIds: getBlockOrder( rootClientId ),
selectedBlockClientId: getSelectedBlockClientId(),
multiSelectedBlockClientIds: getMultiSelectedBlockClientIds(),
hasMultiSelection: hasMultiSelection(),
};
}
const {
blockClientIds,
selectedBlockClientId,
multiSelectedBlockClientIds,
hasMultiSelection,
} = useSelect( selector, [ rootClientId ] );
return (
<LayoutProvider value={ layout }>
{ blockClientIds.map( ( clientId, index ) => {
const isBlockInSelection = hasMultiSelection
? multiSelectedBlockClientIds.includes( clientId )
: selectedBlockClientId === clientId;
return (
<AsyncModeProvider
key={ clientId }
value={ ! isBlockInSelection }
>
<BlockListBlock
rootClientId={ rootClientId }
clientId={ clientId }
// This prop is explicitely computed and passed down
// to avoid being impacted by the async mode
// otherwise there might be a small delay to trigger the animation.
index={ index }
/>
</AsyncModeProvider>
);
} ) }
{ blockClientIds.length < 1 && placeholder }
<BlockListAppender
tagName={ __experimentalAppenderTagName }
rootClientId={ rootClientId }
renderAppender={ renderAppender }
/>
</LayoutProvider>
);
}
export function BlockListItems( props ) {
// This component needs to always be synchronous as it's the one changing
// the async mode depending on the block selection.
return (
<AsyncModeProvider value={ false }>
<Items { ...props } />
</AsyncModeProvider>
);
}