diff options
Diffstat (limited to 'packages/excalidraw/components/Stats/MultiFontSize.tsx')
| -rw-r--r-- | packages/excalidraw/components/Stats/MultiFontSize.tsx | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/packages/excalidraw/components/Stats/MultiFontSize.tsx b/packages/excalidraw/components/Stats/MultiFontSize.tsx new file mode 100644 index 0000000..419bbbc --- /dev/null +++ b/packages/excalidraw/components/Stats/MultiFontSize.tsx @@ -0,0 +1,164 @@ +import { isTextElement, redrawTextBoundingBox } from "../../element"; +import { mutateElement } from "../../element/mutateElement"; +import { hasBoundTextElement } from "../../element/typeChecks"; +import type { + ExcalidrawElement, + ExcalidrawTextElement, + NonDeletedSceneElementsMap, +} from "../../element/types"; +import { isInGroup } from "../../groups"; +import type Scene from "../../scene/Scene"; +import { fontSizeIcon } from "../icons"; +import StatsDragInput from "./DragInput"; +import type { DragInputCallbackType } from "./DragInput"; +import { getStepSizedValue } from "./utils"; +import type { AppState } from "../../types"; +import { getBoundTextElement } from "../../element/textElement"; + +interface MultiFontSizeProps { + elements: readonly ExcalidrawElement[]; + scene: Scene; + elementsMap: NonDeletedSceneElementsMap; + appState: AppState; + property: "fontSize"; +} + +const MIN_FONT_SIZE = 4; +const STEP_SIZE = 4; + +const getApplicableTextElements = ( + elements: readonly (ExcalidrawElement | undefined)[], + elementsMap: NonDeletedSceneElementsMap, +) => + elements.reduce( + (acc: ExcalidrawTextElement[], el) => { + if (!el || isInGroup(el)) { + return acc; + } + if (isTextElement(el)) { + acc.push(el); + return acc; + } + if (hasBoundTextElement(el)) { + const boundTextElement = getBoundTextElement(el, elementsMap); + if (boundTextElement) { + acc.push(boundTextElement); + return acc; + } + } + + return acc; + }, + + [], + ); + +const handleFontSizeChange: DragInputCallbackType< + MultiFontSizeProps["property"], + ExcalidrawTextElement +> = ({ + accumulatedChange, + originalElements, + shouldChangeByStepSize, + nextValue, + scene, +}) => { + const elementsMap = scene.getNonDeletedElementsMap(); + const latestTextElements = originalElements.map((el) => + elementsMap.get(el.id), + ) as ExcalidrawTextElement[]; + + let nextFontSize; + + if (nextValue) { + nextFontSize = Math.max(Math.round(nextValue), MIN_FONT_SIZE); + + for (const textElement of latestTextElements) { + mutateElement( + textElement, + { + fontSize: nextFontSize, + }, + false, + ); + + redrawTextBoundingBox( + textElement, + scene.getContainerElement(textElement), + elementsMap, + false, + ); + } + + scene.triggerUpdate(); + } else { + const originalTextElements = originalElements 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); + } + mutateElement( + latestElement, + { + fontSize: nextFontSize, + }, + false, + ); + + redrawTextBoundingBox( + latestElement, + scene.getContainerElement(latestElement), + elementsMap, + false, + ); + } + + scene.triggerUpdate(); + } +}; + +const MultiFontSize = ({ + elements, + scene, + appState, + property, + elementsMap, +}: MultiFontSizeProps) => { + const latestTextElements = getApplicableTextElements(elements, elementsMap); + + if (!latestTextElements.length) { + return null; + } + + 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 + label="F" + icon={fontSizeIcon} + elements={latestTextElements} + dragInputCallback={handleFontSizeChange} + value={value} + editable={editable} + scene={scene} + property={property} + appState={appState} + /> + ); +}; + +export default MultiFontSize; |
