@blocklet/editor 2.4.90 → 2.4.91
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/lib/blocklet-editor-viewer.d.ts +4 -0
- package/lib/blocklet-editor-viewer.js +12 -0
- package/lib/blocklet-editor.d.ts +2 -0
- package/lib/blocklet-editor.js +12 -0
- package/lib/ext/PagesKitComponent/PagesKitComponentRenderer.js +2 -2
- package/lib/index.d.ts +0 -3
- package/lib/index.js +2 -2
- package/lib/main/index.css +0 -954
- package/lib/main/index.d.ts +3 -0
- package/lib/main/index.js +3 -0
- package/lib/main/nodes/PlaygroundNodes.js +0 -2
- package/lib/main/style/editable.css +476 -0
- package/lib/main/viewer/index.d.ts +5 -0
- package/lib/main/viewer/index.js +26 -0
- package/lib/main/viewer/types.d.ts +14 -0
- package/lib/main/viewer/types.js +1 -0
- package/lib/main/viewer/viewer.d.ts +3 -0
- package/lib/main/viewer/viewer.js +30 -0
- package/lib/types.d.ts +2 -1
- package/package.json +4 -2
- package/lib/main/Settings.d.ts +0 -9
- package/lib/main/Settings.js +0 -39
- package/lib/main/plugins/KeywordsPlugin/index.d.ts +0 -9
- package/lib/main/plugins/KeywordsPlugin/index.js +0 -38
- package/lib/main/plugins/PasteLogPlugin/index.d.ts +0 -9
- package/lib/main/plugins/PasteLogPlugin/index.js +0 -34
- package/lib/main/plugins/TestRecorderPlugin/index.d.ts +0 -10
- package/lib/main/plugins/TestRecorderPlugin/index.js +0 -340
- package/lib/main/ui/Switch.d.ts +0 -15
- package/lib/main/ui/Switch.js +0 -6
|
@@ -1,38 +0,0 @@
|
|
|
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 { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
9
|
-
import { useLexicalTextEntity } from '@lexical/react/useLexicalTextEntity';
|
|
10
|
-
import { useCallback, useEffect } from 'react';
|
|
11
|
-
import { $createKeywordNode, KeywordNode } from '../../nodes/KeywordNode';
|
|
12
|
-
const KEYWORDS_REGEX = /(^|$|[^A-Za-zªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԧԱ-Ֆՙա-ևא-תװ-ײؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠࢢ-ࢬऄ-हऽॐक़-ॡॱ-ॷॹ-ॿঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-ళవ-హఽౘౙౠౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൠൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄງຈຊຍດ-ທນ-ຟມ-ຣລວສຫອ-ະາຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤜᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎↃↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ々〆〱-〵〻〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚗꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞓꞠ-Ɦꟸ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꪀ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ])(congrats|congratulations|gratuluju|gratuluji|gratulujeme|blahopřeju|blahopřeji|blahopřejeme|Til lykke|Tillykke|Glückwunsch|Gratuliere|felicitaciones|enhorabuena|paljon onnea|onnittelut|Félicitations|gratula|gratulálok|gratulálunk|congratulazioni|complimenti|おめでとう|おめでとうございます|축하해|축하해요|gratulerer|Gefeliciteerd|gratulacje|Parabéns|parabéns|felicitações|felicitări|мои поздравления|поздравляем|поздравляю|gratulujem|blahoželám|ยินดีด้วย|ขอแสดงความยินดี|tebrikler|tebrik ederim|恭喜|祝贺你|恭喜你|恭喜|恭喜|baie geluk|veels geluk|অভিনন্দন|Čestitam|Čestitke|Čestitamo|Συγχαρητήρια|Μπράβο|અભિનંદન|badhai|बधाई|अभिनंदन|Честитам|Свака част|hongera|வாழ்த்துகள்|வாழ்த்துக்கள்|అభినందనలు|അഭിനന്ദനങ്ങൾ|Chúc mừng|מזל טוב|mazel tov|mazal tov)(^|$|[^A-Za-zªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԧԱ-Ֆՙա-ևא-תװ-ײؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠࢢ-ࢬऄ-हऽॐक़-ॡॱ-ॷॹ-ॿঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-ళవ-హఽౘౙౠౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൠൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄງຈຊຍດ-ທນ-ຟມ-ຣລວສຫອ-ະາຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤜᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎↃↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ々〆〱-〵〻〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚗꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞓꞠ-Ɦꟸ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꪀ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ])/i;
|
|
13
|
-
export default function KeywordsPlugin() {
|
|
14
|
-
const [editor] = useLexicalComposerContext();
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
if (!editor.hasNodes([KeywordNode])) {
|
|
17
|
-
throw new Error('KeywordsPlugin: KeywordNode not registered on editor');
|
|
18
|
-
}
|
|
19
|
-
}, [editor]);
|
|
20
|
-
const createKeywordNode = useCallback((textNode) => {
|
|
21
|
-
return $createKeywordNode(textNode.getTextContent());
|
|
22
|
-
}, []);
|
|
23
|
-
const getKeywordMatch = useCallback((text) => {
|
|
24
|
-
const matchArr = KEYWORDS_REGEX.exec(text);
|
|
25
|
-
if (matchArr === null) {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
const hashtagLength = matchArr[2].length;
|
|
29
|
-
const startOffset = matchArr.index + matchArr[1].length;
|
|
30
|
-
const endOffset = startOffset + hashtagLength;
|
|
31
|
-
return {
|
|
32
|
-
end: endOffset,
|
|
33
|
-
start: startOffset,
|
|
34
|
-
};
|
|
35
|
-
}, []);
|
|
36
|
-
useLexicalTextEntity(getKeywordMatch, KeywordNode, createKeywordNode);
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
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 JSX } from 'react';
|
|
9
|
-
export default function PasteLogPlugin(): JSX.Element;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*
|
|
8
|
-
*/
|
|
9
|
-
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
10
|
-
import { COMMAND_PRIORITY_NORMAL, PASTE_COMMAND } from 'lexical';
|
|
11
|
-
import { useEffect, useState } from 'react';
|
|
12
|
-
export default function PasteLogPlugin() {
|
|
13
|
-
const [editor] = useLexicalComposerContext();
|
|
14
|
-
const [isActive, setIsActive] = useState(false);
|
|
15
|
-
const [lastClipboardData, setLastClipboardData] = useState(null);
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
if (isActive) {
|
|
18
|
-
return editor.registerCommand(PASTE_COMMAND, (e) => {
|
|
19
|
-
const { clipboardData } = e;
|
|
20
|
-
const allData = [];
|
|
21
|
-
if (clipboardData && clipboardData.types) {
|
|
22
|
-
clipboardData.types.forEach((type) => {
|
|
23
|
-
allData.push(type.toUpperCase(), clipboardData.getData(type));
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
setLastClipboardData(allData.join('\n\n'));
|
|
27
|
-
return false;
|
|
28
|
-
}, COMMAND_PRIORITY_NORMAL);
|
|
29
|
-
}
|
|
30
|
-
}, [editor, isActive]);
|
|
31
|
-
return (_jsxs(_Fragment, { children: [_jsx("button", { id: "paste-log-button", className: `editor-dev-button ${isActive ? 'active' : ''}`, onClick: () => {
|
|
32
|
-
setIsActive(!isActive);
|
|
33
|
-
}, title: isActive ? 'Disable paste log' : 'Enable paste log' }), isActive && lastClipboardData !== null ? _jsx("pre", { children: lastClipboardData }) : null] }));
|
|
34
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
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 JSX } from 'react';
|
|
9
|
-
export declare function isSelectAll(event: KeyboardEvent): boolean;
|
|
10
|
-
export default function TestRecorderPlugin(): JSX.Element;
|
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
3
|
-
import { $createParagraphNode, $createTextNode, $getRoot } from 'lexical';
|
|
4
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
5
|
-
import { IS_APPLE } from '../../../shared/environment';
|
|
6
|
-
import useLayoutEffect from '../../../shared/useLayoutEffect';
|
|
7
|
-
const copy = (text) => {
|
|
8
|
-
const textArea = document.createElement('textarea');
|
|
9
|
-
textArea.value = text || '';
|
|
10
|
-
textArea.style.position = 'absolute';
|
|
11
|
-
textArea.style.opacity = '0';
|
|
12
|
-
document.body?.appendChild(textArea);
|
|
13
|
-
textArea.focus();
|
|
14
|
-
textArea.select();
|
|
15
|
-
try {
|
|
16
|
-
const result = document.execCommand('copy');
|
|
17
|
-
// eslint-disable-next-line no-console
|
|
18
|
-
console.log(result);
|
|
19
|
-
}
|
|
20
|
-
catch (error) {
|
|
21
|
-
console.error(error);
|
|
22
|
-
}
|
|
23
|
-
document.body?.removeChild(textArea);
|
|
24
|
-
};
|
|
25
|
-
const download = (filename, text) => {
|
|
26
|
-
const a = document.createElement('a');
|
|
27
|
-
a.setAttribute('href', `data:text/plain;charset=utf-8,${encodeURIComponent(text || '')}`);
|
|
28
|
-
a.setAttribute('download', filename);
|
|
29
|
-
a.style.display = 'none';
|
|
30
|
-
document.body?.appendChild(a);
|
|
31
|
-
a.click();
|
|
32
|
-
document.body?.removeChild(a);
|
|
33
|
-
};
|
|
34
|
-
const formatStep = (step) => {
|
|
35
|
-
const formatOneStep = (name, value) => {
|
|
36
|
-
switch (name) {
|
|
37
|
-
case 'click': {
|
|
38
|
-
return ` await page.mouse.click(${value.x}, ${value.y});`;
|
|
39
|
-
}
|
|
40
|
-
case 'press': {
|
|
41
|
-
return ` await page.keyboard.press('${value}');`;
|
|
42
|
-
}
|
|
43
|
-
case 'keydown': {
|
|
44
|
-
return ` await page.keyboard.keydown('${value}');`;
|
|
45
|
-
}
|
|
46
|
-
case 'keyup': {
|
|
47
|
-
return ` await page.keyboard.keyup('${value}');`;
|
|
48
|
-
}
|
|
49
|
-
case 'type': {
|
|
50
|
-
return ` await page.keyboard.type('${value}');`;
|
|
51
|
-
}
|
|
52
|
-
case 'selectAll': {
|
|
53
|
-
return ' await selectAll(page);';
|
|
54
|
-
}
|
|
55
|
-
case 'snapshot': {
|
|
56
|
-
return ` await assertHTMLSnapshot(page);
|
|
57
|
-
await assertSelection(page, {
|
|
58
|
-
anchorPath: [${value.anchorPath.toString()}],
|
|
59
|
-
anchorOffset: ${value.anchorOffset},
|
|
60
|
-
focusPath: [${value.focusPath.toString()}],
|
|
61
|
-
focusOffset: ${value.focusOffset},
|
|
62
|
-
});
|
|
63
|
-
`;
|
|
64
|
-
}
|
|
65
|
-
default:
|
|
66
|
-
return '';
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
const formattedStep = formatOneStep(step.name, step.value);
|
|
70
|
-
switch (step.count) {
|
|
71
|
-
case 1:
|
|
72
|
-
return formattedStep;
|
|
73
|
-
case 2:
|
|
74
|
-
return [formattedStep, formattedStep].join('\n');
|
|
75
|
-
default:
|
|
76
|
-
return ` await repeat(${step.count}, async () => {
|
|
77
|
-
${formattedStep}
|
|
78
|
-
);`;
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
export function isSelectAll(event) {
|
|
82
|
-
return event.keyCode === 65 && (IS_APPLE ? event.metaKey : event.ctrlKey);
|
|
83
|
-
}
|
|
84
|
-
// stolen from LexicalSelection-test
|
|
85
|
-
function sanitizeSelection(selection) {
|
|
86
|
-
const { anchorNode, focusNode } = selection;
|
|
87
|
-
let { anchorOffset, focusOffset } = selection;
|
|
88
|
-
if (anchorOffset !== 0) {
|
|
89
|
-
anchorOffset--;
|
|
90
|
-
}
|
|
91
|
-
if (focusOffset !== 0) {
|
|
92
|
-
focusOffset--;
|
|
93
|
-
}
|
|
94
|
-
return { anchorNode, anchorOffset, focusNode, focusOffset };
|
|
95
|
-
}
|
|
96
|
-
function getPathFromNodeToEditor(node, rootElement) {
|
|
97
|
-
let currentNode = node;
|
|
98
|
-
const path = [];
|
|
99
|
-
while (currentNode !== rootElement) {
|
|
100
|
-
if (currentNode !== null && currentNode !== undefined) {
|
|
101
|
-
path.unshift(Array.from(currentNode?.parentNode?.childNodes ?? []).indexOf(currentNode));
|
|
102
|
-
}
|
|
103
|
-
currentNode = currentNode?.parentNode;
|
|
104
|
-
}
|
|
105
|
-
return path;
|
|
106
|
-
}
|
|
107
|
-
const keyPresses = new Set([
|
|
108
|
-
'Enter',
|
|
109
|
-
'Backspace',
|
|
110
|
-
'Delete',
|
|
111
|
-
'Escape',
|
|
112
|
-
'ArrowLeft',
|
|
113
|
-
'ArrowRight',
|
|
114
|
-
'ArrowUp',
|
|
115
|
-
'ArrowDown',
|
|
116
|
-
]);
|
|
117
|
-
function useTestRecorder(editor) {
|
|
118
|
-
const [steps, setSteps] = useState([]);
|
|
119
|
-
const [isRecording, setIsRecording] = useState(false);
|
|
120
|
-
const [, setCurrentInnerHTML] = useState('');
|
|
121
|
-
const [templatedTest, setTemplatedTest] = useState('');
|
|
122
|
-
const previousSelectionRef = useRef(null);
|
|
123
|
-
const skipNextSelectionChangeRef = useRef(false);
|
|
124
|
-
const preRef = useRef(null);
|
|
125
|
-
const getCurrentEditor = useCallback(() => {
|
|
126
|
-
return editor;
|
|
127
|
-
}, [editor]);
|
|
128
|
-
const generateTestContent = useCallback(() => {
|
|
129
|
-
const rootElement = editor.getRootElement();
|
|
130
|
-
const browserSelection = window.getSelection();
|
|
131
|
-
if (rootElement == null ||
|
|
132
|
-
browserSelection == null ||
|
|
133
|
-
browserSelection.anchorNode == null ||
|
|
134
|
-
browserSelection.focusNode == null ||
|
|
135
|
-
!rootElement.contains(browserSelection.anchorNode) ||
|
|
136
|
-
!rootElement.contains(browserSelection.focusNode)) {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
return `
|
|
140
|
-
/**
|
|
141
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
142
|
-
*
|
|
143
|
-
* This source code is licensed under the MIT license found in the
|
|
144
|
-
* LICENSE file in the root directory of this source tree.
|
|
145
|
-
*
|
|
146
|
-
*/
|
|
147
|
-
|
|
148
|
-
import {
|
|
149
|
-
initializeE2E,
|
|
150
|
-
assertHTMLSnapshot,
|
|
151
|
-
assertSelection,
|
|
152
|
-
repeat,
|
|
153
|
-
} from '../utils';
|
|
154
|
-
import {selectAll} from '../keyboardShortcuts';
|
|
155
|
-
import { RangeSelection } from 'lexical';
|
|
156
|
-
import { NodeSelection } from 'lexical';
|
|
157
|
-
|
|
158
|
-
describe('Test case', () => {
|
|
159
|
-
initializeE2E((e2e) => {
|
|
160
|
-
it('Should pass this test', async () => {
|
|
161
|
-
const {page} = e2e;
|
|
162
|
-
|
|
163
|
-
await page.focus('div[contenteditable="true"]');
|
|
164
|
-
${steps.map(formatStep).join('\n')}
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
`;
|
|
168
|
-
}, [editor, steps]);
|
|
169
|
-
// just a wrapper around inserting new actions so that we can
|
|
170
|
-
// coalesce some actions like insertText/moveNativeSelection
|
|
171
|
-
const pushStep = useCallback((name, value) => {
|
|
172
|
-
setSteps((currentSteps) => {
|
|
173
|
-
// trying to group steps
|
|
174
|
-
const currentIndex = steps.length - 1;
|
|
175
|
-
const lastStep = steps[currentIndex];
|
|
176
|
-
if (lastStep) {
|
|
177
|
-
if (lastStep.name === name) {
|
|
178
|
-
if (name === 'type') {
|
|
179
|
-
// for typing events we just append the text
|
|
180
|
-
return [...steps.slice(0, currentIndex), { ...lastStep, value: lastStep.value + value }];
|
|
181
|
-
}
|
|
182
|
-
// for other events we bump the counter if their values are the same
|
|
183
|
-
if (lastStep.value === value) {
|
|
184
|
-
return [...steps.slice(0, currentIndex), { ...lastStep, count: lastStep.count + 1 }];
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
// could not group, just append a new one
|
|
189
|
-
return [...currentSteps, { count: 1, name, value }];
|
|
190
|
-
});
|
|
191
|
-
}, [steps, setSteps]);
|
|
192
|
-
useLayoutEffect(() => {
|
|
193
|
-
const onKeyDown = (event) => {
|
|
194
|
-
if (!isRecording) {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
const { key } = event;
|
|
198
|
-
if (isSelectAll(event)) {
|
|
199
|
-
pushStep('selectAll', '');
|
|
200
|
-
}
|
|
201
|
-
else if (keyPresses.has(key)) {
|
|
202
|
-
pushStep('press', event.key);
|
|
203
|
-
}
|
|
204
|
-
else if ([...key].length > 1) {
|
|
205
|
-
pushStep('keydown', event.key);
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
pushStep('type', event.key);
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
const onKeyUp = (event) => {
|
|
212
|
-
if (!isRecording) {
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
const { key } = event;
|
|
216
|
-
if (!keyPresses.has(key) && [...key].length > 1) {
|
|
217
|
-
pushStep('keyup', event.key);
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
return editor.registerRootListener((rootElement, prevRootElement) => {
|
|
221
|
-
if (prevRootElement !== null) {
|
|
222
|
-
prevRootElement.removeEventListener('keydown', onKeyDown);
|
|
223
|
-
prevRootElement.removeEventListener('keyup', onKeyUp);
|
|
224
|
-
}
|
|
225
|
-
if (rootElement !== null) {
|
|
226
|
-
rootElement.addEventListener('keydown', onKeyDown);
|
|
227
|
-
rootElement.addEventListener('keyup', onKeyUp);
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
}, [editor, isRecording, pushStep]);
|
|
231
|
-
useLayoutEffect(() => {
|
|
232
|
-
if (preRef.current) {
|
|
233
|
-
preRef.current.scrollTo(0, preRef.current.scrollHeight);
|
|
234
|
-
}
|
|
235
|
-
}, [generateTestContent]);
|
|
236
|
-
useEffect(() => {
|
|
237
|
-
if (steps) {
|
|
238
|
-
const testContent = generateTestContent();
|
|
239
|
-
if (testContent !== null) {
|
|
240
|
-
setTemplatedTest(testContent);
|
|
241
|
-
}
|
|
242
|
-
if (preRef.current) {
|
|
243
|
-
preRef.current.scrollTo(0, preRef.current.scrollHeight);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}, [generateTestContent, steps]);
|
|
247
|
-
useEffect(() => {
|
|
248
|
-
const removeUpdateListener = editor.registerUpdateListener(({ editorState, dirtyLeaves, dirtyElements }) => {
|
|
249
|
-
if (!isRecording) {
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
const currentSelection = editorState._selection;
|
|
253
|
-
const previousSelection = previousSelectionRef.current;
|
|
254
|
-
const skipNextSelectionChange = skipNextSelectionChangeRef.current;
|
|
255
|
-
if (previousSelection !== currentSelection) {
|
|
256
|
-
if (dirtyLeaves.size === 0 && dirtyElements.size === 0 && !skipNextSelectionChange) {
|
|
257
|
-
const browserSelection = window.getSelection();
|
|
258
|
-
if (browserSelection && (browserSelection.anchorNode == null || browserSelection.focusNode == null)) {
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
previousSelectionRef.current = currentSelection;
|
|
263
|
-
}
|
|
264
|
-
skipNextSelectionChangeRef.current = false;
|
|
265
|
-
const testContent = generateTestContent();
|
|
266
|
-
if (testContent !== null) {
|
|
267
|
-
setTemplatedTest(testContent);
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
return removeUpdateListener;
|
|
271
|
-
}, [editor, generateTestContent, isRecording, pushStep]);
|
|
272
|
-
// save innerHTML
|
|
273
|
-
useEffect(() => {
|
|
274
|
-
if (!isRecording) {
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
const removeUpdateListener = editor.registerUpdateListener(() => {
|
|
278
|
-
const rootElement = editor.getRootElement();
|
|
279
|
-
if (rootElement !== null) {
|
|
280
|
-
setCurrentInnerHTML(rootElement?.innerHTML);
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
return removeUpdateListener;
|
|
284
|
-
}, [editor, isRecording]);
|
|
285
|
-
// clear editor and start recording
|
|
286
|
-
const toggleEditorSelection = useCallback((currentEditor) => {
|
|
287
|
-
if (!isRecording) {
|
|
288
|
-
currentEditor.update(() => {
|
|
289
|
-
const root = $getRoot();
|
|
290
|
-
root.clear();
|
|
291
|
-
const text = $createTextNode();
|
|
292
|
-
root.append($createParagraphNode().append(text));
|
|
293
|
-
text.select();
|
|
294
|
-
});
|
|
295
|
-
setSteps([]);
|
|
296
|
-
}
|
|
297
|
-
setIsRecording((currentIsRecording) => !currentIsRecording);
|
|
298
|
-
}, [isRecording]);
|
|
299
|
-
const onSnapshotClick = useCallback(() => {
|
|
300
|
-
if (!isRecording) {
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
const browserSelection = window.getSelection();
|
|
304
|
-
if (browserSelection === null || browserSelection.anchorNode == null || browserSelection.focusNode == null) {
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
const { anchorNode, anchorOffset, focusNode, focusOffset } = sanitizeSelection(browserSelection);
|
|
308
|
-
const rootElement = getCurrentEditor().getRootElement();
|
|
309
|
-
let anchorPath;
|
|
310
|
-
if (anchorNode !== null) {
|
|
311
|
-
anchorPath = getPathFromNodeToEditor(anchorNode, rootElement);
|
|
312
|
-
}
|
|
313
|
-
let focusPath;
|
|
314
|
-
if (focusNode !== null) {
|
|
315
|
-
focusPath = getPathFromNodeToEditor(focusNode, rootElement);
|
|
316
|
-
}
|
|
317
|
-
pushStep('snapshot', {
|
|
318
|
-
anchorNode,
|
|
319
|
-
anchorOffset,
|
|
320
|
-
anchorPath,
|
|
321
|
-
focusNode,
|
|
322
|
-
focusOffset,
|
|
323
|
-
focusPath,
|
|
324
|
-
});
|
|
325
|
-
}, [pushStep, isRecording, getCurrentEditor]);
|
|
326
|
-
const onCopyClick = useCallback(() => {
|
|
327
|
-
copy(generateTestContent());
|
|
328
|
-
}, [generateTestContent]);
|
|
329
|
-
const onDownloadClick = useCallback(() => {
|
|
330
|
-
download('test.js', generateTestContent());
|
|
331
|
-
}, [generateTestContent]);
|
|
332
|
-
const button = (_jsx("button", { id: "test-recorder-button", className: `editor-dev-button ${isRecording ? 'active' : ''}`, onClick: () => toggleEditorSelection(getCurrentEditor()), title: isRecording ? 'Disable test recorder' : 'Enable test recorder' }));
|
|
333
|
-
const output = isRecording ? (_jsxs("div", { className: "test-recorder-output", children: [_jsxs("div", { className: "test-recorder-toolbar", children: [_jsx("button", { className: "test-recorder-button", id: "test-recorder-button-snapshot", title: "Insert snapshot", onClick: onSnapshotClick }), _jsx("button", { className: "test-recorder-button", id: "test-recorder-button-copy", title: "Copy to clipboard", onClick: onCopyClick }), _jsx("button", { className: "test-recorder-button", id: "test-recorder-button-download", title: "Download as a file", onClick: onDownloadClick })] }), _jsx("pre", { id: "test-recorder", ref: preRef, children: templatedTest })] })) : null;
|
|
334
|
-
return [button, output];
|
|
335
|
-
}
|
|
336
|
-
export default function TestRecorderPlugin() {
|
|
337
|
-
const [editor] = useLexicalComposerContext();
|
|
338
|
-
const [testRecorderButton, testRecorderOutput] = useTestRecorder(editor);
|
|
339
|
-
return (_jsxs(_Fragment, { children: [testRecorderButton, testRecorderOutput] }));
|
|
340
|
-
}
|
package/lib/main/ui/Switch.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
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 * as React from 'react';
|
|
9
|
-
import { type JSX } from 'react';
|
|
10
|
-
export default function Switch({ checked, onClick, text, id, }: Readonly<{
|
|
11
|
-
checked: boolean;
|
|
12
|
-
id?: string;
|
|
13
|
-
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
|
14
|
-
text: string;
|
|
15
|
-
}>): JSX.Element;
|
package/lib/main/ui/Switch.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
|
-
export default function Switch({ checked, onClick, text, id, }) {
|
|
4
|
-
const buttonId = useMemo(() => `id_${Math.floor(Math.random() * 10000)}`, []);
|
|
5
|
-
return (_jsxs("div", { className: "switch", id: id, children: [_jsx("label", { htmlFor: buttonId, children: text }), _jsx("button", { role: "switch", "aria-checked": checked, id: buttonId, onClick: onClick, children: _jsx("span", {}) })] }));
|
|
6
|
-
}
|