diff --git a/bin/api-docs/gen-block-lib-list.js b/bin/api-docs/gen-block-lib-list.js
index 60b0a99b096ab8..1b98efc7cced63 100644
--- a/bin/api-docs/gen-block-lib-list.js
+++ b/bin/api-docs/gen-block-lib-list.js
@@ -73,6 +73,7 @@ function getTruthyKeys( obj ) {
return Object.keys( obj )
.filter( ( key ) => ! key.startsWith( '__exp' ) )
+ .filter( ( key ) => ! obj[ key ].internal )
.map( ( key ) => ( obj[ key ] ? key : `~~${ key }~~` ) );
}
diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js
index 3980dd7b2aead3..d02501a054a8e0 100644
--- a/packages/block-editor/src/components/block-list/block.js
+++ b/packages/block-editor/src/components/block-list/block.js
@@ -315,22 +315,48 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => {
__unstableMarkLastChangeAsPersistent,
moveBlocksToPosition,
removeBlock,
+ __unstableMarkNextChangeAsNotPersistent,
} = dispatch( blockEditorStore );
// Do not add new properties here, use `useDispatch` instead to avoid
// leaking new props to the public API (editor.BlockListBlock filter).
return {
- setAttributes( newAttributes ) {
+ setAttributes( attributes ) {
const { getMultiSelectedBlockClientIds } =
registry.select( blockEditorStore );
const multiSelectedBlockClientIds =
getMultiSelectedBlockClientIds();
- const { clientId } = ownProps;
+ const { clientId, name } = ownProps;
const clientIds = multiSelectedBlockClientIds.length
? multiSelectedBlockClientIds
: [ clientId ];
- updateBlockAttributes( clientIds, newAttributes );
+ const blockType = getBlockType( name );
+
+ const internalKeys = new Set();
+ for ( const key in blockType.attributes ) {
+ if ( blockType.attributes[ key ].internal ) {
+ internalKeys.add( key );
+ }
+ }
+
+ const internalAttributes = {};
+ const nonInternalAttributes = {};
+ for ( const key in attributes ) {
+ if ( internalKeys.has( key ) ) {
+ internalAttributes[ key ] = attributes[ key ];
+ } else {
+ nonInternalAttributes[ key ] = attributes[ key ];
+ }
+ }
+
+ if ( Object.keys( internalAttributes ).length ) {
+ __unstableMarkNextChangeAsNotPersistent();
+ updateBlockAttributes( clientId, internalAttributes );
+ }
+ if ( Object.keys( nonInternalAttributes ).length ) {
+ updateBlockAttributes( clientIds, nonInternalAttributes );
+ }
},
onInsertBlocks( blocks, index ) {
const { rootClientId } = ownProps;
diff --git a/packages/block-library/src/file/block.json b/packages/block-library/src/file/block.json
index 08a78f3a94ce42..b2d16eb9dc07fe 100644
--- a/packages/block-library/src/file/block.json
+++ b/packages/block-library/src/file/block.json
@@ -52,6 +52,10 @@
"previewHeight": {
"type": "number",
"default": 600
+ },
+ "_blobURL": {
+ "type": "string",
+ "internal": true
}
},
"supports": {
diff --git a/packages/block-library/src/file/edit.js b/packages/block-library/src/file/edit.js
index 733cdf9a9351fc..c23a8fc9cfbc76 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';
@@ -71,7 +71,9 @@ function FileEdit( { attributes, isSelected, setAttributes, clientId } ) {
downloadButtonText,
displayPreview,
previewHeight,
+ _blobURL: blobURL,
} = attributes;
+
const { media, mediaUpload } = useSelect(
( select ) => ( {
media:
@@ -87,21 +89,31 @@ 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 );
+ revokeBlobURL( blobURL );
}
if ( downloadButtonText === undefined ) {
+ __unstableMarkNextChangeAsNotPersistent();
changeDownloadButtonText( _x( 'Download', 'button label' ) );
}
}, [] );
@@ -114,9 +126,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 +193,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 +247,7 @@ function FileEdit( { attributes, isSelected, setAttributes, clientId } ) {
/>
diff --git a/packages/block-library/src/file/transforms.js b/packages/block-library/src/file/transforms.js
index 35dd9807daddd7..bdcf4b80aefaf7 100644
--- a/packages/block-library/src/file/transforms.js
+++ b/packages/block-library/src/file/transforms.js
@@ -21,14 +21,10 @@ const transforms = {
const blocks = [];
files.forEach( ( file ) => {
- const blobURL = createBlobURL( file );
-
- // File will be uploaded in componentDidMount()
blocks.push(
createBlock( 'core/file', {
- href: blobURL,
- fileName: file.name,
- textLinkHref: blobURL,
+ // File will be uploaded when block mounts.
+ _blobURL: createBlobURL( file ),
} )
);
} );
diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json
index ce2bed0d8837f6..ce4b9f609765ed 100644
--- a/packages/block-library/src/navigation/block.json
+++ b/packages/block-library/src/navigation/block.json
@@ -71,6 +71,10 @@
"templateLock": {
"type": [ "string", "boolean" ],
"enum": [ "all", "insert", "contentOnly", false ]
+ },
+ "_classicMenuId": {
+ "type": "number",
+ "internal": true
}
},
"providesContext": {
diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js
index 16cc4c014cf90f..448aaa2ba669ae 100644
--- a/packages/block-library/src/navigation/edit/index.js
+++ b/packages/block-library/src/navigation/edit/index.js
@@ -102,6 +102,7 @@ function Navigation( {
} = {},
hasIcon,
icon = 'handle',
+ _classicMenuId: classicMenuId,
} = attributes;
const ref = attributes.ref;
@@ -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 ( classicMenuId ) {
+ const classicMenu = classicMenus?.find(
+ ( menu ) => menu.id === classicMenuId
+ );
+ if ( classicMenu ) {
+ onSelectClassicMenu( classicMenu );
+ }
}
- };
+ }, [ classicMenuId, classicMenus, onSelectClassicMenu ] );
const onSelectNavigationMenu = ( menuId ) => {
handleUpdateMenu( menuId );
diff --git a/packages/blocks/src/api/serializer.js b/packages/blocks/src/api/serializer.js
index 3300e0893d2459..1c45583c4b769e 100644
--- a/packages/blocks/src/api/serializer.js
+++ b/packages/blocks/src/api/serializer.js
@@ -225,6 +225,11 @@ export function getCommentAttributes( blockType, attributes ) {
return accumulator;
}
+ // Ignore internal attributes.
+ if ( attributeSchema.internal ) {
+ return accumulator;
+ }
+
// Ignore default value.
if (
'default' in attributeSchema &&
diff --git a/packages/blocks/src/api/utils.js b/packages/blocks/src/api/utils.js
index c43445c6272264..da1cddf22bdf1e 100644
--- a/packages/blocks/src/api/utils.js
+++ b/packages/blocks/src/api/utils.js
@@ -261,6 +261,10 @@ export function __experimentalSanitizeBlockAttributes( name, attributes ) {
return Object.entries( blockType.attributes ).reduce(
( accumulator, [ key, schema ] ) => {
+ if ( schema.internal ) {
+ return accumulator;
+ }
+
const value = attributes[ key ];
if ( undefined !== value ) {
diff --git a/packages/edit-widgets/src/store/actions.js b/packages/edit-widgets/src/store/actions.js
index 8124ace66bdb3a..51bad6c274cdea 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
+ // Set _widgetId on the block. This will be persisted to the
// store when we dispatch receiveEntityRecords( post ) below.
- post.blocks[ position ].attributes.__internalWidgetId = widget.id;
+ post.blocks[ position ].attributes._widgetId = widget.id;
const error = registry
.select( coreStore )
diff --git a/packages/widgets/package.json b/packages/widgets/package.json
index a3bfcb9c8d88fb..058d56b0625a39 100644
--- a/packages/widgets/package.json
+++ b/packages/widgets/package.json
@@ -29,6 +29,7 @@
"@wordpress/core-data": "file:../core-data",
"@wordpress/data": "file:../data",
"@wordpress/element": "file:../element",
+ "@wordpress/hooks": "file:../hooks",
"@wordpress/i18n": "file:../i18n",
"@wordpress/icons": "file:../icons",
"@wordpress/notices": "file:../notices",
diff --git a/packages/widgets/src/blocks/legacy-widget/transforms.js b/packages/widgets/src/blocks/legacy-widget/transforms.js
index 3e4aab3cc0b5b1..73bf1b9a805a5f 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,58 @@ 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', {
+ _classicMenuId: 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/hooks/index.js b/packages/widgets/src/hooks/index.js
new file mode 100644
index 00000000000000..bcc37c146bfae4
--- /dev/null
+++ b/packages/widgets/src/hooks/index.js
@@ -0,0 +1,4 @@
+/**
+ * Internal dependencies
+ */
+import './widget-id';
diff --git a/packages/widgets/src/hooks/widget-id.js b/packages/widgets/src/hooks/widget-id.js
new file mode 100644
index 00000000000000..8f1c708183ca00
--- /dev/null
+++ b/packages/widgets/src/hooks/widget-id.js
@@ -0,0 +1,19 @@
+/**
+ * WordPress dependencies
+ */
+import { addFilter } from '@wordpress/hooks';
+
+addFilter(
+ 'blocks.registerBlockType',
+ 'core/widgets/widget-id',
+ ( settings ) => ( {
+ ...settings,
+ attributes: {
+ ...settings.attributes,
+ _widgetId: {
+ type: 'string',
+ internal: true,
+ },
+ },
+ } )
+);
diff --git a/packages/widgets/src/index.js b/packages/widgets/src/index.js
index 9520943bcae873..a85848675725bf 100644
--- a/packages/widgets/src/index.js
+++ b/packages/widgets/src/index.js
@@ -8,6 +8,7 @@ import { registerBlockType } from '@wordpress/blocks';
*/
import * as legacyWidget from './blocks/legacy-widget';
import * as widgetGroup from './blocks/widget-group';
+import './hooks';
export * from './components';
export * from './utils';
diff --git a/packages/widgets/src/utils.js b/packages/widgets/src/utils.js
index 75b1f3d08140ec..64d48836ccfdbb 100644
--- a/packages/widgets/src/utils.js
+++ b/packages/widgets/src/utils.js
@@ -4,15 +4,15 @@
* Get the internal widget id from block.
*
* @typedef {Object} Attributes
- * @property {string} __internalWidgetId The internal widget id.
+ * @property {string} _widgetId The internal widget id.
* @typedef {Object} Block
- * @property {Attributes} attributes The attributes of the block.
+ * @property {Attributes} attributes The attributes of the block.
*
- * @param {Block} block The block.
+ * @param {Block} block The block.
* @return {string} The internal widget id.
*/
export function getWidgetIdFromBlock( block ) {
- return block.attributes.__internalWidgetId;
+ return block.attributes._widgetId;
}
/**
@@ -27,7 +27,7 @@ export function addWidgetIdToBlock( block, widgetId ) {
...block,
attributes: {
...( block.attributes || {} ),
- __internalWidgetId: widgetId,
+ _widgetId: widgetId,
},
};
}