@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.
@@ -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 popperElement && (popperElement === element || popperElement.contains(element));
133
+ return (popperElementRef.current && (popperElementRef.current === element || popperElementRef.current.contains(element)));
123
134
  };
124
- const { styles, attributes } = usePopper(hoveringTranslationElement, popperElement, {
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
- const editorContainer = rootElement?.closest('.be-shell');
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
- return (_jsx(Box, { ref: setPopperElement, style: styles.popper, ...attributes.popper, sx: { zIndex: 'modal', mt: '-4px' }, onMouseLeave: reset, children: originalText ? (_jsxs(Box, { sx: {
172
- position: 'relative',
173
- zIndex: 'modal',
174
- maxWidth: 600,
175
- maxHeight: 400,
176
- p: 2,
177
- border: 1,
178
- borderColor: 'divider',
179
- borderRadius: 1,
180
- fontSize: 13,
181
- bgcolor: '#fff',
182
- boxShadow: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
183
- 'h1,h2,h3,h4,h5,h6': {
184
- margin: 0,
185
- },
186
- }, children: [_jsx(Box, { sx: { color: 'text.secondary' }, children: translations.original[locale] || translations.original.en }), _jsx(Box, { dangerouslySetInnerHTML: { __html: originalText } })] })) : (_jsxs(Stack, { direction: "row", alignItems: "center", gap: 0.5, sx: {
187
- height: 28,
188
- pr: 2,
189
- color: 'text.secondary',
190
- fontSize: 12,
191
- cursor: 'pointer',
192
- }, onClick: () => setOriginalText(originalText ? null : editorTranslator.getOriginalText(hoveringTranslationElement.dataset.sid) || ''), children: [_jsx("i", { className: "iconify", "data-icon": "tabler:tooltip", "data-height": 16 }), translations.original[locale] || translations.original.en] })) }));
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.128",
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.128"
69
+ "@blocklet/pdf": "^2.1.129"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@babel/core": "^7.25.2",