@gechiui/block-editor
Version:
175 lines (156 loc) • 4.98 kB
JavaScript
/**
* GeChiUI dependencies
*/
import { useCallback } from '@gechiui/element';
import {
serialize,
pasteHandler,
store as blocksStore,
} from '@gechiui/blocks';
import {
documentHasSelection,
documentHasUncollapsedSelection,
} from '@gechiui/dom';
import { useDispatch, useSelect } from '@gechiui/data';
import { __, _n, sprintf } from '@gechiui/i18n';
import { store as noticesStore } from '@gechiui/notices';
import { useRefEffect } from '@gechiui/compose';
/**
* Internal dependencies
*/
import { getPasteEventData } from '../../utils/get-paste-event-data';
import { store as blockEditorStore } from '../../store';
export function useNotifyCopy() {
const { getBlockName } = useSelect( blockEditorStore );
const { getBlockType } = useSelect( blocksStore );
const { createSuccessNotice } = useDispatch( noticesStore );
return useCallback( ( eventType, selectedBlockClientIds ) => {
let notice = '';
if ( selectedBlockClientIds.length === 1 ) {
const clientId = selectedBlockClientIds[ 0 ];
const title = getBlockType( getBlockName( clientId ) )?.title;
notice =
eventType === 'copy'
? sprintf(
// Translators: Name of the block being copied, e.g. "Paragraph".
__( '“%s”区块已复制至剪贴板。' ),
title
)
: sprintf(
// Translators: Name of the block being cut, e.g. "Paragraph".
__( '“%s”区块已移动至剪贴板。' ),
title
);
} else {
notice =
eventType === 'copy'
? sprintf(
// Translators: %d: Number of blocks being copied.
_n(
'已将%d区块复制至剪贴板。',
'Copied %d blocks to clipboard.',
selectedBlockClientIds.length
),
selectedBlockClientIds.length
)
: sprintf(
// Translators: %d: Number of blocks being cut.
_n(
'已将%d区块移至剪贴板。',
'Moved %d blocks to clipboard.',
selectedBlockClientIds.length
),
selectedBlockClientIds.length
);
}
createSuccessNotice( notice, {
type: 'snackbar',
} );
}, [] );
}
export function useClipboardHandler() {
const {
getBlocksByClientId,
getSelectedBlockClientIds,
hasMultiSelection,
getSettings,
} = useSelect( blockEditorStore );
const { flashBlock, removeBlocks, replaceBlocks } = useDispatch(
blockEditorStore
);
const notifyCopy = useNotifyCopy();
return useRefEffect( ( node ) => {
function handler( event ) {
const selectedBlockClientIds = getSelectedBlockClientIds();
if ( selectedBlockClientIds.length === 0 ) {
return;
}
// Always handle multiple selected blocks.
if ( ! hasMultiSelection() ) {
const { target } = event;
const { ownerDocument } = target;
// If copying, only consider actual text selection as selection.
// Otherwise, any focus on an input field is considered.
const hasSelection =
event.type === 'copy' || event.type === 'cut'
? documentHasUncollapsedSelection( ownerDocument )
: documentHasSelection( ownerDocument );
// Let native copy behaviour take over in input fields.
if ( hasSelection ) {
return;
}
}
if ( ! node.contains( event.target.ownerDocument.activeElement ) ) {
return;
}
const eventDefaultPrevented = event.defaultPrevented;
event.preventDefault();
if ( event.type === 'copy' || event.type === 'cut' ) {
if ( selectedBlockClientIds.length === 1 ) {
flashBlock( selectedBlockClientIds[ 0 ] );
}
notifyCopy( event.type, selectedBlockClientIds );
const blocks = getBlocksByClientId( selectedBlockClientIds );
const serialized = serialize( blocks );
event.clipboardData.setData( 'text/plain', serialized );
event.clipboardData.setData( 'text/html', serialized );
}
if ( event.type === 'cut' ) {
removeBlocks( selectedBlockClientIds );
} else if ( event.type === 'paste' ) {
if ( eventDefaultPrevented ) {
// This was likely already handled in rich-text/use-paste-handler.js
return;
}
const {
__experimentalCanUserUseUnfilteredHTML: canUserUseUnfilteredHTML,
} = getSettings();
const { plainText, html } = getPasteEventData( event );
const blocks = pasteHandler( {
HTML: html,
plainText,
mode: 'BLOCKS',
canUserUseUnfilteredHTML,
} );
replaceBlocks(
selectedBlockClientIds,
blocks,
blocks.length - 1,
-1
);
}
}
node.ownerDocument.addEventListener( 'copy', handler );
node.ownerDocument.addEventListener( 'cut', handler );
node.ownerDocument.addEventListener( 'paste', handler );
return () => {
node.ownerDocument.removeEventListener( 'copy', handler );
node.ownerDocument.removeEventListener( 'cut', handler );
node.ownerDocument.removeEventListener( 'paste', handler );
};
}, [] );
}
function CopyHandler( { children } ) {
return <div ref={ useClipboardHandler() }>{ children }</div>;
}
export default CopyHandler;