Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Editable Component #18361

Merged
merged 3 commits into from
Mar 19, 2020
Merged
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
4 changes: 4 additions & 0 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ _Returns_

Undocumented declaration.

<a name="Editable" href="#Editable">#</a> **Editable**

Renders an editable text input in which text formatting is not allowed.

<a name="FontSizePicker" href="#FontSizePicker">#</a> **FontSizePicker**

Undocumented declaration.
Expand Down
114 changes: 114 additions & 0 deletions packages/block-editor/src/components/editable/README.md
Original file line number Diff line number Diff line change
@@ -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 (
<Editable
className={ className }
value={ attributes.content }
onChange={ ( content ) => setAttributes( { content } ) }
/>
);
},

save( { attributes } ) {
return <Editable.Content value={ attributes.content } />;
}
} );
```
{% end %}
17 changes: 17 additions & 0 deletions packages/block-editor/src/components/editable/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Internal dependencies
*/
import RichText from '../rich-text';

function Editable( props ) {
return <RichText { ...props } __unstableDisableFormats />;
}

Editable.Content = ( { value = '', tagName: Tag = 'div', ...props } ) => {
return <Tag { ...props }>{ value }</Tag>;
};

/**
* Renders an editable text input in which text formatting is not allowed.
*/
export default Editable;
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
24 changes: 21 additions & 3 deletions packages/block-editor/src/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -83,6 +91,8 @@ function getAllowedFormats( { allowedFormats, formattingControls } ) {
return formattingControls.map( ( name ) => `core/${ name }` );
}

getAllowedFormats.EMPTY_ARRAY = [];

function RichTextWrapper(
{
children,
Expand Down Expand Up @@ -112,6 +122,8 @@ function RichTextWrapper(
style,
preserveWhiteSpace,
__unstableEmbedURLOnPaste,
__unstableDisableFormats: disableFormats,
disableLineBreaks,
...props
},
forwardedRef
Expand Down Expand Up @@ -207,6 +219,7 @@ function RichTextWrapper(
const adjustedAllowedFormats = getAllowedFormats( {
allowedFormats,
formattingControls,
disableFormats,
} );
const hasFormats =
! adjustedAllowedFormats || adjustedAllowedFormats.length > 0;
Expand Down Expand Up @@ -334,14 +347,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 );
}
Expand Down Expand Up @@ -511,6 +528,7 @@ function RichTextWrapper(
__unstableMarkAutomaticChange={ __unstableMarkAutomaticChange }
__unstableDidAutomaticChange={ didAutomaticChange }
__unstableUndo={ undo }
__unstableDisableFormats={ disableFormats }
style={ style }
preserveWhiteSpace={ preserveWhiteSpace }
disabled={ disabled }
Expand Down
19 changes: 19 additions & 0 deletions packages/block-library/src/site-title/edit.js
Original file line number Diff line number Diff line change
@@ -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 (
<Editable
tagName="h1"
placeholder={ __( 'Site Title' ) }
value={ title }
onChange={ setTitle }
disableLineBreaks
/>
);
}
19 changes: 19 additions & 0 deletions packages/rich-text/src/component/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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' ) {
Expand Down