|
@@ -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);
|