1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-04-21 13:51: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 Text from './text'
import keycode from 'keycode'
import { isCommand, isWindowsCommand } from '../utils/event'
/**
* Content.
@ -64,6 +65,31 @@ class Content extends React.Component {
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.
*
@ -110,8 +136,7 @@ class Content extends React.Component {
let { document, selection } = state
const native = window.getSelection()
// No selection is active, so unset `isFocused`.
if (!native.rangeCount && selection.isFocused) {
if (!native.rangeCount) {
selection = selection.merge({ isFocused: false })
state = state.merge({ selection })
this.onChange(state)
@ -122,24 +147,16 @@ class Content extends React.Component {
const { anchorNode, anchorOffset, focusNode, focusOffset } = native
const anchor = OffsetKey.findPoint(anchorNode, anchorOffset)
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({
anchorKey: anchor.key,
anchorOffset: anchor.offset,
focusKey: focus.key,
focusOffset: focus.offset,
isBackward: isBackward,
isFocused: true
})
selection = selection.normalize(document)
state = state.merge({ selection })
this.onChange(state)
}
@ -167,11 +184,11 @@ class Content extends React.Component {
<div
contentEditable suppressContentEditableWarning
style={style}
onKeyDown={e => this.onKeyDown(e)}
onSelect={e => this.onSelect(e)}
onPaste={e => this.onPaste(e)}
onCopy={e => this.onEvent('onCopy', e)}
onCut={e => this.onEvent('onCut', e)}
onKeyDown={e => this.onEvent('onKeyDown', e)}
onBeforeInput={e => this.onEvent('onBeforeInput', e)}
>
{children}

View File

@ -1,6 +1,7 @@
import React from 'react'
import keycode from 'keycode'
import { isCommand, isCtrl, isWindowsCommand, isWord } from '../utils/event'
import { IS_WINDOWS, IS_MAC } from '../utils/environment'
/**
@ -71,7 +72,6 @@ export default {
switch (key) {
case 'enter': {
e.preventDefault()
return state
.transform()
.splitBlock()
@ -79,9 +79,6 @@ export default {
}
case 'backspace': {
// COMPAT: Windows has a special "cut" behavior for the shift key.
if (IS_WINDOWS && e.shiftKey) return
e.preventDefault()
return isWord(e)
? state
.transform()
@ -94,9 +91,6 @@ export default {
}
case 'delete': {
// COMPAT: Windows has a special "cut" behavior for the shift key.
if (IS_WINDOWS && e.shiftKey) return
e.preventDefault()
return isWord(e)
? state
.transform()
@ -108,19 +102,8 @@ export default {
.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': {
if (!isCtrl(e) || !IS_WINDOWS) return
e.preventDefault()
if (!isWindowsCommand(e)) return
return state
.transform()
.redo()
@ -128,7 +111,6 @@ export default {
case 'z': {
if (!isCommand(e)) return
e.preventDefault()
return IS_MAC && e.shiftKey
? state
.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 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 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)
}