add Preferences default menu item

This commit is contained in:
dwelle
2025-08-31 23:30:26 +02:00
parent 40c5c743b1
commit ecbaeb1701
17 changed files with 306 additions and 56 deletions

View File

@@ -37,6 +37,7 @@ export const AppMainMenu: React.FC<{
)} )}
<MainMenu.DefaultItems.CommandPalette className="highlighted" /> <MainMenu.DefaultItems.CommandPalette className="highlighted" />
<MainMenu.DefaultItems.SearchMenu /> <MainMenu.DefaultItems.SearchMenu />
<MainMenu.DefaultItems.Preferences />
<MainMenu.DefaultItems.Help /> <MainMenu.DefaultItems.Help />
<MainMenu.DefaultItems.ClearCanvas /> <MainMenu.DefaultItems.ClearCanvas />
<MainMenu.Separator /> <MainMenu.Separator />

View File

@@ -54,7 +54,8 @@ export type ShortcutName =
| "saveScene" | "saveScene"
| "imageExport" | "imageExport"
| "commandPalette" | "commandPalette"
| "searchMenu"; | "searchMenu"
| "toolLock";
const shortcutMap: Record<ShortcutName, string[]> = { const shortcutMap: Record<ShortcutName, string[]> = {
toggleTheme: [getShortcutKey("Shift+Alt+D")], toggleTheme: [getShortcutKey("Shift+Alt+D")],
@@ -116,6 +117,7 @@ const shortcutMap: Record<ShortcutName, string[]> = {
toggleShortcuts: [getShortcutKey("?")], toggleShortcuts: [getShortcutKey("?")],
searchMenu: [getShortcutKey("CtrlOrCmd+F")], searchMenu: [getShortcutKey("CtrlOrCmd+F")],
wrapSelectionInFrame: [], wrapSelectionInFrame: [],
toolLock: [getShortcutKey("Q")],
}; };
export const getShortcutFromShortcutName = (name: ShortcutName, idx = 0) => { export const getShortcutFromShortcutName = (name: ShortcutName, idx = 0) => {

View File

@@ -451,10 +451,10 @@ export const ShapesSwitcher = ({
<DropdownMenu.Item <DropdownMenu.Item
onSelect={() => app.onMagicframeToolSelect()} onSelect={() => app.onMagicframeToolSelect()}
icon={MagicIcon} icon={MagicIcon}
badge={<DropdownMenu.Item.Badge>AI</DropdownMenu.Item.Badge>}
data-testid="toolbar-magicframe" data-testid="toolbar-magicframe"
> >
{t("toolBar.magicframe")} {t("toolBar.magicframe")}
<DropdownMenu.Item.Badge>AI</DropdownMenu.Item.Badge>
</DropdownMenu.Item> </DropdownMenu.Item>
</> </>
)} )}

View File

@@ -25,10 +25,6 @@ import { PropertiesPopover } from "../PropertiesPopover";
import { QuickSearch } from "../QuickSearch"; import { QuickSearch } from "../QuickSearch";
import { ScrollableList } from "../ScrollableList"; import { ScrollableList } from "../ScrollableList";
import DropdownMenuGroup from "../dropdownMenu/DropdownMenuGroup"; import DropdownMenuGroup from "../dropdownMenu/DropdownMenuGroup";
import DropdownMenuItem, {
DropDownMenuItemBadgeType,
DropDownMenuItemBadge,
} from "../dropdownMenu/DropdownMenuItem";
import { import {
FontFamilyCodeIcon, FontFamilyCodeIcon,
FontFamilyHeadingIcon, FontFamilyHeadingIcon,
@@ -36,8 +32,15 @@ import {
FreedrawIcon, FreedrawIcon,
} from "../icons"; } from "../icons";
import { Ellipsify } from "../Ellipsify";
import { fontPickerKeyHandler } from "./keyboardNavHandlers"; import { fontPickerKeyHandler } from "./keyboardNavHandlers";
import {
FontPickerListItem,
FontPickerListItemBadgeType,
} from "./FontPickerListItem";
import type { JSX } from "react"; import type { JSX } from "react";
export interface FontDescriptor { export interface FontDescriptor {
@@ -46,7 +49,7 @@ export interface FontDescriptor {
text: string; text: string;
deprecated?: true; deprecated?: true;
badge?: { badge?: {
type: ValueOf<typeof DropDownMenuItemBadgeType>; type: ValueOf<typeof FontPickerListItemBadgeType>;
placeholder: string; placeholder: string;
}; };
} }
@@ -112,7 +115,7 @@ export const FontPickerList = React.memo(
Object.assign(fontDescriptor, { Object.assign(fontDescriptor, {
deprecated: metadata.deprecated, deprecated: metadata.deprecated,
badge: { badge: {
type: DropDownMenuItemBadgeType.RED, type: FontPickerListItemBadgeType.RED,
placeholder: t("fontList.badge.old"), placeholder: t("fontList.badge.old"),
}, },
}); });
@@ -227,7 +230,7 @@ export const FontPickerList = React.memo(
); );
const renderFont = (font: FontDescriptor, index: number) => ( const renderFont = (font: FontDescriptor, index: number) => (
<DropdownMenuItem <FontPickerListItem
key={font.value} key={font.value}
icon={font.icon} icon={font.icon}
value={font.value} value={font.value}
@@ -239,8 +242,8 @@ export const FontPickerList = React.memo(
selected={font.value === selectedFontFamily} selected={font.value === selectedFontFamily}
// allow to tab between search and selected font // allow to tab between search and selected font
tabIndex={font.value === selectedFontFamily ? 0 : -1} tabIndex={font.value === selectedFontFamily ? 0 : -1}
onClick={(e) => { onSelect={() => {
onSelect(Number(e.currentTarget.value)); onSelect(font.value);
}} }}
onMouseMove={() => { onMouseMove={() => {
if (hoveredFont?.value !== font.value) { if (hoveredFont?.value !== font.value) {
@@ -248,13 +251,13 @@ export const FontPickerList = React.memo(
} }
}} }}
> >
{font.text} <Ellipsify>{font.text}</Ellipsify>
{font.badge && ( {font.badge && (
<DropDownMenuItemBadge type={font.badge.type}> <FontPickerListItem.Badge type={font.badge.type}>
{font.badge.placeholder} {font.badge.placeholder}
</DropDownMenuItemBadge> </FontPickerListItem.Badge>
)} )}
</DropdownMenuItem> </FontPickerListItem>
); );
const groups = []; const groups = [];

View File

@@ -0,0 +1,151 @@
import React, { useEffect, useRef } from "react";
import { THEME } from "@excalidraw/common";
import type { ValueOf } from "@excalidraw/common/utility-types";
import { Button } from "../Button";
import { useExcalidrawAppState } from "../App";
import { useDevice } from "../App";
import { getDropdownMenuItemClassName } from "../dropdownMenu/common";
import type { JSX } from "react";
const MenuItemContent = ({
textStyle,
icon,
shortcut,
children,
}: {
icon?: React.ReactNode;
shortcut?: string;
textStyle?: React.CSSProperties;
children: React.ReactNode;
}) => {
const device = useDevice();
return (
<>
{icon && <div className="dropdown-menu-item__icon">{icon}</div>}
<div style={textStyle} className="dropdown-menu-item__text">
{children}
</div>
{shortcut && !device.editor.isMobile && (
<div className="dropdown-menu-item__shortcut">{shortcut}</div>
)}
</>
);
};
export const FontPickerListItem = ({
icon,
value,
order,
children,
shortcut,
className,
hovered,
selected,
textStyle,
onSelect,
onClick,
...rest
}: {
icon?: JSX.Element;
value?: string | number | undefined;
order?: number;
onSelect: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
children: React.ReactNode;
shortcut?: string;
hovered?: boolean;
selected?: boolean;
textStyle?: React.CSSProperties;
className?: string;
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect">) => {
const ref = useRef<HTMLButtonElement>(null);
useEffect(() => {
if (hovered) {
if (order === 0) {
// scroll into the first item differently, so it's visible what is above (i.e. group title)
ref.current?.scrollIntoView({ block: "end" });
} else {
ref.current?.scrollIntoView({ block: "nearest" });
}
}
}, [hovered, order]);
return (
<div className="radix-menu-item">
<Button
{...rest}
ref={ref}
onSelect={onSelect}
className={getDropdownMenuItemClassName(className, selected, hovered)}
title={rest.title ?? rest["aria-label"]}
>
<MenuItemContent textStyle={textStyle} icon={icon} shortcut={shortcut}>
{children}
</MenuItemContent>
</Button>
</div>
);
};
FontPickerListItem.displayName = "FontPickerListItem";
export const FontPickerListItemBadgeType = {
GREEN: "green",
RED: "red",
BLUE: "blue",
} as const;
export const FontPickerListItemBadge = ({
type = FontPickerListItemBadgeType.BLUE,
children,
}: {
type?: ValueOf<typeof FontPickerListItemBadgeType>;
children: React.ReactNode;
}) => {
const { theme } = useExcalidrawAppState();
const style = {
display: "inline-flex",
marginLeft: "auto",
padding: "2px 4px",
borderRadius: 6,
fontSize: 9,
fontFamily: "Cascadia, monospace",
border: theme === THEME.LIGHT ? "1.5px solid white" : "none",
};
switch (type) {
case FontPickerListItemBadgeType.GREEN:
Object.assign(style, {
backgroundColor: "var(--background-color-badge)",
color: "var(--color-badge)",
});
break;
case FontPickerListItemBadgeType.RED:
Object.assign(style, {
backgroundColor: "pink",
color: "darkred",
});
break;
case FontPickerListItemBadgeType.BLUE:
default:
Object.assign(style, {
background: "var(--color-promo)",
color: "var(--color-surface-lowest)",
});
}
return (
<div className="DropDownMenuItemBadge" style={style}>
{children}
</div>
);
};
FontPickerListItemBadge.displayName = "DropdownMenuItemBadge";
FontPickerListItem.Badge = FontPickerListItemBadge;

View File

@@ -238,7 +238,10 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
shortcuts={[getShortcutKey("Enter"), getShortcutKey("Escape")]} shortcuts={[getShortcutKey("Enter"), getShortcutKey("Escape")]}
isOr={true} isOr={true}
/> />
<Shortcut label={t("toolBar.lock")} shortcuts={[KEYS.Q]} /> <Shortcut
label={t("toolBar.lock")}
shortcuts={[getShortcutFromShortcutName("toolLock")]}
/>
<Shortcut <Shortcut
label={t("helpDialog.preventBinding")} label={t("helpDialog.preventBinding")}
shortcuts={[getShortcutKey("CtrlOrCmd")]} shortcuts={[getShortcutKey("CtrlOrCmd")]}

View File

@@ -26,9 +26,9 @@ export const TTDDialogTrigger = ({
setAppState({ openDialog: { name: "ttd", tab: "text-to-diagram" } }); setAppState({ openDialog: { name: "ttd", tab: "text-to-diagram" } });
}} }}
icon={icon ?? brainIcon} icon={icon ?? brainIcon}
badge={<DropdownMenu.Item.Badge>AI</DropdownMenu.Item.Badge>}
> >
{children ?? t("labels.textToDiagram")} {children ?? t("labels.textToDiagram")}
<DropdownMenu.Item.Badge>AI</DropdownMenu.Item.Badge>
</DropdownMenu.Item> </DropdownMenu.Item>
</TTDDialogTriggerTunnel.In> </TTDDialogTriggerTunnel.In>
); );

View File

@@ -7,6 +7,7 @@
.dropdown-menu { .dropdown-menu {
max-width: 16rem; max-width: 16rem;
margin-top: 0.25rem;
&__submenu-trigger { &__submenu-trigger {
&[aria-expanded="true"] { &[aria-expanded="true"] {
@@ -60,6 +61,7 @@
.dropdown-menu-item-base { .dropdown-menu-item-base {
display: flex; display: flex;
padding: 0 0.625rem;
column-gap: 0.625rem; column-gap: 0.625rem;
font-size: 0.875rem; font-size: 0.875rem;
color: var(--color-on-surface); color: var(--color-on-surface);
@@ -125,7 +127,7 @@
border: 1px solid transparent; border: 1px solid transparent;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
border-radius: var(--border-radius-sm); border-radius: var(--border-radius-md);
flex: 1 0 auto; flex: 1 0 auto;
@media screen and (min-width: 1921px) { @media screen and (min-width: 1921px) {

View File

@@ -1,5 +1,7 @@
import React from "react"; import React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import DropdownMenuContent from "./DropdownMenuContent"; import DropdownMenuContent from "./DropdownMenuContent";
import DropdownMenuGroup from "./DropdownMenuGroup"; import DropdownMenuGroup from "./DropdownMenuGroup";
import DropdownMenuItem from "./DropdownMenuItem"; import DropdownMenuItem from "./DropdownMenuItem";
@@ -14,8 +16,6 @@ import {
import "./DropdownMenu.scss"; import "./DropdownMenu.scss";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
const DropdownMenu = ({ const DropdownMenu = ({
children, children,
open, open,

View File

@@ -3,6 +3,8 @@ import React, { useEffect, useRef } from "react";
import { EVENT, KEYS } from "@excalidraw/common"; import { EVENT, KEYS } from "@excalidraw/common";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { useOutsideClick } from "../../hooks/useOutsideClick"; import { useOutsideClick } from "../../hooks/useOutsideClick";
import { useStable } from "../../hooks/useStable"; import { useStable } from "../../hooks/useStable";
import { useDevice } from "../App"; import { useDevice } from "../App";
@@ -11,8 +13,6 @@ import Stack from "../Stack";
import { DropdownMenuContentPropsContext } from "./common"; import { DropdownMenuContentPropsContext } from "./common";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
const MenuContent = ({ const MenuContent = ({
children, children,
onClickOutside, onClickOutside,
@@ -89,7 +89,7 @@ const MenuContent = ({
) : ( ) : (
<Island <Island
className="dropdown-menu-container" className="dropdown-menu-container"
padding={1} padding={2}
style={{ zIndex: 2 }} style={{ zIndex: 2 }}
> >
{children} {children}

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef } from "react"; import React, { useRef } from "react";
import { THEME } from "@excalidraw/common"; import { THEME } from "@excalidraw/common";
@@ -22,53 +22,41 @@ import type { JSX } from "react";
const DropdownMenuItem = ({ const DropdownMenuItem = ({
icon, icon,
value, value,
badge,
order, order,
children, children,
shortcut, shortcut,
className, className,
hovered,
selected, selected,
textStyle,
onSelect, onSelect,
onClick, onClick,
...rest ...rest
}: { }: {
icon?: JSX.Element; icon?: JSX.Element;
badge?: React.ReactNode;
value?: string | number | undefined; value?: string | number | undefined;
order?: number; order?: number;
onSelect?: (event: Event) => void; onSelect?: (event: Event) => void;
children: React.ReactNode; children: React.ReactNode;
shortcut?: string; shortcut?: string;
hovered?: boolean;
selected?: boolean; selected?: boolean;
textStyle?: React.CSSProperties;
className?: string; className?: string;
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect">) => { } & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect">) => {
const handleClick = useHandleDropdownMenuItemClick(onClick, onSelect); const handleClick = useHandleDropdownMenuItemClick(onClick, onSelect);
const ref = useRef<HTMLButtonElement>(null); const ref = useRef<HTMLButtonElement>(null);
useEffect(() => {
if (hovered) {
if (order === 0) {
// scroll into the first item differently, so it's visible what is above (i.e. group title)
ref.current?.scrollIntoView({ block: "end" });
} else {
ref.current?.scrollIntoView({ block: "nearest" });
}
}
}, [hovered, order]);
return ( return (
<DropdownMenuPrimitive.Item className="radix-menu-item"> <DropdownMenuPrimitive.Item className="radix-menu-item">
<Button <Button
{...rest} {...rest}
ref={ref} ref={ref}
// onClick={handleClick}
onSelect={handleClick} onSelect={handleClick}
className={getDropdownMenuItemClassName(className, selected, hovered)} className={getDropdownMenuItemClassName(className)}
title={rest.title ?? rest["aria-label"]} title={rest.title ?? rest["aria-label"]}
> >
<MenuItemContent textStyle={textStyle} icon={icon} shortcut={shortcut}> <MenuItemContent icon={icon} shortcut={shortcut} badge={badge}>
{children} {children}
</MenuItemContent> </MenuItemContent>
</Button> </Button>

View File

@@ -2,25 +2,24 @@ import { useDevice } from "../App";
import { Ellipsify } from "../Ellipsify"; import { Ellipsify } from "../Ellipsify";
import type { JSX } from "react";
const MenuItemContent = ({ const MenuItemContent = ({
textStyle,
icon, icon,
badge,
shortcut, shortcut,
children, children,
}: { }: {
icon?: JSX.Element; icon?: React.ReactNode;
shortcut?: string; shortcut?: string;
textStyle?: React.CSSProperties;
children: React.ReactNode; children: React.ReactNode;
badge?: React.ReactNode;
}) => { }) => {
const device = useDevice(); const device = useDevice();
return ( return (
<> <>
{icon && <div className="dropdown-menu-item__icon">{icon}</div>} {icon && <div className="dropdown-menu-item__icon">{icon}</div>}
<div style={textStyle} className="dropdown-menu-item__text"> <div className="dropdown-menu-item__text">
<Ellipsify>{children}</Ellipsify> <Ellipsify>{children}</Ellipsify>
{badge}
</div> </div>
{shortcut && !device.editor.isMobile && ( {shortcut && !device.editor.isMobile && (
<div className="dropdown-menu-item__shortcut">{shortcut}</div> <div className="dropdown-menu-item__shortcut">{shortcut}</div>

View File

@@ -8,8 +8,6 @@ import {
useHandleDropdownMenuItemClick, useHandleDropdownMenuItemClick,
} from "./common"; } from "./common";
import type { JSX } from "react";
const DropdownMenuSubItem = ({ const DropdownMenuSubItem = ({
icon, icon,
onSelect, onSelect,
@@ -18,7 +16,7 @@ const DropdownMenuSubItem = ({
className, className,
...rest ...rest
}: { }: {
icon?: JSX.Element; icon?: React.ReactNode;
onSelect: (event: Event) => void; onSelect: (event: Event) => void;
children: React.ReactNode; children: React.ReactNode;
shortcut?: string; shortcut?: string;
@@ -30,8 +28,7 @@ const DropdownMenuSubItem = ({
<DropdownMenuPrimitive.Item className="radix-menu-item"> <DropdownMenuPrimitive.Item className="radix-menu-item">
<Button <Button
{...rest} {...rest}
onClick={handleClick} onSelect={handleClick}
onSelect={() => {}}
type="button" type="button"
className={getDropdownMenuItemClassName(className)} className={getDropdownMenuItemClassName(className)}
title={rest.title ?? rest["aria-label"]} title={rest.title ?? rest["aria-label"]}

View File

@@ -1,8 +1,9 @@
import clsx from "clsx"; import clsx from "clsx";
import { useDevice } from "../App";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { useDevice } from "../App";
const MenuTrigger = ({ const MenuTrigger = ({
className = "", className = "",
children, children,

View File

@@ -2278,3 +2278,21 @@ export const elementLinkIcon = createIcon(
</g>, </g>,
tablerIconProps, tablerIconProps,
); );
export const settingsIcon = createIcon(
<g strokeWidth={1.25}>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M14 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M4 6l8 0" />
<path d="M16 6l4 0" />
<path d="M8 12m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M4 12l2 0" />
<path d="M10 12l10 0" />
<path d="M17 18m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M4 18l11 0" />
<path d="M19 18l1 0" />
</g>,
tablerIconProps,
);
export const emptyIcon = <div style={{ width: "1rem", height: "1rem" }} />;

View File

@@ -9,8 +9,11 @@ import {
actionLoadScene, actionLoadScene,
actionSaveToActiveFile, actionSaveToActiveFile,
actionShortcuts, actionShortcuts,
actionToggleGridMode,
actionToggleObjectsSnapMode,
actionToggleSearchMenu, actionToggleSearchMenu,
actionToggleTheme, actionToggleTheme,
actionToggleZenMode,
} from "../../actions"; } from "../../actions";
import { getShortcutFromShortcutName } from "../../actions/shortcuts"; import { getShortcutFromShortcutName } from "../../actions/shortcuts";
import { trackEvent } from "../../analytics"; import { trackEvent } from "../../analytics";
@@ -23,13 +26,23 @@ import {
useExcalidrawActionManager, useExcalidrawActionManager,
useExcalidrawElements, useExcalidrawElements,
useAppProps, useAppProps,
useApp,
} from "../App"; } from "../App";
import { openConfirmModal } from "../OverwriteConfirm/OverwriteConfirmState"; import { openConfirmModal } from "../OverwriteConfirm/OverwriteConfirmState";
import Trans from "../Trans"; import Trans from "../Trans";
import DropdownMenuItem from "../dropdownMenu/DropdownMenuItem"; import DropdownMenuItem from "../dropdownMenu/DropdownMenuItem";
import DropdownMenuItemContentRadio from "../dropdownMenu/DropdownMenuItemContentRadio"; import DropdownMenuItemContentRadio from "../dropdownMenu/DropdownMenuItemContentRadio";
import DropdownMenuItemLink from "../dropdownMenu/DropdownMenuItemLink"; import DropdownMenuItemLink from "../dropdownMenu/DropdownMenuItemLink";
import { GithubIcon, DiscordIcon, XBrandIcon } from "../icons"; import DropdownMenuSub from "../dropdownMenu/DropdownMenuSub";
import { actionToggleViewMode } from "../../actions/actionToggleViewMode";
import {
GithubIcon,
DiscordIcon,
XBrandIcon,
settingsIcon,
checkIcon,
emptyIcon,
} from "../icons";
import { import {
boltIcon, boltIcon,
DeviceDesktopIcon, DeviceDesktopIcon,
@@ -396,3 +409,73 @@ export const LiveCollaborationTrigger = ({
}; };
LiveCollaborationTrigger.displayName = "LiveCollaborationTrigger"; LiveCollaborationTrigger.displayName = "LiveCollaborationTrigger";
export const Preferences = ({ children }: { children?: React.ReactNode }) => {
const { t } = useI18n();
const actionManager = useExcalidrawActionManager();
const appState = useUIAppState();
const app = useApp();
return (
<DropdownMenuSub>
<DropdownMenuSub.Trigger icon={settingsIcon}>
{t("labels.preferences")}
</DropdownMenuSub.Trigger>
<DropdownMenuSub.Content className="excalidraw-main-menu-preferences-submenu">
<DropdownMenuSub.Item
icon={appState.activeTool.locked ? checkIcon : emptyIcon}
shortcut={getShortcutFromShortcutName("toolLock")}
onSelect={(event) => {
app.toggleLock();
event.preventDefault();
}}
>
{t("labels.preferences_toolLock")}
</DropdownMenuSub.Item>
<DropdownMenuSub.Item
icon={appState.objectsSnapModeEnabled ? checkIcon : emptyIcon}
shortcut={getShortcutFromShortcutName("objectsSnapMode")}
onSelect={(event) => {
actionManager.executeAction(actionToggleObjectsSnapMode);
event.preventDefault();
}}
>
{t("buttons.objectsSnapMode")}
</DropdownMenuSub.Item>
<DropdownMenuSub.Item
icon={appState.gridModeEnabled ? checkIcon : emptyIcon}
shortcut={getShortcutFromShortcutName("gridMode")}
onSelect={(event) => {
actionManager.executeAction(actionToggleGridMode);
event.preventDefault();
}}
>
{t("labels.toggleGrid")}
</DropdownMenuSub.Item>
<DropdownMenuSub.Item
icon={appState.zenModeEnabled ? checkIcon : emptyIcon}
shortcut={getShortcutFromShortcutName("zenMode")}
onSelect={(event) => {
actionManager.executeAction(actionToggleZenMode);
event.preventDefault();
}}
>
{t("buttons.zenMode")}
</DropdownMenuSub.Item>
<DropdownMenuSub.Item
icon={appState.viewModeEnabled ? checkIcon : emptyIcon}
shortcut={getShortcutFromShortcutName("viewMode")}
onSelect={(event) => {
actionManager.executeAction(actionToggleViewMode);
event.preventDefault();
}}
>
{t("labels.viewMode")}
</DropdownMenuSub.Item>
{children}
</DropdownMenuSub.Content>
</DropdownMenuSub>
);
};
Preferences.displayName = "Preferences";

View File

@@ -171,7 +171,9 @@
"linkToElement": "Link to object", "linkToElement": "Link to object",
"wrapSelectionInFrame": "Wrap selection in frame", "wrapSelectionInFrame": "Wrap selection in frame",
"tab": "Tab", "tab": "Tab",
"shapeSwitch": "Switch shape" "shapeSwitch": "Switch shape",
"preferences": "Preferences",
"preferences_toolLock": "Tool lock"
}, },
"elementLink": { "elementLink": {
"title": "Link to object", "title": "Link to object",