mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
refactor: website components (#8600)
This commit is contained in:
@@ -43,14 +43,14 @@
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.56",
|
||||
"@vitest/coverage-c8": "^0.23.0",
|
||||
"@types/node": "^16.11.57",
|
||||
"@vitest/coverage-c8": "^0.23.1",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
"prettier": "^2.7.1",
|
||||
"tsup": "^6.2.3",
|
||||
"typescript": "^4.8.2",
|
||||
"vitest": "^0.23.0"
|
||||
"vitest": "^0.23.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"@microsoft/tsdoc": "^0.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.56",
|
||||
"@types/node": "^16.11.57",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
"prettier": "^2.7.1",
|
||||
|
||||
@@ -164,7 +164,7 @@ export class ApiNodeJSONEncoder {
|
||||
case ApiItemKind.Variable:
|
||||
return this.encodeVariable(model, node as ApiVariable, version);
|
||||
default:
|
||||
console.log(`Unknown API item kind: ${node.kind}`);
|
||||
// console.log(`Unknown API item kind: ${node.kind}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,15 +64,15 @@
|
||||
"@discordjs/docgen": "workspace:^",
|
||||
"@favware/cliff-jumper": "^1.8.7",
|
||||
"@microsoft/api-extractor": "^7.30.0",
|
||||
"@types/node": "^16.11.56",
|
||||
"@vitest/coverage-c8": "^0.23.0",
|
||||
"@types/node": "^16.11.57",
|
||||
"@vitest/coverage-c8": "^0.23.1",
|
||||
"downlevel-dts": "^0.10.1",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
"prettier": "^2.7.1",
|
||||
"tsup": "^6.2.3",
|
||||
"typescript": "^4.8.2",
|
||||
"vitest": "^0.23.0"
|
||||
"vitest": "^0.23.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
|
||||
@@ -53,15 +53,15 @@
|
||||
"@discordjs/docgen": "workspace:^",
|
||||
"@favware/cliff-jumper": "^1.8.7",
|
||||
"@microsoft/api-extractor": "^7.30.0",
|
||||
"@types/node": "^16.11.56",
|
||||
"@vitest/coverage-c8": "^0.23.0",
|
||||
"@types/node": "^16.11.57",
|
||||
"@vitest/coverage-c8": "^0.23.1",
|
||||
"downlevel-dts": "^0.10.1",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
"prettier": "^2.7.1",
|
||||
"tsup": "^6.2.3",
|
||||
"typescript": "^4.8.2",
|
||||
"vitest": "^0.23.0"
|
||||
"vitest": "^0.23.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"devDependencies": {
|
||||
"@discordjs/docgen": "workspace:^",
|
||||
"@favware/cliff-jumper": "^1.8.7",
|
||||
"@types/node": "^16.11.56",
|
||||
"@types/node": "^16.11.57",
|
||||
"dtslint": "^4.2.1",
|
||||
"eslint": "^8.23.0",
|
||||
"jest": "^29.0.2",
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"devDependencies": {
|
||||
"@favware/cliff-jumper": "^1.8.7",
|
||||
"@types/jsdoc-to-markdown": "^7.0.3",
|
||||
"@types/node": "^16.11.56",
|
||||
"@types/node": "^16.11.57",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
"prettier": "^2.7.1",
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.56",
|
||||
"@types/node": "^16.11.57",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
"prettier": "^2.7.1",
|
||||
|
||||
@@ -62,9 +62,9 @@
|
||||
"@discordjs/docgen": "workspace:^",
|
||||
"@favware/cliff-jumper": "^1.8.7",
|
||||
"@microsoft/api-extractor": "^7.30.0",
|
||||
"@types/node": "^16.11.56",
|
||||
"@types/node": "^16.11.57",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"@vitest/coverage-c8": "^0.23.0",
|
||||
"@vitest/coverage-c8": "^0.23.1",
|
||||
"downlevel-dts": "^0.10.1",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
@@ -72,7 +72,7 @@
|
||||
"supertest": "^6.2.4",
|
||||
"tsup": "^6.2.3",
|
||||
"typescript": "^4.8.2",
|
||||
"vitest": "^0.23.0"
|
||||
"vitest": "^0.23.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
|
||||
@@ -64,15 +64,15 @@
|
||||
"@discordjs/docgen": "workspace:^",
|
||||
"@favware/cliff-jumper": "^1.8.7",
|
||||
"@microsoft/api-extractor": "^7.30.0",
|
||||
"@types/node": "^16.11.56",
|
||||
"@vitest/coverage-c8": "^0.23.0",
|
||||
"@types/node": "^16.11.57",
|
||||
"@vitest/coverage-c8": "^0.23.1",
|
||||
"downlevel-dts": "^0.10.1",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
"prettier": "^2.7.1",
|
||||
"tsup": "^6.2.3",
|
||||
"typescript": "^4.8.2",
|
||||
"vitest": "^0.23.0"
|
||||
"vitest": "^0.23.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
|
||||
@@ -52,14 +52,14 @@
|
||||
"undici": "^5.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.56",
|
||||
"@vitest/coverage-c8": "^0.23.0",
|
||||
"@types/node": "^16.11.57",
|
||||
"@vitest/coverage-c8": "^0.23.1",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
"prettier": "^2.7.1",
|
||||
"tsup": "^6.2.3",
|
||||
"typescript": "^4.8.2",
|
||||
"vitest": "^0.23.0"
|
||||
"vitest": "^0.23.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
|
||||
@@ -59,14 +59,14 @@
|
||||
"ws": "^8.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.13",
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
"@babel/core": "^7.19.0",
|
||||
"@babel/preset-env": "^7.19.0",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@discordjs/docgen": "workspace:^",
|
||||
"@favware/cliff-jumper": "^1.8.7",
|
||||
"@microsoft/api-extractor": "^7.30.0",
|
||||
"@types/jest": "^29.0.0",
|
||||
"@types/node": "^16.11.56",
|
||||
"@types/node": "^16.11.57",
|
||||
"downlevel-dts": "^0.10.1",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/* eslint-disable tsdoc/syntax */
|
||||
import { URL, fileURLToPath } from 'node:url';
|
||||
|
||||
/**
|
||||
* @type {import('next').NextConfig}
|
||||
*/
|
||||
@@ -12,6 +15,7 @@ export default {
|
||||
images: {
|
||||
allowFutureImage: true,
|
||||
},
|
||||
outputFileTracingRoot: fileURLToPath(new URL('../../', import.meta.url)),
|
||||
},
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
|
||||
@@ -41,52 +41,50 @@
|
||||
"dependencies": {
|
||||
"@discordjs/api-extractor-utils": "workspace:^",
|
||||
"@discordjs/scripts": "workspace:^",
|
||||
"@emotion/react": "^11.10.4",
|
||||
"@emotion/server": "^11.10.0",
|
||||
"@mantine/core": "^5.2.5",
|
||||
"@mantine/hooks": "^5.2.5",
|
||||
"@mantine/next": "^5.2.5",
|
||||
"@mantine/nprogress": "^5.2.5",
|
||||
"@mantine/spotlight": "^5.2.5",
|
||||
"@microsoft/api-extractor-model": "7.24.0",
|
||||
"@microsoft/tsdoc": "0.14.1",
|
||||
"@vscode/codicons": "^0.0.32",
|
||||
"ariakit": "^2.0.0-next.41",
|
||||
"minisearch": "^5.0.0",
|
||||
"next": "^12.2.5",
|
||||
"next-mdx-remote": "^4.1.0",
|
||||
"next-progress": "^2.2.0",
|
||||
"next-themes": "^0.2.0",
|
||||
"react": "^18.2.0",
|
||||
"react-custom-scrollbars-2": "^4.5.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-use": "^17.4.0",
|
||||
"rehype-ignore": "^1.0.1",
|
||||
"rehype-pretty-code": "^0.3.2",
|
||||
"rehype-raw": "^6.1.1",
|
||||
"rehype-slug": "^5.0.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"sharp": "^0.30.7",
|
||||
"sharp": "^0.31.0",
|
||||
"shiki": "^0.11.1",
|
||||
"swr": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/node": "^16.11.56",
|
||||
"@types/node": "^16.11.57",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react-syntax-highlighter": "^15.5.5",
|
||||
"@unocss/cli": "^0.45.15",
|
||||
"@unocss/preset-web-fonts": "^0.45.15",
|
||||
"@unocss/reset": "^0.45.15",
|
||||
"@vitejs/plugin-react": "^2.0.1",
|
||||
"@vitest/coverage-c8": "^0.23.0",
|
||||
"@vitejs/plugin-react": "^2.1.0",
|
||||
"@vitest/coverage-c8": "^0.23.1",
|
||||
"concurrently": "^7.3.0",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
"happy-dom": "^6.0.4",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-tailwindcss": "^0.1.13",
|
||||
"typescript": "^4.8.2",
|
||||
"unocss": "^0.45.15",
|
||||
"vercel": "^28.2.2",
|
||||
"vitest": "^0.23.0"
|
||||
"vitest": "^0.23.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 85 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
@@ -1,6 +1,4 @@
|
||||
import type { TokenDocumentation, ApiItemJSON, AnyDocNodeJSON, InheritanceData } from '@discordjs/api-extractor-utils';
|
||||
import { ActionIcon, Badge, Box, createStyles, Group, MediaQuery, Stack, Title } from '@mantine/core';
|
||||
import { useMediaQuery } from '@mantine/hooks';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { FiLink } from 'react-icons/fi';
|
||||
import { HyperlinkedText } from './HyperlinkedText';
|
||||
@@ -12,19 +10,6 @@ export enum CodeListingSeparatorType {
|
||||
Value = '=',
|
||||
}
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
outer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 16,
|
||||
|
||||
[theme.fn.smallerThan('sm')]: {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'unset',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export function CodeListing({
|
||||
name,
|
||||
separator = CodeListingSeparatorType.Type,
|
||||
@@ -47,48 +32,51 @@ export function CodeListing({
|
||||
summary?: ApiItemJSON['summary'];
|
||||
typeTokens: TokenDocumentation[];
|
||||
}>) {
|
||||
const { classes } = useStyles();
|
||||
const matches = useMediaQuery('(max-width: 768px)');
|
||||
|
||||
return (
|
||||
<Stack id={name} className="scroll-mt-30" spacing="xs">
|
||||
<Box className={classes.outer} ml={matches ? 0 : -45}>
|
||||
<MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
|
||||
<ActionIcon component="a" href={`#${name}`} variant="transparent">
|
||||
<div id={name} className="scroll-mt-30 flex flex-col gap-4">
|
||||
<div className={`md:-ml-8.5 flex flex-col gap-0.5 md:flex-row md:place-items-center md:gap-2`}>
|
||||
<a className="hidden md:inline-block" href={`#${name}`}>
|
||||
<FiLink size={20} />
|
||||
</ActionIcon>
|
||||
</MediaQuery>
|
||||
</a>
|
||||
{deprecation || readonly || optional ? (
|
||||
<Group spacing={10} noWrap>
|
||||
<div className="flex flex-row flex-wrap gap-1">
|
||||
{deprecation ? (
|
||||
<Badge variant="filled" color="red">
|
||||
<div className="h-5 place-content-center rounded-full bg-red-500 px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Deprecated
|
||||
</Badge>
|
||||
</div>
|
||||
) : null}
|
||||
{readonly ? <Badge variant="filled">Readonly</Badge> : null}
|
||||
{optional ? <Badge variant="filled">Optional</Badge> : null}
|
||||
</Group>
|
||||
{readonly ? (
|
||||
<div className="bg-blurple h-5 place-content-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Readonly
|
||||
</div>
|
||||
) : null}
|
||||
<Group spacing={10}>
|
||||
<Title order={4} className="font-mono">
|
||||
{optional ? (
|
||||
<div className="bg-blurple h-5 place-content-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Optional
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex flex-row flex-wrap place-items-center gap-1">
|
||||
<h4 className="break-all font-mono text-lg font-bold">
|
||||
{name}
|
||||
{optional ? '?' : ''}
|
||||
</Title>
|
||||
<Title order={4}>{separator}</Title>
|
||||
<Title sx={{ wordBreak: 'break-all' }} order={4} className="font-mono">
|
||||
</h4>
|
||||
<h4 className="font-mono text-lg font-bold">{separator}</h4>
|
||||
<h4 className="break-all font-mono text-lg font-bold">
|
||||
<HyperlinkedText tokens={typeTokens} />
|
||||
</Title>
|
||||
</Group>
|
||||
</Box>
|
||||
<Group>
|
||||
<Stack>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
{summary || inheritanceData ? (
|
||||
<div className="flex flex-col gap-4">
|
||||
{deprecation ? <TSDoc node={deprecation} /> : null}
|
||||
{summary && <TSDoc node={summary} />}
|
||||
{comment && <TSDoc node={comment} />}
|
||||
{summary ? <TSDoc node={summary} /> : null}
|
||||
{comment ? <TSDoc node={comment} /> : null}
|
||||
{inheritanceData ? <InheritanceText data={inheritanceData} /> : null}
|
||||
{children}
|
||||
</Stack>
|
||||
</Group>
|
||||
</Stack>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,22 +5,8 @@ import type {
|
||||
ApiClassJSON,
|
||||
ApiInterfaceJSON,
|
||||
} from '@discordjs/api-extractor-utils';
|
||||
import {
|
||||
Group,
|
||||
Stack,
|
||||
Title,
|
||||
Text,
|
||||
Box,
|
||||
MediaQuery,
|
||||
Aside,
|
||||
ScrollArea,
|
||||
Skeleton,
|
||||
Divider,
|
||||
useMantineColorScheme,
|
||||
} from '@mantine/core';
|
||||
import { useMediaQuery } from '@mantine/hooks';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Fragment, type PropsWithChildren } from 'react';
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||
import {
|
||||
VscSymbolClass,
|
||||
VscSymbolMethod,
|
||||
@@ -30,10 +16,10 @@ import {
|
||||
VscListSelection,
|
||||
VscSymbolParameter,
|
||||
} from 'react-icons/vsc';
|
||||
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus, ghcolors } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
import { useMedia } from 'react-use';
|
||||
import { HyperlinkedText } from './HyperlinkedText';
|
||||
import { Section } from './Section';
|
||||
import { SyntaxHighlighter } from './SyntaxHighlighter';
|
||||
import { TableOfContentItems } from './TableOfContentItems';
|
||||
import { TypeParamTable } from './TypeParamTable';
|
||||
import { TSDoc } from './tsdoc/TSDoc';
|
||||
@@ -75,71 +61,47 @@ export function DocContainer({
|
||||
methods,
|
||||
properties,
|
||||
}: DocContainerProps) {
|
||||
const router = useRouter();
|
||||
const matches = useMediaQuery('(max-width: 768px)');
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const matches = useMedia('(max-width: 768px)', true);
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<Stack sx={{ flexGrow: 1, maxWidth: '100%' }}>
|
||||
<Skeleton visible={router.isFallback} radius="sm">
|
||||
<Title sx={{ wordBreak: 'break-all' }} order={2} ml="xs">
|
||||
<Group>
|
||||
{generateIcon(kind)}
|
||||
<>
|
||||
<div className="flex flex-col gap-4">
|
||||
<h2 className="flex flex-row place-items-center gap-2 break-all text-2xl font-bold">
|
||||
<span>{generateIcon(kind)}</span>
|
||||
{name}
|
||||
</Group>
|
||||
</Title>
|
||||
</Skeleton>
|
||||
</h2>
|
||||
|
||||
<Skeleton visible={router.isFallback} radius="sm">
|
||||
<Section title="Summary" icon={<VscListSelection size={20} />} padded dense={matches}>
|
||||
{summary ? <TSDoc node={summary} /> : <Text>No summary provided.</Text>}
|
||||
<Divider size="md" mt={20} />
|
||||
{summary ? <TSDoc node={summary} /> : <span>No summary provided.</span>}
|
||||
<div className="border-light-900 -mx-8 mt-6 border-t-2" />
|
||||
</Section>
|
||||
</Skeleton>
|
||||
|
||||
<Skeleton visible={router.isFallback} radius="sm">
|
||||
<Box pb="xs">
|
||||
<SyntaxHighlighter
|
||||
wrapLongLines
|
||||
language="typescript"
|
||||
style={colorScheme === 'dark' ? vscDarkPlus : ghcolors}
|
||||
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
|
||||
>
|
||||
{excerpt}
|
||||
</SyntaxHighlighter>
|
||||
</Box>
|
||||
</Skeleton>
|
||||
<SyntaxHighlighter code={excerpt} />
|
||||
|
||||
{extendsTokens?.length ? (
|
||||
<Group pb="xs" noWrap>
|
||||
<Title order={3} ml="xs">
|
||||
Extends
|
||||
</Title>
|
||||
<Text sx={{ wordBreak: 'break-all' }} className="font-mono">
|
||||
<div className="flex flex-row place-items-center gap-4">
|
||||
<h3 className="text-xl font-bold">Extends</h3>
|
||||
<span className="break-all font-mono">
|
||||
<HyperlinkedText tokens={extendsTokens} />
|
||||
</Text>
|
||||
</Group>
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{implementsTokens?.length ? (
|
||||
<Group pb="xs" noWrap>
|
||||
<Title order={3} ml="xs">
|
||||
Implements
|
||||
</Title>
|
||||
<Text sx={{ wordBreak: 'break-all' }} className="font-mono">
|
||||
<div className="flex flex-row place-items-center gap-4">
|
||||
<h3 className="text-xl font-bold">Implements</h3>
|
||||
<span className="break-all font-mono">
|
||||
{implementsTokens.map((token, idx) => (
|
||||
<Fragment key={idx}>
|
||||
<HyperlinkedText tokens={token} />
|
||||
{idx < implementsTokens.length - 1 ? ', ' : ''}
|
||||
</Fragment>
|
||||
))}
|
||||
</Text>
|
||||
</Group>
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<Skeleton visible={router.isFallback} radius="sm">
|
||||
<Stack>
|
||||
<div className="flex flex-col gap-4">
|
||||
{typeParams?.length ? (
|
||||
<Section
|
||||
title="Type Parameters"
|
||||
@@ -151,19 +113,24 @@ export function DocContainer({
|
||||
<TypeParamTable data={typeParams} />
|
||||
</Section>
|
||||
) : null}
|
||||
<Stack>{children}</Stack>
|
||||
</Stack>
|
||||
</Skeleton>
|
||||
</Stack>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
{(kind === 'Class' || kind === 'Interface') && (methods?.length || properties?.length) ? (
|
||||
<MediaQuery smallerThan="lg" styles={{ display: 'none' }}>
|
||||
<Aside hiddenBreakpoint="lg" width={{ lg: 250 }} withBorder>
|
||||
<ScrollArea p="sm" offsetScrollbars>
|
||||
<aside className="h-[calc(100vh - 72px)] dark:bg-dark-600 dark:border-dark-100 border-light-800 fixed top-[72px] right-0 bottom-0 z-20 hidden w-64 border-l bg-white pr-2 xl:block">
|
||||
<Scrollbars
|
||||
universal
|
||||
autoHide
|
||||
hideTracksWhenNotNeeded
|
||||
renderTrackVertical={(props) => (
|
||||
<div {...props} className="absolute top-0.5 right-0.5 bottom-0.5 z-30 w-1.5 rounded" />
|
||||
)}
|
||||
renderThumbVertical={(props) => <div {...props} className="dark:bg-dark-100 bg-light-900 z-30 rounded" />}
|
||||
>
|
||||
<TableOfContentItems properties={properties ?? []} methods={methods ?? []} />
|
||||
</ScrollArea>
|
||||
</Aside>
|
||||
</MediaQuery>
|
||||
</Scrollbars>
|
||||
</aside>
|
||||
) : null}
|
||||
</Group>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { TokenDocumentation } from '@discordjs/api-extractor-utils';
|
||||
import { Anchor, Text } from '@mantine/core';
|
||||
import Link from 'next/link';
|
||||
|
||||
export function HyperlinkedText({ tokens }: { tokens: TokenDocumentation[] }) {
|
||||
@@ -8,19 +7,13 @@ export function HyperlinkedText({ tokens }: { tokens: TokenDocumentation[] }) {
|
||||
{tokens.map((token, idx) => {
|
||||
if (token.path) {
|
||||
return (
|
||||
<Link key={idx} href={token.path} passHref prefetch={false}>
|
||||
<Anchor component="a" inherit>
|
||||
{token.text}
|
||||
</Anchor>
|
||||
<Link key={idx} href={token.path} prefetch={false}>
|
||||
<a className="text-blurple">{token.text}</a>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Text key={idx} span unstyled>
|
||||
{token.text}
|
||||
</Text>
|
||||
);
|
||||
return <span key={idx}>{token.text}</span>;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import type { InheritanceData } from '@discordjs/api-extractor-utils';
|
||||
import { Anchor, Text } from '@mantine/core';
|
||||
import Link from 'next/link';
|
||||
|
||||
export function InheritanceText({ data }: { data: InheritanceData }) {
|
||||
return (
|
||||
<Text weight={600}>
|
||||
<span className="font-semibold">
|
||||
{'Inherited from '}
|
||||
<Link href={data.path} passHref prefetch={false}>
|
||||
<Anchor component="a" className="font-mono">
|
||||
{data.parentName}
|
||||
</Anchor>
|
||||
<Link href={data.path} prefetch={false}>
|
||||
<a className="text-blurple font-mono">{data.parentName}</a>
|
||||
</Link>
|
||||
</Text>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,86 +1,77 @@
|
||||
import type { ApiMethodJSON, ApiMethodSignatureJSON } from '@discordjs/api-extractor-utils';
|
||||
import { ActionIcon, Badge, Box, createStyles, Group, MediaQuery, Stack, Title } from '@mantine/core';
|
||||
import { useMediaQuery } from '@mantine/hooks';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { FiLink } from 'react-icons/fi';
|
||||
import { HyperlinkedText } from './HyperlinkedText';
|
||||
import { InheritanceText } from './InheritanceText';
|
||||
import { ParameterTable } from './ParameterTable';
|
||||
import { TSDoc } from './tsdoc/TSDoc';
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
outer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 16,
|
||||
export function MethodItem({ data }: { data: ApiMethodJSON | ApiMethodSignatureJSON }) {
|
||||
const method = data as ApiMethodJSON;
|
||||
const key = useMemo(
|
||||
() => `${data.name}${data.overloadIndex && data.overloadIndex > 1 ? `:${data.overloadIndex}` : ''}`,
|
||||
[data.name, data.overloadIndex],
|
||||
);
|
||||
|
||||
[theme.fn.smallerThan('sm')]: {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'unset',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
function getShorthandName(data: ApiMethodJSON | ApiMethodSignatureJSON) {
|
||||
return `${data.name}${data.optional ? '?' : ''}(${data.parameters.reduce((prev, cur, index) => {
|
||||
const getShorthandName = useCallback(
|
||||
(data: ApiMethodJSON | ApiMethodSignatureJSON) =>
|
||||
`${data.name}${data.optional ? '?' : ''}(${data.parameters.reduce((prev, cur, index) => {
|
||||
if (index === 0) {
|
||||
return `${prev}${cur.isOptional ? `${cur.name}?` : cur.name}`;
|
||||
}
|
||||
|
||||
return `${prev}, ${cur.isOptional ? `${cur.name}?` : cur.name}`;
|
||||
}, '')})`;
|
||||
}
|
||||
|
||||
export function MethodItem({ data }: { data: ApiMethodJSON | ApiMethodSignatureJSON }) {
|
||||
const { classes } = useStyles();
|
||||
const matches = useMediaQuery('(max-width: 768px)');
|
||||
const method = data as ApiMethodJSON;
|
||||
const key = `${data.name}${data.overloadIndex && data.overloadIndex > 1 ? `:${data.overloadIndex}` : ''}`;
|
||||
}, '')})`,
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack id={key} className="scroll-mt-30" spacing="xs">
|
||||
<Group>
|
||||
<Stack>
|
||||
<Box className={classes.outer} ml={matches ? 0 : -45}>
|
||||
<MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
|
||||
<ActionIcon component="a" href={`#${key}`} variant="transparent" color="dark">
|
||||
<div id={key} className="scroll-mt-30 flex flex-col gap-4">
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col gap-2 md:-ml-9 md:flex-row md:place-items-center">
|
||||
<a className="hidden md:inline-block" href={`#${key}`}>
|
||||
<FiLink size={20} />
|
||||
</ActionIcon>
|
||||
</MediaQuery>
|
||||
</a>
|
||||
{data.deprecated ||
|
||||
(data.kind === 'Method' && method.protected) ||
|
||||
(data.kind === 'Method' && method.static) ? (
|
||||
<Group spacing={10} noWrap>
|
||||
<div className="flex flex-row flex-wrap gap-1">
|
||||
{data.deprecated ? (
|
||||
<Badge variant="filled" color="red">
|
||||
<div className="h-5 place-content-center rounded-full bg-red-500 px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Deprecated
|
||||
</Badge>
|
||||
</div>
|
||||
) : null}
|
||||
{data.kind === 'Method' && method.protected ? <Badge variant="filled">Protected</Badge> : null}
|
||||
{data.kind === 'Method' && method.static ? <Badge variant="filled">Static</Badge> : null}
|
||||
</Group>
|
||||
{data.kind === 'Method' && method.protected ? (
|
||||
<div className="bg-blurple h-5 place-content-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Protected
|
||||
</div>
|
||||
) : null}
|
||||
<Group spacing={10}>
|
||||
<Title sx={{ wordBreak: 'break-all' }} order={4} className="font-mono">{`${getShorthandName(
|
||||
data,
|
||||
)}`}</Title>
|
||||
<Title order={4}>:</Title>
|
||||
<Title sx={{ wordBreak: 'break-all' }} order={4} className="font-mono">
|
||||
{data.kind === 'Method' && method.static ? (
|
||||
<div className="bg-blurple h-5 place-content-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Static
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex flex-row flex-wrap gap-1">
|
||||
<h4 className="break-all font-mono text-lg font-bold">{`${getShorthandName(data)}`}</h4>
|
||||
<h4 className="font-mono text-lg font-bold">:</h4>
|
||||
<h4 className="break-all font-mono text-lg font-bold">
|
||||
<HyperlinkedText tokens={data.returnTypeTokens} />
|
||||
</Title>
|
||||
</Group>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Group>
|
||||
<Group sx={{ display: data.summary || data.parameters.length ? 'block' : 'none' }} mb="lg">
|
||||
<Stack>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{data.summary || data.parameters.length ? (
|
||||
<div className="mb-4 flex flex-col gap-4">
|
||||
{data.deprecated ? <TSDoc node={data.deprecated} /> : null}
|
||||
{data.summary ? <TSDoc node={data.summary} /> : null}
|
||||
{data.remarks ? <TSDoc node={data.remarks} /> : null}
|
||||
{data.comment ? <TSDoc node={data.comment} /> : null}
|
||||
{data.parameters.length ? <ParameterTable data={data.parameters} /> : null}
|
||||
{data.inheritanceData ? <InheritanceText data={data.inheritanceData} /> : null}
|
||||
</Stack>
|
||||
</Group>
|
||||
</Stack>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import type { ApiMethodJSON, ApiMethodSignatureJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Divider, Stack } from '@mantine/core';
|
||||
import { Fragment } from 'react';
|
||||
import { Fragment, useMemo } from 'react';
|
||||
import { MethodItem } from './MethodItem';
|
||||
|
||||
export function MethodList({ data }: { data: (ApiMethodJSON | ApiMethodSignatureJSON)[] }) {
|
||||
return (
|
||||
<Stack>
|
||||
{data.map((method) => (
|
||||
const methodItems = useMemo(
|
||||
() =>
|
||||
data.map((method) => (
|
||||
<Fragment
|
||||
key={`${method.name}${method.overloadIndex && method.overloadIndex > 1 ? `:${method.overloadIndex}` : ''}`}
|
||||
>
|
||||
<MethodItem data={method} />
|
||||
<Divider size="md" />
|
||||
<div className="border-light-900 -mx-8 border-t-2" />
|
||||
</Fragment>
|
||||
))}
|
||||
</Stack>
|
||||
)),
|
||||
[data],
|
||||
);
|
||||
|
||||
return <div className="flex flex-col gap-4">{methodItems}</div>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ParameterDocumentation } from '@discordjs/api-extractor-utils';
|
||||
import { Box, ScrollArea } from '@mantine/core';
|
||||
import { useMemo } from 'react';
|
||||
import { HyperlinkedText } from './HyperlinkedText';
|
||||
import { Table } from './Table';
|
||||
import { TSDoc } from './tsdoc/TSDoc';
|
||||
@@ -10,18 +10,20 @@ const columnStyles = {
|
||||
};
|
||||
|
||||
export function ParameterTable({ data }: { data: ParameterDocumentation[] }) {
|
||||
const rows = data.map((param) => ({
|
||||
const rows = useMemo(
|
||||
() =>
|
||||
data.map((param) => ({
|
||||
Name: param.name,
|
||||
Type: <HyperlinkedText tokens={param.tokens} />,
|
||||
Optional: param.isOptional ? 'Yes' : 'No',
|
||||
Description: param.paramCommentBlock ? <TSDoc node={param.paramCommentBlock} /> : 'None',
|
||||
}));
|
||||
})),
|
||||
[data],
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<ScrollArea pb="xs" offsetScrollbars>
|
||||
<div className="overflow-x-auto">
|
||||
<Table columns={['Name', 'Type', 'Optional', 'Description']} rows={rows} columnStyles={columnStyles} />
|
||||
</ScrollArea>
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import type { ApiPropertyItemJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Divider, Stack } from '@mantine/core';
|
||||
import { Fragment } from 'react';
|
||||
import { Fragment, useMemo } from 'react';
|
||||
import { CodeListing } from './CodeListing';
|
||||
|
||||
export function PropertyList({ data }: { data: ApiPropertyItemJSON[] }) {
|
||||
return (
|
||||
<Stack>
|
||||
{data.map((prop) => (
|
||||
const propertyItems = useMemo(
|
||||
() =>
|
||||
data.map((prop) => (
|
||||
<Fragment key={prop.name}>
|
||||
<CodeListing
|
||||
name={prop.name}
|
||||
@@ -18,9 +17,11 @@ export function PropertyList({ data }: { data: ApiPropertyItemJSON[] }) {
|
||||
deprecation={prop.deprecated}
|
||||
inheritanceData={prop.inheritanceData}
|
||||
/>
|
||||
<Divider size="md" />
|
||||
<div className="border-light-900 -mx-8 border-t-2" />
|
||||
</Fragment>
|
||||
))}
|
||||
</Stack>
|
||||
)),
|
||||
[data],
|
||||
);
|
||||
|
||||
return <div className="flex flex-col gap-4">{propertyItems}</div>;
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { startNavigationProgress, resetNavigationProgress, NavigationProgress } from '@mantine/nprogress';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export function RouterTransition() {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
const handleStart = (url: string) => url !== router.asPath && startNavigationProgress();
|
||||
const handleComplete = () => resetNavigationProgress();
|
||||
|
||||
router.events.on('routeChangeStart', handleStart);
|
||||
router.events.on('routeChangeComplete', handleComplete);
|
||||
router.events.on('routeChangeError', handleComplete);
|
||||
|
||||
return () => {
|
||||
router.events.off('routeChangeStart', handleStart);
|
||||
router.events.off('routeChangeComplete', handleComplete);
|
||||
router.events.off('routeChangeError', handleComplete);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [router.asPath]);
|
||||
|
||||
return <NavigationProgress color="blurple" />;
|
||||
}
|
||||
@@ -1,37 +1,7 @@
|
||||
import {
|
||||
createStyles,
|
||||
UnstyledButton,
|
||||
Group,
|
||||
ThemeIcon,
|
||||
Collapse,
|
||||
Box,
|
||||
Text,
|
||||
useMantineColorScheme,
|
||||
} from '@mantine/core';
|
||||
import { useState, useEffect, type PropsWithChildren } from 'react';
|
||||
import { Disclosure, DisclosureContent, useDisclosureState } from 'ariakit/disclosure';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { VscChevronDown } from 'react-icons/vsc';
|
||||
|
||||
const useStyles = createStyles((theme, { opened }: { opened: boolean }) => ({
|
||||
control: {
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
padding: theme.spacing.xs,
|
||||
color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black,
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![7] : 'transparent',
|
||||
borderRadius: theme.radius.xs,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![5] : theme.colors.gray![2],
|
||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||
},
|
||||
},
|
||||
|
||||
icon: {
|
||||
transition: 'transform 150ms ease',
|
||||
transform: opened ? 'rotate(180deg)' : 'rotate(0deg)',
|
||||
},
|
||||
}));
|
||||
|
||||
export function Section({
|
||||
title,
|
||||
icon,
|
||||
@@ -46,41 +16,28 @@ export function Section({
|
||||
padded?: boolean;
|
||||
title: string;
|
||||
}>) {
|
||||
const [opened, setOpened] = useState(!defaultClosed);
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const { classes } = useStyles({ opened });
|
||||
|
||||
useEffect(() => {
|
||||
setOpened(!defaultClosed);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
const disclosure = useDisclosureState({ defaultOpen: !defaultClosed });
|
||||
|
||||
return (
|
||||
<Box sx={{ wordBreak: 'break-all' }}>
|
||||
<UnstyledButton className={classes.control} onClick={() => setOpened((isOpen) => !isOpen)}>
|
||||
<Group position="apart">
|
||||
<Group>
|
||||
{icon ? (
|
||||
<ThemeIcon variant={colorScheme === 'dark' ? 'filled' : 'outline'} radius="sm" size={30}>
|
||||
{icon}
|
||||
</ThemeIcon>
|
||||
) : null}
|
||||
<Text weight={600} size="md">
|
||||
{title}
|
||||
</Text>
|
||||
</Group>
|
||||
<VscChevronDown size={20} className={classes.icon} />
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
<Collapse in={opened}>
|
||||
{padded ? (
|
||||
<Box py={20} px={dense ? 0 : 31} mx={dense ? 10 : 25}>
|
||||
{children}
|
||||
</Box>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</Collapse>
|
||||
</Box>
|
||||
<div>
|
||||
<Disclosure
|
||||
className="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 rounded p-3"
|
||||
state={disclosure}
|
||||
>
|
||||
<div className="flex flex-row place-content-between place-items-center">
|
||||
<div className="flex flex-row place-items-center gap-3">
|
||||
{icon ?? null}
|
||||
<span className="font-semibold">{title}</span>
|
||||
</div>
|
||||
<VscChevronDown
|
||||
className={`transform transition duration-150 ease-in-out ${disclosure.open ? 'rotate-180' : 'rotate-0'}`}
|
||||
size={20}
|
||||
/>
|
||||
</div>
|
||||
</Disclosure>
|
||||
<DisclosureContent state={disclosure}>
|
||||
{padded ? <div className={`py-5 ${dense ? 'mx-2 px-0' : 'px-4.5 mx-6.5'}`}>{children}</div> : children}
|
||||
</DisclosureContent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ import type {
|
||||
ParameterDocumentation,
|
||||
ApiConstructorJSON,
|
||||
} from '@discordjs/api-extractor-utils';
|
||||
import { Stack, Group, Badge, Title } from '@mantine/core';
|
||||
import { useMediaQuery } from '@mantine/hooks';
|
||||
import { useMemo } from 'react';
|
||||
import { VscSymbolConstant, VscSymbolMethod, VscSymbolProperty } from 'react-icons/vsc';
|
||||
import { useMedia } from 'react-use';
|
||||
import { MethodList } from './MethodList';
|
||||
import { ParameterTable } from './ParameterTable';
|
||||
import { PropertyList } from './PropertyList';
|
||||
@@ -14,7 +14,7 @@ import { Section } from './Section';
|
||||
import { TSDoc } from './tsdoc/TSDoc';
|
||||
|
||||
export function PropertiesSection({ data }: { data: ApiClassJSON['properties'] | ApiInterfaceJSON['properties'] }) {
|
||||
const matches = useMediaQuery('(max-width: 768px)');
|
||||
const matches = useMedia('(max-width: 768px)', true);
|
||||
|
||||
return data.length ? (
|
||||
<Section title="Properties" icon={<VscSymbolProperty size={20} />} padded dense={matches}>
|
||||
@@ -24,7 +24,7 @@ export function PropertiesSection({ data }: { data: ApiClassJSON['properties'] |
|
||||
}
|
||||
|
||||
export function MethodsSection({ data }: { data: ApiClassJSON['methods'] | ApiInterfaceJSON['methods'] }) {
|
||||
const matches = useMediaQuery('(max-width: 768px)');
|
||||
const matches = useMedia('(max-width: 768px)', true);
|
||||
|
||||
return data.length ? (
|
||||
<Section title="Methods" icon={<VscSymbolMethod size={20} />} padded dense={matches}>
|
||||
@@ -34,7 +34,7 @@ export function MethodsSection({ data }: { data: ApiClassJSON['methods'] | ApiIn
|
||||
}
|
||||
|
||||
export function ParametersSection({ data }: { data: ParameterDocumentation[] }) {
|
||||
const matches = useMediaQuery('(max-width: 768px)');
|
||||
const matches = useMedia('(max-width: 768px)', true);
|
||||
|
||||
return data.length ? (
|
||||
<Section title="Parameters" icon={<VscSymbolConstant size={20} />} padded dense={matches}>
|
||||
@@ -44,45 +44,52 @@ export function ParametersSection({ data }: { data: ParameterDocumentation[] })
|
||||
}
|
||||
|
||||
export function ConstructorSection({ data }: { data: ApiConstructorJSON }) {
|
||||
const matches = useMediaQuery('(max-width: 768px)');
|
||||
const matches = useMedia('(max-width: 768px)', true);
|
||||
|
||||
const getShorthandName = () =>
|
||||
const getShorthandName = useMemo(
|
||||
() =>
|
||||
`constructor(${data.parameters.reduce((prev, cur, index) => {
|
||||
if (index === 0) {
|
||||
return `${prev}${cur.isOptional ? `${cur.name}?` : cur.name}`;
|
||||
}
|
||||
|
||||
return `${prev}, ${cur.isOptional ? `${cur.name}?` : cur.name}`;
|
||||
}, '')})`;
|
||||
}, '')})`,
|
||||
[data.parameters],
|
||||
);
|
||||
|
||||
return data.parameters.length ? (
|
||||
<Section title="Constructor" icon={<VscSymbolMethod size={20} />} padded dense={matches}>
|
||||
<Stack id={data.name} className="scroll-mt-30" spacing="xs">
|
||||
<Group>
|
||||
<Stack>
|
||||
<Group>
|
||||
<div id={data.name} className="scroll-mt-30 flex flex-col gap-4">
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col gap-2 md:flex-row md:place-items-center">
|
||||
{data.deprecated || data.protected ? (
|
||||
<div className="flex flex-row flex-wrap gap-1">
|
||||
{data.deprecated ? (
|
||||
<Badge variant="filled" color="red">
|
||||
<div className="h-5 place-content-center rounded-full bg-red-500 px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Deprecated
|
||||
</Badge>
|
||||
</div>
|
||||
) : null}
|
||||
{data.protected ? <Badge variant="filled">Protected</Badge> : null}
|
||||
<Title sx={{ wordBreak: 'break-all' }} order={4} className="font-mono">
|
||||
{getShorthandName()}
|
||||
</Title>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Group>
|
||||
<Group sx={{ display: data.summary || data.parameters.length ? 'block' : 'none' }} mb="lg">
|
||||
<Stack>
|
||||
{data.protected ? (
|
||||
<div className="bg-blurple h-5 place-content-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Protected
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<h4 className="break-all font-mono text-lg font-bold">{getShorthandName}</h4>
|
||||
</div>
|
||||
</div>
|
||||
{data.summary || data.parameters.length ? (
|
||||
<div className="mb-4 flex flex-col gap-4">
|
||||
{data.deprecated ? <TSDoc node={data.deprecated} /> : null}
|
||||
{data.summary ? <TSDoc node={data.summary} /> : null}
|
||||
{data.remarks ? <TSDoc node={data.remarks} /> : null}
|
||||
{data.comment ? <TSDoc node={data.comment} /> : null}
|
||||
{data.parameters.length ? <ParameterTable data={data.parameters} /> : null}
|
||||
</Stack>
|
||||
</Group>
|
||||
</Stack>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</Section>
|
||||
) : null;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { createStyles, Group, Text, NavLink, Box } from '@mantine/core';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { type Dispatch, type SetStateAction, useEffect, useState, useMemo } from 'react';
|
||||
@@ -66,32 +65,6 @@ function resolveIcon(item: keyof GroupedMembers) {
|
||||
}
|
||||
}
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
link: {
|
||||
...theme.fn.focusStyles(),
|
||||
fontWeight: 500,
|
||||
display: 'block',
|
||||
width: 'unset',
|
||||
padding: 5,
|
||||
paddingLeft: 31,
|
||||
marginLeft: 25,
|
||||
fontSize: theme.fontSizes.sm,
|
||||
color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.colors.gray![7],
|
||||
borderLeft: `1px solid ${theme.colorScheme === 'dark' ? theme.colors.dark![4] : theme.colors.gray![3]}`,
|
||||
|
||||
'&[data-active]': {
|
||||
'&:hover': {
|
||||
color: theme.white,
|
||||
},
|
||||
},
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0],
|
||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export function SidebarItems({
|
||||
members,
|
||||
setOpened,
|
||||
@@ -101,7 +74,6 @@ export function SidebarItems({
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [asPathWithoutQueryAndAnchor, setAsPathWithoutQueryAndAnchor] = useState('');
|
||||
const { classes } = useStyles();
|
||||
const groupItems = useMemo(() => groupMembers(members), [members]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -109,36 +81,33 @@ export function SidebarItems({
|
||||
}, [router.asPath]);
|
||||
|
||||
return (
|
||||
<Box sx={(theme) => ({ paddingBottom: 48, [theme.fn.smallerThan('md')]: { paddingBottom: 128 } })}>
|
||||
<div className="flex flex-col gap-3 p-3 pb-32 lg:pb-12">
|
||||
{(Object.keys(groupItems) as (keyof GroupedMembers)[])
|
||||
.filter((group) => groupItems[group].length)
|
||||
.map((group, idx) => (
|
||||
<Section key={idx} title={group} icon={resolveIcon(group)}>
|
||||
{groupItems[group].map((member, index) => (
|
||||
<Link key={index} href={member.path} passHref prefetch={false}>
|
||||
<NavLink
|
||||
className={classes.link}
|
||||
component="a"
|
||||
onClick={() => setOpened((isOpened) => !isOpened)}
|
||||
label={
|
||||
<Group>
|
||||
<Text sx={{ textOverflow: 'ellipsis', overflow: 'hidden' }} className="line-clamp-1">
|
||||
{member.name}
|
||||
</Text>
|
||||
<Link key={index} href={member.path} prefetch={false}>
|
||||
<a
|
||||
className={`dark:border-dark-100 border-light-800 ml-5 border-l p-[5px] pl-6 ${
|
||||
asPathWithoutQueryAndAnchor === member.path
|
||||
? 'bg-blurple text-white'
|
||||
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
||||
}`}
|
||||
title={member.name}
|
||||
onClick={() => setOpened(false)}
|
||||
>
|
||||
<div className="flex flex-row place-items-center gap-2 text-sm">
|
||||
<span className="truncate">{member.name}</span>
|
||||
{member.overloadIndex && member.overloadIndex > 1 ? (
|
||||
<Text size="xs" color="dimmed">
|
||||
{member.overloadIndex}
|
||||
</Text>
|
||||
<span className="text-xs">{member.overloadIndex}</span>
|
||||
) : null}
|
||||
</Group>
|
||||
}
|
||||
active={asPathWithoutQueryAndAnchor === member.path}
|
||||
variant="filled"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
))}
|
||||
</Section>
|
||||
))}
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,41 +1,20 @@
|
||||
import type { getMembers, ApiItemJSON } from '@discordjs/api-extractor-utils';
|
||||
import {
|
||||
useMantineTheme,
|
||||
AppShell,
|
||||
Navbar,
|
||||
MediaQuery,
|
||||
Header,
|
||||
Burger,
|
||||
Anchor,
|
||||
Breadcrumbs,
|
||||
ScrollArea,
|
||||
Group,
|
||||
Text,
|
||||
ThemeIcon,
|
||||
Box,
|
||||
UnstyledButton,
|
||||
createStyles,
|
||||
Menu,
|
||||
ActionIcon,
|
||||
useMantineColorScheme,
|
||||
Stack,
|
||||
Skeleton,
|
||||
LoadingOverlay,
|
||||
Container,
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { NextLink } from '@mantine/next';
|
||||
import { Button } from 'ariakit/button';
|
||||
import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit/menu';
|
||||
import Image from 'next/future/image';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
|
||||
import { type PropsWithChildren, useState, useEffect, useMemo } from 'react';
|
||||
import { VscChevronDown, VscGithubInverted, VscPackage, VscVersions } from 'react-icons/vsc';
|
||||
import { WiDaySunny, WiNightClear } from 'react-icons/wi';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { type PropsWithChildren, useState, useEffect, useMemo, Fragment } from 'react';
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||
import { VscChevronDown, VscColorMode, VscGithubInverted, VscMenu, VscPackage, VscVersions } from 'react-icons/vsc';
|
||||
import { useMedia /* useLockBodyScroll */ } from 'react-use';
|
||||
import useSWR from 'swr';
|
||||
import vercelLogo from '../assets/powered-by-vercel.svg';
|
||||
import { SidebarItems } from './SidebarItems';
|
||||
import { PACKAGES } from '~/util/constants';
|
||||
import type { findMember } from '~/util/model.server';
|
||||
import { PACKAGES } from '~/util/packages';
|
||||
|
||||
const fetcher = async (url: string) => {
|
||||
const res = await fetch(url);
|
||||
@@ -66,89 +45,6 @@ export interface GroupedMembers {
|
||||
Variables: Members;
|
||||
}
|
||||
|
||||
const useStyles = createStyles(
|
||||
(theme, { openedLib, openedVersion }: { openedLib: boolean; openedVersion: boolean }) => ({
|
||||
control: {
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
padding: theme.spacing.xs,
|
||||
color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black,
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![1],
|
||||
borderRadius: theme.radius.xs,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![5] : theme.colors.gray![2],
|
||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||
},
|
||||
},
|
||||
|
||||
iconLib: {
|
||||
transition: 'transform 150ms ease',
|
||||
transform: openedLib ? 'rotate(180deg)' : 'rotate(0deg)',
|
||||
},
|
||||
|
||||
iconVersion: {
|
||||
transition: 'transform 150ms ease',
|
||||
transform: openedVersion ? 'rotate(180deg)' : 'rotate(0deg)',
|
||||
},
|
||||
|
||||
content: {
|
||||
position: 'relative',
|
||||
minHeight: 'calc(100vh - 50px)',
|
||||
zIndex: 1,
|
||||
background: theme.colorScheme === 'dark' ? theme.colors.dark![8] : theme.colors.gray![0],
|
||||
boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
|
||||
},
|
||||
|
||||
footer: {
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 200,
|
||||
background: theme.colorScheme === 'dark' ? theme.colors.dark![7] : theme.colors.gray![0],
|
||||
paddingLeft: 324,
|
||||
|
||||
[theme.fn.smallerThan('lg')]: {
|
||||
paddingRight: 54,
|
||||
},
|
||||
|
||||
[theme.fn.smallerThan('md')]: {
|
||||
paddingLeft: 24,
|
||||
},
|
||||
|
||||
[theme.fn.smallerThan('sm')]: {
|
||||
paddingRight: 24,
|
||||
height: 300,
|
||||
},
|
||||
},
|
||||
|
||||
links: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
|
||||
[theme.fn.smallerThan('sm')]: {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: 50,
|
||||
},
|
||||
},
|
||||
|
||||
link: { color: theme.colorScheme === 'dark' ? theme.white : theme.black },
|
||||
}),
|
||||
);
|
||||
|
||||
const packageMenuItems = PACKAGES.map((pkg) => (
|
||||
<Menu.Item
|
||||
key={pkg}
|
||||
component={NextLink}
|
||||
href={`/docs/packages/${pkg}/main`}
|
||||
sx={(theme) => ({ color: theme.colorScheme === 'dark' ? theme.white : theme.black })}
|
||||
>
|
||||
{pkg}
|
||||
</Menu.Item>
|
||||
));
|
||||
|
||||
export function SidebarLayout({
|
||||
packageName,
|
||||
branchName,
|
||||
@@ -157,261 +53,244 @@ export function SidebarLayout({
|
||||
}: PropsWithChildren<Partial<SidebarLayoutProps>>) {
|
||||
const router = useRouter();
|
||||
const [asPathWithoutQueryAndAnchor, setAsPathWithoutQueryAndAnchor] = useState('');
|
||||
const { data: versions } = useSWR<string[]>(
|
||||
`https://docs.discordjs.dev/api/info?package=${packageName ?? 'builders'}`,
|
||||
fetcher,
|
||||
);
|
||||
const theme = useMantineTheme();
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
|
||||
|
||||
const { data: versions } = useSWR<string[]>(`https://docs.discordjs.dev/api/info?package=${packageName}`, fetcher);
|
||||
const { resolvedTheme, setTheme } = useTheme();
|
||||
const toggleTheme = () => setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
|
||||
const matches = useMedia('(min-width: 992px)', false);
|
||||
const [opened, setOpened] = useState(false);
|
||||
const [openedLibPicker, setOpenedLibPicker] = useState(false);
|
||||
const [openedVersionPicker, setOpenedVersionPicker] = useState(false);
|
||||
const packageMenu = useMenuState({ gutter: 8, sameWidth: true });
|
||||
const versionMenu = useMenuState({ gutter: 8, sameWidth: true });
|
||||
// useLockBodyScroll(opened);
|
||||
|
||||
useEffect(() => {
|
||||
if (matches) {
|
||||
setOpened(false);
|
||||
setOpenedLibPicker(false);
|
||||
setOpenedVersionPicker(false);
|
||||
}, []);
|
||||
}
|
||||
}, [matches]);
|
||||
|
||||
useEffect(() => {
|
||||
setAsPathWithoutQueryAndAnchor(router.asPath.split('?')[0]?.split('#')[0]?.split(':')[0] ?? '');
|
||||
}, [router.asPath]);
|
||||
|
||||
const { classes } = useStyles({ openedLib: openedLibPicker, openedVersion: openedVersionPicker });
|
||||
const packageMenuItems = PACKAGES.map((pkg) => (
|
||||
<Link key={pkg} href={`/docs/packages/${pkg}/main`} passHref prefetch={false}>
|
||||
<MenuItem
|
||||
className="hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 rounded bg-white p-3 text-sm"
|
||||
as="a"
|
||||
state={packageMenu}
|
||||
>
|
||||
{pkg}
|
||||
</MenuItem>
|
||||
</Link>
|
||||
));
|
||||
|
||||
const versionMenuItems = useMemo(
|
||||
() =>
|
||||
versions?.map((item) => (
|
||||
<Menu.Item
|
||||
key={item}
|
||||
component={NextLink}
|
||||
href={`/docs/packages/${packageName ?? 'builders'}/${item}`}
|
||||
sx={(theme) => ({ color: theme.colorScheme === 'dark' ? theme.white : theme.black })}
|
||||
versions
|
||||
?.map((item) => (
|
||||
<Link key={item} href={`/docs/packages/${packageName}/${item}`} passHref prefetch={false}>
|
||||
<MenuItem
|
||||
className="hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 rounded bg-white p-3 text-sm"
|
||||
as="a"
|
||||
state={versionMenu}
|
||||
>
|
||||
{item}
|
||||
</Menu.Item>
|
||||
)) ?? [],
|
||||
</MenuItem>
|
||||
</Link>
|
||||
))
|
||||
.reverse() ?? [],
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[versions, packageName],
|
||||
);
|
||||
|
||||
const breadcrumbs = useMemo(
|
||||
const pathElements = useMemo(
|
||||
() =>
|
||||
asPathWithoutQueryAndAnchor.split('/').map((path, idx, original) => (
|
||||
<Link key={idx} href={original.slice(0, idx + 1).join('/')} passHref prefetch={false}>
|
||||
<Anchor component="a" sx={(theme) => ({ color: theme.colorScheme === 'dark' ? theme.white : theme.black })}>
|
||||
{path}
|
||||
</Anchor>
|
||||
<Link key={idx} href={original.slice(0, idx + 1).join('/')} prefetch={false}>
|
||||
<a className="hover:underline">{path}</a>
|
||||
</Link>
|
||||
)),
|
||||
[asPathWithoutQueryAndAnchor],
|
||||
);
|
||||
|
||||
const breadcrumbs = useMemo(
|
||||
() =>
|
||||
pathElements.flatMap((el, idx, array) => {
|
||||
if (idx !== array.length - 1) {
|
||||
return (
|
||||
<AppShell
|
||||
sx={(theme) => ({
|
||||
main: {
|
||||
background: theme.colorScheme === 'dark' ? theme.colors.dark![8] : theme.colors.gray![0],
|
||||
overflowX: 'auto',
|
||||
},
|
||||
})}
|
||||
padding={0}
|
||||
navbarOffsetBreakpoint="md"
|
||||
asideOffsetBreakpoint="md"
|
||||
navbar={
|
||||
<>
|
||||
<MediaQuery smallerThan="md" styles={{ display: 'none' }}>
|
||||
<LoadingOverlay
|
||||
sx={{ position: 'fixed', top: 70, width: 300 }}
|
||||
visible={router.isFallback}
|
||||
overlayBlur={2}
|
||||
/>
|
||||
</MediaQuery>
|
||||
<Navbar hiddenBreakpoint="md" hidden={!opened} width={{ md: 300 }}>
|
||||
{packageName && data ? (
|
||||
<>
|
||||
<Navbar.Section p="xs">
|
||||
<Stack spacing="xs">
|
||||
<Menu
|
||||
onOpen={() => setOpenedLibPicker(true)}
|
||||
onClose={() => setOpenedLibPicker(false)}
|
||||
radius="sm"
|
||||
width="target"
|
||||
>
|
||||
<Menu.Target>
|
||||
<UnstyledButton className={classes.control}>
|
||||
<Group position="apart">
|
||||
<Group>
|
||||
<ThemeIcon variant={colorScheme === 'dark' ? 'filled' : 'outline'} radius="sm" size={30}>
|
||||
<VscPackage size={20} />
|
||||
</ThemeIcon>
|
||||
<Text weight="600" size="md">
|
||||
{packageName}
|
||||
</Text>
|
||||
</Group>
|
||||
<VscChevronDown className={classes.iconLib} size={20} />
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>{packageMenuItems}</Menu.Dropdown>
|
||||
</Menu>
|
||||
|
||||
<Menu
|
||||
onOpen={() => setOpenedVersionPicker(true)}
|
||||
onClose={() => setOpenedVersionPicker(false)}
|
||||
radius="sm"
|
||||
width="target"
|
||||
>
|
||||
<Menu.Target>
|
||||
<UnstyledButton className={classes.control}>
|
||||
<Group position="apart">
|
||||
<Group>
|
||||
<ThemeIcon variant={colorScheme === 'dark' ? 'filled' : 'outline'} radius="sm" size={30}>
|
||||
<VscVersions size={20} />
|
||||
</ThemeIcon>
|
||||
<Text weight="600" size="md">
|
||||
{branchName}
|
||||
</Text>
|
||||
</Group>
|
||||
<VscChevronDown className={classes.iconVersion} size={20} />
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>{versionMenuItems}</Menu.Dropdown>
|
||||
</Menu>
|
||||
</Stack>
|
||||
</Navbar.Section>
|
||||
|
||||
<Navbar.Section px="xs" pb="xs" grow component={ScrollArea}>
|
||||
<SidebarItems members={data.members} setOpened={setOpened} />
|
||||
</Navbar.Section>
|
||||
</>
|
||||
) : null}
|
||||
</Navbar>
|
||||
</>
|
||||
<Fragment key={idx}>
|
||||
{el}
|
||||
<div className="mx-2">/</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
header={
|
||||
<Header
|
||||
sx={(theme) => ({
|
||||
boxShadow:
|
||||
theme.colorScheme === 'dark'
|
||||
? '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)'
|
||||
: 'unset',
|
||||
})}
|
||||
height={70}
|
||||
p="md"
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', height: '100%' }}>
|
||||
<Box>
|
||||
<MediaQuery largerThan="md" styles={{ display: 'none' }}>
|
||||
<Burger
|
||||
opened={opened}
|
||||
onClick={() => (router.isFallback ? null : setOpened((isOpened) => !isOpened))}
|
||||
size="sm"
|
||||
color={theme.colors.gray![6]}
|
||||
mr="xl"
|
||||
/>
|
||||
</MediaQuery>
|
||||
|
||||
<MediaQuery smallerThan="md" styles={{ display: 'none' }}>
|
||||
<Skeleton visible={router.isFallback} radius="sm">
|
||||
<Breadcrumbs>{breadcrumbs}</Breadcrumbs>
|
||||
</Skeleton>
|
||||
</MediaQuery>
|
||||
</Box>
|
||||
<Group>
|
||||
<Link href="https://github.com/discordjs/discord.js" passHref prefetch={false}>
|
||||
<ActionIcon
|
||||
component="a"
|
||||
return <Fragment key={idx}>{el}</Fragment>;
|
||||
}),
|
||||
[pathElements],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="dark:bg-dark-600 dark:border-dark-100 bg-light-600 border-light-800 fixed top-0 left-0 z-20 w-full border-b">
|
||||
<div className="h-18 block px-6">
|
||||
<div className="flex h-full flex-row place-content-between place-items-center">
|
||||
<Button
|
||||
className="flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline active:translate-y-px lg:hidden"
|
||||
onClick={() => setOpened((open) => !open)}
|
||||
>
|
||||
<VscMenu size={24} />
|
||||
</Button>
|
||||
<div className="hidden md:flex md:flex-row">{breadcrumbs}</div>
|
||||
<div className="flex flex-row gap-4">
|
||||
<Button
|
||||
as="a"
|
||||
className="flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline active:translate-y-px"
|
||||
href="https://github.com/discordjs/discord.js"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
variant="transparent"
|
||||
color="dark"
|
||||
title="GitHub repository"
|
||||
>
|
||||
<VscGithubInverted size={20} />
|
||||
</ActionIcon>
|
||||
</Link>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color={colorScheme === 'dark' ? 'yellow' : 'blue'}
|
||||
onClick={() => toggleColorScheme()}
|
||||
title="Toggle color scheme"
|
||||
radius="sm"
|
||||
<VscGithubInverted size={24} />
|
||||
</Button>
|
||||
<Button
|
||||
className="flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline active:translate-y-px"
|
||||
role="button"
|
||||
onClick={() => toggleTheme()}
|
||||
>
|
||||
{colorScheme === 'dark' ? <WiDaySunny size={30} /> : <WiNightClear size={30} />}
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
</Box>
|
||||
</Header>
|
||||
}
|
||||
<VscColorMode size={24} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<nav
|
||||
className={`h-[calc(100vh - 73px)] dark:bg-dark-600 dark:border-dark-100 border-light-800 fixed top-[73px] left-0 bottom-0 z-20 w-full border-r bg-white ${
|
||||
opened ? 'block' : 'hidden'
|
||||
} lg:w-76 lg:max-w-76 lg:block`}
|
||||
>
|
||||
<article>
|
||||
<Box className={classes.content} p="lg" pb={80}>
|
||||
{children}
|
||||
</Box>
|
||||
<Box sx={(theme) => ({ height: 200, [theme.fn.smallerThan('sm')]: { height: 300 } })} />
|
||||
<Box
|
||||
component="footer"
|
||||
sx={{ paddingRight: data?.member?.kind !== 'Class' && data?.member?.kind !== 'Interface' ? 54 : 324 }}
|
||||
className={classes.footer}
|
||||
pt={50}
|
||||
<Scrollbars
|
||||
universal
|
||||
autoHide
|
||||
hideTracksWhenNotNeeded
|
||||
renderTrackVertical={(props) => (
|
||||
<div {...props} className="absolute top-0.5 right-0.5 bottom-0.5 z-30 w-1.5 rounded" />
|
||||
)}
|
||||
renderThumbVertical={(props) => <div {...props} className="dark:bg-dark-100 bg-light-900 z-30 rounded" />}
|
||||
>
|
||||
<Container>
|
||||
<Box className={classes.links}>
|
||||
<Link href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss" prefetch={false}>
|
||||
<a title="Vercel">
|
||||
<Image
|
||||
src="/powered-by-vercel.svg"
|
||||
alt="Vercel"
|
||||
width={0}
|
||||
height={0}
|
||||
style={{ height: '100%', width: '100%', maxWidth: 200 }}
|
||||
<div className="flex flex-col gap-3 px-3 pt-3">
|
||||
<MenuButton
|
||||
className="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 rounded p-3"
|
||||
state={packageMenu}
|
||||
>
|
||||
<div className="flex flex-row place-content-between place-items-center">
|
||||
<div className="flex flex-row place-items-center gap-3">
|
||||
<VscPackage size={20} />
|
||||
<span className="font-semibold">{packageName}</span>
|
||||
</div>
|
||||
<VscChevronDown
|
||||
className={`transform transition duration-150 ease-in-out ${
|
||||
packageMenu.open ? 'rotate-180' : 'rotate-0'
|
||||
}`}
|
||||
size={20}
|
||||
/>
|
||||
</div>
|
||||
</MenuButton>
|
||||
<Menu
|
||||
className="dark:bg-dark-600 border-light-800 dark:border-dark-100 z-20 rounded border bg-white p-1"
|
||||
state={packageMenu}
|
||||
>
|
||||
{packageMenuItems}
|
||||
</Menu>
|
||||
|
||||
<MenuButton
|
||||
className="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 rounded p-3"
|
||||
state={versionMenu}
|
||||
>
|
||||
<div className="flex flex-row place-content-between place-items-center">
|
||||
<div className="flex flex-row place-items-center gap-3">
|
||||
<VscVersions size={20} />
|
||||
<span className="font-semibold">{branchName}</span>
|
||||
</div>
|
||||
<VscChevronDown
|
||||
className={`transform transition duration-150 ease-in-out ${
|
||||
versionMenu.open ? 'rotate-180' : 'rotate-0'
|
||||
}`}
|
||||
size={20}
|
||||
/>
|
||||
</div>
|
||||
</MenuButton>
|
||||
<Menu
|
||||
className="dark:bg-dark-600 border-light-800 dark:border-dark-100 z-20 rounded border bg-white p-1"
|
||||
state={versionMenu}
|
||||
>
|
||||
{versionMenuItems}
|
||||
</Menu>
|
||||
</div>
|
||||
|
||||
<SidebarItems members={data?.members ?? []} setOpened={setOpened} />
|
||||
</Scrollbars>
|
||||
</nav>
|
||||
<main
|
||||
className={`pt-18 lg:pl-76 ${
|
||||
data?.member?.kind === 'Class' || data?.member?.kind === 'Interface' ? 'xl:pr-64' : ''
|
||||
}`}
|
||||
>
|
||||
<article className="dark:bg-dark-600 bg-light-600">
|
||||
<div className="min-h-[calc(100vh - 50px)] dark:bg-dark-800 relative z-10 bg-white p-6 pb-20 shadow">
|
||||
{children}
|
||||
</div>
|
||||
<div className="h-76 md:h-52" />
|
||||
<footer
|
||||
className={`dark:bg-dark-600 h-76 lg:pl-84 bg-light-600 fixed bottom-0 left-0 right-0 md:h-52 md:pl-4 md:pr-16 ${
|
||||
data?.member?.kind === 'Class' || data?.member?.kind === 'Interface' ? 'xl:pr-76' : 'xl:pr-16'
|
||||
}`}
|
||||
>
|
||||
<div className="mx-auto flex max-w-6xl flex-col place-items-center gap-12 pt-12 lg:place-content-center">
|
||||
<div className="flex w-full flex-col place-content-between place-items-center gap-12 md:flex-row md:gap-0">
|
||||
<a
|
||||
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="Vercel"
|
||||
>
|
||||
<Image src={vercelLogo} alt="Vercel" />
|
||||
</a>
|
||||
</Link>
|
||||
<Group sx={(theme) => ({ gap: 50, [theme.fn.smallerThan('sm')]: { gap: 25 } })} align="flex-start">
|
||||
<Stack spacing={8}>
|
||||
<Title order={4}>Community</Title>
|
||||
<Stack spacing={0}>
|
||||
<Link href="https://discord.gg/djs" passHref prefetch={false}>
|
||||
<Anchor component="a" target="_blank" rel="noopener noreferrer" className={classes.link}>
|
||||
<div className="flex flex-row gap-6 md:gap-12">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h4 className="text-lg font-semibold">Community</h4>
|
||||
<div className="flex flex-col gap-1">
|
||||
<a href="https://discord.gg/djs" target="_blank" rel="noopener noreferrer">
|
||||
Discord
|
||||
</Anchor>
|
||||
</Link>
|
||||
<Link href="https://github.com/discordjs/discord.js/discussions" passHref prefetch={false}>
|
||||
<Anchor component="a" target="_blank" rel="noopener noreferrer" className={classes.link}>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/discordjs/discord.js/discussions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
GitHub discussions
|
||||
</Anchor>
|
||||
</Link>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack spacing={8}>
|
||||
<Title order={4}>Project</Title>
|
||||
<Stack spacing={0}>
|
||||
<Link href="https://github.com/discordjs/discord.js" passHref prefetch={false}>
|
||||
<Anchor component="a" target="_blank" rel="noopener noreferrer" className={classes.link}>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<h4 className="text-lg font-semibold">Project</h4>
|
||||
<div className="flex flex-col gap-1">
|
||||
<a href="https://github.com/discordjs/discord.js" target="_blank" rel="noopener noreferrer">
|
||||
discord.js
|
||||
</Anchor>
|
||||
</Link>
|
||||
<Link href="https://discordjs.guide" passHref prefetch={false}>
|
||||
<Anchor component="a" target="_blank" rel="noopener noreferrer" className={classes.link}>
|
||||
</a>
|
||||
<a href="https://discordjs.guide" target="_blank" rel="noopener noreferrer">
|
||||
discord.js guide
|
||||
</Anchor>
|
||||
</Link>
|
||||
<Link href="https://discord-api-types.dev" passHref prefetch={false}>
|
||||
<Anchor component="a" target="_blank" rel="noopener noreferrer" className={classes.link}>
|
||||
</a>
|
||||
<a href="https://discord-api-types.dev" target="_blank" rel="noopener noreferrer">
|
||||
discord-api-types
|
||||
</Anchor>
|
||||
</Link>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</AppShell>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
31
packages/website/src/components/SyntaxHighlighter.tsx
Normal file
31
packages/website/src/components/SyntaxHighlighter.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { PrismAsyncLight } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus, prism } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
|
||||
export function SyntaxHighlighter({ language = 'typescript', code }: { code: string; language?: string }) {
|
||||
return (
|
||||
<>
|
||||
<div data-theme="dark">
|
||||
<PrismAsyncLight
|
||||
wrapLines
|
||||
wrapLongLines
|
||||
language={language}
|
||||
style={vscDarkPlus}
|
||||
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
|
||||
>
|
||||
{code}
|
||||
</PrismAsyncLight>
|
||||
</div>
|
||||
<div data-theme="light">
|
||||
<PrismAsyncLight
|
||||
wrapLines
|
||||
wrapLongLines
|
||||
language={language}
|
||||
style={prism}
|
||||
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
|
||||
>
|
||||
{code}
|
||||
</PrismAsyncLight>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Table as MantineTable } from '@mantine/core';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useMemo, type ReactNode } from 'react';
|
||||
|
||||
export function Table({
|
||||
rows,
|
||||
@@ -10,28 +9,39 @@ export function Table({
|
||||
columns: string[];
|
||||
rows: Record<string, ReactNode>[];
|
||||
}) {
|
||||
return (
|
||||
<MantineTable>
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map((column) => (
|
||||
<th key={column} className="break-normal">
|
||||
const cols = useMemo(
|
||||
() =>
|
||||
columns.map((column) => (
|
||||
<th key={column} className="border-light-900 break-normal border-b px-3 py-2 text-left text-sm">
|
||||
{column}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, idx) => (
|
||||
<tr key={idx}>
|
||||
)),
|
||||
[columns],
|
||||
);
|
||||
|
||||
const data = useMemo(
|
||||
() =>
|
||||
rows.map((row, idx) => (
|
||||
<tr key={idx} className="[&>td]:last-of-type:border-0">
|
||||
{Object.entries(row).map(([colName, val]) => (
|
||||
<td key={colName} className={columnStyles?.[colName] ?? ''}>
|
||||
<td
|
||||
key={colName}
|
||||
className={`border-light-900 border-b px-3 py-2 text-left text-sm ${columnStyles?.[colName] ?? ''}`}
|
||||
>
|
||||
{val}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</MantineTable>
|
||||
)),
|
||||
[columnStyles, rows],
|
||||
);
|
||||
|
||||
return (
|
||||
<table className="w-full border-collapse">
|
||||
<thead>
|
||||
<tr>{cols}</tr>
|
||||
</thead>
|
||||
<tbody>{data}</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,31 +1,7 @@
|
||||
import type { ApiClassJSON, ApiInterfaceJSON } from '@discordjs/api-extractor-utils';
|
||||
import { createStyles, Group, Text, Box, Stack, ThemeIcon, useMantineColorScheme } from '@mantine/core';
|
||||
import { useMemo } from 'react';
|
||||
import { VscListSelection, VscSymbolMethod, VscSymbolProperty } from 'react-icons/vsc';
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
link: {
|
||||
...theme.fn.focusStyles(),
|
||||
fontWeight: 500,
|
||||
display: 'block',
|
||||
textDecoration: 'none',
|
||||
color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.colors.gray![7],
|
||||
lineHeight: 1.2,
|
||||
fontSize: theme.fontSizes.sm,
|
||||
padding: 5,
|
||||
paddingLeft: theme.spacing.md,
|
||||
marginLeft: 14,
|
||||
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],
|
||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export function TableOfContentItems({
|
||||
methods,
|
||||
properties,
|
||||
@@ -33,22 +9,19 @@ export function TableOfContentItems({
|
||||
methods: ApiClassJSON['methods'] | ApiInterfaceJSON['methods'];
|
||||
properties: ApiClassJSON['properties'] | ApiInterfaceJSON['properties'];
|
||||
}) {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const { classes } = useStyles();
|
||||
|
||||
const propertyItems = useMemo(
|
||||
() =>
|
||||
properties.map((prop) => (
|
||||
<Box<'a'> key={prop.name} href={`#${prop.name}`} component="a" className={classes.link}>
|
||||
<Group>
|
||||
<Text sx={{ textOverflow: 'ellipsis', overflow: 'hidden' }} className="line-clamp-1">
|
||||
{prop.name}
|
||||
</Text>
|
||||
</Group>
|
||||
</Box>
|
||||
<a
|
||||
key={prop.name}
|
||||
href={`#${prop.name}`}
|
||||
title={prop.name}
|
||||
className="dark:border-dark-100 border-light-800 dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800 pl-6.5 ml-[10px] border-l p-[5px] text-sm"
|
||||
>
|
||||
<span className="line-clamp-1">{prop.name}</span>
|
||||
</a>
|
||||
)),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[properties, colorScheme],
|
||||
[properties],
|
||||
);
|
||||
|
||||
const methodItems = useMemo(
|
||||
@@ -59,61 +32,52 @@ export function TableOfContentItems({
|
||||
}`;
|
||||
|
||||
return (
|
||||
<Box<'a'> key={key} component="a" href={`#${key}`} className={classes.link}>
|
||||
<Group>
|
||||
<Text sx={{ textOverflow: 'ellipsis', overflow: 'hidden' }} className="line-clamp-1">
|
||||
{member.name}
|
||||
</Text>
|
||||
<a
|
||||
key={key}
|
||||
href={`#${key}`}
|
||||
title={member.name}
|
||||
className="dark:border-dark-100 border-light-800 dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800 pl-6.5 ml-[10px] flex flex-row place-items-center gap-2 border-l p-[5px] text-sm"
|
||||
>
|
||||
<span className="line-clamp-1">{member.name}</span>
|
||||
{member.overloadIndex && member.overloadIndex > 1 ? (
|
||||
<Text size="xs" color="dimmed">
|
||||
{member.overloadIndex}
|
||||
</Text>
|
||||
<span className="text-xs">{member.overloadIndex}</span>
|
||||
) : null}
|
||||
</Group>
|
||||
</Box>
|
||||
</a>
|
||||
);
|
||||
}),
|
||||
[methods, classes.link],
|
||||
[methods],
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ wordBreak: 'break-all' }} pb="xl">
|
||||
<Group spacing="xs" mt={6} mb="sm" ml={6}>
|
||||
<div className="flex flex-col break-all p-3 pb-8">
|
||||
<div className="mt-4 ml-2 flex flex-row gap-2">
|
||||
<VscListSelection size={25} />
|
||||
<Text weight={600}>Table of contents</Text>
|
||||
</Group>
|
||||
<Stack spacing={0} mt={26} ml={4}>
|
||||
<span className="font-semibold">Contents</span>
|
||||
</div>
|
||||
<div className="mt-5.5 ml-2 flex flex-col gap-2">
|
||||
{propertyItems.length ? (
|
||||
<Box>
|
||||
<Group spacing="xs">
|
||||
<ThemeIcon variant={colorScheme === 'dark' ? 'filled' : 'outline'} radius="sm" size={30}>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-row place-items-center gap-4">
|
||||
<VscSymbolProperty size={20} />
|
||||
</ThemeIcon>
|
||||
<Box p="sm" pl={0}>
|
||||
<Text weight={600} size="md">
|
||||
Properties
|
||||
</Text>
|
||||
</Box>
|
||||
</Group>
|
||||
<div className="p-3 pl-0">
|
||||
<span className="font-semibold">Properties</span>
|
||||
</div>
|
||||
</div>
|
||||
{propertyItems}
|
||||
</Box>
|
||||
</div>
|
||||
) : null}
|
||||
{methodItems.length ? (
|
||||
<Box>
|
||||
<Group spacing="xs">
|
||||
<ThemeIcon variant={colorScheme === 'dark' ? 'filled' : 'outline'} radius="sm" size={30}>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-row place-items-center gap-4">
|
||||
<VscSymbolMethod size={20} />
|
||||
</ThemeIcon>
|
||||
<Box p="sm" pl={0}>
|
||||
<Text weight={600} size="md">
|
||||
Methods
|
||||
</Text>
|
||||
</Box>
|
||||
</Group>
|
||||
<div className="p-3 pl-0">
|
||||
<span className="font-semibold">Methods</span>
|
||||
</div>
|
||||
</div>
|
||||
{methodItems}
|
||||
</Box>
|
||||
</div>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { TypeParameterData } from '@discordjs/api-extractor-utils';
|
||||
import { ScrollArea } from '@mantine/core';
|
||||
import { useMemo } from 'react';
|
||||
import { HyperlinkedText } from './HyperlinkedText';
|
||||
import { Table } from './Table';
|
||||
import { TSDoc } from './tsdoc/TSDoc';
|
||||
@@ -11,21 +11,25 @@ const rowElements = {
|
||||
};
|
||||
|
||||
export function TypeParamTable({ data }: { data: TypeParameterData[] }) {
|
||||
const rows = data.map((typeParam) => ({
|
||||
const rows = useMemo(
|
||||
() =>
|
||||
data.map((typeParam) => ({
|
||||
Name: typeParam.name,
|
||||
Constraints: <HyperlinkedText tokens={typeParam.constraintTokens} />,
|
||||
Optional: typeParam.optional ? 'Yes' : 'No',
|
||||
Default: <HyperlinkedText tokens={typeParam.defaultTokens} />,
|
||||
Description: typeParam.commentBlock ? <TSDoc node={typeParam.commentBlock} /> : 'None',
|
||||
}));
|
||||
})),
|
||||
[data],
|
||||
);
|
||||
|
||||
return (
|
||||
<ScrollArea pb="xs" offsetScrollbars>
|
||||
<div className="overflow-x-auto">
|
||||
<Table
|
||||
columns={['Name', 'Constraints', 'Optional', 'Default', 'Description']}
|
||||
rows={rows}
|
||||
columnStyles={rowElements}
|
||||
/>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import type { ApiClassJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Skeleton } from '@mantine/core';
|
||||
import { useRouter } from 'next/router';
|
||||
import { DocContainer } from '../DocContainer';
|
||||
import { ConstructorSection, MethodsSection, PropertiesSection } from '../Sections';
|
||||
|
||||
export function Class({ data }: { data: ApiClassJSON }) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<DocContainer
|
||||
name={data.name}
|
||||
@@ -20,12 +16,8 @@ export function Class({ data }: { data: ApiClassJSON }) {
|
||||
properties={data.properties}
|
||||
>
|
||||
{data.constructor ? <ConstructorSection data={data.constructor} /> : null}
|
||||
<Skeleton visible={router.isFallback} radius="sm">
|
||||
<PropertiesSection data={data.properties} />
|
||||
</Skeleton>
|
||||
<Skeleton visible={router.isFallback} radius="sm">
|
||||
<MethodsSection data={data.methods} />
|
||||
</Skeleton>
|
||||
</DocContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
import type { ApiEnumJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Skeleton, Stack } from '@mantine/core';
|
||||
import { useMediaQuery } from '@mantine/hooks';
|
||||
import { useRouter } from 'next/router';
|
||||
import { VscSymbolEnumMember } from 'react-icons/vsc';
|
||||
import { useMedia } from 'react-use';
|
||||
import { CodeListing, CodeListingSeparatorType } from '../CodeListing';
|
||||
import { DocContainer } from '../DocContainer';
|
||||
import { Section } from '../Section';
|
||||
|
||||
export function Enum({ data }: { data: ApiEnumJSON }) {
|
||||
const router = useRouter();
|
||||
const matches = useMediaQuery('(max-width: 768px)');
|
||||
const matches = useMedia('(max-width: 768px)', true);
|
||||
|
||||
return (
|
||||
<DocContainer name={data.name} kind={data.kind} excerpt={data.excerpt} summary={data.summary}>
|
||||
<Skeleton visible={router.isFallback} radius="sm">
|
||||
<Section title="Members" icon={<VscSymbolEnumMember size={20} />} padded dense={matches}>
|
||||
<Stack>
|
||||
<div className="flex flex-col gap-4">
|
||||
{data.members.map((member) => (
|
||||
<CodeListing
|
||||
key={member.name}
|
||||
@@ -25,9 +21,8 @@ export function Enum({ data }: { data: ApiEnumJSON }) {
|
||||
summary={member.summary}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</div>
|
||||
</Section>
|
||||
</Skeleton>
|
||||
</DocContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import type { ApiFunctionJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Skeleton } from '@mantine/core';
|
||||
import { useRouter } from 'next/router';
|
||||
import { DocContainer } from '../DocContainer';
|
||||
import { ParametersSection } from '../Sections';
|
||||
|
||||
export function Function({ data }: { data: ApiFunctionJSON }) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<DocContainer
|
||||
name={`${data.name}${data.overloadIndex && data.overloadIndex > 1 ? ` (${data.overloadIndex})` : ''}`}
|
||||
@@ -15,9 +11,7 @@ export function Function({ data }: { data: ApiFunctionJSON }) {
|
||||
summary={data.summary}
|
||||
typeParams={data.typeParameters}
|
||||
>
|
||||
<Skeleton visible={router.isFallback} radius="sm">
|
||||
<ParametersSection data={data.parameters} />
|
||||
</Skeleton>
|
||||
</DocContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import type { ApiInterfaceJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Skeleton } from '@mantine/core';
|
||||
import { useRouter } from 'next/router';
|
||||
import { DocContainer } from '../DocContainer';
|
||||
import { MethodsSection, PropertiesSection } from '../Sections';
|
||||
|
||||
export function Interface({ data }: { data: ApiInterfaceJSON }) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<DocContainer
|
||||
name={data.name}
|
||||
@@ -17,12 +13,8 @@ export function Interface({ data }: { data: ApiInterfaceJSON }) {
|
||||
methods={data.methods}
|
||||
properties={data.properties}
|
||||
>
|
||||
<Skeleton visible={router.isFallback} radius="sm">
|
||||
<PropertiesSection data={data.properties} />
|
||||
</Skeleton>
|
||||
<Skeleton visible={router.isFallback} radius="sm">
|
||||
<MethodsSection data={data.methods} />
|
||||
</Skeleton>
|
||||
</DocContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { Alert, Box, Title, Text } from '@mantine/core';
|
||||
import { StandardTags } from '@microsoft/tsdoc';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { VscWarning } from 'react-icons/vsc';
|
||||
|
||||
export function Block({ children, title }: PropsWithChildren<{ title: string }>) {
|
||||
return (
|
||||
<Box>
|
||||
<Title order={5}>{title}</Title>
|
||||
<div className="flex flex-col gap-2">
|
||||
<h5 className="font-bold">{title}</h5>
|
||||
{children}
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,9 +20,17 @@ export function ExampleBlock({
|
||||
|
||||
export function DeprecatedBlock({ children }: PropsWithChildren): JSX.Element {
|
||||
return (
|
||||
<Alert icon={<VscWarning />} title="Deprecated" variant="outline" color="red" radius="sm">
|
||||
<div className="rounded border border-red-500 p-4">
|
||||
<div className="flex flex-row place-items-center gap-4">
|
||||
<span className="text-red-500">
|
||||
<VscWarning size={20} />
|
||||
</span>
|
||||
<div className="flex flex-col gap-2 text-sm">
|
||||
<span className="font-semibold text-red-500">Deprecated</span>
|
||||
{children}
|
||||
</Alert>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,7 +61,7 @@ export function BlockComment({
|
||||
return <DefaultValueBlock>{children}</DefaultValueBlock>;
|
||||
case StandardTags.typeParam.tagNameWithUpperCase:
|
||||
case StandardTags.param.tagNameWithUpperCase:
|
||||
return <Text>{children}</Text>;
|
||||
return <span>{children}</span>;
|
||||
default: // TODO: Support more blocks in the future.
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
@@ -7,34 +7,29 @@ import type {
|
||||
DocBlockJSON,
|
||||
DocCommentJSON,
|
||||
} from '@discordjs/api-extractor-utils';
|
||||
import { Anchor, Box, Code, Text, useMantineColorScheme } from '@mantine/core';
|
||||
import { DocNodeKind, StandardTags } from '@microsoft/tsdoc';
|
||||
import Link from 'next/link';
|
||||
import { Fragment, useCallback, type ReactNode } from 'react';
|
||||
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus, ghcolors } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
import { SyntaxHighlighter } from '../SyntaxHighlighter';
|
||||
import { BlockComment } from './BlockComment';
|
||||
|
||||
export function TSDoc({ node }: { node: AnyDocNodeJSON }): JSX.Element {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
|
||||
const createNode = useCallback(
|
||||
(node: AnyDocNodeJSON, idx?: number): ReactNode => {
|
||||
const createNode = useCallback((node: AnyDocNodeJSON, idx?: number): ReactNode => {
|
||||
let numberOfExamples = 0;
|
||||
let exampleIndex = 0;
|
||||
|
||||
switch (node.kind) {
|
||||
case DocNodeKind.PlainText:
|
||||
return (
|
||||
<Text key={idx} span style={{ wordBreak: 'break-word' }}>
|
||||
<span key={idx} className="break-words">
|
||||
{(node as DocPlainTextJSON).text}
|
||||
</Text>
|
||||
</span>
|
||||
);
|
||||
case DocNodeKind.Paragraph:
|
||||
return (
|
||||
<Text key={idx} inline style={{ wordBreak: 'break-word' }}>
|
||||
<span key={idx} className="break-words leading-relaxed">
|
||||
{(node as DocNodeContainerJSON).nodes.map((node, idx) => createNode(node, idx))}
|
||||
</Text>
|
||||
</span>
|
||||
);
|
||||
case DocNodeKind.SoftBreak:
|
||||
return <Fragment key={idx} />;
|
||||
@@ -43,20 +38,16 @@ export function TSDoc({ node }: { node: AnyDocNodeJSON }): JSX.Element {
|
||||
|
||||
if (codeDestination) {
|
||||
return (
|
||||
<Link key={idx} href={codeDestination.path} passHref prefetch={false}>
|
||||
<Anchor component="a" className="font-mono">
|
||||
{text ?? codeDestination.name}
|
||||
</Anchor>
|
||||
<Link key={idx} href={codeDestination.path} prefetch={false}>
|
||||
<a className="text-blurple font-mono">{text ?? codeDestination.name}</a>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
if (urlDestination) {
|
||||
return (
|
||||
<Link key={idx} href={urlDestination} passHref prefetch={false}>
|
||||
<Anchor component="a" className="font-mono">
|
||||
{text ?? urlDestination}
|
||||
</Anchor>
|
||||
<Link key={idx} href={urlDestination} prefetch={false}>
|
||||
<a className="text-blurple font-mono">{text ?? urlDestination}</a>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
@@ -67,26 +58,15 @@ export function TSDoc({ node }: { node: AnyDocNodeJSON }): JSX.Element {
|
||||
case DocNodeKind.CodeSpan: {
|
||||
const { code } = node as DocFencedCodeJSON;
|
||||
return (
|
||||
<Code key={idx} sx={{ display: 'inline' }} className="text-sm font-mono">
|
||||
<code key={idx} className="font-mono text-sm">
|
||||
{code}
|
||||
</Code>
|
||||
</code>
|
||||
);
|
||||
}
|
||||
|
||||
case DocNodeKind.FencedCode: {
|
||||
const { language, code } = node as DocFencedCodeJSON;
|
||||
return (
|
||||
<SyntaxHighlighter
|
||||
key={idx}
|
||||
wrapLines
|
||||
wrapLongLines
|
||||
language={language}
|
||||
style={colorScheme === 'dark' ? vscDarkPlus : ghcolors}
|
||||
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
|
||||
>
|
||||
{code}
|
||||
</SyntaxHighlighter>
|
||||
);
|
||||
return <SyntaxHighlighter key={idx} language={language} code={code} />;
|
||||
}
|
||||
|
||||
case DocNodeKind.ParamBlock:
|
||||
@@ -108,31 +88,32 @@ export function TSDoc({ node }: { node: AnyDocNodeJSON }): JSX.Element {
|
||||
|
||||
case DocNodeKind.Comment: {
|
||||
const comment = node as DocCommentJSON;
|
||||
|
||||
if (!comment.customBlocks.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Cheat a bit by finding out how many comments we have beforehand...
|
||||
numberOfExamples = comment.customBlocks.filter(
|
||||
(block) => block.tag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase,
|
||||
).length;
|
||||
|
||||
return <Box key={idx}>{comment.customBlocks.map((node, idx) => createNode(node, idx))}</Box>;
|
||||
return <div key={idx}>{comment.customBlocks.map((node, idx) => createNode(node, idx))}</div>;
|
||||
}
|
||||
|
||||
default:
|
||||
console.log(`Captured unknown node kind: ${node.kind}`);
|
||||
break;
|
||||
}
|
||||
|
||||
// console.log(`Captured unknown node kind: ${node.kind}`);
|
||||
return null;
|
||||
},
|
||||
[colorScheme],
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<>
|
||||
{node.kind === 'Paragraph' || node.kind === 'Section' ? (
|
||||
<>{(node as DocNodeContainerJSON).nodes.map((node, idx) => createNode(node, idx))}</>
|
||||
) : (
|
||||
createNode(node)
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
import { NextResponse, type NextRequest } from 'next/server';
|
||||
import { PACKAGES } from './util/constants';
|
||||
|
||||
export default async function middleware(request: NextRequest) {
|
||||
if (PACKAGES.some((pkg) => request.nextUrl.pathname.includes(pkg))) {
|
||||
const packageName = /\/docs\/packages\/([^/]+)\/.*/.exec(request.nextUrl.pathname)?.[1] ?? 'builders';
|
||||
const res = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName}`);
|
||||
const data: string[] = await res.json();
|
||||
|
||||
const latestVersion = data.at(-2);
|
||||
return NextResponse.redirect(
|
||||
new URL(request.nextUrl.pathname.replace('stable', latestVersion ?? 'main'), request.url),
|
||||
);
|
||||
}
|
||||
|
||||
export default function middleware(request: NextRequest) {
|
||||
return NextResponse.redirect(new URL('/docs/packages', request.url));
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: ['/docs'],
|
||||
matcher: ['/docs', '/docs/packages/:package/stable/:member*'],
|
||||
};
|
||||
|
||||
@@ -1,41 +1,22 @@
|
||||
import { Container, Title, Group, Button, Box, createStyles } from '@mantine/core';
|
||||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
label: {
|
||||
textAlign: 'center',
|
||||
fontWeight: 900,
|
||||
fontSize: 220,
|
||||
lineHeight: 1,
|
||||
marginBottom: theme.spacing.xl * 1.5,
|
||||
color: theme.colorScheme === 'dark' ? theme.colors.dark![4] : theme.colors.gray![2],
|
||||
|
||||
[theme.fn.smallerThan('sm')]: {
|
||||
fontSize: 120,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function FourOhFourPage() {
|
||||
const { classes } = useStyles();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title key="title">discord.js | 404</title>
|
||||
<meta key="og_title" property="og:title" content="discord.js | 404" />
|
||||
</Head>
|
||||
<Container pt={96} pb={96}>
|
||||
<Box className={classes.label}>404</Box>
|
||||
<Title align="center">Not found.</Title>
|
||||
<Group position="center">
|
||||
<Link href="/docs/packages" passHref prefetch={false}>
|
||||
<Button component="a" variant="filled" size="md" mt="xl">
|
||||
<div className="mx-auto flex h-full max-w-lg flex-col place-content-center place-items-center gap-8 py-16 px-8 lg:py-0 lg:px-6">
|
||||
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">404</h1>
|
||||
<h2 className="text-[2rem] md:text-[3rem]">Not found.</h2>
|
||||
<Link href="/docs/packages" prefetch={false}>
|
||||
<a className="bg-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 px-6 text-base font-semibold leading-none text-white no-underline active:translate-y-px">
|
||||
Take me back
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</Group>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,135 +1,31 @@
|
||||
import { type ColorScheme, ColorSchemeProvider, MantineProvider } from '@mantine/core';
|
||||
import { useLocalStorage } from '@mantine/hooks';
|
||||
import { type SpotlightAction, SpotlightProvider } from '@mantine/spotlight';
|
||||
import type { AppProps } from 'next/app';
|
||||
import Head from 'next/head';
|
||||
import { type NextRouter, useRouter } from 'next/router';
|
||||
import { VscPackage } from 'react-icons/vsc';
|
||||
import { RouterTransition } from '../components/RouterTransition';
|
||||
import NextProgress from 'next-progress';
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
import '@unocss/reset/antfu.css';
|
||||
import '../styles/unocss.css';
|
||||
import '../styles/main.css';
|
||||
import { miniSearch } from '~/util/search';
|
||||
|
||||
const actions: (router: NextRouter) => SpotlightAction[] = (router: NextRouter) => [
|
||||
{
|
||||
title: 'Home',
|
||||
description: 'Go to landing page',
|
||||
onTrigger: () => void router.push('/'),
|
||||
icon: <VscPackage size={20} />,
|
||||
},
|
||||
{
|
||||
title: 'Packages',
|
||||
description: 'Go to the package selection',
|
||||
onTrigger: () => void router.push('/docs/packages'),
|
||||
icon: <VscPackage size={20} />,
|
||||
},
|
||||
{
|
||||
id: 'packages-builders',
|
||||
title: 'Builders',
|
||||
description: 'Go to the @discordjs/builders documentation',
|
||||
onTrigger: () => void router.push('/docs/packages/builders'),
|
||||
icon: <VscPackage size={20} />,
|
||||
},
|
||||
{
|
||||
id: 'packages-collection',
|
||||
title: 'Collection',
|
||||
description: 'Go to the @discordjs/collection documentation',
|
||||
onTrigger: () => void router.push('/docs/packages/collection'),
|
||||
icon: <VscPackage size={20} />,
|
||||
},
|
||||
{
|
||||
id: 'packages-proxy',
|
||||
title: 'Proxy',
|
||||
description: 'Go to the @discordjs/proxy documentation',
|
||||
onTrigger: () => void router.push('/docs/packages/proxy'),
|
||||
icon: <VscPackage size={20} />,
|
||||
},
|
||||
{
|
||||
id: 'packages-rest',
|
||||
title: 'REST',
|
||||
description: 'Go to the @discordjs/rest documentation',
|
||||
onTrigger: () => void router.push('/docs/packages/voice'),
|
||||
icon: <VscPackage size={20} />,
|
||||
},
|
||||
{
|
||||
id: 'packages-voice',
|
||||
title: 'Voice',
|
||||
description: 'Go to the @discordjs/voice documentation',
|
||||
onTrigger: () => void router.push('/docs/packages/ws'),
|
||||
icon: <VscPackage size={20} />,
|
||||
},
|
||||
{
|
||||
id: 'packages-ws',
|
||||
title: 'WS',
|
||||
description: 'Go to the @discordjs/ws documentation',
|
||||
onTrigger: () => void router.push('/docs'),
|
||||
icon: <VscPackage size={20} />,
|
||||
},
|
||||
];
|
||||
|
||||
export default function MyApp({ Component, pageProps }: AppProps) {
|
||||
const router = useRouter();
|
||||
const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>({
|
||||
key: 'theme',
|
||||
defaultValue: 'dark',
|
||||
getInitialValueInEffect: true,
|
||||
});
|
||||
const toggleColorScheme = (value?: ColorScheme) =>
|
||||
setColorScheme(value ?? (colorScheme === 'dark' ? 'light' : 'dark'));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title key="title">discord.js</title>
|
||||
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
|
||||
<meta name="theme-color" content="#5865f2" />
|
||||
</Head>
|
||||
<ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
|
||||
<MantineProvider
|
||||
theme={{
|
||||
fontFamily: 'Inter var',
|
||||
fontFamilyMonospace: 'JetBrains Mono',
|
||||
headings: {
|
||||
fontFamily: 'Inter var',
|
||||
},
|
||||
colorScheme,
|
||||
colors: {
|
||||
blurple: [
|
||||
'#5865f2',
|
||||
'#5865f2',
|
||||
'#5865f2',
|
||||
'#5865f2',
|
||||
'#5865f2',
|
||||
'#5865f2',
|
||||
'#5865f2',
|
||||
'#5865f2',
|
||||
'#5865f2',
|
||||
'#5865f2',
|
||||
],
|
||||
},
|
||||
primaryColor: 'blurple',
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
value={{
|
||||
light: 'light',
|
||||
dark: 'dark',
|
||||
}}
|
||||
withCSSVariables
|
||||
withNormalizeCSS
|
||||
withGlobalStyles
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<SpotlightProvider
|
||||
shortcut={['mod + P', 'mod + K', '/']}
|
||||
actions={actions(router)}
|
||||
limit={7}
|
||||
filter={(query, actions) => {
|
||||
if (!query) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
const search = miniSearch.search(query);
|
||||
return actions.filter((action) => search.some((res) => res.name === action.title));
|
||||
}}
|
||||
>
|
||||
<RouterTransition />
|
||||
<NextProgress color="#5865f2" options={{ showSpinner: false }} />
|
||||
<Component {...pageProps} />
|
||||
</SpotlightProvider>
|
||||
</MantineProvider>
|
||||
</ColorSchemeProvider>
|
||||
</ThemeProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { createGetInitialProps } from '@mantine/next';
|
||||
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
||||
import { Html, Head, Main, NextScript } from 'next/document';
|
||||
import { DESCRIPTION } from '~/util/constants';
|
||||
|
||||
const getInitialProps = createGetInitialProps();
|
||||
|
||||
export default class _Document extends Document {
|
||||
public static override getInitialProps = getInitialProps;
|
||||
|
||||
public override render() {
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
@@ -16,28 +11,23 @@ export default class _Document extends Document {
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#090a16" />
|
||||
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<meta name="apple-mobile-web-app-title" content="discord.js" />
|
||||
<meta name="application-name" content="discord.js" />
|
||||
<meta name="msapplication-TileColor" content="#090a16" />
|
||||
<meta name="theme-color" content="#1a1b1e" />
|
||||
<meta
|
||||
name="description"
|
||||
content="discord.js is a powerful node.js module that allows you to interact with the Discord API very easily. It takes a much more object-oriented approach than most other JS Discord libraries, making your bot's code significantly tidier and easier to comprehend."
|
||||
/>
|
||||
<meta key="description" name="description" content={DESCRIPTION} />
|
||||
<meta property="og:site_name" content="discord.js" />
|
||||
<meta property="og:title" content="discord.js" />
|
||||
<meta
|
||||
name="og:description"
|
||||
content="discord.js is a powerful node.js module that allows you to interact with the Discord API very easily. It takes a much more object-oriented approach than most other JS Discord libraries, making your bot's code significantly tidier and easier to comprehend."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta key="og_title" property="og:title" content="discord.js" />
|
||||
<meta key="og_description" name="og:description" content={DESCRIPTION} />
|
||||
<meta property="og:image" content="https://discordjs.dev/open-graph.png" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:creator" content="@iCrawlToGo" />
|
||||
</Head>
|
||||
<body>
|
||||
<body className="dark:bg-dark-800 bg-white">
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,22 +13,23 @@ import {
|
||||
type ApiEnumJSON,
|
||||
} from '@discordjs/api-extractor-utils';
|
||||
import { createApiModel } from '@discordjs/scripts';
|
||||
import { ActionIcon, Affix, Box, LoadingOverlay, Transition } from '@mantine/core';
|
||||
import { useMediaQuery, useWindowScroll } from '@mantine/hooks';
|
||||
import { registerSpotlightActions } from '@mantine/spotlight';
|
||||
import { ApiFunction, ApiItemKind, type ApiPackage } from '@microsoft/api-extractor-model';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { GetStaticPaths, GetStaticProps } from 'next/types';
|
||||
import { MDXRemote } from 'next-mdx-remote';
|
||||
import { serialize } from 'next-mdx-remote/serialize';
|
||||
import { useEffect } from 'react';
|
||||
import { VscChevronUp } from 'react-icons/vsc';
|
||||
import { useMemo } from 'react';
|
||||
import rehypeIgnore from 'rehype-ignore';
|
||||
import rehypePrettyCode from 'rehype-pretty-code';
|
||||
import rehypePrettyCode, { type Options } from 'rehype-pretty-code';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { getHighlighter } from 'shiki';
|
||||
import shikiLangJavascript from 'shiki/languages/javascript.tmLanguage.json';
|
||||
import shikiLangTypescript from 'shiki/languages/typescript.tmLanguage.json';
|
||||
import shikiThemeDarkPlus from 'shiki/themes/dark-plus.json';
|
||||
import shikiThemeLightPlus from 'shiki/themes/light-plus.json';
|
||||
import { SidebarLayout, type SidebarLayoutProps } from '~/components/SidebarLayout';
|
||||
import { Class } from '~/components/model/Class';
|
||||
import { Enum } from '~/components/model/Enum';
|
||||
@@ -37,9 +38,9 @@ import { Interface } from '~/components/model/Interface';
|
||||
import { TypeAlias } from '~/components/model/TypeAlias';
|
||||
import { Variable } from '~/components/model/Variable';
|
||||
import { MemberProvider } from '~/contexts/member';
|
||||
import { PACKAGES } from '~/util/constants';
|
||||
import { findMember, findMemberByKey } from '~/util/model.server';
|
||||
import { PACKAGES } from '~/util/packages';
|
||||
import { miniSearch } from '~/util/search';
|
||||
// import { miniSearch } from '~/util/search';
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const pkgs = (
|
||||
@@ -54,6 +55,7 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
||||
} else {
|
||||
const response = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName}`);
|
||||
versions = await response.json();
|
||||
versions = versions.slice(-2);
|
||||
|
||||
for (const version of versions) {
|
||||
const res = await fetch(`https://docs.discordjs.dev/docs/${packageName}/${version}.api.json`);
|
||||
@@ -123,7 +125,13 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
const [, packageName = 'builders', branchName = 'main', member] = params!.slug as string[];
|
||||
const [path, packageName = 'builders', branchName = 'main', member] = params!.slug as string[];
|
||||
|
||||
if (path !== 'packages' || !PACKAGES.includes(packageName)) {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
const [memberName, overloadIndex] = member?.split(':') ?? [];
|
||||
|
||||
@@ -134,7 +142,30 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
mdxOptions: {
|
||||
remarkPlugins: [remarkGfm],
|
||||
remarkRehypeOptions: { allowDangerousHtml: true },
|
||||
rehypePlugins: [rehypeRaw, rehypeIgnore, rehypeSlug, [rehypePrettyCode, { theme: 'dark-plus' }]],
|
||||
rehypePlugins: [
|
||||
rehypeRaw,
|
||||
rehypeIgnore,
|
||||
rehypeSlug,
|
||||
[
|
||||
rehypePrettyCode,
|
||||
{
|
||||
theme: {
|
||||
dark: shikiThemeDarkPlus,
|
||||
light: shikiThemeLightPlus,
|
||||
},
|
||||
getHighlighter: async (options?: Partial<Options>) =>
|
||||
getHighlighter({
|
||||
...options,
|
||||
langs: [
|
||||
// @ts-expect-error: Working as intended
|
||||
{ id: 'javascript', aliases: ['js'], scopeName: 'source.js', grammar: shikiLangJavascript },
|
||||
// @ts-expect-error: Working as intended
|
||||
{ id: 'typescript', aliases: ['ts'], scopeName: 'source.ts', grammar: shikiLangTypescript },
|
||||
],
|
||||
}),
|
||||
},
|
||||
],
|
||||
],
|
||||
format: 'md',
|
||||
},
|
||||
});
|
||||
@@ -186,7 +217,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
props: {
|
||||
error: error_,
|
||||
},
|
||||
revalidate: 3_600,
|
||||
revalidate: 1,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -206,46 +237,30 @@ const member = (props?: ApiItemJSON | undefined) => {
|
||||
case 'Enum':
|
||||
return <Enum data={props as ApiEnumJSON} />;
|
||||
default:
|
||||
return <Box>Cannot render that item type</Box>;
|
||||
return <div>Cannot render that item type</div>;
|
||||
}
|
||||
};
|
||||
|
||||
export default function SlugPage(props: Partial<SidebarLayoutProps & { error?: string }>) {
|
||||
const router = useRouter();
|
||||
const [scroll, scrollTo] = useWindowScroll();
|
||||
const matches = useMediaQuery('(max-width: 1200px)');
|
||||
|
||||
useEffect(() => {
|
||||
if (props.data?.searchIndex) {
|
||||
const searchIndex = props.data?.searchIndex.map((idx, index) => ({ id: index, ...idx })) ?? [];
|
||||
miniSearch.addAll(searchIndex);
|
||||
|
||||
registerSpotlightActions(
|
||||
searchIndex.map((idx) => ({
|
||||
title: idx.name,
|
||||
description: idx.summary ?? '',
|
||||
onTrigger: () => void router.push(idx.path),
|
||||
})),
|
||||
const name = useMemo(
|
||||
() => `discord.js${props.data?.member?.name ? ` | ${props.data.member.name}` : ''}`,
|
||||
[props.data?.member?.name],
|
||||
);
|
||||
const ogTitle = useMemo(
|
||||
() => `${props.packageName ?? 'discord.js'}${props.data?.member?.name ? ` | ${props.data.member.name}` : ''}`,
|
||||
[props.packageName, props.data?.member?.name],
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const name = `discord.js${props.data?.member?.name ? ` | ${props.data.member.name}` : ''}`;
|
||||
|
||||
if (router.isFallback) {
|
||||
return (
|
||||
<SidebarLayout>
|
||||
<LoadingOverlay visible overlayBlur={2} />
|
||||
</SidebarLayout>
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Just in case
|
||||
// return <iframe src="https://discord.js.org" style={{ border: 0, height: '100%', width: '100%' }}></iframe>;
|
||||
|
||||
return props.error ? (
|
||||
<Box sx={{ display: 'flex', maxWidth: '100%', height: '100%' }}>{props.error}</Box>
|
||||
<div className="flex h-full max-h-full w-full max-w-full flex-row">{props.error}</div>
|
||||
) : (
|
||||
<MemberProvider member={props.data?.member}>
|
||||
<SidebarLayout {...props}>
|
||||
@@ -253,50 +268,20 @@ export default function SlugPage(props: Partial<SidebarLayoutProps & { error?: s
|
||||
<>
|
||||
<Head>
|
||||
<title key="title">{name}</title>
|
||||
<meta key="og_title" property="og:title" content={ogTitle} />
|
||||
</Head>
|
||||
{member(props.data.member)}
|
||||
</>
|
||||
) : props.data?.source ? (
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
a: {
|
||||
backgroundColor: 'transparent',
|
||||
color: theme.colors.blurple![0],
|
||||
textDecoration: 'none',
|
||||
},
|
||||
img: {
|
||||
borderStyle: 'none',
|
||||
maxWidth: '100%',
|
||||
boxSizing: 'content-box',
|
||||
},
|
||||
})}
|
||||
px="xl"
|
||||
>
|
||||
<div className="prose max-w-none">
|
||||
<MDXRemote {...props.data.source} />
|
||||
</Box>
|
||||
</div>
|
||||
) : null}
|
||||
<Affix
|
||||
position={{
|
||||
bottom: 20,
|
||||
right:
|
||||
matches || (props.data?.member?.kind !== 'Class' && props.data?.member?.kind !== 'Interface') ? 20 : 268,
|
||||
}}
|
||||
>
|
||||
<Transition transition="slide-up" mounted={scroll.y > 200}>
|
||||
{(transitionStyles) => (
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="blurple"
|
||||
size={30}
|
||||
style={transitionStyles}
|
||||
onClick={() => scrollTo({ y: 0 })}
|
||||
>
|
||||
<VscChevronUp size={20} />
|
||||
</ActionIcon>
|
||||
)}
|
||||
</Transition>
|
||||
</Affix>
|
||||
</SidebarLayout>
|
||||
</MemberProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export const config = {
|
||||
unstable_includeFiles: ['../{builders,collection,proxy,rest,voice,ws}/README.md'],
|
||||
};
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
import {
|
||||
Container,
|
||||
UnstyledButton,
|
||||
createStyles,
|
||||
Group,
|
||||
ThemeIcon,
|
||||
Text,
|
||||
Stack,
|
||||
Box,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
Affix,
|
||||
} from '@mantine/core';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { GetStaticPaths, GetStaticProps } from 'next/types';
|
||||
import { VscArrowLeft, VscArrowRight, VscVersions } from 'react-icons/vsc';
|
||||
import { PACKAGES } from '~/util/packages';
|
||||
import { PACKAGES } from '~/util/constants';
|
||||
|
||||
interface VersionProps {
|
||||
data: {
|
||||
@@ -34,20 +20,21 @@ export const getStaticPaths: GetStaticPaths = () => {
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
const packageName = params!.package as string | undefined;
|
||||
const packageName = params!.package as string;
|
||||
|
||||
if (!PACKAGES.includes(packageName)) {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName ?? 'builders'}`);
|
||||
const res = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName}`);
|
||||
const data: string[] = await res.json();
|
||||
|
||||
if (!data.length) {
|
||||
console.error('No tags');
|
||||
|
||||
return {
|
||||
props: {
|
||||
error: 'No tags',
|
||||
},
|
||||
revalidate: 3_600,
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,7 +42,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
props: {
|
||||
packageName,
|
||||
data: {
|
||||
versions: data,
|
||||
versions: data.reverse(),
|
||||
},
|
||||
},
|
||||
revalidate: 3_600,
|
||||
@@ -68,66 +55,39 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
props: {
|
||||
error: error_,
|
||||
},
|
||||
revalidate: 3_600,
|
||||
revalidate: 1,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
outer: {
|
||||
display: 'flex',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
},
|
||||
|
||||
control: {
|
||||
padding: theme.spacing.xs,
|
||||
color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black,
|
||||
borderRadius: theme.radius.xs,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0],
|
||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function VersionsRoute(props: Partial<VersionProps> & { error?: string }) {
|
||||
const router = useRouter();
|
||||
const { classes } = useStyles();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
|
||||
return props.error ? (
|
||||
<Box sx={{ display: 'flex', maxWidth: '100%', height: '100%' }}>{props.error}</Box>
|
||||
<div className="min-w-xs sm:w-md mx-auto flex h-full flex-row place-content-center place-items-center gap-8 py-0 px-4 lg:py-0 lg:px-6">
|
||||
{props.error}
|
||||
</div>
|
||||
) : (
|
||||
<Container className={classes.outer} size="xs">
|
||||
<Stack sx={{ flexGrow: 1 }}>
|
||||
<Title order={2} ml="xs">
|
||||
Select a version:
|
||||
</Title>
|
||||
<div className="min-w-xs sm:w-md mx-auto flex h-full flex-row place-content-center place-items-center gap-8 py-0 px-4 lg:py-0 lg:px-6">
|
||||
<div className="flex grow flex-col place-content-center gap-4">
|
||||
<h1 className="text-2xl font-semibold">Select a version:</h1>
|
||||
{props.data?.versions.map((version) => (
|
||||
<Link key={version} href={`/docs/packages/${props.packageName!}/${version}`} passHref prefetch={false}>
|
||||
<UnstyledButton className={classes.control} component="a">
|
||||
<Group position="apart">
|
||||
<Group>
|
||||
<ThemeIcon variant={colorScheme === 'dark' ? 'filled' : 'outline'} radius="sm" size={30}>
|
||||
<VscVersions size={20} />
|
||||
</ThemeIcon>
|
||||
<Text weight={600} size="md">
|
||||
{version}
|
||||
</Text>
|
||||
</Group>
|
||||
<Link key={version} href={`/docs/packages/${props.packageName}/${version}`} prefetch={false}>
|
||||
<a className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 flex flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-col place-content-center rounded border border-neutral-300 bg-transparent p-4 text-base font-semibold leading-none text-black hover:bg-neutral-100 active:translate-y-px active:bg-neutral-200 dark:text-white">
|
||||
<div className="flex flex-row place-content-between place-items-center gap-4">
|
||||
<div className="flex flex-row place-content-between place-items-center gap-4">
|
||||
<VscVersions size={25} />
|
||||
<h2 className="font-semibold">{version}</h2>
|
||||
</div>
|
||||
<VscArrowRight size={20} />
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
)) ?? null}
|
||||
</Stack>
|
||||
<Affix position={{ top: 20, left: 20 }}>
|
||||
<UnstyledButton onClick={() => void router.push('/docs/packages')}>
|
||||
<VscArrowLeft size={25} />
|
||||
</UnstyledButton>
|
||||
</Affix>
|
||||
</Container>
|
||||
<Link href="/docs/packages" prefetch={false}>
|
||||
<a className="bg-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none place-items-center gap-2 place-self-center rounded border-0 px-4 text-base font-semibold leading-none text-white no-underline active:translate-y-px">
|
||||
<VscArrowLeft size={20} /> Go back
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,95 +1,98 @@
|
||||
import {
|
||||
Container,
|
||||
UnstyledButton,
|
||||
createStyles,
|
||||
Group,
|
||||
ThemeIcon,
|
||||
Text,
|
||||
Stack,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
Button,
|
||||
} from '@mantine/core';
|
||||
import { Button } from 'ariakit/button';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { MouseEvent } from 'react';
|
||||
import { VscArrowRight, VscPackage } from 'react-icons/vsc';
|
||||
import { PACKAGES } from '~/util/packages';
|
||||
import type { GetStaticProps } from 'next/types';
|
||||
import { useCallback, type MouseEvent } from 'react';
|
||||
import { VscArrowLeft, VscArrowRight, VscPackage } from 'react-icons/vsc';
|
||||
import { PACKAGES } from '~/util/constants';
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
outer: {
|
||||
display: 'flex',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
interface PackageProps {
|
||||
data: {
|
||||
versions: { packageName: string; version: string }[];
|
||||
};
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps = async () => {
|
||||
try {
|
||||
const versions = await Promise.all(
|
||||
PACKAGES.map(async (pkg) => {
|
||||
const response = await fetch(`https://docs.discordjs.dev/api/info?package=${pkg}`);
|
||||
const versions = await response.json();
|
||||
const latestVersion = versions.at(-2);
|
||||
return { packageName: pkg, version: latestVersion };
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
props: {
|
||||
versions,
|
||||
},
|
||||
revalidate: 3_600,
|
||||
};
|
||||
} catch (error_) {
|
||||
const error = error_ as Error;
|
||||
console.error(error);
|
||||
|
||||
control: {
|
||||
padding: theme.spacing.xs,
|
||||
color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black,
|
||||
borderRadius: theme.radius.xs,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0],
|
||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||
return {
|
||||
props: {
|
||||
error: error_,
|
||||
},
|
||||
},
|
||||
}));
|
||||
revalidate: 1,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default function PackagesRoute() {
|
||||
const { classes } = useStyles();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
export default function PackagesRoute(props: Partial<PackageProps> & { error?: string }) {
|
||||
const router = useRouter();
|
||||
const findLatestVersion = useCallback(
|
||||
(pkg: string) => props.data?.versions.find((version) => version.packageName === pkg),
|
||||
[props.data?.versions],
|
||||
);
|
||||
|
||||
const handleClick = async (ev: MouseEvent<HTMLDivElement>, packageName: string) => {
|
||||
ev.stopPropagation();
|
||||
|
||||
const res = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName ?? 'builders'}`);
|
||||
const data: string[] = await res.json();
|
||||
|
||||
const latestVersion = data.at(-2);
|
||||
void router.push(`/docs/packages/${packageName}/${latestVersion}`);
|
||||
void router.push(`/docs/packages/${packageName}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Container className={classes.outer} size="xs">
|
||||
<Stack sx={{ flexGrow: 1 }}>
|
||||
<Title order={2} ml="xs">
|
||||
Select a package:
|
||||
</Title>
|
||||
return props.error ? (
|
||||
<div className="min-w-xs sm:w-md mx-auto flex h-full flex-row place-content-center place-items-center gap-8 py-0 px-4 lg:py-0 lg:px-6">
|
||||
{props.error}
|
||||
</div>
|
||||
) : (
|
||||
<div className="min-w-xs sm:w-md mx-auto flex h-full flex-row place-content-center place-items-center gap-8 py-0 px-4 lg:py-0 lg:px-6">
|
||||
<div className="flex grow flex-col place-content-center gap-4">
|
||||
<h1 className="text-2xl font-semibold">Select a package:</h1>
|
||||
{PACKAGES.map((pkg) => (
|
||||
<UnstyledButton
|
||||
component="div"
|
||||
key={pkg}
|
||||
role="link"
|
||||
className={classes.control}
|
||||
onClick={(ev: MouseEvent<HTMLDivElement>) => void handleClick(ev, pkg)}
|
||||
>
|
||||
<Group position="apart">
|
||||
<Group sx={{ flexGrow: 1 }} position="apart">
|
||||
<Group>
|
||||
<ThemeIcon variant={colorScheme === 'dark' ? 'filled' : 'outline'} radius="sm" size={30}>
|
||||
<VscPackage size={20} />
|
||||
</ThemeIcon>
|
||||
<Text weight={600} size="md">
|
||||
{pkg}
|
||||
</Text>
|
||||
</Group>
|
||||
<Link href={`/docs/packages/${pkg}`} passHref prefetch={false}>
|
||||
<Link key={pkg} href={`/docs/packages/${pkg}/${findLatestVersion(pkg)?.version ?? 'main'}`} prefetch={false}>
|
||||
<a className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 flex h-11 transform-gpu cursor-pointer select-none appearance-none place-content-between rounded border border-neutral-300 bg-transparent p-4 text-base font-semibold leading-none text-black hover:bg-neutral-100 active:translate-y-px active:bg-neutral-200 dark:text-white">
|
||||
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
||||
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
||||
<div className="flex flex-row place-content-between place-items-center gap-4">
|
||||
<VscPackage size={25} />
|
||||
<h2 className="font-semibold">{pkg}</h2>
|
||||
</div>
|
||||
<Link href={`/docs/packages/${pkg}`} prefetch={false}>
|
||||
<Button
|
||||
component="a"
|
||||
size="xs"
|
||||
compact
|
||||
onClick={(ev: MouseEvent<HTMLAnchorElement>) => ev.stopPropagation()}
|
||||
as="div"
|
||||
role="link"
|
||||
className="bg-blurple flex h-6 transform-gpu cursor-pointer select-none appearance-none place-content-center place-items-center rounded border-0 px-2 text-xs font-semibold leading-none text-white active:translate-y-px"
|
||||
onClick={async (ev: MouseEvent<HTMLDivElement>) => handleClick(ev, pkg)}
|
||||
>
|
||||
Select version
|
||||
</Button>
|
||||
</Link>
|
||||
</Group>
|
||||
</div>
|
||||
<VscArrowRight size={20} />
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
))}
|
||||
</Stack>
|
||||
</Container>
|
||||
<Link href="/" prefetch={false}>
|
||||
<a className="bg-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none place-items-center gap-2 place-self-center rounded border-0 px-4 text-base font-semibold leading-none text-white no-underline active:translate-y-px">
|
||||
<VscArrowLeft size={20} /> Go back
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,133 +1,52 @@
|
||||
import { createStyles, Container, Title, Button, Group, Text, Center, Box, useMantineColorScheme } from '@mantine/core';
|
||||
import Image from 'next/future/image';
|
||||
import Link from 'next/link';
|
||||
import { FiExternalLink } from 'react-icons/fi';
|
||||
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus, ghcolors } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
outer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
justifyContent: 'center',
|
||||
gap: 50,
|
||||
padding: 32,
|
||||
|
||||
[theme.fn.smallerThan('md')]: {
|
||||
height: 'unset',
|
||||
},
|
||||
},
|
||||
|
||||
inner: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
|
||||
[theme.fn.smallerThan('md')]: {
|
||||
flexDirection: 'column',
|
||||
gap: 50,
|
||||
},
|
||||
},
|
||||
|
||||
content: {
|
||||
maxWidth: 480,
|
||||
marginRight: theme.spacing.xl,
|
||||
|
||||
[theme.fn.smallerThan('md')]: {
|
||||
marginRight: 0,
|
||||
},
|
||||
},
|
||||
|
||||
title: {
|
||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||
fontSize: 44,
|
||||
lineHeight: 1.2,
|
||||
fontWeight: 900,
|
||||
|
||||
[theme.fn.smallerThan('xs')]: {
|
||||
fontSize: 28,
|
||||
},
|
||||
},
|
||||
|
||||
highlight: {
|
||||
position: 'relative',
|
||||
backgroundColor: theme.fn.variant({ variant: 'light', color: theme.primaryColor }).background!,
|
||||
borderRadius: theme.radius.sm,
|
||||
padding: '4px 12px',
|
||||
},
|
||||
}));
|
||||
import vercelLogo from '../assets/powered-by-vercel.svg';
|
||||
import { SyntaxHighlighter } from '~/components/SyntaxHighlighter';
|
||||
import { CODE_EXAMPLE } from '~/util/constants';
|
||||
|
||||
export default function IndexRoute() {
|
||||
const { classes } = useStyles();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
|
||||
return (
|
||||
<Container className={classes.outer} size="lg">
|
||||
<Box className={classes.inner}>
|
||||
<Box className={classes.content}>
|
||||
<Title className={classes.title}>
|
||||
The <span className={classes.highlight}>most popular</span> way to build Discord <br /> bots.
|
||||
</Title>
|
||||
<Text color="dimmed" mt="md">
|
||||
<div className="mx-auto flex max-w-6xl flex-col place-items-center gap-12 py-16 px-8 lg:h-full lg:place-content-center lg:py-0 lg:px-6">
|
||||
<div className="flex flex-col place-items-center gap-10 lg:flex-row lg:gap-6">
|
||||
<div className="flex max-w-lg flex-col gap-3 lg:mr-8">
|
||||
<h1 className="text-3xl font-black leading-tight sm:text-5xl sm:leading-tight">
|
||||
The <span className="bg-blurple relative rounded py-1 px-3 text-white">most popular</span> way to build
|
||||
Discord <br /> bots.
|
||||
</h1>
|
||||
<p className="my-6 leading-normal text-neutral-700 dark:text-neutral-300">
|
||||
discord.js is a powerful node.js module that allows you to interact with the Discord API very easily. It
|
||||
takes a much more object-oriented approach than most other JS Discord libraries, making your bot's code
|
||||
significantly tidier and easier to comprehend.
|
||||
</Text>
|
||||
|
||||
<Group mt={30}>
|
||||
<Link href="/docs" passHref prefetch={false}>
|
||||
<Button component="a" radius="sm" size="md">
|
||||
</p>
|
||||
<div className="flex flex-row gap-4">
|
||||
<Link href="/docs" prefetch={false}>
|
||||
<a className="bg-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 px-6 text-base font-semibold leading-none text-white no-underline active:translate-y-px">
|
||||
Docs
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="https://discordjs.guide" passHref prefetch={false}>
|
||||
<Button component="a" variant="default" radius="sm" size="md" rightIcon={<FiExternalLink />}>
|
||||
Guide
|
||||
</Button>
|
||||
</Link>
|
||||
</Group>
|
||||
</Box>
|
||||
<Box pb="xs">
|
||||
<SyntaxHighlighter
|
||||
wrapLongLines
|
||||
language="typescript"
|
||||
style={colorScheme === 'dark' ? vscDarkPlus : ghcolors}
|
||||
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
|
||||
>
|
||||
{`import { Client, GatewayIntentBits } from 'discord.js';
|
||||
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
client.on('ready', () => {
|
||||
console.log(\`Logged in as \${client.user.tag}!\`);
|
||||
});
|
||||
|
||||
client.on('interactionCreate', async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
if (interaction.commandName === 'ping') {
|
||||
await interaction.reply('Pong!');
|
||||
}
|
||||
});
|
||||
|
||||
await client.login('token');`}
|
||||
</SyntaxHighlighter>
|
||||
</Box>
|
||||
</Box>
|
||||
<Center>
|
||||
<Link href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss" prefetch={false}>
|
||||
<a title="Vercel">
|
||||
<Image
|
||||
src="/powered-by-vercel.svg"
|
||||
alt="Vercel"
|
||||
width={0}
|
||||
height={0}
|
||||
style={{ height: '100%', width: '100%', maxWidth: 250 }}
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
</Center>
|
||||
</Container>
|
||||
<a
|
||||
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 border-light-900 hover:bg-light-200 active:bg-light-300 flex h-11 transform-gpu cursor-pointer select-none appearance-none place-items-center gap-2 rounded border bg-transparent px-4 text-base font-semibold leading-none text-black no-underline active:translate-y-px dark:text-white"
|
||||
href="https://discordjs.guide"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Guide <FiExternalLink />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<SyntaxHighlighter code={CODE_EXAMPLE} />
|
||||
</div>
|
||||
<div className="flex place-content-center">
|
||||
<a
|
||||
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="Vercel"
|
||||
>
|
||||
<Image src={vercelLogo} alt="Vercel" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,29 +1,43 @@
|
||||
@import url('https://rsms.me/inter/inter.css');
|
||||
|
||||
@supports (font-variation-settings: normal) {
|
||||
html {
|
||||
font-family: 'Inter var', sans-serif;
|
||||
:root {
|
||||
font-family: 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue;
|
||||
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
|
||||
}
|
||||
|
||||
@supports (font-variation-settings: normal) {
|
||||
:root {
|
||||
font-family: 'Inter var', Arial, Noto Sans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', Segoe UI Symbol,
|
||||
'Noto Color Emoji';
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
font-family: 'Inter var', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
||||
Helvetica Neue, Arial, Noto Sans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', Segoe UI Symbol,
|
||||
'Noto Color Emoji';
|
||||
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#__next {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.light [data-theme='dark'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dark [data-theme='light'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
pre[data-theme='light'] {
|
||||
background: #ffffff;
|
||||
border: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
pre[data-theme='dark'] {
|
||||
background: #1e1e1e;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size: 13px !important;
|
||||
white-space: pre;
|
||||
@@ -35,10 +49,9 @@ pre {
|
||||
padding: 1em;
|
||||
margin: 0.5em 0;
|
||||
overflow: auto;
|
||||
background: #1e1e1e;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'JetBrains Mono' !important;
|
||||
font-family: 'JetBrains Mono', monospace !important;
|
||||
}
|
||||
|
||||
22
packages/website/src/util/constants.ts
Normal file
22
packages/website/src/util/constants.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export const PACKAGES = ['builders', 'collection', 'proxy', 'rest', 'voice', 'ws'];
|
||||
|
||||
export const DESCRIPTION =
|
||||
"discord.js is a powerful node.js module that allows you to interact with the Discord API very easily. It takes a much more object-oriented approach than most other JS Discord libraries, making your bot's code significantly tidier and easier to comprehend.";
|
||||
|
||||
export const CODE_EXAMPLE = `import { Client, GatewayIntentBits } from 'discord.js';
|
||||
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
client.on('ready', () => {
|
||||
console.log(\`Logged in as \${client.user.tag}!\`);
|
||||
});
|
||||
|
||||
client.on('interactionCreate', async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
if (interaction.commandName === 'ping') {
|
||||
await interaction.reply('Pong!');
|
||||
}
|
||||
});
|
||||
|
||||
await client.login('token');`;
|
||||
@@ -1 +0,0 @@
|
||||
export const PACKAGES = ['builders', 'collection', 'proxy', 'rest', 'voice', 'ws'];
|
||||
@@ -1,6 +0,0 @@
|
||||
import MiniSearch from 'minisearch';
|
||||
|
||||
export const miniSearch = new MiniSearch({
|
||||
fields: ['name', 'summary'],
|
||||
storeFields: ['name', 'summary', 'path'],
|
||||
});
|
||||
@@ -1,10 +1,20 @@
|
||||
import presetWebFonts from '@unocss/preset-web-fonts';
|
||||
import { defineConfig, presetUno } from 'unocss';
|
||||
import { defineConfig, presetTypography, presetUno, presetWebFonts } from 'unocss';
|
||||
|
||||
export default defineConfig({
|
||||
theme: {
|
||||
colors: {
|
||||
blurple: '#5865F2',
|
||||
blurple: {
|
||||
50: '#e0e3ff',
|
||||
100: '#cdd2ff',
|
||||
200: '#9ea7ff',
|
||||
300: '#7782fa',
|
||||
DEFAULT: '#5865F2',
|
||||
500: '#3d48c3',
|
||||
600: '#293294',
|
||||
700: '#1a2165',
|
||||
800: '#0e1137',
|
||||
900: '#020208',
|
||||
},
|
||||
},
|
||||
},
|
||||
presets: [
|
||||
@@ -12,7 +22,43 @@ export default defineConfig({
|
||||
presetWebFonts({
|
||||
provider: 'google',
|
||||
fonts: {
|
||||
mono: ['JetBrains Mono', 'JetBrains Mono:400,600'],
|
||||
mono: ['JetBrains Mono', 'JetBrains Mono:400,600,700'],
|
||||
},
|
||||
}),
|
||||
presetTypography({
|
||||
cssExtend: {
|
||||
pre: {
|
||||
padding: '1em',
|
||||
'line-height': '1.5',
|
||||
'border-radius': '4px',
|
||||
},
|
||||
code: {
|
||||
'font-size': '1em',
|
||||
'font-weight': 'unset',
|
||||
},
|
||||
':where(:not(pre) > code)::before': {
|
||||
content: '""',
|
||||
},
|
||||
':where(:not(pre) > code)::after': {
|
||||
content: '""',
|
||||
},
|
||||
a: {
|
||||
color: '#5865F2',
|
||||
'text-decoration': 'none',
|
||||
},
|
||||
'a > img': {
|
||||
display: 'inline-block',
|
||||
},
|
||||
h2: {
|
||||
'margin-top': '1.25em',
|
||||
},
|
||||
h3: {
|
||||
'margin-top': '0.75em',
|
||||
},
|
||||
// eslint-disable-next-line id-length
|
||||
p: {
|
||||
margin: '.5em 0',
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -65,8 +65,8 @@
|
||||
"@discordjs/docgen": "workspace:^",
|
||||
"@favware/cliff-jumper": "^1.8.7",
|
||||
"@microsoft/api-extractor": "^7.30.0",
|
||||
"@types/node": "^16.11.56",
|
||||
"@vitest/coverage-c8": "^0.23.0",
|
||||
"@types/node": "^16.11.57",
|
||||
"@vitest/coverage-c8": "^0.23.1",
|
||||
"downlevel-dts": "^0.10.1",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-neon": "^0.1.33",
|
||||
@@ -75,7 +75,7 @@
|
||||
"tsup": "^6.2.3",
|
||||
"typescript": "^4.8.2",
|
||||
"undici": "^5.10.0",
|
||||
"vitest": "^0.23.0",
|
||||
"vitest": "^0.23.1",
|
||||
"zlib-sync": "^0.1.7"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
Reference in New Issue
Block a user