mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-16 12:14:14 +02:00
refactor slate-hotkeys, fix deleting at start of block (#2048)
This commit is contained in:
@@ -4,6 +4,14 @@ This document maintains a list of changes to the `slate-hotkeys` package with ea
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### `0.2.0` — August 7, 2018
|
||||||
|
|
||||||
|
###### BREAKING
|
||||||
|
|
||||||
|
**Some hotkey checkers have changed or been removed.** Please check the source to see the changes, sorry for the hassle. This was needed to cleanup the behavior and get this package to not be leaky in terms of what checkers it exposed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### `0.1.0` — April 5, 2018
|
### `0.1.0` — April 5, 2018
|
||||||
|
|
||||||
:tada:
|
:tada:
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
"lib/"
|
"lib/"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-hotkey": "^0.1.1",
|
"is-hotkey": "^0.1.3",
|
||||||
"slate-dev-environment": "^0.1.4"
|
"slate-dev-environment": "^0.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@@ -2,104 +2,80 @@ import { isKeyHotkey } from 'is-hotkey'
|
|||||||
import { IS_IOS, IS_MAC } from 'slate-dev-environment'
|
import { IS_IOS, IS_MAC } from 'slate-dev-environment'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is Apple?
|
* Hotkey mappings for each platform.
|
||||||
*
|
*
|
||||||
* @type {Boolean}
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const IS_APPLE = IS_IOS || IS_MAC
|
const HOTKEYS = {
|
||||||
|
bold: 'mod+b',
|
||||||
|
compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
|
||||||
|
moveBackward: 'mod?+ctrl?+alt?+left',
|
||||||
|
moveForward: 'mod?+ctrl?+alt?+right',
|
||||||
|
deleteBackward: 'shift?+backspace',
|
||||||
|
deleteForward: 'shift?+delete',
|
||||||
|
extendBackward: 'shift+left',
|
||||||
|
extendForward: 'shift+right',
|
||||||
|
italic: 'mod+i',
|
||||||
|
splitBlock: 'shift?+enter',
|
||||||
|
undo: 'mod+z',
|
||||||
|
}
|
||||||
|
|
||||||
|
const APPLE_HOTKEYS = {
|
||||||
|
moveLineBackward: 'opt+up',
|
||||||
|
moveLineForward: 'opt+down',
|
||||||
|
deleteBackward: ['ctrl+backspace', 'ctrl+h'],
|
||||||
|
deleteForward: ['ctrl+delete', 'ctrl+d'],
|
||||||
|
deleteLineBackward: 'cmd+shift?+backspace',
|
||||||
|
deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
|
||||||
|
deleteWordBackward: 'opt+shift?+backspace',
|
||||||
|
deleteWordForward: 'opt+shift?+delete',
|
||||||
|
extendLineBackward: 'opt+shift+up',
|
||||||
|
extendLineForward: 'opt+shift+down',
|
||||||
|
redo: 'cmd+shift+z',
|
||||||
|
transposeCharacter: 'ctrl+t',
|
||||||
|
}
|
||||||
|
|
||||||
|
const WINDOWS_HOTKEYS = {
|
||||||
|
deleteWordBackward: 'ctrl+shift?+backspace',
|
||||||
|
deleteWordForward: 'ctrl+shift?+delete',
|
||||||
|
redo: 'ctrl+y',
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hotkeys.
|
* Hotkeys.
|
||||||
*
|
*
|
||||||
* @type {Function}
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const isBold = isKeyHotkey('mod+b')
|
const Hotkeys = {}
|
||||||
const isItalic = isKeyHotkey('mod+i')
|
|
||||||
|
|
||||||
const isEnter = isKeyHotkey('enter')
|
const IS_APPLE = IS_IOS || IS_MAC
|
||||||
const isShiftEnter = isKeyHotkey('shift+enter')
|
const IS_WINDOWS = !IS_APPLE
|
||||||
const isSplitBlock = e => isEnter(e) || isShiftEnter(e)
|
const KEYS = []
|
||||||
|
.concat(Object.keys(HOTKEYS))
|
||||||
|
.concat(Object.keys(APPLE_HOTKEYS))
|
||||||
|
.concat(Object.keys(WINDOWS_HOTKEYS))
|
||||||
|
|
||||||
const isBackspace = isKeyHotkey('backspace')
|
KEYS.forEach(key => {
|
||||||
const isShiftBackspace = isKeyHotkey('shift+backspace')
|
const method = `is${key[0].toUpperCase()}${key.slice(1)}`
|
||||||
const isDelete = isKeyHotkey('delete')
|
if (Hotkeys[method]) return
|
||||||
const isShiftDelete = isKeyHotkey('shift+delete')
|
|
||||||
const isDeleteBackward = e => isBackspace(e) || isShiftBackspace(e)
|
|
||||||
const isDeleteForward = e => isDelete(e) || isShiftDelete(e)
|
|
||||||
|
|
||||||
const isDeleteCharBackwardMac = isKeyHotkey('ctrl+h')
|
const generic = HOTKEYS[key]
|
||||||
const isDeleteCharForwardMac = isKeyHotkey('ctrl+d')
|
const apple = APPLE_HOTKEYS[key]
|
||||||
const isDeleteCharBackward = e =>
|
const windows = WINDOWS_HOTKEYS[key]
|
||||||
isDeleteBackward(e) || (IS_APPLE && isDeleteCharBackwardMac(e))
|
|
||||||
const isDeleteCharForward = e =>
|
|
||||||
isDeleteForward(e) || (IS_APPLE && isDeleteCharForwardMac(e))
|
|
||||||
|
|
||||||
const isDeleteLineBackwardMac = e =>
|
const isGeneric = generic && isKeyHotkey(generic)
|
||||||
isKeyHotkey('cmd+shift+backspace', e) || isKeyHotkey('cmd+backspace', e)
|
const isApple = apple && isKeyHotkey(apple)
|
||||||
const isDeleteLineForwardMac = isKeyHotkey('ctrl+k')
|
const isWindows = windows && isKeyHotkey(windows)
|
||||||
const isDeleteLineBackward = e => IS_APPLE && isDeleteLineBackwardMac(e)
|
|
||||||
const isDeleteLineForward = e => IS_APPLE && isDeleteLineForwardMac(e)
|
|
||||||
|
|
||||||
const isDeleteWordBackwardMac = e =>
|
Hotkeys[method] = event => {
|
||||||
isKeyHotkey('shift+option+backspace', e) || isKeyHotkey('option+backspace', e)
|
if (isGeneric && isGeneric(event)) return true
|
||||||
const isDeleteWordBackwardPC = isKeyHotkey('ctrl+backspace')
|
if (IS_APPLE && isApple && isApple(event)) return true
|
||||||
const isDeleteWordForwardMac = e =>
|
if (IS_WINDOWS && isWindows && isWindows(event)) return true
|
||||||
isKeyHotkey('shift+option+delete', e) || isKeyHotkey('option+delete', e)
|
return false
|
||||||
const isDeleteWordForwardPC = isKeyHotkey('ctrl+delete')
|
}
|
||||||
const isDeleteWordBackward = e =>
|
})
|
||||||
IS_APPLE ? isDeleteWordBackwardMac(e) : isDeleteWordBackwardPC(e)
|
|
||||||
const isDeleteWordForward = e =>
|
|
||||||
IS_APPLE ? isDeleteWordForwardMac(e) : isDeleteWordForwardPC(e)
|
|
||||||
|
|
||||||
const isExtendCharForward = isKeyHotkey('shift+right')
|
|
||||||
const isExtendCharBackward = isKeyHotkey('shift+left')
|
|
||||||
|
|
||||||
const isRightArrow = isKeyHotkey('right')
|
|
||||||
const isLeftArrow = isKeyHotkey('left')
|
|
||||||
const isCollapseCharForward = e => isRightArrow(e) && !isExtendCharForward(e)
|
|
||||||
const isCollapseCharBackward = e => isLeftArrow(e) && !isExtendCharBackward(e)
|
|
||||||
|
|
||||||
const isCollapseLineBackwardMac = isKeyHotkey('option+up')
|
|
||||||
const isCollapseLineForwardMac = isKeyHotkey('option+down')
|
|
||||||
const isCollapseLineBackward = e => IS_APPLE && isCollapseLineBackwardMac(e)
|
|
||||||
const isCollapseLineForward = e => IS_APPLE && isCollapseLineForwardMac(e)
|
|
||||||
|
|
||||||
const isExtendLineBackwardMac = isKeyHotkey('option+shift+up')
|
|
||||||
const isExtendLineForwardMac = isKeyHotkey('option+shift+down')
|
|
||||||
const isExtendLineBackward = e => IS_APPLE && isExtendLineBackwardMac(e)
|
|
||||||
const isExtendLineForward = e => IS_APPLE && isExtendLineForwardMac(e)
|
|
||||||
|
|
||||||
const isUndo = isKeyHotkey('mod+z')
|
|
||||||
const isRedoMac = isKeyHotkey('mod+shift+z')
|
|
||||||
const isRedoPC = isKeyHotkey('mod+y')
|
|
||||||
const isRedo = e => (IS_APPLE ? isRedoMac(e) : isRedoPC(e))
|
|
||||||
|
|
||||||
const isTransposeCharacterMac = isKeyHotkey('ctrl+t')
|
|
||||||
const isTransposeCharacter = e => IS_APPLE && isTransposeCharacterMac(e)
|
|
||||||
|
|
||||||
const isContentEditable = e =>
|
|
||||||
isBold(e) ||
|
|
||||||
isDeleteCharBackward(e) ||
|
|
||||||
isDeleteCharForward(e) ||
|
|
||||||
isDeleteLineBackward(e) ||
|
|
||||||
isDeleteLineForward(e) ||
|
|
||||||
isDeleteWordBackward(e) ||
|
|
||||||
isDeleteWordForward(e) ||
|
|
||||||
isItalic(e) ||
|
|
||||||
isRedo(e) ||
|
|
||||||
isSplitBlock(e) ||
|
|
||||||
isTransposeCharacter(e) ||
|
|
||||||
isUndo(e)
|
|
||||||
|
|
||||||
const isComposing = e =>
|
|
||||||
e.key == 'ArrowDown' ||
|
|
||||||
e.key == 'ArrowLeft' ||
|
|
||||||
e.key == 'ArrowRight' ||
|
|
||||||
e.key == 'ArrowUp' ||
|
|
||||||
e.key == 'Backspace' ||
|
|
||||||
e.key == 'Enter'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export.
|
* Export.
|
||||||
@@ -107,26 +83,4 @@ const isComposing = e =>
|
|||||||
* @type {Object}
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
export default Hotkeys
|
||||||
isBold,
|
|
||||||
isCollapseCharBackward,
|
|
||||||
isCollapseCharForward,
|
|
||||||
isCollapseLineBackward,
|
|
||||||
isCollapseLineForward,
|
|
||||||
isComposing,
|
|
||||||
isContentEditable,
|
|
||||||
isDeleteCharBackward,
|
|
||||||
isDeleteCharForward,
|
|
||||||
isDeleteLineBackward,
|
|
||||||
isDeleteLineForward,
|
|
||||||
isDeleteWordBackward,
|
|
||||||
isDeleteWordForward,
|
|
||||||
isExtendCharBackward,
|
|
||||||
isExtendCharForward,
|
|
||||||
isExtendLineBackward,
|
|
||||||
isExtendLineForward,
|
|
||||||
isItalic,
|
|
||||||
isRedo,
|
|
||||||
isSplitBlock,
|
|
||||||
isUndo,
|
|
||||||
}
|
|
||||||
|
@@ -377,11 +377,11 @@ function AfterPlugin() {
|
|||||||
: change.splitBlock()
|
: change.splitBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Hotkeys.isDeleteCharBackward(event) && !IS_IOS) {
|
if (Hotkeys.isDeleteBackward(event) && !IS_IOS) {
|
||||||
return change.deleteCharBackward()
|
return change.deleteCharBackward()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Hotkeys.isDeleteCharForward(event) && !IS_IOS) {
|
if (Hotkeys.isDeleteForward(event) && !IS_IOS) {
|
||||||
return change.deleteCharForward()
|
return change.deleteCharForward()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,12 +412,12 @@ function AfterPlugin() {
|
|||||||
// COMPAT: Certain browsers don't handle the selection updates properly. In
|
// COMPAT: Certain browsers don't handle the selection updates properly. In
|
||||||
// Chrome, the selection isn't properly extended. And in Firefox, the
|
// Chrome, the selection isn't properly extended. And in Firefox, the
|
||||||
// selection isn't properly collapsed. (2017/10/17)
|
// selection isn't properly collapsed. (2017/10/17)
|
||||||
if (Hotkeys.isCollapseLineBackward(event)) {
|
if (Hotkeys.isMoveLineBackward(event)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return change.moveToStartOfBlock()
|
return change.moveToStartOfBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Hotkeys.isCollapseLineForward(event)) {
|
if (Hotkeys.isMoveLineForward(event)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return change.moveToEndOfBlock()
|
return change.moveToEndOfBlock()
|
||||||
}
|
}
|
||||||
@@ -435,7 +435,7 @@ function AfterPlugin() {
|
|||||||
// COMPAT: If a void node is selected, or a zero-width text node adjacent to
|
// COMPAT: If a void node is selected, or a zero-width text node adjacent to
|
||||||
// an inline is selected, we need to handle these hotkeys manually because
|
// an inline is selected, we need to handle these hotkeys manually because
|
||||||
// browsers won't know what to do.
|
// browsers won't know what to do.
|
||||||
if (Hotkeys.isCollapseCharBackward(event)) {
|
if (Hotkeys.isMoveBackward(event)) {
|
||||||
const { document, isInVoid, previousText, startText } = value
|
const { document, isInVoid, previousText, startText } = value
|
||||||
const isPreviousInVoid =
|
const isPreviousInVoid =
|
||||||
previousText && document.hasVoidParent(previousText.key)
|
previousText && document.hasVoidParent(previousText.key)
|
||||||
@@ -446,7 +446,7 @@ function AfterPlugin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Hotkeys.isCollapseCharForward(event)) {
|
if (Hotkeys.isMoveForward(event)) {
|
||||||
const { document, isInVoid, nextText, startText } = value
|
const { document, isInVoid, nextText, startText } = value
|
||||||
const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
|
const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
|
||||||
|
|
||||||
@@ -456,7 +456,7 @@ function AfterPlugin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Hotkeys.isExtendCharBackward(event)) {
|
if (Hotkeys.isExtendBackward(event)) {
|
||||||
const { document, isInVoid, previousText, startText } = value
|
const { document, isInVoid, previousText, startText } = value
|
||||||
const isPreviousInVoid =
|
const isPreviousInVoid =
|
||||||
previousText && document.hasVoidParent(previousText.key)
|
previousText && document.hasVoidParent(previousText.key)
|
||||||
@@ -467,7 +467,7 @@ function AfterPlugin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Hotkeys.isExtendCharForward(event)) {
|
if (Hotkeys.isExtendForward(event)) {
|
||||||
const { document, isInVoid, nextText, startText } = value
|
const { document, isInVoid, nextText, startText } = value
|
||||||
const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
|
const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
|
||||||
|
|
||||||
|
@@ -387,13 +387,28 @@ function BeforePlugin() {
|
|||||||
// typing. However, certain characters also move the selection before
|
// typing. However, certain characters also move the selection before
|
||||||
// we're able to handle it, so prevent their default behavior.
|
// we're able to handle it, so prevent their default behavior.
|
||||||
if (isComposing) {
|
if (isComposing) {
|
||||||
if (Hotkeys.isComposing(event)) event.preventDefault()
|
if (Hotkeys.isCompose(event)) event.preventDefault()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Certain hotkeys have native behavior in contenteditable elements which
|
// Certain hotkeys have native editing behaviors in `contenteditable`
|
||||||
// will cause our value to be out of sync, so prevent them.
|
// elements which will change the DOM and cause our value to be out of sync,
|
||||||
if (Hotkeys.isContentEditable(event) && !IS_IOS) {
|
// so they need to always be prevented.
|
||||||
|
if (
|
||||||
|
!IS_IOS &&
|
||||||
|
(Hotkeys.isBold(event) ||
|
||||||
|
Hotkeys.isDeleteBackward(event) ||
|
||||||
|
Hotkeys.isDeleteForward(event) ||
|
||||||
|
Hotkeys.isDeleteLineBackward(event) ||
|
||||||
|
Hotkeys.isDeleteLineForward(event) ||
|
||||||
|
Hotkeys.isDeleteWordBackward(event) ||
|
||||||
|
Hotkeys.isDeleteWordForward(event) ||
|
||||||
|
Hotkeys.isItalic(event) ||
|
||||||
|
Hotkeys.isRedo(event) ||
|
||||||
|
Hotkeys.isSplitBlock(event) ||
|
||||||
|
Hotkeys.isTransposeCharacter(event) ||
|
||||||
|
Hotkeys.isUndo(event))
|
||||||
|
) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4706,6 +4706,10 @@ is-hotkey@^0.1.1:
|
|||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.1.1.tgz#b279a2fd108391be9aa93c6cb317f50357da549a"
|
resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.1.1.tgz#b279a2fd108391be9aa93c6cb317f50357da549a"
|
||||||
|
|
||||||
|
is-hotkey@^0.1.3:
|
||||||
|
version "0.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.1.3.tgz#8a129eec16f3941bd4f37191e02b9c3e91950549"
|
||||||
|
|
||||||
is-in-browser@^1.1.3:
|
is-in-browser@^1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
|
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
|
||||||
|
Reference in New Issue
Block a user