From f597bd3e012950940123660b6035e5aa8669a98d Mon Sep 17 00:00:00 2001
From: David Luzar <5153846+dwelle@users.noreply.github.com>
Date: Thu, 11 Apr 2024 11:39:19 +0200
Subject: [PATCH] fix: command palette tweaks and fixes (#7876)
---
excalidraw-app/App.tsx | 15 ++++++++
excalidraw-app/components/AppMainMenu.tsx | 23 ++++++++---
.../components/AppWelcomeScreen.tsx | 6 +--
excalidraw-app/index.scss | 2 +-
.../__snapshots__/MobileMenu.test.tsx.snap | 25 ++++--------
packages/excalidraw/analytics.ts | 2 +-
.../CommandPalette/CommandPalette.tsx | 38 ++++++++++++++-----
packages/excalidraw/components/HelpDialog.tsx | 17 +++++++--
packages/excalidraw/components/icons.tsx | 21 ++++++++++
.../components/main-menu/DefaultItems.tsx | 9 ++++-
10 files changed, 115 insertions(+), 43 deletions(-)
diff --git a/excalidraw-app/App.tsx b/excalidraw-app/App.tsx
index 56033ec15..90127d987 100644
--- a/excalidraw-app/App.tsx
+++ b/excalidraw-app/App.tsx
@@ -122,6 +122,7 @@ import {
usersIcon,
exportToPlus,
share,
+ youtubeIcon,
} from "../packages/excalidraw/components/icons";
import { appThemeAtom, useHandleAppTheme } from "./useHandleAppTheme";
@@ -1053,6 +1054,20 @@ const ExcalidrawWrapper = () => {
);
},
},
+ {
+ label: "YouTube",
+ icon: youtubeIcon,
+ category: DEFAULT_CATEGORIES.links,
+ predicate: true,
+ keywords: ["features", "tutorials", "howto", "help", "community"],
+ perform: () => {
+ window.open(
+ "https://youtube.com/@excalidraw",
+ "_blank",
+ "noopener noreferrer",
+ );
+ },
+ },
...(isExcalidrawPlusSignedUser
? [
{
diff --git a/excalidraw-app/components/AppMainMenu.tsx b/excalidraw-app/components/AppMainMenu.tsx
index fe3f36c9e..2cc055b32 100644
--- a/excalidraw-app/components/AppMainMenu.tsx
+++ b/excalidraw-app/components/AppMainMenu.tsx
@@ -1,7 +1,11 @@
import React from "react";
-import { PlusPromoIcon } from "../../packages/excalidraw/components/icons";
+import {
+ arrowBarToLeftIcon,
+ ExcalLogo,
+} from "../../packages/excalidraw/components/icons";
import { Theme } from "../../packages/excalidraw/element/types";
import { MainMenu } from "../../packages/excalidraw/index";
+import { isExcalidrawPlusSignedUser } from "../app_constants";
import { LanguageList } from "./LanguageList";
export const AppMainMenu: React.FC<{
@@ -23,20 +27,29 @@ export const AppMainMenu: React.FC<{
onSelect={() => props.onCollabDialogOpen()}
/>
)}
-
+
Excalidraw+
+
+ {isExcalidrawPlusSignedUser ? "Sign in" : "Sign up"}
+
- Try Excalidraw Plus!
+ Sign up
)}
diff --git a/excalidraw-app/index.scss b/excalidraw-app/index.scss
index 24741b062..d5cc4770c 100644
--- a/excalidraw-app/index.scss
+++ b/excalidraw-app/index.scss
@@ -38,7 +38,7 @@
background-color: #ecfdf5;
color: #064e3c;
}
- &.ExcalidrawPlus {
+ &.highlighted {
color: var(--color-promo);
}
}
diff --git a/excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap b/excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap
index ad0c9f0f1..b657dbb54 100644
--- a/excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap
+++ b/excalidraw-app/tests/__snapshots__/MobileMenu.test.tsx.snap
@@ -216,32 +216,23 @@ exports[`Test MobileMenu > should initialize with welcome screen and hide once u
stroke-width="2"
viewBox="0 0 24 24"
>
-
+
-
-
@@ -249,7 +240,7 @@ exports[`Test MobileMenu > should initialize with welcome screen and hide once u
diff --git a/packages/excalidraw/analytics.ts b/packages/excalidraw/analytics.ts
index 671f59202..bd4b6191e 100644
--- a/packages/excalidraw/analytics.ts
+++ b/packages/excalidraw/analytics.ts
@@ -1,6 +1,6 @@
// place here categories that you want to track. We want to track just a
// small subset of categories at a given time.
-const ALLOWED_CATEGORIES_TO_TRACK = ["ai"] as string[];
+const ALLOWED_CATEGORIES_TO_TRACK = ["ai", "command_palette"] as string[];
export const trackEvent = (
category: string,
diff --git a/packages/excalidraw/components/CommandPalette/CommandPalette.tsx b/packages/excalidraw/components/CommandPalette/CommandPalette.tsx
index d7ed691c5..f021632b8 100644
--- a/packages/excalidraw/components/CommandPalette/CommandPalette.tsx
+++ b/packages/excalidraw/components/CommandPalette/CommandPalette.tsx
@@ -49,6 +49,8 @@ import { jotaiStore } from "../../jotai";
import { activeConfirmDialogAtom } from "../ActiveConfirmDialog";
import { CommandPaletteItem } from "./types";
import * as defaultItems from "./defaultCommandPaletteItems";
+import { trackEvent } from "../../analytics";
+import { useStable } from "../../hooks/useStable";
import "./CommandPalette.scss";
@@ -130,12 +132,20 @@ export const CommandPalette = Object.assign(
if (isCommandPaletteToggleShortcut(event)) {
event.preventDefault();
event.stopPropagation();
- setAppState((appState) => ({
- openDialog:
+ setAppState((appState) => {
+ const nextState =
appState.openDialog?.name === "commandPalette"
? null
- : { name: "commandPalette" },
- }));
+ : ({ name: "commandPalette" } as const);
+
+ if (nextState) {
+ trackEvent("command_palette", "open", "shortcut");
+ }
+
+ return {
+ openDialog: nextState,
+ };
+ });
}
};
window.addEventListener(EVENT.KEYDOWN, commandPaletteShortcut, {
@@ -174,10 +184,20 @@ function CommandPaletteInner({
const inputRef = useRef(null);
+ const stableDeps = useStable({
+ uiAppState,
+ customCommandPaletteItems,
+ appProps,
+ });
+
useEffect(() => {
- if (!uiAppState || !app.scene || !actionManager) {
- return;
- }
+ // these props change often and we don't want them to re-run the effect
+ // which would renew `allCommands`, cascading down and resetting state.
+ //
+ // This means that the commands won't update on appState/appProps changes
+ // while the command palette is open
+ const { uiAppState, customCommandPaletteItems, appProps } = stableDeps;
+
const getActionLabel = (action: Action) => {
let label = "";
if (action.label) {
@@ -533,15 +553,13 @@ function CommandPaletteInner({
);
}
}, [
+ stableDeps,
app,
- appProps,
- uiAppState,
actionManager,
setAllCommands,
lastUsed?.label,
setLastUsed,
setAppState,
- customCommandPaletteItems,
]);
const [commandSearch, setCommandSearch] = useState("");
diff --git a/packages/excalidraw/components/HelpDialog.tsx b/packages/excalidraw/components/HelpDialog.tsx
index 6f539963b..23c9f8f47 100644
--- a/packages/excalidraw/components/HelpDialog.tsx
+++ b/packages/excalidraw/components/HelpDialog.tsx
@@ -4,7 +4,7 @@ import { KEYS } from "../keys";
import { Dialog } from "./Dialog";
import { getShortcutKey } from "../utils";
import "./HelpDialog.scss";
-import { ExternalLinkIcon } from "./icons";
+import { ExternalLinkIcon, GithubIcon, youtubeIcon } from "./icons";
import { probablySupportsClipboardBlob } from "../clipboard";
import { isDarwin, isFirefox, isWindows } from "../constants";
import { getShortcutFromShortcutName } from "../actions/shortcuts";
@@ -17,8 +17,8 @@ const Header = () => (
target="_blank"
rel="noopener noreferrer"
>
- {t("helpDialog.documentation")}
{ExternalLinkIcon}
+ {t("helpDialog.documentation")}
(
target="_blank"
rel="noopener noreferrer"
>
- {t("helpDialog.blog")}
{ExternalLinkIcon}
+ {t("helpDialog.blog")}
(
target="_blank"
rel="noopener noreferrer"
>
+ {GithubIcon}
{t("helpDialog.github")}
- {ExternalLinkIcon}
+
+
+ {youtubeIcon}
+ YouTube
);
diff --git a/packages/excalidraw/components/icons.tsx b/packages/excalidraw/components/icons.tsx
index 3ebbdfbcd..2f7be43fb 100644
--- a/packages/excalidraw/components/icons.tsx
+++ b/packages/excalidraw/components/icons.tsx
@@ -2095,3 +2095,24 @@ export const DeviceDesktopIcon = createIcon(
,
{ ...tablerIconProps, strokeWidth: 1.5 },
);
+
+// arrow-bar-to-left
+export const arrowBarToLeftIcon = createIcon(
+
+
+
+
+
+
+ ,
+ tablerIconProps,
+);
+
+export const youtubeIcon = createIcon(
+
+
+
+
+ ,
+ tablerIconProps,
+);
diff --git a/packages/excalidraw/components/main-menu/DefaultItems.tsx b/packages/excalidraw/components/main-menu/DefaultItems.tsx
index caacc0578..26ef26000 100644
--- a/packages/excalidraw/components/main-menu/DefaultItems.tsx
+++ b/packages/excalidraw/components/main-menu/DefaultItems.tsx
@@ -39,6 +39,7 @@ import Trans from "../Trans";
import DropdownMenuItemContentRadio from "../dropdownMenu/DropdownMenuItemContentRadio";
import { THEME } from "../../constants";
import type { Theme } from "../../element/types";
+import { trackEvent } from "../../analytics";
import "./DefaultItems.scss";
@@ -122,7 +123,7 @@ export const SaveAsImage = () => {
};
SaveAsImage.displayName = "SaveAsImage";
-export const CommandPalette = () => {
+export const CommandPalette = (opts?: { className?: string }) => {
const setAppState = useExcalidrawSetAppState();
const { t } = useI18n();
@@ -130,9 +131,13 @@ export const CommandPalette = () => {
setAppState({ openDialog: { name: "commandPalette" } })}
+ onSelect={() => {
+ trackEvent("command_palette", "open", "menu");
+ setAppState({ openDialog: { name: "commandPalette" } });
+ }}
shortcut={getShortcutFromShortcutName("commandPalette")}
aria-label={t("commandPalette.title")}
+ className={opts?.className}
>
{t("commandPalette.title")}