1
0
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:
Ian Storm Taylor
2018-08-07 15:58:33 -07:00
committed by GitHub
parent 3eabdea8d6
commit fd9243b8f5
6 changed files with 101 additions and 120 deletions

View File

@@ -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:

View File

@@ -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": {

View File

@@ -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,
}

View File

@@ -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)

View File

@@ -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()
} }

View File

@@ -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"