mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-11-17 11:14:23 +01:00
122 lines
3.4 KiB
TypeScript
122 lines
3.4 KiB
TypeScript
import { newElementWith } from "..";
|
|
import { isTextElement, redrawTextBoundingBox } from "../element";
|
|
import { ExcalidrawElement, ExcalidrawTextElement } from "../element/types";
|
|
import { KEYS } from "../keys";
|
|
import { AppClassProperties, AppState } from "../types";
|
|
import { changeProperty } from "./actionProperties";
|
|
import { register } from "./register";
|
|
|
|
const FONT_SIZE_RELATIVE_INCREASE_STEP = 0.1;
|
|
|
|
const offsetElementAfterFontResize = (
|
|
prevElement: ExcalidrawTextElement,
|
|
nextElement: ExcalidrawTextElement,
|
|
) => {
|
|
if (isBoundToContainer(nextElement)) {
|
|
return nextElement;
|
|
}
|
|
return mutateElement(
|
|
nextElement,
|
|
{
|
|
x:
|
|
prevElement.textAlign === "left"
|
|
? prevElement.x
|
|
: prevElement.x +
|
|
(prevElement.width - nextElement.width) /
|
|
(prevElement.textAlign === "center" ? 2 : 1),
|
|
// centering vertically is non-standard, but for Excalidraw I think
|
|
// it makes sense
|
|
y: prevElement.y + (prevElement.height - nextElement.height) / 2,
|
|
},
|
|
false,
|
|
);
|
|
};
|
|
|
|
const changeFontSize = (
|
|
elements: readonly ExcalidrawElement[],
|
|
appState: AppState,
|
|
app: AppClassProperties,
|
|
getNewFontSize: (element: ExcalidrawTextElement) => number,
|
|
fallbackValue?: ExcalidrawTextElement["fontSize"],
|
|
) => {
|
|
const newFontSizes = new Set<number>();
|
|
|
|
return {
|
|
elements: changeProperty(
|
|
elements,
|
|
appState,
|
|
(oldElement) => {
|
|
if (isTextElement(oldElement)) {
|
|
const newFontSize = getNewFontSize(oldElement);
|
|
newFontSizes.add(newFontSize);
|
|
|
|
let newElement: ExcalidrawTextElement = newElementWith(oldElement, {
|
|
fontSize: newFontSize,
|
|
});
|
|
redrawTextBoundingBox(
|
|
newElement,
|
|
app.scene.getContainerElement(oldElement),
|
|
);
|
|
|
|
newElement = offsetElementAfterFontResize(oldElement, newElement);
|
|
|
|
return newElement;
|
|
}
|
|
|
|
return oldElement;
|
|
},
|
|
true,
|
|
),
|
|
appState: {
|
|
...appState,
|
|
// update state only if we've set all select text elements to
|
|
// the same font size
|
|
currentItemFontSize:
|
|
newFontSizes.size === 1
|
|
? [...newFontSizes][0]
|
|
: fallbackValue ?? appState.currentItemFontSize,
|
|
},
|
|
commitToHistory: true,
|
|
};
|
|
};
|
|
|
|
export const actionDecreaseFontSize = register({
|
|
name: "decreaseFontSize",
|
|
trackEvent: false,
|
|
perform: (elements, appState, value, app) => {
|
|
return changeFontSize(elements, appState, app, (element) =>
|
|
Math.round(
|
|
// get previous value before relative increase (doesn't work fully
|
|
// due to rounding and float precision issues)
|
|
(1 / (1 + FONT_SIZE_RELATIVE_INCREASE_STEP)) * element.fontSize,
|
|
),
|
|
);
|
|
},
|
|
keyTest: (event) => {
|
|
return (
|
|
event[KEYS.CTRL_OR_CMD] &&
|
|
event.shiftKey &&
|
|
// KEYS.COMMA needed for MacOS
|
|
(event.key === KEYS.CHEVRON_LEFT || event.key === KEYS.COMMA)
|
|
);
|
|
},
|
|
});
|
|
|
|
export const actionIncreaseFontSize = register({
|
|
name: "increaseFontSize",
|
|
trackEvent: false,
|
|
perform: (elements, appState, value, app) => {
|
|
return changeFontSize(elements, appState, app, (element) =>
|
|
Math.round(element.fontSize * (1 + FONT_SIZE_RELATIVE_INCREASE_STEP)),
|
|
);
|
|
},
|
|
keyTest: (event) => {
|
|
return (
|
|
event[KEYS.CTRL_OR_CMD] &&
|
|
event.shiftKey &&
|
|
// KEYS.PERIOD needed for MacOS
|
|
(event.key === KEYS.CHEVRON_RIGHT || event.key === KEYS.PERIOD)
|
|
);
|
|
},
|
|
});
|