Skip to content

Commit

Permalink
Send active formats to RCTAztecView so we can type new words in a format
Browse files Browse the repository at this point in the history
  • Loading branch information
Tug committed Dec 21, 2018
1 parent 566f4b3 commit 0c1d597
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 9 deletions.
57 changes: 48 additions & 9 deletions packages/editor/src/components/rich-text/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { View, Platform } from 'react-native';
import { Component, RawHTML } from '@wordpress/element';
import { withInstanceId, compose } from '@wordpress/compose';
import { BlockFormatControls } from '@wordpress/editor';
import { withSelect } from '@wordpress/data';
import {
getActiveFormat,
isEmpty,
create,
split,
Expand All @@ -29,6 +31,12 @@ const isRichTextValueEmpty = ( value ) => {
return ! value || ! value.length;
};

const gutenbergFormatNamesToAztec = {
'core/bold': 'bold',
'core/italic': 'italic',
'core/strikethrough': 'strikethrough',
};

export class RichText extends Component {
constructor() {
super( ...arguments );
Expand All @@ -40,7 +48,11 @@ export class RichText extends Component {
this.onFormatChange = this.onFormatChange.bind( this );
this.onSelectionChange = this.onSelectionChange.bind( this );
this.valueToFormat = this.valueToFormat.bind( this );
this.state = {};
this.state = {
start: 0,
end: 0,
formatPlaceholder: null,
};
}

/**
Expand All @@ -49,12 +61,12 @@ export class RichText extends Component {
* @return {Object} The current record (value and selection).
*/
getRecord() {
const { formatPlaceholder, start, end, lastValue } = this.state;
// Since we get the text selection from Aztec we need to be in sync with the HTML `value`
// Removing the leading or the trailing white spaces using `trim()` should make sure this is the case
const { formats, text } = this.formatToValue( this.props.value.trim() );
const { start, end } = this.state;
const { formats, text } = this.formatToValue( lastValue || this.props.value.trim() );

return { formats, text, start, end };
return { formats, formatPlaceholder, text, start, end };
}

/*
Expand Down Expand Up @@ -113,18 +125,32 @@ export class RichText extends Component {
onSplit( before, after );
}

valueToFormat( { formats, text } ) {
valueToFormat( { formats, formatPlaceholder, text } ) {
const value = toHTMLString( {
value: { formats, text },
value: { formats, formatPlaceholder, text },
multilineTag: this.multilineTag,
} );
// remove the outer root tags
return this.removeRootTagsProduceByAztec( value );
}

getActiveFormatNames( record ) {
const {
formatTypes,
} = this.props;

return formatTypes.map( ( { name } ) => name ).filter( ( name ) => {
return getActiveFormat( record, name ) !== undefined;
} ).map( ( name ) => gutenbergFormatNamesToAztec[ name ] );
}

onFormatChange( record ) {
const newContent = this.valueToFormat( record );
this.props.onChange( newContent );
this.setState( {
formatPlaceholder: record.formatPlaceholder,
lastValue: null,
} );
}

/*
Expand Down Expand Up @@ -195,12 +221,17 @@ export class RichText extends Component {
}
}

onSelectionChange( start, end ) {
onSelectionChange( start, end, text ) {
// `end` can be less than `start` on iOS
// Let's fix that here so `rich-text/slice` can work properly
const realStart = Math.min( start, end );
const realEnd = Math.max( start, end );
this.setState( { start: realStart, end: realEnd } );
this.setState( {
start: realStart,
end: realEnd,
formatPlaceholder: null,
lastValue: text,
} );
}

isEmpty() {
Expand Down Expand Up @@ -275,8 +306,8 @@ export class RichText extends Component {
isSelected,
} = this.props;

// Save back to HTML from React tree
const record = this.getRecord();
// Save back to HTML from React tree
const html = `<${ tagName }>${ this.valueToFormat( record ) }</${ tagName }>`;

return (
Expand All @@ -296,6 +327,7 @@ export class RichText extends Component {
onBlur={ this.props.onBlur }
onEnter={ this.onEnter }
onBackspace={ this.onBackspace }
activeFormats={ this.getActiveFormatNames( record ) }
onContentSizeChange={ this.onContentSizeChange }
onSelectionChange={ this.onSelectionChange }
isSelected={ isSelected }
Expand All @@ -317,6 +349,13 @@ RichText.defaultProps = {

const RichTextContainer = compose( [
withInstanceId,
withSelect( ( select ) => {
const { getFormatTypes } = select( 'core/rich-text' );

return {
formatTypes: getFormatTypes(),
};
} ),
] )( RichText );

RichTextContainer.Content = ( { value, format, tagName: Tag, ...props } ) => {
Expand Down
46 changes: 46 additions & 0 deletions packages/rich-text/src/get-active-format.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* External dependencies
*/

import { find } from 'lodash';

/**
* Gets the format object by type at the start of the selection. This can be
* used to get e.g. the URL of a link format at the current selection, but also
* to check if a format is active at the selection. Returns undefined if there
* is no format at the selection.
*
* @param {Object} value Value to inspect.
* @param {string} formatType Format type to look for.
*
* @return {?Object} Active format object of the specified type, or undefined.
*/
export function getActiveFormat( { formats, formatPlaceholder, start, end }, formatType ) {
if ( start === undefined ) {
return;
}

// if selection is not empty, get the first character format
if ( start !== end ) {
return find( formats[ start ], { type: formatType } );
}

// otherwise get the previous character format (or the next one at the beginning of the text)
const previousLetterFormat = find( formats[ start > 0 ? start - 1 : start ], { type: formatType } );

if ( previousLetterFormat ) {
return previousLetterFormat;
}

// if user picked a format but didn't write anything in this format yet return this format
if (
formatPlaceholder &&
formatPlaceholder.format &&
formatPlaceholder.index === start &&
formatPlaceholder.format.type === formatType
) {
return formatPlaceholder.format;
}

return undefined;
}

0 comments on commit 0c1d597

Please sign in to comment.