William Nemenchainitial commit
86fb32e9/11/2022, 1:13:39 AM
.ts
TypeScript
(application/typescript)
// 3rd-party
import type { ReactView } from "@ethicdevs/react-monolith";
import React from "react";
import styled, { css } from "styled-components";
// app
import type { CommonProps, Page, WithThemeSchemeProp } from "../types";
import { Layout, MarkdownToJsx, TextEllipsis } from "../components";
import { NamedColors } from "../utils/style";

export interface DocPageViewProps extends CommonProps {
  page: Page;
}

const DocPageView: ReactView<DocPageViewProps> = ({ commonProps, page }) => {
  const { themeScheme } = commonProps;
  return (
    <Layout
      {...commonProps}
      showSideMenu
      showSideExamples={
        page.metas.showExamples != null
          ? Boolean(page.metas.showExamples)
          : false
      }
    >
      <StyledDocPageWrapper>
        {page.metas.title != null && (
          <h1 style={{ width: "100%", textAlign: "center" }}>
            {page.metas.title}
          </h1>
        )}
        {page.metas.summary != null && (
          <div
            style={{
              width: "100%",
              textAlign: "center",
              color: NamedColors.TEXT_MUTED[themeScheme],
            }}
          >
            <MarkdownToJsx
              themeScheme={themeScheme}
              markdown={page.metas.summary}
            />
          </div>
        )}
        <StyledDocContentRow>
          <StyledDocContentCol>
            <MarkdownToJsx themeScheme={themeScheme} markdown={page.content} />
          </StyledDocContentCol>
          {page.tableOfContent != null && page.tableOfContent.length >= 1 && (
            <StyledDocTableOfContentsCol>
              <h4
                style={{
                  margin: 0,
                  marginBottom: 16,
                  color: NamedColors.TEXT_DEFAULT[themeScheme],
                }}
              >
                Table of Contents
              </h4>
              {page.tableOfContent.map((entry) => (
                <StyledDocTableOfContentsItem
                  depth={entry.depth}
                  href={`#${entry.targetAnchor}`}
                  key={entry.targetAnchor}
                  title={entry.title}
                  themeScheme={themeScheme}
                >
                  <TextEllipsis numberOfLines={2}>{entry.title}</TextEllipsis>
                </StyledDocTableOfContentsItem>
              ))}
            </StyledDocTableOfContentsCol>
          )}
        </StyledDocContentRow>
        {(page.metas?.prevPage != null || page.metas?.nextPage != null) && (
          <StyledDocNavigationRow themeScheme={themeScheme}>
            {page.metas?.prevPage != null ? (
              <StyledDocNavigationLinkCol
                href={page.metas.prevPage.href}
                themeScheme={themeScheme}
              >
                <span>&lsaquo; {page.metas.prevPage.title}</span>
                <span>{page.metas.prevPage?.subtitle || "Previous"}</span>
              </StyledDocNavigationLinkCol>
            ) : (
              <StyledDocNavigationLinkCol themeScheme={themeScheme} />
            )}
            {page.metas?.nextPage != null ? (
              <StyledDocNavigationLinkCol
                href={page.metas.nextPage.href}
                themeScheme={themeScheme}
              >
                <span>{page.metas.nextPage.title} &rsaquo;</span>
                <span>{page.metas.nextPage?.subtitle || "Up Next"}</span>
              </StyledDocNavigationLinkCol>
            ) : (
              <StyledDocNavigationLinkCol themeScheme={themeScheme} />
            )}
          </StyledDocNavigationRow>
        )}
      </StyledDocPageWrapper>
    </Layout>
  );
};

const StyledDocPageWrapper = styled.div`
  display: flex;
  flex-flow: column nowrap;
  justify-content: flex-start;
  align-items: flex-start;

  gap: 24px;
  padding: 24px;
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
`;

const StyledDocContentRow = styled.div`
  display: flex;
  flex-flow: row wrap;
  justify-content: flex-start;
  align-items: flex-start;
  width: 100%;
`;

const StyledDocContentCol = styled.main`
  display: flex;
  flex-flow: column wrap;
  justify-content: flex-start;
  align-items: flex-start;
  flex: 1;
`;

const StyledDocTableOfContentsCol = styled.aside`
  display: flex;
  flex-flow: column wrap;
  justify-content: flex-start;
  align-items: flex-start;
  flex: 0.3;

  position: sticky;
  top: ${72 + 24}px;

  margin-left: 24px;
`;

const StyledDocTableOfContentsItem = styled.a<
  { depth: number } & WithThemeSchemeProp
>`
  dipslay: flex;
  flex-flow: row nowrap;
  justify-content: flex-start;
  align-item: center;

  width: 100%;

  font-size: 14px;
  font-weight: bold;
  line-height: 16px;
  text-decoration: none;

  &:hover {
    text-decoration: underline;
  }

  padding: 4px 10px 4px 0px;
  ${({ depth }) => css`
    margin-left: ${Math.max(0, (depth - 2) * 8)}px;
  `};

  ${({ themeScheme }) => css`
    color: ${NamedColors.TEXT_MUTED[themeScheme]};
  `};
`;

const StyledDocNavigationLinkCol = styled.a<WithThemeSchemeProp>`
  display: flex;
  flex-flow: column nowrap;
  justify-content: flex-start;
  align-items: flex-start;

  ${({ themeScheme }) => css`
    color: ${NamedColors.TEXT_LINK[themeScheme]};
  `};

  font-weight: bold;
  text-decoration: none;

  &:hover {
    text-decoration: underline;
  }

  & > span:first-child {
    font-size: 16px;
  }

  & > span:last-child {
    margin-top: 4px;
    font-size: 14px;
    font-weight: normal;
    opacity: 0.87;
  }
`;

const StyledDocNavigationRow = styled.div<WithThemeSchemeProp>`
  display: flex;
  flex-flow: row nowrap;
  justify-content: space-between;
  align-items: center;

  width: 100%;
  margin-top: 0;
  padding-top: 24px;

  ${({ themeScheme }) => css`
    border-top: 1px solid ${NamedColors.BORDER_DEFAULT[themeScheme]};
  `};

  & > ${StyledDocNavigationLinkCol}:last-child {
    align-items: flex-end;
  }
`;

DocPageView.displayName = "DocPageView";
export default DocPageView;