Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bfa4941ad1 |
@@ -12,10 +12,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js 18.x
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 18.x
|
||||||
- name: Set up publish access
|
- name: Set up publish access
|
||||||
run: |
|
run: |
|
||||||
npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
|
npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js 18.x
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 18.x
|
||||||
|
|
||||||
- name: Install and lint
|
- name: Install and lint
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.PUSH_TRANSLATIONS_COVERAGE_PAT }}
|
token: ${{ secrets.PUSH_TRANSLATIONS_COVERAGE_PAT }}
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js 18.x
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 18.x
|
||||||
|
|
||||||
- name: Create report file
|
- name: Create report file
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js 18.x
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 18.x
|
||||||
- name: Install and build
|
- name: Install and build
|
||||||
run: |
|
run: |
|
||||||
yarn --frozen-lockfile
|
yarn --frozen-lockfile
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js 18.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 18.x
|
||||||
- name: Install in packages/excalidraw
|
- name: Install in packages/excalidraw
|
||||||
run: yarn
|
run: yarn
|
||||||
working-directory: packages/excalidraw
|
working-directory: packages/excalidraw
|
||||||
|
|||||||
@@ -746,10 +746,7 @@ class Collab extends PureComponent<CollabProps, CollabState> {
|
|||||||
): ReconciledExcalidrawElement[] => {
|
): ReconciledExcalidrawElement[] => {
|
||||||
const localElements = this.getSceneElementsIncludingDeleted();
|
const localElements = this.getSceneElementsIncludingDeleted();
|
||||||
const appState = this.excalidrawAPI.getAppState();
|
const appState = this.excalidrawAPI.getAppState();
|
||||||
const restoredRemoteElements = restoreElements(
|
const restoredRemoteElements = restoreElements(remoteElements, null);
|
||||||
remoteElements,
|
|
||||||
this.excalidrawAPI.getSceneElementsMapIncludingDeleted(),
|
|
||||||
);
|
|
||||||
const reconciledElements = reconcileElements(
|
const reconciledElements = reconcileElements(
|
||||||
localElements,
|
localElements,
|
||||||
restoredRemoteElements as RemoteExcalidrawElement[],
|
restoredRemoteElements as RemoteExcalidrawElement[],
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* x and y position of top left corner, x and y position of bottom right corner
|
|
||||||
*/
|
|
||||||
export type Bounds = readonly [
|
|
||||||
minX: number,
|
|
||||||
minY: number,
|
|
||||||
maxX: number,
|
|
||||||
maxY: number,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const isBounds = (box: unknown): box is Bounds =>
|
|
||||||
Array.isArray(box) &&
|
|
||||||
box.length === 4 &&
|
|
||||||
typeof box[0] === "number" &&
|
|
||||||
typeof box[1] === "number" &&
|
|
||||||
typeof box[2] === "number" &&
|
|
||||||
typeof box[3] === "number";
|
|
||||||
@@ -108,13 +108,6 @@ export const CLASSES = {
|
|||||||
FRAME_NAME: "frame-name",
|
FRAME_NAME: "frame-name",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FONT_SIZES = {
|
|
||||||
sm: 16,
|
|
||||||
md: 20,
|
|
||||||
lg: 28,
|
|
||||||
xl: 36,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const CJK_HAND_DRAWN_FALLBACK_FONT = "Xiaolai";
|
export const CJK_HAND_DRAWN_FALLBACK_FONT = "Xiaolai";
|
||||||
export const WINDOWS_EMOJI_FALLBACK_FONT = "Segoe UI Emoji";
|
export const WINDOWS_EMOJI_FALLBACK_FONT = "Segoe UI Emoji";
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import mobile from "is-mobile";
|
||||||
|
|
||||||
export type StylesPanelMode = "compact" | "full" | "mobile";
|
export type StylesPanelMode = "compact" | "full" | "mobile";
|
||||||
|
|
||||||
export type EditorInterface = Readonly<{
|
export type EditorInterface = Readonly<{
|
||||||
@@ -139,12 +141,13 @@ export const getFormFactor = (
|
|||||||
editorWidth: number,
|
editorWidth: number,
|
||||||
editorHeight: number,
|
editorHeight: number,
|
||||||
): EditorInterface["formFactor"] => {
|
): EditorInterface["formFactor"] => {
|
||||||
if (isMobileBreakpoint(editorWidth, editorHeight)) {
|
if (mobile()) {
|
||||||
return "phone";
|
return "phone";
|
||||||
}
|
} else if (mobile({ tablet: true })) {
|
||||||
|
|
||||||
if (isTabletBreakpoint(editorWidth, editorHeight)) {
|
|
||||||
return "tablet";
|
return "tablet";
|
||||||
|
} else if (isMobileBreakpoint(editorWidth, editorHeight)) {
|
||||||
|
// NOTE: Very small editor sizes should be treated as phone
|
||||||
|
return "phone";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "desktop";
|
return "desktop";
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export * from "./binary-heap";
|
export * from "./binary-heap";
|
||||||
export * from "./bounds";
|
|
||||||
export * from "./colors";
|
export * from "./colors";
|
||||||
export * from "./constants";
|
export * from "./constants";
|
||||||
export * from "./font-metadata";
|
export * from "./font-metadata";
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import {
|
|||||||
type LocalPoint,
|
type LocalPoint,
|
||||||
} from "@excalidraw/math";
|
} from "@excalidraw/math";
|
||||||
|
|
||||||
|
import { isBounds } from "@excalidraw/element";
|
||||||
|
|
||||||
import type { Curve } from "@excalidraw/math";
|
import type { Curve } from "@excalidraw/math";
|
||||||
import type { LineSegment } from "@excalidraw/utils";
|
import type { LineSegment } from "@excalidraw/utils";
|
||||||
|
|
||||||
import { type Bounds, isBounds } from "./bounds";
|
import type { Bounds } from "@excalidraw/element";
|
||||||
|
|
||||||
// The global data holder to collect the debug operations
|
// The global data holder to collect the debug operations
|
||||||
declare global {
|
declare global {
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ import {
|
|||||||
} from "@excalidraw/math";
|
} from "@excalidraw/math";
|
||||||
|
|
||||||
import type { LineSegment, LocalPoint, Radians } from "@excalidraw/math";
|
import type { LineSegment, LocalPoint, Radians } from "@excalidraw/math";
|
||||||
|
|
||||||
import type { AppState } from "@excalidraw/excalidraw/types";
|
import type { AppState } from "@excalidraw/excalidraw/types";
|
||||||
|
|
||||||
import type { MapEntry, Mutable } from "@excalidraw/common/utility-types";
|
import type { MapEntry, Mutable } from "@excalidraw/common/utility-types";
|
||||||
import type { Bounds } from "@excalidraw/common";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
doBoundsIntersect,
|
doBoundsIntersect,
|
||||||
@@ -53,21 +54,17 @@ import {
|
|||||||
isBindableElement,
|
isBindableElement,
|
||||||
isBoundToContainer,
|
isBoundToContainer,
|
||||||
isElbowArrow,
|
isElbowArrow,
|
||||||
isRectangularElement,
|
|
||||||
isRectanguloidElement,
|
isRectanguloidElement,
|
||||||
isTextElement,
|
isTextElement,
|
||||||
} from "./typeChecks";
|
} from "./typeChecks";
|
||||||
|
|
||||||
import { aabbForElement, elementCenterPoint } from "./bounds";
|
import { aabbForElement, elementCenterPoint } from "./bounds";
|
||||||
import { updateElbowArrowPoints } from "./elbowArrow";
|
import { updateElbowArrowPoints } from "./elbowArrow";
|
||||||
import {
|
import { projectFixedPointOntoDiagonal } from "./utils";
|
||||||
deconstructDiamondElement,
|
|
||||||
deconstructRectanguloidElement,
|
|
||||||
projectFixedPointOntoDiagonal,
|
|
||||||
} from "./utils";
|
|
||||||
|
|
||||||
import type { Scene } from "./Scene";
|
import type { Scene } from "./Scene";
|
||||||
|
|
||||||
|
import type { Bounds } from "./bounds";
|
||||||
import type { ElementUpdate } from "./mutateElement";
|
import type { ElementUpdate } from "./mutateElement";
|
||||||
import type {
|
import type {
|
||||||
BindMode,
|
BindMode,
|
||||||
@@ -76,7 +73,6 @@ import type {
|
|||||||
ExcalidrawBindableElement,
|
ExcalidrawBindableElement,
|
||||||
ExcalidrawElbowArrowElement,
|
ExcalidrawElbowArrowElement,
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
ExcalidrawRectanguloidElement,
|
|
||||||
ExcalidrawTextElement,
|
ExcalidrawTextElement,
|
||||||
FixedPoint,
|
FixedPoint,
|
||||||
FixedPointBinding,
|
FixedPointBinding,
|
||||||
@@ -150,22 +146,17 @@ export const isBindingEnabled = (appState: AppState): boolean => {
|
|||||||
export const bindOrUnbindBindingElement = (
|
export const bindOrUnbindBindingElement = (
|
||||||
arrow: NonDeleted<ExcalidrawArrowElement>,
|
arrow: NonDeleted<ExcalidrawArrowElement>,
|
||||||
draggingPoints: PointsPositionUpdates,
|
draggingPoints: PointsPositionUpdates,
|
||||||
scenePointerX: number,
|
|
||||||
scenePointerY: number,
|
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
opts?: {
|
opts?: {
|
||||||
newArrow?: boolean;
|
newArrow?: boolean;
|
||||||
altKey?: boolean;
|
altKey?: boolean;
|
||||||
angleLocked?: boolean;
|
|
||||||
initialBinding?: boolean;
|
initialBinding?: boolean;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const { start, end } = getBindingStrategyForDraggingBindingElementEndpoints(
|
const { start, end } = getBindingStrategyForDraggingBindingElementEndpoints(
|
||||||
arrow,
|
arrow,
|
||||||
draggingPoints,
|
draggingPoints,
|
||||||
scenePointerX,
|
|
||||||
scenePointerY,
|
|
||||||
scene.getNonDeletedElementsMap(),
|
scene.getNonDeletedElementsMap(),
|
||||||
scene.getNonDeletedElements(),
|
scene.getNonDeletedElements(),
|
||||||
appState,
|
appState,
|
||||||
@@ -565,14 +556,12 @@ const bindingStrategyForSimpleArrowEndpointDragging_complex = (
|
|||||||
export const getBindingStrategyForDraggingBindingElementEndpoints = (
|
export const getBindingStrategyForDraggingBindingElementEndpoints = (
|
||||||
arrow: NonDeleted<ExcalidrawArrowElement>,
|
arrow: NonDeleted<ExcalidrawArrowElement>,
|
||||||
draggingPoints: PointsPositionUpdates,
|
draggingPoints: PointsPositionUpdates,
|
||||||
screenPointerX: number,
|
|
||||||
screenPointerY: number,
|
|
||||||
elementsMap: NonDeletedSceneElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
opts?: {
|
opts?: {
|
||||||
newArrow?: boolean;
|
newArrow?: boolean;
|
||||||
angleLocked?: boolean;
|
shiftKey?: boolean;
|
||||||
altKey?: boolean;
|
altKey?: boolean;
|
||||||
finalize?: boolean;
|
finalize?: boolean;
|
||||||
initialBinding?: boolean;
|
initialBinding?: boolean;
|
||||||
@@ -593,8 +582,6 @@ export const getBindingStrategyForDraggingBindingElementEndpoints = (
|
|||||||
return getBindingStrategyForDraggingBindingElementEndpoints_simple(
|
return getBindingStrategyForDraggingBindingElementEndpoints_simple(
|
||||||
arrow,
|
arrow,
|
||||||
draggingPoints,
|
draggingPoints,
|
||||||
screenPointerX,
|
|
||||||
screenPointerY,
|
|
||||||
elementsMap,
|
elementsMap,
|
||||||
elements,
|
elements,
|
||||||
appState,
|
appState,
|
||||||
@@ -605,14 +592,12 @@ export const getBindingStrategyForDraggingBindingElementEndpoints = (
|
|||||||
const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
||||||
arrow: NonDeleted<ExcalidrawArrowElement>,
|
arrow: NonDeleted<ExcalidrawArrowElement>,
|
||||||
draggingPoints: PointsPositionUpdates,
|
draggingPoints: PointsPositionUpdates,
|
||||||
scenePointerX: number,
|
|
||||||
scenePointerY: number,
|
|
||||||
elementsMap: NonDeletedSceneElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
opts?: {
|
opts?: {
|
||||||
newArrow?: boolean;
|
newArrow?: boolean;
|
||||||
angleLocked?: boolean;
|
shiftKey?: boolean;
|
||||||
altKey?: boolean;
|
altKey?: boolean;
|
||||||
finalize?: boolean;
|
finalize?: boolean;
|
||||||
initialBinding?: boolean;
|
initialBinding?: boolean;
|
||||||
@@ -684,15 +669,7 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
elementsMap,
|
elementsMap,
|
||||||
(e) => maxBindingDistance_simple(appState.zoom),
|
(e) => maxBindingDistance_simple(appState.zoom),
|
||||||
);
|
);
|
||||||
const pointInElement =
|
const pointInElement = hit && isPointInElement(globalPoint, hit, elementsMap);
|
||||||
hit &&
|
|
||||||
(opts?.angleLocked
|
|
||||||
? isPointInElement(
|
|
||||||
pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
|
|
||||||
hit,
|
|
||||||
elementsMap,
|
|
||||||
)
|
|
||||||
: isPointInElement(globalPoint, hit, elementsMap));
|
|
||||||
const otherBindableElement = otherBinding
|
const otherBindableElement = otherBinding
|
||||||
? (elementsMap.get(
|
? (elementsMap.get(
|
||||||
otherBinding.elementId,
|
otherBinding.elementId,
|
||||||
@@ -793,12 +770,6 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
}
|
}
|
||||||
: { mode: null };
|
: { mode: null };
|
||||||
|
|
||||||
const otherEndpoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
|
||||||
arrow,
|
|
||||||
startDragged ? -1 : 0,
|
|
||||||
elementsMap,
|
|
||||||
);
|
|
||||||
|
|
||||||
const other: BindingStrategy =
|
const other: BindingStrategy =
|
||||||
otherBindableElement &&
|
otherBindableElement &&
|
||||||
!otherFocusPointIsInElement &&
|
!otherFocusPointIsInElement &&
|
||||||
@@ -808,19 +779,6 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
element: otherBindableElement,
|
element: otherBindableElement,
|
||||||
focusPoint: appState.selectedLinearElement.initialState.altFocusPoint,
|
focusPoint: appState.selectedLinearElement.initialState.altFocusPoint,
|
||||||
}
|
}
|
||||||
: opts?.angleLocked && otherBindableElement
|
|
||||||
? {
|
|
||||||
mode: "orbit",
|
|
||||||
element: otherBindableElement,
|
|
||||||
focusPoint:
|
|
||||||
projectFixedPointOntoDiagonal(
|
|
||||||
arrow,
|
|
||||||
otherEndpoint,
|
|
||||||
otherBindableElement,
|
|
||||||
startDragged ? "end" : "start",
|
|
||||||
elementsMap,
|
|
||||||
) || otherEndpoint,
|
|
||||||
}
|
|
||||||
: { mode: undefined };
|
: { mode: undefined };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -966,8 +924,6 @@ export const bindOrUnbindBindingElements = (
|
|||||||
bindOrUnbindBindingElement(
|
bindOrUnbindBindingElement(
|
||||||
arrow,
|
arrow,
|
||||||
new Map(), // No dragging points in this case
|
new Map(), // No dragging points in this case
|
||||||
Infinity,
|
|
||||||
Infinity,
|
|
||||||
scene,
|
scene,
|
||||||
appState,
|
appState,
|
||||||
);
|
);
|
||||||
@@ -1170,14 +1126,7 @@ export const updateBindings = (
|
|||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
if (isArrowElement(latestElement)) {
|
if (isArrowElement(latestElement)) {
|
||||||
bindOrUnbindBindingElement(
|
bindOrUnbindBindingElement(latestElement, new Map(), scene, appState);
|
||||||
latestElement,
|
|
||||||
new Map(),
|
|
||||||
Infinity,
|
|
||||||
Infinity,
|
|
||||||
scene,
|
|
||||||
appState,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
updateBoundElements(latestElement, scene, {
|
updateBoundElements(latestElement, scene, {
|
||||||
...options,
|
...options,
|
||||||
@@ -2340,434 +2289,3 @@ export const normalizeFixedPoint = <T extends FixedPoint | null>(
|
|||||||
}
|
}
|
||||||
return fixedPoint as any as T extends null ? null : FixedPoint;
|
return fixedPoint as any as T extends null ? null : FixedPoint;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Side =
|
|
||||||
| "top"
|
|
||||||
| "top-right"
|
|
||||||
| "right"
|
|
||||||
| "bottom-right"
|
|
||||||
| "bottom"
|
|
||||||
| "bottom-left"
|
|
||||||
| "left"
|
|
||||||
| "top-left";
|
|
||||||
type ShapeType = "rectangle" | "ellipse" | "diamond";
|
|
||||||
const getShapeType = (element: ExcalidrawBindableElement): ShapeType => {
|
|
||||||
if (element.type === "ellipse" || element.type === "diamond") {
|
|
||||||
return element.type;
|
|
||||||
}
|
|
||||||
return "rectangle";
|
|
||||||
};
|
|
||||||
|
|
||||||
interface SectorConfig {
|
|
||||||
// center angle of the sector in degrees
|
|
||||||
centerAngle: number;
|
|
||||||
// width of the sector in degrees
|
|
||||||
sectorWidth: number;
|
|
||||||
side: Side;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define sector configurations for different shape types
|
|
||||||
const SHAPE_CONFIGS: Record<ShapeType, SectorConfig[]> = {
|
|
||||||
// rectangle: 15° corners, 75° edges
|
|
||||||
rectangle: [
|
|
||||||
{ centerAngle: 0, sectorWidth: 75, side: "right" },
|
|
||||||
{ centerAngle: 45, sectorWidth: 15, side: "bottom-right" },
|
|
||||||
{ centerAngle: 90, sectorWidth: 75, side: "bottom" },
|
|
||||||
{ centerAngle: 135, sectorWidth: 15, side: "bottom-left" },
|
|
||||||
{ centerAngle: 180, sectorWidth: 75, side: "left" },
|
|
||||||
{ centerAngle: 225, sectorWidth: 15, side: "top-left" },
|
|
||||||
{ centerAngle: 270, sectorWidth: 75, side: "top" },
|
|
||||||
{ centerAngle: 315, sectorWidth: 15, side: "top-right" },
|
|
||||||
],
|
|
||||||
|
|
||||||
// diamond: 15° vertices, 75° edges
|
|
||||||
diamond: [
|
|
||||||
{ centerAngle: 0, sectorWidth: 15, side: "right" },
|
|
||||||
{ centerAngle: 45, sectorWidth: 75, side: "bottom-right" },
|
|
||||||
{ centerAngle: 90, sectorWidth: 15, side: "bottom" },
|
|
||||||
{ centerAngle: 135, sectorWidth: 75, side: "bottom-left" },
|
|
||||||
{ centerAngle: 180, sectorWidth: 15, side: "left" },
|
|
||||||
{ centerAngle: 225, sectorWidth: 75, side: "top-left" },
|
|
||||||
{ centerAngle: 270, sectorWidth: 15, side: "top" },
|
|
||||||
{ centerAngle: 315, sectorWidth: 75, side: "top-right" },
|
|
||||||
],
|
|
||||||
|
|
||||||
// ellipse: 15° cardinal points, 75° diagonals
|
|
||||||
ellipse: [
|
|
||||||
{ centerAngle: 0, sectorWidth: 15, side: "right" },
|
|
||||||
{ centerAngle: 45, sectorWidth: 75, side: "bottom-right" },
|
|
||||||
{ centerAngle: 90, sectorWidth: 15, side: "bottom" },
|
|
||||||
{ centerAngle: 135, sectorWidth: 75, side: "bottom-left" },
|
|
||||||
{ centerAngle: 180, sectorWidth: 15, side: "left" },
|
|
||||||
{ centerAngle: 225, sectorWidth: 75, side: "top-left" },
|
|
||||||
{ centerAngle: 270, sectorWidth: 15, side: "top" },
|
|
||||||
{ centerAngle: 315, sectorWidth: 75, side: "top-right" },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSectorBoundaries = (
|
|
||||||
config: SectorConfig[],
|
|
||||||
): Array<{ start: number; end: number; side: Side }> => {
|
|
||||||
return config.map((sector, index) => {
|
|
||||||
const halfWidth = sector.sectorWidth / 2;
|
|
||||||
let start = sector.centerAngle - halfWidth;
|
|
||||||
let end = sector.centerAngle + halfWidth;
|
|
||||||
|
|
||||||
// normalize angles to [0, 360) range
|
|
||||||
start = ((start % 360) + 360) % 360;
|
|
||||||
end = ((end % 360) + 360) % 360;
|
|
||||||
|
|
||||||
return { start, end, side: sector.side };
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// determine which side a point falls into using adaptive sectors
|
|
||||||
const getShapeSideAdaptive = (
|
|
||||||
fixedPoint: FixedPoint,
|
|
||||||
shapeType: ShapeType,
|
|
||||||
): Side => {
|
|
||||||
const [x, y] = fixedPoint;
|
|
||||||
|
|
||||||
// convert to centered coordinates
|
|
||||||
const centerX = x - 0.5;
|
|
||||||
const centerY = y - 0.5;
|
|
||||||
|
|
||||||
// calculate angle
|
|
||||||
let angle = Math.atan2(centerY, centerX);
|
|
||||||
if (angle < 0) {
|
|
||||||
angle += 2 * Math.PI;
|
|
||||||
}
|
|
||||||
const degrees = (angle * 180) / Math.PI;
|
|
||||||
|
|
||||||
// get sector configuration for this shape type
|
|
||||||
const config = SHAPE_CONFIGS[shapeType];
|
|
||||||
const boundaries = getSectorBoundaries(config);
|
|
||||||
|
|
||||||
// find which sector the angle falls into
|
|
||||||
for (const boundary of boundaries) {
|
|
||||||
if (boundary.start <= boundary.end) {
|
|
||||||
// Normal case: sector doesn't cross 0°
|
|
||||||
if (degrees >= boundary.start && degrees <= boundary.end) {
|
|
||||||
return boundary.side;
|
|
||||||
}
|
|
||||||
} else if (degrees >= boundary.start || degrees <= boundary.end) {
|
|
||||||
return boundary.side;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fallback - find nearest sector center
|
|
||||||
let minDiff = Infinity;
|
|
||||||
let nearestSide = config[0].side;
|
|
||||||
|
|
||||||
for (const sector of config) {
|
|
||||||
let diff = Math.abs(degrees - sector.centerAngle);
|
|
||||||
// handle wraparound
|
|
||||||
if (diff > 180) {
|
|
||||||
diff = 360 - diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diff < minDiff) {
|
|
||||||
minDiff = diff;
|
|
||||||
nearestSide = sector.side;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nearestSide;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getBindingSideMidPoint = (
|
|
||||||
binding: FixedPointBinding,
|
|
||||||
elementsMap: ElementsMap,
|
|
||||||
) => {
|
|
||||||
const bindableElement = elementsMap.get(binding.elementId);
|
|
||||||
if (
|
|
||||||
!bindableElement ||
|
|
||||||
bindableElement.isDeleted ||
|
|
||||||
!isBindableElement(bindableElement)
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const center = elementCenterPoint(bindableElement, elementsMap);
|
|
||||||
const shapeType = getShapeType(bindableElement);
|
|
||||||
const side = getShapeSideAdaptive(
|
|
||||||
normalizeFixedPoint(binding.fixedPoint),
|
|
||||||
shapeType,
|
|
||||||
);
|
|
||||||
|
|
||||||
// small offset to avoid precision issues in elbow
|
|
||||||
const OFFSET = 0.01;
|
|
||||||
|
|
||||||
if (bindableElement.type === "diamond") {
|
|
||||||
const [sides, corners] = deconstructDiamondElement(bindableElement);
|
|
||||||
const [bottomRight, bottomLeft, topLeft, topRight] = sides;
|
|
||||||
|
|
||||||
let x: number;
|
|
||||||
let y: number;
|
|
||||||
switch (side) {
|
|
||||||
case "left": {
|
|
||||||
// left vertex - use the center of the left corner curve
|
|
||||||
if (corners.length >= 3) {
|
|
||||||
const leftCorner = corners[2];
|
|
||||||
const midPoint = leftCorner[1];
|
|
||||||
x = midPoint[0] - OFFSET;
|
|
||||||
y = midPoint[1];
|
|
||||||
} else {
|
|
||||||
// fallback for non-rounded diamond
|
|
||||||
const midPoint = getMidPoint(bottomLeft[1], topLeft[0]);
|
|
||||||
x = midPoint[0] - OFFSET;
|
|
||||||
y = midPoint[1];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "right": {
|
|
||||||
if (corners.length >= 1) {
|
|
||||||
const rightCorner = corners[0];
|
|
||||||
const midPoint = rightCorner[1];
|
|
||||||
x = midPoint[0] + OFFSET;
|
|
||||||
y = midPoint[1];
|
|
||||||
} else {
|
|
||||||
const midPoint = getMidPoint(topRight[1], bottomRight[0]);
|
|
||||||
x = midPoint[0] + OFFSET;
|
|
||||||
y = midPoint[1];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "top": {
|
|
||||||
if (corners.length >= 4) {
|
|
||||||
const topCorner = corners[3];
|
|
||||||
const midPoint = topCorner[1];
|
|
||||||
x = midPoint[0];
|
|
||||||
y = midPoint[1] - OFFSET;
|
|
||||||
} else {
|
|
||||||
const midPoint = getMidPoint(topLeft[1], topRight[0]);
|
|
||||||
x = midPoint[0];
|
|
||||||
y = midPoint[1] - OFFSET;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "bottom": {
|
|
||||||
if (corners.length >= 2) {
|
|
||||||
const bottomCorner = corners[1];
|
|
||||||
const midPoint = bottomCorner[1];
|
|
||||||
x = midPoint[0];
|
|
||||||
y = midPoint[1] + OFFSET;
|
|
||||||
} else {
|
|
||||||
const midPoint = getMidPoint(bottomRight[1], bottomLeft[0]);
|
|
||||||
x = midPoint[0];
|
|
||||||
y = midPoint[1] + OFFSET;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "top-right": {
|
|
||||||
const midPoint = getMidPoint(topRight[0], topRight[1]);
|
|
||||||
|
|
||||||
x = midPoint[0] + OFFSET * 0.707;
|
|
||||||
y = midPoint[1] - OFFSET * 0.707;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "bottom-right": {
|
|
||||||
const midPoint = getMidPoint(bottomRight[0], bottomRight[1]);
|
|
||||||
|
|
||||||
x = midPoint[0] + OFFSET * 0.707;
|
|
||||||
y = midPoint[1] + OFFSET * 0.707;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "bottom-left": {
|
|
||||||
const midPoint = getMidPoint(bottomLeft[0], bottomLeft[1]);
|
|
||||||
x = midPoint[0] - OFFSET * 0.707;
|
|
||||||
y = midPoint[1] + OFFSET * 0.707;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "top-left": {
|
|
||||||
const midPoint = getMidPoint(topLeft[0], topLeft[1]);
|
|
||||||
x = midPoint[0] - OFFSET * 0.707;
|
|
||||||
y = midPoint[1] - OFFSET * 0.707;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pointRotateRads(pointFrom(x, y), center, bindableElement.angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bindableElement.type === "ellipse") {
|
|
||||||
const ellipseCenterX = bindableElement.x + bindableElement.width / 2;
|
|
||||||
const ellipseCenterY = bindableElement.y + bindableElement.height / 2;
|
|
||||||
const radiusX = bindableElement.width / 2;
|
|
||||||
const radiusY = bindableElement.height / 2;
|
|
||||||
|
|
||||||
let x: number;
|
|
||||||
let y: number;
|
|
||||||
|
|
||||||
switch (side) {
|
|
||||||
case "top": {
|
|
||||||
x = ellipseCenterX;
|
|
||||||
y = ellipseCenterY - radiusY - OFFSET;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "right": {
|
|
||||||
x = ellipseCenterX + radiusX + OFFSET;
|
|
||||||
y = ellipseCenterY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "bottom": {
|
|
||||||
x = ellipseCenterX;
|
|
||||||
y = ellipseCenterY + radiusY + OFFSET;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "left": {
|
|
||||||
x = ellipseCenterX - radiusX - OFFSET;
|
|
||||||
y = ellipseCenterY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "top-right": {
|
|
||||||
const angle = -Math.PI / 4;
|
|
||||||
const ellipseX = radiusX * Math.cos(angle);
|
|
||||||
const ellipseY = radiusY * Math.sin(angle);
|
|
||||||
x = ellipseCenterX + ellipseX + OFFSET * 0.707;
|
|
||||||
y = ellipseCenterY + ellipseY - OFFSET * 0.707;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "bottom-right": {
|
|
||||||
const angle = Math.PI / 4;
|
|
||||||
const ellipseX = radiusX * Math.cos(angle);
|
|
||||||
const ellipseY = radiusY * Math.sin(angle);
|
|
||||||
x = ellipseCenterX + ellipseX + OFFSET * 0.707;
|
|
||||||
y = ellipseCenterY + ellipseY + OFFSET * 0.707;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "bottom-left": {
|
|
||||||
const angle = (3 * Math.PI) / 4;
|
|
||||||
const ellipseX = radiusX * Math.cos(angle);
|
|
||||||
const ellipseY = radiusY * Math.sin(angle);
|
|
||||||
x = ellipseCenterX + ellipseX - OFFSET * 0.707;
|
|
||||||
y = ellipseCenterY + ellipseY + OFFSET * 0.707;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "top-left": {
|
|
||||||
const angle = (-3 * Math.PI) / 4;
|
|
||||||
const ellipseX = radiusX * Math.cos(angle);
|
|
||||||
const ellipseY = radiusY * Math.sin(angle);
|
|
||||||
x = ellipseCenterX + ellipseX - OFFSET * 0.707;
|
|
||||||
y = ellipseCenterY + ellipseY - OFFSET * 0.707;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pointRotateRads(pointFrom(x, y), center, bindableElement.angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRectangularElement(bindableElement)) {
|
|
||||||
const [sides, corners] = deconstructRectanguloidElement(
|
|
||||||
bindableElement as ExcalidrawRectanguloidElement,
|
|
||||||
);
|
|
||||||
const [top, right, bottom, left] = sides;
|
|
||||||
|
|
||||||
let x: number;
|
|
||||||
let y: number;
|
|
||||||
switch (side) {
|
|
||||||
case "top": {
|
|
||||||
const midPoint = getMidPoint(top[0], top[1]);
|
|
||||||
x = midPoint[0];
|
|
||||||
y = midPoint[1] - OFFSET;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "right": {
|
|
||||||
const midPoint = getMidPoint(right[0], right[1]);
|
|
||||||
x = midPoint[0] + OFFSET;
|
|
||||||
y = midPoint[1];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "bottom": {
|
|
||||||
const midPoint = getMidPoint(bottom[0], bottom[1]);
|
|
||||||
x = midPoint[0];
|
|
||||||
y = midPoint[1] + OFFSET;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "left": {
|
|
||||||
const midPoint = getMidPoint(left[0], left[1]);
|
|
||||||
x = midPoint[0] - OFFSET;
|
|
||||||
y = midPoint[1];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "top-left": {
|
|
||||||
if (corners.length >= 1) {
|
|
||||||
const corner = corners[0];
|
|
||||||
|
|
||||||
const p1 = corner[0];
|
|
||||||
const p2 = corner[3];
|
|
||||||
const midPoint = getMidPoint(p1, p2);
|
|
||||||
|
|
||||||
x = midPoint[0] - OFFSET * 0.707;
|
|
||||||
y = midPoint[1] - OFFSET * 0.707;
|
|
||||||
} else {
|
|
||||||
x = bindableElement.x - OFFSET;
|
|
||||||
y = bindableElement.y - OFFSET;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "top-right": {
|
|
||||||
if (corners.length >= 2) {
|
|
||||||
const corner = corners[1];
|
|
||||||
const p1 = corner[0];
|
|
||||||
const p2 = corner[3];
|
|
||||||
const midPoint = getMidPoint(p1, p2);
|
|
||||||
|
|
||||||
x = midPoint[0] + OFFSET * 0.707;
|
|
||||||
y = midPoint[1] - OFFSET * 0.707;
|
|
||||||
} else {
|
|
||||||
x = bindableElement.x + bindableElement.width + OFFSET;
|
|
||||||
y = bindableElement.y - OFFSET;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "bottom-right": {
|
|
||||||
if (corners.length >= 3) {
|
|
||||||
const corner = corners[2];
|
|
||||||
const p1 = corner[0];
|
|
||||||
const p2 = corner[3];
|
|
||||||
const midPoint = getMidPoint(p1, p2);
|
|
||||||
|
|
||||||
x = midPoint[0] + OFFSET * 0.707;
|
|
||||||
y = midPoint[1] + OFFSET * 0.707;
|
|
||||||
} else {
|
|
||||||
x = bindableElement.x + bindableElement.width + OFFSET;
|
|
||||||
y = bindableElement.y + bindableElement.height + OFFSET;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "bottom-left": {
|
|
||||||
if (corners.length >= 4) {
|
|
||||||
const corner = corners[3];
|
|
||||||
const p1 = corner[0];
|
|
||||||
const p2 = corner[3];
|
|
||||||
const midPoint = getMidPoint(p1, p2);
|
|
||||||
|
|
||||||
x = midPoint[0] - OFFSET * 0.707;
|
|
||||||
y = midPoint[1] + OFFSET * 0.707;
|
|
||||||
} else {
|
|
||||||
x = bindableElement.x - OFFSET;
|
|
||||||
y = bindableElement.y + bindableElement.height + OFFSET;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pointRotateRads(pointFrom(x, y), center, bindableElement.angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMidPoint = (p1: GlobalPoint, p2: GlobalPoint): GlobalPoint => {
|
|
||||||
return pointFrom((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import rough from "roughjs/bin/rough";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
arrayToMap,
|
arrayToMap,
|
||||||
type Bounds,
|
|
||||||
invariant,
|
invariant,
|
||||||
rescalePoints,
|
rescalePoints,
|
||||||
sizeOf,
|
sizeOf,
|
||||||
@@ -79,6 +78,16 @@ export type RectangleBox = {
|
|||||||
|
|
||||||
type MaybeQuadraticSolution = [number | null, number | null] | false;
|
type MaybeQuadraticSolution = [number | null, number | null] | false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* x and y position of top left corner, x and y position of bottom right corner
|
||||||
|
*/
|
||||||
|
export type Bounds = readonly [
|
||||||
|
minX: number,
|
||||||
|
minY: number,
|
||||||
|
maxX: number,
|
||||||
|
maxY: number,
|
||||||
|
];
|
||||||
|
|
||||||
export type SceneBounds = readonly [
|
export type SceneBounds = readonly [
|
||||||
sceneX: number,
|
sceneX: number,
|
||||||
sceneY: number,
|
sceneY: number,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { invariant, isTransparent, type Bounds } from "@excalidraw/common";
|
import { invariant, isTransparent } from "@excalidraw/common";
|
||||||
import {
|
import {
|
||||||
curveIntersectLineSegment,
|
curveIntersectLineSegment,
|
||||||
isPointWithinBounds,
|
isPointWithinBounds,
|
||||||
@@ -29,6 +29,7 @@ import type { FrameNameBounds } from "@excalidraw/excalidraw/types";
|
|||||||
|
|
||||||
import { isPathALoop } from "./utils";
|
import { isPathALoop } from "./utils";
|
||||||
import {
|
import {
|
||||||
|
type Bounds,
|
||||||
doBoundsIntersect,
|
doBoundsIntersect,
|
||||||
elementCenterPoint,
|
elementCenterPoint,
|
||||||
getCenterForBounds,
|
getCenterForBounds,
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ export const hasStrokeColor = (type: ElementOrToolType) =>
|
|||||||
type === "freedraw" ||
|
type === "freedraw" ||
|
||||||
type === "arrow" ||
|
type === "arrow" ||
|
||||||
type === "line" ||
|
type === "line" ||
|
||||||
type === "text" ||
|
type === "text";
|
||||||
type === "embeddable";
|
|
||||||
|
|
||||||
export const hasStrokeWidth = (type: ElementOrToolType) =>
|
export const hasStrokeWidth = (type: ElementOrToolType) =>
|
||||||
type === "rectangle" ||
|
type === "rectangle" ||
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
type Bounds,
|
|
||||||
TEXT_AUTOWRAP_THRESHOLD,
|
TEXT_AUTOWRAP_THRESHOLD,
|
||||||
getGridPoint,
|
getGridPoint,
|
||||||
getFontString,
|
getFontString,
|
||||||
@@ -30,6 +29,7 @@ import {
|
|||||||
|
|
||||||
import type { Scene } from "./Scene";
|
import type { Scene } from "./Scene";
|
||||||
|
|
||||||
|
import type { Bounds } from "./bounds";
|
||||||
import type { ExcalidrawElement } from "./types";
|
import type { ExcalidrawElement } from "./types";
|
||||||
|
|
||||||
export const dragSelectedElements = (
|
export const dragSelectedElements = (
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
} from "@excalidraw/math";
|
} from "@excalidraw/math";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type Bounds,
|
|
||||||
BinaryHeap,
|
BinaryHeap,
|
||||||
invariant,
|
invariant,
|
||||||
isAnyTrue,
|
isAnyTrue,
|
||||||
@@ -55,6 +54,7 @@ import {
|
|||||||
import { aabbForElement, pointInsideBounds } from "./bounds";
|
import { aabbForElement, pointInsideBounds } from "./bounds";
|
||||||
import { getHoveredElementForBinding } from "./collision";
|
import { getHoveredElementForBinding } from "./collision";
|
||||||
|
|
||||||
|
import type { Bounds } from "./bounds";
|
||||||
import type { Heading } from "./heading";
|
import type { Heading } from "./heading";
|
||||||
import type {
|
import type {
|
||||||
Arrowhead,
|
Arrowhead,
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
import {
|
import { invariant, isDevEnv, isTestEnv } from "@excalidraw/common";
|
||||||
invariant,
|
|
||||||
isDevEnv,
|
|
||||||
isTestEnv,
|
|
||||||
type Bounds,
|
|
||||||
} from "@excalidraw/common";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
pointFrom,
|
pointFrom,
|
||||||
@@ -24,7 +19,7 @@ import type {
|
|||||||
Vector,
|
Vector,
|
||||||
} from "@excalidraw/math";
|
} from "@excalidraw/math";
|
||||||
|
|
||||||
import { getCenterForBounds } from "./bounds";
|
import { getCenterForBounds, type Bounds } from "./bounds";
|
||||||
|
|
||||||
import type { ExcalidrawBindableElement } from "./types";
|
import type { ExcalidrawBindableElement } from "./types";
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ export * from "./store";
|
|||||||
export * from "./textElement";
|
export * from "./textElement";
|
||||||
export * from "./textMeasurements";
|
export * from "./textMeasurements";
|
||||||
export * from "./textWrapping";
|
export * from "./textWrapping";
|
||||||
export * from "./transform";
|
|
||||||
export * from "./transformHandles";
|
export * from "./transformHandles";
|
||||||
export * from "./typeChecks";
|
export * from "./typeChecks";
|
||||||
export * from "./utils";
|
export * from "./utils";
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
deconstructLinearOrFreeDrawElement,
|
deconstructLinearOrFreeDrawElement,
|
||||||
|
getHoveredElementForBinding,
|
||||||
isPathALoop,
|
isPathALoop,
|
||||||
moveArrowAboveBindable,
|
moveArrowAboveBindable,
|
||||||
projectFixedPointOntoDiagonal,
|
projectFixedPointOntoDiagonal,
|
||||||
@@ -42,7 +43,6 @@ import type {
|
|||||||
NullableGridSize,
|
NullableGridSize,
|
||||||
Zoom,
|
Zoom,
|
||||||
} from "@excalidraw/excalidraw/types";
|
} from "@excalidraw/excalidraw/types";
|
||||||
import type { Bounds } from "@excalidraw/common";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
calculateFixedPointForNonElbowArrowBinding,
|
calculateFixedPointForNonElbowArrowBinding,
|
||||||
@@ -69,6 +69,7 @@ import { isLineElement } from "./typeChecks";
|
|||||||
|
|
||||||
import type { Scene } from "./Scene";
|
import type { Scene } from "./Scene";
|
||||||
|
|
||||||
|
import type { Bounds } from "./bounds";
|
||||||
import type {
|
import type {
|
||||||
NonDeleted,
|
NonDeleted,
|
||||||
ExcalidrawLinearElement,
|
ExcalidrawLinearElement,
|
||||||
@@ -305,11 +306,21 @@ export class LinearElementEditor {
|
|||||||
const customLineAngle =
|
const customLineAngle =
|
||||||
linearElementEditor.customLineAngle ??
|
linearElementEditor.customLineAngle ??
|
||||||
determineCustomLinearAngle(pivotPoint, element.points[idx]);
|
determineCustomLinearAngle(pivotPoint, element.points[idx]);
|
||||||
|
const hoveredElement = getHoveredElementForBinding(
|
||||||
|
pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
|
||||||
|
elements,
|
||||||
|
elementsMap,
|
||||||
|
);
|
||||||
|
|
||||||
// Determine if point movement should happen and how much
|
// Determine if point movement should happen and how much
|
||||||
let deltaX = 0;
|
let deltaX = 0;
|
||||||
let deltaY = 0;
|
let deltaY = 0;
|
||||||
if (shouldRotateWithDiscreteAngle(event)) {
|
if (
|
||||||
|
shouldRotateWithDiscreteAngle(event) &&
|
||||||
|
!hoveredElement &&
|
||||||
|
!element.startBinding &&
|
||||||
|
!element.endBinding
|
||||||
|
) {
|
||||||
const [width, height] = LinearElementEditor._getShiftLockedDelta(
|
const [width, height] = LinearElementEditor._getShiftLockedDelta(
|
||||||
element,
|
element,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
@@ -343,13 +354,11 @@ export class LinearElementEditor {
|
|||||||
[idx],
|
[idx],
|
||||||
deltaX,
|
deltaX,
|
||||||
deltaY,
|
deltaY,
|
||||||
scenePointerX,
|
|
||||||
scenePointerY,
|
|
||||||
elementsMap,
|
elementsMap,
|
||||||
element,
|
element,
|
||||||
elements,
|
elements,
|
||||||
app,
|
app,
|
||||||
shouldRotateWithDiscreteAngle(event),
|
event.shiftKey,
|
||||||
event.altKey,
|
event.altKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -483,11 +492,22 @@ export class LinearElementEditor {
|
|||||||
const endIsSelected = selectedPointsIndices.includes(
|
const endIsSelected = selectedPointsIndices.includes(
|
||||||
element.points.length - 1,
|
element.points.length - 1,
|
||||||
);
|
);
|
||||||
|
const hoveredElement = getHoveredElementForBinding(
|
||||||
|
pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
|
||||||
|
elements,
|
||||||
|
elementsMap,
|
||||||
|
);
|
||||||
|
|
||||||
// Determine if point movement should happen and how much
|
// Determine if point movement should happen and how much
|
||||||
let deltaX = 0;
|
let deltaX = 0;
|
||||||
let deltaY = 0;
|
let deltaY = 0;
|
||||||
if (shouldRotateWithDiscreteAngle(event) && singlePointDragged) {
|
if (
|
||||||
|
shouldRotateWithDiscreteAngle(event) &&
|
||||||
|
singlePointDragged &&
|
||||||
|
!hoveredElement &&
|
||||||
|
!element.startBinding &&
|
||||||
|
!element.endBinding
|
||||||
|
) {
|
||||||
const [width, height] = LinearElementEditor._getShiftLockedDelta(
|
const [width, height] = LinearElementEditor._getShiftLockedDelta(
|
||||||
element,
|
element,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
@@ -500,6 +520,7 @@ export class LinearElementEditor {
|
|||||||
width + pivotPoint[0],
|
width + pivotPoint[0],
|
||||||
height + pivotPoint[1],
|
height + pivotPoint[1],
|
||||||
);
|
);
|
||||||
|
|
||||||
deltaX = target[0] - draggingPoint[0];
|
deltaX = target[0] - draggingPoint[0];
|
||||||
deltaY = target[1] - draggingPoint[1];
|
deltaY = target[1] - draggingPoint[1];
|
||||||
} else {
|
} else {
|
||||||
@@ -520,13 +541,11 @@ export class LinearElementEditor {
|
|||||||
selectedPointsIndices,
|
selectedPointsIndices,
|
||||||
deltaX,
|
deltaX,
|
||||||
deltaY,
|
deltaY,
|
||||||
scenePointerX,
|
|
||||||
scenePointerY,
|
|
||||||
elementsMap,
|
elementsMap,
|
||||||
element,
|
element,
|
||||||
elements,
|
elements,
|
||||||
app,
|
app,
|
||||||
shouldRotateWithDiscreteAngle(event) && singlePointDragged,
|
event.shiftKey,
|
||||||
event.altKey,
|
event.altKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -2069,13 +2088,11 @@ const pointDraggingUpdates = (
|
|||||||
selectedPointsIndices: readonly number[],
|
selectedPointsIndices: readonly number[],
|
||||||
deltaX: number,
|
deltaX: number,
|
||||||
deltaY: number,
|
deltaY: number,
|
||||||
scenePointerX: number,
|
|
||||||
scenePointerY: number,
|
|
||||||
elementsMap: NonDeletedSceneElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
element: NonDeleted<ExcalidrawLinearElement>,
|
element: NonDeleted<ExcalidrawLinearElement>,
|
||||||
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
||||||
app: AppClassProperties,
|
app: AppClassProperties,
|
||||||
angleLocked: boolean,
|
shiftKey: boolean,
|
||||||
altKey: boolean,
|
altKey: boolean,
|
||||||
): {
|
): {
|
||||||
positions: PointsPositionUpdates;
|
positions: PointsPositionUpdates;
|
||||||
@@ -2111,14 +2128,12 @@ const pointDraggingUpdates = (
|
|||||||
const { start, end } = getBindingStrategyForDraggingBindingElementEndpoints(
|
const { start, end } = getBindingStrategyForDraggingBindingElementEndpoints(
|
||||||
element,
|
element,
|
||||||
naiveDraggingPoints,
|
naiveDraggingPoints,
|
||||||
scenePointerX,
|
|
||||||
scenePointerY,
|
|
||||||
elementsMap,
|
elementsMap,
|
||||||
elements,
|
elements,
|
||||||
app.state,
|
app.state,
|
||||||
{
|
{
|
||||||
newArrow: !!app.state.newElement,
|
newArrow: !!app.state.newElement,
|
||||||
angleLocked,
|
shiftKey,
|
||||||
altKey,
|
altKey,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -2235,15 +2250,10 @@ const pointDraggingUpdates = (
|
|||||||
// We need to use a custom intersector to ensure that if there is a big "jump"
|
// We need to use a custom intersector to ensure that if there is a big "jump"
|
||||||
// in the arrow's position, we can position it with outline avoidance
|
// in the arrow's position, we can position it with outline avoidance
|
||||||
// pixel-perfectly and avoid "dancing" arrows.
|
// pixel-perfectly and avoid "dancing" arrows.
|
||||||
// NOTE: Direction matters here, so we create two intersectors
|
const customIntersector =
|
||||||
const startCustomIntersector =
|
|
||||||
start.focusPoint && end.focusPoint
|
start.focusPoint && end.focusPoint
|
||||||
? lineSegment(start.focusPoint, end.focusPoint)
|
? lineSegment(start.focusPoint, end.focusPoint)
|
||||||
: undefined;
|
: undefined;
|
||||||
const endCustomIntersector =
|
|
||||||
start.focusPoint && end.focusPoint
|
|
||||||
? lineSegment(end.focusPoint, start.focusPoint)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
// Needed to handle a special case where an existing arrow is dragged over
|
// Needed to handle a special case where an existing arrow is dragged over
|
||||||
// the same element it is bound to on the other side
|
// the same element it is bound to on the other side
|
||||||
@@ -2280,7 +2290,7 @@ const pointDraggingUpdates = (
|
|||||||
nextArrow.endBinding,
|
nextArrow.endBinding,
|
||||||
endBindable,
|
endBindable,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
endCustomIntersector,
|
customIntersector,
|
||||||
) || nextArrow.points[nextArrow.points.length - 1]
|
) || nextArrow.points[nextArrow.points.length - 1]
|
||||||
: nextArrow.points[nextArrow.points.length - 1];
|
: nextArrow.points[nextArrow.points.length - 1];
|
||||||
|
|
||||||
@@ -2311,7 +2321,7 @@ const pointDraggingUpdates = (
|
|||||||
nextArrow.startBinding,
|
nextArrow.startBinding,
|
||||||
startBindable,
|
startBindable,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
startCustomIntersector,
|
customIntersector,
|
||||||
) || nextArrow.points[0]
|
) || nextArrow.points[0]
|
||||||
: nextArrow.points[0];
|
: nextArrow.points[0];
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
getResizedElementAbsoluteCoords,
|
getResizedElementAbsoluteCoords,
|
||||||
} from "./bounds";
|
} from "./bounds";
|
||||||
import { newElementWith } from "./mutateElement";
|
import { newElementWith } from "./mutateElement";
|
||||||
import { getBoundTextMaxWidth, getInitialTextMetrics } from "./textElement";
|
import { getBoundTextMaxWidth } from "./textElement";
|
||||||
import { normalizeText, measureText } from "./textMeasurements";
|
import { normalizeText, measureText } from "./textMeasurements";
|
||||||
import { wrapText } from "./textWrapping";
|
import { wrapText } from "./textWrapping";
|
||||||
|
|
||||||
@@ -236,30 +236,27 @@ const getTextElementPositionOffsets = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NewTextElementOptions = {
|
|
||||||
text: string;
|
|
||||||
originalText?: string;
|
|
||||||
fontSize?: number;
|
|
||||||
fontFamily?: FontFamilyValues;
|
|
||||||
textAlign?: TextAlign;
|
|
||||||
verticalAlign?: VerticalAlign;
|
|
||||||
containerId?: ExcalidrawTextContainer["id"] | null;
|
|
||||||
lineHeight?: ExcalidrawTextElement["lineHeight"];
|
|
||||||
autoResize?: ExcalidrawTextElement["autoResize"];
|
|
||||||
} & ElementConstructorOpts;
|
|
||||||
|
|
||||||
export const newTextElement = (
|
export const newTextElement = (
|
||||||
opts: NewTextElementOptions,
|
opts: {
|
||||||
|
text: string;
|
||||||
|
originalText?: string;
|
||||||
|
fontSize?: number;
|
||||||
|
fontFamily?: FontFamilyValues;
|
||||||
|
textAlign?: TextAlign;
|
||||||
|
verticalAlign?: VerticalAlign;
|
||||||
|
containerId?: ExcalidrawTextContainer["id"] | null;
|
||||||
|
lineHeight?: ExcalidrawTextElement["lineHeight"];
|
||||||
|
autoResize?: ExcalidrawTextElement["autoResize"];
|
||||||
|
} & ElementConstructorOpts,
|
||||||
): NonDeleted<ExcalidrawTextElement> => {
|
): NonDeleted<ExcalidrawTextElement> => {
|
||||||
const fontFamily = opts.fontFamily || DEFAULT_FONT_FAMILY;
|
const fontFamily = opts.fontFamily || DEFAULT_FONT_FAMILY;
|
||||||
const fontSize = opts.fontSize || DEFAULT_FONT_SIZE;
|
const fontSize = opts.fontSize || DEFAULT_FONT_SIZE;
|
||||||
const lineHeight = opts.lineHeight || getLineHeight(fontFamily);
|
const lineHeight = opts.lineHeight || getLineHeight(fontFamily);
|
||||||
const normalizedText = normalizeText(opts.text);
|
const text = normalizeText(opts.text);
|
||||||
const originalText = opts.originalText ?? normalizedText;
|
const metrics = measureText(
|
||||||
const metrics = getInitialTextMetrics(
|
text,
|
||||||
{ ...opts, text: normalizedText },
|
getFontString({ fontFamily, fontSize }),
|
||||||
fontFamily,
|
lineHeight,
|
||||||
fontSize,
|
|
||||||
);
|
);
|
||||||
const textAlign = opts.textAlign || DEFAULT_TEXT_ALIGN;
|
const textAlign = opts.textAlign || DEFAULT_TEXT_ALIGN;
|
||||||
const verticalAlign = opts.verticalAlign || DEFAULT_VERTICAL_ALIGN;
|
const verticalAlign = opts.verticalAlign || DEFAULT_VERTICAL_ALIGN;
|
||||||
@@ -270,7 +267,7 @@ export const newTextElement = (
|
|||||||
|
|
||||||
const textElementProps: ExcalidrawTextElement = {
|
const textElementProps: ExcalidrawTextElement = {
|
||||||
..._newElementBase<ExcalidrawTextElement>("text", opts),
|
..._newElementBase<ExcalidrawTextElement>("text", opts),
|
||||||
text: normalizedText,
|
text,
|
||||||
fontSize,
|
fontSize,
|
||||||
fontFamily,
|
fontFamily,
|
||||||
textAlign,
|
textAlign,
|
||||||
@@ -280,7 +277,7 @@ export const newTextElement = (
|
|||||||
width: metrics.width,
|
width: metrics.width,
|
||||||
height: metrics.height,
|
height: metrics.height,
|
||||||
containerId: opts.containerId || null,
|
containerId: opts.containerId || null,
|
||||||
originalText,
|
originalText: opts.originalText ?? text,
|
||||||
autoResize: opts.autoResize ?? true,
|
autoResize: opts.autoResize ?? true,
|
||||||
lineHeight,
|
lineHeight,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
import type { GlobalPoint, LineSegment, LocalPoint } from "@excalidraw/math";
|
import type { GlobalPoint, LineSegment, LocalPoint } from "@excalidraw/math";
|
||||||
|
|
||||||
import type { AppState, Zoom } from "@excalidraw/excalidraw/types";
|
import type { AppState, Zoom } from "@excalidraw/excalidraw/types";
|
||||||
import type { Bounds } from "@excalidraw/common";
|
|
||||||
|
|
||||||
import { getElementAbsoluteCoords } from "./bounds";
|
import { getElementAbsoluteCoords } from "./bounds";
|
||||||
import {
|
import {
|
||||||
@@ -24,6 +23,7 @@ import {
|
|||||||
} from "./transformHandles";
|
} from "./transformHandles";
|
||||||
import { isImageElement, isLinearElement } from "./typeChecks";
|
import { isImageElement, isLinearElement } from "./typeChecks";
|
||||||
|
|
||||||
|
import type { Bounds } from "./bounds";
|
||||||
import type {
|
import type {
|
||||||
TransformHandleType,
|
TransformHandleType,
|
||||||
TransformHandle,
|
TransformHandle,
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import {
|
|||||||
getFontString,
|
getFontString,
|
||||||
isProdEnv,
|
isProdEnv,
|
||||||
invariant,
|
invariant,
|
||||||
DEFAULT_FONT_FAMILY,
|
|
||||||
getLineHeight,
|
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import { pointFrom, pointRotateRads, type Radians } from "@excalidraw/math";
|
import { pointFrom, pointRotateRads, type Radians } from "@excalidraw/math";
|
||||||
@@ -32,8 +30,6 @@ import {
|
|||||||
isTextElement,
|
isTextElement,
|
||||||
} from "./typeChecks";
|
} from "./typeChecks";
|
||||||
|
|
||||||
import type { NewTextElementOptions } from "./newElement";
|
|
||||||
|
|
||||||
import type { Scene } from "./Scene";
|
import type { Scene } from "./Scene";
|
||||||
|
|
||||||
import type { MaybeTransformHandleType } from "./transformHandles";
|
import type { MaybeTransformHandleType } from "./transformHandles";
|
||||||
@@ -44,7 +40,6 @@ import type {
|
|||||||
ExcalidrawTextContainer,
|
ExcalidrawTextContainer,
|
||||||
ExcalidrawTextElement,
|
ExcalidrawTextElement,
|
||||||
ExcalidrawTextElementWithContainer,
|
ExcalidrawTextElementWithContainer,
|
||||||
FontFamilyValues,
|
|
||||||
NonDeletedExcalidrawElement,
|
NonDeletedExcalidrawElement,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
@@ -533,24 +528,3 @@ export const getTextFromElements = (
|
|||||||
.join(separator);
|
.join(separator);
|
||||||
return text;
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** When text is already measured and wrapped, we want to respect those dimensions */
|
|
||||||
export const getInitialTextMetrics = (
|
|
||||||
text: NewTextElementOptions,
|
|
||||||
fontFamily: FontFamilyValues = DEFAULT_FONT_FAMILY,
|
|
||||||
fontSize: number = DEFAULT_FONT_SIZE,
|
|
||||||
) => {
|
|
||||||
const shouldUseProvidedDimensions =
|
|
||||||
text.autoResize === false && text.width && text.height;
|
|
||||||
|
|
||||||
return shouldUseProvidedDimensions
|
|
||||||
? {
|
|
||||||
width: text.width,
|
|
||||||
height: text.height,
|
|
||||||
}
|
|
||||||
: measureText(
|
|
||||||
text.text,
|
|
||||||
getFontString({ fontFamily, fontSize }),
|
|
||||||
text.lineHeight ?? getLineHeight(fontFamily),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import type {
|
|||||||
InteractiveCanvasAppState,
|
InteractiveCanvasAppState,
|
||||||
Zoom,
|
Zoom,
|
||||||
} from "@excalidraw/excalidraw/types";
|
} from "@excalidraw/excalidraw/types";
|
||||||
import type { Bounds } from "@excalidraw/common";
|
|
||||||
|
|
||||||
import { getElementAbsoluteCoords } from "./bounds";
|
import { getElementAbsoluteCoords } from "./bounds";
|
||||||
import {
|
import {
|
||||||
@@ -21,6 +20,7 @@ import {
|
|||||||
isLinearElement,
|
isLinearElement,
|
||||||
} from "./typeChecks";
|
} from "./typeChecks";
|
||||||
|
|
||||||
|
import type { Bounds } from "./bounds";
|
||||||
import type {
|
import type {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { ElementOrToolType } from "@excalidraw/excalidraw/types";
|
|||||||
|
|
||||||
import type { MarkNonNullable } from "@excalidraw/common/utility-types";
|
import type { MarkNonNullable } from "@excalidraw/common/utility-types";
|
||||||
|
|
||||||
|
import type { Bounds } from "./bounds";
|
||||||
import type {
|
import type {
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
ExcalidrawTextElement,
|
ExcalidrawTextElement,
|
||||||
@@ -355,6 +356,15 @@ export const getDefaultRoundnessTypeForElement = (
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Move this to @excalidraw/math
|
||||||
|
export const isBounds = (box: unknown): box is Bounds =>
|
||||||
|
Array.isArray(box) &&
|
||||||
|
box.length === 4 &&
|
||||||
|
typeof box[0] === "number" &&
|
||||||
|
typeof box[1] === "number" &&
|
||||||
|
typeof box[2] === "number" &&
|
||||||
|
typeof box[3] === "number";
|
||||||
|
|
||||||
export const getLinearElementSubType = (
|
export const getLinearElementSubType = (
|
||||||
element: ExcalidrawLinearElement,
|
element: ExcalidrawLinearElement,
|
||||||
): ExcalidrawLinearElementSubType => {
|
): ExcalidrawLinearElementSubType => {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { pointFrom } from "@excalidraw/math";
|
|||||||
|
|
||||||
import { Excalidraw } from "@excalidraw/excalidraw";
|
import { Excalidraw } from "@excalidraw/excalidraw";
|
||||||
import {
|
import {
|
||||||
type Bounds,
|
|
||||||
KEYS,
|
KEYS,
|
||||||
getSizeFromPoints,
|
getSizeFromPoints,
|
||||||
reseed,
|
reseed,
|
||||||
@@ -23,6 +22,7 @@ import { resizeSingleElement } from "../src/resizeElements";
|
|||||||
import { LinearElementEditor } from "../src/linearElementEditor";
|
import { LinearElementEditor } from "../src/linearElementEditor";
|
||||||
import { getElementPointsCoords } from "../src/bounds";
|
import { getElementPointsCoords } from "../src/bounds";
|
||||||
|
|
||||||
|
import type { Bounds } from "../src/bounds";
|
||||||
import type {
|
import type {
|
||||||
ExcalidrawElbowArrowElement,
|
ExcalidrawElbowArrowElement,
|
||||||
ExcalidrawFreeDrawElement,
|
ExcalidrawFreeDrawElement,
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import {
|
|||||||
KEYS,
|
KEYS,
|
||||||
arrayToMap,
|
arrayToMap,
|
||||||
invariant,
|
invariant,
|
||||||
shouldRotateWithDiscreteAngle,
|
|
||||||
updateActiveTool,
|
updateActiveTool,
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
import { isPathALoop } from "@excalidraw/element";
|
import { isPathALoop } from "@excalidraw/element";
|
||||||
@@ -103,19 +102,10 @@ export const actionFinalize = register<FormData>({
|
|||||||
return map;
|
return map;
|
||||||
}, new Map()) ?? new Map();
|
}, new Map()) ?? new Map();
|
||||||
|
|
||||||
bindOrUnbindBindingElement(
|
bindOrUnbindBindingElement(element, draggedPoints, scene, appState, {
|
||||||
element,
|
newArrow,
|
||||||
draggedPoints,
|
altKey: event.altKey,
|
||||||
sceneCoords.x,
|
});
|
||||||
sceneCoords.y,
|
|
||||||
scene,
|
|
||||||
appState,
|
|
||||||
{
|
|
||||||
newArrow,
|
|
||||||
altKey: event.altKey,
|
|
||||||
angleLocked: shouldRotateWithDiscreteAngle(event),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else if (isLineElement(element)) {
|
} else if (isLineElement(element)) {
|
||||||
if (
|
if (
|
||||||
appState.selectedLinearElement?.isEditing &&
|
appState.selectedLinearElement?.isEditing &&
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
isTransparent,
|
isTransparent,
|
||||||
reduceToCommonValue,
|
reduceToCommonValue,
|
||||||
invariant,
|
invariant,
|
||||||
FONT_SIZES,
|
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import { canBecomePolygon, getNonDeletedElements } from "@excalidraw/element";
|
import { canBecomePolygon, getNonDeletedElements } from "@excalidraw/element";
|
||||||
@@ -759,25 +758,25 @@ export const actionChangeFontSize = register<ExcalidrawTextElement["fontSize"]>(
|
|||||||
group="font-size"
|
group="font-size"
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
value: FONT_SIZES.sm,
|
value: 16,
|
||||||
text: t("labels.small"),
|
text: t("labels.small"),
|
||||||
icon: FontSizeSmallIcon,
|
icon: FontSizeSmallIcon,
|
||||||
testId: "fontSize-small",
|
testId: "fontSize-small",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: FONT_SIZES.md,
|
value: 20,
|
||||||
text: t("labels.medium"),
|
text: t("labels.medium"),
|
||||||
icon: FontSizeMediumIcon,
|
icon: FontSizeMediumIcon,
|
||||||
testId: "fontSize-medium",
|
testId: "fontSize-medium",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: FONT_SIZES.lg,
|
value: 28,
|
||||||
text: t("labels.large"),
|
text: t("labels.large"),
|
||||||
icon: FontSizeLargeIcon,
|
icon: FontSizeLargeIcon,
|
||||||
testId: "fontSize-large",
|
testId: "fontSize-large",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: FONT_SIZES.xl,
|
value: 36,
|
||||||
text: t("labels.veryLarge"),
|
text: t("labels.veryLarge"),
|
||||||
icon: FontSizeExtraLargeIcon,
|
icon: FontSizeExtraLargeIcon,
|
||||||
testId: "fontSize-veryLarge",
|
testId: "fontSize-veryLarge",
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
VERTICAL_ALIGN,
|
VERTICAL_ALIGN,
|
||||||
randomId,
|
randomId,
|
||||||
isDevEnv,
|
isDevEnv,
|
||||||
FONT_SIZES,
|
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -214,7 +213,7 @@ const chartXLabels = (
|
|||||||
y: y + BAR_GAP / 2,
|
y: y + BAR_GAP / 2,
|
||||||
width: BAR_WIDTH,
|
width: BAR_WIDTH,
|
||||||
angle: 5.87 as Radians,
|
angle: 5.87 as Radians,
|
||||||
fontSize: FONT_SIZES.sm,
|
fontSize: 16,
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
verticalAlign: "top",
|
verticalAlign: "top",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ export const createPasteEvent = ({
|
|||||||
if (typeof value !== "string") {
|
if (typeof value !== "string") {
|
||||||
files = files || [];
|
files = files || [];
|
||||||
files.push(value);
|
files.push(value);
|
||||||
|
event.clipboardData?.items.add(value);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -248,8 +248,6 @@ import {
|
|||||||
doBoundsIntersect,
|
doBoundsIntersect,
|
||||||
isPointInElement,
|
isPointInElement,
|
||||||
maxBindingDistance_simple,
|
maxBindingDistance_simple,
|
||||||
convertToExcalidrawElements,
|
|
||||||
type ExcalidrawElementSkeleton,
|
|
||||||
} from "@excalidraw/element";
|
} from "@excalidraw/element";
|
||||||
|
|
||||||
import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math";
|
import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math";
|
||||||
@@ -397,6 +395,7 @@ import {
|
|||||||
SnapCache,
|
SnapCache,
|
||||||
isGridModeEnabled,
|
isGridModeEnabled,
|
||||||
} from "../snapping";
|
} from "../snapping";
|
||||||
|
import { convertToExcalidrawElements } from "../data/transform";
|
||||||
import { Renderer } from "../scene/Renderer";
|
import { Renderer } from "../scene/Renderer";
|
||||||
import {
|
import {
|
||||||
setEraserCursor,
|
setEraserCursor,
|
||||||
@@ -458,7 +457,7 @@ import type { ClipboardData, PastedMixedContent } from "../clipboard";
|
|||||||
import type { ExportedElements } from "../data";
|
import type { ExportedElements } from "../data";
|
||||||
import type { ContextMenuItems } from "./ContextMenu";
|
import type { ContextMenuItems } from "./ContextMenu";
|
||||||
import type { FileSystemHandle } from "../data/filesystem";
|
import type { FileSystemHandle } from "../data/filesystem";
|
||||||
|
import type { ExcalidrawElementSkeleton } from "../data/transform";
|
||||||
import type {
|
import type {
|
||||||
AppClassProperties,
|
AppClassProperties,
|
||||||
AppProps,
|
AppProps,
|
||||||
@@ -8618,16 +8617,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
point[0],
|
|
||||||
point[1],
|
|
||||||
this.scene,
|
this.scene,
|
||||||
this.state,
|
this.state,
|
||||||
{
|
{ newArrow: true, altKey: event.altKey, initialBinding: true },
|
||||||
newArrow: true,
|
|
||||||
altKey: event.altKey,
|
|
||||||
initialBinding: true,
|
|
||||||
angleLocked: shouldRotateWithDiscreteAngle(event.nativeEvent),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { hitElementBoundingBox } from "@excalidraw/element";
|
|||||||
|
|
||||||
import type { GlobalPoint, Radians } from "@excalidraw/math";
|
import type { GlobalPoint, Radians } from "@excalidraw/math";
|
||||||
|
|
||||||
import type { Bounds } from "@excalidraw/common";
|
import type { Bounds } from "@excalidraw/element";
|
||||||
import type {
|
import type {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
NonDeletedExcalidrawElement,
|
NonDeletedExcalidrawElement,
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ import type { LocalPoint, Radians } from "@excalidraw/math";
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
ElementsMapOrArray,
|
|
||||||
ExcalidrawArrowElement,
|
ExcalidrawArrowElement,
|
||||||
ExcalidrawBindableElement,
|
ExcalidrawBindableElement,
|
||||||
ExcalidrawElbowArrowElement,
|
ExcalidrawElbowArrowElement,
|
||||||
@@ -130,8 +129,7 @@ const getFontFamilyByName = (fontFamilyName: string): FontFamilyValues => {
|
|||||||
const repairBinding = <T extends ExcalidrawArrowElement>(
|
const repairBinding = <T extends ExcalidrawArrowElement>(
|
||||||
element: T,
|
element: T,
|
||||||
binding: FixedPointBinding | null,
|
binding: FixedPointBinding | null,
|
||||||
targetElementsMap: Readonly<ElementsMap>,
|
elementsMap: Readonly<ElementsMap>,
|
||||||
localElementsMap: Readonly<ElementsMap> | null | undefined,
|
|
||||||
startOrEnd: "start" | "end",
|
startOrEnd: "start" | "end",
|
||||||
): FixedPointBinding | null => {
|
): FixedPointBinding | null => {
|
||||||
if (!binding) {
|
if (!binding) {
|
||||||
@@ -150,27 +148,18 @@ const repairBinding = <T extends ExcalidrawArrowElement>(
|
|||||||
return fixedPointBinding;
|
return fixedPointBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback if the bound element is missing but the binding is at least
|
|
||||||
// looking like a valid one shape-wise
|
|
||||||
if (binding.mode && binding.fixedPoint && binding.elementId) {
|
|
||||||
return {
|
|
||||||
elementId: binding.elementId,
|
|
||||||
mode: binding.mode,
|
|
||||||
fixedPoint: normalizeFixedPoint(binding.fixedPoint || [0.5, 0.5]),
|
|
||||||
} as FixedPointBinding | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetBoundElement =
|
|
||||||
(targetElementsMap.get(binding.elementId) as ExcalidrawBindableElement) ||
|
|
||||||
undefined;
|
|
||||||
const boundElement =
|
const boundElement =
|
||||||
targetBoundElement ||
|
(elementsMap.get(binding.elementId) as ExcalidrawBindableElement) ||
|
||||||
(localElementsMap?.get(binding.elementId) as ExcalidrawBindableElement) ||
|
|
||||||
undefined;
|
undefined;
|
||||||
const elementsMap = targetBoundElement ? targetElementsMap : localElementsMap;
|
if (boundElement) {
|
||||||
|
if (binding.mode) {
|
||||||
|
return {
|
||||||
|
elementId: binding.elementId,
|
||||||
|
mode: binding.mode || "orbit",
|
||||||
|
fixedPoint: normalizeFixedPoint(binding.fixedPoint || [0.5, 0.5]),
|
||||||
|
} as FixedPointBinding | null;
|
||||||
|
}
|
||||||
|
|
||||||
// migrating legacy focus point bindings
|
|
||||||
if (boundElement && elementsMap) {
|
|
||||||
const p = LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
const p = LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||||
element,
|
element,
|
||||||
startOrEnd === "start" ? 0 : element.points.length - 1,
|
startOrEnd === "start" ? 0 : element.points.length - 1,
|
||||||
@@ -204,8 +193,6 @@ const repairBinding = <T extends ExcalidrawArrowElement>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(`could not repair binding for element`);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -297,8 +284,7 @@ const restoreElementWithProperties = <
|
|||||||
|
|
||||||
export const restoreElement = (
|
export const restoreElement = (
|
||||||
element: Exclude<ExcalidrawElement, ExcalidrawSelectionElement>,
|
element: Exclude<ExcalidrawElement, ExcalidrawSelectionElement>,
|
||||||
targetElementsMap: Readonly<ElementsMap>,
|
elementsMap: Readonly<ElementsMap>,
|
||||||
localElementsMap: Readonly<ElementsMap> | null | undefined,
|
|
||||||
opts?: {
|
opts?: {
|
||||||
deleteInvisibleElements?: boolean;
|
deleteInvisibleElements?: boolean;
|
||||||
},
|
},
|
||||||
@@ -419,15 +405,13 @@ export const restoreElement = (
|
|||||||
startBinding: repairBinding(
|
startBinding: repairBinding(
|
||||||
element as ExcalidrawArrowElement,
|
element as ExcalidrawArrowElement,
|
||||||
element.startBinding,
|
element.startBinding,
|
||||||
targetElementsMap,
|
elementsMap,
|
||||||
localElementsMap,
|
|
||||||
"start",
|
"start",
|
||||||
),
|
),
|
||||||
endBinding: repairBinding(
|
endBinding: repairBinding(
|
||||||
element as ExcalidrawArrowElement,
|
element as ExcalidrawArrowElement,
|
||||||
element.endBinding,
|
element.endBinding,
|
||||||
targetElementsMap,
|
elementsMap,
|
||||||
localElementsMap,
|
|
||||||
"end",
|
"end",
|
||||||
),
|
),
|
||||||
startArrowhead,
|
startArrowhead,
|
||||||
@@ -591,7 +575,7 @@ const repairFrameMembership = (
|
|||||||
export const restoreElements = (
|
export const restoreElements = (
|
||||||
targetElements: ImportedDataState["elements"],
|
targetElements: ImportedDataState["elements"],
|
||||||
/** NOTE doesn't serve for reconciliation */
|
/** NOTE doesn't serve for reconciliation */
|
||||||
localElements: Readonly<ElementsMapOrArray> | null | undefined,
|
localElements: readonly ExcalidrawElement[] | null | undefined,
|
||||||
opts?:
|
opts?:
|
||||||
| {
|
| {
|
||||||
refreshDimensions?: boolean;
|
refreshDimensions?: boolean;
|
||||||
@@ -602,7 +586,7 @@ export const restoreElements = (
|
|||||||
): OrderedExcalidrawElement[] => {
|
): OrderedExcalidrawElement[] => {
|
||||||
// used to detect duplicate top-level element ids
|
// used to detect duplicate top-level element ids
|
||||||
const existingIds = new Set<string>();
|
const existingIds = new Set<string>();
|
||||||
const targetElementsMap = arrayToMap(targetElements || []);
|
const elementsMap = arrayToMap(targetElements || []);
|
||||||
const localElementsMap = localElements ? arrayToMap(localElements) : null;
|
const localElementsMap = localElements ? arrayToMap(localElements) : null;
|
||||||
const restoredElements = syncInvalidIndices(
|
const restoredElements = syncInvalidIndices(
|
||||||
(targetElements || []).reduce((elements, element) => {
|
(targetElements || []).reduce((elements, element) => {
|
||||||
@@ -614,8 +598,7 @@ export const restoreElements = (
|
|||||||
|
|
||||||
let migratedElement: ExcalidrawElement | null = restoreElement(
|
let migratedElement: ExcalidrawElement | null = restoreElement(
|
||||||
element,
|
element,
|
||||||
targetElementsMap,
|
elementsMap,
|
||||||
localElementsMap,
|
|
||||||
{
|
{
|
||||||
deleteInvisibleElements: opts?.deleteInvisibleElements,
|
deleteInvisibleElements: opts?.deleteInvisibleElements,
|
||||||
},
|
},
|
||||||
|
|||||||
+4
-5
@@ -1,12 +1,11 @@
|
|||||||
import { pointFrom } from "@excalidraw/math";
|
import { pointFrom } from "@excalidraw/math";
|
||||||
import { vi } from "vitest";
|
import { vi } from "vitest";
|
||||||
|
|
||||||
import {
|
import type { ExcalidrawArrowElement } from "@excalidraw/element/types";
|
||||||
convertToExcalidrawElements,
|
|
||||||
type ExcalidrawElementSkeleton,
|
|
||||||
} from "../transform";
|
|
||||||
|
|
||||||
import type { ExcalidrawArrowElement } from "../types";
|
import { convertToExcalidrawElements } from "./transform";
|
||||||
|
|
||||||
|
import type { ExcalidrawElementSkeleton } from "./transform";
|
||||||
|
|
||||||
const opts = { regenerateIds: false };
|
const opts = { regenerateIds: false };
|
||||||
|
|
||||||
@@ -10,13 +10,13 @@ import {
|
|||||||
arrayToMap,
|
arrayToMap,
|
||||||
assertNever,
|
assertNever,
|
||||||
cloneJSON,
|
cloneJSON,
|
||||||
|
getFontString,
|
||||||
isDevEnv,
|
isDevEnv,
|
||||||
toBrandedType,
|
toBrandedType,
|
||||||
|
getLineHeight,
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import type { MarkOptional } from "@excalidraw/common/utility-types";
|
import { bindBindingElement } from "@excalidraw/element";
|
||||||
|
|
||||||
import { bindBindingElement } from "./binding";
|
|
||||||
import {
|
import {
|
||||||
newArrowElement,
|
newArrowElement,
|
||||||
newElement,
|
newElement,
|
||||||
@@ -25,19 +25,21 @@ import {
|
|||||||
newLinearElement,
|
newLinearElement,
|
||||||
newMagicFrameElement,
|
newMagicFrameElement,
|
||||||
newTextElement,
|
newTextElement,
|
||||||
type ElementConstructorOpts,
|
} from "@excalidraw/element";
|
||||||
} from "./newElement";
|
import { measureText, normalizeText } from "@excalidraw/element";
|
||||||
import { isArrowElement } from "./typeChecks";
|
import { isArrowElement } from "@excalidraw/element";
|
||||||
|
|
||||||
import { syncInvalidIndices } from "./fractionalIndex";
|
import { syncInvalidIndices } from "@excalidraw/element";
|
||||||
|
|
||||||
import { getInitialTextMetrics, redrawTextBoundingBox } from "./textElement";
|
import { redrawTextBoundingBox } from "@excalidraw/element";
|
||||||
|
|
||||||
import { LinearElementEditor } from "./linearElementEditor";
|
import { LinearElementEditor } from "@excalidraw/element";
|
||||||
|
|
||||||
import { getCommonBounds } from "./bounds";
|
import { getCommonBounds } from "@excalidraw/element";
|
||||||
|
|
||||||
import { Scene } from "./Scene";
|
import { Scene } from "@excalidraw/element";
|
||||||
|
|
||||||
|
import type { ElementConstructorOpts } from "@excalidraw/element";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ExcalidrawArrowElement,
|
ExcalidrawArrowElement,
|
||||||
@@ -57,7 +59,9 @@ import type {
|
|||||||
NonDeletedSceneElementsMap,
|
NonDeletedSceneElementsMap,
|
||||||
TextAlign,
|
TextAlign,
|
||||||
VerticalAlign,
|
VerticalAlign,
|
||||||
} from "./types";
|
} from "@excalidraw/element/types";
|
||||||
|
|
||||||
|
import type { MarkOptional } from "@excalidraw/common/utility-types";
|
||||||
|
|
||||||
export type ValidLinearElement = {
|
export type ValidLinearElement = {
|
||||||
type: "arrow" | "line";
|
type: "arrow" | "line";
|
||||||
@@ -576,7 +580,14 @@ export const convertToExcalidrawElements = (
|
|||||||
case "text": {
|
case "text": {
|
||||||
const fontFamily = element?.fontFamily || DEFAULT_FONT_FAMILY;
|
const fontFamily = element?.fontFamily || DEFAULT_FONT_FAMILY;
|
||||||
const fontSize = element?.fontSize || DEFAULT_FONT_SIZE;
|
const fontSize = element?.fontSize || DEFAULT_FONT_SIZE;
|
||||||
const metrics = getInitialTextMetrics(element, fontFamily, fontSize);
|
const lineHeight = element?.lineHeight || getLineHeight(fontFamily);
|
||||||
|
const text = element.text ?? "";
|
||||||
|
const normalizedText = normalizeText(text);
|
||||||
|
const metrics = measureText(
|
||||||
|
normalizedText,
|
||||||
|
getFontString({ fontFamily, fontSize }),
|
||||||
|
lineHeight,
|
||||||
|
);
|
||||||
|
|
||||||
excalidrawElement = newTextElement({
|
excalidrawElement = newTextElement({
|
||||||
width: metrics.width,
|
width: metrics.width,
|
||||||
@@ -28,7 +28,7 @@ import { shouldTestInside } from "@excalidraw/element";
|
|||||||
import { hasBoundTextElement, isBoundToContainer } from "@excalidraw/element";
|
import { hasBoundTextElement, isBoundToContainer } from "@excalidraw/element";
|
||||||
import { getBoundTextElementId } from "@excalidraw/element";
|
import { getBoundTextElementId } from "@excalidraw/element";
|
||||||
|
|
||||||
import type { Bounds } from "@excalidraw/common";
|
import type { Bounds } from "@excalidraw/element";
|
||||||
|
|
||||||
import type { GlobalPoint, LineSegment } from "@excalidraw/math/types";
|
import type { GlobalPoint, LineSegment } from "@excalidraw/math/types";
|
||||||
import type { ElementsMap, ExcalidrawElement } from "@excalidraw/element/types";
|
import type { ElementsMap, ExcalidrawElement } from "@excalidraw/element/types";
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
CJK_HAND_DRAWN_FALLBACK_FONT,
|
CJK_HAND_DRAWN_FALLBACK_FONT,
|
||||||
WINDOWS_EMOJI_FALLBACK_FONT,
|
WINDOWS_EMOJI_FALLBACK_FONT,
|
||||||
getFontFamilyFallbacks,
|
getFontFamilyFallbacks,
|
||||||
FONT_SIZES,
|
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
import { getContainerElement } from "@excalidraw/element";
|
import { getContainerElement } from "@excalidraw/element";
|
||||||
import { charWidth } from "@excalidraw/element";
|
import { charWidth } from "@excalidraw/element";
|
||||||
@@ -241,7 +240,7 @@ export class Fonts {
|
|||||||
for (const [index, fontFamily] of fontFamilies.entries()) {
|
for (const [index, fontFamily] of fontFamilies.entries()) {
|
||||||
const font = getFontString({
|
const font = getFontString({
|
||||||
fontFamily,
|
fontFamily,
|
||||||
fontSize: FONT_SIZES.sm,
|
fontSize: 16,
|
||||||
});
|
});
|
||||||
|
|
||||||
// WARN: without "text" param it does not have to mean that all font faces are loaded as it could be just one irrelevant font face!
|
// WARN: without "text" param it does not have to mean that all font faces are loaded as it could be just one irrelevant font face!
|
||||||
|
|||||||
@@ -293,11 +293,8 @@ export { TTDDialog } from "./components/TTDDialog/TTDDialog";
|
|||||||
export { TTDDialogTrigger } from "./components/TTDDialog/TTDDialogTrigger";
|
export { TTDDialogTrigger } from "./components/TTDDialog/TTDDialogTrigger";
|
||||||
|
|
||||||
export { zoomToFitBounds } from "./actions/actionCanvas";
|
export { zoomToFitBounds } from "./actions/actionCanvas";
|
||||||
export {
|
export { convertToExcalidrawElements } from "./data/transform";
|
||||||
getCommonBounds,
|
export { getCommonBounds, getVisibleSceneBounds } from "@excalidraw/element";
|
||||||
getVisibleSceneBounds,
|
|
||||||
convertToExcalidrawElements,
|
|
||||||
} from "@excalidraw/element";
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
elementsOverlappingBBox,
|
elementsOverlappingBBox,
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ import {
|
|||||||
polygonIncludesPointNonZero,
|
polygonIncludesPointNonZero,
|
||||||
} from "@excalidraw/math";
|
} from "@excalidraw/math";
|
||||||
|
|
||||||
import { type Bounds } from "@excalidraw/common";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
type Bounds,
|
||||||
computeBoundTextPosition,
|
computeBoundTextPosition,
|
||||||
doBoundsIntersect,
|
doBoundsIntersect,
|
||||||
getBoundTextElement,
|
getBoundTextElement,
|
||||||
|
|||||||
@@ -95,6 +95,7 @@
|
|||||||
"fractional-indexing": "3.2.0",
|
"fractional-indexing": "3.2.0",
|
||||||
"fuzzy": "0.1.3",
|
"fuzzy": "0.1.3",
|
||||||
"image-blob-reduce": "3.0.1",
|
"image-blob-reduce": "3.0.1",
|
||||||
|
"is-mobile": "5.0.0",
|
||||||
"jotai": "2.11.0",
|
"jotai": "2.11.0",
|
||||||
"jotai-scope": "0.7.2",
|
"jotai-scope": "0.7.2",
|
||||||
"lodash.debounce": "4.0.8",
|
"lodash.debounce": "4.0.8",
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import { type Mutable } from "@excalidraw/common/utility-types";
|
|||||||
|
|
||||||
import { newTextElement } from "@excalidraw/element";
|
import { newTextElement } from "@excalidraw/element";
|
||||||
|
|
||||||
import type { Bounds } from "@excalidraw/common";
|
import type { Bounds } from "@excalidraw/element";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
|
|
||||||
import type { InclusiveRange } from "@excalidraw/math";
|
import type { InclusiveRange } from "@excalidraw/math";
|
||||||
|
|
||||||
import type { Bounds } from "@excalidraw/common";
|
import type { Bounds } from "@excalidraw/element";
|
||||||
import type { MaybeTransformHandleType } from "@excalidraw/element";
|
import type { MaybeTransformHandleType } from "@excalidraw/element";
|
||||||
import type {
|
import type {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ import { isLinearElementType } from "@excalidraw/element";
|
|||||||
import { getSelectedElements } from "@excalidraw/element";
|
import { getSelectedElements } from "@excalidraw/element";
|
||||||
import { selectGroupsForSelectedElements } from "@excalidraw/element";
|
import { selectGroupsForSelectedElements } from "@excalidraw/element";
|
||||||
|
|
||||||
import { FONT_SIZES } from "@excalidraw/common";
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
ExcalidrawGenericElement,
|
ExcalidrawGenericElement,
|
||||||
@@ -408,7 +406,7 @@ export class API {
|
|||||||
text: opts?.label?.text || "sample-text",
|
text: opts?.label?.text || "sample-text",
|
||||||
width: 50,
|
width: 50,
|
||||||
height: 20,
|
height: 20,
|
||||||
fontSize: FONT_SIZES.sm,
|
fontSize: 16,
|
||||||
containerId: rectangle.id,
|
containerId: rectangle.id,
|
||||||
frameId:
|
frameId:
|
||||||
opts?.label?.frameId === undefined
|
opts?.label?.frameId === undefined
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
type LocalPoint,
|
type LocalPoint,
|
||||||
} from "@excalidraw/math";
|
} from "@excalidraw/math";
|
||||||
|
|
||||||
import type { Bounds } from "@excalidraw/common";
|
import type { Bounds } from "@excalidraw/element";
|
||||||
|
|
||||||
export type LineSegment<P extends LocalPoint | GlobalPoint> = [P, P];
|
export type LineSegment<P extends LocalPoint | GlobalPoint> = [P, P];
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { arrayToMap, type Bounds } from "@excalidraw/common";
|
import { arrayToMap } from "@excalidraw/common";
|
||||||
import { getElementBounds } from "@excalidraw/element";
|
import { getElementBounds } from "@excalidraw/element";
|
||||||
import {
|
import {
|
||||||
isArrowElement,
|
isArrowElement,
|
||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
rangeInclusive,
|
rangeInclusive,
|
||||||
} from "@excalidraw/math";
|
} from "@excalidraw/math";
|
||||||
|
|
||||||
|
import type { Bounds } from "@excalidraw/element";
|
||||||
import type {
|
import type {
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
ExcalidrawFreeDrawElement,
|
ExcalidrawFreeDrawElement,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { API } from "@excalidraw/excalidraw/tests/helpers/api";
|
import { API } from "@excalidraw/excalidraw/tests/helpers/api";
|
||||||
|
|
||||||
import type { Bounds } from "@excalidraw/common";
|
import type { Bounds } from "@excalidraw/element";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
elementPartiallyOverlapsWithOrContainsBBox,
|
elementPartiallyOverlapsWithOrContainsBBox,
|
||||||
|
|||||||
@@ -6398,6 +6398,11 @@ is-map@^2.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e"
|
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e"
|
||||||
integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
|
integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
|
||||||
|
|
||||||
|
is-mobile@5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-mobile/-/is-mobile-5.0.0.tgz#1e08a0ef2c38a67bff84a52af68d67bcef445333"
|
||||||
|
integrity sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==
|
||||||
|
|
||||||
is-module@^1.0.0:
|
is-module@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||||
|
|||||||
Reference in New Issue
Block a user