mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-13 18:53:59 +02:00
refactor onKeyDown to use data object
This commit is contained in:
@@ -5,6 +5,13 @@ This document maintains a list of changes to Slate with each new version. Until
|
|||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
### `0.8.0` — _July 27, 2016_
|
||||||
|
|
||||||
|
- **The `onKeyDown` and `onBeforeInput` handlers signatures have changed!** Previously, some Slate handlers had a signature of `(e, state, editor)` and others had a signature of `(e, data, state, editor)`. Now all handlers will be passed a data object, even if it is empty. This is helpful for future compatibility where we might need to add data to a handler that previously didn't have any, and is nicer for consistency. The `onKeyDown` handler's new `data` object contains the `key` name and a series of `is*` properties to make handling hotkeys easier. The `onBeforeInput` handler's new `data` object is empty.
|
||||||
|
|
||||||
|
- **The `Utils` export has been removed.** Previously, a `Key` utility and the `findDOMNode` utility were exposed under the `Utils` object. The `Key` has been removed in favor of the `data` object passed to `onKeyDown`. And then `findDOMNode` utility has been upgraded to a top-level named export, so you'll now need to access it via `import { findDOMNode } from 'slate'`.
|
||||||
|
|
||||||
|
|
||||||
### `0.7.0` — _July 24, 2016_
|
### `0.7.0` — _July 24, 2016_
|
||||||
|
|
||||||
#### BREAKING CHANGES
|
#### BREAKING CHANGES
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
import { Editor, Raw } from '../..'
|
import { Editor, Raw } from '../..'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import keycode from 'keycode'
|
|
||||||
import initialState from './state.json'
|
import initialState from './state.json'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -108,13 +107,13 @@ class AutoMarkdown extends React.Component {
|
|||||||
* On key down, check for our specific key shortcuts.
|
* On key down, check for our specific key shortcuts.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
|
* @param {Data} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State or Null} state
|
* @return {State or Null} state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onKeyDown = (e, state) => {
|
onKeyDown = (e, data, state) => {
|
||||||
const key = keycode(e.which)
|
switch (data.key) {
|
||||||
switch (key) {
|
|
||||||
case 'space': return this.onSpace(e, state)
|
case 'space': return this.onSpace(e, state)
|
||||||
case 'backspace': return this.onBackspace(e, state)
|
case 'backspace': return this.onBackspace(e, state)
|
||||||
case 'enter': return this.onEnter(e, state)
|
case 'enter': return this.onEnter(e, state)
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
import { Editor, Mark, Raw, Selection } from '../..'
|
import { Editor, Mark, Raw, Selection } from '../..'
|
||||||
import Prism from 'prismjs'
|
import Prism from 'prismjs'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import keycode from 'keycode'
|
|
||||||
import initialState from './state.json'
|
import initialState from './state.json'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,13 +64,13 @@ class CodeHighlighting extends React.Component {
|
|||||||
* On key down inside code blocks, insert soft new lines.
|
* On key down inside code blocks, insert soft new lines.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onKeyDown = (e, state) => {
|
onKeyDown = (e, data, state) => {
|
||||||
const key = keycode(e.which)
|
if (data.key != 'enter') return
|
||||||
if (key != 'enter') return
|
|
||||||
const { startBlock } = state
|
const { startBlock } = state
|
||||||
if (startBlock.type != 'code') return
|
if (startBlock.type != 'code') return
|
||||||
|
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
|
|
||||||
import { Editor, Mark, Raw, Utils } from '../../..'
|
import { Editor, Mark, Raw } from '../../..'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import initialState from './state.json'
|
import initialState from './state.json'
|
||||||
import keycode from 'keycode'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the default node type.
|
* Define the default node type.
|
||||||
@@ -105,16 +104,16 @@ class RichText extends React.Component {
|
|||||||
* On key down, if it's a formatting command toggle a mark.
|
* On key down, if it's a formatting command toggle a mark.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onKeyDown = (e, state) => {
|
onKeyDown = (e, data, state) => {
|
||||||
if (!Utils.Key.isCommand(e)) return
|
if (!data.isMod) return
|
||||||
const key = keycode(e.which)
|
|
||||||
let mark
|
let mark
|
||||||
|
|
||||||
switch (key) {
|
switch (data.key) {
|
||||||
case 'b':
|
case 'b':
|
||||||
mark = 'bold'
|
mark = 'bold'
|
||||||
break
|
break
|
||||||
|
@@ -162,18 +162,18 @@ class Images extends React.Component {
|
|||||||
* On drop, insert the image wherever it is dropped.
|
* On drop, insert the image wherever it is dropped.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
* @param {Object} drop
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onDrop = (e, drop, state) => {
|
onDrop = (e, data, state) => {
|
||||||
if (drop.type != 'node') return
|
if (data.type != 'node') return
|
||||||
return state
|
return state
|
||||||
.transform()
|
.transform()
|
||||||
.removeNodeByKey(drop.node.key)
|
.removeNodeByKey(data.node.key)
|
||||||
.moveTo(drop.target)
|
.moveTo(data.target)
|
||||||
.insertBlock(drop.node)
|
.insertBlock(data.node)
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,16 +181,16 @@ class Images extends React.Component {
|
|||||||
* On paste, if the pasted content is an image URL, insert it.
|
* On paste, if the pasted content is an image URL, insert it.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
* @param {Object} paste
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onPaste = (e, paste, state) => {
|
onPaste = (e, data, state) => {
|
||||||
if (paste.type != 'text') return
|
if (data.type != 'text') return
|
||||||
if (!isUrl(paste.text)) return
|
if (!isUrl(data.text)) return
|
||||||
if (!isImage(paste.text)) return
|
if (!isImage(data.text)) return
|
||||||
return this.insertImage(state, paste.text)
|
return this.insertImage(state, data.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -106,14 +106,14 @@ class Links extends React.Component {
|
|||||||
* On paste, if the text is a link, wrap the selection in a link.
|
* On paste, if the text is a link, wrap the selection in a link.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
* @param {Object} paste
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onPaste = (e, paste, state) => {
|
onPaste = (e, data, state) => {
|
||||||
if (state.isCollapsed) return
|
if (state.isCollapsed) return
|
||||||
if (paste.type != 'text' && paste.type != 'html') return
|
if (data.type != 'text' && data.type != 'html') return
|
||||||
if (!isUrl(paste.text)) return
|
if (!isUrl(data.text)) return
|
||||||
|
|
||||||
let transform = state.transform()
|
let transform = state.transform()
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ class Links extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return transform
|
return transform
|
||||||
.wrapInline('link', { href: paste.text })
|
.wrapInline('link', { href: data.text })
|
||||||
.collapseToEnd()
|
.collapseToEnd()
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
@@ -188,14 +188,13 @@ class PasteHtml extends React.Component {
|
|||||||
* On paste, deserialize the HTML and then insert the fragment.
|
* On paste, deserialize the HTML and then insert the fragment.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
* @param {Object} paste
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onPaste = (e, paste, state) => {
|
onPaste = (e, data, state) => {
|
||||||
if (paste.type != 'html') return
|
if (data.type != 'html') return
|
||||||
const { html } = paste
|
const { document } = serializer.deserialize(data.html)
|
||||||
const { document } = serializer.deserialize(html)
|
|
||||||
|
|
||||||
return state
|
return state
|
||||||
.transform()
|
.transform()
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
|
|
||||||
import { Editor, Mark, Raw, Utils } from '../..'
|
import { Editor, Mark, Raw } from '../..'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import initialState from './state.json'
|
import initialState from './state.json'
|
||||||
import keycode from 'keycode'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the default node type.
|
* Define the default node type.
|
||||||
@@ -105,16 +104,16 @@ class RichText extends React.Component {
|
|||||||
* On key down, if it's a formatting command toggle a mark.
|
* On key down, if it's a formatting command toggle a mark.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onKeyDown = (e, state) => {
|
onKeyDown = (e, data, state) => {
|
||||||
if (!Utils.Key.isCommand(e)) return
|
if (!data.isMod) return
|
||||||
const key = keycode(e.which)
|
|
||||||
let mark
|
let mark
|
||||||
|
|
||||||
switch (key) {
|
switch (data.key) {
|
||||||
case 'b':
|
case 'b':
|
||||||
mark = 'bold'
|
mark = 'bold'
|
||||||
break
|
break
|
||||||
|
@@ -101,13 +101,14 @@ class Tables extends React.Component {
|
|||||||
* On key down, check for our specific key shortcuts.
|
* On key down, check for our specific key shortcuts.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State or Null} state
|
* @return {State or Null} state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onKeyDown = (e, state) => {
|
onKeyDown = (e, data, state) => {
|
||||||
if (state.startBlock.type != 'table-cell') return
|
if (state.startBlock.type != 'table-cell') return
|
||||||
switch (keycode(e.which)) {
|
switch (data.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)
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
import Base64 from '../serializers/base-64'
|
import Base64 from '../serializers/base-64'
|
||||||
import Key from '../utils/key'
|
|
||||||
import Node from './node'
|
import Node from './node'
|
||||||
import OffsetKey from '../utils/offset-key'
|
import OffsetKey from '../utils/offset-key'
|
||||||
import Raw from '../serializers/raw'
|
import Raw from '../serializers/raw'
|
||||||
@@ -9,7 +8,7 @@ import Selection from '../models/selection'
|
|||||||
import TYPES from '../utils/types'
|
import TYPES from '../utils/types'
|
||||||
import includes from 'lodash/includes'
|
import includes from 'lodash/includes'
|
||||||
import keycode from 'keycode'
|
import keycode from 'keycode'
|
||||||
import { IS_FIREFOX } from '../utils/environment'
|
import { IS_FIREFOX, IS_MAC } from '../utils/environment'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Noop.
|
* Noop.
|
||||||
@@ -454,7 +453,23 @@ class Content extends React.Component {
|
|||||||
onKeyDown = (e) => {
|
onKeyDown = (e) => {
|
||||||
if (this.props.readOnly) return
|
if (this.props.readOnly) return
|
||||||
const key = keycode(e.which)
|
const key = keycode(e.which)
|
||||||
|
const data = {}
|
||||||
|
|
||||||
|
// Add helpful properties for handling hotkeys to the data object.
|
||||||
|
data.code = e.which
|
||||||
|
data.key = key
|
||||||
|
data.isAlt = e.altKey
|
||||||
|
data.isCmd = IS_MAC ? e.metaKey && !e.altKey : false
|
||||||
|
data.isCtrl = e.ctrlKey && !e.altKey
|
||||||
|
data.isLine = IS_MAC ? e.metaKey : false
|
||||||
|
data.isMeta = e.metaKey
|
||||||
|
data.isMod = IS_MAC ? e.metaKey && !e.altKey : e.ctrlKey && !e.altKey
|
||||||
|
data.isShift = e.shiftKey
|
||||||
|
data.isWord = IS_MAC ? e.altKey : e.ctrlKey
|
||||||
|
|
||||||
|
// When composing, these characters commit the composition but also move the
|
||||||
|
// selection before we're able to handle it, so prevent their default,
|
||||||
|
// selection-moving behavior.
|
||||||
if (
|
if (
|
||||||
this.tmp.isComposing &&
|
this.tmp.isComposing &&
|
||||||
(key == 'left' || key == 'right' || key == 'up' || key == 'down')
|
(key == 'left' || key == 'right' || key == 'up' || key == 'down')
|
||||||
@@ -463,19 +478,21 @@ class Content extends React.Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These key commands have native behavior in contenteditable elements which
|
||||||
|
// 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' && Key.isCommand(e)) ||
|
(key == 'b' && data.isMod) ||
|
||||||
(key == 'i' && Key.isCommand(e)) ||
|
(key == 'i' && data.isMod) ||
|
||||||
(key == 'y' && Key.isWindowsCommand(e)) ||
|
(key == 'y' && data.isMod) ||
|
||||||
(key == 'z' && Key.isCommand(e))
|
(key == 'z' && data.isMod)
|
||||||
) {
|
) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onKeyDown(e)
|
this.props.onKeyDown(e, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -488,37 +505,37 @@ class Content extends React.Component {
|
|||||||
if (this.props.readOnly) return
|
if (this.props.readOnly) return
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
const data = e.clipboardData
|
const { clipboardData } = e
|
||||||
const paste = {}
|
const data = {}
|
||||||
|
|
||||||
// COMPAT: In Firefox, `types` is array-like. (2016/06/21)
|
// COMPAT: In Firefox, `types` is array-like. (2016/06/21)
|
||||||
const types = Array.from(data.types)
|
const types = Array.from(clipboardData.types)
|
||||||
|
|
||||||
// Handle files.
|
// Handle files.
|
||||||
if (data.files.length) {
|
if (clipboardData.files.length) {
|
||||||
paste.type = 'files'
|
data.type = 'files'
|
||||||
paste.files = data.files
|
data.files = clipboardData.files
|
||||||
}
|
}
|
||||||
|
|
||||||
// Treat it as rich text if there is HTML content.
|
// Treat it as rich text if there is HTML content.
|
||||||
else if (includes(types, TYPES.HTML)) {
|
else if (includes(types, TYPES.HTML)) {
|
||||||
paste.type = 'html'
|
data.type = 'html'
|
||||||
paste.text = data.getData(TYPES.TEXT)
|
data.text = clipboardData.getData(TYPES.TEXT)
|
||||||
paste.html = data.getData(TYPES.HTML)
|
data.html = clipboardData.getData(TYPES.HTML)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Treat everything else as plain text.
|
// Treat everything else as plain text.
|
||||||
else {
|
else {
|
||||||
paste.type = 'text'
|
data.type = 'text'
|
||||||
paste.text = data.getData(TYPES.TEXT)
|
data.text = clipboardData.getData(TYPES.TEXT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If html, and the html includes a `data-fragment` attribute, it's actually
|
// If html, and the html includes a `data-fragment` attribute, it's actually
|
||||||
// a raw-serialized JSON fragment from a previous cut/copy, so deserialize
|
// a raw-serialized JSON fragment from a previous cut/copy, so deserialize
|
||||||
// it and insert it normally.
|
// it and insert it normally.
|
||||||
if (paste.type == 'html' && ~paste.html.indexOf('<span data-fragment="')) {
|
if (data.type == 'html' && ~data.html.indexOf('<span data-fragment="')) {
|
||||||
const regexp = /data-fragment="([^\s]+)"/
|
const regexp = /data-fragment="([^\s]+)"/
|
||||||
const matches = regexp.exec(paste.html)
|
const matches = regexp.exec(data.html)
|
||||||
const [ full, encoded ] = matches
|
const [ full, encoded ] = matches
|
||||||
const fragment = Base64.deserializeNode(encoded)
|
const fragment = Base64.deserializeNode(encoded)
|
||||||
let { state } = this.props
|
let { state } = this.props
|
||||||
@@ -532,8 +549,7 @@ class Content extends React.Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
paste.data = data
|
this.props.onPaste(e, data)
|
||||||
this.props.onPaste(e, paste)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -551,12 +567,12 @@ class Content extends React.Component {
|
|||||||
const { state, renderDecorations } = this.props
|
const { state, renderDecorations } = this.props
|
||||||
let { document, selection } = state
|
let { document, selection } = state
|
||||||
const native = window.getSelection()
|
const native = window.getSelection()
|
||||||
const select = {}
|
const data = {}
|
||||||
|
|
||||||
// If there are no ranges, the editor was blurred natively.
|
// If there are no ranges, the editor was blurred natively.
|
||||||
if (!native.rangeCount) {
|
if (!native.rangeCount) {
|
||||||
select.selection = selection.merge({ isFocused: false })
|
data.selection = selection.merge({ isFocused: false })
|
||||||
select.isNative = true
|
data.isNative = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, determine the Slate selection from the native one.
|
// Otherwise, determine the Slate selection from the native one.
|
||||||
@@ -579,12 +595,12 @@ class Content extends React.Component {
|
|||||||
|
|
||||||
// If the native selection is inside text nodes, we can trust the native
|
// If the native selection is inside text nodes, we can trust the native
|
||||||
// state and not need to re-render.
|
// state and not need to re-render.
|
||||||
select.isNative = (
|
data.isNative = (
|
||||||
anchorNode.nodeType == 3 &&
|
anchorNode.nodeType == 3 &&
|
||||||
focusNode.nodeType == 3
|
focusNode.nodeType == 3
|
||||||
)
|
)
|
||||||
|
|
||||||
select.selection = selection.merge({
|
data.selection = selection.merge({
|
||||||
anchorKey: anchor.key,
|
anchorKey: anchor.key,
|
||||||
anchorOffset: anchor.offset,
|
anchorOffset: anchor.offset,
|
||||||
focusKey: focus.key,
|
focusKey: focus.key,
|
||||||
@@ -593,7 +609,7 @@ class Content extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onSelect(e, select)
|
this.props.onSelect(e, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
14
lib/index.js
14
lib/index.js
@@ -33,14 +33,8 @@ import Raw from './serializers/raw'
|
|||||||
* Utils.
|
* Utils.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Key from './utils/key'
|
|
||||||
import findDOMNode from './utils/find-dom-node'
|
import findDOMNode from './utils/find-dom-node'
|
||||||
|
|
||||||
const Utils = {
|
|
||||||
Key,
|
|
||||||
findDOMNode
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export.
|
* Export.
|
||||||
*/
|
*/
|
||||||
@@ -60,8 +54,8 @@ export {
|
|||||||
Selection,
|
Selection,
|
||||||
State,
|
State,
|
||||||
Text,
|
Text,
|
||||||
Utils,
|
Void,
|
||||||
Void
|
findDOMNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -79,6 +73,6 @@ export default {
|
|||||||
Selection,
|
Selection,
|
||||||
State,
|
State,
|
||||||
Text,
|
Text,
|
||||||
Utils,
|
Void,
|
||||||
Void
|
findDOMNode
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
import Base64 from '../serializers/base-64'
|
import Base64 from '../serializers/base-64'
|
||||||
import Character from '../models/character'
|
import Character from '../models/character'
|
||||||
import Key from '../utils/key'
|
|
||||||
import Placeholder from '../components/placeholder'
|
import Placeholder from '../components/placeholder'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import String from '../utils/string'
|
import String from '../utils/string'
|
||||||
@@ -314,17 +313,18 @@ function Plugin(options = {}) {
|
|||||||
* On key down.
|
* On key down.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDown(e, state) {
|
function onKeyDown(e, data, state) {
|
||||||
switch (keycode(e.which)) {
|
switch (data.key) {
|
||||||
case 'enter': return onKeyDownEnter(e, state)
|
case 'enter': return onKeyDownEnter(e, data, state)
|
||||||
case 'backspace': return onKeyDownBackspace(e, state)
|
case 'backspace': return onKeyDownBackspace(e, data, state)
|
||||||
case 'delete': return onKeyDownDelete(e, state)
|
case 'delete': return onKeyDownDelete(e, data, state)
|
||||||
case 'y': return onKeyDownY(e, state)
|
case 'y': return onKeyDownY(e, data, state)
|
||||||
case 'z': return onKeyDownZ(e, state)
|
case 'z': return onKeyDownZ(e, data, state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,11 +332,12 @@ function Plugin(options = {}) {
|
|||||||
* On `enter` key down, split the current block in half.
|
* On `enter` key down, split the current block in half.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownEnter(e, state) {
|
function onKeyDownEnter(e, data, state) {
|
||||||
const { document, startKey, startBlock } = state
|
const { document, startKey, startBlock } = state
|
||||||
|
|
||||||
// For void blocks, we don't want to split. Instead we just move to the
|
// For void blocks, we don't want to split. Instead we just move to the
|
||||||
@@ -360,11 +361,12 @@ function Plugin(options = {}) {
|
|||||||
* On `backspace` key down, delete backwards.
|
* On `backspace` key down, delete backwards.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownBackspace(e, state) {
|
function onKeyDownBackspace(e, data, state) {
|
||||||
// If expanded, delete regularly.
|
// If expanded, delete regularly.
|
||||||
if (state.isExpanded) {
|
if (state.isExpanded) {
|
||||||
return state
|
return state
|
||||||
@@ -378,11 +380,15 @@ function Plugin(options = {}) {
|
|||||||
let n
|
let n
|
||||||
|
|
||||||
// Determine how far backwards to delete.
|
// Determine how far backwards to delete.
|
||||||
if (Key.isWord(e)) {
|
if (data.isWord) {
|
||||||
n = String.getWordOffsetBackward(text, startOffset)
|
n = String.getWordOffsetBackward(text, startOffset)
|
||||||
} else if (Key.isLine(e)) {
|
}
|
||||||
|
|
||||||
|
else if (data.isLine) {
|
||||||
n = startOffset
|
n = startOffset
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
n = String.getCharOffsetBackward(text, startOffset)
|
n = String.getCharOffsetBackward(text, startOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,11 +402,12 @@ function Plugin(options = {}) {
|
|||||||
* On `delete` key down, delete forwards.
|
* On `delete` key down, delete forwards.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownDelete(e, state) {
|
function onKeyDownDelete(e, data, state) {
|
||||||
// If expanded, delete regularly.
|
// If expanded, delete regularly.
|
||||||
if (state.isExpanded) {
|
if (state.isExpanded) {
|
||||||
return state
|
return state
|
||||||
@@ -414,11 +421,15 @@ function Plugin(options = {}) {
|
|||||||
let n
|
let n
|
||||||
|
|
||||||
// Determine how far forwards to delete.
|
// Determine how far forwards to delete.
|
||||||
if (Key.isWord(e)) {
|
if (data.isWord) {
|
||||||
n = String.getWordOffsetForward(text, startOffset)
|
n = String.getWordOffsetForward(text, startOffset)
|
||||||
} else if (Key.isLine(e)) {
|
}
|
||||||
|
|
||||||
|
else if (data.isLine) {
|
||||||
n = text.length - startOffset
|
n = text.length - startOffset
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
n = String.getCharOffsetForward(text, startOffset)
|
n = String.getCharOffsetForward(text, startOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,12 +443,13 @@ function Plugin(options = {}) {
|
|||||||
* On `y` key down, redo.
|
* On `y` key down, redo.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownY(e, state) {
|
function onKeyDownY(e, data, state) {
|
||||||
if (!Key.isWindowsCommand(e)) return
|
if (!data.isMod) return
|
||||||
return state
|
return state
|
||||||
.transform()
|
.transform()
|
||||||
.redo()
|
.redo()
|
||||||
@@ -447,15 +459,16 @@ function Plugin(options = {}) {
|
|||||||
* On `z` key down, undo or redo.
|
* On `z` key down, undo or redo.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
|
* @param {Object} data
|
||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State}
|
* @return {State}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function onKeyDownZ(e, state) {
|
function onKeyDownZ(e, data, state) {
|
||||||
if (!Key.isCommand(e)) return
|
if (!data.isMod) return
|
||||||
return state
|
return state
|
||||||
.transform()
|
.transform()
|
||||||
[IS_MAC && Key.isShift(e) ? 'redo' : 'undo']()
|
[data.isShift ? 'redo' : 'undo']()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
123
lib/utils/key.js
123
lib/utils/key.js
@@ -1,123 +0,0 @@
|
|||||||
|
|
||||||
import { IS_MAC, IS_WINDOWS } from './environment'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does an `e` have the alt modifier?
|
|
||||||
*
|
|
||||||
* @param {Event} e
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function isAlt(e) {
|
|
||||||
return e.altKey
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does an `e` have the command modifier?
|
|
||||||
*
|
|
||||||
* @param {Event} e
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function isCommand(e) {
|
|
||||||
return IS_MAC
|
|
||||||
? e.metaKey && !e.altKey
|
|
||||||
: e.ctrlKey && !e.altKey
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does an `e` have the control modifier?
|
|
||||||
*
|
|
||||||
* @param {Event} e
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function isCtrl(e) {
|
|
||||||
return e.ctrlKey && !e.altKey
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does an `e` have the line-level modifier?
|
|
||||||
*
|
|
||||||
* @param {Event} e
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function isLine(e) {
|
|
||||||
return IS_MAC
|
|
||||||
? e.metaKey
|
|
||||||
: false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does an `e` have the Mac command modifier?
|
|
||||||
*
|
|
||||||
* @param {Event} e
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function isMacCommand(e) {
|
|
||||||
return IS_MAC && isCommand(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does an `e` have the option modifier?
|
|
||||||
*
|
|
||||||
* @param {Event} e
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function isOption(e) {
|
|
||||||
return IS_MAC && e.altKey
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does an `e` have the shift modifier?
|
|
||||||
*
|
|
||||||
* @param {Event} e
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function isShift(e) {
|
|
||||||
return e.shiftKey
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does an `e` have the Windows command modifier?
|
|
||||||
*
|
|
||||||
* @param {Event} e
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function isWindowsCommand(e) {
|
|
||||||
return IS_WINDOWS && isCommand(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does an `e` have the word-level modifier?
|
|
||||||
*
|
|
||||||
* @param {Event} e
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function isWord(e) {
|
|
||||||
return IS_MAC
|
|
||||||
? e.altKey
|
|
||||||
: e.ctrlKey
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default {
|
|
||||||
isAlt,
|
|
||||||
isCommand,
|
|
||||||
isCtrl,
|
|
||||||
isLine,
|
|
||||||
isMacCommand,
|
|
||||||
isOption,
|
|
||||||
isShift,
|
|
||||||
isWindowsCommand,
|
|
||||||
isWord
|
|
||||||
}
|
|
@@ -58,7 +58,7 @@
|
|||||||
"watchify": "^3.7.0"
|
"watchify": "^3.7.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rm -rf ./dist ./node_modules",
|
"clean": "rm -rf ./dist ./node_modules ./examples/build.js",
|
||||||
"disc": "npm run dist && npm run disc:build && npm run disc:open",
|
"disc": "npm run dist && npm run disc:build && npm run disc:open",
|
||||||
"disc:build": "mkdir -p ./tmp && browserify ./dist/index.js --full-paths --outfile ./tmp/build.js",
|
"disc:build": "mkdir -p ./tmp && browserify ./dist/index.js --full-paths --outfile ./tmp/build.js",
|
||||||
"disc:open": "discify ./tmp/build.js --open",
|
"disc:open": "discify ./tmp/build.js --open",
|
||||||
|
Reference in New Issue
Block a user