mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-29 05:29:52 +01:00
Compare commits
2
Commits
1e1040816b
...
b400ff6d07
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b400ff6d07 | ||
|
|
edc3aba545 |
No files matched your search
@@ -0,0 +1,3 @@
|
||||
<svg width="21" height="54" viewBox="0 0 21 54" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21 38L10 49H0L11 38L0 27H10L21 38ZM21 1V21L20 22H10L21 11L10 0H20L21 1ZM10 11L0 21V1L10 11Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 221 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="30" height="52" viewBox="0 0 30 52" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.8564 44V52H0V36L13.8564 44ZM30 52H16.1436V44L30 36V52ZM28.8564 18V34L15 42L1.14355 34V18L15 10L28.8564 18ZM13.8564 8L0 16V0H13.8564V8ZM30 16L16.1436 8V0H30V16Z" fill="#EEEEF0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 294 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="70" height="35" viewBox="0 0 70 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M70 32L64 35H0L70 0V32ZM0 32V0H64L0 32Z" fill="#EEEEF0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 170 B |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -18,6 +18,7 @@ import { useLocale } from "../../hooks/useLocale";
|
||||
import { CompareEphemeral } from "../../stores/compareEphemeral";
|
||||
import type { EquipmentMatrix } from "../../stores/duel";
|
||||
import { BlitzkitButtonGrayIcon } from "../BlitzkitButtonGrayIcon";
|
||||
import { CompareTankCard } from "../CompareTankCard";
|
||||
import { ConsumablesManager } from "../ConsumablesManager";
|
||||
import { CrewSkillManager } from "../CrewSkillManager";
|
||||
import { EquipmentManager } from "../EquipmentManager";
|
||||
@@ -27,7 +28,6 @@ import { StickyColumnHeaderCell } from "../StickyColumnHeaderCell";
|
||||
import { StickyTableRoot } from "../StickyTableRoot";
|
||||
import { Body } from "./Body";
|
||||
import { InsertionMarker } from "./IntersectionMarker";
|
||||
import { TankCard } from "./TankCard";
|
||||
|
||||
interface CompareTableProps {
|
||||
stats: TankCharacteristics[];
|
||||
@@ -77,7 +77,7 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
</StickyColumnHeaderCell>
|
||||
|
||||
{members.map(({ tank, key }, index) => {
|
||||
return <TankCard index={index} key={key} tank={tank} />;
|
||||
return <CompareTankCard index={index} key={key} tank={tank} />;
|
||||
})}
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.name {
|
||||
max-width: 8rem;
|
||||
}
|
||||
+10
-15
@@ -1,20 +1,22 @@
|
||||
import { type TankDefinition, tankIcon } from "@blitzkit/core";
|
||||
import { Flex, Text } from "@radix-ui/themes";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { Vector2 } from "three";
|
||||
import { useLocale } from "../../hooks/useLocale";
|
||||
import { CompareEphemeral } from "../../stores/compareEphemeral";
|
||||
import { InsertionMarker } from "../Compare/IntersectionMarker";
|
||||
import { insertionMarkers } from "../Compare/IntersectionMarker/constants";
|
||||
import { TankControl } from "../Compare/TankControl";
|
||||
import { Flex } from "../Flex";
|
||||
import { StickyColumnHeaderCell } from "../StickyColumnHeaderCell";
|
||||
import { InsertionMarker } from "./IntersectionMarker";
|
||||
import { insertionMarkers } from "./IntersectionMarker/constants";
|
||||
import { TankControl } from "./TankControl";
|
||||
import { Text } from "../Text";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
interface TankCardProps {
|
||||
index: number;
|
||||
tank: TankDefinition;
|
||||
}
|
||||
|
||||
export function TankCard({ index, tank }: TankCardProps) {
|
||||
export function CompareTankCard({ index, tank }: TankCardProps) {
|
||||
const draggable = useRef<HTMLDivElement>(null);
|
||||
const { unwrap } = useLocale();
|
||||
|
||||
@@ -103,12 +105,12 @@ export function TankCard({ index, tank }: TankCardProps) {
|
||||
|
||||
return (
|
||||
<StickyColumnHeaderCell width="0">
|
||||
<Flex direction="column" align="center" justify="between" gap="2">
|
||||
<Flex column align="center" justify="between" gap="2">
|
||||
<TankControl index={index} key={tank.id} slug={tank.slug} />
|
||||
|
||||
<Flex
|
||||
ref={draggable}
|
||||
direction="column"
|
||||
column
|
||||
align="center"
|
||||
justify="between"
|
||||
gap="2"
|
||||
@@ -129,14 +131,7 @@ export function TankCard({ index, tank }: TankCardProps) {
|
||||
}}
|
||||
/>
|
||||
|
||||
<Text
|
||||
style={{
|
||||
whiteSpace: "nowrap",
|
||||
maxWidth: 128,
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
<Text className={styles.name} nowrap overflow="ellipsis">
|
||||
{unwrap(tank.name!)}
|
||||
</Text>
|
||||
</Flex>
|
||||
@@ -1,3 +1,7 @@
|
||||
.flex {
|
||||
display: flex;
|
||||
|
||||
&[data-column="true"] {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,16 @@ import styles from "./index.module.css";
|
||||
|
||||
interface Props extends ComponentProps<"div"> {
|
||||
gap?: Space;
|
||||
column?: boolean;
|
||||
}
|
||||
|
||||
export function Flex({ children, className, gap, ...props }: Props) {
|
||||
export function Flex({ children, column, className, gap, ...props }: Props) {
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
className={classNames(styles.flex, className)}
|
||||
data-gap={gap}
|
||||
data-column={column}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.recent {
|
||||
/* mt="2" mb="6" */
|
||||
}
|
||||
+14
-10
@@ -1,16 +1,20 @@
|
||||
import { isEqual } from "lodash-es";
|
||||
import { useMemo } from "react";
|
||||
import { api } from "../../../core/blitzkit/api";
|
||||
import { useLocale } from "../../../hooks/useLocale";
|
||||
import { TankFilters } from "../../../stores/tankFilters";
|
||||
import { TankopediaPersistent } from "../../../stores/tankopediaPersistent";
|
||||
import { TankSort } from "../../../stores/tankopediaSort";
|
||||
import { TankCard } from "../../TankCard";
|
||||
import { TankCardWrapper } from "./TankCardWrapper";
|
||||
import { api } from "../../core/blitzkit/api";
|
||||
import { useLocale } from "../../hooks/useLocale";
|
||||
import { TankFilters } from "../../stores/tankFilters";
|
||||
import { TankopediaPersistent } from "../../stores/tankopediaPersistent";
|
||||
import { TankSort } from "../../stores/tankopediaSort";
|
||||
import { Flex } from "../Flex";
|
||||
import { Separator } from "../Separator";
|
||||
import { TankCard } from "../TankCard";
|
||||
import { TankCardWrapper } from "../TankCardWrapper";
|
||||
import { Text } from "../Text";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
const tankDefinitions = await api.tankDefinitions();
|
||||
|
||||
export function RecentlyViewed() {
|
||||
export function RecentlyViewedTanks() {
|
||||
const filters = TankFilters.use();
|
||||
// non-reactive because it is a little weird that it updates instantly even before the page loads
|
||||
const recentlyViewed = TankopediaPersistent.state.recentlyViewed.filter(
|
||||
@@ -34,7 +38,7 @@ export function RecentlyViewed() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="2" mt="2" mb="6">
|
||||
<Flex column gap="2" className={styles.recent}>
|
||||
<Text color="gray" align="center">
|
||||
{strings.website.common.tank_search.recent}
|
||||
</Text>
|
||||
@@ -44,7 +48,7 @@ export function RecentlyViewed() {
|
||||
))}
|
||||
</TankCardWrapper>
|
||||
|
||||
<Separator size="4" />
|
||||
<Separator />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
.separator {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import styles from "./index.module.css";
|
||||
|
||||
export function Separator() {
|
||||
return <div className={styles.separator} />;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
@keyframes skeleton-animation {
|
||||
0% {
|
||||
background-color: var(--color-1);
|
||||
}
|
||||
50% {
|
||||
background-color: var(--color-2);
|
||||
}
|
||||
100% {
|
||||
background-color: var(--color-1);
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton {
|
||||
--color-1: var(--black-a5);
|
||||
--color-2: var(--black-a4);
|
||||
|
||||
:global(:root.dark) & {
|
||||
--color-1: var(--white-a3);
|
||||
--color-2: var(--white-a2);
|
||||
}
|
||||
|
||||
animation: skeleton-animation var(--duration-subtle) ease infinite;
|
||||
|
||||
border-radius: var(--space-2);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { ComponentProps } from "react";
|
||||
import { classNames } from "../../ui/classNames";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
export interface SkeletonProps extends ComponentProps<"div"> {}
|
||||
|
||||
export function Skeleton({ className, ...props }: SkeletonProps) {
|
||||
return <div className={classNames(styles.skeleton, className)} {...props} />;
|
||||
}
|
||||
@@ -5,8 +5,10 @@ import { api } from "../../core/blitzkit/api";
|
||||
import { useLocale } from "../../hooks/useLocale";
|
||||
import { TankopediaPersistent } from "../../stores/tankopediaPersistent";
|
||||
import { classIcons } from "../ClassIcon";
|
||||
import { Flex } from "../Flex";
|
||||
import { LinkI18n } from "../LinkI18n";
|
||||
import { MAX_RECENTLY_VIEWED } from "../TankSearch/constants";
|
||||
import { Text, type TextProps } from "../Text";
|
||||
import "./index.css";
|
||||
|
||||
type TankCardProps = TextProps & {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.icon {
|
||||
/* height="5rem" */
|
||||
}
|
||||
|
||||
.name {
|
||||
/* height="1em" */
|
||||
}
|
||||
+8
-5
@@ -1,11 +1,14 @@
|
||||
import { memo, useRef } from "react";
|
||||
import { useIntersection } from "../../../hooks/useIntersection";
|
||||
import { useIntersection } from "../../hooks/useIntersection";
|
||||
import { Flex } from "../Flex";
|
||||
import { Skeleton } from "../Skeleton";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
interface SkeletonTankCardProps {
|
||||
onIntersection?: () => void;
|
||||
}
|
||||
|
||||
export const SkeletonTankCard = memo(
|
||||
export const TankCardSkeleton = memo(
|
||||
({ onIntersection }: SkeletonTankCardProps) => {
|
||||
const card = useRef<HTMLDivElement>(null!);
|
||||
|
||||
@@ -14,9 +17,9 @@ export const SkeletonTankCard = memo(
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="2" ref={card}>
|
||||
<Skeleton height="5rem" />
|
||||
<Skeleton height="1em" />
|
||||
<Flex column gap="2" ref={card}>
|
||||
<Skeleton className={styles.icon} />
|
||||
<Skeleton className={styles.name} />
|
||||
</Flex>
|
||||
);
|
||||
},
|
||||
@@ -0,0 +1,7 @@
|
||||
.wrapper {
|
||||
/* py="4"
|
||||
columns="repeat(auto-fill, minmax(7rem, 1fr))"
|
||||
flow="dense"
|
||||
gap="4"
|
||||
gapY="8" */
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { ComponentProps } from "react";
|
||||
import { classNames } from "../../ui/classNames";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
export function TankCardWrapper({
|
||||
className,
|
||||
...props
|
||||
}: ComponentProps<"div">) {
|
||||
return <div className={classNames(styles.wrapper, className)} {...props} />;
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import type {
|
||||
TankopediaSortDirection,
|
||||
} from "../../../stores/tankopediaPersistent";
|
||||
import { TankSort } from "../../../stores/tankopediaSort";
|
||||
import { Button } from "../../Button";
|
||||
import { DropdownMenu } from "../../DropdownMenu";
|
||||
|
||||
interface ItemProps {
|
||||
@@ -41,10 +40,8 @@ export function Sort() {
|
||||
return (
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button variant="surface" color="gray">
|
||||
{strings.website.common.tank_search.sort_dropdown.label}
|
||||
<CaretDownIcon />
|
||||
</Button>
|
||||
{strings.website.common.tank_search.sort_dropdown.label}
|
||||
<CaretDownIcon />
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content align="end">
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
export function TankCardWrapper(props: GridProps) {
|
||||
return (
|
||||
<Grid
|
||||
py="4"
|
||||
columns="repeat(auto-fill, minmax(7rem, 1fr))"
|
||||
flow="dense"
|
||||
gap="4"
|
||||
gapY="8"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -25,14 +25,14 @@ import type { MaybeSkeletonComponentProps } from "../../types/maybeSkeletonCompo
|
||||
import { Callout } from "../Callout";
|
||||
import { ExperimentIcon } from "../ExperimentIcon";
|
||||
import { Link } from "../Link";
|
||||
import { RecentlyViewedTanks } from "../RecentlyViewedTanks";
|
||||
import { TankCardSkeleton } from "../TankCardSkeleton";
|
||||
import { TankCardWrapper } from "../TankCardWrapper";
|
||||
import { TankSearchBar } from "../TankSearchBar";
|
||||
import { TankSearchFilters } from "../TankSearchFilters";
|
||||
import { Text } from "../Text";
|
||||
import { TankSearchCard } from "./components/Card";
|
||||
import { NoResults } from "./components/NoResults";
|
||||
import { RecentlyViewed } from "./components/RecentlyViewed";
|
||||
import { SkeletonTankCard } from "./components/SkeletonTankCard";
|
||||
import { TankCardWrapper } from "./components/TankCardWrapper";
|
||||
import { MAX_RECENTLY_VIEWED } from "./constants";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
@@ -439,7 +439,7 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
|
||||
{!tankFilters.search && !tankFilters.searching && <TankSearchFilters />}
|
||||
|
||||
{!skeleton && !compact && <RecentlyViewed />}
|
||||
{!skeleton && !compact && <RecentlyViewedTanks />}
|
||||
|
||||
<div className={styles.tanks}>
|
||||
<div className={styles.count}>
|
||||
@@ -513,7 +513,7 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
Math.min(PREVIEW_COUNT, sorted.length - loadedCards),
|
||||
(index) => {
|
||||
return (
|
||||
<SkeletonTankCard
|
||||
<TankCardSkeleton
|
||||
key={index}
|
||||
onIntersection={() => {
|
||||
setLoadedCards((state) =>
|
||||
@@ -538,7 +538,7 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
skeleton ? DEFAULT_LOADED_CARDS : 10 + 10 * Math.random(),
|
||||
),
|
||||
(index) => (
|
||||
<SkeletonTankCard key={index} />
|
||||
<TankCardSkeleton key={index} />
|
||||
),
|
||||
)}
|
||||
</TankCardWrapper>
|
||||
|
||||
@@ -21,7 +21,6 @@ import { api } from "../../core/blitzkit/api";
|
||||
import { useLocale } from "../../hooks/useLocale";
|
||||
import { App } from "../../stores/app";
|
||||
import { TankFilters, type CaseType } from "../../stores/tankFilters";
|
||||
import { Button } from "../Button";
|
||||
import { classIcons } from "../ClassIcon";
|
||||
import { DropdownMenu } from "../DropdownMenu";
|
||||
import { Flex } from "../Flex";
|
||||
@@ -216,23 +215,21 @@ function TiersFilter() {
|
||||
return (
|
||||
<DropdownMenu.Root modal={false}>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button color="gray" variant="surface">
|
||||
<Flex gap="1">
|
||||
{tiers.slice(0, MAX_ICONS).map((tier) => (
|
||||
<Text size="minor" key={tier}>
|
||||
{TIER_ROMAN_NUMERALS[tier]}
|
||||
</Text>
|
||||
))}
|
||||
<Flex gap="1">
|
||||
{tiers.slice(0, MAX_ICONS).map((tier) => (
|
||||
<Text size="minor" key={tier}>
|
||||
{TIER_ROMAN_NUMERALS[tier]}
|
||||
</Text>
|
||||
))}
|
||||
|
||||
{tiers.length > MAX_ICONS && (
|
||||
<Text size="minor">
|
||||
{literals(strings.common.units.plus, {
|
||||
value: tiers.length - MAX_ICONS,
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Button>
|
||||
{tiers.length > MAX_ICONS && (
|
||||
<Text size="minor">
|
||||
{literals(strings.common.units.plus, {
|
||||
value: tiers.length - MAX_ICONS,
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
@@ -291,22 +288,20 @@ function NationsFilter() {
|
||||
return (
|
||||
<DropdownMenu.Root modal={false}>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button color="gray" variant="surface">
|
||||
<Flex>
|
||||
{nations.map((nation, index) => (
|
||||
<img
|
||||
key={nation}
|
||||
style={{
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
marginLeft: index > 0 ? "-0.5em" : undefined,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
}}
|
||||
src={alias("api", `/flags/circle/${nation}.webp`)}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
</Button>
|
||||
<Flex>
|
||||
{nations.map((nation, index) => (
|
||||
<img
|
||||
key={nation}
|
||||
style={{
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
marginLeft: index > 0 ? "-0.5em" : undefined,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
}}
|
||||
src={alias("api", `/flags/circle/${nation}.webp`)}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
@@ -375,26 +370,24 @@ function ClassFilter() {
|
||||
return (
|
||||
<DropdownMenu.Root modal={false}>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button color="gray" variant="surface">
|
||||
<Flex>
|
||||
{classes.map((tankClass, index) => {
|
||||
const Icon = classIcons[tankClass];
|
||||
return (
|
||||
<Icon
|
||||
key={tankClass}
|
||||
style={{
|
||||
color: "var(--gray-12)",
|
||||
opacity: 1,
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
marginLeft: index > 0 ? "-0.5em" : undefined,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
</Button>
|
||||
<Flex>
|
||||
{classes.map((tankClass, index) => {
|
||||
const Icon = classIcons[tankClass];
|
||||
return (
|
||||
<Icon
|
||||
key={tankClass}
|
||||
style={{
|
||||
color: "var(--gray-12)",
|
||||
opacity: 1,
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
marginLeft: index > 0 ? "-0.5em" : undefined,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
@@ -455,26 +448,24 @@ function GunTypeFilter() {
|
||||
return (
|
||||
<DropdownMenu.Root modal={false}>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button color="gray" variant="surface">
|
||||
<Flex>
|
||||
{gunTypes.map((gunType) => {
|
||||
const Icon = GUN_TYPE_ICONS[gunType];
|
||||
<Flex>
|
||||
{gunTypes.map((gunType) => {
|
||||
const Icon = GUN_TYPE_ICONS[gunType];
|
||||
|
||||
return (
|
||||
<Icon
|
||||
key={gunType}
|
||||
style={{
|
||||
margin: gunType === "regular" ? "0 -0.125em" : undefined,
|
||||
opacity: 1,
|
||||
color: "var(--gray-12)",
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
</Button>
|
||||
return (
|
||||
<Icon
|
||||
key={gunType}
|
||||
style={{
|
||||
margin: gunType === "regular" ? "0 -0.125em" : undefined,
|
||||
opacity: 1,
|
||||
color: "var(--gray-12)",
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
@@ -533,26 +524,24 @@ function TypeFilter() {
|
||||
return (
|
||||
<DropdownMenu.Root modal={false}>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button color="gray" variant="surface">
|
||||
<Flex gap="1">
|
||||
{types.map((tankType) => {
|
||||
return (
|
||||
<ResearchedIcon
|
||||
style={{
|
||||
color: `var(--${TANK_TYPE_COLORS[tankType]}-${
|
||||
tankType === TankType.TANK_TYPE_RESEARCHABLE ? "12" : "11"
|
||||
})`,
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
opacity: 1,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
}}
|
||||
key={tankType}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
</Button>
|
||||
<Flex gap="1">
|
||||
{types.map((tankType) => {
|
||||
return (
|
||||
<ResearchedIcon
|
||||
style={{
|
||||
color: `var(--${TANK_TYPE_COLORS[tankType]}-${
|
||||
tankType === TankType.TANK_TYPE_RESEARCHABLE ? "12" : "11"
|
||||
})`,
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
opacity: 1,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
}}
|
||||
key={tankType}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
@@ -634,32 +623,20 @@ function IndividualShellFilter({
|
||||
return (
|
||||
<DropdownMenu.Root modal={false}>
|
||||
<DropdownMenu.Trigger>
|
||||
<IconButton
|
||||
variant="surface"
|
||||
style={{
|
||||
borderTopLeftRadius: index === 0 ? undefined : 0,
|
||||
borderBottomLeftRadius: index === 0 ? undefined : 0,
|
||||
borderTopRightRadius: index === 2 ? undefined : 0,
|
||||
borderBottomRightRadius: index === 2 ? undefined : 0,
|
||||
}}
|
||||
color="gray"
|
||||
highContrast
|
||||
>
|
||||
{shells[index] === null && (
|
||||
<Text color="gray" style={{ display: "contents" }}>
|
||||
<MissingShellIcon width="1em" height="1em" />
|
||||
</Text>
|
||||
)}
|
||||
{shells[index] !== null && (
|
||||
<img
|
||||
style={{ width: "1em", height: "1em" }}
|
||||
src={alias(
|
||||
"api",
|
||||
`/icons/shells/${shellTypeIcons[shells[index]]}${premium ? "_premium" : ""}.webp`,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</IconButton>
|
||||
{shells[index] === null && (
|
||||
<Text color="gray" style={{ display: "contents" }}>
|
||||
<MissingShellIcon width="1em" height="1em" />
|
||||
</Text>
|
||||
)}
|
||||
{shells[index] !== null && (
|
||||
<img
|
||||
style={{ width: "1em", height: "1em" }}
|
||||
src={alias(
|
||||
"api",
|
||||
`/icons/shells/${shellTypeIcons[shells[index]]}${premium ? "_premium" : ""}.webp`,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
@@ -849,31 +826,29 @@ function ConsumablesFilter() {
|
||||
return (
|
||||
<DropdownMenu.Root modal={false}>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button color="gray" variant="surface">
|
||||
<Flex>
|
||||
{consumables.slice(0, MAX_ICONS).map((consumable, index) => (
|
||||
<img
|
||||
key={consumable}
|
||||
style={{
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
marginLeft: index > 0 ? "-0.5em" : undefined,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
src={alias("api", `/icons/consumables/${consumable}.webp`)}
|
||||
/>
|
||||
))}
|
||||
<Flex>
|
||||
{consumables.slice(0, MAX_ICONS).map((consumable, index) => (
|
||||
<img
|
||||
key={consumable}
|
||||
style={{
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
marginLeft: index > 0 ? "-0.5em" : undefined,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
src={alias("api", `/icons/consumables/${consumable}.webp`)}
|
||||
/>
|
||||
))}
|
||||
|
||||
{consumables.length > MAX_ICONS && (
|
||||
<Text size="minor" className={styles["overflow-plus"]}>
|
||||
{literals(strings.common.units.plus, {
|
||||
value: consumables.length - MAX_ICONS,
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Button>
|
||||
{consumables.length > MAX_ICONS && (
|
||||
<Text size="minor" className={styles["overflow-plus"]}>
|
||||
{literals(strings.common.units.plus, {
|
||||
value: consumables.length - MAX_ICONS,
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
@@ -943,31 +918,29 @@ function ProvisionsFilter() {
|
||||
return (
|
||||
<DropdownMenu.Root modal={false}>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button color="gray" variant="surface">
|
||||
<Flex>
|
||||
{provisions.slice(0, MAX_ICONS).map((provision, index) => (
|
||||
<img
|
||||
key={provision}
|
||||
style={{
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
marginLeft: index > 0 ? "-0.5em" : undefined,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
src={alias("api", `/icons/provisions/${provision}.webp`)}
|
||||
/>
|
||||
))}
|
||||
<Flex>
|
||||
{provisions.slice(0, MAX_ICONS).map((provision, index) => (
|
||||
<img
|
||||
key={provision}
|
||||
style={{
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
marginLeft: index > 0 ? "-0.5em" : undefined,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
src={alias("api", `/icons/provisions/${provision}.webp`)}
|
||||
/>
|
||||
))}
|
||||
|
||||
{provisions.length > MAX_ICONS && (
|
||||
<Text size="minor" className={styles["overflow-plus"]}>
|
||||
{literals(strings.common.units.plus, {
|
||||
value: provisions.length - MAX_ICONS,
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Button>
|
||||
{provisions.length > MAX_ICONS && (
|
||||
<Text size="minor" className={styles["overflow-plus"]}>
|
||||
{literals(strings.common.units.plus, {
|
||||
value: provisions.length - MAX_ICONS,
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
@@ -1052,31 +1025,29 @@ function GameModeAbilitiesFilter() {
|
||||
return (
|
||||
<DropdownMenu.Root modal={false}>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button color="gray" variant="surface">
|
||||
<Flex>
|
||||
{icons.slice(0, MAX_ICONS).map((icon, index) => (
|
||||
<img
|
||||
key={icon}
|
||||
style={{
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
marginLeft: index > 0 ? "-0.5em" : undefined,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
src={icon}
|
||||
/>
|
||||
))}
|
||||
<Flex>
|
||||
{icons.slice(0, MAX_ICONS).map((icon, index) => (
|
||||
<img
|
||||
key={icon}
|
||||
style={{
|
||||
filter: "drop-shadow(0 0 var(--space-1) var(--black-a11))",
|
||||
marginLeft: index > 0 ? "-0.5em" : undefined,
|
||||
width: "1.25em",
|
||||
height: "1.25em",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
src={icon}
|
||||
/>
|
||||
))}
|
||||
|
||||
{icons.length > MAX_ICONS && (
|
||||
<Text size="minor" className={styles["overflow-plus"]}>
|
||||
{literals(strings.common.units.plus, {
|
||||
value: icons.length - MAX_ICONS,
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Button>
|
||||
{icons.length > MAX_ICONS && (
|
||||
<Text size="minor" className={styles["overflow-plus"]}>
|
||||
{literals(strings.common.units.plus, {
|
||||
value: icons.length - MAX_ICONS,
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
|
||||
@@ -6,8 +6,8 @@ import { api } from "../../core/blitzkit/api";
|
||||
import { filterTanks } from "../../core/blitzkit/filterTanks";
|
||||
import { TankFilters } from "../../stores/tankFilters";
|
||||
import { TierList } from "../../stores/tierList";
|
||||
import { SkeletonTankCard } from "../TankSearch/components/SkeletonTankCard";
|
||||
import { TankCardWrapper } from "../TankSearch/components/TankCardWrapper";
|
||||
import { TankCardWrapper } from "../TankCardWrapper";
|
||||
import { TankCardSkeleton } from "../TankCardSkeleton";
|
||||
import { TierListTile } from "./Tile";
|
||||
|
||||
const [tankDefinitions, gameDefinitions] = await Promise.all([
|
||||
@@ -45,7 +45,7 @@ export function TierListTiles() {
|
||||
|
||||
{times(Math.min(PREVIEW_COUNT, sorted.length - loadedTiles), (index) => {
|
||||
return (
|
||||
<SkeletonTankCard
|
||||
<TankCardSkeleton
|
||||
key={index}
|
||||
onIntersection={() => {
|
||||
setLoadedTiles((state) => Math.min(state + 2, sorted.length));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
import { Section } from "../../../../components/Section";
|
||||
import ToolBanner from "../../../../components/ToolBanner.astro";
|
||||
import { tools } from "../../../../constants/tools";
|
||||
import BaseLayout from "../../../../layouts/BaseLayout.astro";
|
||||
@@ -12,7 +13,9 @@ const { locale } = Astro.props;
|
||||
<BaseLayout {locale} accent="purple">
|
||||
<ToolBanner {locale} tool={tools.tanks} slot="pre-app" />
|
||||
|
||||
<Page {locale} client:only="react">
|
||||
<Page {locale} skeleton slot="fallback" />
|
||||
</Page>
|
||||
<Section>
|
||||
<Page {locale} client:only="react">
|
||||
<Page {locale} skeleton slot="fallback" />
|
||||
</Page>
|
||||
</Section>
|
||||
</BaseLayout>
|
||||
Reference in new issue
Block a user