From 0446ed1882c598ad6c34165f67cc1885c414eb40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Thu, 19 Mar 2020 10:18:48 +0100 Subject: [PATCH] Editable Component (#18361) * Text Component * Rename * Lint fixes --- packages/block-editor/README.md | 4 + .../src/components/editable/README.md | 114 ++++++++++++++++++ .../src/components/editable/index.js | 17 +++ packages/block-editor/src/components/index.js | 1 + .../src/components/rich-text/index.js | 24 +++- packages/block-library/src/site-title/edit.js | 19 +++ packages/rich-text/src/component/index.js | 19 +++ 7 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 packages/block-editor/src/components/editable/README.md create mode 100644 packages/block-editor/src/components/editable/index.js create mode 100644 packages/block-library/src/site-title/edit.js diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index e9ea7ce70d1cc0..574e77a1ddce82 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -231,6 +231,10 @@ _Returns_ Undocumented declaration. +# **Editable** + +Renders an editable text input in which text formatting is not allowed. + # **FontSizePicker** Undocumented declaration. diff --git a/packages/block-editor/src/components/editable/README.md b/packages/block-editor/src/components/editable/README.md new file mode 100644 index 00000000000000..38c7c89dc3ba5b --- /dev/null +++ b/packages/block-editor/src/components/editable/README.md @@ -0,0 +1,114 @@ +# `Editable` + +Renders an editable text input in which text formatting is not allowed. + +## Properties + +### `value: String` + +*Required.* String to make editable. + +### `onChange( value: String ): Function` + +*Required.* Called when the value changes. + +### `tagName: String` + +*Default: `div`.* The [tag name](https://www.w3.org/TR/html51/syntax.html#tag-name) of the editable element. Elements that display inline are not supported. + +### `disableLineBreaks: Boolean` + +*Optional.* `Text` won't insert line breaks on `Enter` if set to `true`. + +### `placeholder: String` + +*Optional.* Placeholder text to show when the field is empty, similar to the + [`input` and `textarea` attribute of the same name](https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/HTML5_updates#The_placeholder_attribute). + +### `keepPlaceholderOnFocus: Boolean` + +*Optional.* Show placeholder even when selected/focused, as long as there is no content. + +### `onSplit( value: String ): Function` + +*Optional.* Called when the content can be split, where `value` is a piece of content being split off. Here you should create a new block with that content and return it. Note that you also need to provide `onReplace` in order for this to take any effect. + +### `onReplace( blocks: Array ): Function` + +*Optional.* Called when the `Text` instance can be replaced with the given blocks. + +### `onMerge( forward: Boolean ): Function` + +*Optional.* Called when blocks can be merged. `forward` is true when merging with the next block, false when merging with the previous block. + +### `onRemove( forward: Boolean ): Function` + +*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. + +## Editable.Content + +`Text.Content` should be used in the `save` function of your block to correctly save text content. + +## Example + +{% codetabs %} +{% ES5 %} +```js +wp.blocks.registerBlockType( /* ... */, { + // ... + + attributes: { + content: { + source: 'html', + selector: 'div', + }, + }, + + edit: function( props ) { + return wp.element.createElement( wp.editor.Editable, { + className: props.className, + value: props.attributes.content, + onChange: function( content ) { + props.setAttributes( { content: content } ); + } + } ); + }, + + save: function( props ) { + return wp.element.createElement( wp.editor.Editable.Content, { + value: props.attributes.content + } ); + } +} ); +``` +{% ESNext %} +```js +const { registerBlockType } = wp.blocks; +const { Editable } = wp.editor; + +registerBlockType( /* ... */, { + // ... + + attributes: { + content: { + source: 'html', + selector: '.text', + }, + }, + + edit( { className, attributes, setAttributes } ) { + return ( + setAttributes( { content } ) } + /> + ); + }, + + save( { attributes } ) { + return ; + } +} ); +``` +{% end %} diff --git a/packages/block-editor/src/components/editable/index.js b/packages/block-editor/src/components/editable/index.js new file mode 100644 index 00000000000000..25fb330ea9a1e2 --- /dev/null +++ b/packages/block-editor/src/components/editable/index.js @@ -0,0 +1,17 @@ +/** + * Internal dependencies + */ +import RichText from '../rich-text'; + +function Editable( props ) { + return ; +} + +Editable.Content = ( { value = '', tagName: Tag = 'div', ...props } ) => { + return { value }; +}; + +/** + * Renders an editable text input in which text formatting is not allowed. + */ +export default Editable; diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 31365b395684a9..3467ef2cee5c25 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -47,6 +47,7 @@ export { RichTextToolbarButton, __unstableRichTextInputEvent, } from './rich-text'; +export { default as Editable } from './editable'; export { default as ToolSelector } from './tool-selector'; export { default as URLInput } from './url-input'; export { default as URLInputButton } from './url-input/button'; diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index 7fc7f32291b457..142c02e8ed9074 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -67,7 +67,15 @@ function getMultilineTag( multiline ) { return multiline === true ? 'p' : multiline; } -function getAllowedFormats( { allowedFormats, formattingControls } ) { +function getAllowedFormats( { + allowedFormats, + formattingControls, + disableFormats, +} ) { + if ( disableFormats ) { + return getAllowedFormats.EMPTY_ARRAY; + } + if ( ! allowedFormats && ! formattingControls ) { return; } @@ -83,6 +91,8 @@ function getAllowedFormats( { allowedFormats, formattingControls } ) { return formattingControls.map( ( name ) => `core/${ name }` ); } +getAllowedFormats.EMPTY_ARRAY = []; + function RichTextWrapper( { children, @@ -112,6 +122,8 @@ function RichTextWrapper( style, preserveWhiteSpace, __unstableEmbedURLOnPaste, + __unstableDisableFormats: disableFormats, + disableLineBreaks, ...props }, forwardedRef @@ -202,6 +214,7 @@ function RichTextWrapper( const adjustedAllowedFormats = getAllowedFormats( { allowedFormats, formattingControls, + disableFormats, } ); const hasFormats = ! adjustedAllowedFormats || adjustedAllowedFormats.length > 0; @@ -329,14 +342,18 @@ function RichTextWrapper( if ( multiline ) { if ( shiftKey ) { - onChange( insert( value, '\n' ) ); + if ( ! disableLineBreaks ) { + onChange( insert( value, '\n' ) ); + } } else if ( canSplit && isEmptyLine( value ) ) { splitValue( value ); } else { onChange( insertLineSeparator( value ) ); } } else if ( shiftKey || ! canSplit ) { - onChange( insert( value, '\n' ) ); + if ( ! disableLineBreaks ) { + onChange( insert( value, '\n' ) ); + } } else { splitValue( value ); } @@ -504,6 +521,7 @@ function RichTextWrapper( __unstableMarkAutomaticChange={ __unstableMarkAutomaticChange } __unstableDidAutomaticChange={ didAutomaticChange } __unstableUndo={ undo } + __unstableDisableFormats={ disableFormats } style={ style } preserveWhiteSpace={ preserveWhiteSpace } disabled={ disabled } diff --git a/packages/block-library/src/site-title/edit.js b/packages/block-library/src/site-title/edit.js new file mode 100644 index 00000000000000..382493e17c8c5d --- /dev/null +++ b/packages/block-library/src/site-title/edit.js @@ -0,0 +1,19 @@ +/** + * WordPress dependencies + */ +import { useEntityProp } from '@wordpress/core-data'; +import { __ } from '@wordpress/i18n'; +import { Editable } from '@wordpress/block-editor'; + +export default function SiteTitleEdit() { + const [ title, setTitle ] = useEntityProp( 'root', 'site', 'title' ); + return ( + + ); +} diff --git a/packages/rich-text/src/component/index.js b/packages/rich-text/src/component/index.js index eb0732a37658c4..baa8826f12879e 100644 --- a/packages/rich-text/src/component/index.js +++ b/packages/rich-text/src/component/index.js @@ -620,6 +620,11 @@ class RichText extends Component { * created. */ onChange( record, { withoutHistory } = {} ) { + if ( this.props.__unstableDisableFormats ) { + record.formats = Array( record.text.length ); + record.replacements = Array( record.text.length ); + } + this.applyRecord( record ); const { start, end, activeFormats = [] } = record; @@ -1005,8 +1010,17 @@ class RichText extends Component { format, __unstableMultilineTag: multilineTag, preserveWhiteSpace, + __unstableDisableFormats: disableFormats, } = this.props; + if ( disableFormats ) { + return { + text: value, + formats: Array( value.length ), + replacements: Array( value.length ), + }; + } + if ( format !== 'string' ) { return value; } @@ -1064,8 +1078,13 @@ class RichText extends Component { format, __unstableMultilineTag: multilineTag, preserveWhiteSpace, + __unstableDisableFormats: disableFormats, } = this.props; + if ( disableFormats ) { + return value.text; + } + value = this.removeEditorOnlyFormats( value ); if ( format !== 'string' ) {