在react应用程序中进行上下文感知复制和粘贴



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

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

更糟糕的是:我的页面上没有项目列表,所以我只想有默认的复制粘贴文本行为。

我尝试过以下内容:

  1. 应用程序组件的组件DidMount中的挂钩热键
  2. ListView组件的组件DidMount中的挂钩热键

当我按下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;
}
}

让我们将问题分解为多个部分。

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

您描述的文本输入行为是通用的,除非您想以某种方式修改它,否则我会将其排除在外。

假设您的元素已经是可聚焦的(具有tabIndex属性(,则对于每个项目和整个列表,您都具有以下状态:";"聚焦";以及";不集中";。为了检测从一种状态到另一种状态的变化,您可以使用处理";模糊";以及";焦点";事件。有一个陷阱是,项目中的事件将冒泡到列表中,所以,在我的示例中,我只使用列表中的侦听器。您可能需要更细粒度的事件侦听器附件(甚至可以将其转换为HOC(。

您必须处理的另一件事是如何存储复制和粘贴的信息。若你们像我在示例中所做的那个样将它存储在状态中,那个么用户将失去复制某些内容并粘贴到应用程序外部的能力。此外,在我的示例中,我将复制和粘贴结合在一起,因此只有当您关注其中一个元素时,才能进行粘贴。您可能希望粘贴通用。您可以使用剪贴板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

这个例子只供您开始。它不会解决你的所有问题,但它应该为你提供如何进一步前进的基本概述。你的任务相当复杂,所以会有额外的挑战,你必须自己解决。

最新更新