Skip to content

Commit 44fe569

Browse files
authored
Merge pull request #42 from pythonkr/feature/add-mobile-footer
feat: 모바일 Footer 개발
2 parents 48eb9c2 + 81923d5 commit 44fe569

File tree

2 files changed

+282
-50
lines changed

2 files changed

+282
-50
lines changed
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import styled from "@emotion/styled";
2+
import * as Common from "@frontend/common";
3+
import { Article, Email, Facebook, GitHub, Instagram, LinkedIn, X, YouTube } from "@mui/icons-material";
4+
import * as React from "react";
5+
6+
import FlickrIcon from "@apps/pyconkr/assets/thirdparty/flickr.svg?react";
7+
8+
import { useAppContext } from "../../../../contexts/app_context";
9+
10+
interface IconItem {
11+
icon: React.FC<{ width?: number; height?: number }>;
12+
alt: string;
13+
href: string;
14+
}
15+
16+
const defaultIcons: IconItem[] = [
17+
{
18+
icon: Facebook,
19+
alt: "facebook",
20+
href: "https://www.facebook.com/pyconkorea/",
21+
},
22+
{
23+
icon: YouTube,
24+
alt: "YouTube",
25+
href: "https://www.youtube.com/c/PyConKRtube",
26+
},
27+
{ icon: X, alt: "X", href: "https://x.com/PyConKR" },
28+
{ icon: GitHub, alt: "github", href: "https://github.com/pythonkr" },
29+
{
30+
icon: Instagram,
31+
alt: "Instagram",
32+
href: "https://www.instagram.com/pycon_korea/",
33+
},
34+
{
35+
icon: LinkedIn,
36+
alt: "LinkedIn",
37+
href: "https://www.linkedin.com/company/pyconkorea/",
38+
},
39+
{ icon: Article, alt: "blog", href: "https://blog.pycon.kr/" },
40+
{
41+
icon: FlickrIcon,
42+
alt: "Flickr",
43+
href: "https://www.flickr.com/photos/126829363@N08/",
44+
},
45+
];
46+
47+
const Bar: React.FC = () => <div style={{ display: "inline-block", padding: "0 0.25rem" }}>|</div>;
48+
49+
export default function MobileFooter() {
50+
const { sendEmail } = Common.Hooks.Common.useEmail();
51+
const { language } = useAppContext();
52+
53+
const title = language === "ko" ? "Weave with Python, 파이콘 한국 2025" : "Weave with Python, Pycon KR 2025";
54+
const committeeTitle =
55+
language === "ko"
56+
? "파이콘 한국 2025는 파이콘 한국 준비위원회가 만들고 있습니다"
57+
: "PyCon Korea 2025 is organized by the PyCon Korea Organizing Committee";
58+
const djangoTitle = language === "ko" ? "파이썬 웹 프레임워크 Django로 만들었습니다" : "Built with the Django web framework for Python";
59+
60+
const links = [
61+
{
62+
text: language === "ko" ? "파이콘 한국 행동 강령(CoC)" : "PyCon Korea Code of Conduct",
63+
href: "https://pythonkr.github.io/pycon-code-of-conduct/ko/coc/a_intent_and_purpose.html",
64+
},
65+
{
66+
text: language === "ko" ? "서비스 이용 약관" : "Terms of Service",
67+
href: "/about/terms-of-service",
68+
},
69+
{
70+
text: language === "ko" ? "개인 정보 처리 방침" : "Privacy Policy",
71+
href: "/about/privacy-policy",
72+
},
73+
];
74+
75+
return (
76+
<FooterContainer>
77+
<FooterContent>
78+
<FooterSlogan>
79+
<br />
80+
<FooterBoldText children={title} />
81+
<br />
82+
<FooterNormalText children={committeeTitle} />
83+
<br />
84+
<FooterNormalText children={djangoTitle} />
85+
<br />
86+
</FooterSlogan>
87+
<FooterLinks>
88+
{links.map((link, index) => (
89+
<FooterLinkSlogan key={index}>
90+
<Link key={link.text} href={link.href}>
91+
{link.text}
92+
</Link>
93+
{index < links.length - 1 && <Separator>|</Separator>}
94+
</FooterLinkSlogan>
95+
))}
96+
</FooterLinks>
97+
<FooterIcons>
98+
<IconLink onClick={sendEmail} aria-label="이메일 보내기">
99+
<Email width={20} height={20} aria-hidden="true" />
100+
</IconLink>
101+
{defaultIcons.map((icon) => (
102+
<IconLink key={icon.alt} href={icon.href} target="_blank" rel="noopener noreferrer" aria-label={`${icon.alt}로 이동`}>
103+
<icon.icon width={20} height={20} aria-hidden="true" />
104+
</IconLink>
105+
))}
106+
</FooterIcons>
107+
</FooterContent>
108+
</FooterContainer>
109+
);
110+
}
111+
112+
const FooterContainer = styled.footer`
113+
background: linear-gradient(to bottom, #ffffff 0%, #e4fdff 25%, #92c9cc 50%, #5cadb3 75%, #095a5f 100%);
114+
color: ${({ theme }) => theme.palette.common.white};
115+
font-size: 0.75rem;
116+
display: flex;
117+
flex-direction: column;
118+
align-items: center;
119+
justify-content: center;
120+
width: 100%;
121+
max-height: 16rem;
122+
padding: 5rem 0 1rem 0;
123+
`;
124+
125+
const FooterContent = styled.div`
126+
display: flex;
127+
flex-direction: column;
128+
align-items: center;
129+
justify-content: center;
130+
gap: 0.75rem;
131+
`;
132+
133+
const FooterText = styled.div`
134+
padding: 0 2rem;
135+
margin: 0.1rem;
136+
137+
font-size: 9pt;
138+
139+
a > button {
140+
margin-left: 0.25rem;
141+
padding: 0.05rem 0.25rem;
142+
font-size: 8pt;
143+
color: ${({ theme }) => theme.palette.common.white};
144+
border-color: ${({ theme }) => theme.palette.common.white};
145+
146+
gap: 0.25rem;
147+
148+
& span {
149+
margin-left: -2px;
150+
margin-right: 0;
151+
152+
& svg {
153+
font-size: 12pt !important;
154+
}
155+
}
156+
}
157+
158+
strong {
159+
font-size: 12pt;
160+
}
161+
`;
162+
163+
const FooterBoldText = styled.text`
164+
font-weight: 600;
165+
`;
166+
167+
const FooterNormalText = styled.text`
168+
font-weight: 400;
169+
`;
170+
171+
const FooterSlogan = styled.div`
172+
text-align: center;
173+
`;
174+
175+
const FooterLinkSlogan = styled.div`
176+
display: flex;
177+
gap: 0.3rem;
178+
`;
179+
180+
const FooterLinks = styled.div`
181+
display: flex;
182+
align-items: center;
183+
gap: 0.3rem;
184+
`;
185+
186+
const FooterIcons = styled.div`
187+
display: flex;
188+
align-items: center;
189+
gap: 9px;
190+
`;
191+
192+
const Link = styled.a`
193+
color: ${({ theme }) => theme.palette.common.white};
194+
text-decoration: none;
195+
&:hover {
196+
text-decoration: underline;
197+
}
198+
`;
199+
200+
const Separator = styled.span`
201+
color: ${({ theme }) => theme.palette.common.white};
202+
opacity: 0.5;
203+
margin: 0.05rem 0;
204+
`;
205+
206+
const IconLink = styled.a`
207+
display: flex;
208+
align-items: center;
209+
justify-content: center;
210+
211+
cursor: pointer;
212+
213+
&:hover {
214+
opacity: 0.8;
215+
}
216+
217+
img {
218+
width: 20px;
219+
height: 20px;
220+
}
221+
`;

apps/pyconkr/src/components/layout/Footer/index.tsx

Lines changed: 61 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import styled from "@emotion/styled";
22
import * as Common from "@frontend/common";
33
import { Article, Email, Facebook, GitHub, Instagram, LinkedIn, OpenInNew, X, YouTube } from "@mui/icons-material";
4-
import { Button } from "@mui/material";
4+
import { Button, useMediaQuery, useTheme } from "@mui/material";
55
import * as React from "react";
66

77
import FlickrIcon from "@apps/pyconkr/assets/thirdparty/flickr.svg?react";
88

99
import { useAppContext } from "../../../contexts/app_context";
10+
import MobileFooter from "./Mobile/MobileFooter";
1011

1112
interface IconItem {
1213
icon: React.FC<{ width?: number; height?: number }>;
@@ -49,6 +50,9 @@ const Bar: React.FC = () => <div style={{ display: "inline-block", padding: "0 0
4950

5051
export default function Footer() {
5152
const { sendEmail } = Common.Hooks.Common.useEmail();
53+
const theme = useTheme();
54+
const isMobile = useMediaQuery(theme.breakpoints.down("md"));
55+
5256
const { language } = useAppContext();
5357

5458
const corpPasamoStr = language === "ko" ? "사단법인 파이썬사용자모임" : "Python Korea";
@@ -83,56 +87,62 @@ export default function Footer() {
8387
},
8488
];
8589

86-
return (
87-
<FooterContainer>
88-
<FooterContent>
89-
<FooterText>
90-
<strong>{corpPasamoStr}</strong>
91-
<br />
92-
{corpAddressStr}
93-
<Bar />
94-
{corpRepresentatorStr}
95-
<Bar />
96-
{corpPhoneStr}
97-
<Bar />
98-
{corpCompanyNumberStr}
99-
<a href="http://www.ftc.go.kr/bizCommPop.do?wrkr_no=3388200046" target="_blank" rel="noreferrer">
100-
<Button variant="outlined" startIcon={<OpenInNew sx={{ fontSize: "7pt" }} />}>
101-
{corpCheckBtnStr}
102-
</Button>
103-
</a>
104-
<br />
105-
{corpMailOrderSalesRegistrationNumberStr}
106-
<Bar />
107-
{hostingProviderStr}
108-
<Bar />
109-
{contractEmailStr}
110-
111-
</FooterText>
112-
<FooterLinks>
113-
{links.map((link, index) => (
114-
<React.Fragment key={index}>
115-
<Link key={link.text} href={link.href}>
116-
{link.text}
117-
</Link>
118-
{index < links.length - 1 && <Separator>|</Separator>}
119-
</React.Fragment>
120-
))}
121-
</FooterLinks>
122-
<FooterIcons>
123-
<IconLink onClick={sendEmail} aria-label="이메일 보내기">
124-
<Email width={20} height={20} aria-hidden="true" />
125-
</IconLink>
126-
{defaultIcons.map((icon) => (
127-
<IconLink key={icon.alt} href={icon.href} target="_blank" rel="noopener noreferrer" aria-label={`${icon.alt}로 이동`}>
128-
<icon.icon width={20} height={20} aria-hidden="true" />
90+
console.log("isMobile " + isMobile);
91+
92+
if (isMobile) {
93+
return <MobileFooter />;
94+
} else {
95+
return (
96+
<FooterContainer>
97+
<FooterContent>
98+
<FooterText>
99+
<strong>{corpPasamoStr}</strong>
100+
<br />
101+
{corpAddressStr}
102+
<Bar />
103+
{corpRepresentatorStr}
104+
<Bar />
105+
{corpPhoneStr}
106+
<Bar />
107+
{corpCompanyNumberStr}
108+
<a href="http://www.ftc.go.kr/bizCommPop.do?wrkr_no=3388200046" target="_blank" rel="noreferrer">
109+
<Button variant="outlined" startIcon={<OpenInNew sx={{ fontSize: "7pt" }} />}>
110+
{corpCheckBtnStr}
111+
</Button>
112+
</a>
113+
<br />
114+
{corpMailOrderSalesRegistrationNumberStr}
115+
<Bar />
116+
{hostingProviderStr}
117+
<Bar />
118+
{contractEmailStr}
119+
120+
</FooterText>
121+
<FooterLinks>
122+
{links.map((link, index) => (
123+
<React.Fragment key={index}>
124+
<Link key={link.text} href={link.href}>
125+
{link.text}
126+
</Link>
127+
{index < links.length - 1 && <Separator>|</Separator>}
128+
</React.Fragment>
129+
))}
130+
</FooterLinks>
131+
<FooterIcons>
132+
<IconLink onClick={sendEmail} aria-label="이메일 보내기">
133+
<Email width={20} height={20} aria-hidden="true" />
129134
</IconLink>
130-
))}
131-
</FooterIcons>
132-
<FooterSlogan>{copyrightStr}</FooterSlogan>
133-
</FooterContent>
134-
</FooterContainer>
135-
);
135+
{defaultIcons.map((icon) => (
136+
<IconLink key={icon.alt} href={icon.href} target="_blank" rel="noopener noreferrer" aria-label={`${icon.alt}로 이동`}>
137+
<icon.icon width={20} height={20} aria-hidden="true" />
138+
</IconLink>
139+
))}
140+
</FooterIcons>
141+
<FooterSlogan>{copyrightStr}</FooterSlogan>
142+
</FooterContent>
143+
</FooterContainer>
144+
);
145+
}
136146
}
137147

138148
const FooterContainer = styled.footer`
@@ -195,6 +205,7 @@ const FooterLinks = styled.div`
195205
align-items: center;
196206
gap: 0.625rem;
197207
`;
208+
198209
const FooterIcons = styled.div`
199210
display: flex;
200211
align-items: center;

0 commit comments

Comments
 (0)