actionStyles.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import {
  2. isTextElement,
  3. isExcalidrawElement,
  4. redrawTextBoundingBox,
  5. } from "../element";
  6. import { CODES, KEYS } from "../keys";
  7. import { t } from "../i18n";
  8. import { register } from "./register";
  9. import { newElementWith } from "../element/mutateElement";
  10. import {
  11. DEFAULT_FONT_SIZE,
  12. DEFAULT_FONT_FAMILY,
  13. DEFAULT_TEXT_ALIGN,
  14. } from "../constants";
  15. import { getBoundTextElement } from "../element/textElement";
  16. import { hasBoundTextElement } from "../element/typeChecks";
  17. import { getSelectedElements } from "../scene";
  18. // `copiedStyles` is exported only for tests.
  19. export let copiedStyles: string = "{}";
  20. export const actionCopyStyles = register({
  21. name: "copyStyles",
  22. trackEvent: { category: "element" },
  23. perform: (elements, appState) => {
  24. const elementsCopied = [];
  25. const element = elements.find((el) => appState.selectedElementIds[el.id]);
  26. elementsCopied.push(element);
  27. if (element && hasBoundTextElement(element)) {
  28. const boundTextElement = getBoundTextElement(element);
  29. elementsCopied.push(boundTextElement);
  30. }
  31. if (element) {
  32. copiedStyles = JSON.stringify(elementsCopied);
  33. }
  34. return {
  35. appState: {
  36. ...appState,
  37. toast: { message: t("toast.copyStyles") },
  38. },
  39. commitToHistory: false,
  40. };
  41. },
  42. contextItemLabel: "labels.copyStyles",
  43. keyTest: (event) =>
  44. event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.C,
  45. });
  46. export const actionPasteStyles = register({
  47. name: "pasteStyles",
  48. trackEvent: { category: "element" },
  49. perform: (elements, appState) => {
  50. const elementsCopied = JSON.parse(copiedStyles);
  51. const pastedElement = elementsCopied[0];
  52. const boundTextElement = elementsCopied[1];
  53. if (!isExcalidrawElement(pastedElement)) {
  54. return { elements, commitToHistory: false };
  55. }
  56. const selectedElements = getSelectedElements(elements, appState, true);
  57. const selectedElementIds = selectedElements.map((element) => element.id);
  58. return {
  59. elements: elements.map((element) => {
  60. if (selectedElementIds.includes(element.id)) {
  61. let elementStylesToCopyFrom = pastedElement;
  62. if (isTextElement(element) && element.containerId) {
  63. elementStylesToCopyFrom = boundTextElement;
  64. }
  65. if (!elementStylesToCopyFrom) {
  66. return element;
  67. }
  68. let newElement = newElementWith(element, {
  69. backgroundColor: elementStylesToCopyFrom?.backgroundColor,
  70. strokeWidth: elementStylesToCopyFrom?.strokeWidth,
  71. strokeColor: elementStylesToCopyFrom?.strokeColor,
  72. strokeStyle: elementStylesToCopyFrom?.strokeStyle,
  73. fillStyle: elementStylesToCopyFrom?.fillStyle,
  74. opacity: elementStylesToCopyFrom?.opacity,
  75. roughness: elementStylesToCopyFrom?.roughness,
  76. roundness: elementStylesToCopyFrom?.roundness,
  77. });
  78. if (isTextElement(newElement)) {
  79. newElement = newElementWith(newElement, {
  80. fontSize: elementStylesToCopyFrom?.fontSize || DEFAULT_FONT_SIZE,
  81. fontFamily:
  82. elementStylesToCopyFrom?.fontFamily || DEFAULT_FONT_FAMILY,
  83. textAlign:
  84. elementStylesToCopyFrom?.textAlign || DEFAULT_TEXT_ALIGN,
  85. });
  86. let container = null;
  87. if (newElement.containerId) {
  88. container =
  89. selectedElements.find(
  90. (element) =>
  91. isTextElement(newElement) &&
  92. element.id === newElement.containerId,
  93. ) || null;
  94. }
  95. redrawTextBoundingBox(newElement, container);
  96. }
  97. if (newElement.type === "arrow") {
  98. newElement = newElementWith(newElement, {
  99. startArrowhead: elementStylesToCopyFrom.startArrowhead,
  100. endArrowhead: elementStylesToCopyFrom.endArrowhead,
  101. });
  102. }
  103. return newElement;
  104. }
  105. return element;
  106. }),
  107. commitToHistory: true,
  108. };
  109. },
  110. contextItemLabel: "labels.pasteStyles",
  111. keyTest: (event) =>
  112. event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.V,
  113. });