optimization of cursor, and fix some cursor related issues (#963)

* optimization of cursor, and fix some cursor related issues

* remove duplicate codes
This commit is contained in:
Ran Luo 2019-04-23 23:45:43 +08:00 committed by GitHub
parent c4664546bd
commit a900b7f2ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 97 additions and 67 deletions

View File

@ -64,10 +64,10 @@ const arrowCtrl = ContentState => {
}
}
// Just do nothing if the cursor is not collapsed
// Just do nothing if the cursor is not collapsed or `shiftKey` pressed
if (
(start.key === end.key && start.offset !== end.offset) ||
start.key !== end.key
start.key !== end.key || event.shiftKey
) {
return
}

View File

@ -25,6 +25,7 @@ import inputCtrl from './inputCtrl'
import tocCtrl from './tocCtrl'
import emojiCtrl from './emojiCtrl'
import importMarkdown from '../utils/importMarkdown'
import Cursor from '../selection/cursor'
const prototypes = [
tabCtrl,
@ -77,6 +78,9 @@ class ContentState {
}
set cursor (cursor) {
if (!(cursor instanceof Cursor)) {
cursor = new Cursor(cursor)
}
const handler = () => {
const { blocks, renderRange, currentCursor } = this
this.history.push({
@ -101,8 +105,6 @@ class ContentState {
if (this.historyTimer) clearTimeout(this.historyTimer)
this.historyTimer = setTimeout(handler, 2000)
}
} else {
cursor.noHistory && delete cursor.noHistory
}
}
@ -160,7 +162,7 @@ class ContentState {
if (isRenderCursor) this.setCursor()
}
partialRender () {
partialRender (isRenderCursor = true) {
const { blocks, cursor, searchMatches: { matches, index }, selectedBlock } = this
const activeBlocks = this.getActiveBlocks()
const [ startKey, endKey ] = this.renderRange
@ -174,7 +176,7 @@ class ContentState {
this.setNextRenderRange()
this.stateRender.collectLabels(blocks)
this.stateRender.partialRender(needRenderBlocks, cursor, activeBlocks, matches, startKey, endKey, selectedBlock)
this.setCursor()
if (isRenderCursor) this.setCursor()
}
/**

View File

@ -35,11 +35,11 @@ const updateCtrl = ContentState => {
ContentState.prototype.checkNeedRender = function (cursor = this.cursor) {
const { labels } = this.stateRender
const { start: cStart, end: cEnd } = cursor
const startBlock = this.getBlock(cStart.key)
const endBlock = this.getBlock(cEnd.key)
const startOffset = cStart.offset
const endOffset = cEnd.offset
const { start: cStart, end: cEnd, anchor, focus } = cursor
const startBlock = this.getBlock(cStart ? cStart.key : anchor.key)
const endBlock = this.getBlock(cEnd ? cEnd.key : focus.key)
const startOffset = cStart ? cStart.offset : anchor.offset
const endOffset = cEnd ? cEnd.offset : focus.offset
for (const token of tokenizer(startBlock.text, undefined, undefined, labels)) {
if (token.type === 'text') continue

View File

@ -198,23 +198,23 @@ class Keyboard {
emojiNode
})
}
// is show format float box?
const { start, end } = selection.getCursorRange()
if (!start || !end) {
const { anchor, focus, start, end } = selection.getCursorRange()
if (!anchor || !focus) {
return
}
if (
!this.isComposed
) {
const { start: oldStart, end: oldEnd } = contentState.cursor
const { anchor: oldAnchor, focus: oldFocus } = contentState.cursor
if (
start.key !== oldStart.key ||
start.offset !== oldStart.offset ||
end.key !== oldEnd.key ||
end.offset !== oldEnd.offset
anchor.key !== oldAnchor.key ||
anchor.offset !== oldAnchor.offset ||
focus.key !== oldFocus.key ||
focus.offset !== oldFocus.offset
) {
const needRender = contentState.checkNeedRender(contentState.cursor) || contentState.checkNeedRender({ start, end })
contentState.cursor = { start, end }
contentState.cursor = { anchor, focus }
if (needRender) {
return contentState.partialRender()
}
@ -227,8 +227,8 @@ class Keyboard {
eventCenter.dispatch('muya-image-picker', { list: [] })
}
const block = contentState.getBlock(start.key)
if (start.key === end.key && start.offset !== end.offset && block.functionType !== 'codeLine') {
const block = contentState.getBlock(anchor.key)
if (anchor.key === focus.key && anchor.offset !== focus.offset && block.functionType !== 'codeLine') {
const reference = contentState.getPositionReference()
const { formats } = contentState.selectionFormats()
eventCenter.dispatch('muya-format-picker', { reference, formats })

View File

@ -0,0 +1,43 @@
import { compareParagraphsOrder } from './dom'
class Cursor {
// You need to provide either `anchor`&&`focus` or `start`&&`end` or all.
constructor ({ anchor, focus, start, end, noHistory = false }) {
if (anchor && focus && start && end) {
this.anchor = anchor
this.focus = focus
this.start = start
this.end = end
} else if (anchor && focus) {
this.anchor = anchor
this.focus = focus
if (anchor.key === focus.key) {
if (anchor.offset <= focus.offset) {
this.start = this.anchor
this.end = this.focus
} else {
this.start = this.focus
this.end = this.anchor
}
} else {
const anchorParagraph = document.querySelector(`#${anchor.key}`)
const focusParagraph = document.querySelector(`#${focus.key}`)
const order = compareParagraphsOrder(anchorParagraph, focusParagraph)
if (order) {
this.start = this.anchor
this.end = this.focus
} else {
this.start = this.focus
this.end = this.anchor
}
}
} else {
this.anchor = this.start = start
this.focus = this.end = end
}
this.noHistory = noHistory
}
}
export default Cursor

View File

@ -2,19 +2,18 @@
* This file is copy from [medium-editor](https://github.com/yabwe/medium-editor)
* and customize for specialized use.
*/
import Cursor from './cursor'
import { CLASS_OR_ID } from '../config'
import {
isBlockContainer,
traverseUp,
getFirstSelectableLeafNode,
getClosestBlockContainer,
getCursorPositionWithinMarkedText,
compareParagraphsOrder,
findNearestParagraph,
getTextContent
} from './dom'
import { CLASS_OR_ID } from '../config'
const filterOnlyParentElements = node => {
return isBlockContainer(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
}
@ -353,6 +352,11 @@ class Selection {
return range
}
setFocus (focusNode, focusOffset) {
const selection = this.doc.getSelection()
selection.extend(focusNode, focusOffset)
}
/**
* Clear the current highlighted selection and set the caret to the start or the end of that prior selection, defaults to end.
*
@ -404,9 +408,9 @@ class Selection {
}
setCursorRange (cursorRange) {
const { start, end } = cursorRange
const startParagraph = document.querySelector(`#${start.key}`)
const endParagraph = document.querySelector(`#${end.key}`)
const { anchor, focus } = cursorRange
const anchorParagraph = document.querySelector(`#${anchor.key}`)
const focusParagraph = document.querySelector(`#${focus.key}`)
const getNodeAndOffset = (node, offset) => {
if (node.nodeType === 3) {
return {
@ -430,11 +434,14 @@ class Selection {
return { node, offset }
}
let { node: startNode, offset: startOffset } = getNodeAndOffset(startParagraph, start.offset)
let { node: endNode, offset: endOffset } = getNodeAndOffset(endParagraph, end.offset)
startOffset = Math.min(startOffset, startNode.textContent.length)
endOffset = Math.min(endOffset, endNode.textContent.length)
this.select(startNode, startOffset, endNode, endOffset)
let { node: anchorNode, offset: anchorOffset } = getNodeAndOffset(anchorParagraph, anchor.offset)
let { node: focusNode, offset: focusOffset } = getNodeAndOffset(focusParagraph, focus.offset)
anchorOffset = Math.min(anchorOffset, anchorNode.textContent.length)
focusOffset = Math.min(focusOffset, focusNode.textContent.length)
// First set the anchor node and anchor offeet, make it collapsed
this.select(anchorNode, anchorOffset)
// Secondly, set the focus node and focus offset.
this.setFocus(focusNode, focusOffset)
}
isValidCursorNode (node) {
@ -465,14 +472,16 @@ class Selection {
} else if (!isAnchorValid && !isFocusValid) {
const editor = document.querySelector('#ag-editor-id').parentNode
editor.blur()
return {
return new Cursor({
start: null,
end: null
}
end: null,
anchor: null,
focus: null
})
}
const startParagraph = findNearestParagraph(anchorNode)
const endParagraph = findNearestParagraph(focusNode)
const anchorParagraph = findNearestParagraph(anchorNode)
const focusParagraph = findNearestParagraph(focusNode)
const getOffsetOfParagraph = (node, paragraph) => {
let offset = 0
@ -491,35 +500,11 @@ class Selection {
: offset + getOffsetOfParagraph(node.parentNode, paragraph)
}
let result = null
if (startParagraph === endParagraph) {
const key = startParagraph.id
const offset1 = getOffsetOfParagraph(anchorNode, startParagraph) + anchorOffset
const offset2 = getOffsetOfParagraph(focusNode, endParagraph) + focusOffset
result = {
start: { key, offset: Math.min(offset1, offset2) },
end: { key, offset: Math.max(offset1, offset2) }
}
} else {
const order = compareParagraphsOrder(startParagraph, endParagraph)
const rawCursor = {
start: {
key: startParagraph.id,
offset: getOffsetOfParagraph(anchorNode, startParagraph) + anchorOffset
},
end: {
key: endParagraph.id,
offset: getOffsetOfParagraph(focusNode, endParagraph) + focusOffset
}
}
if (order) {
result = rawCursor
} else {
result = { start: rawCursor.end, end: rawCursor.start }
}
}
const aOffset = getOffsetOfParagraph(anchorNode, anchorParagraph) + anchorOffset
const fOffset = getOffsetOfParagraph(focusNode, focusParagraph) + focusOffset
const anchor = { key: anchorParagraph.id, offset: aOffset }
const focus = { key: focusParagraph.id, offset: fOffset }
const result = new Cursor({ anchor, focus })
if (needFix) {
this.setCursorRange(result)