UNPKG

@gechiui/block-editor

Version:
151 lines (145 loc) 4.88 kB
/** * 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> ); }