Skip to content

Commit 4d09e62

Browse files
committed
feat: 경로에 따라 페이지를 렌더링하는 Component 추가
1 parent 7e71a96 commit 4d09e62

File tree

4 files changed

+94
-21
lines changed

4 files changed

+94
-21
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ const LayoutContainer = styled.div`
5555

5656
const MainContent = styled.main`
5757
flex: 1;
58+
59+
display: flex;
60+
flex-direction: column;
61+
justify-content: center;
62+
align-items: center;
5863
`;
5964

6065
export default function MainLayout() {

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

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,15 @@ import { ErrorBoundary, Suspense } from '@suspensive/react';
77
import * as Common from "@frontend/common";
88

99
const SiteMapRenderer: React.FC = () => {
10-
const { data } = Common.Hooks.BackendAPI.useNestedSiteMapQuery();
11-
return <pre style={{ whiteSpace: "pre-wrap" }}>{JSON.stringify(data, null, 2)}</pre>
12-
};
13-
14-
const parseCss = (t: unknown): React.CSSProperties => R.isString(t) && !R.isEmpty(t) && JSON.parse(t)
15-
16-
const PageRenderer: React.FC<{ id: string }> = ({ id }) => {
17-
const { data } = Common.Hooks.BackendAPI.usePageQuery(id);
18-
19-
return <div style={parseCss(data.css)}>
20-
{
21-
data.sections.map(
22-
(s) => <div style={parseCss(s.css)} key={s.id}>
23-
<Common.Components.MDXRenderer text={s.body} />
24-
</div>
25-
)
26-
}
27-
</div>
10+
const { data } = Common.Hooks.BackendAPI.useFlattenSiteMapQuery();
11+
return <pre style={{ whiteSpace: "pre-wrap" }}>{JSON.stringify(Common.Utils.buildNestedSiteMap(data), null, 2)}</pre>
2812
};
2913

3014
const PageIdSelector: React.FC<{ inputRef: React.Ref<HTMLSelectElement> }> = ({ inputRef }) => {
3115
const { data } = Common.Hooks.BackendAPI.useFlattenSiteMapQuery();
32-
const pageIdList = data.map((sitemap) => sitemap.page);
3316

3417
return <Select inputRef={inputRef}>
35-
{pageIdList.map((pageId) => <MenuItem key={pageId} value={pageId}>{pageId}</MenuItem>)}
18+
{data.map((siteMap) => <MenuItem key={siteMap.id} value={siteMap.page}>{siteMap.name}</MenuItem>)}
3619
</Select>
3720
}
3821

@@ -56,6 +39,6 @@ export const BackendTestPage: React.FC = () => {
5639
<br />
5740
<Button variant="outlined" onClick={() => setPageId(inputRef.current?.value ?? null)}>페이지 렌더링</Button>
5841
<br />
59-
{R.isString(pageId) ? <SuspenseWrapper><PageRenderer id={pageId} /></SuspenseWrapper> : <>페이지를 선택해주세요.</>}
42+
{R.isString(pageId) ? <SuspenseWrapper><Common.Components.PageRenderer id={pageId} /></SuspenseWrapper> : <>페이지를 선택해주세요.</>}
6043
</Stack>
6144
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import * as React from "react";
2+
import { useLocation } from 'react-router-dom';
3+
import * as R from "remeda";
4+
5+
import { CircularProgress } from '@mui/material';
6+
import { ErrorBoundary, Suspense } from '@suspensive/react';
7+
8+
import styled from '@emotion/styled';
9+
import Components from '../components';
10+
import Hooks from "../hooks";
11+
import Schemas from "../schemas";
12+
import Utils from '../utils';
13+
14+
const InitialPageStyle: React.CSSProperties = {
15+
width: '100%',
16+
height: '100%',
17+
display: 'flex',
18+
justifyContent: 'center',
19+
alignItems: 'center',
20+
flexDirection: 'column',
21+
}
22+
23+
const InitialSectionStyle: React.CSSProperties = {
24+
width: '100%',
25+
}
26+
27+
export const PageRenderer: React.FC<{ id: string }> = ({ id }) => {
28+
const { data } = Hooks.BackendAPI.usePageQuery(id);
29+
30+
return <div style={{ ...InitialPageStyle, ...Utils.parseCss(data.css) }}>
31+
{
32+
data.sections.map(
33+
(s) => <div style={{ ...InitialSectionStyle, ...Utils.parseCss(s.css) }} key={s.id}>
34+
<Components.MDXRenderer text={s.body} />
35+
</div>
36+
)
37+
}
38+
</div>
39+
};
40+
41+
const AsyncDynamicRoutePage: React.FC = () => {
42+
const location = useLocation();
43+
const { data } = Hooks.BackendAPI.useFlattenSiteMapQuery();
44+
const nestedSiteMap = Utils.buildNestedSiteMap(data);
45+
46+
const currentRouteCodes = ['', ...location.pathname.split('/').filter((code) => !R.isEmpty(code))];
47+
48+
let currentSitemap: Schemas.NestedSiteMapSchema | null | undefined = nestedSiteMap[currentRouteCodes[0]];
49+
if (currentSitemap === undefined) {
50+
return <>404 Not Found</>;
51+
}
52+
53+
for (const routeCode of currentRouteCodes.slice(1)) {
54+
if ((currentSitemap = currentSitemap.children[routeCode]) === undefined) {
55+
break;
56+
}
57+
}
58+
59+
return R.isNullish(currentSitemap)
60+
? <>404 Not Found</>
61+
: <PageRenderer id={currentSitemap.page} />
62+
}
63+
64+
const FullPage = styled.div`
65+
width: 100%;
66+
height: 100%;
67+
display: flex;
68+
justify-content: center;
69+
align-items: center;
70+
flex-direction: column;
71+
`
72+
73+
export const DynamicRoutePage: React.FC = () => <FullPage>
74+
<ErrorBoundary fallback={Components.ErrorFallback}>
75+
<Suspense fallback={<CircularProgress />}>
76+
<AsyncDynamicRoutePage />
77+
</Suspense>
78+
</ErrorBoundary>
79+
</FullPage>;

packages/common/src/components/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import { CommonContextProvider as CommonContextProviderComponent } from './common_context';
2+
import {
3+
DynamicRoutePage as DynamicRoutePageComponent,
4+
PageRenderer as PageRendererComponent,
5+
} from './dynamic_route';
26
import { ErrorFallback as ErrorFallbackComponent } from './error_handler';
37
import { MDXRenderer as MDXRendererComponent } from "./mdx";
48
import { PythonKorea as PythonKoreaComponent } from './pythonkorea';
59

610
namespace Components {
711
export const CommonContextProvider = CommonContextProviderComponent;
12+
export const DynamicRoutePage = DynamicRoutePageComponent;
13+
export const PageRenderer = PageRendererComponent;
814
export const MDXRenderer = MDXRendererComponent;
915
export const PythonKorea = PythonKoreaComponent;
1016
export const ErrorFallback = ErrorFallbackComponent;

0 commit comments

Comments
 (0)