refactor: inline table of contents

This commit is contained in:
iCrawl
2022-08-17 23:21:23 +02:00
parent 872ce801a0
commit 17ab0e652c
14 changed files with 97 additions and 90 deletions

View File

@@ -42,7 +42,7 @@ export function CodeListing({
{optional ? '?' : ''}
</Title>
<Title order={4}>{separator}</Title>
<Title order={4} className="font-mono break-all">
<Title sx={{ wordBreak: 'break-all' }} order={4} className="font-mono">
<HyperlinkedText tokens={typeTokens} />
</Title>
</Group>

View File

@@ -1,4 +1,4 @@
import { Group, Stack, Title, Text, Box } from '@mantine/core';
import { Group, Stack, Title, Text, Box, MediaQuery, Aside, ScrollArea } from '@mantine/core';
import { useMediaQuery } from '@mantine/hooks';
import type { ReactNode } from 'react';
import { VscListSelection, VscSymbolParameter } from 'react-icons/vsc';
@@ -6,8 +6,10 @@ import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import { HyperlinkedText } from './HyperlinkedText';
import { Section } from './Section';
import { TableOfContentsItems } from './TableOfContentsItems';
import { TypeParamTable } from './TypeParamTable';
import { TSDoc } from './tsdoc/TSDoc';
import type { DocClass } from '~/DocModel/DocClass';
import type { DocItem } from '~/DocModel/DocItem';
import type { AnyDocNodeJSON } from '~/DocModel/comment/CommentNode';
import { generateIcon } from '~/util/icon';
@@ -23,6 +25,7 @@ export interface DocContainerProps {
implementsTokens?: TokenDocumentation[][];
typeParams?: TypeParameterData[];
comment?: AnyDocNodeJSON | null;
methods?: ReturnType<DocClass['toJSON']>['methods'] | null;
}
export function DocContainer({
@@ -34,68 +37,85 @@ export function DocContainer({
children,
extendsTokens,
implementsTokens,
methods,
}: DocContainerProps) {
const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
return (
<Stack>
<Title order={2} ml="xs" className="break-all">
<Group>
{generateIcon(kind)}
{name}
</Group>
</Title>
<Group>
<Stack sx={{ flexGrow: 1 }}>
<Title sx={{ wordBreak: 'break-all' }} order={2} ml="xs">
<Group>
{generateIcon(kind)}
{name}
</Group>
</Title>
<Section title="Summary" icon={<VscListSelection />} padded dense={matches}>
{summary ? <TSDoc node={summary} /> : <Text>No summary provided.</Text>}
</Section>
<Section title="Summary" icon={<VscListSelection />} padded dense={matches}>
{summary ? <TSDoc node={summary} /> : <Text>No summary provided.</Text>}
</Section>
<Box px="xs" pb="xs">
<SyntaxHighlighter
wrapLongLines
language="typescript"
style={vscDarkPlus}
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
>
{excerpt}
</SyntaxHighlighter>
</Box>
<Box px="xs" pb="xs">
<SyntaxHighlighter
wrapLongLines
language="typescript"
style={vscDarkPlus}
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
>
{excerpt}
</SyntaxHighlighter>
</Box>
{extendsTokens?.length ? (
<Group noWrap>
<Title order={3} ml="xs">
Extends
</Title>
<Text className="font-mono break-all">
<HyperlinkedText tokens={extendsTokens} />
</Text>
</Group>
) : null}
{implementsTokens?.length ? (
<Group noWrap>
<Title order={3} ml="xs">
Implements
</Title>
<Text className="font-mono break-all">
{implementsTokens.map((token, idx) => (
<>
<HyperlinkedText tokens={token} />
{idx < implementsTokens.length - 1 ? ', ' : ''}
</>
))}
</Text>
</Group>
) : null}
<Stack>
{typeParams?.length ? (
<Section title="Type Parameters" icon={<VscSymbolParameter />} padded dense={matches} defaultClosed>
<TypeParamTable data={typeParams} />
</Section>
{extendsTokens?.length ? (
<Group noWrap>
<Title order={3} ml="xs">
Extends
</Title>
<Text sx={{ wordBreak: 'break-all' }} className="font-mono">
<HyperlinkedText tokens={extendsTokens} />
</Text>
</Group>
) : null}
<Stack>{children}</Stack>
{implementsTokens?.length ? (
<Group noWrap>
<Title order={3} ml="xs">
Implements
</Title>
<Text sx={{ wordBreak: 'break-all' }} className="font-mono">
{implementsTokens.map((token, idx) => (
<>
<HyperlinkedText tokens={token} />
{idx < implementsTokens.length - 1 ? ', ' : ''}
</>
))}
</Text>
</Group>
) : null}
<Stack>
{typeParams?.length ? (
<Section title="Type Parameters" icon={<VscSymbolParameter />} padded dense={matches} defaultClosed>
<TypeParamTable data={typeParams} />
</Section>
) : null}
<Stack>{children}</Stack>
</Stack>
</Stack>
</Stack>
{kind === 'Class' && methods ? (
<MediaQuery smallerThan="md" styles={{ display: 'none' }}>
<Aside
sx={{ backgroundColor: 'transparent' }}
hiddenBreakpoint="md"
width={{ md: 200, lg: 300 }}
withBorder={false}
>
<ScrollArea p="xs">
<TableOfContentsItems members={methods}></TableOfContentsItems>
</ScrollArea>
</Aside>
</MediaQuery>
) : null}
</Group>
);
}

View File

@@ -34,9 +34,9 @@ export function MethodItem({ data }: { data: MethodResolvable }) {
<Badge variant="filled">Protected</Badge>
) : null}
{data.kind === 'Method' && method.static ? <Badge variant="filled">Static</Badge> : null}
<Title order={4} className="font-mono break-all">{`${getShorthandName(data)}`}</Title>
<Title sx={{ wordBreak: 'break-all' }} order={4} className="font-mono">{`${getShorthandName(data)}`}</Title>
<Title order={4}>:</Title>
<Title order={4} className="font-mono break-all">
<Title sx={{ wordBreak: 'break-all' }} order={4} className="font-mono">
<HyperlinkedText tokens={data.returnTypeTokens} />
</Title>
</Group>

View File

@@ -17,7 +17,7 @@ export function ParameterTable({ data }: { data: ParameterDocumentation[] }) {
};
return (
<Box className="overflow-x-auto">
<Box sx={{ overflowX: 'auto' }}>
<Table columns={['Name', 'Type', 'Optional', 'Description']} rows={rows} columnStyles={columnStyles} />
</Box>
);

View File

@@ -41,7 +41,7 @@ export function Section({
const { classes } = useStyles({ opened });
return (
<Box className="break-all">
<Box sx={{ wordBreak: 'break-all' }}>
<UnstyledButton className={classes.control} onClick={() => setOpened((o) => !o)}>
<Group position="apart">
<Group>

View File

@@ -103,7 +103,9 @@ export function SidebarItems({
<Link key={i} href={member.path} passHref>
<UnstyledButton className={classes.link} component="a" onClick={() => setOpened((o) => !o)}>
<Group>
<Text className="line-clamp-1 text-ellipsis overflow-hidden">{member.name}</Text>
<Text sx={{ textOverflow: 'ellipsis', overflow: 'hidden' }} className="line-clamp-1">
{member.name}
</Text>
{member.overloadIndex && member.overloadIndex > 1 ? (
<Text size="xs" color="dimmed">
{member.overloadIndex}

View File

@@ -3,7 +3,6 @@ import {
AppShell,
Navbar,
MediaQuery,
Aside,
Header,
Burger,
Anchor,
@@ -26,8 +25,6 @@ import { type PropsWithChildren, useState } from 'react';
import { VscChevronDown, VscPackage } from 'react-icons/vsc';
import { WiDaySunny, WiNightClear } from 'react-icons/wi';
import { SidebarItems } from './SidebarItems';
import { TableOfContentsItems } from './TableOfContentsItems';
import type { DocClass } from '~/DocModel/DocClass';
import type { DocItem } from '~/DocModel/DocItem';
import type { findMember } from '~/util/model.server';
import type { getMembers } from '~/util/parse.server';
@@ -155,21 +152,6 @@ export function SidebarLayout({ packageName, data, children }: PropsWithChildren
) : null}
</Navbar>
}
aside={
packageName && data?.member && data.member.kind === 'Class' ? (
<MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
<Aside hiddenBreakpoint="sm" width={{ sm: 200, lg: 300 }}>
<ScrollArea p="xs">
<TableOfContentsItems
members={(data.member as unknown as ReturnType<DocClass['toJSON']>).methods}
></TableOfContentsItems>
</ScrollArea>
</Aside>
</MediaQuery>
) : (
<></>
)
}
// footer={
// <Footer height={60} p="md">
// Application footer

View File

@@ -38,7 +38,9 @@ export function TableOfContentsItems({
return (
<Box<'a'> key={key} href={`#${key}`} component="a" className={classes.link}>
<Group>
<Text className="line-clamp-1 text-ellipsis overflow-hidden">{member.name}</Text>
<Text sx={{ textOverflow: 'ellipsis', overflow: 'hidden' }} className="line-clamp-1">
{member.name}
</Text>
{member.overloadIndex && member.overloadIndex > 1 ? (
<Text size="xs" color="dimmed">
{member.overloadIndex}

View File

@@ -19,7 +19,7 @@ export function TypeParamTable({ data }: { data: TypeParameterData[] }) {
};
return (
<Box className="overflow-x-auto">
<Box sx={{ overflowX: 'auto' }}>
<Table
columns={['Name', 'Constraints', 'Optional', 'Default', 'Description']}
rows={rows}

View File

@@ -13,6 +13,7 @@ export function Class({ data }: { data: ReturnType<DocClass['toJSON']> }) {
extendsTokens={data.extendsTokens}
implementsTokens={data.implementsTokens}
comment={data.comment}
methods={data.methods}
>
<PropertiesSection data={data.properties} />
<MethodsSection data={data.methods} />

View File

@@ -1,4 +1,4 @@
import { Alert } from '@mantine/core';
import { Alert, Box, Title } from '@mantine/core';
import { StandardTags } from '@microsoft/tsdoc';
import type { ReactNode } from 'react';
import { VscWarning } from 'react-icons/vsc';
@@ -10,10 +10,10 @@ export interface BlockProps {
export function Block({ children, title }: BlockProps) {
return (
<div>
<h3 className="m-0">{title}</h3>
<Box>
<Title order={3}>{title}</Title>
{children}
</div>
</Box>
);
}

View File

@@ -1,4 +1,4 @@
import { Anchor, Box, Text } from '@mantine/core';
import { Anchor, Box, Code, Text } from '@mantine/core';
import { DocNodeKind, StandardTags } from '@microsoft/tsdoc';
import Link from 'next/link';
import type { ReactNode } from 'react';
@@ -61,9 +61,9 @@ export function TSDoc({ node }: { node: AnyDocNodeJSON }): JSX.Element {
case DocNodeKind.CodeSpan: {
const { code } = node as DocFencedCodeJSON;
return (
<pre key={idx} className="inline">
<Code key={idx} sx={{ display: 'inline' }} className="text-sm font-mono">
{code}
</pre>
</Code>
);
}
case DocNodeKind.FencedCode: {

View File

@@ -146,7 +146,7 @@ const member = (props: any) => {
export default function Slug(props: Partial<SidebarLayoutProps & { error?: string }>) {
return props.error ? (
<div className="flex max-w-full h-full bg-white dark:bg-dark">{props.error}</div>
<Box sx={{ display: 'flex', maxWidth: '100%', height: '100%' }}>{props.error}</Box>
) : (
<MemberProvider member={props.data?.member}>
<SidebarLayout {...props}>{props.data?.member ? member(props.data.member) : null}</SidebarLayout>

View File

@@ -38,7 +38,7 @@ export function generatePath(items: readonly ApiItem[]) {
const functionItem = item as ApiFunction;
path += `${functionItem.displayName}${
functionItem.overloadIndex && functionItem.overloadIndex > 1 ? `:${functionItem.overloadIndex}` : ''
}:/`;
}/`;
break;
default:
path += `${item.displayName}/`;