Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 13 additions & 0 deletions docs/reference-guides/data/data-core-rich-text.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ Namespace: `core/rich-text`.

<!-- START TOKEN(Autogenerated selectors|../../../packages/rich-text/src/store/selectors.js) -->

### getDisabledFormatTypesForBlock

Returns the list of format type names disabled for a specific block type.

_Parameters_

- _state_ `Object`: Data state.
- _blockName_ `string`: Block name (e.g. 'core/heading').

_Returns_

- `string[]`: Disabled format type names for the block.

### getFormatType

Returns a format type by name.
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ export function RichTextWrapper(
__unstableDependencies: [ tagName ],
allowedFormats: adjustedAllowedFormats,
withoutInteractiveFormatting,
blockName,
__unstableFormatTypeHandlerContext: useMemo(
() => ( {
richTextIdentifier: identifier,
Expand Down
4 changes: 4 additions & 0 deletions packages/rich-text/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### New Feature

- Add `unregisterFormatTypeInBlock( blockName, formatName )` to disable a format type for a specific block type without affecting other blocks ([#77687](https://github.com/WordPress/gutenberg/pull/77687)).

## 7.44.0 (2026-04-15)

## 7.43.0 (2026-04-01)
Expand Down
23 changes: 23 additions & 0 deletions packages/rich-text/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,29 @@ _Returns_

- `WPFormat|undefined`: The previous format value, if it has been successfully unregistered; otherwise `undefined`.

### unregisterFormatTypeInBlock

Disables a format type for a specific block type. Unlike `unregisterFormatType`, which removes the format globally, this function hides the format only when editing a particular block type.

_Usage_

```js
import { unregisterFormatTypeInBlock } from '@wordpress/rich-text';

// Disable italic and bold formatting in heading blocks.
unregisterFormatTypeInBlock( 'core/heading', 'core/italic' );
unregisterFormatTypeInBlock( 'core/heading', 'core/bold' );
```

_Parameters_

- _blockName_ `string`: The name of the block type (e.g. 'core/heading').
- _formatName_ `string`: The name of the format type to disable (e.g. 'core/italic').

_Returns_

- `WPFormat|undefined`: The format settings if successfully disabled; otherwise `undefined`.

### useAnchor

This hook, to be used in a format type's Edit component, returns the active element that is formatted, or a virtual element for the selection range if no format is active. The returned value is meant to be used for positioning UI, e.g. by passing it to the `Popover` component via the `anchor` prop.
Expand Down
2 changes: 2 additions & 0 deletions packages/rich-text/src/hook/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ function useRichTextBase( {
export function useRichText( {
allowedFormats,
withoutInteractiveFormatting,
blockName,
onChange,
__unstableDependencies = [],
__unstableFormatTypeHandlerContext,
Expand All @@ -253,6 +254,7 @@ export function useRichText( {
} = useFormatTypes( {
allowedFormats,
withoutInteractiveFormatting,
blockName,
__unstableFormatTypeHandlerContext,
} );

Expand Down
25 changes: 24 additions & 1 deletion packages/rich-text/src/hook/use-format-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { useSelect, useDispatch } from '@wordpress/data';
*/
import { store as richTextStore } from '../store';

const EMPTY_ARRAY = [];

function formatTypesSelector( select ) {
return select( richTextStore ).getFormatTypes();
}
Expand Down Expand Up @@ -63,20 +65,36 @@ function getPrefixedSelectKeys( selected, prefix ) {
* @param {Object} options Options
* @param {Array} options.allowedFormats Allowed formats
* @param {boolean} options.withoutInteractiveFormatting Whether to clean the interactive formatting or not.
* @param {string} [options.blockName] Optional block type name used to filter out format
* types disabled for that block via `unregisterFormatTypeInBlock`.
* @param {Object} options.__unstableFormatTypeHandlerContext Context object passed to experimental format type methods.
*/
export function useFormatTypes( {
allowedFormats,
withoutInteractiveFormatting,
blockName,
__unstableFormatTypeHandlerContext,
} ) {
const allFormatTypes = useSelect( formatTypesSelector, [] );
const disabledFormatsForBlock = useSelect(
( select ) =>
blockName
? select( richTextStore ).getDisabledFormatTypesForBlock(
blockName
)
: EMPTY_ARRAY,
[ blockName ]
);
const formatTypes = useMemo( () => {
return allFormatTypes.filter( ( { name, interactive, tagName } ) => {
if ( allowedFormats && ! allowedFormats.includes( name ) ) {
return false;
}

if ( disabledFormatsForBlock.includes( name ) ) {
return false;
}

if (
withoutInteractiveFormatting &&
( interactive || interactiveContentTags.has( tagName ) )
Expand All @@ -86,7 +104,12 @@ export function useFormatTypes( {

return true;
} );
}, [ allFormatTypes, allowedFormats, withoutInteractiveFormatting ] );
}, [
allFormatTypes,
allowedFormats,
disabledFormatsForBlock,
withoutInteractiveFormatting,
] );
const keyedSelected = useSelect(
( select ) =>
formatTypes.reduce( ( accumulator, type ) => {
Expand Down
1 change: 1 addition & 0 deletions packages/rich-text/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export { toDom as __unstableToDom } from './to-dom';
export { toHTMLString } from './to-html-string';
export { toggleFormat } from './toggle-format';
export { unregisterFormatType } from './unregister-format-type';
export { unregisterFormatTypeInBlock } from './unregister-format-type-in-block';
export { createElement as __unstableCreateElement } from './create-element';

export { privateApis } from './private-apis';
Expand Down
40 changes: 40 additions & 0 deletions packages/rich-text/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,43 @@ export function removeFormatTypes( names ) {
names: Array.isArray( names ) ? names : [ names ],
};
}

/**
* Returns an action object used to disable a format type for a specific block type.
*
* Ignored from documentation as unregisterFormatTypeInBlock should be used instead from @wordpress/rich-text
*
* @ignore
*
* @param {string} blockName Block name (e.g. 'core/heading').
* @param {string} formatName Format type name (e.g. 'core/bold').
*
* @return {Object} Action object.
*/
export function disableFormatTypeInBlock( blockName, formatName ) {
return {
type: 'DISABLE_FORMAT_TYPE_IN_BLOCK',
blockName,
formatName,
};
}

/**
* Returns an action object used to re-enable a format type for a specific block type.
*
* Ignored from documentation as registerFormatTypeInBlock should be used instead from @wordpress/rich-text
*
* @ignore
*
* @param {string} blockName Block name (e.g. 'core/heading').
* @param {string} formatName Format type name (e.g. 'core/bold').
*
* @return {Object} Action object.
*/
export function enableFormatTypeInBlock( blockName, formatName ) {
return {
type: 'ENABLE_FORMAT_TYPE_IN_BLOCK',
blockName,
formatName,
};
}
40 changes: 39 additions & 1 deletion packages/rich-text/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,42 @@ export function formatTypes( state = {}, action ) {
return state;
}

export default combineReducers( { formatTypes } );
/**
* Reducer managing per-block disabled format types.
* State shape: { [blockName]: string[] }
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {Object} Updated state.
*/
export function disabledFormatTypesByBlock( state = {}, action ) {
switch ( action.type ) {
case 'DISABLE_FORMAT_TYPE_IN_BLOCK': {
const { blockName, formatName } = action;
const existing = state[ blockName ] || [];
if ( existing.includes( formatName ) ) {
return state;
}
return { ...state, [ blockName ]: [ ...existing, formatName ] };
}
case 'ENABLE_FORMAT_TYPE_IN_BLOCK': {
const { blockName, formatName } = action;
if ( ! state[ blockName ] ) {
return state;
}
const filtered = state[ blockName ].filter(
( name ) => name !== formatName
);
if ( filtered.length === 0 ) {
const { [ blockName ]: _removed, ...rest } = state;
return rest;
}
return { ...state, [ blockName ]: filtered };
}
}

return state;
}

export default combineReducers( { formatTypes, disabledFormatTypesByBlock } );
12 changes: 12 additions & 0 deletions packages/rich-text/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ export function getFormatTypeForBareElement( state, bareElementTagName ) {
);
}

/**
* Returns the list of format type names disabled for a specific block type.
*
* @param {Object} state Data state.
* @param {string} blockName Block name (e.g. 'core/heading').
*
* @return {string[]} Disabled format type names for the block.
*/
export function getDisabledFormatTypesForBlock( state, blockName ) {
return state.disabledFormatTypesByBlock[ blockName ] || [];
}

/**
* Gets the format type, if any, that can handle an element, given its classes.
*
Expand Down
31 changes: 30 additions & 1 deletion packages/rich-text/src/store/test/actions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/**
* Internal dependencies
*/
import { addFormatTypes, removeFormatTypes } from '../actions';
import {
addFormatTypes,
removeFormatTypes,
disableFormatTypeInBlock,
enableFormatTypeInBlock,
} from '../actions';

describe( 'actions', () => {
describe( 'addFormatTypes', () => {
Expand All @@ -27,4 +32,28 @@ describe( 'actions', () => {
expect( removeFormatTypes( names ) ).toEqual( expected );
} );
} );

describe( 'disableFormatTypeInBlock', () => {
it( 'should return the correct action object', () => {
expect(
disableFormatTypeInBlock( 'core/heading', 'core/italic' )
).toEqual( {
type: 'DISABLE_FORMAT_TYPE_IN_BLOCK',
blockName: 'core/heading',
formatName: 'core/italic',
} );
} );
} );

describe( 'enableFormatTypeInBlock', () => {
it( 'should return the correct action object', () => {
expect(
enableFormatTypeInBlock( 'core/heading', 'core/italic' )
).toEqual( {
type: 'ENABLE_FORMAT_TYPE_IN_BLOCK',
blockName: 'core/heading',
formatName: 'core/italic',
} );
} );
} );
} );
Loading
Loading