问题
I'm trying to create a new entity for Wagtail
's Draftail
text editor (based on draft.js
).
I've started off with the example here: http://docs.wagtail.io/en/v2.5.1/advanced_topics/customisation/extending_draftail.html#creating-new-entities
I have the extension working as a proof of concept, in that it will take a selection and wrap it in a footnote
entity.
However, I need to be able to preserve existing link
entities, so that they become children of the new footnote
entities.
I've tried various combinations of code (including the draft.js
CompositeDecorator
), but cannot get this to work.
// FootnoteSource.js
const React = window.React;
const Modifier = window.DraftJS.Modifier;
const EditorState = window.DraftJS.EditorState;
class FootnoteSource extends React.Component {
componentDidMount() {
const { editorState, entityType, onComplete } = this.props;
const content = editorState.getCurrentContent();
// // Uses the Draft.js API to create a new entity with the right data.
const contentWithEntity = content.createEntity(entityType.type, 'MUTABLE', {});
const entityKey = contentWithEntity.getLastCreatedEntityKey();
const editorStateWithEntity = Modifier.applyEntity(
editorState.getCurrentContent(),
editorState.getSelection(),
entityKey
)
const nextState = EditorState.push(editorState, editorStateWithEntity, 'apply-entity');
onComplete(nextState);
}
render() {
return null;
}
}
export default FootnoteSource;
// Footnote.js
import PropTypes from 'prop-types';
const Footnote = (props) => {
const { children, entityKey, contentState } = props;
const data = contentState.getEntity(entityKey).getData();
return (
<span
role="button"
className="FootnoteEntity"
>
<span class="FootnoteEntity__label" aria-hidden="true">[Footnote]</span>
{children}
</span>
);
};
Footnote.propTypes = {
entityKey: PropTypes.string.isRequired,
contentState: PropTypes.object.isRequired,
};
export default Footnote;
My understanding is that the children
props of the Footnote
decorator is just text. Is there a way for this to be an entity node/map that I can attach to the new entity as children?
回答1:
Unfortunately at the moment Draft.js doesn’t supported nested / children entities – any bit of content in the editor can only have a single entity attached to it. This makes sense for things like links, but is very problematic when implementing "highlights" / "comments" features, or footnotes, where these could ideally be added on arbitrary text, regardless of whether it has links or not.
You have a few alternatives – none of which are specific to Draftail/Wagtail, so you should be able to find references for other Draft.js-based projects that might have done these to circumvent this limitation. Unfortunately these are quite advanced implementations, with many gotchas that might make them impractical / too costly for your needs. I’d also suggest you assess whether you really do want to implement this (see below).
Add a way for footnote data to be stored on the links
This way the text that has a link will only have a single entity, but you’ll still have your footnote information attached to said text. You could also have a separate footnote entity for things that have no link but could be footnotes. Or make a LINK_AND_OR_FOOTNOTE
entity that does both things at once.
I think this could work relatively well if you’ll only be ever be adding footnotes on whole links, not just sub-parts of them. Within the context of Wagtail, the LINK entity is predefined – this solution means you’d have to redefine your own, which might not be the best approach.
Store footnotes as styles instead of entities
I’m not entirely sure how you’re managing footnote data, but if within text these are just references to "bottom of the page" content, they could potentially be built as styles (like BOLD
, ITALIC
, etc).
- The footnote’s reference would be stored in the style type – so for example
FOOTNOTE_UUID_OF_12h3h53_FOOTNOTE_HERE
. - Then implement custom styles rendering of these based on the prefix
FOOTNOTE_
alone. - You will most likely also need to add logic in the "footnote picker" UI to prevent users from adding multiple footnotes on a single piece of text – unless that seems like a feature you need
I’ve seen Draft.js projects doing this in production, so I know it’s doable, but don’t have open-source examples / code to share unfortunately.
As a last option, I’d suggest you assess whether you do really need to have the footnotes on the text within the link. This would be much easier to build if they were inserted within text, relating to the text they are following, but attached on separate text. Footnotes would then be their own entity, still couldn’t be inserted within links, but could still relate to the text the link is on just by virtue of being shown next to it.
I think there is also a fair chance this would be easier to use for end users – it sounds like a usability issue for a footnote’s clickable/hoverable representation to be within a clickable/hoverable link
This is for example how Wikipedia makes its citations:
The implementation is much simpler because citations are inserted separately, and the end user experience is nice too because users can easily choose to interact with either the link or the footnote.
来源:https://stackoverflow.com/questions/56491813/create-a-new-entity-from-selection-in-draft-js-draftail-wrap-existing-text-a