fix: command palette tweaks and fixes (#7876)

This commit is contained in:
David Luzar 2024-04-11 11:39:19 +02:00 committed by GitHub
parent 4987cc53d0
commit f597bd3e01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 116 additions and 44 deletions

View File

@ -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
? [
{

View File

@ -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()}
/>
)}
<MainMenu.DefaultItems.CommandPalette />
<MainMenu.DefaultItems.CommandPalette className="highlighted" />
<MainMenu.DefaultItems.Help />
<MainMenu.DefaultItems.ClearCanvas />
<MainMenu.Separator />
<MainMenu.ItemLink
icon={PlusPromoIcon}
icon={ExcalLogo}
href={`${
import.meta.env.VITE_APP_PLUS_LP
import.meta.env.VITE_APP_PLUS_APP
}/plus?utm_source=excalidraw&utm_medium=app&utm_content=hamburger`}
className="ExcalidrawPlus"
className=""
>
Excalidraw+
</MainMenu.ItemLink>
<MainMenu.DefaultItems.Socials />
<MainMenu.ItemLink
icon={arrowBarToLeftIcon}
href={`${import.meta.env.VITE_APP_PLUS_APP}${
isExcalidrawPlusSignedUser ? "" : "/sign-up"
}?utm_source=signin&utm_medium=app&utm_content=hamburger`}
className="highlighted"
>
{isExcalidrawPlusSignedUser ? "Sign in" : "Sign up"}
</MainMenu.ItemLink>
<MainMenu.Separator />
<MainMenu.DefaultItems.ToggleTheme
allowSystemTheme

View File

@ -1,5 +1,5 @@
import React from "react";
import { PlusPromoIcon } from "../../packages/excalidraw/components/icons";
import { arrowBarToLeftIcon } from "../../packages/excalidraw/components/icons";
import { useI18n } from "../../packages/excalidraw/i18n";
import { WelcomeScreen } from "../../packages/excalidraw/index";
import { isExcalidrawPlusSignedUser } from "../app_constants";
@ -61,9 +61,9 @@ export const AppWelcomeScreen: React.FC<{
import.meta.env.VITE_APP_PLUS_LP
}/plus?utm_source=excalidraw&utm_medium=app&utm_content=welcomeScreenGuest`}
shortcut={null}
icon={PlusPromoIcon}
icon={arrowBarToLeftIcon}
>
Try Excalidraw Plus!
Sign up
</WelcomeScreen.Center.MenuItemLink>
)}
</WelcomeScreen.Center.Menu>

View File

@ -38,7 +38,7 @@
background-color: #ecfdf5;
color: #064e3c;
}
&.ExcalidrawPlus {
&.highlighted {
color: var(--color-promo);
}
}

View File

@ -216,32 +216,23 @@ exports[`Test MobileMenu > should initialize with welcome screen and hide once u
stroke-width="2"
viewBox="0 0 24 24"
>
<g
stroke-width="1.5"
>
<g>
<path
d="M0 0h24v24H0z"
fill="none"
stroke="none"
/>
<rect
height="4"
rx="1"
width="18"
x="3"
y="8"
/>
<line
x1="12"
x2="12"
y1="8"
y2="21"
<path
d="M10 12l10 0"
/>
<path
d="M19 12v7a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-7"
d="M10 12l4 4"
/>
<path
d="M7.5 8a2.5 2.5 0 0 1 0 -5a4.8 8 0 0 1 4.5 5a4.8 8 0 0 1 4.5 -5a2.5 2.5 0 0 1 0 5"
d="M10 12l4 -4"
/>
<path
d="M4 4l0 16"
/>
</g>
</svg>
@ -249,7 +240,7 @@ exports[`Test MobileMenu > should initialize with welcome screen and hide once u
<div
class="welcome-screen-menu-item__text"
>
Try Excalidraw Plus!
Sign up
</div>
</a>
</div>

View File

@ -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,

View File

@ -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<HTMLInputElement>(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("");

View File

@ -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")}
<div className="HelpDialog__link-icon">{ExternalLinkIcon}</div>
{t("helpDialog.documentation")}
</a>
<a
className="HelpDialog__btn"
@ -26,8 +26,8 @@ const Header = () => (
target="_blank"
rel="noopener noreferrer"
>
{t("helpDialog.blog")}
<div className="HelpDialog__link-icon">{ExternalLinkIcon}</div>
{t("helpDialog.blog")}
</a>
<a
className="HelpDialog__btn"
@ -35,8 +35,17 @@ const Header = () => (
target="_blank"
rel="noopener noreferrer"
>
<div className="HelpDialog__link-icon">{GithubIcon}</div>
{t("helpDialog.github")}
<div className="HelpDialog__link-icon">{ExternalLinkIcon}</div>
</a>
<a
className="HelpDialog__btn"
href="https://youtube.com/@excalidraw"
target="_blank"
rel="noopener noreferrer"
>
<div className="HelpDialog__link-icon">{youtubeIcon}</div>
YouTube
</a>
</div>
);

View File

@ -2095,3 +2095,24 @@ export const DeviceDesktopIcon = createIcon(
</g>,
{ ...tablerIconProps, strokeWidth: 1.5 },
);
// arrow-bar-to-left
export const arrowBarToLeftIcon = createIcon(
<g>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 12l10 0" />
<path d="M10 12l4 4" />
<path d="M10 12l4 -4" />
<path d="M4 4l0 16" />
</g>,
tablerIconProps,
);
export const youtubeIcon = createIcon(
<g>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M2 8a4 4 0 0 1 4 -4h12a4 4 0 0 1 4 4v8a4 4 0 0 1 -4 4h-12a4 4 0 0 1 -4 -4v-8z" />
<path d="M10 9l5 3l-5 3z" />
</g>,
tablerIconProps,
);

View File

@ -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 = () => {
<DropdownMenuItem
icon={boltIcon}
data-testid="command-palette-button"
onSelect={() => 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")}
</DropdownMenuItem>