draft-js
Version:
A React framework for building text editors.
220 lines (157 loc) • 5.95 kB
JavaScript
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*
* @emails oncall+draft_js
*/
'use strict';
var DraftModifier = require("./DraftModifier");
var EditorState = require("./EditorState");
var KeyBindingUtil = require("./KeyBindingUtil");
var Keys = require("fbjs/lib/Keys");
var SecondaryClipboard = require("./SecondaryClipboard");
var UserAgent = require("fbjs/lib/UserAgent");
var isEventHandled = require("./isEventHandled");
var keyCommandBackspaceToStartOfLine = require("./keyCommandBackspaceToStartOfLine");
var keyCommandBackspaceWord = require("./keyCommandBackspaceWord");
var keyCommandDeleteWord = require("./keyCommandDeleteWord");
var keyCommandInsertNewline = require("./keyCommandInsertNewline");
var keyCommandMoveSelectionToEndOfBlock = require("./keyCommandMoveSelectionToEndOfBlock");
var keyCommandMoveSelectionToStartOfBlock = require("./keyCommandMoveSelectionToStartOfBlock");
var keyCommandPlainBackspace = require("./keyCommandPlainBackspace");
var keyCommandPlainDelete = require("./keyCommandPlainDelete");
var keyCommandTransposeCharacters = require("./keyCommandTransposeCharacters");
var keyCommandUndo = require("./keyCommandUndo");
var isOptionKeyCommand = KeyBindingUtil.isOptionKeyCommand;
var isChrome = UserAgent.isBrowser('Chrome');
/**
* Map a `DraftEditorCommand` command value to a corresponding function.
*/
function onKeyCommand(command, editorState, e) {
switch (command) {
case 'redo':
return EditorState.redo(editorState);
case 'delete':
return keyCommandPlainDelete(editorState);
case 'delete-word':
return keyCommandDeleteWord(editorState);
case 'backspace':
return keyCommandPlainBackspace(editorState);
case 'backspace-word':
return keyCommandBackspaceWord(editorState);
case 'backspace-to-start-of-line':
return keyCommandBackspaceToStartOfLine(editorState, e);
case 'split-block':
return keyCommandInsertNewline(editorState);
case 'transpose-characters':
return keyCommandTransposeCharacters(editorState);
case 'move-selection-to-start-of-block':
return keyCommandMoveSelectionToStartOfBlock(editorState);
case 'move-selection-to-end-of-block':
return keyCommandMoveSelectionToEndOfBlock(editorState);
case 'secondary-cut':
return SecondaryClipboard.cut(editorState);
case 'secondary-paste':
return SecondaryClipboard.paste(editorState);
default:
return editorState;
}
}
/**
* Intercept keydown behavior to handle keys and commands manually, if desired.
*
* Keydown combinations may be mapped to `DraftCommand` values, which may
* correspond to command functions that modify the editor or its contents.
*
* See `getDefaultKeyBinding` for defaults. Alternatively, the top-level
* component may provide a custom mapping via the `keyBindingFn` prop.
*/
function editOnKeyDown(editor, e) {
var keyCode = e.which;
var editorState = editor._latestEditorState;
function callDeprecatedHandler(handlerName) {
var deprecatedHandler = editor.props[handlerName];
if (deprecatedHandler) {
deprecatedHandler(e);
return true;
} else {
return false;
}
}
switch (keyCode) {
case Keys.RETURN:
e.preventDefault(); // The top-level component may manually handle newline insertion. If
// no special handling is performed, fall through to command handling.
if (editor.props.handleReturn && isEventHandled(editor.props.handleReturn(e, editorState))) {
return;
}
break;
case Keys.ESC:
e.preventDefault();
if (callDeprecatedHandler('onEscape')) {
return;
}
break;
case Keys.TAB:
if (callDeprecatedHandler('onTab')) {
return;
}
break;
case Keys.UP:
if (callDeprecatedHandler('onUpArrow')) {
return;
}
break;
case Keys.RIGHT:
if (callDeprecatedHandler('onRightArrow')) {
return;
}
break;
case Keys.DOWN:
if (callDeprecatedHandler('onDownArrow')) {
return;
}
break;
case Keys.LEFT:
if (callDeprecatedHandler('onLeftArrow')) {
return;
}
break;
case Keys.SPACE:
// Prevent Chrome on OSX behavior where option + space scrolls.
if (isChrome && isOptionKeyCommand(e)) {
e.preventDefault();
}
}
var command = editor.props.keyBindingFn(e); // If no command is specified, allow keydown event to continue.
if (command == null || command === '') {
if (keyCode === Keys.SPACE && isChrome && isOptionKeyCommand(e)) {
// The default keydown event has already been prevented in order to stop
// Chrome from scrolling. Insert a nbsp into the editor as OSX would for
// other browsers.
var contentState = DraftModifier.replaceText(editorState.getCurrentContent(), editorState.getSelection(), "\xA0");
editor.update(EditorState.push(editorState, contentState, 'insert-characters'));
}
return;
}
if (command === 'undo') {
// Since undo requires some special updating behavior to keep the editor
// in sync, handle it separately.
keyCommandUndo(e, editorState, editor.update);
return;
} // At this point, we know that we're handling a command of some kind, so
// we don't want to insert a character following the keydown.
e.preventDefault(); // Allow components higher up the tree to handle the command first.
if (editor.props.handleKeyCommand && isEventHandled(editor.props.handleKeyCommand(command, editorState, e.timeStamp))) {
return;
}
var newState = onKeyCommand(command, editorState, e);
if (newState !== editorState) {
editor.update(newState);
}
}
module.exports = editOnKeyDown;