mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-14 10:33:30 +01:00
feat: table of contents / method visibility / property modifiers
This commit is contained in:
@@ -1,10 +1,8 @@
|
|||||||
import { Group, Stack, Title } from '@mantine/core';
|
import { Badge, Group, Stack, Title } from '@mantine/core';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { HyperlinkedText } from './HyperlinkedText';
|
import { HyperlinkedText } from './HyperlinkedText';
|
||||||
import { TSDoc } from './tsdoc/TSDoc';
|
import { TSDoc } from './tsdoc/TSDoc';
|
||||||
import type { DocItem } from '~/DocModel/DocItem';
|
import type { DocProperty } from '~/DocModel/DocProperty';
|
||||||
import type { AnyDocNodeJSON } from '~/DocModel/comment/CommentNode';
|
|
||||||
import type { TokenDocumentation } from '~/util/parse.server';
|
|
||||||
|
|
||||||
export enum CodeListingSeparatorType {
|
export enum CodeListingSeparatorType {
|
||||||
Type = ':',
|
Type = ':',
|
||||||
@@ -12,35 +10,30 @@ export enum CodeListingSeparatorType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function CodeListing({
|
export function CodeListing({
|
||||||
name,
|
prop,
|
||||||
separator = CodeListingSeparatorType.Type,
|
separator = CodeListingSeparatorType.Type,
|
||||||
summary,
|
|
||||||
typeTokens,
|
|
||||||
children,
|
children,
|
||||||
comment,
|
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
prop: ReturnType<DocProperty['toJSON']>;
|
||||||
summary?: ReturnType<DocItem['toJSON']>['summary'];
|
|
||||||
typeTokens: TokenDocumentation[];
|
|
||||||
separator?: CodeListingSeparatorType;
|
separator?: CodeListingSeparatorType;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
className?: string | undefined;
|
|
||||||
comment?: AnyDocNodeJSON | null;
|
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Stack spacing="xs" key={name}>
|
<Stack spacing="xs" key={prop.name}>
|
||||||
<Group>
|
<Group>
|
||||||
|
{prop.readonly ? <Badge variant="filled">Readonly</Badge> : null}
|
||||||
|
{prop.optional ? <Badge variant="filled">Optional</Badge> : null}
|
||||||
<Title order={4} className="font-mono">
|
<Title order={4} className="font-mono">
|
||||||
{name}
|
{prop.name}
|
||||||
</Title>
|
</Title>
|
||||||
<Title order={4}>{separator}</Title>
|
<Title order={4}>{separator}</Title>
|
||||||
<Title order={4} className="font-mono break-all">
|
<Title order={4} className="font-mono break-all">
|
||||||
<HyperlinkedText tokens={typeTokens} />
|
<HyperlinkedText tokens={prop.propertyTypeTokens} />
|
||||||
</Title>
|
</Title>
|
||||||
</Group>
|
</Group>
|
||||||
<Group>
|
<Group>
|
||||||
{summary && <TSDoc node={summary} />}
|
{prop.summary && <TSDoc node={prop.summary} />}
|
||||||
{comment && <TSDoc node={comment} />}
|
{prop.comment && <TSDoc node={prop.comment} />}
|
||||||
{children}
|
{children}
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { Group, Stack, Title } from '@mantine/core';
|
import { Badge, Group, Stack, Title } from '@mantine/core';
|
||||||
import { HyperlinkedText } from './HyperlinkedText';
|
import { HyperlinkedText } from './HyperlinkedText';
|
||||||
import { ParameterTable } from './ParameterTable';
|
import { ParameterTable } from './ParameterTable';
|
||||||
import { TSDoc } from './tsdoc/TSDoc';
|
import { TSDoc } from './tsdoc/TSDoc';
|
||||||
import type { DocMethod } from '~/DocModel/DocMethod';
|
import type { DocMethod } from '~/DocModel/DocMethod';
|
||||||
import type { DocMethodSignature } from '~/DocModel/DocMethodSignature';
|
import type { DocMethodSignature } from '~/DocModel/DocMethodSignature';
|
||||||
|
import { Visibility } from '~/DocModel/Visibility';
|
||||||
|
|
||||||
type MethodResolvable = ReturnType<DocMethod['toJSON']> | ReturnType<DocMethodSignature['toJSON']>;
|
type MethodResolvable = ReturnType<DocMethod['toJSON']> | ReturnType<DocMethodSignature['toJSON']>;
|
||||||
|
|
||||||
@@ -18,11 +19,21 @@ function getShorthandName(data: MethodResolvable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function MethodItem({ data }: { data: MethodResolvable }) {
|
export function MethodItem({ data }: { data: MethodResolvable }) {
|
||||||
|
const method = data as ReturnType<DocMethod['toJSON']>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing="xs">
|
<Stack
|
||||||
|
id={`${data.name}${data.overloadIndex && data.overloadIndex > 1 ? `:${data.overloadIndex}` : ''}`}
|
||||||
|
className="scroll-mt-30"
|
||||||
|
spacing="xs"
|
||||||
|
>
|
||||||
<Group>
|
<Group>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Group>
|
<Group>
|
||||||
|
{data.kind === 'Method' && method.visibility === Visibility.Protected ? (
|
||||||
|
<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 order={4} className="font-mono break-all">{`${getShorthandName(data)}`}</Title>
|
||||||
<Title order={4}>:</Title>
|
<Title order={4}>:</Title>
|
||||||
<Title order={4} className="font-mono break-all">
|
<Title order={4} className="font-mono break-all">
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ export function MethodList({
|
|||||||
<Stack>
|
<Stack>
|
||||||
{data.map((method) => (
|
{data.map((method) => (
|
||||||
<>
|
<>
|
||||||
<MethodItem key={method.name} data={method} />
|
<MethodItem
|
||||||
|
key={`${method.name}${method.overloadIndex && method.overloadIndex > 1 ? `:${method.overloadIndex}` : ''}`}
|
||||||
|
data={method}
|
||||||
|
/>
|
||||||
<Divider className="bg-gray-100" size="md" />
|
<Divider className="bg-gray-100" size="md" />
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -6,13 +6,7 @@ export function PropertyList({ data }: { data: ReturnType<DocProperty['toJSON']>
|
|||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
{data.map((prop) => (
|
{data.map((prop) => (
|
||||||
<CodeListing
|
<CodeListing key={prop.name} prop={prop} />
|
||||||
key={prop.name}
|
|
||||||
name={prop.name}
|
|
||||||
typeTokens={prop.propertyTypeTokens}
|
|
||||||
summary={prop.summary}
|
|
||||||
comment={prop.comment}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const useStyles = createStyles((theme, { opened }: { opened: boolean }) => ({
|
|||||||
fontSize: theme.fontSizes.sm,
|
fontSize: theme.fontSizes.sm,
|
||||||
|
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![7] : theme.colors.gray![0],
|
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0],
|
||||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,10 +4,15 @@ import { MethodList } from './MethodList';
|
|||||||
import { ParameterTable } from './ParameterTable';
|
import { ParameterTable } from './ParameterTable';
|
||||||
import { PropertyList } from './PropertyList';
|
import { PropertyList } from './PropertyList';
|
||||||
import { Section } from './Section';
|
import { Section } from './Section';
|
||||||
|
import type { DocClass } from '~/DocModel/DocClass';
|
||||||
import type { DocInterface } from '~/DocModel/DocInterface';
|
import type { DocInterface } from '~/DocModel/DocInterface';
|
||||||
import type { ParameterDocumentation } from '~/util/parse.server';
|
import type { ParameterDocumentation } from '~/util/parse.server';
|
||||||
|
|
||||||
export function PropertiesSection({ data }: { data: ReturnType<DocInterface['toJSON']>['properties'] }) {
|
export function PropertiesSection({
|
||||||
|
data,
|
||||||
|
}: {
|
||||||
|
data: ReturnType<DocClass['toJSON']>['properties'] | ReturnType<DocInterface['toJSON']>['properties'];
|
||||||
|
}) {
|
||||||
const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
|
const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
|
||||||
|
|
||||||
return data.length ? (
|
return data.length ? (
|
||||||
@@ -17,7 +22,11 @@ export function PropertiesSection({ data }: { data: ReturnType<DocInterface['toJ
|
|||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MethodsSection({ data }: { data: ReturnType<DocInterface['toJSON']>['methods'] }) {
|
export function MethodsSection({
|
||||||
|
data,
|
||||||
|
}: {
|
||||||
|
data: ReturnType<DocClass['toJSON']>['methods'] | ReturnType<DocInterface['toJSON']>['methods'];
|
||||||
|
}) {
|
||||||
const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
|
const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
|
||||||
|
|
||||||
return data.length ? (
|
return data.length ? (
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ const useStyles = createStyles((theme) => ({
|
|||||||
borderLeft: `1px solid ${theme.colorScheme === 'dark' ? theme.colors.dark![4] : theme.colors.gray![3]}`,
|
borderLeft: `1px solid ${theme.colorScheme === 'dark' ? theme.colors.dark![4] : theme.colors.gray![3]}`,
|
||||||
|
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![7] : theme.colors.gray![0],
|
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0],
|
||||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
AppShell,
|
AppShell,
|
||||||
Navbar,
|
Navbar,
|
||||||
MediaQuery,
|
MediaQuery,
|
||||||
// Aside,
|
Aside,
|
||||||
Header,
|
Header,
|
||||||
Burger,
|
Burger,
|
||||||
Anchor,
|
Anchor,
|
||||||
@@ -26,6 +26,8 @@ import { type PropsWithChildren, useState } from 'react';
|
|||||||
import { VscChevronDown, VscPackage } from 'react-icons/vsc';
|
import { VscChevronDown, VscPackage } from 'react-icons/vsc';
|
||||||
import { WiDaySunny, WiNightClear } from 'react-icons/wi';
|
import { WiDaySunny, WiNightClear } from 'react-icons/wi';
|
||||||
import { SidebarItems } from './SidebarItems';
|
import { SidebarItems } from './SidebarItems';
|
||||||
|
import { TableOfContentsItems } from './TableOfContentsItems';
|
||||||
|
import type { DocClass } from '~/DocModel/DocClass';
|
||||||
import type { DocItem } from '~/DocModel/DocItem';
|
import type { DocItem } from '~/DocModel/DocItem';
|
||||||
import type { findMember } from '~/util/model.server';
|
import type { findMember } from '~/util/model.server';
|
||||||
import type { getMembers } from '~/util/parse.server';
|
import type { getMembers } from '~/util/parse.server';
|
||||||
@@ -59,7 +61,7 @@ const useStyles = createStyles((theme, { opened }: { opened: boolean }) => ({
|
|||||||
color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black,
|
color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black,
|
||||||
|
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![7] : theme.colors.gray![0],
|
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0],
|
||||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -153,19 +155,21 @@ export function SidebarLayout({ packageName, data, children }: PropsWithChildren
|
|||||||
) : null}
|
) : null}
|
||||||
</Navbar>
|
</Navbar>
|
||||||
}
|
}
|
||||||
// aside={
|
aside={
|
||||||
// packageName && data?.member ? (
|
packageName && data?.member && data.member.kind === 'Class' ? (
|
||||||
// <MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
|
<MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
|
||||||
// <Aside hiddenBreakpoint="sm" width={{ sm: 200, lg: 300 }}>
|
<Aside hiddenBreakpoint="sm" width={{ sm: 200, lg: 300 }}>
|
||||||
// <ScrollArea p="xs">
|
<ScrollArea p="xs">
|
||||||
// <SidebarItems members={data.members} />
|
<TableOfContentsItems
|
||||||
// </ScrollArea>
|
members={(data.member as unknown as ReturnType<DocClass['toJSON']>).methods}
|
||||||
// </Aside>
|
></TableOfContentsItems>
|
||||||
// </MediaQuery>
|
</ScrollArea>
|
||||||
// ) : (
|
</Aside>
|
||||||
// <></>
|
</MediaQuery>
|
||||||
// )
|
) : (
|
||||||
// }
|
<></>
|
||||||
|
)
|
||||||
|
}
|
||||||
// footer={
|
// footer={
|
||||||
// <Footer height={60} p="md">
|
// <Footer height={60} p="md">
|
||||||
// Application footer
|
// Application footer
|
||||||
@@ -195,7 +199,7 @@ export function SidebarLayout({ packageName, data, children }: PropsWithChildren
|
|||||||
onClick={() => toggleColorScheme()}
|
onClick={() => toggleColorScheme()}
|
||||||
title="Toggle color scheme"
|
title="Toggle color scheme"
|
||||||
>
|
>
|
||||||
{dark ? <WiDaySunny size={18} /> : <WiNightClear size={18} />}
|
{dark ? <WiDaySunny size={20} /> : <WiNightClear size={20} />}
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Box>
|
</Box>
|
||||||
</Header>
|
</Header>
|
||||||
|
|||||||
61
packages/website/src/components/TableOfContentsItems.tsx
Normal file
61
packages/website/src/components/TableOfContentsItems.tsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { createStyles, Group, Text, Box } from '@mantine/core';
|
||||||
|
import { VscListSelection } from 'react-icons/vsc';
|
||||||
|
import type { DocClass } from '~/DocModel/DocClass';
|
||||||
|
import type { DocInterface } from '~/DocModel/DocInterface';
|
||||||
|
|
||||||
|
const useStyles = createStyles((theme) => ({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
link: {
|
||||||
|
...theme.fn.focusStyles(),
|
||||||
|
display: 'block',
|
||||||
|
textDecoration: 'none',
|
||||||
|
color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black,
|
||||||
|
lineHeight: 1.2,
|
||||||
|
fontSize: theme.fontSizes.sm,
|
||||||
|
padding: theme.spacing.xs,
|
||||||
|
paddingLeft: theme.spacing.md,
|
||||||
|
marginLeft: 8,
|
||||||
|
borderTopRightRadius: theme.radius.sm,
|
||||||
|
borderBottomRightRadius: theme.radius.sm,
|
||||||
|
borderLeft: `1px solid ${theme.colorScheme === 'dark' ? theme.colors.dark![4] : theme.colors.gray![3]}`,
|
||||||
|
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export function TableOfContentsItems({
|
||||||
|
members,
|
||||||
|
}: {
|
||||||
|
members: ReturnType<DocClass['toJSON']>['methods'] | ReturnType<DocInterface['toJSON']>['methods'];
|
||||||
|
}) {
|
||||||
|
const { classes } = useStyles();
|
||||||
|
|
||||||
|
const items = members.map((member) => {
|
||||||
|
const key = `${member.name}${member.overloadIndex && member.overloadIndex > 1 ? `:${member.overloadIndex}` : ''}`;
|
||||||
|
|
||||||
|
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>
|
||||||
|
{member.overloadIndex && member.overloadIndex > 1 ? (
|
||||||
|
<Text size="xs" color="dimmed">
|
||||||
|
{member.overloadIndex}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
</Group>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Group mb="md">
|
||||||
|
<VscListSelection size={20} />
|
||||||
|
<Text>Table of contents</Text>
|
||||||
|
</Group>
|
||||||
|
{items}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import type { DocFunction } from '~/DocModel/DocFunction';
|
|||||||
export function Function({ data }: { data: ReturnType<DocFunction['toJSON']> }) {
|
export function Function({ data }: { data: ReturnType<DocFunction['toJSON']> }) {
|
||||||
return (
|
return (
|
||||||
<DocContainer
|
<DocContainer
|
||||||
name={`${data.name}${data.overloadIndex ? ` (${data.overloadIndex})` : ''}`}
|
name={`${data.name}${data.overloadIndex && data.overloadIndex > 1 ? ` (${data.overloadIndex})` : ''}`}
|
||||||
kind={data.kind}
|
kind={data.kind}
|
||||||
excerpt={data.excerpt}
|
excerpt={data.excerpt}
|
||||||
summary={data.summary}
|
summary={data.summary}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
|||||||
// causing next.js export to error
|
// causing next.js export to error
|
||||||
.filter((member) => member.name !== 'RESTEvents')
|
.filter((member) => member.name !== 'RESTEvents')
|
||||||
.map((member) => {
|
.map((member) => {
|
||||||
if (member.kind === 'Function' && member.overloadIndex) {
|
if (member.kind === 'Function' && member.overloadIndex && member.overloadIndex > 1) {
|
||||||
return {
|
return {
|
||||||
params: {
|
params: {
|
||||||
slug: ['main', 'packages', packageName, `${member.name}:${member.overloadIndex}`],
|
slug: ['main', 'packages', packageName, `${member.name}:${member.overloadIndex}`],
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ export function generatePath(items: readonly ApiItem[]) {
|
|||||||
path += `${item.displayName}/`;
|
path += `${item.displayName}/`;
|
||||||
break;
|
break;
|
||||||
case ApiItemKind.Function:
|
case ApiItemKind.Function:
|
||||||
path += `${item.displayName}:${(item as ApiFunction).overloadIndex}/`;
|
// eslint-disable-next-line no-case-declarations
|
||||||
|
const functionItem = item as ApiFunction;
|
||||||
|
path += `${functionItem.displayName}${
|
||||||
|
functionItem.overloadIndex && functionItem.overloadIndex > 1 ? `:${functionItem.overloadIndex}` : ''
|
||||||
|
}:/`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
path += `${item.displayName}/`;
|
path += `${item.displayName}/`;
|
||||||
|
|||||||
Reference in New Issue
Block a user