Displaying the Genres
首先 Ctrl+T
找到 Genre 接口,增加接口返回的 image_background
再使用 chakra UI 的列表组件(<List>
,<ListItem>
),这样不会有 bullet point。
为了使图片和名称并列,需要使用 <HStack>
,因此在每个 ListItem
内再增加一个 <HStack>
,并在其中增加 <Image>
和 <Text>
组件。
对于图片大小,可以使用 BoxSize
属性控制尺寸。
![[Pasted image 20240816235731.png]]
现在有两个样式问题,分类列表太靠左和分类项互相之间太靠近。
对于列表太靠左,应当从整个 aside
入手,否则对于侧栏的每一个组件都要添加相同的 padding,因此要把 paddingY
加在 App.tsx
中。
分类项目则增加 <ListItem>
的 paddingX
即可。
GenreList.tsx |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | import { List, ListItem, HStack, Image, Text } from "@chakra-ui/react";
import useGenres from "../hooks/useGenres";
import getCroppedImageUrl from "../services/image-url";
const GenreList = () => {
const { data } = useGenres();
return (
<List>
{data.map((genre) => (
<ListItem key={genre.id} paddingY="5px">
<HStack>
<Image
boxSize="32px"
borderRadius={8}
src={getCroppedImageUrl(genre.image_background)}
></Image>
<Text fontSize="lg">{genre.name}</Text>
</HStack>
</ListItem>
))}
</List>
);
};
export default GenreList;
|
我们还需要给侧边栏一个固定的宽度,否则如果右侧内容没有加载,则侧边栏会占据绝大部分空间,因此需要给一个固定宽度 200px。
App.tsx |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 | import { Grid, GridItem, Show } from "@chakra-ui/react";
import NavBar from "./components/NavBar";
import GameGrid from "./components/GameGrid";
import GenreList from "./components/GenreList";
function App() {
return (
<Grid
templateAreas={{
base: `"nav" "main"`,
lg: `"nav nav" "aside main"`,
}}
templateColumns={{
base: "1fr",
lg: "200px 1fr",
}}
>
<GridItem area="nav">
<NavBar />
</GridItem>
<Show above="lg">
<GridItem area="aside" paddingX={5}>
<GenreList />
</GridItem>
</Show>
<GridItem area="main">
<GameGrid />
</GridItem>
</Grid>
);
}
export default App;
|
然而右侧卡片还是很挤,因为我们给卡片固定宽度 300 px,加上侧边栏 200 px,不算间距已经有 1100 px,而总共就 1024 px,肯定超了,所以需要给卡片移除 300 px 的限制,使其自由发挥。
GameCardContainer.tsx |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | import { Box } from "@chakra-ui/react";
import { ReactNode } from "react";
interface Props {
children: ReactNode;
}
const GameCardContainer = ({ children }: Props) => {
return (
<Box borderRadius={10} overflow="hidden">
{children}
</Box>
);
};
export default GameCardContainer;
|
![[Pasted image 20240817002418.png]]
卡片之间的距离比较宽,再调整一下 spacing(10->3)
GameGrid.tsx |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 | import { Text, SimpleGrid, Skeleton } from "@chakra-ui/react";
import useGames from "../hooks/useGames";
import GameCard from "./GameCard";
import GameCardSkeleton from "./GameCardSkeleton";
import GameCardContainer from "./GameCardContainer";
const GameGrid = () => {
const { data, error, isLoading } = useGames();
const skeletons = [1, 2, 3, 4, 5, 6];
return (
<>
{error && <Text>{error}</Text>}
<SimpleGrid
columns={{ sm: 1, md: 2, lg: 3, xl: 5 }}
padding="10px"
spacing={3}
>
{isLoading &&
skeletons.map((Skeleton) => (
<GameCardContainer>
<GameCardSkeleton key={Skeleton} />
</GameCardContainer>
))}
{data.map((game) => (
<GameCardContainer>
<GameCard key={game.id} game={game} />
</GameCardContainer>
))}
</SimpleGrid>
</>
);
};
export default GameGrid;
|
完美!
![[Pasted image 20240817003150.png]]
之前之所以固定 300 px 的宽度是因为默认情况下侧边栏和内容区各占 50%,在未获取到数据之前,多个骨架屏只在 50% 的空间里显示,与数据加载后的卡片对不齐,所以给了卡片和骨架屏共同的父组件固定的宽度来确保二者显示一致。但是在给侧边栏设置固定宽度后,便不需要这么做了。
GameCardContainer.tsx |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | import { Box } from "@chakra-ui/react";
import { ReactNode } from "react";
interface Props {
children: ReactNode;
}
const GameCardContainer = ({ children }: Props) => {
return (
<Box borderRadius={10} overflow="hidden">
{children}
</Box>
);
};
export default GameCardContainer;
|
Showing a Spinner
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | import { List, ListItem, HStack, Image, Text, Spinner } from "@chakra-ui/react";
import useGenres from "../hooks/useGenres";
import getCroppedImageUrl from "../services/image-url";
const GenreList = () => {
const { data, isLoading, error } = useGenres();
if (error) return null;
if (isLoading) return <Spinner />;
return (
<List>
{data.map((genre) => (
<ListItem key={genre.id} paddingY="5px">
<HStack>
<Image
boxSize="32px"
borderRadius={8}
src={getCroppedImageUrl(genre.image_background)}
></Image>
<Text fontSize="lg">{genre.name}</Text>
</HStack>
</ListItem>
))}
</List>
);
};
export default GenreList;
|
挑战:
试试骨架屏
2024-07-22 01:18 2024-09-13 01:15