mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-28 14:18:04 -05:00
builds page show loading without layout jumps
This commit is contained in:
32
components/builds/BuildsSkeleton.tsx
Normal file
32
components/builds/BuildsSkeleton.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Flex, Skeleton } from "@chakra-ui/react";
|
||||
|
||||
const BuildsSkeleton = () => (
|
||||
<Flex flexWrap="wrap" justifyContent="center" mt={4}>
|
||||
<Skeleton
|
||||
w="300px"
|
||||
height="500px"
|
||||
rounded="lg"
|
||||
boxShadow="md"
|
||||
p="20px"
|
||||
m={2}
|
||||
/>
|
||||
<Skeleton
|
||||
w="300px"
|
||||
height="500px"
|
||||
rounded="lg"
|
||||
boxShadow="md"
|
||||
p="20px"
|
||||
m={2}
|
||||
/>
|
||||
<Skeleton
|
||||
w="300px"
|
||||
height="500px"
|
||||
rounded="lg"
|
||||
boxShadow="md"
|
||||
p="20px"
|
||||
m={2}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
export default BuildsSkeleton;
|
||||
@@ -110,15 +110,17 @@ export function useBuildsByWeapon() {
|
||||
{ weapon: "", filters: [], expandedUsers: new Set() as Set<number> }
|
||||
);
|
||||
|
||||
const { data = [] } = useSWR<GetBuildsByWeaponData>(() => {
|
||||
const { data } = useSWR<GetBuildsByWeaponData>(() => {
|
||||
if (!state.weapon) return null;
|
||||
|
||||
const key = state.weapon as keyof typeof weaponToCode;
|
||||
return `/api/builds/${weaponToCode[key]}`;
|
||||
});
|
||||
|
||||
const buildArrays = data ?? [];
|
||||
|
||||
const buildsToShow = state.filters.length
|
||||
? data.reduce((acc: GetBuildsByWeaponData, buildArray) => {
|
||||
? buildArrays.reduce((acc: GetBuildsByWeaponData, buildArray) => {
|
||||
const filteredArray = buildArray.filter((build) => {
|
||||
return state.filters.every((filter) => {
|
||||
// @ts-ignore
|
||||
@@ -146,12 +148,13 @@ export function useBuildsByWeapon() {
|
||||
|
||||
return [...acc, filteredArray];
|
||||
}, [])
|
||||
: data;
|
||||
: buildArrays;
|
||||
|
||||
return {
|
||||
data: buildsToShow,
|
||||
isLoading: state.weapon && !data,
|
||||
state,
|
||||
dispatch,
|
||||
hiddenBuildCount: data.length - buildsToShow.length,
|
||||
hiddenBuildCount: buildArrays.length - buildsToShow.length,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Box, Flex } from "@chakra-ui/react";
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import BuildCard from "components/builds/BuildCard";
|
||||
import BuildFilters from "components/builds/BuildFilters";
|
||||
import BuildsSkeleton from "components/builds/BuildsSkeleton";
|
||||
import Breadcrumbs from "components/common/Breadcrumbs";
|
||||
import MyInfiniteScroller from "components/common/MyInfiniteScroller";
|
||||
import WeaponImage from "components/common/WeaponImage";
|
||||
@@ -10,7 +11,13 @@ import { useBuildsByWeapon } from "hooks/builds";
|
||||
import { useMyTheme } from "lib/useMyTheme";
|
||||
|
||||
const BuildsPage = () => {
|
||||
const { data, state, dispatch, hiddenBuildCount } = useBuildsByWeapon();
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
state,
|
||||
dispatch,
|
||||
hiddenBuildCount,
|
||||
} = useBuildsByWeapon();
|
||||
const { themeColorHex } = useMyTheme();
|
||||
return (
|
||||
<>
|
||||
@@ -21,39 +28,44 @@ const BuildsPage = () => {
|
||||
excludeAlt
|
||||
isHeader
|
||||
/>
|
||||
{state.weapon && (data.length > 0 || hiddenBuildCount > 0) && (
|
||||
<>
|
||||
<Box mt={4} pr={3} mb="-5rem">
|
||||
<>
|
||||
<Box mt={4} pr={3} mb="-5rem">
|
||||
{state.weapon ? (
|
||||
<WeaponImage name={state.weapon} size={128} />
|
||||
</Box>
|
||||
<Flex
|
||||
justifyContent="flex-end"
|
||||
p={2}
|
||||
mb={8}
|
||||
w="100%"
|
||||
bg={`linear-gradient(to right, ${themeColorHex}, #f8ffae);`}
|
||||
rounded="lg"
|
||||
fontSize="sm"
|
||||
boxShadow="md"
|
||||
color="black"
|
||||
>
|
||||
<Flex justifyContent="space-between">
|
||||
<Box>
|
||||
{data.length} <Trans>builds</Trans>{" "}
|
||||
{hiddenBuildCount > 0 && (
|
||||
<>
|
||||
(+ {hiddenBuildCount} <Trans>hidden</Trans>)
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
) : (
|
||||
<Box w="135px" h="135px" />
|
||||
)}
|
||||
</Box>
|
||||
<Flex
|
||||
justifyContent="flex-end"
|
||||
p={2}
|
||||
mb={8}
|
||||
w="100%"
|
||||
bg={themeColorHex}
|
||||
rounded="lg"
|
||||
fontSize="sm"
|
||||
boxShadow="md"
|
||||
color="black"
|
||||
>
|
||||
<Flex justifyContent="space-between">
|
||||
<Box visibility={data.length === 0 ? "hidden" : undefined}>
|
||||
{data.length} <Trans>builds</Trans>{" "}
|
||||
{hiddenBuildCount > 0 && (
|
||||
<>
|
||||
(+ {hiddenBuildCount} <Trans>hidden</Trans>)
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
</>
|
||||
|
||||
{state.weapon && (
|
||||
<BuildFilters filters={state.filters} dispatch={dispatch} />
|
||||
)}
|
||||
|
||||
{isLoading && <BuildsSkeleton />}
|
||||
|
||||
<MyInfiniteScroller>
|
||||
{data.flatMap((buildArray) =>
|
||||
state.expandedUsers.has(buildArray[0].userId) ? (
|
||||
|
||||
Reference in New Issue
Block a user