文件
mynode/packages/editor/ProseMirror/plugins/utils/createExternalEditorPlugin.ts
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

127 行
3.0 KiB
TypeScript

import { Command, EditorState, Plugin } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { Node } from 'prosemirror-model';
// See the fold example for more information about
// writing similar ProseMirror plugins:
// https://prosemirror.net/examples/fold/
type EditRequest = {
nodeStart: number;
showEditor: true;
} | {
nodeStart?: undefined;
showEditor: false;
};
interface PluginState {
editingNodeAt: number|null;
}
interface EditorDialog {
onPositionChange: (position: number)=> void;
dismiss: ()=> void;
}
export type OnHide = ()=> void;
interface Options {
canEdit: (node: Node, pos: number)=> boolean;
showEditor: (pos: number, view: EditorView, onHide: OnHide)=> EditorDialog;
}
const createExternalEditorPlugin = (options: Options) => {
const plugin = new Plugin<PluginState>({
state: {
init: () => ({
editingNodeAt: null,
}),
apply: (tr, oldValue) => {
let editingAt = oldValue.editingNodeAt;
const editRequest: EditRequest|null = tr.getMeta(plugin);
if (editRequest) {
if (editRequest.showEditor) {
editingAt = editRequest.nodeStart;
} else {
editingAt = null;
}
}
if (editingAt) {
editingAt = tr.mapping.map(editingAt, 1);
}
return { editingNodeAt: editingAt };
},
},
view: () => {
let dialog: EditorDialog|null = null;
return {
update(view, prevState) {
const oldState = plugin.getState(prevState);
const newState = plugin.getState(view.state);
if (newState.editingNodeAt !== null) {
if (oldState.editingNodeAt === null) {
const onHide = () => {
hideEditor(view.state, view.dispatch, view);
};
dialog = options.showEditor(newState.editingNodeAt, view, onHide);
}
dialog?.onPositionChange(newState.editingNodeAt);
} else if (dialog) {
const lastDialog = dialog;
// Set dialog to null before dismissing to prevent infinite recursion.
// Dismissing the dialog can cause the editor state to update, which can
// result in this callback being re-run.
dialog = null;
lastDialog.dismiss();
}
},
};
},
});
const editAt = (nodeStart: number): Command => (state, dispatch) => {
const node = state.doc.nodeAt(nodeStart);
if (!options.canEdit(node, nodeStart)) {
return false;
}
if (dispatch) {
const editRequest: EditRequest = {
nodeStart,
showEditor: true,
};
dispatch(state.tr.setMeta(plugin, editRequest));
}
return true;
};
const isEditorVisible = (state: EditorState) => {
return plugin.getState(state).editingNodeAt !== null;
};
const hideEditor: Command = (state, dispatch) => {
const isEditing = isEditorVisible(state);
if (!isEditing) {
return false;
}
if (dispatch) {
const editRequest: EditRequest = {
showEditor: false,
};
dispatch(state.tr.setMeta(plugin, editRequest));
}
return true;
};
return { plugin, hideEditor, editAt, isEditorVisible };
};
export default createExternalEditorPlugin;