在 Draft JS 中实现自动完成,但没有像“@”这样的“触发器”

Posted

技术标签:

【中文标题】在 Draft JS 中实现自动完成,但没有像“@”这样的“触发器”【英文标题】:Implement Autocomplete in Draft JS but without a "trigger" like "@" 【发布时间】:2018-06-14 01:54:54 【问题描述】:

我想实现一个标签编辑器之类的东西。但是,它仅适用于那些标签,所以我希望用户看到自动完成建议弹出窗口,而无需输入诸如 @# 之类的内容,只需输入文本本身。

我有一些有点可以工作的东西,但是弹出窗口显示在屏幕上奇怪的位置:

当我第一次输入内容并出现弹出窗口时,它出现在屏幕左上角附近的某处 创建第一个实体后,当按 SPACE 并再次开始输入时,弹出窗口会出现在其直观位置右侧几个像素处(即在单词的第一个字母下方)

这里是此类知名编辑器的示例(虽然没有使用 Draft 实现),因此您可以更好地了解我想要实现的内容。

首先,这里是触发建议弹窗的函数:

private onChange(editorState: EditorState) 
  const content = editorState.getCurrentContent();
  const selection = editorState.getSelection();
  const currentBlock = content.getBlockForKey(selection.getAnchorKey());

  if (selection.isCollapsed()) 
    const blockText = currentBlock.getText();
    const wordMeta = getWordAt(blockText, selection.getAnchorOffset());
    const categoryRegex = /([\w]*)/;
    const matches = wordMeta.word.match(categoryRegex);
    const existingEntity = currentBlock.getEntityAt(wordMeta.begin);

    if (!existingEntity && matches) 
      const categorySearch = matches[1];
      const selection = window.getSelection();
      if (this.state.autoComplete.search !== categorySearch && selection.rangeCount > 0) 
        const range = selection.getRangeAt(0);
        const boundingRect = getRangeBoundingClientRect(range);

        this.setState((prevState: StateType) => 
          let state = 
            autoComplete: 
              active: true,
              search: categorySearch,
              searchMeta: 
                begin: wordMeta.begin,
                end: wordMeta.end,
              ,
            ,
            selectionRect: prevState.selectionRect,
          ;

          if (prevState.autoComplete.active === false) 
            state.selectionRect = boundingRect;
          

          return state;
        );
      
    
  

  this.props.onChange(editorState);

这里是getWordAt 函数:

function getWordAt(text: string, pos: number): WordMeta

  const left = text.slice(0, pos + 1).search(/\S+$/);
  const right = text.slice(pos).search(/\s/);

  if (right < 0) 
    return 
      word: text.slice(left),
      begin: left,
      end: text.length,
    ;
  

  return 
    word: text.slice(left, right + pos),
    begin: left,
    end: right + pos,
  ;

有什么更好的方法来处理弹出窗口的位置,甚至还有这种自动完成的策略?谢谢!

【问题讨论】:

如果它只是一个定位问题(而不是基于时间或行为),并且它在浏览器中是一致的,可能会调查只是添加一些 CSS 来强制弹出到理想的位置 @GregRozmarynowycz 这不仅仅是定位。 Facebook 提供的实体创建方法基于@-handles,但我的案例似乎无法使用该方法正确实现。我正在寻找一种无论如何都不会 hacky 并且会产生正确结果的方法! @Victor 不用输入@就可以显示自动提示了吗?现在你正面临定位问题?或者您需要自动建议和定位的解决方案? 两者都有!您在我的代码 sn-p 中看到的东西非常容易出错,并且以后很难扩展……这就是为什么我认为 Regex 解决方案不好所以我在这里问!但是,是的!我需要定位和自动建议方面的帮助 据我了解,您的问题是您不知道需要多少像素才能将建议框相对于输入字段向右推,以便它出现在用户光标的正下方?你见过这个:***.com/q/6930578? 【参考方案1】:

代替draft-js-typeahead - TypeaheadEditor 是一个包装草稿编辑器的反应组件。您可以使用 React-Autosuggest 满足要求的组件。它具有可与 React 元素原生配合使用的自定义渲染。它快速且非常容易定制。完全控制建议的呈现。

我们可以让它处理 JS 对象而不是纯字符串。

    onSuggestionSelected 属性是获取所选建议的回调 suggestionRenderer 方法接受建议并返回 React 标记

查看React-Autosuggest。

您可以通过custom block renderer 使用上述组件,可以在您的编辑器框架内引入复杂的丰富交互。

你必须打破你的头脑才能实现你想要的,它不是直截了当的。这是我的建议,您可以通过它实现它,但并不那么容易。

【讨论】:

react-autosuggest 似乎只支持一个开箱即用的选定项目,即使没有draft-js 也不难获得。我的问题来自需要更多可以选择的项目。 然后诸如使用左/右键在选定元素之间导航或使用 DeleteBackspace 删除它们或右键单击并复制等行为会极难达到。这就是我想用 DraftJS 实现这一点的原因,Facebook 团队已经以非常成功的方式实现了这些行为 但是,您可以实现如下操作: - 用户从自动建议中选择一个项目。 - 在选择时,将所选项目添加到在自动建议之外维护的列表中。 - 选择项目后,清除输入,以便用户可以将另一个项目添加到列表中。 有一种方法可以通过react-autosuggest 使用renderInputComponent 函数来实现它——仅在您需要自定义输入的呈现时使用。我认为你应该试一试。 是的,你是对的,你应该使用 DraftJS,但是你可以只为那个元素使用这个组件来实现你想要的。

以上是关于在 Draft JS 中实现自动完成,但没有像“@”这样的“触发器”的主要内容,如果未能解决你的问题,请参考以下文章

尝试在 wordpress 自定义页面中实现 jQuery 以进行搜索自动完成但不工作

如何在 GAE 中实现自动建议(自动完成)功能

如何在.net core mvc的搜索框中实现自动完成功能?

在Pytorch中实现WNGrad?

shell脚本中实现自动判断用户有无密码

Jquery UI 和 Bootstrap JS 冲突使自动完成停止工作