Make GlobalSearch actual links so we can ctrl+click for new tab

This commit is contained in:
Kalle
2026-03-24 19:56:44 +02:00
parent 970bb4f04b
commit 0fae97a189
3 changed files with 65 additions and 31 deletions

View File

@@ -131,11 +131,18 @@
}
.listBoxItem {
display: block;
padding: var(--s-2) var(--s-3);
border-radius: var(--radius-field);
cursor: pointer;
font-size: var(--font-sm);
outline: none;
color: inherit;
text-decoration: none;
}
.listBoxItem[data-hovered] {
background-color: var(--color-bg-high);
}
.listBoxItem[data-focused] {

View File

@@ -13,7 +13,7 @@ import {
RadioGroup,
} from "react-aria-components";
import { useTranslation } from "react-i18next";
import { useFetcher, useNavigate, useSearchParams } from "react-router";
import { useFetcher, useSearchParams } from "react-router";
import { useDebounce } from "react-use";
import { Avatar } from "~/components/Avatar";
import { Image } from "~/components/Image";
@@ -33,10 +33,8 @@ import styles from "./GlobalSearch.module.css";
import {
filterWeaponResults,
getRecentWeapons,
getWeaponDestinationUrl,
type SelectedWeapon,
saveRecentWeapon,
type WeaponDestination,
WeaponDestinationMenu,
WeaponResultsList,
} from "./WeaponSearch";
@@ -182,7 +180,6 @@ function GlobalSearchContent({
initialWeaponId: string | null;
}) {
const { t } = useTranslation(["common", "weapons"]);
const navigate = useNavigate();
const [query, setQuery] = React.useState("");
const [searchType, setSearchType] = React.useState<SearchType>(
initialSearchType ?? getInitialSearchType(),
@@ -193,6 +190,11 @@ function GlobalSearchContent({
);
const inputRef = React.useRef<HTMLInputElement>(null);
const listBoxRef = React.useRef<HTMLDivElement>(null);
const modifierKeyRef = React.useRef(false);
const handleClickCapture = (e: React.MouseEvent) => {
modifierKeyRef.current = e.metaKey || e.ctrlKey;
};
const fetcher = useFetcher<SearchLoaderData>();
@@ -254,9 +256,7 @@ function GlobalSearchContent({
return;
}
const result = results.find((r) => getResultKey(r) === key);
if (result) {
navigate(getResultHref(result));
if (!modifierKeyRef.current) {
onClose();
}
};
@@ -274,16 +274,13 @@ function GlobalSearchContent({
}
};
const handleDestinationSelect = (key: React.Key) => {
const handleDestinationSelect = () => {
if (!selectedWeapon) return;
const url = getWeaponDestinationUrl(
key as WeaponDestination,
selectedWeapon,
);
saveRecentWeapon(selectedWeapon.id);
navigate(url);
onClose();
if (!modifierKeyRef.current) {
onClose();
}
};
const handleBackToWeaponSearch = () => {
@@ -292,17 +289,19 @@ function GlobalSearchContent({
if (searchType === "weapons" && selectedWeapon) {
return (
<WeaponDestinationMenu
selectedWeapon={selectedWeapon}
onBack={handleBackToWeaponSearch}
onSelect={handleDestinationSelect}
listBoxRef={listBoxRef}
/>
<div onClickCapture={handleClickCapture}>
<WeaponDestinationMenu
selectedWeapon={selectedWeapon}
onBack={handleBackToWeaponSearch}
onSelect={handleDestinationSelect}
listBoxRef={listBoxRef}
/>
</div>
);
}
return (
<>
<div onClickCapture={handleClickCapture}>
<Input
ref={inputRef}
className={styles.input}
@@ -355,7 +354,6 @@ function GlobalSearchContent({
ref={listBoxRef}
className={clsx(styles.listBox, "scrollbar")}
aria-label={t("common:search")}
selectionMode="single"
onAction={handleSelect}
renderEmptyState={() =>
hasQuery ? (
@@ -371,6 +369,7 @@ function GlobalSearchContent({
<ListBoxItem
key={getResultKey(result)}
id={getResultKey(result)}
href={getResultHref(result)}
className={styles.listBoxItem}
>
<ResultItem result={result} />
@@ -378,7 +377,7 @@ function GlobalSearchContent({
))}
</ListBox>
)}
</>
</div>
);
}

View File

@@ -86,7 +86,7 @@ export function filterWeaponResults(
return matches;
}
export function getWeaponDestinationUrl(
function getWeaponDestinationUrl(
key: WeaponDestination,
weapon: SelectedWeapon,
): string {
@@ -145,7 +145,11 @@ export function WeaponDestinationMenu({
onAction={onSelect}
autoFocus="first"
>
<ListBoxItem id="builds" className={styles.listBoxItem}>
<ListBoxItem
id="builds"
href={getWeaponDestinationUrl("builds", selectedWeapon)}
className={styles.listBoxItem}
>
<div className={styles.resultItem}>
<FlaskConical size={20} />
<span className={styles.resultName}>
@@ -153,7 +157,11 @@ export function WeaponDestinationMenu({
</span>
</div>
</ListBoxItem>
<ListBoxItem id="popular" className={styles.listBoxItem}>
<ListBoxItem
id="popular"
href={getWeaponDestinationUrl("popular", selectedWeapon)}
className={styles.listBoxItem}
>
<div className={styles.resultItem}>
<Flame size={20} />
<span className={styles.resultName}>
@@ -161,7 +169,11 @@ export function WeaponDestinationMenu({
</span>
</div>
</ListBoxItem>
<ListBoxItem id="stats" className={styles.listBoxItem}>
<ListBoxItem
id="stats"
href={getWeaponDestinationUrl("stats", selectedWeapon)}
className={styles.listBoxItem}
>
<div className={styles.resultItem}>
<ChartColumnBig size={20} />
<span className={styles.resultName}>
@@ -169,7 +181,11 @@ export function WeaponDestinationMenu({
</span>
</div>
</ListBoxItem>
<ListBoxItem id="analyzer" className={styles.listBoxItem}>
<ListBoxItem
id="analyzer"
href={getWeaponDestinationUrl("analyzer", selectedWeapon)}
className={styles.listBoxItem}
>
<div className={styles.resultItem}>
<Calculator size={20} />
<span className={styles.resultName}>
@@ -177,19 +193,31 @@ export function WeaponDestinationMenu({
</span>
</div>
</ListBoxItem>
<ListBoxItem id="vods" className={styles.listBoxItem}>
<ListBoxItem
id="vods"
href={getWeaponDestinationUrl("vods", selectedWeapon)}
className={styles.listBoxItem}
>
<div className={styles.resultItem}>
<Videotape size={20} />
<span className={styles.resultName}>{t("common:pages.vods")}</span>
</div>
</ListBoxItem>
<ListBoxItem id="art" className={styles.listBoxItem}>
<ListBoxItem
id="art"
href={getWeaponDestinationUrl("art", selectedWeapon)}
className={styles.listBoxItem}
>
<div className={styles.resultItem}>
<ImageIcon size={20} />
<span className={styles.resultName}>{t("common:pages.art")}</span>
</div>
</ListBoxItem>
<ListBoxItem id="lfg" className={styles.listBoxItem}>
<ListBoxItem
id="lfg"
href={getWeaponDestinationUrl("lfg", selectedWeapon)}
className={styles.listBoxItem}
>
<div className={styles.resultItem}>
<Users size={20} />
<span className={styles.resultName}>{t("common:pages.lfg")}</span>