@lexical/react 0.3.8 → 0.3.9
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/LexicalAutoEmbedPlugin.d.ts +46 -0
- package/LexicalAutoEmbedPlugin.dev.js +490 -0
- package/LexicalAutoEmbedPlugin.js +9 -0
- package/LexicalAutoEmbedPlugin.prod.js +21 -0
- package/LexicalAutoLinkPlugin.dev.js +1 -1
- package/LexicalAutoLinkPlugin.prod.js +2 -2
- package/LexicalCollaborationContext.d.ts +19 -0
- package/LexicalCollaborationContext.dev.js +38 -0
- package/LexicalCollaborationContext.js +9 -0
- package/LexicalCollaborationContext.js.flow +21 -0
- package/LexicalCollaborationContext.prod.js +8 -0
- package/LexicalCollaborationPlugin.d.ts +0 -10
- package/LexicalCollaborationPlugin.dev.js +12 -20
- package/LexicalCollaborationPlugin.js.flow +0 -9
- package/LexicalCollaborationPlugin.prod.js +9 -10
- package/LexicalLinkPlugin.dev.js +15 -2
- package/LexicalLinkPlugin.prod.js +2 -1
- package/LexicalNestedComposer.dev.js +11 -5
- package/LexicalNestedComposer.prod.js +3 -3
- package/LexicalOnChangePlugin.d.ts +2 -1
- package/LexicalOnChangePlugin.dev.js +6 -3
- package/LexicalOnChangePlugin.js.flow +2 -0
- package/LexicalOnChangePlugin.prod.js +2 -2
- package/LexicalTableOfContents__EXPERIMENTAL.js.flow +2 -2
- package/LexicalTreeView.dev.js +43 -9
- package/LexicalTreeView.prod.js +13 -13
- package/LexicalTypeaheadMenuPlugin.d.ts +11 -3
- package/LexicalTypeaheadMenuPlugin.dev.js +115 -58
- package/LexicalTypeaheadMenuPlugin.prod.js +16 -13
- package/package.json +19 -19
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
import type { LexicalNode } from 'lexical';
|
|
9
|
+
import { TypeaheadOption } from '@lexical/react/src/LexicalTypeaheadMenuPlugin';
|
|
10
|
+
import { LexicalCommand, LexicalEditor } from 'lexical';
|
|
11
|
+
import * as React from 'react';
|
|
12
|
+
export declare type EmbedMatchResult = {
|
|
13
|
+
url: string;
|
|
14
|
+
id: string;
|
|
15
|
+
};
|
|
16
|
+
export interface EmbedConfig {
|
|
17
|
+
type: string;
|
|
18
|
+
parseUrl: (text: string) => EmbedMatchResult | null;
|
|
19
|
+
insertNode: (editor: LexicalEditor, result: EmbedMatchResult) => void;
|
|
20
|
+
}
|
|
21
|
+
export declare const URL_MATCHER: RegExp;
|
|
22
|
+
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
|
+
export declare class AutoEmbedOption extends TypeaheadOption {
|
|
31
|
+
title: string;
|
|
32
|
+
icon?: JSX.Element;
|
|
33
|
+
onSelect: (targetNode: LexicalNode | null) => void;
|
|
34
|
+
constructor(title: string, options: {
|
|
35
|
+
icon?: JSX.Element;
|
|
36
|
+
onSelect: (targetNode: LexicalNode | null) => void;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
declare type LexicalAutoEmbedPluginProps<TEmbedConfig extends EmbedConfig> = {
|
|
40
|
+
embedConfigs: Array<TEmbedConfig>;
|
|
41
|
+
onOpenEmbedModalForConfig: (embedConfig: TEmbedConfig) => void;
|
|
42
|
+
menuComponent: EmbedMenuComponent;
|
|
43
|
+
getMenuOptions: (activeEmbedConfig: TEmbedConfig, embedFn: () => void, dismissFn: () => void) => Array<AutoEmbedOption>;
|
|
44
|
+
};
|
|
45
|
+
export declare function LexicalAutoEmbedPlugin<TEmbedConfig extends EmbedConfig>({ embedConfigs, onOpenEmbedModalForConfig, getMenuOptions, menuComponent: MenuComponent, }: LexicalAutoEmbedPluginProps<TEmbedConfig>): JSX.Element | null;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
var link = require('@lexical/link');
|
|
10
|
+
var LexicalComposerContext = require('@lexical/react/LexicalComposerContext');
|
|
11
|
+
var utils = require('@lexical/utils');
|
|
12
|
+
var lexical = require('lexical');
|
|
13
|
+
var React = require('react');
|
|
14
|
+
var ReactDOM = require('react-dom');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
18
|
+
*
|
|
19
|
+
* This source code is licensed under the MIT license found in the
|
|
20
|
+
* LICENSE file in the root directory of this source tree.
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
27
|
+
*
|
|
28
|
+
* This source code is licensed under the MIT license found in the
|
|
29
|
+
* LICENSE file in the root directory of this source tree.
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
const useLayoutEffectImpl = CAN_USE_DOM ? React.useLayoutEffect : React.useEffect;
|
|
33
|
+
var useLayoutEffect = useLayoutEffectImpl;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
37
|
+
*
|
|
38
|
+
* This source code is licensed under the MIT license found in the
|
|
39
|
+
* LICENSE file in the root directory of this source tree.
|
|
40
|
+
*
|
|
41
|
+
*/
|
|
42
|
+
class TypeaheadOption {
|
|
43
|
+
constructor(key) {
|
|
44
|
+
this.key = key;
|
|
45
|
+
this.ref = {
|
|
46
|
+
current: null
|
|
47
|
+
};
|
|
48
|
+
this.setRefElement = this.setRefElement.bind(this);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setRefElement(element) {
|
|
52
|
+
this.ref = {
|
|
53
|
+
current: element
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const scrollIntoViewIfNeeded = target => {
|
|
60
|
+
const container = document.getElementById('typeahead-menu');
|
|
61
|
+
|
|
62
|
+
if (container) {
|
|
63
|
+
const containerRect = container.getBoundingClientRect();
|
|
64
|
+
const targetRect = target.getBoundingClientRect();
|
|
65
|
+
|
|
66
|
+
if (targetRect.bottom > containerRect.bottom) {
|
|
67
|
+
target.scrollIntoView(false);
|
|
68
|
+
} else if (targetRect.top < containerRect.top) {
|
|
69
|
+
target.scrollIntoView();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Walk backwards along user input and forward through entity title to try
|
|
75
|
+
* and replace more of the user's text with entity.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
function getFullMatchOffset(documentText, entryText, offset) {
|
|
80
|
+
let triggerOffset = offset;
|
|
81
|
+
|
|
82
|
+
for (let i = triggerOffset; i <= entryText.length; i++) {
|
|
83
|
+
if (documentText.substr(-i) === entryText.substr(0, i)) {
|
|
84
|
+
triggerOffset = i;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return triggerOffset;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Split Lexical TextNode and return a new TextNode only containing matched text.
|
|
92
|
+
* Common use cases include: removing the node, replacing with a new node.
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
function splitNodeContainingQuery(editor, match) {
|
|
97
|
+
const selection = lexical.$getSelection();
|
|
98
|
+
|
|
99
|
+
if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed()) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const anchor = selection.anchor;
|
|
104
|
+
|
|
105
|
+
if (anchor.type !== 'text') {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const anchorNode = anchor.getNode();
|
|
110
|
+
|
|
111
|
+
if (!anchorNode.isSimpleText()) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const selectionOffset = anchor.offset;
|
|
116
|
+
const textContent = anchorNode.getTextContent().slice(0, selectionOffset);
|
|
117
|
+
const characterOffset = match.replaceableString.length;
|
|
118
|
+
const queryOffset = getFullMatchOffset(textContent, match.matchingString, characterOffset);
|
|
119
|
+
const startOffset = selectionOffset - queryOffset;
|
|
120
|
+
|
|
121
|
+
if (startOffset < 0) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let newNode;
|
|
126
|
+
|
|
127
|
+
if (startOffset === 0) {
|
|
128
|
+
[newNode] = anchorNode.splitText(selectionOffset);
|
|
129
|
+
} else {
|
|
130
|
+
[, newNode] = anchorNode.splitText(startOffset, selectionOffset);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return newNode;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function startTransition(callback) {
|
|
137
|
+
if (React.startTransition) {
|
|
138
|
+
React.startTransition(callback);
|
|
139
|
+
} else {
|
|
140
|
+
callback();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function LexicalPopoverMenu({
|
|
145
|
+
close,
|
|
146
|
+
editor,
|
|
147
|
+
anchorElement,
|
|
148
|
+
resolution,
|
|
149
|
+
options,
|
|
150
|
+
menuRenderFn,
|
|
151
|
+
onSelectOption
|
|
152
|
+
}) {
|
|
153
|
+
const [selectedIndex, setHighlightedIndex] = React.useState(null);
|
|
154
|
+
React.useEffect(() => {
|
|
155
|
+
setHighlightedIndex(0);
|
|
156
|
+
}, [resolution.match.matchingString]);
|
|
157
|
+
const selectOptionAndCleanUp = React.useCallback(async selectedEntry => {
|
|
158
|
+
editor.update(() => {
|
|
159
|
+
const textNodeContainingQuery = splitNodeContainingQuery(editor, resolution.match);
|
|
160
|
+
onSelectOption(selectedEntry, textNodeContainingQuery, close, resolution.match.matchingString);
|
|
161
|
+
});
|
|
162
|
+
}, [close, editor, resolution.match, onSelectOption]);
|
|
163
|
+
const updateSelectedIndex = React.useCallback(index => {
|
|
164
|
+
const rootElem = editor.getRootElement();
|
|
165
|
+
|
|
166
|
+
if (rootElem !== null) {
|
|
167
|
+
rootElem.setAttribute('aria-activedescendant', 'typeahead-item-' + index);
|
|
168
|
+
setHighlightedIndex(index);
|
|
169
|
+
}
|
|
170
|
+
}, [editor]);
|
|
171
|
+
React.useEffect(() => {
|
|
172
|
+
return () => {
|
|
173
|
+
const rootElem = editor.getRootElement();
|
|
174
|
+
|
|
175
|
+
if (rootElem !== null) {
|
|
176
|
+
rootElem.removeAttribute('aria-activedescendant');
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}, [editor]);
|
|
180
|
+
useLayoutEffect(() => {
|
|
181
|
+
if (options === null) {
|
|
182
|
+
setHighlightedIndex(null);
|
|
183
|
+
} else if (selectedIndex === null) {
|
|
184
|
+
updateSelectedIndex(0);
|
|
185
|
+
}
|
|
186
|
+
}, [options, selectedIndex, updateSelectedIndex]);
|
|
187
|
+
React.useEffect(() => {
|
|
188
|
+
return utils.mergeRegister(editor.registerCommand(lexical.KEY_ARROW_DOWN_COMMAND, payload => {
|
|
189
|
+
const event = payload;
|
|
190
|
+
|
|
191
|
+
if (options !== null && options.length && selectedIndex !== null) {
|
|
192
|
+
const newSelectedIndex = selectedIndex !== options.length - 1 ? selectedIndex + 1 : 0;
|
|
193
|
+
updateSelectedIndex(newSelectedIndex);
|
|
194
|
+
const option = options[newSelectedIndex];
|
|
195
|
+
|
|
196
|
+
if (option.ref != null && option.ref.current) {
|
|
197
|
+
scrollIntoViewIfNeeded(option.ref.current);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
event.preventDefault();
|
|
201
|
+
event.stopImmediatePropagation();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return true;
|
|
205
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ARROW_UP_COMMAND, payload => {
|
|
206
|
+
const event = payload;
|
|
207
|
+
|
|
208
|
+
if (options !== null && options.length && selectedIndex !== null) {
|
|
209
|
+
const newSelectedIndex = selectedIndex !== 0 ? selectedIndex - 1 : options.length - 1;
|
|
210
|
+
updateSelectedIndex(newSelectedIndex);
|
|
211
|
+
const option = options[newSelectedIndex];
|
|
212
|
+
|
|
213
|
+
if (option.ref != null && option.ref.current) {
|
|
214
|
+
scrollIntoViewIfNeeded(option.ref.current);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
event.preventDefault();
|
|
218
|
+
event.stopImmediatePropagation();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return true;
|
|
222
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ESCAPE_COMMAND, payload => {
|
|
223
|
+
const event = payload;
|
|
224
|
+
event.preventDefault();
|
|
225
|
+
event.stopImmediatePropagation();
|
|
226
|
+
close();
|
|
227
|
+
return true;
|
|
228
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_TAB_COMMAND, payload => {
|
|
229
|
+
const event = payload;
|
|
230
|
+
|
|
231
|
+
if (options === null || selectedIndex === null || options[selectedIndex] == null) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
event.preventDefault();
|
|
236
|
+
event.stopImmediatePropagation();
|
|
237
|
+
selectOptionAndCleanUp(options[selectedIndex]);
|
|
238
|
+
return true;
|
|
239
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ENTER_COMMAND, event => {
|
|
240
|
+
if (options === null || selectedIndex === null || options[selectedIndex] == null) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (event !== null) {
|
|
245
|
+
event.preventDefault();
|
|
246
|
+
event.stopImmediatePropagation();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
selectOptionAndCleanUp(options[selectedIndex]);
|
|
250
|
+
return true;
|
|
251
|
+
}, lexical.COMMAND_PRIORITY_LOW));
|
|
252
|
+
}, [selectOptionAndCleanUp, close, editor, options, selectedIndex, updateSelectedIndex]);
|
|
253
|
+
const listItemProps = React.useMemo(() => ({
|
|
254
|
+
selectOptionAndCleanUp,
|
|
255
|
+
selectedIndex,
|
|
256
|
+
setHighlightedIndex
|
|
257
|
+
}), [selectOptionAndCleanUp, selectedIndex]);
|
|
258
|
+
return menuRenderFn(anchorElement, listItemProps, resolution.match.matchingString);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function useAnchorElementRef(resolution, options) {
|
|
262
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
263
|
+
const anchorElementRef = React.useRef(document.createElement('div'));
|
|
264
|
+
React.useEffect(() => {
|
|
265
|
+
const rootElement = editor.getRootElement();
|
|
266
|
+
|
|
267
|
+
function positionMenu() {
|
|
268
|
+
const containerDiv = anchorElementRef.current;
|
|
269
|
+
containerDiv.setAttribute('aria-label', 'Typeahead menu');
|
|
270
|
+
containerDiv.setAttribute('id', 'typeahead-menu');
|
|
271
|
+
containerDiv.setAttribute('role', 'listbox');
|
|
272
|
+
|
|
273
|
+
if (rootElement !== null && resolution !== null) {
|
|
274
|
+
const {
|
|
275
|
+
left,
|
|
276
|
+
top,
|
|
277
|
+
height,
|
|
278
|
+
width
|
|
279
|
+
} = resolution.getRect();
|
|
280
|
+
containerDiv.style.top = `${top + height + window.pageYOffset}px`;
|
|
281
|
+
containerDiv.style.left = `${left + width + window.pageXOffset}px`;
|
|
282
|
+
containerDiv.style.display = 'block';
|
|
283
|
+
containerDiv.style.position = 'absolute';
|
|
284
|
+
|
|
285
|
+
if (!containerDiv.isConnected) {
|
|
286
|
+
document.body.append(containerDiv);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
anchorElementRef.current = containerDiv;
|
|
290
|
+
rootElement.setAttribute('aria-controls', 'typeahead-menu');
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (resolution !== null) {
|
|
295
|
+
positionMenu();
|
|
296
|
+
window.addEventListener('resize', positionMenu);
|
|
297
|
+
return () => {
|
|
298
|
+
window.removeEventListener('resize', positionMenu);
|
|
299
|
+
|
|
300
|
+
if (rootElement !== null) {
|
|
301
|
+
rootElement.removeAttribute('aria-controls');
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}, [editor, resolution, options]);
|
|
306
|
+
return anchorElementRef;
|
|
307
|
+
}
|
|
308
|
+
function LexicalNodeMenuPlugin({
|
|
309
|
+
options,
|
|
310
|
+
nodeKey,
|
|
311
|
+
onClose,
|
|
312
|
+
onSelectOption,
|
|
313
|
+
menuRenderFn
|
|
314
|
+
}) {
|
|
315
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
316
|
+
const [resolution, setResolution] = React.useState(null);
|
|
317
|
+
const anchorElementRef = useAnchorElementRef(resolution, options);
|
|
318
|
+
React.useEffect(() => {
|
|
319
|
+
if (nodeKey && resolution == null) {
|
|
320
|
+
editor.update(() => {
|
|
321
|
+
const node = lexical.$getNodeByKey(nodeKey);
|
|
322
|
+
const domElement = editor.getElementByKey(nodeKey);
|
|
323
|
+
|
|
324
|
+
if (node != null && domElement != null) {
|
|
325
|
+
const text = node.getTextContent();
|
|
326
|
+
startTransition(() => setResolution({
|
|
327
|
+
getRect: () => domElement.getBoundingClientRect(),
|
|
328
|
+
match: {
|
|
329
|
+
leadOffset: text.length,
|
|
330
|
+
matchingString: text,
|
|
331
|
+
replaceableString: text
|
|
332
|
+
}
|
|
333
|
+
}));
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
} else if (nodeKey == null && resolution != null) {
|
|
337
|
+
setResolution(null);
|
|
338
|
+
}
|
|
339
|
+
}, [editor, nodeKey, resolution]);
|
|
340
|
+
return resolution === null || editor === null ? null : /*#__PURE__*/React.createElement(LexicalPopoverMenu, {
|
|
341
|
+
close: onClose,
|
|
342
|
+
resolution: resolution,
|
|
343
|
+
editor: editor,
|
|
344
|
+
anchorElement: anchorElementRef.current,
|
|
345
|
+
options: options,
|
|
346
|
+
menuRenderFn: menuRenderFn,
|
|
347
|
+
onSelectOption: onSelectOption
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
353
|
+
*
|
|
354
|
+
* This source code is licensed under the MIT license found in the
|
|
355
|
+
* LICENSE file in the root directory of this source tree.
|
|
356
|
+
*
|
|
357
|
+
*/
|
|
358
|
+
const URL_MATCHER = /((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
|
|
359
|
+
const INSERT_EMBED_COMMAND = lexical.createCommand();
|
|
360
|
+
class AutoEmbedOption extends TypeaheadOption {
|
|
361
|
+
constructor(title, options) {
|
|
362
|
+
super(title);
|
|
363
|
+
this.title = title;
|
|
364
|
+
this.icon = options.icon;
|
|
365
|
+
this.onSelect = options.onSelect.bind(this);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
}
|
|
369
|
+
function LexicalAutoEmbedPlugin({
|
|
370
|
+
embedConfigs,
|
|
371
|
+
onOpenEmbedModalForConfig,
|
|
372
|
+
getMenuOptions,
|
|
373
|
+
menuComponent: MenuComponent
|
|
374
|
+
}) {
|
|
375
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
376
|
+
const [nodeKey, setNodeKey] = React.useState(null);
|
|
377
|
+
const [activeEmbedConfig, setActiveEmbedConfig] = React.useState(null);
|
|
378
|
+
const reset = React.useCallback(() => {
|
|
379
|
+
setNodeKey(null);
|
|
380
|
+
setActiveEmbedConfig(null);
|
|
381
|
+
}, []);
|
|
382
|
+
const checkIfLinkNodeIsEmbeddable = React.useCallback(key => {
|
|
383
|
+
editor.getEditorState().read(() => {
|
|
384
|
+
const linkNode = lexical.$getNodeByKey(key);
|
|
385
|
+
|
|
386
|
+
if (link.$isLinkNode(linkNode)) {
|
|
387
|
+
const embedConfigMatch = embedConfigs.find(embedConfig => embedConfig.parseUrl(linkNode.__url));
|
|
388
|
+
|
|
389
|
+
if (embedConfigMatch != null) {
|
|
390
|
+
setActiveEmbedConfig(embedConfigMatch);
|
|
391
|
+
setNodeKey(linkNode.getKey());
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}, [editor, embedConfigs]);
|
|
396
|
+
React.useEffect(() => {
|
|
397
|
+
const listener = (nodeMutations, {
|
|
398
|
+
updateTags,
|
|
399
|
+
dirtyLeaves
|
|
400
|
+
}) => {
|
|
401
|
+
for (const [key, mutation] of nodeMutations) {
|
|
402
|
+
if (mutation === 'created' && updateTags.has('paste') && dirtyLeaves.size === 1) {
|
|
403
|
+
checkIfLinkNodeIsEmbeddable(key);
|
|
404
|
+
} else if (key === nodeKey) {
|
|
405
|
+
reset();
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
return utils.mergeRegister(...[link.LinkNode, link.AutoLinkNode].map(Klass => editor.registerMutationListener(Klass, (...args) => listener(...args))));
|
|
411
|
+
}, [checkIfLinkNodeIsEmbeddable, editor, embedConfigs, nodeKey, reset]);
|
|
412
|
+
React.useEffect(() => {
|
|
413
|
+
return editor.registerCommand(INSERT_EMBED_COMMAND, embedConfigType => {
|
|
414
|
+
const embedConfig = embedConfigs.find(({
|
|
415
|
+
type
|
|
416
|
+
}) => type === embedConfigType);
|
|
417
|
+
|
|
418
|
+
if (embedConfig) {
|
|
419
|
+
onOpenEmbedModalForConfig(embedConfig);
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return false;
|
|
424
|
+
}, lexical.COMMAND_PRIORITY_EDITOR);
|
|
425
|
+
}, [editor, embedConfigs, onOpenEmbedModalForConfig]);
|
|
426
|
+
const embedLinkViaActiveEmbedConfig = React.useCallback(() => {
|
|
427
|
+
if (activeEmbedConfig != null && nodeKey != null) {
|
|
428
|
+
const linkNode = editor.getEditorState().read(() => {
|
|
429
|
+
const node = lexical.$getNodeByKey(nodeKey);
|
|
430
|
+
|
|
431
|
+
if (link.$isLinkNode(node)) {
|
|
432
|
+
return node;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return null;
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
if (link.$isLinkNode(linkNode)) {
|
|
439
|
+
const result = activeEmbedConfig.parseUrl(linkNode.__url);
|
|
440
|
+
|
|
441
|
+
if (result != null) {
|
|
442
|
+
editor.update(() => {
|
|
443
|
+
activeEmbedConfig.insertNode(editor, result);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
if (linkNode.isAttached()) {
|
|
447
|
+
editor.update(() => {
|
|
448
|
+
linkNode.remove();
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}, [activeEmbedConfig, editor, nodeKey]);
|
|
455
|
+
const options = React.useMemo(() => {
|
|
456
|
+
return activeEmbedConfig != null && nodeKey != null ? getMenuOptions(activeEmbedConfig, embedLinkViaActiveEmbedConfig, reset) : [];
|
|
457
|
+
}, [activeEmbedConfig, embedLinkViaActiveEmbedConfig, getMenuOptions, nodeKey, reset]);
|
|
458
|
+
const onSelectOption = React.useCallback((selectedOption, targetNode, closeMenu) => {
|
|
459
|
+
editor.update(() => {
|
|
460
|
+
selectedOption.onSelect(targetNode);
|
|
461
|
+
closeMenu();
|
|
462
|
+
});
|
|
463
|
+
}, [editor]);
|
|
464
|
+
return nodeKey != null ? /*#__PURE__*/React.createElement(LexicalNodeMenuPlugin, {
|
|
465
|
+
nodeKey: nodeKey,
|
|
466
|
+
onClose: reset,
|
|
467
|
+
onSelectOption: onSelectOption,
|
|
468
|
+
options: options,
|
|
469
|
+
menuRenderFn: (anchorElement, {
|
|
470
|
+
selectedIndex,
|
|
471
|
+
selectOptionAndCleanUp,
|
|
472
|
+
setHighlightedIndex
|
|
473
|
+
}) => anchorElement && nodeKey != null ? /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(MenuComponent, {
|
|
474
|
+
options: options,
|
|
475
|
+
selectedItemIndex: selectedIndex,
|
|
476
|
+
onOptionClick: (option, index) => {
|
|
477
|
+
setHighlightedIndex(index);
|
|
478
|
+
selectOptionAndCleanUp(option);
|
|
479
|
+
},
|
|
480
|
+
onOptionMouseEnter: index => {
|
|
481
|
+
setHighlightedIndex(index);
|
|
482
|
+
}
|
|
483
|
+
}), anchorElement) : null
|
|
484
|
+
}) : null;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
exports.AutoEmbedOption = AutoEmbedOption;
|
|
488
|
+
exports.INSERT_EMBED_COMMAND = INSERT_EMBED_COMMAND;
|
|
489
|
+
exports.LexicalAutoEmbedPlugin = LexicalAutoEmbedPlugin;
|
|
490
|
+
exports.URL_MATCHER = URL_MATCHER;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
'use strict'
|
|
8
|
+
const LexicalAutoEmbedPlugin = process.env.NODE_ENV === 'development' ? require('./LexicalAutoEmbedPlugin.dev.js') : require('./LexicalAutoEmbedPlugin.prod.js')
|
|
9
|
+
module.exports = LexicalAutoEmbedPlugin;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
'use strict';var f=require("@lexical/link"),n=require("@lexical/react/LexicalComposerContext"),z=require("@lexical/utils"),A=require("lexical"),C=require("react"),D=require("react-dom"),E="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?C.useLayoutEffect:C.useEffect;class F{constructor(b){this.key=b;this.ref={current:null};this.setRefElement=this.setRefElement.bind(this)}setRefElement(b){this.ref={current:b}}}
|
|
8
|
+
let G=b=>{var a=document.getElementById("typeahead-menu");if(a){a=a.getBoundingClientRect();const k=b.getBoundingClientRect();k.bottom>a.bottom?b.scrollIntoView(!1):k.top<a.top&&b.scrollIntoView()}};
|
|
9
|
+
function H(b,a){b=A.$getSelection();if(!A.$isRangeSelection(b)||!b.isCollapsed())return null;var k=b.anchor;if("text"!==k.type)return null;b=k.getNode();if(!b.isSimpleText())return null;k=k.offset;let q=b.getTextContent().slice(0,k);var c=a.matchingString;a=a.replaceableString.length;for(let e=a;e<=c.length;e++)q.substr(-e)===c.substr(0,e)&&(a=e);a=k-a;if(0>a)return null;let g;0===a?[g]=b.splitText(k):[,g]=b.splitText(a,k);return g}function I(b){C.startTransition?C.startTransition(b):b()}
|
|
10
|
+
function J({close:b,editor:a,anchorElement:k,resolution:q,options:c,menuRenderFn:g,onSelectOption:e}){let [d,v]=C.useState(null);C.useEffect(()=>{v(0)},[q.match.matchingString]);let p=C.useCallback(async h=>{a.update(()=>{const l=H(a,q.match);e(h,l,b,q.match.matchingString)})},[b,a,q.match,e]),t=C.useCallback(h=>{const l=a.getRootElement();null!==l&&(l.setAttribute("aria-activedescendant","typeahead-item-"+h),v(h))},[a]);C.useEffect(()=>()=>{let h=a.getRootElement();null!==h&&h.removeAttribute("aria-activedescendant")},
|
|
11
|
+
[a]);E(()=>{null===c?v(null):null===d&&t(0)},[c,d,t]);C.useEffect(()=>z.mergeRegister(a.registerCommand(A.KEY_ARROW_DOWN_COMMAND,h=>{if(null!==c&&c.length&&null!==d){var l=d!==c.length-1?d+1:0;t(l);l=c[l];null!=l.ref&&l.ref.current&&G(l.ref.current);h.preventDefault();h.stopImmediatePropagation()}return!0},A.COMMAND_PRIORITY_LOW),a.registerCommand(A.KEY_ARROW_UP_COMMAND,h=>{if(null!==c&&c.length&&null!==d){var l=0!==d?d-1:c.length-1;t(l);l=c[l];null!=l.ref&&l.ref.current&&G(l.ref.current);h.preventDefault();
|
|
12
|
+
h.stopImmediatePropagation()}return!0},A.COMMAND_PRIORITY_LOW),a.registerCommand(A.KEY_ESCAPE_COMMAND,h=>{h.preventDefault();h.stopImmediatePropagation();b();return!0},A.COMMAND_PRIORITY_LOW),a.registerCommand(A.KEY_TAB_COMMAND,h=>{if(null===c||null===d||null==c[d])return!1;h.preventDefault();h.stopImmediatePropagation();p(c[d]);return!0},A.COMMAND_PRIORITY_LOW),a.registerCommand(A.KEY_ENTER_COMMAND,h=>{if(null===c||null===d||null==c[d])return!1;null!==h&&(h.preventDefault(),h.stopImmediatePropagation());
|
|
13
|
+
p(c[d]);return!0},A.COMMAND_PRIORITY_LOW)),[p,b,a,c,d,t]);let w=C.useMemo(()=>({selectOptionAndCleanUp:p,selectedIndex:d,setHighlightedIndex:v}),[p,d]);return g(k,w,q.match.matchingString)}
|
|
14
|
+
function K(b,a){let [k]=n.useLexicalComposerContext(),q=C.useRef(document.createElement("div"));C.useEffect(()=>{function c(){let e=q.current;e.setAttribute("aria-label","Typeahead menu");e.setAttribute("id","typeahead-menu");e.setAttribute("role","listbox");if(null!==g&&null!==b){let {left:d,top:v,height:p,width:t}=b.getRect();e.style.top=`${v+p+window.pageYOffset}px`;e.style.left=`${d+t+window.pageXOffset}px`;e.style.display="block";e.style.position="absolute";e.isConnected||document.body.append(e);
|
|
15
|
+
q.current=e;g.setAttribute("aria-controls","typeahead-menu")}}let g=k.getRootElement();if(null!==b)return c(),window.addEventListener("resize",c),()=>{window.removeEventListener("resize",c);null!==g&&g.removeAttribute("aria-controls")}},[k,b,a]);return q}
|
|
16
|
+
function L({options:b,nodeKey:a,onClose:k,onSelectOption:q,menuRenderFn:c}){let [g]=n.useLexicalComposerContext(),[e,d]=C.useState(null),v=K(e,b);C.useEffect(()=>{a&&null==e?g.update(()=>{let p=A.$getNodeByKey(a),t=g.getElementByKey(a);if(null!=p&&null!=t){let w=p.getTextContent();I(()=>d({getRect:()=>t.getBoundingClientRect(),match:{leadOffset:w.length,matchingString:w,replaceableString:w}}))}}):null==a&&null!=e&&d(null)},[g,a,e]);return null===e||null===g?null:C.createElement(J,{close:k,resolution:e,
|
|
17
|
+
editor:g,anchorElement:v.current,options:b,menuRenderFn:c,onSelectOption:q})}let M=A.createCommand();class N extends F{constructor(b,a){super(b);this.title=b;this.icon=a.icon;this.onSelect=a.onSelect.bind(this)}}exports.AutoEmbedOption=N;exports.INSERT_EMBED_COMMAND=M;
|
|
18
|
+
exports.LexicalAutoEmbedPlugin=function({embedConfigs:b,onOpenEmbedModalForConfig:a,getMenuOptions:k,menuComponent:q}){let [c]=n.useLexicalComposerContext(),[g,e]=C.useState(null),[d,v]=C.useState(null),p=C.useCallback(()=>{e(null);v(null)},[]),t=C.useCallback(r=>{c.getEditorState().read(()=>{const m=A.$getNodeByKey(r);if(f.$isLinkNode(m)){const u=b.find(x=>x.parseUrl(m.__url));null!=u&&(v(u),e(m.getKey()))}})},[c,b]);C.useEffect(()=>{let r=(m,{updateTags:u,dirtyLeaves:x})=>{for(const [y,B]of m)"created"===
|
|
19
|
+
B&&u.has("paste")&&1===x.size?t(y):y===g&&p()};return z.mergeRegister(...[f.LinkNode,f.AutoLinkNode].map(m=>c.registerMutationListener(m,(...u)=>r(...u))))},[t,c,b,g,p]);C.useEffect(()=>c.registerCommand(M,r=>{let m=b.find(({type:u})=>u===r);return m?(a(m),!0):!1},A.COMMAND_PRIORITY_EDITOR),[c,b,a]);let w=C.useCallback(()=>{if(null!=d&&null!=g){const r=c.getEditorState().read(()=>{const m=A.$getNodeByKey(g);return f.$isLinkNode(m)?m:null});if(f.$isLinkNode(r)){const m=d.parseUrl(r.__url);null!=m&&
|
|
20
|
+
(c.update(()=>{d.insertNode(c,m)}),r.isAttached()&&c.update(()=>{r.remove()}))}}},[d,c,g]),h=C.useMemo(()=>null!=d&&null!=g?k(d,w,p):[],[d,w,k,g,p]),l=C.useCallback((r,m,u)=>{c.update(()=>{r.onSelect(m);u()})},[c]);return null!=g?C.createElement(L,{nodeKey:g,onClose:p,onSelectOption:l,options:h,menuRenderFn:(r,{selectedIndex:m,selectOptionAndCleanUp:u,setHighlightedIndex:x})=>r&&null!=g?D.createPortal(C.createElement(q,{options:h,selectedItemIndex:m,onOptionClick:(y,B)=>{x(B);u(y)},onOptionMouseEnter:y=>
|
|
21
|
+
{x(y)}}),r):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()@:%_+.~#?&//=]*)/
|
|
@@ -183,7 +183,7 @@ function useAutoLink(editor, matchers, onChange) {
|
|
|
183
183
|
react.useEffect(() => {
|
|
184
184
|
if (!editor.hasNodes([link.AutoLinkNode])) {
|
|
185
185
|
{
|
|
186
|
-
throw Error(`LexicalAutoLinkPlugin: AutoLinkNode
|
|
186
|
+
throw Error(`LexicalAutoLinkPlugin: AutoLinkNode not registered on editor`);
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
'use strict';var h=require("@lexical/link"),l=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
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
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 #
|
|
11
|
-
0,t=d;for(var g;(g=x(u,c))&&null!==g;){var m=g.index,n=z+m;let q=g.length;var r=void 0;r=0<n?" "===f[n-1]:y(d);n=n+q<E?" "===f[n+q]:A(d);if(r&&n){var k=void 0;0===m?[k,t]=t.splitText(q):[,k,t]=t.splitText(m,m+q);r=h.$createAutoLinkNode(g.url);r.append(v.$createTextNode(g.text));k.replace(r);b&&b(g.url,null)}m+=q;u=u.substring(m);z+=m}}k=d.getPreviousSibling();g=d.getNextSibling();d=d.getTextContent();h.$isAutoLinkNode(k)&&!d.startsWith(" ")&&(C(k),k=k.getURL(),b&&b(null,k));h.$isAutoLinkNode(g)&&
|
|
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,t=d;for(var g;(g=x(u,c))&&null!==g;){var m=g.index,n=z+m;let q=g.length;var r=void 0;r=0<n?" "===f[n-1]:y(d);n=n+q<E?" "===f[n+q]:A(d);if(r&&n){var k=void 0;0===m?[k,t]=t.splitText(q):[,k,t]=t.splitText(m,m+q);r=h.$createAutoLinkNode(g.url);r.append(v.$createTextNode(g.text));k.replace(r);b&&b(g.url,null)}m+=q;u=u.substring(m);z+=m}}k=d.getPreviousSibling();g=d.getNextSibling();d=d.getTextContent();h.$isAutoLinkNode(k)&&!d.startsWith(" ")&&(C(k),k=k.getURL(),b&&b(null,k));h.$isAutoLinkNode(g)&&
|
|
12
12
|
!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]=l.useLexicalComposerContext();D(b,a,c);return null}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
/// <reference types="react" />
|
|
9
|
+
import type { Doc } from 'yjs';
|
|
10
|
+
declare type CollaborationContextType = {
|
|
11
|
+
clientID: number;
|
|
12
|
+
color: string;
|
|
13
|
+
isCollabActive: boolean;
|
|
14
|
+
name: string;
|
|
15
|
+
yjsDocMap: Map<string, Doc>;
|
|
16
|
+
};
|
|
17
|
+
export declare const CollaborationContext: import("react").Context<CollaborationContextType>;
|
|
18
|
+
export declare function useCollaborationContext(username?: string): CollaborationContextType;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
var react = require('react');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
13
|
+
*
|
|
14
|
+
* This source code is licensed under the MIT license found in the
|
|
15
|
+
* LICENSE file in the root directory of this source tree.
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
const entries = [['Cat', '255,165,0'], ['Dog', '0,200,55'], ['Rabbit', '160,0,200'], ['Frog', '0,172,200'], ['Fox', '197,200,0'], ['Hedgehog', '31,200,0'], ['Pigeon', '200,0,0'], ['Squirrel', '200,0,148'], ['Bear', '255,235,0'], ['Tiger', '86,255,0'], ['Leopard', '0,255,208'], ['Zebra', '0,243,255'], ['Wolf', '0,102,255'], ['Owl', '147,0,255'], ['Gull', '255,0,153'], ['Squid', '0,220,255']];
|
|
19
|
+
const randomEntry = entries[Math.floor(Math.random() * entries.length)];
|
|
20
|
+
const CollaborationContext = /*#__PURE__*/react.createContext({
|
|
21
|
+
clientID: 0,
|
|
22
|
+
color: randomEntry[1],
|
|
23
|
+
isCollabActive: false,
|
|
24
|
+
name: randomEntry[0],
|
|
25
|
+
yjsDocMap: new Map()
|
|
26
|
+
});
|
|
27
|
+
function useCollaborationContext(username) {
|
|
28
|
+
const collabContext = react.useContext(CollaborationContext);
|
|
29
|
+
|
|
30
|
+
if (username != null) {
|
|
31
|
+
collabContext.name = username;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return collabContext;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
exports.CollaborationContext = CollaborationContext;
|
|
38
|
+
exports.useCollaborationContext = useCollaborationContext;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
'use strict'
|
|
8
|
+
const LexicalCollaborationContext = process.env.NODE_ENV === 'development' ? require('./LexicalCollaborationContext.dev.js') : require('./LexicalCollaborationContext.prod.js')
|
|
9
|
+
module.exports = LexicalCollaborationContext;
|