mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-17 20:51:20 +02:00
remove keyboard data.*
properties (#1235)
* update examples and walkthroughs * deprecate data keyboard properties * update examples * add is-hotkey to resources doc * udpate docs * update docs * fix split-block test
This commit is contained in:
@@ -6,7 +6,8 @@ A few resources that are helpful for building with Slate.
|
|||||||
|
|
||||||
## Libraries
|
## Libraries
|
||||||
|
|
||||||
- [`react-broadcast`] works well when you need to have your custom node components re-render based on state that lives outside the `document`. It's the same pattern that `react-router` uses to update `<Link>` components.
|
- [`is-hotkey`](https://github.com/ianstormtaylor/is-hotkey) is a simple way to check whether an `onKeyDown` handler should fire for a given hotkey, handling cross-platform concerns like <kbd>cmd</kbd> vs. <kbd>ctrl</kbd> keys for you automatically.
|
||||||
|
- [`react-broadcast`](https://github.com/ReactTraining/react-broadcast) works well when you need to have your custom node components re-render based on state that lives outside the `document`. It's the same pattern that `react-router` uses to update `<Link>` components.
|
||||||
|
|
||||||
|
|
||||||
## Tooling
|
## Tooling
|
||||||
|
@@ -115,30 +115,6 @@ If no other plugin handles this event, it will be handled by the [Core plugin](.
|
|||||||
|
|
||||||
This handler is called when any key is pressed in the `contenteditable` element, before any action is taken.
|
This handler is called when any key is pressed in the `contenteditable` element, before any action is taken.
|
||||||
|
|
||||||
The `data` object contains the `key` which is a string name of the key that was pressed, as well as it's `code`. It also contains a series of helpful utility properties for determining hotkey logic. For example, `isCtrl` is true if the `control` key was pressed before.
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
key: String,
|
|
||||||
code: Number,
|
|
||||||
isAlt: Boolean,
|
|
||||||
isCmd: Boolean,
|
|
||||||
isCtrl: Boolean,
|
|
||||||
isLine: Boolean,
|
|
||||||
isMeta: Boolean,
|
|
||||||
isMod: Boolean,
|
|
||||||
isModAlt: Boolean,
|
|
||||||
isShift: Boolean,
|
|
||||||
isWord: Boolean
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `isMod` boolean is `true` if the `control` key was pressed on Windows or the `command` key was pressed on Mac _without_ the `alt/option` key also being pressed. This is a convenience for adding hotkeys like `command+b`.
|
|
||||||
|
|
||||||
The `isModAlt` boolean is `true` if the `control` key was pressed on Windows or the `command` key was pressed on Mac _and_ the `alt/option` key was also being pressed. This is a convenience for secondary hotkeys like `command+option+1`.
|
|
||||||
|
|
||||||
The `isLine` and `isWord` booleans represent whether the "line modifier" or "word modifier" hotkeys are pressed when deleting or moving the cursor. For example, on a Mac `option + right` moves the cursor to the right one word at a time.
|
|
||||||
|
|
||||||
Make sure to `event.preventDefault()` if you do not want the default insertion behavior to occur! If no other plugin handles this event, it will be handled by the [Core plugin](./core.md).
|
Make sure to `event.preventDefault()` if you do not want the default insertion behavior to occur! If no other plugin handles this event, it will be handled by the [Core plugin](./core.md).
|
||||||
|
|
||||||
### `onKeyUp`
|
### `onKeyUp`
|
||||||
@@ -146,8 +122,6 @@ Make sure to `event.preventDefault()` if you do not want the default insertion b
|
|||||||
|
|
||||||
This handler is called when any key is released in the `contenteditable` element.
|
This handler is called when any key is released in the `contenteditable` element.
|
||||||
|
|
||||||
The `data` object contains the same information as the `data` object of `onKeyDown`.
|
|
||||||
|
|
||||||
### `onPaste`
|
### `onPaste`
|
||||||
`Function onPaste(event: Event, data: Object, change: Change, editor: Editor) => Change || Void`
|
`Function onPaste(event: Event, data: Object, change: Change, editor: Editor) => Change || Void`
|
||||||
|
|
||||||
|
@@ -49,9 +49,9 @@ class App extends React.Component {
|
|||||||
this.setState({ state })
|
this.setState({ state })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define a new handler which prints the key code that was pressed.
|
// Define a new handler which prints the key that was pressed.
|
||||||
onKeyDown = (event, data, change) => {
|
onKeyDown = (event, data, change) => {
|
||||||
console.log(event.which)
|
console.log(event.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -85,8 +85,8 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown = (event, data, change) => {
|
onKeyDown = (event, data, change) => {
|
||||||
// Return with no changes if it's not the "7" key with shift pressed.
|
// Return with no changes if it's not the "&" key.
|
||||||
if (event.which != 55 || !event.shiftKey) return
|
if (event.key != '&') return
|
||||||
|
|
||||||
// Prevent the ampersand character from being inserted.
|
// Prevent the ampersand character from being inserted.
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
@@ -28,7 +28,7 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown = (event, data, change) => {
|
onKeyDown = (event, data, change) => {
|
||||||
if (event.which != 67 || !event.metaKey || !event.altKey) return
|
if (event.key != '`' || !event.metaKey) return
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const isCode = change.state.blocks.some(block => block.type == 'code')
|
const isCode = change.state.blocks.some(block => block.type == 'code')
|
||||||
|
|
||||||
@@ -72,16 +72,15 @@ class App extends React.Component {
|
|||||||
if (!event.metaKey) return
|
if (!event.metaKey) return
|
||||||
|
|
||||||
// Decide what to do based on the key code...
|
// Decide what to do based on the key code...
|
||||||
switch (event.which) {
|
switch (event.key) {
|
||||||
// When "B" is pressed, add a "bold" mark to the text.
|
// When "B" is pressed, add a "bold" mark to the text.
|
||||||
case 66: {
|
case 'b': {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
change.addMark('bold')
|
change.addMark('bold')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// When "`" is pressed, keep our existing code block logic.
|
// When "`" is pressed, keep our existing code block logic.
|
||||||
case 67: {
|
case '`': {
|
||||||
if (!event.altKey) return
|
|
||||||
const isCode = change.state.blocks.some(block => block.type == 'code')
|
const isCode = change.state.blocks.some(block => block.type == 'code')
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
change.setBlock(isCode ? 'paragraph' : 'code')
|
change.setBlock(isCode ? 'paragraph' : 'code')
|
||||||
@@ -148,14 +147,13 @@ class App extends React.Component {
|
|||||||
onKeyDown = (event, data, change) => {
|
onKeyDown = (event, data, change) => {
|
||||||
if (!event.metaKey) return
|
if (!event.metaKey) return
|
||||||
|
|
||||||
switch (event.which) {
|
switch (event.key) {
|
||||||
case 66: {
|
case 'b': {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
change.toggleMark('bold')
|
change.toggleMark('bold')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case 67: {
|
case '`': {
|
||||||
if (!event.altKey) return
|
|
||||||
const isCode = change.state.blocks.some(block => block.type == 'code')
|
const isCode = change.state.blocks.some(block => block.type == 'code')
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
state.setBlock(isCode ? 'paragraph' : 'code')
|
state.setBlock(isCode ? 'paragraph' : 'code')
|
||||||
|
@@ -23,10 +23,8 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown = (event, data, change) => {
|
onKeyDown = (event, data, change) => {
|
||||||
if (event.which != 55 || !event.shiftKey) return
|
if (event.key != '&') return
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
change.insertText('and');
|
change.insertText('and');
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -87,10 +85,8 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown = (event, data, change) => {
|
onKeyDown = (event, data, change) => {
|
||||||
if (event.which != 55 || !event.shiftKey) return
|
if (event.key != '&') return
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
change.insertText('and')
|
change.insertText('and')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -110,7 +106,7 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Okay, but now we'll need a way for the user to actually turn a block into a code block. So let's change our `onKeyDown` function to add a `⌘-Alt-C` shortcut that does just that:
|
Okay, but now we'll need a way for the user to actually turn a block into a code block. So let's change our `onKeyDown` function to add a `⌘-\`` shortcut that does just that:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function CodeNode(props) {
|
function CodeNode(props) {
|
||||||
@@ -134,7 +130,7 @@ class App extends React.Component {
|
|||||||
|
|
||||||
onKeyDown = (event, data, change) => {
|
onKeyDown = (event, data, change) => {
|
||||||
// Return with no changes if it's not the "`" key with cmd/ctrl pressed.
|
// Return with no changes if it's not the "`" key with cmd/ctrl pressed.
|
||||||
if (event.which != 67 || !event.metaKey || !event.altKey) return
|
if (event.key != '`' || !event.metaKey) return
|
||||||
|
|
||||||
// Prevent the "`" from being inserted by default.
|
// Prevent the "`" from being inserted by default.
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@@ -158,9 +154,9 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, if you press `⌘-Alt-C`, the block your cursor is in should turn into a code block! Magic!
|
Now, if you press `⌘-\`` the block your cursor is in should turn into a code block! Magic!
|
||||||
|
|
||||||
But we forgot one thing. When you hit `⌘-Alt-C` again, it should change the code block back into a paragraph. To do that, we'll need to add a bit of logic to change the type we set based on whether any of the currently selected blocks are already a code block:
|
But we forgot one thing. When you hit `⌘-\`` again, it should change the code block back into a paragraph. To do that, we'll need to add a bit of logic to change the type we set based on whether any of the currently selected blocks are already a code block:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function CodeNode(props) {
|
function CodeNode(props) {
|
||||||
@@ -183,7 +179,7 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown = (event, data, change) => {
|
onKeyDown = (event, data, change) => {
|
||||||
if (event.which != 67 || !event.metaKey || !event.altKey) return
|
if (event.key != '`' || !event.metaKey) return
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
@@ -209,7 +205,7 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
And there you have it! If you press `⌘-Alt-C` while inside a code block, it should turn back into a paragraph!
|
And there you have it! If you press `⌘-\`` while inside a code block, it should turn back into a paragraph!
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<p align="center"><strong>Next:</strong><br/><a href="./applying-custom-formatting.md">Applying Custom Formatting</a></p>
|
<p align="center"><strong>Next:</strong><br/><a href="./applying-custom-formatting.md">Applying Custom Formatting</a></p>
|
||||||
|
@@ -30,7 +30,7 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown = (event, data, change) => {
|
onKeyDown = (event, data, change) => {
|
||||||
if (!event.metaKey || event.which != 66) return
|
if (event.key != 'b' || !event.metaKey) return
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
change.toggleMark('bold')
|
change.toggleMark('bold')
|
||||||
return true
|
return true
|
||||||
@@ -50,12 +50,12 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's write a new function, that takes a set of options: the mark `type` to toggle and the key `code` to press.
|
Let's write a new function, that takes a set of options: the mark `type` to toggle and the `key` to press.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function MarkHotkey(options) {
|
function MarkHotkey(options) {
|
||||||
// Grab our options from the ones passed in.
|
// Grab our options from the ones passed in.
|
||||||
const { type, code, isAltKey = false } = options
|
const { type, key } = options
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -67,13 +67,13 @@ In this case our plugin object will have one property: a `onKeyDown` handler, wi
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
function MarkHotkey(options) {
|
function MarkHotkey(options) {
|
||||||
const { type, code, isAltKey = false } = options
|
const { type, key } = options
|
||||||
|
|
||||||
// Return our "plugin" object, containing the `onKeyDown` handler.
|
// Return our "plugin" object, containing the `onKeyDown` handler.
|
||||||
return {
|
return {
|
||||||
onKeyDown(event, data, change) {
|
onKeyDown(event, data, change) {
|
||||||
// Check that the key pressed matches our `code` option.
|
// Check that the key pressed matches our `key` option.
|
||||||
if (!event.metaKey || event.which != code || event.altKey != isAltKey) return
|
if (!event.metaKey || event.key != key) return
|
||||||
|
|
||||||
// Prevent the default characters from being inserted.
|
// Prevent the default characters from being inserted.
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@@ -94,7 +94,7 @@ Now that we have our plugin, let's remove the hard-coded logic from our app, and
|
|||||||
// Initialize our bold-mark-adding plugin.
|
// Initialize our bold-mark-adding plugin.
|
||||||
const boldPlugin = MarkHotkey({
|
const boldPlugin = MarkHotkey({
|
||||||
type: 'bold',
|
type: 'bold',
|
||||||
code: 66
|
key: 'b'
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create an array of plugins.
|
// Create an array of plugins.
|
||||||
@@ -139,11 +139,11 @@ Let's add _italic_, `code`, ~~strikethrough~~ and underline marks:
|
|||||||
```js
|
```js
|
||||||
// Initialize a plugin for each mark...
|
// Initialize a plugin for each mark...
|
||||||
const plugins = [
|
const plugins = [
|
||||||
MarkHotkey({ code: 66, type: 'bold' }),
|
MarkHotkey({ key: 'b', type: 'bold' }),
|
||||||
MarkHotkey({ code: 67, type: 'code', isAltKey: true }),
|
MarkHotkey({ key: '`', type: 'code' }),
|
||||||
MarkHotkey({ code: 73, type: 'italic' }),
|
MarkHotkey({ key: 'i', type: 'italic' }),
|
||||||
MarkHotkey({ code: 68, type: 'strikethrough' }),
|
MarkHotkey({ key: '~', type: 'strikethrough' }),
|
||||||
MarkHotkey({ code: 85, type: 'underline' })
|
MarkHotkey({ key: 'u', type: 'underline' })
|
||||||
]
|
]
|
||||||
|
|
||||||
class App extends React.Component {
|
class App extends React.Component {
|
||||||
@@ -182,87 +182,6 @@ class App extends React.Component {
|
|||||||
|
|
||||||
And there you have it! We just added a ton of functionality to the editor with very little work. And we can keep all of our mark hotkey logic tested and isolated in a single place, making maintaining the code easier.
|
And there you have it! We just added a ton of functionality to the editor with very little work. And we can keep all of our mark hotkey logic tested and isolated in a single place, making maintaining the code easier.
|
||||||
|
|
||||||
Of course... now that it's reusable, we could actually make our `MarkHotkey` plugin a little easier to use. What if instead of a `code` argument it took the text of the `key` itself? That would make the calling code a lot clearer, since key codes are really obtuse.
|
|
||||||
|
|
||||||
In fact, unless you have weirdly good keycode knowledge, you probably have no idea what our current hotkeys actually are.
|
|
||||||
|
|
||||||
Let's fix that.
|
|
||||||
|
|
||||||
Using the `keycode` module in npm makes this dead simple.
|
|
||||||
|
|
||||||
First install it:
|
|
||||||
|
|
||||||
```
|
|
||||||
npm install keycode
|
|
||||||
```
|
|
||||||
|
|
||||||
And then we can add it our plugin:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Import the keycode module.
|
|
||||||
import keycode from `keycode`
|
|
||||||
|
|
||||||
function MarkHotkey(options) {
|
|
||||||
// Change the options to take a `key`.
|
|
||||||
const { type, key, isAltKey = false } = options
|
|
||||||
|
|
||||||
return {
|
|
||||||
onKeyDown(event, data, change) {
|
|
||||||
// Change the comparison to use the key name.
|
|
||||||
if (!event.metaKey || keycode(event.which) != key || event.altKey != isAltKey) return
|
|
||||||
event.preventDefault()
|
|
||||||
change.toggleMark(type)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And now we can make our app code much clearer for the next person who reads it:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Use the much clearer key names instead of key codes!
|
|
||||||
const plugins = [
|
|
||||||
MarkHotkey({ key: 'b', type: 'bold' }),
|
|
||||||
MarkHotkey({ key: 'c', type: 'code', isAltKey: true }),
|
|
||||||
MarkHotkey({ key: 'i', type: 'italic' }),
|
|
||||||
MarkHotkey({ key: 'd', type: 'strikethrough' }),
|
|
||||||
MarkHotkey({ key: 'u', type: 'underline' })
|
|
||||||
]
|
|
||||||
|
|
||||||
class App extends React.Component {
|
|
||||||
|
|
||||||
state = {
|
|
||||||
state: initialState,
|
|
||||||
schema: {
|
|
||||||
marks: {
|
|
||||||
bold: props => <strong>{props.children}</strong>,
|
|
||||||
code: props => <code>{props.children}</code>,
|
|
||||||
italic: props => <em>{props.children}</em>,
|
|
||||||
strikethrough: props => <del>{props.children}</del>,
|
|
||||||
underline: props => <u>{props.children}</u>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange = ({ state }) => {
|
|
||||||
this.setState({ state })
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Editor
|
|
||||||
plugins={plugins}
|
|
||||||
schema={this.state.schema}
|
|
||||||
state={this.state.state}
|
|
||||||
onChange={this.onChange}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
That's why plugins are awesome. They let you get really expressive while also making your codebase easier to manage. And since Slate is built with plugins as a primary consideration, using them is dead simple!
|
That's why plugins are awesome. They let you get really expressive while also making your codebase easier to manage. And since Slate is built with plugins as a primary consideration, using them is dead simple!
|
||||||
|
|
||||||
|
|
||||||
|
@@ -116,7 +116,7 @@ class CheckLists extends React.Component {
|
|||||||
const { state } = change
|
const { state } = change
|
||||||
|
|
||||||
if (
|
if (
|
||||||
data.key == 'enter' &&
|
e.key == 'Enter' &&
|
||||||
state.startBlock.type == 'check-list-item'
|
state.startBlock.type == 'check-list-item'
|
||||||
) {
|
) {
|
||||||
return change
|
return change
|
||||||
@@ -125,7 +125,7 @@ class CheckLists extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
data.key == 'backspace' &&
|
e.key == 'Backspace' &&
|
||||||
state.isCollapsed &&
|
state.isCollapsed &&
|
||||||
state.startBlock.type == 'check-list-item' &&
|
state.startBlock.type == 'check-list-item' &&
|
||||||
state.selection.startOffset == 0
|
state.selection.startOffset == 0
|
||||||
|
@@ -174,7 +174,7 @@ class CodeHighlighting extends React.Component {
|
|||||||
onKeyDown = (e, data, change) => {
|
onKeyDown = (e, data, change) => {
|
||||||
const { state } = change
|
const { state } = change
|
||||||
const { startBlock } = state
|
const { startBlock } = state
|
||||||
if (data.key != 'enter') return
|
if (e.key != 'Enter') return
|
||||||
if (startBlock.type != 'code') return
|
if (startBlock.type != 'code') return
|
||||||
if (state.isExpanded) change.delete()
|
if (state.isExpanded) change.delete()
|
||||||
return change.insertText('\n')
|
return change.insertText('\n')
|
||||||
|
@@ -96,40 +96,6 @@ class HugeDocument extends React.Component {
|
|||||||
this.setState({ state })
|
this.setState({ state })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* On key down, if it's a formatting command toggle a mark.
|
|
||||||
*
|
|
||||||
* @param {Event} e
|
|
||||||
* @param {Object} data
|
|
||||||
* @param {Change} change
|
|
||||||
*/
|
|
||||||
|
|
||||||
onKeyDown = (e, data, change) => {
|
|
||||||
if (!data.isMod) return
|
|
||||||
let mark
|
|
||||||
|
|
||||||
switch (data.key) {
|
|
||||||
case 'b':
|
|
||||||
mark = 'bold'
|
|
||||||
break
|
|
||||||
case 'i':
|
|
||||||
mark = 'italic'
|
|
||||||
break
|
|
||||||
case 'u':
|
|
||||||
mark = 'underlined'
|
|
||||||
break
|
|
||||||
case '`':
|
|
||||||
mark = 'code'
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
e.preventDefault()
|
|
||||||
change.toggleMark(mark)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the editor.
|
* Render the editor.
|
||||||
*
|
*
|
||||||
|
@@ -105,10 +105,10 @@ class MarkdownShortcuts extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
onKeyDown = (e, data, change) => {
|
onKeyDown = (e, data, change) => {
|
||||||
switch (data.key) {
|
switch (e.key) {
|
||||||
case 'space': return this.onSpace(e, change)
|
case ' ': return this.onSpace(e, change)
|
||||||
case 'backspace': return this.onBackspace(e, change)
|
case 'Backspace': return this.onBackspace(e, change)
|
||||||
case 'enter': return this.onEnter(e, change)
|
case 'Enter': return this.onEnter(e, change)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,14 +3,28 @@ import { Editor } from 'slate-react'
|
|||||||
import { State } from 'slate'
|
import { State } from 'slate'
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import isHotkey from 'is-hotkey'
|
||||||
import initialState from './state.json'
|
import initialState from './state.json'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the default node type.
|
* Define the default node type.
|
||||||
|
*
|
||||||
|
* @type {String}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const DEFAULT_NODE = 'paragraph'
|
const DEFAULT_NODE = 'paragraph'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hotkey matchers.
|
||||||
|
*
|
||||||
|
* @type {Function}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const isBoldHotkey = isHotkey('mod+b')
|
||||||
|
const isItalicHotkey = isHotkey('mod+i')
|
||||||
|
const isUnderlinedHotkey = isHotkey('mod+u')
|
||||||
|
const isCodeHotkey = isHotkey('mod+`')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a schema.
|
* Define a schema.
|
||||||
*
|
*
|
||||||
@@ -107,24 +121,18 @@ class RichTextExample extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
onKeyDown = (e, data, change) => {
|
onKeyDown = (e, data, change) => {
|
||||||
if (!data.isMod) return
|
|
||||||
let mark
|
let mark
|
||||||
|
|
||||||
switch (data.key) {
|
if (isBoldHotkey(e)) {
|
||||||
case 'b':
|
mark = 'bold'
|
||||||
mark = 'bold'
|
} else if (isItalicHotkey(e)) {
|
||||||
break
|
mark = 'italic'
|
||||||
case 'i':
|
} else if (isUnderlinedHotkey(e)) {
|
||||||
mark = 'italic'
|
mark = 'underlined'
|
||||||
break
|
} else if (isCodeHotkey(e)) {
|
||||||
case 'u':
|
mark = 'code'
|
||||||
mark = 'underlined'
|
} else {
|
||||||
break
|
return
|
||||||
case '`':
|
|
||||||
mark = 'code'
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@@ -54,7 +54,7 @@ class PlainText extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
onKeyDown = (e, data, change) => {
|
onKeyDown = (e, data, change) => {
|
||||||
if (data.key == 'enter' && data.isShift) {
|
if (e.key == 'Enter' && e.shiftKey) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
change.insertText('\n')
|
change.insertText('\n')
|
||||||
return true
|
return true
|
||||||
|
@@ -3,8 +3,20 @@ import { Editor } from 'slate-react'
|
|||||||
import { State } from 'slate'
|
import { State } from 'slate'
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import isHotkey from 'is-hotkey'
|
||||||
import initialState from './state.json'
|
import initialState from './state.json'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hotkey matchers.
|
||||||
|
*
|
||||||
|
* @type {Function}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const isBoldHotkey = isHotkey('mod+b')
|
||||||
|
const isItalicHotkey = isHotkey('mod+i')
|
||||||
|
const isUnderlinedHotkey = isHotkey('mod+u')
|
||||||
|
const isCodeHotkey = isHotkey('mod+`')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a schema.
|
* Define a schema.
|
||||||
*
|
*
|
||||||
@@ -100,24 +112,18 @@ class SyncingEditor extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
onKeyDown = (e, data, change) => {
|
onKeyDown = (e, data, change) => {
|
||||||
if (!data.isMod) return
|
|
||||||
let mark
|
let mark
|
||||||
|
|
||||||
switch (data.key) {
|
if (isBoldHotkey(e)) {
|
||||||
case 'b':
|
mark = 'bold'
|
||||||
mark = 'bold'
|
} else if (isItalicHotkey(e)) {
|
||||||
break
|
mark = 'italic'
|
||||||
case 'i':
|
} else if (isUnderlinedHotkey(e)) {
|
||||||
mark = 'italic'
|
mark = 'underlined'
|
||||||
break
|
} else if (isCodeHotkey(e)) {
|
||||||
case 'u':
|
mark = 'code'
|
||||||
mark = 'underlined'
|
} else {
|
||||||
break
|
return
|
||||||
case '`':
|
|
||||||
mark = 'code'
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@@ -118,10 +118,10 @@ class Tables extends React.Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (data.key) {
|
switch (e.key) {
|
||||||
case 'backspace': return this.onBackspace(e, state)
|
case 'Backspace': return this.onBackspace(e, state)
|
||||||
case 'delete': return this.onDelete(e, state)
|
case 'Delete': return this.onDelete(e, state)
|
||||||
case 'enter': return this.onEnter(e, state)
|
case 'Enter': return this.onEnter(e, state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,6 +27,7 @@
|
|||||||
"gh-pages": "^0.11.0",
|
"gh-pages": "^0.11.0",
|
||||||
"http-server": "^0.9.0",
|
"http-server": "^0.9.0",
|
||||||
"immutable": "^3.8.1",
|
"immutable": "^3.8.1",
|
||||||
|
"is-hotkey": "^0.0.1",
|
||||||
"is-image": "^1.0.1",
|
"is-image": "^1.0.1",
|
||||||
"is-url": "^1.2.2",
|
"is-url": "^1.2.2",
|
||||||
"jest": "^17.0.3",
|
"jest": "^17.0.3",
|
||||||
|
@@ -572,14 +572,17 @@ class Content extends React.Component {
|
|||||||
if (this.props.readOnly) return
|
if (this.props.readOnly) return
|
||||||
if (!this.isInEditor(event.target)) return
|
if (!this.isInEditor(event.target)) return
|
||||||
|
|
||||||
const { altKey, ctrlKey, metaKey, shiftKey, which } = event
|
const { key, metaKey, ctrlKey } = event
|
||||||
const key = keycode(which)
|
|
||||||
const data = {}
|
const data = {}
|
||||||
|
const modKey = IS_MAC ? metaKey : ctrlKey
|
||||||
|
|
||||||
|
// COMPAT: add the deprecated keyboard event properties.
|
||||||
|
addDeprecatedKeyProperties(data, event)
|
||||||
|
|
||||||
// Keep track of an `isShifting` flag, because it's often used to trigger
|
// Keep track of an `isShifting` flag, because it's often used to trigger
|
||||||
// "Paste and Match Style" commands, but isn't available on the event in a
|
// "Paste and Match Style" commands, but isn't available on the event in a
|
||||||
// normal paste event.
|
// normal paste event.
|
||||||
if (key == 'shift') {
|
if (key == 'Shift') {
|
||||||
this.tmp.isShifting = true
|
this.tmp.isShifting = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,35 +591,23 @@ class Content extends React.Component {
|
|||||||
// selection-moving behavior.
|
// selection-moving behavior.
|
||||||
if (
|
if (
|
||||||
this.tmp.isComposing &&
|
this.tmp.isComposing &&
|
||||||
(key == 'left' || key == 'right' || key == 'up' || key == 'down')
|
(key == 'ArrowLeft' || key == 'ArrowRight' || key == 'ArrowUp' || key == 'ArrowDown')
|
||||||
) {
|
) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add helpful properties for handling hotkeys to the data object.
|
|
||||||
data.code = which
|
|
||||||
data.key = key
|
|
||||||
data.isAlt = altKey
|
|
||||||
data.isCmd = IS_MAC ? metaKey && !altKey : false
|
|
||||||
data.isCtrl = ctrlKey && !altKey
|
|
||||||
data.isLine = IS_MAC ? metaKey : false
|
|
||||||
data.isMeta = metaKey
|
|
||||||
data.isMod = IS_MAC ? metaKey && !altKey : ctrlKey && !altKey
|
|
||||||
data.isModAlt = IS_MAC ? metaKey && altKey : ctrlKey && altKey
|
|
||||||
data.isShift = shiftKey
|
|
||||||
data.isWord = IS_MAC ? altKey : ctrlKey
|
|
||||||
|
|
||||||
// These key commands have native behavior in contenteditable elements which
|
// These key commands have native behavior in contenteditable elements which
|
||||||
// will cause our state to be out of sync, so prevent them.
|
// will cause our state to be out of sync, so prevent them.
|
||||||
if (
|
if (
|
||||||
(key == 'enter') ||
|
(key == 'Enter') ||
|
||||||
(key == 'backspace') ||
|
(key == 'Backspace') ||
|
||||||
(key == 'delete') ||
|
(key == 'Delete') ||
|
||||||
(key == 'b' && data.isMod) ||
|
(key == 'b' && modKey) ||
|
||||||
(key == 'i' && data.isMod) ||
|
(key == 'i' && modKey) ||
|
||||||
(key == 'y' && data.isMod) ||
|
(key == 'y' && modKey) ||
|
||||||
(key == 'z' && data.isMod)
|
(key == 'z' && modKey) ||
|
||||||
|
(key == 'Z' && modKey)
|
||||||
) {
|
) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
@@ -632,27 +623,15 @@ class Content extends React.Component {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
onKeyUp = (event) => {
|
onKeyUp = (event) => {
|
||||||
const { altKey, ctrlKey, metaKey, shiftKey, which } = event
|
|
||||||
const key = keycode(which)
|
|
||||||
const data = {}
|
const data = {}
|
||||||
|
|
||||||
if (key == 'shift') {
|
// COMPAT: add the deprecated keyboard event properties.
|
||||||
|
addDeprecatedKeyProperties(data, event)
|
||||||
|
|
||||||
|
if (event.key == 'Shift') {
|
||||||
this.tmp.isShifting = false
|
this.tmp.isShifting = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add helpful properties for handling hotkeys to the data object.
|
|
||||||
data.code = which
|
|
||||||
data.key = key
|
|
||||||
data.isAlt = altKey
|
|
||||||
data.isCmd = IS_MAC ? metaKey && !altKey : false
|
|
||||||
data.isCtrl = ctrlKey && !altKey
|
|
||||||
data.isLine = IS_MAC ? metaKey : false
|
|
||||||
data.isMeta = metaKey
|
|
||||||
data.isMod = IS_MAC ? metaKey && !altKey : ctrlKey && !altKey
|
|
||||||
data.isModAlt = IS_MAC ? metaKey && altKey : ctrlKey && altKey
|
|
||||||
data.isShift = shiftKey
|
|
||||||
data.isWord = IS_MAC ? altKey : ctrlKey
|
|
||||||
|
|
||||||
debug('onKeyUp', { event, data })
|
debug('onKeyUp', { event, data })
|
||||||
this.props.onKeyUp(event, data)
|
this.props.onKeyUp(event, data)
|
||||||
}
|
}
|
||||||
@@ -669,9 +648,16 @@ class Content extends React.Component {
|
|||||||
|
|
||||||
const data = getTransferData(event.clipboardData)
|
const data = getTransferData(event.clipboardData)
|
||||||
|
|
||||||
// Attach the `isShift` flag, so that people can use it to trigger "Paste
|
// COMPAT: Attach the `isShift` flag, so that people can use it to trigger
|
||||||
// and Match Style" logic.
|
// "Paste and Match Style" logic.
|
||||||
data.isShift = !!this.tmp.isShifting
|
Object.defineProperty(data, 'isShift', {
|
||||||
|
enumerable: true,
|
||||||
|
get() {
|
||||||
|
logger.deprecate('0.28.0', 'The `data.isShift` property of paste events has been deprecated. If you need this functionality, you\'ll need to keep track of that state with `onKeyDown` and `onKeyUp` events instead')
|
||||||
|
return !!this.tmp.isShifting
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
debug('onPaste', { event, data })
|
debug('onPaste', { event, data })
|
||||||
|
|
||||||
// COMPAT: In IE 11, only plain text can be retrieved from the event's
|
// COMPAT: In IE 11, only plain text can be retrieved from the event's
|
||||||
@@ -897,6 +883,40 @@ class Content extends React.Component {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add deprecated `data` fields from a key `event`.
|
||||||
|
*
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Object} event
|
||||||
|
*/
|
||||||
|
|
||||||
|
function addDeprecatedKeyProperties(data, event) {
|
||||||
|
const { altKey, ctrlKey, metaKey, shiftKey, which } = event
|
||||||
|
const name = keycode(which)
|
||||||
|
|
||||||
|
function define(key, value) {
|
||||||
|
Object.defineProperty(data, key, {
|
||||||
|
enumerable: true,
|
||||||
|
get() {
|
||||||
|
logger.deprecate('0.28.0', `The \`data.${key}\` property of keyboard events is deprecated, please use the native \`event\` properties instead.`)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
define('code', which)
|
||||||
|
define('key', name)
|
||||||
|
define('isAlt', altKey)
|
||||||
|
define('isCmd', IS_MAC ? metaKey && !altKey : false)
|
||||||
|
define('isCtrl', ctrlKey && !altKey)
|
||||||
|
define('isLine', IS_MAC ? metaKey : false)
|
||||||
|
define('isMeta', metaKey)
|
||||||
|
define('isMod', IS_MAC ? metaKey && !altKey : ctrlKey && !altKey)
|
||||||
|
define('isModAlt', IS_MAC ? metaKey && altKey : ctrlKey && altKey)
|
||||||
|
define('isShift', shiftKey)
|
||||||
|
define('isWord', IS_MAC ? altKey : ctrlKey)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export.
|
* Export.
|
||||||
*
|
*
|
||||||
|
@@ -407,19 +407,20 @@ function Plugin(options = {}) {
|
|||||||
function onKeyDown(e, data, change) {
|
function onKeyDown(e, data, change) {
|
||||||
debug('onKeyDown', { data })
|
debug('onKeyDown', { data })
|
||||||
|
|
||||||
switch (data.key) {
|
switch (e.key) {
|
||||||
case 'enter': return onKeyDownEnter(e, data, change)
|
case 'Enter': return onKeyDownEnter(e, data, change)
|
||||||
case 'backspace': return onKeyDownBackspace(e, data, change)
|
case 'Backspace': return onKeyDownBackspace(e, data, change)
|
||||||
case 'delete': return onKeyDownDelete(e, data, change)
|
case 'Delete': return onKeyDownDelete(e, data, change)
|
||||||
case 'left': return onKeyDownLeft(e, data, change)
|
case 'ArrowLeft': return onKeyDownLeft(e, data, change)
|
||||||
case 'right': return onKeyDownRight(e, data, change)
|
case 'ArrowRight': return onKeyDownRight(e, data, change)
|
||||||
case 'up': return onKeyDownUp(e, data, change)
|
case 'ArrowUp': return onKeyDownUp(e, data, change)
|
||||||
case 'down': return onKeyDownDown(e, data, change)
|
case 'ArrowDown': return onKeyDownDown(e, data, change)
|
||||||
case 'd': return onKeyDownD(e, data, change)
|
case 'd': return onKeyDownD(e, data, change)
|
||||||
case 'h': return onKeyDownH(e, data, change)
|
case 'h': return onKeyDownH(e, data, change)
|
||||||
case 'k': return onKeyDownK(e, data, change)
|
case 'k': return onKeyDownK(e, data, change)
|
||||||
case 'y': return onKeyDownY(e, data, change)
|
case 'y': return onKeyDownY(e, data, change)
|
||||||
case 'z': return onKeyDownZ(e, data, change)
|
case 'z':
|
||||||
|
case 'Z': return onKeyDownZ(e, data, change)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,9 +458,13 @@ function Plugin(options = {}) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownBackspace(e, data, change) {
|
function onKeyDownBackspace(e, data, change) {
|
||||||
|
const isWord = IS_MAC ? e.altKey : e.ctrlKey
|
||||||
|
const isLine = IS_MAC ? e.metaKey : false
|
||||||
|
|
||||||
let boundary = 'Char'
|
let boundary = 'Char'
|
||||||
if (data.isWord) boundary = 'Word'
|
if (isWord) boundary = 'Word'
|
||||||
if (data.isLine) boundary = 'Line'
|
if (isLine) boundary = 'Line'
|
||||||
|
|
||||||
change[`delete${boundary}Backward`]()
|
change[`delete${boundary}Backward`]()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,9 +477,13 @@ function Plugin(options = {}) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownDelete(e, data, change) {
|
function onKeyDownDelete(e, data, change) {
|
||||||
|
const isWord = IS_MAC ? e.altKey : e.ctrlKey
|
||||||
|
const isLine = IS_MAC ? e.metaKey : false
|
||||||
|
|
||||||
let boundary = 'Char'
|
let boundary = 'Char'
|
||||||
if (data.isWord) boundary = 'Word'
|
if (isWord) boundary = 'Word'
|
||||||
if (data.isLine) boundary = 'Line'
|
if (isLine) boundary = 'Line'
|
||||||
|
|
||||||
change[`delete${boundary}Forward`]()
|
change[`delete${boundary}Forward`]()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,8 +505,8 @@ function Plugin(options = {}) {
|
|||||||
function onKeyDownLeft(e, data, change) {
|
function onKeyDownLeft(e, data, change) {
|
||||||
const { state } = change
|
const { state } = change
|
||||||
|
|
||||||
if (data.isCtrl) return
|
if (e.ctrlKey) return
|
||||||
if (data.isAlt) return
|
if (e.altKey) return
|
||||||
if (state.isExpanded) return
|
if (state.isExpanded) return
|
||||||
|
|
||||||
const { document, startKey, startText } = state
|
const { document, startKey, startText } = state
|
||||||
@@ -519,7 +528,7 @@ function Plugin(options = {}) {
|
|||||||
const previousInline = document.getClosestInline(previous.key)
|
const previousInline = document.getClosestInline(previous.key)
|
||||||
|
|
||||||
if (previousBlock === startBlock && previousInline && !previousInline.isVoid) {
|
if (previousBlock === startBlock && previousInline && !previousInline.isVoid) {
|
||||||
const extendOrMove = data.isShift ? 'extend' : 'move'
|
const extendOrMove = e.shiftKey ? 'extend' : 'move'
|
||||||
change.collapseToEndOf(previous)[extendOrMove](-1)
|
change.collapseToEndOf(previous)[extendOrMove](-1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -552,8 +561,8 @@ function Plugin(options = {}) {
|
|||||||
function onKeyDownRight(e, data, change) {
|
function onKeyDownRight(e, data, change) {
|
||||||
const { state } = change
|
const { state } = change
|
||||||
|
|
||||||
if (data.isCtrl) return
|
if (e.ctrlKey) return
|
||||||
if (data.isAlt) return
|
if (e.altKey) return
|
||||||
if (state.isExpanded) return
|
if (state.isExpanded) return
|
||||||
|
|
||||||
const { document, startKey, startText } = state
|
const { document, startKey, startText } = state
|
||||||
@@ -581,7 +590,7 @@ function Plugin(options = {}) {
|
|||||||
const nextInline = document.getClosestInline(next.key)
|
const nextInline = document.getClosestInline(next.key)
|
||||||
|
|
||||||
if (nextBlock == startBlock && nextInline) {
|
if (nextBlock == startBlock && nextInline) {
|
||||||
const extendOrMove = data.isShift ? 'extend' : 'move'
|
const extendOrMove = e.shiftKey ? 'extend' : 'move'
|
||||||
change.collapseToStartOf(next)[extendOrMove](1)
|
change.collapseToStartOf(next)[extendOrMove](1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -604,11 +613,11 @@ function Plugin(options = {}) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownUp(e, data, change) {
|
function onKeyDownUp(e, data, change) {
|
||||||
if (!IS_MAC || data.isCtrl || !data.isAlt) return
|
if (!IS_MAC || e.ctrlKey || !e.altKey) return
|
||||||
|
|
||||||
const { state } = change
|
const { state } = change
|
||||||
const { selection, document, focusKey, focusBlock } = state
|
const { selection, document, focusKey, focusBlock } = state
|
||||||
const transform = data.isShift ? 'extendToStartOf' : 'collapseToStartOf'
|
const transform = e.shiftKey ? 'extendToStartOf' : 'collapseToStartOf'
|
||||||
const block = selection.hasFocusAtStartOf(focusBlock)
|
const block = selection.hasFocusAtStartOf(focusBlock)
|
||||||
? document.getPreviousBlock(focusKey)
|
? document.getPreviousBlock(focusKey)
|
||||||
: focusBlock
|
: focusBlock
|
||||||
@@ -633,11 +642,11 @@ function Plugin(options = {}) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownDown(e, data, change) {
|
function onKeyDownDown(e, data, change) {
|
||||||
if (!IS_MAC || data.isCtrl || !data.isAlt) return
|
if (!IS_MAC || e.ctrlKey || !e.altKey) return
|
||||||
|
|
||||||
const { state } = change
|
const { state } = change
|
||||||
const { selection, document, focusKey, focusBlock } = state
|
const { selection, document, focusKey, focusBlock } = state
|
||||||
const transform = data.isShift ? 'extendToEndOf' : 'collapseToEndOf'
|
const transform = e.shiftKey ? 'extendToEndOf' : 'collapseToEndOf'
|
||||||
const block = selection.hasFocusAtEndOf(focusBlock)
|
const block = selection.hasFocusAtEndOf(focusBlock)
|
||||||
? document.getNextBlock(focusKey)
|
? document.getNextBlock(focusKey)
|
||||||
: focusBlock
|
: focusBlock
|
||||||
@@ -658,7 +667,7 @@ function Plugin(options = {}) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownD(e, data, change) {
|
function onKeyDownD(e, data, change) {
|
||||||
if (!IS_MAC || !data.isCtrl) return
|
if (!IS_MAC || !e.ctrlKey || e.altKey) return
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
change.deleteCharForward()
|
change.deleteCharForward()
|
||||||
}
|
}
|
||||||
@@ -672,7 +681,7 @@ function Plugin(options = {}) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownH(e, data, change) {
|
function onKeyDownH(e, data, change) {
|
||||||
if (!IS_MAC || !data.isCtrl) return
|
if (!IS_MAC || !e.ctrlKey || e.altKey) return
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
change.deleteCharBackward()
|
change.deleteCharBackward()
|
||||||
}
|
}
|
||||||
@@ -686,7 +695,7 @@ function Plugin(options = {}) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownK(e, data, change) {
|
function onKeyDownK(e, data, change) {
|
||||||
if (!IS_MAC || !data.isCtrl) return
|
if (!IS_MAC || !e.ctrlKey || e.altKey) return
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
change.deleteLineForward()
|
change.deleteLineForward()
|
||||||
}
|
}
|
||||||
@@ -700,7 +709,8 @@ function Plugin(options = {}) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownY(e, data, change) {
|
function onKeyDownY(e, data, change) {
|
||||||
if (!data.isMod) return
|
const modKey = IS_MAC ? e.metaKey : e.ctrlKey
|
||||||
|
if (!modKey) return
|
||||||
change.redo()
|
change.redo()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,8 +723,9 @@ function Plugin(options = {}) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownZ(e, data, change) {
|
function onKeyDownZ(e, data, change) {
|
||||||
if (!data.isMod) return
|
const modKey = IS_MAC ? e.metaKey : e.ctrlKey
|
||||||
change[data.isShift ? 'redo' : 'undo']()
|
if (!modKey) return
|
||||||
|
change[e.shiftKey ? 'redo' : 'undo']()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
import h from '../../../helpers/h'
|
import h from '../../../helpers/h'
|
||||||
|
|
||||||
export default function (simulator) {
|
export default function (simulator) {
|
||||||
simulator.keyDown(null, { key: 'enter' })
|
simulator.keyDown({ key: 'Enter' })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const input = (
|
export const input = (
|
||||||
|
@@ -3360,6 +3360,10 @@ is-glob@^3.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-extglob "^2.1.0"
|
is-extglob "^2.1.0"
|
||||||
|
|
||||||
|
is-hotkey@^0.0.1:
|
||||||
|
version "0.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.0.1.tgz#d8d817209b34292551a85357e65cdbfcfa763443"
|
||||||
|
|
||||||
is-image@^1.0.1:
|
is-image@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-image/-/is-image-1.0.1.tgz#6fd51a752a1a111506d060d952118b0b989b426e"
|
resolved "https://registry.yarnpkg.com/is-image/-/is-image-1.0.1.tgz#6fd51a752a1a111506d060d952118b0b989b426e"
|
||||||
|
Reference in New Issue
Block a user