You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

402 lines
9.8 KiB

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {Children, useContext, useMemo} from 'react';
import * as React from 'react';
import cn from 'classnames';
import CodeBlock from './CodeBlock';
import {CodeDiagram} from './CodeDiagram';
import ConsoleBlock from './ConsoleBlock';
import Convention from './Convention';
import ExpandableCallout from './ExpandableCallout';
import ExpandableExample from './ExpandableExample';
import {H1, H2, H3, H4} from './Heading';
import HomepageHero from './HomepageHero';
import InlineCode from './InlineCode';
import Intro from './Intro';
import Link from './Link';
import {PackageImport} from './PackageImport';
import Recap from './Recap';
import Sandpack from './Sandpack';
import Diagram from './Diagram';
import DiagramGroup from './DiagramGroup';
import SimpleCallout from './SimpleCallout';
import TerminalBlock from './TerminalBlock';
import YouWillLearnCard from './YouWillLearnCard';
import {Challenges, Hint, Solution} from './Challenges';
import {IconNavArrow} from '../Icon/IconNavArrow';
import ButtonLink from 'components/ButtonLink';
import {TocContext} from './TocContext';
import type {Toc, TocItem} from './TocContext';
function CodeStep({children, step}: {children: any; step: number}) {
return (
<span
data-step={step}
className={cn(
'code-step bg-opacity-10 dark:bg-opacity-20 relative rounded px-[6px] py-[1.5px] border-b-[2px] border-opacity-60',
{
'bg-blue-40 border-blue-40 text-blue-60 dark:text-blue-30':
step === 1,
'bg-yellow-40 border-yellow-40 text-yellow-60 dark:text-yellow-30':
step === 2,
'bg-purple-40 border-purple-40 text-purple-60 dark:text-purple-30':
step === 3,
'bg-green-40 border-green-40 text-green-60 dark:text-green-30':
step === 4,
}
)}>
{children}
</span>
);
}
const P = (p: JSX.IntrinsicElements['p']) => (
<p className="whitespace-pre-wrap my-4" {...p} />
);
const Strong = (strong: JSX.IntrinsicElements['strong']) => (
<strong className="font-bold" {...strong} />
);
const OL = (p: JSX.IntrinsicElements['ol']) => (
<ol className="ml-6 my-3 list-decimal" {...p} />
);
const LI = (p: JSX.IntrinsicElements['li']) => (
<li className="leading-relaxed mb-1" {...p} />
);
const UL = (p: JSX.IntrinsicElements['ul']) => (
<ul className="ml-6 my-3 list-disc" {...p} />
);
const Divider = () => (
<hr className="my-6 block border-b border-border dark:border-border-dark" />
);
const Wip = ({children}: {children: React.ReactNode}) => (
<ExpandableCallout type="wip">{children}</ExpandableCallout>
);
const Gotcha = ({children}: {children: React.ReactNode}) => (
<ExpandableCallout type="gotcha">{children}</ExpandableCallout>
);
const Note = ({children}: {children: React.ReactNode}) => (
<ExpandableCallout type="note">{children}</ExpandableCallout>
);
const Blockquote = ({
children,
...props
}: JSX.IntrinsicElements['blockquote']) => {
return (
<blockquote
className="mdx-blockquote py-4 px-8 my-8 shadow-inner bg-highlight dark:bg-highlight-dark bg-opacity-50 rounded-lg leading-6 flex relative"
{...props}>
<span className="block relative">{children}</span>
</blockquote>
);
};
function LearnMore({
children,
path,
}: {
title: string;
path?: string;
children: any;
}) {
return (
<>
<section className="p-8 mt-16 mb-16 flex flex-row shadow-inner justify-between items-center bg-card dark:bg-card-dark rounded-lg">
<div className="flex-col">
<h2 className="text-primary dark:text-primary-dark font-bold text-2xl leading-tight">
Ready to learn this topic?
</h2>
{children}
{path ? (
<ButtonLink
className="mt-1"
label="Read More"
href={path}
type="primary">
Read More
<IconNavArrow displayDirection="right" className="inline ml-1" />
</ButtonLink>
) : null}
</div>
</section>
<hr className="border-border dark:border-border-dark mb-14" />
</>
);
}
function Math({children}: {children: any}) {
return (
<span
style={{
fontFamily: 'STIXGeneral-Regular, Georgia, serif',
fontSize: '1.2rem',
}}>
{children}
</span>
);
}
function MathI({children}: {children: any}) {
return (
<span
style={{
fontFamily: 'STIXGeneral-Italic, Georgia, serif',
fontSize: '1.2rem',
}}>
{children}
</span>
);
}
function YouWillLearn({
children,
isChapter,
}: {
children: any;
isChapter?: boolean;
}) {
let title = isChapter ? 'In this chapter' : 'You will learn';
return <SimpleCallout title={title}>{children}</SimpleCallout>;
}
// TODO: typing.
function Recipes(props: any) {
return <Challenges {...props} isRecipes={true} />;
}
function AuthorCredit({
author,
authorLink,
}: {
author: string;
authorLink: string;
}) {
return (
<p className="text-center text-secondary dark:text-secondary-dark text-base mt-2">
<cite>
Illustrated by{' '}
{authorLink ? (
<a className="text-link dark:text-link-dark" href={authorLink}>
{author}
</a>
) : (
author
)}
</cite>
</p>
);
}
function Illustration({
caption,
src,
alt,
author,
authorLink,
}: {
caption: string;
src: string;
alt: string;
author: string;
authorLink: string;
}) {
return (
<div className="my-16 mx-0 2xl:mx-auto max-w-4xl 2xl:max-w-6xl">
<figure className="my-8 flex justify-center">
<img
src={src}
alt={alt}
style={{maxHeight: 300}}
className="bg-white rounded-lg"
/>
{caption ? (
<figcaption className="text-center leading-tight mt-4">
{caption}
</figcaption>
) : null}
</figure>
{author ? <AuthorCredit author={author} authorLink={authorLink} /> : null}
</div>
);
}
function IllustrationBlock({
title,
sequential,
author,
authorLink,
children,
}: {
title: string;
author: string;
authorLink: string;
sequential: boolean;
children: any;
}) {
const imageInfos = Children.toArray(children).map(
(child: any) => child.props
);
const images = imageInfos.map((info, index) => (
<figure key={index}>
<div className="bg-white rounded-lg p-4 flex-1 flex xl:p-6 justify-center items-center my-4">
<img src={info.src} alt={info.alt} height={info.height} />
</div>
{info.caption ? (
<figcaption className="text-secondary dark:text-secondary-dark text-center leading-tight mt-4">
{info.caption}
</figcaption>
) : null}
</figure>
));
return (
<div className="my-16 mx-0 2xl:mx-auto max-w-4xl 2xl:max-w-6xl">
{title ? (
<h3 className="text-center text-xl font-bold leading-9 mb-4">
{title}
</h3>
) : null}
{sequential ? (
<ol className="mdx-illustration-block flex">
{images.map((x: any, i: number) => (
<li className="flex-1" key={i}>
{x}
</li>
))}
</ol>
) : (
<div className="mdx-illustration-block">{images}</div>
)}
{author ? <AuthorCredit author={author} authorLink={authorLink} /> : null}
</div>
);
}
type NestedTocRoot = {
item: null;
children: Array<NestedTocNode>;
};
type NestedTocNode = {
item: TocItem;
children: Array<NestedTocNode>;
};
function calculateNestedToc(toc: Toc): NestedTocRoot {
const currentAncestors = new Map<number, NestedTocNode | NestedTocRoot>();
const root: NestedTocRoot = {
item: null,
children: [],
};
const startIndex = 1; // Skip "Overview"
for (let i = startIndex; i < toc.length; i++) {
const item = toc[i];
const currentParent: NestedTocNode | NestedTocRoot =
currentAncestors.get(item.depth - 1) || root;
const node: NestedTocNode = {
item,
children: [],
};
currentParent.children.push(node);
currentAncestors.set(item.depth, node);
}
return root;
}
function InlineToc() {
const toc = useContext(TocContext);
const root = useMemo(() => calculateNestedToc(toc), [toc]);
return <InlineTocItem items={root.children} />;
}
function InlineTocItem({items}: {items: Array<NestedTocNode>}) {
return (
<UL>
{items.map((node) => (
<LI key={node.item.url}>
<Link href={node.item.url}>{node.item.text}</Link>
{node.children.length > 0 && <InlineTocItem items={node.children} />}
</LI>
))}
</UL>
);
}
function LinkWithTodo({href, children, ...props}: JSX.IntrinsicElements['a']) {
if (href?.startsWith('TODO')) {
return children;
}
return (
<Link href={href} {...props}>
{children}
</Link>
);
}
export const MDXComponents = {
p: P,
strong: Strong,
blockquote: Blockquote,
ol: OL,
ul: UL,
li: LI,
h1: H1,
h2: H2,
h3: H3,
h4: H4,
hr: Divider,
a: LinkWithTodo,
code: InlineCode,
pre: CodeBlock,
CodeDiagram,
ConsoleBlock,
Convention,
DeepDive: (props: {
children: React.ReactNode;
title: string;
excerpt: string;
}) => <ExpandableExample {...props} type="DeepDive" />,
Diagram,
DiagramGroup,
FullWidth({children}: {children: any}) {
return children;
},
MaxWidth({children}: {children: any}) {
return <div className="max-w-4xl ml-0 2xl:mx-auto">{children}</div>;
},
Gotcha,
Wip,
HomepageHero,
Illustration,
IllustrationBlock,
Intro,
InlineToc,
LearnMore,
Math,
MathI,
Note,
PackageImport,
Recap,
Recipes,
Sandpack,
TerminalBlock,
YouWillLearn,
YouWillLearnCard,
Challenges,
Hint,
Solution,
CodeStep,
};
for (let key in MDXComponents) {
if (MDXComponents.hasOwnProperty(key)) {
const MDXComponent: any = (MDXComponents as any)[key];
MDXComponent.mdxName = key;
}
}