Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c552ff4554 | |||
| 26f9b54199 | |||
| 7f5b7bab69 | |||
| bf7c91536f | |||
| 4372e992e0 | |||
| 1e4bfceb13 | |||
| 539071fcfe | |||
| 3700cf2d10 | |||
| 89218ba596 | |||
| bc5436592e | |||
| 750055ddfa | |||
| 93e4cb8d25 | |||
| a2dd3c6ea2 | |||
| 0360e64219 | |||
| c2867c9a93 | |||
| 14bca119f7 | |||
| afea0df141 | |||
| d2a508104e | |||
| 3697618266 | |||
| e7cc2337ea |
+2
-2
@@ -49,11 +49,11 @@
|
||||
"png-chunk-text": "1.0.0",
|
||||
"png-chunks-encode": "1.0.0",
|
||||
"png-chunks-extract": "1.0.0",
|
||||
"points-on-curve": "0.2.0",
|
||||
"points-on-curve": "1.0.1",
|
||||
"pwacompat": "2.0.17",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"roughjs": "4.5.2",
|
||||
"roughjs": "4.6.5",
|
||||
"sass": "1.51.0",
|
||||
"socket.io-client": "2.3.1",
|
||||
"tunnel-rat": "0.1.2"
|
||||
|
||||
+72
-8
@@ -374,6 +374,7 @@ import {
|
||||
resetCursor,
|
||||
setCursorForShape,
|
||||
} from "../cursor";
|
||||
import { Emitter } from "../emitter";
|
||||
|
||||
const AppContext = React.createContext<AppClassProperties>(null!);
|
||||
const AppPropsContext = React.createContext<AppProps>(null!);
|
||||
@@ -505,6 +506,30 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
laserPathManager: LaserPathManager = new LaserPathManager(this);
|
||||
|
||||
onChangeEmitter = new Emitter<
|
||||
[
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
files: BinaryFiles,
|
||||
]
|
||||
>();
|
||||
|
||||
onPointerDownEmitter = new Emitter<
|
||||
[
|
||||
activeTool: AppState["activeTool"],
|
||||
pointerDownState: PointerDownState,
|
||||
event: React.PointerEvent<HTMLElement>,
|
||||
]
|
||||
>();
|
||||
|
||||
onPointerUpEmitter = new Emitter<
|
||||
[
|
||||
activeTool: AppState["activeTool"],
|
||||
pointerDownState: PointerDownState,
|
||||
event: PointerEvent,
|
||||
]
|
||||
>();
|
||||
|
||||
constructor(props: AppProps) {
|
||||
super(props);
|
||||
const defaultAppState = getDefaultAppState();
|
||||
@@ -568,6 +593,9 @@ class App extends React.Component<AppProps, AppState> {
|
||||
resetCursor: this.resetCursor,
|
||||
updateFrameRendering: this.updateFrameRendering,
|
||||
toggleSidebar: this.toggleSidebar,
|
||||
onChange: (cb) => this.onChangeEmitter.on(cb),
|
||||
onPointerDown: (cb) => this.onPointerDownEmitter.on(cb),
|
||||
onPointerUp: (cb) => this.onPointerUpEmitter.on(cb),
|
||||
} as const;
|
||||
if (typeof excalidrawRef === "function") {
|
||||
excalidrawRef(api);
|
||||
@@ -1750,6 +1778,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.scene.destroy();
|
||||
this.library.destroy();
|
||||
this.laserPathManager.destroy();
|
||||
this.onChangeEmitter.destroy();
|
||||
ShapeCache.destroy();
|
||||
SnapCache.destroy();
|
||||
clearTimeout(touchTimeout);
|
||||
@@ -2034,6 +2063,11 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.state,
|
||||
this.files,
|
||||
);
|
||||
this.onChangeEmitter.trigger(
|
||||
this.scene.getElementsIncludingDeleted(),
|
||||
this.state,
|
||||
this.files,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3215,7 +3249,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
});
|
||||
|
||||
setActiveTool = (
|
||||
tool:
|
||||
tool: (
|
||||
| (
|
||||
| { type: Exclude<ToolType, "image"> }
|
||||
| {
|
||||
@@ -3223,7 +3257,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
insertOnCanvasDirectly?: boolean;
|
||||
}
|
||||
)
|
||||
| { type: "custom"; customType: string },
|
||||
| { type: "custom"; customType: string }
|
||||
) & { locked?: boolean },
|
||||
) => {
|
||||
const nextActiveTool = updateActiveTool(this.state, tool);
|
||||
if (nextActiveTool.type === "hand") {
|
||||
@@ -3715,9 +3750,32 @@ class App extends React.Component<AppProps, AppState> {
|
||||
return;
|
||||
}
|
||||
|
||||
let { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords(
|
||||
event,
|
||||
this.state,
|
||||
);
|
||||
|
||||
const selectedElements = this.scene.getSelectedElements(this.state);
|
||||
|
||||
if (selectedElements.length === 1 && isLinearElement(selectedElements[0])) {
|
||||
if (!event[KEYS.CTRL_OR_CMD]) {
|
||||
// If double clicked without any ctrl/cmd modifier on top of a point,
|
||||
// toggle split mode for that point. Else, treat as regular double click.
|
||||
const pointUnderCursorIndex =
|
||||
LinearElementEditor.getPointIndexUnderCursor(
|
||||
selectedElements[0],
|
||||
this.state.zoom,
|
||||
sceneX,
|
||||
sceneY,
|
||||
);
|
||||
if (pointUnderCursorIndex >= 0) {
|
||||
LinearElementEditor.toggleSegmentSplitAtIndex(
|
||||
selectedElements[0],
|
||||
pointUnderCursorIndex,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (
|
||||
event[KEYS.CTRL_OR_CMD] &&
|
||||
(!this.state.editingLinearElement ||
|
||||
@@ -3741,11 +3799,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
resetCursor(this.interactiveCanvas);
|
||||
|
||||
let { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords(
|
||||
event,
|
||||
this.state,
|
||||
);
|
||||
|
||||
const selectedGroupIds = getSelectedGroupIds(this.state);
|
||||
|
||||
if (selectedGroupIds.length > 0) {
|
||||
@@ -4680,7 +4733,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
pointerDownState,
|
||||
);
|
||||
} else if (this.state.activeTool.type === "custom") {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.AUTO);
|
||||
setCursorForShape(this.interactiveCanvas, this.state);
|
||||
} else if (this.state.activeTool.type === "frame") {
|
||||
this.createFrameElementOnPointerDown(pointerDownState);
|
||||
} else if (this.state.activeTool.type === "laser") {
|
||||
@@ -4699,6 +4752,11 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
|
||||
this.props?.onPointerDown?.(this.state.activeTool, pointerDownState);
|
||||
this.onPointerDownEmitter.trigger(
|
||||
this.state.activeTool,
|
||||
pointerDownState,
|
||||
event,
|
||||
);
|
||||
|
||||
const onPointerMove =
|
||||
this.onPointerMoveFromPointerDownHandler(pointerDownState);
|
||||
@@ -6551,6 +6609,12 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.setState({ pendingImageElementId: null });
|
||||
}
|
||||
|
||||
this.onPointerUpEmitter.trigger(
|
||||
this.state.activeTool,
|
||||
pointerDownState,
|
||||
childEvent,
|
||||
);
|
||||
|
||||
if (draggingElement?.type === "freedraw") {
|
||||
const pointerCoords = viewportCoordsToSceneCoords(
|
||||
childEvent,
|
||||
|
||||
+9
-3
@@ -296,6 +296,12 @@ export const ROUNDNESS = {
|
||||
* collaboration */
|
||||
export const PRECEDING_ELEMENT_KEY = "__precedingElement__";
|
||||
|
||||
export const ROUGHNESS = {
|
||||
architect: 0,
|
||||
artist: 1,
|
||||
cartoonist: 2,
|
||||
} as const;
|
||||
|
||||
export const DEFAULT_ELEMENT_PROPS: {
|
||||
strokeColor: ExcalidrawElement["strokeColor"];
|
||||
backgroundColor: ExcalidrawElement["backgroundColor"];
|
||||
@@ -308,10 +314,10 @@ export const DEFAULT_ELEMENT_PROPS: {
|
||||
} = {
|
||||
strokeColor: COLOR_PALETTE.black,
|
||||
backgroundColor: COLOR_PALETTE.transparent,
|
||||
fillStyle: "hachure",
|
||||
strokeWidth: 1,
|
||||
fillStyle: "solid",
|
||||
strokeWidth: 2,
|
||||
strokeStyle: "solid",
|
||||
roughness: 1,
|
||||
roughness: ROUGHNESS.artist,
|
||||
opacity: 100,
|
||||
locked: false,
|
||||
};
|
||||
|
||||
@@ -99,5 +99,7 @@ export const setCursorForShape = (
|
||||
interactiveCanvas.style.cursor = `url(${url}), auto`;
|
||||
} else if (!["image", "custom"].includes(appState.activeTool.type)) {
|
||||
interactiveCanvas.style.cursor = CURSOR_TYPE.CROSSHAIR;
|
||||
} else {
|
||||
interactiveCanvas.style.cursor = CURSOR_TYPE.AUTO;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
|
||||
"type": "arrow",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 300,
|
||||
@@ -28,7 +28,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#66a80f",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "ellipse",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -49,7 +49,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
|
||||
"type": "arrow",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
@@ -63,7 +63,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#9c36b5",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "diamond",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -85,7 +85,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
|
||||
"focus": -0.008153707962747813,
|
||||
"gap": 1,
|
||||
},
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 35,
|
||||
@@ -116,7 +116,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
|
||||
},
|
||||
"strokeColor": "#1864ab",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -138,7 +138,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
|
||||
"focus": 0.10666666666666667,
|
||||
"gap": 3.834326468444573,
|
||||
},
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
@@ -169,7 +169,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
|
||||
},
|
||||
"strokeColor": "#e67700",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -190,7 +190,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
|
||||
"type": "arrow",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 300,
|
||||
@@ -204,7 +204,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -227,7 +227,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
|
||||
},
|
||||
],
|
||||
"containerId": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -245,7 +245,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#c2255c",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "HEYYYYY",
|
||||
"textAlign": "left",
|
||||
"type": "text",
|
||||
@@ -271,7 +271,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
|
||||
},
|
||||
],
|
||||
"containerId": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -289,7 +289,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "Whats up ?",
|
||||
"textAlign": "left",
|
||||
"type": "text",
|
||||
@@ -319,7 +319,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
|
||||
"focus": 0,
|
||||
"gap": 5,
|
||||
},
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
@@ -350,7 +350,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
|
||||
},
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -368,7 +368,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id43",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -386,7 +386,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "HELLO WORLD!!",
|
||||
"textAlign": "center",
|
||||
"type": "text",
|
||||
@@ -416,7 +416,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
|
||||
"focus": 0,
|
||||
"gap": 1,
|
||||
},
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
@@ -447,7 +447,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
|
||||
},
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -465,7 +465,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id32",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -483,7 +483,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "HELLO WORLD!!",
|
||||
"textAlign": "center",
|
||||
"type": "text",
|
||||
@@ -507,7 +507,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
|
||||
"type": "arrow",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
@@ -521,7 +521,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -542,7 +542,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
|
||||
"type": "arrow",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
@@ -556,7 +556,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "ellipse",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -583,7 +583,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
|
||||
"focus": 0,
|
||||
"gap": 1,
|
||||
},
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
@@ -614,7 +614,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
|
||||
},
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -632,7 +632,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id36",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -650,7 +650,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "HELLO WORLD!!",
|
||||
"textAlign": "center",
|
||||
"type": "text",
|
||||
@@ -676,7 +676,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
|
||||
},
|
||||
],
|
||||
"containerId": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -694,7 +694,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "HEYYYYY",
|
||||
"textAlign": "left",
|
||||
"type": "text",
|
||||
@@ -720,7 +720,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
|
||||
},
|
||||
],
|
||||
"containerId": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -738,7 +738,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "WHATS UP ?",
|
||||
"textAlign": "left",
|
||||
"type": "text",
|
||||
@@ -757,7 +757,7 @@ exports[`Test Transform > should not allow duplicate ids 1`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 200,
|
||||
@@ -771,7 +771,7 @@ exports[`Test Transform > should not allow duplicate ids 1`] = `
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -789,7 +789,7 @@ exports[`Test Transform > should transform linear elements 1`] = `
|
||||
"boundElements": null,
|
||||
"endArrowhead": "arrow",
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
@@ -816,7 +816,7 @@ exports[`Test Transform > should transform linear elements 1`] = `
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -834,7 +834,7 @@ exports[`Test Transform > should transform linear elements 2`] = `
|
||||
"boundElements": null,
|
||||
"endArrowhead": "triangle",
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
@@ -879,7 +879,7 @@ exports[`Test Transform > should transform linear elements 3`] = `
|
||||
"boundElements": null,
|
||||
"endArrowhead": null,
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
@@ -906,7 +906,7 @@ exports[`Test Transform > should transform linear elements 3`] = `
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "line",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -924,7 +924,7 @@ exports[`Test Transform > should transform linear elements 4`] = `
|
||||
"boundElements": null,
|
||||
"endArrowhead": null,
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
@@ -967,7 +967,7 @@ exports[`Test Transform > should transform regular shapes 1`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
@@ -981,7 +981,7 @@ exports[`Test Transform > should transform regular shapes 1`] = `
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -997,7 +997,7 @@ exports[`Test Transform > should transform regular shapes 2`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
@@ -1011,7 +1011,7 @@ exports[`Test Transform > should transform regular shapes 2`] = `
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "ellipse",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -1027,7 +1027,7 @@ exports[`Test Transform > should transform regular shapes 3`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
@@ -1041,7 +1041,7 @@ exports[`Test Transform > should transform regular shapes 3`] = `
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "diamond",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -1057,7 +1057,7 @@ exports[`Test Transform > should transform regular shapes 4`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "#c0eb75",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
@@ -1149,7 +1149,7 @@ exports[`Test Transform > should transform text element 1`] = `
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -1167,7 +1167,7 @@ exports[`Test Transform > should transform text element 1`] = `
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "HELLO WORLD!",
|
||||
"textAlign": "left",
|
||||
"type": "text",
|
||||
@@ -1188,7 +1188,7 @@ exports[`Test Transform > should transform text element 2`] = `
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -1206,7 +1206,7 @@ exports[`Test Transform > should transform text element 2`] = `
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#5f3dc4",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "STYLED HELLO WORLD!",
|
||||
"textAlign": "left",
|
||||
"type": "text",
|
||||
@@ -1232,7 +1232,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
],
|
||||
"endArrowhead": "arrow",
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
@@ -1259,7 +1259,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -1282,7 +1282,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
],
|
||||
"endArrowhead": "arrow",
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
@@ -1309,7 +1309,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -1332,7 +1332,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
],
|
||||
"endArrowhead": "arrow",
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 130,
|
||||
@@ -1382,7 +1382,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
],
|
||||
"endArrowhead": "arrow",
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 130,
|
||||
@@ -1427,7 +1427,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id24",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -1445,7 +1445,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "LABELED ARROW",
|
||||
"textAlign": "center",
|
||||
"type": "text",
|
||||
@@ -1466,7 +1466,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id25",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -1484,7 +1484,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#099268",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "STYLED LABELED ARROW",
|
||||
"textAlign": "center",
|
||||
"type": "text",
|
||||
@@ -1505,7 +1505,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id26",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -1523,7 +1523,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1098ad",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "ANOTHER STYLED
|
||||
LABELLED ARROW",
|
||||
"textAlign": "center",
|
||||
@@ -1545,7 +1545,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id27",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -1563,7 +1563,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#099268",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "ANOTHER STYLED
|
||||
LABELLED ARROW",
|
||||
"textAlign": "center",
|
||||
@@ -1588,7 +1588,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"type": "text",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 35,
|
||||
@@ -1602,7 +1602,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -1623,7 +1623,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"type": "text",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 85,
|
||||
@@ -1637,7 +1637,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "ellipse",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -1658,7 +1658,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"type": "text",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 170,
|
||||
@@ -1672,7 +1672,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "diamond",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -1693,7 +1693,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"type": "text",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 120,
|
||||
@@ -1728,7 +1728,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"type": "text",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 85,
|
||||
@@ -1742,7 +1742,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#c2255c",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -1763,7 +1763,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"type": "text",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 120,
|
||||
@@ -1777,7 +1777,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#f08c00",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "ellipse",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -1795,7 +1795,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id12",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -1813,7 +1813,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "RECTANGLE TEXT CONTAINER",
|
||||
"textAlign": "center",
|
||||
"type": "text",
|
||||
@@ -1834,7 +1834,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id13",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -1852,7 +1852,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "ELLIPSE TEXT
|
||||
CONTAINER",
|
||||
"textAlign": "center",
|
||||
@@ -1874,7 +1874,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id14",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -1893,7 +1893,7 @@ TEXT CONTAINER",
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "DIAMOND
|
||||
TEXT
|
||||
CONTAINER",
|
||||
@@ -1916,7 +1916,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id15",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -1934,7 +1934,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#099268",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "STYLED DIAMOND
|
||||
TEXT CONTAINER",
|
||||
"textAlign": "center",
|
||||
@@ -1956,7 +1956,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id16",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -1974,7 +1974,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#c2255c",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "TOP LEFT ALIGNED
|
||||
RECTANGLE TEXT
|
||||
CONTAINER",
|
||||
@@ -1997,7 +1997,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"baseline": 0,
|
||||
"boundElements": null,
|
||||
"containerId": "id17",
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 20,
|
||||
"frameId": null,
|
||||
@@ -2015,7 +2015,7 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#c2255c",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "STYLED
|
||||
ELLIPSE TEXT
|
||||
CONTAINER",
|
||||
|
||||
@@ -285,6 +285,9 @@ const restoreElement = (
|
||||
points,
|
||||
x,
|
||||
y,
|
||||
segmentSplitIndices: element.segmentSplitIndices
|
||||
? [...element.segmentSplitIndices]
|
||||
: [],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -741,7 +741,7 @@ export const getElementPointsCoords = (
|
||||
element: ExcalidrawLinearElement,
|
||||
points: readonly (readonly [number, number])[],
|
||||
): [number, number, number, number] => {
|
||||
// This might be computationally heavey
|
||||
// This might be computationally heavy
|
||||
const gen = rough.generator();
|
||||
const curve =
|
||||
element.roundness == null
|
||||
|
||||
@@ -494,7 +494,7 @@ const hitTestFreeDrawElement = (
|
||||
// for filled freedraw shapes, support
|
||||
// selecting from inside
|
||||
if (shape && shape.sets.length) {
|
||||
return hitTestRoughShape(shape, x, y, threshold);
|
||||
return hitTestCurveInside(shape, x, y, "round");
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -547,7 +547,10 @@ export class LinearElementEditor {
|
||||
endPointIndex: number,
|
||||
) {
|
||||
let segmentMidPoint = centerPoint(startPoint, endPoint);
|
||||
if (element.points.length > 2 && element.roundness) {
|
||||
const splits = element.segmentSplitIndices || [];
|
||||
const treatAsCurve =
|
||||
splits.includes(endPointIndex) || splits.includes(endPointIndex - 1);
|
||||
if (element.points.length > 2 && (element.roundness || treatAsCurve)) {
|
||||
const controlPoints = getControlPointsForBezierCurve(
|
||||
element,
|
||||
element.points[endPointIndex],
|
||||
@@ -1042,13 +1045,15 @@ export class LinearElementEditor {
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
|
||||
const isDeletingOriginPoint = pointIndices.includes(0);
|
||||
const indexSet = new Set(pointIndices);
|
||||
|
||||
const isDeletingOriginPoint = indexSet.has(0);
|
||||
|
||||
// if deleting first point, make the next to be [0,0] and recalculate
|
||||
// positions of the rest with respect to it
|
||||
if (isDeletingOriginPoint) {
|
||||
const firstNonDeletedPoint = element.points.find((point, idx) => {
|
||||
return !pointIndices.includes(idx);
|
||||
return !indexSet.has(idx);
|
||||
});
|
||||
if (firstNonDeletedPoint) {
|
||||
offsetX = firstNonDeletedPoint[0];
|
||||
@@ -1057,7 +1062,7 @@ export class LinearElementEditor {
|
||||
}
|
||||
|
||||
const nextPoints = element.points.reduce((acc: Point[], point, idx) => {
|
||||
if (!pointIndices.includes(idx)) {
|
||||
if (!indexSet.has(idx)) {
|
||||
acc.push(
|
||||
!acc.length ? [0, 0] : [point[0] - offsetX, point[1] - offsetY],
|
||||
);
|
||||
@@ -1065,7 +1070,22 @@ export class LinearElementEditor {
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
LinearElementEditor._updatePoints(element, nextPoints, offsetX, offsetY);
|
||||
const splits: number[] = [];
|
||||
(element.segmentSplitIndices || []).forEach((index) => {
|
||||
if (!indexSet.has(index)) {
|
||||
let shift = 0;
|
||||
for (const pointIndex of pointIndices) {
|
||||
if (index > pointIndex) {
|
||||
shift++;
|
||||
}
|
||||
}
|
||||
splits.push(index - shift);
|
||||
}
|
||||
});
|
||||
|
||||
LinearElementEditor._updatePoints(element, nextPoints, offsetX, offsetY, {
|
||||
segmentSplitIndices: splits.sort((a, b) => a - b),
|
||||
});
|
||||
}
|
||||
|
||||
static addPoints(
|
||||
@@ -1204,9 +1224,13 @@ export class LinearElementEditor {
|
||||
midpoint,
|
||||
...element.points.slice(segmentMidpoint.index!),
|
||||
];
|
||||
const splits = (element.segmentSplitIndices || []).map((index) =>
|
||||
index >= segmentMidpoint.index! ? index + 1 : index,
|
||||
);
|
||||
|
||||
mutateElement(element, {
|
||||
points,
|
||||
segmentSplitIndices: splits.sort((a, b) => a - b),
|
||||
});
|
||||
|
||||
ret.pointerDownState = {
|
||||
@@ -1226,7 +1250,11 @@ export class LinearElementEditor {
|
||||
nextPoints: readonly Point[],
|
||||
offsetX: number,
|
||||
offsetY: number,
|
||||
otherUpdates?: { startBinding?: PointBinding; endBinding?: PointBinding },
|
||||
otherUpdates?: {
|
||||
startBinding?: PointBinding;
|
||||
endBinding?: PointBinding;
|
||||
segmentSplitIndices?: number[];
|
||||
},
|
||||
) {
|
||||
const nextCoords = getElementPointsCoords(element, nextPoints);
|
||||
const prevCoords = getElementPointsCoords(element, element.points);
|
||||
@@ -1472,6 +1500,27 @@ export class LinearElementEditor {
|
||||
|
||||
return coords;
|
||||
};
|
||||
|
||||
static toggleSegmentSplitAtIndex(
|
||||
element: NonDeleted<ExcalidrawLinearElement>,
|
||||
index: number,
|
||||
) {
|
||||
let found = false;
|
||||
const splitIndices = (element.segmentSplitIndices || []).filter((idx) => {
|
||||
if (idx === index) {
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!found) {
|
||||
splitIndices.push(index);
|
||||
}
|
||||
|
||||
mutateElement(element, {
|
||||
segmentSplitIndices: splitIndices.sort((a, b) => a - b),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeSelectedPoints = (
|
||||
|
||||
@@ -25,7 +25,7 @@ export const mutateElement = <TElement extends Mutable<ExcalidrawElement>>(
|
||||
|
||||
// casting to any because can't use `in` operator
|
||||
// (see https://github.com/microsoft/TypeScript/issues/21732)
|
||||
const { points, fileId } = updates as any;
|
||||
const { points, fileId, segmentSplitIndices } = updates as any;
|
||||
|
||||
if (typeof points !== "undefined") {
|
||||
updates = { ...getSizeFromPoints(points), ...updates };
|
||||
@@ -86,6 +86,7 @@ export const mutateElement = <TElement extends Mutable<ExcalidrawElement>>(
|
||||
if (
|
||||
typeof updates.height !== "undefined" ||
|
||||
typeof updates.width !== "undefined" ||
|
||||
typeof segmentSplitIndices !== "undefined" ||
|
||||
typeof fileId != "undefined" ||
|
||||
typeof points !== "undefined"
|
||||
) {
|
||||
|
||||
@@ -374,6 +374,7 @@ export const newLinearElement = (
|
||||
endBinding: null,
|
||||
startArrowhead: opts.startArrowhead || null,
|
||||
endArrowhead: opts.endArrowhead || null,
|
||||
segmentSplitIndices: [],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
mockBoundingClientRect,
|
||||
restoreOriginalGetBoundingClientRect,
|
||||
} from "../tests/test-utils";
|
||||
import { queryByText, waitFor } from "@testing-library/react";
|
||||
import { queryByText } from "@testing-library/react";
|
||||
|
||||
import { FONT_FAMILY, TEXT_ALIGN, VERTICAL_ALIGN } from "../constants";
|
||||
import {
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
import { API } from "../tests/helpers/api";
|
||||
import { mutateElement } from "./mutateElement";
|
||||
import { getOriginalContainerHeightFromCache } from "./textWysiwyg";
|
||||
import { getTextEditor } from "../tests/queries/dom";
|
||||
|
||||
// Unmount ReactDOM from root
|
||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||
@@ -25,18 +26,6 @@ ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||
const tab = " ";
|
||||
const mouse = new Pointer("mouse");
|
||||
|
||||
const getTextEditor = async (waitForEditor = false) => {
|
||||
const query = () =>
|
||||
document.querySelector(
|
||||
".excalidraw-textEditorContainer > textarea",
|
||||
) as HTMLTextAreaElement;
|
||||
if (waitForEditor) {
|
||||
waitFor(() => expect(query()).not.toBe(null));
|
||||
return query();
|
||||
}
|
||||
return query();
|
||||
};
|
||||
|
||||
const updateTextEditor = (editor: HTMLTextAreaElement, value: string) => {
|
||||
fireEvent.change(editor, { target: { value } });
|
||||
editor.dispatchEvent(new Event("input"));
|
||||
@@ -206,7 +195,7 @@ describe("textWysiwyg", () => {
|
||||
|
||||
mouse.clickAt(text.x + 50, text.y + 50);
|
||||
|
||||
const editor = await getTextEditor();
|
||||
const editor = await getTextEditor(false);
|
||||
|
||||
expect(editor).not.toBe(null);
|
||||
expect(h.state.editingElement?.id).toBe(text.id);
|
||||
@@ -228,7 +217,7 @@ describe("textWysiwyg", () => {
|
||||
|
||||
mouse.doubleClickAt(text.x + 50, text.y + 50);
|
||||
|
||||
const editor = await getTextEditor();
|
||||
const editor = await getTextEditor(false);
|
||||
|
||||
expect(editor).not.toBe(null);
|
||||
expect(h.state.editingElement?.id).toBe(text.id);
|
||||
@@ -1436,7 +1425,7 @@ describe("textWysiwyg", () => {
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
fillStyle: "hachure",
|
||||
fillStyle: "solid",
|
||||
groupIds: [],
|
||||
height: 35,
|
||||
isDeleted: false,
|
||||
@@ -1449,7 +1438,7 @@ describe("textWysiwyg", () => {
|
||||
},
|
||||
strokeColor: "#1e1e1e",
|
||||
strokeStyle: "solid",
|
||||
strokeWidth: 1,
|
||||
strokeWidth: 2,
|
||||
type: "rectangle",
|
||||
updated: 1,
|
||||
version: 1,
|
||||
|
||||
@@ -195,6 +195,7 @@ export type ExcalidrawLinearElement = _ExcalidrawElementBase &
|
||||
Readonly<{
|
||||
type: "line" | "arrow";
|
||||
points: readonly Point[];
|
||||
segmentSplitIndices: readonly number[] | null;
|
||||
lastCommittedPoint: Point | null;
|
||||
startBinding: PointBinding | null;
|
||||
endBinding: PointBinding | null;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
type Subscriber<T extends any[]> = (...payload: T) => void;
|
||||
|
||||
export class Emitter<T extends any[] = []> {
|
||||
public subscribers: Subscriber<T>[] = [];
|
||||
public value: T | undefined;
|
||||
private updateOnChangeOnly: boolean;
|
||||
|
||||
constructor(opts?: { initialState?: T; updateOnChangeOnly?: boolean }) {
|
||||
this.updateOnChangeOnly = opts?.updateOnChangeOnly ?? false;
|
||||
this.value = opts?.initialState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches subscriber
|
||||
*
|
||||
* @returns unsubscribe function
|
||||
*/
|
||||
on(...handlers: Subscriber<T>[] | Subscriber<T>[][]) {
|
||||
const _handlers = handlers
|
||||
.flat()
|
||||
.filter((item) => typeof item === "function");
|
||||
|
||||
this.subscribers.push(..._handlers);
|
||||
|
||||
return () => this.off(_handlers);
|
||||
}
|
||||
|
||||
off(...handlers: Subscriber<T>[] | Subscriber<T>[][]) {
|
||||
const _handlers = handlers.flat();
|
||||
this.subscribers = this.subscribers.filter(
|
||||
(handler) => !_handlers.includes(handler),
|
||||
);
|
||||
}
|
||||
|
||||
trigger(...payload: T): any[] {
|
||||
if (this.updateOnChangeOnly && this.value === payload) {
|
||||
return [];
|
||||
}
|
||||
this.value = payload;
|
||||
return this.subscribers.map((handler) => handler(...payload));
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.subscribers = [];
|
||||
this.value = undefined;
|
||||
}
|
||||
}
|
||||
+12
-117
@@ -17,14 +17,6 @@
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.18.6"
|
||||
|
||||
"@babel/code-frame@^7.22.13":
|
||||
version "7.22.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
|
||||
integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.22.13"
|
||||
chalk "^2.4.2"
|
||||
|
||||
"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.18.8":
|
||||
version "7.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d"
|
||||
@@ -60,16 +52,6 @@
|
||||
"@jridgewell/gen-mapping" "^0.3.2"
|
||||
jsesc "^2.5.1"
|
||||
|
||||
"@babel/generator@^7.23.0":
|
||||
version "7.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420"
|
||||
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
|
||||
dependencies:
|
||||
"@babel/types" "^7.23.0"
|
||||
"@jridgewell/gen-mapping" "^0.3.2"
|
||||
"@jridgewell/trace-mapping" "^0.3.17"
|
||||
jsesc "^2.5.1"
|
||||
|
||||
"@babel/helper-annotate-as-pure@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb"
|
||||
@@ -135,11 +117,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
|
||||
integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
|
||||
|
||||
"@babel/helper-environment-visitor@^7.22.20":
|
||||
version "7.22.20"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
|
||||
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
|
||||
|
||||
"@babel/helper-explode-assignable-expression@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096"
|
||||
@@ -155,14 +132,6 @@
|
||||
"@babel/template" "^7.18.6"
|
||||
"@babel/types" "^7.18.9"
|
||||
|
||||
"@babel/helper-function-name@^7.23.0":
|
||||
version "7.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
|
||||
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
|
||||
dependencies:
|
||||
"@babel/template" "^7.22.15"
|
||||
"@babel/types" "^7.23.0"
|
||||
|
||||
"@babel/helper-hoist-variables@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
|
||||
@@ -170,13 +139,6 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helper-hoist-variables@^7.22.5":
|
||||
version "7.22.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb"
|
||||
integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
|
||||
dependencies:
|
||||
"@babel/types" "^7.22.5"
|
||||
|
||||
"@babel/helper-member-expression-to-functions@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815"
|
||||
@@ -259,28 +221,11 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helper-split-export-declaration@^7.22.6":
|
||||
version "7.22.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
|
||||
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
|
||||
dependencies:
|
||||
"@babel/types" "^7.22.5"
|
||||
|
||||
"@babel/helper-string-parser@^7.22.5":
|
||||
version "7.22.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
|
||||
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
|
||||
integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.22.20":
|
||||
version "7.22.20"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
|
||||
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
|
||||
|
||||
"@babel/helper-validator-option@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
|
||||
@@ -314,25 +259,11 @@
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/highlight@^7.22.13":
|
||||
version "7.22.20"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54"
|
||||
integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.22.20"
|
||||
chalk "^2.4.2"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@^7.18.6", "@babel/parser@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539"
|
||||
integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==
|
||||
|
||||
"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
|
||||
version "7.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
|
||||
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
|
||||
|
||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2"
|
||||
@@ -980,28 +911,19 @@
|
||||
"@babel/parser" "^7.18.6"
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/template@^7.22.15":
|
||||
version "7.22.15"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
|
||||
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.22.13"
|
||||
"@babel/parser" "^7.22.15"
|
||||
"@babel/types" "^7.22.15"
|
||||
|
||||
"@babel/traverse@^7.13.0", "@babel/traverse@^7.18.9":
|
||||
version "7.23.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
|
||||
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98"
|
||||
integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.22.13"
|
||||
"@babel/generator" "^7.23.0"
|
||||
"@babel/helper-environment-visitor" "^7.22.20"
|
||||
"@babel/helper-function-name" "^7.23.0"
|
||||
"@babel/helper-hoist-variables" "^7.22.5"
|
||||
"@babel/helper-split-export-declaration" "^7.22.6"
|
||||
"@babel/parser" "^7.23.0"
|
||||
"@babel/types" "^7.23.0"
|
||||
"@babel/code-frame" "^7.18.6"
|
||||
"@babel/generator" "^7.18.9"
|
||||
"@babel/helper-environment-visitor" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.18.9"
|
||||
"@babel/helper-hoist-variables" "^7.18.6"
|
||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||
"@babel/parser" "^7.18.9"
|
||||
"@babel/types" "^7.18.9"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
@@ -1013,15 +935,6 @@
|
||||
"@babel/helper-validator-identifier" "^7.18.6"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0":
|
||||
version "7.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
|
||||
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.22.5"
|
||||
"@babel/helper-validator-identifier" "^7.22.20"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@discoveryjs/json-ext@^0.5.0":
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752"
|
||||
@@ -1049,11 +962,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
|
||||
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
|
||||
|
||||
"@jridgewell/resolve-uri@^3.1.0":
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
|
||||
integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
|
||||
|
||||
"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
|
||||
@@ -1072,19 +980,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
||||
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.14":
|
||||
version "1.4.15"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
|
||||
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.17":
|
||||
version "0.3.20"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f"
|
||||
integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.9":
|
||||
version "0.3.14"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed"
|
||||
@@ -1527,7 +1422,7 @@ chalk@^1.1.3:
|
||||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chalk@^2.0.0, chalk@^2.4.2:
|
||||
chalk@^2.0.0:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
|
||||
+64
-17
@@ -166,6 +166,21 @@ const fillCircle = (
|
||||
}
|
||||
};
|
||||
|
||||
const fillSquare = (
|
||||
context: CanvasRenderingContext2D,
|
||||
cx: number,
|
||||
cy: number,
|
||||
side: number,
|
||||
stroke = true,
|
||||
) => {
|
||||
context.beginPath();
|
||||
context.rect(cx - side / 2, cy - side / 2, side, side);
|
||||
context.fill();
|
||||
if (stroke) {
|
||||
context.stroke();
|
||||
}
|
||||
};
|
||||
|
||||
const strokeGrid = (
|
||||
context: CanvasRenderingContext2D,
|
||||
gridSize: number,
|
||||
@@ -224,6 +239,7 @@ const renderSingleLinearPoint = (
|
||||
point: Point,
|
||||
radius: number,
|
||||
isSelected: boolean,
|
||||
renderAsSquare: boolean,
|
||||
isPhantomPoint = false,
|
||||
) => {
|
||||
context.strokeStyle = "#5e5ad8";
|
||||
@@ -235,13 +251,29 @@ const renderSingleLinearPoint = (
|
||||
context.fillStyle = "rgba(177, 151, 252, 0.7)";
|
||||
}
|
||||
|
||||
fillCircle(
|
||||
context,
|
||||
point[0],
|
||||
point[1],
|
||||
radius / appState.zoom.value,
|
||||
!isPhantomPoint,
|
||||
);
|
||||
const effectiveRadius = radius / appState.zoom.value;
|
||||
|
||||
if (renderAsSquare) {
|
||||
fillSquare(
|
||||
context,
|
||||
point[0],
|
||||
point[1],
|
||||
effectiveRadius * 2,
|
||||
!isPhantomPoint,
|
||||
);
|
||||
} else {
|
||||
fillCircle(context, point[0], point[1], effectiveRadius, !isPhantomPoint);
|
||||
}
|
||||
};
|
||||
|
||||
const isLinearPointAtIndexSquared = (
|
||||
element: NonDeleted<ExcalidrawLinearElement>,
|
||||
index: number,
|
||||
) => {
|
||||
const splitting = element.segmentSplitIndices
|
||||
? element.segmentSplitIndices.includes(index)
|
||||
: false;
|
||||
return element.roundness ? splitting : !splitting;
|
||||
};
|
||||
|
||||
const renderLinearPointHandles = (
|
||||
@@ -265,7 +297,14 @@ const renderLinearPointHandles = (
|
||||
const isSelected =
|
||||
!!appState.editingLinearElement?.selectedPointsIndices?.includes(idx);
|
||||
|
||||
renderSingleLinearPoint(context, appState, point, radius, isSelected);
|
||||
renderSingleLinearPoint(
|
||||
context,
|
||||
appState,
|
||||
point,
|
||||
radius,
|
||||
isSelected,
|
||||
isLinearPointAtIndexSquared(element, idx),
|
||||
);
|
||||
});
|
||||
|
||||
//Rendering segment mid points
|
||||
@@ -293,6 +332,7 @@ const renderLinearPointHandles = (
|
||||
segmentMidPoint,
|
||||
radius,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
highlightPoint(segmentMidPoint, context, appState);
|
||||
} else {
|
||||
@@ -303,6 +343,7 @@ const renderLinearPointHandles = (
|
||||
segmentMidPoint,
|
||||
radius,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
}
|
||||
} else if (appState.editingLinearElement || points.length === 2) {
|
||||
@@ -312,6 +353,7 @@ const renderLinearPointHandles = (
|
||||
segmentMidPoint,
|
||||
POINT_HANDLE_SIZE / 2,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
}
|
||||
@@ -324,16 +366,16 @@ const highlightPoint = (
|
||||
point: Point,
|
||||
context: CanvasRenderingContext2D,
|
||||
appState: InteractiveCanvasAppState,
|
||||
renderAsSquare = false,
|
||||
) => {
|
||||
context.fillStyle = "rgba(105, 101, 219, 0.4)";
|
||||
const radius = LinearElementEditor.POINT_HANDLE_SIZE / appState.zoom.value;
|
||||
|
||||
fillCircle(
|
||||
context,
|
||||
point[0],
|
||||
point[1],
|
||||
LinearElementEditor.POINT_HANDLE_SIZE / appState.zoom.value,
|
||||
false,
|
||||
);
|
||||
if (renderAsSquare) {
|
||||
fillSquare(context, point[0], point[1], radius * 2, false);
|
||||
} else {
|
||||
fillCircle(context, point[0], point[1], radius, false);
|
||||
}
|
||||
};
|
||||
const renderLinearElementPointHighlight = (
|
||||
context: CanvasRenderingContext2D,
|
||||
@@ -355,10 +397,15 @@ const renderLinearElementPointHighlight = (
|
||||
element,
|
||||
hoverPointIndex,
|
||||
);
|
||||
|
||||
context.save();
|
||||
context.translate(appState.scrollX, appState.scrollY);
|
||||
|
||||
highlightPoint(point, context, appState);
|
||||
highlightPoint(
|
||||
point,
|
||||
context,
|
||||
appState,
|
||||
isLinearPointAtIndexSquared(element, hoverPointIndex),
|
||||
);
|
||||
context.restore();
|
||||
};
|
||||
|
||||
|
||||
+70
-8
@@ -12,11 +12,23 @@ import type {
|
||||
import { isPathALoop, getCornerRadius } from "../math";
|
||||
import { generateFreeDrawShape } from "../renderer/renderElement";
|
||||
import { isTransparent, assertNever } from "../utils";
|
||||
import { simplify } from "points-on-curve";
|
||||
import { ROUGHNESS } from "../constants";
|
||||
import { Point } from "../types";
|
||||
|
||||
const getDashArrayDashed = (strokeWidth: number) => [8, 8 + strokeWidth];
|
||||
|
||||
const getDashArrayDotted = (strokeWidth: number) => [1.5, 6 + strokeWidth];
|
||||
|
||||
function adjustRoughness(size: number, roughness: number): number {
|
||||
if (size >= 50) {
|
||||
return roughness;
|
||||
}
|
||||
const factor = 2 + (50 - size) / 10;
|
||||
|
||||
return roughness / factor;
|
||||
}
|
||||
|
||||
export const generateRoughOptions = (
|
||||
element: ExcalidrawElement,
|
||||
continuousPath = false,
|
||||
@@ -43,9 +55,13 @@ export const generateRoughOptions = (
|
||||
// calculate them (and we don't want the fills to be modified)
|
||||
fillWeight: element.strokeWidth / 2,
|
||||
hachureGap: element.strokeWidth * 4,
|
||||
roughness: element.roughness,
|
||||
roughness: adjustRoughness(
|
||||
Math.min(element.width, element.height),
|
||||
element.roughness,
|
||||
),
|
||||
stroke: element.strokeColor,
|
||||
preserveVertices: continuousPath,
|
||||
preserveVertices:
|
||||
continuousPath || element.roughness < ROUGHNESS.cartoonist,
|
||||
};
|
||||
|
||||
switch (element.type) {
|
||||
@@ -213,18 +229,44 @@ export const _generateElementShape = (
|
||||
|
||||
// points array can be empty in the beginning, so it is important to add
|
||||
// initial position to it
|
||||
const points = element.points.length ? element.points : [[0, 0]];
|
||||
const points = element.points.length
|
||||
? element.points
|
||||
: ([[0, 0]] as Point[]);
|
||||
|
||||
// curve is always the first element
|
||||
// this simplifies finding the curve for an element
|
||||
const splits = element.segmentSplitIndices || [];
|
||||
if (!element.roundness) {
|
||||
if (options.fill) {
|
||||
shape = [generator.polygon(points as [number, number][], options)];
|
||||
if (splits.length === 0) {
|
||||
if (options.fill) {
|
||||
shape = [generator.polygon(points as [number, number][], options)];
|
||||
} else {
|
||||
shape = [
|
||||
generator.linearPath(points as [number, number][], options),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
shape = [generator.linearPath(points as [number, number][], options)];
|
||||
const splitInverse: number[] = [];
|
||||
const splitSet = new Set(splits);
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
if (!splitSet.has(i)) {
|
||||
splitInverse.push(i);
|
||||
}
|
||||
}
|
||||
shape = [
|
||||
generator.curve(
|
||||
computeMultipleCurvesFromSplits(points, splitInverse),
|
||||
options,
|
||||
),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
shape = [generator.curve(points as [number, number][], options)];
|
||||
shape = [
|
||||
generator.curve(
|
||||
computeMultipleCurvesFromSplits(points, splits),
|
||||
options,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
// add lines only in arrow
|
||||
@@ -334,7 +376,8 @@ export const _generateElementShape = (
|
||||
|
||||
if (isPathALoop(element.points)) {
|
||||
// generate rough polygon to fill freedraw shape
|
||||
shape = generator.polygon(element.points as [number, number][], {
|
||||
const simplifiedPoints = simplify(element.points, 0.75);
|
||||
shape = generator.curve(simplifiedPoints as [number, number][], {
|
||||
...generateRoughOptions(element),
|
||||
stroke: "none",
|
||||
});
|
||||
@@ -360,3 +403,22 @@ export const _generateElementShape = (
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const computeMultipleCurvesFromSplits = (
|
||||
points: readonly Point[],
|
||||
splits: readonly number[],
|
||||
): [number, number][][] => {
|
||||
const pointList: Point[][] = [];
|
||||
let currentIndex = 0;
|
||||
for (const index of splits) {
|
||||
const slice = points.slice(currentIndex, index + 1);
|
||||
if (slice.length) {
|
||||
pointList.push([...slice]);
|
||||
}
|
||||
currentIndex = index;
|
||||
}
|
||||
if (currentIndex < points.length - 1) {
|
||||
pointList.push(points.slice(currentIndex));
|
||||
}
|
||||
return pointList as [number, number][][];
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
|
||||
"boundElements": null,
|
||||
"endArrowhead": "arrow",
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -38,7 +38,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -56,7 +56,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -72,7 +72,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
|
||||
"seed": 1278240551,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "diamond",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -90,7 +90,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -106,7 +106,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
|
||||
"seed": 1278240551,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "ellipse",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -124,7 +124,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
|
||||
"boundElements": null,
|
||||
"endArrowhead": null,
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -153,7 +153,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "line",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -171,7 +171,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -187,7 +187,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
|
||||
"seed": 1278240551,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
|
||||
@@ -5,7 +5,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 1`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -21,7 +21,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 1`] = `
|
||||
"seed": 1014066025,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
@@ -37,7 +37,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 2`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -53,7 +53,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 2`] = `
|
||||
"seed": 1278240551,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
@@ -69,7 +69,7 @@ exports[`move element > rectangle 1`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -85,7 +85,7 @@ exports[`move element > rectangle 1`] = `
|
||||
"seed": 1278240551,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -106,7 +106,7 @@ exports[`move element > rectangles with binding arrow 1`] = `
|
||||
"type": "arrow",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
@@ -122,7 +122,7 @@ exports[`move element > rectangles with binding arrow 1`] = `
|
||||
"seed": 1278240551,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -143,7 +143,7 @@ exports[`move element > rectangles with binding arrow 2`] = `
|
||||
"type": "arrow",
|
||||
},
|
||||
],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 300,
|
||||
@@ -159,7 +159,7 @@ exports[`move element > rectangles with binding arrow 2`] = `
|
||||
"seed": 2019559783,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 6,
|
||||
@@ -181,7 +181,7 @@ exports[`move element > rectangles with binding arrow 3`] = `
|
||||
"focus": -0.46666666666666673,
|
||||
"gap": 10,
|
||||
},
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 81.48231043525051,
|
||||
@@ -214,7 +214,7 @@ exports[`move element > rectangles with binding arrow 3`] = `
|
||||
},
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "line",
|
||||
"updated": 1,
|
||||
"version": 11,
|
||||
|
||||
@@ -7,7 +7,7 @@ exports[`multi point mode in linear elements > arrow 1`] = `
|
||||
"boundElements": null,
|
||||
"endArrowhead": "arrow",
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 110,
|
||||
@@ -43,7 +43,7 @@ exports[`multi point mode in linear elements > arrow 1`] = `
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 7,
|
||||
@@ -61,7 +61,7 @@ exports[`multi point mode in linear elements > line 1`] = `
|
||||
"boundElements": null,
|
||||
"endArrowhead": null,
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 110,
|
||||
@@ -97,7 +97,7 @@ exports[`multi point mode in linear elements > line 1`] = `
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "line",
|
||||
"updated": 1,
|
||||
"version": 7,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ exports[`select single element on the scene > arrow 1`] = `
|
||||
"boundElements": null,
|
||||
"endArrowhead": "arrow",
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -36,7 +36,7 @@ exports[`select single element on the scene > arrow 1`] = `
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -54,7 +54,7 @@ exports[`select single element on the scene > arrow escape 1`] = `
|
||||
"boundElements": null,
|
||||
"endArrowhead": null,
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -83,7 +83,7 @@ exports[`select single element on the scene > arrow escape 1`] = `
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "line",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
@@ -99,7 +99,7 @@ exports[`select single element on the scene > diamond 1`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -115,7 +115,7 @@ exports[`select single element on the scene > diamond 1`] = `
|
||||
"seed": 1278240551,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "diamond",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -131,7 +131,7 @@ exports[`select single element on the scene > ellipse 1`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -147,7 +147,7 @@ exports[`select single element on the scene > ellipse 1`] = `
|
||||
"seed": 1278240551,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "ellipse",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
@@ -163,7 +163,7 @@ exports[`select single element on the scene > rectangle 1`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 50,
|
||||
@@ -179,7 +179,7 @@ exports[`select single element on the scene > rectangle 1`] = `
|
||||
"seed": 1278240551,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 2,
|
||||
|
||||
@@ -7,7 +7,7 @@ exports[`restoreElements > should restore arrow element correctly 1`] = `
|
||||
"boundElements": [],
|
||||
"endArrowhead": null,
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
@@ -36,7 +36,7 @@ exports[`restoreElements > should restore arrow element correctly 1`] = `
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -160,7 +160,7 @@ exports[`restoreElements > should restore freedraw element correctly 1`] = `
|
||||
"angle": 0,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": [],
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 0,
|
||||
@@ -180,7 +180,7 @@ exports[`restoreElements > should restore freedraw element correctly 1`] = `
|
||||
"simulatePressure": true,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "freedraw",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -198,7 +198,7 @@ exports[`restoreElements > should restore line and draw elements correctly 1`] =
|
||||
"boundElements": [],
|
||||
"endArrowhead": null,
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
@@ -227,7 +227,7 @@ exports[`restoreElements > should restore line and draw elements correctly 1`] =
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "line",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -245,7 +245,7 @@ exports[`restoreElements > should restore line and draw elements correctly 2`] =
|
||||
"boundElements": [],
|
||||
"endArrowhead": null,
|
||||
"endBinding": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"frameId": null,
|
||||
"groupIds": [],
|
||||
"height": 100,
|
||||
@@ -274,7 +274,7 @@ exports[`restoreElements > should restore line and draw elements correctly 2`] =
|
||||
"startBinding": null,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"type": "line",
|
||||
"updated": 1,
|
||||
"version": 1,
|
||||
@@ -292,7 +292,7 @@ exports[`restoreElements > should restore text element correctly passing value f
|
||||
"baseline": 0,
|
||||
"boundElements": [],
|
||||
"containerId": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 14,
|
||||
"frameId": null,
|
||||
@@ -312,7 +312,7 @@ exports[`restoreElements > should restore text element correctly passing value f
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "text",
|
||||
"textAlign": "center",
|
||||
"type": "text",
|
||||
@@ -333,7 +333,7 @@ exports[`restoreElements > should restore text element correctly with unknown fo
|
||||
"baseline": 0,
|
||||
"boundElements": [],
|
||||
"containerId": null,
|
||||
"fillStyle": "hachure",
|
||||
"fillStyle": "solid",
|
||||
"fontFamily": 1,
|
||||
"fontSize": 10,
|
||||
"frameId": null,
|
||||
@@ -353,7 +353,7 @@ exports[`restoreElements > should restore text element correctly with unknown fo
|
||||
"seed": Any<Number>,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeWidth": 2,
|
||||
"text": "",
|
||||
"textAlign": "left",
|
||||
"type": "text",
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
} from "../../element/typeChecks";
|
||||
import { getCommonBounds, getElementPointsCoords } from "../../element/bounds";
|
||||
import { rotatePoint } from "../../math";
|
||||
import { getTextEditor } from "../queries/dom";
|
||||
|
||||
const { h } = window;
|
||||
|
||||
@@ -476,11 +477,7 @@ export class UI {
|
||||
Keyboard.keyPress(KEYS.ENTER);
|
||||
}
|
||||
|
||||
const editor =
|
||||
openedEditor ??
|
||||
document.querySelector<HTMLTextAreaElement>(
|
||||
".excalidraw-textEditorContainer > textarea",
|
||||
);
|
||||
const editor = await getTextEditor();
|
||||
if (!editor) {
|
||||
throw new Error("Can't find wysiwyg text editor in the dom");
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ exports[`exportToSvg > with default arguments 1`] = `
|
||||
"currentChartType": "bar",
|
||||
"currentItemBackgroundColor": "transparent",
|
||||
"currentItemEndArrowhead": "arrow",
|
||||
"currentItemFillStyle": "hachure",
|
||||
"currentItemFillStyle": "solid",
|
||||
"currentItemFontFamily": 1,
|
||||
"currentItemFontSize": 20,
|
||||
"currentItemOpacity": 100,
|
||||
@@ -23,7 +23,7 @@ exports[`exportToSvg > with default arguments 1`] = `
|
||||
"currentItemStartArrowhead": null,
|
||||
"currentItemStrokeColor": "#1e1e1e",
|
||||
"currentItemStrokeStyle": "solid",
|
||||
"currentItemStrokeWidth": 1,
|
||||
"currentItemStrokeWidth": 2,
|
||||
"currentItemTextAlign": "left",
|
||||
"cursorButton": "up",
|
||||
"defaultSidebarDockedPreference": false,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { vi } from "vitest";
|
||||
import { Excalidraw } from "../../packages/excalidraw/index";
|
||||
import { ExcalidrawImperativeAPI } from "../../types";
|
||||
import { resolvablePromise } from "../../utils";
|
||||
import { render } from "../test-utils";
|
||||
import { Pointer } from "../helpers/ui";
|
||||
|
||||
describe("event callbacks", () => {
|
||||
const h = window.h;
|
||||
|
||||
let excalidrawAPI: ExcalidrawImperativeAPI;
|
||||
|
||||
const mouse = new Pointer("mouse");
|
||||
|
||||
beforeEach(async () => {
|
||||
const excalidrawAPIPromise = resolvablePromise<ExcalidrawImperativeAPI>();
|
||||
await render(
|
||||
<Excalidraw ref={(api) => excalidrawAPIPromise.resolve(api as any)} />,
|
||||
);
|
||||
excalidrawAPI = await excalidrawAPIPromise;
|
||||
});
|
||||
|
||||
it("should trigger onChange on render", async () => {
|
||||
const onChange = vi.fn();
|
||||
|
||||
const origBackgroundColor = h.state.viewBackgroundColor;
|
||||
excalidrawAPI.onChange(onChange);
|
||||
excalidrawAPI.updateScene({ appState: { viewBackgroundColor: "red" } });
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
// elements
|
||||
[],
|
||||
// appState
|
||||
expect.objectContaining({
|
||||
viewBackgroundColor: "red",
|
||||
}),
|
||||
// files
|
||||
{},
|
||||
);
|
||||
expect(onChange.mock.lastCall[1].viewBackgroundColor).not.toBe(
|
||||
origBackgroundColor,
|
||||
);
|
||||
});
|
||||
|
||||
it("should trigger onPointerDown/onPointerUp on canvas pointerDown/pointerUp", async () => {
|
||||
const onPointerDown = vi.fn();
|
||||
const onPointerUp = vi.fn();
|
||||
|
||||
excalidrawAPI.onPointerDown(onPointerDown);
|
||||
excalidrawAPI.onPointerUp(onPointerUp);
|
||||
|
||||
mouse.downAt(100);
|
||||
expect(onPointerDown).toHaveBeenCalledTimes(1);
|
||||
expect(onPointerUp).not.toHaveBeenCalled();
|
||||
mouse.up();
|
||||
expect(onPointerDown).toHaveBeenCalledTimes(1);
|
||||
expect(onPointerUp).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { waitFor } from "@testing-library/dom";
|
||||
|
||||
export const getTextEditor = async (waitForEditor = true) => {
|
||||
const query = () =>
|
||||
document.querySelector(
|
||||
".excalidraw-textEditorContainer > textarea",
|
||||
) as HTMLTextAreaElement;
|
||||
if (waitForEditor) {
|
||||
await waitFor(() => expect(query()).not.toBe(null));
|
||||
return query();
|
||||
}
|
||||
return query();
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,55 @@
|
||||
import { Excalidraw } from "../packages/excalidraw/index";
|
||||
import { ExcalidrawImperativeAPI } from "../types";
|
||||
import { resolvablePromise } from "../utils";
|
||||
import { render } from "./test-utils";
|
||||
import { Pointer } from "./helpers/ui";
|
||||
|
||||
describe("setActiveTool()", () => {
|
||||
const h = window.h;
|
||||
|
||||
let excalidrawAPI: ExcalidrawImperativeAPI;
|
||||
|
||||
const mouse = new Pointer("mouse");
|
||||
|
||||
beforeEach(async () => {
|
||||
const excalidrawAPIPromise = resolvablePromise<ExcalidrawImperativeAPI>();
|
||||
await render(
|
||||
<Excalidraw ref={(api) => excalidrawAPIPromise.resolve(api as any)} />,
|
||||
);
|
||||
excalidrawAPI = await excalidrawAPIPromise;
|
||||
});
|
||||
|
||||
it("should expose setActiveTool on package API", () => {
|
||||
expect(excalidrawAPI.setActiveTool).toBeDefined();
|
||||
expect(excalidrawAPI.setActiveTool).toBe(h.app.setActiveTool);
|
||||
});
|
||||
|
||||
it("should set the active tool type", async () => {
|
||||
expect(h.state.activeTool.type).toBe("selection");
|
||||
excalidrawAPI.setActiveTool({ type: "rectangle" });
|
||||
expect(h.state.activeTool.type).toBe("rectangle");
|
||||
|
||||
mouse.down(10, 10);
|
||||
mouse.up(20, 20);
|
||||
|
||||
expect(h.state.activeTool.type).toBe("selection");
|
||||
});
|
||||
|
||||
it("should support tool locking", async () => {
|
||||
expect(h.state.activeTool.type).toBe("selection");
|
||||
excalidrawAPI.setActiveTool({ type: "rectangle", locked: true });
|
||||
expect(h.state.activeTool.type).toBe("rectangle");
|
||||
|
||||
mouse.down(10, 10);
|
||||
mouse.up(20, 20);
|
||||
|
||||
expect(h.state.activeTool.type).toBe("rectangle");
|
||||
});
|
||||
|
||||
it("should set custom tool", async () => {
|
||||
expect(h.state.activeTool.type).toBe("selection");
|
||||
excalidrawAPI.setActiveTool({ type: "custom", customType: "comment" });
|
||||
expect(h.state.activeTool.type).toBe("custom");
|
||||
expect(h.state.activeTool.customType).toBe("comment");
|
||||
});
|
||||
});
|
||||
@@ -607,6 +607,8 @@ export type PointerDownState = Readonly<{
|
||||
};
|
||||
}>;
|
||||
|
||||
type UnsubscribeCallback = () => void;
|
||||
|
||||
export type ExcalidrawImperativeAPI = {
|
||||
updateScene: InstanceType<typeof App>["updateScene"];
|
||||
updateLibrary: InstanceType<typeof Library>["updateLibrary"];
|
||||
@@ -637,6 +639,27 @@ export type ExcalidrawImperativeAPI = {
|
||||
* used in conjunction with view mode (props.viewModeEnabled).
|
||||
*/
|
||||
updateFrameRendering: InstanceType<typeof App>["updateFrameRendering"];
|
||||
onChange: (
|
||||
callback: (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
files: BinaryFiles,
|
||||
) => void,
|
||||
) => UnsubscribeCallback;
|
||||
onPointerDown: (
|
||||
callback: (
|
||||
activeTool: AppState["activeTool"],
|
||||
pointerDownState: PointerDownState,
|
||||
event: React.PointerEvent<HTMLElement>,
|
||||
) => void,
|
||||
) => UnsubscribeCallback;
|
||||
onPointerUp: (
|
||||
callback: (
|
||||
activeTool: AppState["activeTool"],
|
||||
pointerDownState: PointerDownState,
|
||||
event: PointerEvent,
|
||||
) => void,
|
||||
) => UnsubscribeCallback;
|
||||
};
|
||||
|
||||
export type Device = Readonly<{
|
||||
|
||||
+6
-2
@@ -363,18 +363,21 @@ export const distance = (x: number, y: number) => Math.abs(x - y);
|
||||
|
||||
export const updateActiveTool = (
|
||||
appState: Pick<AppState, "activeTool">,
|
||||
data: (
|
||||
data: ((
|
||||
| {
|
||||
type: ToolType;
|
||||
}
|
||||
| { type: "custom"; customType: string }
|
||||
) & { lastActiveToolBeforeEraser?: ActiveTool | null },
|
||||
) & { locked?: boolean }) & {
|
||||
lastActiveToolBeforeEraser?: ActiveTool | null;
|
||||
},
|
||||
): AppState["activeTool"] => {
|
||||
if (data.type === "custom") {
|
||||
return {
|
||||
...appState.activeTool,
|
||||
type: "custom",
|
||||
customType: data.customType,
|
||||
locked: data.locked ?? appState.activeTool.locked,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -386,6 +389,7 @@ export const updateActiveTool = (
|
||||
: data.lastActiveToolBeforeEraser,
|
||||
type: data.type,
|
||||
customType: null,
|
||||
locked: data.locked ?? appState.activeTool.locked,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -4684,6 +4684,11 @@ grapheme-splitter@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
|
||||
integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
|
||||
|
||||
hachure-fill@^0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/hachure-fill/-/hachure-fill-0.5.2.tgz#d19bc4cc8750a5962b47fb1300557a85fcf934cc"
|
||||
integrity sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==
|
||||
|
||||
has-bigints@^1.0.1, has-bigints@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
|
||||
@@ -6028,6 +6033,11 @@ points-on-curve@0.2.0, points-on-curve@^0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/points-on-curve/-/points-on-curve-0.2.0.tgz#7dbb98c43791859434284761330fa893cb81b4d1"
|
||||
integrity sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==
|
||||
|
||||
points-on-curve@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/points-on-curve/-/points-on-curve-1.0.1.tgz#03fcdc08e48e3bfdc7e8bd1d7ccd4d5f11e132c6"
|
||||
integrity sha512-3nmX4/LIiyuwGLwuUrfhTlDeQFlAhi7lyK/zcRNGhalwapDWgAGR82bUpmn2mA03vII3fvNCG8jAONzKXwpxAg==
|
||||
|
||||
points-on-path@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/points-on-path/-/points-on-path-0.2.1.tgz#553202b5424c53bed37135b318858eacff85dd52"
|
||||
@@ -6449,11 +6459,12 @@ rollup@^3.25.2:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
roughjs@4.5.2:
|
||||
version "4.5.2"
|
||||
resolved "https://registry.yarnpkg.com/roughjs/-/roughjs-4.5.2.tgz#aab644dcb41e9a75826c8bd5a5b0a859095f2f10"
|
||||
integrity sha512-2xSlLDKdsWyFxrveYWk9YQ/Y9UfK38EAMRNkYkMqYBJvPX8abCa9PN0x3w02H8Oa6/0bcZICJU+U95VumPqseg==
|
||||
roughjs@4.6.5:
|
||||
version "4.6.5"
|
||||
resolved "https://registry.yarnpkg.com/roughjs/-/roughjs-4.6.5.tgz#1db965cf1a043cb7f05181dd7d119f7960fba8d8"
|
||||
integrity sha512-4Q6XBbZWlp8yj1uipq2bQ1CPlxMhW/ukufwkuhh+2L79utk+O5kMSbfVh4UNBMtKJ3PxHQ9Ou3ncNt1iQcphJA==
|
||||
dependencies:
|
||||
hachure-fill "^0.5.2"
|
||||
path-data-parser "^0.1.0"
|
||||
points-on-curve "^0.2.0"
|
||||
points-on-path "^0.2.1"
|
||||
|
||||
Reference in New Issue
Block a user