actionDistribute.tsx 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import React from "react";
  2. import { CODES } from "../keys";
  3. import { t } from "../i18n";
  4. import { register } from "./register";
  5. import {
  6. DistributeHorizontallyIcon,
  7. DistributeVerticallyIcon,
  8. } from "../components/icons";
  9. import { getSelectedElements, isSomeElementSelected } from "../scene";
  10. import { getElementMap, getNonDeletedElements } from "../element";
  11. import { ToolButton } from "../components/ToolButton";
  12. import { ExcalidrawElement } from "../element/types";
  13. import { AppState } from "../types";
  14. import { distributeElements, Distribution } from "../disitrubte";
  15. import { getShortcutKey } from "../utils";
  16. const enableActionGroup = (
  17. elements: readonly ExcalidrawElement[],
  18. appState: AppState,
  19. ) => getSelectedElements(getNonDeletedElements(elements), appState).length > 1;
  20. const distributeSelectedElements = (
  21. elements: readonly ExcalidrawElement[],
  22. appState: Readonly<AppState>,
  23. distribution: Distribution,
  24. ) => {
  25. const selectedElements = getSelectedElements(
  26. getNonDeletedElements(elements),
  27. appState,
  28. );
  29. const updatedElements = distributeElements(selectedElements, distribution);
  30. const updatedElementsMap = getElementMap(updatedElements);
  31. return elements.map((element) => updatedElementsMap[element.id] || element);
  32. };
  33. export const distributeHorizontally = register({
  34. name: "distributeHorizontally",
  35. perform: (elements, appState) => {
  36. return {
  37. appState,
  38. elements: distributeSelectedElements(elements, appState, {
  39. space: "between",
  40. axis: "x",
  41. }),
  42. commitToHistory: true,
  43. };
  44. },
  45. keyTest: (event) => event.altKey && event.code === CODES.H,
  46. PanelComponent: ({ elements, appState, updateData }) => (
  47. <ToolButton
  48. hidden={!enableActionGroup(elements, appState)}
  49. type="button"
  50. icon={<DistributeHorizontallyIcon appearance={appState.appearance} />}
  51. onClick={() => updateData(null)}
  52. title={`${t("labels.distributeHorizontally")} — ${getShortcutKey(
  53. "Alt+H",
  54. )}`}
  55. aria-label={t("labels.distributeHorizontally")}
  56. visible={isSomeElementSelected(getNonDeletedElements(elements), appState)}
  57. />
  58. ),
  59. });
  60. export const distributeVertically = register({
  61. name: "distributeVertically",
  62. perform: (elements, appState) => {
  63. return {
  64. appState,
  65. elements: distributeSelectedElements(elements, appState, {
  66. space: "between",
  67. axis: "y",
  68. }),
  69. commitToHistory: true,
  70. };
  71. },
  72. keyTest: (event) => event.altKey && event.code === CODES.V,
  73. PanelComponent: ({ elements, appState, updateData }) => (
  74. <ToolButton
  75. hidden={!enableActionGroup(elements, appState)}
  76. type="button"
  77. icon={<DistributeVerticallyIcon appearance={appState.appearance} />}
  78. onClick={() => updateData(null)}
  79. title={`${t("labels.distributeVertically")} — ${getShortcutKey("Alt+V")}`}
  80. aria-label={t("labels.distributeVertically")}
  81. visible={isSomeElementSelected(getNonDeletedElements(elements), appState)}
  82. />
  83. ),
  84. });