selection.ts 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import {
  2. ExcalidrawElement,
  3. NonDeletedExcalidrawElement,
  4. } from "../element/types";
  5. import { getElementAbsoluteCoords, getElementBounds } from "../element";
  6. import { AppState } from "../types";
  7. import { isBoundToContainer } from "../element/typeChecks";
  8. export const getElementsWithinSelection = (
  9. elements: readonly NonDeletedExcalidrawElement[],
  10. selection: NonDeletedExcalidrawElement,
  11. ) => {
  12. const [selectionX1, selectionY1, selectionX2, selectionY2] =
  13. getElementAbsoluteCoords(selection);
  14. return elements.filter((element) => {
  15. const [elementX1, elementY1, elementX2, elementY2] =
  16. getElementBounds(element);
  17. return (
  18. element.type !== "selection" &&
  19. !isBoundToContainer(element) &&
  20. selectionX1 <= elementX1 &&
  21. selectionY1 <= elementY1 &&
  22. selectionX2 >= elementX2 &&
  23. selectionY2 >= elementY2
  24. );
  25. });
  26. };
  27. export const isSomeElementSelected = (
  28. elements: readonly NonDeletedExcalidrawElement[],
  29. appState: AppState,
  30. ): boolean =>
  31. elements.some((element) => appState.selectedElementIds[element.id]);
  32. /**
  33. * Returns common attribute (picked by `getAttribute` callback) of selected
  34. * elements. If elements don't share the same value, returns `null`.
  35. */
  36. export const getCommonAttributeOfSelectedElements = <T>(
  37. elements: readonly NonDeletedExcalidrawElement[],
  38. appState: AppState,
  39. getAttribute: (element: ExcalidrawElement) => T,
  40. ): T | null => {
  41. const attributes = Array.from(
  42. new Set(
  43. getSelectedElements(elements, appState).map((element) =>
  44. getAttribute(element),
  45. ),
  46. ),
  47. );
  48. return attributes.length === 1 ? attributes[0] : null;
  49. };
  50. export const getSelectedElements = (
  51. elements: readonly NonDeletedExcalidrawElement[],
  52. appState: AppState,
  53. includeBoundTextElement: boolean = false,
  54. ) =>
  55. elements.filter((element) => {
  56. if (appState.selectedElementIds[element.id]) {
  57. return element;
  58. }
  59. if (
  60. includeBoundTextElement &&
  61. isBoundToContainer(element) &&
  62. appState.selectedElementIds[element?.containerId]
  63. ) {
  64. return element;
  65. }
  66. return null;
  67. });
  68. export const getTargetElements = (
  69. elements: readonly NonDeletedExcalidrawElement[],
  70. appState: AppState,
  71. ) =>
  72. appState.editingElement
  73. ? [appState.editingElement]
  74. : getSelectedElements(elements, appState, true);