botframework Webchat React 中的建议列表问题

IT技术 reactjs .net-core autocomplete react-redux botframework
2021-05-14 22:09:49

我刚刚使用 react.js 在我的机器人框架网络聊天(v-4)中添加了自动建议/自动完成功能。但是有一些问题我需要解决;

1.)在获得建议的同时,我想让我在网络聊天中输入的单词在结果建议列表中加粗。我这样做了,但我现在面临的问题是,它只是将第一个字母设为粗体(如您在图像中所见),即使它在句子中,我也想将其设为粗体。

2.)当我从建议列表中选择一个选项时,它必须关闭。它关闭其他选项,但所选选项不是。(如图所示)。我也想关闭它。

3.)我想制作我的向上/向下箭头以从建议列表中选择选项。

请在以下链接中找到图片,

1个回答

对于您的第一个问题,可能有两种方法可以做到这一点。要以 React 方式执行此操作,您可以使用indexOf在建议中查找用户文本的索引,然后将文本拆分为多个 React 元素,其中一个以粗体显示。如果您想像replace目前一样使用,那么这可能是使用的好机会dangerouslySetInnerHTML

<div className="SuggestionParent" id="Suggestion1">
  {this.state.suggestions.map(suggestion => (
    <div className="Suggestion" onClick={this.handleSuggestionClick} >
      <div dangerouslySetInnerHTML={this.getSuggestionHtml(suggestion)} />
    </div>
  ))}
</div>

“危险”警告是因为您需要确保不允许用户提供任何可以进入内部 HTML 的潜在值,否则他们可能会注入脚本标签。只要您的建议是从固定数据库中提取的并且数据是安全的,那么您可能没问题。否则,您将不得不清理 HTML,在这种情况下,根本不使用可能会更容易dangerouslySetInnerHTML如果我们确实设置了内部 HTML,那么我们可以使用replace将 HTML 标签直接应用于字符串:

getSuggestionHtml(suggestion) {
  const lowerCaseSuggestion = suggestion.toLowerCase();
  return {
    __html: lowerCaseSuggestion.includes(this.state.suggestionTypedText) ? lowerCaseSuggestion
      .replace(this.state.suggestionTypedText, `<b>${this.state.suggestionTypedText}</b>`) : lowerCaseSuggestion
  };
}

第二个问题,你说你已经解决了。我可以看到您正在使用布尔开关来暂时关闭您响应 WEB_CHAT/SET_SEND_BOX 操作的方式。

对于您的第三个问题,在确定您的 UI 将如何工作时,您必须问自己很多设计注意事项,例如“如果用户在使用箭头键时将鼠标悬停在建议上会发生什么? ” 和“在用户按下 Enter 键之前,是否应该在发送框中预览突出显示的建议?” 我希望找到一个预先存在的 React 自动完成组件,您可以使用它而不是构建自己的组件,因为这已经解决了所有这些潜在的陷阱。不幸的是,两个著名的 React 自动完成包(这里这里)都有两个相同的问题:

  1. 他们目前没有得到维护
  2. 目标输入包含在组件中,因此您无法将组件连接到预先存在的输入。

但是,它们都是开源的,因此我们可以在它们之后为我们自己的自动完成功能建模。我将指导您完成基本功能,您可以根据需要进行扩展。

键盘事件通常在 React 中使用onKeyDown属性处理我把它放在一个包含网络聊天和你的建议父元素的元素上:

<div className={ROOT_CSS} onKeyDown={this.handleKeyDown.bind(this)}>
  <div className={WEB_CHAT_CSS + ''}>
    <ReactWebChat

这将处理所有按键操作,因此您需要一种方法来路由到正确键的功能。您可以使用switch语句,但react-autocomplete的源代码使用查找对象,我认为这很聪明。

keyDownHandlers = {
  ArrowDown(event) {
    this.moveHighlight(event, 1);
  },
  ArrowUp(event) {
    this.moveHighlight(event, -1);
  },
  Enter(event) {
    const {suggestions} = this.state;
    if (!suggestions.length) {
      // menu is closed so there is no selection to accept -> do nothing
      return
    }
    event.preventDefault()
    this.applySuggestion(suggestions[this.state.highlightedIndex]);
  },
}

handleKeyDown(event) {
  if (this.keyDownHandlers[event.key])
  this.keyDownHandlers[event.key].call(this, event)
}

我已将向上和向下箭头的功能集中到一个功能中:moveHighlight. 您将需要在您的状态中定义一个新属性,以跟踪键盘选择了哪个建议。我保留了highlightedIndexreact自动完成的名称

moveHighlight(event, direction) {
  event.preventDefault();
  const { highlightedIndex, suggestions } = this.state;
  if (!suggestions.length) return;
  let newIndex = (highlightedIndex + direction + suggestions.length) % suggestions.length;
  if (newIndex !== highlightedIndex) {
    this.setState({
      highlightedIndex: newIndex,
    });
  }
}

对于应用建议的回车键,您需要集中您的功能,以便它的工作方式与鼠标单击相同。

async handleSuggestionClick(event) {
  await this.applySuggestion(event.currentTarget.textContent);
}

async applySuggestion(newValue) {
  await this.setState({ typingChecking: "false", suggestions: [], highlightedIndex: 0 });
  this.state.suggestionCallback.dispatch({
    type: 'WEB_CHAT/SET_SEND_BOX',
    payload: {
      text: newValue,
    }
  });
  await this.setState({ typingChecking: "true" });
}

最后,确保该highlightedIndex属性用于以不同方式呈现突出显示的索引。

getSuggestionCss(index) {
  return index === this.state.highlightedIndex ? HIGHLIGHTED_CSS : SUGGESTION_CSS;
}

. . .

<div className="SuggestionParent" id="Suggestion1">
  {this.state.suggestions.map((suggestion, index) => (
    <div className={this.getSuggestionCss(index)} key={index} onClick={this.handleSuggestionClick} >
      <div dangerouslySetInnerHTML={this.getSuggestionHtml(suggestion)} />
    </div>
  ))}
</div>