mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-18 21:20:55 -05:00
User builds page dropdown weapon filter Closes #1427
This commit is contained in:
parent
9d9d2ae887
commit
2eb7c9505a
|
|
@ -2,22 +2,22 @@ import { Menu as HeadlessUIMenu, Transition } from "@headlessui/react";
|
|||
import clsx from "clsx";
|
||||
import * as React from "react";
|
||||
|
||||
export function Menu({
|
||||
button,
|
||||
items,
|
||||
className,
|
||||
}: {
|
||||
export interface MenuProps {
|
||||
button: React.ElementType;
|
||||
items: {
|
||||
// type: "button"; TODO: type: "link"
|
||||
text: string;
|
||||
id: string;
|
||||
id: string | number;
|
||||
icon?: React.ReactNode;
|
||||
onClick: () => void;
|
||||
disabled?: boolean;
|
||||
selected?: boolean;
|
||||
}[];
|
||||
className?: string;
|
||||
}) {
|
||||
scrolling?: boolean;
|
||||
}
|
||||
|
||||
export function Menu({ button, items, className, scrolling }: MenuProps) {
|
||||
return (
|
||||
<HeadlessUIMenu as="div" className={clsx("menu-container", className)}>
|
||||
<HeadlessUIMenu.Button as={button} />
|
||||
|
|
@ -30,7 +30,11 @@ export function Menu({
|
|||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<HeadlessUIMenu.Items className="menu__items-container">
|
||||
<HeadlessUIMenu.Items
|
||||
className={clsx("menu__items-container", {
|
||||
"menu-container__scrolling": scrolling,
|
||||
})}
|
||||
>
|
||||
{items.map((item) => {
|
||||
return (
|
||||
<HeadlessUIMenu.Item key={item.id} disabled={item.disabled}>
|
||||
|
|
@ -39,6 +43,7 @@ export function Menu({
|
|||
className={clsx("menu__item", {
|
||||
menu__item__active: active,
|
||||
menu__item__disabled: item.disabled,
|
||||
menu__item__selected: item.selected,
|
||||
})}
|
||||
onClick={item.onClick}
|
||||
data-testid={`menu-item-${item.id}`}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,15 @@ import { BuildCard } from "~/components/BuildCard";
|
|||
import { Button, LinkButton } from "~/components/Button";
|
||||
import { Dialog } from "~/components/Dialog";
|
||||
import { FormMessage } from "~/components/FormMessage";
|
||||
import { WeaponImage } from "~/components/Image";
|
||||
import { Image, WeaponImage } from "~/components/Image";
|
||||
import { Menu, type MenuProps } from "~/components/Menu";
|
||||
import { Popover } from "~/components/Popover";
|
||||
import { SubmitButton } from "~/components/SubmitButton";
|
||||
import { LockIcon } from "~/components/icons/Lock";
|
||||
import { PlusIcon } from "~/components/icons/Plus";
|
||||
import { SortIcon } from "~/components/icons/Sort";
|
||||
import { TrashIcon } from "~/components/icons/Trash";
|
||||
import { UnlockIcon } from "~/components/icons/Unlock";
|
||||
import { BUILD } from "~/constants";
|
||||
import { BUILD_SORT_IDENTIFIERS, type BuildSort } from "~/db/tables";
|
||||
import { useUser } from "~/features/auth/core/user";
|
||||
|
|
@ -20,7 +22,7 @@ import type { MainWeaponId } from "~/modules/in-game-lists";
|
|||
import { mainWeaponIds } from "~/modules/in-game-lists";
|
||||
import { atOrError } from "~/utils/arrays";
|
||||
import type { SendouRouteHandle } from "~/utils/remix";
|
||||
import { userNewBuildPage } from "~/utils/urls";
|
||||
import { userNewBuildPage, weaponCategoryUrl } from "~/utils/urls";
|
||||
import { DEFAULT_BUILD_SORT } from "../user-page-constants";
|
||||
import type { UserPageLoaderData } from "./u.$identifier";
|
||||
|
||||
|
|
@ -153,6 +155,42 @@ function BuildsFilters({
|
|||
const showPublicPrivateFilters =
|
||||
user?.id === parentPageData.id && privateBuildsCount > 0;
|
||||
|
||||
const WeaponFilterMenuButton = React.forwardRef((props, ref) => (
|
||||
<Button
|
||||
variant={typeof weaponFilter === "number" ? undefined : "outlined"}
|
||||
size="tiny"
|
||||
className="u__build-filter-button"
|
||||
{...props}
|
||||
_ref={ref}
|
||||
>
|
||||
<Image
|
||||
path={weaponCategoryUrl("SHOOTERS")}
|
||||
width={24}
|
||||
height={24}
|
||||
alt=""
|
||||
/>
|
||||
{t("builds:filters.filterByWeapon")}
|
||||
</Button>
|
||||
));
|
||||
|
||||
const weaponFilterMenuItems = mainWeaponIds
|
||||
.map((weaponId) => {
|
||||
const count = data.weaponCounts[weaponId];
|
||||
|
||||
if (!count) return null;
|
||||
|
||||
const item: MenuProps["items"][number] = {
|
||||
id: weaponId,
|
||||
text: `${t(`weapons:MAIN_${weaponId}`)} (${count})`,
|
||||
icon: <WeaponImage weaponSplId={weaponId} variant="build" size={18} />,
|
||||
onClick: () => setWeaponFilter(weaponId),
|
||||
selected: weaponFilter === weaponId,
|
||||
};
|
||||
|
||||
return item;
|
||||
})
|
||||
.filter((item) => item !== null);
|
||||
|
||||
return (
|
||||
<div className="stack horizontal sm flex-wrap">
|
||||
<Button
|
||||
|
|
@ -170,6 +208,7 @@ function BuildsFilters({
|
|||
variant={weaponFilter === "PUBLIC" ? undefined : "outlined"}
|
||||
size="tiny"
|
||||
className="u__build-filter-button"
|
||||
icon={<UnlockIcon />}
|
||||
>
|
||||
{t("builds:stats.public")} ({publicBuildsCount})
|
||||
</Button>
|
||||
|
|
@ -185,24 +224,11 @@ function BuildsFilters({
|
|||
</>
|
||||
) : null}
|
||||
|
||||
{mainWeaponIds.map((weaponId) => {
|
||||
const count = data.weaponCounts[weaponId];
|
||||
|
||||
if (!count) return null;
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={weaponId}
|
||||
onClick={() => setWeaponFilter(weaponId)}
|
||||
variant={weaponFilter === weaponId ? undefined : "outlined"}
|
||||
size="tiny"
|
||||
className="u__build-filter-button"
|
||||
>
|
||||
<WeaponImage weaponSplId={weaponId} variant="build" width={20} />
|
||||
{t(`weapons:MAIN_${weaponId}`)} ({count})
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
<Menu
|
||||
items={weaponFilterMenuItems}
|
||||
button={WeaponFilterMenuButton}
|
||||
scrolling
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1343,6 +1343,14 @@ dialog::backdrop {
|
|||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.menu-container__scrolling {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
scrollbar-color: rgb(83 65 91) transparent;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.menu__item {
|
||||
display: flex;
|
||||
font-size: var(--fonts-xs);
|
||||
|
|
@ -1374,6 +1382,11 @@ dialog::backdrop {
|
|||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.menu__item__selected {
|
||||
background-color: var(--theme-transparent);
|
||||
font-weight: var(--extra-bold);
|
||||
}
|
||||
|
||||
.menu__item__icon {
|
||||
width: 18px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@
|
|||
.u__build-filter-button {
|
||||
padding: 0 var(--s-1-5) !important;
|
||||
gap: var(--s-1);
|
||||
min-height: 24px;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.u__placements {
|
||||
|
|
|
|||
|
|
@ -41,5 +41,6 @@
|
|||
"filters.atLeast": "At least",
|
||||
"filters.atMost": "At most",
|
||||
"filters.date.since": "Since",
|
||||
"filters.date.custom": "Custom"
|
||||
"filters.date.custom": "Custom",
|
||||
"filters.filterByWeapon": "Filter by weapon"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user