@kerebron/editor 0.4.28 → 0.4.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/esm/CoreEditor.js +1 -0
  2. package/esm/CoreEditor.js.map +1 -0
  3. package/esm/DummyEditorView.js +1 -0
  4. package/esm/DummyEditorView.js.map +1 -0
  5. package/esm/Extension.js +1 -0
  6. package/esm/Extension.js.map +1 -0
  7. package/esm/ExtensionManager.js +1 -0
  8. package/esm/ExtensionManager.js.map +1 -0
  9. package/esm/Mark.js +1 -0
  10. package/esm/Mark.js.map +1 -0
  11. package/esm/Node.js +1 -0
  12. package/esm/Node.js.map +1 -0
  13. package/esm/commands/CommandManager.js +1 -0
  14. package/esm/commands/CommandManager.js.map +1 -0
  15. package/esm/commands/baseCommandFactories.js +1 -0
  16. package/esm/commands/baseCommandFactories.js.map +1 -0
  17. package/esm/commands/createChainableState.js +1 -0
  18. package/esm/commands/createChainableState.js.map +1 -0
  19. package/esm/commands/keyCommandFactories.js +1 -0
  20. package/esm/commands/keyCommandFactories.js.map +1 -0
  21. package/esm/commands/mod.js +1 -0
  22. package/esm/commands/mod.js.map +1 -0
  23. package/esm/commands/replaceCommandFactories.js +1 -0
  24. package/esm/commands/replaceCommandFactories.js.map +1 -0
  25. package/esm/commands/types.js +1 -0
  26. package/esm/commands/types.js.map +1 -0
  27. package/esm/mod.js +1 -0
  28. package/esm/mod.js.map +1 -0
  29. package/esm/nodeToTreeString.js +1 -0
  30. package/esm/nodeToTreeString.js.map +1 -0
  31. package/esm/plugins/TrackSelecionPlugin.js +1 -0
  32. package/esm/plugins/TrackSelecionPlugin.js.map +1 -0
  33. package/esm/plugins/input-rules/InputRulesPlugin.js +1 -0
  34. package/esm/plugins/input-rules/InputRulesPlugin.js.map +1 -0
  35. package/esm/plugins/input-rules/mod.js +1 -0
  36. package/esm/plugins/input-rules/mod.js.map +1 -0
  37. package/esm/plugins/input-rules/rulebuilders.js +1 -0
  38. package/esm/plugins/input-rules/rulebuilders.js.map +1 -0
  39. package/esm/plugins/keymap/keymap.js +1 -0
  40. package/esm/plugins/keymap/keymap.js.map +1 -0
  41. package/esm/plugins/keymap/mod.js +1 -0
  42. package/esm/plugins/keymap/mod.js.map +1 -0
  43. package/esm/plugins/keymap/w3c-keyname.js +1 -0
  44. package/esm/plugins/keymap/w3c-keyname.js.map +1 -0
  45. package/esm/search/mod.js +1 -0
  46. package/esm/search/mod.js.map +1 -0
  47. package/esm/search/query.js +1 -0
  48. package/esm/search/query.js.map +1 -0
  49. package/esm/search/search.js +1 -0
  50. package/esm/search/search.js.map +1 -0
  51. package/esm/types.js +1 -0
  52. package/esm/types.js.map +1 -0
  53. package/esm/ui.js +1 -0
  54. package/esm/ui.js.map +1 -0
  55. package/esm/utilities/SmartOutput.js +1 -0
  56. package/esm/utilities/SmartOutput.js.map +1 -0
  57. package/esm/utilities/createNodeFromContent.js +1 -0
  58. package/esm/utilities/createNodeFromContent.js.map +1 -0
  59. package/esm/utilities/getHtmlAttributes.js +1 -0
  60. package/esm/utilities/getHtmlAttributes.js.map +1 -0
  61. package/esm/utilities/getShadowRoot.js +1 -0
  62. package/esm/utilities/getShadowRoot.js.map +1 -0
  63. package/esm/utilities/mod.js +1 -0
  64. package/esm/utilities/mod.js.map +1 -0
  65. package/esm/utilities/toRawTextResult.js +1 -0
  66. package/esm/utilities/toRawTextResult.js.map +1 -0
  67. package/package.json +5 -1
  68. package/src/CoreEditor.ts +277 -0
  69. package/src/DummyEditorView.ts +403 -0
  70. package/src/Extension.ts +63 -0
  71. package/src/ExtensionManager.ts +328 -0
  72. package/src/Mark.ts +47 -0
  73. package/src/Node.ts +66 -0
  74. package/src/commands/CommandManager.ts +145 -0
  75. package/src/commands/baseCommandFactories.ts +1103 -0
  76. package/src/commands/createChainableState.ts +36 -0
  77. package/src/commands/keyCommandFactories.ts +26 -0
  78. package/src/commands/mod.ts +104 -0
  79. package/src/commands/replaceCommandFactories.ts +129 -0
  80. package/src/commands/types.ts +30 -0
  81. package/src/mod.ts +8 -0
  82. package/src/nodeToTreeString.ts +100 -0
  83. package/src/plugins/TrackSelecionPlugin.ts +27 -0
  84. package/src/plugins/input-rules/InputRulesPlugin.ts +242 -0
  85. package/src/plugins/input-rules/mod.ts +2 -0
  86. package/src/plugins/input-rules/rulebuilders.ts +88 -0
  87. package/src/plugins/keymap/keymap.ts +117 -0
  88. package/src/plugins/keymap/mod.ts +1 -0
  89. package/src/plugins/keymap/w3c-keyname.ts +123 -0
  90. package/src/search/mod.ts +2 -0
  91. package/src/search/query.ts +412 -0
  92. package/src/search/search.ts +284 -0
  93. package/src/types.ts +71 -0
  94. package/src/ui.ts +35 -0
  95. package/src/utilities/SmartOutput.ts +284 -0
  96. package/src/utilities/createNodeFromContent.ts +66 -0
  97. package/src/utilities/getHtmlAttributes.ts +68 -0
  98. package/src/utilities/getShadowRoot.ts +18 -0
  99. package/src/utilities/mod.ts +5 -0
  100. package/src/utilities/toRawTextResult.ts +27 -0
  101. package/assets/base.css +0 -114
  102. package/assets/content.css +0 -35
  103. package/assets/gapcursor.css +0 -25
  104. package/assets/index.css +0 -2
  105. package/assets/main.css +0 -8
  106. package/assets/mobile.css +0 -33
  107. package/assets/prosemirror.css +0 -20
  108. package/assets/search.css +0 -6
  109. package/assets/vars.css +0 -110
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toRawTextResult.js","sourceRoot":"","sources":["../../src/utilities/toRawTextResult.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,OAAO,GAAG,CAAC;IAC1D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,MAAM,UAAU,GAA2B,EAAE,CAAC;IAE9C,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,UAAU,CAAC,IAAI,CAAC;YACd,OAAO;YACP,SAAS;YACT,SAAS,EAAE,CAAC;YACZ,SAAS;SACV,CAAC,CAAC;QAEH,SAAS,EAAE,CAAC;QACZ,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAC7B,OAAO,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO;QACL,OAAO;QACP,UAAU;KACX,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerebron/editor",
3
- "version": "0.4.28",
3
+ "version": "0.4.30",
4
4
  "license": "MIT",
5
5
  "module": "./esm/mod.js",
6
6
  "exports": {
@@ -30,6 +30,10 @@
30
30
  }
31
31
  },
32
32
  "scripts": {},
33
+ "files": [
34
+ "esm",
35
+ "src"
36
+ ],
33
37
  "style": "assets/index.css",
34
38
  "dependencies": {
35
39
  "prosemirror-model": "1.25.3",
@@ -0,0 +1,277 @@
1
+ import { EditorView } from 'prosemirror-view';
2
+ import { Node as ProseMirrorNode, Schema } from 'prosemirror-model';
3
+
4
+ import { ExtensionManager } from './ExtensionManager.js';
5
+ import type { EditorConfig, JSONContent } from './types.js';
6
+ import { EditorState, Transaction } from 'prosemirror-state';
7
+ import { CommandManager } from './commands/CommandManager.js';
8
+ import { nodeToTreeString } from './nodeToTreeString.js';
9
+ import { DummyEditorView } from './DummyEditorView.js';
10
+ import { ChainedCommands } from './commands/mod.js';
11
+ import { createNodeFromObject } from './utilities/createNodeFromContent.js';
12
+ import { Extension } from './Extension.js';
13
+ import { defaultUi, EditorUi } from './ui.js';
14
+
15
+ function ensureDocSchema(
16
+ doc: ProseMirrorNode,
17
+ schema: Schema,
18
+ ): ProseMirrorNode {
19
+ if (doc.type.schema === schema) {
20
+ return doc;
21
+ }
22
+
23
+ const json = doc.toJSON();
24
+ return ProseMirrorNode.fromJSON(schema, json);
25
+ }
26
+
27
+ export class CoreEditor extends EventTarget {
28
+ public readonly config: Partial<EditorConfig> = {
29
+ element: undefined,
30
+ extensions: [],
31
+ };
32
+ private extensionManager: ExtensionManager;
33
+ private commandManager: CommandManager;
34
+ public view!: EditorView | DummyEditorView;
35
+ public state!: EditorState;
36
+ public ui: EditorUi = defaultUi(this);
37
+
38
+ constructor(config: Partial<EditorConfig> = {}) {
39
+ super();
40
+ this.config = {
41
+ ...this.config,
42
+ ...config,
43
+ };
44
+
45
+ this.commandManager = new CommandManager(
46
+ this,
47
+ );
48
+
49
+ this.extensionManager = new ExtensionManager(
50
+ this.config.extensions || [],
51
+ this,
52
+ this.commandManager,
53
+ );
54
+
55
+ this.extensionManager.created();
56
+
57
+ // const content = this.options.content ? this.options.content : {
58
+ // type: this.extensionManager.schema.topNodeType.name,
59
+ // content: this.extensionManager.schema.topNodeType.spec.EMPTY_DOC,
60
+ // };
61
+ const content = this.config.content
62
+ ? this.config.content
63
+ : this.extensionManager.schema.topNodeType.spec.EMPTY_DOC;
64
+
65
+ this.createView(content);
66
+ this.setupPlugins();
67
+ }
68
+
69
+ getExtension<T extends Extension>(name: string): T | undefined {
70
+ return this.extensionManager.getExtension<T>(name);
71
+ }
72
+
73
+ public get schema() {
74
+ return this.extensionManager.schema;
75
+ }
76
+
77
+ public get run() {
78
+ return this.commandManager.run;
79
+ }
80
+
81
+ public get commandFactories() {
82
+ return this.commandManager.commandFactories;
83
+ }
84
+
85
+ public chain(): ChainedCommands {
86
+ return this.commandManager.createChain();
87
+ }
88
+
89
+ public can(): ChainedCommands {
90
+ return this.commandManager.createCan();
91
+ }
92
+
93
+ private createView(content: any) {
94
+ const doc = createNodeFromObject(content, this.schema);
95
+
96
+ this.state = EditorState.create({ doc });
97
+
98
+ if (this.config.element) {
99
+ const view = new EditorView(this.config.element, {
100
+ state: this.state,
101
+ attributes: {
102
+ class: 'kb-editor',
103
+ },
104
+ dispatchTransaction: (tx: Transaction) => this.dispatchTransaction(tx),
105
+ editable: () => !this.config.readOnly,
106
+ });
107
+ this.view = view;
108
+
109
+ const parent = this.config.element.parentNode;
110
+ if (parent) {
111
+ const observer = new MutationObserver((mutations) => {
112
+ for (const mutation of mutations) {
113
+ for (const removedNode of mutation.removedNodes) {
114
+ if (removedNode.contains(view.dom)) {
115
+ // Editor DOM was removed
116
+ observer.disconnect(); // Prevent multiple calls
117
+ view.destroy();
118
+ return;
119
+ }
120
+ }
121
+ }
122
+ });
123
+ }
124
+ } else {
125
+ this.view = new DummyEditorView({
126
+ state: this.state,
127
+ dispatchTransaction: (tx: Transaction) => this.dispatchTransaction(tx),
128
+ });
129
+ }
130
+
131
+ const event = new CustomEvent('doc:loaded', {
132
+ detail: {
133
+ editor: this,
134
+ doc,
135
+ },
136
+ });
137
+ this.dispatchEvent(event);
138
+ }
139
+
140
+ public dispatchTransaction(transaction: Transaction) {
141
+ this.state = this.state.apply(transaction);
142
+ if (this.view) {
143
+ this.view.updateState(this.state);
144
+ const event = new CustomEvent('transaction', {
145
+ detail: {
146
+ editor: this,
147
+ transaction,
148
+ },
149
+ });
150
+ this.dispatchEvent(event);
151
+ }
152
+ if (transaction.docChanged) {
153
+ const event = new CustomEvent('changed', {
154
+ detail: {
155
+ editor: this,
156
+ },
157
+ });
158
+ this.dispatchEvent(event);
159
+ }
160
+ }
161
+
162
+ private setupPlugins() {
163
+ this.state = this.state.reconfigure({
164
+ plugins: this.extensionManager.plugins,
165
+ });
166
+
167
+ if (this.view) {
168
+ this.view.updateState(this.state);
169
+
170
+ this.view.setProps({
171
+ nodeViews: this.extensionManager.nodeViews,
172
+ });
173
+ }
174
+ }
175
+
176
+ public clearDocument() {
177
+ const content = {
178
+ type: this.extensionManager.schema.topNodeType.name,
179
+ content: this.extensionManager.schema.topNodeType.spec.EMPTY_DOC.content,
180
+ };
181
+
182
+ this.setDocument(content);
183
+ }
184
+
185
+ public setDocument(content: any) {
186
+ let doc = createNodeFromObject(content, this.schema, {
187
+ errorOnInvalidContent: true,
188
+ });
189
+ doc = ensureDocSchema(doc, this.schema);
190
+
191
+ this.state = EditorState.create({
192
+ doc,
193
+ plugins: this.state.plugins,
194
+ storedMarks: this.state.storedMarks,
195
+ });
196
+
197
+ if (this.view) {
198
+ this.view.updateState(this.state);
199
+ }
200
+
201
+ const event = new CustomEvent('doc:loaded', {
202
+ detail: {
203
+ editor: this,
204
+ doc,
205
+ },
206
+ });
207
+ this.dispatchEvent(event);
208
+ }
209
+
210
+ public getDocument() {
211
+ return this.state.doc;
212
+ }
213
+
214
+ public async loadDocument(mediaType: string, content: Uint8Array) {
215
+ const converter = this.extensionManager.converters[mediaType];
216
+ if (!converter) {
217
+ throw new Error('Converter not found for: ' + mediaType);
218
+ }
219
+ const doc = await converter.toDoc(content);
220
+
221
+ this.state = EditorState.create({
222
+ doc,
223
+ plugins: this.state.plugins,
224
+ storedMarks: this.state.storedMarks,
225
+ });
226
+
227
+ if (this.view) {
228
+ this.view.updateState(this.state);
229
+ }
230
+
231
+ const event = new CustomEvent('doc:loaded', {
232
+ detail: {
233
+ editor: this,
234
+ doc,
235
+ },
236
+ });
237
+ this.dispatchEvent(event);
238
+ }
239
+
240
+ public async saveDocument(mediaType: string): Promise<Uint8Array> {
241
+ const converter = this.extensionManager.converters[mediaType];
242
+ if (!converter) {
243
+ throw new Error('Converter not found for: ' + mediaType);
244
+ }
245
+
246
+ const json = this.state.doc.toJSON();
247
+ const clonedDoc = ProseMirrorNode.fromJSON(this.state.schema, json);
248
+
249
+ return await converter.fromDoc(clonedDoc);
250
+ }
251
+
252
+ public getJSON(): JSONContent {
253
+ return this.state.doc.toJSON();
254
+ }
255
+
256
+ public clone(options: Partial<EditorConfig> = {}): CoreEditor {
257
+ return new CoreEditor({
258
+ ...options,
259
+ extensions: [...(Array.from(this.extensionManager.extensions) || [])],
260
+ });
261
+ }
262
+
263
+ public debug(doc?: ProseMirrorNode) {
264
+ if (!doc) {
265
+ doc = this.state.doc;
266
+ }
267
+ console.debug(nodeToTreeString(doc));
268
+ }
269
+
270
+ public destroy() {
271
+ const event = new CustomEvent('beforeDestroy', {
272
+ detail: {},
273
+ });
274
+ this.dispatchEvent(event);
275
+ this.view.destroy();
276
+ }
277
+ }
@@ -0,0 +1,403 @@
1
+ // Headless View to be used on Server-side
2
+ // TODO: remove all unnecessary props and methods
3
+
4
+ import {
5
+ EditorState,
6
+ Plugin,
7
+ PluginView,
8
+ Transaction,
9
+ } from 'prosemirror-state';
10
+ import { Mark, Node } from 'prosemirror-model';
11
+
12
+ import { EditorView, MarkView, NodeView } from 'prosemirror-view';
13
+
14
+ import { Decoration, DecorationSource } from 'prosemirror-view';
15
+
16
+ /// An editor view manages the DOM structure that represents an
17
+ /// editable document. Its state and behavior are determined by its
18
+ /// [props](#view.DirectEditorProps).
19
+ export class DummyEditorView {
20
+ /// @internal
21
+ private _props: DirectEditorProps;
22
+ private directPlugins: readonly Plugin[];
23
+ /// @internal
24
+ private nodeViews: NodeViewSet;
25
+ private prevDirectPlugins: readonly Plugin[] = [];
26
+ private pluginViews: PluginView[] = [];
27
+
28
+ /// The view's current [state](#state.EditorState).
29
+ public state: EditorState;
30
+
31
+ /// Create a view. `place` may be a DOM node that the editor should
32
+ /// be appended to, a function that will place it into the document,
33
+ /// or an object whose `mount` property holds the node to use as the
34
+ /// document container. If it is `null`, the editor will not be
35
+ /// added to the document.
36
+ constructor(props: DirectEditorProps) {
37
+ this._props = props;
38
+ this.state = props.state;
39
+ this.directPlugins = props.plugins || [];
40
+ this.directPlugins.forEach(checkStateComponent);
41
+
42
+ this.dispatch = this.dispatch.bind(this);
43
+
44
+ this.editable = getEditable(this);
45
+ this.nodeViews = buildNodeViews(this);
46
+ // TODO initInput(this)
47
+ this.updatePluginViews();
48
+ }
49
+
50
+ /// Indicates whether the editor is currently [editable](#view.EditorProps.editable).
51
+ editable: boolean;
52
+
53
+ /// Holds `true` when a
54
+ /// [composition](https://w3c.github.io/uievents/#events-compositionevents)
55
+ /// is active.
56
+ get composing() {
57
+ return false;
58
+ }
59
+
60
+ get dom() {
61
+ return {
62
+ addEventListener() {},
63
+ removeEventListener() {},
64
+ };
65
+ }
66
+
67
+ /// The view's current [props](#view.EditorProps).
68
+ get props() {
69
+ if (this._props.state != this.state) {
70
+ let prev = this._props;
71
+ this._props = {} as any;
72
+ for (let name in prev) (this._props as any)[name] = (prev as any)[name];
73
+ this._props.state = this.state;
74
+ }
75
+ return this._props;
76
+ }
77
+
78
+ /// Update the view's props. Will immediately cause an update to
79
+ /// the DOM.
80
+ update(props: DirectEditorProps) {
81
+ let prevProps = this._props;
82
+ this._props = props;
83
+ if (props.plugins) {
84
+ props.plugins.forEach(checkStateComponent);
85
+ this.directPlugins = props.plugins;
86
+ }
87
+ this.updateStateInner(props.state, prevProps);
88
+ }
89
+
90
+ /// Update the view by updating existing props object with the object
91
+ /// given as argument. Equivalent to `view.update(Object.assign({},
92
+ /// view.props, props))`.
93
+ setProps(props: Partial<DirectEditorProps>) {
94
+ let updated = {} as DirectEditorProps;
95
+ for (let name in this._props) {
96
+ (updated as any)[name] = (this._props as any)[name];
97
+ }
98
+ updated.state = this.state;
99
+ for (let name in props) (updated as any)[name] = (props as any)[name];
100
+ this.update(updated);
101
+ }
102
+
103
+ /// Update the editor's `state` prop, without touching any of the
104
+ /// other props.
105
+ updateState(state: EditorState) {
106
+ this.updateStateInner(state, this._props);
107
+ }
108
+
109
+ private updateStateInner(state: EditorState, prevProps: DirectEditorProps) {
110
+ let prev = this.state, redraw = false, updateSel = false;
111
+ // When stored marks are added, stop composition, so that they can
112
+ // be displayed.
113
+ if (state.storedMarks && this.composing) {
114
+ // TODO clearComposition(this)
115
+ updateSel = true;
116
+ }
117
+ this.state = state;
118
+ let pluginsChanged = prev.plugins != state.plugins ||
119
+ this._props.plugins != prevProps.plugins;
120
+ if (
121
+ pluginsChanged || this._props.plugins != prevProps.plugins ||
122
+ this._props.nodeViews != prevProps.nodeViews
123
+ ) {
124
+ let nodeViews = buildNodeViews(this);
125
+ if (changedNodeViews(nodeViews, this.nodeViews)) {
126
+ this.nodeViews = nodeViews;
127
+ redraw = true;
128
+ }
129
+ }
130
+
131
+ this.editable = getEditable(this);
132
+
133
+ let updateDoc = redraw;
134
+ if (updateDoc || !state.selection.eq(prev.selection)) updateSel = true;
135
+
136
+ if (updateSel) {
137
+ // Work around an issue in Chrome, IE, and Edge where changing
138
+ // the DOM around an active selection puts it into a broken
139
+ // state where the thing the user sees differs from the
140
+ // selection reported by the Selection object (#710, #973,
141
+ // #1011, #1013, #1035).
142
+ let forceSelUpdate = false;
143
+ if (updateDoc) {
144
+ // If the node that the selection points into is written to,
145
+ // Chrome sometimes starts misreporting the selection, so this
146
+ // tracks that and forces a selection reset when our update
147
+ // did write to the node.
148
+ // TODO if (this.composing) this.input.compositionNode = findCompositionNode(this)
149
+ }
150
+ }
151
+
152
+ this.updatePluginViews(prev);
153
+ }
154
+
155
+ /// @internal
156
+ scrollToSelection() {
157
+ }
158
+
159
+ private destroyPluginViews() {
160
+ let view;
161
+ while (view = this.pluginViews.pop()) if (view.destroy) view.destroy();
162
+ }
163
+
164
+ private updatePluginViews(prevState?: EditorState) {
165
+ if (
166
+ !prevState || prevState.plugins != this.state.plugins ||
167
+ this.directPlugins != this.prevDirectPlugins
168
+ ) {
169
+ this.prevDirectPlugins = this.directPlugins;
170
+ this.destroyPluginViews();
171
+ for (let i = 0; i < this.directPlugins.length; i++) {
172
+ let plugin = this.directPlugins[i];
173
+ if (plugin.spec.view) {
174
+ this.pluginViews.push(plugin.spec.view(<any> this));
175
+ }
176
+ }
177
+ for (let i = 0; i < this.state.plugins.length; i++) {
178
+ let plugin = this.state.plugins[i];
179
+ if (plugin.spec.view) {
180
+ this.pluginViews.push(plugin.spec.view(<any> this));
181
+ }
182
+ }
183
+ } else {
184
+ for (let i = 0; i < this.pluginViews.length; i++) {
185
+ let pluginView = this.pluginViews[i];
186
+ if (pluginView.update) pluginView.update(<any> this, prevState);
187
+ }
188
+ }
189
+ }
190
+
191
+ /// Goes over the values of a prop, first those provided directly,
192
+ /// then those from plugins given to the view, then from plugins in
193
+ /// the state (in order), and calls `f` every time a non-undefined
194
+ /// value is found. When `f` returns a truthy value, that is
195
+ /// immediately returned. When `f` isn't provided, it is treated as
196
+ /// the identity function (the prop value is returned directly).
197
+ someProp<PropName extends keyof EditorProps, Result>(
198
+ propName: PropName,
199
+ f: (value: NonNullable<EditorProps[PropName]>) => Result,
200
+ ): Result | undefined;
201
+ someProp<PropName extends keyof EditorProps>(
202
+ propName: PropName,
203
+ ): NonNullable<EditorProps[PropName]> | undefined;
204
+ someProp<PropName extends keyof EditorProps, Result>(
205
+ propName: PropName,
206
+ f?: (value: NonNullable<EditorProps[PropName]>) => Result,
207
+ ): Result | undefined {
208
+ let prop = this._props && this._props[propName], value;
209
+ if (prop != null && (value = f ? f(prop as any) : prop)) {
210
+ return value as any;
211
+ }
212
+ for (let i = 0; i < this.directPlugins.length; i++) {
213
+ let prop = this.directPlugins[i].props[propName];
214
+ if (prop != null && (value = f ? f(prop as any) : prop)) {
215
+ return value as any;
216
+ }
217
+ }
218
+ let plugins = this.state.plugins;
219
+ if (plugins) {
220
+ for (let i = 0; i < plugins.length; i++) {
221
+ let prop = plugins[i].props[propName];
222
+ if (
223
+ prop != null && (value = f ? f(prop as any) : prop)
224
+ ) return value as any;
225
+ }
226
+ }
227
+ }
228
+
229
+ /// Query whether the view has focus.
230
+ hasFocus() {
231
+ return false;
232
+ }
233
+
234
+ /// Focus the editor.
235
+ focus() {
236
+ }
237
+
238
+ /// Removes the editor from the DOM and destroys all [node
239
+ /// views](#view.NodeView).
240
+ destroy() {
241
+ this.destroyPluginViews();
242
+ }
243
+
244
+ /// This is true when the view has been
245
+ /// [destroyed](#view.DummyEditorView.destroy) (and thus should not be
246
+ /// used anymore).
247
+ get isDestroyed() {
248
+ return false;
249
+ }
250
+
251
+ /// Used for testing.
252
+ dispatchEvent(event: Event) {
253
+ }
254
+
255
+ /// Dispatch a transaction. Will call
256
+ /// [`dispatchTransaction`](#view.DirectEditorProps.dispatchTransaction)
257
+ /// when given, and otherwise defaults to applying the transaction to
258
+ /// the current state and calling
259
+ /// [`updateState`](#view.DummyEditorView.updateState) with the result.
260
+ /// This method is bound to the view instance, so that it can be
261
+ /// easily passed around.
262
+ declare dispatch: (tr: Transaction) => void;
263
+ }
264
+
265
+ DummyEditorView.prototype.dispatch = function (tr: Transaction) {
266
+ let dispatchTransaction = this.props.dispatchTransaction;
267
+ if (dispatchTransaction) dispatchTransaction.call(this, tr);
268
+ else this.updateState(this.state.apply(tr));
269
+ };
270
+
271
+ function getEditable(view: DummyEditorView) {
272
+ return !view.someProp('editable', (value) => value(view.state) === false);
273
+ }
274
+
275
+ function buildNodeViews(view: DummyEditorView) {
276
+ let result: NodeViewSet = Object.create(null);
277
+ function add(obj: NodeViewSet) {
278
+ for (let prop in obj) {
279
+ if (!Object.prototype.hasOwnProperty.call(result, prop)) {
280
+ result[prop] = obj[prop];
281
+ }
282
+ }
283
+ }
284
+ view.someProp('nodeViews', add);
285
+ view.someProp('markViews', add);
286
+ return result;
287
+ }
288
+
289
+ function changedNodeViews(a: NodeViewSet, b: NodeViewSet) {
290
+ let nA = 0, nB = 0;
291
+ for (let prop in a) {
292
+ if (a[prop] != b[prop]) return true;
293
+ nA++;
294
+ }
295
+ for (let _ in b) nB++;
296
+ return nA != nB;
297
+ }
298
+
299
+ function checkStateComponent(plugin: Plugin) {
300
+ if (
301
+ plugin.spec.state || plugin.spec.filterTransaction ||
302
+ plugin.spec.appendTransaction
303
+ ) {
304
+ throw new RangeError(
305
+ 'Plugins passed directly to the view must not have a state component',
306
+ );
307
+ }
308
+ }
309
+
310
+ /// The type of function [provided](#view.EditorProps.nodeViews) to
311
+ /// create [node views](#view.NodeView).
312
+ export type NodeViewConstructor = (
313
+ node: Node,
314
+ view: EditorView,
315
+ getPos: () => number | undefined,
316
+ decorations: readonly Decoration[],
317
+ innerDecorations: DecorationSource,
318
+ ) => NodeView;
319
+
320
+ /// The function types [used](#view.EditorProps.markViews) to create
321
+ /// mark views.
322
+ export type MarkViewConstructor = (
323
+ mark: Mark,
324
+ view: EditorView,
325
+ inline: boolean,
326
+ ) => MarkView;
327
+
328
+ type NodeViewSet = {
329
+ [name: string]: NodeViewConstructor | MarkViewConstructor;
330
+ };
331
+
332
+ /// Helper type that maps event names to event object types, but
333
+ /// includes events that TypeScript's HTMLElementEventMap doesn't know
334
+ /// about.
335
+ export interface DOMEventMap extends HTMLElementEventMap {
336
+ [event: string]: any;
337
+ }
338
+
339
+ /// Props are configuration values that can be passed to an editor view
340
+ /// or included in a plugin. This interface lists the supported props.
341
+ export interface EditorProps<P = any> {
342
+ /// Allows you to pass custom rendering and behavior logic for
343
+ /// nodes. Should map node names to constructor functions that
344
+ /// produce a [`NodeView`](#view.NodeView) object implementing the
345
+ /// node's display behavior. The third argument `getPos` is a
346
+ /// function that can be called to get the node's current position,
347
+ /// which can be useful when creating transactions to update it.
348
+ /// Note that if the node is not in the document, the position
349
+ /// returned by this function will be `undefined`.
350
+ ///
351
+ /// (For backwards compatibility reasons, [mark
352
+ /// views](#view.EditorProps.markViews) can also be included in this
353
+ /// object.)
354
+ nodeViews?: { [node: string]: NodeViewConstructor };
355
+
356
+ /// Pass custom mark rendering functions. Note that these cannot
357
+ /// provide the kind of dynamic behavior that [node
358
+ /// views](#view.NodeView) can—they just provide custom rendering
359
+ /// logic. The third argument indicates whether the mark's content
360
+ /// is inline.
361
+ markViews?: { [mark: string]: MarkViewConstructor };
362
+
363
+ /// When this returns false, the content of the view is not directly
364
+ /// editable.
365
+ editable?: (this: P, state: EditorState) => boolean;
366
+
367
+ /// Control the DOM attributes of the editable element. May be either
368
+ /// an object or a function going from an editor state to an object.
369
+ /// By default, the element will get a class `"ProseMirror"`, and
370
+ /// will have its `contentEditable` attribute determined by the
371
+ /// [`editable` prop](#view.EditorProps.editable). Additional classes
372
+ /// provided here will be added to the class. For other attributes,
373
+ /// the value provided first (as in
374
+ /// [`someProp`](#view.DummyEditorView.someProp)) will be used.
375
+ attributes?:
376
+ | { [name: string]: string }
377
+ | ((state: EditorState) => { [name: string]: string });
378
+ }
379
+
380
+ /// The props object given directly to the editor view supports some
381
+ /// fields that can't be used in plugins:
382
+ export interface DirectEditorProps extends EditorProps {
383
+ /// The current state of the editor.
384
+ state: EditorState;
385
+
386
+ /// A set of plugins to use in the view, applying their [plugin
387
+ /// view](#state.PluginSpec.view) and
388
+ /// [props](#state.PluginSpec.props). Passing plugins with a state
389
+ /// component (a [state field](#state.PluginSpec.state) field or a
390
+ /// [transaction](#state.PluginSpec.filterTransaction) filter or
391
+ /// appender) will result in an error, since such plugins must be
392
+ /// present in the state to work.
393
+ plugins?: readonly Plugin[];
394
+
395
+ /// The callback over which to send transactions (state updates)
396
+ /// produced by the view. If you specify this, you probably want to
397
+ /// make sure this ends up calling the view's
398
+ /// [`updateState`](#view.DummyEditorView.updateState) method with a new
399
+ /// state that has the transaction
400
+ /// [applied](#state.EditorState.apply). The callback will be bound to have
401
+ /// the view instance as its `this` binding.
402
+ dispatchTransaction?: (tr: Transaction) => void;
403
+ }