@handlewithcare/react-prosemirror 3.0.5 → 3.0.6

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.
package/README.md CHANGED
@@ -837,10 +837,40 @@ significant contributors help shape our roadmap priorities.
837
837
  [Become a Sponsor](https://handlewithcare.dev/pitter-patter/#become-a-sponsor)
838
838
 
839
839
  <h3>Sponsors</h3>
840
- <p>
841
- <a href="https://www.moment.dev/"><img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Moment.png" alt="Moment" height="128"></a>
842
- <a href="https://www.lingco.io/"><img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Lingco.png" alt="Lingco" height="128"></a>
843
- <a href="https://dskrpt.de/"><img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Dskrpt.png" alt="Dskrpt" height="128"></a>
844
- <a href="https://github.com/fastrepl/"><img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Fastrepl.png" alt="Fastrepl" height="128"></a>
845
- </p>
840
+
841
+ <table>
842
+ <tbody>
843
+ <tr>
844
+ <td align="center" valign="top" >
845
+ <a href="https://moment.dev/">
846
+ <img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Moment.png" alt="Moment" height="128">
847
+ <br>
848
+ Moment
849
+ </a>
850
+ </td>
851
+ <td align="center" valign="top" >
852
+ <a href="https://www.lingco.io/">
853
+ <img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Lingco.png" alt="Lingco" height="128">
854
+ <br>
855
+ Lingco
856
+ </a>
857
+ </td>
858
+ <td align="center" valign="top" >
859
+ <a href="https://dskrpt.de/">
860
+ <img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Dskrpt.png" alt="dskrpt" height="128">
861
+ <br>
862
+ dskrpt
863
+ </a>
864
+ </td>
865
+ <td align="center" valign="top" >
866
+ <a href="https://char.com/">
867
+ <img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Fastrepl.png" alt="Fastrepl" height="128">
868
+ <br>
869
+ Fastrepl
870
+ </a>
871
+ </td>
872
+ </tr>
873
+ </tbody>
874
+ </table>
875
+
846
876
  <!--sponsorsend-->
@@ -78,6 +78,7 @@ function WidgetView(param) {
78
78
  viewDescRef.current.parent = parentRef.current;
79
79
  viewDescRef.current.widget = widget;
80
80
  viewDescRef.current.dom = domRef.current;
81
+ viewDescRef.current.dom.pmViewDesc = viewDescRef.current;
81
82
  }
82
83
  if (!siblingsRef.current.includes(viewDescRef.current)) {
83
84
  siblingsRef.current.push(viewDescRef.current);
@@ -9,8 +9,8 @@ Object.defineProperty(exports, "useComponentEventListeners", {
9
9
  }
10
10
  });
11
11
  const _react = require("react");
12
- const _componentEventListeners = require("../plugins/componentEventListeners.js");
13
- function useComponentEventListeners() {
12
+ const _reactdom = require("react-dom");
13
+ function useComponentEventListeners(handleDOMEventsProp) {
14
14
  const [registry, setRegistry] = (0, _react.useState)(new Map());
15
15
  const registerEventListener = (0, _react.useCallback)((eventType, handler)=>{
16
16
  const handlers = registry.get(eventType) ?? [];
@@ -28,12 +28,45 @@ function useComponentEventListeners() {
28
28
  }, [
29
29
  registry
30
30
  ]);
31
- const componentEventListenersPlugin = (0, _react.useMemo)(()=>(0, _componentEventListeners.componentEventListeners)(registry), [
31
+ (0, _react.useLayoutEffect)(()=>{
32
+ if (!handleDOMEventsProp) return;
33
+ for (const [eventType, handler] of Object.entries(handleDOMEventsProp)){
34
+ if (!handler) return;
35
+ registerEventListener(eventType, handler);
36
+ }
37
+ return ()=>{
38
+ for (const [eventType, handler] of Object.entries(handleDOMEventsProp)){
39
+ if (!handler) return;
40
+ unregisterEventListener(eventType, handler);
41
+ }
42
+ };
43
+ }, [
44
+ handleDOMEventsProp,
45
+ registerEventListener,
46
+ unregisterEventListener
47
+ ]);
48
+ const handleDOMEvents = (0, _react.useMemo)(()=>{
49
+ const domEventHandlers = {};
50
+ for (const [eventType, handlers] of registry.entries()){
51
+ function handleEvent(view, event) {
52
+ for (const handler of handlers){
53
+ let handled = false;
54
+ (0, _reactdom.unstable_batchedUpdates)(()=>{
55
+ handled = !!handler(view, event);
56
+ });
57
+ if (handled || event.defaultPrevented) return true;
58
+ }
59
+ return false;
60
+ }
61
+ domEventHandlers[eventType] = handleEvent;
62
+ }
63
+ return domEventHandlers;
64
+ }, [
32
65
  registry
33
66
  ]);
34
67
  return {
35
68
  registerEventListener,
36
69
  unregisterEventListener,
37
- componentEventListenersPlugin
70
+ handleDOMEvents
38
71
  };
39
72
  }
@@ -32,7 +32,7 @@ function useEditor(mount, options) {
32
32
  const defaultState = options.defaultState ?? _constants.EMPTY_STATE;
33
33
  const [_state, setState] = (0, _react.useState)(defaultState);
34
34
  const state = options.state ?? _state;
35
- const { componentEventListenersPlugin, registerEventListener, unregisterEventListener } = (0, _useComponentEventListeners.useComponentEventListeners)();
35
+ const { handleDOMEvents, registerEventListener, unregisterEventListener } = (0, _useComponentEventListeners.useComponentEventListeners)(options.handleDOMEvents);
36
36
  const setCursorWrapper = (0, _react.useCallback)((deco)=>{
37
37
  (0, _reactdom.flushSync)(()=>{
38
38
  _setCursorWrapper(deco);
@@ -40,11 +40,9 @@ function useEditor(mount, options) {
40
40
  }, []);
41
41
  const plugins = (0, _react.useMemo)(()=>[
42
42
  ...options.plugins ?? [],
43
- componentEventListenersPlugin,
44
43
  (0, _beforeInputPlugin.beforeInputPlugin)(setCursorWrapper)
45
44
  ], [
46
45
  options.plugins,
47
- componentEventListenersPlugin,
48
46
  setCursorWrapper
49
47
  ]);
50
48
  const dispatchTransaction = (0, _react.useCallback)(function dispatchTransaction(tr) {
@@ -73,7 +71,8 @@ function useEditor(mount, options) {
73
71
  ...options,
74
72
  state,
75
73
  plugins,
76
- dispatchTransaction
74
+ dispatchTransaction,
75
+ handleDOMEvents
77
76
  };
78
77
  const [view, setView] = (0, _react.useState)(()=>{
79
78
  return new _StaticEditorView.StaticEditorView(directEditorProps);
@@ -27,6 +27,7 @@ export function WidgetView(param) {
27
27
  viewDescRef.current.parent = parentRef.current;
28
28
  viewDescRef.current.widget = widget;
29
29
  viewDescRef.current.dom = domRef.current;
30
+ viewDescRef.current.dom.pmViewDesc = viewDescRef.current;
30
31
  }
31
32
  if (!siblingsRef.current.includes(viewDescRef.current)) {
32
33
  siblingsRef.current.push(viewDescRef.current);
@@ -1,5 +1,5 @@
1
- /* Copyright (c) The New York Times Company */ import { useCallback, useMemo, useState } from "react";
2
- import { componentEventListeners } from "../plugins/componentEventListeners.js";
1
+ /* Copyright (c) The New York Times Company */ import { useCallback, useLayoutEffect, useMemo, useState } from "react";
2
+ import { unstable_batchedUpdates as batch } from "react-dom";
3
3
  /**
4
4
  * Produces a plugin that can be used with ProseMirror to handle DOM
5
5
  * events at the EditorView.dom element.
@@ -14,18 +14,18 @@ import { componentEventListeners } from "../plugins/componentEventListeners.js";
14
14
  * @privateRemarks
15
15
  *
16
16
  * This hook uses a combination of mutable and immutable updates to give
17
- * us precise control over when we re-create the ProseMirror plugin.
17
+ * us precise control over when we re-create the event listeners.
18
18
  *
19
- * The plugin has a mutable reference to the set of handlers for each
19
+ * The hook has a mutable reference to the set of handlers for each
20
20
  * event type, but the set of event types is static. This means that we
21
- * need to produce a new ProseMirror plugin whenever a new event type is
22
- * registered. We avoid producing a new ProseMirrer plugin in any other
23
- * scenario to avoid the performance overhead of reconfiguring the plugins
24
- * in the EditorView.
21
+ * need to produce a new handleDOMEVents record whenever a new event type is
22
+ * registered. We avoid producing a new record in any other
23
+ * scenario to avoid the performance overhead of re-registering the event
24
+ * listeners in the EditorView.
25
25
  *
26
26
  * To accomplish this, we shallowly clone the registry whenever a new event
27
27
  * type is registered.
28
- */ export function useComponentEventListeners() {
28
+ */ export function useComponentEventListeners(handleDOMEventsProp) {
29
29
  const [registry, setRegistry] = useState(new Map());
30
30
  const registerEventListener = useCallback((eventType, handler)=>{
31
31
  const handlers = registry.get(eventType) ?? [];
@@ -43,12 +43,45 @@ import { componentEventListeners } from "../plugins/componentEventListeners.js";
43
43
  }, [
44
44
  registry
45
45
  ]);
46
- const componentEventListenersPlugin = useMemo(()=>componentEventListeners(registry), [
46
+ useLayoutEffect(()=>{
47
+ if (!handleDOMEventsProp) return;
48
+ for (const [eventType, handler] of Object.entries(handleDOMEventsProp)){
49
+ if (!handler) return;
50
+ registerEventListener(eventType, handler);
51
+ }
52
+ return ()=>{
53
+ for (const [eventType, handler] of Object.entries(handleDOMEventsProp)){
54
+ if (!handler) return;
55
+ unregisterEventListener(eventType, handler);
56
+ }
57
+ };
58
+ }, [
59
+ handleDOMEventsProp,
60
+ registerEventListener,
61
+ unregisterEventListener
62
+ ]);
63
+ const handleDOMEvents = useMemo(()=>{
64
+ const domEventHandlers = {};
65
+ for (const [eventType, handlers] of registry.entries()){
66
+ function handleEvent(view, event) {
67
+ for (const handler of handlers){
68
+ let handled = false;
69
+ batch(()=>{
70
+ handled = !!handler(view, event);
71
+ });
72
+ if (handled || event.defaultPrevented) return true;
73
+ }
74
+ return false;
75
+ }
76
+ domEventHandlers[eventType] = handleEvent;
77
+ }
78
+ return domEventHandlers;
79
+ }, [
47
80
  registry
48
81
  ]);
49
82
  return {
50
83
  registerEventListener,
51
84
  unregisterEventListener,
52
- componentEventListenersPlugin
85
+ handleDOMEvents
53
86
  };
54
87
  }
@@ -30,7 +30,7 @@ let didWarnValueDefaultValue = false;
30
30
  const defaultState = options.defaultState ?? EMPTY_STATE;
31
31
  const [_state, setState] = useState(defaultState);
32
32
  const state = options.state ?? _state;
33
- const { componentEventListenersPlugin, registerEventListener, unregisterEventListener } = useComponentEventListeners();
33
+ const { handleDOMEvents, registerEventListener, unregisterEventListener } = useComponentEventListeners(options.handleDOMEvents);
34
34
  const setCursorWrapper = useCallback((deco)=>{
35
35
  flushSync(()=>{
36
36
  _setCursorWrapper(deco);
@@ -38,11 +38,9 @@ let didWarnValueDefaultValue = false;
38
38
  }, []);
39
39
  const plugins = useMemo(()=>[
40
40
  ...options.plugins ?? [],
41
- componentEventListenersPlugin,
42
41
  beforeInputPlugin(setCursorWrapper)
43
42
  ], [
44
43
  options.plugins,
45
- componentEventListenersPlugin,
46
44
  setCursorWrapper
47
45
  ]);
48
46
  const dispatchTransaction = useCallback(function dispatchTransaction(tr) {
@@ -71,7 +69,8 @@ let didWarnValueDefaultValue = false;
71
69
  ...options,
72
70
  state,
73
71
  plugins,
74
- dispatchTransaction
72
+ dispatchTransaction,
73
+ handleDOMEvents
75
74
  };
76
75
  const [view, setView] = useState(()=>{
77
76
  return new StaticEditorView(directEditorProps);