浏览代码

feat: wrap long text when pasting (#8026)

Co-authored-by: dwelle <[email protected]>
Ryan Di 1 年之前
父节点
当前提交
c540bd68aa
共有 2 个文件被更改,包括 38 次插入11 次删除
  1. 34 9
      packages/excalidraw/components/App.tsx
  2. 4 2
      packages/excalidraw/element/newElement.ts

+ 34 - 9
packages/excalidraw/components/App.tsx

@@ -88,6 +88,7 @@ import {
   isIOS,
   isIOS,
   supportsResizeObserver,
   supportsResizeObserver,
   DEFAULT_COLLISION_THRESHOLD,
   DEFAULT_COLLISION_THRESHOLD,
+  DEFAULT_TEXT_ALIGN,
 } from "../constants";
 } from "../constants";
 import type { ExportedElements } from "../data";
 import type { ExportedElements } from "../data";
 import { exportCanvas, loadFromBlob } from "../data";
 import { exportCanvas, loadFromBlob } from "../data";
@@ -331,6 +332,8 @@ import {
   getLineHeightInPx,
   getLineHeightInPx,
   isMeasureTextSupported,
   isMeasureTextSupported,
   isValidTextContainer,
   isValidTextContainer,
+  measureText,
+  wrapText,
 } from "../element/textElement";
 } from "../element/textElement";
 import {
 import {
   showHyperlinkTooltip,
   showHyperlinkTooltip,
@@ -430,6 +433,7 @@ import {
 } from "./hyperlink/helpers";
 } from "./hyperlink/helpers";
 import { getShortcutFromShortcutName } from "../actions/shortcuts";
 import { getShortcutFromShortcutName } from "../actions/shortcuts";
 import { actionTextAutoResize } from "../actions/actionTextAutoResize";
 import { actionTextAutoResize } from "../actions/actionTextAutoResize";
+import { getVisibleSceneBounds } from "../element/bounds";
 
 
 const AppContext = React.createContext<AppClassProperties>(null!);
 const AppContext = React.createContext<AppClassProperties>(null!);
 const AppPropsContext = React.createContext<AppProps>(null!);
 const AppPropsContext = React.createContext<AppProps>(null!);
@@ -2565,7 +2569,7 @@ class App extends React.Component<AppProps, AppState> {
       addEventListener(document, EVENT.KEYUP, this.onKeyUp, { passive: true }),
       addEventListener(document, EVENT.KEYUP, this.onKeyUp, { passive: true }),
       addEventListener(
       addEventListener(
         document,
         document,
-        EVENT.MOUSE_MOVE,
+        EVENT.POINTER_MOVE,
         this.updateCurrentCursorPosition,
         this.updateCurrentCursorPosition,
       ),
       ),
       // rerender text elements on font load to fix #637 && #1553
       // rerender text elements on font load to fix #637 && #1553
@@ -3341,32 +3345,53 @@ class App extends React.Component<AppProps, AppState> {
       text,
       text,
       fontSize: this.state.currentItemFontSize,
       fontSize: this.state.currentItemFontSize,
       fontFamily: this.state.currentItemFontFamily,
       fontFamily: this.state.currentItemFontFamily,
-      textAlign: this.state.currentItemTextAlign,
+      textAlign: DEFAULT_TEXT_ALIGN,
       verticalAlign: DEFAULT_VERTICAL_ALIGN,
       verticalAlign: DEFAULT_VERTICAL_ALIGN,
       locked: false,
       locked: false,
     };
     };
-
+    const fontString = getFontString({
+      fontSize: textElementProps.fontSize,
+      fontFamily: textElementProps.fontFamily,
+    });
+    const lineHeight = getDefaultLineHeight(textElementProps.fontFamily);
+    const [x1, , x2] = getVisibleSceneBounds(this.state);
+    // long texts should not go beyond 800 pixels in width nor should it go below 200 px
+    const maxTextWidth = Math.max(Math.min((x2 - x1) * 0.5, 800), 200);
     const LINE_GAP = 10;
     const LINE_GAP = 10;
     let currentY = y;
     let currentY = y;
 
 
     const lines = isPlainPaste ? [text] : text.split("\n");
     const lines = isPlainPaste ? [text] : text.split("\n");
     const textElements = lines.reduce(
     const textElements = lines.reduce(
       (acc: ExcalidrawTextElement[], line, idx) => {
       (acc: ExcalidrawTextElement[], line, idx) => {
-        const text = line.trim();
-
-        const lineHeight = getDefaultLineHeight(textElementProps.fontFamily);
-        if (text.length) {
+        const originalText = line.trim();
+        if (originalText.length) {
           const topLayerFrame = this.getTopLayerFrameAtSceneCoords({
           const topLayerFrame = this.getTopLayerFrameAtSceneCoords({
             x,
             x,
             y: currentY,
             y: currentY,
           });
           });
 
 
+          let metrics = measureText(originalText, fontString, lineHeight);
+          const isTextWrapped = metrics.width > maxTextWidth;
+
+          const text = isTextWrapped
+            ? wrapText(originalText, fontString, maxTextWidth)
+            : originalText;
+
+          metrics = isTextWrapped
+            ? measureText(text, fontString, lineHeight)
+            : metrics;
+
+          const startX = x - metrics.width / 2;
+          const startY = currentY - metrics.height / 2;
+
           const element = newTextElement({
           const element = newTextElement({
             ...textElementProps,
             ...textElementProps,
-            x,
-            y: currentY,
+            x: startX,
+            y: startY,
             text,
             text,
+            originalText,
             lineHeight,
             lineHeight,
+            autoResize: !isTextWrapped,
             frameId: topLayerFrame ? topLayerFrame.id : null,
             frameId: topLayerFrame ? topLayerFrame.id : null,
           });
           });
           acc.push(element);
           acc.push(element);

+ 4 - 2
packages/excalidraw/element/newElement.ts

@@ -215,6 +215,7 @@ const getTextElementPositionOffsets = (
 export const newTextElement = (
 export const newTextElement = (
   opts: {
   opts: {
     text: string;
     text: string;
+    originalText?: string;
     fontSize?: number;
     fontSize?: number;
     fontFamily?: FontFamilyValues;
     fontFamily?: FontFamilyValues;
     textAlign?: TextAlign;
     textAlign?: TextAlign;
@@ -222,6 +223,7 @@ export const newTextElement = (
     containerId?: ExcalidrawTextContainer["id"] | null;
     containerId?: ExcalidrawTextContainer["id"] | null;
     lineHeight?: ExcalidrawTextElement["lineHeight"];
     lineHeight?: ExcalidrawTextElement["lineHeight"];
     strokeWidth?: ExcalidrawTextElement["strokeWidth"];
     strokeWidth?: ExcalidrawTextElement["strokeWidth"];
+    autoResize?: ExcalidrawTextElement["autoResize"];
   } & ElementConstructorOpts,
   } & ElementConstructorOpts,
 ): NonDeleted<ExcalidrawTextElement> => {
 ): NonDeleted<ExcalidrawTextElement> => {
   const fontFamily = opts.fontFamily || DEFAULT_FONT_FAMILY;
   const fontFamily = opts.fontFamily || DEFAULT_FONT_FAMILY;
@@ -252,8 +254,8 @@ export const newTextElement = (
     width: metrics.width,
     width: metrics.width,
     height: metrics.height,
     height: metrics.height,
     containerId: opts.containerId || null,
     containerId: opts.containerId || null,
-    originalText: text,
-    autoResize: true,
+    originalText: opts.originalText ?? text,
+    autoResize: opts.autoResize ?? true,
     lineHeight,
     lineHeight,
   };
   };