@lexical/react 0.5.0 → 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.
@@ -6,4 +6,5 @@
6
6
  *
7
7
  */
8
8
  import type { LexicalEditor } from 'lexical';
9
- export declare function useLexicalDecorators(editor: LexicalEditor): Array<JSX.Element>;
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 react = require('react');
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 ? react.useLayoutEffect : react.useEffect;
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
- const [decorators, setDecorators] = react.useState(() => editor.getDecorators()); // Subscribe to changes
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
- react.useEffect(() => {
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 react.useMemo(() => {
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 = decorators[nodeKey];
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
- return useDecorators(editor);
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 b=require("react"),g=require("react-dom"),l="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?b.useLayoutEffect:b.useEffect;
8
- function m(a){let [e,h]=b.useState(()=>a.getDecorators());l(()=>a.registerDecoratorListener(d=>{g.flushSync(()=>{h(d)})}),[a]);b.useEffect(()=>{h(a.getDecorators())},[a]);return b.useMemo(()=>{let d=[],k=Object.keys(e);for(let f=0;f<k.length;f++){var c=k[f];let n=e[c];c=a.getElementByKey(c);null!==c&&d.push(g.createPortal(n,c))}return d},[e,a])}exports.useLexicalDecorators=function(a){return m(a)}
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,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,13 +19,6 @@ 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
24
  onSelect: (targetNode: LexicalNode | null) => void;
@@ -37,8 +29,8 @@ export declare class AutoEmbedOption extends TypeaheadOption {
37
29
  declare type LexicalAutoEmbedPluginProps<TEmbedConfig extends EmbedConfig> = {
38
30
  embedConfigs: Array<TEmbedConfig>;
39
31
  onOpenEmbedModalForConfig: (embedConfig: TEmbedConfig) => void;
40
- menuComponent: EmbedMenuComponent;
41
32
  getMenuOptions: (activeEmbedConfig: TEmbedConfig, embedFn: () => void, dismissFn: () => void) => Array<AutoEmbedOption>;
33
+ menuRenderFn: MenuRenderFn<AutoEmbedOption>;
42
34
  };
43
- export declare function LexicalAutoEmbedPlugin<TEmbedConfig extends EmbedConfig>({ embedConfigs, onOpenEmbedModalForConfig, getMenuOptions, menuComponent: MenuComponent, }: LexicalAutoEmbedPluginProps<TEmbedConfig>): JSX.Element | null;
35
+ export declare function LexicalAutoEmbedPlugin<TEmbedConfig extends EmbedConfig>({ embedConfigs, onOpenEmbedModalForConfig, getMenuOptions, menuRenderFn, }: LexicalAutoEmbedPluginProps<TEmbedConfig>): JSX.Element | null;
44
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,7 +21,7 @@ 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);
@@ -35,7 +34,7 @@ function LexicalAutoEmbedPlugin({
35
34
  embedConfigs,
36
35
  onOpenEmbedModalForConfig,
37
36
  getMenuOptions,
38
- menuComponent: MenuComponent
37
+ menuRenderFn
39
38
  }) {
40
39
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
41
40
  const [nodeKey, setNodeKey] = React.useState(null);
@@ -131,21 +130,7 @@ function LexicalAutoEmbedPlugin({
131
130
  onClose: reset,
132
131
  onSelectOption: onSelectOption,
133
132
  options: options,
134
- menuRenderFn: (anchorElement, {
135
- selectedIndex,
136
- selectOptionAndCleanUp,
137
- setHighlightedIndex
138
- }) => anchorElement && nodeKey != null ? /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(MenuComponent, {
139
- options: options,
140
- selectedItemIndex: selectedIndex,
141
- onOptionClick: (option, index) => {
142
- setHighlightedIndex(index);
143
- selectOptionAndCleanUp(option);
144
- },
145
- onOptionMouseEnter: index => {
146
- setHighlightedIndex(index);
147
- }
148
- }), anchorElement) : null
133
+ menuRenderFn: menuRenderFn
149
134
  }) : null;
150
135
  }
151
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 e=require("@lexical/link"),m=require("@lexical/react/LexicalComposerContext"),p=require("@lexical/react/LexicalTypeaheadMenuPlugin"),t=require("@lexical/utils"),u=require("lexical"),B=require("react"),C=require("react-dom");let D=u.createCommand();class E extends p.TypeaheadOption{constructor(g,q){super(g);this.title=g;this.onSelect=q.onSelect.bind(this)}}exports.AutoEmbedOption=E;exports.INSERT_EMBED_COMMAND=D;
8
- exports.LexicalAutoEmbedPlugin=function({embedConfigs:g,onOpenEmbedModalForConfig:q,getMenuOptions:v,menuComponent:F}){let [c]=m.useLexicalComposerContext(),[f,w]=B.useState(null),[h,x]=B.useState(null),n=B.useCallback(()=>{w(null);x(null)},[]),y=B.useCallback(b=>{c.getEditorState().read(()=>{const a=u.$getNodeByKey(b);if(e.$isLinkNode(a)){const d=g.find(k=>k.parseUrl(a.__url));null!=d&&(x(d),w(a.getKey()))}})},[c,g]);B.useEffect(()=>{let b=(a,{updateTags:d,dirtyLeaves:k})=>{for(const [l,r]of a)"created"===
9
- r&&d.has("paste")&&1===k.size?y(l):l===f&&n()};return t.mergeRegister(...[e.LinkNode,e.AutoLinkNode].map(a=>c.registerMutationListener(a,(...d)=>b(...d))))},[y,c,g,f,n]);B.useEffect(()=>c.registerCommand(D,b=>{let a=g.find(({type:d})=>d===b);return a?(q(a),!0):!1},u.COMMAND_PRIORITY_EDITOR),[c,g,q]);let z=B.useCallback(()=>{if(null!=h&&null!=f){const b=c.getEditorState().read(()=>{const a=u.$getNodeByKey(f);return e.$isLinkNode(a)?a:null});if(e.$isLinkNode(b)){const a=h.parseUrl(b.__url);null!=a&&
10
- (c.update(()=>{h.insertNode(c,a)}),b.isAttached()&&c.update(()=>{b.remove()}))}}},[h,c,f]),A=B.useMemo(()=>null!=h&&null!=f?v(h,z,n):[],[h,z,v,f,n]),G=B.useCallback((b,a,d)=>{c.update(()=>{b.onSelect(a);d()})},[c]);return null!=f?B.createElement(p.LexicalNodeMenuPlugin,{nodeKey:f,onClose:n,onSelectOption:G,options:A,menuRenderFn:(b,{selectedIndex:a,selectOptionAndCleanUp:d,setHighlightedIndex:k})=>b&&null!=f?C.createPortal(B.createElement(F,{options:A,selectedItemIndex:a,onOptionClick:(l,r)=>{k(r);
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().endsWith(' ');
56
+ return previousNode === null || lexical.$isLineBreakNode(previousNode) || lexical.$isTextNode(previousNode) && endsWithSeparator(previousNode.getTextContent());
43
57
  }
44
58
 
45
59
  function isNextNodeValid(node) {
@@ -49,64 +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().startsWith(' ');
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 textOffset = 0;
60
- let lastNode = node;
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 matchOffset = match.index;
65
- const offset = textOffset + matchOffset;
66
- const matchLength = match.length; // Previous node is valid if any of:
67
- // 1. Space before same node
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
- let contentAfterMatchIsValid;
93
+ if (isValid) {
94
+ let linkTextNode;
84
95
 
85
- if (offset + matchLength < nodeTextLength) {
86
- contentAfterMatchIsValid = nodeText[offset + matchLength] === ' ';
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
- [, middleNode, lastNode] = lastNode.splitText(matchOffset, matchOffset + matchLength);
99
+ [, linkTextNode, remainingTextNode] = remainingTextNode.splitText(invalidMatchEnd + matchStart, invalidMatchEnd + matchStart + matchLength);
98
100
  }
99
101
 
100
- const nodeFormat = node.__format;
101
102
  const linkNode = link.$createAutoLinkNode(match.url);
102
- linkNode.append(lexical.$createTextNode(match.text).setFormat(nodeFormat));
103
- middleNode.replace(linkNode);
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);
104
108
  onChange(match.url, null);
109
+ invalidMatchEnd = 0;
110
+ } else {
111
+ invalidMatchEnd += matchEnd;
105
112
  }
106
113
 
107
- const iterationOffset = matchOffset + matchLength;
108
- text = text.substring(iterationOffset);
109
- textOffset += iterationOffset;
114
+ text = text.substring(matchEnd);
110
115
  }
111
116
  }
112
117
 
@@ -157,12 +162,12 @@ function handleBadNeighbors(textNode, onChange) {
157
162
  const nextSibling = textNode.getNextSibling();
158
163
  const text = textNode.getTextContent();
159
164
 
160
- if (link.$isAutoLinkNode(previousSibling) && !text.startsWith(' ')) {
165
+ if (link.$isAutoLinkNode(previousSibling) && !startsWithSeparator(text)) {
161
166
  replaceWithChildren(previousSibling);
162
167
  onChange(null, previousSibling.getURL());
163
168
  }
164
169
 
165
- if (link.$isAutoLinkNode(nextSibling) && !text.endsWith(' ')) {
170
+ if (link.$isAutoLinkNode(nextSibling) && !endsWithSeparator(text)) {
166
171
  replaceWithChildren(nextSibling);
167
172
  onChange(null, nextSibling.getURL());
168
173
  }
@@ -206,8 +211,6 @@ function useAutoLink(editor, matchers, onChange) {
206
211
 
207
212
  handleBadNeighbors(textNode, onChangeWrapped);
208
213
  }
209
- }), editor.registerNodeTransform(link.AutoLinkNode, linkNode => {
210
- handleLinkEdit(linkNode, matchers, onChangeWrapped);
211
214
  }));
212
215
  }, [editor, matchers, onChange]);
213
216
  }
@@ -4,9 +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 h=require("@lexical/link"),m=require("@lexical/react/LexicalComposerContext"),p=require("@lexical/utils"),v=require("lexical"),w=require("react");function x(a,c){for(let b=0;b<c.length;b++){let e=c[b](a);if(e)return e}return null}function y(a){a=a.getPreviousSibling();v.$isElementNode(a)&&(a=a.getLastDescendant());return null===a||v.$isLineBreakNode(a)||v.$isTextNode(a)&&a.getTextContent().endsWith(" ")}
8
- function A(a){a=a.getNextSibling();v.$isElementNode(a)&&(a=a.getFirstDescendant());return null===a||v.$isLineBreakNode(a)||v.$isTextNode(a)&&a.getTextContent().startsWith(" ")}
9
- function B(a,c,b){var e=a.getChildren();let d=e.length;for(let f=0;f<d;f++){let g=e[f];if(!v.$isTextNode(g)||!g.isSimpleText()){C(a);b(null,a.getURL());return}}e=a.getTextContent();c=x(e,c);null===c||c.text!==e?(C(a),b(null,a.getURL())):y(a)&&A(a)?(e=a.getURL(),null!==c&&e!==c.url&&(a.setURL(c.url),b(c.url,e))):(C(a),b(null,a.getURL()))}function C(a){let c=a.getChildren();var b=c.length;for(--b;0<=b;b--)a.insertAfter(c[b]);a.remove();return c.map(e=>e.getLatest())}
10
- function D(a,c,b){w.useEffect(()=>{if(!a.hasNodes([h.AutoLinkNode]))throw Error("Minified Lexical error #77; visit https://lexical.dev/docs/error?code=77 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.");let e=(d,f)=>{b&&b(d,f)};return p.mergeRegister(a.registerNodeTransform(v.TextNode,d=>{var f=d.getParentOrThrow();if(h.$isAutoLinkNode(f))B(f,c,e);else if(!h.$isLinkNode(f)){if(d.isSimpleText()){f=d.getTextContent();let E=f.length,u=f,
11
- z=0,r=d;for(var g;(g=x(u,c))&&null!==g;){var n=g.index,k=z+n;let q=g.length;var t=void 0;t=0<k?" "===f[k-1]:y(d);k=k+q<E?" "===f[k+q]:A(d);if(t&&k){var l=void 0;0===n?[l,r]=r.splitText(q):[,l,r]=r.splitText(n,n+q);t=d.__format;k=h.$createAutoLinkNode(g.url);k.append(v.$createTextNode(g.text).setFormat(t));l.replace(k);b&&b(g.url,null)}n+=q;u=u.substring(n);z+=n}}l=d.getPreviousSibling();g=d.getNextSibling();d=d.getTextContent();h.$isAutoLinkNode(l)&&!d.startsWith(" ")&&(C(l),l=l.getURL(),b&&b(null,
12
- l));h.$isAutoLinkNode(g)&&!d.endsWith(" ")&&(C(g),d=g.getURL(),b&&b(null,d))}}),a.registerNodeTransform(h.AutoLinkNode,d=>{B(d,c,e)}))},[a,c,b])}exports.AutoLinkPlugin=function({matchers:a,onChange:c}){let [b]=m.useLexicalComposerContext();D(b,a,c);return null}
7
+ 'use strict';var h=require("@lexical/link"),l=require("@lexical/react/LexicalComposerContext"),m=require("@lexical/utils"),q=require("lexical"),y=require("react");function z(b,e){for(let c=0;c<e.length;c++){let a=e[c](b);if(a)return a}return null}let A=/[.,;\s]/;function C(b){b=b.getPreviousSibling();q.$isElementNode(b)&&(b=b.getLastDescendant());var e;!(e=null===b||q.$isLineBreakNode(b))&&(e=q.$isTextNode(b))&&(b=b.getTextContent(),e=A.test(b[b.length-1]));return e}
8
+ function D(b){b=b.getNextSibling();q.$isElementNode(b)&&(b=b.getFirstDescendant());return null===b||q.$isLineBreakNode(b)||q.$isTextNode(b)&&A.test(b.getTextContent()[0])}function E(b){let e=b.getChildren();var c=e.length;for(--c;0<=c;c--)b.insertAfter(e[c]);b.remove();return e.map(a=>a.getLatest())}
9
+ function F(b,e,c){y.useEffect(()=>{if(!b.hasNodes([h.AutoLinkNode]))throw Error("Minified Lexical error #77; visit https://lexical.dev/docs/error?code=77 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.");return m.mergeRegister(b.registerNodeTransform(q.TextNode,a=>{var f=a.getParentOrThrow();if(h.$isAutoLinkNode(f))a:{a=f.getChildren();var d=a.length;for(var g=0;g<d;g++){var n=a[g];if(!q.$isTextNode(n)||!n.isSimpleText()){E(f);a=f.getURL();
10
+ c&&c(null,a);break a}}d=f.getTextContent();a=z(d,e);null===a||a.text!==d?(E(f),a=f.getURL(),c&&c(null,a)):C(f)&&D(f)?(d=f.getURL(),null!==a&&d!==a.url&&(f.setURL(a.url),c&&c(a.url,d))):(E(f),a=f.getURL(),c&&c(null,a))}else if(!h.$isLinkNode(f)){if(a.isSimpleText()){n=f=a.getTextContent();let k=0,u=a;for(;(d=z(n,e))&&null!==d;){let r=d.index,v=d.length,w=r+v;var t=k+r,p=k+w,x=f,B=a;(0<t?A.test(x[t-1]):C(B))&&(p<x.length?A.test(x[p]):D(B))?(0===k+r?[g,u]=u.splitText(k+v):[,g,u]=u.splitText(k+r,k+r+
11
+ v),t=h.$createAutoLinkNode(d.url),p=q.$createTextNode(d.text),p.setFormat(g.getFormat()),p.setDetail(g.getDetail()),t.append(p),g.replace(t),c&&c(d.url,null),k=0):k+=w;n=n.substring(w)}}g=a.getPreviousSibling();d=a.getNextSibling();a=a.getTextContent();h.$isAutoLinkNode(g)&&!A.test(a[0])&&(E(g),g=g.getURL(),c&&c(null,g));h.$isAutoLinkNode(d)&&!A.test(a[a.length-1])&&(E(d),a=d.getURL(),c&&c(null,a))}}))},[b,e,c])}
12
+ exports.AutoLinkPlugin=function({matchers:b,onChange:e}){let [c]=l.useLexicalComposerContext();F(c,b,e);return null}
@@ -19,7 +19,7 @@ var React = require('react');
19
19
  * LICENSE file in the root directory of this source tree.
20
20
  *
21
21
  */
22
- const INSERT_HORIZONTAL_RULE_COMMAND = lexical.createCommand();
22
+ const INSERT_HORIZONTAL_RULE_COMMAND = lexical.createCommand('INSERT_HORIZONTAL_RULE_COMMAND');
23
23
 
24
24
  function HorizontalRuleComponent({
25
25
  nodeKey
@@ -4,7 +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 a=require("@lexical/react/LexicalComposerContext"),d=require("@lexical/react/useLexicalNodeSelection"),h=require("@lexical/utils"),l=require("lexical"),p=require("react");let q=l.createCommand();
7
+ 'use strict';var a=require("@lexical/react/LexicalComposerContext"),d=require("@lexical/react/useLexicalNodeSelection"),h=require("@lexical/utils"),l=require("lexical"),p=require("react");let q=l.createCommand("INSERT_HORIZONTAL_RULE_COMMAND");
8
8
  function r({nodeKey:b}){let [f]=a.useLexicalComposerContext(),m=p.useRef(null),[e,g,n]=d.useLexicalNodeSelection(b),k=p.useCallback(c=>{e&&l.$isNodeSelection(l.$getSelection())&&(c.preventDefault(),c=l.$getNodeByKey(b),t(c)&&c.remove(),g(!1));return!1},[e,b,g]);p.useEffect(()=>h.mergeRegister(f.registerCommand(l.CLICK_COMMAND,c=>c.target===m.current?(c.shiftKey||n(),g(!e),!0):!1,l.COMMAND_PRIORITY_LOW),f.registerCommand(l.KEY_DELETE_COMMAND,k,l.COMMAND_PRIORITY_LOW),f.registerCommand(l.KEY_BACKSPACE_COMMAND,
9
9
  k,l.COMMAND_PRIORITY_LOW)),[n,f,e,k,g]);return p.createElement("hr",{ref:m,className:e?"selected":void 0})}
10
10
  class u extends l.DecoratorNode{static getType(){return"horizontalrule"}static clone(b){return new u(b.__key)}static importJSON(){return v()}static importDOM(){return{hr:()=>({conversion:w,priority:0})}}exportJSON(){return{type:"horizontalrule",version:1}}exportDOM(){return{element:document.createElement("hr")}}createDOM(){let b=document.createElement("div");b.style.display="contents";return b}getTextContent(){return"\n"}isInline(){return!1}updateDOM(){return!1}decorate(){return p.createElement(r,{nodeKey:this.__key})}}
@@ -5,7 +5,9 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  */
8
- export declare function PlainTextPlugin({ contentEditable, placeholder, }: {
8
+ import { ErrorBoundaryType } from './shared/useDecorators';
9
+ export declare function PlainTextPlugin({ contentEditable, placeholder, ErrorBoundary, }: {
9
10
  contentEditable: JSX.Element;
10
11
  placeholder: JSX.Element | string;
12
+ ErrorBoundary?: ErrorBoundaryType;
11
13
  }): JSX.Element;