mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-18 21:21:21 +02:00
add support for spellcheck
This commit is contained in:
@@ -37,6 +37,7 @@ class Content extends React.Component {
|
|||||||
readOnly: React.PropTypes.bool,
|
readOnly: React.PropTypes.bool,
|
||||||
renderMark: React.PropTypes.func.isRequired,
|
renderMark: React.PropTypes.func.isRequired,
|
||||||
renderNode: React.PropTypes.func.isRequired,
|
renderNode: React.PropTypes.func.isRequired,
|
||||||
|
spellCheck: React.PropTypes.bool,
|
||||||
state: React.PropTypes.object.isRequired,
|
state: React.PropTypes.object.isRequired,
|
||||||
style: React.PropTypes.object
|
style: React.PropTypes.object
|
||||||
};
|
};
|
||||||
@@ -47,6 +48,7 @@ class Content extends React.Component {
|
|||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
|
spellCheck: true,
|
||||||
style: {}
|
style: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -376,6 +378,50 @@ class Content extends React.Component {
|
|||||||
this.props.onDrop(e, drop)
|
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
|
* On key down, prevent the default behavior of certain commands that will
|
||||||
* leave the editor in an out-of-sync state, then bubble up.
|
* leave the editor in an out-of-sync state, then bubble up.
|
||||||
@@ -547,6 +593,11 @@ class Content extends React.Component {
|
|||||||
...this.props.style,
|
...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 (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`slate-content-${this.forces}`}
|
key={`slate-content-${this.forces}`}
|
||||||
@@ -563,10 +614,12 @@ class Content extends React.Component {
|
|||||||
onDragOver={this.onDragOver}
|
onDragOver={this.onDragOver}
|
||||||
onDragStart={this.onDragStart}
|
onDragStart={this.onDragStart}
|
||||||
onDrop={this.onDrop}
|
onDrop={this.onDrop}
|
||||||
|
onInput={this.onInput}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
onKeyUp={noop}
|
onKeyUp={noop}
|
||||||
onPaste={this.onPaste}
|
onPaste={this.onPaste}
|
||||||
onSelect={this.onSelect}
|
onSelect={this.onSelect}
|
||||||
|
spellCheck={spellCheck}
|
||||||
style={style}
|
style={style}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@@ -703,10 +703,11 @@ class State extends new Record(DEFAULTS) {
|
|||||||
* Insert a `text` string at the current selection.
|
* Insert a `text` string at the current selection.
|
||||||
*
|
*
|
||||||
* @param {String} text
|
* @param {String} text
|
||||||
|
* @param {Set} marks (optional)
|
||||||
* @return {State} state
|
* @return {State} state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
insertText(text) {
|
insertText(text, marks) {
|
||||||
let state = this
|
let state = this
|
||||||
let { cursorMarks, document, selection } = state
|
let { cursorMarks, document, selection } = state
|
||||||
let after = selection
|
let after = selection
|
||||||
@@ -721,7 +722,7 @@ class State extends new Record(DEFAULTS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert the text and update the selection.
|
// Insert the text and update the selection.
|
||||||
document = document.insertTextAtRange(selection, text, cursorMarks)
|
document = document.insertTextAtRange(selection, text, marks || cursorMarks)
|
||||||
selection = after
|
selection = after
|
||||||
state = state.merge({ document, selection })
|
state = state.merge({ document, selection })
|
||||||
return state
|
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 {Element} element
|
||||||
* @param {Offset} offset
|
* @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`.
|
* Parse an offset key `string`.
|
||||||
*
|
*
|
||||||
@@ -103,7 +120,7 @@ function parse(string) {
|
|||||||
const [ original, key, index ] = matches
|
const [ original, key, index ] = matches
|
||||||
return {
|
return {
|
||||||
key,
|
key,
|
||||||
index
|
index: parseInt(index, 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +145,7 @@ export default {
|
|||||||
findBounds,
|
findBounds,
|
||||||
findKey,
|
findKey,
|
||||||
findPoint,
|
findPoint,
|
||||||
|
findRange,
|
||||||
parse,
|
parse,
|
||||||
stringify
|
stringify
|
||||||
}
|
}
|
||||||
|
@@ -54,6 +54,7 @@ function clean(html) {
|
|||||||
$(el).removeAttr('data-offset-key')
|
$(el).removeAttr('data-offset-key')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$.root().children().removeAttr('spellcheck')
|
||||||
$.root().children().removeAttr('style')
|
$.root().children().removeAttr('style')
|
||||||
|
|
||||||
return $.html()
|
return $.html()
|
||||||
|
Reference in New Issue
Block a user