diff --git a/packages/block-editor/src/components/block-controls/groups.js b/packages/block-editor/src/components/block-controls/groups.js
index 9b9dfec8d8d450..391f1bf0b2279a 100644
--- a/packages/block-editor/src/components/block-controls/groups.js
+++ b/packages/block-editor/src/components/block-controls/groups.js
@@ -8,6 +8,7 @@ const BlockControlsBlock = createSlotFill( 'BlockControlsBlock' );
const BlockControlsInline = createSlotFill( 'BlockFormatControls' );
const BlockControlsOther = createSlotFill( 'BlockControlsOther' );
const BlockControlsParent = createSlotFill( 'BlockControlsParent' );
+const BlockControlsAlt = createSlotFill( 'BlockControlsParent' );
const groups = {
default: BlockControlsDefault,
@@ -15,6 +16,7 @@ const groups = {
inline: BlockControlsInline,
other: BlockControlsOther,
parent: BlockControlsParent,
+ alt: BlockControlsAlt,
};
export default groups;
diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js
index def5b425760d10..762451371769f2 100644
--- a/packages/block-editor/src/components/block-toolbar/index.js
+++ b/packages/block-editor/src/components/block-toolbar/index.js
@@ -32,6 +32,7 @@ export default function BlockToolbar( { hideDragHandle } ) {
hasReducedUI,
isValid,
isVisual,
+ isAltMode,
} = useSelect( ( select ) => {
const {
getBlockName,
@@ -61,6 +62,9 @@ export default function BlockToolbar( { hideDragHandle } ) {
isVisual: selectedBlockClientIds.every(
( id ) => getBlockMode( id ) === 'visual'
),
+ isAltMode: selectedBlockClientIds.every(
+ ( id ) => getBlockMode( id ) === 'alt'
+ ),
};
}, [] );
@@ -105,6 +109,17 @@ export default function BlockToolbar( { hideDragHandle } ) {
shouldShowMovers && 'is-showing-movers'
);
+ if ( isAltMode ) {
+ return (
+
+
+
+ );
+ }
+
return (
{ ! isMultiToolbar && ! displayHeaderToolbar && (
diff --git a/packages/block-editor/src/components/image-editor/index.js b/packages/block-editor/src/components/image-editor/index.js
index 87a48f67e41fdc..5cf9affcbec1e6 100644
--- a/packages/block-editor/src/components/image-editor/index.js
+++ b/packages/block-editor/src/components/image-editor/index.js
@@ -31,7 +31,7 @@ export default function ImageEditor( {
naturalHeight={ naturalHeight }
naturalWidth={ naturalWidth }
/>
-
+
diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js
index 2d0d3cc77ecddb..b5aa946576e6ff 100644
--- a/packages/block-editor/src/store/actions.js
+++ b/packages/block-editor/src/store/actions.js
@@ -896,6 +896,22 @@ export function toggleBlockMode( clientId ) {
};
}
+/**
+ * Returns an action object used to set alternate editing modes.
+ *
+ * @param {string} clientId Block client ID.
+ * @param {string} mode Block editing mode.
+ *
+ * @return {Object} Action object.
+ */
+export function setBlockMode( clientId, mode ) {
+ return {
+ type: 'SET_BLOCK_MODE',
+ clientId,
+ mode,
+ };
+}
+
/**
* Returns an action object used in signalling that the user has begun to type.
*
diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js
index e5c333aacd223d..b433155313d5f5 100644
--- a/packages/block-editor/src/store/reducer.js
+++ b/packages/block-editor/src/store/reducer.js
@@ -1390,15 +1390,22 @@ export function initialPosition( state = null, action ) {
}
export function blocksMode( state = {}, action ) {
- if ( action.type === 'TOGGLE_BLOCK_MODE' ) {
- const { clientId } = action;
- return {
- ...state,
- [ clientId ]:
- state[ clientId ] && state[ clientId ] === 'html'
- ? 'visual'
- : 'html',
- };
+ const { clientId } = action;
+
+ switch ( action.type ) {
+ case 'TOGGLE_BLOCK_MODE':
+ return {
+ ...state,
+ [ clientId ]:
+ state[ clientId ] && state[ clientId ] === 'html'
+ ? 'visual'
+ : 'html',
+ };
+ case 'SET_BLOCK_MODE':
+ return {
+ ...state,
+ [ clientId ]: action.mode,
+ };
}
return state;
diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js
index 8c49687685d87b..b0de5d11859c34 100644
--- a/packages/block-library/src/image/image.js
+++ b/packages/block-library/src/image/image.js
@@ -50,6 +50,8 @@ import { isExternalImage } from './edit';
*/
import { MIN_SIZE, ALLOWED_MEDIA_TYPES } from './constants';
+const IMAGE_EDITING_BLOCK_MODE = 'alt';
+
export default function Image( {
temporaryURL,
attributes: {
@@ -110,12 +112,14 @@ export default function Image( {
imageSizes,
maxWidth,
mediaUpload,
+ isEditingImage,
} = useSelect(
( select ) => {
const {
getBlockRootClientId,
getSettings,
canInsertBlockType,
+ getBlockMode,
} = select( blockEditorStore );
const rootClientId = getBlockRootClientId( clientId );
@@ -132,11 +136,15 @@ export default function Image( {
'core/cover',
rootClientId
),
+ isEditingImage:
+ getBlockMode( clientId ) === IMAGE_EDITING_BLOCK_MODE,
};
},
[ clientId ]
);
- const { replaceBlocks, toggleSelection } = useDispatch( blockEditorStore );
+ const { replaceBlocks, toggleSelection, setBlockMode } = useDispatch(
+ blockEditorStore
+ );
const { createErrorNotice, createSuccessNotice } = useDispatch(
noticesStore
);
@@ -146,7 +154,6 @@ export default function Image( {
{ loadedNaturalWidth, loadedNaturalHeight },
setLoadedNaturalSize,
] = useState( {} );
- const [ isEditingImage, setIsEditingImage ] = useState( false );
const [ externalBlob, setExternalBlob ] = useState();
const clientWidth = useClientWidth( containerRef, [ align ] );
const isResizable = allowResize && ! ( isWideAligned && isLargeViewport );
@@ -156,6 +163,11 @@ export default function Image( {
),
( { name, slug } ) => ( { value: slug, label: name } )
);
+ const setIsEditingImage = ( isEditing ) =>
+ setBlockMode(
+ clientId,
+ isEditing ? IMAGE_EDITING_BLOCK_MODE : 'visual'
+ );
// If an image is externally hosted, try to fetch the image data. This may
// fail if the image host doesn't allow CORS with the domain. If it works,