【问题标题】:Context aware copy and paste in react app上下文感知复制和粘贴到反应应用程序
【发布时间】:2021-06-12 14:47:07
【问题描述】:

我正在构建一个 react 应用程序,它允许用户复制和粘贴文本(从一个输入字段到另一个输入字段)以及复制列表中的列表项。

现在我想支持这两种用例的键盘快捷键。我尝试使用MouseTrap(也尝试hotkeys)覆盖默认行为,只要我只复制文本或列表项,它就可以正常工作。但我还没有设法支持上下文感知复制粘贴。我希望复制命令在列表项获得焦点时复制列表项(或鼠标光标在列表区域内),并在列表未获得焦点时复制文本。

让事情变得更糟的是:我的页面没有项目列表,所以我只想拥有默认的复制粘贴文本行为。

我尝试了以下方法:

  1. 在 App 组件的 componentDidMount 中挂接热键
  2. 在 ListView 组件的 componentDidMount 中挂接热键

当我按下 ctrl+c 时,两个组件都会触发事件,即使我返回 false(这应该会阻止事件冒泡),它也会在两个组件中触发。 我做错了什么?

// ListView.js
componentDidMount() {
  hotkeys('cmd+c,cmd+v', 'TestView', this.onHotKey)
}
onHotKey = (event, handler) => {
  switch (handler.key) {
    case 'cmd+c':
      console.log('Testview: COPY!')
      break
    case 'cmd+v':
      console.log('Testview: PASTE!')
      break
  }
  event.preventDefault()
  return false
}

// App.js
componentDidMount = () => {
  hotkeys('cmd+c,cmd+v', this.onHotKey);
}
onHotKey = (event, handler) => {
  switch (handler.key) {
    case 'cmd+c': console.log('App: COPY!')
      break;
    case 'cmd+v': console.log('App: PASTE!')
      break;
  }
}

【问题讨论】:

    标签: javascript reactjs hotkeys


    【解决方案1】:

    让我们把你的问题分成几部分。

    1. 您希望复制在所有一般情况下都能按预期工作
    2. 您想要覆盖默认行为,并且如果 list 获得焦点 - 复制列表项
    3. 您想要覆盖默认行为,并且如果 列表项 获得焦点 - 复制获得焦点的项

    您描述的文本输入行为是通用的,除非您想以某种方式对其进行修改,否则我将其省略。

    假设您的元素已经可以聚焦(具有tabIndex 属性),您对每个项目和整个列表都有以下状态:“聚焦”和“不聚焦”。要检测从一种状态到另一种状态的变化,您可以使用处理“模糊”和“焦点”事件的事件侦听器。有一个问题是,来自 item 的事件会冒泡到列表中,因此,在我的示例中,我只使用列表中的侦听器。您可能需要更细粒度的事件侦听器附件(您甚至可以将其转换为 HOC)。

    您必须处理的另一件事是您打算如何存储复制和粘贴的信息。如果您将它存储在我在示例中所做的状态中,用户将失去复制某些内容并粘贴到您的应用程序之外的能力。此外,在我的示例中,我将复制和粘贴结合起来,因此只有在聚焦其中一个元素时才能粘贴。您可能希望 paste 通用。您可以为此使用Clipboard API 或已弃用的execCommand

    最后,由于我使用的是 Windows,cmd 不起作用,所以我将其更改为 ctrl

    现在举个例子:

    import { Component } from "react";
    import "./styles.css";
    import hotkeys from "hotkeys-js";
    
    class List extends Component {
      // this is bad place to store copied values. Example only!
      state = {
        copiedText: ""
      };
      // this is your code, modified to store elements inner text
      onHotKey = (event, handler) => {
        switch (handler.key) {
          case "ctrl+c": {
            console.log("Copy", document.activeElement.innerText);
            this.setState({ copiedText: document.activeElement.innerText });
            break;
          }
          case "ctrl+v": {
            console.log("Paste:", this.state.copiedText);
            break;
          }
          default:
            break;
        }
      };
    
      onFocus = (event) => {
        // we stop bubbling to prevent something higher in the tree from setting it's own handler
        event.stopPropagation();
        // attaching hotkeys
        hotkeys("ctrl+c,ctrl+v", this.onHotKey);
      };
      /**
       * @param {React.FocusEvent<HTMLElement>} event
       */
      onBlur = (event) => {
        // we again stop event from bubbling
        event.stopPropagation();
        // and removing hotkey
        hotkeys.unbind("ctrl+c,ctrl+v");
      };
      componentWillUnmount() {
        // This is precaution. Without it fast refresh can break our page
        hotkeys.unbind("ctrl+c,ctrl+v");
      }
      render() {
        return (
          <ul tabIndex={0} onFocus={this.onFocus} onBlur={this.onBlur}>
            {this.props.children}
          </ul>
        );
      }
    }
    
    // ListItem just attaches tabIndex
    class ListItem extends Component {
      render() {
        return <li tabIndex={0}>{this.props.children}</li>;
      }
    }
    
    export default function App() {
      return (
        <div className="App">
          <List>
            <ListItem>First element</ListItem>
            <ListItem>Second element</ListItem>
          </List>
        </div>
      );
    }
    

    您可以在此处查看实时版本:https://codesandbox.io/s/mutable-bush-jfosr?file=/src/App.js:0-1752

    此示例仅供您开始。它不会解决您的所有问题,但它应该为您提供有关如何进一步进行的基本概述。您的任务非常复杂,因此您必须自己解决额外的挑战。

    【讨论】:

      猜你喜欢
      • 2017-02-08
      • 1970-01-01
      • 2019-07-11
      • 1970-01-01
      • 2011-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-01
      相关资源
      最近更新 更多