import clsx from 'clsx'; import { Button, DropZone, Icon as WCIcon, Spinner as WCSpinner, __experimentalText as WCText, __experimentalTruncate as Truncate, __experimentalVStack as VStack, __experimentalHStack as HStack, BaseControl, withFilters, } from '@wordpress/components'; import { isBlobURL, getBlobTypeByURL } from '@wordpress/blob'; import { store as coreStore, type Attachment } from '@wordpress/core-data'; import { useSelect, useDispatch } from '@wordpress/data'; import { useCallback, useEffect, useMemo, useRef, useState, } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { decodeEntities } from '@wordpress/html-entities'; import { archive, audio, video, file, closeSmall, chevronUp, chevronDown, chevronLeft, chevronRight, } from '@wordpress/icons'; import { Stack, Tooltip, ValidityIndicator, VisuallyHidden, } from '@wordpress/ui'; import { speak } from '@wordpress/a11y'; import { MediaUpload, uploadMedia, privateApis as mediaUtilsPrivateApis, } from '@wordpress/media-utils'; import { store as noticesStore } from '@wordpress/notices'; import { unlock } from '../../lock-unlock'; import type { MediaEditProps } from '../../types'; import useMovingAnimation from './use-moving-animation'; const { MediaUploadModal } = unlock( mediaUtilsPrivateApis ); function AnimatedMediaItem( { children, index, className, }: { children: React.ReactNode; index: number; className?: string; } ) { const ref = useMovingAnimation( index ); return (
{ children }
); } type BlobItem = { id: string; source_url: string; mime_type: string | undefined; alt_text?: string; }; function normalizeValue( value: number | number[] | undefined ): number[] { if ( Array.isArray( value ) ) { return value; } return value ? [ value ] : []; } /** * Conditional Media component that uses MediaUploadModal when experiment is enabled, * otherwise falls back to media-utils MediaUpload. Also what `FilteredMediaUpload` * falls back to when nothing is registered on `editor.MediaUpload`, as in tests. * * @param root0 Component props. * @param root0.render Render prop function that receives { open } object. * @param root0.multiple Whether to allow multiple media selections; `'add'` keeps the current selection in the legacy media frame. * @return The component. */ function ConditionalMediaUpload( { render, multiple, ...props }: any ) { const [ isModalOpen, setIsModalOpen ] = useState( false ); if ( ( window as any ).__experimentalDataViewsMediaModal ) { return ( <> { render && render( { open: () => setIsModalOpen( true ) } ) } { isModalOpen && ( { setIsModalOpen( false ); props.onClose?.(); } } onSelect={ ( media: any ) => { setIsModalOpen( false ); props.onSelect?.( media ); } } /> ) } ); } // Fallback to media-utils MediaUpload when experiment is disabled. return ; } /* * `ConditionalMediaUpload` resolved through `editor.MediaUpload`: the hook * through which the editor supplies its media library to blocks and plugins * extend it (extra sources, validation). Those callbacks assume an editor * context, so `MediaEdit` does not use it; `MediaEditControl` does when * `isPickerFiltered` is set, for fields that render inside the editor. */ const FilteredMediaUpload = withFilters( 'editor.MediaUpload' )( ConditionalMediaUpload ) as React.ComponentType< Record< string, unknown > >; function MediaPickerButton( { open, children, label, showTooltip = false, onFilesDrop, attachment, isUploading = false, }: { open: () => void; children: React.ReactNode; label: string; showTooltip?: boolean; onFilesDrop: MediaEditAttachmentsProps[ 'onFilesDrop' ]; attachment?: MediaEditAttachment; isUploading?: boolean; } ) { const isBlob = attachment && isBlobURL( attachment.source_url ); const mediaPickerButton = (
{ if ( ! isUploading ) { open(); } } } onKeyDown={ ( event ) => { if ( isUploading ) { return; } if ( event.key === 'Enter' || event.key === ' ' ) { event.preventDefault(); open(); } } } aria-label={ label } aria-disabled={ isUploading } > { children } { isBlob && ( ) } { ! isUploading && ( onFilesDrop( files, attachment?.id as number ) } /> ) }
); if ( ! showTooltip ) { return mediaPickerButton; } return ( { label } ); } const archiveMimeTypes = [ 'application/zip', 'application/x-zip-compressed', 'application/x-rar-compressed', 'application/x-7z-compressed', 'application/x-tar', 'application/x-gzip', ]; function MediaTitle( { attachment }: { attachment: Attachment< 'view' > } ) { return ( { decodeEntities( attachment.title.rendered ) } ); } function MediaEditPlaceholder( props: { open: () => void; label: string; onFilesDrop: MediaEditAttachmentsProps[ 'onFilesDrop' ]; isUploading: boolean; } ) { return ( { props.label } ); } function MoveButtons( { itemId, index, totalItems, isUploading, moveItem, orientation = 'vertical', }: { itemId: number; index: number; totalItems: number; isUploading: boolean; moveItem: ( id: number, direction: 'up' | 'down' ) => void; orientation?: 'vertical' | 'horizontal'; } ) { const isHorizontal = orientation === 'horizontal'; return ( <>