Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { IconButton, styled } from "@mui/material";
import * as React from "react";

interface HamburgerButtonProps {
isOpen: boolean;
onClick: () => void;
isMainPath?: boolean;
}

export const HamburgerButton: React.FC<HamburgerButtonProps> = ({ isOpen, onClick, isMainPath = true }) => {
return (
<StyledIconButton onClick={onClick} isMainPath={isMainPath}>
<HamburgerIcon isOpen={isOpen} isMainPath={isMainPath}>
<span />
<span />
<span />
</HamburgerIcon>
</StyledIconButton>
);
};

const StyledIconButton = styled(IconButton)<{ isMainPath: boolean }>(({ theme, isMainPath }) => ({
padding: 0,
width: 26,
height: 18,
color: isMainPath ? theme.palette.mobileHeader.main.text : theme.palette.mobileHeader.sub.text,
}));

const HamburgerIcon = styled("div")<{ isOpen: boolean; isMainPath: boolean }>(({ isOpen, theme, isMainPath }) => ({
width: 26,
height: 18,
position: "relative",
cursor: "pointer",
display: "flex",
flexDirection: "column",
justifyContent: "space-between",

"& span": {
display: "block",
height: isOpen ? 3 : 2,
width: "100%",
backgroundColor: isMainPath ? theme.palette.mobileHeader.main.text : theme.palette.mobileHeader.sub.text,
borderRadius: 1,
transition: "height 0.3s ease",
},
}));
90 changes: 90 additions & 0 deletions apps/pyconkr/src/components/layout/Header/Mobile/MobileHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import * as Common from "@frontend/common";
import { Box, Stack, styled, Typography } from "@mui/material";
import * as React from "react";
import { Link, useLocation } from "react-router-dom";

import { HamburgerButton } from "./HamburgerButton";
import { MobileLanguageToggle } from "./MobileLanguageToggle";
import { MobileNavigation } from "./MobileNavigation";
import { useAppContext } from "../../../../contexts/app_context";

interface MobileHeaderProps {
isNavigationOpen?: boolean;
onToggleNavigation?: () => void;
}

export const MobileHeader: React.FC<MobileHeaderProps> = ({ isNavigationOpen = false, onToggleNavigation }) => {
const { siteMapNode } = useAppContext();
const location = useLocation();
const [internalNavigationOpen, setInternalNavigationOpen] = React.useState(false);

const navigationOpen = onToggleNavigation ? isNavigationOpen : internalNavigationOpen;
const toggleNavigation = onToggleNavigation || (() => setInternalNavigationOpen(!internalNavigationOpen));

const isMainPath = location.pathname === "/";

return (
<>
<MobileHeaderContainer isOpen={navigationOpen} isMainPath={isMainPath}>
<LeftContent>
<HamburgerButton isOpen={navigationOpen} onClick={toggleNavigation} isMainPath={isMainPath} />
<LogoAndTextContainer>
<Link to="/" style={{ textDecoration: "none" }}>
<Stack direction="row" alignItems="center" spacing={0.375}>
<Common.Components.PythonKorea style={{ width: 29, height: 29 }} />
<Typography
variant="h6"
sx={{
color: isMainPath ? "white" : "rgba(18, 109, 127, 0.6)",
fontSize: 18,
fontWeight: 600,
}}
>
파이콘 한국 2025
</Typography>
</Stack>
</Link>
</LogoAndTextContainer>
</LeftContent>

<MobileLanguageToggle isMainPath={isMainPath} />
</MobileHeaderContainer>

<MobileNavigation isOpen={navigationOpen} onClose={() => toggleNavigation()} siteMapNode={siteMapNode} />
</>
);
};

const MobileHeaderContainer = styled("header")<{ isOpen: boolean; isMainPath: boolean }>(({ theme, isOpen, isMainPath }) => ({
position: isMainPath ? "fixed" : "sticky",
top: 0,
left: 0,
right: 0,

display: isOpen ? "none" : "flex",
alignItems: "center",
justifyContent: "space-between",

width: "100%",
height: 60,

padding: "15px 23px",

backgroundColor: isMainPath ? "rgba(182, 216, 215, 0.1)" : "#B6D8D7",
backdropFilter: isMainPath ? "blur(8px)" : "none",
WebkitBackdropFilter: isMainPath ? "blur(8px)" : "none",
color: isMainPath ? "white" : "rgba(18, 109, 127, 0.6)",

zIndex: isMainPath ? theme.zIndex.appBar + 100000 : theme.zIndex.appBar,
}));

const LeftContent = styled(Box)({
display: "flex",
alignItems: "center",
gap: 17,
});

const LogoAndTextContainer = styled(Box)({
display: "flex",
alignItems: "center",
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { ButtonBase, styled } from "@mui/material";
import * as React from "react";

import { LOCAL_STORAGE_LANGUAGE_KEY } from "../../../../consts/local_stroage";
import { useAppContext } from "../../../../contexts/app_context";

interface MobileLanguageToggleProps {
isMainPath?: boolean;
}

export const MobileLanguageToggle: React.FC<MobileLanguageToggleProps> = ({ isMainPath = true }) => {
const { language, setAppContext } = useAppContext();

const handleLanguageChange = (newLanguage: "ko" | "en") => {
localStorage.setItem(LOCAL_STORAGE_LANGUAGE_KEY, newLanguage);
setAppContext((ps) => ({ ...ps, language: newLanguage }));
};
return (
<ToggleContainer isMainPath={isMainPath}>
<LanguageButton isActive={language === "ko"} isMainPath={isMainPath} onClick={() => handleLanguageChange("ko")}>
KO
</LanguageButton>
<LanguageButton isActive={language === "en"} isMainPath={isMainPath} onClick={() => handleLanguageChange("en")}>
EN
</LanguageButton>
</ToggleContainer>
);
};

const ToggleContainer = styled("div")<{ isMainPath: boolean }>(({ theme, isMainPath }) => ({
display: "flex",
width: 94,
height: 29,
border: "1px solid white",
borderRadius: 15,
padding: 2,
gap: 2,
backgroundColor: isMainPath
? theme.palette.mobileNavigation.main.languageToggle.background
: theme.palette.mobileNavigation.sub.languageToggle.background,
}));

const LanguageButton = styled(ButtonBase)<{ isActive: boolean; isMainPath: boolean }>(({ theme, isActive, isMainPath }) => ({
flex: 1,
height: "100%",
borderRadius: 13,
fontSize: 12,
fontWeight: 400,
transition: "all 0.2s ease",

color: isMainPath ? theme.palette.mobileHeader.main.text : theme.palette.mobileHeader.sub.text,
backgroundColor: "transparent",

...(isActive && {
backgroundColor: isMainPath
? theme.palette.mobileNavigation.main.languageToggle.active.background
: theme.palette.mobileNavigation.sub.languageToggle.active.background,
color: isMainPath ? theme.palette.mobileHeader.main.activeLanguage : theme.palette.mobileHeader.sub.activeLanguage,
fontWeight: 600,
}),

"&:hover": {
backgroundColor: isActive
? isMainPath
? theme.palette.mobileNavigation.main.languageToggle.active.hover
: theme.palette.mobileNavigation.sub.languageToggle.active.hover
: isMainPath
? theme.palette.mobileNavigation.main.languageToggle.inactive.hover
: theme.palette.mobileNavigation.sub.languageToggle.inactive.hover,
},

WebkitFontSmoothing: "antialiased",
MozOsxFontSmoothing: "grayscale",
textRendering: "optimizeLegibility",
WebkitTextStroke: "0.5px transparent",
}));
Loading