Files
protocol/packages/website/ts/pages/docs/page.tsx

179 lines
5.7 KiB
TypeScript

import * as _ from 'lodash';
import * as React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { MDXProvider } from '@mdx-js/react';
import { Animation } from 'ts/components/docs/mdx/animation';
import { Code } from 'ts/components/docs/mdx/code';
import { CodeTabs } from 'ts/components/docs/mdx/code_tabs';
import { Emphasis } from 'ts/components/docs/mdx/emphasis';
import { H1, H2, H3, H4, H5, H6 } from 'ts/components/docs/mdx/headings';
import { HelpCallout } from 'ts/components/docs/mdx/help_callout';
import { HelpfulCta } from 'ts/components/docs/mdx/helpful_cta';
import { Image } from 'ts/components/docs/mdx/image';
import { InlineCode } from 'ts/components/docs/mdx/inline_code';
import { InlineLink } from 'ts/components/docs/mdx/inline_link';
import { NewsletterWidget } from 'ts/components/docs/mdx/newsletter_widget';
import { Note } from 'ts/components/docs/mdx/note';
import { Notification } from 'ts/components/docs/mdx/notification';
import { OrderedList } from 'ts/components/docs/mdx/ordered_list';
import { Paragraph } from 'ts/components/docs/mdx/paragraph';
import { Table } from 'ts/components/docs/mdx/table';
import { UnorderedList } from 'ts/components/docs/mdx/unordered_list';
import { Separator } from 'ts/components/docs/shared/separator';
import { StepLinks } from 'ts/components/docs/shared/step_links';
import { Columns } from 'ts/components/docs/layout/columns';
import { ContentWrapper } from 'ts/components/docs/layout/content_wrapper';
import { DocsPageLayout } from 'ts/components/docs/layout/docs_page_layout';
import { IContents, TableOfContents } from 'ts/components/docs/sidebar/table_of_contents';
import { PageNotFound } from 'ts/pages/docs/page_not_found';
import { docs } from 'ts/style/docs';
import meta from 'ts/utils/algolia_meta.json';
interface IDocsPageProps extends RouteComponentProps<any> {}
interface IDocsPageState {
Component: React.ReactNode;
contents: IContents[];
wasNotFound: boolean;
}
export const DocsPage: React.FC<IDocsPageProps> = props => {
const { page, type, version } = props.match.params;
const { hash } = props.location;
// For api explorer / core-concepts the url does not include the page, i.e. it's only 'docs/core-concepts'
const key = page ? page : type;
// @ts-ignore
const pageMeta = meta[type] && meta[type][key];
if (!pageMeta) {
return <PageNotFound />;
}
const [state, setState] = React.useState<IDocsPageState>({
Component: '',
contents: [],
wasNotFound: false,
});
const { Component, contents, wasNotFound } = state;
const { description, keywords, path, subtitle, title, versions } = pageMeta;
const isLoading = !Component;
// If the route path includes a version, replace the initial version on path
const filePath = versions && version ? path.replace(versions[0], version) : path;
React.useEffect(() => {
void loadPageAsync(filePath);
}, [filePath]);
const loadPageAsync = async (_filePath: string) => {
try {
const component = await import(/* webpackChunkName: "mdx/[request]" */ `mdx/${_filePath}`);
setState({
...state,
Component: component.default,
contents: component.tableOfContents(),
});
if (hash) {
await waitForImages(); // images will push down content when loading, so we wait...
scrollToHash(hash); // ...and then scroll to hash when ready not to push the content down
}
} catch (error) {
setState({ ...state, wasNotFound: true });
}
};
if (wasNotFound) {
return <PageNotFound />;
}
return (
<DocsPageLayout
title={title}
description={description}
keywords={keywords}
subtitle={subtitle}
loading={isLoading}
>
<Columns>
<TableOfContents contents={contents} versions={versions} />
<Separator />
<ContentWrapper>
<MDXProvider components={mdxComponents}>
{/*
// @ts-ignore */}
<Component />
</MDXProvider>
<HelpCallout />
<HelpfulCta page={key} />
</ContentWrapper>
</Columns>
</DocsPageLayout>
);
};
const mdxComponents = {
a: InlineLink,
code: Code,
em: Emphasis,
h1: H1,
h2: H2,
h3: H3,
h4: H4,
h5: H5,
h6: H6,
hr: Separator,
img: Image,
inlineCode: InlineCode,
ol: OrderedList,
p: Paragraph,
table: Table,
ul: UnorderedList,
Animation,
CodeTabs,
Image,
NewsletterWidget,
Note,
Notification,
StepLinks,
};
const waitForImages = async () => {
const images = document.querySelectorAll('img');
return Promise.all(
_.compact(
_.map(images, (img: HTMLImageElement) => {
if (!img.complete) {
// tslint:disable-next-line:no-inferred-empty-object-type
return new Promise(resolve => {
img.addEventListener('load', () => resolve());
});
}
return false;
}),
),
);
};
const scrollToHash = (hash: string): void => {
const element = document.getElementById(hash.substring(1));
if (element) {
const bodyRect = document.body.getBoundingClientRect();
const elemRect = element.getBoundingClientRect();
const elemOffset = elemRect.top - bodyRect.top;
const totalOffset = elemOffset - docs.headerOffset;
window.scrollTo(0, totalOffset);
}
};