Skip to content

Commit 521e058

Browse files
committed
split out mdx components
1 parent 84e4e75 commit 521e058

File tree

3 files changed

+197
-87
lines changed

3 files changed

+197
-87
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
'use client';
2+
3+
import React, {Children} from 'react';
4+
5+
const IllustrationContext = React.createContext<{
6+
isInBlock?: boolean;
7+
}>({
8+
isInBlock: false,
9+
});
10+
11+
function AuthorCredit({
12+
author = 'Rachel Lee Nabors',
13+
authorLink = 'https://nearestnabors.com/',
14+
}: {
15+
author: string;
16+
authorLink: string;
17+
}) {
18+
return (
19+
<div className="sr-only group-hover:not-sr-only group-focus-within:not-sr-only hover:sr-only">
20+
<p className="bg-card dark:bg-card-dark text-center text-sm text-secondary dark:text-secondary-dark leading-tight p-2 rounded-lg absolute start-1/2 -top-4 -translate-x-1/2 -translate-y-full group-hover:flex group-hover:opacity-100 after:content-[''] after:absolute after:start-1/2 after:top-[95%] after:-translate-x-1/2 after:border-8 after:border-x-transparent after:border-b-transparent after:border-t-card after:dark:border-t-card-dark opacity-0 transition-opacity duration-300">
21+
<cite>
22+
Illustrated by{' '}
23+
{authorLink ? (
24+
<a
25+
target="_blank"
26+
rel="noreferrer"
27+
className="text-link dark:text-link-dark"
28+
href={authorLink}>
29+
{author}
30+
</a>
31+
) : (
32+
author
33+
)}
34+
</cite>
35+
</p>
36+
</div>
37+
);
38+
}
39+
40+
export function Illustration({
41+
caption,
42+
src,
43+
alt,
44+
author,
45+
authorLink,
46+
}: {
47+
caption: string;
48+
src: string;
49+
alt: string;
50+
author: string;
51+
authorLink: string;
52+
}) {
53+
const {isInBlock} = React.useContext(IllustrationContext);
54+
55+
return (
56+
<div className="relative group before:absolute before:-inset-y-16 before:inset-x-0 my-16 mx-0 2xl:mx-auto max-w-4xl 2xl:max-w-6xl">
57+
<figure className="my-8 flex justify-center">
58+
<img
59+
src={src}
60+
alt={alt}
61+
style={{maxHeight: 300}}
62+
className="rounded-lg"
63+
/>
64+
{caption ? (
65+
<figcaption className="text-center leading-tight mt-4">
66+
{caption}
67+
</figcaption>
68+
) : null}
69+
</figure>
70+
{!isInBlock && <AuthorCredit author={author} authorLink={authorLink} />}
71+
</div>
72+
);
73+
}
74+
75+
const isInBlockTrue = {isInBlock: true};
76+
77+
export function IllustrationBlock({
78+
sequential,
79+
author,
80+
authorLink,
81+
children,
82+
}: {
83+
author: string;
84+
authorLink: string;
85+
sequential: boolean;
86+
children: any;
87+
}) {
88+
const imageInfos = Children.toArray(children).map(
89+
(child: any) => child.props
90+
);
91+
const images = imageInfos.map((info, index) => (
92+
<figure key={index}>
93+
<div className="bg-white rounded-lg p-4 flex-1 flex xl:p-6 justify-center items-center my-4">
94+
<img
95+
className="text-primary"
96+
src={info.src}
97+
alt={info.alt}
98+
height={info.height}
99+
/>
100+
</div>
101+
{info.caption ? (
102+
<figcaption className="text-secondary dark:text-secondary-dark text-center leading-tight mt-4">
103+
{info.caption}
104+
</figcaption>
105+
) : null}
106+
</figure>
107+
));
108+
return (
109+
<IllustrationContext.Provider value={isInBlockTrue}>
110+
<div className="relative group before:absolute before:-inset-y-16 before:inset-x-0 my-16 mx-0 2xl:mx-auto max-w-4xl 2xl:max-w-6xl">
111+
{sequential ? (
112+
<ol className="mdx-illustration-block flex">
113+
{images.map((x: any, i: number) => (
114+
<li className="flex-1" key={i}>
115+
{x}
116+
</li>
117+
))}
118+
</ol>
119+
) : (
120+
<div className="mdx-illustration-block">{images}</div>
121+
)}
122+
<AuthorCredit author={author} authorLink={authorLink} />
123+
</div>
124+
</IllustrationContext.Provider>
125+
);
126+
}

src/components/MDX/InlineToc.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
'use client';
2+
3+
import Link from 'next/link';
4+
import {HTMLAttributes, useContext, useMemo} from 'react';
5+
import {Toc, TocContext, TocItem} from './TocContext';
6+
7+
type NestedTocRoot = {
8+
item: null;
9+
children: Array<NestedTocNode>;
10+
};
11+
12+
type NestedTocNode = {
13+
item: TocItem;
14+
children: Array<NestedTocNode>;
15+
};
16+
17+
function calculateNestedToc(toc: Toc): NestedTocRoot {
18+
const currentAncestors = new Map<number, NestedTocNode | NestedTocRoot>();
19+
const root: NestedTocRoot = {
20+
item: null,
21+
children: [],
22+
};
23+
const startIndex = 1; // Skip "Overview"
24+
for (let i = startIndex; i < toc.length; i++) {
25+
const item = toc[i];
26+
const currentParent: NestedTocNode | NestedTocRoot =
27+
currentAncestors.get(item.depth - 1) || root;
28+
const node: NestedTocNode = {
29+
item,
30+
children: [],
31+
};
32+
currentParent.children.push(node);
33+
currentAncestors.set(item.depth, node);
34+
}
35+
return root;
36+
}
37+
38+
export function InlineToc() {
39+
const toc = useContext(TocContext);
40+
const root = useMemo(() => calculateNestedToc(toc), [toc]);
41+
if (root.children.length < 2) {
42+
return null;
43+
}
44+
return <InlineTocItem items={root.children} />;
45+
}
46+
47+
const LI = (p: HTMLAttributes<HTMLLIElement>) => (
48+
<li className="leading-relaxed mb-1" {...p} />
49+
);
50+
const UL = (p: HTMLAttributes<HTMLUListElement>) => (
51+
<ul className="ms-6 my-3 list-disc" {...p} />
52+
);
53+
54+
function InlineTocItem({items}: {items: Array<NestedTocNode>}) {
55+
return (
56+
<UL>
57+
{items.map((node) => (
58+
<LI key={node.item.url}>
59+
<Link href={node.item.url}>{node.item.text}</Link>
60+
{node.children.length > 0 && <InlineTocItem items={node.children} />}
61+
</LI>
62+
))}
63+
</UL>
64+
);
65+
}

src/components/MDX/MDXComponents.tsx

Lines changed: 6 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@ import YouWillLearnCard from './YouWillLearnCard';
3131
import {Challenges, Hint, Solution} from './Challenges';
3232
import {IconNavArrow} from '../Icon/IconNavArrow';
3333
import ButtonLink from 'components/ButtonLink';
34-
import {TocContext} from './TocContext';
35-
import type {Toc, TocItem} from './TocContext';
34+
3635
import {TeamMember} from './TeamMember';
3736
import {LanguagesContext} from './LanguagesContext';
3837
import {finishedTranslations} from 'utils/finishedTranslations';
3938

4039
import ErrorDecoder from './ErrorDecoder';
4140
import {IconCanary} from '../Icon/IconCanary';
41+
import {InlineToc} from './InlineToc';
42+
import {Illustration, IllustrationBlock} from './Illustration';
4243

4344
function CodeStep({children, step}: {children: any; step: number}) {
4445
return (
@@ -234,35 +235,6 @@ function Recipes(props: any) {
234235
return <Challenges {...props} isRecipes={true} />;
235236
}
236237

237-
function AuthorCredit({
238-
author = 'Rachel Lee Nabors',
239-
authorLink = 'https://nearestnabors.com/',
240-
}: {
241-
author: string;
242-
authorLink: string;
243-
}) {
244-
return (
245-
<div className="sr-only group-hover:not-sr-only group-focus-within:not-sr-only hover:sr-only">
246-
<p className="bg-card dark:bg-card-dark text-center text-sm text-secondary dark:text-secondary-dark leading-tight p-2 rounded-lg absolute start-1/2 -top-4 -translate-x-1/2 -translate-y-full group-hover:flex group-hover:opacity-100 after:content-[''] after:absolute after:start-1/2 after:top-[95%] after:-translate-x-1/2 after:border-8 after:border-x-transparent after:border-b-transparent after:border-t-card after:dark:border-t-card-dark opacity-0 transition-opacity duration-300">
247-
<cite>
248-
Illustrated by{' '}
249-
{authorLink ? (
250-
<a
251-
target="_blank"
252-
rel="noreferrer"
253-
className="text-link dark:text-link-dark"
254-
href={authorLink}>
255-
{author}
256-
</a>
257-
) : (
258-
author
259-
)}
260-
</cite>
261-
</p>
262-
</div>
263-
);
264-
}
265-
266238
// const IllustrationContext = React.createContext<{
267239
// isInBlock?: boolean;
268240
// }>({
@@ -357,59 +329,6 @@ const isInBlockTrue = {isInBlock: true};
357329
// );
358330
// }
359331

360-
type NestedTocRoot = {
361-
item: null;
362-
children: Array<NestedTocNode>;
363-
};
364-
365-
type NestedTocNode = {
366-
item: TocItem;
367-
children: Array<NestedTocNode>;
368-
};
369-
370-
function calculateNestedToc(toc: Toc): NestedTocRoot {
371-
const currentAncestors = new Map<number, NestedTocNode | NestedTocRoot>();
372-
const root: NestedTocRoot = {
373-
item: null,
374-
children: [],
375-
};
376-
const startIndex = 1; // Skip "Overview"
377-
for (let i = startIndex; i < toc.length; i++) {
378-
const item = toc[i];
379-
const currentParent: NestedTocNode | NestedTocRoot =
380-
currentAncestors.get(item.depth - 1) || root;
381-
const node: NestedTocNode = {
382-
item,
383-
children: [],
384-
};
385-
currentParent.children.push(node);
386-
currentAncestors.set(item.depth, node);
387-
}
388-
return root;
389-
}
390-
391-
// function InlineToc() {
392-
// const toc = useContext(TocContext);
393-
// const root = useMemo(() => calculateNestedToc(toc), [toc]);
394-
// if (root.children.length < 2) {
395-
// return null;
396-
// }
397-
// return <InlineTocItem items={root.children} />;
398-
// }
399-
400-
// function InlineTocItem({items}: {items: Array<NestedTocNode>}) {
401-
// return (
402-
// <UL>
403-
// {items.map((node) => (
404-
// <LI key={node.item.url}>
405-
// <Link href={node.item.url}>{node.item.text}</Link>
406-
// {node.children.length > 0 && <InlineTocItem items={node.children} />}
407-
// </LI>
408-
// ))}
409-
// </UL>
410-
// );
411-
// }
412-
413332
type TranslationProgress = 'complete' | 'in-progress';
414333

415334
function LanguageList({progress}: {progress: TranslationProgress}) {
@@ -500,10 +419,10 @@ export const MDXComponents = {
500419
Pitfall,
501420
Deprecated,
502421
Wip,
503-
// Illustration,
504-
// IllustrationBlock,
422+
Illustration,
423+
IllustrationBlock,
505424
Intro,
506-
// InlineToc,
425+
InlineToc,
507426
LanguageList,
508427
LearnMore,
509428
Math,

0 commit comments

Comments
 (0)