@gechiui/block-editor
Version:
201 lines (189 loc) • 5.26 kB
JavaScript
/**
* External dependencies
*/
import { uniqueId, noop } from 'lodash';
/**
* GeChiUI dependencies
*/
import { useState, createRef, renderToString } from '@gechiui/element';
import { __ } from '@gechiui/i18n';
import { speak } from '@gechiui/a11y';
import {
FormFileUpload,
NavigableMenu,
MenuItem,
ToolbarButton,
Dropdown,
withFilters,
} from '@gechiui/components';
import { withDispatch, useSelect } from '@gechiui/data';
import { DOWN } from '@gechiui/keycodes';
import { compose } from '@gechiui/compose';
import { upload, media as mediaIcon } from '@gechiui/icons';
import { store as noticesStore } from '@gechiui/notices';
/**
* Internal dependencies
*/
import MediaUpload from '../media-upload';
import MediaUploadCheck from '../media-upload/check';
import LinkControl from '../link-control';
import { store as blockEditorStore } from '../../store';
const MediaReplaceFlow = ( {
mediaURL,
mediaId,
allowedTypes,
accept,
onSelect,
onSelectURL,
onFilesUpload = noop,
name = __( '替换' ),
createNotice,
removeNotice,
children,
} ) => {
const [ mediaURLValue, setMediaURLValue ] = useState( mediaURL );
const mediaUpload = useSelect( ( select ) => {
return select( blockEditorStore ).getSettings().mediaUpload;
}, [] );
const editMediaButtonRef = createRef();
const errorNoticeID = uniqueId(
'block-editor/media-replace-flow/error-notice/'
);
const onError = ( message ) => {
const errorElement = document.createElement( 'div' );
errorElement.innerHTML = renderToString( message );
// The default error contains some HTML that,
// for example, makes the filename bold.
// The notice, by default, accepts strings only and so
// we need to remove the html from the error.
const renderMsg =
errorElement.textContent || errorElement.innerText || '';
// We need to set a timeout for showing the notice
// so that VoiceOver and possibly other screen readers
// can announce the error afer the toolbar button
// regains focus once the upload dialog closes.
// Otherwise VO simply skips over the notice and announces
// the focused element and the open menu.
setTimeout( () => {
createNotice( 'error', renderMsg, {
speak: true,
id: errorNoticeID,
isDismissible: true,
} );
}, 1000 );
};
const selectMedia = ( media ) => {
setMediaURLValue( media.url );
// Calling `onSelect` after the state update since it might unmount the component.
onSelect( media );
speak( __( '此媒体文件已被替换' ) );
removeNotice( errorNoticeID );
};
const selectURL = ( newURL ) => {
onSelectURL( newURL );
};
const uploadFiles = ( event ) => {
const files = event.target.files;
onFilesUpload( files );
const setMedia = ( [ media ] ) => {
selectMedia( media );
};
mediaUpload( {
allowedTypes,
filesList: files,
onFileChange: setMedia,
onError,
} );
};
const openOnArrowDown = ( event ) => {
if ( event.keyCode === DOWN ) {
event.preventDefault();
event.target.click();
}
};
const POPOVER_PROPS = {
isAlternate: true,
};
return (
<Dropdown
popoverProps={ POPOVER_PROPS }
contentClassName="block-editor-media-replace-flow__options"
renderToggle={ ( { isOpen, onToggle } ) => (
<ToolbarButton
ref={ editMediaButtonRef }
aria-expanded={ isOpen }
aria-haspopup="true"
onClick={ onToggle }
onKeyDown={ openOnArrowDown }
>
{ name }
</ToolbarButton>
) }
renderContent={ ( { onClose } ) => (
<>
<NavigableMenu className="block-editor-media-replace-flow__media-upload-menu">
<MediaUpload
value={ mediaId }
onSelect={ ( media ) => selectMedia( media ) }
allowedTypes={ allowedTypes }
render={ ( { open } ) => (
<MenuItem icon={ mediaIcon } onClick={ open }>
{ __( '打开媒体库' ) }
</MenuItem>
) }
/>
<MediaUploadCheck>
<FormFileUpload
onChange={ ( event ) => {
uploadFiles( event, onClose );
} }
accept={ accept }
render={ ( { openFileDialog } ) => {
return (
<MenuItem
icon={ upload }
onClick={ () => {
openFileDialog();
} }
>
{ __( '上传' ) }
</MenuItem>
);
} }
/>
</MediaUploadCheck>
{ children }
</NavigableMenu>
{ onSelectURL && (
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
<form className="block-editor-media-flow__url-input">
<span className="block-editor-media-replace-flow__image-url-label">
{ __( '当前媒体网址:' ) }
</span>
<LinkControl
value={ { url: mediaURLValue } }
settings={ [] }
showSuggestions={ false }
onChange={ ( { url } ) => {
setMediaURLValue( url );
selectURL( url );
editMediaButtonRef.current.focus();
} }
/>
</form>
) }
</>
) }
/>
);
};
export default compose( [
withDispatch( ( dispatch ) => {
const { createNotice, removeNotice } = dispatch( noticesStore );
return {
createNotice,
removeNotice,
};
} ),
withFilters( 'editor.MediaReplaceFlow' ),
] )( MediaReplaceFlow );