fix: stats state leaking & race conds (#8177)

This commit is contained in:
David Luzar 2024-06-26 23:31:08 +02:00 committed by GitHub
parent 6ba9bd60e8
commit 744b3e5d09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 945 additions and 761 deletions

View file

@ -2,7 +2,6 @@ import { isTextElement, refreshTextDimensions } from "../../element";
import { mutateElement } from "../../element/mutateElement";
import { isBoundToContainer } from "../../element/typeChecks";
import type {
ElementsMap,
ExcalidrawElement,
ExcalidrawTextElement,
} from "../../element/types";
@ -12,83 +11,56 @@ import { fontSizeIcon } from "../icons";
import StatsDragInput from "./DragInput";
import type { DragInputCallbackType } from "./DragInput";
import { getStepSizedValue } from "./utils";
import type { AppState } from "../../types";
interface MultiFontSizeProps {
elements: readonly ExcalidrawElement[];
elementsMap: ElementsMap;
scene: Scene;
appState: AppState;
property: "fontSize";
}
const MIN_FONT_SIZE = 4;
const STEP_SIZE = 4;
const MultiFontSize = ({
elements,
elementsMap,
scene,
}: MultiFontSizeProps) => {
const latestTextElements = elements.filter(
(el) => !isInGroup(el) && isTextElement(el) && !isBoundToContainer(el),
const getApplicableTextElements = (
elements: readonly (ExcalidrawElement | undefined)[],
) =>
elements.filter(
(el) =>
el && !isInGroup(el) && isTextElement(el) && !isBoundToContainer(el),
) as ExcalidrawTextElement[];
const fontSizes = latestTextElements.map(
(textEl) => Math.round(textEl.fontSize * 10) / 10,
const handleFontSizeChange: DragInputCallbackType<
MultiFontSizeProps["property"]
> = ({
accumulatedChange,
originalElements,
shouldChangeByStepSize,
nextValue,
scene,
}) => {
const elementsMap = scene.getNonDeletedElementsMap();
const latestTextElements = getApplicableTextElements(
originalElements.map((el) => elementsMap.get(el.id)),
);
const value = new Set(fontSizes).size === 1 ? fontSizes[0] : "Mixed";
const editable = fontSizes.length > 0;
const handleFontSizeChange: DragInputCallbackType = ({
accumulatedChange,
originalElements,
shouldChangeByStepSize,
nextValue,
}) => {
if (nextValue) {
const nextFontSize = Math.max(Math.round(nextValue), MIN_FONT_SIZE);
if (nextValue) {
const nextFontSize = Math.max(Math.round(nextValue), MIN_FONT_SIZE);
for (const textElement of latestTextElements) {
const newElement = {
...textElement,
fontSize: nextFontSize,
};
const updates = refreshTextDimensions(newElement, null, elementsMap);
mutateElement(
textElement,
{
...updates,
fontSize: nextFontSize,
},
false,
);
}
scene.triggerUpdate();
return;
}
const originalTextElements = originalElements.filter(
(el) => !isInGroup(el) && isTextElement(el) && !isBoundToContainer(el),
) as ExcalidrawTextElement[];
for (let i = 0; i < latestTextElements.length; i++) {
const latestElement = latestTextElements[i];
const originalElement = originalTextElements[i];
const originalFontSize = Math.round(originalElement.fontSize);
const changeInFontSize = Math.round(accumulatedChange);
let nextFontSize = Math.max(
originalFontSize + changeInFontSize,
MIN_FONT_SIZE,
);
if (shouldChangeByStepSize) {
nextFontSize = getStepSizedValue(nextFontSize, STEP_SIZE);
for (const textElement of latestTextElements.map((el) =>
elementsMap.get(el.id),
)) {
if (!textElement || !isTextElement(textElement)) {
continue;
}
const newElement = {
...latestElement,
...textElement,
fontSize: nextFontSize,
};
const updates = refreshTextDimensions(newElement, null, elementsMap);
mutateElement(
latestElement,
textElement,
{
...updates,
fontSize: nextFontSize,
@ -98,7 +70,56 @@ const MultiFontSize = ({
}
scene.triggerUpdate();
};
return;
}
const originalTextElements = originalElements.filter(
(el) => !isInGroup(el) && isTextElement(el) && !isBoundToContainer(el),
) as ExcalidrawTextElement[];
for (let i = 0; i < latestTextElements.length; i++) {
const latestElement = latestTextElements[i];
const originalElement = originalTextElements[i];
const originalFontSize = Math.round(originalElement.fontSize);
const changeInFontSize = Math.round(accumulatedChange);
let nextFontSize = Math.max(
originalFontSize + changeInFontSize,
MIN_FONT_SIZE,
);
if (shouldChangeByStepSize) {
nextFontSize = getStepSizedValue(nextFontSize, STEP_SIZE);
}
const newElement = {
...latestElement,
fontSize: nextFontSize,
};
const updates = refreshTextDimensions(newElement, null, elementsMap);
mutateElement(
latestElement,
{
...updates,
fontSize: nextFontSize,
},
false,
);
}
scene.triggerUpdate();
};
const MultiFontSize = ({
elements,
scene,
appState,
property,
}: MultiFontSizeProps) => {
const latestTextElements = getApplicableTextElements(elements);
const fontSizes = latestTextElements.map(
(textEl) => Math.round(textEl.fontSize * 10) / 10,
);
const value = new Set(fontSizes).size === 1 ? fontSizes[0] : "Mixed";
const editable = fontSizes.length > 0;
return (
<StatsDragInput
@ -108,6 +129,9 @@ const MultiFontSize = ({
dragInputCallback={handleFontSizeChange}
value={value}
editable={editable}
scene={scene}
property={property}
appState={appState}
/>
);
};