aboutsummaryrefslogtreecommitdiffstats
path: root/packages/excalidraw/actions/actionDistribute.tsx
diff options
context:
space:
mode:
authorkj_sh6042026-03-15 16:19:35 -0400
committerkj_sh6042026-03-15 16:19:35 -0400
commit6ec259a0e71174651bae95d4628138bf6fd68742 (patch)
tree5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/excalidraw/actions/actionDistribute.tsx
parent16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff)
refactor: packages/
Diffstat (limited to 'packages/excalidraw/actions/actionDistribute.tsx')
-rw-r--r--packages/excalidraw/actions/actionDistribute.tsx110
1 files changed, 110 insertions, 0 deletions
diff --git a/packages/excalidraw/actions/actionDistribute.tsx b/packages/excalidraw/actions/actionDistribute.tsx
new file mode 100644
index 0000000..15d33b1
--- /dev/null
+++ b/packages/excalidraw/actions/actionDistribute.tsx
@@ -0,0 +1,110 @@
+import {
+ DistributeHorizontallyIcon,
+ DistributeVerticallyIcon,
+} from "../components/icons";
+import { ToolButton } from "../components/ToolButton";
+import type { Distribution } from "../distribute";
+import { distributeElements } from "../distribute";
+import { getNonDeletedElements } from "../element";
+import { isFrameLikeElement } from "../element/typeChecks";
+import type { ExcalidrawElement } from "../element/types";
+import { updateFrameMembershipOfSelectedElements } from "../frame";
+import { t } from "../i18n";
+import { CODES, KEYS } from "../keys";
+import { isSomeElementSelected } from "../scene";
+import { CaptureUpdateAction } from "../store";
+import type { AppClassProperties, AppState } from "../types";
+import { arrayToMap, getShortcutKey } from "../utils";
+import { register } from "./register";
+
+const enableActionGroup = (appState: AppState, app: AppClassProperties) => {
+ const selectedElements = app.scene.getSelectedElements(appState);
+ return (
+ selectedElements.length > 1 &&
+ // TODO enable distributing frames when implemented properly
+ !selectedElements.some((el) => isFrameLikeElement(el))
+ );
+};
+
+const distributeSelectedElements = (
+ elements: readonly ExcalidrawElement[],
+ appState: Readonly<AppState>,
+ app: AppClassProperties,
+ distribution: Distribution,
+) => {
+ const selectedElements = app.scene.getSelectedElements(appState);
+
+ const updatedElements = distributeElements(
+ selectedElements,
+ app.scene.getNonDeletedElementsMap(),
+ distribution,
+ );
+
+ const updatedElementsMap = arrayToMap(updatedElements);
+
+ return updateFrameMembershipOfSelectedElements(
+ elements.map((element) => updatedElementsMap.get(element.id) || element),
+ appState,
+ app,
+ );
+};
+
+export const distributeHorizontally = register({
+ name: "distributeHorizontally",
+ label: "labels.distributeHorizontally",
+ trackEvent: { category: "element" },
+ perform: (elements, appState, _, app) => {
+ return {
+ appState,
+ elements: distributeSelectedElements(elements, appState, app, {
+ space: "between",
+ axis: "x",
+ }),
+ captureUpdate: CaptureUpdateAction.IMMEDIATELY,
+ };
+ },
+ keyTest: (event) =>
+ !event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.H,
+ PanelComponent: ({ elements, appState, updateData, app }) => (
+ <ToolButton
+ hidden={!enableActionGroup(appState, app)}
+ type="button"
+ icon={DistributeHorizontallyIcon}
+ onClick={() => updateData(null)}
+ title={`${t("labels.distributeHorizontally")} — ${getShortcutKey(
+ "Alt+H",
+ )}`}
+ aria-label={t("labels.distributeHorizontally")}
+ visible={isSomeElementSelected(getNonDeletedElements(elements), appState)}
+ />
+ ),
+});
+
+export const distributeVertically = register({
+ name: "distributeVertically",
+ label: "labels.distributeVertically",
+ trackEvent: { category: "element" },
+ perform: (elements, appState, _, app) => {
+ return {
+ appState,
+ elements: distributeSelectedElements(elements, appState, app, {
+ space: "between",
+ axis: "y",
+ }),
+ captureUpdate: CaptureUpdateAction.IMMEDIATELY,
+ };
+ },
+ keyTest: (event) =>
+ !event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.V,
+ PanelComponent: ({ elements, appState, updateData, app }) => (
+ <ToolButton
+ hidden={!enableActionGroup(appState, app)}
+ type="button"
+ icon={DistributeVerticallyIcon}
+ onClick={() => updateData(null)}
+ title={`${t("labels.distributeVertically")} — ${getShortcutKey("Alt+V")}`}
+ aria-label={t("labels.distributeVertically")}
+ visible={isSomeElementSelected(getNonDeletedElements(elements), appState)}
+ />
+ ),
+});