fix: set the width correctly using measureText in editor (#6162)

This commit is contained in:
Aakansha Doshi 2023-01-28 16:39:53 +05:30 committed by GitHub
parent b52c8943e4
commit e41ea9562b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 20 additions and 14 deletions

View File

@ -171,7 +171,7 @@ describe("Test measureText", () => {
expect(res.container).toMatchInlineSnapshot(` expect(res.container).toMatchInlineSnapshot(`
<div <div
style="position: absolute; white-space: pre-wrap; font: Emoji 20px 20px; min-height: 1em; width: 111px; overflow: hidden; word-break: break-word; line-height: 0px;" style="position: absolute; white-space: pre-wrap; font: Emoji 20px 20px; min-height: 1em; max-width: 191px; overflow: hidden; word-break: break-word; line-height: 0px;"
> >
<span <span
style="display: inline-block; overflow: hidden; width: 1px; height: 1px;" style="display: inline-block; overflow: hidden; width: 1px; height: 1px;"

View File

@ -271,12 +271,11 @@ export const measureText = (
container.style.whiteSpace = "pre"; container.style.whiteSpace = "pre";
container.style.font = font; container.style.font = font;
container.style.minHeight = "1em"; container.style.minHeight = "1em";
const textWidth = getTextWidth(text, font);
if (maxWidth) { if (maxWidth) {
const lineHeight = getApproxLineHeight(font); const lineHeight = getApproxLineHeight(font);
container.style.width = `${String(Math.min(textWidth, maxWidth) + 1)}px`; // since we are adding a span of width 1px later
container.style.maxWidth = `${maxWidth + 1}px`;
container.style.overflow = "hidden"; container.style.overflow = "hidden";
container.style.wordBreak = "break-word"; container.style.wordBreak = "break-word";
container.style.lineHeight = `${String(lineHeight)}px`; container.style.lineHeight = `${String(lineHeight)}px`;
@ -293,11 +292,7 @@ export const measureText = (
container.appendChild(span); container.appendChild(span);
// Baseline is important for positioning text on canvas // Baseline is important for positioning text on canvas
const baseline = span.offsetTop + span.offsetHeight; const baseline = span.offsetTop + span.offsetHeight;
// Since span adds 1px extra width to the container const width = container.offsetWidth;
let width = container.offsetWidth;
if (maxWidth && textWidth > maxWidth) {
width = width - 1;
}
const height = container.offsetHeight; const height = container.offsetHeight;
document.body.removeChild(container); document.body.removeChild(container);
if (isTestEnv()) { if (isTestEnv()) {
@ -332,8 +327,11 @@ const getLineWidth = (text: string, font: FontString) => {
if (isTestEnv()) { if (isTestEnv()) {
return metrics.width * 10; return metrics.width * 10;
} }
// Since measureText behaves differently in different browsers
// OS so considering a adjustment factor of 0.2
const adjustmentFactor = 0.2;
return metrics.width; return metrics.width + adjustmentFactor;
}; };
export const getTextWidth = (text: string, font: FontString) => { export const getTextWidth = (text: string, font: FontString) => {

View File

@ -142,11 +142,11 @@ export const textWysiwyg = ({
const appState = app.state; const appState = app.state;
const updatedTextElement = const updatedTextElement =
Scene.getScene(element)?.getElement<ExcalidrawTextElement>(id); Scene.getScene(element)?.getElement<ExcalidrawTextElement>(id);
if (!updatedTextElement) { if (!updatedTextElement) {
return; return;
} }
const { textAlign, verticalAlign } = updatedTextElement; const { textAlign, verticalAlign } = updatedTextElement;
const approxLineHeight = getApproxLineHeight( const approxLineHeight = getApproxLineHeight(
getFontString(updatedTextElement), getFontString(updatedTextElement),
); );
@ -161,6 +161,7 @@ export const textWysiwyg = ({
// Set to element height by default since that's // Set to element height by default since that's
// what is going to be used for unbounded text // what is going to be used for unbounded text
let textElementHeight = updatedTextElement.height; let textElementHeight = updatedTextElement.height;
if (container && updatedTextElement.containerId) { if (container && updatedTextElement.containerId) {
if (isArrowElement(container)) { if (isArrowElement(container)) {
const boundTextCoords = const boundTextCoords =
@ -206,7 +207,6 @@ export const textWysiwyg = ({
maxHeight = getMaxContainerHeight(container); maxHeight = getMaxContainerHeight(container);
// autogrow container height if text exceeds // autogrow container height if text exceeds
if (!isArrowElement(container) && textElementHeight > maxHeight) { if (!isArrowElement(container) && textElementHeight > maxHeight) {
const diff = Math.min( const diff = Math.min(
textElementHeight - maxHeight, textElementHeight - maxHeight,
@ -276,7 +276,6 @@ export const textWysiwyg = ({
// Make sure text editor height doesn't go beyond viewport // Make sure text editor height doesn't go beyond viewport
const editorMaxHeight = const editorMaxHeight =
(appState.height - viewportY) / appState.zoom.value; (appState.height - viewportY) / appState.zoom.value;
Object.assign(editable.style, { Object.assign(editable.style, {
font: getFontString(updatedTextElement), font: getFontString(updatedTextElement),
// must be defined *after* font ¯\_(ツ)_/¯ // must be defined *after* font ¯\_(ツ)_/¯
@ -395,11 +394,12 @@ export const textWysiwyg = ({
// first line as well as setting height to "auto" // first line as well as setting height to "auto"
// doubles the height as soon as user starts typing // doubles the height as soon as user starts typing
if (isBoundToContainer(element) && lines > 1) { if (isBoundToContainer(element) && lines > 1) {
const container = getContainerElement(element);
let height = "auto"; let height = "auto";
editable.style.height = "0px"; editable.style.height = "0px";
let heightSet = false; let heightSet = false;
if (lines === 2) { if (lines === 2) {
const container = getContainerElement(element);
const actualLineCount = wrapText( const actualLineCount = wrapText(
editable.value, editable.value,
font, font,
@ -416,6 +416,14 @@ export const textWysiwyg = ({
heightSet = true; heightSet = true;
} }
} }
const wrappedText = wrapText(
normalizeText(editable.value),
font,
getMaxContainerWidth(container!),
);
const width = getTextWidth(wrappedText, font);
editable.style.width = `${width}px`;
if (!heightSet) { if (!heightSet) {
editable.style.height = `${editable.scrollHeight}px`; editable.style.height = `${editable.scrollHeight}px`;
} }