mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-09-22 08:50:56 +02:00
Merge remote-tracking branch 'origin/master' into aakansha-create-text-containers-programmatically
This commit is contained in:
@@ -305,6 +305,7 @@ import { activeConfirmDialogAtom } from "./ActiveConfirmDialog";
|
||||
import { actionWrapTextInContainer } from "../actions/actionBoundText";
|
||||
import BraveMeasureTextError from "./BraveMeasureTextError";
|
||||
import { convertToExcalidrawElements } from "../data/transform";
|
||||
import { activeEyeDropperAtom } from "./EyeDropper";
|
||||
|
||||
const AppContext = React.createContext<AppClassProperties>(null!);
|
||||
const AppPropsContext = React.createContext<AppProps>(null!);
|
||||
@@ -367,8 +368,6 @@ export const useExcalidrawActionManager = () =>
|
||||
|
||||
let didTapTwice: boolean = false;
|
||||
let tappedTwiceTimer = 0;
|
||||
let cursorX = 0;
|
||||
let cursorY = 0;
|
||||
let isHoldingSpace: boolean = false;
|
||||
let isPanning: boolean = false;
|
||||
let isDraggingScrollBar: boolean = false;
|
||||
@@ -426,7 +425,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
hitLinkElement?: NonDeletedExcalidrawElement;
|
||||
lastPointerDown: React.PointerEvent<HTMLCanvasElement> | null = null;
|
||||
lastPointerUp: React.PointerEvent<HTMLElement> | PointerEvent | null = null;
|
||||
lastScenePointer: { x: number; y: number } | null = null;
|
||||
lastViewportPosition = { x: 0, y: 0 };
|
||||
|
||||
constructor(props: AppProps) {
|
||||
super(props);
|
||||
@@ -635,6 +634,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
</LayerUI>
|
||||
<div className="excalidraw-textEditorContainer" />
|
||||
<div className="excalidraw-contextMenuContainer" />
|
||||
<div className="excalidraw-eye-dropper-container" />
|
||||
{selectedElement.length === 1 &&
|
||||
!this.state.contextMenu &&
|
||||
this.state.showHyperlinkPopup && (
|
||||
@@ -725,6 +725,49 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
};
|
||||
|
||||
private openEyeDropper = ({ type }: { type: "stroke" | "background" }) => {
|
||||
jotaiStore.set(activeEyeDropperAtom, {
|
||||
swapPreviewOnAlt: true,
|
||||
previewType: type === "stroke" ? "strokeColor" : "backgroundColor",
|
||||
onSelect: (color, event) => {
|
||||
const shouldUpdateStrokeColor =
|
||||
(type === "background" && event.altKey) ||
|
||||
(type === "stroke" && !event.altKey);
|
||||
const selectedElements = getSelectedElements(
|
||||
this.scene.getElementsIncludingDeleted(),
|
||||
this.state,
|
||||
);
|
||||
if (
|
||||
!selectedElements.length ||
|
||||
this.state.activeTool.type !== "selection"
|
||||
) {
|
||||
if (shouldUpdateStrokeColor) {
|
||||
this.setState({
|
||||
currentItemStrokeColor: color,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
currentItemBackgroundColor: color,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.updateScene({
|
||||
elements: this.scene.getElementsIncludingDeleted().map((el) => {
|
||||
if (this.state.selectedElementIds[el.id]) {
|
||||
return newElementWith(el, {
|
||||
[shouldUpdateStrokeColor ? "strokeColor" : "backgroundColor"]:
|
||||
color,
|
||||
});
|
||||
}
|
||||
return el;
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
keepOpenOnAlt: false,
|
||||
});
|
||||
};
|
||||
|
||||
private syncActionResult = withBatchedUpdates(
|
||||
(actionResult: ActionResult) => {
|
||||
if (this.unmounted || actionResult === false) {
|
||||
@@ -1073,6 +1116,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.unmounted = true;
|
||||
this.removeEventListeners();
|
||||
this.scene.destroy();
|
||||
this.library.destroy();
|
||||
clearTimeout(touchTimeout);
|
||||
touchTimeout = 0;
|
||||
}
|
||||
@@ -1569,7 +1613,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
return;
|
||||
}
|
||||
|
||||
const elementUnderCursor = document.elementFromPoint(cursorX, cursorY);
|
||||
const elementUnderCursor = document.elementFromPoint(
|
||||
this.lastViewportPosition.x,
|
||||
this.lastViewportPosition.y,
|
||||
);
|
||||
if (
|
||||
event &&
|
||||
(!(elementUnderCursor instanceof HTMLCanvasElement) ||
|
||||
@@ -1596,7 +1643,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
// prefer spreadsheet data over image file (MS Office/Libre Office)
|
||||
if (isSupportedImageFile(file) && !data.spreadsheet) {
|
||||
const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords(
|
||||
{ clientX: cursorX, clientY: cursorY },
|
||||
{
|
||||
clientX: this.lastViewportPosition.x,
|
||||
clientY: this.lastViewportPosition.y,
|
||||
},
|
||||
this.state,
|
||||
);
|
||||
|
||||
@@ -1659,13 +1709,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
typeof opts.position === "object"
|
||||
? opts.position.clientX
|
||||
: opts.position === "cursor"
|
||||
? cursorX
|
||||
? this.lastViewportPosition.x
|
||||
: this.state.width / 2 + this.state.offsetLeft;
|
||||
const clientY =
|
||||
typeof opts.position === "object"
|
||||
? opts.position.clientY
|
||||
: opts.position === "cursor"
|
||||
? cursorY
|
||||
? this.lastViewportPosition.y
|
||||
: this.state.height / 2 + this.state.offsetTop;
|
||||
|
||||
const { x, y } = viewportCoordsToSceneCoords(
|
||||
@@ -1749,7 +1799,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
private addTextFromPaste(text: string, isPlainPaste = false) {
|
||||
const { x, y } = viewportCoordsToSceneCoords(
|
||||
{ clientX: cursorX, clientY: cursorY },
|
||||
{
|
||||
clientX: this.lastViewportPosition.x,
|
||||
clientY: this.lastViewportPosition.y,
|
||||
},
|
||||
this.state,
|
||||
);
|
||||
|
||||
@@ -2084,8 +2137,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
private updateCurrentCursorPosition = withBatchedUpdates(
|
||||
(event: MouseEvent) => {
|
||||
cursorX = event.clientX;
|
||||
cursorY = event.clientY;
|
||||
this.lastViewportPosition.x = event.clientX;
|
||||
this.lastViewportPosition.y = event.clientY;
|
||||
},
|
||||
);
|
||||
|
||||
@@ -2158,6 +2211,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
event.shiftKey &&
|
||||
event[KEYS.CTRL_OR_CMD]
|
||||
) {
|
||||
event.preventDefault();
|
||||
this.setState({ openDialog: "imageExport" });
|
||||
return;
|
||||
}
|
||||
@@ -2342,6 +2396,20 @@ class App extends React.Component<AppProps, AppState> {
|
||||
) {
|
||||
jotaiStore.set(activeConfirmDialogAtom, "clearCanvas");
|
||||
}
|
||||
|
||||
// eye dropper
|
||||
// -----------------------------------------------------------------------
|
||||
const lowerCased = event.key.toLocaleLowerCase();
|
||||
const isPickingStroke = lowerCased === KEYS.S && event.shiftKey;
|
||||
const isPickingBackground =
|
||||
event.key === KEYS.I || (lowerCased === KEYS.G && event.shiftKey);
|
||||
|
||||
if (isPickingStroke || isPickingBackground) {
|
||||
this.openEyeDropper({
|
||||
type: isPickingStroke ? "stroke" : "background",
|
||||
});
|
||||
}
|
||||
// -----------------------------------------------------------------------
|
||||
},
|
||||
);
|
||||
|
||||
@@ -2471,8 +2539,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.setState((state) => ({
|
||||
...getStateForZoom(
|
||||
{
|
||||
viewportX: cursorX,
|
||||
viewportY: cursorY,
|
||||
viewportX: this.lastViewportPosition.x,
|
||||
viewportY: this.lastViewportPosition.y,
|
||||
nextZoom: getNormalizedZoom(initialScale * event.scale),
|
||||
},
|
||||
state,
|
||||
@@ -6468,8 +6536,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.translateCanvas((state) => ({
|
||||
...getStateForZoom(
|
||||
{
|
||||
viewportX: cursorX,
|
||||
viewportY: cursorY,
|
||||
viewportX: this.lastViewportPosition.x,
|
||||
viewportY: this.lastViewportPosition.y,
|
||||
nextZoom: getNormalizedZoom(newZoom),
|
||||
},
|
||||
state,
|
||||
|
Reference in New Issue
Block a user