@prosekit/core 0.8.2 → 0.8.4

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 (168) hide show
  1. package/dist/editor-CfkZ4TNU.d.ts +748 -0
  2. package/dist/editor-CfkZ4TNU.d.ts.map +1 -0
  3. package/dist/{editor-DbMrpnmL.js → editor-CizSwUN8.js} +102 -192
  4. package/dist/editor-CizSwUN8.js.map +1 -0
  5. package/dist/prosekit-core-test.d.ts +20 -19
  6. package/dist/prosekit-core-test.d.ts.map +1 -0
  7. package/dist/prosekit-core-test.js +4 -5
  8. package/dist/prosekit-core-test.js.map +1 -0
  9. package/dist/prosekit-core.d.ts +782 -757
  10. package/dist/prosekit-core.d.ts.map +1 -0
  11. package/dist/prosekit-core.js +30 -45
  12. package/dist/prosekit-core.js.map +1 -0
  13. package/package.json +14 -11
  14. package/src/commands/add-mark.ts +53 -0
  15. package/src/commands/expand-mark.ts +96 -0
  16. package/src/commands/insert-default-block.spec.ts +102 -0
  17. package/src/commands/insert-default-block.ts +49 -0
  18. package/src/commands/insert-node.ts +71 -0
  19. package/src/commands/insert-text.ts +24 -0
  20. package/src/commands/remove-mark.ts +54 -0
  21. package/src/commands/remove-node.ts +43 -0
  22. package/src/commands/select-all.ts +16 -0
  23. package/src/commands/set-block-type.ts +64 -0
  24. package/src/commands/set-node-attrs.ts +68 -0
  25. package/src/commands/toggle-mark.ts +65 -0
  26. package/src/commands/toggle-node.ts +47 -0
  27. package/src/commands/toggle-wrap.spec.ts +35 -0
  28. package/src/commands/toggle-wrap.ts +42 -0
  29. package/src/commands/unset-block-type.spec.ts +49 -0
  30. package/src/commands/unset-block-type.ts +84 -0
  31. package/src/commands/unset-mark.spec.ts +35 -0
  32. package/src/commands/unset-mark.ts +38 -0
  33. package/src/commands/wrap.ts +50 -0
  34. package/src/editor/action.spec.ts +143 -0
  35. package/src/editor/action.ts +248 -0
  36. package/src/editor/editor.spec.ts +186 -0
  37. package/src/editor/editor.ts +563 -0
  38. package/src/editor/union.spec.ts +108 -0
  39. package/src/editor/union.ts +47 -0
  40. package/src/editor/with-priority.ts +25 -0
  41. package/src/error.ts +28 -0
  42. package/src/extensions/clipboard-serializer.ts +107 -0
  43. package/src/extensions/command.ts +121 -0
  44. package/src/extensions/default-state.spec.ts +60 -0
  45. package/src/extensions/default-state.ts +76 -0
  46. package/src/extensions/doc.ts +31 -0
  47. package/src/extensions/events/doc-change.ts +34 -0
  48. package/src/extensions/events/dom-event.spec.ts +70 -0
  49. package/src/extensions/events/dom-event.ts +117 -0
  50. package/src/extensions/events/editor-event.ts +293 -0
  51. package/src/extensions/events/focus.spec.ts +50 -0
  52. package/src/extensions/events/focus.ts +28 -0
  53. package/src/extensions/events/plugin-view.ts +132 -0
  54. package/src/extensions/history.ts +81 -0
  55. package/src/extensions/keymap-base.ts +60 -0
  56. package/src/extensions/keymap.spec.ts +89 -0
  57. package/src/extensions/keymap.ts +96 -0
  58. package/src/extensions/mark-spec.spec.ts +177 -0
  59. package/src/extensions/mark-spec.ts +181 -0
  60. package/src/extensions/mark-view-effect.ts +85 -0
  61. package/src/extensions/mark-view.ts +43 -0
  62. package/src/extensions/node-spec.spec.ts +224 -0
  63. package/src/extensions/node-spec.ts +199 -0
  64. package/src/extensions/node-view-effect.ts +85 -0
  65. package/src/extensions/node-view.ts +43 -0
  66. package/src/extensions/paragraph.ts +61 -0
  67. package/src/extensions/plugin.ts +91 -0
  68. package/src/extensions/text.ts +34 -0
  69. package/src/facets/base-extension.ts +54 -0
  70. package/src/facets/command.ts +21 -0
  71. package/src/facets/facet-extension.spec.ts +173 -0
  72. package/src/facets/facet-extension.ts +53 -0
  73. package/src/facets/facet-node.spec.ts +265 -0
  74. package/src/facets/facet-node.ts +185 -0
  75. package/src/facets/facet-types.ts +9 -0
  76. package/src/facets/facet.spec.ts +76 -0
  77. package/src/facets/facet.ts +84 -0
  78. package/src/facets/root.ts +44 -0
  79. package/src/facets/schema-spec.ts +30 -0
  80. package/src/facets/schema.ts +26 -0
  81. package/src/facets/state.ts +57 -0
  82. package/src/facets/union-extension.ts +41 -0
  83. package/src/index.ts +302 -0
  84. package/src/test/index.ts +4 -0
  85. package/src/test/test-builder.ts +68 -0
  86. package/src/test/test-editor.spec.ts +104 -0
  87. package/src/test/test-editor.ts +113 -0
  88. package/src/testing/index.ts +283 -0
  89. package/src/testing/keyboard.ts +5 -0
  90. package/src/types/any-function.ts +4 -0
  91. package/src/types/assert-type-equal.ts +8 -0
  92. package/src/types/attrs.ts +32 -0
  93. package/src/types/base-node-view-options.ts +33 -0
  94. package/src/types/dom-node.ts +1 -0
  95. package/src/types/extension-command.ts +52 -0
  96. package/src/types/extension-mark.ts +15 -0
  97. package/src/types/extension-node.ts +15 -0
  98. package/src/types/extension.spec.ts +56 -0
  99. package/src/types/extension.ts +168 -0
  100. package/src/types/model.ts +54 -0
  101. package/src/types/object-entries.ts +13 -0
  102. package/src/types/pick-string-literal.spec.ts +10 -0
  103. package/src/types/pick-string-literal.ts +6 -0
  104. package/src/types/pick-sub-type.spec.ts +20 -0
  105. package/src/types/pick-sub-type.ts +6 -0
  106. package/src/types/priority.ts +12 -0
  107. package/src/types/setter.ts +4 -0
  108. package/src/types/simplify-deeper.spec.ts +40 -0
  109. package/src/types/simplify-deeper.ts +6 -0
  110. package/src/types/simplify-union.spec.ts +21 -0
  111. package/src/types/simplify-union.ts +11 -0
  112. package/src/utils/array-grouping.spec.ts +29 -0
  113. package/src/utils/array-grouping.ts +25 -0
  114. package/src/utils/array.ts +21 -0
  115. package/src/utils/assert.ts +13 -0
  116. package/src/utils/attrs-match.ts +20 -0
  117. package/src/utils/can-use-regex-lookbehind.ts +12 -0
  118. package/src/utils/clsx.spec.ts +14 -0
  119. package/src/utils/clsx.ts +12 -0
  120. package/src/utils/collect-children.ts +21 -0
  121. package/src/utils/collect-nodes.ts +37 -0
  122. package/src/utils/combine-event-handlers.spec.ts +27 -0
  123. package/src/utils/combine-event-handlers.ts +27 -0
  124. package/src/utils/contains-inline-node.ts +17 -0
  125. package/src/utils/deep-equals.spec.ts +26 -0
  126. package/src/utils/deep-equals.ts +29 -0
  127. package/src/utils/default-block-at.ts +15 -0
  128. package/src/utils/editor-content.spec.ts +47 -0
  129. package/src/utils/editor-content.ts +77 -0
  130. package/src/utils/env.ts +6 -0
  131. package/src/utils/find-parent-node-of-type.ts +29 -0
  132. package/src/utils/find-parent-node.spec.ts +68 -0
  133. package/src/utils/find-parent-node.ts +55 -0
  134. package/src/utils/get-custom-selection.ts +19 -0
  135. package/src/utils/get-dom-api.ts +56 -0
  136. package/src/utils/get-id.spec.ts +14 -0
  137. package/src/utils/get-id.ts +13 -0
  138. package/src/utils/get-mark-type.ts +20 -0
  139. package/src/utils/get-node-type.ts +20 -0
  140. package/src/utils/get-node-types.ts +19 -0
  141. package/src/utils/includes-mark.ts +18 -0
  142. package/src/utils/is-at-block-start.ts +26 -0
  143. package/src/utils/is-in-code-block.ts +18 -0
  144. package/src/utils/is-mark-absent.spec.ts +53 -0
  145. package/src/utils/is-mark-absent.ts +42 -0
  146. package/src/utils/is-mark-active.ts +27 -0
  147. package/src/utils/is-node-active.ts +25 -0
  148. package/src/utils/is-subset.spec.ts +12 -0
  149. package/src/utils/is-subset.ts +11 -0
  150. package/src/utils/maybe-run.spec.ts +39 -0
  151. package/src/utils/maybe-run.ts +11 -0
  152. package/src/utils/merge-objects.spec.ts +30 -0
  153. package/src/utils/merge-objects.ts +11 -0
  154. package/src/utils/merge-specs.ts +35 -0
  155. package/src/utils/object-equal.spec.ts +26 -0
  156. package/src/utils/object-equal.ts +28 -0
  157. package/src/utils/output-spec.test.ts +95 -0
  158. package/src/utils/output-spec.ts +130 -0
  159. package/src/utils/parse.spec.ts +46 -0
  160. package/src/utils/parse.ts +321 -0
  161. package/src/utils/remove-undefined-values.spec.ts +15 -0
  162. package/src/utils/remove-undefined-values.ts +9 -0
  163. package/src/utils/set-selection-around.ts +11 -0
  164. package/src/utils/type-assertion.ts +91 -0
  165. package/src/utils/unicode.spec.ts +10 -0
  166. package/src/utils/unicode.ts +4 -0
  167. package/src/utils/with-skip-code-block.ts +15 -0
  168. package/dist/editor-CjVyjJqw.d.ts +0 -739
@@ -0,0 +1,293 @@
1
+ import type {
2
+ Node,
3
+ Slice,
4
+ } from '@prosekit/pm/model'
5
+ import {
6
+ PluginKey,
7
+ ProseMirrorPlugin,
8
+ } from '@prosekit/pm/state'
9
+ import type { EditorView } from '@prosekit/pm/view'
10
+
11
+ import {
12
+ defineFacet,
13
+ type Facet,
14
+ } from '../../facets/facet'
15
+ import { defineFacetPayload } from '../../facets/facet-extension'
16
+ import type { PlainExtension } from '../../types/extension'
17
+ import type { ObjectEntries } from '../../types/object-entries'
18
+ import { groupEntries } from '../../utils/array-grouping'
19
+ import { combineEventHandlers } from '../../utils/combine-event-handlers'
20
+ import {
21
+ pluginFacet,
22
+ type PluginPayload,
23
+ } from '../plugin'
24
+
25
+ export type KeyDownHandler = (
26
+ view: EditorView,
27
+ event: KeyboardEvent,
28
+ ) => boolean | void
29
+ export type KeyPressHandler = (
30
+ view: EditorView,
31
+ event: KeyboardEvent,
32
+ ) => boolean | void
33
+ export type TextInputHandler = (
34
+ view: EditorView,
35
+ from: number,
36
+ to: number,
37
+ text: string,
38
+ ) => boolean | void
39
+ export type ClickOnHandler = (
40
+ view: EditorView,
41
+ pos: number,
42
+ node: Node,
43
+ nodePos: number,
44
+ event: MouseEvent,
45
+ direct: boolean,
46
+ ) => boolean | void
47
+ export type ClickHandler = (
48
+ view: EditorView,
49
+ pos: number,
50
+ event: MouseEvent,
51
+ ) => boolean | void
52
+ export type DoubleClickOnHandler = (
53
+ view: EditorView,
54
+ pos: number,
55
+ node: Node,
56
+ nodePos: number,
57
+ event: MouseEvent,
58
+ direct: boolean,
59
+ ) => boolean | void
60
+ export type DoubleClickHandler = (
61
+ view: EditorView,
62
+ pos: number,
63
+ event: MouseEvent,
64
+ ) => boolean | void
65
+ export type TripleClickOnHandler = (
66
+ view: EditorView,
67
+ pos: number,
68
+ node: Node,
69
+ nodePos: number,
70
+ event: MouseEvent,
71
+ direct: boolean,
72
+ ) => boolean | void
73
+ export type TripleClickHandler = (
74
+ view: EditorView,
75
+ pos: number,
76
+ event: MouseEvent,
77
+ ) => boolean | void
78
+ export type PasteHandler = (
79
+ view: EditorView,
80
+ event: ClipboardEvent,
81
+ slice: Slice,
82
+ ) => boolean | void
83
+ export type DropHandler = (
84
+ view: EditorView,
85
+ event: DragEvent,
86
+ slice: Slice,
87
+ moved: boolean,
88
+ ) => boolean | void
89
+ export type ScrollToSelectionHandler = (view: EditorView) => boolean
90
+
91
+ function defineEventFacetPayload(payload: EditorEventPayload): PlainExtension {
92
+ return defineFacetPayload(editorEventFacet, [payload]) as PlainExtension
93
+ }
94
+
95
+ /**
96
+ * @public
97
+ *
98
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleKeyDown}
99
+ */
100
+ export function defineKeyDownHandler(handler: KeyDownHandler): PlainExtension {
101
+ return defineEventFacetPayload(['keyDown', handler])
102
+ }
103
+ /**
104
+ * @public
105
+ *
106
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleKeyPress}
107
+ */
108
+ export function defineKeyPressHandler(
109
+ handler: KeyPressHandler,
110
+ ): PlainExtension {
111
+ return defineEventFacetPayload(['keyPress', handler])
112
+ }
113
+ /**
114
+ * @public
115
+ *
116
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleTextInput}
117
+ */
118
+ export function defineTextInputHandler(
119
+ handler: TextInputHandler,
120
+ ): PlainExtension {
121
+ return defineEventFacetPayload(['textInput', handler])
122
+ }
123
+ /**
124
+ * @public
125
+ *
126
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleClickOn}
127
+ */
128
+ export function defineClickOnHandler(handler: ClickOnHandler): PlainExtension {
129
+ return defineEventFacetPayload(['clickOn', handler])
130
+ }
131
+ /**
132
+ * @public
133
+ *
134
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleClick}
135
+ */
136
+ export function defineClickHandler(handler: ClickHandler): PlainExtension {
137
+ return defineEventFacetPayload(['click', handler])
138
+ }
139
+ /**
140
+ * @public
141
+ *
142
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleDoubleClickOn}
143
+ */
144
+ export function defineDoubleClickOnHandler(
145
+ handler: DoubleClickOnHandler,
146
+ ): PlainExtension {
147
+ return defineEventFacetPayload(['doubleClickOn', handler])
148
+ }
149
+ /**
150
+ * @public
151
+ *
152
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleDoubleClick}
153
+ */
154
+ export function defineDoubleClickHandler(
155
+ handler: DoubleClickHandler,
156
+ ): PlainExtension {
157
+ return defineEventFacetPayload(['doubleClick', handler])
158
+ }
159
+ /**
160
+ * @public
161
+ *
162
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleTripleClickOn}
163
+ */
164
+ export function defineTripleClickOnHandler(
165
+ handler: TripleClickOnHandler,
166
+ ): PlainExtension {
167
+ return defineEventFacetPayload(['tripleClickOn', handler])
168
+ }
169
+ /**
170
+ * @public
171
+ *
172
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleTripleClick}
173
+ */
174
+ export function defineTripleClickHandler(
175
+ handler: TripleClickHandler,
176
+ ): PlainExtension {
177
+ return defineEventFacetPayload(['tripleClick', handler])
178
+ }
179
+ /**
180
+ * @public
181
+ *
182
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handlePaste}
183
+ */
184
+ export function definePasteHandler(handler: PasteHandler): PlainExtension {
185
+ return defineEventFacetPayload(['paste', handler])
186
+ }
187
+ /**
188
+ * @public
189
+ *
190
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleDrop}
191
+ */
192
+ export function defineDropHandler(handler: DropHandler): PlainExtension {
193
+ return defineEventFacetPayload(['drop', handler])
194
+ }
195
+ /**
196
+ * @public
197
+ *
198
+ * See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleScrollToSelection}
199
+ */
200
+ export function defineScrollToSelectionHandler(
201
+ handler: ScrollToSelectionHandler,
202
+ ): PlainExtension {
203
+ return defineEventFacetPayload(['scrollToSelection', handler])
204
+ }
205
+
206
+ interface EditorEventMap {
207
+ keyDown: KeyDownHandler
208
+ keyPress: KeyPressHandler
209
+ textInput: TextInputHandler
210
+ clickOn: ClickOnHandler
211
+ click: ClickHandler
212
+ doubleClickOn: DoubleClickOnHandler
213
+ doubleClick: DoubleClickHandler
214
+ tripleClickOn: TripleClickOnHandler
215
+ tripleClick: TripleClickHandler
216
+ paste: PasteHandler
217
+ drop: DropHandler
218
+ scrollToSelection: ScrollToSelectionHandler
219
+ }
220
+
221
+ /**
222
+ * @internal
223
+ */
224
+ export type EditorEventPayload = ObjectEntries<EditorEventMap>
225
+
226
+ /**
227
+ * @internal
228
+ */
229
+ export const editorEventFacet: Facet<EditorEventPayload, PluginPayload> = defineFacet<EditorEventPayload, PluginPayload>({
230
+ reduce: () => {
231
+ const [update, plugin] = setupEditorEventPlugin()
232
+
233
+ return (entries) => {
234
+ update(entries)
235
+ return plugin
236
+ }
237
+ },
238
+ parent: pluginFacet,
239
+ singleton: true,
240
+ })
241
+
242
+ // dprint-ignore
243
+ function setupEditorEventPlugin() {
244
+ const [setKeyDownHandlers, handleKeyDown] = combineEventHandlers<KeyDownHandler>()
245
+ const [setKeyPressHandlers, handleKeyPress] = combineEventHandlers<KeyPressHandler>()
246
+ const [setTextInputHandlers, handleTextInput] = combineEventHandlers<TextInputHandler>()
247
+ const [setClickOnHandlers, handleClickOn] = combineEventHandlers<ClickOnHandler>()
248
+ const [setClickHandlers, handleClick] = combineEventHandlers<ClickHandler>()
249
+ const [setDoubleClickOnHandlers, handleDoubleClickOn] = combineEventHandlers<DoubleClickOnHandler>()
250
+ const [setDoubleClickHandlers, handleDoubleClick] = combineEventHandlers<DoubleClickHandler>()
251
+ const [setTripleClickOnHandlers, handleTripleClickOn] = combineEventHandlers<TripleClickOnHandler>()
252
+ const [setTripleClickHandlers, handleTripleClick] = combineEventHandlers<TripleClickHandler>()
253
+ const [setPasteHandlers, handlePaste] = combineEventHandlers<PasteHandler>()
254
+ const [setDropHandlers, handleDrop] = combineEventHandlers<DropHandler>()
255
+ const [setScrollToSelectionHandlers, handleScrollToSelection] = combineEventHandlers<ScrollToSelectionHandler>()
256
+
257
+ const update = (entries: EditorEventPayload[]) => {
258
+ const map = groupEntries<EditorEventMap>(entries)
259
+
260
+ setKeyDownHandlers(map.keyDown ?? [])
261
+ setKeyPressHandlers(map.keyPress ?? [])
262
+ setTextInputHandlers(map.textInput ?? [])
263
+ setClickOnHandlers(map.clickOn ?? [])
264
+ setClickHandlers(map.click ?? [])
265
+ setDoubleClickOnHandlers(map.doubleClickOn ?? [])
266
+ setDoubleClickHandlers(map.doubleClick ?? [])
267
+ setTripleClickOnHandlers(map.tripleClickOn ?? [])
268
+ setTripleClickHandlers(map.tripleClick ?? [])
269
+ setPasteHandlers(map.paste ?? [])
270
+ setDropHandlers(map.drop ?? [])
271
+ setScrollToSelectionHandlers(map.scrollToSelection ?? [])
272
+ }
273
+
274
+ const plugin = new ProseMirrorPlugin({
275
+ key: new PluginKey('prosekit-editor-event'),
276
+ props: {
277
+ handleKeyDown,
278
+ handleKeyPress,
279
+ handleTextInput,
280
+ handleClickOn,
281
+ handleClick,
282
+ handleDoubleClickOn,
283
+ handleDoubleClick,
284
+ handleTripleClickOn,
285
+ handleTripleClick,
286
+ handlePaste,
287
+ handleDrop,
288
+ handleScrollToSelection,
289
+ },
290
+ })
291
+
292
+ return [update, plugin] as const
293
+ }
@@ -0,0 +1,50 @@
1
+ import {
2
+ describe,
3
+ expect,
4
+ it,
5
+ vi,
6
+ } from 'vitest'
7
+
8
+ import { createEditor } from '../../editor/editor'
9
+ import { union } from '../../editor/union'
10
+ import { defineTestExtension } from '../../testing'
11
+
12
+ import { defineFocusChangeHandler } from './focus'
13
+
14
+ describe('defineFocusChangeHandler', () => {
15
+ it('should call the handler when the editor is focused or blurred', () => {
16
+ const div = document.body.appendChild(document.createElement('div'))
17
+ const handleFocusChange = vi.fn()
18
+
19
+ const extension = union(
20
+ defineTestExtension(),
21
+ defineFocusChangeHandler(handleFocusChange),
22
+ )
23
+ const editor = createEditor({ extension })
24
+ editor.mount(div)
25
+
26
+ expect(handleFocusChange).toHaveBeenCalledTimes(0)
27
+
28
+ editor.focus()
29
+ expect(handleFocusChange).toHaveBeenCalledTimes(1)
30
+ expect(handleFocusChange).toHaveBeenLastCalledWith(true)
31
+
32
+ editor.blur()
33
+ expect(handleFocusChange).toHaveBeenCalledTimes(2)
34
+ expect(handleFocusChange).toHaveBeenLastCalledWith(false)
35
+
36
+ editor.focus()
37
+ editor.focus()
38
+ editor.focus()
39
+ editor.focus()
40
+ expect(handleFocusChange).toHaveBeenCalledTimes(3)
41
+ expect(handleFocusChange).toHaveBeenLastCalledWith(true)
42
+
43
+ editor.blur()
44
+ editor.blur()
45
+ editor.blur()
46
+ editor.blur()
47
+ expect(handleFocusChange).toHaveBeenCalledTimes(4)
48
+ expect(handleFocusChange).toHaveBeenLastCalledWith(false)
49
+ })
50
+ })
@@ -0,0 +1,28 @@
1
+ import type { PlainExtension } from '../../types/extension'
2
+
3
+ import { defineDomEventFacetPayload } from './dom-event'
4
+
5
+ /**
6
+ * A function that is called when the editor gains or loses focus.
7
+ *
8
+ * @param hasFocus - Whether the editor has focus.
9
+ *
10
+ * @public
11
+ */
12
+ export type FocusChangeHandler = (hasFocus: boolean) => void
13
+
14
+ /**
15
+ * Registers a event handler that is called when the editor gains or loses focus.
16
+ *
17
+ * @public
18
+ */
19
+ export function defineFocusChangeHandler(
20
+ handler: FocusChangeHandler,
21
+ ): PlainExtension {
22
+ const handleFocus = () => handler(true)
23
+ const handleBlur = () => handler(false)
24
+ return defineDomEventFacetPayload(
25
+ ['focus', handleFocus],
26
+ ['blur', handleBlur],
27
+ )
28
+ }
@@ -0,0 +1,132 @@
1
+ import {
2
+ PluginKey,
3
+ ProseMirrorPlugin,
4
+ type EditorState,
5
+ } from '@prosekit/pm/state'
6
+ import type { EditorView } from '@prosekit/pm/view'
7
+
8
+ import { defineFacet } from '../../facets/facet'
9
+ import { defineFacetPayload } from '../../facets/facet-extension'
10
+ import type { PlainExtension } from '../../types/extension'
11
+ import {
12
+ pluginFacet,
13
+ type PluginPayload,
14
+ } from '../plugin'
15
+
16
+ /**
17
+ * A function that is called when the editor view is mounted.
18
+ *
19
+ * @param view - The editor view.
20
+ *
21
+ * @public
22
+ */
23
+ export type MountHandler = (view: EditorView) => void
24
+
25
+ /**
26
+ * A function that is called when the editor state is updated.
27
+ *
28
+ * @param view - The editor view.
29
+ * @param prevState - The previous editor state.
30
+ *
31
+ * @public
32
+ */
33
+ export type UpdateHandler = (view: EditorView, prevState: EditorState) => void
34
+
35
+ /**
36
+ * A function that is called when the editor view is unmounted.
37
+ *
38
+ * @public
39
+ */
40
+ export type UnmountHandler = () => void
41
+
42
+ /**
43
+ * Registers a event handler that is called when the editor view is mounted.
44
+ *
45
+ * @public
46
+ */
47
+ export function defineMountHandler(handler: MountHandler): PlainExtension {
48
+ return definePluginViewFacetPayload(['mount', handler])
49
+ }
50
+
51
+ /**
52
+ * Registers a event handler that is called when the editor state is updated.
53
+ *
54
+ * @public
55
+ */
56
+ export function defineUpdateHandler(handler: UpdateHandler): PlainExtension {
57
+ return definePluginViewFacetPayload(['update', handler])
58
+ }
59
+
60
+ /**
61
+ * Registers a event handler that is called when the editor view is unmounted.
62
+ *
63
+ * @public
64
+ */
65
+ export function defineUnmountHandler(handler: UnmountHandler): PlainExtension {
66
+ return definePluginViewFacetPayload(['unmount', handler])
67
+ }
68
+
69
+ function definePluginViewFacetPayload(
70
+ input: PluginViewHandlerArgs,
71
+ ): PlainExtension {
72
+ return defineFacetPayload(pluginViewFacet, [input]) as PlainExtension
73
+ }
74
+
75
+ type PluginViewHandlerArgs =
76
+ | ['mount', MountHandler]
77
+ | ['update', UpdateHandler]
78
+ | ['unmount', UnmountHandler]
79
+
80
+ const pluginViewFacet = defineFacet<PluginViewHandlerArgs, PluginPayload>({
81
+ reduce: () => {
82
+ let mountHandlers: MountHandler[] = []
83
+ let updateHandlers: UpdateHandler[] = []
84
+ let unmountHandlers: UnmountHandler[] = []
85
+
86
+ const plugin = new ProseMirrorPlugin({
87
+ key: pluginKey,
88
+ view: (view) => {
89
+ // Run all handlers after the view is mounted
90
+ mountHandlers.forEach((fn) => fn(view))
91
+
92
+ return {
93
+ update: (view, prevState) => {
94
+ updateHandlers.forEach((fn) => fn(view, prevState))
95
+ },
96
+ destroy: () => {
97
+ unmountHandlers.forEach((fn) => fn())
98
+ },
99
+ }
100
+ },
101
+ })
102
+
103
+ const register = (input: PluginViewHandlerArgs[]) => {
104
+ mountHandlers = []
105
+ updateHandlers = []
106
+ unmountHandlers = []
107
+
108
+ for (const args of input) {
109
+ switch (args[0]) {
110
+ case 'mount':
111
+ mountHandlers.push(args[1])
112
+ break
113
+ case 'update':
114
+ updateHandlers.push(args[1])
115
+ break
116
+ case 'unmount':
117
+ unmountHandlers.push(args[1])
118
+ break
119
+ }
120
+ }
121
+ }
122
+
123
+ return function reducer(input: PluginViewHandlerArgs[]) {
124
+ register(input)
125
+ return plugin
126
+ }
127
+ },
128
+ parent: pluginFacet,
129
+ singleton: true,
130
+ })
131
+
132
+ const pluginKey = new PluginKey('prosekit-plugin-view-handler')
@@ -0,0 +1,81 @@
1
+ import {
2
+ history,
3
+ redo,
4
+ undo,
5
+ } from '@prosekit/pm/history'
6
+
7
+ import { union } from '../editor/union'
8
+ import type { Extension } from '../types/extension'
9
+ import { isApple } from '../utils/env'
10
+
11
+ import { defineCommands } from './command'
12
+ import {
13
+ defineKeymap,
14
+ type Keymap,
15
+ } from './keymap'
16
+ import { definePlugin } from './plugin'
17
+
18
+ const keymap: Keymap = {
19
+ 'Mod-z': undo,
20
+ 'Shift-Mod-z': redo,
21
+ }
22
+
23
+ if (!isApple) {
24
+ keymap['Mod-y'] = redo
25
+ }
26
+
27
+ const commands = {
28
+ undo: () => undo,
29
+ redo: () => redo,
30
+ } as const
31
+
32
+ /**
33
+ * Options for {@link defineHistory}.
34
+ *
35
+ * @public
36
+ */
37
+ export interface HistoryOptions {
38
+ /**
39
+ * The amount of history events that are collected before the oldest events
40
+ * are discarded.
41
+ *
42
+ * @default 200
43
+ */
44
+ depth?: number
45
+
46
+ /**
47
+ * The delay in milliseconds between changes after which a new group should be
48
+ * started.
49
+ *
50
+ * @default 250
51
+ */
52
+ newGroupDelay?: number
53
+ }
54
+
55
+ /**
56
+ * @internal
57
+ */
58
+ export type HistoryExtension = Extension<{
59
+ Commands: {
60
+ undo: []
61
+ redo: []
62
+ }
63
+ }>
64
+
65
+ /**
66
+ * Add undo/redo history to the editor.
67
+ *
68
+ * @param options
69
+ *
70
+ * @public
71
+ */
72
+ export function defineHistory({
73
+ depth = 200,
74
+ newGroupDelay = 250,
75
+ }: HistoryOptions = {}): HistoryExtension {
76
+ return union(
77
+ definePlugin(history({ depth, newGroupDelay })),
78
+ defineKeymap(keymap),
79
+ defineCommands(commands),
80
+ )
81
+ }
@@ -0,0 +1,60 @@
1
+ import {
2
+ baseKeymap,
3
+ chainCommands,
4
+ createParagraphNear,
5
+ deleteSelection,
6
+ joinTextblockBackward,
7
+ liftEmptyBlock,
8
+ newlineInCode,
9
+ selectNodeBackward,
10
+ } from '@prosekit/pm/commands'
11
+ import { splitSplittableBlock } from 'prosemirror-splittable'
12
+
13
+ import { withPriority } from '../editor/with-priority'
14
+ import type { PlainExtension } from '../types/extension'
15
+ import { Priority } from '../types/priority'
16
+
17
+ import { defineKeymap } from './keymap'
18
+
19
+ // Replace `splitBlock` with `splitSplittableBlock`
20
+ const customEnter = chainCommands(
21
+ newlineInCode,
22
+ createParagraphNear,
23
+ liftEmptyBlock,
24
+ splitSplittableBlock,
25
+ )
26
+
27
+ // Replace `joinBackward` with `joinTextblockBackward`
28
+ const customBackspace = chainCommands(
29
+ deleteSelection,
30
+ joinTextblockBackward,
31
+ selectNodeBackward,
32
+ )
33
+
34
+ const customBaseKeymap = {
35
+ ...baseKeymap,
36
+ Enter: customEnter,
37
+ Backspace: customBackspace,
38
+ }
39
+
40
+ /**
41
+ * @internal
42
+ */
43
+ export type BaseKeymapExtension = PlainExtension
44
+
45
+ /**
46
+ * Defines some basic key bindings.
47
+ *
48
+ * @public
49
+ */
50
+ export function defineBaseKeymap(options?: {
51
+ /**
52
+ * The priority of the keymap.
53
+ *
54
+ * @default Priority.low
55
+ */
56
+ priority?: Priority
57
+ }): BaseKeymapExtension {
58
+ const priority = options?.priority ?? Priority.low
59
+ return withPriority(defineKeymap(customBaseKeymap), priority)
60
+ }