mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-18 21:21:21 +02:00
refactor hotkeys to constants, and add transforms (#1251)
* refactor hotkeys to constants, and add transforms * update hotkey helper
This commit is contained in:
@@ -3,8 +3,8 @@ import { Editor } from 'slate-react'
|
|||||||
import { State } from 'slate'
|
import { State } from 'slate'
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import isHotkey from 'is-hotkey'
|
|
||||||
import initialState from './state.json'
|
import initialState from './state.json'
|
||||||
|
import { isKeyHotkey } from 'is-hotkey'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the default node type.
|
* Define the default node type.
|
||||||
@@ -20,10 +20,10 @@ const DEFAULT_NODE = 'paragraph'
|
|||||||
* @type {Function}
|
* @type {Function}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const isBoldHotkey = isHotkey('mod+b')
|
const isBoldHotkey = isKeyHotkey('mod+b')
|
||||||
const isItalicHotkey = isHotkey('mod+i')
|
const isItalicHotkey = isKeyHotkey('mod+i')
|
||||||
const isUnderlinedHotkey = isHotkey('mod+u')
|
const isUnderlinedHotkey = isKeyHotkey('mod+u')
|
||||||
const isCodeHotkey = isHotkey('mod+`')
|
const isCodeHotkey = isKeyHotkey('mod+`')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a schema.
|
* Define a schema.
|
||||||
|
@@ -3,8 +3,8 @@ import { Editor } from 'slate-react'
|
|||||||
import { State } from 'slate'
|
import { State } from 'slate'
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import isHotkey from 'is-hotkey'
|
|
||||||
import initialState from './state.json'
|
import initialState from './state.json'
|
||||||
|
import { isKeyHotkey } from 'is-hotkey'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hotkey matchers.
|
* Hotkey matchers.
|
||||||
@@ -12,10 +12,10 @@ import initialState from './state.json'
|
|||||||
* @type {Function}
|
* @type {Function}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const isBoldHotkey = isHotkey('mod+b')
|
const isBoldHotkey = isKeyHotkey('mod+b')
|
||||||
const isItalicHotkey = isHotkey('mod+i')
|
const isItalicHotkey = isKeyHotkey('mod+i')
|
||||||
const isUnderlinedHotkey = isHotkey('mod+u')
|
const isUnderlinedHotkey = isKeyHotkey('mod+u')
|
||||||
const isCodeHotkey = isHotkey('mod+`')
|
const isCodeHotkey = isKeyHotkey('mod+`')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a schema.
|
* Define a schema.
|
||||||
|
@@ -27,7 +27,7 @@
|
|||||||
"gh-pages": "^0.11.0",
|
"gh-pages": "^0.11.0",
|
||||||
"http-server": "^0.9.0",
|
"http-server": "^0.9.0",
|
||||||
"immutable": "^3.8.1",
|
"immutable": "^3.8.1",
|
||||||
"is-hotkey": "^0.0.1",
|
"is-hotkey": "^0.1.1",
|
||||||
"is-image": "^1.0.1",
|
"is-image": "^1.0.1",
|
||||||
"is-url": "^1.2.2",
|
"is-url": "^1.2.2",
|
||||||
"jest": "^17.0.3",
|
"jest": "^17.0.3",
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^2.3.2",
|
"debug": "^2.3.2",
|
||||||
"get-window": "^1.1.1",
|
"get-window": "^1.1.1",
|
||||||
"is-hotkey": "^0.0.3",
|
"is-hotkey": "^0.1.1",
|
||||||
"is-in-browser": "^1.1.3",
|
"is-in-browser": "^1.1.3",
|
||||||
"is-window": "^1.0.2",
|
"is-window": "^1.0.2",
|
||||||
"keycode": "^2.1.2",
|
"keycode": "^2.1.2",
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import isHotkey from 'is-hotkey'
|
import { isKeyHotkey } from 'is-hotkey'
|
||||||
|
|
||||||
import { IS_MAC } from './environment'
|
import { IS_MAC } from './environment'
|
||||||
|
|
||||||
@@ -9,32 +9,73 @@ import { IS_MAC } from './environment'
|
|||||||
* @type {Function}
|
* @type {Function}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const BOLD = isHotkey('mod+b')
|
const BOLD = isKeyHotkey('mod+b')
|
||||||
const ITALIC = isHotkey('mod+i')
|
const ITALIC = isKeyHotkey('mod+i')
|
||||||
|
|
||||||
const UNDO = isHotkey('mod+z')
|
const ENTER = isKeyHotkey('enter')
|
||||||
const REDO_MAC = isHotkey('mod+shift+z')
|
const SHIFT_ENTER = isKeyHotkey('shift+enter')
|
||||||
const REDO_OTHER = isHotkey('mod+y')
|
const SPLIT_BLOCK = e => ENTER(e) || SHIFT_ENTER(e)
|
||||||
const REDO = e => IS_MAC ? REDO_MAC(e) : REDO_OTHER(e)
|
|
||||||
|
|
||||||
const DELETE_CHAR_BACKWARD_MAC = isHotkey('ctrl+h')
|
const BACKSPACE = isKeyHotkey('backspace')
|
||||||
const DELETE_CHAR_FORWARD_MAC = isHotkey('ctrl+d')
|
const SHIFT_BACKSPACE = isKeyHotkey('shift+backspace')
|
||||||
const DELETE_LINE_FORWARD_MAC = isHotkey('ctrl+k')
|
const DELETE = isKeyHotkey('delete')
|
||||||
const DELETE_CHAR_BACKWARD = e => IS_MAC ? DELETE_CHAR_BACKWARD_MAC(e) : false
|
const SHIFT_DELETE = isKeyHotkey('shift+delete')
|
||||||
const DELETE_CHAR_FORWARD = e => IS_MAC ? DELETE_CHAR_FORWARD_MAC(e) : false
|
const DELETE_BACKWARD = e => BACKSPACE(e) || SHIFT_BACKSPACE(e)
|
||||||
const DELETE_LINE_FORWARD = e => IS_MAC ? DELETE_LINE_FORWARD_MAC(e) : false
|
const DELETE_FORWARD = e => DELETE(e) || SHIFT_DELETE(e)
|
||||||
|
|
||||||
|
const DELETE_CHAR_BACKWARD_MAC = isKeyHotkey('ctrl+h')
|
||||||
|
const DELETE_CHAR_FORWARD_MAC = isKeyHotkey('ctrl+d')
|
||||||
|
const DELETE_CHAR_BACKWARD = e => DELETE_BACKWARD(e) || (IS_MAC && DELETE_CHAR_BACKWARD_MAC(e))
|
||||||
|
const DELETE_CHAR_FORWARD = e => DELETE_FORWARD(e) || (IS_MAC && DELETE_CHAR_FORWARD_MAC(e))
|
||||||
|
|
||||||
|
const DELETE_LINE_BACKWARD_MAC = isKeyHotkey('cmd+backspace')
|
||||||
|
const DELETE_LINE_FORWARD_MAC = isKeyHotkey('ctrl+k')
|
||||||
|
const DELETE_LINE_BACKWARD = e => IS_MAC && DELETE_LINE_BACKWARD_MAC(e)
|
||||||
|
const DELETE_LINE_FORWARD = e => IS_MAC && DELETE_LINE_FORWARD_MAC(e)
|
||||||
|
|
||||||
|
const DELETE_WORD_BACKWARD_MAC = isKeyHotkey('option+backspace')
|
||||||
|
const DELETE_WORD_BACKWARD_PC = isKeyHotkey('ctrl+backspace')
|
||||||
|
const DELETE_WORD_FORWARD_MAC = isKeyHotkey('option+delete')
|
||||||
|
const DELETE_WORD_FORWARD_PC = isKeyHotkey('ctrl+delete')
|
||||||
|
const DELETE_WORD_BACKWARD = e => IS_MAC ? DELETE_WORD_BACKWARD_MAC(e) : DELETE_WORD_BACKWARD_PC(e)
|
||||||
|
const DELETE_WORD_FORWARD = e => IS_MAC ? DELETE_WORD_FORWARD_MAC(e) : DELETE_WORD_FORWARD_PC(e)
|
||||||
|
|
||||||
|
const COLLAPSE_CHAR_FORWARD = isKeyHotkey('right')
|
||||||
|
const COLLAPSE_CHAR_BACKWARD = isKeyHotkey('left')
|
||||||
|
|
||||||
|
const COLLAPSE_LINE_BACKWARD_MAC = isKeyHotkey('option+up')
|
||||||
|
const COLLAPSE_LINE_FORWARD_MAC = isKeyHotkey('option+down')
|
||||||
|
const COLLAPSE_LINE_BACKWARD = e => IS_MAC && COLLAPSE_LINE_BACKWARD_MAC(e)
|
||||||
|
const COLLAPSE_LINE_FORWARD = e => IS_MAC && COLLAPSE_LINE_FORWARD_MAC(e)
|
||||||
|
|
||||||
|
const EXTEND_CHAR_FORWARD = isKeyHotkey('shift+right')
|
||||||
|
const EXTEND_CHAR_BACKWARD = isKeyHotkey('shift+left')
|
||||||
|
|
||||||
|
const EXTEND_LINE_BACKWARD_MAC = isKeyHotkey('option+shift+up')
|
||||||
|
const EXTEND_LINE_FORWARD_MAC = isKeyHotkey('option+shift+down')
|
||||||
|
const EXTEND_LINE_BACKWARD = e => IS_MAC && EXTEND_LINE_BACKWARD_MAC(e)
|
||||||
|
const EXTEND_LINE_FORWARD = e => IS_MAC && EXTEND_LINE_FORWARD_MAC(e)
|
||||||
|
|
||||||
|
const UNDO = isKeyHotkey('mod+z')
|
||||||
|
const REDO_MAC = isKeyHotkey('mod+shift+z')
|
||||||
|
const REDO_PC = isKeyHotkey('mod+y')
|
||||||
|
const REDO = e => IS_MAC ? REDO_MAC(e) : REDO_PC(e)
|
||||||
|
|
||||||
|
const TRANSPOSE_CHARACTER_MAC = isKeyHotkey('ctrl+t')
|
||||||
|
const TRANSPOSE_CHARACTER = e => IS_MAC && TRANSPOSE_CHARACTER_MAC(e)
|
||||||
|
|
||||||
const CONTENTEDITABLE = e => (
|
const CONTENTEDITABLE = e => (
|
||||||
e.key == 'Backspace' ||
|
|
||||||
e.key == 'Delete' ||
|
|
||||||
e.key == 'Enter' ||
|
|
||||||
e.key == 'Insert' ||
|
|
||||||
BOLD(e) ||
|
BOLD(e) ||
|
||||||
DELETE_CHAR_BACKWARD(e) ||
|
DELETE_CHAR_BACKWARD(e) ||
|
||||||
DELETE_CHAR_FORWARD(e) ||
|
DELETE_CHAR_FORWARD(e) ||
|
||||||
|
DELETE_LINE_BACKWARD(e) ||
|
||||||
DELETE_LINE_FORWARD(e) ||
|
DELETE_LINE_FORWARD(e) ||
|
||||||
|
DELETE_WORD_BACKWARD(e) ||
|
||||||
|
DELETE_WORD_FORWARD(e) ||
|
||||||
ITALIC(e) ||
|
ITALIC(e) ||
|
||||||
REDO(e) ||
|
REDO(e) ||
|
||||||
|
SPLIT_BLOCK(e) ||
|
||||||
|
TRANSPOSE_CHARACTER(e) ||
|
||||||
UNDO(e)
|
UNDO(e)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,12 +94,24 @@ const COMPOSING = e => (
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
BOLD,
|
BOLD,
|
||||||
|
COLLAPSE_LINE_BACKWARD,
|
||||||
|
COLLAPSE_LINE_FORWARD,
|
||||||
|
COLLAPSE_CHAR_FORWARD,
|
||||||
|
COLLAPSE_CHAR_BACKWARD,
|
||||||
COMPOSING,
|
COMPOSING,
|
||||||
CONTENTEDITABLE,
|
CONTENTEDITABLE,
|
||||||
DELETE_CHAR_BACKWARD,
|
DELETE_CHAR_BACKWARD,
|
||||||
DELETE_CHAR_FORWARD,
|
DELETE_CHAR_FORWARD,
|
||||||
|
DELETE_LINE_BACKWARD,
|
||||||
DELETE_LINE_FORWARD,
|
DELETE_LINE_FORWARD,
|
||||||
|
DELETE_WORD_BACKWARD,
|
||||||
|
DELETE_WORD_FORWARD,
|
||||||
|
EXTEND_LINE_BACKWARD,
|
||||||
|
EXTEND_LINE_FORWARD,
|
||||||
|
EXTEND_CHAR_FORWARD,
|
||||||
|
EXTEND_CHAR_BACKWARD,
|
||||||
ITALIC,
|
ITALIC,
|
||||||
REDO,
|
REDO,
|
||||||
|
SPLIT_BLOCK,
|
||||||
UNDO,
|
UNDO,
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ import findRange from '../utils/find-range'
|
|||||||
import getEventRange from '../utils/get-event-range'
|
import getEventRange from '../utils/get-event-range'
|
||||||
import getEventTransfer from '../utils/get-event-transfer'
|
import getEventTransfer from '../utils/get-event-transfer'
|
||||||
import setEventTransfer from '../utils/set-event-transfer'
|
import setEventTransfer from '../utils/set-event-transfer'
|
||||||
import { IS_CHROME, IS_MAC, IS_SAFARI } from '../constants/environment'
|
import { IS_CHROME, IS_SAFARI } from '../constants/environment'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug.
|
* Debug.
|
||||||
@@ -479,269 +479,107 @@ function AfterPlugin(options = {}) {
|
|||||||
function onKeyDown(event, change, editor) {
|
function onKeyDown(event, change, editor) {
|
||||||
debug('onKeyDown', { event })
|
debug('onKeyDown', { event })
|
||||||
|
|
||||||
switch (event.key) {
|
const { state } = change
|
||||||
case 'Enter': return onKeyDownEnter(event, change)
|
|
||||||
case 'Backspace': return onKeyDownBackspace(event, change)
|
if (HOTKEYS.SPLIT_BLOCK(event)) {
|
||||||
case 'Delete': return onKeyDownDelete(event, change)
|
return state.isInVoid
|
||||||
case 'ArrowLeft': return onKeyDownLeft(event, change)
|
? change.collapseToStartOfNextText()
|
||||||
case 'ArrowRight': return onKeyDownRight(event, change)
|
: change.splitBlock()
|
||||||
case 'ArrowUp': return onKeyDownUp(event, change)
|
|
||||||
case 'ArrowDown': return onKeyDownDown(event, change)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HOTKEYS.DELETE_CHAR_BACKWARD(event)) {
|
if (HOTKEYS.DELETE_CHAR_BACKWARD(event)) {
|
||||||
change.deleteCharBackward()
|
return change.deleteCharBackward()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HOTKEYS.DELETE_CHAR_FORWARD(event)) {
|
if (HOTKEYS.DELETE_CHAR_FORWARD(event)) {
|
||||||
change.deleteCharForward()
|
return change.deleteCharForward()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HOTKEYS.DELETE_LINE_BACKWARD(event)) {
|
||||||
|
return change.deleteLineBackward()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HOTKEYS.DELETE_LINE_FORWARD(event)) {
|
if (HOTKEYS.DELETE_LINE_FORWARD(event)) {
|
||||||
change.deleteLineForward()
|
return change.deleteLineForward()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HOTKEYS.DELETE_WORD_BACKWARD(event)) {
|
||||||
|
return change.deleteWordBackward()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HOTKEYS.DELETE_WORD_FORWARD(event)) {
|
||||||
|
return change.deleteWordForward()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HOTKEYS.REDO(event)) {
|
if (HOTKEYS.REDO(event)) {
|
||||||
change.redo()
|
return change.redo()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HOTKEYS.UNDO(event)) {
|
if (HOTKEYS.UNDO(event)) {
|
||||||
change.undo()
|
return change.undo()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On `enter` key down, split the current block in half.
|
|
||||||
*
|
|
||||||
* @param {Event} event
|
|
||||||
* @param {Change} change
|
|
||||||
* @param {Editor} editor
|
|
||||||
*/
|
|
||||||
|
|
||||||
function onKeyDownEnter(event, change, editor) {
|
|
||||||
const { state } = change
|
|
||||||
const { document, startKey } = state
|
|
||||||
const hasVoidParent = document.hasVoidParent(startKey)
|
|
||||||
|
|
||||||
// For void nodes, we don't want to split. Instead we just move to the start
|
|
||||||
// of the next text node if one exists.
|
|
||||||
if (hasVoidParent) {
|
|
||||||
const text = document.getNextText(startKey)
|
|
||||||
if (!text) return
|
|
||||||
change.collapseToStartOf(text)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
change.splitBlock()
|
// COMPAT: Certain browsers don't handle the selection updates properly. In
|
||||||
}
|
// Chrome, the selection isn't properly extended. And in Firefox, the
|
||||||
|
// selection isn't properly collapsed. (2017/10/17)
|
||||||
/**
|
if (HOTKEYS.COLLAPSE_LINE_BACKWARD(event)) {
|
||||||
* On `backspace` key down, delete backwards.
|
|
||||||
*
|
|
||||||
* @param {Event} event
|
|
||||||
* @param {Change} change
|
|
||||||
* @param {Editor} editor
|
|
||||||
*/
|
|
||||||
|
|
||||||
function onKeyDownBackspace(event, change, editor) {
|
|
||||||
const isWord = IS_MAC ? event.altKey : event.ctrlKey
|
|
||||||
const isLine = IS_MAC ? event.metaKey : false
|
|
||||||
|
|
||||||
let boundary = 'Char'
|
|
||||||
if (isWord) boundary = 'Word'
|
|
||||||
if (isLine) boundary = 'Line'
|
|
||||||
|
|
||||||
change[`delete${boundary}Backward`]()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On `delete` key down, delete forwards.
|
|
||||||
*
|
|
||||||
* @param {Event} event
|
|
||||||
* @param {Change} change
|
|
||||||
* @param {Editor} editor
|
|
||||||
*/
|
|
||||||
|
|
||||||
function onKeyDownDelete(event, change, editor) {
|
|
||||||
const isWord = IS_MAC ? event.altKey : event.ctrlKey
|
|
||||||
const isLine = IS_MAC ? event.metaKey : false
|
|
||||||
|
|
||||||
let boundary = 'Char'
|
|
||||||
if (isWord) boundary = 'Word'
|
|
||||||
if (isLine) boundary = 'Line'
|
|
||||||
|
|
||||||
change[`delete${boundary}Forward`]()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On `left` key down, move backward.
|
|
||||||
*
|
|
||||||
* COMPAT: This is required to make navigating with the left arrow work when
|
|
||||||
* a void node is selected.
|
|
||||||
*
|
|
||||||
* COMPAT: This is also required to solve for the case where an inline node is
|
|
||||||
* surrounded by empty text nodes with zero-width spaces in them. Without this
|
|
||||||
* the zero-width spaces will cause two arrow keys to jump to the next text.
|
|
||||||
*
|
|
||||||
* @param {Event} event
|
|
||||||
* @param {Change} change
|
|
||||||
* @param {Editor} editor
|
|
||||||
*/
|
|
||||||
|
|
||||||
function onKeyDownLeft(event, change, editor) {
|
|
||||||
const { state } = change
|
|
||||||
|
|
||||||
if (event.ctrlKey) return
|
|
||||||
if (event.altKey) return
|
|
||||||
if (state.isExpanded) return
|
|
||||||
|
|
||||||
const { document, startKey, startText } = state
|
|
||||||
const hasVoidParent = document.hasVoidParent(startKey)
|
|
||||||
|
|
||||||
// If the current text node is empty, or we're inside a void parent, we're
|
|
||||||
// going to need to handle the selection behavior.
|
|
||||||
if (startText.text == '' || hasVoidParent) {
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const previous = document.getPreviousText(startKey)
|
return change.collapseLineBackward()
|
||||||
|
|
||||||
// If there's no previous text node in the document, abort.
|
|
||||||
if (!previous) return
|
|
||||||
|
|
||||||
// If the previous text is in the current block, and inside a non-void
|
|
||||||
// inline node, move one character into the inline node.
|
|
||||||
const { startBlock } = state
|
|
||||||
const previousBlock = document.getClosestBlock(previous.key)
|
|
||||||
const previousInline = document.getClosestInline(previous.key)
|
|
||||||
|
|
||||||
if (previousBlock === startBlock && previousInline && !previousInline.isVoid) {
|
|
||||||
const extendOrMove = event.shiftKey ? 'extend' : 'move'
|
|
||||||
change.collapseToEndOf(previous)[extendOrMove](-1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, move to the end of the previous node.
|
|
||||||
change.collapseToEndOf(previous)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (HOTKEYS.COLLAPSE_LINE_FORWARD(event)) {
|
||||||
* On `right` key down, move forward.
|
|
||||||
*
|
|
||||||
* COMPAT: This is required to make navigating with the right arrow work when
|
|
||||||
* a void node is selected.
|
|
||||||
*
|
|
||||||
* COMPAT: This is also required to solve for the case where an inline node is
|
|
||||||
* surrounded by empty text nodes with zero-width spaces in them. Without this
|
|
||||||
* the zero-width spaces will cause two arrow keys to jump to the next text.
|
|
||||||
*
|
|
||||||
* COMPAT: In Chrome & Safari, selections that are at the zero offset of
|
|
||||||
* an inline node will be automatically replaced to be at the last offset
|
|
||||||
* of a previous inline node, which screws us up, so we never want to set the
|
|
||||||
* selection to the very start of an inline node here. (2016/11/29)
|
|
||||||
*
|
|
||||||
* @param {Event} event
|
|
||||||
* @param {Change} change
|
|
||||||
* @param {Editor} editor
|
|
||||||
*/
|
|
||||||
|
|
||||||
function onKeyDownRight(event, change, editor) {
|
|
||||||
const { state } = change
|
|
||||||
|
|
||||||
if (event.ctrlKey) return
|
|
||||||
if (event.altKey) return
|
|
||||||
if (state.isExpanded) return
|
|
||||||
|
|
||||||
const { document, startKey, startText } = state
|
|
||||||
const hasVoidParent = document.hasVoidParent(startKey)
|
|
||||||
|
|
||||||
// If the current text node is empty, or we're inside a void parent, we're
|
|
||||||
// going to need to handle the selection behavior.
|
|
||||||
if (startText.text == '' || hasVoidParent) {
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const next = document.getNextText(startKey)
|
return change.collapseLineForward()
|
||||||
|
|
||||||
// If there's no next text node in the document, abort.
|
|
||||||
if (!next) return
|
|
||||||
|
|
||||||
// If the next text is inside a void node, move to the end of it.
|
|
||||||
if (document.hasVoidParent(next.key)) {
|
|
||||||
change.collapseToEndOf(next)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the next text is in the current block, and inside an inline node,
|
|
||||||
// move one character into the inline node.
|
|
||||||
const { startBlock } = state
|
|
||||||
const nextBlock = document.getClosestBlock(next.key)
|
|
||||||
const nextInline = document.getClosestInline(next.key)
|
|
||||||
|
|
||||||
if (nextBlock == startBlock && nextInline) {
|
|
||||||
const extendOrMove = event.shiftKey ? 'extend' : 'move'
|
|
||||||
change.collapseToStartOf(next)[extendOrMove](1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, move to the start of the next text node.
|
|
||||||
change.collapseToStartOf(next)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (HOTKEYS.EXTEND_LINE_BACKWARD(event)) {
|
||||||
* On `up` key down, for Macs, move the selection to start of the block.
|
event.preventDefault()
|
||||||
*
|
return change.extendLineBackward()
|
||||||
* COMPAT: Certain browsers don't handle the selection updates properly. In
|
}
|
||||||
* Chrome, option-shift-up doesn't properly extend the selection. And in
|
|
||||||
* Firefox, option-up doesn't properly move the selection.
|
|
||||||
*
|
|
||||||
* @param {Event} event
|
|
||||||
* @param {Change} change
|
|
||||||
* @param {Editor} editor
|
|
||||||
*/
|
|
||||||
|
|
||||||
function onKeyDownUp(event, change, editor) {
|
if (HOTKEYS.EXTEND_LINE_FORWARD(event)) {
|
||||||
if (!IS_MAC || event.ctrlKey || !event.altKey) return
|
event.preventDefault()
|
||||||
|
return change.extendLineForward()
|
||||||
|
}
|
||||||
|
|
||||||
const { state } = change
|
// COMPAT: If a void node is selected, or a zero-width text node adjacent to
|
||||||
const { selection, document, focusKey, focusBlock } = state
|
// an inline is selected, we need to handle these hotkeys manually because
|
||||||
const transform = event.shiftKey ? 'extendToStartOf' : 'collapseToStartOf'
|
// browsers won't know what to do.
|
||||||
const block = selection.hasFocusAtStartOf(focusBlock)
|
if (HOTKEYS.COLLAPSE_CHAR_BACKWARD(event)) {
|
||||||
? document.getPreviousBlock(focusKey)
|
const { isInVoid, previousText, document } = state
|
||||||
: focusBlock
|
const isPreviousInVoid = previousText && document.hasVoidParent(previousText.key)
|
||||||
|
if (isInVoid || isPreviousInVoid) {
|
||||||
|
event.preventDefault()
|
||||||
|
return change.collapseCharBackward()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!block) return
|
if (HOTKEYS.COLLAPSE_CHAR_FORWARD(event)) {
|
||||||
const text = block.getFirstText()
|
const { isInVoid, nextText, document } = state
|
||||||
|
const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
|
||||||
|
if (isInVoid || isNextInVoid) {
|
||||||
|
event.preventDefault()
|
||||||
|
return change.collapseCharForward()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
event.preventDefault()
|
if (HOTKEYS.EXTEND_CHAR_BACKWARD(event)) {
|
||||||
change[transform](text)
|
const { isInVoid, previousText, document } = state
|
||||||
}
|
const isPreviousInVoid = previousText && document.hasVoidParent(previousText.key)
|
||||||
|
if (isInVoid || isPreviousInVoid) {
|
||||||
|
event.preventDefault()
|
||||||
|
return change.extendCharBackward()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
if (HOTKEYS.EXTEND_CHAR_FORWARD(event)) {
|
||||||
* On `down` key down, for Macs, move the selection to end of the block.
|
const { isInVoid, nextText, document } = state
|
||||||
*
|
const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
|
||||||
* COMPAT: Certain browsers don't handle the selection updates properly. In
|
if (isInVoid || isNextInVoid) {
|
||||||
* Chrome, option-shift-down doesn't properly extend the selection. And in
|
event.preventDefault()
|
||||||
* Firefox, option-down doesn't properly move the selection.
|
return change.extendCharForward()
|
||||||
*
|
}
|
||||||
* @param {Event} event
|
}
|
||||||
* @param {Change} change
|
|
||||||
* @param {Editor} editor
|
|
||||||
*/
|
|
||||||
|
|
||||||
function onKeyDownDown(event, change, editor) {
|
|
||||||
if (!IS_MAC || event.ctrlKey || !event.altKey) return
|
|
||||||
|
|
||||||
const { state } = change
|
|
||||||
const { selection, document, focusKey, focusBlock } = state
|
|
||||||
const transform = event.shiftKey ? 'extendToEndOf' : 'collapseToEndOf'
|
|
||||||
const block = selection.hasFocusAtEndOf(focusBlock)
|
|
||||||
? document.getNextBlock(focusKey)
|
|
||||||
: focusBlock
|
|
||||||
|
|
||||||
if (!block) return
|
|
||||||
const text = block.getLastText()
|
|
||||||
|
|
||||||
event.preventDefault()
|
|
||||||
change[transform](text)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -108,46 +108,185 @@ Changes.snapshotSelection = (change) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set `properties` on the selection.
|
* Move the anchor point backward, accounting for being at the start of a block.
|
||||||
*
|
*
|
||||||
* @param {Mixed} ...args
|
|
||||||
* @param {Change} change
|
* @param {Change} change
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Changes.moveTo = (change, properties) => {
|
Changes.moveAnchorCharBackward = (change) => {
|
||||||
logger.deprecate('0.17.0', 'The `moveTo()` change is deprecated, please use `select()` instead.')
|
const { state } = change
|
||||||
change.select(properties)
|
const { document, selection, anchorText, anchorBlock } = state
|
||||||
|
const { anchorOffset } = selection
|
||||||
|
const previousText = document.getPreviousText(anchorText.key)
|
||||||
|
const isInVoid = document.hasVoidParent(anchorText.key)
|
||||||
|
const isPreviousInVoid = previousText && document.hasVoidParent(previousText.key)
|
||||||
|
|
||||||
|
if (!isInVoid && anchorOffset > 0) {
|
||||||
|
change.moveAnchor(-1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!previousText) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
change.moveAnchorToEndOf(previousText)
|
||||||
|
|
||||||
|
if (!isInVoid && !isPreviousInVoid && anchorBlock.hasNode(previousText.key)) {
|
||||||
|
change.moveAnchor(-1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unset the selection's marks.
|
* Move the anchor point forward, accounting for being at the end of a block.
|
||||||
*
|
*
|
||||||
* @param {Change} change
|
* @param {Change} change
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Changes.unsetMarks = (change) => {
|
Changes.moveAnchorCharForward = (change) => {
|
||||||
logger.deprecate('0.17.0', 'The `unsetMarks()` change is deprecated.')
|
const { state } = change
|
||||||
change.select({ marks: null })
|
const { document, selection, anchorText, anchorBlock } = state
|
||||||
|
const { anchorOffset } = selection
|
||||||
|
const nextText = document.getNextText(anchorText.key)
|
||||||
|
const isInVoid = document.hasVoidParent(anchorText.key)
|
||||||
|
const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
|
||||||
|
|
||||||
|
if (!isInVoid && anchorOffset < anchorText.text.length) {
|
||||||
|
change.moveAnchor(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nextText) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
change.moveAnchorToStartOf(nextText)
|
||||||
|
|
||||||
|
if (!isInVoid && !isNextInVoid && anchorBlock.hasNode(nextText.key)) {
|
||||||
|
change.moveAnchor(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unset the selection, removing an association to a node.
|
* Move the focus point backward, accounting for being at the start of a block.
|
||||||
*
|
*
|
||||||
* @param {Change} change
|
* @param {Change} change
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Changes.unsetSelection = (change) => {
|
Changes.moveFocusCharBackward = (change) => {
|
||||||
logger.deprecate('0.17.0', 'The `unsetSelection()` change is deprecated, please use `deselect()` instead.')
|
const { state } = change
|
||||||
change.select({
|
const { document, selection, focusText, focusBlock } = state
|
||||||
anchorKey: null,
|
const { focusOffset } = selection
|
||||||
anchorOffset: 0,
|
const previousText = document.getPreviousText(focusText.key)
|
||||||
focusKey: null,
|
const isInVoid = document.hasVoidParent(focusText.key)
|
||||||
focusOffset: 0,
|
const isPreviousInVoid = previousText && document.hasVoidParent(previousText.key)
|
||||||
isFocused: false,
|
|
||||||
isBackward: false
|
if (!isInVoid && focusOffset > 0) {
|
||||||
})
|
change.moveFocus(-1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!previousText) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
change.moveFocusToEndOf(previousText)
|
||||||
|
|
||||||
|
if (!isInVoid && !isPreviousInVoid && focusBlock.hasNode(previousText.key)) {
|
||||||
|
change.moveFocus(-1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the focus point forward, accounting for being at the end of a block.
|
||||||
|
*
|
||||||
|
* @param {Change} change
|
||||||
|
*/
|
||||||
|
|
||||||
|
Changes.moveFocusCharForward = (change) => {
|
||||||
|
const { state } = change
|
||||||
|
const { document, selection, focusText, focusBlock } = state
|
||||||
|
const { focusOffset } = selection
|
||||||
|
const nextText = document.getNextText(focusText.key)
|
||||||
|
const isInVoid = document.hasVoidParent(focusText.key)
|
||||||
|
const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
|
||||||
|
|
||||||
|
if (!isInVoid && focusOffset < focusText.text.length) {
|
||||||
|
change.moveFocus(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nextText) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
change.moveFocusToStartOf(nextText)
|
||||||
|
|
||||||
|
if (!isInVoid && !isNextInVoid && focusBlock.hasNode(nextText.key)) {
|
||||||
|
change.moveFocus(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mix in move methods.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const MOVE_DIRECTIONS = [
|
||||||
|
'Forward',
|
||||||
|
'Backward',
|
||||||
|
]
|
||||||
|
|
||||||
|
MOVE_DIRECTIONS.forEach((direction) => {
|
||||||
|
const anchor = `moveAnchorChar${direction}`
|
||||||
|
const focus = `moveFocusChar${direction}`
|
||||||
|
|
||||||
|
Changes[`moveChar${direction}`] = (change) => {
|
||||||
|
change[anchor]()[focus]()
|
||||||
|
}
|
||||||
|
|
||||||
|
Changes[`moveStartChar${direction}`] = (change) => {
|
||||||
|
if (change.state.isBackward) {
|
||||||
|
change[focus]()
|
||||||
|
} else {
|
||||||
|
change[anchor]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Changes[`moveEndChar${direction}`] = (change) => {
|
||||||
|
if (change.state.isBackward) {
|
||||||
|
change[anchor]()
|
||||||
|
} else {
|
||||||
|
change[focus]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Changes[`extendChar${direction}`] = (change) => {
|
||||||
|
change[`moveFocusChar${direction}`]()
|
||||||
|
}
|
||||||
|
|
||||||
|
Changes[`collapseChar${direction}`] = (change) => {
|
||||||
|
const collapse = direction == 'Forward' ? 'collapseToEnd' : 'collapseToStart'
|
||||||
|
change[collapse]()[`moveChar${direction}`]()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mix in alias methods.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ALIAS_METHODS = [
|
||||||
|
['collapseLineBackward', 'collapseToStartOfBlock'],
|
||||||
|
['collapseLineForward', 'collapseToEndOfBlock'],
|
||||||
|
['extendLineBackward', 'extendToStartOfBlock'],
|
||||||
|
['extendLineForward', 'extendToEndOfBlock'],
|
||||||
|
]
|
||||||
|
|
||||||
|
ALIAS_METHODS.forEach(([ alias, method ]) => {
|
||||||
|
Changes[alias] = function (change, ...args) {
|
||||||
|
change[method](change, ...args)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mix in selection changes that are just a proxy for the selection method.
|
* Mix in selection changes that are just a proxy for the selection method.
|
||||||
*/
|
*/
|
||||||
@@ -211,6 +350,10 @@ PROXY_TRANSFORMS.forEach((method) => {
|
|||||||
|
|
||||||
const PREFIXES = [
|
const PREFIXES = [
|
||||||
'moveTo',
|
'moveTo',
|
||||||
|
'moveAnchorTo',
|
||||||
|
'moveFocusTo',
|
||||||
|
'moveStartTo',
|
||||||
|
'moveEndTo',
|
||||||
'collapseTo',
|
'collapseTo',
|
||||||
'extendTo',
|
'extendTo',
|
||||||
]
|
]
|
||||||
@@ -237,29 +380,78 @@ PREFIXES.forEach((prefix) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
edges.forEach((edge) => {
|
edges.forEach((edge) => {
|
||||||
DIRECTIONS.forEach((direction) => {
|
const method = `${prefix}${edge}Of`
|
||||||
KINDS.forEach((kind) => {
|
|
||||||
const get = `get${direction}${kind}`
|
|
||||||
const getAtRange = `get${kind}sAtRange`
|
|
||||||
const index = direction == 'Next' ? 'last' : 'first'
|
|
||||||
const method = `${prefix}${edge}Of`
|
|
||||||
const name = `${method}${direction}${kind}`
|
|
||||||
|
|
||||||
Changes[name] = (change) => {
|
KINDS.forEach((kind) => {
|
||||||
|
const getNode = kind == 'Text' ? 'getNode' : `getClosest${kind}`
|
||||||
|
|
||||||
|
Changes[`${method}${kind}`] = (change) => {
|
||||||
|
const { state } = change
|
||||||
|
const { document, selection } = state
|
||||||
|
const node = document[getNode](selection.startKey)
|
||||||
|
if (!node) return
|
||||||
|
change[method](node)
|
||||||
|
}
|
||||||
|
|
||||||
|
DIRECTIONS.forEach((direction) => {
|
||||||
|
const getDirectionNode = `get${direction}${kind}`
|
||||||
|
const directionKey = direction == 'Next' ? 'startKey' : 'endKey'
|
||||||
|
|
||||||
|
Changes[`${method}${direction}${kind}`] = (change) => {
|
||||||
const { state } = change
|
const { state } = change
|
||||||
const { document, selection } = state
|
const { document, selection } = state
|
||||||
const nodes = document[getAtRange](selection)
|
const node = document[getNode](selection[directionKey])
|
||||||
const node = nodes[index]()
|
if (!node) return
|
||||||
const target = document[get](node.key)
|
const target = document[getDirectionNode](node.key)
|
||||||
if (!target) return
|
if (!target) return
|
||||||
const next = selection[method](target)
|
change[method](target)
|
||||||
change.select(next)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set `properties` on the selection.
|
||||||
|
*
|
||||||
|
* @param {Mixed} ...args
|
||||||
|
* @param {Change} change
|
||||||
|
*/
|
||||||
|
|
||||||
|
Changes.moveTo = (change, properties) => {
|
||||||
|
logger.deprecate('0.17.0', 'The `moveTo()` change is deprecated, please use `select()` instead.')
|
||||||
|
change.select(properties)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unset the selection's marks.
|
||||||
|
*
|
||||||
|
* @param {Change} change
|
||||||
|
*/
|
||||||
|
|
||||||
|
Changes.unsetMarks = (change) => {
|
||||||
|
logger.deprecate('0.17.0', 'The `unsetMarks()` change is deprecated.')
|
||||||
|
change.select({ marks: null })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unset the selection, removing an association to a node.
|
||||||
|
*
|
||||||
|
* @param {Change} change
|
||||||
|
*/
|
||||||
|
|
||||||
|
Changes.unsetSelection = (change) => {
|
||||||
|
logger.deprecate('0.17.0', 'The `unsetSelection()` change is deprecated, please use `deselect()` instead.')
|
||||||
|
change.select({
|
||||||
|
anchorKey: null,
|
||||||
|
anchorOffset: 0,
|
||||||
|
focusKey: null,
|
||||||
|
focusOffset: 0,
|
||||||
|
isFocused: false,
|
||||||
|
isBackward: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mix in deprecated changes with a warning.
|
* Mix in deprecated changes with a warning.
|
||||||
*/
|
*/
|
||||||
|
@@ -326,9 +326,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get startBlock() {
|
get startBlock() {
|
||||||
return this.selection.isUnset
|
return this.startKey && this.document.getClosestBlock(this.startKey)
|
||||||
? null
|
|
||||||
: this.document.getClosestBlock(this.selection.startKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -338,9 +336,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get endBlock() {
|
get endBlock() {
|
||||||
return this.selection.isUnset
|
return this.endKey && this.document.getClosestBlock(this.endKey)
|
||||||
? null
|
|
||||||
: this.document.getClosestBlock(this.selection.endKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -350,9 +346,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get anchorBlock() {
|
get anchorBlock() {
|
||||||
return this.selection.isUnset
|
return this.anchorKey && this.document.getClosestBlock(this.anchorKey)
|
||||||
? null
|
|
||||||
: this.document.getClosestBlock(this.selection.anchorKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -362,9 +356,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get focusBlock() {
|
get focusBlock() {
|
||||||
return this.selection.isUnset
|
return this.focusKey && this.document.getClosestBlock(this.focusKey)
|
||||||
? null
|
|
||||||
: this.document.getClosestBlock(this.selection.focusKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -374,9 +366,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get startInline() {
|
get startInline() {
|
||||||
return this.selection.isUnset
|
return this.startKey && this.document.getClosestInline(this.startKey)
|
||||||
? null
|
|
||||||
: this.document.getClosestInline(this.selection.startKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -386,9 +376,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get endInline() {
|
get endInline() {
|
||||||
return this.selection.isUnset
|
return this.endKey && this.document.getClosestInline(this.endKey)
|
||||||
? null
|
|
||||||
: this.document.getClosestInline(this.selection.endKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -398,9 +386,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get anchorInline() {
|
get anchorInline() {
|
||||||
return this.selection.isUnset
|
return this.anchorKey && this.document.getClosestInline(this.anchorKey)
|
||||||
? null
|
|
||||||
: this.document.getClosestInline(this.selection.anchorKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -410,9 +396,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get focusInline() {
|
get focusInline() {
|
||||||
return this.selection.isUnset
|
return this.focusKey && this.document.getClosestInline(this.focusKey)
|
||||||
? null
|
|
||||||
: this.document.getClosestInline(this.selection.focusKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -422,9 +406,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get startText() {
|
get startText() {
|
||||||
return this.selection.isUnset
|
return this.startKey && this.document.getDescendant(this.startKey)
|
||||||
? null
|
|
||||||
: this.document.getDescendant(this.selection.startKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -434,9 +416,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get endText() {
|
get endText() {
|
||||||
return this.selection.isUnset
|
return this.endKey && this.document.getDescendant(this.endKey)
|
||||||
? null
|
|
||||||
: this.document.getDescendant(this.selection.endKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -446,9 +426,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get anchorText() {
|
get anchorText() {
|
||||||
return this.selection.isUnset
|
return this.anchorKey && this.document.getDescendant(this.anchorKey)
|
||||||
? null
|
|
||||||
: this.document.getDescendant(this.selection.anchorKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -458,9 +436,67 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get focusText() {
|
get focusText() {
|
||||||
return this.selection.isUnset
|
return this.focusKey && this.document.getDescendant(this.focusKey)
|
||||||
? null
|
}
|
||||||
: this.document.getDescendant(this.selection.focusKey)
|
|
||||||
|
/**
|
||||||
|
* Get the next block node.
|
||||||
|
*
|
||||||
|
* @return {Block}
|
||||||
|
*/
|
||||||
|
|
||||||
|
get nextBlock() {
|
||||||
|
return this.endKey && this.document.getNextBlock(this.endKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the previous block node.
|
||||||
|
*
|
||||||
|
* @return {Block}
|
||||||
|
*/
|
||||||
|
|
||||||
|
get previousBlock() {
|
||||||
|
return this.startKey && this.document.getPreviousBlock(this.startKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next inline node.
|
||||||
|
*
|
||||||
|
* @return {Inline}
|
||||||
|
*/
|
||||||
|
|
||||||
|
get nextInline() {
|
||||||
|
return this.endKey && this.document.getNextInline(this.endKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the previous inline node.
|
||||||
|
*
|
||||||
|
* @return {Inline}
|
||||||
|
*/
|
||||||
|
|
||||||
|
get previousInline() {
|
||||||
|
return this.startKey && this.document.getPreviousInline(this.startKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next text node.
|
||||||
|
*
|
||||||
|
* @return {Text}
|
||||||
|
*/
|
||||||
|
|
||||||
|
get nextText() {
|
||||||
|
return this.endKey && this.document.getNextText(this.endKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the previous text node.
|
||||||
|
*
|
||||||
|
* @return {Text}
|
||||||
|
*/
|
||||||
|
|
||||||
|
get previousText() {
|
||||||
|
return this.startKey && this.document.getPreviousText(this.startKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -554,19 +590,22 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get isEmpty() {
|
get isEmpty() {
|
||||||
const { startOffset, endOffset } = this
|
if (this.isCollapsed) return true
|
||||||
|
if (this.endOffset != 0 && this.startOffset != 0) return false
|
||||||
if (this.isCollapsed) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endOffset != 0 && startOffset != 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.fragment.text.length == 0
|
return this.fragment.text.length == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the selection is collapsed in a void node.
|
||||||
|
*
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
get isInVoid() {
|
||||||
|
if (this.isExpanded) return false
|
||||||
|
return this.document.hasVoidParent(this.startKey)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new `Change` with the current state as a starting point.
|
* Create a new `Change` with the current state as a starting point.
|
||||||
*
|
*
|
||||||
|
10
yarn.lock
10
yarn.lock
@@ -3352,13 +3352,9 @@ is-glob@^3.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-extglob "^2.1.0"
|
is-extglob "^2.1.0"
|
||||||
|
|
||||||
is-hotkey@^0.0.1:
|
is-hotkey@^0.1.1:
|
||||||
version "0.0.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.0.1.tgz#d8d817209b34292551a85357e65cdbfcfa763443"
|
resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.1.1.tgz#b279a2fd108391be9aa93c6cb317f50357da549a"
|
||||||
|
|
||||||
is-hotkey@^0.0.3:
|
|
||||||
version "0.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.0.3.tgz#3713fea135f86528c87cf39810b3934e45151390"
|
|
||||||
|
|
||||||
is-image@^1.0.1:
|
is-image@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
|
Reference in New Issue
Block a user