Building Search Input
SearchInput.tsx |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | import { Input, InputGroup, InputLeftElement } from "@chakra-ui/react";
import { BsSearch } from "react-icons/bs";
const SearchInput = () => {
return (
<InputGroup>
<InputLeftElement children={<BsSearch />} />
<Input
borderRadius={20}
placeholder="Search games..."
variant="filled"
/>
</InputGroup>
);
};
export default SearchInput;
|
添加到 NavBar.tsx
,其中 <HStack>
删除了 justifyContent="space-between"
,因为现在添加了搜索组件,会占据剩余空间,所以样式没区别:
NavBar.tsx |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | import { HStack, Image } from "@chakra-ui/react";
import logo from "../assets/logo.webp";
import ColorModeSwitch from "./ColorModeSwitch";
import SearchInput from "./SearchInput";
const NavBar = () => {
return (
<HStack padding="10px">
<Image src={logo} boxSize="60px" />
<SearchInput />
<ColorModeSwitch />
</HStack>
);
};
export default NavBar;
|
其中主题切换组件的文字换行了,需要修复一下样式,使用原生的 CSS 即可实现:
ColorModeSwitch.tsx |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | import { HStack, Switch, Text, useColorMode } from "@chakra-ui/react";
const ColorModeSwitch = () => {
const { toggleColorMode, colorMode } = useColorMode();
return (
<HStack>
<Switch
colorScheme="green"
isChecked={colorMode === "dark"}
onChange={toggleColorMode}
/>
{/* fix the text wrap after adding the search input */}
<Text whiteSpace="nowrap">Dark Mode</Text>
</HStack>
);
};
export default ColorModeSwitch;
|
Searching Games
SearchInput
将组件用 form
标签包裹,监听 onSubmit
事件并使用 useRef
获取输入的值,并使用 onSearch
函数
SearchInput.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
36
37
38
39
40
41
42
43
44
45 | // 从 @chakra-ui/react 库中导入 Input, InputGroup 和 InputLeftElement 组件
import { Input, InputGroup, InputLeftElement } from "@chakra-ui/react";
// 从 react 库中导入 useRef hook
import { useRef } from "react";
// 从 react-icons 库中导入搜索图标 BsSearch
import { BsSearch } from "react-icons/bs";
// 定义 Props 接口,接受一个 onSearch 函数作为参数
interface Props {
onSearch: (searchText: string) => void; // onSearch 是一个接收字符串并无返回值的函数
}
// 定义 SearchInput 组件,接受 onSearch 作为属性
const SearchInput = ({ onSearch }: Props) => {
// 使用 useRef 创建一个 ref,用来引用 HTML 输入元素
const ref = useRef<HTMLInputElement>(null);
// 返回组件的 JSX 结构
return (
// 表单元素,onSubmit 事件处理函数拦截表单提交并调用 onSearch 函数
<form
onSubmit={(event) => {
event.preventDefault(); // 阻止表单默认提交行为
if (ref.current) onSearch(ref.current.value); // 如果 ref 存在,调用 onSearch 并传入输入框的值
}}
>
{/* 使用 InputGroup 将输入框和搜索图标包装在一起 */}
<InputGroup>
{/* 在输入框左边添加搜索图标 */}
<InputLeftElement children={<BsSearch />} />
{/* 定义搜索输入框 */}
<Input
ref={ref} // 绑定 ref,使其可以获取输入框的值
borderRadius={20} // 设置输入框的圆角
placeholder="Search games..." // 输入框提示文字
variant="filled" // 使用填充样式的输入框
/>
</InputGroup>
</form>
);
};
export default SearchInput;
|
因为需要接收输入框的值作为参数去请求接口返回数据,因此需要向上传递到 App.tsx
。
NavBar.tsx |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | import { HStack, Image } from "@chakra-ui/react";
import logo from "../assets/logo.webp";
import ColorModeSwitch from "./ColorModeSwitch";
import SearchInput from "./SearchInput";
interface Props {
onSearch: (searchText: string) => void;
}
const NavBar = ({ onSearch }: Props) => {
return (
<HStack padding="10px">
<Image src={logo} boxSize="60px" />
<SearchInput onSearch={onSearch} />
<ColorModeSwitch />
</HStack>
);
};
export default NavBar;
|
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 | import { Grid, GridItem, HStack, Show } from "@chakra-ui/react";
import NavBar from "./components/NavBar";
import GameGrid from "./components/GameGrid";
import GenreList from "./components/GenreList";
import { useState } from "react";
import { Genre } from "./hooks/useGenres";
import PlatfromSelector from "./components/PlatfromSelector";
import { Platform } from "./hooks/useGames";
import SortSelector from "./components/SortSelector";
export interface GameQuery {
genre: Genre | null;
platform: Platform | null;
sortOrder: string;
searchText: string;
}
function App() {
const [gameQuery, setGameQuery] = useState<GameQuery>({} as GameQuery);
return (
<Grid
templateAreas={{
base: `"nav" "main"`,
lg: `"nav nav" "aside main"`,
}}
templateColumns={{
base: "1fr",
lg: "250px 1fr",
}}
>
<GridItem area="nav">
<NavBar
onSearch={(searchText) =>
setGameQuery({ ...gameQuery, searchText })
}
/>
</GridItem>
<Show above="lg">
<GridItem area="aside" paddingX={5}>
<GenreList
onSelectGenre={(genre) =>
setGameQuery({ ...gameQuery, genre })
}
selectedGenre={gameQuery.genre}
/>
</GridItem>
</Show>
<GridItem area="main">
<HStack spacing={5} paddingLeft={2} marginBottom={5}>
<PlatfromSelector
onSelectPlatform={(platform) =>
setGameQuery({ ...gameQuery, platform })
}
selectedPlatform={gameQuery.platform}
/>
<SortSelector
onSelectSortOrder={(sortOrder) =>
setGameQuery({ ...gameQuery, sortOrder })
}
sortOrder={gameQuery.sortOrder}
/>
</HStack>
<GameGrid gameQuery={gameQuery} />
</GridItem>
</Grid>
);
}
export default App;
|
2024-09-08 21:02 2024-09-11 23:39