mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: support shrinking text containers to original height when text removed (#6025)
* fix:cache bind text containers height so that it could autoshrink to original height when text deleted * revert * rename * reset cache when resized * safe check * restore original containr height when text is unbind * update cache when redrawing bounding box * reset cache when unbind * make type-safe * add specs * skip one test * remoe mock * fix Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
parent
9086674b27
commit
8ec5f7b982
4 changed files with 200 additions and 19 deletions
|
@ -17,6 +17,7 @@ import {
|
|||
ExcalidrawLinearElement,
|
||||
ExcalidrawTextElementWithContainer,
|
||||
ExcalidrawTextElement,
|
||||
ExcalidrawTextContainer,
|
||||
} from "./types";
|
||||
import { AppState } from "../types";
|
||||
import { mutateElement } from "./mutateElement";
|
||||
|
@ -60,6 +61,38 @@ const getTransform = (
|
|||
return `translate(${translateX}px, ${translateY}px) scale(${zoom.value}) rotate(${degree}deg)`;
|
||||
};
|
||||
|
||||
const originalContainerCache: {
|
||||
[id: ExcalidrawTextContainer["id"]]:
|
||||
| {
|
||||
height: ExcalidrawTextContainer["height"];
|
||||
}
|
||||
| undefined;
|
||||
} = {};
|
||||
|
||||
export const updateOriginalContainerCache = (
|
||||
id: ExcalidrawTextContainer["id"],
|
||||
height: ExcalidrawTextContainer["height"],
|
||||
) => {
|
||||
const data =
|
||||
originalContainerCache[id] || (originalContainerCache[id] = { height });
|
||||
data.height = height;
|
||||
return data;
|
||||
};
|
||||
|
||||
export const resetOriginalContainerCache = (
|
||||
id: ExcalidrawTextContainer["id"],
|
||||
) => {
|
||||
if (originalContainerCache[id]) {
|
||||
delete originalContainerCache[id];
|
||||
}
|
||||
};
|
||||
|
||||
export const getOriginalContainerHeightFromCache = (
|
||||
id: ExcalidrawTextContainer["id"],
|
||||
) => {
|
||||
return originalContainerCache[id]?.height ?? null;
|
||||
};
|
||||
|
||||
export const textWysiwyg = ({
|
||||
id,
|
||||
onChange,
|
||||
|
@ -87,6 +120,9 @@ export const textWysiwyg = ({
|
|||
updatedTextElement: ExcalidrawTextElement,
|
||||
editable: HTMLTextAreaElement,
|
||||
) => {
|
||||
if (!editable.style.fontFamily || !editable.style.fontSize) {
|
||||
return false;
|
||||
}
|
||||
const currentFont = editable.style.fontFamily.replace(/"/g, "");
|
||||
if (
|
||||
getFontFamilyString({ fontFamily: updatedTextElement.fontFamily }) !==
|
||||
|
@ -99,7 +135,6 @@ export const textWysiwyg = ({
|
|||
}
|
||||
return false;
|
||||
};
|
||||
let originalContainerHeight: number;
|
||||
|
||||
const updateWysiwygStyle = () => {
|
||||
const appState = app.state;
|
||||
|
@ -123,7 +158,7 @@ export const textWysiwyg = ({
|
|||
const width = updatedTextElement.width;
|
||||
// Set to element height by default since that's
|
||||
// what is going to be used for unbounded text
|
||||
let height = updatedTextElement.height;
|
||||
let textElementHeight = updatedTextElement.height;
|
||||
if (container && updatedTextElement.containerId) {
|
||||
if (isArrowElement(container)) {
|
||||
const boundTextCoords =
|
||||
|
@ -142,34 +177,52 @@ export const textWysiwyg = ({
|
|||
// using editor.style.height to get the accurate height of text editor
|
||||
const editorHeight = Number(editable.style.height.slice(0, -2));
|
||||
if (editorHeight > 0) {
|
||||
height = editorHeight;
|
||||
textElementHeight = editorHeight;
|
||||
}
|
||||
if (propertiesUpdated) {
|
||||
originalContainerHeight = containerDims.height;
|
||||
|
||||
// update height of the editor after properties updated
|
||||
height = updatedTextElement.height;
|
||||
textElementHeight = updatedTextElement.height;
|
||||
}
|
||||
if (!originalContainerHeight) {
|
||||
originalContainerHeight = containerDims.height;
|
||||
|
||||
let originalContainerData;
|
||||
if (propertiesUpdated) {
|
||||
originalContainerData = updateOriginalContainerCache(
|
||||
container.id,
|
||||
containerDims.height,
|
||||
);
|
||||
} else {
|
||||
originalContainerData = originalContainerCache[container.id];
|
||||
if (!originalContainerData) {
|
||||
originalContainerData = updateOriginalContainerCache(
|
||||
container.id,
|
||||
containerDims.height,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
maxWidth = getMaxContainerWidth(container);
|
||||
maxHeight = getMaxContainerHeight(container);
|
||||
|
||||
// autogrow container height if text exceeds
|
||||
|
||||
if (!isArrowElement(container) && height > maxHeight) {
|
||||
const diff = Math.min(height - maxHeight, approxLineHeight);
|
||||
if (!isArrowElement(container) && textElementHeight > maxHeight) {
|
||||
const diff = Math.min(
|
||||
textElementHeight - maxHeight,
|
||||
approxLineHeight,
|
||||
);
|
||||
mutateElement(container, { height: containerDims.height + diff });
|
||||
return;
|
||||
} else if (
|
||||
// autoshrink container height until original container height
|
||||
// is reached when text is removed
|
||||
!isArrowElement(container) &&
|
||||
containerDims.height > originalContainerHeight &&
|
||||
height < maxHeight
|
||||
containerDims.height > originalContainerData.height &&
|
||||
textElementHeight < maxHeight
|
||||
) {
|
||||
const diff = Math.min(maxHeight - height, approxLineHeight);
|
||||
const diff = Math.min(
|
||||
maxHeight - textElementHeight,
|
||||
approxLineHeight,
|
||||
);
|
||||
mutateElement(container, { height: containerDims.height - diff });
|
||||
}
|
||||
// Start pushing text upward until a diff of 30px (padding)
|
||||
|
@ -178,14 +231,15 @@ export const textWysiwyg = ({
|
|||
// vertically center align the text
|
||||
if (verticalAlign === VERTICAL_ALIGN.MIDDLE) {
|
||||
if (!isArrowElement(container)) {
|
||||
coordY = container.y + containerDims.height / 2 - height / 2;
|
||||
coordY =
|
||||
container.y + containerDims.height / 2 - textElementHeight / 2;
|
||||
}
|
||||
}
|
||||
if (verticalAlign === VERTICAL_ALIGN.BOTTOM) {
|
||||
coordY =
|
||||
container.y +
|
||||
containerDims.height -
|
||||
height -
|
||||
textElementHeight -
|
||||
getBoundTextElementOffset(updatedTextElement);
|
||||
}
|
||||
}
|
||||
|
@ -226,12 +280,12 @@ export const textWysiwyg = ({
|
|||
// must be defined *after* font ¯\_(ツ)_/¯
|
||||
lineHeight: `${lineHeight}px`,
|
||||
width: `${Math.min(width, maxWidth)}px`,
|
||||
height: `${height}px`,
|
||||
height: `${textElementHeight}px`,
|
||||
left: `${viewportX}px`,
|
||||
top: `${viewportY}px`,
|
||||
transform: getTransform(
|
||||
width,
|
||||
height,
|
||||
textElementHeight,
|
||||
getTextElementAngle(updatedTextElement),
|
||||
appState,
|
||||
maxWidth,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue