@blocklet/editor 2.1.128 → 2.1.129
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/ext/InlineTranslationPlugin/EditorTranslator.d.ts +1 -0
- package/lib/ext/InlineTranslationPlugin/EditorTranslator.js +3 -0
- package/lib/ext/InlineTranslationPlugin/InlineTranslationPlugin.js +65 -30
- package/lib/ext/InlineTranslationPlugin/TranslationNode.js +1 -0
- package/package.json +2 -2
|
@@ -36,5 +36,6 @@ export declare class EditorTranslator {
|
|
|
36
36
|
restore(sid?: string): void;
|
|
37
37
|
preprocessNodes(): Promise<void>;
|
|
38
38
|
getOriginalText(sid: string): string | undefined;
|
|
39
|
+
getTranslationText(sid: string): string | undefined;
|
|
39
40
|
}
|
|
40
41
|
export declare function useEditorTranslator(options: EditorTranslatorOptions): EditorTranslator;
|
|
@@ -175,6 +175,9 @@ export class EditorTranslator {
|
|
|
175
175
|
getOriginalText(sid) {
|
|
176
176
|
return this.sourceItems.find((item) => item.sid === sid)?.text;
|
|
177
177
|
}
|
|
178
|
+
getTranslationText(sid) {
|
|
179
|
+
return this.translations.find((item) => item.sid === sid)?.text;
|
|
180
|
+
}
|
|
178
181
|
}
|
|
179
182
|
export function useEditorTranslator(options) {
|
|
180
183
|
const [editor] = useLexicalComposerContext();
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
|
-
import { Box, Stack } from '@mui/material';
|
|
3
|
+
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
4
|
+
import { Box, ClickAwayListener, Stack } from '@mui/material';
|
|
5
5
|
import { useInViewport, usePrevious } from 'ahooks';
|
|
6
6
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
7
7
|
import { usePopper } from 'react-popper';
|
|
8
|
+
import { createPortal } from 'react-dom';
|
|
9
|
+
import { css, Global } from '@emotion/react';
|
|
10
|
+
import blue from '@mui/material/colors/blue';
|
|
8
11
|
import { useInlineTranslationStore, useStatus, useTargetLanguage } from './store';
|
|
9
12
|
import { useEditorTranslator } from './EditorTranslator';
|
|
10
13
|
import { NODE_TYPE } from './TranslationNode';
|
|
@@ -17,6 +20,10 @@ const translations = {
|
|
|
17
20
|
en: 'Original',
|
|
18
21
|
zh: '原文',
|
|
19
22
|
},
|
|
23
|
+
translation: {
|
|
24
|
+
en: 'Translation',
|
|
25
|
+
zh: '翻译',
|
|
26
|
+
},
|
|
20
27
|
};
|
|
21
28
|
function InternalInlineTranslationPlugin({ translateService, detectLanguage, onTranslate, onRestore, showTranslationBadge = true, }) {
|
|
22
29
|
const [editor] = useLexicalComposerContext();
|
|
@@ -111,19 +118,40 @@ export function InlineTranslationPlugin(props) {
|
|
|
111
118
|
function TranslationElementPopper({ editorTranslator }) {
|
|
112
119
|
const [editor] = useLexicalComposerContext();
|
|
113
120
|
const [popperElement, setPopperElement] = useState(null);
|
|
121
|
+
const popperElementRef = useRef(null);
|
|
114
122
|
const [hoveringTranslationElement, setHoveringTranslationElement] = useState(null);
|
|
115
123
|
const [originalText, setOriginalText] = useState(null);
|
|
116
124
|
const { locale } = useLocaleContext();
|
|
125
|
+
useLayoutEffect(() => {
|
|
126
|
+
popperElementRef.current = popperElement;
|
|
127
|
+
}, [popperElement]);
|
|
117
128
|
const reset = () => {
|
|
118
129
|
setHoveringTranslationElement(null);
|
|
119
130
|
setOriginalText(null);
|
|
120
131
|
};
|
|
121
132
|
const isInsidePopper = (element) => {
|
|
122
|
-
return
|
|
133
|
+
return (popperElementRef.current && (popperElementRef.current === element || popperElementRef.current.contains(element)));
|
|
123
134
|
};
|
|
124
|
-
const
|
|
135
|
+
const hoveringTranslationElementPopper = usePopper(hoveringTranslationElement, popperElement, {
|
|
136
|
+
strategy: 'fixed',
|
|
137
|
+
placement: 'left-start',
|
|
138
|
+
modifiers: [
|
|
139
|
+
{
|
|
140
|
+
name: 'flip',
|
|
141
|
+
options: {
|
|
142
|
+
fallbackPlacements: ['bottom-start'],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
});
|
|
147
|
+
const translationPreviewPopper = usePopper(hoveringTranslationElement, popperElement, {
|
|
125
148
|
strategy: 'fixed',
|
|
126
149
|
placement: 'bottom-start',
|
|
150
|
+
modifiers: [
|
|
151
|
+
{
|
|
152
|
+
name: 'flip',
|
|
153
|
+
},
|
|
154
|
+
],
|
|
127
155
|
});
|
|
128
156
|
useEffect(() => {
|
|
129
157
|
return editor.registerRootListener((rootElement, prevRootElement) => {
|
|
@@ -147,10 +175,7 @@ function TranslationElementPopper({ editorTranslator }) {
|
|
|
147
175
|
clearTimeout(timer);
|
|
148
176
|
}
|
|
149
177
|
timer = setTimeout(() => {
|
|
150
|
-
|
|
151
|
-
// - popper 在 editorContainer 与 editor root 之间, 所以使用 `editorContainer.contains(relatedTarget)` 来检测
|
|
152
|
-
// - `editorContainer === relatedTarget` 也表示从 editor 移出
|
|
153
|
-
if (rootElement && (editorContainer === relatedTarget || !editorContainer?.contains(relatedTarget))) {
|
|
178
|
+
if (rootElement && !rootElement?.contains(relatedTarget) && !isInsidePopper(relatedTarget)) {
|
|
154
179
|
reset();
|
|
155
180
|
}
|
|
156
181
|
}, 100);
|
|
@@ -168,26 +193,36 @@ function TranslationElementPopper({ editorTranslator }) {
|
|
|
168
193
|
if (!hoveringTranslationElement) {
|
|
169
194
|
return null;
|
|
170
195
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
196
|
+
const globalStyles = css `
|
|
197
|
+
.inline-translation-node[data-eid='${editor.getKey()}'][data-sid='${hoveringTranslationElement.dataset.sid}'] > * {
|
|
198
|
+
background-color: ${blue[50]};
|
|
199
|
+
border-radius: 4px;
|
|
200
|
+
}
|
|
201
|
+
`;
|
|
202
|
+
return createPortal(_jsxs(_Fragment, { children: [!!originalText && (_jsxs(_Fragment, { children: [_jsx(Global, { styles: globalStyles }), _jsx(ClickAwayListener, { onClickAway: reset, children: _jsxs(Box, { style: translationPreviewPopper.styles.popper, ...translationPreviewPopper.attributes.popper, sx: {
|
|
203
|
+
position: 'relative',
|
|
204
|
+
zIndex: 'modal',
|
|
205
|
+
maxWidth: 600,
|
|
206
|
+
maxHeight: 400,
|
|
207
|
+
p: 2,
|
|
208
|
+
border: 1,
|
|
209
|
+
borderColor: 'divider',
|
|
210
|
+
borderRadius: 1,
|
|
211
|
+
fontSize: 13,
|
|
212
|
+
bgcolor: '#fff',
|
|
213
|
+
boxShadow: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
|
|
214
|
+
'h1,h2,h3,h4,h5,h6': {
|
|
215
|
+
margin: 0,
|
|
216
|
+
},
|
|
217
|
+
}, children: [_jsxs(Box, { children: [_jsx(Box, { sx: { color: 'text.secondary', fontWeight: 'medium' }, children: translations.original[locale] || translations.original.en }), _jsx(Box, { dangerouslySetInnerHTML: { __html: originalText } })] }), _jsxs(Box, { sx: { mt: 1.5 }, children: [_jsx(Box, { sx: { color: 'text.secondary', fontWeight: 'medium' }, children: translations.translation[locale] ||
|
|
218
|
+
translations.translation.en }), _jsx(Box, { dangerouslySetInnerHTML: {
|
|
219
|
+
__html: editorTranslator.getTranslationText(hoveringTranslationElement.dataset.sid) || '',
|
|
220
|
+
} })] })] }) })] })), _jsx(Box, { ref: setPopperElement, style: hoveringTranslationElementPopper.styles.popper, ...hoveringTranslationElementPopper.attributes.popper, sx: { zIndex: 'modal' }, onMouseLeave: reset, children: !originalText && (_jsx(Stack, { direction: "row", alignItems: hoveringTranslationElement.clientHeight > 100 ? 'center' : 'flex-start', sx: {
|
|
221
|
+
pr: 1,
|
|
222
|
+
pt: 0.5,
|
|
223
|
+
color: 'text.secondary',
|
|
224
|
+
fontSize: 12,
|
|
225
|
+
cursor: 'pointer',
|
|
226
|
+
height: hoveringTranslationElement.clientHeight,
|
|
227
|
+
}, onClick: () => setOriginalText(originalText ? null : editorTranslator.getOriginalText(hoveringTranslationElement.dataset.sid) || ''), children: _jsxs(Stack, { direction: "row", alignItems: "center", gap: 0.25, children: [_jsx("i", { className: "iconify", "data-icon": "tabler:tooltip", "data-height": 16 }), translations.original[locale] || translations.original.en] }) })) })] }), document.body);
|
|
193
228
|
}
|
|
@@ -15,6 +15,7 @@ export class TranslationNode extends ElementNode {
|
|
|
15
15
|
createDOM(config, editor) {
|
|
16
16
|
const dom = document.createElement('div');
|
|
17
17
|
dom.classList.add(NODE_TYPE, `${NODE_TYPE}-${this.__data.displayMode}`);
|
|
18
|
+
dom.dataset.eid = editor.getKey();
|
|
18
19
|
dom.dataset.sid = this.__data.sid;
|
|
19
20
|
return dom;
|
|
20
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/editor",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.129",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"ufo": "^1.5.4",
|
|
67
67
|
"url-join": "^4.0.1",
|
|
68
68
|
"zustand": "^4.5.5",
|
|
69
|
-
"@blocklet/pdf": "^2.1.
|
|
69
|
+
"@blocklet/pdf": "^2.1.129"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@babel/core": "^7.25.2",
|