add: editor insert & replace api with selection

This commit is contained in:
windingwind 2023-04-15 23:57:30 +08:00
parent 31d8e98594
commit f36c2b3932
2 changed files with 65 additions and 6 deletions

View File

@ -1,6 +1,14 @@
// The prosemirror imports are only for type hint
import { Node, NodeType } from "prosemirror-model";
import { Mark, MarkType, ResolvedPos, Attrs } from "prosemirror-model";
import {
Node,
NodeType,
Mark,
MarkType,
ResolvedPos,
Attrs,
DOMParser,
Schema,
} from "prosemirror-model";
import { EditorState, TextSelection } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
@ -8,6 +16,38 @@ declare const _currentEditorInstance: {
_editorCore: EditorCore;
};
function fromHTML(schema: Schema, html: string, slice?: boolean) {
let domNode = document.createElement("div");
domNode.innerHTML = html;
let fragment = document.createDocumentFragment();
while (domNode.firstChild) {
fragment.appendChild(domNode.firstChild);
}
if (slice) {
return DOMParser.fromSchema(schema).parseSlice(fragment);
} else {
return DOMParser.fromSchema(schema).parse(fragment);
}
}
function getSliceFromHTML(state: EditorState, html: string) {
return fromHTML(state.schema, html, true);
}
function getNodeFromHTML(state: EditorState, html: string) {
return fromHTML(state.schema, html);
}
function setSelection(anchor: number, head?: number | undefined) {
return (state: EditorState, dispatch?: EditorView["dispatch"]) => {
const { tr, selection } = state;
const _TextSelection =
selection.constructor as unknown as typeof TextSelection;
tr.setSelection(_TextSelection.create(tr.doc, anchor, head));
dispatch && dispatch(tr);
};
}
// Code from https://github.com/ueberdosis/tiptap/tree/main/packages/core/src/helpers
function objectIncludes(object1: any, object2: any) {
@ -148,7 +188,8 @@ function replaceRangeNode(
nodeType: NodeType,
nodeAttrs: Attrs | string,
markType?: MarkType,
markAttrs?: Attrs | string
markAttrs?: Attrs | string,
select?: boolean
) {
return (state: EditorState, dispatch: EditorView["dispatch"]) => {
const { tr } = state;
@ -166,6 +207,9 @@ function replaceRangeNode(
);
console.log("Replace Node", from, to, node);
tr.replaceWith(from, to, node);
if (select) {
setSelection(from + node.nodeSize, from)(state);
}
dispatch(tr);
};
}
@ -333,6 +377,9 @@ export const BetterNotesEditorAPI = {
updateImageDimensions,
getHeadingLevelInRange,
updateHeadingsInRange,
getSliceFromHTML,
getNodeFromHTML,
setSelection,
};
// @ts-ignore

View File

@ -14,6 +14,7 @@ export {
getRangeAtCursor,
getLineAtCursor,
getPositionAtLine,
getPositionAtCursor,
getURLAtCursor,
updateImageDimensionsAtCursor,
updateURLAtCursor,
@ -24,9 +25,11 @@ export {
function insert(
editor: Zotero.EditorInstance,
content: string = "",
position: number | "end" | "start" | "cursor" = "cursor"
position: number | "end" | "start" | "cursor" = "cursor",
select?: boolean
) {
const core = getEditorCore(editor);
const EditorAPI = getEditorAPI(editor);
if (position === "cursor") {
position = getPositionAtCursor(editor);
} else if (position === "end") {
@ -36,6 +39,13 @@ function insert(
}
position = Math.max(0, Math.min(position, core.view.state.doc.content.size));
(core as any).insertHTML(position, content);
if (select) {
const slice = EditorAPI.getSliceFromHTML(core.view.state, content);
EditorAPI.setSelection(position + slice.content.size, position)(
core.view.state,
core.view.dispatch
);
}
}
function del(editor: Zotero.EditorInstance, from: number, to: number) {
@ -92,7 +102,8 @@ function replace(
| "backgroundColor"
| "link"
| "code",
markAttrs: Record<string, any>
markAttrs: Record<string, any>,
select?: boolean
) {
const core = getEditorCore(editor);
const EditorAPI = getEditorAPI(editor);
@ -104,7 +115,8 @@ function replace(
schema.nodes[nodeTypeName],
JSON.stringify(nodeAttrs),
schema.marks[markTypeName],
JSON.stringify(markAttrs)
JSON.stringify(markAttrs),
select
)(core.view.state, core.view.dispatch);
}