diff --git a/src/components/footer/FooterCenter.scss b/src/components/footer/FooterCenter.scss
index 1e17db6c5..ce6565922 100644
--- a/src/components/footer/FooterCenter.scss
+++ b/src/components/footer/FooterCenter.scss
@@ -1,10 +1,11 @@
.footer-center {
pointer-events: none;
& > * {
- pointer-events: all;
+ pointer-events: var(--ui-pointerEvents);
}
display: flex;
width: 100%;
justify-content: flex-start;
+ margin-inline-end: 0.6rem;
}
diff --git a/src/components/icons.tsx b/src/components/icons.tsx
index 726fecc49..82ae655ab 100644
--- a/src/components/icons.tsx
+++ b/src/components/icons.tsx
@@ -255,14 +255,12 @@ export const WelcomeScreenTopToolbarArrow = createIcon(
// custom
export const ExcalLogo = createIcon(
-
,
- { width: 26, height: 62, fill: "none" },
+ { width: 40, height: 40, fill: "none" },
);
// custom
diff --git a/src/components/welcome-screen/WelcomeScreen.Center.tsx b/src/components/welcome-screen/WelcomeScreen.Center.tsx
index 2d43761e6..46a51e3fe 100644
--- a/src/components/welcome-screen/WelcomeScreen.Center.tsx
+++ b/src/components/welcome-screen/WelcomeScreen.Center.tsx
@@ -3,8 +3,9 @@ import { getShortcutFromShortcutName } from "../../actions/shortcuts";
import { t, useI18n } from "../../i18n";
import { useDevice, useExcalidrawActionManager } from "../App";
import { useTunnels } from "../../context/tunnels";
-import { ExcalLogo, HelpIcon, LoadIcon, usersIcon } from "../icons";
+import { HelpIcon, LoadIcon, usersIcon } from "../icons";
import { useUIAppState } from "../../context/ui-appState";
+import { ExcalidrawLogo } from "../ExcalidrawLogo";
const WelcomeScreenMenuItemContent = ({
icon,
@@ -109,7 +110,7 @@ Center.displayName = "Center";
const Logo = ({ children }: { children?: React.ReactNode }) => {
return (
- {children || <>{ExcalLogo} Excalidraw>}
+ {children || }
);
};
diff --git a/src/components/welcome-screen/WelcomeScreen.scss b/src/components/welcome-screen/WelcomeScreen.scss
index f856d4594..8f1a09bf3 100644
--- a/src/components/welcome-screen/WelcomeScreen.scss
+++ b/src/components/welcome-screen/WelcomeScreen.scss
@@ -10,6 +10,13 @@
pointer-events: none;
color: var(--color-gray-40);
+
+ a {
+ --color: var(--color-primary);
+ color: var(--color);
+ text-decoration: none;
+ margin-bottom: -6px;
+ }
}
&.theme--dark {
@@ -136,11 +143,6 @@
align-items: center;
column-gap: 0.75rem;
font-size: 2.25rem;
-
- svg {
- width: 1.625rem;
- height: auto;
- }
}
.welcome-screen-center__heading {
@@ -159,7 +161,7 @@
.welcome-screen-menu-item {
box-sizing: border-box;
- pointer-events: all;
+ pointer-events: var(--ui-pointerEvents);
color: var(--color-gray-50);
font-size: 0.875rem;
@@ -200,7 +202,7 @@
}
}
- &:not(:active) .welcome-screen-menu-item:hover {
+ .welcome-screen-menu-item:hover {
text-decoration: none;
background: var(--color-gray-10);
@@ -244,7 +246,7 @@
}
}
- &:not(:active) .welcome-screen-menu-item:hover {
+ .welcome-screen-menu-item:hover {
background: var(--color-gray-85);
.welcome-screen-menu-item__shortcut {
diff --git a/src/constants.ts b/src/constants.ts
index c07723d88..3dcce75ed 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -41,6 +41,14 @@ export const POINTER_BUTTON = {
TOUCH: -1,
} as const;
+export const POINTER_EVENTS = {
+ enabled: "all",
+ disabled: "none",
+ // asserted as any so it can be freely assigned to React Element
+ // "pointerEnvets" CSS prop
+ inheritFromUI: "var(--ui-pointerEvents)" as any,
+} as const;
+
export enum EVENT {
COPY = "copy",
PASTE = "paste",
diff --git a/src/css/styles.scss b/src/css/styles.scss
index f1d359d5e..4ca58af4f 100644
--- a/src/css/styles.scss
+++ b/src/css/styles.scss
@@ -6,6 +6,8 @@
--zIndex-interactiveCanvas: 2;
--zIndex-wysiwyg: 3;
--zIndex-layerUI: 4;
+ --zIndex-eyeDropperBackdrop: 5;
+ --zIndex-eyeDropperPreview: 6;
--zIndex-modal: 1000;
--zIndex-popup: 1001;
@@ -251,7 +253,7 @@
max-height: 100%;
display: flex;
flex-direction: column;
- pointer-events: initial;
+ pointer-events: var(--ui-pointerEvents);
.panelColumn {
padding: 8px 8px 0 8px;
@@ -299,7 +301,7 @@
pointer-events: none !important;
& > * {
- pointer-events: all;
+ pointer-events: var(--ui-pointerEvents);
}
}
@@ -310,16 +312,16 @@
cursor: default;
pointer-events: none !important;
+ & > * {
+ pointer-events: var(--ui-pointerEvents);
+ }
+
@media (min-width: 1536px) {
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 3rem;
}
}
- .layer-ui__wrapper:not(.disable-pointerEvents) .App-menu_top > * {
- pointer-events: all;
- }
-
.App-menu_top > *:first-child {
justify-self: flex-start;
}
@@ -423,17 +425,6 @@
}
}
- .disable-zen-mode {
- border-radius: var(--border-radius-lg);
- background-color: var(--color-gray-20);
- border: 1px solid var(--color-gray-30);
- padding: 10px 20px;
-
- &:hover {
- background-color: var(--color-gray-30);
- }
- }
-
.scroll-back-to-content {
border-radius: var(--border-radius-lg);
background-color: var(--island-bg-color);
@@ -445,7 +436,7 @@
left: 50%;
bottom: 30px;
transform: translateX(-50%);
- pointer-events: all;
+ pointer-events: var(--ui-pointerEvents);
font-family: inherit;
&:hover {
diff --git a/src/css/theme.scss b/src/css/theme.scss
index 643f9346d..1db22313a 100644
--- a/src/css/theme.scss
+++ b/src/css/theme.scss
@@ -126,6 +126,9 @@
--color-success: #268029;
--color-success-lighter: #cafccc;
+ --color-logo-icon: var(--color-primary);
+ --color-logo-text: #190064;
+
--border-radius-md: 0.375rem;
--border-radius-lg: 0.5rem;
@@ -219,5 +222,7 @@
--color-muted-background-darker: var(--color-gray-20);
--color-promo: #d297ff;
+
+ --color-logo-text: #e2dfff;
}
}
diff --git a/src/element/linearElementEditor.ts b/src/element/linearElementEditor.ts
index f0dee4faa..adc5aafc4 100644
--- a/src/element/linearElementEditor.ts
+++ b/src/element/linearElementEditor.ts
@@ -42,7 +42,7 @@ import {
} from "./binding";
import { tupleToCoors } from "../utils";
import { isBindingElement } from "./typeChecks";
-import { shouldRotateWithDiscreteAngle } from "../keys";
+import { KEYS, shouldRotateWithDiscreteAngle } from "../keys";
import { getBoundTextElement, handleBindTextResize } from "./textElement";
import { DRAGGING_THRESHOLD } from "../constants";
import { Mutable } from "../utility-types";
@@ -221,7 +221,7 @@ export class LinearElementEditor {
element,
referencePoint,
[scenePointerX, scenePointerY],
- appState.gridSize,
+ event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize,
);
LinearElementEditor.movePoints(element, [
@@ -238,7 +238,7 @@ export class LinearElementEditor {
element,
scenePointerX - linearElementEditor.pointerOffset.x,
scenePointerY - linearElementEditor.pointerOffset.y,
- appState.gridSize,
+ event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize,
);
const deltaX = newDraggingPointPosition[0] - draggingPoint[0];
@@ -254,7 +254,7 @@ export class LinearElementEditor {
element,
scenePointerX - linearElementEditor.pointerOffset.x,
scenePointerY - linearElementEditor.pointerOffset.y,
- appState.gridSize,
+ event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize,
)
: ([
element.points[pointIndex][0] + deltaX,
@@ -647,7 +647,7 @@ export class LinearElementEditor {
element,
scenePointer.x,
scenePointer.y,
- appState.gridSize,
+ event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize,
),
],
});
@@ -798,7 +798,7 @@ export class LinearElementEditor {
element,
lastCommittedPoint,
[scenePointerX, scenePointerY],
- appState.gridSize,
+ event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize,
);
newPoint = [
@@ -810,7 +810,7 @@ export class LinearElementEditor {
element,
scenePointerX - appState.editingLinearElement.pointerOffset.x,
scenePointerY - appState.editingLinearElement.pointerOffset.y,
- appState.gridSize,
+ event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize,
);
}
@@ -1176,6 +1176,7 @@ export class LinearElementEditor {
linearElementEditor: LinearElementEditor,
pointerCoords: PointerCoords,
appState: AppState,
+ snapToGrid: boolean,
) {
const element = LinearElementEditor.getElement(
linearElementEditor.elementId,
@@ -1196,7 +1197,7 @@ export class LinearElementEditor {
element,
pointerCoords.x,
pointerCoords.y,
- appState.gridSize,
+ snapToGrid ? appState.gridSize : null,
);
const points = [
...element.points.slice(0, segmentMidpoint.index!),
diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx
index a2301b964..c855de357 100644
--- a/src/element/textWysiwyg.test.tsx
+++ b/src/element/textWysiwyg.test.tsx
@@ -1509,4 +1509,30 @@ describe("textWysiwyg", () => {
expect(text.text).toBe("Excalidraw");
});
});
+
+ it("should bump the version of labelled arrow when label updated", async () => {
+ await render(
);
+ const arrow = UI.createElement("arrow", {
+ width: 300,
+ height: 0,
+ });
+
+ mouse.select(arrow);
+ Keyboard.keyPress(KEYS.ENTER);
+ let editor = getTextEditor();
+ await new Promise((r) => setTimeout(r, 0));
+ updateTextEditor(editor, "Hello");
+ editor.blur();
+
+ const { version } = arrow;
+
+ mouse.select(arrow);
+ Keyboard.keyPress(KEYS.ENTER);
+ editor = getTextEditor();
+ await new Promise((r) => setTimeout(r, 0));
+ updateTextEditor(editor, "Hello\nworld!");
+ editor.blur();
+
+ expect(arrow.version).toEqual(version + 1);
+ });
});
diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx
index b60fdeed2..d7fed6981 100644
--- a/src/element/textWysiwyg.tsx
+++ b/src/element/textWysiwyg.tsx
@@ -20,7 +20,7 @@ import {
ExcalidrawTextContainer,
} from "./types";
import { AppState } from "../types";
-import { mutateElement } from "./mutateElement";
+import { bumpVersion, mutateElement } from "./mutateElement";
import {
getBoundTextElementId,
getContainerElement,
@@ -541,6 +541,9 @@ export const textWysiwyg = ({
id: element.id,
}),
});
+ } else if (isArrowElement(container)) {
+ // updating an arrow label may change bounds, prevent stale cache:
+ bumpVersion(container);
}
} else {
mutateElement(container, {
diff --git a/src/element/typeChecks.ts b/src/element/typeChecks.ts
index 3f61e3fc9..a9c568a40 100644
--- a/src/element/typeChecks.ts
+++ b/src/element/typeChecks.ts
@@ -121,6 +121,7 @@ export const isBindableElement = (
element.type === "ellipse" ||
element.type === "image" ||
element.type === "embeddable" ||
+ element.type === "frame" ||
(element.type === "text" && !element.containerId))
);
};
diff --git a/src/excalidraw-app/components/AppFooter.tsx b/src/excalidraw-app/components/AppFooter.tsx
index 7011a1c4d..624873218 100644
--- a/src/excalidraw-app/components/AppFooter.tsx
+++ b/src/excalidraw-app/components/AppFooter.tsx
@@ -2,6 +2,7 @@ import React from "react";
import { Footer } from "../../packages/excalidraw/index";
import { EncryptedIcon } from "./EncryptedIcon";
import { ExcalidrawPlusAppLink } from "./ExcalidrawPlusAppLink";
+import { isExcalidrawPlusSignedUser } from "../app_constants";
export const AppFooter = React.memo(() => {
return (
@@ -13,8 +14,11 @@ export const AppFooter = React.memo(() => {
alignItems: "center",
}}
>
-
-
+ {isExcalidrawPlusSignedUser ? (
+
+ ) : (
+
+ )}
);
diff --git a/src/excalidraw-app/components/AppMainMenu.tsx b/src/excalidraw-app/components/AppMainMenu.tsx
index 7b562f8b7..6e12d7811 100644
--- a/src/excalidraw-app/components/AppMainMenu.tsx
+++ b/src/excalidraw-app/components/AppMainMenu.tsx
@@ -26,7 +26,9 @@ export const AppMainMenu: React.FC<{