Skip to content

Commit a5f0901

Browse files
committed
feat : 모바일 GNB 마크업
1 parent c93d50a commit a5f0901

File tree

8 files changed

+650
-12
lines changed

8 files changed

+650
-12
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { IconButton, styled } from "@mui/material";
2+
import * as React from "react";
3+
4+
interface HamburgerButtonProps {
5+
isOpen: boolean;
6+
onClick: () => void;
7+
isMainPath?: boolean;
8+
}
9+
10+
export const HamburgerButton: React.FC<HamburgerButtonProps> = ({ isOpen, onClick, isMainPath = true }) => {
11+
return (
12+
<StyledIconButton onClick={onClick} isMainPath={isMainPath}>
13+
<HamburgerIcon isOpen={isOpen} isMainPath={isMainPath}>
14+
<span />
15+
<span />
16+
<span />
17+
</HamburgerIcon>
18+
</StyledIconButton>
19+
);
20+
};
21+
22+
const StyledIconButton = styled(IconButton)<{ isMainPath: boolean }>(({ theme, isMainPath }) => ({
23+
padding: 0,
24+
width: 26,
25+
height: 18,
26+
color: isMainPath ? theme.palette.mobileHeader.main.text : theme.palette.mobileHeader.sub.text,
27+
}));
28+
29+
const HamburgerIcon = styled("div")<{ isOpen: boolean; isMainPath: boolean }>(({ isOpen, theme, isMainPath }) => ({
30+
width: 26,
31+
height: 18,
32+
position: "relative",
33+
cursor: "pointer",
34+
display: "flex",
35+
flexDirection: "column",
36+
justifyContent: "space-between",
37+
38+
"& span": {
39+
display: "block",
40+
height: isOpen ? 3 : 2,
41+
width: "100%",
42+
backgroundColor: isMainPath ? theme.palette.mobileHeader.main.text : theme.palette.mobileHeader.sub.text,
43+
borderRadius: 1,
44+
transition: "height 0.3s ease",
45+
},
46+
}));
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import * as Common from "@frontend/common";
2+
import { Box, Stack, styled, Typography } from "@mui/material";
3+
import * as React from "react";
4+
import { Link, useLocation } from "react-router-dom";
5+
6+
import { HamburgerButton } from "./HamburgerButton";
7+
import { MobileLanguageToggle } from "./MobileLanguageToggle";
8+
import { MobileNavigation } from "./MobileNavigation";
9+
import { useAppContext } from "../../../../contexts/app_context";
10+
11+
interface MobileHeaderProps {
12+
isNavigationOpen?: boolean;
13+
onToggleNavigation?: () => void;
14+
}
15+
16+
export const MobileHeader: React.FC<MobileHeaderProps> = ({ isNavigationOpen = false, onToggleNavigation }) => {
17+
const { siteMapNode, language } = useAppContext();
18+
const location = useLocation();
19+
const [internalNavigationOpen, setInternalNavigationOpen] = React.useState(false);
20+
21+
const navigationOpen = onToggleNavigation ? isNavigationOpen : internalNavigationOpen;
22+
const toggleNavigation = onToggleNavigation || (() => setInternalNavigationOpen(!internalNavigationOpen));
23+
24+
const isMainPath = location.pathname === "/";
25+
26+
const handleLanguageChange = (newLanguage: string) => {
27+
// TODO: 언어 변경 로직 구현
28+
console.log("Language changed to:", newLanguage);
29+
};
30+
31+
return (
32+
<>
33+
<MobileHeaderContainer isOpen={navigationOpen} isMainPath={isMainPath}>
34+
<LeftContent>
35+
<HamburgerButton isOpen={navigationOpen} onClick={toggleNavigation} isMainPath={isMainPath} />
36+
<LogoAndTextContainer>
37+
<Link to="/" style={{ textDecoration: "none" }}>
38+
<Stack direction="row" alignItems="center" spacing={0.375}>
39+
<Common.Components.PythonKorea style={{ width: 29, height: 29 }} />
40+
<Typography
41+
variant="h6"
42+
sx={{
43+
color: isMainPath ? "white" : "rgba(18, 109, 127, 0.6)",
44+
fontSize: 18,
45+
fontWeight: 600,
46+
}}
47+
>
48+
파이콘 한국 2025
49+
</Typography>
50+
</Stack>
51+
</Link>
52+
</LogoAndTextContainer>
53+
</LeftContent>
54+
55+
<MobileLanguageToggle currentLanguage={language} onLanguageChange={handleLanguageChange} isMainPath={isMainPath} />
56+
</MobileHeaderContainer>
57+
58+
<MobileNavigation isOpen={navigationOpen} onClose={() => toggleNavigation()} siteMapNode={siteMapNode} />
59+
</>
60+
);
61+
};
62+
63+
const MobileHeaderContainer = styled("header")<{ isOpen: boolean; isMainPath: boolean }>(({ theme, isOpen, isMainPath }) => ({
64+
position: "fixed",
65+
top: 0,
66+
left: 0,
67+
right: 0,
68+
69+
display: isOpen ? "none" : "flex",
70+
alignItems: "center",
71+
justifyContent: "space-between",
72+
73+
width: "100%",
74+
height: 60,
75+
76+
padding: "15px 23px",
77+
78+
backgroundColor: isMainPath ? "rgba(182, 216, 215, 0.1)" : "#B6D8D7",
79+
backdropFilter: "blur(8px)",
80+
WebkitBackdropFilter: "blur(8px)",
81+
color: isMainPath ? "white" : "rgba(18, 109, 127, 0.6)",
82+
83+
zIndex: theme.zIndex.appBar + 100000,
84+
}));
85+
86+
const LeftContent = styled(Box)({
87+
display: "flex",
88+
alignItems: "center",
89+
gap: 17,
90+
});
91+
92+
const LogoAndTextContainer = styled(Box)({
93+
display: "flex",
94+
alignItems: "center",
95+
});
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { ButtonBase, styled } from "@mui/material";
2+
import * as React from "react";
3+
4+
interface MobileLanguageToggleProps {
5+
currentLanguage: string;
6+
onLanguageChange: (newLanguage: string) => void;
7+
isMainPath?: boolean;
8+
}
9+
10+
export const MobileLanguageToggle: React.FC<MobileLanguageToggleProps> = ({ currentLanguage, onLanguageChange, isMainPath = true }) => {
11+
return (
12+
<ToggleContainer isMainPath={isMainPath}>
13+
<LanguageButton isActive={currentLanguage === "ko"} isMainPath={isMainPath} onClick={() => onLanguageChange("ko")}>
14+
KO
15+
</LanguageButton>
16+
<LanguageButton isActive={currentLanguage === "en"} isMainPath={isMainPath} onClick={() => onLanguageChange("en")}>
17+
EN
18+
</LanguageButton>
19+
</ToggleContainer>
20+
);
21+
};
22+
23+
const ToggleContainer = styled("div")<{ isMainPath: boolean }>(({ isMainPath }) => ({
24+
display: "flex",
25+
width: 94,
26+
height: 29,
27+
border: "1px solid white",
28+
borderRadius: 15,
29+
padding: 2,
30+
gap: 2,
31+
backgroundColor: isMainPath ? "transparent" : "rgba(255, 255, 255, 0.1)",
32+
}));
33+
34+
const LanguageButton = styled(ButtonBase)<{ isActive: boolean; isMainPath: boolean }>(({ isActive, isMainPath }) => ({
35+
flex: 1,
36+
height: "100%",
37+
borderRadius: 13,
38+
fontSize: 12,
39+
fontWeight: 400,
40+
transition: "all 0.2s ease",
41+
42+
color: isMainPath ? "white" : "rgba(18, 109, 127, 0.6)",
43+
backgroundColor: "transparent",
44+
45+
...(isActive && {
46+
backgroundColor: isMainPath ? "rgba(255, 255, 255, 0.7)" : "rgba(255, 255, 255, 0.9)",
47+
color: isMainPath ? "#888888" : "#126D7F",
48+
fontWeight: 600,
49+
}),
50+
51+
"&:hover": {
52+
backgroundColor: isActive
53+
? isMainPath
54+
? "rgba(255, 255, 255, 0.8)"
55+
: "rgba(255, 255, 255, 1)"
56+
: isMainPath
57+
? "rgba(255, 255, 255, 0.1)"
58+
: "rgba(255, 255, 255, 0.3)",
59+
},
60+
61+
WebkitFontSmoothing: "antialiased",
62+
MozOsxFontSmoothing: "grayscale",
63+
textRendering: "optimizeLegibility",
64+
WebkitTextStroke: "0.5px transparent",
65+
}));

0 commit comments

Comments
 (0)