mirror of
https://github.com/element-hq/element-web
synced 2024-11-23 17:56:01 +03:00
actually hook up RTE
This commit is contained in:
parent
ae208da805
commit
e51554c626
2 changed files with 189 additions and 108 deletions
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
|
|
||||||
import { Value } from 'slate';
|
import { Value } from 'slate';
|
||||||
import Html from 'slate-html-serializer';
|
import Html from 'slate-html-serializer';
|
||||||
import { Markdown as Md } from 'slate-md-serializer';
|
import Md from 'slate-md-serializer';
|
||||||
import Plain from 'slate-plain-serializer';
|
import Plain from 'slate-plain-serializer';
|
||||||
import * as RichText from './RichText';
|
import * as RichText from './RichText';
|
||||||
import Markdown from './Markdown';
|
import Markdown from './Markdown';
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { Editor } from 'slate-react';
|
||||||
import { Value, Document, Event, Inline, Text, Range, Node } from 'slate';
|
import { Value, Document, Event, Inline, Text, Range, Node } from 'slate';
|
||||||
|
|
||||||
import Html from 'slate-html-serializer';
|
import Html from 'slate-html-serializer';
|
||||||
import { Markdown as Md } from 'slate-md-serializer';
|
import Md from 'slate-md-serializer';
|
||||||
import Plain from 'slate-plain-serializer';
|
import Plain from 'slate-plain-serializer';
|
||||||
import PlainWithPillsSerializer from "../../../autocomplete/PlainWithPillsSerializer";
|
import PlainWithPillsSerializer from "../../../autocomplete/PlainWithPillsSerializer";
|
||||||
|
|
||||||
|
@ -107,43 +107,6 @@ export default class MessageComposerInput extends React.Component {
|
||||||
onInputStateChanged: PropTypes.func,
|
onInputStateChanged: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
static getKeyBinding(ev: SyntheticKeyboardEvent): string {
|
|
||||||
// Restrict a subset of key bindings to ONLY having ctrl/meta* pressed and
|
|
||||||
// importantly NOT having alt, shift, meta/ctrl* pressed. draft-js does not
|
|
||||||
// handle this in `getDefaultKeyBinding` so we do it ourselves here.
|
|
||||||
//
|
|
||||||
// * if macOS, read second option
|
|
||||||
const ctrlCmdCommand = {
|
|
||||||
// C-m => Toggles between rich text and markdown modes
|
|
||||||
[KeyCode.KEY_M]: 'toggle-mode',
|
|
||||||
[KeyCode.KEY_B]: 'bold',
|
|
||||||
[KeyCode.KEY_I]: 'italic',
|
|
||||||
[KeyCode.KEY_U]: 'underline',
|
|
||||||
[KeyCode.KEY_J]: 'code',
|
|
||||||
[KeyCode.KEY_O]: 'split-block',
|
|
||||||
}[ev.keyCode];
|
|
||||||
|
|
||||||
if (ctrlCmdCommand) {
|
|
||||||
if (!isOnlyCtrlOrCmdKeyEvent(ev)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return ctrlCmdCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle keys such as return, left and right arrows etc.
|
|
||||||
return getDefaultKeyBinding(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static getBlockStyle(block: ContentBlock): ?string {
|
|
||||||
if (block.getType() === 'strikethrough') {
|
|
||||||
return 'mx_Markdown_STRIKETHROUGH';
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
client: MatrixClient;
|
client: MatrixClient;
|
||||||
autocomplete: Autocomplete;
|
autocomplete: Autocomplete;
|
||||||
historyManager: ComposerHistoryManager;
|
historyManager: ComposerHistoryManager;
|
||||||
|
@ -181,6 +144,8 @@ export default class MessageComposerInput extends React.Component {
|
||||||
this.plainWithMdPills = new PlainWithPillsSerializer({ pillFormat: 'md' });
|
this.plainWithMdPills = new PlainWithPillsSerializer({ pillFormat: 'md' });
|
||||||
this.plainWithIdPills = new PlainWithPillsSerializer({ pillFormat: 'id' });
|
this.plainWithIdPills = new PlainWithPillsSerializer({ pillFormat: 'id' });
|
||||||
this.plainWithPlainPills = new PlainWithPillsSerializer({ pillFormat: 'plain' });
|
this.plainWithPlainPills = new PlainWithPillsSerializer({ pillFormat: 'plain' });
|
||||||
|
this.md = new Md();
|
||||||
|
this.html = new Html();
|
||||||
|
|
||||||
this.suppressAutoComplete = false;
|
this.suppressAutoComplete = false;
|
||||||
this.direction = '';
|
this.direction = '';
|
||||||
|
@ -191,9 +156,9 @@ export default class MessageComposerInput extends React.Component {
|
||||||
* - whether we've got rich text mode enabled
|
* - whether we've got rich text mode enabled
|
||||||
* - contentState was passed in
|
* - contentState was passed in
|
||||||
*/
|
*/
|
||||||
createEditorState(richText: boolean, value: ?Value): Value {
|
createEditorState(richText: boolean, editorState: ?Value): Value {
|
||||||
if (value instanceof Value) {
|
if (editorState instanceof Value) {
|
||||||
return value;
|
return editorState;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// ...or create a new one.
|
// ...or create a new one.
|
||||||
|
@ -275,7 +240,7 @@ export default class MessageComposerInput extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -403,7 +368,7 @@ export default class MessageComposerInput extends React.Component {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const currentBlock = editorState.getSelection().getStartKey();
|
const currentBlock = editorState.getSelection().getStartKey();
|
||||||
const currentSelection = editorState.getSelection();
|
const currentSelection = editorState.getSelection();
|
||||||
const currentStartOffset = editorState.getSelection().getStartOffset();
|
const currentStartOffset = editorState.getSelection().getStartOffset();
|
||||||
|
@ -477,27 +442,54 @@ export default class MessageComposerInput extends React.Component {
|
||||||
// FIXME: this conversion should be handled in the store, surely
|
// FIXME: this conversion should be handled in the store, surely
|
||||||
// i.e. "convert my current composer value into Rich or MD, as ComposerHistoryManager already does"
|
// i.e. "convert my current composer value into Rich or MD, as ComposerHistoryManager already does"
|
||||||
|
|
||||||
let value = null;
|
let editorState = null;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
// const md = new Markdown(this.state.editorState.getCurrentContent().getPlainText());
|
// const sourceWithPills = this.plainWithMdPills.serialize(this.state.editorState);
|
||||||
// contentState = RichText.htmlToContentState(md.toHTML());
|
// const markdown = new Markdown(sourceWithPills);
|
||||||
|
// editorState = this.html.deserialize(markdown.toHTML());
|
||||||
|
|
||||||
value = Md.deserialize(Plain.serialize(this.state.editorState));
|
// we don't really want a custom MD parser hanging around, but the
|
||||||
|
// alternative would be:
|
||||||
|
editorState = this.md.deserialize(this.plainWithMdPills.serialize(this.state.editorState));
|
||||||
} else {
|
} else {
|
||||||
// let markdown = RichText.stateToMarkdown(this.state.editorState.getCurrentContent());
|
// let markdown = RichText.stateToMarkdown(this.state.editorState.getCurrentContent());
|
||||||
// value = ContentState.createFromText(markdown);
|
// value = ContentState.createFromText(markdown);
|
||||||
|
|
||||||
value = Plain.deserialize(Md.serialize(this.state.editorState));
|
editorState = Plain.deserialize(this.md.serialize(this.state.editorState));
|
||||||
}
|
}
|
||||||
|
|
||||||
Analytics.setRichtextMode(enabled);
|
Analytics.setRichtextMode(enabled);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
editorState: this.createEditorState(enabled, value),
|
editorState: this.createEditorState(enabled, editorState),
|
||||||
isRichtextEnabled: enabled,
|
isRichtextEnabled: enabled,
|
||||||
});
|
});
|
||||||
SettingsStore.setValue("MessageComposerInput.isRichTextEnabled", null, SettingLevel.ACCOUNT, enabled);
|
SettingsStore.setValue("MessageComposerInput.isRichTextEnabled", null, SettingLevel.ACCOUNT, enabled);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the current selection has a mark with `type` in it.
|
||||||
|
*
|
||||||
|
* @param {String} type
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
hasMark = type => {
|
||||||
|
const { editorState } = this.state
|
||||||
|
return editorState.activeMarks.some(mark => mark.type == type)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the any of the currently selected blocks are of `type`.
|
||||||
|
*
|
||||||
|
* @param {String} type
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
hasBlock = type => {
|
||||||
|
const { editorState } = this.state
|
||||||
|
return editorState.blocks.some(node => node.type == type)
|
||||||
|
};
|
||||||
|
|
||||||
onKeyDown = (ev: Event, change: Change, editor: Editor) => {
|
onKeyDown = (ev: Event, change: Change, editor: Editor) => {
|
||||||
|
|
||||||
|
@ -514,6 +506,22 @@ export default class MessageComposerInput extends React.Component {
|
||||||
this.direction = '';
|
this.direction = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isOnlyCtrlOrCmdKeyEvent(ev)) {
|
||||||
|
const ctrlCmdCommand = {
|
||||||
|
// C-m => Toggles between rich text and markdown modes
|
||||||
|
[KeyCode.KEY_M]: 'toggle-mode',
|
||||||
|
[KeyCode.KEY_B]: 'bold',
|
||||||
|
[KeyCode.KEY_I]: 'italic',
|
||||||
|
[KeyCode.KEY_U]: 'underline',
|
||||||
|
[KeyCode.KEY_J]: 'code',
|
||||||
|
}[ev.keyCode];
|
||||||
|
|
||||||
|
if (ctrlCmdCommand) {
|
||||||
|
return this.handleKeyCommand(ctrlCmdCommand);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
switch (ev.keyCode) {
|
switch (ev.keyCode) {
|
||||||
case KeyCode.ENTER:
|
case KeyCode.ENTER:
|
||||||
return this.handleReturn(ev);
|
return this.handleReturn(ev);
|
||||||
|
@ -529,7 +537,7 @@ export default class MessageComposerInput extends React.Component {
|
||||||
// don't intercept it
|
// don't intercept it
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleKeyCommand = (command: string): boolean => {
|
handleKeyCommand = (command: string): boolean => {
|
||||||
if (command === 'toggle-mode') {
|
if (command === 'toggle-mode') {
|
||||||
|
@ -541,32 +549,79 @@ export default class MessageComposerInput extends React.Component {
|
||||||
|
|
||||||
// Draft handles rich text mode commands by default but we need to do it ourselves for Markdown.
|
// Draft handles rich text mode commands by default but we need to do it ourselves for Markdown.
|
||||||
if (this.state.isRichtextEnabled) {
|
if (this.state.isRichtextEnabled) {
|
||||||
/*
|
const type = command;
|
||||||
// These are block types, not handled by RichUtils by default.
|
const { editorState } = this.state;
|
||||||
const blockCommands = ['code-block', 'blockquote', 'unordered-list-item', 'ordered-list-item'];
|
const change = editorState.change();
|
||||||
const currentBlockType = RichUtils.getCurrentBlockType(this.state.editorState);
|
const { document } = editorState;
|
||||||
|
switch (type) {
|
||||||
|
// list-blocks:
|
||||||
|
case 'bulleted-list':
|
||||||
|
case 'numbered-list': {
|
||||||
|
// Handle the extra wrapping required for list buttons.
|
||||||
|
const isList = this.hasBlock('list-item');
|
||||||
|
const isType = editorState.blocks.some(block => {
|
||||||
|
return !!document.getClosest(block.key, parent => parent.type == type);
|
||||||
|
});
|
||||||
|
|
||||||
const shouldToggleBlockFormat = (
|
if (isList && isType) {
|
||||||
command === 'backspace' ||
|
change
|
||||||
command === 'split-block'
|
.setBlocks(DEFAULT_NODE)
|
||||||
) && currentBlockType !== 'unstyled';
|
.unwrapBlock('bulleted-list')
|
||||||
|
.unwrapBlock('numbered-list');
|
||||||
if (blockCommands.includes(command)) {
|
} else if (isList) {
|
||||||
newState = RichUtils.toggleBlockType(this.state.editorState, command);
|
change
|
||||||
} else if (command === 'strike') {
|
.unwrapBlock(
|
||||||
// this is the only inline style not handled by Draft by default
|
type == 'bulleted-list' ? 'numbered-list' : 'bulleted-list'
|
||||||
newState = RichUtils.toggleInlineStyle(this.state.editorState, 'STRIKETHROUGH');
|
)
|
||||||
} else if (shouldToggleBlockFormat) {
|
.wrapBlock(type);
|
||||||
const currentStartOffset = this.state.editorState.getSelection().getStartOffset();
|
} else {
|
||||||
const currentEndOffset = this.state.editorState.getSelection().getEndOffset();
|
change.setBlocks('list-item').wrapBlock(type);
|
||||||
if (currentStartOffset === 0 && currentEndOffset === 0) {
|
}
|
||||||
// Toggle current block type (setting it to 'unstyled')
|
|
||||||
newState = RichUtils.toggleBlockType(this.state.editorState, currentBlockType);
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// simple blocks
|
||||||
|
case 'paragraph':
|
||||||
|
case 'block-quote':
|
||||||
|
case 'heading-one':
|
||||||
|
case 'heading-two':
|
||||||
|
case 'heading-three':
|
||||||
|
case 'list-item':
|
||||||
|
case 'code-block': {
|
||||||
|
const isActive = this.hasBlock(type);
|
||||||
|
const isList = this.hasBlock('list-item');
|
||||||
|
|
||||||
|
if (isList) {
|
||||||
|
change
|
||||||
|
.setBlocks(isActive ? DEFAULT_NODE : type)
|
||||||
|
.unwrapBlock('bulleted-list')
|
||||||
|
.unwrapBlock('numbered-list');
|
||||||
|
} else {
|
||||||
|
change.setBlocks(isActive ? DEFAULT_NODE : type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// marks:
|
||||||
|
case 'bold':
|
||||||
|
case 'italic':
|
||||||
|
case 'code':
|
||||||
|
case 'underline':
|
||||||
|
case 'strikethrough': {
|
||||||
|
change.toggleMark(type);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn(`ignoring unrecognised RTE command ${type}`);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
this.onChange(change);
|
||||||
|
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
const contentState = this.state.editorState.getCurrentContent();
|
const contentState = this.state.editorState.getCurrentContent();
|
||||||
const multipleLinesSelected = RichText.hasMultiLineSelection(this.state.editorState);
|
const multipleLinesSelected = RichText.hasMultiLineSelection(this.state.editorState);
|
||||||
|
|
||||||
|
@ -641,7 +696,8 @@ export default class MessageComposerInput extends React.Component {
|
||||||
this.setState({editorState: newState});
|
this.setState({editorState: newState});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
/*
|
/*
|
||||||
|
@ -671,19 +727,14 @@ export default class MessageComposerInput extends React.Component {
|
||||||
if (ev.shiftKey) {
|
if (ev.shiftKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
const currentBlockType = RichUtils.getCurrentBlockType(this.state.editorState);
|
if (this.state.editorState.blocks.some(
|
||||||
if (
|
block => block in ['code-block', 'block-quote', 'bulleted-list', 'numbered-list']
|
||||||
['code-block', 'blockquote', 'unordered-list-item', 'ordered-list-item']
|
)) {
|
||||||
.includes(currentBlockType)
|
// allow the user to terminate blocks by hitting return rather than sending a msg
|
||||||
) {
|
return;
|
||||||
// By returning false, we allow the default draft-js key binding to occur,
|
|
||||||
// which in this case invokes "split-block". This creates a new block of the
|
|
||||||
// same type, allowing the user to delete it with backspace.
|
|
||||||
// See handleKeyCommand (when command === 'backspace')
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
const editorState = this.state.editorState;
|
const editorState = this.state.editorState;
|
||||||
|
|
||||||
let contentText;
|
let contentText;
|
||||||
|
@ -989,6 +1040,17 @@ export default class MessageComposerInput extends React.Component {
|
||||||
await this.setDisplayedCompletion(null); // restore originalEditorState
|
await this.setDisplayedCompletion(null); // restore originalEditorState
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* returns inline style and block type of current SelectionState so MessageComposer can render formatting
|
||||||
|
buttons. */
|
||||||
|
getSelectionInfo(editorState: Value) {
|
||||||
|
return {
|
||||||
|
marks: editorState.activeMarks,
|
||||||
|
// XXX: shouldn't we return all the types of blocks in the current selection,
|
||||||
|
// not just the anchor?
|
||||||
|
blockType: editorState.anchorBlock.type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/* If passed null, restores the original editor content from state.originalEditorState.
|
/* If passed null, restores the original editor content from state.originalEditorState.
|
||||||
* If passed a non-null displayedCompletion, modifies state.originalEditorState to compute new state.editorState.
|
* If passed a non-null displayedCompletion, modifies state.originalEditorState to compute new state.editorState.
|
||||||
*/
|
*/
|
||||||
|
@ -1059,9 +1121,24 @@ export default class MessageComposerInput extends React.Component {
|
||||||
const { attributes, children, node, isSelected } = props;
|
const { attributes, children, node, isSelected } = props;
|
||||||
|
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case 'paragraph': {
|
case 'paragraph':
|
||||||
return <p {...attributes}>{children}</p>
|
return <p {...attributes}>{children}</p>;
|
||||||
}
|
case 'block-quote':
|
||||||
|
return <blockquote {...attributes}>{children}</blockquote>;
|
||||||
|
case 'bulleted-list':
|
||||||
|
return <ul {...attributes}>{children}</ul>;
|
||||||
|
case 'heading-one':
|
||||||
|
return <h1 {...attributes}>{children}</h1>;
|
||||||
|
case 'heading-two':
|
||||||
|
return <h2 {...attributes}>{children}</h2>;
|
||||||
|
case 'heading-three':
|
||||||
|
return <h3 {...attributes}>{children}</h3>;
|
||||||
|
case 'list-item':
|
||||||
|
return <li {...attributes}>{children}</li>;
|
||||||
|
case 'numbered-list':
|
||||||
|
return <ol {...attributes}>{children}</ol>;
|
||||||
|
case 'code-block':
|
||||||
|
return <p {...attributes}><code {...attributes}>{children}</code></p>;
|
||||||
case 'pill': {
|
case 'pill': {
|
||||||
const { data } = node;
|
const { data } = node;
|
||||||
const url = data.get('url');
|
const url = data.get('url');
|
||||||
|
@ -1106,29 +1183,35 @@ export default class MessageComposerInput extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
renderMark = props => {
|
||||||
|
const { children, mark, attributes } = props;
|
||||||
|
switch (mark.type) {
|
||||||
|
case 'bold':
|
||||||
|
return <strong {...{ attributes }}>{children}</strong>;
|
||||||
|
case 'italic':
|
||||||
|
return <em {...{ attributes }}>{children}</em>;
|
||||||
|
case 'code':
|
||||||
|
return <code {...{ attributes }}>{children}</code>;
|
||||||
|
case 'underline':
|
||||||
|
return <u {...{ attributes }}>{children}</u>;
|
||||||
|
case 'strikethrough':
|
||||||
|
return <del {...{ attributes }}>{children}</del>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onFormatButtonClicked = (name: "bold" | "italic" | "strike" | "code" | "underline" | "quote" | "bullet" | "numbullet", e) => {
|
onFormatButtonClicked = (name: "bold" | "italic" | "strike" | "code" | "underline" | "quote" | "bullet" | "numbullet", e) => {
|
||||||
e.preventDefault(); // don't steal focus from the editor!
|
e.preventDefault(); // don't steal focus from the editor!
|
||||||
|
|
||||||
const command = {
|
const command = {
|
||||||
code: 'code-block',
|
code: 'code-block',
|
||||||
quote: 'blockquote',
|
quote: 'block-quote',
|
||||||
bullet: 'unordered-list-item',
|
bullet: 'bulleted-list',
|
||||||
numbullet: 'ordered-list-item',
|
numbullet: 'numbered-list',
|
||||||
|
strike: 'strike-through',
|
||||||
}[name] || name;
|
}[name] || name;
|
||||||
this.handleKeyCommand(command);
|
this.handleKeyCommand(command);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* returns inline style and block type of current SelectionState so MessageComposer can render formatting
|
|
||||||
buttons. */
|
|
||||||
getSelectionInfo(editorState: Value) {
|
|
||||||
return {
|
|
||||||
marks: editorState.activeMarks,
|
|
||||||
// XXX: shouldn't we return all the types of blocks in the current selection,
|
|
||||||
// not just the anchor?
|
|
||||||
blockType: editorState.anchorBlock.type,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getAutocompleteQuery(editorState: Value) {
|
getAutocompleteQuery(editorState: Value) {
|
||||||
// We can just return the current block where the selection begins, which
|
// We can just return the current block where the selection begins, which
|
||||||
// should be enough to capture any autocompletion input, given autocompletion
|
// should be enough to capture any autocompletion input, given autocompletion
|
||||||
|
@ -1154,7 +1237,7 @@ export default class MessageComposerInput extends React.Component {
|
||||||
const range = {
|
const range = {
|
||||||
beginning, // whether the selection is in the first block of the editor or not
|
beginning, // whether the selection is in the first block of the editor or not
|
||||||
start: editorState.selection.anchorOffset,
|
start: editorState.selection.anchorOffset,
|
||||||
end: (editorState.selection.anchorKey == editorState.selection.focusKey) ?
|
end: (editorState.selection.anchorKey == editorState.selection.focusKey) ?
|
||||||
editorState.selection.focusOffset : editorState.selection.anchorOffset,
|
editorState.selection.focusOffset : editorState.selection.anchorOffset,
|
||||||
}
|
}
|
||||||
if (range.start > range.end) {
|
if (range.start > range.end) {
|
||||||
|
@ -1203,11 +1286,9 @@ export default class MessageComposerInput extends React.Component {
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
renderNode={this.renderNode}
|
renderNode={this.renderNode}
|
||||||
|
renderMark={this.renderMark}
|
||||||
spellCheck={true}
|
spellCheck={true}
|
||||||
/*
|
/*
|
||||||
blockStyleFn={MessageComposerInput.getBlockStyle}
|
|
||||||
keyBindingFn={MessageComposerInput.getKeyBinding}
|
|
||||||
handleKeyCommand={this.handleKeyCommand}
|
|
||||||
handlePastedText={this.onTextPasted}
|
handlePastedText={this.onTextPasted}
|
||||||
handlePastedFiles={this.props.onFilesPasted}
|
handlePastedFiles={this.props.onFilesPasted}
|
||||||
stripPastedStyles={!this.state.isRichtextEnabled}
|
stripPastedStyles={!this.state.isRichtextEnabled}
|
||||||
|
|
Loading…
Reference in a new issue