diff --git a/docs/reference-guides/data/data-core-edit-post.md b/docs/reference-guides/data/data-core-edit-post.md
index 1716482d5178c0..40c890748e3199 100644
--- a/docs/reference-guides/data/data-core-edit-post.md
+++ b/docs/reference-guides/data/data-core-edit-post.md
@@ -476,11 +476,11 @@ _Parameters_
### switchEditorMode
-Triggers an action used to switch editor mode.
+Sets the editor mode (for text editing or visual editing).
_Parameters_
-- _mode_ `string`: The editor mode.
+- _mode_ `'visual'|'text'`: The mode, either 'visual' or 'text'.
### toggleDistractionFree
diff --git a/docs/reference-guides/data/data-core-edit-site.md b/docs/reference-guides/data/data-core-edit-site.md
index 636ccab4f3c6da..e6423935b6f07f 100644
--- a/docs/reference-guides/data/data-core-edit-site.md
+++ b/docs/reference-guides/data/data-core-edit-site.md
@@ -395,7 +395,11 @@ _Returns_
### switchEditorMode
-Undocumented declaration.
+Sets the editor mode (for text editing or visual editing).
+
+_Parameters_
+
+- _mode_ `'visual'|'text'`: The mode, either 'visual' or 'text'.
### toggleDistractionFree
diff --git a/packages/edit-post/src/components/header/mode-switcher/index.js b/packages/edit-post/src/components/header/mode-switcher/index.js
index 1ca01a4b024f81..21ae30a8e8884a 100644
--- a/packages/edit-post/src/components/header/mode-switcher/index.js
+++ b/packages/edit-post/src/components/header/mode-switcher/index.js
@@ -1,90 +1,40 @@
/**
* WordPress dependencies
*/
-import { __ } from '@wordpress/i18n';
-import { MenuItemsChoice, MenuGroup } from '@wordpress/components';
-import { useSelect, useDispatch } from '@wordpress/data';
+import { useSelect } from '@wordpress/data';
+import {
+ privateApis as editorPrivateApis,
+ store as editorStore,
+} from '@wordpress/editor';
import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
-import { store as editorStore } from '@wordpress/editor';
/**
* Internal dependencies
*/
-import { store as editPostStore } from '../../../store';
+import { unlock } from '../../../lock-unlock';
-/**
- * Set of available mode options.
- *
- * @type {Array}
- */
-const MODES = [
- {
- value: 'visual',
- label: __( 'Visual editor' ),
- },
- {
- value: 'text',
- label: __( 'Code editor' ),
- },
-];
+const { ModeSwitcher: EditorModeSwitcher } = unlock( editorPrivateApis );
function ModeSwitcher() {
- const { shortcut, isRichEditingEnabled, isCodeEditingEnabled, mode } =
- useSelect(
- ( select ) => ( {
- shortcut: select(
- keyboardShortcutsStore
- ).getShortcutRepresentation( 'core/edit-post/toggle-mode' ),
- isRichEditingEnabled:
- select( editorStore ).getEditorSettings()
- .richEditingEnabled,
- isCodeEditingEnabled:
- select( editorStore ).getEditorSettings()
- .codeEditingEnabled,
- mode: select( editPostStore ).getEditorMode(),
- } ),
- []
- );
- const { switchEditorMode } = useDispatch( editPostStore );
-
- let selectedMode = mode;
- if ( ! isRichEditingEnabled && mode === 'visual' ) {
- selectedMode = 'text';
- }
- if ( ! isCodeEditingEnabled && mode === 'text' ) {
- selectedMode = 'visual';
- }
-
- const choices = MODES.map( ( choice ) => {
- if ( ! isCodeEditingEnabled && choice.value === 'text' ) {
- choice = {
- ...choice,
- disabled: true,
- };
- }
- if ( ! isRichEditingEnabled && choice.value === 'visual' ) {
- choice = {
- ...choice,
- disabled: true,
- info: __(
- 'You can enable the visual editor in your profile settings.'
- ),
- };
- }
- if ( choice.value !== selectedMode && ! choice.disabled ) {
- return { ...choice, shortcut };
- }
- return choice;
- } );
+ const { shortcut, isRichEditingEnabled, isCodeEditingEnabled } = useSelect(
+ ( select ) => ( {
+ shortcut: select(
+ keyboardShortcutsStore
+ ).getShortcutRepresentation( 'core/edit-post/toggle-mode' ),
+ isRichEditingEnabled:
+ select( editorStore ).getEditorSettings().richEditingEnabled,
+ isCodeEditingEnabled:
+ select( editorStore ).getEditorSettings().codeEditingEnabled,
+ } ),
+ []
+ );
return (
-
-
-
+
);
}
diff --git a/packages/edit-post/src/store/actions.js b/packages/edit-post/src/store/actions.js
index a1246b4f59928c..0aca86f9ae2f71 100644
--- a/packages/edit-post/src/store/actions.js
+++ b/packages/edit-post/src/store/actions.js
@@ -5,9 +5,7 @@ import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import { store as interfaceStore } from '@wordpress/interface';
import { store as preferencesStore } from '@wordpress/preferences';
-import { speak } from '@wordpress/a11y';
import { store as noticesStore } from '@wordpress/notices';
-import { store as blockEditorStore } from '@wordpress/block-editor';
import { store as editorStore } from '@wordpress/editor';
import deprecated from '@wordpress/deprecated';
import { addFilter } from '@wordpress/hooks';
@@ -188,33 +186,14 @@ export const toggleFeature =
.toggle( 'core/edit-post', feature );
/**
- * Triggers an action used to switch editor mode.
+ * Sets the editor mode (for text editing or visual editing).
*
- * @param {string} mode The editor mode.
+ * @param {'visual'|'text'} mode The mode, either 'visual' or 'text'.
*/
export const switchEditorMode =
( mode ) =>
- ( { dispatch, registry } ) => {
- registry.dispatch( preferencesStore ).set( 'core', 'editorMode', mode );
-
- // Unselect blocks when we switch to the code editor.
- if ( mode !== 'visual' ) {
- registry.dispatch( blockEditorStore ).clearSelectedBlock();
- }
-
- if (
- mode === 'text' &&
- registry.select( preferencesStore ).get( 'core', 'distractionFree' )
- ) {
- dispatch.toggleDistractionFree();
- }
-
- const message =
- mode === 'visual'
- ? __( 'Visual editor selected' )
- : __( 'Code editor selected' );
- speak( message, 'assertive' );
- };
+ ( { registry } ) =>
+ registry.dispatch( editorStore ).setEditorMode( mode );
/**
* Triggers an action object used to toggle a plugin name flag.
diff --git a/packages/edit-site/src/components/header-edit-mode/mode-switcher/index.js b/packages/edit-site/src/components/header-edit-mode/mode-switcher/index.js
index 2c27444f669c03..68614a38e50dba 100644
--- a/packages/edit-site/src/components/header-edit-mode/mode-switcher/index.js
+++ b/packages/edit-site/src/components/header-edit-mode/mode-switcher/index.js
@@ -1,60 +1,27 @@
/**
* WordPress dependencies
*/
-import { __ } from '@wordpress/i18n';
-import { MenuItemsChoice, MenuGroup } from '@wordpress/components';
-import { useSelect, useDispatch } from '@wordpress/data';
+import { useSelect } from '@wordpress/data';
+import { privateApis as editorPrivateApis } from '@wordpress/editor';
import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
/**
* Internal dependencies
*/
-import { store as editSiteStore } from '../../../store';
+import { unlock } from '../../../lock-unlock';
-/**
- * Set of available mode options.
- *
- * @type {Array}
- */
-const MODES = [
- {
- value: 'visual',
- label: __( 'Visual editor' ),
- },
- {
- value: 'text',
- label: __( 'Code editor' ),
- },
-];
+const { ModeSwitcher: EditorModeSwitcher } = unlock( editorPrivateApis );
function ModeSwitcher() {
- const { shortcut, mode } = useSelect(
- ( select ) => ( {
- shortcut: select(
- keyboardShortcutsStore
- ).getShortcutRepresentation( 'core/edit-site/toggle-mode' ),
- mode: select( editSiteStore ).getEditorMode(),
- } ),
+ const shortcut = useSelect(
+ ( select ) =>
+ select( keyboardShortcutsStore ).getShortcutRepresentation(
+ 'core/edit-site/toggle-mode'
+ ),
[]
);
- const { switchEditorMode } = useDispatch( editSiteStore );
- const choices = MODES.map( ( choice ) => {
- if ( choice.value !== mode ) {
- return { ...choice, shortcut };
- }
- return choice;
- } );
-
- return (
-
-
-
- );
+ return ;
}
export default ModeSwitcher;
diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js
index 728081bf2fc0f4..7cf114664319e0 100644
--- a/packages/edit-site/src/store/actions.js
+++ b/packages/edit-site/src/store/actions.js
@@ -11,7 +11,6 @@ import { store as coreStore } from '@wordpress/core-data';
import { store as interfaceStore } from '@wordpress/interface';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { store as editorStore } from '@wordpress/editor';
-import { speak } from '@wordpress/a11y';
import { store as preferencesStore } from '@wordpress/preferences';
/**
@@ -500,30 +499,15 @@ export const closeGeneralSidebar =
.disableComplementaryArea( editSiteStoreName );
};
+/**
+ * Sets the editor mode (for text editing or visual editing).
+ *
+ * @param {'visual'|'text'} mode The mode, either 'visual' or 'text'.
+ */
export const switchEditorMode =
( mode ) =>
- ( { dispatch, registry } ) => {
- registry
- .dispatch( 'core/preferences' )
- .set( 'core', 'editorMode', mode );
-
- // Unselect blocks when we switch to a non visual mode.
- if ( mode !== 'visual' ) {
- registry.dispatch( blockEditorStore ).clearSelectedBlock();
- }
-
- if ( mode === 'visual' ) {
- speak( __( 'Visual editor selected' ), 'assertive' );
- } else if ( mode === 'text' ) {
- const isDistractionFree = registry
- .select( preferencesStore )
- .get( 'core', 'distractionFree' );
- if ( isDistractionFree ) {
- dispatch.toggleDistractionFree();
- }
- speak( __( 'Code editor selected' ), 'assertive' );
- }
- };
+ ( { registry } ) =>
+ registry.dispatch( editorStore ).setEditorMode( mode );
/**
* Sets whether or not the editor allows only page content to be edited.
diff --git a/packages/editor/src/components/mode-switcher/index.js b/packages/editor/src/components/mode-switcher/index.js
new file mode 100644
index 00000000000000..0d0dd9fd1244cb
--- /dev/null
+++ b/packages/editor/src/components/mode-switcher/index.js
@@ -0,0 +1,87 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { MenuItemsChoice, MenuGroup } from '@wordpress/components';
+import { useSelect, useDispatch } from '@wordpress/data';
+import { store as preferencesStore } from '@wordpress/preferences';
+
+/**
+ * Internal dependencies
+ */
+import { store as editorStore } from '../../store';
+import { unlock } from '../../lock-unlock';
+
+/**
+ * Set of available mode options.
+ *
+ * @type {Array}
+ */
+const MODES = [
+ {
+ value: 'visual',
+ label: __( 'Visual editor' ),
+ },
+ {
+ value: 'text',
+ label: __( 'Code editor' ),
+ },
+];
+
+function ModeSwitcher( {
+ shortcut,
+ isCodeEditingEnabled = true,
+ isRichEditingEnabled = true,
+} ) {
+ const { mode } = useSelect(
+ ( select ) => ( {
+ mode:
+ select( preferencesStore ).get( 'core', 'editorMode' ) ??
+ 'visual',
+ } ),
+ []
+ );
+ const { setEditorMode } = unlock( useDispatch( editorStore ) );
+
+ let selectedMode = mode;
+ if ( ! isRichEditingEnabled && mode === 'visual' ) {
+ selectedMode = 'text';
+ }
+ if ( ! isCodeEditingEnabled && mode === 'text' ) {
+ selectedMode = 'visual';
+ }
+
+ const choices = MODES.map( ( choice ) => {
+ if ( ! isCodeEditingEnabled && choice.value === 'text' ) {
+ choice = {
+ ...choice,
+ disabled: true,
+ };
+ }
+ if ( ! isRichEditingEnabled && choice.value === 'visual' ) {
+ choice = {
+ ...choice,
+ disabled: true,
+ info: __(
+ 'You can enable the visual editor in your profile settings.'
+ ),
+ };
+ }
+ if ( choice.value !== selectedMode && ! choice.disabled ) {
+ return { ...choice, shortcut };
+ }
+ return choice;
+ } );
+
+ return (
+
+
+
+ );
+}
+
+export default ModeSwitcher;
diff --git a/packages/editor/src/private-apis.js b/packages/editor/src/private-apis.js
index 16c27b1b57c193..061393acab1ebe 100644
--- a/packages/editor/src/private-apis.js
+++ b/packages/editor/src/private-apis.js
@@ -10,6 +10,7 @@ import useBlockEditorSettings from './components/provider/use-block-editor-setti
import DocumentTools from './components/document-tools';
import InserterSidebar from './components/inserter-sidebar';
import ListViewSidebar from './components/list-view-sidebar';
+import ModeSwitcher from './components/mode-switcher';
import PluginPostExcerpt from './components/post-excerpt/plugin';
import PostPanelRow from './components/post-panel-row';
import PostViewLink from './components/post-view-link';
@@ -25,6 +26,7 @@ lock( privateApis, {
EntitiesSavedStatesExtensible,
InserterSidebar,
ListViewSidebar,
+ ModeSwitcher,
PluginPostExcerpt,
PostPanelRow,
PostViewLink,
diff --git a/packages/editor/src/store/private-actions.js b/packages/editor/src/store/private-actions.js
index 936d0bd5bf4471..4620d151ed047d 100644
--- a/packages/editor/src/store/private-actions.js
+++ b/packages/editor/src/store/private-actions.js
@@ -1,6 +1,8 @@
/**
* WordPress dependencies
*/
+import { speak } from '@wordpress/a11y';
+import { store as blockEditorStore } from '@wordpress/block-editor';
import { store as coreStore } from '@wordpress/core-data';
import { __ } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
@@ -109,3 +111,33 @@ export const hideBlockTypes =
.dispatch( preferencesStore )
.set( 'core', 'hiddenBlockTypes', [ ...mergedBlockNames ] );
};
+
+/**
+ * Sets the editor mode (for text editing or visual editing).
+ *
+ * @param {'visual'|'text'} mode The mode, either 'visual' or 'text'.
+ */
+export const setEditorMode =
+ ( mode ) =>
+ ( { dispatch, registry } ) => {
+ registry
+ .dispatch( 'core/preferences' )
+ .set( 'core', 'editorMode', mode );
+
+ // Unselect blocks when we switch to a non visual mode.
+ if ( mode !== 'visual' ) {
+ registry.dispatch( blockEditorStore ).clearSelectedBlock();
+ }
+
+ if ( mode === 'visual' ) {
+ speak( __( 'Visual editor selected' ), 'assertive' );
+ } else if ( mode === 'text' ) {
+ const isDistractionFree = registry
+ .select( preferencesStore )
+ .get( 'core', 'distractionFree' );
+ if ( isDistractionFree ) {
+ dispatch.toggleDistractionFree();
+ }
+ speak( __( 'Code editor selected' ), 'assertive' );
+ }
+ };