diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index 768ffbb0cdd2dc..310b2a0ce63264 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -132,8 +132,12 @@ export function RichTextWrapper( return { isSelected: false }; } - const { getSelectionStart, getSelectionEnd } = - select( blockEditorStore ); + const { + getSelectionStart, + getSelectionEnd, + getBlockEditingMode, + isNavigationMode, + } = select( blockEditorStore ); const selectionStart = getSelectionStart(); const selectionEnd = getSelectionEnd(); @@ -154,15 +158,19 @@ export function RichTextWrapper( selectionStart: isSelected ? selectionStart.offset : undefined, selectionEnd: isSelected ? selectionEnd.offset : undefined, isSelected, + isContentOnlyWriteMode: + isNavigationMode() && + getBlockEditingMode( clientId ) === 'contentOnly', }; }; - const { selectionStart, selectionEnd, isSelected } = useSelect( selector, [ - clientId, - identifier, - instanceId, - originalIsSelected, - isBlockSelected, - ] ); + const { selectionStart, selectionEnd, isSelected, isContentOnlyWriteMode } = + useSelect( selector, [ + clientId, + identifier, + instanceId, + originalIsSelected, + isBlockSelected, + ] ); const { disableBoundBlock, bindingsPlaceholder, bindingsLabel } = useSelect( ( select ) => { @@ -323,8 +331,9 @@ export function RichTextWrapper( } = useFormatTypes( { clientId, identifier, - withoutInteractiveFormatting, allowedFormats: adjustedAllowedFormats, + withoutInteractiveFormatting, + disableNoneEssentialFormatting: isContentOnlyWriteMode, } ); function addEditorOnlyFormats( value ) { diff --git a/packages/block-editor/src/components/rich-text/use-format-types.js b/packages/block-editor/src/components/rich-text/use-format-types.js index 0bbebbf262367d..f6d55ea1cb9644 100644 --- a/packages/block-editor/src/components/rich-text/use-format-types.js +++ b/packages/block-editor/src/components/rich-text/use-format-types.js @@ -5,6 +5,11 @@ import { useMemo } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; import { store as richTextStore } from '@wordpress/rich-text'; +/** + * Internal dependencies + */ +import { essentialFormatKey } from '../../store/private-keys'; + function formatTypesSelector( select ) { return select( richTextStore ).getFormatTypes(); } @@ -56,35 +61,53 @@ function getPrefixedSelectKeys( selected, prefix ) { * This hook provides RichText with the `formatTypes` and its derived props from * experimental format type settings. * - * @param {Object} $0 Options - * @param {string} $0.clientId Block client ID. - * @param {string} $0.identifier Block attribute. - * @param {boolean} $0.withoutInteractiveFormatting Whether to clean the interactive formatting or not. - * @param {Array} $0.allowedFormats Allowed formats + * @param {Object} options Options + * @param {string} options.clientId Block client ID. + * @param {string} options.identifier Block attribute. + * @param {Array} options.allowedFormats Allowed formats + * @param {boolean} options.withoutInteractiveFormatting Whether to clean the interactive formatting or not. + * @param {boolean} options.disableNoneEssentialFormatting Whether to disable none-essential formatting or not. */ export function useFormatTypes( { clientId, identifier, - withoutInteractiveFormatting, allowedFormats, + withoutInteractiveFormatting, + disableNoneEssentialFormatting = false, } ) { const allFormatTypes = useSelect( formatTypesSelector, [] ); const formatTypes = useMemo( () => { - return allFormatTypes.filter( ( { name, interactive, tagName } ) => { - if ( allowedFormats && ! allowedFormats.includes( name ) ) { - return false; - } + return allFormatTypes.filter( + ( { + name, + interactive, + tagName, + [ essentialFormatKey ]: isEssential, + } ) => { + if ( allowedFormats && ! allowedFormats.includes( name ) ) { + return false; + } - if ( - withoutInteractiveFormatting && - ( interactive || interactiveContentTags.has( tagName ) ) - ) { - return false; - } + if ( disableNoneEssentialFormatting && ! isEssential ) { + return false; + } - return true; - } ); - }, [ allFormatTypes, allowedFormats, withoutInteractiveFormatting ] ); + if ( + withoutInteractiveFormatting && + ( interactive || interactiveContentTags.has( tagName ) ) + ) { + return false; + } + + return true; + } + ); + }, [ + allFormatTypes, + allowedFormats, + disableNoneEssentialFormatting, + withoutInteractiveFormatting, + ] ); const keyedSelected = useSelect( ( select ) => formatTypes.reduce( ( accumulator, type ) => { diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index 0ecacd124e97ae..f6d5e8f5fe1cb2 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, + essentialFormatKey, } from './store/private-keys'; import { requiresWrapperOnCopy } from './components/writing-flow/utils'; import { PrivateRichText } from './components/rich-text/'; @@ -101,4 +102,5 @@ lock( privateApis, { CommentIconSlotFill, CommentIconToolbarSlotFill, mediaEditKey, + essentialFormatKey, } ); diff --git a/packages/block-editor/src/store/private-keys.js b/packages/block-editor/src/store/private-keys.js index 15da423296c3ff..8fde1a53aaa523 100644 --- a/packages/block-editor/src/store/private-keys.js +++ b/packages/block-editor/src/store/private-keys.js @@ -4,3 +4,4 @@ export const selectBlockPatternsKey = Symbol( 'selectBlockPatternsKey' ); export const reusableBlocksSelectKey = Symbol( 'reusableBlocksSelect' ); export const sectionRootClientIdKey = Symbol( 'sectionRootClientIdKey' ); export const mediaEditKey = Symbol( 'mediaEditKey' ); +export const essentialFormatKey = Symbol( 'essentialFormat' ); diff --git a/packages/format-library/src/bold/index.js b/packages/format-library/src/bold/index.js index 98f5b4e3a9542b..78dc26ac43dc50 100644 --- a/packages/format-library/src/bold/index.js +++ b/packages/format-library/src/bold/index.js @@ -7,9 +7,17 @@ import { RichTextToolbarButton, RichTextShortcut, __unstableRichTextInputEvent, + privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { formatBold } from '@wordpress/icons'; +/** + * Internal dependencies + */ +import { unlock } from '../lock-unlock'; + +const { essentialFormatKey } = unlock( blockEditorPrivateApis ); + const name = 'core/bold'; const title = __( 'Bold' ); @@ -18,6 +26,7 @@ export const bold = { title, tagName: 'strong', className: null, + [ essentialFormatKey ]: true, edit( { isActive, value, onChange, onFocus } ) { function onToggle() { onChange( toggleFormat( value, { type: name, title } ) ); diff --git a/packages/format-library/src/italic/index.js b/packages/format-library/src/italic/index.js index 7287cff6546d29..15355e31841004 100644 --- a/packages/format-library/src/italic/index.js +++ b/packages/format-library/src/italic/index.js @@ -7,9 +7,17 @@ import { RichTextToolbarButton, RichTextShortcut, __unstableRichTextInputEvent, + privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { formatItalic } from '@wordpress/icons'; +/** + * Internal dependencies + */ +import { unlock } from '../lock-unlock'; + +const { essentialFormatKey } = unlock( blockEditorPrivateApis ); + const name = 'core/italic'; const title = __( 'Italic' ); @@ -18,6 +26,7 @@ export const italic = { title, tagName: 'em', className: null, + [ essentialFormatKey ]: true, edit( { isActive, value, onChange, onFocus } ) { function onToggle() { onChange( toggleFormat( value, { type: name, title } ) ); diff --git a/packages/format-library/src/link/index.js b/packages/format-library/src/link/index.js index 508ed4655a6695..a8f9bc58bc3f0b 100644 --- a/packages/format-library/src/link/index.js +++ b/packages/format-library/src/link/index.js @@ -16,6 +16,7 @@ import { isURL, isEmail, isPhoneNumber } from '@wordpress/url'; import { RichTextToolbarButton, RichTextShortcut, + privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { decodeEntities } from '@wordpress/html-entities'; import { link as linkIcon } from '@wordpress/icons'; @@ -26,6 +27,9 @@ import { speak } from '@wordpress/a11y'; */ import InlineLinkUI from './inline'; import { isValidHref } from './utils'; +import { unlock } from '../lock-unlock'; + +const { essentialFormatKey } = unlock( blockEditorPrivateApis ); const name = 'core/link'; const title = __( 'Link' ); @@ -228,6 +232,7 @@ export const link = { target: 'target', rel: 'rel', }, + [ essentialFormatKey ]: true, __unstablePasteRule( value, { html, plainText } ) { const pastedText = ( html || plainText ) .replace( /<[^>]+>/g, '' )