Skip to content

Commit 7c9efc5

Browse files
authored
Merge pull request #7 from pythonkr/feature/mdx-import-possible
MDXRenderer의 기본 컴포넌트가 정상 노출되도록 수정
2 parents e237b32 + 567ef5a commit 7c9efc5

File tree

13 files changed

+254
-137
lines changed

13 files changed

+254
-137
lines changed

apps/pyconkr/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<meta name="robots" content="index, follow" />
4040

4141
<script src="https://cdn.iamport.kr/v1/iamport.js"></script>
42+
<link rel="stylesheet" href="github-markdown.css">
4243

4344
<title>PyCon Korea 2025</title>
4445
</head>

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ const HeaderContainer = styled.header`
9191
justify-content: space-between;
9292
align-items: center;
9393
position: relative;
94+
95+
ul {
96+
list-style: none;
97+
}
9498
`;
9599

96100
const HeaderLogo = styled.div`

apps/pyconkr/src/components/pages/test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { ShopTestPage } from "../../debug/page/shop_test";
88
type SelectedTabType = "shop" | "mdi";
99

1010
export const Test: React.FC = () => {
11-
const [selectedTab, setSelectedTab] = React.useState<SelectedTabType>("shop");
11+
const [selectedTab, setSelectedTab] = React.useState<SelectedTabType>("mdi");
1212

1313
return (
1414
<Box>

apps/pyconkr/src/debug/page/mdi_test.tsx

Lines changed: 95 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,105 @@
1-
import styled from "@emotion/styled";
2-
import { evaluate } from "@mdx-js/mdx";
3-
import { MDXProvider } from "@mdx-js/react";
4-
import {
5-
Box,
6-
Button,
7-
Card,
8-
CardContent,
9-
Stack,
10-
TextField,
11-
Typography,
12-
} from "@mui/material";
13-
import React, { useState } from "react";
14-
import * as runtime from "react/jsx-runtime";
15-
16-
// styled 컴포넌트 방식
17-
const StyledCard = styled(Card)`
18-
transition: transform 0.2s;
19-
&:hover {
20-
transform: translateY(-4px);
21-
}
22-
`;
23-
24-
const StyledCardContent = styled(CardContent)`
25-
animation: fadeIn 0.5s ease-in;
26-
@keyframes fadeIn {
27-
from {
28-
opacity: 0;
29-
}
30-
to {
31-
opacity: 1;
32-
}
33-
}
34-
`;
1+
import React from "react";
2+
3+
import { Box, Button, Card, CardContent, TextField, Typography } from "@mui/material";
4+
5+
import * as Common from "@frontend/common";
6+
7+
const LOCAL_STEORAGE_KEY = "mdi_test_input";
8+
const MDX_TEST_STRING = `\
9+
MDX 간단 사용 설명서
10+
11+
긴 가로줄을 넣고 싶다면, \`---\`를 사용해주세요!
12+
13+
---
14+
15+
제목은 아래와 같이 표현해요.
16+
# H1
17+
## H2
18+
### H3
19+
#### H4
20+
##### H5
21+
###### H6
22+
23+
---
24+
25+
**굵은 글자는 이렇게**
26+
*이탤릭(기울어진 글자)는 요렇게*
27+
응용 표현으로 ***굵으면서 기울어진 글자는 요렇게***
28+
29+
[링크는 이렇게 사용하고요,](https://pycon.kr)
30+
변수명과 같은 짧은 코드는 \`이렇게\` 표현해요!
31+
\`\`\`
32+
# 긴 코드는 이렇게 표현할 수 있어요.
33+
import antigravity
34+
\`\`\`
35+
36+
---
37+
38+
HTML 태그 중 일부를 사용할 수 있어요!
39+
40+
예를 들면 <sub>sub</sub> 태그나
41+
위로 가는 <sup>sup</sup> 태그도 있고요,
42+
밑줄도 <ins>표현할 수 있죠!</ins>
43+
44+
---
45+
46+
> 인용구는 이렇게 표현해요.
47+
> 여러 줄을 표현할수도 있고요!
48+
49+
---
50+
51+
사진 첨부는 이렇게 해요!
52+
![OctoCat](https://myoctocat.com/assets/images/base-octocat.svg)
53+
54+
만약 크기 조절을 하고 싶다면 HTML 태그도 가능해요!
55+
<img width="150px" src="https://myoctocat.com/assets/images/base-octocat.svg" />
56+
57+
---
58+
59+
- 순번이 없는 목록은 이렇게 사용해요.
60+
- 만약 하위 항목을 표현하고 싶으시다면
61+
- 이렇게 앞에 공백 2개를 붙여주세요!
62+
63+
---
64+
65+
1. 순번이 있는 목록은 이렇게 사용해요.
66+
2. 실수로 다음의 숫자를 잘못 적어도
67+
1. 자동으로 제대로 3번으로 나와요!
68+
69+
---
70+
71+
{ /*
72+
화면 상에는 노출되지 않는 주석은 이렇게 사용해요.
73+
주의하실 점은, 서버에서 클라이언트로 페이지의 내용을 응답할때는 요 주석 데이터도 같이 보내지므로, 절때 민감한 내용을 주석에 담지는 말아주세요!
74+
*/ }
75+
`
76+
77+
const getMdxInputFromLocalStorage: () => string = () => {
78+
const input = localStorage.getItem(LOCAL_STEORAGE_KEY);
79+
return input ? input : "";
80+
}
81+
82+
const setMdxInputToLocalStorage: (input: string) => string = (input) => {
83+
localStorage.setItem(LOCAL_STEORAGE_KEY, input);
84+
return input;
85+
}
3586

3687
export const MdiTestPage: React.FC = () => {
37-
const [mdxInput, setMdxInput] = useState("");
38-
const [Content, setContent] = useState<React.ComponentType>(() => () => null);
39-
const [count, setCount] = useState(0);
40-
41-
const handleInputChange = async (text: string) => {
42-
setMdxInput(text);
43-
try {
44-
const { default: Content } = await evaluate(text, {
45-
...runtime,
46-
baseUrl: import.meta.url,
47-
});
48-
setContent(() => Content);
49-
} catch (error) {
50-
console.error("MDX 변환 오류:", error);
51-
}
52-
};
88+
const inputRef = React.useRef<HTMLTextAreaElement>(null);
89+
const [mdxInput, setMdxInput] = React.useState(getMdxInputFromLocalStorage());
5390

5491
return (
5592
<Box sx={{ p: 3 }}>
56-
<StyledCard sx={{ mb: 3 }}>
57-
<StyledCardContent>
58-
<Typography variant="h4" gutterBottom>
59-
MUI 테스트
60-
</Typography>
61-
<Stack direction="row" spacing={2} sx={{ mb: 2 }}>
62-
<Button variant="contained" onClick={() => setCount(count + 1)}>
63-
카운트: {count}
64-
</Button>
65-
<Button variant="outlined" color="secondary">
66-
세컨더리
67-
</Button>
68-
</Stack>
69-
<TextField
70-
fullWidth
71-
label="테스트 입력"
72-
variant="outlined"
73-
sx={{ mb: 2 }}
74-
/>
75-
</StyledCardContent>
76-
</StyledCard>
77-
78-
<Typography variant="h5" gutterBottom>
79-
MDX 에디터
80-
</Typography>
81-
<TextField
82-
multiline
83-
fullWidth
84-
minRows={4}
85-
value={mdxInput}
86-
onChange={(e) => handleInputChange(e.target.value)}
87-
sx={{ mb: 2 }}
88-
/>
93+
<Typography variant="h5" gutterBottom>MDX 에디터</Typography>
94+
<TextField inputRef={inputRef} defaultValue={mdxInput} multiline fullWidth minRows={4} sx={{ my: 2 }} />
95+
<Button variant="contained" onClick={() => inputRef.current && setMdxInput(setMdxInputToLocalStorage(inputRef.current.value))}>변환</Button>
96+
&nbsp;
97+
<Button variant="contained" onClick={() => setMdxInput(MDX_TEST_STRING)}>테스트용 Help Text 로딩</Button>
98+
<br />
99+
<br />
89100
<Card>
90101
<CardContent>
91-
<MDXProvider>
92-
<Content />
93-
</MDXProvider>
102+
<Common.Components.MDXRenderer text={mdxInput} />
94103
</CardContent>
95104
</Card>
96105
</Box>

apps/pyconkr/src/main.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ const queryClient = new QueryClient({
3535
});
3636

3737
const CommonOptions: Common.Contexts.ContextOptions = {
38-
baseUrl: import.meta.env.VITE_API_BASE_URL,
38+
debug: import.meta.env.MODE === "development",
39+
baseUrl: '.',
3940
};
4041

4142
const ShopOptions: Shop.Contexts.ContextOptions = {

apps/pyconkr/src/styles/globalStyles.ts

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,6 @@ export const muiTheme = createTheme({
5757
export const globalStyles = css`
5858
@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css");
5959
60-
* {
61-
margin: 0;
62-
padding: 0;
63-
box-sizing: border-box;
64-
65-
-webkit-user-drag: none;
66-
-khtml-user-drag: none;
67-
-moz-user-drag: none;
68-
-o-user-drag: none;
69-
user-drag: none;
70-
}
71-
7260
html,
7361
body {
7462
font-family:
@@ -98,21 +86,15 @@ export const globalStyles = css`
9886
word-break: keep-all;
9987
overflow-wrap: break-all;
10088
89+
-webkit-user-drag: none;
90+
-khtml-user-drag: none;
91+
-moz-user-drag: none;
92+
-o-user-drag: none;
93+
user-drag: none;
10194
}
10295
10396
a {
10497
text-decoration: none;
10598
color: inherit;
10699
}
107-
108-
button {
109-
border: none;
110-
background: none;
111-
cursor: pointer;
112-
}
113-
114-
ul,
115-
ol {
116-
list-style: none;
117-
}
118100
`;

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
"@tanstack/react-query": "^5.76.1",
3232
"astring": "^1.9.0",
3333
"axios": "^1.9.0",
34+
"github-markdown-css": "^5.8.1",
3435
"globals": "^15.15.0",
36+
"mui-mdx-components": "^0.5.0",
3537
"notistack": "^3.0.2",
3638
"react": "^19.1.0",
3739
"react-dom": "^19.1.0",
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import * as React from "react";
2+
3+
import { Button, Typography } from "@mui/material";
4+
import { Suspense } from "@suspensive/react";
5+
6+
import CommonContext from '../hooks/';
7+
8+
const DetailedErrorFallback: React.FC<{ error: Error, reset: () => void }> = ({ error, reset }) => {
9+
const errorObject = Object.getOwnPropertyNames(error).reduce((acc, key) => ({ ...acc, [key]: (error as unknown as { [key: string]: unknown })[key] }), {});
10+
return <>
11+
<Typography variant="body2" color="error">error.message = {error.message}</Typography>
12+
<details open>
13+
<summary>오류 상세</summary>
14+
<pre style={{
15+
whiteSpace: "pre-wrap",
16+
backgroundColor: "#f5f5f5",
17+
padding: "1em",
18+
borderRadius: "4px",
19+
userSelect: "text",
20+
}}>
21+
<code>{JSON.stringify(errorObject, null, 2)}</code>
22+
</pre>
23+
</details>
24+
<br />
25+
<Button variant="outlined" onClick={reset}>다시 시도</Button>
26+
</>;
27+
};
28+
29+
const SimplifiedErrorFallback: React.FC<{ reset: () => void }> = ({ reset }) => {
30+
return <>
31+
<Typography variant="body2" color="error">
32+
문제가 발생했습니다, 잠시 후 다시 시도해주세요.<br />
33+
만약 문제가 계속 발생한다면, 파이콘 한국 준비 위원회에게 알려주세요!<br />
34+
<br />
35+
An error occurred, please try again later.<br />
36+
If the problem persists, please let the PyCon Korea organizing committee know!
37+
</Typography>
38+
<br />
39+
<Button variant="outlined" onClick={reset}>다시 시도 | Retry</Button>
40+
</>;
41+
}
42+
43+
export const ErrorFallback: React.FC<{ error: Error, reset: () => void }> = ({ error, reset }) => {
44+
const InnerErrorFallback: React.FC<{ error: Error, reset: () => void }> = ({ error, reset }) => {
45+
const { debug } = CommonContext.useCommonContext();
46+
return debug ? <DetailedErrorFallback error={error} reset={reset} /> : <SimplifiedErrorFallback reset={reset} />;
47+
}
48+
49+
return <Suspense fallback={<>로딩 중...</>}><InnerErrorFallback error={error} reset={reset} /></Suspense>
50+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { CommonContextProvider as CommonContextProviderComponent } from './common_context';
2+
import { ErrorFallback as ErrorFallbackComponent } from './error_handler';
23
import { MDXRenderer as MDXRendererComponent } from "./mdx";
34
import { PythonKorea as PythonKoreaComponent } from './pythonkorea';
45

56
namespace Components {
67
export const CommonContextProvider = CommonContextProviderComponent;
78
export const MDXRenderer = MDXRendererComponent;
89
export const PythonKorea = PythonKoreaComponent;
10+
export const ErrorFallback = ErrorFallbackComponent;
911
}
1012

1113
export default Components;

0 commit comments

Comments
 (0)