mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-26 16:44:22 +02:00
Android input handing rewrite (#4988)
* wip * wip * wip - fully working without hard marks * fix editor crashes when inserting/deleting at the edges of marks * fix various restore dom related crashes * fix delete with pending changes, zero widths on android, mutation tracking * track placeholder delete in detached strings, zero-widths * wip mark placeholders * get rid of mutation detection in favor of beforeinput * fix various selection race conditions * fix various crashes when deleting at the beginning of nodes * wip diff transforms, selection handling fixes * cleanup restoreDOM and fix noop restore edge-case * fix mark placeholders * fix toSlatePoint edge-case * properly flush user select with pending changes * Prevent editor crash when deleting before a non-contenteditable element * wip markdown shortcut example * transform pending changes and selection by remote changes, simplify pending actions, handle all input types * improve change transform, mark(-placeholder) handling * manually handle gboard bug, fix restoredom nested editor * fix parent mutation condition * cleanup, mark placeholder fixes * mark placeholder fixes * fix mark placeholder condition * hide placeholder if we have pending diffs * cleanup * yarn install * add workaround for swiftkey placeholder issue * cleanup * add changeset * feat(slate-react): fix edge-case crash, add androidPendingDiffs, rename scheduleFlushPendingChanges * flush pending selection on same line without pending changes/action * keep formatting of pending diffs when adding/removing selection marks * unref selection ref on unmatching dom state * improve markdown shortcut example flush trigger to show how a more generic solution would work * fix markdown shortcut example trigger logic * fix isInsertAfterMarkPlaceholder logic
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { Slate, Editable, withReact } from 'slate-react'
|
||||
import {
|
||||
Editor,
|
||||
Transforms,
|
||||
Range,
|
||||
Point,
|
||||
createEditor,
|
||||
Element as SlateElement,
|
||||
Descendant,
|
||||
Editor,
|
||||
Element as SlateElement,
|
||||
Node as SlateNode,
|
||||
Point,
|
||||
Range,
|
||||
Transforms,
|
||||
} from 'slate'
|
||||
import { withHistory } from 'slate-history'
|
||||
import { Editable, ReactEditor, Slate, withReact } from 'slate-react'
|
||||
import { BulletedListElement } from './custom-types'
|
||||
|
||||
const SHORTCUTS = {
|
||||
@@ -31,9 +32,44 @@ const MarkdownShortcutsExample = () => {
|
||||
() => withShortcuts(withReact(withHistory(createEditor()))),
|
||||
[]
|
||||
)
|
||||
|
||||
const handleDOMBeforeInput = useCallback((e: InputEvent) => {
|
||||
queueMicrotask(() => {
|
||||
const pendingDiffs = ReactEditor.androidPendingDiffs(editor)
|
||||
|
||||
const scheduleFlush = pendingDiffs?.some(({ diff, path }) => {
|
||||
if (!diff.text.endsWith(' ')) {
|
||||
return false
|
||||
}
|
||||
|
||||
const { text } = SlateNode.leaf(editor, path)
|
||||
const beforeText = text.slice(0, diff.start) + diff.text.slice(0, -1)
|
||||
if (!(beforeText in SHORTCUTS)) {
|
||||
return
|
||||
}
|
||||
|
||||
const blockEntry = Editor.above(editor, {
|
||||
at: path,
|
||||
match: n => Editor.isBlock(editor, n),
|
||||
})
|
||||
if (!blockEntry) {
|
||||
return false
|
||||
}
|
||||
|
||||
const [, blockPath] = blockEntry
|
||||
return Editor.isStart(editor, Editor.start(editor, path), blockPath)
|
||||
})
|
||||
|
||||
if (scheduleFlush) {
|
||||
ReactEditor.androidScheduleFlush(editor)
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Slate editor={editor} value={initialValue}>
|
||||
<Editable
|
||||
onDOMBeforeInput={handleDOMBeforeInput}
|
||||
renderElement={renderElement}
|
||||
placeholder="Write some markdown..."
|
||||
spellCheck
|
||||
@@ -49,7 +85,7 @@ const withShortcuts = editor => {
|
||||
editor.insertText = text => {
|
||||
const { selection } = editor
|
||||
|
||||
if (text === ' ' && selection && Range.isCollapsed(selection)) {
|
||||
if (text.endsWith(' ') && selection && Range.isCollapsed(selection)) {
|
||||
const { anchor } = selection
|
||||
const block = Editor.above(editor, {
|
||||
match: n => Editor.isBlock(editor, n),
|
||||
@@ -57,12 +93,16 @@ const withShortcuts = editor => {
|
||||
const path = block ? block[1] : []
|
||||
const start = Editor.start(editor, path)
|
||||
const range = { anchor, focus: start }
|
||||
const beforeText = Editor.string(editor, range)
|
||||
const beforeText = Editor.string(editor, range) + text.slice(0, -1)
|
||||
const type = SHORTCUTS[beforeText]
|
||||
|
||||
if (type) {
|
||||
Transforms.select(editor, range)
|
||||
Transforms.delete(editor)
|
||||
|
||||
if (!Range.isCollapsed(range)) {
|
||||
Transforms.delete(editor)
|
||||
}
|
||||
|
||||
const newProperties: Partial<SlateElement> = {
|
||||
type,
|
||||
}
|
||||
|
Reference in New Issue
Block a user