UNPKG

@gechiui/block-editor

Version:
425 lines (389 loc) 10.5 kB
/** * External dependencies */ import { noop } from 'lodash'; import classnames from 'classnames'; /** * GeChiUI dependencies */ import { Button, FormFileUpload, Placeholder, DropZone, withFilters, } from '@gechiui/components'; import { __ } from '@gechiui/i18n'; import { useState, useEffect } from '@gechiui/element'; import { useSelect } from '@gechiui/data'; import deprecated from '@gechiui/deprecated'; import { keyboardReturn } from '@gechiui/icons'; /** * Internal dependencies */ import MediaUpload from '../media-upload'; import MediaUploadCheck from '../media-upload/check'; import URLPopover from '../url-popover'; import { store as blockEditorStore } from '../../store'; const InsertFromURLPopover = ( { src, onChange, onSubmit, onClose } ) => ( <URLPopover onClose={ onClose }> <form className="block-editor-media-placeholder__url-input-form" onSubmit={ onSubmit } > <input className="block-editor-media-placeholder__url-input-field" type="text" aria-label={ __( 'URL' ) } placeholder={ __( '粘贴或键入URL' ) } onChange={ onChange } value={ src } /> <Button className="block-editor-media-placeholder__url-input-submit-button" icon={ keyboardReturn } label={ __( '应用' ) } type="submit" /> </form> </URLPopover> ); export function MediaPlaceholder( { value = {}, allowedTypes, className, icon, labels = {}, mediaPreview, notices, isAppender, accept, addToGallery, multiple = false, handleUpload = true, dropZoneUIOnly, disableDropZone, disableMediaButtons, onError, onSelect, onCancel, onSelectURL, onDoubleClick, onFilesPreUpload = noop, onHTMLDrop = noop, children, mediaLibraryButton, placeholder, style, } ) { const mediaUpload = useSelect( ( select ) => { const { getSettings } = select( blockEditorStore ); return getSettings().mediaUpload; }, [] ); const [ src, setSrc ] = useState( '' ); const [ isURLInputVisible, setIsURLInputVisible ] = useState( false ); useEffect( () => { setSrc( value?.src ?? '' ); }, [ value?.src ] ); const onlyAllowsImages = () => { if ( ! allowedTypes || allowedTypes.length === 0 ) { return false; } return allowedTypes.every( ( allowedType ) => allowedType === 'image' || allowedType.startsWith( 'image/' ) ); }; const onChangeSrc = ( event ) => { setSrc( event.target.value ); }; const openURLInput = () => { setIsURLInputVisible( true ); }; const closeURLInput = () => { setIsURLInputVisible( false ); }; const onSubmitSrc = ( event ) => { event.preventDefault(); if ( src && onSelectURL ) { onSelectURL( src ); closeURLInput(); } }; const onFilesUpload = ( files ) => { if ( ! handleUpload ) { return onSelect( files ); } onFilesPreUpload( files ); let setMedia; if ( multiple ) { if ( addToGallery ) { // Since the setMedia function runs multiple times per upload group // and is passed newMedia containing every item in its group each time, we must // filter out whatever this upload group had previously returned to the // gallery before adding and returning the image array with replacement newMedia // values. // Define an array to store urls from newMedia between subsequent function calls. let lastMediaPassed = []; setMedia = ( newMedia ) => { // Remove any images this upload group is responsible for (lastMediaPassed). // Their replacements are contained in newMedia. const filteredMedia = ( value ?? [] ).filter( ( item ) => { // If Item has id, only remove it if lastMediaPassed has an item with that id. if ( item.id ) { return ! lastMediaPassed.some( // Be sure to convert to number for comparison. ( { id } ) => Number( id ) === Number( item.id ) ); } // Compare transient images via .includes since gallery may append extra info onto the url. return ! lastMediaPassed.some( ( { urlSlug } ) => item.url.includes( urlSlug ) ); } ); // Return the filtered media array along with newMedia. onSelect( filteredMedia.concat( newMedia ) ); // Reset lastMediaPassed and set it with ids and urls from newMedia. lastMediaPassed = newMedia.map( ( media ) => { // Add everything up to '.fileType' to compare via .includes. const cutOffIndex = media.url.lastIndexOf( '.' ); const urlSlug = media.url.slice( 0, cutOffIndex ); return { id: media.id, urlSlug }; } ); }; } else { setMedia = onSelect; } } else { setMedia = ( [ media ] ) => onSelect( media ); } mediaUpload( { allowedTypes, filesList: files, onFileChange: setMedia, onError, } ); }; const onUpload = ( event ) => { onFilesUpload( event.target.files ); }; const defaultRenderPlaceholder = ( content ) => { let { instructions, title } = labels; if ( ! mediaUpload && ! onSelectURL ) { instructions = __( '要编辑此区块,您需要有上传媒体的权限。' ); } if ( instructions === undefined || title === undefined ) { const typesAllowed = allowedTypes ?? []; const [ firstAllowedType ] = typesAllowed; const isOneType = 1 === typesAllowed.length; const isAudio = isOneType && 'audio' === firstAllowedType; const isImage = isOneType && 'image' === firstAllowedType; const isVideo = isOneType && 'video' === firstAllowedType; if ( instructions === undefined && mediaUpload ) { instructions = __( '上传媒体文件,或从您的媒体库中选择。' ); if ( isAudio ) { instructions = __( '上传音频文件,从您的媒体库中选择,或通过URL添加。' ); } else if ( isImage ) { instructions = __( '上传图片,或从您的媒体库中选择,或通过URL添加。' ); } else if ( isVideo ) { instructions = __( '上传视频文件,从您的媒体库中选择,或通过URL添加。' ); } } if ( title === undefined ) { title = __( '媒体' ); if ( isAudio ) { title = __( '音频' ); } else if ( isImage ) { title = __( '图片' ); } else if ( isVideo ) { title = __( '视频' ); } } } const placeholderClassName = classnames( 'block-editor-media-placeholder', className, { 'is-appender': isAppender, } ); return ( <Placeholder icon={ icon } label={ title } instructions={ instructions } className={ placeholderClassName } notices={ notices } onDoubleClick={ onDoubleClick } preview={ mediaPreview } style={ style } > { content } { children } </Placeholder> ); }; const renderPlaceholder = placeholder ?? defaultRenderPlaceholder; const renderDropZone = () => { if ( disableDropZone ) { return null; } return ( <DropZone onFilesDrop={ onFilesUpload } onHTMLDrop={ onHTMLDrop } /> ); }; const renderCancelLink = () => { return ( onCancel && ( <Button className="block-editor-media-placeholder__cancel-button" title={ __( '取消' ) } variant="link" onClick={ onCancel } > { __( '取消' ) } </Button> ) ); }; const renderUrlSelectionUI = () => { return ( onSelectURL && ( <div className="block-editor-media-placeholder__url-input-container"> <Button className="block-editor-media-placeholder__button" onClick={ openURLInput } isPressed={ isURLInputVisible } variant="tertiary" > { __( '从URL插入' ) } </Button> { isURLInputVisible && ( <InsertFromURLPopover src={ src } onChange={ onChangeSrc } onSubmit={ onSubmitSrc } onClose={ closeURLInput } /> ) } </div> ) ); }; const renderMediaUploadChecked = () => { const defaultButton = ( { open } ) => { return ( <Button variant="tertiary" onClick={ () => { open(); } } > { __( '媒体库' ) } </Button> ); }; const libraryButton = mediaLibraryButton ?? defaultButton; const uploadMediaLibraryButton = ( <MediaUpload addToGallery={ addToGallery } gallery={ multiple && onlyAllowsImages() } multiple={ multiple } onSelect={ onSelect } allowedTypes={ allowedTypes } value={ Array.isArray( value ) ? value.map( ( { id } ) => id ) : value.id } render={ libraryButton } /> ); if ( mediaUpload && isAppender ) { return ( <> { renderDropZone() } <FormFileUpload onChange={ onUpload } accept={ accept } multiple={ multiple } render={ ( { openFileDialog } ) => { const content = ( <> <Button variant="primary" className={ classnames( 'block-editor-media-placeholder__button', 'block-editor-media-placeholder__upload-button' ) } onClick={ openFileDialog } > { __( '上传' ) } </Button> { uploadMediaLibraryButton } { renderUrlSelectionUI() } { renderCancelLink() } </> ); return renderPlaceholder( content ); } } /> </> ); } if ( mediaUpload ) { const content = ( <> { renderDropZone() } <FormFileUpload variant="primary" className={ classnames( 'block-editor-media-placeholder__button', 'block-editor-media-placeholder__upload-button' ) } onChange={ onUpload } accept={ accept } multiple={ multiple } > { __( '上传' ) } </FormFileUpload> { uploadMediaLibraryButton } { renderUrlSelectionUI() } { renderCancelLink() } </> ); return renderPlaceholder( content ); } return renderPlaceholder( uploadMediaLibraryButton ); }; if ( dropZoneUIOnly || disableMediaButtons ) { if ( dropZoneUIOnly ) { deprecated( 'gc.blockEditor.MediaPlaceholder dropZoneUIOnly prop', { since: '5.4', alternative: 'disableMediaButtons', } ); } return <MediaUploadCheck>{ renderDropZone() }</MediaUploadCheck>; } return ( <MediaUploadCheck fallback={ renderPlaceholder( renderUrlSelectionUI() ) } > { renderMediaUploadChecked() } </MediaUploadCheck> ); } /** * @see https://github.com/GeChiUI/gutenberg/blob/HEAD/packages/block-editor/src/components/media-placeholder/README.md */ export default withFilters( 'editor.MediaPlaceholder' )( MediaPlaceholder );