8000 Merge branch 'feature/solution-add-page' into dev · codeisneverodd/home@83ade91 · GitHub
[go: up one dir, main page]

Skip to content

Commit 83ade91

Browse files
Merge branch 'feature/solution-add-page' into dev
2 parents ff715d3 + c331449 commit 83ade91

26 files changed

+2266
-573
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ yarn-error.log*
3434
# typescript
3535
*.tsbuildinfo
3636
next-env.d.ts
37+
.env

next.config.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
/** @type {import('next').NextConfig} */
2+
3+
const removeImports = require("next-remove-imports")();
4+
25
const nextConfig = {
3-
reactStrictMode: true,
4-
}
6+
reactStrictMode: true
7+
};
58

6-
module.exports = nextConfig
9+
module.exports = removeImports({
10+
experimental: { esmExternals: true },
11+
...nextConfig
12+
});

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,35 @@
1818
"@fortawesome/free-regular-svg-icons": "^6.3.0",
1919
"@fortawesome/free-solid-svg-icons": "^6.3.0",
2020
"@fortawesome/react-fontawesome": "^0.2.0",
21+
"@octokit/types": "^9.0.0",
2122
"@tanstack/react-query": "^4.24.10",
2223
"@tanstack/react-query-devtools": "^4.26.1",
2324
"@toss/hangul": "^1.2.0",
2425
"@types/node": "18.14.2",
2526
"@types/react": "18.0.28",
2627
"@types/react-dom": "18.0.11",
28+
"@uiw/react-textarea-code-editor": "^2.1.1",
29+
"axios": "^1.3.4",
2730
"eslint": "8.35.0",
2831
"eslint-config-next": "13.2.1",
2932
"framer-motion": "^10.0.0",
3033
"husky": "^8.0.3",
3134
"next": "13.2.1",
35+
"next-auth": "^4.20.1",
36+
"next-remove-imports": "^1.0.10",
3237
"octokit": "^2.0.14",
3338
"react": "18.2.0",
3439
"react-dom": "18.2.0",
3540
"react-intersection-observer": "^9.4.3",
41+
"react-virtualized-auto-sizer": "^1.0.7",
42+
"react-window": "^1.8.8",
3643
"recoil": "^0.7.6",
3744
"typescript": "4.9.5",
3845
"zod": "^3.20.6"
3946
},
4047
"devDependencies": {
48+
"@types/react-virtualized-auto-sizer": "^1.0.1",
49+
"@types/react-window": "^1.8.5",
4150
"@typescript-eslint/eslint-plugin": "^5.53.0",
4251
"@typescript-eslint/parser": "^5.53.0",
4352
"eslint-config-airbnb": "^19.0.4",

src/lib/components/MainLayout.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,7 @@ function Menu() {
8484
{ icon: typeof faClock; title: string; href: string }[]
8585
>([
8686
{ icon: faHome, title: "Home", href: "/" },
87-
{ icon: faCode, title: "Solution Pass", href: "/solution-pass" },
88-
{ icon: faClock, title: "Timer", href: "/timer" }
87+
{ icon: faCode, title: "Solution Pass", href: "/solution-pass" }
8988
]);
9089

9190
return (

src/lib/hooks/useClient.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { useEffect, useState } from "react";
2+
3+
export default function useClient() {
4+
const [isClient, setIsClient] = useState(false);
5+
useEffect(() => {
6+
if (!isClient) setIsClient(true);
7+
}, [isClient]);
8+
9+
return isClient;
10+
}

src/lib/hooks/useColor.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { Prob } from "../solution-pass/hooks/useRepo";
44
export default function useColor() {
55
const bodyBg = useColorModeValue("white", "gray.800");
66
const subtleBg = useColorModeValue("gray.100", "gray.700");
7+
const alphaBg = useColorModeValue("gray.100", "whiteAlpha.200");
8+
const accentBg = useColorModeValue("gray.200", "gray.900");
79
const levelColors: { [key in Prob["level"]]: string } = {
810
0: "#2189ff",
911
1: "#1bbaff",
@@ -13,12 +15,5 @@ export default function useColor() {
1315
5: "#c658e1"
1416
};
1517

16-
return { bodyBg, subtleBg, levelColors };
18+
return { bodyBg, subtleBg, alphaBg, accentBg, levelColors };
1719
}
18-
19-
// --chakra-colors-chakra-body-text: var(--chakra-colors-gray-800);
20-
// --chakra-colors-chakra-border-color: var(--chakra-colors-gray-200);
21-
// --chakra-colors-chakra-placeholder-color: var(--chakra-colors-gray-500);
22-
// --chakra-colors-chakra-body-text: var(--chakra-colors-whiteAlpha-900);
23-
// --chakra-colors-chakra-border-color: var(--chakra-colors-whiteAlpha-300);
24-
// --chakra-colors-chakra-placeholder-color: var(--chakra-colors-whiteAlpha-400);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import useColor from "@/lib/hooks/useColor";
2+
import { Box, Center, Flex, Text, useOutsideClick } from "@chakra-ui/react";
3+
import { ComponentProps, useRef, useState } from "react";
4+
import AutoSizer from "react-virtualized-auto-sizer";
5+
import { FixedSizeList } from "react-window";
6+
import { Prob } from "../hooks/useRepo";
7+
import useSearch from "../hooks/useSearch";
8+
import SearhBar from "./SearchBar";
9+
10+
export default function DropdownSearhBar() {
11+
const [isOpen, setIsOpen] = useState(false);
12+
13+
return (
14+
<Flex direction="column" w="full" pos="relative">
15+
<SearhBar rounded="none" onClick={() => setIsOpen(true)} />
16+
{isOpen && <Result onClose={() => setIsOpen(false)} />}
17+
</Flex>
18+
);
19+
}
20+
21+
function Result({ onClose }: { onClose: () => void }) {
22+
const { result, select } = useSearch();
23+
const { subtleBg } = useColor();
24+
25+
const ref = useRef(null);
26+
27+
useOutsideClick({
28+
ref,
29+
handler: onClose
30+
});
31+
32+
if (result.keyword === "") return null;
33+
if (result.probs.length === 0)
34+
return (
35+
<Center
36+
pos="absolute"
37+
w="full"
38+
top="48px"
39+
zIndex="modal"
40+
h="100px"
41+
bg={subtleBg}
42+
>
43+
<Text>일치하는 문제가 없어요</Text>
44+
</Center>
45+
);
46+
47+
return (
48+
<Box ref={ref} h="260px" pos="absolute" w="full" top="48px" zIndex="modal">
49+
<AutoSizer>
50+
{({ height, width }) => (
51+
<FixedSizeList
52+
height={height}
53+
width={width}
54+
itemSize={60}
55+
itemCount={result.probs.length}
56+
>
57+
{({ index, style }) => (
58+
<ResultRow
59+
probData={result.probs[index]}
60+
style={style}
61+
onClick={() => {
62+
select(result.probs[index].id);
63+
onClose();
64+
}}
65+
/>
66+
)}
67+
</FixedSizeList>
68+
)}
69+
</AutoSizer>
70+
</Box>
71+
);
72+
}
73+
74+
function ResultRow({
75+
probData: { id, level, title },
76+
...props
77+
}: { probData: Prob } & ComponentProps<typeof Flex>) {
78+
const { levelColors, subtleBg, accentBg } = useColor();
79+
80+
return (
81+
<Flex
82+
key={id}
83+
alignItems="center"
84+
gap="20px"
85+
w="full"
86+
h="60px"
87+
rounded="none"
88+
bg={subtleBg}
89+
_hover={{ bg: accentBg }}
90+
cursor="pointer"
91+
{...props}
92+
>
93+
<Text
94+
textAlign="center"
95+
w="60px"
96+
fontSize="lg"
97+
color={levelColors[level]}
98+
fontWeight="bold"
99+
>
100+
{level}
101+
</Text>
102+
<Text flex="1">{title}</Text>
103+
</Flex>
104+
);
105+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Button, Icon, IconButton } from "@chakra-ui/react";
2+
import { faPencil, faSearch } from "@fortawesome/free-solid-svg-icons";
3+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
4+
import { ComponentProps } from "react";
5+
6+
const modeIcon: {
7+
[key in ModeToggleBtnProps["mode"]]: {
8+
icon: typeof faPencil;
9+
title: string;
10+
};
11+
} = {
12+
write: {
13+
icon: faPencil,
14+
title: "정답 추가하기"
15+
},
16+
search: {
17+
icon: faSearch,
18+
title: "정답 검색하기"
19+
}
20+
};
21+
22+
export default function ModeToggleBtn({ mode, ...props }: ModeToggleBtnProps) {
23+
return (
24+
<Button
25+
rounded="full"
26+
pos="fixed"
27+
bottom="40px"
28+
right="40px"
29+
size="lg"
30+
variant="solid"
31+
backdropFilter="auto"
32+
backdropBlur="3xl"
33+
leftIcon={<Icon as={FontAwesomeIcon} icon={modeIcon[mode].icon} />}
34+
{...props}
35+
>
36+
{modeIcon[mode].title}
37+
</Button>
38+
);
39+
}
40+
41+
type ModeToggleBtnProps = {
42+
mode: "write" | "search";
43+
} & ComponentProps<typeof IconButton>;

src/lib/solution-pass/components/ResultSection.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,7 @@ function Solutions({ solData: { author, code } }: { solData: Sol }) {
198198
toast({
199199
title: "코드가 복사되었어요",
200200
status: "success",
201-
duration: 1000,
202-
position: "bottom-left"
201+
duration: 1000
203202
});
204203
}}
205204
pos="absolute"

src/lib/solution-pass/components/SearchBar.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ import {
88
} from "@chakra-ui/react";
99
import { faSearch } from "@fortawesome/free-solid-svg-icons";
1010
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
11-
import { useState } from "react";
11+
import { ComponentProps, useState } from "react";
1212
import useSearch from "../hooks/useSearch";
1313

1414
let timeoutId: ReturnType<typeof setTimeout> | null = null;
1515
const DEBOUNCE_TIME = 100;
1616

17-
export default function SearhBar() {
17+
export default function SearhBar(props: ComponentProps<typeof Input>) {
1818
const [value, setValue] = useState("");
1919
const [isTyping, setIsTyping] = useState(false);
20-
const { search } = useSearch();
20+
const { search, result } = useSearch();
2121

2222
const handleTyping = (keyword: string) => {
2323
if (timeoutId) clearTimeout(timeoutId);
@@ -32,12 +32,13 @@ export default function SearhBar() {
3232
return (
3333
<InputGroup size="lg">
3434
<Input
35-
value={value}
35+
value={value === "" ? result.keyword : value}
3636
variant="filled"
3737
type="text"
3838
onChange={e => handleTyping(e.target.value)}
3939
autoComplete="off"
4040
placeholder="문제를 검색해주세요"
41+
{...props}
4142
/>
4243
<InputLeftElement>
4344
<Icon as={FontAwesomeIcon} icon={faSearch} />
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Flex } from "@chakra-ui/react";
2+
import { ComponentProps } from "react";
3+
4+
export default function SolutionPassLayout(props: ComponentProps<typeof Flex>) {
5+
return (
6+
<Flex
7+
w="full"
8+
maxW="1000px"
9+
direction="column"
10+
pt="20px"
11+
align="center"
12+
m="auto"
13+
px="20px"
14+
gap="20px"
15+
{...props}
16+
/>
17+
);
18+
}

0 commit comments

Comments
 (0)
0