fix: Cannot ctrl-click to open in a new tab some menu items (#9542)

* wip

* done
This commit is contained in:
Tom Moor
2025-07-04 09:09:55 -04:00
committed by GitHub
parent e21bd23bd8
commit f6d28811e5
10 changed files with 74 additions and 34 deletions

View File

@@ -47,7 +47,7 @@ export const openCollection = createAction({
name: collection.name,
icon: <ColorCollectionIcon collection={collection} />,
section: CollectionSection,
perform: () => history.push(collection.path),
to: collection.path,
}));
},
});

View File

@@ -98,7 +98,7 @@ export const openDocument = createAction({
<DocumentIcon />
),
section: DocumentSection,
perform: () => history.push(item.url),
to: item.url,
}));
},
});
@@ -840,7 +840,7 @@ export const searchDocumentsForQuery = (query: string) =>
analyticsName: "Search documents",
section: DocumentSection,
icon: <SearchIcon />,
perform: () => history.push(searchPath({ query })),
to: searchPath({ query }),
visible: ({ location }) => location.pathname !== searchPath(),
});

View File

@@ -21,7 +21,6 @@ import KeyboardShortcuts from "~/scenes/KeyboardShortcuts";
import { createAction } from "~/actions";
import { NavigationSection, RecentSearchesSection } from "~/actions/sections";
import Desktop from "~/utils/Desktop";
import history from "~/utils/history";
import isCloudHosted from "~/utils/isCloudHosted";
import {
homePath,
@@ -38,7 +37,7 @@ export const navigateToHome = createAction({
section: NavigationSection,
shortcut: ["d"],
icon: <HomeIcon />,
perform: () => history.push(homePath()),
to: homePath(),
visible: ({ location }) => location.pathname !== homePath(),
});
@@ -48,7 +47,7 @@ export const navigateToRecentSearchQuery = (searchQuery: SearchQuery) =>
name: searchQuery.query,
analyticsName: "Navigate to recent search query",
icon: <SearchIcon />,
perform: () => history.push(searchPath({ query: searchQuery.query })),
to: searchPath({ query: searchQuery.query }),
});
export const navigateToDrafts = createAction({
@@ -56,7 +55,7 @@ export const navigateToDrafts = createAction({
analyticsName: "Navigate to drafts",
section: NavigationSection,
icon: <DraftsIcon />,
perform: () => history.push(draftsPath()),
to: draftsPath(),
visible: ({ location }) => location.pathname !== draftsPath(),
});
@@ -65,7 +64,7 @@ export const navigateToSearch = createAction({
analyticsName: "Navigate to search",
section: NavigationSection,
icon: <SearchIcon />,
perform: () => history.push(searchPath()),
to: searchPath(),
visible: ({ location }) => location.pathname !== searchPath(),
});
@@ -75,7 +74,7 @@ export const navigateToArchive = createAction({
section: NavigationSection,
shortcut: ["g", "a"],
icon: <ArchiveIcon />,
perform: () => history.push(archivePath()),
to: archivePath(),
visible: ({ location }) => location.pathname !== archivePath(),
});
@@ -84,7 +83,7 @@ export const navigateToTrash = createAction({
analyticsName: "Navigate to trash",
section: NavigationSection,
icon: <TrashIcon />,
perform: () => history.push(trashPath()),
to: trashPath(),
visible: ({ location }) => location.pathname !== trashPath(),
});
@@ -95,7 +94,7 @@ export const navigateToSettings = createAction({
shortcut: ["g", "s"],
icon: <SettingsIcon />,
visible: () => stores.policies.abilities(stores.auth.team?.id || "").update,
perform: () => history.push(settingsPath()),
to: settingsPath(),
});
export const navigateToWorkspaceSettings = createAction({
@@ -104,7 +103,7 @@ export const navigateToWorkspaceSettings = createAction({
section: NavigationSection,
icon: <SettingsIcon />,
visible: () => stores.policies.abilities(stores.auth.team?.id || "").update,
perform: () => history.push(settingsPath("details")),
to: settingsPath("details"),
});
export const navigateToProfileSettings = createAction({
@@ -113,7 +112,7 @@ export const navigateToProfileSettings = createAction({
section: NavigationSection,
iconInContextMenu: false,
icon: <ProfileIcon />,
perform: () => history.push(settingsPath()),
to: settingsPath(),
});
export const navigateToTemplateSettings = createAction({
@@ -122,7 +121,7 @@ export const navigateToTemplateSettings = createAction({
section: NavigationSection,
iconInContextMenu: false,
icon: <ShapesIcon />,
perform: () => history.push(settingsPath("templates")),
to: settingsPath("templates"),
});
export const navigateToNotificationSettings = createAction({
@@ -131,7 +130,7 @@ export const navigateToNotificationSettings = createAction({
section: NavigationSection,
iconInContextMenu: false,
icon: <EmailIcon />,
perform: () => history.push(settingsPath("notifications")),
to: settingsPath("notifications"),
});
export const navigateToAccountPreferences = createAction({
@@ -140,7 +139,7 @@ export const navigateToAccountPreferences = createAction({
section: NavigationSection,
iconInContextMenu: false,
icon: <SettingsIcon />,
perform: () => history.push(settingsPath("preferences")),
to: settingsPath("preferences"),
});
export const openDocumentation = createAction({
@@ -149,7 +148,10 @@ export const openDocumentation = createAction({
section: NavigationSection,
iconInContextMenu: false,
icon: <OpenIcon />,
perform: () => window.open(UrlHelper.guide),
to: {
url: UrlHelper.guide,
target: "_blank",
},
});
export const openAPIDocumentation = createAction({
@@ -158,7 +160,10 @@ export const openAPIDocumentation = createAction({
section: NavigationSection,
iconInContextMenu: false,
icon: <OpenIcon />,
perform: () => window.open(UrlHelper.developers),
to: {
url: UrlHelper.developers,
target: "_blank",
},
});
export const toggleSidebar = createAction({
@@ -175,14 +180,20 @@ export const openFeedbackUrl = createAction({
section: NavigationSection,
iconInContextMenu: false,
icon: <EmailIcon />,
perform: () => window.open(UrlHelper.contact),
to: {
url: UrlHelper.contact,
target: "_blank",
},
});
export const openBugReportUrl = createAction({
name: ({ t }) => t("Report a bug"),
analyticsName: "Open bug report",
section: NavigationSection,
perform: () => window.open(UrlHelper.github),
to: {
url: UrlHelper.github,
target: "_blank",
},
});
export const openChangelog = createAction({
@@ -191,7 +202,10 @@ export const openChangelog = createAction({
section: NavigationSection,
iconInContextMenu: false,
icon: <OpenIcon />,
perform: () => window.open(UrlHelper.changelog),
to: {
url: UrlHelper.changelog,
target: "_blank",
},
});
export const openKeyboardShortcuts = createAction({
@@ -219,8 +233,9 @@ export const downloadApp = createAction({
iconInContextMenu: false,
icon: <BrowserIcon />,
visible: () => !Desktop.isElectron() && isMac() && isCloudHosted,
perform: () => {
window.open("https://desktop.getoutline.com");
to: {
url: "https://desktop.getoutline.com",
target: "_blank",
},
});

View File

@@ -32,7 +32,10 @@ export const switchTeamsList = ({ stores }: { stores: RootStore }) =>
);
},
visible: ({ currentTeamId }: ActionContext) => currentTeamId !== session.id,
perform: () => (window.location.href = session.url),
to: {
url: session.url,
target: "_self",
},
})) ?? [];
export const switchTeam = createAction({

View File

@@ -6,6 +6,8 @@ import {
Action,
ActionContext,
CommandBarAction,
MenuExternalLink,
MenuInternalLink,
MenuItemButton,
MenuItemWithChildren,
} from "~/types";
@@ -31,7 +33,6 @@ export function createAction(definition: Optional<Action, "id">): Action {
: "contextmenu",
});
}
return definition.perform?.(context);
}
: undefined,
@@ -42,7 +43,7 @@ export function createAction(definition: Optional<Action, "id">): Action {
export function actionToMenuItem(
action: Action,
context: ActionContext
): MenuItemButton | MenuItemWithChildren {
): MenuItemButton | MenuExternalLink | MenuInternalLink | MenuItemWithChildren {
const resolvedIcon = resolve<React.ReactElement<any>>(action.icon, context);
const resolvedChildren = resolve<Action[]>(action.children, context);
const visible = action.visible ? action.visible(context) : true;
@@ -67,6 +68,26 @@ export function actionToMenuItem(
};
}
if (action.to) {
return typeof action.to === "string"
? {
type: "route",
title,
icon,
visible,
to: action.to,
selected: action.selected?.(context),
}
: {
type: "link",
title,
icon,
visible,
href: action.to,
selected: action.selected?.(context),
};
}
return {
type: "button",
title,

View File

@@ -4,7 +4,6 @@ import Icon from "@shared/components/Icon";
import { createAction } from "~/actions";
import { RecentSection } from "~/actions/sections";
import useStores from "~/hooks/useStores";
import history from "~/utils/history";
import { documentPath } from "~/utils/routeHelpers";
const useRecentDocumentActions = (count = 6) => {
@@ -25,7 +24,7 @@ const useRecentDocumentActions = (count = 6) => {
) : (
<DocumentIcon />
),
perform: () => history.push(documentPath(item)),
to: documentPath(item),
})
),
[count, ui.activeDocumentId, documents.recentlyViewed]

View File

@@ -3,7 +3,6 @@ import { useMemo } from "react";
import { createAction } from "~/actions";
import { NavigationSection } from "~/actions/sections";
import useSettingsConfig from "~/hooks/useSettingsConfig";
import history from "~/utils/history";
const useSettingsAction = () => {
const config = useSettingsConfig();
@@ -16,7 +15,7 @@ const useSettingsAction = () => {
name: item.name,
icon: <Icon />,
section: NavigationSection,
perform: () => history.push(item.path),
to: item.path,
};
}),
[config]

View File

@@ -20,7 +20,7 @@ type Props = {
dangerous?: boolean;
to?: LocationDescriptor;
href?: string;
target?: "_blank";
target?: string;
as?: string | React.ComponentType<any>;
hide?: () => void;
level?: number;

View File

@@ -155,12 +155,14 @@ function Template({ items, actions, context, showIcons, ...menu }: Props) {
return (
<MenuItem
id={`${item.title}-${index}`}
href={item.href}
href={typeof item.href === "string" ? item.href : item.href.url}
key={`${item.type}-${item.title}-${index}`}
disabled={item.disabled}
selected={item.selected}
level={item.level}
target={item.href.startsWith("#") ? undefined : "_blank"}
target={
typeof item.href === "string" ? undefined : item.href.target
}
icon={showIcons !== false ? item.icon : undefined}
{...menu}
>

View File

@@ -65,7 +65,7 @@ export type MenuInternalLink = {
export type MenuExternalLink = {
type: "link";
title: React.ReactNode;
href: string;
href: string | { url: string; target?: string };
visible?: boolean;
selected?: boolean;
disabled?: boolean;
@@ -117,6 +117,7 @@ export type Action = {
* instead. Errors will be caught and displayed to the user as a toast message.
*/
perform?: (context: ActionContext) => any;
to?: string | { url: string; target?: string };
children?: ((context: ActionContext) => Action[]) | Action[];
};