Moved selection in element, refactor common

This commit is contained in:
Marcel Mraz 2025-03-19 15:26:05 +01:00
parent e7a0a7e0b7
commit dfd48c221c
No known key found for this signature in database
GPG key ID: 4EBD6E62DC830CD2
19 changed files with 67 additions and 110 deletions

View file

@ -4,6 +4,8 @@ import {
type LocalPoint,
} from "@excalidraw/math";
import type { NullableGridSize } from "@excalidraw/excalidraw/types";
export const getSizeFromPoints = (
points: readonly (GlobalPoint | LocalPoint)[],
) => {
@ -61,3 +63,18 @@ export const rescalePoints = <Point extends GlobalPoint | LocalPoint>(
return nextPoints;
};
// TODO: Rounding this point causes some shake when free drawing
export const getGridPoint = (
x: number,
y: number,
gridSize: NullableGridSize,
): [number, number] => {
if (gridSize) {
return [
Math.round(x / gridSize) * gridSize,
Math.round(y / gridSize) * gridSize,
];
}
return [x, y];
};

View file

@ -1206,59 +1206,3 @@ export const escapeDoubleQuotes = (str: string) => {
export const castArray = <T>(value: T | T[]): T[] =>
Array.isArray(value) ? value : [value];
// TODO_SEP: perhaps could be refactored away
// TODO: Rounding this point causes some shake when free drawing
export const getGridPoint = (
x: number,
y: number,
gridSize: NullableGridSize,
): [number, number] => {
if (gridSize) {
return [
Math.round(x / gridSize) * gridSize,
Math.round(y / gridSize) * gridSize,
];
}
return [x, y];
};
export const elementsAreInSameGroup = (
elements: readonly ExcalidrawElement[],
) => {
const allGroups = elements.flatMap((element) => element.groupIds);
const groupCount = new Map<string, number>();
let maxGroup = 0;
for (const group of allGroups) {
groupCount.set(group, (groupCount.get(group) ?? 0) + 1);
if (groupCount.get(group)! > maxGroup) {
maxGroup = groupCount.get(group)!;
}
}
return maxGroup === elements.length;
};
export const isInGroup = (element: NonDeletedExcalidrawElement) => {
return element.groupIds.length > 0;
};
export const getNewGroupIdsForDuplication = (
groupIds: ExcalidrawElement["groupIds"],
editingGroupId: AppState["editingGroupId"],
mapper: (groupId: GroupId) => GroupId,
) => {
const copy = [...groupIds];
const positionOfEditingGroupId = editingGroupId
? groupIds.indexOf(editingGroupId)
: -1;
const endIndex =
positionOfEditingGroupId > -1 ? positionOfEditingGroupId : groupIds.length;
for (let index = 0; index < endIndex; index++) {
copy[index] = mapper(copy[index]);
}
return copy;
};

View file

@ -12,7 +12,9 @@ import type {
OrderedExcalidrawElement,
} from "@excalidraw/element/types";
import { InvalidFractionalIndexError } from "../../excalidraw/errors";
export class InvalidFractionalIndexError extends Error {
public code = "ELEMENT_HAS_INVALID_INDEX" as const;
}
/**
* Envisioned relation between array order and fractional indices:

View file

@ -5,11 +5,6 @@ import {
elementsOverlappingBBox,
} from "@excalidraw/utils";
import {
getElementsWithinSelection,
getSelectedElements,
} from "@excalidraw/excalidraw/scene";
import { getElementAbsoluteCoords, isTextElement } from "@excalidraw/element";
import type {
@ -31,6 +26,7 @@ import type {
import type { ReadonlySetLike } from "@excalidraw/common/utility-types";
import { getElementsWithinSelection, getSelectedElements } from "./selection";
import { getElementsInGroup, selectGroupsFromGivenElements } from "./groups";
import { getElementLineSegments, getCommonBounds } from "./bounds";

View file

@ -1,8 +1,5 @@
import { getBoundTextElement } from "@excalidraw/element/textElement";
import { getSelectedElements } from "@excalidraw/excalidraw/scene";
import { makeNextSelectedElementIds } from "@excalidraw/excalidraw/scene/selection";
import type {
GroupId,
ExcalidrawElement,
@ -19,6 +16,8 @@ import type {
} from "@excalidraw/excalidraw/types";
import type { Mutable } from "@excalidraw/common/utility-types";
import { makeNextSelectedElementIds, getSelectedElements } from "./selection";
export const selectGroup = (
groupId: GroupId,
appState: InteractiveCanvasAppState,

View file

@ -11,7 +11,6 @@ import {
import {
MIN_FONT_SIZE,
SHIFT_LOCKING_ANGLE,
isInGroup,
rescalePoints,
getFontString,
} from "@excalidraw/common";
@ -59,6 +58,8 @@ import {
isTextElement,
} from "./typeChecks";
import { isInGroup } from "./groups";
import type { BoundingBox } from "./bounds";
import type {
MaybeTransformHandleType,

View file

@ -1,17 +1,3 @@
import {
getElementAbsoluteCoords,
getElementBounds,
} from "@excalidraw/element/bounds";
import { isElementInViewport } from "@excalidraw/element/sizeHelpers";
import {
isBoundToContainer,
isFrameLikeElement,
} from "@excalidraw/element/typeChecks";
import {
elementOverlapsWithFrame,
getContainingFrame,
getFrameChildren,
} from "@excalidraw/element/frame";
import { isShallowEqual } from "@excalidraw/common";
import type {
@ -21,7 +7,19 @@ import type {
NonDeletedExcalidrawElement,
} from "@excalidraw/element/types";
import type { AppState, InteractiveCanvasAppState } from "../types";
import type {
AppState,
InteractiveCanvasAppState,
} from "@excalidraw/excalidraw/types";
import { getElementAbsoluteCoords, getElementBounds } from "./bounds";
import { isElementInViewport } from "./sizeHelpers";
import { isBoundToContainer, isFrameLikeElement } from "./typeChecks";
import {
elementOverlapsWithFrame,
getContainingFrame,
getFrameChildren,
} from "./frame";
/**
* Frames and their containing elements are not to be selected at the same time.

View file

@ -1,7 +1,7 @@
import { getSelectedElements } from "@excalidraw/excalidraw/scene";
import type { UIAppState } from "@excalidraw/excalidraw/types";
import { getSelectedElements } from "./selection";
import type { NonDeletedExcalidrawElement } from "./types";
export const showSelectedShapeActions = (

View file

@ -1,4 +1,4 @@
import { makeNextSelectedElementIds } from "./selection";
import { makeNextSelectedElementIds } from "../src/selection";
describe("makeNextSelectedElementIds", () => {
const _makeNextSelectedElementIds = (

View file

@ -39,6 +39,11 @@ import {
getElementsInGroup,
} from "@excalidraw/element/groups";
import {
excludeElementsInFramesFromSelection,
getSelectedElements,
} from "@excalidraw/element/selection";
import type { ExcalidrawElement } from "@excalidraw/element/types";
import { ToolButton } from "../components/ToolButton";
@ -46,10 +51,6 @@ import { DuplicateIcon } from "../components/icons";
import { t } from "../i18n";
import { isSomeElementSelected } from "../scene";
import {
excludeElementsInFramesFromSelection,
getSelectedElements,
} from "../scene/selection";
import { CaptureUpdateAction } from "../store";
import { register } from "./register";

View file

@ -289,6 +289,11 @@ import {
syncMovedIndices,
} from "@excalidraw/element/fractionalIndex";
import {
excludeElementsInFramesFromSelection,
makeNextSelectedElementIds,
} from "@excalidraw/element/selection";
import type { LocalPoint, Radians } from "@excalidraw/math";
import type {
@ -420,10 +425,6 @@ import {
import { Fonts } from "../fonts";
import { getLineHeight } from "../fonts/FontMetadata";
import {
excludeElementsInFramesFromSelection,
makeNextSelectedElementIds,
} from "../scene/selection";
import { editorJotaiStore } from "../editor-jotai";
import { ImageSceneDataError } from "../errors";
import {

View file

@ -1,5 +1,3 @@
import { isInGroup } from "@excalidraw/common";
import { degreesToRadians, radiansToDegrees } from "@excalidraw/math";
import { mutateElement } from "@excalidraw/element/mutateElement";
@ -7,6 +5,8 @@ import { mutateElement } from "@excalidraw/element/mutateElement";
import { getBoundTextElement } from "@excalidraw/element/textElement";
import { isArrowElement } from "@excalidraw/element/typeChecks";
import { isInGroup } from "@excalidraw/element/groups";
import type { Degrees } from "@excalidraw/math";
import type { ExcalidrawElement } from "@excalidraw/element/types";

View file

@ -1,10 +1,10 @@
import { isInGroup } from "@excalidraw/common";
import { isTextElement, redrawTextBoundingBox } from "@excalidraw/element";
import { mutateElement } from "@excalidraw/element/mutateElement";
import { getBoundTextElement } from "@excalidraw/element/textElement";
import { hasBoundTextElement } from "@excalidraw/element/typeChecks";
import { isInGroup } from "@excalidraw/element/groups";
import type {
ExcalidrawElement,
ExcalidrawTextElement,

View file

@ -5,7 +5,9 @@ import { vi } from "vitest";
import { getCommonBounds, isTextElement } from "@excalidraw/element";
import { setDateTimeForTests, isInGroup, reseed } from "@excalidraw/common";
import { setDateTimeForTests, reseed } from "@excalidraw/common";
import { isInGroup } from "@excalidraw/element/groups";
import type { Degrees } from "@excalidraw/math";

View file

@ -1,4 +1,3 @@
import { isInGroup } from "@excalidraw/common";
import { pointFrom, pointRotateRads } from "@excalidraw/math";
import {
@ -16,6 +15,7 @@ import {
import {
getSelectedGroupIds,
getElementsInGroup,
isInGroup,
} from "@excalidraw/element/groups";
import type { Radians } from "@excalidraw/math";

View file

@ -33,10 +33,6 @@ export class ImageSceneDataError extends Error {
}
}
export class InvalidFractionalIndexError extends Error {
public code = "ELEMENT_HAS_INVALID_INDEX" as const;
}
type WorkerErrorCodes = "WORKER_URL_NOT_DEFINED" | "WORKER_IN_THE_MAIN_CHUNK";
export class WorkerUrlNotDefinedError extends Error {

View file

@ -17,6 +17,8 @@ import {
validateFractionalIndices,
} from "@excalidraw/element/fractionalIndex";
import { getSelectedElements } from "@excalidraw/element/selection";
import type { LinearElementEditor } from "@excalidraw/element/linearElementEditor";
import type {
ExcalidrawElement,
@ -32,8 +34,6 @@ import type {
import type { Assert, SameType } from "@excalidraw/common/utility-types";
import { getSelectedElements } from "./selection";
import type { AppState } from "../types";
type ElementIdKey = InstanceType<typeof LinearElementEditor>["elementId"];

View file

@ -4,7 +4,7 @@ export {
getCommonAttributeOfSelectedElements,
getSelectedElements,
getTargetElements,
} from "./selection";
} from "@excalidraw/element/selection";
export { calculateScrollCenter } from "./scroll";
export {
hasBackground,

View file

@ -20,6 +20,11 @@ import {
import { getMaximumGroups } from "@excalidraw/element/groups";
import {
getSelectedElements,
getVisibleAndNonSelectedElements,
} from "@excalidraw/element/selection";
import type { InclusiveRange } from "@excalidraw/math";
import type { Bounds } from "@excalidraw/element/bounds";
@ -30,11 +35,6 @@ import type {
NonDeletedExcalidrawElement,
} from "@excalidraw/element/types";
import {
getSelectedElements,
getVisibleAndNonSelectedElements,
} from "./scene/selection";
import type {
AppClassProperties,
AppState,