Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add write mode format filtering with essential flag
- Add withWriteModeFilter HOC to filter format edit components in write mode
- Add essential property to format registrations (default: false)
- Mark core formats (bold, italic, link, strikethrough) as essential
- Apply HOC in FormatEdit component to filter non-essential formats
- Maintain backward compatibility for existing formats
- Enable third-party plugins to opt formats into write mode via essential flag

This allows write mode to show only essential formatting controls while
maintaining full functionality in default mode.
  • Loading branch information
getdave committed Aug 6, 2025
commit a32322bdb73cd8c84ddf147bb0de76a6fe6bdec7
27 changes: 20 additions & 7 deletions packages/block-editor/src/components/rich-text/format-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { useContext, useMemo } from '@wordpress/element';
* Internal dependencies
*/
import BlockContext from '../block-context';

const DEFAULT_BLOCK_CONTEXT = {};
import { blockEditingModeKey } from '../block-edit/context';
import withWriteModeFilter from './with-write-mode-filter';

export const usesContextKey = Symbol( 'usesContext' );

Expand All @@ -24,27 +24,40 @@ function Edit( { onChange, onFocus, value, forwardedRef, settings } ) {

// Assign context values using the block type's declared context needs.
const context = useMemo( () => {
return usesContext
? Object.fromEntries(
// Always include blockEditingMode in context for write mode filtering
const baseContext = {
blockEditingMode: blockContext[ blockEditingModeKey ],
};

if ( usesContext ) {
return {
...baseContext,
...Object.fromEntries(
Object.entries( blockContext ).filter( ( [ key ] ) =>
usesContext.includes( key )
)
)
: DEFAULT_BLOCK_CONTEXT;
),
};
}

return baseContext;
}, [ usesContext, blockContext ] );

if ( ! EditFunction ) {
return null;
}

// Apply the write mode filter HOC
const FilteredEditFunction = withWriteModeFilter( EditFunction, settings );

const activeFormat = getActiveFormat( value, name );
const isActive = activeFormat !== undefined;
const activeObject = getActiveObject( value );
const isObjectActive =
activeObject !== undefined && activeObject.type === name;

return (
<EditFunction
<FilteredEditFunction
key={ name }
isActive={ isActive }
activeAttributes={ isActive ? activeFormat.attributes || {} : {} }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,51 +44,45 @@ const FormatToolbar = () => {
key={ format }
/>
) ) }
{ ! isWriteMode && (
<Slot name="RichText.ToolbarControls">
{ ( fills ) => {
if ( ! fills.length ) {
return null;
}
<Slot name="RichText.ToolbarControls">
{ ( fills ) => {
if ( ! fills.length ) {
return null;
}

const allProps = fills.map(
( [ { props } ] ) => props
);
const hasActive = allProps.some(
( { isActive } ) => isActive
);
const allProps = fills.map( ( [ { props } ] ) => props );
const hasActive = allProps.some(
( { isActive } ) => isActive
);

return (
<ToolbarItem>
{ ( toggleProps ) => (
<DropdownMenu
icon={ chevronDown }
/* translators: button label text should, if possible, be under 16 characters. */
label={ __( 'More' ) }
toggleProps={ {
...toggleProps,
className: clsx(
toggleProps.className,
{ 'is-pressed': hasActive }
),
description: __(
'Displays more block tools'
),
} }
controls={ orderBy(
fills.map(
( [ { props } ] ) => props
),
'title'
) }
popoverProps={ POPOVER_PROPS }
/>
) }
</ToolbarItem>
);
} }
</Slot>
) }
return (
<ToolbarItem>
{ ( toggleProps ) => (
<DropdownMenu
icon={ chevronDown }
/* translators: button label text should, if possible, be under 16 characters. */
label={ __( 'More' ) }
toggleProps={ {
...toggleProps,
className: clsx(
toggleProps.className,
{ 'is-pressed': hasActive }
),
description: __(
'Displays more block tools'
),
} }
controls={ orderBy(
fills.map( ( [ { props } ] ) => props ),
'title'
) }
popoverProps={ POPOVER_PROPS }
/>
) }
</ToolbarItem>
);
} }
</Slot>
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { useBlockEditingMode } from '../block-editing-mode';

/**
* Higher-Order Component that filters format edit components based on write mode.
*
* @param {Function} WrappedComponent The format edit component to wrap
* @param {Object} formatSettings The format settings including essential flag
* @return {Function} The wrapped component
*/
const withWriteModeFilter = ( WrappedComponent, formatSettings ) => {
return ( props ) => {
const blockEditingMode = useBlockEditingMode();
const isNavigationMode = useSelect(
( select ) => select( blockEditorStore ).isNavigationMode(),
[]
);
const isContentOnlyMode = blockEditingMode === 'contentOnly';
const isWriteMode = isNavigationMode && isContentOnlyMode;

// In write mode, only show essential formats
if ( isWriteMode && ! formatSettings?.essential ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that if essential is not defined then the default behaviour is not to show in Write Mode.

return null;
}

return <WrappedComponent { ...props } />;
};
};

export default withWriteModeFilter;
1 change: 1 addition & 0 deletions packages/format-library/src/bold/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const bold = {
title,
tagName: 'strong',
className: null,
essential: true,
edit( { isActive, value, onChange, onFocus } ) {
function onToggle() {
onChange( toggleFormat( value, { type: name, title } ) );
Expand Down
1 change: 1 addition & 0 deletions packages/format-library/src/code/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const code = {
title,
tagName: 'code',
className: null,
essential: false,
__unstableInputRule( value ) {
const BACKTICK = '`';
const { start, text } = value;
Expand Down
1 change: 1 addition & 0 deletions packages/format-library/src/italic/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const italic = {
title,
tagName: 'em',
className: null,
essential: true,
edit( { isActive, value, onChange, onFocus } ) {
function onToggle() {
onChange( toggleFormat( value, { type: name, title } ) );
Expand Down
1 change: 1 addition & 0 deletions packages/format-library/src/link/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ export const link = {
target: 'target',
rel: 'rel',
},
essential: true,
__unstablePasteRule( value, { html, plainText } ) {
const pastedText = ( html || plainText )
.replace( /<[^>]+>/g, '' )
Expand Down
1 change: 1 addition & 0 deletions packages/format-library/src/strikethrough/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const strikethrough = {
title,
tagName: 's',
className: null,
essential: true,
edit( { isActive, value, onChange, onFocus } ) {
function onClick() {
onChange( toggleFormat( value, { type: name, title } ) );
Expand Down
1 change: 1 addition & 0 deletions packages/format-library/src/underline/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const underline = {
attributes: {
style: 'style',
},
essential: false,
edit( { value, onChange } ) {
const onToggle = () => {
onChange(
Expand Down