mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-23 19:07:56 -05:00
Focus search when going back to weapon landing
This commit is contained in:
@@ -2,7 +2,7 @@ import clsx from "clsx";
|
||||
import { Search as SearchIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router";
|
||||
import { Link, useLocation } from "react-router";
|
||||
import { BackLink } from "~/components/BackLink";
|
||||
import { CircleBackdrop } from "~/components/CircleBackdrop";
|
||||
import { EmptyState } from "~/components/EmptyState";
|
||||
@@ -11,6 +11,7 @@ import { Input } from "~/components/Input";
|
||||
import { PageHeader } from "~/components/PageHeader";
|
||||
import { WeaponPoolBanner } from "~/components/WeaponPoolBanner";
|
||||
import type { AnyWeapon } from "~/features/build-analyzer/analyzer-types";
|
||||
import { DESKTOP_LAYOUT_QUERY } from "~/hooks/useLayoutSize";
|
||||
import type {
|
||||
MainWeaponId,
|
||||
SpecialWeaponId,
|
||||
@@ -41,6 +42,12 @@ type WeaponLandingCategory = NonNullable<
|
||||
|
||||
type SubOrSpecialWeapon = Extract<AnyWeapon, { type: "SUB" | "SPECIAL" }>;
|
||||
|
||||
/** Location state accepted by the weapon landing (e.g. from a weapon page's back link). */
|
||||
export interface WeaponLandingState {
|
||||
/** If set, the search is focused on load on desktop. */
|
||||
focusSearch?: boolean;
|
||||
}
|
||||
|
||||
interface WeaponLandingWeapon {
|
||||
id: MainWeaponId;
|
||||
subWeaponId: SubWeaponId;
|
||||
@@ -82,6 +89,8 @@ export function WeaponLanding<W extends WeaponLandingWeapon>({
|
||||
const { t } = useTranslation(["common", "weapons"]);
|
||||
const [rawCategory] = useSearchParam(weaponLandingSearchParams, "category");
|
||||
const [searchTerm, setSearchTerm] = React.useState("");
|
||||
const searchInputRef = React.useRef<HTMLInputElement>(null);
|
||||
useFocusSearchOnReturn(searchInputRef);
|
||||
|
||||
const categories: WeaponLandingCategory[] = [
|
||||
...weaponCategories.map((weaponCategory) => weaponCategory.name),
|
||||
@@ -198,6 +207,7 @@ export function WeaponLanding<W extends WeaponLandingWeapon>({
|
||||
})}
|
||||
>
|
||||
<Input
|
||||
ref={searchInputRef}
|
||||
className={styles.search}
|
||||
icon={<SearchIcon />}
|
||||
value={searchTerm}
|
||||
@@ -274,6 +284,21 @@ export function WeaponLanding<W extends WeaponLandingWeapon>({
|
||||
);
|
||||
}
|
||||
|
||||
/** Returning from a weapon page on desktop lets the user search for the next weapon right away. */
|
||||
function useFocusSearchOnReturn(ref: React.RefObject<HTMLInputElement | null>) {
|
||||
const location = useLocation();
|
||||
const focusSearch = (location.state as WeaponLandingState | null)
|
||||
?.focusSearch;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!focusSearch || !window.matchMedia(DESKTOP_LAYOUT_QUERY).matches) {
|
||||
return;
|
||||
}
|
||||
|
||||
ref.current?.focus();
|
||||
}, [focusSearch, ref]);
|
||||
}
|
||||
|
||||
function WeaponRow({
|
||||
weapon,
|
||||
href,
|
||||
|
||||
@@ -29,7 +29,10 @@ import { Main } from "~/components/Main";
|
||||
import { PageHeader } from "~/components/PageHeader";
|
||||
import { Placeholder } from "~/components/Placeholder";
|
||||
import { Table } from "~/components/Table";
|
||||
import { WeaponLanding } from "~/components/WeaponLanding";
|
||||
import {
|
||||
WeaponLanding,
|
||||
type WeaponLandingState,
|
||||
} from "~/components/WeaponLanding";
|
||||
import { useUser } from "~/features/auth/core/user";
|
||||
import { objectDamageCalculatorPage } from "~/features/object-damage-calculator/calculator-urls";
|
||||
import { FULL_GROUP_SIZE } from "~/features/sendouq/q-constants";
|
||||
@@ -1122,6 +1125,7 @@ function WeaponHeader({ mainWeaponId }: { mainWeaponId: MainWeaponId }) {
|
||||
weapon: null,
|
||||
category: weaponIdToCategory(mainWeaponId),
|
||||
})}
|
||||
state={{ focusSearch: true } satisfies WeaponLandingState}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { FilterBar } from "~/components/filter-bar/FilterBar";
|
||||
import { Image, ModeImage, WeaponImage } from "~/components/Image";
|
||||
import { Main } from "~/components/Main";
|
||||
import { PageHeader } from "~/components/PageHeader";
|
||||
import type { WeaponLandingState } from "~/components/WeaponLanding";
|
||||
import { weaponLandingSearchParams } from "~/components/weapon-landing-search-params";
|
||||
import { possibleApValues } from "~/features/build-analyzer/analyzer-constants";
|
||||
import { useDateTimeFormat } from "~/hooks/intl/useDateTimeFormat";
|
||||
@@ -145,6 +146,7 @@ export default function WeaponsBuildsPage() {
|
||||
to={weaponLandingSearchParams.href(BUILDS_PAGE, {
|
||||
category: weaponIdToCategory(data.weaponId),
|
||||
})}
|
||||
state={{ focusSearch: true } satisfies WeaponLandingState}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -21,7 +21,10 @@ import {
|
||||
import { Label } from "~/components/Label";
|
||||
import { Main } from "~/components/Main";
|
||||
import { PageHeader } from "~/components/PageHeader";
|
||||
import { WeaponLanding } from "~/components/WeaponLanding";
|
||||
import {
|
||||
WeaponLanding,
|
||||
type WeaponLandingState,
|
||||
} from "~/components/WeaponLanding";
|
||||
import { possibleApValues } from "~/features/build-analyzer/analyzer-constants";
|
||||
import type { AnyWeapon } from "~/features/build-analyzer/analyzer-types";
|
||||
import type {
|
||||
@@ -264,6 +267,7 @@ function WeaponHeader({ weapon }: { weapon: AnyWeapon }) {
|
||||
weapon: null,
|
||||
category: weaponLandingCategory(weapon),
|
||||
})}
|
||||
state={{ focusSearch: true } satisfies WeaponLandingState}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user