diff --git a/package-lock.json b/package-lock.json index 0e27a7383cf08..16b1a4037b3d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3732,6 +3732,7 @@ "@wordpress/components": "file:packages/components", "@wordpress/compose": "file:packages/compose", "@wordpress/data": "file:packages/data", + "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/dom": "file:packages/dom", "@wordpress/element": "file:packages/element", "@wordpress/hooks": "file:packages/hooks", diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 2d97dd70be0c2..67c7e2a9725f7 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -1,5 +1,9 @@ ## Master +### New Features + +- Added a new `allowedFormats` prop to `RichText` to fine tune allowed formats. Deprecated the `formattingControls` prop in favour of this. Also added a `withoutInteractiveFormatting` to specifically disable format types that would insert interactive elements, which can not be nested. + ### Breaking Changes - `BlockEditorProvider` no longer renders a wrapping `SlotFillProvider` or `DropZoneProvider` (from `@wordpress/components`). For custom block editors, you should render your own as wrapping the `BlockEditorProvider`. A future release will include a new `BlockEditor` component for simple, standard usage. `BlockEditorProvider` will serve the simple purpose of establishing its own context for block editors. diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 5add09430524e..61c49f9d9a088 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -29,6 +29,7 @@ "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", "@wordpress/data": "file:../data", + "@wordpress/deprecated": "file:../deprecated", "@wordpress/dom": "file:../dom", "@wordpress/element": "file:../element", "@wordpress/hooks": "file:../hooks", diff --git a/packages/block-editor/src/components/rich-text/README.md b/packages/block-editor/src/components/rich-text/README.md index cca45bce3ec04..9cc0b3e93fccd 100644 --- a/packages/block-editor/src/components/rich-text/README.md +++ b/packages/block-editor/src/components/rich-text/README.md @@ -41,9 +41,13 @@ Render a rich [`contenteditable` input](https://developer.mozilla.org/en-US/docs *Optional.* Called when the block can be removed. `forward` is true when the selection is expected to move to the next block, false to the previous block. -### `formattingControls: Array` +### `allowedFormats: Array` -*Optional.* By default, all formatting controls are present. This setting can be used to fine-tune formatting controls. Possible items: `[ 'bold', 'italic', 'strikethrough', 'link' ]`. +*Optional.* By default, all registered formats are allowed. This setting can be used to fine-tune the allowed formats. Example: `[ 'core/bold', 'core/link' ]`. + +### `withoutInteractiveFormatting: Boolean` + +*Optional.* By default, all formatting controls are present. This setting can be used to remove formatting controls that would make content [interactive](https://html.spec.whatwg.org/multipage/dom.html#interactive-content). This is useful if you want to make content that is already interactive editable. ### `isSelected: Boolean` diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index 2c46ea36d61f7..8a3ef0550045b 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -27,6 +27,7 @@ import { } from '@wordpress/rich-text'; import { withFilters, IsolatedEventContainer } from '@wordpress/components'; import { createBlobURL } from '@wordpress/blob'; +import deprecated from '@wordpress/deprecated'; /** * Internal dependencies @@ -253,6 +254,24 @@ class RichTextWrapper extends Component { onReplace( [ block ] ); } + getAllowedFormats() { + const { allowedFormats, formattingControls } = this.props; + + if ( ! allowedFormats && ! formattingControls ) { + return; + } + + if ( allowedFormats ) { + return allowedFormats; + } + + deprecated( 'wp.blockEditor.RichText formattingControls prop', { + alternative: 'allowedFormats', + } ); + + return formattingControls.map( ( name ) => `core/${ name }` ); + } + render() { const { tagName, @@ -276,6 +295,9 @@ class RichTextWrapper extends Component { placeholder, keepPlaceholderOnFocus, // eslint-disable-next-line no-unused-vars + allowedFormats, + withoutInteractiveFormatting, + // eslint-disable-next-line no-unused-vars onRemove, // eslint-disable-next-line no-unused-vars onMerge, @@ -293,6 +315,8 @@ class RichTextWrapper extends Component { ...experimentalProps } = this.props; + const adjustedAllowedFormats = this.getAllowedFormats(); + const hasFormats = ! adjustedAllowedFormats || adjustedAllowedFormats.length > 0; let adjustedValue = originalValue; let adjustedOnChange = originalOnChange; @@ -317,6 +341,8 @@ class RichTextWrapper extends Component { className={ classnames( classes, className ) } placeholder={ placeholder } keepPlaceholderOnFocus={ keepPlaceholderOnFocus } + allowedFormats={ adjustedAllowedFormats } + withoutInteractiveFormatting={ withoutInteractiveFormatting } onEnter={ this.onEnter } onDelete={ this.onDelete } onPaste={ this.onPaste } @@ -341,12 +367,12 @@ class RichTextWrapper extends Component { onChange={ onChange } /> ) } - { isSelected && ! inlineToolbar && ( + { isSelected && ! inlineToolbar && hasFormats && ( ) } - { isSelected && inlineToolbar && ( + { isSelected && inlineToolbar && hasFormats && ( diff --git a/packages/block-library/src/button/edit.js b/packages/block-library/src/button/edit.js index 4e8f0dfedc326..a5f70204d6557 100644 --- a/packages/block-library/src/button/edit.js +++ b/packages/block-library/src/button/edit.js @@ -114,7 +114,7 @@ class ButtonEdit extends Component { placeholder={ __( 'Add text…' ) } value={ text } onChange={ ( value ) => setAttributes( { text: value } ) } - formattingControls={ [ 'bold', 'italic', 'strikethrough' ] } + withoutInteractiveFormatting className={ classnames( 'wp-block-button__link', { 'has-background': backgroundColor.color, diff --git a/packages/block-library/src/file/edit.js b/packages/block-library/src/file/edit.js index 2ab0baaa08f8b..616092ac82203 100644 --- a/packages/block-library/src/file/edit.js +++ b/packages/block-library/src/file/edit.js @@ -213,7 +213,7 @@ class FileEdit extends Component { value={ fileName } placeholder={ __( 'Write file name…' ) } keepPlaceholderOnFocus - formattingControls={ [] } // disable controls + withoutInteractiveFormatting onChange={ ( text ) => setAttributes( { fileName: text } ) } /> { showDownloadButton && @@ -223,7 +223,7 @@ class FileEdit extends Component { tagName="div" // must be block-level or else cursor disappears className={ 'wp-block-file__button' } value={ downloadButtonText } - formattingControls={ [] } // disable controls + withoutInteractiveFormatting placeholder={ __( 'Add text…' ) } keepPlaceholderOnFocus onChange={ ( text ) => setAttributes( { downloadButtonText: text } ) } diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js index 6525f5a82408f..40834ab63f3ab 100644 --- a/packages/block-library/src/search/edit.js +++ b/packages/block-library/src/search/edit.js @@ -14,7 +14,7 @@ export default function SearchEdit( { className, attributes, setAttributes } ) { aria-label={ __( 'Label text' ) } placeholder={ __( 'Add label…' ) } keepPlaceholderOnFocus - formattingControls={ [] } + withoutInteractiveFormatting value={ label } onChange={ ( html ) => setAttributes( { label: html } ) } /> @@ -34,7 +34,7 @@ export default function SearchEdit( { className, attributes, setAttributes } ) { aria-label={ __( 'Button text' ) } placeholder={ __( 'Add button text…' ) } keepPlaceholderOnFocus - formattingControls={ [] } + withoutInteractiveFormatting value={ buttonText } onChange={ ( html ) => setAttributes( { buttonText: html } ) } /> diff --git a/packages/rich-text/src/component/format-edit.js b/packages/rich-text/src/component/format-edit.js index 9a9d1945ba884..2bee3de123782 100644 --- a/packages/rich-text/src/component/format-edit.js +++ b/packages/rich-text/src/component/format-edit.js @@ -9,45 +9,74 @@ import { withSelect } from '@wordpress/data'; import { getActiveFormat } from '../get-active-format'; import { getActiveObject } from '../get-active-object'; -const FormatEdit = ( { formatTypes, onChange, value } ) => { - return ( - <> - { formatTypes.map( ( { name, edit: Edit } ) => { - if ( ! Edit ) { - return null; +/** + * Set of all interactive content tags. + * + * @see https://html.spec.whatwg.org/multipage/dom.html#interactive-content + */ +const interactiveContentTags = new Set( [ + 'a', + 'audio', + 'button', + 'details', + 'embed', + 'iframe', + 'input', + 'label', + 'select', + 'textarea', + 'video', +] ); + +const FormatEdit = ( { + formatTypes, + onChange, + value, + allowedFormats, + withoutInteractiveFormatting, +} ) => + formatTypes.map( ( { + name, + edit: Edit, + tagName, + } ) => { + if ( ! Edit ) { + return null; + } + + if ( allowedFormats && allowedFormats.indexOf( name ) === -1 ) { + return null; + } + + if ( + withoutInteractiveFormatting && + interactiveContentTags.has( tagName ) + ) { + return null; + } + + const activeFormat = getActiveFormat( value, name ); + const isActive = activeFormat !== undefined; + const activeObject = getActiveObject( value ); + const isObjectActive = activeObject !== undefined; + + return ( + + ); + } ); - const activeFormat = getActiveFormat( value, name ); - const isActive = activeFormat !== undefined; - const activeObject = getActiveObject( value ); - const isObjectActive = activeObject !== undefined; - - return ( - - ); - } ) } - - ); -}; - -export default withSelect( - ( select ) => { - const { getFormatTypes } = select( 'core/rich-text' ); - - return { - formatTypes: getFormatTypes(), - }; - } -)( FormatEdit ); +export default withSelect( ( select ) => ( { + formatTypes: select( 'core/rich-text' ).getFormatTypes(), +} ) )( FormatEdit ); diff --git a/packages/rich-text/src/component/index.js b/packages/rich-text/src/component/index.js index c2f76c4a22c29..34792ad1d2b3a 100644 --- a/packages/rich-text/src/component/index.js +++ b/packages/rich-text/src/component/index.js @@ -864,6 +864,8 @@ class RichText extends Component { __unstableAutocompleters: autocompleters, __unstableAutocomplete: Autocomplete = ( { children: ch } ) => ch( {} ), __unstableOnReplace: onReplace, + allowedFormats, + withoutInteractiveFormatting, } = this.props; // Generating a key that includes `tagName` ensures that if the tag @@ -908,7 +910,12 @@ class RichText extends Component { { MultilineTag ? { placeholder } : placeholder } } - { isSelected && } + { isSelected && } );