@lexical/react 0.4.1 → 0.5.1-next.0
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/DEPRECATED_useLexical.js.flow +0 -1
- package/DEPRECATED_useLexicalDecorators.d.ts +2 -1
- package/DEPRECATED_useLexicalDecorators.dev.js +125 -10
- package/DEPRECATED_useLexicalDecorators.js.flow +8 -0
- package/DEPRECATED_useLexicalDecorators.prod.js +6 -2
- package/DEPRECATED_useLexicalPlainText.d.ts +2 -2
- package/DEPRECATED_useLexicalPlainText.dev.js +4 -4
- package/DEPRECATED_useLexicalPlainText.js.flow +0 -1
- package/DEPRECATED_useLexicalPlainText.prod.js +2 -2
- package/DEPRECATED_useLexicalRichText.d.ts +2 -2
- package/DEPRECATED_useLexicalRichText.dev.js +4 -4
- package/DEPRECATED_useLexicalRichText.js.flow +0 -1
- package/DEPRECATED_useLexicalRichText.prod.js +2 -2
- package/LexicalAutoEmbedPlugin.d.ts +3 -13
- package/LexicalAutoEmbedPlugin.dev.js +3 -19
- package/LexicalAutoEmbedPlugin.js.flow +3 -11
- package/LexicalAutoEmbedPlugin.prod.js +4 -5
- package/LexicalAutoLinkPlugin.dev.js +49 -45
- package/LexicalAutoLinkPlugin.prod.js +6 -6
- package/LexicalBlockWithAlignableContents.dev.js +1 -1
- package/LexicalBlockWithAlignableContents.prod.js +1 -1
- package/LexicalCollaborationPlugin.d.ts +5 -1
- package/LexicalCollaborationPlugin.dev.js +54 -16
- package/LexicalCollaborationPlugin.js.flow +1 -0
- package/LexicalCollaborationPlugin.prod.js +9 -9
- package/LexicalComposer.d.ts +1 -2
- package/LexicalComposer.dev.js +15 -1
- package/LexicalComposer.js.flow +14 -7
- package/LexicalComposer.prod.js +3 -3
- package/LexicalContentEditable.dev.js +1 -1
- package/LexicalContentEditable.prod.js +1 -1
- package/LexicalHorizontalRuleNode.d.ts +4 -2
- package/LexicalHorizontalRuleNode.dev.js +25 -3
- package/LexicalHorizontalRuleNode.js.flow +0 -1
- package/LexicalHorizontalRuleNode.prod.js +5 -5
- package/LexicalNestedComposer.d.ts +4 -2
- package/LexicalNestedComposer.dev.js +25 -3
- package/LexicalNestedComposer.js.flow +1 -0
- package/LexicalNestedComposer.prod.js +3 -3
- package/LexicalOnChangePlugin.d.ts +1 -2
- package/LexicalOnChangePlugin.dev.js +3 -9
- package/LexicalOnChangePlugin.js.flow +0 -2
- package/LexicalOnChangePlugin.prod.js +2 -2
- package/LexicalPlainTextPlugin.d.ts +3 -3
- package/LexicalPlainTextPlugin.dev.js +111 -20
- package/LexicalPlainTextPlugin.js.flow +3 -1
- package/LexicalPlainTextPlugin.prod.js +6 -4
- package/LexicalRichTextPlugin.d.ts +3 -3
- package/LexicalRichTextPlugin.dev.js +111 -20
- package/LexicalRichTextPlugin.js.flow +3 -1
- package/LexicalRichTextPlugin.prod.js +6 -4
- package/LexicalTableOfContents__EXPERIMENTAL.js.flow +1 -1
- package/LexicalTablePlugin.dev.js +1 -1
- package/LexicalTablePlugin.prod.js +1 -1
- package/LexicalTreeView.dev.js +28 -13
- package/LexicalTreeView.prod.js +14 -13
- package/LexicalTypeaheadMenuPlugin.d.ts +20 -8
- package/LexicalTypeaheadMenuPlugin.dev.js +200 -57
- package/LexicalTypeaheadMenuPlugin.js.flow +19 -21
- package/LexicalTypeaheadMenuPlugin.prod.js +18 -14
- package/package.json +19 -19
- package/shared/ReactErrorBoundary.d.ts +63 -0
- package/shared/useDecorators.d.ts +8 -1
- package/shared/usePlainTextSetup.d.ts +1 -2
- package/shared/useRichTextSetup.d.ts +1 -2
- package/shared/useYjsCollaboration.d.ts +4 -1
|
@@ -18,7 +18,6 @@ import type {
|
|
|
18
18
|
declare export function useLexical<EditorContext>(editorConfig?: {
|
|
19
19
|
namespace?: string,
|
|
20
20
|
onError: (error: Error) => void,
|
|
21
|
-
initialEditorState?: EditorState,
|
|
22
21
|
theme?: EditorThemeClasses,
|
|
23
22
|
context?: EditorContext,
|
|
24
23
|
nodes?: Array<Class<LexicalNode>>,
|
|
@@ -6,4 +6,5 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
import type { LexicalEditor } from 'lexical';
|
|
9
|
-
|
|
9
|
+
import { ErrorBoundaryType } from './shared/useDecorators';
|
|
10
|
+
export declare function useLexicalDecorators(editor: LexicalEditor, ErrorBoundary?: ErrorBoundaryType): Array<JSX.Element>;
|
|
@@ -6,9 +6,106 @@
|
|
|
6
6
|
*/
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var React = require('react');
|
|
10
10
|
var reactDom = require('react-dom');
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
14
|
+
*
|
|
15
|
+
* This source code is licensed under the MIT license found in the
|
|
16
|
+
* LICENSE file in the root directory of this source tree.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const changedArray = (a = [], b = []) => a.length !== b.length || a.some((item, index) => !Object.is(item, b[index]));
|
|
21
|
+
|
|
22
|
+
const initialState = {
|
|
23
|
+
error: null
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
class ErrorBoundary extends React.Component {
|
|
27
|
+
constructor(props) {
|
|
28
|
+
super(props);
|
|
29
|
+
this.state = initialState;
|
|
30
|
+
this.resetErrorBoundary = this.resetErrorBoundary.bind(this);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static getDerivedStateFromError(error) {
|
|
34
|
+
return {
|
|
35
|
+
error
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
resetErrorBoundary(...args) {
|
|
40
|
+
// @ts-ignore
|
|
41
|
+
// eslint-disable-next-line no-unused-expressions
|
|
42
|
+
this.props.onReset && this.props.onReset(...args);
|
|
43
|
+
this.reset();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
reset() {
|
|
47
|
+
this.setState(initialState);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
componentDidCatch(error, info) {
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
// eslint-disable-next-line no-unused-expressions
|
|
53
|
+
this.props.onError && this.props.onError(error, info);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
componentDidUpdate(prevProps, prevState) {
|
|
57
|
+
const {
|
|
58
|
+
error
|
|
59
|
+
} = this.state;
|
|
60
|
+
const {
|
|
61
|
+
resetKeys
|
|
62
|
+
} = this.props; // There's an edge case where if the thing that triggered the error
|
|
63
|
+
// happens to *also* be in the resetKeys array, we'd end up resetting
|
|
64
|
+
// the error boundary immediately. This would likely trigger a second
|
|
65
|
+
// error to be thrown.
|
|
66
|
+
// So we make sure that we don't check the resetKeys on the first call
|
|
67
|
+
// of cDU after the error is set
|
|
68
|
+
|
|
69
|
+
if (error !== null && prevState.error !== null && changedArray(prevProps.resetKeys, resetKeys)) {
|
|
70
|
+
// @ts-ignore
|
|
71
|
+
// eslint-disable-next-line no-unused-expressions
|
|
72
|
+
this.props.onResetKeysChange && this.props.onResetKeysChange(prevProps.resetKeys, resetKeys);
|
|
73
|
+
this.reset();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
render() {
|
|
78
|
+
const {
|
|
79
|
+
error
|
|
80
|
+
} = this.state;
|
|
81
|
+
const {
|
|
82
|
+
fallbackRender,
|
|
83
|
+
FallbackComponent,
|
|
84
|
+
fallback
|
|
85
|
+
} = this.props;
|
|
86
|
+
|
|
87
|
+
if (error !== null) {
|
|
88
|
+
const props = {
|
|
89
|
+
error,
|
|
90
|
+
resetErrorBoundary: this.resetErrorBoundary
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if ( /*#__PURE__*/React.isValidElement(fallback)) {
|
|
94
|
+
return fallback;
|
|
95
|
+
} else if (typeof fallbackRender === 'function') {
|
|
96
|
+
return fallbackRender(props);
|
|
97
|
+
} else if (FallbackComponent) {
|
|
98
|
+
return /*#__PURE__*/React.createElement(FallbackComponent, props);
|
|
99
|
+
} else {
|
|
100
|
+
throw new Error('react-error-boundary requires either a fallback, fallbackRender, or FallbackComponent prop');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return this.props.children;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
12
109
|
/**
|
|
13
110
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
14
111
|
*
|
|
@@ -25,7 +122,7 @@ const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !==
|
|
|
25
122
|
* LICENSE file in the root directory of this source tree.
|
|
26
123
|
*
|
|
27
124
|
*/
|
|
28
|
-
const useLayoutEffectImpl = CAN_USE_DOM ?
|
|
125
|
+
const useLayoutEffectImpl = CAN_USE_DOM ? React.useLayoutEffect : React.useEffect;
|
|
29
126
|
var useLayoutEffect = useLayoutEffectImpl;
|
|
30
127
|
|
|
31
128
|
/**
|
|
@@ -35,8 +132,15 @@ var useLayoutEffect = useLayoutEffectImpl;
|
|
|
35
132
|
* LICENSE file in the root directory of this source tree.
|
|
36
133
|
*
|
|
37
134
|
*/
|
|
38
|
-
function useDecorators(editor
|
|
39
|
-
|
|
135
|
+
function useDecorators(editor, // TODO 0.6 Make non-optional non-default
|
|
136
|
+
ErrorBoundary$1 = ({
|
|
137
|
+
children,
|
|
138
|
+
onError
|
|
139
|
+
}) => /*#__PURE__*/React.createElement(ErrorBoundary, {
|
|
140
|
+
fallback: null,
|
|
141
|
+
onError: onError
|
|
142
|
+
}, children)) {
|
|
143
|
+
const [decorators, setDecorators] = React.useState(() => editor.getDecorators()); // Subscribe to changes
|
|
40
144
|
|
|
41
145
|
useLayoutEffect(() => {
|
|
42
146
|
return editor.registerDecoratorListener(nextDecorators => {
|
|
@@ -45,20 +149,24 @@ function useDecorators(editor) {
|
|
|
45
149
|
});
|
|
46
150
|
});
|
|
47
151
|
}, [editor]);
|
|
48
|
-
|
|
152
|
+
React.useEffect(() => {
|
|
49
153
|
// If the content editable mounts before the subscription is added, then
|
|
50
154
|
// nothing will be rendered on initial pass. We can get around that by
|
|
51
155
|
// ensuring that we set the value.
|
|
52
156
|
setDecorators(editor.getDecorators());
|
|
53
157
|
}, [editor]); // Return decorators defined as React Portals
|
|
54
158
|
|
|
55
|
-
return
|
|
159
|
+
return React.useMemo(() => {
|
|
56
160
|
const decoratedPortals = [];
|
|
57
161
|
const decoratorKeys = Object.keys(decorators);
|
|
58
162
|
|
|
59
163
|
for (let i = 0; i < decoratorKeys.length; i++) {
|
|
60
164
|
const nodeKey = decoratorKeys[i];
|
|
61
|
-
const reactDecorator =
|
|
165
|
+
const reactDecorator = /*#__PURE__*/React.createElement(ErrorBoundary$1, {
|
|
166
|
+
onError: e => editor._onError(e)
|
|
167
|
+
}, /*#__PURE__*/React.createElement(React.Suspense, {
|
|
168
|
+
fallback: null
|
|
169
|
+
}, decorators[nodeKey]));
|
|
62
170
|
const element = editor.getElementByKey(nodeKey);
|
|
63
171
|
|
|
64
172
|
if (element !== null) {
|
|
@@ -67,7 +175,7 @@ function useDecorators(editor) {
|
|
|
67
175
|
}
|
|
68
176
|
|
|
69
177
|
return decoratedPortals;
|
|
70
|
-
}, [decorators, editor]);
|
|
178
|
+
}, [ErrorBoundary$1, decorators, editor]);
|
|
71
179
|
}
|
|
72
180
|
|
|
73
181
|
/**
|
|
@@ -77,8 +185,15 @@ function useDecorators(editor) {
|
|
|
77
185
|
* LICENSE file in the root directory of this source tree.
|
|
78
186
|
*
|
|
79
187
|
*/
|
|
80
|
-
function useLexicalDecorators(editor
|
|
81
|
-
|
|
188
|
+
function useLexicalDecorators(editor, // TODO 0.6 Make non-optional non-default
|
|
189
|
+
ErrorBoundary$1 = ({
|
|
190
|
+
children,
|
|
191
|
+
onError
|
|
192
|
+
}) => /*#__PURE__*/React.createElement(ErrorBoundary, {
|
|
193
|
+
fallback: null,
|
|
194
|
+
onError: onError
|
|
195
|
+
}, children)) {
|
|
196
|
+
return useDecorators(editor, ErrorBoundary$1);
|
|
82
197
|
}
|
|
83
198
|
|
|
84
199
|
exports.useLexicalDecorators = useLexicalDecorators;
|
|
@@ -12,6 +12,14 @@ import type {LexicalEditor} from 'lexical';
|
|
|
12
12
|
|
|
13
13
|
import * as React from 'react';
|
|
14
14
|
|
|
15
|
+
type ErrorBoundaryProps = $ReadOnly<{
|
|
16
|
+
children: React.Node,
|
|
17
|
+
onError?: (error: Error) => void,
|
|
18
|
+
}>;
|
|
19
|
+
export type ErrorBoundaryType = React.AbstractComponent<ErrorBoundaryProps>;
|
|
20
|
+
|
|
15
21
|
declare export function useLexicalDecorators(
|
|
16
22
|
editor: LexicalEditor,
|
|
23
|
+
// TODO 0.6 Make non-optional non-default
|
|
24
|
+
ErrorBoundary?: ErrorBoundaryType,
|
|
17
25
|
): Array<React.Node>;
|
|
@@ -4,5 +4,9 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
'use strict';var
|
|
8
|
-
|
|
7
|
+
'use strict';var e=require("react"),k=require("react-dom");let l=(a=[],b=[])=>a.length!==b.length||a.some((c,d)=>!Object.is(c,b[d])),m={error:null};
|
|
8
|
+
class p extends e.Component{constructor(a){super(a);this.state=m;this.resetErrorBoundary=this.resetErrorBoundary.bind(this)}static getDerivedStateFromError(a){return{error:a}}resetErrorBoundary(...a){this.props.onReset&&this.props.onReset(...a);this.reset()}reset(){this.setState(m)}componentDidCatch(a,b){this.props.onError&&this.props.onError(a,b)}componentDidUpdate(a,b){let {error:c}=this.state,{resetKeys:d}=this.props;null!==c&&null!==b.error&&l(a.resetKeys,d)&&(this.props.onResetKeysChange&&this.props.onResetKeysChange(a.resetKeys,
|
|
9
|
+
d),this.reset())}render(){var {error:a}=this.state;let {fallbackRender:b,FallbackComponent:c,fallback:d}=this.props;if(null!==a){a={error:a,resetErrorBoundary:this.resetErrorBoundary};if(e.isValidElement(d))return d;if("function"===typeof b)return b(a);if(c)return e.createElement(c,a);throw Error("react-error-boundary requires either a fallback, fallbackRender, or FallbackComponent prop");}return this.props.children}}
|
|
10
|
+
var q="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?e.useLayoutEffect:e.useEffect;
|
|
11
|
+
function r(a,b=({children:c,onError:d})=>e.createElement(p,{fallback:null,onError:d},c)){let [c,d]=e.useState(()=>a.getDecorators());q(()=>a.registerDecoratorListener(g=>{k.flushSync(()=>{d(g)})}),[a]);e.useEffect(()=>{d(a.getDecorators())},[a]);return e.useMemo(()=>{let g=[],n=Object.keys(c);for(let h=0;h<n.length;h++){var f=n[h];let u=e.createElement(b,{onError:t=>a._onError(t)},e.createElement(e.Suspense,{fallback:null},c[f]));f=a.getElementByKey(f);null!==f&&g.push(k.createPortal(u,f))}return g},
|
|
12
|
+
[b,c,a])}exports.useLexicalDecorators=function(a,b=({children:c,onError:d})=>e.createElement(p,{fallback:null,onError:d},c)){return r(a,b)}
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
import type { HistoryState } from './DEPRECATED_useLexicalHistory';
|
|
9
|
-
import type {
|
|
10
|
-
export declare function useLexicalPlainText(editor: LexicalEditor, externalHistoryState?: HistoryState
|
|
9
|
+
import type { LexicalEditor } from 'lexical';
|
|
10
|
+
export declare function useLexicalPlainText(editor: LexicalEditor, externalHistoryState?: HistoryState): void;
|
|
@@ -63,9 +63,9 @@ var useLayoutEffect = useLayoutEffectImpl;
|
|
|
63
63
|
* LICENSE file in the root directory of this source tree.
|
|
64
64
|
*
|
|
65
65
|
*/
|
|
66
|
-
function usePlainTextSetup(editor
|
|
66
|
+
function usePlainTextSetup(editor) {
|
|
67
67
|
useLayoutEffect(() => {
|
|
68
|
-
return utils.mergeRegister(plainText.registerPlainText(editor
|
|
68
|
+
return utils.mergeRegister(plainText.registerPlainText(editor), dragon.registerDragonSupport(editor)); // We only do this for init
|
|
69
69
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70
70
|
}, [editor]);
|
|
71
71
|
}
|
|
@@ -77,8 +77,8 @@ function usePlainTextSetup(editor, initialEditorState) {
|
|
|
77
77
|
* LICENSE file in the root directory of this source tree.
|
|
78
78
|
*
|
|
79
79
|
*/
|
|
80
|
-
function useLexicalPlainText(editor, externalHistoryState
|
|
81
|
-
usePlainTextSetup(editor
|
|
80
|
+
function useLexicalPlainText(editor, externalHistoryState) {
|
|
81
|
+
usePlainTextSetup(editor);
|
|
82
82
|
useLexicalHistory(editor, externalHistoryState);
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
'use strict';var history=require("@lexical/history"),
|
|
8
|
-
function n(a
|
|
7
|
+
'use strict';var history=require("@lexical/history"),c=require("react"),f=require("@lexical/dragon"),g=require("@lexical/plain-text"),h=require("@lexical/utils");function k(a,b,d=1E3){let e=c.useMemo(()=>b||history.createEmptyHistoryState(),[b]);c.useEffect(()=>history.registerHistory(a,e,d),[d,a,e])}function l(a,b,d=1E3){return k(a,b,d)}var m="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?c.useLayoutEffect:c.useEffect;
|
|
8
|
+
function n(a){m(()=>h.mergeRegister(g.registerPlainText(a),f.registerDragonSupport(a)),[a])}exports.useLexicalPlainText=function(a,b){n(a);l(a,b)}
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
import type { HistoryState } from './DEPRECATED_useLexicalHistory';
|
|
9
|
-
import type {
|
|
10
|
-
export declare function useLexicalRichText(editor: LexicalEditor, externalHistoryState?: HistoryState
|
|
9
|
+
import type { LexicalEditor } from 'lexical';
|
|
10
|
+
export declare function useLexicalRichText(editor: LexicalEditor, externalHistoryState?: HistoryState): void;
|
|
@@ -63,9 +63,9 @@ var useLayoutEffect = useLayoutEffectImpl;
|
|
|
63
63
|
* LICENSE file in the root directory of this source tree.
|
|
64
64
|
*
|
|
65
65
|
*/
|
|
66
|
-
function useRichTextSetup(editor
|
|
66
|
+
function useRichTextSetup(editor) {
|
|
67
67
|
useLayoutEffect(() => {
|
|
68
|
-
return utils.mergeRegister(richText.registerRichText(editor
|
|
68
|
+
return utils.mergeRegister(richText.registerRichText(editor), dragon.registerDragonSupport(editor)); // We only do this for init
|
|
69
69
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70
70
|
}, [editor]);
|
|
71
71
|
}
|
|
@@ -77,8 +77,8 @@ function useRichTextSetup(editor, initialEditorState) {
|
|
|
77
77
|
* LICENSE file in the root directory of this source tree.
|
|
78
78
|
*
|
|
79
79
|
*/
|
|
80
|
-
function useLexicalRichText(editor, externalHistoryState
|
|
81
|
-
useRichTextSetup(editor
|
|
80
|
+
function useLexicalRichText(editor, externalHistoryState) {
|
|
81
|
+
useRichTextSetup(editor);
|
|
82
82
|
useLexicalHistory(editor, externalHistoryState);
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
'use strict';var history=require("@lexical/history"),
|
|
8
|
-
function n(a
|
|
7
|
+
'use strict';var history=require("@lexical/history"),c=require("react"),f=require("@lexical/dragon"),g=require("@lexical/rich-text"),h=require("@lexical/utils");function k(a,b,d=1E3){let e=c.useMemo(()=>b||history.createEmptyHistoryState(),[b]);c.useEffect(()=>history.registerHistory(a,e,d),[d,a,e])}function l(a,b,d=1E3){return k(a,b,d)}var m="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?c.useLayoutEffect:c.useEffect;
|
|
8
|
+
function n(a){m(()=>h.mergeRegister(g.registerRichText(a),f.registerDragonSupport(a)),[a])}exports.useLexicalRichText=function(a,b){n(a);l(a,b)}
|
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
import type { LexicalNode } from 'lexical';
|
|
9
|
-
import { TypeaheadOption } from '@lexical/react/LexicalTypeaheadMenuPlugin';
|
|
9
|
+
import { MenuRenderFn, TypeaheadOption } from '@lexical/react/LexicalTypeaheadMenuPlugin';
|
|
10
10
|
import { LexicalCommand, LexicalEditor } from 'lexical';
|
|
11
|
-
import * as React from 'react';
|
|
12
11
|
export declare type EmbedMatchResult = {
|
|
13
12
|
url: string;
|
|
14
13
|
id: string;
|
|
@@ -20,27 +19,18 @@ export interface EmbedConfig {
|
|
|
20
19
|
}
|
|
21
20
|
export declare const URL_MATCHER: RegExp;
|
|
22
21
|
export declare const INSERT_EMBED_COMMAND: LexicalCommand<EmbedConfig['type']>;
|
|
23
|
-
export declare type EmbedMenuProps = {
|
|
24
|
-
selectedItemIndex: number | null;
|
|
25
|
-
onOptionClick: (option: AutoEmbedOption, index: number) => void;
|
|
26
|
-
onOptionMouseEnter: (index: number) => void;
|
|
27
|
-
options: Array<AutoEmbedOption>;
|
|
28
|
-
};
|
|
29
|
-
export declare type EmbedMenuComponent = React.ComponentType<EmbedMenuProps>;
|
|
30
22
|
export declare class AutoEmbedOption extends TypeaheadOption {
|
|
31
23
|
title: string;
|
|
32
|
-
icon?: JSX.Element;
|
|
33
24
|
onSelect: (targetNode: LexicalNode | null) => void;
|
|
34
25
|
constructor(title: string, options: {
|
|
35
|
-
icon?: JSX.Element;
|
|
36
26
|
onSelect: (targetNode: LexicalNode | null) => void;
|
|
37
27
|
});
|
|
38
28
|
}
|
|
39
29
|
declare type LexicalAutoEmbedPluginProps<TEmbedConfig extends EmbedConfig> = {
|
|
40
30
|
embedConfigs: Array<TEmbedConfig>;
|
|
41
31
|
onOpenEmbedModalForConfig: (embedConfig: TEmbedConfig) => void;
|
|
42
|
-
menuComponent: EmbedMenuComponent;
|
|
43
32
|
getMenuOptions: (activeEmbedConfig: TEmbedConfig, embedFn: () => void, dismissFn: () => void) => Array<AutoEmbedOption>;
|
|
33
|
+
menuRenderFn: MenuRenderFn<AutoEmbedOption>;
|
|
44
34
|
};
|
|
45
|
-
export declare function LexicalAutoEmbedPlugin<TEmbedConfig extends EmbedConfig>({ embedConfigs, onOpenEmbedModalForConfig, getMenuOptions,
|
|
35
|
+
export declare function LexicalAutoEmbedPlugin<TEmbedConfig extends EmbedConfig>({ embedConfigs, onOpenEmbedModalForConfig, getMenuOptions, menuRenderFn, }: LexicalAutoEmbedPluginProps<TEmbedConfig>): JSX.Element | null;
|
|
46
36
|
export {};
|
|
@@ -12,7 +12,6 @@ var LexicalTypeaheadMenuPlugin = require('@lexical/react/LexicalTypeaheadMenuPlu
|
|
|
12
12
|
var utils = require('@lexical/utils');
|
|
13
13
|
var lexical = require('lexical');
|
|
14
14
|
var React = require('react');
|
|
15
|
-
var ReactDOM = require('react-dom');
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -22,12 +21,11 @@ var ReactDOM = require('react-dom');
|
|
|
22
21
|
*
|
|
23
22
|
*/
|
|
24
23
|
const URL_MATCHER = /((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
|
|
25
|
-
const INSERT_EMBED_COMMAND = lexical.createCommand();
|
|
24
|
+
const INSERT_EMBED_COMMAND = lexical.createCommand('INSERT_EMBED_COMMAND');
|
|
26
25
|
class AutoEmbedOption extends LexicalTypeaheadMenuPlugin.TypeaheadOption {
|
|
27
26
|
constructor(title, options) {
|
|
28
27
|
super(title);
|
|
29
28
|
this.title = title;
|
|
30
|
-
this.icon = options.icon;
|
|
31
29
|
this.onSelect = options.onSelect.bind(this);
|
|
32
30
|
}
|
|
33
31
|
|
|
@@ -36,7 +34,7 @@ function LexicalAutoEmbedPlugin({
|
|
|
36
34
|
embedConfigs,
|
|
37
35
|
onOpenEmbedModalForConfig,
|
|
38
36
|
getMenuOptions,
|
|
39
|
-
|
|
37
|
+
menuRenderFn
|
|
40
38
|
}) {
|
|
41
39
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
42
40
|
const [nodeKey, setNodeKey] = React.useState(null);
|
|
@@ -132,21 +130,7 @@ function LexicalAutoEmbedPlugin({
|
|
|
132
130
|
onClose: reset,
|
|
133
131
|
onSelectOption: onSelectOption,
|
|
134
132
|
options: options,
|
|
135
|
-
menuRenderFn:
|
|
136
|
-
selectedIndex,
|
|
137
|
-
selectOptionAndCleanUp,
|
|
138
|
-
setHighlightedIndex
|
|
139
|
-
}) => anchorElement && nodeKey != null ? /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(MenuComponent, {
|
|
140
|
-
options: options,
|
|
141
|
-
selectedItemIndex: selectedIndex,
|
|
142
|
-
onOptionClick: (option, index) => {
|
|
143
|
-
setHighlightedIndex(index);
|
|
144
|
-
selectOptionAndCleanUp(option);
|
|
145
|
-
},
|
|
146
|
-
onOptionMouseEnter: index => {
|
|
147
|
-
setHighlightedIndex(index);
|
|
148
|
-
}
|
|
149
|
-
}), anchorElement) : null
|
|
133
|
+
menuRenderFn: menuRenderFn
|
|
150
134
|
}) : null;
|
|
151
135
|
}
|
|
152
136
|
|
|
@@ -13,6 +13,7 @@ import {TypeaheadOption} from '@lexical/react/LexicalTypeaheadMenuPlugin';
|
|
|
13
13
|
import type {LexicalCommand, LexicalEditor, NodeKey, TextNode} from 'lexical';
|
|
14
14
|
import * as React from 'react';
|
|
15
15
|
import {createCommand} from 'lexical';
|
|
16
|
+
import type {MenuRenderFn} from './LexicalTypeaheadMenuPlugin';
|
|
16
17
|
|
|
17
18
|
export type EmbedMatchResult = {
|
|
18
19
|
url: string,
|
|
@@ -32,26 +33,17 @@ export const URL_MATCHER: RegExp =
|
|
|
32
33
|
/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
|
|
33
34
|
|
|
34
35
|
export const INSERT_EMBED_COMMAND: LexicalCommand<EmbedConfig['type']> =
|
|
35
|
-
createCommand();
|
|
36
|
-
|
|
37
|
-
export type EmbedMenuProps = {
|
|
38
|
-
selectedItemIndex: number | null,
|
|
39
|
-
onOptionClick: (option: AutoEmbedOption, index: number) => void,
|
|
40
|
-
onOptionMouseEnter: (index: number) => void,
|
|
41
|
-
options: Array<AutoEmbedOption>,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export type EmbedMenuComponent = React.ComponentType<EmbedMenuProps>;
|
|
36
|
+
createCommand('INSERT_EMBED_COMMAND');
|
|
45
37
|
|
|
46
38
|
type LexicalAutoEmbedPluginProps<TEmbedConfig> = {
|
|
47
39
|
embedConfigs: Array<TEmbedConfig>,
|
|
48
40
|
onOpenEmbedModalForConfig: (embedConfig: TEmbedConfig) => void,
|
|
49
|
-
menuComponent: EmbedMenuComponent,
|
|
50
41
|
getMenuOptions: (
|
|
51
42
|
activeEmbedConfig: TEmbedConfig,
|
|
52
43
|
embedFn: () => void,
|
|
53
44
|
dismissFn: () => void,
|
|
54
45
|
) => Array<AutoEmbedOption>,
|
|
46
|
+
menuRenderFn: MenuRenderFn<AutoEmbedOption>,
|
|
55
47
|
};
|
|
56
48
|
|
|
57
49
|
declare export class AutoEmbedOption extends TypeaheadOption {
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
'use strict';var
|
|
8
|
-
exports.LexicalAutoEmbedPlugin=function({embedConfigs:g,onOpenEmbedModalForConfig:n,getMenuOptions:
|
|
9
|
-
|
|
10
|
-
(
|
|
11
|
-
d(l)},onOptionMouseEnter:l=>{k(l)}}),b):null}):null};exports.URL_MATCHER=/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
|
|
7
|
+
'use strict';var d=require("@lexical/link"),k=require("@lexical/react/LexicalComposerContext"),m=require("@lexical/react/LexicalTypeaheadMenuPlugin"),q=require("@lexical/utils"),r=require("lexical"),z=require("react");let A=r.createCommand("INSERT_EMBED_COMMAND");class B extends m.TypeaheadOption{constructor(g,n){super(g);this.title=g;this.onSelect=n.onSelect.bind(this)}}exports.AutoEmbedOption=B;exports.INSERT_EMBED_COMMAND=A;
|
|
8
|
+
exports.LexicalAutoEmbedPlugin=function({embedConfigs:g,onOpenEmbedModalForConfig:n,getMenuOptions:t,menuRenderFn:C}){let [b]=k.useLexicalComposerContext(),[f,u]=z.useState(null),[h,v]=z.useState(null),l=z.useCallback(()=>{u(null);v(null)},[]),w=z.useCallback(c=>{b.getEditorState().read(()=>{const a=r.$getNodeByKey(c);if(d.$isLinkNode(a)){const e=g.find(p=>p.parseUrl(a.__url));null!=e&&(v(e),u(a.getKey()))}})},[b,g]);z.useEffect(()=>{let c=(a,{updateTags:e,dirtyLeaves:p})=>{for(const [x,D]of a)"created"===
|
|
9
|
+
D&&e.has("paste")&&1===p.size?w(x):x===f&&l()};return q.mergeRegister(...[d.LinkNode,d.AutoLinkNode].map(a=>b.registerMutationListener(a,(...e)=>c(...e))))},[w,b,g,f,l]);z.useEffect(()=>b.registerCommand(A,c=>{let a=g.find(({type:e})=>e===c);return a?(n(a),!0):!1},r.COMMAND_PRIORITY_EDITOR),[b,g,n]);let y=z.useCallback(()=>{if(null!=h&&null!=f){const c=b.getEditorState().read(()=>{const a=r.$getNodeByKey(f);return d.$isLinkNode(a)?a:null});if(d.$isLinkNode(c)){const a=h.parseUrl(c.__url);null!=a&&
|
|
10
|
+
(b.update(()=>{h.insertNode(b,a)}),c.isAttached()&&b.update(()=>{c.remove()}))}}},[h,b,f]),E=z.useMemo(()=>null!=h&&null!=f?t(h,y,l):[],[h,y,t,f,l]),F=z.useCallback((c,a,e)=>{b.update(()=>{c.onSelect(a);e()})},[b]);return null!=f?z.createElement(m.LexicalNodeMenuPlugin,{nodeKey:f,onClose:l,onSelectOption:F,options:E,menuRenderFn:C}):null};exports.URL_MATCHER=/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
|
|
@@ -32,6 +32,20 @@ function findFirstMatch(text, matchers) {
|
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
const PUNCTUATION_OR_SPACE = /[.,;\s]/;
|
|
36
|
+
|
|
37
|
+
function isSeparator(char) {
|
|
38
|
+
return PUNCTUATION_OR_SPACE.test(char);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function endsWithSeparator(textContent) {
|
|
42
|
+
return isSeparator(textContent[textContent.length - 1]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function startsWithSeparator(textContent) {
|
|
46
|
+
return isSeparator(textContent[0]);
|
|
47
|
+
}
|
|
48
|
+
|
|
35
49
|
function isPreviousNodeValid(node) {
|
|
36
50
|
let previousNode = node.getPreviousSibling();
|
|
37
51
|
|
|
@@ -39,7 +53,7 @@ function isPreviousNodeValid(node) {
|
|
|
39
53
|
previousNode = previousNode.getLastDescendant();
|
|
40
54
|
}
|
|
41
55
|
|
|
42
|
-
return previousNode === null || lexical.$isLineBreakNode(previousNode) || lexical.$isTextNode(previousNode) && previousNode.getTextContent()
|
|
56
|
+
return previousNode === null || lexical.$isLineBreakNode(previousNode) || lexical.$isTextNode(previousNode) && endsWithSeparator(previousNode.getTextContent());
|
|
43
57
|
}
|
|
44
58
|
|
|
45
59
|
function isNextNodeValid(node) {
|
|
@@ -49,63 +63,55 @@ function isNextNodeValid(node) {
|
|
|
49
63
|
nextNode = nextNode.getFirstDescendant();
|
|
50
64
|
}
|
|
51
65
|
|
|
52
|
-
return nextNode === null || lexical.$isLineBreakNode(nextNode) || lexical.$isTextNode(nextNode) && nextNode.getTextContent()
|
|
66
|
+
return nextNode === null || lexical.$isLineBreakNode(nextNode) || lexical.$isTextNode(nextNode) && startsWithSeparator(nextNode.getTextContent());
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isContentAroundIsValid(matchStart, matchEnd, text, node) {
|
|
70
|
+
const contentBeforeIsValid = matchStart > 0 ? isSeparator(text[matchStart - 1]) : isPreviousNodeValid(node);
|
|
71
|
+
|
|
72
|
+
if (!contentBeforeIsValid) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const contentAfterIsValid = matchEnd < text.length ? isSeparator(text[matchEnd]) : isNextNodeValid(node);
|
|
77
|
+
return contentAfterIsValid;
|
|
53
78
|
}
|
|
54
79
|
|
|
55
80
|
function handleLinkCreation(node, matchers, onChange) {
|
|
56
81
|
const nodeText = node.getTextContent();
|
|
57
|
-
const nodeTextLength = nodeText.length;
|
|
58
82
|
let text = nodeText;
|
|
59
|
-
let
|
|
60
|
-
let
|
|
83
|
+
let invalidMatchEnd = 0;
|
|
84
|
+
let remainingTextNode = node;
|
|
61
85
|
let match;
|
|
62
86
|
|
|
63
87
|
while ((match = findFirstMatch(text, matchers)) && match !== null) {
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
// 2. Space in previous simple text node
|
|
69
|
-
// 3. Previous node is LineBreakNode
|
|
70
|
-
|
|
71
|
-
let contentBeforeMatchIsValid;
|
|
72
|
-
|
|
73
|
-
if (offset > 0) {
|
|
74
|
-
contentBeforeMatchIsValid = nodeText[offset - 1] === ' ';
|
|
75
|
-
} else {
|
|
76
|
-
contentBeforeMatchIsValid = isPreviousNodeValid(node);
|
|
77
|
-
} // Next node is valid if any of:
|
|
78
|
-
// 1. Space after same node
|
|
79
|
-
// 2. Space in next simple text node
|
|
80
|
-
// 3. Next node is LineBreakNode
|
|
81
|
-
|
|
88
|
+
const matchStart = match.index;
|
|
89
|
+
const matchLength = match.length;
|
|
90
|
+
const matchEnd = matchStart + matchLength;
|
|
91
|
+
const isValid = isContentAroundIsValid(invalidMatchEnd + matchStart, invalidMatchEnd + matchEnd, nodeText, node);
|
|
82
92
|
|
|
83
|
-
|
|
93
|
+
if (isValid) {
|
|
94
|
+
let linkTextNode;
|
|
84
95
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
} else {
|
|
88
|
-
contentAfterMatchIsValid = isNextNodeValid(node);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (contentBeforeMatchIsValid && contentAfterMatchIsValid) {
|
|
92
|
-
let middleNode;
|
|
93
|
-
|
|
94
|
-
if (matchOffset === 0) {
|
|
95
|
-
[middleNode, lastNode] = lastNode.splitText(matchLength);
|
|
96
|
+
if (invalidMatchEnd + matchStart === 0) {
|
|
97
|
+
[linkTextNode, remainingTextNode] = remainingTextNode.splitText(invalidMatchEnd + matchLength);
|
|
96
98
|
} else {
|
|
97
|
-
[,
|
|
99
|
+
[, linkTextNode, remainingTextNode] = remainingTextNode.splitText(invalidMatchEnd + matchStart, invalidMatchEnd + matchStart + matchLength);
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
const linkNode = link.$createAutoLinkNode(match.url);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
const textNode = lexical.$createTextNode(match.text);
|
|
104
|
+
textNode.setFormat(linkTextNode.getFormat());
|
|
105
|
+
textNode.setDetail(linkTextNode.getDetail());
|
|
106
|
+
linkNode.append(textNode);
|
|
107
|
+
linkTextNode.replace(linkNode);
|
|
103
108
|
onChange(match.url, null);
|
|
109
|
+
invalidMatchEnd = 0;
|
|
110
|
+
} else {
|
|
111
|
+
invalidMatchEnd += matchEnd;
|
|
104
112
|
}
|
|
105
113
|
|
|
106
|
-
|
|
107
|
-
text = text.substring(iterationOffset);
|
|
108
|
-
textOffset += iterationOffset;
|
|
114
|
+
text = text.substring(matchEnd);
|
|
109
115
|
}
|
|
110
116
|
}
|
|
111
117
|
|
|
@@ -156,12 +162,12 @@ function handleBadNeighbors(textNode, onChange) {
|
|
|
156
162
|
const nextSibling = textNode.getNextSibling();
|
|
157
163
|
const text = textNode.getTextContent();
|
|
158
164
|
|
|
159
|
-
if (link.$isAutoLinkNode(previousSibling) && !text
|
|
165
|
+
if (link.$isAutoLinkNode(previousSibling) && !startsWithSeparator(text)) {
|
|
160
166
|
replaceWithChildren(previousSibling);
|
|
161
167
|
onChange(null, previousSibling.getURL());
|
|
162
168
|
}
|
|
163
169
|
|
|
164
|
-
if (link.$isAutoLinkNode(nextSibling) && !text
|
|
170
|
+
if (link.$isAutoLinkNode(nextSibling) && !endsWithSeparator(text)) {
|
|
165
171
|
replaceWithChildren(nextSibling);
|
|
166
172
|
onChange(null, nextSibling.getURL());
|
|
167
173
|
}
|
|
@@ -205,8 +211,6 @@ function useAutoLink(editor, matchers, onChange) {
|
|
|
205
211
|
|
|
206
212
|
handleBadNeighbors(textNode, onChangeWrapped);
|
|
207
213
|
}
|
|
208
|
-
}), editor.registerNodeTransform(link.AutoLinkNode, linkNode => {
|
|
209
|
-
handleLinkEdit(linkNode, matchers, onChangeWrapped);
|
|
210
214
|
}));
|
|
211
215
|
}, [editor, matchers, onChange]);
|
|
212
216
|
}
|