enable version bumping for collaboration
This commit is contained in:
parent
30903fbe04
commit
1419f17175
@ -7,6 +7,7 @@ import { done } from "../components/icons";
|
|||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import { register } from "./register";
|
import { register } from "./register";
|
||||||
import { invalidateShapeForElement } from "../renderer/renderElement";
|
import { invalidateShapeForElement } from "../renderer/renderElement";
|
||||||
|
import { mutateElement } from "../element/mutateElement";
|
||||||
|
|
||||||
export const actionFinalize = register({
|
export const actionFinalize = register({
|
||||||
name: "finalize",
|
name: "finalize",
|
||||||
@ -18,10 +19,12 @@ export const actionFinalize = register({
|
|||||||
if (appState.multiElement) {
|
if (appState.multiElement) {
|
||||||
// pen and mouse have hover
|
// pen and mouse have hover
|
||||||
if (appState.lastPointerDownWith !== "touch") {
|
if (appState.lastPointerDownWith !== "touch") {
|
||||||
appState.multiElement.points = appState.multiElement.points.slice(
|
mutateElement(appState.multiElement, multiElement => {
|
||||||
0,
|
multiElement.points = multiElement.points.slice(
|
||||||
appState.multiElement.points.length - 1,
|
0,
|
||||||
);
|
multiElement.points.length - 1,
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (isInvisiblySmallElement(appState.multiElement)) {
|
if (isInvisiblySmallElement(appState.multiElement)) {
|
||||||
newElements = newElements.slice(0, -1);
|
newElements = newElements.slice(0, -1);
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
import { KEYS } from "../keys";
|
import { KEYS } from "../keys";
|
||||||
import { DEFAULT_FONT } from "../appState";
|
import { DEFAULT_FONT } from "../appState";
|
||||||
import { register } from "./register";
|
import { register } from "./register";
|
||||||
|
import { mutateTextElement } from "../element/mutateElement";
|
||||||
|
|
||||||
let copiedStyles: string = "{}";
|
let copiedStyles: string = "{}";
|
||||||
|
|
||||||
@ -44,8 +45,10 @@ export const actionPasteStyles = register({
|
|||||||
roughness: pastedElement?.roughness,
|
roughness: pastedElement?.roughness,
|
||||||
};
|
};
|
||||||
if (isTextElement(newElement)) {
|
if (isTextElement(newElement)) {
|
||||||
newElement.font = pastedElement?.font || DEFAULT_FONT;
|
mutateTextElement(newElement, newElement => {
|
||||||
redrawTextBoundingBox(newElement);
|
newElement.font = pastedElement?.font || DEFAULT_FONT;
|
||||||
|
redrawTextBoundingBox(newElement);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return newElement;
|
return newElement;
|
||||||
}
|
}
|
||||||
|
@ -88,6 +88,7 @@ import { LayerUI } from "./LayerUI";
|
|||||||
import { ScrollBars } from "../scene/types";
|
import { ScrollBars } from "../scene/types";
|
||||||
import { invalidateShapeForElement } from "../renderer/renderElement";
|
import { invalidateShapeForElement } from "../renderer/renderElement";
|
||||||
import { generateCollaborationLink, getCollaborationLinkData } from "../data";
|
import { generateCollaborationLink, getCollaborationLinkData } from "../data";
|
||||||
|
import { mutateElement } from "../element/mutateElement";
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// TEST HOOKS
|
// TEST HOOKS
|
||||||
@ -262,7 +263,29 @@ export class App extends React.Component<any, AppState> {
|
|||||||
sceneAppState || getDefaultAppState(),
|
sceneAppState || getDefaultAppState(),
|
||||||
{ scrollToContent: true },
|
{ scrollToContent: true },
|
||||||
);
|
);
|
||||||
elements = restoredState.elements;
|
if (elements == null || elements.length === 0) {
|
||||||
|
elements = restoredState.elements;
|
||||||
|
} else {
|
||||||
|
const elementMap = elements.reduce(
|
||||||
|
(
|
||||||
|
acc: { [key: string]: ExcalidrawElement },
|
||||||
|
element: ExcalidrawElement,
|
||||||
|
) => {
|
||||||
|
acc[element.id] = element;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
elements = restoredState.elements.map(element => {
|
||||||
|
if (
|
||||||
|
elementMap.hasOwnProperty(element.id) &&
|
||||||
|
elementMap[element.id].version > element.version
|
||||||
|
) {
|
||||||
|
return elementMap[element.id];
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
});
|
||||||
|
}
|
||||||
this.setState({});
|
this.setState({});
|
||||||
if (this.socketInitialized === false) {
|
if (this.socketInitialized === false) {
|
||||||
this.socketInitialized = true;
|
this.socketInitialized = true;
|
||||||
@ -774,8 +797,10 @@ export class App extends React.Component<any, AppState> {
|
|||||||
textY = centerElementYInViewport;
|
textY = centerElementYInViewport;
|
||||||
|
|
||||||
// x and y will change after calling newTextElement function
|
// x and y will change after calling newTextElement function
|
||||||
element.x = centerElementX;
|
mutateElement(element, element => {
|
||||||
element.y = centerElementY;
|
element.x = centerElementX;
|
||||||
|
element.y = centerElementY;
|
||||||
|
});
|
||||||
} else if (!event.altKey) {
|
} else if (!event.altKey) {
|
||||||
const snappedToCenterPosition = this.getTextWysiwygSnappedToCenterPosition(
|
const snappedToCenterPosition = this.getTextWysiwygSnappedToCenterPosition(
|
||||||
x,
|
x,
|
||||||
@ -783,8 +808,10 @@ export class App extends React.Component<any, AppState> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (snappedToCenterPosition) {
|
if (snappedToCenterPosition) {
|
||||||
element.x = snappedToCenterPosition.elementCenterX;
|
mutateElement(element, element => {
|
||||||
element.y = snappedToCenterPosition.elementCenterY;
|
element.x = snappedToCenterPosition.elementCenterX;
|
||||||
|
element.y = snappedToCenterPosition.elementCenterY;
|
||||||
|
});
|
||||||
textX = snappedToCenterPosition.wysiwygX;
|
textX = snappedToCenterPosition.wysiwygX;
|
||||||
textY = snappedToCenterPosition.wysiwygY;
|
textY = snappedToCenterPosition.wysiwygY;
|
||||||
}
|
}
|
||||||
@ -1336,13 +1363,17 @@ export class App extends React.Component<any, AppState> {
|
|||||||
|
|
||||||
const dx = element.x + width + p1[0];
|
const dx = element.x + width + p1[0];
|
||||||
const dy = element.y + height + p1[1];
|
const dy = element.y + height + p1[1];
|
||||||
element.x = dx;
|
mutateElement(element, element => {
|
||||||
element.y = dy;
|
element.x = dx;
|
||||||
|
element.y = dy;
|
||||||
|
});
|
||||||
p1[0] = absPx - element.x;
|
p1[0] = absPx - element.x;
|
||||||
p1[1] = absPy - element.y;
|
p1[1] = absPy - element.y;
|
||||||
} else {
|
} else {
|
||||||
element.x += deltaX;
|
mutateElement(element, element => {
|
||||||
element.y += deltaY;
|
element.x += deltaX;
|
||||||
|
element.y += deltaY;
|
||||||
|
});
|
||||||
p1[0] -= deltaX;
|
p1[0] -= deltaX;
|
||||||
p1[1] -= deltaY;
|
p1[1] -= deltaY;
|
||||||
}
|
}
|
||||||
@ -1452,16 +1483,17 @@ export class App extends React.Component<any, AppState> {
|
|||||||
event.shiftKey,
|
event.shiftKey,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
element.width -= deltaX;
|
mutateElement(element, element => {
|
||||||
element.x += deltaX;
|
element.width -= deltaX;
|
||||||
|
element.x += deltaX;
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
element.y += element.height - element.width;
|
element.y += element.height - element.width;
|
||||||
element.height = element.width;
|
element.height = element.width;
|
||||||
} else {
|
} else {
|
||||||
element.height -= deltaY;
|
element.height -= deltaY;
|
||||||
element.y += deltaY;
|
element.y += deltaY;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "ne":
|
case "ne":
|
||||||
@ -1484,14 +1516,16 @@ export class App extends React.Component<any, AppState> {
|
|||||||
event.shiftKey,
|
event.shiftKey,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
element.width += deltaX;
|
mutateElement(element, element => {
|
||||||
if (event.shiftKey) {
|
element.width += deltaX;
|
||||||
element.y += element.height - element.width;
|
if (event.shiftKey) {
|
||||||
element.height = element.width;
|
element.y += element.height - element.width;
|
||||||
} else {
|
element.height = element.width;
|
||||||
element.height -= deltaY;
|
} else {
|
||||||
element.y += deltaY;
|
element.height -= deltaY;
|
||||||
}
|
element.y += deltaY;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "sw":
|
case "sw":
|
||||||
@ -1514,13 +1548,15 @@ export class App extends React.Component<any, AppState> {
|
|||||||
event.shiftKey,
|
event.shiftKey,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
element.width -= deltaX;
|
mutateElement(element, element => {
|
||||||
element.x += deltaX;
|
element.width -= deltaX;
|
||||||
if (event.shiftKey) {
|
element.x += deltaX;
|
||||||
element.height = element.width;
|
if (event.shiftKey) {
|
||||||
} else {
|
element.height = element.width;
|
||||||
element.height += deltaY;
|
} else {
|
||||||
}
|
element.height += deltaY;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "se":
|
case "se":
|
||||||
@ -1543,18 +1579,22 @@ export class App extends React.Component<any, AppState> {
|
|||||||
event.shiftKey,
|
event.shiftKey,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (event.shiftKey) {
|
mutateElement(element, element => {
|
||||||
element.width += deltaX;
|
if (event.shiftKey) {
|
||||||
element.height = element.width;
|
element.width += deltaX;
|
||||||
} else {
|
element.height = element.width;
|
||||||
element.width += deltaX;
|
} else {
|
||||||
element.height += deltaY;
|
element.width += deltaX;
|
||||||
}
|
element.height += deltaY;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "n": {
|
case "n": {
|
||||||
element.height -= deltaY;
|
mutateElement(element, element => {
|
||||||
element.y += deltaY;
|
element.height -= deltaY;
|
||||||
|
element.y += deltaY;
|
||||||
|
});
|
||||||
|
|
||||||
if (element.points.length > 0) {
|
if (element.points.length > 0) {
|
||||||
const len = element.points.length;
|
const len = element.points.length;
|
||||||
@ -1569,8 +1609,10 @@ export class App extends React.Component<any, AppState> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "w": {
|
case "w": {
|
||||||
element.width -= deltaX;
|
mutateElement(element, element => {
|
||||||
element.x += deltaX;
|
element.width -= deltaX;
|
||||||
|
element.x += deltaX;
|
||||||
|
});
|
||||||
|
|
||||||
if (element.points.length > 0) {
|
if (element.points.length > 0) {
|
||||||
const len = element.points.length;
|
const len = element.points.length;
|
||||||
@ -1584,29 +1626,37 @@ export class App extends React.Component<any, AppState> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "s": {
|
case "s": {
|
||||||
element.height += deltaY;
|
mutateElement(element, element => {
|
||||||
if (element.points.length > 0) {
|
element.height += deltaY;
|
||||||
const len = element.points.length;
|
if (element.points.length > 0) {
|
||||||
const points = [...element.points].sort((a, b) => a[1] - b[1]);
|
const len = element.points.length;
|
||||||
|
const points = [...element.points].sort(
|
||||||
|
(a, b) => a[1] - b[1],
|
||||||
|
);
|
||||||
|
|
||||||
for (let i = 1; i < points.length; ++i) {
|
for (let i = 1; i < points.length; ++i) {
|
||||||
const pnt = points[i];
|
const pnt = points[i];
|
||||||
pnt[1] += deltaY / (len - i);
|
pnt[1] += deltaY / (len - i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "e": {
|
case "e": {
|
||||||
element.width += deltaX;
|
mutateElement(element, element => {
|
||||||
if (element.points.length > 0) {
|
element.width += deltaX;
|
||||||
const len = element.points.length;
|
if (element.points.length > 0) {
|
||||||
const points = [...element.points].sort((a, b) => a[0] - b[0]);
|
const len = element.points.length;
|
||||||
|
const points = [...element.points].sort(
|
||||||
|
(a, b) => a[0] - b[0],
|
||||||
|
);
|
||||||
|
|
||||||
for (let i = 1; i < points.length; ++i) {
|
for (let i = 1; i < points.length; ++i) {
|
||||||
const pnt = points[i];
|
const pnt = points[i];
|
||||||
pnt[0] += deltaX / (len - i);
|
pnt[0] += deltaX / (len - i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1620,8 +1670,10 @@ export class App extends React.Component<any, AppState> {
|
|||||||
element,
|
element,
|
||||||
resizeHandle,
|
resizeHandle,
|
||||||
});
|
});
|
||||||
el.x = element.x;
|
mutateElement(el, el => {
|
||||||
el.y = element.y;
|
el.x = element.x;
|
||||||
|
el.y = element.y;
|
||||||
|
});
|
||||||
invalidateShapeForElement(el);
|
invalidateShapeForElement(el);
|
||||||
|
|
||||||
lastX = x;
|
lastX = x;
|
||||||
@ -1644,8 +1696,10 @@ export class App extends React.Component<any, AppState> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
selectedElements.forEach(element => {
|
selectedElements.forEach(element => {
|
||||||
element.x += x - lastX;
|
mutateElement(element, element => {
|
||||||
element.y += y - lastY;
|
element.x += x - lastX;
|
||||||
|
element.y += y - lastY;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
lastX = x;
|
lastX = x;
|
||||||
lastY = y;
|
lastY = y;
|
||||||
@ -1707,11 +1761,13 @@ export class App extends React.Component<any, AppState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draggingElement.x = x < originX ? originX - width : originX;
|
mutateElement(draggingElement, draggingElement => {
|
||||||
draggingElement.y = y < originY ? originY - height : originY;
|
draggingElement.x = x < originX ? originX - width : originX;
|
||||||
|
draggingElement.y = y < originY ? originY - height : originY;
|
||||||
|
|
||||||
draggingElement.width = width;
|
draggingElement.width = width;
|
||||||
draggingElement.height = height;
|
draggingElement.height = height;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateShapeForElement(draggingElement);
|
invalidateShapeForElement(draggingElement);
|
||||||
|
20
src/element/mutateElement.ts
Normal file
20
src/element/mutateElement.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import {
|
||||||
|
MutableExcalidrawElement,
|
||||||
|
MutableExcalidrawTextElement,
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
|
export function mutateElement(
|
||||||
|
element: MutableExcalidrawElement,
|
||||||
|
callback: (mutatableElement: MutableExcalidrawElement) => void,
|
||||||
|
): void {
|
||||||
|
element.version++;
|
||||||
|
callback(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mutateTextElement(
|
||||||
|
element: MutableExcalidrawTextElement,
|
||||||
|
callback: (mutatableElement: MutableExcalidrawTextElement) => void,
|
||||||
|
): void {
|
||||||
|
element.version++;
|
||||||
|
callback(element);
|
||||||
|
}
|
@ -33,6 +33,7 @@ export function newElement(
|
|||||||
opacity,
|
opacity,
|
||||||
seed: randomSeed(),
|
seed: randomSeed(),
|
||||||
points: [] as Point[],
|
points: [] as Point[],
|
||||||
|
version: 1,
|
||||||
};
|
};
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ExcalidrawElement } from "./types";
|
import { ExcalidrawElement, MutableExcalidrawElement } from "./types";
|
||||||
import { invalidateShapeForElement } from "../renderer/renderElement";
|
import { invalidateShapeForElement } from "../renderer/renderElement";
|
||||||
|
import { mutateElement } from "./mutateElement";
|
||||||
|
|
||||||
export function isInvisiblySmallElement(element: ExcalidrawElement): boolean {
|
export function isInvisiblySmallElement(element: ExcalidrawElement): boolean {
|
||||||
if (element.type === "arrow" || element.type === "line") {
|
if (element.type === "arrow" || element.type === "line") {
|
||||||
@ -35,7 +36,7 @@ export function getPerfectElementSize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resizePerfectLineForNWHandler(
|
export function resizePerfectLineForNWHandler(
|
||||||
element: ExcalidrawElement,
|
element: MutableExcalidrawElement,
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
) {
|
) {
|
||||||
@ -78,13 +79,17 @@ export function normalizeDimensions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (element.width < 0) {
|
if (element.width < 0) {
|
||||||
element.width = Math.abs(element.width);
|
mutateElement(element, element => {
|
||||||
element.x -= element.width;
|
element.width = Math.abs(element.width);
|
||||||
|
element.x -= element.width;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.height < 0) {
|
if (element.height < 0) {
|
||||||
element.height = Math.abs(element.height);
|
mutateElement(element, element => {
|
||||||
element.y -= element.height;
|
element.height = Math.abs(element.height);
|
||||||
|
element.y -= element.height;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateShapeForElement(element);
|
invalidateShapeForElement(element);
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { measureText } from "../utils";
|
import { measureText } from "../utils";
|
||||||
import { ExcalidrawTextElement } from "./types";
|
import { MutableExcalidrawTextElement } from "./types";
|
||||||
|
|
||||||
export const redrawTextBoundingBox = (element: ExcalidrawTextElement) => {
|
export const redrawTextBoundingBox = (
|
||||||
|
element: MutableExcalidrawTextElement,
|
||||||
|
) => {
|
||||||
const metrics = measureText(element.text, element.font);
|
const metrics = measureText(element.text, element.font);
|
||||||
element.width = metrics.width;
|
element.width = metrics.width;
|
||||||
element.height = metrics.height;
|
element.height = metrics.height;
|
||||||
|
@ -5,8 +5,10 @@ import { newElement } from "./newElement";
|
|||||||
* no computed data. The list of all ExcalidrawElements should be shareable
|
* no computed data. The list of all ExcalidrawElements should be shareable
|
||||||
* between peers and contain no state local to the peer.
|
* between peers and contain no state local to the peer.
|
||||||
*/
|
*/
|
||||||
export type ExcalidrawElement = ReturnType<typeof newElement>;
|
export type ExcalidrawElement = Readonly<ReturnType<typeof newElement>>;
|
||||||
export type ExcalidrawTextElement = ExcalidrawElement & {
|
export type MutableExcalidrawElement = ReturnType<typeof newElement>;
|
||||||
|
|
||||||
|
export type MutableExcalidrawTextElement = MutableExcalidrawElement & {
|
||||||
type: "text";
|
type: "text";
|
||||||
font: string;
|
font: string;
|
||||||
text: string;
|
text: string;
|
||||||
@ -15,4 +17,6 @@ export type ExcalidrawTextElement = ExcalidrawElement & {
|
|||||||
baseline: number;
|
baseline: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ExcalidrawTextElement = Readonly<MutableExcalidrawTextElement>;
|
||||||
|
|
||||||
export type PointerType = "mouse" | "pen" | "touch";
|
export type PointerType = "mouse" | "pen" | "touch";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user