在draft.js//Draftail 中从selection 创建一个新实体,包裹现有文本并链接实体 [英] Create a new entity from selection in draft.js / /Draftail, wrap existing text and link entities

查看:23
本文介绍了在draft.js//Draftail 中从selection 创建一个新实体,包裹现有文本并链接实体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为 WagtailDrafttail 文本编辑器(基于 draft.js)创建一个新实体.

我从这里的例子开始:

实现要简单得多,因为引用是单独插入的,最终用户体验也很好,因为用户可以轻松选择与链接或脚注进行交互.

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?

解决方案

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.

这篇关于在draft.js//Draftail 中从selection 创建一个新实体,包裹现有文本并链接实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆