Files
protocol/packages/website/ts/pages/docs/page.tsx
2019-08-24 00:03:49 +02:00

156 lines
5.3 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 { 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 { 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 { Notification } from 'ts/components/docs/mdx/notification';
import { OrderedList } from 'ts/components/docs/mdx/ordered_list';
import { Table } from 'ts/components/docs/mdx/table';
import { UnorderedList } from 'ts/components/docs/mdx/unordered_list';
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 { Separator } from 'ts/components/docs/separator';
import { IContents, TableOfContents } from 'ts/components/docs/sidebar/table_of_contents';
import { FullscreenMessage } from 'ts/pages/fullscreen_message';
import { Paragraph } from 'ts/components/text';
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 [state, setState] = React.useState<IDocsPageState>({
Component: '',
contents: [],
wasNotFound: false,
});
const { Component, contents, wasNotFound } = state;
const isLoading = !Component && !wasNotFound;
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 { path, subtitle, title, versions } = meta[key];
React.useEffect(() => {
void loadPageAsync(path);
}, [path]);
const loadPageAsync = async (path: string) => {
try {
const component = await import(`mdx/${path}`);
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 });
}
};
const waitForImages = async () => {
const images = document.querySelectorAll('img');
return Promise.all(
_.compact(
_.map(images, (img: HTMLImageElement) => {
if (!img.complete) {
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);
}
};
return (
<DocsPageLayout title={wasNotFound ? '404' : title} subtitle={subtitle} loading={isLoading}>
{wasNotFound ? (
<FullscreenMessage
headerText={'Not found'}
headerTextColor={'#000'}
bodyText={"Hm... looks like we couldn't find what you are looking for."}
/>
) : (
<Columns>
<TableOfContents contents={contents} versions={versions} />
<Separator />
<ContentWrapper>
<MDXProvider components={mdxComponents}>
{/*
// @ts-ignore */}
<Component />
</MDXProvider>
<NewsletterWidget />
<HelpCallout />
<HelpfulCta page={key} />
</ContentWrapper>
</Columns>
)}
</DocsPageLayout>
);
};
const mdxComponents = {
code: Code,
h1: H1,
h2: H2,
h3: H3,
h4: H4,
h5: H5,
h6: H6,
hr: Separator,
inlineCode: InlineCode,
a: InlineLink,
ol: OrderedList,
p: Paragraph,
table: Table,
ul: UnorderedList,
Animation,
CodeTabs,
Notification,
};