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