mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
1 parent
ee1ecb83b7
commit
7b650bf39b
5 files changed
+62
-36
No files matched your search
@@ -1,12 +1,12 @@
|
||||
@keyframes skeleton-animation {
|
||||
0% {
|
||||
background-color: var(--black-a8);
|
||||
background-color: var(--black-a6);
|
||||
}
|
||||
50% {
|
||||
background-color: var(--black-a5);
|
||||
}
|
||||
100% {
|
||||
background-color: var(--black-a8);
|
||||
background-color: var(--black-a6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,19 +31,17 @@
|
||||
|
||||
background-size: 140%;
|
||||
background-position: 40% center;
|
||||
|
||||
.cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(transparent 75%, var(--gray-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.skeleton {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,10 @@ import { api } from "../../core/api/dynamic";
|
||||
import { useAwait } from "../../hooks/useAwait";
|
||||
import { useGameStrings } from "../../hooks/useGameStrings";
|
||||
import { useLocale } from "../../hooks/useLocale";
|
||||
import type { PropsWithSkeleton } from "../../types/propsWithSkeleton";
|
||||
import { InlineSkeleton } from "../InlineSkeleton";
|
||||
import { LinkI18n } from "../LinkI18n";
|
||||
import { Skeleton } from "../Skeleton";
|
||||
import { Text } from "../Text";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
@@ -11,37 +14,46 @@ interface TankCardProps {
|
||||
subtitle?: string;
|
||||
}
|
||||
|
||||
export function TankCard({ id }: TankCardProps) {
|
||||
const tank = useAwait(() => api.tank(id), `tank-${id}`);
|
||||
const gameStrings = useGameStrings("TankEntity");
|
||||
export function TankCard(props: PropsWithSkeleton<TankCardProps>) {
|
||||
const tank = props.skeleton
|
||||
? undefined
|
||||
: useAwait(() => api.tank(props.id), `tank-${props.id}`);
|
||||
const gameStrings = props.skeleton ? undefined : useGameStrings("TankEntity");
|
||||
const locale = useLocale();
|
||||
|
||||
return (
|
||||
<LinkI18n
|
||||
href={`/tanks/${tank.slug}`}
|
||||
href={props.skeleton ? undefined : `/tanks/${tank!.slug}`}
|
||||
underline="never"
|
||||
locale={locale}
|
||||
className={styles["tank-card"]}
|
||||
>
|
||||
<div className={styles["icon-wrapper"]}>
|
||||
<div
|
||||
className={styles.flag}
|
||||
style={{
|
||||
backgroundImage: `url(/api/nations/${tank.tank!.nation}/card.webp)`,
|
||||
}}
|
||||
/>
|
||||
{!props.skeleton && (
|
||||
<div
|
||||
className={styles.flag}
|
||||
style={{
|
||||
backgroundImage: `url(/api/nations/${tank!.tank!.nation}/card.webp)`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={styles.icon}
|
||||
style={{
|
||||
backgroundImage: `url(/api/tanks/${id}.webp)`,
|
||||
}}
|
||||
>
|
||||
<div className={styles.cover} />
|
||||
</div>
|
||||
{!props.skeleton && (
|
||||
<div
|
||||
className={styles.icon}
|
||||
style={{
|
||||
backgroundImage: `url(/api/tanks/${props.id}.webp)`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{props.skeleton && <Skeleton className={styles.icon} />}
|
||||
</div>
|
||||
|
||||
<Text className={styles.name}>{gameStrings[tank.tank!.name!.value]}</Text>
|
||||
<Text className={styles.name}>
|
||||
{props.skeleton && <InlineSkeleton className={styles.skeleton} />}
|
||||
{!props.skeleton && gameStrings![tank!.tank!.name!.value]}
|
||||
</Text>
|
||||
</LinkI18n>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import fuzzysort from "fuzzysort";
|
||||
import { useMemo } from "react";
|
||||
import { IncrementalLoader } from "../../../components/IncrementalLoader";
|
||||
import { TankCard } from "../../../components/TankCard";
|
||||
import { TankCards } from "../../../components/TankCards";
|
||||
import { TankSearch } from "../../../components/TankSearch";
|
||||
@@ -41,7 +42,7 @@ function Content() {
|
||||
compareTiers(a.tank!.tier_catalog_id, b.tank!.tier_catalog_id),
|
||||
);
|
||||
|
||||
return list;
|
||||
return list.map(({ id }) => id);
|
||||
}, []);
|
||||
const tanksFiltered = useMemo(() => {
|
||||
if (!search) return tanksOrdered;
|
||||
@@ -50,17 +51,32 @@ function Content() {
|
||||
keys: ["id", "name", "deburred"],
|
||||
});
|
||||
|
||||
return results.map(({ obj }) => obj);
|
||||
return results.map(({ obj }) => obj.id);
|
||||
}, [search]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TankSearch />
|
||||
<TankCards>
|
||||
{tanksFiltered.slice(0, 32).map(({ id }) => (
|
||||
<TankCard key={id} id={id} />
|
||||
))}
|
||||
</TankCards>
|
||||
<List tanks={tanksFiltered} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface ListProps {
|
||||
tanks: string[];
|
||||
}
|
||||
|
||||
function List({ tanks }: ListProps) {
|
||||
const data = useMemo(() => tanks.map((id) => ({ key: id, id })), [tanks]);
|
||||
|
||||
return (
|
||||
<TankCards>
|
||||
<IncrementalLoader
|
||||
initial={7 * 5}
|
||||
intermediate={7 * 3}
|
||||
data={data}
|
||||
Component={TankCard}
|
||||
/>
|
||||
</TankCards>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
export type PropsWithSkeleton<Props> =
|
||||
| { skeleton: true }
|
||||
| { skeleton: true; onIntersection?: () => void }
|
||||
| (Props & { skeleton?: false });
|
||||
Reference in new issue
Block a user