@gechiui/block-editor
Version:
151 lines (145 loc) • 4.88 kB
JavaScript
/**
* External dependencies
*/
import { first, last } from 'lodash';
/**
* GeChiUI dependencies
*/
import { useSelect, useDispatch } from '@gechiui/data';
import { useViewportMatch } from '@gechiui/compose';
import { Popover } from '@gechiui/components';
import { __unstableUseShortcutEventMatch as useShortcutEventMatch } from '@gechiui/keyboard-shortcuts';
/**
* Internal dependencies
*/
import InsertionPoint from './insertion-point';
import BlockPopover from './block-popover';
import { store as blockEditorStore } from '../../store';
import BlockContextualToolbar from './block-contextual-toolbar';
import { usePopoverScroll } from './use-popover-scroll';
/**
* Renders block tools (the block toolbar, select/navigation mode toolbar, the
* insertion point and a slot for the inline rich text toolbar). Must be wrapped
* around the block content and editor styles wrapper or iframe.
*
* @param {Object} $0 Props.
* @param {Object} $0.children The block content and style container.
* @param {Object} $0.__unstableContentRef Ref holding the content scroll container.
*/
export default function BlockTools( {
children,
__unstableContentRef,
...props
} ) {
const isLargeViewport = useViewportMatch( 'medium' );
const hasFixedToolbar = useSelect(
( select ) => select( blockEditorStore ).getSettings().hasFixedToolbar,
[]
);
const isMatch = useShortcutEventMatch();
const { getSelectedBlockClientIds, getBlockRootClientId } = useSelect(
blockEditorStore
);
const {
duplicateBlocks,
removeBlocks,
insertAfterBlock,
insertBeforeBlock,
clearSelectedBlock,
moveBlocksUp,
moveBlocksDown,
} = useDispatch( blockEditorStore );
function onKeyDown( event ) {
if ( isMatch( 'core/block-editor/move-up', event ) ) {
const clientIds = getSelectedBlockClientIds();
if ( clientIds.length ) {
event.preventDefault();
const rootClientId = getBlockRootClientId( first( clientIds ) );
moveBlocksUp( clientIds, rootClientId );
}
} else if ( isMatch( 'core/block-editor/move-down', event ) ) {
const clientIds = getSelectedBlockClientIds();
if ( clientIds.length ) {
event.preventDefault();
const rootClientId = getBlockRootClientId( first( clientIds ) );
moveBlocksDown( clientIds, rootClientId );
}
} else if ( isMatch( 'core/block-editor/duplicate', event ) ) {
const clientIds = getSelectedBlockClientIds();
if ( clientIds.length ) {
event.preventDefault();
duplicateBlocks( clientIds );
}
} else if ( isMatch( 'core/block-editor/remove', event ) ) {
const clientIds = getSelectedBlockClientIds();
if ( clientIds.length ) {
event.preventDefault();
removeBlocks( clientIds );
}
} else if ( isMatch( 'core/block-editor/insert-after', event ) ) {
const clientIds = getSelectedBlockClientIds();
if ( clientIds.length ) {
event.preventDefault();
insertAfterBlock( last( clientIds ) );
}
} else if ( isMatch( 'core/block-editor/insert-before', event ) ) {
const clientIds = getSelectedBlockClientIds();
if ( clientIds.length ) {
event.preventDefault();
insertBeforeBlock( first( clientIds ) );
}
} else if (
isMatch( 'core/block-editor/delete-multi-selection', event )
) {
/**
* Check if the target element is a text area, input or
* event.defaultPrevented and return early. In all these
* cases backspace could be handled elsewhere.
*/
if (
[ 'INPUT', 'TEXTAREA' ].includes( event.target.nodeName ) ||
event.defaultPrevented
) {
return;
}
const clientIds = getSelectedBlockClientIds();
if ( clientIds.length > 1 ) {
event.preventDefault();
removeBlocks( clientIds );
}
} else if ( isMatch( 'core/block-editor/unselect', event ) ) {
const clientIds = getSelectedBlockClientIds();
if ( clientIds.length > 1 ) {
event.preventDefault();
clearSelectedBlock();
event.target.ownerDocument.defaultView
.getSelection()
.removeAllRanges();
}
}
}
return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div { ...props } onKeyDown={ onKeyDown }>
<InsertionPoint __unstableContentRef={ __unstableContentRef }>
{ ( hasFixedToolbar || ! isLargeViewport ) && (
<BlockContextualToolbar isFixed />
) }
{ /* Even if the toolbar is fixed, the block popover is still
needed for navigation mode. */ }
<BlockPopover __unstableContentRef={ __unstableContentRef } />
{ /* Used for the inline rich text toolbar. */ }
<Popover.Slot
name="block-toolbar"
ref={ usePopoverScroll( __unstableContentRef ) }
/>
{ children }
{ /* Used for inline rich text popovers. */ }
<Popover.Slot
name="__unstable-block-tools-after"
ref={ usePopoverScroll( __unstableContentRef ) }
/>
</InsertionPoint>
</div>
);
}