diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 3980dd7b2aead..bfddb75538776 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -96,6 +96,7 @@ function BlockListBlock( { onInsertBlocksAfter, onMerge, toggleSelection, + params, } ) { const { themeSupportsLayout, @@ -155,6 +156,7 @@ function BlockListBlock( { __unstableParentLayout={ Object.keys( parentLayout ).length ? parentLayout : undefined } + params={ params } /> ); @@ -283,7 +285,7 @@ const applyWithSelect = withSelect( ( select, { clientId, rootClientId } ) => { // This function should never be called when a block is not present in // the state. It happens now because the order in withSelect rendering // is not correct. - const { name, attributes, isValid } = block || {}; + const { name, attributes, isValid, params } = block || {}; // Do not add new properties here, use `useSelect` instead to avoid // leaking new props to the public API (editor.BlockListBlock filter). @@ -302,6 +304,7 @@ const applyWithSelect = withSelect( ( select, { clientId, rootClientId } ) => { attributes, isValid, isSelected, + params, }; } ); diff --git a/packages/block-library/src/file/edit.js b/packages/block-library/src/file/edit.js index 733cdf9a9351f..33986f2620ff1 100644 --- a/packages/block-library/src/file/edit.js +++ b/packages/block-library/src/file/edit.js @@ -23,7 +23,7 @@ import { store as blockEditorStore, __experimentalGetElementClassName, } from '@wordpress/block-editor'; -import { useEffect } from '@wordpress/element'; +import { useEffect, useState } from '@wordpress/element'; import { useCopyToClipboard } from '@wordpress/compose'; import { __, _x } from '@wordpress/i18n'; import { file as icon } from '@wordpress/icons'; @@ -59,7 +59,13 @@ function ClipboardToolbarButton( { text, disabled } ) { ); } -function FileEdit( { attributes, isSelected, setAttributes, clientId } ) { +function FileEdit( { + attributes, + isSelected, + setAttributes, + clientId, + params, +} ) { const { id, fileId, @@ -72,6 +78,9 @@ function FileEdit( { attributes, isSelected, setAttributes, clientId } ) { displayPreview, previewHeight, } = attributes; + + const { blobURL } = params; + const { media, mediaUpload } = useSelect( ( select ) => ( { media: @@ -87,22 +96,27 @@ function FileEdit( { attributes, isSelected, setAttributes, clientId } ) { const { toggleSelection, __unstableMarkNextChangeAsNotPersistent } = useDispatch( blockEditorStore ); + const [ isUploadingBlob, setIsUploadingBlob ] = useState( false ); + useEffect( () => { // Upload a file drag-and-dropped into the editor. - if ( isBlobURL( href ) ) { - const file = getBlobByURL( href ); + const file = getBlobByURL( blobURL ); + if ( file ) { + setIsUploadingBlob( true ); mediaUpload( { filesList: [ file ], - onFileChange: ( [ newMedia ] ) => onSelectFile( newMedia ), - onError: onUploadError, + onFileChange: ( [ newMedia ] ) => { + onSelectFile( newMedia, { isPersistent: false } ); + setIsUploadingBlob( false ); + }, + onError: ( message ) => { + onUploadError( message, { isPersistent: false } ); + setIsUploadingBlob( false ); + }, } ); - revokeBlobURL( href ); - } - - if ( downloadButtonText === undefined ) { - changeDownloadButtonText( _x( 'Download', 'button label' ) ); + revokeBlobURL( blobURL ); } }, [] ); @@ -114,9 +128,12 @@ function FileEdit( { attributes, isSelected, setAttributes, clientId } ) { } }, [ href, fileId, clientId ] ); - function onSelectFile( newMedia ) { - if ( newMedia && newMedia.url ) { + function onSelectFile( newMedia, { isPersistent = true } = {} ) { + if ( newMedia && newMedia.url && ! isBlobURL( newMedia.url ) ) { const isPdf = newMedia.url.endsWith( '.pdf' ); + if ( ! isPersistent ) { + __unstableMarkNextChangeAsNotPersistent(); + } setAttributes( { href: newMedia.url, fileName: newMedia.title, @@ -178,9 +195,9 @@ function FileEdit( { attributes, isSelected, setAttributes, clientId } ) { const blockProps = useBlockProps( { className: classnames( - isBlobURL( href ) && getAnimateClassName( { type: 'loading' } ), + isUploadingBlob && getAnimateClassName( { type: 'loading' } ), { - 'is-transient': isBlobURL( href ), + 'is-transient': isUploadingBlob, } ), } ); @@ -232,7 +249,7 @@ function FileEdit( { attributes, isSelected, setAttributes, clientId } ) { />
@@ -297,7 +314,10 @@ function FileEdit( { attributes, isSelected, setAttributes, clientId } ) { 'button' ) ) } - value={ downloadButtonText } + value={ + downloadButtonText ?? + _x( 'Download', 'button label' ) + } withoutInteractiveFormatting placeholder={ __( 'Add text…' ) } onChange={ ( text ) => diff --git a/packages/block-library/src/file/transforms.js b/packages/block-library/src/file/transforms.js index 35dd9807daddd..06ef30a78937d 100644 --- a/packages/block-library/src/file/transforms.js +++ b/packages/block-library/src/file/transforms.js @@ -25,10 +25,8 @@ const transforms = { // File will be uploaded in componentDidMount() blocks.push( - createBlock( 'core/file', { - href: blobURL, - fileName: file.name, - textLinkHref: blobURL, + createBlock( 'core/file', {}, [], { + blobURL, } ) ); } ); diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js index 16cc4c014cf90..a2d2f6ac094f6 100644 --- a/packages/block-library/src/navigation/edit/index.js +++ b/packages/block-library/src/navigation/edit/index.js @@ -83,6 +83,7 @@ function Navigation( { setOverlayBackgroundColor, overlayTextColor, setOverlayTextColor, + params, // These props are used by the navigation editor to override specific // navigation block settings. @@ -402,18 +403,34 @@ function Navigation( { ] = useState(); const [ detectedOverlayColor, setDetectedOverlayColor ] = useState(); - const onSelectClassicMenu = async ( classicMenu ) => { - const navMenu = await convertClassicMenu( - classicMenu.id, - classicMenu.name, - 'draft' - ); - if ( navMenu ) { - handleUpdateMenu( navMenu.id, { - focusNavigationBlock: true, - } ); + const onSelectClassicMenu = useCallback( + async ( classicMenu ) => { + const navMenu = await convertClassicMenu( + classicMenu.id, + classicMenu.name, + 'draft' + ); + if ( navMenu ) { + handleUpdateMenu( navMenu.id, { + focusNavigationBlock: true, + } ); + } + }, + [ convertClassicMenu, handleUpdateMenu ] + ); + + // Convert the classic menu provided by the Legacy Widget block transform if + // it exists. + useEffect( () => { + if ( params.menuId ) { + const classicMenu = classicMenus?.find( + ( menu ) => menu.id === params.menuId + ); + if ( classicMenu ) { + onSelectClassicMenu( classicMenu ); + } } - }; + }, [ params.menuId, classicMenus, onSelectClassicMenu ] ); const onSelectNavigationMenu = ( menuId ) => { handleUpdateMenu( menuId ); diff --git a/packages/blocks/README.md b/packages/blocks/README.md index 27f6010786b18..64cabbc3dd7dd 100644 --- a/packages/blocks/README.md +++ b/packages/blocks/README.md @@ -205,8 +205,9 @@ Returns a block object given its type and attributes. _Parameters_ - _name_ `string`: Block name. -- _attributes_ `Object`: Block attributes. -- _innerBlocks_ `?Array`: Nested blocks. +- _attributes_ `[Object]`: Block attributes. +- _innerBlocks_ `[Array]`: Nested blocks. +- _params_ `[Object]`: Block params. _Returns_ diff --git a/packages/blocks/src/api/factory.js b/packages/blocks/src/api/factory.js index 6df8eb00005a8..983da72a964e7 100644 --- a/packages/blocks/src/api/factory.js +++ b/packages/blocks/src/api/factory.js @@ -24,13 +24,20 @@ import { /** * Returns a block object given its type and attributes. * - * @param {string} name Block name. - * @param {Object} attributes Block attributes. - * @param {?Array} innerBlocks Nested blocks. + * @param {string} name Block name. + * @param {Object} [attributes] Block attributes. + * @param {Array} [innerBlocks] Nested blocks. + * @param {Object} [params] Block params. * * @return {Object} Block object. + * */ -export function createBlock( name, attributes = {}, innerBlocks = [] ) { +export function createBlock( + name, + attributes = {}, + innerBlocks = [], + params = {} +) { const sanitizedAttributes = __experimentalSanitizeBlockAttributes( name, attributes @@ -46,6 +53,7 @@ export function createBlock( name, attributes = {}, innerBlocks = [] ) { isValid: true, attributes: sanitizedAttributes, innerBlocks, + params, }; } diff --git a/packages/edit-widgets/src/store/actions.js b/packages/edit-widgets/src/store/actions.js index 8124ace66bdb3..2ed29ad268831 100644 --- a/packages/edit-widgets/src/store/actions.js +++ b/packages/edit-widgets/src/store/actions.js @@ -235,9 +235,9 @@ export const saveWidgetArea = const widget = preservedRecords[ i ]; const { block, position } = batchMeta[ i ]; - // Set __internalWidgetId on the block. This will be persisted to the - // store when we dispatch receiveEntityRecords( post ) below. - post.blocks[ position ].attributes.__internalWidgetId = widget.id; + // Set widget ID on the block. This will be persisted to the store + // when we dispatch receiveEntityRecords( post ) below. + post.blocks[ position ].params.widgetId = widget.id; const error = registry .select( coreStore ) diff --git a/packages/widgets/src/blocks/legacy-widget/transforms.js b/packages/widgets/src/blocks/legacy-widget/transforms.js index 3e4aab3cc0b5b..242a224bd880f 100644 --- a/packages/widgets/src/blocks/legacy-widget/transforms.js +++ b/packages/widgets/src/blocks/legacy-widget/transforms.js @@ -3,100 +3,96 @@ */ import { createBlock } from '@wordpress/blocks'; -const legacyWidgetTransforms = [ +const toTransforms = [ { - block: 'core/calendar', - widget: 'calendar', + idBase: 'calendar', + blockName: 'core/calendar', + convert: () => createBlock( 'core/calendar' ), }, { - block: 'core/search', - widget: 'search', + idBase: 'search', + blockName: 'core/search', + convert: () => createBlock( 'core/search' ), }, { - block: 'core/html', - widget: 'custom_html', - transform: ( { content } ) => ( { - content, - } ), + idBase: 'custom_html', + blockName: 'core/html', + convert: ( { content } ) => + createBlock( 'core/html', { + content, + } ), }, { - block: 'core/archives', - widget: 'archives', - transform: ( { count, dropdown } ) => { - return { + idBase: 'archives', + blockName: 'core/archives', + convert: ( { count, dropdown } ) => + createBlock( 'core/archives', { displayAsDropdown: !! dropdown, showPostCounts: !! count, - }; - }, + } ), }, { - block: 'core/latest-posts', - widget: 'recent-posts', - transform: ( { show_date: displayPostDate, number } ) => { - return { + idBase: 'recent-posts', + blockName: 'core/latest-posts', + convert: ( { show_date: displayPostDate, number } ) => + createBlock( 'core/latest-posts', { displayPostDate: !! displayPostDate, postsToShow: number, - }; - }, + } ), }, { - block: 'core/latest-comments', - widget: 'recent-comments', - transform: ( { number } ) => { - return { + idBase: 'recent-comments', + blockName: 'core/latest-comments', + convert: ( { number } ) => + createBlock( 'core/latest-comments', { commentsToShow: number, - }; - }, + } ), }, { - block: 'core/tag-cloud', - widget: 'tag_cloud', - transform: ( { taxonomy, count } ) => { - return { + idBase: 'tag_cloud', + blockName: 'core/tag-cloud', + convert: ( { taxonomy, count } ) => + createBlock( 'core/tag-cloud', { showTagCounts: !! count, taxonomy, - }; - }, + } ), }, { - block: 'core/categories', - widget: 'categories', - transform: ( { count, dropdown, hierarchical } ) => { - return { + idBase: 'categories', + blockName: 'core/categories', + convert: ( { count, dropdown, hierarchical } ) => + createBlock( 'core/categories', { displayAsDropdown: !! dropdown, showPostCounts: !! count, showHierarchy: !! hierarchical, - }; - }, + } ), }, { - block: 'core/audio', - widget: 'media_audio', - transform: ( { url, preload, loop, attachment_id: id } ) => { - return { + idBase: 'media_audio', + blockName: 'core/audio', + convert: ( { url, preload, loop, attachment_id: id } ) => + createBlock( 'core/audio', { src: url, id, preload, loop, - }; - }, + } ), }, { - block: 'core/video', - widget: 'media_video', - transform: ( { url, preload, loop, attachment_id: id } ) => { - return { + idBase: 'media_video', + blockName: 'core/video', + convert: ( { url, preload, loop, attachment_id: id } ) => + createBlock( 'core/video', { src: url, id, preload, loop, - }; - }, + } ), }, { - block: 'core/image', - widget: 'media_image', - transform: ( { + idBase: 'media_image', + blockName: 'core/image', + convert: ( { alt, attachment_id: id, caption, @@ -109,8 +105,8 @@ const legacyWidgetTransforms = [ size: sizeSlug, url, width, - } ) => { - return { + } ) => + createBlock( 'core/image', { alt, caption, height, @@ -123,14 +119,13 @@ const legacyWidgetTransforms = [ sizeSlug, url, width, - }; - }, + } ), }, { - block: 'core/gallery', - widget: 'media_gallery', - transform: ( { ids, link_type: linkTo, size, number } ) => { - return { + idBase: 'media_gallery', + blockName: 'core/gallery', + convert: ( { ids, link_type: linkTo, size, number } ) => + createBlock( 'core/gallery', { ids, columns: number, linkTo, @@ -138,55 +133,56 @@ const legacyWidgetTransforms = [ images: ids.map( ( id ) => ( { id, } ) ), - }; - }, + } ), }, { - block: 'core/rss', - widget: 'rss', - transform: ( { + idBase: 'rss', + blockName: 'core/rss', + convert: ( { url, show_author: displayAuthor, show_date: displayDate, show_summary: displayExcerpt, items, - } ) => { - return { + } ) => + createBlock( 'core/rss', { feedURL: url, displayAuthor: !! displayAuthor, displayDate: !! displayDate, displayExcerpt: !! displayExcerpt, itemsToShow: items, - }; - }, + } ), }, -].map( ( { block, widget, transform } ) => { + { + idBase: 'nav_menu', + blockName: 'core/navigation', + convert: ( { nav_menu: navMenu } ) => + createBlock( 'core/navigation', {}, [], { menuId: navMenu } ), + }, +].map( ( { idBase, blockName, convert } ) => { return { type: 'block', - blocks: [ block ], - isMatch: ( { idBase, instance } ) => { - return idBase === widget && !! instance?.raw; + blocks: [ blockName ], + isMatch( attributes ) { + return attributes.idBase === idBase && !! attributes.instance?.raw; }, - transform: ( { instance } ) => { - const transformedBlock = createBlock( - block, - transform ? transform( instance.raw ) : undefined - ); - if ( ! instance.raw?.title ) { - return transformedBlock; + transform( attributes ) { + const block = convert( attributes.instance.raw ); + if ( ! attributes.instance.raw?.title ) { + return block; } return [ createBlock( 'core/heading', { - content: instance.raw.title, + content: attributes.instance.raw.title, } ), - transformedBlock, + block, ]; }, }; } ); const transforms = { - to: legacyWidgetTransforms, + to: toTransforms, }; export default transforms; diff --git a/packages/widgets/src/utils.js b/packages/widgets/src/utils.js index 75b1f3d08140e..0d0c1f5b0e238 100644 --- a/packages/widgets/src/utils.js +++ b/packages/widgets/src/utils.js @@ -3,31 +3,26 @@ /** * Get the internal widget id from block. * - * @typedef {Object} Attributes - * @property {string} __internalWidgetId The internal widget id. - * @typedef {Object} Block - * @property {Attributes} attributes The attributes of the block. - * - * @param {Block} block The block. + * @param {Object} block The block. * @return {string} The internal widget id. */ export function getWidgetIdFromBlock( block ) { - return block.attributes.__internalWidgetId; + return block.params.widgetId; } /** * Add internal widget id to block's attributes. * - * @param {Block} block The block. + * @param {Object} block The block. * @param {string} widgetId The widget id. - * @return {Block} The updated block. + * @return {Object} The updated block. */ export function addWidgetIdToBlock( block, widgetId ) { return { ...block, - attributes: { - ...( block.attributes || {} ), - __internalWidgetId: widgetId, + params: { + ...block.params, + widgetId, }, }; }