UNPKG

@wordpress/block-editor

Version:
259 lines (244 loc) 6.4 kB
/** * External dependencies */ import classnames from 'classnames'; /** * WordPress dependencies */ import { useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { speak } from '@wordpress/a11y'; import { FormFileUpload, NavigableMenu, MenuItem, ToolbarButton, Dropdown, withFilters, Tooltip, } from '@wordpress/components'; import { useSelect, withDispatch } from '@wordpress/data'; import { DOWN } from '@wordpress/keycodes'; import { postFeaturedImage, upload, media as mediaIcon, } from '@wordpress/icons'; import { compose } from '@wordpress/compose'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; import { store as noticesStore } from '@wordpress/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 noop = () => {}; let uniqueId = 0; const MediaReplaceFlow = ( { mediaURL, mediaId, mediaIds, allowedTypes, accept, onError, onSelect, onSelectURL, onToggleFeaturedImage, useFeaturedImage, onFilesUpload = noop, name = __( 'Replace' ), createNotice, removeNotice, children, multiple = false, addToGallery, handleUpload = true, popoverProps = { variant: 'toolbar', }, } ) => { const mediaUpload = useSelect( ( select ) => { return select( blockEditorStore ).getSettings().mediaUpload; }, [] ); const canUpload = !! mediaUpload; const editMediaButtonRef = useRef(); const errorNoticeID = `block-editor/media-replace-flow/error-notice/${ ++uniqueId }`; const onUploadError = ( message ) => { const safeMessage = stripHTML( message ); if ( onError ) { onError( safeMessage ); return; } // 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', safeMessage, { speak: true, id: errorNoticeID, isDismissible: true, } ); }, 1000 ); }; const selectMedia = ( media, closeMenu ) => { if ( useFeaturedImage && onToggleFeaturedImage ) { onToggleFeaturedImage(); } closeMenu(); // Calling `onSelect` after the state update since it might unmount the component. onSelect( media ); speak( __( 'The media file has been replaced' ) ); removeNotice( errorNoticeID ); }; const uploadFiles = ( event, closeMenu ) => { const files = event.target.files; if ( ! handleUpload ) { closeMenu(); return onSelect( files ); } onFilesUpload( files ); mediaUpload( { allowedTypes, filesList: files, onFileChange: ( [ media ] ) => { selectMedia( media, closeMenu ); }, onError: onUploadError, } ); }; const openOnArrowDown = ( event ) => { if ( event.keyCode === DOWN ) { event.preventDefault(); event.target.click(); } }; const onlyAllowsImages = () => { if ( ! allowedTypes || allowedTypes.length === 0 ) { return false; } return allowedTypes.every( ( allowedType ) => allowedType === 'image' || allowedType.startsWith( 'image/' ) ); }; const gallery = multiple && onlyAllowsImages(); return ( <Dropdown popoverProps={ popoverProps } 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"> <MediaUploadCheck> <MediaUpload gallery={ gallery } addToGallery={ addToGallery } multiple={ multiple } value={ multiple ? mediaIds : mediaId } onSelect={ ( media ) => selectMedia( media, onClose ) } allowedTypes={ allowedTypes } render={ ( { open } ) => ( <MenuItem icon={ mediaIcon } onClick={ open } > { __( 'Open Media Library' ) } </MenuItem> ) } /> <FormFileUpload onChange={ ( event ) => { uploadFiles( event, onClose ); } } accept={ accept } multiple={ multiple } render={ ( { openFileDialog } ) => { return ( <MenuItem icon={ upload } onClick={ () => { openFileDialog(); } } > { __( 'Upload' ) } </MenuItem> ); } } /> </MediaUploadCheck> { onToggleFeaturedImage && ( <MenuItem icon={ postFeaturedImage } onClick={ onToggleFeaturedImage } isPressed={ useFeaturedImage } > { __( 'Use featured image' ) } </MenuItem> ) } { children } </NavigableMenu> { onSelectURL && ( // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions <form className={ classnames( 'block-editor-media-flow__url-input', { 'has-siblings': canUpload || onToggleFeaturedImage, } ) } > <span className="block-editor-media-replace-flow__image-url-label"> { __( 'Current media URL:' ) } </span> <Tooltip text={ mediaURL } position="bottom"> <div> <LinkControl value={ { url: mediaURL } } settings={ [] } showSuggestions={ false } onChange={ ( { url } ) => { onSelectURL( url ); editMediaButtonRef.current.focus(); } } /> </div> </Tooltip> </form> ) } </> ) } /> ); }; /** * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/media-replace-flow/README.md */ export default compose( [ withDispatch( ( dispatch ) => { const { createNotice, removeNotice } = dispatch( noticesStore ); return { createNotice, removeNotice, }; } ), withFilters( 'editor.MediaReplaceFlow' ), ] )( MediaReplaceFlow );