Merge branch 'master' into cleanipp

cleanipp
Lipis 4 years ago committed by GitHub
commit e4a0254c47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3587,9 +3587,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
transformElements( transformElements(
pointerDownState, pointerDownState,
transformHandleType, transformHandleType,
(newTransformHandle) => {
pointerDownState.resize.handleType = newTransformHandle;
},
selectedElements, selectedElements,
pointerDownState.resize.arrowDirection, pointerDownState.resize.arrowDirection,
getRotateWithDiscreteAngleKey(event), getRotateWithDiscreteAngleKey(event),

@ -90,3 +90,4 @@ export const TAP_TWICE_TIMEOUT = 300;
export const TITLE_TIMEOUT = 10000; export const TITLE_TIMEOUT = 10000;
export const TOAST_TIMEOUT = 5000; export const TOAST_TIMEOUT = 5000;
export const TOUCH_CTX_MENU_TIMEOUT = 500; export const TOUCH_CTX_MENU_TIMEOUT = 500;
export const VERSION_TIMEOUT = 15000;

@ -34,7 +34,6 @@ export {
export { export {
resizeTest, resizeTest,
getCursorForResizingElement, getCursorForResizingElement,
normalizeTransformHandleType,
getElementWithTransformHandleType, getElementWithTransformHandleType,
getTransformHandleTypeFromCoords, getTransformHandleTypeFromCoords,
} from "./resizeTest"; } from "./resizeTest";

@ -4,7 +4,6 @@ import { rescalePoints } from "../points";
import { import {
rotate, rotate,
adjustXYWithRotation, adjustXYWithRotation,
getFlipAdjustment,
centerPoint, centerPoint,
rotatePoint, rotatePoint,
} from "../math"; } from "../math";
@ -13,21 +12,16 @@ import {
ExcalidrawTextElement, ExcalidrawTextElement,
NonDeletedExcalidrawElement, NonDeletedExcalidrawElement,
NonDeleted, NonDeleted,
ExcalidrawGenericElement,
ExcalidrawElement,
} from "./types"; } from "./types";
import { import {
getElementAbsoluteCoords, getElementAbsoluteCoords,
getCommonBounds, getCommonBounds,
getResizedElementAbsoluteCoords, getResizedElementAbsoluteCoords,
} from "./bounds"; } from "./bounds";
import { isGenericElement, isLinearElement, isTextElement } from "./typeChecks"; import { isLinearElement, isTextElement } from "./typeChecks";
import { mutateElement } from "./mutateElement"; import { mutateElement } from "./mutateElement";
import { getPerfectElementSize } from "./sizeHelpers"; import { getPerfectElementSize } from "./sizeHelpers";
import { import { getCursorForResizingElement } from "./resizeTest";
getCursorForResizingElement,
normalizeTransformHandleType,
} from "./resizeTest";
import { measureText, getFontString } from "../utils"; import { measureText, getFontString } from "../utils";
import { updateBoundElements } from "./binding"; import { updateBoundElements } from "./binding";
import { import {
@ -49,7 +43,6 @@ const normalizeAngle = (angle: number): number => {
export const transformElements = ( export const transformElements = (
pointerDownState: PointerDownState, pointerDownState: PointerDownState,
transformHandleType: MaybeTransformHandleType, transformHandleType: MaybeTransformHandleType,
setTransformHandle: (nextTransformHandle: MaybeTransformHandleType) => void,
selectedElements: readonly NonDeletedExcalidrawElement[], selectedElements: readonly NonDeletedExcalidrawElement[],
resizeArrowDirection: "origin" | "end", resizeArrowDirection: "origin" | "end",
isRotateWithDiscreteAngle: boolean, isRotateWithDiscreteAngle: boolean,
@ -101,36 +94,15 @@ export const transformElements = (
); );
updateBoundElements(element); updateBoundElements(element);
} else if (transformHandleType) { } else if (transformHandleType) {
if (isGenericElement(element)) { resizeSingleElement(
resizeSingleGenericElement( pointerDownState.originalElements.get(element.id) as typeof element,
pointerDownState.originalElements.get(element.id) as typeof element, shouldKeepSidesRatio,
shouldKeepSidesRatio, element,
element, transformHandleType,
transformHandleType, isResizeCenterPoint,
isResizeCenterPoint, pointerX,
pointerX, pointerY,
pointerY, );
);
} else {
const keepSquareAspectRatio = shouldKeepSidesRatio;
resizeSingleNonGenericElement(
element,
transformHandleType,
isResizeCenterPoint,
keepSquareAspectRatio,
pointerX,
pointerY,
);
setTransformHandle(
normalizeTransformHandleType(element, transformHandleType),
);
if (element.width < 0) {
mutateElement(element, { width: -element.width });
}
if (element.height < 0) {
mutateElement(element, { height: -element.height });
}
}
} }
// update cursor // update cursor
@ -414,8 +386,8 @@ const resizeSingleTextElement = (
} }
}; };
const resizeSingleGenericElement = ( const resizeSingleElement = (
stateAtResizeStart: NonDeleted<ExcalidrawGenericElement>, stateAtResizeStart: NonDeletedExcalidrawElement,
shouldKeepSidesRatio: boolean, shouldKeepSidesRatio: boolean,
element: NonDeletedExcalidrawElement, element: NonDeletedExcalidrawElement,
transformHandleDirection: TransformHandleDirection, transformHandleDirection: TransformHandleDirection,
@ -423,251 +395,184 @@ const resizeSingleGenericElement = (
pointerX: number, pointerX: number,
pointerY: number, pointerY: number,
) => { ) => {
const [x1, y1, x2, y2] = getElementAbsoluteCoords(stateAtResizeStart); // Gets bounds corners
const [x1, y1, x2, y2] = getResizedElementAbsoluteCoords(
stateAtResizeStart,
stateAtResizeStart.width,
stateAtResizeStart.height,
);
const startTopLeft: Point = [x1, y1]; const startTopLeft: Point = [x1, y1];
const startBottomRight: Point = [x2, y2]; const startBottomRight: Point = [x2, y2];
const startCenter: Point = centerPoint(startTopLeft, startBottomRight); const startCenter: Point = centerPoint(startTopLeft, startBottomRight);
// Calculate new dimensions based on cursor position // Calculate new dimensions based on cursor position
let newWidth = stateAtResizeStart.width;
let newHeight = stateAtResizeStart.height;
const rotatedPointer = rotatePoint( const rotatedPointer = rotatePoint(
[pointerX, pointerY], [pointerX, pointerY],
startCenter, startCenter,
-stateAtResizeStart.angle, -stateAtResizeStart.angle,
); );
//Get bounds corners rendered on screen
const [esx1, esy1, esx2, esy2] = getResizedElementAbsoluteCoords(
element,
element.width,
element.height,
);
const boundsCurrentWidth = esx2 - esx1;
const boundsCurrentHeight = esy2 - esy1;
// It's important we set the initial scale value based on the width and height at resize start,
// otherwise previous dimensions affected by modifiers will be taken into account.
const atStartBoundsWidth = startBottomRight[0] - startTopLeft[0];
const atStartBoundsHeight = startBottomRight[1] - startTopLeft[1];
let scaleX = atStartBoundsWidth / boundsCurrentWidth;
let scaleY = atStartBoundsHeight / boundsCurrentHeight;
if (transformHandleDirection.includes("e")) { if (transformHandleDirection.includes("e")) {
newWidth = rotatedPointer[0] - startTopLeft[0]; scaleX = (rotatedPointer[0] - startTopLeft[0]) / boundsCurrentWidth;
} }
if (transformHandleDirection.includes("s")) { if (transformHandleDirection.includes("s")) {
newHeight = rotatedPointer[1] - startTopLeft[1]; scaleY = (rotatedPointer[1] - startTopLeft[1]) / boundsCurrentHeight;
} }
if (transformHandleDirection.includes("w")) { if (transformHandleDirection.includes("w")) {
newWidth = startBottomRight[0] - rotatedPointer[0]; scaleX = (startBottomRight[0] - rotatedPointer[0]) / boundsCurrentWidth;
} }
if (transformHandleDirection.includes("n")) { if (transformHandleDirection.includes("n")) {
newHeight = startBottomRight[1] - rotatedPointer[1]; scaleY = (startBottomRight[1] - rotatedPointer[1]) / boundsCurrentHeight;
} }
// Linear elements dimensions differ from bounds dimensions
const eleInitialWidth = stateAtResizeStart.width;
const eleInitialHeight = stateAtResizeStart.height;
// We have to use dimensions of element on screen, otherwise the scaling of the
// dimensions won't match the cursor for linear elements.
let eleNewWidth = element.width * scaleX;
let eleNewHeight = element.height * scaleY;
// adjust dimensions for resizing from center // adjust dimensions for resizing from center
if (isResizeFromCenter) { if (isResizeFromCenter) {
newWidth = 2 * newWidth - stateAtResizeStart.width; eleNewWidth = 2 * eleNewWidth - eleInitialWidth;
newHeight = 2 * newHeight - stateAtResizeStart.height; eleNewHeight = 2 * eleNewHeight - eleInitialHeight;
} }
// adjust dimensions to keep sides ratio // adjust dimensions to keep sides ratio
if (shouldKeepSidesRatio) { if (shouldKeepSidesRatio) {
const widthRatio = Math.abs(newWidth) / stateAtResizeStart.width; const widthRatio = Math.abs(eleNewWidth) / eleInitialWidth;
const heightRatio = Math.abs(newHeight) / stateAtResizeStart.height; const heightRatio = Math.abs(eleNewHeight) / eleInitialHeight;
if (transformHandleDirection.length === 1) { if (transformHandleDirection.length === 1) {
newHeight *= widthRatio; eleNewHeight *= widthRatio;
newWidth *= heightRatio; eleNewWidth *= heightRatio;
} }
if (transformHandleDirection.length === 2) { if (transformHandleDirection.length === 2) {
const ratio = Math.max(widthRatio, heightRatio); const ratio = Math.max(widthRatio, heightRatio);
newWidth = stateAtResizeStart.width * ratio * Math.sign(newWidth); eleNewWidth = eleInitialWidth * ratio * Math.sign(eleNewWidth);
newHeight = stateAtResizeStart.height * ratio * Math.sign(newHeight); eleNewHeight = eleInitialHeight * ratio * Math.sign(eleNewHeight);
} }
} }
const [
newBoundsX1,
newBoundsY1,
newBoundsX2,
newBoundsY2,
] = getResizedElementAbsoluteCoords(
stateAtResizeStart,
eleNewWidth,
eleNewHeight,
);
const newBoundsWidth = newBoundsX2 - newBoundsX1;
const newBoundsHeight = newBoundsY2 - newBoundsY1;
// Calculate new topLeft based on fixed corner during resize // Calculate new topLeft based on fixed corner during resize
let newTopLeft = startTopLeft as [number, number]; let newTopLeft = [...startTopLeft] as [number, number];
if (["n", "w", "nw"].includes(transformHandleDirection)) { if (["n", "w", "nw"].includes(transformHandleDirection)) {
newTopLeft = [ newTopLeft = [
startBottomRight[0] - Math.abs(newWidth), startBottomRight[0] - Math.abs(newBoundsWidth),
startBottomRight[1] - Math.abs(newHeight), startBottomRight[1] - Math.abs(newBoundsHeight),
]; ];
} }
if (transformHandleDirection === "ne") { if (transformHandleDirection === "ne") {
const bottomLeft = [ const bottomLeft = [startTopLeft[0], startBottomRight[1]];
stateAtResizeStart.x, newTopLeft = [bottomLeft[0], bottomLeft[1] - Math.abs(newBoundsHeight)];
stateAtResizeStart.y + stateAtResizeStart.height,
];
newTopLeft = [bottomLeft[0], bottomLeft[1] - Math.abs(newHeight)];
} }
if (transformHandleDirection === "sw") { if (transformHandleDirection === "sw") {
const topRight = [ const topRight = [startBottomRight[0], startTopLeft[1]];
stateAtResizeStart.x + stateAtResizeStart.width, newTopLeft = [topRight[0] - Math.abs(newBoundsWidth), topRight[1]];
stateAtResizeStart.y,
];
newTopLeft = [topRight[0] - Math.abs(newWidth), topRight[1]];
} }
// Keeps opposite handle fixed during resize // Keeps opposite handle fixed during resize
if (shouldKeepSidesRatio) { if (shouldKeepSidesRatio) {
if (["s", "n"].includes(transformHandleDirection)) { if (["s", "n"].includes(transformHandleDirection)) {
newTopLeft[0] = startCenter[0] - newWidth / 2; newTopLeft[0] = startCenter[0] - newBoundsWidth / 2;
} }
if (["e", "w"].includes(transformHandleDirection)) { if (["e", "w"].includes(transformHandleDirection)) {
newTopLeft[1] = startCenter[1] - newHeight / 2; newTopLeft[1] = startCenter[1] - newBoundsHeight / 2;
} }
} }
// Flip horizontally // Flip horizontally
if (newWidth < 0) { if (eleNewWidth < 0) {
if (transformHandleDirection.includes("e")) { if (transformHandleDirection.includes("e")) {
newTopLeft[0] -= Math.abs(newWidth); newTopLeft[0] -= Math.abs(newBoundsWidth);
} }
if (transformHandleDirection.includes("w")) { if (transformHandleDirection.includes("w")) {
newTopLeft[0] += Math.abs(newWidth); newTopLeft[0] += Math.abs(newBoundsWidth);
} }
} }
// Flip vertically // Flip vertically
if (newHeight < 0) { if (eleNewHeight < 0) {
if (transformHandleDirection.includes("s")) { if (transformHandleDirection.includes("s")) {
newTopLeft[1] -= Math.abs(newHeight); newTopLeft[1] -= Math.abs(newBoundsHeight);
} }
if (transformHandleDirection.includes("n")) { if (transformHandleDirection.includes("n")) {
newTopLeft[1] += Math.abs(newHeight); newTopLeft[1] += Math.abs(newBoundsHeight);
} }
} }
if (isResizeFromCenter) { if (isResizeFromCenter) {
newTopLeft[0] = startCenter[0] - Math.abs(newWidth) / 2; newTopLeft[0] = startCenter[0] - Math.abs(newBoundsWidth) / 2;
newTopLeft[1] = startCenter[1] - Math.abs(newHeight) / 2; newTopLeft[1] = startCenter[1] - Math.abs(newBoundsHeight) / 2;
} }
// adjust topLeft to new rotation point // adjust topLeft to new rotation point
const angle = stateAtResizeStart.angle; const angle = stateAtResizeStart.angle;
const rotatedTopLeft = rotatePoint(newTopLeft, startCenter, angle); const rotatedTopLeft = rotatePoint(newTopLeft, startCenter, angle);
const newCenter: Point = [ const newCenter: Point = [
newTopLeft[0] + Math.abs(newWidth) / 2, newTopLeft[0] + Math.abs(newBoundsWidth) / 2,
newTopLeft[1] + Math.abs(newHeight) / 2, newTopLeft[1] + Math.abs(newBoundsHeight) / 2,
]; ];
const rotatedNewCenter = rotatePoint(newCenter, startCenter, angle); const rotatedNewCenter = rotatePoint(newCenter, startCenter, angle);
newTopLeft = rotatePoint(rotatedTopLeft, rotatedNewCenter, -angle); newTopLeft = rotatePoint(rotatedTopLeft, rotatedNewCenter, -angle);
const resizedElement = { // Readjust points for linear elements
width: Math.abs(newWidth), const rescaledPoints = rescalePointsInElement(
height: Math.abs(newHeight), stateAtResizeStart,
x: newTopLeft[0], eleNewWidth,
y: newTopLeft[1], eleNewHeight,
};
updateBoundElements(element, {
newSize: { width: resizedElement.width, height: resizedElement.height },
});
mutateElement(element, resizedElement);
};
const resizeSingleNonGenericElement = (
element: NonDeleted<Exclude<ExcalidrawElement, ExcalidrawGenericElement>>,
transformHandleType: "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se",
isResizeFromCenter: boolean,
keepSquareAspectRatio: boolean,
pointerX: number,
pointerY: number,
) => {
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
const cx = (x1 + x2) / 2;
const cy = (y1 + y2) / 2;
// rotation pointer with reverse angle
const [rotatedX, rotatedY] = rotate(
pointerX,
pointerY,
cx,
cy,
-element.angle,
); );
// For linear elements (x,y) are the coordinates of the first drawn point not the top-left corner
// So we need to readjust (x,y) to be where the first point should be
const newOrigin = [...newTopLeft];
newOrigin[0] += stateAtResizeStart.x - newBoundsX1;
newOrigin[1] += stateAtResizeStart.y - newBoundsY1;
let scaleX = 1; const resizedElement = {
let scaleY = 1; width: Math.abs(eleNewWidth),
if ( height: Math.abs(eleNewHeight),
transformHandleType === "e" || x: newOrigin[0],
transformHandleType === "ne" || y: newOrigin[1],
transformHandleType === "se" ...rescaledPoints,
) { };
scaleX = (rotatedX - x1) / (x2 - x1);
}
if (
transformHandleType === "s" ||
transformHandleType === "sw" ||
transformHandleType === "se"
) {
scaleY = (rotatedY - y1) / (y2 - y1);
}
if (
transformHandleType === "w" ||
transformHandleType === "nw" ||
transformHandleType === "sw"
) {
scaleX = (x2 - rotatedX) / (x2 - x1);
}
if (
transformHandleType === "n" ||
transformHandleType === "nw" ||
transformHandleType === "ne"
) {
scaleY = (y2 - rotatedY) / (y2 - y1);
}
let nextWidth = element.width * scaleX;
let nextHeight = element.height * scaleY;
if (keepSquareAspectRatio) {
nextWidth = nextHeight = Math.max(nextWidth, nextHeight);
}
const [nextX1, nextY1, nextX2, nextY2] = getResizedElementAbsoluteCoords(
element,
nextWidth,
nextHeight,
);
const deltaX1 = (x1 - nextX1) / 2;
const deltaY1 = (y1 - nextY1) / 2;
const deltaX2 = (x2 - nextX2) / 2;
const deltaY2 = (y2 - nextY2) / 2;
const rescaledPoints = rescalePointsInElement(element, nextWidth, nextHeight);
updateBoundElements(element, {
newSize: { width: nextWidth, height: nextHeight },
});
const [finalX1, finalY1, finalX2, finalY2] = getResizedElementAbsoluteCoords(
{
...element,
...rescaledPoints,
},
Math.abs(nextWidth),
Math.abs(nextHeight),
);
const [flipDiffX, flipDiffY] = getFlipAdjustment(
transformHandleType,
nextWidth,
nextHeight,
nextX1,
nextY1,
nextX2,
nextY2,
finalX1,
finalY1,
finalX2,
finalY2,
isLinearElement(element),
element.angle,
);
const [nextElementX, nextElementY] = adjustXYWithRotation(
getSidesForTransformHandle(transformHandleType, isResizeFromCenter),
element.x - flipDiffX,
element.y - flipDiffY,
element.angle,
deltaX1,
deltaY1,
deltaX2,
deltaY2,
);
if ( if (
nextWidth !== 0 && resizedElement.width !== 0 &&
nextHeight !== 0 && resizedElement.height !== 0 &&
Number.isFinite(nextElementX) && Number.isFinite(resizedElement.x) &&
Number.isFinite(nextElementY) Number.isFinite(resizedElement.y)
) { ) {
mutateElement(element, { updateBoundElements(element, {
width: nextWidth, newSize: { width: resizedElement.width, height: resizedElement.height },
height: nextHeight,
x: nextElementX,
y: nextElementY,
...rescaledPoints,
}); });
mutateElement(element, resizedElement);
} }
}; };

@ -173,57 +173,3 @@ export const getCursorForResizingElement = (resizingElement: {
return cursor ? `${cursor}-resize` : ""; return cursor ? `${cursor}-resize` : "";
}; };
export const normalizeTransformHandleType = (
element: ExcalidrawElement,
transformHandleType: TransformHandleType,
): TransformHandleType => {
if (element.width >= 0 && element.height >= 0) {
return transformHandleType;
}
if (element.width < 0 && element.height < 0) {
switch (transformHandleType) {
case "nw":
return "se";
case "ne":
return "sw";
case "se":
return "nw";
case "sw":
return "ne";
}
} else if (element.width < 0) {
switch (transformHandleType) {
case "nw":
return "ne";
case "ne":
return "nw";
case "se":
return "sw";
case "sw":
return "se";
case "e":
return "w";
case "w":
return "e";
}
} else {
switch (transformHandleType) {
case "nw":
return "sw";
case "ne":
return "se";
case "se":
return "ne";
case "sw":
return "nw";
case "n":
return "s";
case "s":
return "n";
}
}
return transformHandleType;
};

@ -11,7 +11,7 @@ import { getDefaultAppState } from "../appState";
import { ExcalidrawImperativeAPI } from "../components/App"; import { ExcalidrawImperativeAPI } from "../components/App";
import { ErrorDialog } from "../components/ErrorDialog"; import { ErrorDialog } from "../components/ErrorDialog";
import { TopErrorBoundary } from "../components/TopErrorBoundary"; import { TopErrorBoundary } from "../components/TopErrorBoundary";
import { APP_NAME, EVENT, TITLE_TIMEOUT } from "../constants"; import { APP_NAME, EVENT, TITLE_TIMEOUT, VERSION_TIMEOUT } from "../constants";
import { ImportedDataState } from "../data/types"; import { ImportedDataState } from "../data/types";
import { import {
ExcalidrawElement, ExcalidrawElement,
@ -229,18 +229,10 @@ const ExcalidrawWrapper = (props: { collab: CollabAPI }) => {
const { collab } = props; const { collab } = props;
useEffect(() => { useEffect(() => {
// delayed by 15 sec so that the app has a time to load the latest SW // Delayed so that the app has a time to load the latest SW
setTimeout(() => { setTimeout(() => {
const version = getVersion(); trackEvent("load", "version", getVersion());
const loggedVersion = window.localStorage.getItem( }, VERSION_TIMEOUT);
"excalidraw-lastLoggedVersion",
);
// prevent logging on multiple visits
if (version && version !== loggedVersion) {
window.localStorage.setItem("excalidraw-lastLoggedVersion", version);
trackEvent("load", "version", version);
}
}, 15000);
excalidrawRef.current!.readyPromise.then((excalidrawApi) => { excalidrawRef.current!.readyPromise.then((excalidrawApi) => {
initializeScene({ initializeScene({

@ -70,64 +70,6 @@ export const adjustXYWithRotation = (
return [x, y]; return [x, y];
}; };
export const getFlipAdjustment = (
side: "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se",
nextWidth: number,
nextHeight: number,
nextX1: number,
nextY1: number,
nextX2: number,
nextY2: number,
finalX1: number,
finalY1: number,
finalX2: number,
finalY2: number,
needsRotation: boolean,
angle: number,
): [number, number] => {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
let flipDiffX = 0;
let flipDiffY = 0;
if (nextWidth < 0) {
if (side === "e" || side === "ne" || side === "se") {
if (needsRotation) {
flipDiffX += (finalX2 - nextX1) * cos;
flipDiffY += (finalX2 - nextX1) * sin;
} else {
flipDiffX += finalX2 - nextX1;
}
}
if (side === "w" || side === "nw" || side === "sw") {
if (needsRotation) {
flipDiffX += (finalX1 - nextX2) * cos;
flipDiffY += (finalX1 - nextX2) * sin;
} else {
flipDiffX += finalX1 - nextX2;
}
}
}
if (nextHeight < 0) {
if (side === "s" || side === "se" || side === "sw") {
if (needsRotation) {
flipDiffY += (finalY2 - nextY1) * cos;
flipDiffX += (finalY2 - nextY1) * -sin;
} else {
flipDiffY += finalY2 - nextY1;
}
}
if (side === "n" || side === "ne" || side === "nw") {
if (needsRotation) {
flipDiffY += (finalY1 - nextY2) * cos;
flipDiffX += (finalY1 - nextY2) * -sin;
} else {
flipDiffY += finalY1 - nextY2;
}
}
}
return [flipDiffX, flipDiffY];
};
export const getPointOnAPath = (point: Point, path: Point[]) => { export const getPointOnAPath = (point: Point, path: Point[]) => {
const [px, py] = point; const [px, py] = point;
const [start, ...other] = path; const [start, ...other] = path;

@ -12,15 +12,21 @@ The change should be grouped under one of the below section and must contain PR
Please add the latest change on the top under the correct section. Please add the latest change on the top under the correct section.
--> -->
## [Unreleased] ## 0.2.0
### Features ### Features
- Add toast [#2772](https://github.com/excalidraw/excalidraw/pull/2772)
- Add `cmd+o` shortcut to load scene [#2732](https://github.com/excalidraw/excalidraw/pull/2732) - Add `cmd+o` shortcut to load scene [#2732](https://github.com/excalidraw/excalidraw/pull/2732)
- Remove language picker, and add `langCode`, `renderFooter` [#2644](https://github.com/excalidraw/excalidraw/pull/2644): - Remove language picker, and add `langCode`, `renderFooter` [#2644](https://github.com/excalidraw/excalidraw/pull/2644):
- BREAKING: removed the language picker from UI. It is now the host app's responsibility to implement a language picker if desirable, using the newly added [`renderFooter`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#renderFooter) prop. The reasoning is that the i18n should be controlled by the app itself, not by the nested Excalidraw component. - BREAKING: removed the language picker from UI. It is now the host app's responsibility to implement a language picker if desirable, using the newly added [`renderFooter`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#renderFooter) prop. The reasoning is that the i18n should be controlled by the app itself, not by the nested Excalidraw component.
- Added [`langCode`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#langCode) prop to control the UI language. - Added [`langCode`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#langCode) prop to control the UI language.
- Add support for `exportToBackend` prop to allow host apps to implement shareable links [#2612](https://github.com/excalidraw/excalidraw/pull/2612/files) - Add support for `exportToBackend` prop to allow host apps to implement shareable links [#2612](https://github.com/excalidraw/excalidraw/pull/2612/files)
- Require use of a preset dialog size; adjust dialog sizing [#2684](https://github.com/excalidraw/excalidraw/pull/2684)
- Add line chart and paste dialog selection [#2670](https://github.com/excalidraw/excalidraw/pull/2670)
- Tweak editing behavior [#2668](https://github.com/excalidraw/excalidraw/pull/2668)
- Change title to Excalidraw after a timeout
- Checkmark to toggle context-menu-items [#2645](https://github.com/excalidraw/excalidraw/pull/2645)
- Add zoom to selection [#2522](https://github.com/excalidraw/excalidraw/pull/2522) - Add zoom to selection [#2522](https://github.com/excalidraw/excalidraw/pull/2522)
- Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527) - Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527)
- Show shortcut context menu [#2501](https://github.com/excalidraw/excalidraw/pull/2501) - Show shortcut context menu [#2501](https://github.com/excalidraw/excalidraw/pull/2501)
@ -31,7 +37,12 @@ Please add the latest change on the top under the correct section.
### Fixes ### Fixes
- Fix typo for initialData and point all links to master [#2707](https://github.com/excalidraw/excalidraw/pull/2707)
- Fix compile error [#2685](https://github.com/excalidraw/excalidraw/pull/2685)
- Center zoom on iPhone and iPad [#2642](https://github.com/excalidraw/excalidraw/pull/2642)
- Allow text-selecting in dialogs & reset cursor [#2783](https://github.com/excalidraw/excalidraw/pull/2783) - Allow text-selecting in dialogs & reset cursor [#2783](https://github.com/excalidraw/excalidraw/pull/2783)
- Don't render due to zoom after unmount [#2779](https://github.com/excalidraw/excalidraw/pull/2779)
- Track the chart type correctly [#2773](https://github.com/excalidraw/excalidraw/pull/2773)
- Fix late-render due to debounced zoom [#2779](https://github.com/excalidraw/excalidraw/pull/2779) - Fix late-render due to debounced zoom [#2779](https://github.com/excalidraw/excalidraw/pull/2779)
- Fix initialization when browser tab not focused [#2677](https://github.com/excalidraw/excalidraw/pull/2677) - Fix initialization when browser tab not focused [#2677](https://github.com/excalidraw/excalidraw/pull/2677)
- Consistent case for export locale strings [#2622](https://github.com/excalidraw/excalidraw/pull/2622) - Consistent case for export locale strings [#2622](https://github.com/excalidraw/excalidraw/pull/2622)
@ -47,6 +58,11 @@ Please add the latest change on the top under the correct section.
### Improvements ### Improvements
- Added Zen Mode to the context menu [#2734](https://github.com/excalidraw/excalidraw/pull/2734) - Added Zen Mode to the context menu [#2734](https://github.com/excalidraw/excalidraw/pull/2734)
- Do not reset to selection for draw tool [#2721]((https://github.com/excalidraw/excalidraw/pull/2721)
- Make dialogs look more like dialogs [#2686](https://github.com/excalidraw/excalidraw/pull/2686)
- Browse libraries styles fixed [#2694](https://github.com/excalidraw/excalidraw/pull/2694)
- Change hint for 2-point lines on resize [#2655](https://github.com/excalidraw/excalidraw/pull/2655)
- Align items in context menu [#2640](https://github.com/excalidraw/excalidraw/pull/2640)
- Do not reset to selection when using the draw tool [#2721](https://github.com/excalidraw/excalidraw/pull/2721) - Do not reset to selection when using the draw tool [#2721](https://github.com/excalidraw/excalidraw/pull/2721)
- Display proper tooltip for 2-point lines during resize, and normalize modifier key labels in hints [#2655](https://github.com/excalidraw/excalidraw/pull/2655) - Display proper tooltip for 2-point lines during resize, and normalize modifier key labels in hints [#2655](https://github.com/excalidraw/excalidraw/pull/2655)
- Improve error message around importing images [#2619](https://github.com/excalidraw/excalidraw/pull/2619) - Improve error message around importing images [#2619](https://github.com/excalidraw/excalidraw/pull/2619)
@ -56,6 +72,14 @@ Please add the latest change on the top under the correct section.
- Hide shortcuts on pickers for mobile [#2508](https://github.com/excalidraw/excalidraw/pull/2508) - Hide shortcuts on pickers for mobile [#2508](https://github.com/excalidraw/excalidraw/pull/2508)
- Hide stats and scrollToContent-button when mobile menus open [#2509](https://github.com/excalidraw/excalidraw/pull/2509) - Hide stats and scrollToContent-button when mobile menus open [#2509](https://github.com/excalidraw/excalidraw/pull/2509)
### Refactor
- refactor: Converting span to kbd tag [#2774](https://github.com/excalidraw/excalidraw/pull/2774)
- Media queries [#2680](https://github.com/excalidraw/excalidraw/pull/2680)
- Remove duplicate entry from en.json[#2654](https://github.com/excalidraw/excalidraw/pull/2654)
- Remove the word toggle from labels [#2648](https://github.com/excalidraw/excalidraw/pull/2648)
-
### Chore ### Chore
- Bump ini from 1.3.5 to 1.3.7 in /src/packages/excalidraw [#2500](https://github.com/excalidraw/excalidraw/pull/2500) - Bump ini from 1.3.5 to 1.3.7 in /src/packages/excalidraw [#2500](https://github.com/excalidraw/excalidraw/pull/2500)

@ -142,6 +142,51 @@ export default function App() {
| [`langCode`](#langCode) | string | `en` | Language code string | | [`langCode`](#langCode) | string | `en` | Language code string |
| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer | | [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
### `Extra API's`
#### `getSceneVersion`
**How to use**
<pre>
import { getSceneVersion } from "@excalidraw/excalidraw";
getSceneVersion(elements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>)
</pre>
This function returns the current scene version.
#### `getSyncableElements`
**_Signature_**
<pre>
getSyncableElements(elements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>):<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>
</pre>
**How to use**
```js
import { getSyncableElements } from "@excalidraw/excalidraw";
```
This function returns all the deleted elements of the scene.
### `getElementMap`
**_Signature_**
<pre>
getElementsMap(elements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>): {[id: string]: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement</a>}
</pre>
**How to use**
```js
import { getElementsMap } from "@excalidraw/excalidraw";
```
This function returns an object where each element is mapped to its id.
#### `width` #### `width`
This props defines the `width` of the Excalidraw component. Defaults to `window.innerWidth` if not passed. This props defines the `width` of the Excalidraw component. Defaults to `window.innerWidth` if not passed.

@ -1,6 +1,6 @@
{ {
"name": "@excalidraw/excalidraw", "name": "@excalidraw/excalidraw",
"version": "0.1.1", "version": "0.2.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

@ -1,6 +1,6 @@
{ {
"name": "@excalidraw/excalidraw", "name": "@excalidraw/excalidraw",
"version": "0.1.1", "version": "0.2.0",
"main": "dist/excalidraw.min.js", "main": "dist/excalidraw.min.js",
"files": [ "files": [
"dist/*" "dist/*"

@ -41,7 +41,7 @@ describe("resize rectangle ellipses and diamond elements", () => {
${"s"} | ${[_, 39]} | ${[100, 139]} | ${[elemData.x, elemData.x]} ${"s"} | ${[_, 39]} | ${[100, 139]} | ${[elemData.x, elemData.x]}
${"e"} | ${[-20, _]} | ${[80, 100]} | ${[elemData.x, elemData.y]} ${"e"} | ${[-20, _]} | ${[80, 100]} | ${[elemData.x, elemData.y]}
${"w"} | ${[-20, _]} | ${[120, 100]} | ${[-20, elemData.y]} ${"w"} | ${[-20, _]} | ${[120, 100]} | ${[-20, elemData.y]}
${"ne"} | ${[10, 55]} | ${[110, 45]} | ${[elemData.x, 55]} ${"ne"} | ${[5, 55]} | ${[105, 45]} | ${[elemData.x, 55]}
${"se"} | ${[-30, -10]} | ${[70, 90]} | ${[elemData.x, elemData.y]} ${"se"} | ${[-30, -10]} | ${[70, 90]} | ${[elemData.x, elemData.y]}
${"nw"} | ${[-300, -200]} | ${[400, 300]} | ${[-300, -200]} ${"nw"} | ${[-300, -200]} | ${[400, 300]} | ${[-300, -200]}
${"sw"} | ${[40, -20]} | ${[60, 80]} | ${[40, 0]} ${"sw"} | ${[40, -20]} | ${[60, 80]} | ${[40, 0]}

Loading…
Cancel
Save