Skip to content

Commit 4ee35ae

Browse files
authored
Merge pull request #50 from pythonkr/fix/remove-2025-dependency-on-mdx-components
fix: mdx components에서 2025년도 의존성 제거
2 parents 34666cb + 57eb922 commit 4ee35ae

6 files changed

Lines changed: 165 additions & 91 deletions

File tree

apps/pyconkr/src/consts/mdx_components.ts

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
// 후대의 개발자님께 : 컴포넌트 맨 첫글자가 대문자로 시작하지 않으면 JSX 컴포넌트가 아니라 일반 HTML 태그로 인식합니다. 제발 대문자로 시작해주세요.
2-
import { Components } from "@frontend/common";
2+
import { Components, Schemas } from "@frontend/common";
33
import * as Shop from "@frontend/shop";
44
import * as mui from "@mui/material";
55
import type { MDXComponents } from "mdx/types.js";
6+
import * as React from "react";
7+
8+
import PyCon2025HostLogoBig from "../../../../packages/common/src/assets/pyconkr2025_hostlogo_big.png";
9+
import PyCon2025HostLogoSmall from "../../../../packages/common/src/assets/pyconkr2025_hostlogo_small.png";
10+
import PyCon2025MobileLogoImage from "../../../../packages/common/src/assets/pyconkr2025_main_cover_image.png";
11+
import PyCon2025MobileLogoTitle from "../../../../packages/common/src/assets/pyconkr2025_main_cover_title.png";
12+
import PyCon2025Logo from "../assets/pyconkr2025_logo.png";
613

714
const MUIMDXComponents: MDXComponents = {
815
Mui__material__Accordion: mui.Accordion,
@@ -130,6 +137,48 @@ const MUIMDXComponents: MDXComponents = {
130137
Mui__material__Zoom: mui.Zoom,
131138
};
132139

140+
const getPyConKR2025SessionUrl = (session: Schemas.BackendAPI.SessionSchema): string => {
141+
const urlSafeTitle = session.title
142+
.replace(/ /g, "-")
143+
.replace(/([.])/g, "_")
144+
.replace(/(?![.0-9A-Za-z---])./g, "");
145+
return `/presentations/${session.id}#${urlSafeTitle}`;
146+
};
147+
148+
const PyConKR2025FallbackImage = React.createElement("img", {
149+
src: PyCon2025Logo,
150+
alt: "PyCon 2025 Logo",
151+
style: { width: "100%", height: "100%", objectFit: "cover", borderRadius: "50%" },
152+
});
153+
154+
const PyConKR2025SessionList: React.FC<React.ComponentProps<typeof Components.MDX.SessionList>> = (props) =>
155+
React.createElement(Components.MDX.SessionList, {
156+
...props,
157+
fallbackImage: PyConKR2025FallbackImage,
158+
getSessionUrl: getPyConKR2025SessionUrl,
159+
});
160+
161+
const PyConKR2025SessionTimeTable: React.FC<React.ComponentProps<typeof Components.MDX.SessionTimeTable>> = (props) =>
162+
React.createElement(Components.MDX.SessionTimeTable, {
163+
...props,
164+
getSessionUrl: getPyConKR2025SessionUrl,
165+
});
166+
167+
const PyConKR2025MobileAccordion: React.FC<object> = () =>
168+
React.createElement(Components.MDX.MobileAccordion, {
169+
marqueeText: "AUG 15 - 17",
170+
marqueeLogoSrc: PyCon2025HostLogoSmall,
171+
hostLogoBigSrc: PyCon2025HostLogoBig,
172+
venueKo: "서울특별시 중구 필동로 1길 30 동국대학교 신공학관",
173+
venueEnLines: ["New Engineering Building, Dongguk University", "Pildong-ro 1-gil, Jung-gu, Seoul, Republic of Korea"],
174+
});
175+
176+
const PyConKR2025MobileCover: React.FC<object> = () =>
177+
React.createElement(Components.MDX.MobileCover, {
178+
coverImageSrc: PyCon2025MobileLogoImage,
179+
coverTitleSrc: PyCon2025MobileLogoTitle,
180+
});
181+
133182
const PyConKRCommonMDXComponents: MDXComponents = {
134183
Common__Components__Lottie: Components.LottiePlayer,
135184
Common__Components__NetworkLottie: Components.NetworkLottiePlayer,
@@ -139,8 +188,10 @@ const PyConKRCommonMDXComponents: MDXComponents = {
139188
Common__Components__MDX__Map: Components.MDX.Map,
140189
Common__Components__MDX__FAQAccordion: Components.MDX.FAQAccordion,
141190
Common__Components__MDX__FullWidthStyledButton: Components.MDX.StyledFullWidthButton,
142-
Common__Components__Session__List: Components.MDX.SessionList,
143-
Common__Components__Session__TimeTable: Components.MDX.SessionTimeTable,
191+
Common__Components__Session__List: PyConKR2025SessionList,
192+
Common__Components__Session__TimeTable: PyConKR2025SessionTimeTable,
193+
Common__Components__MDX__MobileAccordion: PyConKR2025MobileAccordion,
194+
Common__Components__MDX__MobileCover: PyConKR2025MobileCover,
144195
};
145196

146197
const PythonKRShopMDXComponents: MDXComponents = {

packages/common/src/components/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import {
2121
} from "./mdx_components/faq_accordion";
2222
import type { MapPropType as MapComponentPropType } from "./mdx_components/map";
2323
import { Map as MapComponent } from "./mdx_components/map";
24+
import { MobileAccordion as MobileAccordionComponent } from "./mdx_components/mobile_accordion";
25+
import { MobileCover as MobileCoverComponent } from "./mdx_components/mobile_cover";
2426
import { OneDetailsOpener as OneDetailsOpenerComponent } from "./mdx_components/one_details_opener";
2527
import { SessionList as SessionListComponent } from "./mdx_components/session_list";
2628
import { SessionTimeTable as SessionTimeTableComponent } from "./mdx_components/session_timetable";
@@ -51,6 +53,8 @@ namespace Components {
5153

5254
export namespace MDX {
5355
export const Confetti = ConfettiComponent;
56+
export const MobileAccordion = MobileAccordionComponent;
57+
export const MobileCover = MobileCoverComponent;
5458
export const StyledFullWidthButton = StyledFullWidthButtonComponent;
5559
export const PrimaryStyledDetails = PrimaryStyledDetailsComponent;
5660
export const SecondaryStyledDetails = SecondaryStyledDetailsComponent;

packages/common/src/components/mdx_components/mobile_accordion.tsx

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,33 @@ import { AccordionDetails, AccordionSummary, Accordion as MuiAccordion, Stack, T
44
import * as React from "react";
55
import Marquee from "react-fast-marquee";
66

7-
import { useAppContext } from "../../../../../apps/pyconkr/src/contexts/app_context";
8-
import PyCon2025HostLogoBig from "../../assets/pyconkr2025_hostlogo_big.png";
9-
import PyCon2025HostLogoSmall from "../../assets/pyconkr2025_hostlogo_small.png";
7+
import * as Hooks from "../../hooks";
108

11-
const MarqueeAccordion: React.FC = () => {
9+
const MarqueeAccordion: React.FC<{ marqueeText: string; marqueeLogoSrc: string }> = ({ marqueeText, marqueeLogoSrc }) => {
1210
const marqueeWidth = window.innerWidth * 0.9;
1311
const marqueeGradientWidth = window.innerWidth * 0.1;
1412
const items = React.useMemo(() => {
1513
return Array.from({ length: 100 }, () => (
1614
<Stack direction="row" sx={{ gap: 0 }}>
17-
<StyledTypography>AUG 15 - 17</StyledTypography>
18-
<img alt="logo" src={PyCon2025HostLogoSmall} />
15+
<StyledTypography>{marqueeText}</StyledTypography>
16+
<img alt="logo" src={marqueeLogoSrc} />
1917
</Stack>
2018
));
21-
}, []);
19+
}, [marqueeText, marqueeLogoSrc]);
2220

2321
return <Marquee loop={0} gradient={true} gradientWidth={marqueeGradientWidth} speed={30} style={{ width: marqueeWidth }} children={items} />;
2422
};
2523

26-
export const MobileAccordion: React.FC = () => {
27-
const { language } = useAppContext();
24+
type MobileAccordionProps = {
25+
marqueeText: string;
26+
marqueeLogoSrc: string;
27+
hostLogoBigSrc: string;
28+
venueKo: string;
29+
venueEnLines: string[];
30+
};
31+
32+
export const MobileAccordion: React.FC<MobileAccordionProps> = ({ marqueeText, marqueeLogoSrc, hostLogoBigSrc, venueKo, venueEnLines }) => {
33+
const { language } = Hooks.Common.useCommonContext();
2834
const [expanded, setExpanded] = React.useState<boolean>(false);
2935

3036
return (
@@ -44,27 +50,26 @@ export const MobileAccordion: React.FC = () => {
4450
}
4551
sx={{ margin: 0, padding: 0 }}
4652
>
47-
{expanded ? null : <MarqueeAccordion />}
53+
{expanded ? null : <MarqueeAccordion marqueeText={marqueeText} marqueeLogoSrc={marqueeLogoSrc} />}
4854
</AccordionSummary>
4955
<StyledAccordionDetails>
5056
<Stack>
5157
<Stack sx={{ padding: "30px 0px", borderRadius: "16px", alignItems: "center", justifyContent: "center" }}>
52-
<img src={PyCon2025HostLogoBig} alt="PyCon 2025 Host Logo" style={{ width: "90%", height: "90%" }} />
58+
<img src={hostLogoBigSrc} alt="Host Logo" style={{ width: "90%", height: "90%" }} />
5359
</Stack>
5460
{language === "ko" ? (
5561
<Stack direction="column" sx={{ transform: "translateY(-280%)" }}>
5662
<Typography color="#938A85" textAlign="center" fontSize="11px" fontWeight={400}>
57-
{"서울특별시 중구 필동로 1길 30 동국대학교 신공학관"}
63+
{venueKo}
5864
</Typography>
5965
</Stack>
6066
) : (
6167
<Stack direction="column" sx={{ transform: "translateY(-180%)" }}>
62-
<Typography color="#938A85" textAlign="center" fontSize="10px" fontWeight={400}>
63-
{"New Engineering Building, Dongguk University"}
64-
</Typography>
65-
<Typography color="#938A85" textAlign="center" fontWeight={400} fontSize="10px">
66-
{"Pildong-ro 1-gil, Jung-gu, Seoul, Republic of Korea"}
67-
</Typography>
68+
{venueEnLines.map((line, i) => (
69+
<Typography key={i} color="#938A85" textAlign="center" fontWeight={400} fontSize="10px">
70+
{line}
71+
</Typography>
72+
))}
6873
</Stack>
6974
)}
7075
</Stack>

packages/common/src/components/mdx_components/mobile_cover.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
22
import { ButtonBase, Stack, Typography } from "@mui/material";
33
import * as React from "react";
4-
import { useAppContext } from "../../../../../apps/pyconkr/src/contexts/app_context";
5-
import PyCon2025MobileLogoImage from "../../assets/pyconkr2025_main_cover_image.png";
6-
import PyCon2025MobileLogoTitle from "../../assets/pyconkr2025_main_cover_title.png";
74

8-
export const MobileCover: React.FC = () => {
9-
const { language } = useAppContext();
10-
const buttonTitle = language === "ko" ? "티켓 구매하기" : "Buy Ticket";
5+
import * as Hooks from "../../hooks";
6+
7+
type MobileCoverProps = {
8+
coverImageSrc: string;
9+
coverTitleSrc: string;
10+
buttonTextKo?: string;
11+
buttonTextEn?: string;
12+
};
13+
14+
export const MobileCover: React.FC<MobileCoverProps> = ({
15+
coverImageSrc,
16+
coverTitleSrc,
17+
buttonTextKo = "티켓 구매하기",
18+
buttonTextEn = "Buy Ticket",
19+
}) => {
20+
const { language } = Hooks.Common.useCommonContext();
21+
const buttonTitle = language === "ko" ? buttonTextKo : buttonTextEn;
1122

1223
return (
1324
<Stack sx={{ display: "flex", flexDirection: "column", position: "relative", width: "100vw", height: "100vh", overflow: "hidden" }}>
1425
<Stack sx={{ zIndex: 1, position: "absolute", top: 0, left: 0, flex: 1, display: "flex", width: "100%" }}>
15-
<img src={PyCon2025MobileLogoImage} alt="Pycon 2025 Mobile Image" style={{ flex: 1, objectFit: "cover" }} />
26+
<img src={coverImageSrc} alt="Mobile Cover Image" style={{ flex: 1, objectFit: "cover" }} />
1627
</Stack>
1728
<Stack sx={{ zIndex: 2, position: "absolute", top: 96, left: 46 }}>
18-
<img src={PyCon2025MobileLogoTitle} alt="Pycon 2025 Mobile Title" style={{ objectFit: "contain" }} />
29+
<img src={coverTitleSrc} alt="Mobile Cover Title" style={{ objectFit: "contain" }} />
1930
</Stack>
2031
<Stack sx={{ zIndex: 3, position: "absolute", top: 351, left: 48 }}>
2132
<ButtonBase

packages/common/src/components/mdx_components/session_list.tsx

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import * as React from "react";
44
import { Link } from "react-router-dom";
55
import * as R from "remeda";
66

7-
import PyCon2025Logo from "../../assets/pyconkr2025_logo.png";
87
import * as Hooks from "../../hooks";
98
import * as BackendAPISchemas from "../../schemas/backendAPI";
109
import { ErrorFallback } from "../error_handler";
@@ -13,65 +12,72 @@ import { StyledDivider } from "./styled_divider";
1312

1413
const EXCLUDE_CATEGORIES = ["후원사", "Sponsor"];
1514

16-
const SessionItem: React.FC<{ session: BackendAPISchemas.SessionSchema; enableLink?: boolean }> = Suspense.with(
17-
{ fallback: <CircularProgress /> },
18-
({ session, enableLink }) => {
19-
const sessionTitle = session.title.replace("\\n", "\n");
20-
21-
let speakerImgSrc = session.image || "";
22-
if (!speakerImgSrc && R.isArray(session.speakers) && !R.isEmpty(session.speakers)) {
23-
for (const speaker of session.speakers) {
24-
if (speaker.image) {
25-
speakerImgSrc = speaker.image;
26-
break;
27-
}
15+
const SessionItem: React.FC<{
16+
session: BackendAPISchemas.SessionSchema;
17+
enableLink?: boolean;
18+
fallbackImage?: React.ReactNode;
19+
getSessionUrl?: (session: BackendAPISchemas.SessionSchema) => string;
20+
}> = Suspense.with({ fallback: <CircularProgress /> }, ({ session, enableLink, fallbackImage, getSessionUrl }) => {
21+
const sessionTitle = session.title.replace("\\n", "\n");
22+
23+
let speakerImgSrc = session.image || "";
24+
if (!speakerImgSrc && R.isArray(session.speakers) && !R.isEmpty(session.speakers)) {
25+
for (const speaker of session.speakers) {
26+
if (speaker.image) {
27+
speakerImgSrc = speaker.image;
28+
break;
2829
}
2930
}
31+
}
3032

31-
const urlSafeTitle = session.title
32-
.replace(/ /g, "-")
33-
.replace(/([.])/g, "_")
34-
.replace(/(?![0-9A-Za-z---_])./g, "");
35-
const sessionDetailedUrl = `/presentations/${session.id}#${urlSafeTitle}`;
36-
const result = (
37-
<SessionItemContainer direction="row">
38-
<SessionImageContainer
39-
children={<SessionImage src={speakerImgSrc} alt="Session Image" loading="lazy" errorFallback={<SessionImageErrorFallback />} />}
40-
/>
41-
<Stack direction="column" sx={{ flexGrow: 1, py: 0.5, gap: 0.75 }}>
42-
<SessionTitle children={sessionTitle} />
43-
{session.summary && <Typography variant="subtitle1" sx={{ whiteSpace: "pre-wrap" }} children={session.summary} />}
44-
<Stack direction="row" spacing={0.5}>
45-
{session.speakers.map((speaker) => (
46-
<Chip key={speaker.id} size="small" label={speaker.nickname} />
47-
))}
48-
</Stack>
49-
<Stack direction="row" spacing={0.5}>
50-
{session.categories.map((tag) => (
51-
<Chip key={tag.id} variant="outlined" color="primary" size="small" label={tag.name} />
52-
))}
53-
</Stack>
33+
const sessionDetailedUrl = getSessionUrl ? getSessionUrl(session) : undefined;
34+
const result = (
35+
<SessionItemContainer direction="row">
36+
<SessionImageContainer
37+
children={
38+
<SessionImage
39+
src={speakerImgSrc}
40+
alt="Session Image"
41+
loading="lazy"
42+
errorFallback={<SessionImageErrorFallback>{fallbackImage}</SessionImageErrorFallback>}
43+
/>
44+
}
45+
/>
46+
<Stack direction="column" sx={{ flexGrow: 1, py: 0.5, gap: 0.75 }}>
47+
<SessionTitle children={sessionTitle} />
48+
{session.summary && <Typography variant="subtitle1" sx={{ whiteSpace: "pre-wrap" }} children={session.summary} />}
49+
<Stack direction="row" spacing={0.5}>
50+
{session.speakers.map((speaker) => (
51+
<Chip key={speaker.id} size="small" label={speaker.nickname} />
52+
))}
5453
</Stack>
55-
</SessionItemContainer>
56-
);
57-
return (
58-
<>
59-
{enableLink ? <Link to={sessionDetailedUrl} style={{ textDecoration: "none" }} children={result} /> : result}
60-
<StyledDivider />
61-
</>
62-
);
63-
}
64-
);
54+
<Stack direction="row" spacing={0.5}>
55+
{session.categories.map((tag) => (
56+
<Chip key={tag.id} variant="outlined" color="primary" size="small" label={tag.name} />
57+
))}
58+
</Stack>
59+
</Stack>
60+
</SessionItemContainer>
61+
);
62+
return (
63+
<>
64+
{enableLink && sessionDetailedUrl ? <Link to={sessionDetailedUrl} style={{ textDecoration: "none" }} children={result} /> : result}
65+
<StyledDivider />
66+
</>
67+
);
68+
});
6569

6670
type SessionListPropType = {
6771
event?: string;
6872
types?: string | string[];
6973
enableLink?: boolean;
74+
fallbackImage?: React.ReactNode;
75+
getSessionUrl?: (session: BackendAPISchemas.SessionSchema) => string;
7076
};
7177

7278
export const SessionList: React.FC<SessionListPropType> = ErrorBoundary.with(
7379
{ fallback: ErrorFallback },
74-
Suspense.with({ fallback: <CircularProgress /> }, ({ event, types, enableLink }) => {
80+
Suspense.with({ fallback: <CircularProgress /> }, ({ event, types, enableLink, fallbackImage, getSessionUrl }) => {
7581
const { language } = Hooks.Common.useCommonContext();
7682
const backendAPIClient = Hooks.BackendAPI.useBackendClient();
7783
const params = { ...(event && { event }), ...(types && { types: R.isString(types) ? types : types.join(",") }) };
@@ -122,7 +128,7 @@ export const SessionList: React.FC<SessionListPropType> = ErrorBoundary.with(
122128
)}
123129
</Box>
124130
{filteredSessions.map((s) => (
125-
<SessionItem key={s.id} session={s} enableLink={enableLink} />
131+
<SessionItem key={s.id} session={s} enableLink={enableLink} fallbackImage={fallbackImage} getSessionUrl={getSessionUrl} />
126132
))}
127133
</Box>
128134
);
@@ -194,10 +200,8 @@ const SessionImageErrorFallbackBox = styled(Box)(({ theme }) => ({
194200
justifyContent: "center",
195201
}));
196202

197-
const SessionImageErrorFallback: React.FC = () => (
198-
<SessionImageErrorFallbackBox>
199-
<img src={PyCon2025Logo} alt="PyCon 2025 Logo" style={{ width: "100%", height: "100%", objectFit: "cover", borderRadius: "50%" }} />
200-
</SessionImageErrorFallbackBox>
203+
const SessionImageErrorFallback: React.FC<{ children?: React.ReactNode }> = ({ children }) => (
204+
<SessionImageErrorFallbackBox>{children}</SessionImageErrorFallbackBox>
201205
);
202206

203207
const SessionTitle = styled(Typography)({

0 commit comments

Comments
 (0)