79 lines
1.7 KiB
TypeScript
79 lines
1.7 KiB
TypeScript
import { EditorState, TextSelection } from 'prosemirror-state'
|
||
import { Command, RawCommands, FocusPosition } from '../types'
|
||
import minMax from '../utilities/minMax'
|
||
import isTextSelection from '../helpers/isTextSelection'
|
||
|
||
function resolveSelection(state: EditorState, position: FocusPosition = null) {
|
||
if (!position) {
|
||
return null
|
||
}
|
||
|
||
if (position === 'start' || position === true) {
|
||
return {
|
||
from: 0,
|
||
to: 0,
|
||
}
|
||
}
|
||
|
||
if (position === 'end') {
|
||
const { size } = state.doc.content
|
||
|
||
return {
|
||
from: size,
|
||
to: size,
|
||
}
|
||
}
|
||
|
||
return {
|
||
from: position,
|
||
to: position,
|
||
}
|
||
}
|
||
|
||
declare module '@tiptap/core' {
|
||
interface Commands {
|
||
focus: {
|
||
/**
|
||
* Focus the editor at the given position.
|
||
*/
|
||
focus: (position?: FocusPosition) => Command,
|
||
}
|
||
}
|
||
}
|
||
|
||
export const focus: RawCommands['focus'] = (position = null) => ({
|
||
editor,
|
||
view,
|
||
tr,
|
||
dispatch,
|
||
}) => {
|
||
if ((view.hasFocus() && position === null) || position === false) {
|
||
return true
|
||
}
|
||
|
||
// we don’t try to resolve a NodeSelection or CellSelection
|
||
if (dispatch && position === null && !isTextSelection(editor.state.selection)) {
|
||
view.focus()
|
||
return true
|
||
}
|
||
|
||
const { from, to } = resolveSelection(editor.state, position) || editor.state.selection
|
||
const { doc, storedMarks } = tr
|
||
const resolvedFrom = minMax(from, 0, doc.content.size)
|
||
const resolvedEnd = minMax(to, 0, doc.content.size)
|
||
const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd)
|
||
const isSameSelection = editor.state.selection.eq(selection)
|
||
|
||
if (dispatch) {
|
||
tr.setSelection(selection)
|
||
|
||
if (isSameSelection && storedMarks) {
|
||
tr.setStoredMarks(storedMarks)
|
||
}
|
||
|
||
view.focus()
|
||
}
|
||
|
||
return true
|
||
}
|