mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-17 20:51:20 +02:00
add support for spellcheck
This commit is contained in:
@@ -37,6 +37,7 @@ class Content extends React.Component {
|
||||
readOnly: React.PropTypes.bool,
|
||||
renderMark: React.PropTypes.func.isRequired,
|
||||
renderNode: React.PropTypes.func.isRequired,
|
||||
spellCheck: React.PropTypes.bool,
|
||||
state: React.PropTypes.object.isRequired,
|
||||
style: React.PropTypes.object
|
||||
};
|
||||
@@ -47,6 +48,7 @@ class Content extends React.Component {
|
||||
|
||||
static defaultProps = {
|
||||
readOnly: false,
|
||||
spellCheck: true,
|
||||
style: {}
|
||||
};
|
||||
|
||||
@@ -376,6 +378,50 @@ class Content extends React.Component {
|
||||
this.props.onDrop(e, drop)
|
||||
}
|
||||
|
||||
/**
|
||||
* On input, handle spellcheck and other similar edits that don't go trigger
|
||||
* the `onBeforeInput` and instead update the DOM directly.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
|
||||
onInput = (e) => {
|
||||
let { state } = this.props
|
||||
const { selection } = state
|
||||
const native = window.getSelection()
|
||||
const { anchorNode, anchorOffset, focusOffset } = native
|
||||
const { textContent } = anchorNode
|
||||
const offsetKey = OffsetKey.findKey(anchorNode)
|
||||
const { key, index } = OffsetKey.parse(offsetKey)
|
||||
const { start, end } = OffsetKey.findBounds(key, index, state)
|
||||
const range = OffsetKey.findRange(anchorNode, state)
|
||||
const { text, marks } = range
|
||||
|
||||
// If the text is no different, abort.
|
||||
if (textContent == text) return
|
||||
|
||||
// Determine what the selection should be after changing the text.
|
||||
const delta = textContent.length - text.length
|
||||
const after = selection.collapseToEnd().moveForward(delta)
|
||||
|
||||
// Create an updated state with the text replaced.
|
||||
state = state
|
||||
.transform()
|
||||
.moveTo({
|
||||
anchorKey: key,
|
||||
anchorOffset: start,
|
||||
focusKey: key,
|
||||
focusOffset: end
|
||||
})
|
||||
.delete()
|
||||
.insertText(textContent, marks)
|
||||
.moveTo(after)
|
||||
.apply()
|
||||
|
||||
// Change the current state.
|
||||
this.onChange(state)
|
||||
}
|
||||
|
||||
/**
|
||||
* On key down, prevent the default behavior of certain commands that will
|
||||
* leave the editor in an out-of-sync state, then bubble up.
|
||||
@@ -547,6 +593,11 @@ class Content extends React.Component {
|
||||
...this.props.style,
|
||||
}
|
||||
|
||||
// COMPAT: In Firefox, spellchecking can remove entire wrapping elements
|
||||
// including inline ones like `<a>`, which is jarring for the user but also
|
||||
// causes the DOM to get into an irreconilable state.
|
||||
const spellCheck = IS_FIREFOX ? false : this.props.spellCheck
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`slate-content-${this.forces}`}
|
||||
@@ -563,10 +614,12 @@ class Content extends React.Component {
|
||||
onDragOver={this.onDragOver}
|
||||
onDragStart={this.onDragStart}
|
||||
onDrop={this.onDrop}
|
||||
onInput={this.onInput}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={noop}
|
||||
onPaste={this.onPaste}
|
||||
onSelect={this.onSelect}
|
||||
spellCheck={spellCheck}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
|
@@ -703,10 +703,11 @@ class State extends new Record(DEFAULTS) {
|
||||
* Insert a `text` string at the current selection.
|
||||
*
|
||||
* @param {String} text
|
||||
* @param {Set} marks (optional)
|
||||
* @return {State} state
|
||||
*/
|
||||
|
||||
insertText(text) {
|
||||
insertText(text, marks) {
|
||||
let state = this
|
||||
let { cursorMarks, document, selection } = state
|
||||
let after = selection
|
||||
@@ -721,7 +722,7 @@ class State extends new Record(DEFAULTS) {
|
||||
}
|
||||
|
||||
// Insert the text and update the selection.
|
||||
document = document.insertTextAtRange(selection, text, cursorMarks)
|
||||
document = document.insertTextAtRange(selection, text, marks || cursorMarks)
|
||||
selection = after
|
||||
state = state.merge({ document, selection })
|
||||
return state
|
||||
|
@@ -66,7 +66,7 @@ function findKey(element) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the selection point from an `element`, `offset`, and list of `ranges`.
|
||||
* Find the selection point from an `element`, `offset`, and `state`.
|
||||
*
|
||||
* @param {Element} element
|
||||
* @param {Offset} offset
|
||||
@@ -90,6 +90,23 @@ function findPoint(element, offset, state) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the range from an `element`.
|
||||
*
|
||||
* @param {Element} element
|
||||
* @param {State} state
|
||||
* @return {Range} range
|
||||
*/
|
||||
|
||||
function findRange(element, state) {
|
||||
const offsetKey = findKey(element)
|
||||
const { key, index } = parse(offsetKey)
|
||||
const text = state.document.getDescendant(key)
|
||||
const ranges = text.getDecoratedRanges()
|
||||
const range = ranges.get(index)
|
||||
return range
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an offset key `string`.
|
||||
*
|
||||
@@ -103,7 +120,7 @@ function parse(string) {
|
||||
const [ original, key, index ] = matches
|
||||
return {
|
||||
key,
|
||||
index
|
||||
index: parseInt(index, 10)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +145,7 @@ export default {
|
||||
findBounds,
|
||||
findKey,
|
||||
findPoint,
|
||||
findRange,
|
||||
parse,
|
||||
stringify
|
||||
}
|
||||
|
@@ -54,6 +54,7 @@ function clean(html) {
|
||||
$(el).removeAttr('data-offset-key')
|
||||
})
|
||||
|
||||
$.root().children().removeAttr('spellcheck')
|
||||
$.root().children().removeAttr('style')
|
||||
|
||||
return $.html()
|
||||
|
Reference in New Issue
Block a user