Newer
Older
import 'katex/dist/katex.min.css';
// prosemirror imports
import { Schema, Node } from 'prosemirror-model';
import { EditorView } from 'prosemirror-view';
import { EditorState, Plugin, PluginKey } from 'prosemirror-state';
import { schema as basicSchema } from 'prosemirror-schema-basic';
import { addListNodes } from 'prosemirror-schema-list';
import {
chainCommands, deleteSelection, selectNodeBackward, joinBackward,
} from 'prosemirror-commands';
import { keymap } from 'prosemirror-keymap';
import { inputRules } from 'prosemirror-inputrules';
import 'prosemirror-view/style/prosemirror.css';
import 'prosemirror-menu/style/menu.css';
import { exampleSetup } from 'prosemirror-example-setup';
import 'prosemirror-example-setup/style/style.css';
import {
mathPlugin, mathBackspaceCmd, insertMathCmd, mathSerializer,
makeBlockMathInputRule, makeInlineMathInputRule,
REGEX_INLINE_MATH_DOLLARS, REGEX_BLOCK_MATH_DOLLARS,
// I've no idea why eslint can't find the module; rollup can.
// eslint-disable-next-line import/no-unresolved
} from '@benrbray/prosemirror-math';
import '@benrbray/prosemirror-math/dist/prosemirror-math.css';
let anyEditorChangedFlag = false;
export function anyEditorChanged() {
return anyEditorChangedFlag;
}
export function resetAnyEditorChanged() {
anyEditorChangedFlag = false;
}
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
const schema = new Schema({
nodes: addListNodes(basicSchema.spec.nodes, 'paragraph block*', 'block')
.remove('image')
.addToEnd('math_inline', {
group: 'inline math',
content: 'text*', // important!
inline: true, // important!
atom: true, // important!
toDOM: () => ['math-inline', { class: 'math-node' }, 0],
parseDOM: [{
tag: 'math-inline', // important!
}],
})
.addToEnd('math_display', {
group: 'block math',
content: 'text*', // important!
atom: true, // important!
code: true, // important!
toDOM: () => ['math-display', { class: 'math-node' }, 0],
parseDOM: [{
tag: 'math-display', // important!
}],
}),
marks: basicSchema.spec.marks,
});
const inlineMathInputRule = makeInlineMathInputRule(
REGEX_INLINE_MATH_DOLLARS,
schema.nodes.math_inline,
);
const blockMathInputRule = makeBlockMathInputRule(
REGEX_BLOCK_MATH_DOLLARS,
schema.nodes.math_display,
);
const readonlyPlugin = new Plugin({
key: new PluginKey('readonly'),
// Allow selections but prevent any other changes
filterTransaction: (transaction) => transaction.docChanged === false,
});
const changeListenerPlugin = new Plugin({
key: new PluginKey('readonly'),
filterTransaction: (transaction) => {
if (transaction.docChanged) {
anyEditorChangedFlag = true;
}
return true;
},
});
// eslint-disable-next-line import/prefer-default-export
export function editorFromTextArea(textarea, autofocus) {
const plugins = [
...exampleSetup({ schema }),
mathPlugin,
keymap({
'Mod-Space': insertMathCmd(schema.nodes.math_inline),
Backspace: chainCommands(
deleteSelection,
mathBackspaceCmd,
joinBackward,
selectNodeBackward,
),
}),
inputRules({ rules: [inlineMathInputRule, blockMathInputRule] }),
];
if (textarea.disabled || textarea.readOnly) {
plugins.push(readonlyPlugin);
}
// Change listener should be after readonly.
plugins.push(changeListenerPlugin);
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
let docJson = null;
if (textarea.value) {
docJson = JSON.parse(textarea.value);
}
let doc;
if (docJson) {
doc = Node.fromJSON(schema, docJson);
} else {
doc = schema.topNodeType.createAndFill();
}
const state = EditorState.create({ schema, plugins, doc });
const editorElt = document.createElement('div');
editorElt.classList.add('rl-prosemirror-container');
const view = new EditorView(editorElt, {
state,
clipboardTextSerializer: mathSerializer.serializeSlice,
});
if (autofocus) {
document.addEventListener('DOMContentLoaded', () => {
view.focus();
});
}
textarea.parentNode.insertBefore(editorElt, textarea);
// eslint-disable-next-line no-param-reassign
textarea.style.display = 'none';
if (textarea.form) {
textarea.form.addEventListener('submit', () => {
// eslint-disable-next-line no-param-reassign
textarea.value = JSON.stringify(view.state.doc.toJSON());
});
}
textarea.classList.add('rl-managed-by-prosemirror');
return view;
}