Skip to content
This repository was archived by the owner on Feb 6, 2023. It is now read-only.

Commit dc5ca7f

Browse files
steveluscherfacebook-github-bot
authored andcommitted
Deprecated the coarse onArrowUp et al key handler props on DraftEditor.react to make it possible to produce editor commands from these keys
Summary: Previously, it was impossible to do this: ``` function mapKeysToEditorCommands(e) { if (mentionsAutocompleteIsOpen) { switch (e.keyCode) { case Keys.UP: return 'MentionsAutocomplete/select-previous'; case Keys.DOWN: return 'MentionsAutocomplete/select-next'; } } return getDefaultKeyBinding(e); } <DraftEditor keyBindingFn={mapKeysToEditorCommands} ... /> ``` …because we didn't give TAB, ESCAPE, LEFT, UP, RIGHT, or DOWN a chance to produce a command name. This pull request: 1. deprecates the prop-based key handlers (`onUpArrow` et al.) and encourages people to move their custom key logic into a `keyBindingFn`, and 2. lets the named keypresses fall through to the `keyBindingFn` whenever a prop-based key handler isn't supplied. 3. Updates the homepage and the rich text example. ![tabs](https://user-images.githubusercontent.com/13243/35651838-f129bf4a-0695-11e8-971b-38dbae48fd23.gif) Closes #1637 Differential Revision: D6864155 fbshipit-source-id: 23efb4caac25b1bc867350a1212f142b5b3ce7ab
1 parent cda13cb commit dc5ca7f

File tree

6 files changed

+107
-59
lines changed

6 files changed

+107
-59
lines changed

docs/APIReference-Editor.md

+10-33
Original file line numberDiff line numberDiff line change
@@ -262,45 +262,22 @@ Handle other drop operations.
262262

263263
### Key Handlers (Optional)
264264

265-
These prop functions expose common useful key events. Example: At Facebook, these are
266-
used to provide keyboard interaction for mention results in inputs.
265+
Draft lets you supply a custom `keyDown` handler that wraps or overrides its
266+
default one.
267267

268-
#### onEscape
269-
```
270-
onEscape?: (e: SyntheticKeyboardEvent) => void
271-
```
272-
273-
#### onTab
274-
```
275-
onTab?: (e: SyntheticKeyboardEvent) => void
276-
```
277-
278-
#### onUpArrow
279-
```
280-
onUpArrow?: (e: SyntheticKeyboardEvent) => void
281-
```
282-
283-
#### onRightArrow
284-
```
285-
onRightArrow?: (e: SyntheticKeyboardEvent) => void
286-
```
287-
288-
#### onDownArrow
289-
```
290-
onDownArrow?: (e: SyntheticKeyboardEvent) => void
291-
```
292268
#### keyBindingFn
293269

294270
```
295-
keyBindingFn?: (e: SyntheticKeyboardEvent) => void
271+
keyBindingFn?: (e: SyntheticKeyboardEvent) => ?string
296272
```
297273

298-
This prop lets you handle key events directly and provides an opportunity to return custom editor commands. You can find a more detailed explanation of this [here](/docs/advanced-topics-key-bindings.html).
299-
300-
#### onLeftArrow
301-
```
302-
onLeftArrow?: (e: SyntheticKeyboardEvent) => void
303-
```
274+
This prop function exposes `keyDown` events to a handler of your choosing. If an
275+
event of interest happens, you can perform custom logic and/or return a string
276+
corresponding to a `DraftEditorCommand` or a custom editor command of your
277+
own creation. Example: At Facebook, this is used to provide keyboard interaction
278+
for the mentions autocomplete menu that appears when typing a friend's name.
279+
You can find a more detailed explanation of this
280+
[here](/docs/advanced-topics-key-bindings.html).
304281

305282
### Mouse events
306283

examples/draft-0-10-0/rich/rich.html

+17-6
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
<script type="text/babel">
3434
'use strict';
3535

36-
const {Editor, EditorState, RichUtils} = Draft;
36+
const {Editor, EditorState, RichUtils, getDefaultKeyBinding} = Draft;
3737

3838
class RichEditorExample extends React.Component {
3939
constructor(props) {
@@ -44,7 +44,7 @@
4444
this.onChange = (editorState) => this.setState({editorState});
4545

4646
this.handleKeyCommand = this._handleKeyCommand.bind(this);
47-
this.onTab = this._onTab.bind(this);
47+
this.mapKeyToEditorCommand = this._mapKeyToEditorCommand.bind(this);
4848
this.toggleBlockType = this._toggleBlockType.bind(this);
4949
this.toggleInlineStyle = this._toggleInlineStyle.bind(this);
5050
}
@@ -58,9 +58,20 @@
5858
return false;
5959
}
6060

61-
_onTab(e) {
62-
const maxDepth = 4;
63-
this.onChange(RichUtils.onTab(e, this.state.editorState, maxDepth));
61+
_mapKeyToEditorCommand(e) {
62+
switch (e.keyCode) {
63+
case 9: // TAB
64+
const newEditorState = RichUtils.onTab(
65+
e,
66+
this.state.editorState,
67+
4, /* maxDepth */
68+
);
69+
if (newEditorState !== this.state.editorState) {
70+
this.onChange(newEditorState);
71+
}
72+
return;
73+
}
74+
return getDefaultKeyBinding(e);
6475
}
6576

6677
_toggleBlockType(blockType) {
@@ -110,8 +121,8 @@
110121
customStyleMap={styleMap}
111122
editorState={editorState}
112123
handleKeyCommand={this.handleKeyCommand}
124+
keyBindingFn={this.mapKeyToEditorCommand}
113125
onChange={this.onChange}
114-
onTab={this.onTab}
115126
placeholder="Tell a story..."
116127
ref="editor"
117128
spellCheck={true}

src/component/base/DraftEditor.react.js

+21
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,27 @@ class DraftEditor extends React.Component<DraftEditorProps, State> {
165165

166166
this.getEditorKey = () => this._editorKey;
167167

168+
if (__DEV__) {
169+
[
170+
'onDownArrow',
171+
'onEscape',
172+
'onLeftArrow',
173+
'onRightArrow',
174+
'onTab',
175+
'onUpArrow',
176+
].forEach(propName => {
177+
if (props.hasOwnProperty(propName)) {
178+
// eslint-disable-next-line no-console
179+
console.warn(
180+
`Supplying an \`${propName}\` prop to \`DraftEditor\` has ` +
181+
'been deprecated. If your handler needs access to the keyboard ' +
182+
'event, supply a custom `keyBindingFn` prop that falls back to ' +
183+
'the default one (eg. https://is.gd/RG31RJ).',
184+
);
185+
}
186+
});
187+
}
188+
168189
// See `restoreEditorDOM()`.
169190
this.state = {contentsKey: 0};
170191
}

src/component/base/DraftEditorProps.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export type DraftEditorProps = {
153153
) => DraftHandleValue,
154154

155155
/**
156-
* Non-cancelable event triggers.
156+
* Deprecated event triggers.
157157
*/
158158
onEscape?: (e: SyntheticKeyboardEvent<>) => void,
159159
onTab?: (e: SyntheticKeyboardEvent<>) => void,

src/component/handlers/edit/editOnKeyDown.js

+41-13
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,23 @@ function onKeyCommand(
8787
function editOnKeyDown(editor: DraftEditor, e: SyntheticKeyboardEvent<>): void {
8888
var keyCode = e.which;
8989
var editorState = editor._latestEditorState;
90-
90+
function callDeprecatedHandler(
91+
handlerName:
92+
| 'onDownArrow'
93+
| 'onEscape'
94+
| 'onLeftArrow'
95+
| 'onRightArrow'
96+
| 'onTab'
97+
| 'onUpArrow',
98+
): boolean {
99+
const deprecatedHandler = editor.props[handlerName];
100+
if (deprecatedHandler) {
101+
deprecatedHandler(e);
102+
return true;
103+
} else {
104+
return false;
105+
}
106+
}
91107
switch (keyCode) {
92108
case Keys.RETURN:
93109
e.preventDefault();
@@ -102,23 +118,35 @@ function editOnKeyDown(editor: DraftEditor, e: SyntheticKeyboardEvent<>): void {
102118
break;
103119
case Keys.ESC:
104120
e.preventDefault();
105-
editor.props.onEscape && editor.props.onEscape(e);
106-
return;
121+
if (callDeprecatedHandler('onEscape')) {
122+
return;
123+
}
124+
break;
107125
case Keys.TAB:
108-
editor.props.onTab && editor.props.onTab(e);
109-
return;
126+
if (callDeprecatedHandler('onTab')) {
127+
return;
128+
}
129+
break;
110130
case Keys.UP:
111-
editor.props.onUpArrow && editor.props.onUpArrow(e);
112-
return;
131+
if (callDeprecatedHandler('onUpArrow')) {
132+
return;
133+
}
134+
break;
113135
case Keys.RIGHT:
114-
editor.props.onRightArrow && editor.props.onRightArrow(e);
115-
return;
136+
if (callDeprecatedHandler('onRightArrow')) {
137+
return;
138+
}
139+
break;
116140
case Keys.DOWN:
117-
editor.props.onDownArrow && editor.props.onDownArrow(e);
118-
return;
141+
if (callDeprecatedHandler('onDownArrow')) {
142+
return;
143+
}
144+
break;
119145
case Keys.LEFT:
120-
editor.props.onLeftArrow && editor.props.onLeftArrow(e);
121-
return;
146+
if (callDeprecatedHandler('onLeftArrow')) {
147+
return;
148+
}
149+
break;
122150
case Keys.SPACE:
123151
// Handling for OSX where option + space scrolls.
124152
if (isChrome && isOptionKeyCommand(e)) {

website/src/index.js

+17-6
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ var unindent = require('unindent');
2121
var richExample = `
2222
'use strict';
2323
24-
const {Editor, EditorState, RichUtils} = Draft;
24+
const {Editor, EditorState, RichUtils, getDefaultKeyBinding} = Draft;
2525
2626
class RichEditorExample extends React.Component {
2727
constructor(props) {
@@ -32,7 +32,7 @@ class RichEditorExample extends React.Component {
3232
this.onChange = (editorState) => this.setState({editorState});
3333
3434
this.handleKeyCommand = this._handleKeyCommand.bind(this);
35-
this.onTab = this._onTab.bind(this);
35+
this.mapKeyToEditorCommand = this._mapKeyToEditorCommand.bind(this);
3636
this.toggleBlockType = this._toggleBlockType.bind(this);
3737
this.toggleInlineStyle = this._toggleInlineStyle.bind(this);
3838
}
@@ -46,9 +46,20 @@ class RichEditorExample extends React.Component {
4646
return false;
4747
}
4848
49-
_onTab(e) {
50-
const maxDepth = 4;
51-
this.onChange(RichUtils.onTab(e, this.state.editorState, maxDepth));
49+
_mapKeyToEditorCommand(e) {
50+
switch (e.keyCode) {
51+
case 9: // TAB
52+
const newEditorState = RichUtils.onTab(
53+
e,
54+
this.state.editorState,
55+
4, /* maxDepth */
56+
);
57+
if (newEditorState !== this.state.editorState) {
58+
this.onChange(newEditorState);
59+
}
60+
return;
61+
}
62+
return getDefaultKeyBinding(e);
5263
}
5364
5465
_toggleBlockType(blockType) {
@@ -98,8 +109,8 @@ class RichEditorExample extends React.Component {
98109
customStyleMap={styleMap}
99110
editorState={editorState}
100111
handleKeyCommand={this.handleKeyCommand}
112+
keyBindingFn={this.mapKeyToEditorCommand}
101113
onChange={this.onChange}
102-
onTab={this.onTab}
103114
placeholder="Tell a story..."
104115
ref={(ref) => this.editor = ref}
105116
spellCheck={true}

0 commit comments

Comments
 (0)