@blocklet/editor 1.6.195 → 1.6.197
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.
|
@@ -4,7 +4,10 @@ import { usePopper } from 'react-popper';
|
|
|
4
4
|
import { useState } from 'react';
|
|
5
5
|
const CreatePopperElement = ({ portalElement, parentElement, }) => {
|
|
6
6
|
const [popperElement, setPopperElement] = useState(null);
|
|
7
|
-
const { styles, attributes } = usePopper(parentElement, popperElement, {
|
|
7
|
+
const { styles, attributes } = usePopper(parentElement, popperElement, {
|
|
8
|
+
placement: 'bottom-start',
|
|
9
|
+
strategy: 'fixed',
|
|
10
|
+
});
|
|
8
11
|
return (_jsx("div", { ref: setPopperElement, style: { ...styles.popper, zIndex: 1 }, ...attributes.popper, children: portalElement }));
|
|
9
12
|
};
|
|
10
13
|
const createPortal = (portalElement, parentElement) => {
|
|
@@ -9,7 +9,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
9
9
|
import { $getSelection, $isRangeSelection, $createTextNode, COMMAND_PRIORITY_EDITOR, KEY_DOWN_COMMAND, } from 'lexical';
|
|
10
10
|
import { AutoEmbedOption, URL_MATCHER, LexicalAutoEmbedPlugin, } from '@lexical/react/LexicalAutoEmbedPlugin';
|
|
11
11
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
12
|
-
import { useMemo, useState, useEffect } from 'react';
|
|
12
|
+
import { useMemo, useState, useEffect, useRef } from 'react';
|
|
13
13
|
import { $getNearestNodeOfType } from '@lexical/utils';
|
|
14
14
|
import { LinkNode, $createLinkNode } from '@lexical/link';
|
|
15
15
|
import compact from 'lodash/compact';
|
|
@@ -24,11 +24,11 @@ import { createBookmarkOption } from '../../../ext/BookmarkPlugin';
|
|
|
24
24
|
import { get as getFromUrlParseCache, wrap } from './urlParseCache';
|
|
25
25
|
import { BilibiliEmbedConfig } from '../../../ext/BilibiliPlugin/config';
|
|
26
26
|
import { createPostLinkAutoEmbedOption } from '../../../ext/PostLinkEmbedPlugin';
|
|
27
|
-
import createPortal from '../../../components/createPortal';
|
|
28
27
|
import { $createGithubNode } from '../../../ext/nodes/GithubNode';
|
|
29
28
|
import { $createEmojiNode } from '../../nodes/EmojiNode';
|
|
30
29
|
import { useEditorConfig } from '../../../config';
|
|
31
30
|
import { fetchOpenGraphInfo } from '../../../ext/utils';
|
|
31
|
+
import createPortal from '../../../components/createPortal';
|
|
32
32
|
export const YoutubeEmbedConfig = {
|
|
33
33
|
contentName: 'Youtube Video',
|
|
34
34
|
exampleUrl: 'https://www.youtube.com/watch?v=jNQXAC9IVRw',
|
|
@@ -206,10 +206,19 @@ export default function AutoEmbedPlugin() {
|
|
|
206
206
|
const [editor] = useLexicalComposerContext();
|
|
207
207
|
const [modal, showModal] = useModal();
|
|
208
208
|
const editorConfig = useEditorConfig();
|
|
209
|
+
const dismissFnRef = useRef(null);
|
|
209
210
|
const openEmbedModal = (embedConfig) => {
|
|
210
211
|
showModal(`Embed ${embedConfig.contentName}`, (onClose) => (_jsx(AutoEmbedDialog, { embedConfig: embedConfig, onClose: onClose })));
|
|
211
212
|
};
|
|
213
|
+
// Dismiss the menu when moving the cursor
|
|
214
|
+
// (https://community.arcblock.io/discussions/29f1b7a4-2af6-407f-8653-1267067d8899)
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
return editor.registerUpdateListener(() => {
|
|
217
|
+
dismissFnRef.current?.();
|
|
218
|
+
});
|
|
219
|
+
}, [editor]);
|
|
212
220
|
const getMenuOptions = (activeEmbedConfig, embedFn, dismissFn) => {
|
|
221
|
+
dismissFnRef.current = dismissFn;
|
|
213
222
|
return compact([
|
|
214
223
|
new AutoEmbedOption('Dismiss', {
|
|
215
224
|
onSelect: dismissFn,
|
|
@@ -314,15 +323,17 @@ export default function AutoEmbedPlugin() {
|
|
|
314
323
|
return false;
|
|
315
324
|
}, COMMAND_PRIORITY_EDITOR);
|
|
316
325
|
}, [editor]);
|
|
317
|
-
return (_jsxs(_Fragment, { children: [modal, _jsx(LexicalAutoEmbedPlugin, { embedConfigs: EmbedConfigs, onOpenEmbedModalForConfig: openEmbedModal, getMenuOptions: getMenuOptions, menuRenderFn: (anchorElementRef, { selectedIndex, options, selectOptionAndCleanUp, setHighlightedIndex }) =>
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
326
|
+
return (_jsxs(_Fragment, { children: [modal, _jsx(LexicalAutoEmbedPlugin, { embedConfigs: EmbedConfigs, onOpenEmbedModalForConfig: openEmbedModal, getMenuOptions: getMenuOptions, menuRenderFn: (anchorElementRef, { selectedIndex, options, selectOptionAndCleanUp, setHighlightedIndex }) => {
|
|
327
|
+
return anchorElementRef.current
|
|
328
|
+
? createPortal(_jsx("div", { className: "typeahead-popover auto-embed-menu", style: {
|
|
329
|
+
marginLeft: `${Math.max(parseFloat(anchorElementRef.current.style.width) - 200, 0)}px`,
|
|
330
|
+
width: 200,
|
|
331
|
+
}, children: _jsx(AutoEmbedMenu, { options: options, selectedItemIndex: selectedIndex, onOptionClick: (option, index) => {
|
|
332
|
+
setHighlightedIndex(index);
|
|
333
|
+
selectOptionAndCleanUp(option);
|
|
334
|
+
}, onOptionMouseEnter: (index) => {
|
|
335
|
+
setHighlightedIndex(index);
|
|
336
|
+
} }) }), anchorElementRef.current)
|
|
337
|
+
: null;
|
|
338
|
+
} })] }));
|
|
328
339
|
}
|
|
@@ -8,22 +8,21 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
10
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
11
|
-
import {
|
|
11
|
+
import { LexicalTypeaheadMenuPlugin, useBasicTypeaheadTriggerMatch, } from '@lexical/react/LexicalTypeaheadMenuPlugin';
|
|
12
12
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
13
13
|
import debounce from 'lodash/debounce';
|
|
14
|
-
import { LexicalTypeaheadMenuPlugin, TypeaheadOption, useBasicTypeaheadTriggerMatch, } from '../LexicalTypeaheadMenuPlugin';
|
|
15
14
|
import { $createMentionNode } from '../../nodes/MentionNode';
|
|
16
15
|
import { useEditorConfig } from '../../../config';
|
|
17
16
|
import HighlightStyle from './HighlightStyle';
|
|
18
|
-
import createPortal from '../../../components/createPortal';
|
|
19
17
|
import sortArrayByKey from '../../../libs/sort-array-by-key';
|
|
18
|
+
import { TypeaheadOption } from '../LexicalTypeaheadMenuPlugin';
|
|
19
|
+
import createPortal from '../../../components/createPortal';
|
|
20
20
|
const PUNCTUATION = '\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%\'"~=<>_:;';
|
|
21
21
|
const NAME = `\\b[A-Z][^\\s${PUNCTUATION}]`;
|
|
22
22
|
const DocumentMentionsRegex = {
|
|
23
23
|
NAME,
|
|
24
24
|
PUNCTUATION,
|
|
25
25
|
};
|
|
26
|
-
const CapitalizedNameMentionsRegex = new RegExp(`(^|[^#])((?:${DocumentMentionsRegex.NAME}{${1},})$)`);
|
|
27
26
|
const PUNC = DocumentMentionsRegex.PUNCTUATION;
|
|
28
27
|
const TRIGGERS = ['@'].join('');
|
|
29
28
|
// Chars we expect to see in a mention (non-space, non-punctuation).
|
|
@@ -75,23 +74,6 @@ function useMentionLookupService(mentionString) {
|
|
|
75
74
|
}, [mentionString]);
|
|
76
75
|
return results;
|
|
77
76
|
}
|
|
78
|
-
function checkForCapitalizedNameMentions(text, minMatchLength) {
|
|
79
|
-
const match = CapitalizedNameMentionsRegex.exec(text);
|
|
80
|
-
if (match !== null) {
|
|
81
|
-
// The strategy ignores leading whitespace but we need to know it's
|
|
82
|
-
// length to add it to the leadOffset
|
|
83
|
-
const maybeLeadingWhitespace = match[1];
|
|
84
|
-
const matchingString = match[2];
|
|
85
|
-
if (matchingString != null && matchingString.length >= minMatchLength) {
|
|
86
|
-
return {
|
|
87
|
-
leadOffset: match.index + maybeLeadingWhitespace.length,
|
|
88
|
-
matchingString,
|
|
89
|
-
replaceableString: matchingString,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
77
|
function checkForAtSignMentions(text, minMatchLength) {
|
|
96
78
|
// 输入 @ 符号立即显示用户列表 (#383)
|
|
97
79
|
if (/([\s]|^)@$/.test(text)) {
|
|
@@ -121,10 +103,7 @@ function checkForAtSignMentions(text, minMatchLength) {
|
|
|
121
103
|
return null;
|
|
122
104
|
}
|
|
123
105
|
function getPossibleQueryMatch(text) {
|
|
124
|
-
|
|
125
|
-
// 禁用 "连续输入 (4 个?) 大写字母触发 mention"
|
|
126
|
-
// return match === null ? checkForCapitalizedNameMentions(text, 3) : match;
|
|
127
|
-
return match;
|
|
106
|
+
return checkForAtSignMentions(text, 1);
|
|
128
107
|
}
|
|
129
108
|
class MentionTypeaheadOption extends TypeaheadOption {
|
|
130
109
|
user;
|
|
@@ -155,27 +134,28 @@ export default function NewMentionsPlugin() {
|
|
|
155
134
|
nodeToReplace.replace(mentionNode);
|
|
156
135
|
}
|
|
157
136
|
mentionNode.select();
|
|
158
|
-
const textNode = $createTextNode(' ');
|
|
159
|
-
const selection = $getSelection();
|
|
160
|
-
selection?.insertNodes([textNode]);
|
|
161
137
|
closeMenu();
|
|
162
138
|
});
|
|
163
139
|
}, [editor]);
|
|
164
140
|
const checkForMentionMatch = useCallback((text) => {
|
|
165
|
-
const mentionMatch = getPossibleQueryMatch(text);
|
|
166
141
|
const slashMatch = checkForSlashTriggerMatch(text, editor);
|
|
167
|
-
|
|
142
|
+
if (slashMatch !== null) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
return getPossibleQueryMatch(text);
|
|
168
146
|
}, [checkForSlashTriggerMatch, editor]);
|
|
169
|
-
return (_jsxs(_Fragment, { children: [_jsx(LexicalTypeaheadMenuPlugin, { onQueryChange: setQueryString, onSelectOption: onSelectOption, triggerFn: checkForMentionMatch, options: options, menuRenderFn: (anchorElementRef, { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }) =>
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
option
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
147
|
+
return (_jsxs(_Fragment, { children: [_jsx(LexicalTypeaheadMenuPlugin, { onQueryChange: setQueryString, onSelectOption: onSelectOption, triggerFn: checkForMentionMatch, options: options, menuRenderFn: (anchorElementRef, { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }) => {
|
|
148
|
+
return anchorElementRef.current && results.length
|
|
149
|
+
? createPortal(_jsx("div", { className: "typeahead-popover mentions-menu", children: _jsx("ul", { children: options.map((option, i) => (_jsx(MentionsTypeaheadMenuItem, { index: i, isSelected: selectedIndex === i, onClick: () => {
|
|
150
|
+
setHighlightedIndex(i);
|
|
151
|
+
selectOptionAndCleanUp(option);
|
|
152
|
+
}, onMouseEnter: () => {
|
|
153
|
+
setHighlightedIndex(i);
|
|
154
|
+
// hover option 时显示用户 did
|
|
155
|
+
if (option?.ref?.current) {
|
|
156
|
+
option.ref.current.title = option.user.did;
|
|
157
|
+
}
|
|
158
|
+
}, option: option }, option.key))) }) }), anchorElementRef.current)
|
|
159
|
+
: null;
|
|
160
|
+
} }), _jsx(HighlightStyle, {})] }));
|
|
181
161
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/editor",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.197",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "npm run storybook",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
]
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@arcblock/ux": "^2.9.
|
|
40
|
+
"@arcblock/ux": "^2.9.63",
|
|
41
41
|
"@blocklet/embed": "^0.1.11",
|
|
42
|
-
"@blocklet/pdf": "1.6.
|
|
42
|
+
"@blocklet/pdf": "1.6.197",
|
|
43
43
|
"@excalidraw/excalidraw": "^0.14.2",
|
|
44
44
|
"@iconify/iconify": "^3.0.1",
|
|
45
45
|
"@lexical/clipboard": "0.13.1",
|
|
@@ -108,5 +108,5 @@
|
|
|
108
108
|
"react": "*",
|
|
109
109
|
"react-dom": "*"
|
|
110
110
|
},
|
|
111
|
-
"gitHead": "
|
|
111
|
+
"gitHead": "0dfec64ec8d8047b70efdffe51b2ded92fbe796f"
|
|
112
112
|
}
|