-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Support Prepare RichText tree #11595
Changes from 12 commits
dfbca5f
6612db8
614ceb4
604c1e0
880123b
759c6a2
ca8e50e
61b0e95
ca71e2f
88740cd
447d63a
aa6f609
89539fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,7 @@ import { | |
isCollapsed, | ||
} from '@wordpress/rich-text'; | ||
import { decodeEntities } from '@wordpress/html-entities'; | ||
import { withFilters } from '@wordpress/components'; | ||
|
||
/** | ||
* Internal dependencies | ||
|
@@ -238,6 +239,7 @@ export class RichText extends Component { | |
unwrapNode: ( node ) => !! node.getAttribute( 'data-mce-bogus' ), | ||
removeAttribute: ( attribute ) => attribute.indexOf( 'data-mce-' ) === 0, | ||
filterString: ( string ) => string.replace( TINYMCE_ZWSP, '' ), | ||
prepareEditableTree: this.props.prepareEditableTree, | ||
} ); | ||
} | ||
|
||
|
@@ -252,6 +254,7 @@ export class RichText extends Component { | |
element.setAttribute( 'data-mce-bogus', '1' ); | ||
return element; | ||
}, | ||
prepareEditableTree: this.props.prepareEditableTree, | ||
} ); | ||
} | ||
|
||
|
@@ -413,10 +416,7 @@ export class RichText extends Component { | |
const record = this.createRecord(); | ||
const transformed = this.patterns.reduce( ( accumlator, transform ) => transform( accumlator ), record ); | ||
|
||
// Don't apply changes if there's no transform. Content will be up to | ||
// date. In the future we could always let it flow back in the live DOM | ||
// if there are no performance issues. | ||
this.onChange( transformed, record === transformed ); | ||
this.onChange( transformed ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This worries me that our standard set of operations for a single input have suddenly become more non-performant. Am I wrong to be worried? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only thing that changes the difference between the created DOM tree by us will be applied to the live DOM. With most input, there will be no changes. |
||
} | ||
|
||
/** | ||
|
@@ -780,6 +780,22 @@ export class RichText extends Component { | |
record.end = length; | ||
this.applyRecord( record ); | ||
} | ||
|
||
// If any format props update, reapply value. | ||
const shouldReapply = Object.keys( this.props ).some( ( name ) => { | ||
if ( name.indexOf( 'format_' ) !== 0 ) { | ||
return false; | ||
} | ||
|
||
return Object.keys( this.props[ name ] ).some( ( subName ) => { | ||
return this.props[ name ][ subName ] !== prevProps[ name ][ subName ]; | ||
} ); | ||
} ); | ||
|
||
if ( shouldReapply ) { | ||
const record = this.formatToValue( value ); | ||
this.applyRecord( record ); | ||
} | ||
} | ||
|
||
formatToValue( value ) { | ||
|
@@ -809,6 +825,20 @@ export class RichText extends Component { | |
return value; | ||
} | ||
|
||
valueToEditableHTML( value ) { | ||
return unstableToDom( { | ||
value, | ||
multilineTag: this.multilineTag, | ||
multilineWrapperTags: this.multilineWrapperTags, | ||
createLinePadding( doc ) { | ||
const element = doc.createElement( 'br' ); | ||
element.setAttribute( 'data-mce-bogus', '1' ); | ||
return element; | ||
}, | ||
prepareEditableTree: this.props.prepareEditableTree, | ||
} ).body.innerHTML; | ||
} | ||
|
||
valueToFormat( { formats, text } ) { | ||
// Handle deprecated `children` and `node` sources. | ||
if ( this.usedDeprecatedChildrenSource ) { | ||
|
@@ -834,7 +864,6 @@ export class RichText extends Component { | |
const { | ||
tagName: Tagname = 'div', | ||
style, | ||
value, | ||
wrapperClassName, | ||
className, | ||
inlineToolbar = false, | ||
|
@@ -883,7 +912,7 @@ export class RichText extends Component { | |
getSettings={ this.getSettings } | ||
onSetup={ this.onSetup } | ||
style={ style } | ||
defaultValue={ value } | ||
defaultValue={ this.valueToEditableHTML( record ) } | ||
isPlaceholderVisible={ isPlaceholderVisible } | ||
aria-label={ placeholder } | ||
aria-autocomplete="list" | ||
|
@@ -973,6 +1002,7 @@ const RichTextContainer = compose( [ | |
}; | ||
} ), | ||
withSafeTimeout, | ||
withFilters( 'experimentalRichText' ), | ||
] )( RichText ); | ||
|
||
RichTextContainer.Content = ( { value, tagName: Tag, multiline, ...props } ) => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,8 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { isFunction } from 'lodash'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { select, dispatch } from '@wordpress/data'; | ||
import { select, dispatch, withSelect } from '@wordpress/data'; | ||
import { addFilter } from '@wordpress/hooks'; | ||
|
||
/** | ||
* Registers a new format provided a unique name and an object defining its | ||
|
@@ -45,13 +41,6 @@ export function registerFormatType( name, settings ) { | |
return; | ||
} | ||
|
||
if ( ! settings || ! isFunction( settings.edit ) ) { | ||
window.console.error( | ||
'The "edit" property must be specified and must be a valid function.' | ||
); | ||
return; | ||
} | ||
|
||
if ( | ||
typeof settings.tagName !== 'string' || | ||
settings.tagName === '' | ||
|
@@ -124,5 +113,24 @@ export function registerFormatType( name, settings ) { | |
|
||
dispatch( 'core/rich-text' ).addFormatTypes( settings ); | ||
|
||
if ( | ||
settings.__experimentalCreatePrepareEditableTree && | ||
settings.__experimentalGetPropsForEditableTreePreparation | ||
) { | ||
addFilter( 'experimentalRichText', name, ( OriginalComponent ) => { | ||
return withSelect( ( sel ) => ( { | ||
[ `format_${ name }` ]: settings.__experimentalGetPropsForEditableTreePreparation( sel ), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can simplify it to: return withSelect( ( select, ownProps ) => ( {
prepareEditableTree: [
...ownProps.prepareEditableTree,
settings.__experimentalCreatePrepareEditableTree( settings.__experimentalGetPropsForEditableTreePreparation( select ) ),
],
} ) )( OriginalComponent ); Updated: I thought settings.__experimentalGetPropsForEditableTreePreparation returns a boolean value. It still has this issue that it will create a new reference each time props change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ☝️ this way you will have only one wrapping HOC instead of two. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried this, it doesn't behave exactly the same. |
||
} ) )( ( props ) => ( | ||
<OriginalComponent | ||
{ ...props } | ||
prepareEditableTree={ [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are generating a new array here on each render, I wonder if this is the source of the rerendering happening in all RichText. I wonder if we could memoize it somehow in Anyway, it doesn't feel unsolvable, so for the sake of iterations, I'm fine leaving this optimisation for a follow-up. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could it also be creating a new object in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we create an issue for this one? |
||
...( props.prepareEditableTree || [] ), | ||
settings.__experimentalCreatePrepareEditableTree( props[ `format_${ name }` ] ), | ||
] } | ||
/> | ||
) ); | ||
} ); | ||
} | ||
|
||
return settings; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,11 +58,7 @@ exports[`Quote can be converted to paragraphs and renders a paragraph for the ci | |
<!-- /wp:paragraph -->" | ||
`; | ||
|
||
exports[`Quote can be converted to paragraphs and renders a void paragraph if both the cite and quote are void 1`] = ` | ||
"<!-- wp:paragraph --> | ||
<p></p> | ||
<!-- /wp:paragraph -->" | ||
`; | ||
exports[`Quote can be converted to paragraphs and renders a void paragraph if both the cite and quote are void 1`] = `""`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did this change? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Why did this change? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have no idea tbh. But now this is true, where it wasn't before: "Quote can be converted to paragraphs and renders a void paragraph if both the cite and quote are void". |
||
|
||
exports[`Quote can be converted to paragraphs and renders one paragraph block per <p> within quote 1`] = ` | ||
"<!-- wp:paragraph --> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these changes here normal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it was actually wrong before. We're expecting a padded multiline element.