diff options
| author | kj_sh604 | 2026-03-15 16:19:35 -0400 |
|---|---|---|
| committer | kj_sh604 | 2026-03-15 16:19:35 -0400 |
| commit | 6ec259a0e71174651bae95d4628138bf6fd68742 (patch) | |
| tree | 5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/excalidraw/components/Stats/Angle.tsx | |
| parent | 16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff) | |
refactor: packages/
Diffstat (limited to 'packages/excalidraw/components/Stats/Angle.tsx')
| -rw-r--r-- | packages/excalidraw/components/Stats/Angle.tsx | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/packages/excalidraw/components/Stats/Angle.tsx b/packages/excalidraw/components/Stats/Angle.tsx new file mode 100644 index 0000000..409476a --- /dev/null +++ b/packages/excalidraw/components/Stats/Angle.tsx @@ -0,0 +1,95 @@ +import { mutateElement } from "../../element/mutateElement"; +import { getBoundTextElement } from "../../element/textElement"; +import { isArrowElement, isElbowArrow } from "../../element/typeChecks"; +import type { ExcalidrawElement } from "../../element/types"; +import { angleIcon } from "../icons"; +import DragInput from "./DragInput"; +import type { DragInputCallbackType } from "./DragInput"; +import { getStepSizedValue, isPropertyEditable, updateBindings } from "./utils"; +import type Scene from "../../scene/Scene"; +import type { AppState } from "../../types"; +import type { Degrees } from "@excalidraw/math"; +import { degreesToRadians, radiansToDegrees } from "@excalidraw/math"; + +interface AngleProps { + element: ExcalidrawElement; + scene: Scene; + appState: AppState; + property: "angle"; +} + +const STEP_SIZE = 15; + +const handleDegreeChange: DragInputCallbackType<AngleProps["property"]> = ({ + accumulatedChange, + originalElements, + shouldChangeByStepSize, + nextValue, + scene, +}) => { + const elementsMap = scene.getNonDeletedElementsMap(); + const elements = scene.getNonDeletedElements(); + const origElement = originalElements[0]; + if (origElement && !isElbowArrow(origElement)) { + const latestElement = elementsMap.get(origElement.id); + if (!latestElement) { + return; + } + + if (nextValue !== undefined) { + const nextAngle = degreesToRadians(nextValue as Degrees); + mutateElement(latestElement, { + angle: nextAngle, + }); + updateBindings(latestElement, elementsMap, elements, scene); + + const boundTextElement = getBoundTextElement(latestElement, elementsMap); + if (boundTextElement && !isArrowElement(latestElement)) { + mutateElement(boundTextElement, { angle: nextAngle }); + } + + return; + } + + const originalAngleInDegrees = + Math.round(radiansToDegrees(origElement.angle) * 100) / 100; + const changeInDegrees = Math.round(accumulatedChange); + let nextAngleInDegrees = (originalAngleInDegrees + changeInDegrees) % 360; + if (shouldChangeByStepSize) { + nextAngleInDegrees = getStepSizedValue(nextAngleInDegrees, STEP_SIZE); + } + + nextAngleInDegrees = + nextAngleInDegrees < 0 ? nextAngleInDegrees + 360 : nextAngleInDegrees; + + const nextAngle = degreesToRadians(nextAngleInDegrees as Degrees); + + mutateElement(latestElement, { + angle: nextAngle, + }); + updateBindings(latestElement, elementsMap, elements, scene); + + const boundTextElement = getBoundTextElement(latestElement, elementsMap); + if (boundTextElement && !isArrowElement(latestElement)) { + mutateElement(boundTextElement, { angle: nextAngle }); + } + } +}; + +const Angle = ({ element, scene, appState, property }: AngleProps) => { + return ( + <DragInput + label="A" + icon={angleIcon} + value={Math.round((radiansToDegrees(element.angle) % 360) * 100) / 100} + elements={[element]} + dragInputCallback={handleDegreeChange} + editable={isPropertyEditable(element, "angle")} + scene={scene} + appState={appState} + property={property} + /> + ); +}; + +export default Angle; |
