mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-11-03 20:34:40 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			101 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			101 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import type { AppState } from "@excalidraw/excalidraw/types";
 | 
						|
 | 
						|
import { getCommonBoundingBox } from "./bounds";
 | 
						|
import { newElementWith } from "./mutateElement";
 | 
						|
 | 
						|
import { getSelectedElementsByGroup } from "./groups";
 | 
						|
 | 
						|
import type { ElementsMap, ExcalidrawElement } from "./types";
 | 
						|
 | 
						|
export interface Distribution {
 | 
						|
  space: "between";
 | 
						|
  axis: "x" | "y";
 | 
						|
}
 | 
						|
 | 
						|
export const distributeElements = (
 | 
						|
  selectedElements: ExcalidrawElement[],
 | 
						|
  elementsMap: ElementsMap,
 | 
						|
  distribution: Distribution,
 | 
						|
  appState: Readonly<AppState>,
 | 
						|
): ExcalidrawElement[] => {
 | 
						|
  const [start, mid, end, extent] =
 | 
						|
    distribution.axis === "x"
 | 
						|
      ? (["minX", "midX", "maxX", "width"] as const)
 | 
						|
      : (["minY", "midY", "maxY", "height"] as const);
 | 
						|
 | 
						|
  const bounds = getCommonBoundingBox(selectedElements);
 | 
						|
  const groups = getSelectedElementsByGroup(
 | 
						|
    selectedElements,
 | 
						|
    elementsMap,
 | 
						|
    appState,
 | 
						|
  )
 | 
						|
    .map((group) => [group, getCommonBoundingBox(group)] as const)
 | 
						|
    .sort((a, b) => a[1][mid] - b[1][mid]);
 | 
						|
 | 
						|
  let span = 0;
 | 
						|
  for (const group of groups) {
 | 
						|
    span += group[1][extent];
 | 
						|
  }
 | 
						|
 | 
						|
  const step = (bounds[extent] - span) / (groups.length - 1);
 | 
						|
 | 
						|
  if (step < 0) {
 | 
						|
    // If we have a negative step, we'll need to distribute from centers
 | 
						|
    // rather than from gaps. Buckle up, this is a weird one.
 | 
						|
 | 
						|
    // Get indices of boxes that define start and end of our bounding box
 | 
						|
    const index0 = groups.findIndex((g) => g[1][start] === bounds[start]);
 | 
						|
    const index1 = groups.findIndex((g) => g[1][end] === bounds[end]);
 | 
						|
 | 
						|
    // Get our step, based on the distance between the center points of our
 | 
						|
    // start and end boxes
 | 
						|
    const step =
 | 
						|
      (groups[index1][1][mid] - groups[index0][1][mid]) / (groups.length - 1);
 | 
						|
 | 
						|
    let pos = groups[index0][1][mid];
 | 
						|
 | 
						|
    return groups.flatMap(([group, box], index) => {
 | 
						|
      const translation = {
 | 
						|
        x: 0,
 | 
						|
        y: 0,
 | 
						|
      };
 | 
						|
 | 
						|
      // Don't move our start and end boxes
 | 
						|
      if (index !== index0 && index !== index1) {
 | 
						|
        pos += step;
 | 
						|
        translation[distribution.axis] = pos - box[mid];
 | 
						|
      }
 | 
						|
 | 
						|
      return group.map((element) =>
 | 
						|
        newElementWith(element, {
 | 
						|
          x: element.x + translation.x,
 | 
						|
          y: element.y + translation.y,
 | 
						|
        }),
 | 
						|
      );
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  // Distribute from gaps
 | 
						|
 | 
						|
  let pos = bounds[start];
 | 
						|
 | 
						|
  return groups.flatMap(([group, box]) => {
 | 
						|
    const translation = {
 | 
						|
      x: 0,
 | 
						|
      y: 0,
 | 
						|
    };
 | 
						|
 | 
						|
    translation[distribution.axis] = pos - box[start];
 | 
						|
 | 
						|
    pos += step;
 | 
						|
    pos += box[extent];
 | 
						|
 | 
						|
    return group.map((element) =>
 | 
						|
      newElementWith(element, {
 | 
						|
        x: element.x + translation.x,
 | 
						|
        y: element.y + translation.y,
 | 
						|
      }),
 | 
						|
    );
 | 
						|
  });
 | 
						|
};
 |