From fab47d0b2aa8e7d5057afd44135e0109fed59451 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 10 Sep 2025 16:16:28 +0800 Subject: [PATCH 01/60] Boilerplate --- .../components/content-only-controls/index.js | 107 ++++++++++++++++++ packages/block-library/src/heading/block.json | 6 +- .../block-library/src/paragraph/block.json | 6 +- 3 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 packages/block-editor/src/components/content-only-controls/index.js diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js new file mode 100644 index 00000000000000..068c4365218cfa --- /dev/null +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -0,0 +1,107 @@ +/** + * WordPress dependencies + */ +import { store as blocksStore } from '@wordpress/blocks'; +import { + __experimentalToolsPanel as ToolsPanel, + __experimentalToolsPanelItem as ToolsPanelItem, +} from '@wordpress/components'; +import { useDispatch, useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../../store'; +import useBlockDisplayTitle from '../block-title/use-block-display-title'; + +function RichTextControl() {} + +function getContentAttributesWithControls( blockType ) { + return Object.keys( blockType?.attributes ?? {} ) + .filter( ( attributeKey ) => { + const attribute = blockType.attributes[ attributeKey ]; + return attribute?.role === 'content' && !! attribute.control; + } ) + .map( ( attributeKey ) => ( { + key: attributeKey, + ...blockType?.attributes[ attributeKey ], + } ) ); +} + +function getControlForAttribute( attribute ) { + if ( attribute.control.type === 'RichText' ) { + return RichTextControl; + } +} + +function BlockAttributeControl( { attributeDefinition, setAttribute, value } ) { + const Control = getControlForAttribute( attributeDefinition ); + + if ( ! Control ) { + return null; + } + + return ( + + + + ); +} + +function BlockControls( { clientId } ) { + const { blockType, attributes } = useSelect( + ( select ) => { + const { getBlockName, getBlockAttributes } = + select( blockEditorStore ); + const { getBlockType } = select( blocksStore ); + + const blockName = getBlockName( clientId ); + + return { + blockType: getBlockType( blockName ), + attributes: getBlockAttributes( clientId ), + }; + }, + [ clientId ] + ); + const { updateBlockAttributes } = useDispatch( blockEditorStore ); + + const contentAttributesWithControls = + getContentAttributesWithControls( blockType ); + + const blockTitle = useBlockDisplayTitle( { + clientId, + context: 'list-view', + } ); + + if ( ! contentAttributesWithControls?.length ) { + return null; + } + + return ( + + { contentAttributesWithControls?.each( ( attribute ) => ( + + updateBlockAttributes( clientId, { + [ attribute.key ]: value, + } ) + } + value={ attributes[ attribute.key ] } + /> + ) ) } + + ); +} + +export default function ContentOnlyControls( { clientIds } ) { + if ( ! clientIds.length ) { + return null; + } + + return clientIds.map( ( clientId ) => ( + + ) ); +} diff --git a/packages/block-library/src/heading/block.json b/packages/block-library/src/heading/block.json index d9488a3156528d..f79d98c46cb66b 100644 --- a/packages/block-library/src/heading/block.json +++ b/packages/block-library/src/heading/block.json @@ -15,7 +15,11 @@ "type": "rich-text", "source": "rich-text", "selector": "h1,h2,h3,h4,h5,h6", - "role": "content" + "role": "content", + "control": { + "type": "RichText", + "label": "Content" + } }, "level": { "type": "number", diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 99ce2a3a27b1e7..fed8e4a2e3dcac 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -12,7 +12,11 @@ "type": "rich-text", "source": "rich-text", "selector": "p", - "role": "content" + "role": "content", + "control": { + "type": "RichText", + "label": "Content" + } }, "dropCap": { "type": "boolean", From b9e18ae0a5ce674e76daffd57facba783444adbe Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 10 Sep 2025 16:35:34 +0800 Subject: [PATCH 02/60] v1 --- .../components/content-only-controls/index.js | 93 +++++++++++++++---- packages/block-library/src/heading/block.json | 3 +- 2 files changed, 77 insertions(+), 19 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index 068c4365218cfa..a4aa6e294ffac4 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -5,8 +5,10 @@ import { store as blocksStore } from '@wordpress/blocks'; import { __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, + TextControl, } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; +import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; /** * Internal dependencies @@ -14,18 +16,17 @@ import { useDispatch, useSelect } from '@wordpress/data'; import { store as blockEditorStore } from '../../store'; import useBlockDisplayTitle from '../block-title/use-block-display-title'; -function RichTextControl() {} - -function getContentAttributesWithControls( blockType ) { - return Object.keys( blockType?.attributes ?? {} ) - .filter( ( attributeKey ) => { - const attribute = blockType.attributes[ attributeKey ]; - return attribute?.role === 'content' && !! attribute.control; - } ) - .map( ( attributeKey ) => ( { - key: attributeKey, - ...blockType?.attributes[ attributeKey ], - } ) ); +function RichTextControl( { label, value, setValue } ) { + return ( + + ); } function getControlForAttribute( attribute ) { @@ -34,20 +35,65 @@ function getControlForAttribute( attribute ) { } } -function BlockAttributeControl( { attributeDefinition, setAttribute, value } ) { +function getDefaultValue( attribute ) { + if ( attribute.default ) { + return attribute.default; + } + + return undefined; +} + +function BlockAttributeToolsPanelItem( { + attributeDefinition, + setValue, + value, +} ) { const Control = getControlForAttribute( attributeDefinition ); if ( ! Control ) { return null; } + const defaultValue = getDefaultValue( attributeDefinition ); + return ( - - + value !== defaultValue } + onDeselect={ () => setValue( defaultValue ) } + isShownByDefault={ attributeDefinition.control.shownByDefault } + > + ); } +function getContentAttributesWithControls( blockType ) { + return Object.keys( blockType?.attributes ?? {} ) + .filter( ( attributeKey ) => { + const attribute = blockType.attributes[ attributeKey ]; + return attribute?.role === 'content' && !! attribute.control; + } ) + .map( ( attributeKey ) => ( { + key: attributeKey, + ...blockType?.attributes[ attributeKey ], + } ) ); +} + +function getResetAllValue( attributes ) { + const resetValue = {}; + + attributes.each( ( attribute ) => { + resetValue[ attribute.key ] = getDefaultValue( attribute ); + } ); + + return resetValue; +} + function BlockControls( { clientId } ) { const { blockType, attributes } = useSelect( ( select ) => { @@ -75,16 +121,27 @@ function BlockControls( { clientId } ) { } ); if ( ! contentAttributesWithControls?.length ) { + // TODO - we might still want to show a placeholder for blocks with no controls. + // for example, a way to select the block. return null; } return ( - + { + updateBlockAttributes( + clientId, + getResetAllValue( contentAttributesWithControls ) + ); + } } + > { contentAttributesWithControls?.each( ( attribute ) => ( - + setValue={ ( value ) => updateBlockAttributes( clientId, { [ attribute.key ]: value, } ) diff --git a/packages/block-library/src/heading/block.json b/packages/block-library/src/heading/block.json index f79d98c46cb66b..9e7030886d2252 100644 --- a/packages/block-library/src/heading/block.json +++ b/packages/block-library/src/heading/block.json @@ -18,7 +18,8 @@ "role": "content", "control": { "type": "RichText", - "label": "Content" + "label": "Content", + "shownByDefault": true } }, "level": { From de34b38360d9f7ac7378ab58abe597de20488e8a Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 10 Sep 2025 16:44:46 +0800 Subject: [PATCH 03/60] Fixes --- .../src/components/block-inspector/index.js | 1 - .../src/components/content-only-controls/index.js | 5 +++-- .../inspector-controls-tabs/content-tab.js | 12 +++++++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index a4c491d9cd3218..07d6dc9a78f3f6 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -29,7 +29,6 @@ import PositionControls from '../inspector-controls-tabs/position-controls-panel import useBlockInspectorAnimationSettings from './useBlockInspectorAnimationSettings'; import { useBorderPanelLabel } from '../../hooks/border'; import ContentTab from '../inspector-controls-tabs/content-tab'; - import { unlock } from '../../lock-unlock'; function BlockStylesPanel( { clientId } ) { diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index a4aa6e294ffac4..906fe5b3811ebd 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -87,7 +87,7 @@ function getContentAttributesWithControls( blockType ) { function getResetAllValue( attributes ) { const resetValue = {}; - attributes.each( ( attribute ) => { + attributes.forEach( ( attribute ) => { resetValue[ attribute.key ] = getDefaultValue( attribute ); } ); @@ -137,8 +137,9 @@ function BlockControls( { clientId } ) { ); } } > - { contentAttributesWithControls?.each( ( attribute ) => ( + { contentAttributesWithControls?.map( ( attribute ) => ( diff --git a/packages/block-editor/src/components/inspector-controls-tabs/content-tab.js b/packages/block-editor/src/components/inspector-controls-tabs/content-tab.js index 5d03e5637be8c1..8a03ddcdc0600f 100644 --- a/packages/block-editor/src/components/inspector-controls-tabs/content-tab.js +++ b/packages/block-editor/src/components/inspector-controls-tabs/content-tab.js @@ -8,6 +8,7 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import BlockQuickNavigation from '../block-quick-navigation'; +import ContentOnlyControls from '../content-only-controls'; const ContentTab = ( { contentClientIds } ) => { if ( ! contentClientIds || contentClientIds.length === 0 ) { @@ -16,7 +17,16 @@ const ContentTab = ( { contentClientIds } ) => { return ( - + <> + { ! window?.__experimentalContentOnlyPatternInsertion && ( + + + + ) } + { window?.__experimentalContentOnlyPatternInsertion && ( + + ) } + ); }; From ad416621410adab3a8b1b2ce3c1d2a79ce4934f9 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 10 Sep 2025 16:56:59 +0800 Subject: [PATCH 04/60] Small improvements: - Pass paneId to tools panel items so that reset works. - set shownByDefault consistently. --- .../block-editor/src/components/content-only-controls/index.js | 2 ++ packages/block-library/src/paragraph/block.json | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index 906fe5b3811ebd..7cc93a1b81b691 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -44,6 +44,7 @@ function getDefaultValue( attribute ) { } function BlockAttributeToolsPanelItem( { + clientId, attributeDefinition, setValue, value, @@ -58,6 +59,7 @@ function BlockAttributeToolsPanelItem( { return ( value !== defaultValue } onDeselect={ () => setValue( defaultValue ) } diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index fed8e4a2e3dcac..827d2e616de58f 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -15,7 +15,8 @@ "role": "content", "control": { "type": "RichText", - "label": "Content" + "label": "Content", + "shownByDefault": true } }, "dropCap": { From 615f532822ca0496bd71766969cc2cbf585d1fdc Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 10 Sep 2025 17:49:28 +0800 Subject: [PATCH 05/60] Add block icon --- .../src/components/content-only-controls/index.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index 7cc93a1b81b691..e693e1cb363194 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -5,6 +5,7 @@ import { store as blocksStore } from '@wordpress/blocks'; import { __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, + __experimentalHStack as HStack, TextControl, } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; @@ -14,7 +15,9 @@ import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; * Internal dependencies */ import { store as blockEditorStore } from '../../store'; +import BlockIcon from '../block-icon'; import useBlockDisplayTitle from '../block-title/use-block-display-title'; +import useBlockDisplayInformation from '../use-block-display-information'; function RichTextControl( { label, value, setValue } ) { return ( @@ -121,6 +124,7 @@ function BlockControls( { clientId } ) { clientId, context: 'list-view', } ); + const blockInformation = useBlockDisplayInformation( clientId ); if ( ! contentAttributesWithControls?.length ) { // TODO - we might still want to show a placeholder for blocks with no controls. @@ -130,7 +134,12 @@ function BlockControls( { clientId } ) { return ( + +
{ blockTitle }
+ + } panelId={ clientId } resetAll={ () => { updateBlockAttributes( From 5cd8ebc7e9d4270718c6b78ce57b2ad10c1b888b Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 16 Sep 2025 18:13:05 +0800 Subject: [PATCH 06/60] Refactor to a `controls` array in the block index --- .../components/content-only-controls/index.js | 153 +++++++----------- .../block-library/src/paragraph/block.json | 7 +- packages/block-library/src/paragraph/index.js | 13 ++ 3 files changed, 74 insertions(+), 99 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index e693e1cb363194..84af0b58ff488e 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -19,106 +19,83 @@ import BlockIcon from '../block-icon'; import useBlockDisplayTitle from '../block-title/use-block-display-title'; import useBlockDisplayInformation from '../use-block-display-information'; -function RichTextControl( { label, value, setValue } ) { - return ( - - ); -} - -function getControlForAttribute( attribute ) { - if ( attribute.control.type === 'RichText' ) { - return RichTextControl; - } -} - -function getDefaultValue( attribute ) { - if ( attribute.default ) { - return attribute.default; - } - - return undefined; -} +const controls = { + RichText( { + clientId, + control, + blockType, + attributeValues, + updateAttributes, + } ) { + const valueKey = control.mapping.value; + const value = attributeValues[ valueKey ]; + const defaultValue = blockType.attributes[ valueKey ]?.defaultValue; + + return ( + value !== defaultValue } + onDeselect={ () => { + updateAttributes( { [ valueKey ]: defaultValue } ); + } } + isShownByDefault={ control.shownByDefault } + > + { + updateAttributes( { [ valueKey ]: newValue } ); + } } + autoComplete="off" + /> + + ); + }, +}; function BlockAttributeToolsPanelItem( { clientId, - attributeDefinition, - setValue, - value, + control, + blockType, + attributeValues, } ) { - const Control = getControlForAttribute( attributeDefinition ); + const { updateBlockAttributes } = useDispatch( blockEditorStore ); + const ControlComponent = controls[ control.type ]; - if ( ! Control ) { + if ( ! ControlComponent ) { return null; } - const defaultValue = getDefaultValue( attributeDefinition ); - return ( - value !== defaultValue } - onDeselect={ () => setValue( defaultValue ) } - isShownByDefault={ attributeDefinition.control.shownByDefault } - > - - + + updateBlockAttributes( clientId, attributes ) + } + /> ); } -function getContentAttributesWithControls( blockType ) { - return Object.keys( blockType?.attributes ?? {} ) - .filter( ( attributeKey ) => { - const attribute = blockType.attributes[ attributeKey ]; - return attribute?.role === 'content' && !! attribute.control; - } ) - .map( ( attributeKey ) => ( { - key: attributeKey, - ...blockType?.attributes[ attributeKey ], - } ) ); -} - -function getResetAllValue( attributes ) { - const resetValue = {}; - - attributes.forEach( ( attribute ) => { - resetValue[ attribute.key ] = getDefaultValue( attribute ); - } ); - - return resetValue; -} - function BlockControls( { clientId } ) { - const { blockType, attributes } = useSelect( + const { attributes, blockType } = useSelect( ( select ) => { - const { getBlockName, getBlockAttributes } = + const { getBlockAttributes, getBlockName } = select( blockEditorStore ); const { getBlockType } = select( blocksStore ); - const blockName = getBlockName( clientId ); - return { - blockType: getBlockType( blockName ), attributes: getBlockAttributes( clientId ), + blockType: getBlockType( blockName ), }; }, [ clientId ] ); - const { updateBlockAttributes } = useDispatch( blockEditorStore ); - - const contentAttributesWithControls = - getContentAttributesWithControls( blockType ); const blockTitle = useBlockDisplayTitle( { clientId, @@ -126,7 +103,7 @@ function BlockControls( { clientId } ) { } ); const blockInformation = useBlockDisplayInformation( clientId ); - if ( ! contentAttributesWithControls?.length ) { + if ( ! blockType?.controls?.length ) { // TODO - we might still want to show a placeholder for blocks with no controls. // for example, a way to select the block. return null; @@ -141,24 +118,14 @@ function BlockControls( { clientId } ) { } panelId={ clientId } - resetAll={ () => { - updateBlockAttributes( - clientId, - getResetAllValue( contentAttributesWithControls ) - ); - } } > - { contentAttributesWithControls?.map( ( attribute ) => ( + { blockType?.controls?.map( ( control, index ) => ( - updateBlockAttributes( clientId, { - [ attribute.key ]: value, - } ) - } - value={ attributes[ attribute.key ] } + control={ control } + blockType={ blockType } + attributeValues={ attributes } /> ) ) }
diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 827d2e616de58f..99ce2a3a27b1e7 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -12,12 +12,7 @@ "type": "rich-text", "source": "rich-text", "selector": "p", - "role": "content", - "control": { - "type": "RichText", - "label": "Content", - "shownByDefault": true - } + "role": "content" }, "dropCap": { "type": "boolean", diff --git a/packages/block-library/src/paragraph/index.js b/packages/block-library/src/paragraph/index.js index a2328b1ecdf82d..0429817696ddcd 100644 --- a/packages/block-library/src/paragraph/index.js +++ b/packages/block-library/src/paragraph/index.js @@ -58,4 +58,17 @@ export const settings = { variations, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Content' ), + type: 'RichText', + shownByDefault: true, + mapping: { + value: 'content', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From bea3948bd3ee21c465e0351e131cd8a90a0a5ae9 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 16 Sep 2025 18:29:48 +0800 Subject: [PATCH 07/60] Stub out media and link controls --- .../components/content-only-controls/index.js | 42 +++++++++++++++++++ packages/block-library/src/heading/index.js | 13 ++++++ packages/block-library/src/image/index.js | 27 ++++++++++++ 3 files changed, 82 insertions(+) diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index 84af0b58ff488e..42e4e531e0f8cf 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -54,6 +54,48 @@ const controls = {
); }, + Media( { + clientId, + control, + blockType, + attributeValues, + updateAttributes, + } ) { + return ( + false } // TODO. + onDeselect={ () => { + // TODO. + } } + isShownByDefault={ control.shownByDefault } + > + Media + + ); + }, + Link( { + clientId, + control, + blockType, + attributeValues, + updateAttributes, + } ) { + return ( + true } // TODO. + onDeselect={ () => { + // TODO. + } } + isShownByDefault={ control.shownByDefault } + > + Link + + ); + }, }; function BlockAttributeToolsPanelItem( { diff --git a/packages/block-library/src/heading/index.js b/packages/block-library/src/heading/index.js index 2ab252c6c9c18f..1cdae54f763d7d 100644 --- a/packages/block-library/src/heading/index.js +++ b/packages/block-library/src/heading/index.js @@ -69,4 +69,17 @@ export const settings = { variations, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Content' ), + type: 'RichText', + shownByDefault: true, + mapping: { + value: 'content', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/block-library/src/image/index.js b/packages/block-library/src/image/index.js index 68625bd77cc873..83c1bd076e740b 100644 --- a/packages/block-library/src/image/index.js +++ b/packages/block-library/src/image/index.js @@ -62,4 +62,31 @@ export const settings = { deprecated, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Image' ), + type: 'Media', + shownByDefault: true, + mapping: { + id: 'id', + src: 'url', + caption: 'caption', + alt: 'alt', + }, + }, + { + label: __( 'Link' ), + type: 'Link', + shownByDefault: false, + mapping: { + href: 'href', + rel: 'rel', + target: 'linkTarget', + destination: 'linkDestination', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From 58592e099d4bc56eb4f2103510abf3cda8530c16 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 10:30:07 +0800 Subject: [PATCH 08/60] Add first draft of media control --- .../components/content-only-controls/index.js | 170 ++++++++++++++++-- packages/block-library/src/image/index.js | 4 + 2 files changed, 161 insertions(+), 13 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index 42e4e531e0f8cf..c4f3ae7fedb9a1 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -3,13 +3,17 @@ */ import { store as blocksStore } from '@wordpress/blocks'; import { + Button, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalHStack as HStack, + __experimentalGrid as Grid, TextControl, } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; +import { __ } from '@wordpress/i18n'; +import { lineSolid, update } from '@wordpress/icons'; /** * Internal dependencies @@ -18,6 +22,8 @@ import { store as blockEditorStore } from '../../store'; import BlockIcon from '../block-icon'; import useBlockDisplayTitle from '../block-title/use-block-display-title'; import useBlockDisplayInformation from '../use-block-display-information'; +import MediaUpload from '../media-upload'; +import MediaUploadCheck from '../media-upload/check'; const controls = { RichText( { @@ -29,7 +35,8 @@ const controls = { } ) { const valueKey = control.mapping.value; const value = attributeValues[ valueKey ]; - const defaultValue = blockType.attributes[ valueKey ]?.defaultValue; + const defaultValue = + blockType.attributes[ valueKey ]?.defaultValue ?? undefined; return ( false } // TODO. - onDeselect={ () => { - // TODO. - } } - isShownByDefault={ control.shownByDefault } - > - Media - + + !! src } + onDeselect={ () => { + updateAttributes( { + [ idKey ]: idDefaultValue, + [ srcKey ]: srcDefaultValue, + [ captionKey ]: captionDefaultValue, + [ altKey ]: altDefaultValue, + } ); + } } + isShownByDefault={ control.shownByDefault } + > + { + if ( selectedMedia.id && selectedMedia.url ) { + const optionalAttributes = {}; + + if ( ! caption && selectedMedia.caption ) { + optionalAttributes[ captionKey ] = + selectedMedia.caption; + } + if ( ! alt && selectedMedia.alt ) { + optionalAttributes[ altKey ] = + selectedMedia.alt; + } + + updateAttributes( { + [ idKey ]: selectedMedia.id, + [ srcKey ]: selectedMedia.url, + ...optionalAttributes, + } ); + } + } } + allowedTypes={ control.args.allowedTypes } + multiple={ control.args.multiple } + render={ ( { open } ) => { + return ( +
{ + open(); + } } + onKeyDown={ open } + > + + { src && ( + <> + + + { + // TODO - use media title instead of src. + src + } + + + ) } + { ! src && ( + <> + + + { chooseItemLabel } + + + ) } + { src && ( + <> +
+ ); + } } + /> +
+
); }, Link( { @@ -86,7 +230,7 @@ const controls = { true } // TODO. + hasValue={ () => false } // TODO. onDeselect={ () => { // TODO. } } diff --git a/packages/block-library/src/image/index.js b/packages/block-library/src/image/index.js index 83c1bd076e740b..7725d6b76c5376 100644 --- a/packages/block-library/src/image/index.js +++ b/packages/block-library/src/image/index.js @@ -74,6 +74,10 @@ if ( window.__experimentalContentOnlyPatternInsertion ) { caption: 'caption', alt: 'alt', }, + args: { + allowedTypes: [ 'image' ], + multiple: false, + }, }, { label: __( 'Link' ), From 749e65225663d62b012a554a7c5dfdfc9399b1d1 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 10:36:40 +0800 Subject: [PATCH 09/60] Move controls to separate files --- .../components/content-only-controls/index.js | 231 +----------------- .../content-only-controls/link/index.js | 26 ++ .../content-only-controls/media/index.js | 175 +++++++++++++ .../content-only-controls/rich-text/index.js | 44 ++++ 4 files changed, 253 insertions(+), 223 deletions(-) create mode 100644 packages/block-editor/src/components/content-only-controls/link/index.js create mode 100644 packages/block-editor/src/components/content-only-controls/media/index.js create mode 100644 packages/block-editor/src/components/content-only-controls/rich-text/index.js diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index c4f3ae7fedb9a1..7747d28a652d0d 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -3,17 +3,10 @@ */ import { store as blocksStore } from '@wordpress/blocks'; import { - Button, __experimentalToolsPanel as ToolsPanel, - __experimentalToolsPanelItem as ToolsPanelItem, __experimentalHStack as HStack, - __experimentalGrid as Grid, - TextControl, } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; -import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; -import { __ } from '@wordpress/i18n'; -import { lineSolid, update } from '@wordpress/icons'; /** * Internal dependencies @@ -22,224 +15,16 @@ import { store as blockEditorStore } from '../../store'; import BlockIcon from '../block-icon'; import useBlockDisplayTitle from '../block-title/use-block-display-title'; import useBlockDisplayInformation from '../use-block-display-information'; -import MediaUpload from '../media-upload'; -import MediaUploadCheck from '../media-upload/check'; -const controls = { - RichText( { - clientId, - control, - blockType, - attributeValues, - updateAttributes, - } ) { - const valueKey = control.mapping.value; - const value = attributeValues[ valueKey ]; - const defaultValue = - blockType.attributes[ valueKey ]?.defaultValue ?? undefined; - - return ( - value !== defaultValue } - onDeselect={ () => { - updateAttributes( { [ valueKey ]: defaultValue } ); - } } - isShownByDefault={ control.shownByDefault } - > - { - updateAttributes( { [ valueKey ]: newValue } ); - } } - autoComplete="off" - /> - - ); - }, - Media( { - clientId, - control, - blockType, - attributeValues, - updateAttributes, - } ) { - const idKey = control.mapping.id; - const srcKey = control.mapping.src; - const captionKey = control.mapping.caption; - const altKey = control.mapping.alt; - - const src = attributeValues[ srcKey ]; - const caption = attributeValues[ captionKey ]; - const alt = attributeValues[ altKey ]; - - const idDefaultValue = - blockType.attributes[ idKey ]?.defaultValue ?? undefined; - const srcDefaultValue = - blockType.attributes[ srcKey ]?.defaultValue ?? undefined; - const captionDefaultValue = - blockType.attributes[ captionKey ]?.defaultValue ?? undefined; - const altDefaultValue = - blockType.attributes[ altKey ]?.defaultValue ?? undefined; +// controls +import RichText from './rich-text'; +import Media from './media'; +import Link from './link'; - // TODO - pluralize. - let chooseItemLabel; - if ( control.args.allowedTypes.length === 1 ) { - const allowedType = control.args.allowedTypes[ 0 ]; - if ( allowedType === 'image' ) { - chooseItemLabel = __( 'Choose image' ); - } else if ( allowedType === 'video' ) { - chooseItemLabel = __( 'Choose video' ); - } else if ( allowedType === 'application' ) { - chooseItemLabel = __( 'Choose file' ); - } else { - chooseItemLabel = __( 'Choose media item' ); - } - } else { - chooseItemLabel = __( 'Choose media item' ); - } - - return ( - - !! src } - onDeselect={ () => { - updateAttributes( { - [ idKey ]: idDefaultValue, - [ srcKey ]: srcDefaultValue, - [ captionKey ]: captionDefaultValue, - [ altKey ]: altDefaultValue, - } ); - } } - isShownByDefault={ control.shownByDefault } - > - { - if ( selectedMedia.id && selectedMedia.url ) { - const optionalAttributes = {}; - - if ( ! caption && selectedMedia.caption ) { - optionalAttributes[ captionKey ] = - selectedMedia.caption; - } - if ( ! alt && selectedMedia.alt ) { - optionalAttributes[ altKey ] = - selectedMedia.alt; - } - - updateAttributes( { - [ idKey ]: selectedMedia.id, - [ srcKey ]: selectedMedia.url, - ...optionalAttributes, - } ); - } - } } - allowedTypes={ control.args.allowedTypes } - multiple={ control.args.multiple } - render={ ( { open } ) => { - return ( -
{ - open(); - } } - onKeyDown={ open } - > - - { src && ( - <> - - - { - // TODO - use media title instead of src. - src - } - - - ) } - { ! src && ( - <> - - - { chooseItemLabel } - - - ) } - { src && ( - <> -
- ); - } } - /> -
-
- ); - }, - Link( { - clientId, - control, - blockType, - attributeValues, - updateAttributes, - } ) { - return ( - false } // TODO. - onDeselect={ () => { - // TODO. - } } - isShownByDefault={ control.shownByDefault } - > - Link - - ); - }, +const controls = { + RichText, + Media, + Link, }; function BlockAttributeToolsPanelItem( { diff --git a/packages/block-editor/src/components/content-only-controls/link/index.js b/packages/block-editor/src/components/content-only-controls/link/index.js new file mode 100644 index 00000000000000..53cc994979127a --- /dev/null +++ b/packages/block-editor/src/components/content-only-controls/link/index.js @@ -0,0 +1,26 @@ +/** + * WordPress dependencies + */ +import { __experimentalToolsPanelItem as ToolsPanelItem } from '@wordpress/components'; + +export default function Link( { + clientId, + control, + blockType, + attributeValues, + updateAttributes, +} ) { + return ( + false } // TODO. + onDeselect={ () => { + // TODO. + } } + isShownByDefault={ control.shownByDefault } + > + Link + + ); +} diff --git a/packages/block-editor/src/components/content-only-controls/media/index.js b/packages/block-editor/src/components/content-only-controls/media/index.js new file mode 100644 index 00000000000000..86ce5e4d4920c9 --- /dev/null +++ b/packages/block-editor/src/components/content-only-controls/media/index.js @@ -0,0 +1,175 @@ +/** + * WordPress dependencies + */ +import { + Button, + __experimentalToolsPanelItem as ToolsPanelItem, + __experimentalGrid as Grid, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { lineSolid } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import MediaUpload from '../../media-upload'; +import MediaUploadCheck from '../../media-upload/check'; + +export default function Media( { + clientId, + control, + blockType, + attributeValues, + updateAttributes, +} ) { + const idKey = control.mapping.id; + const srcKey = control.mapping.src; + const captionKey = control.mapping.caption; + const altKey = control.mapping.alt; + + const src = attributeValues[ srcKey ]; + const caption = attributeValues[ captionKey ]; + const alt = attributeValues[ altKey ]; + + const idDefaultValue = + blockType.attributes[ idKey ]?.defaultValue ?? undefined; + const srcDefaultValue = + blockType.attributes[ srcKey ]?.defaultValue ?? undefined; + const captionDefaultValue = + blockType.attributes[ captionKey ]?.defaultValue ?? undefined; + const altDefaultValue = + blockType.attributes[ altKey ]?.defaultValue ?? undefined; + + // TODO - pluralize. + let chooseItemLabel; + if ( control.args.allowedTypes.length === 1 ) { + const allowedType = control.args.allowedTypes[ 0 ]; + if ( allowedType === 'image' ) { + chooseItemLabel = __( 'Choose image' ); + } else if ( allowedType === 'video' ) { + chooseItemLabel = __( 'Choose video' ); + } else if ( allowedType === 'application' ) { + chooseItemLabel = __( 'Choose file' ); + } else { + chooseItemLabel = __( 'Choose media item' ); + } + } else { + chooseItemLabel = __( 'Choose media item' ); + } + + return ( + + !! src } + onDeselect={ () => { + updateAttributes( { + [ idKey ]: idDefaultValue, + [ srcKey ]: srcDefaultValue, + [ captionKey ]: captionDefaultValue, + [ altKey ]: altDefaultValue, + } ); + } } + isShownByDefault={ control.shownByDefault } + > + { + if ( selectedMedia.id && selectedMedia.url ) { + const optionalAttributes = {}; + + if ( ! caption && selectedMedia.caption ) { + optionalAttributes[ captionKey ] = + selectedMedia.caption; + } + if ( ! alt && selectedMedia.alt ) { + optionalAttributes[ altKey ] = + selectedMedia.alt; + } + + updateAttributes( { + [ idKey ]: selectedMedia.id, + [ srcKey ]: selectedMedia.url, + ...optionalAttributes, + } ); + } + } } + allowedTypes={ control.args.allowedTypes } + multiple={ control.args.multiple } + render={ ( { open } ) => { + return ( +
{ + open(); + } } + onKeyDown={ open } + > + + { src && ( + <> + + + { + // TODO - use media title instead of src. + src + } + + + ) } + { ! src && ( + <> + + + { chooseItemLabel } + + + ) } + { src && ( + <> +
+ ); + } } + /> +
+
+ ); +} diff --git a/packages/block-editor/src/components/content-only-controls/rich-text/index.js b/packages/block-editor/src/components/content-only-controls/rich-text/index.js new file mode 100644 index 00000000000000..2ac251c9ac3952 --- /dev/null +++ b/packages/block-editor/src/components/content-only-controls/rich-text/index.js @@ -0,0 +1,44 @@ +/** + * WordPress dependencies + */ +import { + __experimentalToolsPanelItem as ToolsPanelItem, + TextControl, +} from '@wordpress/components'; +import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; + +export default function RichText( { + clientId, + control, + blockType, + attributeValues, + updateAttributes, +} ) { + const valueKey = control.mapping.value; + const value = attributeValues[ valueKey ]; + const defaultValue = + blockType.attributes[ valueKey ]?.defaultValue ?? undefined; + + return ( + value !== defaultValue } + onDeselect={ () => { + updateAttributes( { [ valueKey ]: defaultValue } ); + } } + isShownByDefault={ control.shownByDefault } + > + { + updateAttributes( { [ valueKey ]: newValue } ); + } } + autoComplete="off" + /> + + ); +} From be3560c0bbbd02642287add7d7f6dc2423f0e4f7 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 11:00:39 +0800 Subject: [PATCH 10/60] Add styles for media control --- .../content-only-controls/media/index.js | 148 +++++++++--------- .../content-only-controls/media/styles.scss | 41 +++++ .../content-only-controls/styles.scss | 1 + packages/block-editor/src/style.scss | 1 + 4 files changed, 119 insertions(+), 72 deletions(-) create mode 100644 packages/block-editor/src/components/content-only-controls/media/styles.scss create mode 100644 packages/block-editor/src/components/content-only-controls/styles.scss diff --git a/packages/block-editor/src/components/content-only-controls/media/index.js b/packages/block-editor/src/components/content-only-controls/media/index.js index 86ce5e4d4920c9..291d3225bf6fda 100644 --- a/packages/block-editor/src/components/content-only-controls/media/index.js +++ b/packages/block-editor/src/components/content-only-controls/media/index.js @@ -40,21 +40,21 @@ export default function Media( { const altDefaultValue = blockType.attributes[ altKey ]?.defaultValue ?? undefined; - // TODO - pluralize. + // TODO - pluralize when multiple. let chooseItemLabel; if ( control.args.allowedTypes.length === 1 ) { const allowedType = control.args.allowedTypes[ 0 ]; if ( allowedType === 'image' ) { - chooseItemLabel = __( 'Choose image' ); + chooseItemLabel = __( 'Choose an image…' ); } else if ( allowedType === 'video' ) { - chooseItemLabel = __( 'Choose video' ); + chooseItemLabel = __( 'Choose a video…' ); } else if ( allowedType === 'application' ) { - chooseItemLabel = __( 'Choose file' ); + chooseItemLabel = __( 'Choose a file…' ); } else { - chooseItemLabel = __( 'Choose media item' ); + chooseItemLabel = __( 'Choose a media item…' ); } } else { - chooseItemLabel = __( 'Choose media item' ); + chooseItemLabel = __( 'Choose a media item…' ); } return ( @@ -98,73 +98,77 @@ export default function Media( { multiple={ control.args.multiple } render={ ( { open } ) => { return ( -
{ - open(); - } } - onKeyDown={ open } - > - +
{ + open(); + } } + onKeyDown={ open } > - { src && ( - <> - - - { - // TODO - use media title instead of src. - src - } - - - ) } - { ! src && ( - <> - - - { chooseItemLabel } - - - ) } - { src && ( - <> -
); } } diff --git a/packages/block-editor/src/components/content-only-controls/media/styles.scss b/packages/block-editor/src/components/content-only-controls/media/styles.scss new file mode 100644 index 00000000000000..2e99f680d30dde --- /dev/null +++ b/packages/block-editor/src/components/content-only-controls/media/styles.scss @@ -0,0 +1,41 @@ +.block-editor-content-only-controls__media { + border: $border-width solid $gray-300; + border-radius: $radius-small; + // Add important to prevent toolspanel from unsetting padding. + padding: 8px 12px !important; + cursor: pointer; + &:hover { + background-color: $gray-100; + } +} + +.block-editor-content-only-controls__media-row { + align-items: center; +} + +.block-editor-content-only-controls__media-placeholder { + width: 24px; + height: 24px; + border-radius: $radius-small; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2); + display: inline-block; + padding: 0; + background: + $white + linear-gradient(-45deg, transparent 48%, $gray-300 48%, $gray-300 52%, transparent 52%); +} + +.block-editor-content-only-controls__media-title { + width: 100%; + color: $gray-900; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +block-editor-content-only-controls__media-thumbnail { + width: 100%; + height: 100%; + border-radius: $radius-small; + align-self: center; +} diff --git a/packages/block-editor/src/components/content-only-controls/styles.scss b/packages/block-editor/src/components/content-only-controls/styles.scss new file mode 100644 index 00000000000000..2522fe65f0459f --- /dev/null +++ b/packages/block-editor/src/components/content-only-controls/styles.scss @@ -0,0 +1 @@ +@use "./media/styles.scss" as *; diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index aa303c90b0aa3c..e7a5f2c341e4c0 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -29,6 +29,7 @@ @use "./components/block-variation-transforms/style.scss" as *; @use "./components/border-radius-control/style.scss" as *; @use "./components/colors-gradients/style.scss" as *; +@use "./components/content-only-controls/styles.scss" as *; @use "./components/date-format-picker/style.scss" as *; @use "./components/duotone-control/style.scss" as *; @use "./components/font-appearance-control/style.scss" as *; From 590d0cf357b0baf49fb481bfb9fbe9e2631e2c11 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 11:14:09 +0800 Subject: [PATCH 11/60] Add caption and alt to image --- .../content-only-controls/rich-text/index.js | 7 ++++++- packages/block-library/src/image/index.js | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/content-only-controls/rich-text/index.js b/packages/block-editor/src/components/content-only-controls/rich-text/index.js index 2ac251c9ac3952..c7e410e4416479 100644 --- a/packages/block-editor/src/components/content-only-controls/rich-text/index.js +++ b/packages/block-editor/src/components/content-only-controls/rich-text/index.js @@ -23,7 +23,11 @@ export default function RichText( { value !== defaultValue } + hasValue={ () => { + return ( + value !== defaultValue && stripHTML( value )?.length !== 0 + ); + } } onDeselect={ () => { updateAttributes( { [ valueKey ]: defaultValue } ); } } @@ -38,6 +42,7 @@ export default function RichText( { updateAttributes( { [ valueKey ]: newValue } ); } } autoComplete="off" + hideLabelFromVision={ control.shownByDefault } /> ); diff --git a/packages/block-library/src/image/index.js b/packages/block-library/src/image/index.js index 7725d6b76c5376..5e2f9a2b0012d8 100644 --- a/packages/block-library/src/image/index.js +++ b/packages/block-library/src/image/index.js @@ -90,6 +90,22 @@ if ( window.__experimentalContentOnlyPatternInsertion ) { destination: 'linkDestination', }, }, + { + label: __( 'Caption' ), + type: 'RichText', + shownByDefault: false, + mapping: { + value: 'caption', + }, + }, + { + label: __( 'Alt text' ), + type: 'RichText', + shownByDefault: false, + mapping: { + value: 'alt', + }, + }, ]; } From e9dea9ed95f6c298d18c57b960aa4cc8b7d94f5e Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 11:20:30 +0800 Subject: [PATCH 12/60] Make toolspanel dropdown open to the side --- .../block-editor/src/components/content-only-controls/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index 7747d28a652d0d..bb1f30972e5252 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -15,6 +15,7 @@ import { store as blockEditorStore } from '../../store'; import BlockIcon from '../block-icon'; import useBlockDisplayTitle from '../block-title/use-block-display-title'; import useBlockDisplayInformation from '../use-block-display-information'; +import { useToolsPanelDropdownMenuProps } from '../global-styles/utils'; // controls import RichText from './rich-text'; @@ -73,6 +74,7 @@ function BlockControls( { clientId } ) { context: 'list-view', } ); const blockInformation = useBlockDisplayInformation( clientId ); + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); if ( ! blockType?.controls?.length ) { // TODO - we might still want to show a placeholder for blocks with no controls. @@ -89,6 +91,7 @@ function BlockControls( { clientId } ) { } panelId={ clientId } + dropdownMenuProps={ dropdownMenuProps } > { blockType?.controls?.map( ( control, index ) => ( Date: Wed, 17 Sep 2025 12:06:46 +0800 Subject: [PATCH 13/60] Add link control initial implementation --- .../components/content-only-controls/index.js | 6 +- .../content-only-controls/link/index.js | 199 +++++++++++++++++- .../use-inspector-popover-placement.js | 19 ++ 3 files changed, 217 insertions(+), 7 deletions(-) create mode 100644 packages/block-editor/src/components/content-only-controls/use-inspector-popover-placement.js diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index bb1f30972e5252..46b24150967521 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -15,7 +15,7 @@ import { store as blockEditorStore } from '../../store'; import BlockIcon from '../block-icon'; import useBlockDisplayTitle from '../block-title/use-block-display-title'; import useBlockDisplayInformation from '../use-block-display-information'; -import { useToolsPanelDropdownMenuProps } from '../global-styles/utils'; +import { useInspectorPopoverPlacement } from './use-inspector-popover-placement'; // controls import RichText from './rich-text'; @@ -74,7 +74,7 @@ function BlockControls( { clientId } ) { context: 'list-view', } ); const blockInformation = useBlockDisplayInformation( clientId ); - const dropdownMenuProps = useToolsPanelDropdownMenuProps(); + const popoverPlacementProps = useInspectorPopoverPlacement(); if ( ! blockType?.controls?.length ) { // TODO - we might still want to show a placeholder for blocks with no controls. @@ -91,7 +91,7 @@ function BlockControls( { clientId } ) { } panelId={ clientId } - dropdownMenuProps={ dropdownMenuProps } + dropdownMenuProps={ popoverPlacementProps } > { blockType?.controls?.map( ( control, index ) => ( ( { url: href, opensInNewTab, nofollow } ), + [ href, opensInNewTab, nofollow ] + ); + return ( false } // TODO. + hasValue={ () => !! href } onDeselect={ () => { - // TODO. + updateAttributes( { + [ hrefKey ]: hrefDefaultValue, + [ relKey ]: relDefaultValue, + [ targetKey ]: targetDefaultValue, + [ destinationKey ]: destinationDefaultValue, + } ); } } isShownByDefault={ control.shownByDefault } > - Link +
+
{ + setIsLinkControlOpen( true ); + } } + onKeyDown={ () => setIsLinkControlOpen( true ) } + > + + { href && ( + <> + + + { href } + + + ) } + { ! href && ( + <> + + + { __( 'Link' ) } + + + ) } + { href && ( + <> +
+
+ { isLinkControlOpen && ( + { + setIsLinkControlOpen( false ); + } } + { ...( popoverProps ?? {} ) } + > + { + const updatedAttrs = getUpdatedLinkAttributes( { + rel, + ...newValues, + } ); + + updateAttributes( { + [ hrefKey ]: updatedAttrs.url, + [ relKey ]: updatedAttrs.rel, + [ targetKey ]: updatedAttrs.linkTarget, + } ); + } } + onRemove={ () => { + updateAttributes( { + [ hrefKey ]: hrefDefaultValue, + [ relKey ]: relDefaultValue, + [ targetKey ]: targetDefaultValue, + [ destinationKey ]: destinationDefaultValue, + } ); + } } + /> + + ) }
); } diff --git a/packages/block-editor/src/components/content-only-controls/use-inspector-popover-placement.js b/packages/block-editor/src/components/content-only-controls/use-inspector-popover-placement.js new file mode 100644 index 00000000000000..4aac7451c7a2fb --- /dev/null +++ b/packages/block-editor/src/components/content-only-controls/use-inspector-popover-placement.js @@ -0,0 +1,19 @@ +/** + * WordPress dependencies + */ +import { useViewportMatch } from '@wordpress/compose'; + +export function useInspectorPopoverPlacement( + { isControl } = { isControl: false } +) { + const isMobile = useViewportMatch( 'medium', '<' ); + return ! isMobile + ? { + popoverProps: { + placement: 'left-start', + // For non-mobile, inner sidebar width (248px) - button width (24px) - border (1px) + padding (16px) + spacing (20px) + offset: isControl ? 35 : 259, + }, + } + : {}; +} From da5a41f2ed02dc783f254d5121d585b0ef15d91e Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 12:23:14 +0800 Subject: [PATCH 14/60] Add image controls to cover --- packages/block-library/src/cover/index.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/block-library/src/cover/index.js b/packages/block-library/src/cover/index.js index 6dcac170b063b9..bffbf861686eac 100644 --- a/packages/block-library/src/cover/index.js +++ b/packages/block-library/src/cover/index.js @@ -52,4 +52,25 @@ export const settings = { variations, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Background' ), + type: 'Media', + shownByDefault: true, + mapping: { + id: 'id', + src: 'url', + alt: 'alt', + }, + args: { + // TODO - work out how to support video. Also background gradient. + // Does this need to be a custom control? + allowedTypes: [ 'image' ], + multiple: false, + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From d094b8e29c399509da4ba1754ca139c80b625898 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 12:52:53 +0800 Subject: [PATCH 15/60] Add social link, button support --- packages/block-library/src/button/index.js | 23 +++++++++++++++++++ .../block-library/src/social-link/index.js | 23 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/packages/block-library/src/button/index.js b/packages/block-library/src/button/index.js index 32212afc2eb706..6cae3683f3d4c9 100644 --- a/packages/block-library/src/button/index.js +++ b/packages/block-library/src/button/index.js @@ -34,4 +34,27 @@ export const settings = { } ), }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Content' ), + type: 'RichText', + shownByDefault: true, + mapping: { + value: 'text', + }, + }, + { + label: __( 'Link' ), + type: 'Link', + shownByDefault: true, + mapping: { + href: 'url', + rel: 'rel', + target: 'linkTarget', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/block-library/src/social-link/index.js b/packages/block-library/src/social-link/index.js index 161e6f2697acca..919020e0107266 100644 --- a/packages/block-library/src/social-link/index.js +++ b/packages/block-library/src/social-link/index.js @@ -1,6 +1,7 @@ /** * WordPress dependencies */ +import { __ } from '@wordpress/i18n'; import { share as icon } from '@wordpress/icons'; /** @@ -21,4 +22,26 @@ export const settings = { variations, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Link' ), + type: 'Link', + shownByDefault: true, + mapping: { + href: 'url', + rel: 'rel', + }, + }, + { + label: __( 'Label' ), + type: 'RichText', + shownByDefault: false, + mapping: { + value: 'label', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From b3498191bb3d55113c25f222b010f4f07284ff9d Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 14:07:18 +0800 Subject: [PATCH 16/60] Improve thumbnail --- .../content-only-controls/media/index.js | 126 +++++++++++++----- packages/block-library/src/video/index.js | 28 ++++ 2 files changed, 120 insertions(+), 34 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/media/index.js b/packages/block-editor/src/components/content-only-controls/media/index.js index 291d3225bf6fda..39fc94c98fa410 100644 --- a/packages/block-editor/src/components/content-only-controls/media/index.js +++ b/packages/block-editor/src/components/content-only-controls/media/index.js @@ -3,11 +3,18 @@ */ import { Button, + Icon, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalGrid as Grid, } from '@wordpress/components'; +import { useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import { lineSolid } from '@wordpress/icons'; +import { + image as imageIcon, + media as mediaIcon, + video as videoIcon, + lineSolid, +} from '@wordpress/icons'; /** * Internal dependencies @@ -15,6 +22,58 @@ import { lineSolid } from '@wordpress/icons'; import MediaUpload from '../../media-upload'; import MediaUploadCheck from '../../media-upload/check'; +function MediaThumbnail( { control, attributeValues } ) { + const { allowedTypes, multiple } = control.args; + const mapping = control.mapping; + if ( multiple ) { + return 'todo multiple'; + } + + if ( allowedTypes.length === 1 ) { + let src; + if ( + allowedTypes[ 0 ] === 'image' && + mapping.src && + attributeValues[ mapping.src ] + ) { + src = attributeValues[ mapping.src ]; + } else if ( + allowedTypes[ 0 ] === 'video' && + mapping.poster && + attributeValues[ mapping.poster ] + ) { + src = attributeValues[ mapping.poster ]; + } + + if ( src ) { + return ( + + ); + } + + let icon; + if ( allowedTypes[ 0 ] === 'image' ) { + icon = imageIcon; + } else if ( allowedTypes[ 1 ] === 'video' ) { + icon = videoIcon; + } else { + icon = mediaIcon; + } + + if ( icon ) { + return ; + } + } + + return ; +} + export default function Media( { clientId, control, @@ -26,20 +85,12 @@ export default function Media( { const srcKey = control.mapping.src; const captionKey = control.mapping.caption; const altKey = control.mapping.alt; + const posterKey = control.mapping.poster; const src = attributeValues[ srcKey ]; const caption = attributeValues[ captionKey ]; const alt = attributeValues[ altKey ]; - const idDefaultValue = - blockType.attributes[ idKey ]?.defaultValue ?? undefined; - const srcDefaultValue = - blockType.attributes[ srcKey ]?.defaultValue ?? undefined; - const captionDefaultValue = - blockType.attributes[ captionKey ]?.defaultValue ?? undefined; - const altDefaultValue = - blockType.attributes[ altKey ]?.defaultValue ?? undefined; - // TODO - pluralize when multiple. let chooseItemLabel; if ( control.args.allowedTypes.length === 1 ) { @@ -57,6 +108,18 @@ export default function Media( { chooseItemLabel = __( 'Choose a media item…' ); } + const defaultValues = useMemo( () => { + return Object.fromEntries( + Object.entries( control.mapping ).map( ( [ , attributeKey ] ) => { + return [ + attributeKey, + blockType.attributes[ attributeKey ]?.defaultValue ?? + undefined, + ]; + } ) + ); + }, [ blockType.attributes, control.mapping ] ); + return ( !! src } onDeselect={ () => { - updateAttributes( { - [ idKey ]: idDefaultValue, - [ srcKey ]: srcDefaultValue, - [ captionKey ]: captionDefaultValue, - [ altKey ]: altDefaultValue, - } ); + updateAttributes( defaultValues ); } } isShownByDefault={ control.shownByDefault } > @@ -78,14 +136,22 @@ export default function Media( { if ( selectedMedia.id && selectedMedia.url ) { const optionalAttributes = {}; - if ( ! caption && selectedMedia.caption ) { + if ( + captionKey && + ! caption && + selectedMedia.caption + ) { optionalAttributes[ captionKey ] = selectedMedia.caption; } - if ( ! alt && selectedMedia.alt ) { + if ( altKey && ! alt && selectedMedia.alt ) { optionalAttributes[ altKey ] = selectedMedia.alt; } + if ( posterKey && selectedMedia.poster ) { + optionalAttributes[ posterKey ] = + selectedMedia.poster; + } updateAttributes( { [ idKey ]: selectedMedia.id, @@ -115,12 +181,11 @@ export default function Media( { > { src && ( <> - { @@ -153,16 +218,9 @@ export default function Media( { icon={ lineSolid } onClick={ ( event ) => { event.stopPropagation(); - updateAttributes( { - [ idKey ]: - idDefaultValue, - [ srcKey ]: - srcDefaultValue, - [ captionKey ]: - captionDefaultValue, - [ altKey ]: - altDefaultValue, - } ); + updateAttributes( + defaultValues + ); } } /> diff --git a/packages/block-library/src/video/index.js b/packages/block-library/src/video/index.js index 57201ef7125658..c268ffc12cc6b4 100644 --- a/packages/block-library/src/video/index.js +++ b/packages/block-library/src/video/index.js @@ -33,4 +33,32 @@ export const settings = { save, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Video' ), + type: 'Media', + shownByDefault: true, + mapping: { + id: 'id', + src: 'src', + caption: 'caption', + poster: 'poster', + }, + args: { + allowedTypes: [ 'video' ], + multiple: false, + }, + }, + { + label: __( 'Caption' ), + type: 'RichText', + shownByDefault: false, + mapping: { + value: 'caption', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From b455dc59e2bbecce0431e70a6157448efe1efc03 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 14:08:16 +0800 Subject: [PATCH 17/60] Add support to audio block --- .../content-only-controls/media/index.js | 5 +++- packages/block-library/src/audio/index.js | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/content-only-controls/media/index.js b/packages/block-editor/src/components/content-only-controls/media/index.js index 39fc94c98fa410..ee8ba094446ac3 100644 --- a/packages/block-editor/src/components/content-only-controls/media/index.js +++ b/packages/block-editor/src/components/content-only-controls/media/index.js @@ -10,6 +10,7 @@ import { import { useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { + audio as audioIcon, image as imageIcon, media as mediaIcon, video as videoIcon, @@ -60,8 +61,10 @@ function MediaThumbnail( { control, attributeValues } ) { let icon; if ( allowedTypes[ 0 ] === 'image' ) { icon = imageIcon; - } else if ( allowedTypes[ 1 ] === 'video' ) { + } else if ( allowedTypes[ 0 ] === 'video' ) { icon = videoIcon; + } else if ( allowedTypes[ 0 ] === 'audio' ) { + icon = audioIcon; } else { icon = mediaIcon; } diff --git a/packages/block-library/src/audio/index.js b/packages/block-library/src/audio/index.js index aa1bba1c341d2f..3bf8b018574e34 100644 --- a/packages/block-library/src/audio/index.js +++ b/packages/block-library/src/audio/index.js @@ -1,6 +1,7 @@ /** * WordPress dependencies */ +import { __ } from '@wordpress/i18n'; import { audio as icon } from '@wordpress/icons'; /** @@ -31,4 +32,30 @@ export const settings = { save, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Audio' ), + type: 'Media', + shownByDefault: true, + mapping: { + id: 'id', + src: 'src', + }, + args: { + allowedTypes: [ 'audio' ], + multiple: false, + }, + }, + { + label: __( 'Caption' ), + type: 'RichText', + shownByDefault: false, + mapping: { + value: 'caption', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From 43bf8b7a9278437935f96d9355927feddeeb4555 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 14:12:25 +0800 Subject: [PATCH 18/60] Add support for code --- packages/block-library/src/code/index.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/block-library/src/code/index.js b/packages/block-library/src/code/index.js index c602045256d6e9..e00e0ca4d88bf0 100644 --- a/packages/block-library/src/code/index.js +++ b/packages/block-library/src/code/index.js @@ -39,4 +39,17 @@ export const settings = { save, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Code' ), + type: 'RichText', + shownByDefault: true, + mapping: { + value: 'content', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From 805feaf75bf6a56a4b97c0a0dff34b5b7f095ab0 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 14:35:04 +0800 Subject: [PATCH 19/60] Add media-text support --- .../content-only-controls/media/index.js | 6 ++++ .../block-library/src/media-text/index.js | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/packages/block-editor/src/components/content-only-controls/media/index.js b/packages/block-editor/src/components/content-only-controls/media/index.js index ee8ba094446ac3..a5fd5b20525d0a 100644 --- a/packages/block-editor/src/components/content-only-controls/media/index.js +++ b/packages/block-editor/src/components/content-only-controls/media/index.js @@ -84,6 +84,7 @@ export default function Media( { attributeValues, updateAttributes, } ) { + const typeKey = control.mapping.type; const idKey = control.mapping.id; const srcKey = control.mapping.src; const captionKey = control.mapping.caption; @@ -139,6 +140,11 @@ export default function Media( { if ( selectedMedia.id && selectedMedia.url ) { const optionalAttributes = {}; + if ( typeKey && selectedMedia.type ) { + optionalAttributes[ typeKey ] = + selectedMedia.type; + } + if ( captionKey && ! caption && diff --git a/packages/block-library/src/media-text/index.js b/packages/block-library/src/media-text/index.js index 373050cb77fd56..51c7e2cb23589d 100644 --- a/packages/block-library/src/media-text/index.js +++ b/packages/block-library/src/media-text/index.js @@ -50,4 +50,33 @@ export const settings = { deprecated, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Media' ), + type: 'Media', + shownByDefault: true, + mapping: { + id: 'mediaId', + type: 'mediaType', + src: 'mediaUrl', + }, + args: { + allowedTypes: [ 'image', 'video' ], + multiple: false, + }, + }, + { + label: __( 'Link' ), + type: 'Link', + shownByDefault: false, + mapping: { + href: 'href', + rel: 'rel', + target: 'linkTarget', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From bd84bec51ed01ba4465d35222aa296092a557231 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 14:39:33 +0800 Subject: [PATCH 20/60] Add support for More and Preformatted --- packages/block-library/src/more/index.js | 14 ++++++++++++++ packages/block-library/src/preformatted/index.js | 13 +++++++++++++ 2 files changed, 27 insertions(+) diff --git a/packages/block-library/src/more/index.js b/packages/block-library/src/more/index.js index b40bb2123bc727..00b96db1995d4f 100644 --- a/packages/block-library/src/more/index.js +++ b/packages/block-library/src/more/index.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { more as icon } from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -35,4 +36,17 @@ export const settings = { save, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Content' ), + type: 'RichText', + shownByDefault: true, + mapping: { + value: 'customText', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/block-library/src/preformatted/index.js b/packages/block-library/src/preformatted/index.js index a2e1fecc185528..b102b3f7fdbcc4 100644 --- a/packages/block-library/src/preformatted/index.js +++ b/packages/block-library/src/preformatted/index.js @@ -39,4 +39,17 @@ export const settings = { }, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Content' ), + type: 'RichText', + shownByDefault: true, + mapping: { + value: 'content', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From 9a49fa70591cee5ec5f9448c7aa8a327dfad4216 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 14:41:50 +0800 Subject: [PATCH 21/60] Add support to pullquote --- packages/block-library/src/pullquote/index.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/block-library/src/pullquote/index.js b/packages/block-library/src/pullquote/index.js index 9d2b42f7698ab5..9c86d6e348e0d6 100644 --- a/packages/block-library/src/pullquote/index.js +++ b/packages/block-library/src/pullquote/index.js @@ -36,4 +36,25 @@ export const settings = { deprecated, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Content' ), + type: 'RichText', + shownByDefault: true, + mapping: { + value: 'value', + }, + }, + { + label: __( 'Citation' ), + type: 'RichText', + shownByDefault: false, + mapping: { + value: 'citation', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From c6bae845b1490d0e683b5012d937f49389bbfd80 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 15:11:10 +0800 Subject: [PATCH 22/60] Add support to search --- packages/block-library/src/search/index.js | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/block-library/src/search/index.js b/packages/block-library/src/search/index.js index 85770a23268cba..6c168c99421801 100644 --- a/packages/block-library/src/search/index.js +++ b/packages/block-library/src/search/index.js @@ -26,4 +26,33 @@ export const settings = { edit, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Label' ), + type: 'RichText', + shownByDefault: true, + mapping: { + value: 'label', + }, + }, + { + label: __( 'Button text' ), + type: 'RichText', + shownByDefault: false, + mapping: { + value: 'buttonText', + }, + }, + { + label: __( 'Placeholder' ), + type: 'RichText', + shownByDefault: false, + mapping: { + value: 'placeholder', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From 76c0f8c582fb10d02d628f7d09c516170bcb7715 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 15:13:14 +0800 Subject: [PATCH 23/60] Add support for verse --- packages/block-library/src/verse/index.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/block-library/src/verse/index.js b/packages/block-library/src/verse/index.js index 5d80b63a4ca84c..916dd73016a673 100644 --- a/packages/block-library/src/verse/index.js +++ b/packages/block-library/src/verse/index.js @@ -41,4 +41,17 @@ export const settings = { save, }; +if ( window.__experimentalContentOnlyPatternInsertion ) { + settings.controls = [ + { + label: __( 'Content' ), + type: 'RichText', + shownByDefault: true, + mapping: { + value: 'content', + }, + }, + ]; +} + export const init = () => initBlock( { name, metadata, settings } ); From b7425edcdb76c28f115f7a94f28e29844df9b03e Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 15:20:14 +0800 Subject: [PATCH 24/60] Better support for video in cover block --- packages/block-library/src/cover/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/cover/index.js b/packages/block-library/src/cover/index.js index bffbf861686eac..d252f82fe2837c 100644 --- a/packages/block-library/src/cover/index.js +++ b/packages/block-library/src/cover/index.js @@ -59,14 +59,15 @@ if ( window.__experimentalContentOnlyPatternInsertion ) { type: 'Media', shownByDefault: true, mapping: { + type: 'backgroundType', id: 'id', src: 'url', alt: 'alt', }, args: { - // TODO - work out how to support video. Also background gradient. - // Does this need to be a custom control? - allowedTypes: [ 'image' ], + // TODO - How to support custom gradient? + // Build it into Media, or use a custom control? + allowedTypes: [ 'image', 'video' ], multiple: false, }, }, From 5e23b2717ad23a57f02398c6f2056e238a628a1d Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 15:31:39 +0800 Subject: [PATCH 25/60] Show controls when for root block when it is a content block --- .../src/components/block-inspector/index.js | 5 ++- .../components/content-only-controls/index.js | 31 ++++++++++++++++--- .../inspector-controls-tabs/content-tab.js | 7 +++-- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index 07d6dc9a78f3f6..3c5c15cc8d602d 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -367,7 +367,10 @@ const BlockInspectorSingleBlock = ( { { hasBlockStyles && ( ) } - + { ! isSectionBlock && ( { + const { getBlockName } = select( blockEditorStore ); + const blockName = getBlockName( rootClientId ); + const { hasContentRoleAttribute } = unlock( select( blocksStore ) ); + return hasContentRoleAttribute( blockName ); + }, + [ rootClientId ] + ); + + if ( ! isRootContentBlock && ! contentClientIds.length ) { return null; } - return clientIds.map( ( clientId ) => ( - - ) ); + return ( + <> + { isRootContentBlock && ( + + ) } + { contentClientIds.map( ( clientId ) => ( + + ) ) } + + ); } diff --git a/packages/block-editor/src/components/inspector-controls-tabs/content-tab.js b/packages/block-editor/src/components/inspector-controls-tabs/content-tab.js index 8a03ddcdc0600f..c4cf71dab5a0aa 100644 --- a/packages/block-editor/src/components/inspector-controls-tabs/content-tab.js +++ b/packages/block-editor/src/components/inspector-controls-tabs/content-tab.js @@ -10,7 +10,7 @@ import { __ } from '@wordpress/i18n'; import BlockQuickNavigation from '../block-quick-navigation'; import ContentOnlyControls from '../content-only-controls'; -const ContentTab = ( { contentClientIds } ) => { +const ContentTab = ( { rootClientId, contentClientIds } ) => { if ( ! contentClientIds || contentClientIds.length === 0 ) { return null; } @@ -24,7 +24,10 @@ const ContentTab = ( { contentClientIds } ) => { ) } { window?.__experimentalContentOnlyPatternInsertion && ( - + ) } From 5d62731b491c2ca7720bf67a986688d2cbeecefd Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 16:18:00 +0800 Subject: [PATCH 26/60] Switch to MediaReplaceFlow --- .../content-only-controls/link/index.js | 9 +- .../content-only-controls/media/index.js | 145 +++++++++--------- .../content-only-controls/media/styles.scss | 5 + .../components/media-replace-flow/index.js | 2 + 4 files changed, 88 insertions(+), 73 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/link/index.js b/packages/block-editor/src/components/content-only-controls/link/index.js index 671cbe5a3d320f..409222878160e5 100644 --- a/packages/block-editor/src/components/content-only-controls/link/index.js +++ b/packages/block-editor/src/components/content-only-controls/link/index.js @@ -11,6 +11,7 @@ import { import { useMemo, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { lineSolid, link } from '@wordpress/icons'; +import { ENTER, SPACE } from '@wordpress/keycodes'; import { prependHTTP } from '@wordpress/url'; /** @@ -130,7 +131,13 @@ export default function Link( { onClick={ () => { setIsLinkControlOpen( true ); } } - onKeyDown={ () => setIsLinkControlOpen( true ) } + onKeyDown={ ( event ) => { + const { keyCode } = event; + + if ( keyCode === ENTER || keyCode === SPACE ) { + setIsLinkControlOpen( true ); + } + } } > - { + updateAttributes( defaultValues ); + } } onSelect={ ( selectedMedia ) => { if ( selectedMedia.id && selectedMedia.url ) { const optionalAttributes = {}; @@ -169,76 +182,64 @@ export default function Media( { } ); } } } - allowedTypes={ control.args.allowedTypes } - multiple={ control.args.multiple } - render={ ( { open } ) => { - return ( -
-
{ - open(); - } } - onKeyDown={ open } + renderToggle={ ( buttonProps ) => ( +
+
{ + const { keyCode } = event; + + if ( + keyCode === ENTER || + keyCode === SPACE + ) { + buttonProps.onClick(); + } + } } + > + - - { src && ( - <> - - - { - // TODO - use media title instead of src. - // TODO - truncate to show filename when there's no title. - src - } - - - ) } - { ! src && ( - <> - - - { chooseItemLabel } - - - ) } - { src && ( - <> -
+ { src && ( + <> + + + { + // TODO - use media title instead of src. + // TODO - truncate to show filename when there's no title. + src + } + + + ) } + { ! src && ( + <> + + + { chooseItemLabel } + + + ) } +
- ); - } } +
+ ) } /> diff --git a/packages/block-editor/src/components/content-only-controls/media/styles.scss b/packages/block-editor/src/components/content-only-controls/media/styles.scss index 2e99f680d30dde..157f5f110d01b7 100644 --- a/packages/block-editor/src/components/content-only-controls/media/styles.scss +++ b/packages/block-editor/src/components/content-only-controls/media/styles.scss @@ -9,6 +9,11 @@ } } +.block-editor-content-only-controls__media-replace-flow { + // Required, otherwise the width collapses. + display: block; +} + .block-editor-content-only-controls__media-row { align-items: center; } diff --git a/packages/block-editor/src/components/media-replace-flow/index.js b/packages/block-editor/src/components/media-replace-flow/index.js index eb4dcd0165d3c2..64280dde597d7c 100644 --- a/packages/block-editor/src/components/media-replace-flow/index.js +++ b/packages/block-editor/src/components/media-replace-flow/index.js @@ -93,6 +93,7 @@ const MediaReplaceFlow = ( { handleUpload = true, popoverProps, renderToggle, + className, } ) => { const { getSettings } = useSelect( blockEditorStore ); const errorNoticeID = `block-editor/media-replace-flow/error-notice/${ ++uniqueId }`; @@ -169,6 +170,7 @@ const MediaReplaceFlow = ( { return ( { if ( renderToggle ) { From 59b21d21dc5e9a8ee962879fefe4b3f3bde8dc7d Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 16:25:12 +0800 Subject: [PATCH 27/60] Support featured image in cover block --- .../components/content-only-controls/media/index.js | 12 ++++++++++++ packages/block-library/src/cover/index.js | 1 + 2 files changed, 13 insertions(+) diff --git a/packages/block-editor/src/components/content-only-controls/media/index.js b/packages/block-editor/src/components/content-only-controls/media/index.js index 582d436691b164..e4d5b5cec65d3f 100644 --- a/packages/block-editor/src/components/content-only-controls/media/index.js +++ b/packages/block-editor/src/components/content-only-controls/media/index.js @@ -93,11 +93,13 @@ export default function Media( { const captionKey = control.mapping.caption; const altKey = control.mapping.alt; const posterKey = control.mapping.poster; + const featuredImageKey = control.mapping.featuredImage; const id = attributeValues[ idKey ]; const src = attributeValues[ srcKey ]; const caption = attributeValues[ captionKey ]; const alt = attributeValues[ altKey ]; + const useFeaturedImage = attributeValues[ featuredImageKey ]; // TODO - pluralize when multiple. let chooseItemLabel; @@ -149,6 +151,16 @@ export default function Media( { onReset={ () => { updateAttributes( defaultValues ); } } + useFeaturedImage={ !! useFeaturedImage } + onToggleFeaturedImage={ + !! featuredImageKey && + ( () => { + updateAttributes( { + ...defaultValues, + [ featuredImageKey ]: ! useFeaturedImage, + } ); + } ) + } onSelect={ ( selectedMedia ) => { if ( selectedMedia.id && selectedMedia.url ) { const optionalAttributes = {}; diff --git a/packages/block-library/src/cover/index.js b/packages/block-library/src/cover/index.js index d252f82fe2837c..80e455e953e069 100644 --- a/packages/block-library/src/cover/index.js +++ b/packages/block-library/src/cover/index.js @@ -63,6 +63,7 @@ if ( window.__experimentalContentOnlyPatternInsertion ) { id: 'id', src: 'url', alt: 'alt', + featuredImage: 'useFeaturedImage', }, args: { // TODO - How to support custom gradient? From ed501df4162a0810c00c9c69c29690e286ce2b69 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 17:27:22 +0800 Subject: [PATCH 28/60] Remove unset buttons --- .../content-only-controls/link/index.js | 26 +++---------------- .../content-only-controls/media/index.js | 2 +- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/link/index.js b/packages/block-editor/src/components/content-only-controls/link/index.js index 409222878160e5..6802feaef705ba 100644 --- a/packages/block-editor/src/components/content-only-controls/link/index.js +++ b/packages/block-editor/src/components/content-only-controls/link/index.js @@ -2,7 +2,6 @@ * WordPress dependencies */ import { - Button, Icon, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalGrid as Grid, @@ -10,7 +9,7 @@ import { } from '@wordpress/components'; import { useMemo, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import { lineSolid, link } from '@wordpress/icons'; +import { link } from '@wordpress/icons'; import { ENTER, SPACE } from '@wordpress/keycodes'; import { prependHTTP } from '@wordpress/url'; @@ -127,7 +126,7 @@ export default function Link( {
{ setIsLinkControlOpen( true ); } } @@ -142,7 +141,7 @@ export default function Link( { { href && ( @@ -165,25 +164,6 @@ export default function Link( { ) } - { href && ( - <> -
diff --git a/packages/block-editor/src/components/content-only-controls/media/index.js b/packages/block-editor/src/components/content-only-controls/media/index.js index e4d5b5cec65d3f..e571487b460420 100644 --- a/packages/block-editor/src/components/content-only-controls/media/index.js +++ b/packages/block-editor/src/components/content-only-controls/media/index.js @@ -198,7 +198,7 @@ export default function Media( {
{ const { keyCode } = event; From 66b5ced1974993a847609910df6f374989dcd4cc Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 17:28:39 +0800 Subject: [PATCH 29/60] Semi functioning rich text control --- .../content-only-controls/rich-text/index.js | 89 +++++++++++++++---- 1 file changed, 70 insertions(+), 19 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/rich-text/index.js b/packages/block-editor/src/components/content-only-controls/rich-text/index.js index c7e410e4416479..186c856bc62920 100644 --- a/packages/block-editor/src/components/content-only-controls/rich-text/index.js +++ b/packages/block-editor/src/components/content-only-controls/rich-text/index.js @@ -1,23 +1,74 @@ /** * WordPress dependencies */ -import { - __experimentalToolsPanelItem as ToolsPanelItem, - TextControl, -} from '@wordpress/components'; +import { __experimentalToolsPanelItem as ToolsPanelItem } from '@wordpress/components'; +import { useInstanceId, useMergeRefs } from '@wordpress/compose'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; +import { useState } from '@wordpress/element'; +import { + __unstableUseRichText as useRichText, + // removeFormat, +} from '@wordpress/rich-text'; + +/** + * Internal dependencies + */ +import { useFormatTypes } from '../rich-text'; -export default function RichText( { +export default function RichTextControl( { clientId, control, blockType, attributeValues, updateAttributes, } ) { + const instanceId = useInstanceId( RichTextControl ); + const controlId = `block-editor-content-only-controls__rich-text-${ instanceId }`; const valueKey = control.mapping.value; - const value = attributeValues[ valueKey ]; + const attrValue = attributeValues[ valueKey ]; const defaultValue = blockType.attributes[ valueKey ]?.defaultValue ?? undefined; + const [ selection, setSelection ] = useState( { + start: undefined, + end: undefined, + } ); + + // const { + // formatTypes, + // prepareHandlers, + // valueHandlers, + // changeHandlers, + // dependencies, + // } = useFormatTypes( { + // clientId, + // identifier, + // allowedFormats: adjustedAllowedFormats, + // withoutInteractiveFormatting, + // disableNoneEssentialFormatting: isContentOnlyWriteMode, + // } ); + + const { value, ref: richTextRef } = useRichText( { + value: attrValue, + onChange( html, { __unstableFormats, __unstableText } ) { + updateAttributes( { [ valueKey ]: html } ); + // Object.values( changeHandlers ).forEach( ( changeHandler ) => { + // changeHandler( __unstableFormats, __unstableText ); + // } ); + }, + selectionStart: selection.start, + selectionEnd: selection.end, + onSelectionChange: ( start, end ) => setSelection( { start, end } ), + // placeholder: bindingsPlaceholder || placeholder, + // __unstableIsSelected: isSelected, + // __unstableDisableFormats: disableFormats, + // preserveWhiteSpace, + // __unstableDependencies: dependencies, + // __unstableAfterParse: addEditorOnlyFormats, + // __unstableBeforeSerialize: removeEditorOnlyFormats, + // __unstableAddInvisibleFormats: addInvisibleFormats, + } ); + + const hasVisibleLabel = ! control.shownByDefault; return ( { - updateAttributes( { [ valueKey ]: defaultValue } ); - } } + onDeselect={ () => {} } isShownByDefault={ control.shownByDefault } > - { - updateAttributes( { [ valueKey ]: newValue } ); - } } - autoComplete="off" - hideLabelFromVision={ control.shownByDefault } + { hasVisibleLabel && ( + + ) } +
); From 41761c7f50f94ad569503f8dd335d50c6c7cea52 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 17:49:28 +0800 Subject: [PATCH 30/60] More rich text stuff --- .../content-only-controls/rich-text/index.js | 102 ++++++++++++------ 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/rich-text/index.js b/packages/block-editor/src/components/content-only-controls/rich-text/index.js index 186c856bc62920..a1755a7e2359c3 100644 --- a/packages/block-editor/src/components/content-only-controls/rich-text/index.js +++ b/packages/block-editor/src/components/content-only-controls/rich-text/index.js @@ -3,17 +3,18 @@ */ import { __experimentalToolsPanelItem as ToolsPanelItem } from '@wordpress/components'; import { useInstanceId, useMergeRefs } from '@wordpress/compose'; -import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; import { useState } from '@wordpress/element'; import { __unstableUseRichText as useRichText, - // removeFormat, + isEmpty, + removeFormat, } from '@wordpress/rich-text'; /** * Internal dependencies */ -import { useFormatTypes } from '../rich-text'; +import { useFormatTypes } from '../../rich-text/use-format-types'; +import { getAllowedFormats } from '../../rich-text/utils'; export default function RichTextControl( { clientId, @@ -32,40 +33,77 @@ export default function RichTextControl( { start: undefined, end: undefined, } ); + const [ isSelected, setIsSelected ] = useState( false ); - // const { - // formatTypes, - // prepareHandlers, - // valueHandlers, - // changeHandlers, - // dependencies, - // } = useFormatTypes( { - // clientId, - // identifier, - // allowedFormats: adjustedAllowedFormats, - // withoutInteractiveFormatting, - // disableNoneEssentialFormatting: isContentOnlyWriteMode, - // } ); + const adjustedAllowedFormats = getAllowedFormats( { + allowedFormats: control.args?.allowedFormats, + disableFormats: control.args?.disableFormats, + } ); + + const { + formatTypes, + prepareHandlers, + valueHandlers, + changeHandlers, + dependencies, + } = useFormatTypes( { + clientId, + identifier: valueKey, + allowedFormats: adjustedAllowedFormats, + withoutInteractiveFormatting: + control.args?.withoutInteractiveFormatting, + disableNoneEssentialFormatting: true, + } ); + + function addEditorOnlyFormats( value ) { + return valueHandlers.reduce( + ( accumulator, fn ) => fn( accumulator, value.text ), + value.formats + ); + } + + function removeEditorOnlyFormats( value ) { + formatTypes.forEach( ( formatType ) => { + // Remove formats created by prepareEditableTree, because they are editor only. + if ( formatType.__experimentalCreatePrepareEditableTree ) { + value = removeFormat( + value, + formatType.name, + 0, + value.text.length + ); + } + } ); + + return value.formats; + } + + function addInvisibleFormats( value ) { + return prepareHandlers.reduce( + ( accumulator, fn ) => fn( accumulator, value.text ), + value.formats + ); + } const { value, ref: richTextRef } = useRichText( { value: attrValue, onChange( html, { __unstableFormats, __unstableText } ) { updateAttributes( { [ valueKey ]: html } ); - // Object.values( changeHandlers ).forEach( ( changeHandler ) => { - // changeHandler( __unstableFormats, __unstableText ); - // } ); + Object.values( changeHandlers ).forEach( ( changeHandler ) => { + changeHandler( __unstableFormats, __unstableText ); + } ); }, selectionStart: selection.start, selectionEnd: selection.end, onSelectionChange: ( start, end ) => setSelection( { start, end } ), - // placeholder: bindingsPlaceholder || placeholder, - // __unstableIsSelected: isSelected, - // __unstableDisableFormats: disableFormats, - // preserveWhiteSpace, - // __unstableDependencies: dependencies, - // __unstableAfterParse: addEditorOnlyFormats, - // __unstableBeforeSerialize: removeEditorOnlyFormats, - // __unstableAddInvisibleFormats: addInvisibleFormats, + __unstableIsSelected: isSelected, + preserveWhiteSpace: !! control.args?.preserveWhiteSpace, + placeholder: control.args?.placeholder, + __unstableDisableFormats: control.args?.disableFormats, + __unstableDependencies: dependencies, + __unstableAfterParse: addEditorOnlyFormats, + __unstableBeforeSerialize: removeEditorOnlyFormats, + __unstableAddInvisibleFormats: addInvisibleFormats, } ); const hasVisibleLabel = ! control.shownByDefault; @@ -75,11 +113,11 @@ export default function RichTextControl( { panelId={ clientId } label={ control.label } hasValue={ () => { - return ( - value !== defaultValue && stripHTML( value )?.length !== 0 - ); + return value?.text && ! isEmpty( value ); } } - onDeselect={ () => {} } + onDeselect={ () => + updateAttributes( { [ valueKey ]: defaultValue } ) + } isShownByDefault={ control.shownByDefault } > { hasVisibleLabel && ( @@ -93,6 +131,8 @@ export default function RichTextControl( { aria-label={ hasVisibleLabel ? undefined : control.label } aria-multiline={ ! control.args?.disableLineBreaks } ref={ useMergeRefs( [ richTextRef ] ) } + onFocus={ () => setIsSelected( true ) } + onBlur={ () => setIsSelected( false ) } contentEditable /> From 4057ba1da16e25cc73e65ca5e11a4bceaa659784 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 18:25:11 +0800 Subject: [PATCH 31/60] Improve richtext control styles (hacky) --- .../content-only-controls/rich-text/index.js | 43 ++++++++++--------- .../rich-text/styles.scss | 21 +++++++++ .../content-only-controls/styles.scss | 1 + 3 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 packages/block-editor/src/components/content-only-controls/rich-text/styles.scss diff --git a/packages/block-editor/src/components/content-only-controls/rich-text/index.js b/packages/block-editor/src/components/content-only-controls/rich-text/index.js index a1755a7e2359c3..8a9286edd2f7ae 100644 --- a/packages/block-editor/src/components/content-only-controls/rich-text/index.js +++ b/packages/block-editor/src/components/content-only-controls/rich-text/index.js @@ -1,8 +1,12 @@ /** * WordPress dependencies */ -import { __experimentalToolsPanelItem as ToolsPanelItem } from '@wordpress/components'; -import { useInstanceId, useMergeRefs } from '@wordpress/compose'; +import { + BaseControl, + useBaseControlProps, + __experimentalToolsPanelItem as ToolsPanelItem, +} from '@wordpress/components'; +import { useMergeRefs } from '@wordpress/compose'; import { useState } from '@wordpress/element'; import { __unstableUseRichText as useRichText, @@ -23,8 +27,6 @@ export default function RichTextControl( { attributeValues, updateAttributes, } ) { - const instanceId = useInstanceId( RichTextControl ); - const controlId = `block-editor-content-only-controls__rich-text-${ instanceId }`; const valueKey = control.mapping.value; const attrValue = attributeValues[ valueKey ]; const defaultValue = @@ -106,7 +108,10 @@ export default function RichTextControl( { __unstableAddInvisibleFormats: addInvisibleFormats, } ); - const hasVisibleLabel = ! control.shownByDefault; + const { baseControlProps, controlProps } = useBaseControlProps( { + hideLabelFromVision: control.shownByDefault, + label: control.label, + } ); return ( - { hasVisibleLabel && ( - - ) } -
setIsSelected( true ) } - onBlur={ () => setIsSelected( false ) } - contentEditable - /> + +
setIsSelected( true ) } + onBlur={ () => setIsSelected( false ) } + contentEditable + { ...controlProps } + /> + ); } diff --git a/packages/block-editor/src/components/content-only-controls/rich-text/styles.scss b/packages/block-editor/src/components/content-only-controls/rich-text/styles.scss new file mode 100644 index 00000000000000..40b01b6514c58d --- /dev/null +++ b/packages/block-editor/src/components/content-only-controls/rich-text/styles.scss @@ -0,0 +1,21 @@ + +.block-editor-content-only-controls__rich-text { + width: 100%; + // Override input style margin in WP forms.css. + margin: 0; + background: $white; + color: $gray-900; + @include input-control( var(--wp-admin-theme-color, #3858e9) ); + border-color: $gray-600; + + &::placeholder { + color: color-mix(in srgb, $gray-900, transparent 38%); + } + + min-height: $grid-unit-50; + + // Subtract 1px to account for the border, which isn't included on the element + // on newer components like InputControl, SelectControl, etc. + // These values should be shared with the `controlPaddingX` in ./utils/config-values.js + padding: $grid-unit-15; +} diff --git a/packages/block-editor/src/components/content-only-controls/styles.scss b/packages/block-editor/src/components/content-only-controls/styles.scss index 2522fe65f0459f..0f57db76f57265 100644 --- a/packages/block-editor/src/components/content-only-controls/styles.scss +++ b/packages/block-editor/src/components/content-only-controls/styles.scss @@ -1 +1,2 @@ @use "./media/styles.scss" as *; +@use "./rich-text/styles.scss" as *; From 1eb93d4594b7132ab817c8be38807e2eb213585e Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 18:43:03 +0800 Subject: [PATCH 32/60] Kitchen sink to get formats working --- .../content-only-controls/rich-text/index.js | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/rich-text/index.js b/packages/block-editor/src/components/content-only-controls/rich-text/index.js index 8a9286edd2f7ae..0f125d87aaf3c4 100644 --- a/packages/block-editor/src/components/content-only-controls/rich-text/index.js +++ b/packages/block-editor/src/components/content-only-controls/rich-text/index.js @@ -7,7 +7,8 @@ import { __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; import { useMergeRefs } from '@wordpress/compose'; -import { useState } from '@wordpress/element'; +import { useRegistry } from '@wordpress/data'; +import { useRef, useState } from '@wordpress/element'; import { __unstableUseRichText as useRichText, isEmpty, @@ -19,6 +20,9 @@ import { */ import { useFormatTypes } from '../../rich-text/use-format-types'; import { getAllowedFormats } from '../../rich-text/utils'; +import { useEventListeners } from '../../rich-text/event-listeners'; +import FormatEdit from '../../rich-text/format-edit'; +import { keyboardShortcutContext, inputEventContext } from '../../rich-text'; export default function RichTextControl( { clientId, @@ -27,6 +31,7 @@ export default function RichTextControl( { attributeValues, updateAttributes, } ) { + const registry = useRegistry(); const valueKey = control.mapping.value; const attrValue = attributeValues[ valueKey ]; const defaultValue = @@ -36,6 +41,9 @@ export default function RichTextControl( { end: undefined, } ); const [ isSelected, setIsSelected ] = useState( false ); + const anchorRef = useRef(); + const inputEvents = useRef( new Set() ); + const keyboardShortcuts = useRef( new Set() ); const adjustedAllowedFormats = getAllowedFormats( { allowedFormats: control.args?.allowedFormats, @@ -87,7 +95,16 @@ export default function RichTextControl( { ); } - const { value, ref: richTextRef } = useRichText( { + function onFocus() { + anchorRef.current?.focus(); + } + + const { + value, + getValue, + onChange, + ref: richTextRef, + } = useRichText( { value: attrValue, onChange( html, { __unstableFormats, __unstableText } ) { updateAttributes( { [ valueKey ]: html } ); @@ -125,13 +142,46 @@ export default function RichTextControl( { } isShownByDefault={ control.shownByDefault } > + { isSelected && ( + + +
+ +
+
+
+ ) }
setIsSelected( true ) } onBlur={ () => setIsSelected( false ) } contentEditable From be274f6da27660bb1bcfc30f4c10bd57f982c6ae Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 18:52:16 +0800 Subject: [PATCH 33/60] Fix formats showing on toolbar twice --- .../src/components/content-only-controls/rich-text/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-editor/src/components/content-only-controls/rich-text/index.js b/packages/block-editor/src/components/content-only-controls/rich-text/index.js index 0f125d87aaf3c4..7273ea41e9c4ed 100644 --- a/packages/block-editor/src/components/content-only-controls/rich-text/index.js +++ b/packages/block-editor/src/components/content-only-controls/rich-text/index.js @@ -152,6 +152,7 @@ export default function RichTextControl( { onFocus={ onFocus } formatTypes={ formatTypes } forwardedRef={ anchorRef } + isVisible={ false } />
From 40b3d536c70ba4e44630535b3be7c44da97014c0 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 18:53:37 +0800 Subject: [PATCH 34/60] Fix tagName --- .../src/components/content-only-controls/rich-text/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/rich-text/index.js b/packages/block-editor/src/components/content-only-controls/rich-text/index.js index 7273ea41e9c4ed..ea02718e231b22 100644 --- a/packages/block-editor/src/components/content-only-controls/rich-text/index.js +++ b/packages/block-editor/src/components/content-only-controls/rich-text/index.js @@ -161,7 +161,6 @@ export default function RichTextControl( {
Date: Wed, 17 Sep 2025 18:56:07 +0800 Subject: [PATCH 35/60] Match border color --- .../src/components/content-only-controls/media/styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/content-only-controls/media/styles.scss b/packages/block-editor/src/components/content-only-controls/media/styles.scss index 157f5f110d01b7..ddf972010d8a36 100644 --- a/packages/block-editor/src/components/content-only-controls/media/styles.scss +++ b/packages/block-editor/src/components/content-only-controls/media/styles.scss @@ -1,5 +1,5 @@ .block-editor-content-only-controls__media { - border: $border-width solid $gray-300; + border: $border-width solid $gray-600; border-radius: $radius-small; // Add important to prevent toolspanel from unsetting padding. padding: 8px 12px !important; From 4302be9bc8ac27e06416f15e74daf3e26394c4ff Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 19:02:56 +0800 Subject: [PATCH 36/60] Add plain text control --- .../components/content-only-controls/index.js | 2 + .../content-only-controls/plain-text/index.js | 49 +++++++++++++++++++ packages/block-library/src/image/index.js | 2 +- 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 packages/block-editor/src/components/content-only-controls/plain-text/index.js diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index dac692eeb0490f..6712d6e7314ad2 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -19,11 +19,13 @@ import useBlockDisplayInformation from '../use-block-display-information'; import { useInspectorPopoverPlacement } from './use-inspector-popover-placement'; // controls +import PlainText from './plain-text'; import RichText from './rich-text'; import Media from './media'; import Link from './link'; const controls = { + PlainText, RichText, Media, Link, diff --git a/packages/block-editor/src/components/content-only-controls/plain-text/index.js b/packages/block-editor/src/components/content-only-controls/plain-text/index.js new file mode 100644 index 00000000000000..c7e410e4416479 --- /dev/null +++ b/packages/block-editor/src/components/content-only-controls/plain-text/index.js @@ -0,0 +1,49 @@ +/** + * WordPress dependencies + */ +import { + __experimentalToolsPanelItem as ToolsPanelItem, + TextControl, +} from '@wordpress/components'; +import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; + +export default function RichText( { + clientId, + control, + blockType, + attributeValues, + updateAttributes, +} ) { + const valueKey = control.mapping.value; + const value = attributeValues[ valueKey ]; + const defaultValue = + blockType.attributes[ valueKey ]?.defaultValue ?? undefined; + + return ( + { + return ( + value !== defaultValue && stripHTML( value )?.length !== 0 + ); + } } + onDeselect={ () => { + updateAttributes( { [ valueKey ]: defaultValue } ); + } } + isShownByDefault={ control.shownByDefault } + > + { + updateAttributes( { [ valueKey ]: newValue } ); + } } + autoComplete="off" + hideLabelFromVision={ control.shownByDefault } + /> + + ); +} diff --git a/packages/block-library/src/image/index.js b/packages/block-library/src/image/index.js index 5e2f9a2b0012d8..022061959770e8 100644 --- a/packages/block-library/src/image/index.js +++ b/packages/block-library/src/image/index.js @@ -100,7 +100,7 @@ if ( window.__experimentalContentOnlyPatternInsertion ) { }, { label: __( 'Alt text' ), - type: 'RichText', + type: 'PlainText', shownByDefault: false, mapping: { value: 'alt', From 8f831c68eda02d6360b3d95ffbbb374888054fe4 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 17 Sep 2025 19:03:16 +0800 Subject: [PATCH 37/60] Add tagName back again to fix formatting --- .../src/components/content-only-controls/rich-text/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-editor/src/components/content-only-controls/rich-text/index.js b/packages/block-editor/src/components/content-only-controls/rich-text/index.js index ea02718e231b22..559641874c2e09 100644 --- a/packages/block-editor/src/components/content-only-controls/rich-text/index.js +++ b/packages/block-editor/src/components/content-only-controls/rich-text/index.js @@ -174,6 +174,7 @@ export default function RichTextControl( { isSelected, disableFormats: control.args?.disableFormats, value, + tagName: 'div', removeEditorOnlyFormats, disableLineBreaks: control.args?.disableLineBreaks, keyboardShortcuts, From 37508b3e11994c860e3ad7e3fae47c408111fd7c Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 18 Sep 2025 10:43:39 +0800 Subject: [PATCH 38/60] Remove old configuration --- packages/block-library/src/heading/block.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/block-library/src/heading/block.json b/packages/block-library/src/heading/block.json index 9e7030886d2252..d9488a3156528d 100644 --- a/packages/block-library/src/heading/block.json +++ b/packages/block-library/src/heading/block.json @@ -15,12 +15,7 @@ "type": "rich-text", "source": "rich-text", "selector": "h1,h2,h3,h4,h5,h6", - "role": "content", - "control": { - "type": "RichText", - "label": "Content", - "shownByDefault": true - } + "role": "content" }, "level": { "type": "number", From d658f2f210a81d3fa0b67cf731d4316da2caa554 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 18 Sep 2025 12:59:20 +0800 Subject: [PATCH 39/60] Show media thumbnail and title when possible --- .../content-only-controls/media/index.js | 48 +++++++++++++++++-- packages/block-editor/src/private-apis.js | 2 + .../block-editor/src/store/private-keys.js | 1 + .../provider/use-block-editor-settings.js | 8 ++++ 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/media/index.js b/packages/block-editor/src/components/content-only-controls/media/index.js index e571487b460420..559f87366c1a78 100644 --- a/packages/block-editor/src/components/content-only-controls/media/index.js +++ b/packages/block-editor/src/components/content-only-controls/media/index.js @@ -6,6 +6,7 @@ import { __experimentalToolsPanelItem as ToolsPanelItem, __experimentalGrid as Grid, } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { @@ -22,14 +23,32 @@ import { ENTER, SPACE } from '@wordpress/keycodes'; import MediaReplaceFlow from '../../media-replace-flow'; import MediaUploadCheck from '../../media-upload/check'; import { useInspectorPopoverPlacement } from '../use-inspector-popover-placement'; +import { getMediaSelectKey } from '../../../store/private-keys'; +import { store as blockEditorStore } from '../../../store'; -function MediaThumbnail( { control, attributeValues } ) { +function MediaThumbnail( { control, attributeValues, attachment } ) { const { allowedTypes, multiple } = control.args; const mapping = control.mapping; if ( multiple ) { return 'todo multiple'; } + if ( attachment?.media_type === 'image' || attachment?.poster ) { + return ( + + ); + } + if ( allowedTypes.length === 1 ) { let src; if ( @@ -101,6 +120,24 @@ export default function Media( { const alt = attributeValues[ altKey ]; const useFeaturedImage = attributeValues[ featuredImageKey ]; + const attachment = useSelect( + ( select ) => { + if ( ! id ) { + return; + } + + const settings = select( blockEditorStore ).getSettings(); + const getMedia = settings[ getMediaSelectKey ]; + + if ( ! getMedia ) { + return; + } + + return getMedia( select, id ); + }, + [ id ] + ); + // TODO - pluralize when multiple. let chooseItemLabel; if ( control.args.allowedTypes.length === 1 ) { @@ -145,7 +182,7 @@ export default function Media( { className="block-editor-content-only-controls__media-replace-flow" allowedTypes={ control.args.allowedTypes } mediaId={ id } - mediaURL={ srcKey } + mediaURL={ src } multiple={ control.args.multiple } popoverProps={ popoverProps } onReset={ () => { @@ -220,6 +257,7 @@ export default function Media( { { src && ( <> { - // TODO - use media title instead of src. - // TODO - truncate to show filename when there's no title. - src + // TODO - truncate long titles or url smartly (e.g. show filename). + attachment?.title?.raw ?? + src } diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index 47c704f6d7b394..7502b2cf9dc207 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -40,6 +40,7 @@ import { globalStylesLinksDataKey, sectionRootClientIdKey, mediaEditKey, + getMediaSelectKey, essentialFormatKey, } from './store/private-keys'; import { requiresWrapperOnCopy } from './components/writing-flow/utils'; @@ -108,6 +109,7 @@ lock( privateApis, { CommentIconSlotFill, CommentIconToolbarSlotFill, mediaEditKey, + getMediaSelectKey, essentialFormatKey, useBlockElement, useBlockElementRef, diff --git a/packages/block-editor/src/store/private-keys.js b/packages/block-editor/src/store/private-keys.js index 8fde1a53aaa523..35ca6b1dd4d09c 100644 --- a/packages/block-editor/src/store/private-keys.js +++ b/packages/block-editor/src/store/private-keys.js @@ -4,4 +4,5 @@ export const selectBlockPatternsKey = Symbol( 'selectBlockPatternsKey' ); export const reusableBlocksSelectKey = Symbol( 'reusableBlocksSelect' ); export const sectionRootClientIdKey = Symbol( 'sectionRootClientIdKey' ); export const mediaEditKey = Symbol( 'mediaEditKey' ); +export const getMediaSelectKey = Symbol( 'getMediaSelect' ); export const essentialFormatKey = Symbol( 'essentialFormat' ); diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index 136bb88dd10fae..0a3ebb6df33d62 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -96,6 +96,7 @@ const { reusableBlocksSelectKey, sectionRootClientIdKey, mediaEditKey, + getMediaSelectKey, } = unlock( privateApis ); /** @@ -293,6 +294,13 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) { hasFixedToolbar, isDistractionFree, keepCaretInsideBlock, + [ getMediaSelectKey ]: ( select, attachmentId ) => { + return select( coreStore ).getEntityRecord( + 'postType', + 'attachment', + attachmentId + ); + }, [ mediaEditKey ]: hasUploadPermissions ? editMediaEntity : undefined, From cfc0590d5baae5d081e0d69858f7bf904049e9de Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 18 Sep 2025 13:37:46 +0800 Subject: [PATCH 40/60] Fix component name --- .../src/components/content-only-controls/plain-text/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/content-only-controls/plain-text/index.js b/packages/block-editor/src/components/content-only-controls/plain-text/index.js index c7e410e4416479..8281c4a8e5f153 100644 --- a/packages/block-editor/src/components/content-only-controls/plain-text/index.js +++ b/packages/block-editor/src/components/content-only-controls/plain-text/index.js @@ -7,7 +7,7 @@ import { } from '@wordpress/components'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; -export default function RichText( { +export default function PlainText( { clientId, control, blockType, From 825b2f8be5accda2e6c8dd0d81c417cd03da5b78 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 18 Sep 2025 14:44:46 +0800 Subject: [PATCH 41/60] Use actual buttons --- .../content-only-controls/link/index.js | 80 +++++++------- .../content-only-controls/link/styles.scss | 14 +++ .../content-only-controls/media/index.js | 100 ++++++++---------- .../content-only-controls/media/styles.scss | 10 +- .../content-only-controls/styles.scss | 1 + 5 files changed, 95 insertions(+), 110 deletions(-) create mode 100644 packages/block-editor/src/components/content-only-controls/link/styles.scss diff --git a/packages/block-editor/src/components/content-only-controls/link/index.js b/packages/block-editor/src/components/content-only-controls/link/index.js index 6802feaef705ba..23789ede759e1d 100644 --- a/packages/block-editor/src/components/content-only-controls/link/index.js +++ b/packages/block-editor/src/components/content-only-controls/link/index.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { + Button, Icon, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalGrid as Grid, @@ -10,7 +11,6 @@ import { import { useMemo, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { link } from '@wordpress/icons'; -import { ENTER, SPACE } from '@wordpress/keycodes'; import { prependHTTP } from '@wordpress/url'; /** @@ -123,50 +123,42 @@ export default function Link( { } } isShownByDefault={ control.shownByDefault } > -
-
{ - setIsLinkControlOpen( true ); - } } - onKeyDown={ ( event ) => { - const { keyCode } = event; - - if ( keyCode === ENTER || keyCode === SPACE ) { - setIsLinkControlOpen( true ); - } - } } +
-
+ { href && ( + <> + + + { href } + + + ) } + { ! href && ( + <> + + + { __( 'Link' ) } + + + ) } + + { isLinkControlOpen && ( { diff --git a/packages/block-editor/src/components/content-only-controls/link/styles.scss b/packages/block-editor/src/components/content-only-controls/link/styles.scss new file mode 100644 index 00000000000000..2f74920faa9f04 --- /dev/null +++ b/packages/block-editor/src/components/content-only-controls/link/styles.scss @@ -0,0 +1,14 @@ +.block-editor-content-only-controls__link { + width: 100%; +} + +.block-editor-content-only-controls__link-row { + align-items: center; +} + +.block-editor-content-only-controls__link-title { + width: 100%; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} diff --git a/packages/block-editor/src/components/content-only-controls/media/index.js b/packages/block-editor/src/components/content-only-controls/media/index.js index 559f87366c1a78..1a2fc195890ef3 100644 --- a/packages/block-editor/src/components/content-only-controls/media/index.js +++ b/packages/block-editor/src/components/content-only-controls/media/index.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { + Button, Icon, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalGrid as Grid, @@ -15,7 +16,6 @@ import { media as mediaIcon, video as videoIcon, } from '@wordpress/icons'; -import { ENTER, SPACE } from '@wordpress/keycodes'; /** * Internal dependencies @@ -232,63 +232,49 @@ export default function Media( { } } } renderToggle={ ( buttonProps ) => ( -
-
{ - const { keyCode } = event; - - if ( - keyCode === ENTER || - keyCode === SPACE - ) { - buttonProps.onClick(); - } - } } +
-
+ { src && ( + <> + + + { + // TODO - truncate long titles or url smartly (e.g. show filename). + attachment?.title?.raw ?? src + } + + + ) } + { ! src && ( + <> + + + { chooseItemLabel } + + + ) } + + ) } /> diff --git a/packages/block-editor/src/components/content-only-controls/media/styles.scss b/packages/block-editor/src/components/content-only-controls/media/styles.scss index ddf972010d8a36..fdb03d142ec5f2 100644 --- a/packages/block-editor/src/components/content-only-controls/media/styles.scss +++ b/packages/block-editor/src/components/content-only-controls/media/styles.scss @@ -1,12 +1,5 @@ .block-editor-content-only-controls__media { - border: $border-width solid $gray-600; - border-radius: $radius-small; - // Add important to prevent toolspanel from unsetting padding. - padding: 8px 12px !important; - cursor: pointer; - &:hover { - background-color: $gray-100; - } + width: 100%; } .block-editor-content-only-controls__media-replace-flow { @@ -32,7 +25,6 @@ .block-editor-content-only-controls__media-title { width: 100%; - color: $gray-900; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; diff --git a/packages/block-editor/src/components/content-only-controls/styles.scss b/packages/block-editor/src/components/content-only-controls/styles.scss index 0f57db76f57265..d33719bc77d2a1 100644 --- a/packages/block-editor/src/components/content-only-controls/styles.scss +++ b/packages/block-editor/src/components/content-only-controls/styles.scss @@ -1,2 +1,3 @@ +@use "./link/styles.scss" as *; @use "./media/styles.scss" as *; @use "./rich-text/styles.scss" as *; From 80bad4abb103d906cb6e0614ed86e560c65e999a Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 18 Sep 2025 15:16:09 +0800 Subject: [PATCH 42/60] Improve fallback to URL --- .../src/components/content-only-controls/media/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/content-only-controls/media/index.js b/packages/block-editor/src/components/content-only-controls/media/index.js index 1a2fc195890ef3..58512ae66e977d 100644 --- a/packages/block-editor/src/components/content-only-controls/media/index.js +++ b/packages/block-editor/src/components/content-only-controls/media/index.js @@ -254,7 +254,10 @@ export default function Media( { { // TODO - truncate long titles or url smartly (e.g. show filename). - attachment?.title?.raw ?? src + attachment?.title?.raw && + attachment?.title?.raw !== '' + ? attachment?.title?.raw + : src } From a37780ea6f8cfb1357944481c09e3ef80e66ee6a Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 18 Sep 2025 15:56:22 +0800 Subject: [PATCH 43/60] More button style iterations --- .../src/components/content-only-controls/link/index.js | 1 - .../src/components/content-only-controls/link/styles.scss | 6 ++++++ .../src/components/content-only-controls/media/index.js | 1 - .../src/components/content-only-controls/media/styles.scss | 6 ++++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/link/index.js b/packages/block-editor/src/components/content-only-controls/link/index.js index 23789ede759e1d..356f1102bbc867 100644 --- a/packages/block-editor/src/components/content-only-controls/link/index.js +++ b/packages/block-editor/src/components/content-only-controls/link/index.js @@ -125,7 +125,6 @@ export default function Link( { >
) } - { isRootContentBlock && ( - - ) } + { isRootContentBlock && } { contentClientIds.map( ( clientId ) => { if ( parentClientIds?.[ clientId ] ) { return ( @@ -182,7 +180,7 @@ function ContentOnlyControlsScreen( { ); } - return ; + return ; } ) }
); diff --git a/packages/block-library/src/audio/index.js b/packages/block-library/src/audio/index.js index 3bf8b018574e34..e37123683d9104 100644 --- a/packages/block-library/src/audio/index.js +++ b/packages/block-library/src/audio/index.js @@ -33,7 +33,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Audio' ), type: 'Media', diff --git a/packages/block-library/src/button/index.js b/packages/block-library/src/button/index.js index 6051a0a5d28904..4765fe5b089d66 100644 --- a/packages/block-library/src/button/index.js +++ b/packages/block-library/src/button/index.js @@ -35,7 +35,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Content' ), type: 'RichText', diff --git a/packages/block-library/src/code/index.js b/packages/block-library/src/code/index.js index e00e0ca4d88bf0..e3a928461f82a1 100644 --- a/packages/block-library/src/code/index.js +++ b/packages/block-library/src/code/index.js @@ -40,7 +40,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Code' ), type: 'RichText', diff --git a/packages/block-library/src/cover/index.js b/packages/block-library/src/cover/index.js index 80e455e953e069..88777cc137524f 100644 --- a/packages/block-library/src/cover/index.js +++ b/packages/block-library/src/cover/index.js @@ -53,7 +53,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Background' ), type: 'Media', diff --git a/packages/block-library/src/details/index.js b/packages/block-library/src/details/index.js index 6da0b01997592b..8d33963fd9486b 100644 --- a/packages/block-library/src/details/index.js +++ b/packages/block-library/src/details/index.js @@ -62,7 +62,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Summary' ), type: 'RichText', diff --git a/packages/block-library/src/file/index.js b/packages/block-library/src/file/index.js index 386cfd49983bea..c33cd03338be49 100644 --- a/packages/block-library/src/file/index.js +++ b/packages/block-library/src/file/index.js @@ -33,7 +33,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'File' ), type: 'Media', diff --git a/packages/block-library/src/heading/index.js b/packages/block-library/src/heading/index.js index 1cdae54f763d7d..aec0fa016e3028 100644 --- a/packages/block-library/src/heading/index.js +++ b/packages/block-library/src/heading/index.js @@ -70,7 +70,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Content' ), type: 'RichText', diff --git a/packages/block-library/src/image/index.js b/packages/block-library/src/image/index.js index 022061959770e8..f389d26a1b0cd8 100644 --- a/packages/block-library/src/image/index.js +++ b/packages/block-library/src/image/index.js @@ -63,7 +63,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Image' ), type: 'Media', diff --git a/packages/block-library/src/list-item/index.js b/packages/block-library/src/list-item/index.js index 2ed8124c7beab6..582f7c1db0218d 100644 --- a/packages/block-library/src/list-item/index.js +++ b/packages/block-library/src/list-item/index.js @@ -34,7 +34,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Content' ), type: 'RichText', diff --git a/packages/block-library/src/media-text/index.js b/packages/block-library/src/media-text/index.js index 51c7e2cb23589d..6bbc7a613b4523 100644 --- a/packages/block-library/src/media-text/index.js +++ b/packages/block-library/src/media-text/index.js @@ -51,7 +51,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Media' ), type: 'Media', diff --git a/packages/block-library/src/more/index.js b/packages/block-library/src/more/index.js index 00b96db1995d4f..2022d0db00ee02 100644 --- a/packages/block-library/src/more/index.js +++ b/packages/block-library/src/more/index.js @@ -37,7 +37,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Content' ), type: 'RichText', diff --git a/packages/block-library/src/navigation-link/index.js b/packages/block-library/src/navigation-link/index.js index b546e9cb34a2f9..72c9cc67178aa9 100644 --- a/packages/block-library/src/navigation-link/index.js +++ b/packages/block-library/src/navigation-link/index.js @@ -90,7 +90,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Label' ), type: 'RichText', diff --git a/packages/block-library/src/navigation-submenu/index.js b/packages/block-library/src/navigation-submenu/index.js index 58ac4dae8aeb26..c72c891ac09dae 100644 --- a/packages/block-library/src/navigation-submenu/index.js +++ b/packages/block-library/src/navigation-submenu/index.js @@ -49,7 +49,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Label' ), type: 'RichText', diff --git a/packages/block-library/src/paragraph/index.js b/packages/block-library/src/paragraph/index.js index 0429817696ddcd..a040f8d2273019 100644 --- a/packages/block-library/src/paragraph/index.js +++ b/packages/block-library/src/paragraph/index.js @@ -59,7 +59,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Content' ), type: 'RichText', diff --git a/packages/block-library/src/preformatted/index.js b/packages/block-library/src/preformatted/index.js index b102b3f7fdbcc4..ec41dc0e8e1f0a 100644 --- a/packages/block-library/src/preformatted/index.js +++ b/packages/block-library/src/preformatted/index.js @@ -40,7 +40,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Content' ), type: 'RichText', diff --git a/packages/block-library/src/pullquote/index.js b/packages/block-library/src/pullquote/index.js index 9c86d6e348e0d6..efdfdebdf5ed24 100644 --- a/packages/block-library/src/pullquote/index.js +++ b/packages/block-library/src/pullquote/index.js @@ -37,7 +37,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Content' ), type: 'RichText', diff --git a/packages/block-library/src/search/index.js b/packages/block-library/src/search/index.js index 6c168c99421801..10ce7c6732ad16 100644 --- a/packages/block-library/src/search/index.js +++ b/packages/block-library/src/search/index.js @@ -27,7 +27,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Label' ), type: 'RichText', diff --git a/packages/block-library/src/social-link/index.js b/packages/block-library/src/social-link/index.js index 919020e0107266..d601e36876df18 100644 --- a/packages/block-library/src/social-link/index.js +++ b/packages/block-library/src/social-link/index.js @@ -23,7 +23,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Link' ), type: 'Link', diff --git a/packages/block-library/src/verse/index.js b/packages/block-library/src/verse/index.js index 916dd73016a673..35a5b4dec27475 100644 --- a/packages/block-library/src/verse/index.js +++ b/packages/block-library/src/verse/index.js @@ -42,7 +42,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Content' ), type: 'RichText', diff --git a/packages/block-library/src/video/index.js b/packages/block-library/src/video/index.js index c268ffc12cc6b4..79cc5bb52657a5 100644 --- a/packages/block-library/src/video/index.js +++ b/packages/block-library/src/video/index.js @@ -34,7 +34,7 @@ export const settings = { }; if ( window.__experimentalContentOnlyPatternInsertion ) { - settings.controls = [ + settings.fields = [ { label: __( 'Video' ), type: 'Media', From 2b7004da43f34784fe0c5b763cd3535947f1e278 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Mon, 10 Nov 2025 11:41:07 +0800 Subject: [PATCH 58/60] Remove navigation duplicate content role --- packages/block-library/src/navigation/block.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json index 923eb1a95aa0bf..1315cce9318fbb 100644 --- a/packages/block-library/src/navigation/block.json +++ b/packages/block-library/src/navigation/block.json @@ -139,8 +139,7 @@ } }, "interactivity": true, - "renaming": false, - "contentRole": true + "renaming": false }, "editorStyle": "wp-block-navigation-editor", "style": "wp-block-navigation" From e8400bf0c585057cccb89e62e15f8e214cc74a75 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:37:01 +1100 Subject: [PATCH 59/60] Fix navigation screen padding --- .../src/components/content-only-controls/index.js | 15 +++++++++++---- .../components/content-only-controls/styles.scss | 4 +++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/content-only-controls/index.js b/packages/block-editor/src/components/content-only-controls/index.js index c00ed6b75b7a78..120f614f8ab0f8 100644 --- a/packages/block-editor/src/components/content-only-controls/index.js +++ b/packages/block-editor/src/components/content-only-controls/index.js @@ -158,7 +158,7 @@ function ContentOnlyControlsScreen( { } return ( -
+ <> { isNested && (
@@ -182,7 +182,7 @@ function ContentOnlyControlsScreen( { return ; } ) } -
+ ); } @@ -268,7 +268,10 @@ export default function ContentOnlyControls( { rootClientId } ) { return ( - + { Object.keys( nestedContentClientIds ).map( ( clientId ) => ( - + Date: Thu, 13 Nov 2025 11:39:24 +1100 Subject: [PATCH 60/60] Ensure consistency with what this branch was previously doing for link visibility --- packages/format-library/src/link/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/format-library/src/link/index.js b/packages/format-library/src/link/index.js index ec8eb1f0d32675..0c96dc6a0591aa 100644 --- a/packages/format-library/src/link/index.js +++ b/packages/format-library/src/link/index.js @@ -206,7 +206,7 @@ function Edit( { aria-expanded={ addingLink } /> ) } - { addingLink && ( + { isVisible && addingLink && (