1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-15 03:33:59 +02:00

fix prevent default on key down

This commit is contained in:
Ian Storm Taylor
2016-06-27 17:16:18 -07:00
parent 5d21a315f7
commit 0c22e6172b
4 changed files with 124 additions and 110 deletions

View File

@@ -4,6 +4,7 @@ import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import Text from './text' import Text from './text'
import keycode from 'keycode' import keycode from 'keycode'
import { isCommand, isWindowsCommand } from '../utils/event'
/** /**
* Content. * Content.
@@ -64,6 +65,31 @@ class Content extends React.Component {
this.props[name](e) this.props[name](e)
} }
/**
* On key down, prevent the default behavior of certain commands that will
* leave the editor in an out-of-sync state, then bubble up.
*
* @param {Event} e
*/
onKeyDown(e) {
const key = keycode(e.which)
if (
(key == 'enter') ||
(key == 'backspace') ||
(key == 'delete') ||
(key == 'b' && isCommand(e)) ||
(key == 'i' && isCommand(e)) ||
(key == 'y' && isWindowsCommand(e)) ||
(key == 'z' && isCommand(e))
) {
e.preventDefault()
}
this.props.onKeyDown(e)
}
/** /**
* On paste, determine the type and bubble up. * On paste, determine the type and bubble up.
* *
@@ -110,8 +136,7 @@ class Content extends React.Component {
let { document, selection } = state let { document, selection } = state
const native = window.getSelection() const native = window.getSelection()
// No selection is active, so unset `isFocused`. if (!native.rangeCount) {
if (!native.rangeCount && selection.isFocused) {
selection = selection.merge({ isFocused: false }) selection = selection.merge({ isFocused: false })
state = state.merge({ selection }) state = state.merge({ selection })
this.onChange(state) this.onChange(state)
@@ -122,24 +147,16 @@ class Content extends React.Component {
const { anchorNode, anchorOffset, focusNode, focusOffset } = native const { anchorNode, anchorOffset, focusNode, focusOffset } = native
const anchor = OffsetKey.findPoint(anchorNode, anchorOffset) const anchor = OffsetKey.findPoint(anchorNode, anchorOffset)
const focus = OffsetKey.findPoint(focusNode, focusOffset) const focus = OffsetKey.findPoint(focusNode, focusOffset)
const edges = document.filterDescendants((node) => {
return node.key == anchor.key || node.key == focus.key
})
const isBackward = (
(edges.size == 2 && edges.first().key == focus.key) ||
(edges.size == 1 && anchor.offset > focus.offset)
)
selection = selection.merge({ selection = selection.merge({
anchorKey: anchor.key, anchorKey: anchor.key,
anchorOffset: anchor.offset, anchorOffset: anchor.offset,
focusKey: focus.key, focusKey: focus.key,
focusOffset: focus.offset, focusOffset: focus.offset,
isBackward: isBackward,
isFocused: true isFocused: true
}) })
selection = selection.normalize(document)
state = state.merge({ selection }) state = state.merge({ selection })
this.onChange(state) this.onChange(state)
} }
@@ -167,11 +184,11 @@ class Content extends React.Component {
<div <div
contentEditable suppressContentEditableWarning contentEditable suppressContentEditableWarning
style={style} style={style}
onKeyDown={e => this.onKeyDown(e)}
onSelect={e => this.onSelect(e)} onSelect={e => this.onSelect(e)}
onPaste={e => this.onPaste(e)} onPaste={e => this.onPaste(e)}
onCopy={e => this.onEvent('onCopy', e)} onCopy={e => this.onEvent('onCopy', e)}
onCut={e => this.onEvent('onCut', e)} onCut={e => this.onEvent('onCut', e)}
onKeyDown={e => this.onEvent('onKeyDown', e)}
onBeforeInput={e => this.onEvent('onBeforeInput', e)} onBeforeInput={e => this.onEvent('onBeforeInput', e)}
> >
{children} {children}

View File

@@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import keycode from 'keycode' import keycode from 'keycode'
import { isCommand, isCtrl, isWindowsCommand, isWord } from '../utils/event'
import { IS_WINDOWS, IS_MAC } from '../utils/environment' import { IS_WINDOWS, IS_MAC } from '../utils/environment'
/** /**
@@ -71,7 +72,6 @@ export default {
switch (key) { switch (key) {
case 'enter': { case 'enter': {
e.preventDefault()
return state return state
.transform() .transform()
.splitBlock() .splitBlock()
@@ -79,9 +79,6 @@ export default {
} }
case 'backspace': { case 'backspace': {
// COMPAT: Windows has a special "cut" behavior for the shift key.
if (IS_WINDOWS && e.shiftKey) return
e.preventDefault()
return isWord(e) return isWord(e)
? state ? state
.transform() .transform()
@@ -94,9 +91,6 @@ export default {
} }
case 'delete': { case 'delete': {
// COMPAT: Windows has a special "cut" behavior for the shift key.
if (IS_WINDOWS && e.shiftKey) return
e.preventDefault()
return isWord(e) return isWord(e)
? state ? state
.transform() .transform()
@@ -108,19 +102,8 @@ export default {
.apply() .apply()
} }
case 'b':
case 'i': {
if (!isCommand(e)) return
// COMPAT: Prevent the built-in contenteditable bold and italic
// shortcuts. They should be re-added at a higher level that is
// state-aware if you want to allow for them.
e.preventDefault()
return
}
case 'y': { case 'y': {
if (!isCtrl(e) || !IS_WINDOWS) return if (!isWindowsCommand(e)) return
e.preventDefault()
return state return state
.transform() .transform()
.redo() .redo()
@@ -128,7 +111,6 @@ export default {
case 'z': { case 'z': {
if (!isCommand(e)) return if (!isCommand(e)) return
e.preventDefault()
return IS_MAC && e.shiftKey return IS_MAC && e.shiftKey
? state ? state
.transform() .transform()
@@ -206,61 +188,3 @@ export default {
} }
/**
* Does an `e` have the word-level modifier?
*
* @param {Event} e
* @return {Boolean}
*/
function isWord(e) {
if (IS_MAC && e.altKey) return true
if (e.ctrlKey) return true
return false
}
/**
* 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 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 command modifier?
*
* @param {Event} e
* @return {Boolean}
*/
function isCommand(e) {
return IS_MAC
? e.metaKey && !e.altKey
: e.ctrlKey && !e.altKey
}

View File

@@ -2,27 +2,17 @@
import browser from 'detect-browser' import browser from 'detect-browser'
import Parser from 'ua-parser-js' import Parser from 'ua-parser-js'
/**
* Read the environment.
*/
const ENVIRONMENT = process.browser
? {
IS_ANDROID: browser.name === 'android',
IS_CHROME: browser.name === 'chrome',
IS_EDGE: browser.name === 'edge',
IS_FIREFOX: browser.name === 'firefox',
IS_IE: browser.name === 'ie',
IS_IOS: browser.name === 'ios',
IS_MAC: new Parser().getOS().name === 'Mac OS',
IS_UBUNTU: new Parser().getOS().name === 'Ubuntu',
IS_SAFARI: browser.name === 'safari',
IS_WINDOWS: new Parser().getOS().name.includes('Windows')
}
: {}
/** /**
* Export. * Export.
*/ */
export default ENVIRONMENT export const IS_ANDROID = process.browser && browser.name === 'android'
export const IS_CHROME = process.browser && browser.name === 'chrome'
export const IS_EDGE = process.browser && browser.name === 'edge'
export const IS_FIREFOX = process.browser && browser.name === 'firefox'
export const IS_IE = process.browser && browser.name === 'ie'
export const IS_IOS = process.browser && browser.name === 'ios'
export const IS_MAC = process.browser && new Parser().getOS().name === 'Mac OS'
export const IS_SAFARI = process.browser && browser.name === 'safari'
export const IS_UBUNTU = process.browser && new Parser().getOS().name === 'Ubuntu'
export const IS_WINDOWS = process.browser && new Parser().getOS().name.includes('Windows')

83
lib/utils/event.js Normal file
View File

@@ -0,0 +1,83 @@
import { IS_MAC, IS_WINDOWS } from './environment'
/**
* Does an `e` have the word-level modifier?
*
* @param {Event} e
* @return {Boolean}
*/
export function isWord(e) {
return IS_MAC
? e.altKey
: e.ctrlKey
}
/**
* Does an `e` have the control modifier?
*
* @param {Event} e
* @return {Boolean}
*/
export function isCtrl(e) {
return e.ctrlKey && !e.altKey
}
/**
* Does an `e` have the option modifier?
*
* @param {Event} e
* @return {Boolean}
*/
export function isOption(e) {
return IS_MAC && e.altKey
}
/**
* Does an `e` have the shift modifier?
*
* @param {Event} e
* @return {Boolean}
*/
export function isShift(e) {
return e.shiftKey
}
/**
* Does an `e` have the command modifier?
*
* @param {Event} e
* @return {Boolean}
*/
export function isCommand(e) {
return IS_MAC
? e.metaKey && !e.altKey
: e.ctrlKey && !e.altKey
}
/**
* Does an `e` have the Mac command modifier?
*
* @param {Event} e
* @return {Boolean}
*/
export function isMacCommand(e) {
return IS_MAC && isCommand(e)
}
/**
* Does an `e` have the Windows command modifier?
*
* @param {Event} e
* @return {Boolean}
*/
export function isWindowsCommand(e) {
return IS_WINDOWS && isCommand(e)
}