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

RichText: change value to have separate keys for line and object formats #13948

Merged
merged 18 commits into from
Mar 13, 2019
Merged
13 changes: 10 additions & 3 deletions packages/block-editor/src/components/rich-text/format-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { withSelect } from '@wordpress/data';
import { Fragment } from '@wordpress/element';
import { getActiveFormat } from '@wordpress/rich-text';
import { getActiveFormat, getActiveObject } from '@wordpress/rich-text';

const FormatEdit = ( { formatTypes, onChange, value } ) => {
return (
Expand All @@ -15,13 +15,20 @@ const FormatEdit = ( { formatTypes, onChange, value } ) => {

const activeFormat = getActiveFormat( value, name );
const isActive = activeFormat !== undefined;
const activeAttributes = isActive ? activeFormat.attributes || {} : {};
const activeObject = getActiveObject( value );
const isObjectActive = activeObject !== undefined;

return (
<Edit
key={ name }
isActive={ isActive }
activeAttributes={ activeAttributes }
activeAttributes={
isActive ? activeFormat.attributes || {} : {}
}
isObjectActive={ isObjectActive }
activeObjectAttributes={
isObjectActive ? activeObject.attributes || {} : {}
}
value={ value }
onChange={ onChange }
/>
Expand Down
22 changes: 13 additions & 9 deletions packages/block-editor/src/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@ export class RichText extends Component {
* @return {Object} The current record (value and selection).
*/
getRecord() {
const { formats, text } = this.formatToValue( this.props.value );
const { formats, replacements, text } = this.formatToValue( this.props.value );
const { start, end, selectedFormat } = this.state;

return { formats, text, start, end, selectedFormat };
return { formats, replacements, text, start, end, selectedFormat };
}

createRecord() {
Expand Down Expand Up @@ -394,13 +394,17 @@ export class RichText extends Component {
}

let { selectedFormat } = this.state;
const { formats, text, start, end } = this.createRecord();
const { formats, replacements, text, start, end } = this.createRecord();

if ( this.formatPlaceholder ) {
formats[ this.state.start ] = formats[ this.state.start ] || [];
formats[ this.state.start ].push( this.formatPlaceholder );
selectedFormat = formats[ this.state.start ].length;
} else if ( selectedFormat ) {
selectedFormat = this.formatPlaceholder.length;

if ( selectedFormat > 0 ) {
formats[ this.state.start ] = this.formatPlaceholder;
} else {
delete formats[ this.state.start ];
}
} else if ( selectedFormat > 0 ) {
const formatsBefore = formats[ start - 1 ] || [];
const formatsAfter = formats[ start ] || [];

Expand All @@ -411,12 +415,13 @@ export class RichText extends Component {
}

source = source.slice( 0, selectedFormat );

formats[ this.state.start ] = source;
} else {
delete formats[ this.state.start ];
}

const change = { formats, text, start, end, selectedFormat };
const change = { formats, replacements, text, start, end, selectedFormat };

this.onChange( change, {
withoutHistory: true,
Expand Down Expand Up @@ -984,7 +989,6 @@ export class RichText extends Component {
return toHTMLString( {
value,
multilineTag: this.multilineTag,
multilineWrapperTags: this.multilineWrapperTags,
ellatrix marked this conversation as resolved.
Show resolved Hide resolved
} );
}

Expand Down
21 changes: 10 additions & 11 deletions packages/format-library/src/image/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const image = {
}

static getDerivedStateFromProps( props, state ) {
const { activeAttributes: { style } } = props;
const { activeObjectAttributes: { style } } = props;

if ( style === state.previousStyle ) {
return null;
Expand Down Expand Up @@ -79,8 +79,8 @@ export const image = {
}

render() {
const { value, onChange, isActive, activeAttributes } = this.props;
const { style } = activeAttributes;
const { value, onChange, isObjectActive, activeObjectAttributes } = this.props;
const { style } = activeObjectAttributes;
// Rerender PositionedAtSelection when the selection changes or when
// the width changes.
const key = value.start + style;
Expand All @@ -91,7 +91,7 @@ export const image = {
icon={ <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><Path d="M4 16h10c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v9c0 1.1.9 2 2 2zM4 5h10v9H4V5zm14 9v2h4v-2h-4zM2 20h20v-2H2v2zm6.4-8.8L7 9.4 5 12h8l-2.6-3.4-2 2.6z" /></SVG> }
title={ __( 'Inline Image' ) }
onClick={ this.openModal }
isActive={ isActive }
isActive={ isObjectActive }
/>
{ this.state.modal && <MediaUpload
allowedTypes={ ALLOWED_MEDIA_TYPES }
Expand All @@ -113,7 +113,7 @@ export const image = {
return null;
} }
/> }
{ isActive && <PositionedAtSelection key={ key }>
{ isObjectActive && <PositionedAtSelection key={ key }>
<Popover
position="bottom center"
focusOnMount={ false }
Expand All @@ -125,20 +125,19 @@ export const image = {
onKeyPress={ stopKeyPropagation }
onKeyDown={ this.onKeyDown }
onSubmit={ ( event ) => {
const newFormats = value.formats.slice( 0 );
const newReplacements = value.replacements.slice();

newFormats[ value.start ] = [ {
newReplacements[ value.start ] = {
type: name,
object: true,
attributes: {
...activeAttributes,
...activeObjectAttributes,
style: `width: ${ this.state.width }px;`,
},
} ];
};

onChange( {
...value,
formats: newFormats,
replacements: newReplacements,
} );

event.preventDefault();
Expand Down
65 changes: 49 additions & 16 deletions packages/rich-text/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,26 @@ called without any input, an empty value will be created. If
`multilineTag` will be separated by two newlines. The optional functions can
be used to filter out content.

A value will have the following shape, which you are strongly encouraged not
to modify without the use of helper functions:

```js
{
text: string,
formats: Array,
replacements: Array,
?start: number,
?end: number,
}
```

As you can see, text and formatting are separated. `text` holds the text,
including any replacement characters for objects and lines. `formats`,
`objects` and `lines` are all sparse arrays of the same length as `text`. It
holds information about the formatting at the relevant text indices. Finally
`start` and `end` state which text indices are selected. They are only
provided if a `Range` was given.

**Parameters**

- **$1** `[Object]`: Optional named arguments.
Expand Down Expand Up @@ -93,9 +113,23 @@ is no format at the selection.

`(Object|undefined)`: Active format object of the specified type, or undefined.

### getActiveObject

[src/index.js#L11-L11](src/index.js#L11-L11)

Gets the active object, if there is any.

**Parameters**

- **value** `Object`: Value to inspect.

**Returns**

`?Object`: Active object, or undefined.

### getTextContent

[src/index.js#L13-L13](src/index.js#L13-L13)
[src/index.js#L14-L14](src/index.js#L14-L14)

Get the textual content of a Rich Text value. This is similar to
`Element.textContent`.
Expand All @@ -110,7 +144,7 @@ Get the textual content of a Rich Text value. This is similar to

### insert

[src/index.js#L21-L21](src/index.js#L21-L21)
[src/index.js#L22-L22](src/index.js#L22-L22)

Insert a Rich Text value, an HTML string, or a plain text string, into a
Rich Text value at the given `startIndex`. Any content between `startIndex`
Expand All @@ -130,7 +164,7 @@ none are provided.

### insertObject

[src/index.js#L24-L24](src/index.js#L24-L24)
[src/index.js#L25-L25](src/index.js#L25-L25)

Insert a format as an object into a Rich Text value at the given
`startIndex`. Any content between `startIndex` and `endIndex` will be
Expand All @@ -149,7 +183,7 @@ removed. Indices are retrieved from the selection if none are provided.

### isCollapsed

[src/index.js#L14-L14](src/index.js#L14-L14)
[src/index.js#L15-L15](src/index.js#L15-L15)

Check if the selection of a Rich Text value is collapsed or not. Collapsed
means that no characters are selected, but there is a caret present. If there
Expand All @@ -166,7 +200,7 @@ is no selection, `undefined` will be returned. This is similar to

### isEmpty

[src/index.js#L15-L15](src/index.js#L15-L15)
[src/index.js#L16-L16](src/index.js#L16-L16)

Check if a Rich Text value is Empty, meaning it contains no text or any
objects (such as images).
Expand All @@ -181,7 +215,7 @@ objects (such as images).

### join

[src/index.js#L16-L16](src/index.js#L16-L16)
[src/index.js#L17-L17](src/index.js#L17-L17)

Combine an array of Rich Text values into one, optionally separated by
`separator`, which can be a Rich Text value, HTML string, or plain text
Expand All @@ -198,7 +232,7 @@ string. This is similar to `Array.prototype.join`.

### registerFormatType

[src/index.js#L17-L17](src/index.js#L17-L17)
[src/index.js#L18-L18](src/index.js#L18-L18)

Registers a new format provided a unique name and an object defining its
behavior.
Expand All @@ -218,7 +252,7 @@ behavior.

### remove

[src/index.js#L19-L19](src/index.js#L19-L19)
[src/index.js#L20-L20](src/index.js#L20-L20)

Remove content from a Rich Text value between the given `startIndex` and
`endIndex`. Indices are retrieved from the selection if none are provided.
Expand All @@ -235,7 +269,7 @@ Remove content from a Rich Text value between the given `startIndex` and

### removeFormat

[src/index.js#L18-L18](src/index.js#L18-L18)
[src/index.js#L19-L19](src/index.js#L19-L19)

Remove any format object from a Rich Text value by type from the given
`startIndex` to the given `endIndex`. Indices are retrieved from the
Expand All @@ -254,7 +288,7 @@ selection if none are provided.

### replace

[src/index.js#L20-L20](src/index.js#L20-L20)
[src/index.js#L21-L21](src/index.js#L21-L21)

Search a Rich Text value and replace the match(es) with `replacement`. This
is similar to `String.prototype.replace`.
Expand All @@ -271,7 +305,7 @@ is similar to `String.prototype.replace`.

### slice

[src/index.js#L25-L25](src/index.js#L25-L25)
[src/index.js#L26-L26](src/index.js#L26-L26)

Slice a Rich Text value from `startIndex` to `endIndex`. Indices are
retrieved from the selection if none are provided. This is similar to
Expand All @@ -289,7 +323,7 @@ retrieved from the selection if none are provided. This is similar to

### split

[src/index.js#L26-L26](src/index.js#L26-L26)
[src/index.js#L27-L27](src/index.js#L27-L27)

Split a Rich Text value in two at the given `startIndex` and `endIndex`, or
split at the given separator. This is similar to `String.prototype.split`.
Expand All @@ -307,7 +341,7 @@ Indices are retrieved from the selection if none are provided.

### toggleFormat

[src/index.js#L29-L29](src/index.js#L29-L29)
[src/index.js#L30-L30](src/index.js#L30-L30)

Toggles a format object to a Rich Text value at the current selection.

Expand All @@ -322,7 +356,7 @@ Toggles a format object to a Rich Text value at the current selection.

### toHTMLString

[src/index.js#L28-L28](src/index.js#L28-L28)
[src/index.js#L29-L29](src/index.js#L29-L29)

Create an HTML string from a Rich Text value. If a `multilineTag` is
provided, text separated by a line separator will be wrapped in it.
Expand All @@ -332,15 +366,14 @@ provided, text separated by a line separator will be wrapped in it.
- **$1** `Object`: Named argements.
- **$1.value** `Object`: Rich text value.
- **$1.multilineTag** `[string]`: Multiline tag.
- **$1.multilineWrapperTags** `[Array]`: Tags where lines can be found if nesting is possible.

**Returns**

`string`: HTML string.

### unregisterFormatType

[src/index.js#L31-L31](src/index.js#L31-L31)
[src/index.js#L32-L32](src/index.js#L32-L32)

Unregisters a format.

Expand Down
18 changes: 7 additions & 11 deletions packages/rich-text/src/apply-format.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ import { normaliseFormats } from './normalise-formats';
* @return {Object} A new value with the format applied.
*/
export function applyFormat(
{ formats, text, start, end },
value,
format,
startIndex = start,
endIndex = end
startIndex = value.start,
endIndex = value.end
) {
const newFormats = formats.slice( 0 );
const newFormats = value.formats.slice( 0 );

// The selection is collapsed.
if ( startIndex === endIndex ) {
Expand All @@ -52,14 +52,10 @@ export function applyFormat(
// with the format applied.
} else {
const previousFormat = newFormats[ startIndex - 1 ] || [];
const hasType = find( previousFormat, { type: format.type } );

return {
formats,
text,
start,
end,
formatPlaceholder: hasType ? undefined : format,
...value,
ellatrix marked this conversation as resolved.
Show resolved Hide resolved
formatPlaceholder: [ ...previousFormat, format ],
};
}
} else {
Expand All @@ -68,7 +64,7 @@ export function applyFormat(
}
}

return normaliseFormats( { formats: newFormats, text, start, end } );
return normaliseFormats( { ...value, formats: newFormats } );
}

function applyFormats( formats, index, format ) {
Expand Down
Loading