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

Try avoiding parse to get content of Editable #523

Merged
merged 7 commits into from
May 1, 2017
Merged

Conversation

ellatrix
Copy link
Member

This is just an experiment. Creating this branch to see what others think.

The current behaviour is DOM => HTML => DOM => VDOM (React). This change proposes DOM => VDOM (React) which would eliminate HTML => DOM parsing every time getContent is called.

WIP

@aduth aduth added the [Status] In Progress Tracking issues with work in progress label Apr 27, 2017
Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea. 👍 Seems everywhere we're using html-to-react we have access to a DOM node anyways, so a nice optimization to skip the step of converting the string to a DOM.

@@ -0,0 +1,29 @@
import { createElement } from 'react';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we'll need this in blocks/api/query.js as well, we'll probably want to place the file somewhere more generic. Kinda ties to #408, as it seems in addition to component components, we'll need common utilities as well.

That, or we could just commit to individually publishing npm modules for the utilities we create.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That, or we could just commit to individually publishing npm modules for the utilities we create.

If we don't depend on html-to-react anyway, I was thinking of just creating a separate package that has some options to handle nodes (like we need to ignore internal mce stuff).

@ellatrix
Copy link
Member Author

If this works well, I'll go ahead and create a package for it.

htmlToReactParser.parse( before ),
htmlToReactParser.parse( after )
map( before, nodeToReact ),
hasAfter ? map( childNodes.slice( splitIndex ), nodeToReact ) : ''
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the fallback value shouldn't be a string here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null?

@ellatrix
Copy link
Member Author

@aduth
Copy link
Member

aduth commented Apr 27, 2017

It's not obvious because we use flow there, but the signature for the return value of a matcher is ( node: Element ): mixed, so we could change it to:

export function children( selector ) {
	return ( node ) => {
		let match = node;
		if ( selector ) {
			match = node.querySelector( selector );
		}

		return nodeToReact( match );
	};
}

@ellatrix
Copy link
Member Author

For me this seems to be working well now.

return node.nodeValue;
}

if ( node.getAttribute( 'data-mce-bogus' ) === 'all' ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the plan for handling this and the below key.startsWith( 'data-mce-' ) when we make the utility more generic?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's the next thing :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So right now you can pass the createElement callback, which means that you can filter stuff, there is no dependency on React, and you can use it with other React-like libraries.

const props = reduce( node.attributes, ( result, { name, value } ) => {
let key = camelCaseAttrMap[ name.replace( /[-:]/, '' ) ] || name;

if ( key.startsWith( 'data-mce-' ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use babel-polyfill, so we don't have access to ES2015 base type prototype methods like Array#includes or String#startsWith. This will error in IE11. We may consider incorporating the polyfill. It's not small though. But this is a common enough problem that it's probably worth considering since it's easy for a developer to overlook. That or some custom lint behaviors.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, didn't realise this.

match = node.querySelector( selector );
}

if ( match ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we just return nodeToReact( match );?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need the childNodes here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need the childNodes here?

Yeah, you're right.

@ellatrix
Copy link
Member Author

Just to clarify; I believe data-mce-bogus=all means that the whole element and its children should be ignored, data-mce-bogus means that the element itself should be ignored, but not its children, and any data-mce-* attributes should be ignored. Does this sound right @spocke?

@aduth
Copy link
Member

aduth commented Apr 28, 2017

Maybe an option could be created for nodeToReact to provide hooks for mapping nodes and attributes?

nodeListToReact( element, {
	mapNode: ( node ) => {
		switch ( node.getAttribute( 'data-mce-bogus' ) ) {
			case 'all': return null;
			case null: return node;
			default: return node.childNodes;
		}
	},
	mapAttribute: ( value, key ) => startsWith( 'data-mce-', key ) ? null : value
} );

@ellatrix
Copy link
Member Author

ellatrix commented Apr 28, 2017

You don't like the current solution? :)

@aduth
Copy link
Member

aduth commented Apr 28, 2017

Oh, I think I missed the transition to passing in createElement and thought you'd reimplemented a separate nodeListToReact in the Editable. At a glance, yeah, this seems like a nicer way to hook the element creation itself instead.

@ellatrix
Copy link
Member Author

Yeah, it's also nice that it's very flexible, you can use it with other React-like libraries.

@ellatrix
Copy link
Member Author

I added style support in the package, and this seems to be working well. Any concerns, thoughts? Mergeable?

@ellatrix ellatrix removed the [Status] In Progress Tracking issues with work in progress label Apr 28, 2017
Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a rebase, but otherwise looks good 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants