mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-17 12:03:31 +01:00
refactor: docs design (#8487)
This commit is contained in:
@@ -1,8 +1,57 @@
|
||||
import { ColorScheme, ColorSchemeProvider, MantineProvider } from '@mantine/core';
|
||||
import { useColorScheme } from '@mantine/hooks';
|
||||
import type { AppProps } from 'next/app';
|
||||
import '@unocss/reset/normalize.css';
|
||||
import Head from 'next/head';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { RouterTransition } from '~/components/RouterTransition';
|
||||
import '../styles/unocss.css';
|
||||
import '../styles/main.css';
|
||||
|
||||
export default function MyApp({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />;
|
||||
const preferredColorScheme = useColorScheme('dark', { getInitialValueInEffect: true });
|
||||
const [colorScheme, setColorScheme] = useState<ColorScheme>(preferredColorScheme);
|
||||
const toggleColorScheme = (value?: ColorScheme) =>
|
||||
setColorScheme(value ?? (colorScheme === 'dark' ? 'light' : 'dark'));
|
||||
|
||||
useEffect(() => {
|
||||
setColorScheme(preferredColorScheme);
|
||||
}, [preferredColorScheme]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
|
||||
</Head>
|
||||
|
||||
<ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
|
||||
<MantineProvider
|
||||
theme={{
|
||||
fontFamily: 'Inter',
|
||||
colorScheme,
|
||||
colors: {
|
||||
blurple: [
|
||||
'#5865F2',
|
||||
'#5865F2',
|
||||
'#5865F2',
|
||||
'#5865F2',
|
||||
'#5865F2',
|
||||
'#5865F2',
|
||||
'#5865F2',
|
||||
'#5865F2',
|
||||
'#5865F2',
|
||||
'#5865F2',
|
||||
],
|
||||
},
|
||||
primaryColor: 'blurple',
|
||||
}}
|
||||
withCSSVariables
|
||||
withNormalizeCSS
|
||||
withGlobalStyles
|
||||
>
|
||||
<RouterTransition />
|
||||
<Component {...pageProps} />
|
||||
</MantineProvider>
|
||||
</ColorSchemeProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
import { Html, Head, Main, NextScript } from 'next/document';
|
||||
import { createGetInitialProps } from '@mantine/next';
|
||||
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head />
|
||||
<body>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `(() => {
|
||||
const prefersDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const persistedColorPreference = localStorage.getItem('theme') || 'auto';
|
||||
if (persistedColorPreference === 'dark' || (prefersDarkMode && persistedColorPreference !== 'light')) {
|
||||
document.documentElement.classList.toggle('dark', true);
|
||||
}
|
||||
})();`,
|
||||
}}
|
||||
/>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
const getInitialProps = createGetInitialProps();
|
||||
export default class _Document extends Document {
|
||||
public static override getInitialProps = getInitialProps;
|
||||
|
||||
public override render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-throw-literal */
|
||||
import { Box } from '@mantine/core';
|
||||
import { ApiFunction } from '@microsoft/api-extractor-model';
|
||||
import type { GetStaticPaths, GetStaticProps } from 'next/types';
|
||||
import type { DocClass } from '~/DocModel/DocClass';
|
||||
@@ -7,8 +8,7 @@ import type { DocFunction } from '~/DocModel/DocFunction';
|
||||
import type { DocInterface } from '~/DocModel/DocInterface';
|
||||
import type { DocTypeAlias } from '~/DocModel/DocTypeAlias';
|
||||
import type { DocVariable } from '~/DocModel/DocVariable';
|
||||
import type { ItemListProps } from '~/components/ItemSidebar';
|
||||
import { SidebarLayout } from '~/components/SidebarLayout';
|
||||
import { SidebarLayout, type SidebarLayoutProps } from '~/components/SidebarLayout';
|
||||
import { Class } from '~/components/model/Class';
|
||||
import { Enum } from '~/components/model/Enum';
|
||||
import { Function } from '~/components/model/Function';
|
||||
@@ -26,10 +26,6 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const pkgs = (
|
||||
await Promise.all(
|
||||
packages.map(async (packageName) => {
|
||||
if (packageName === 'rest') {
|
||||
return { params: { slug: ['main', 'packages', packageName] } };
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`https://docs.discordjs.dev/docs/${packageName}/main.api.json`);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
@@ -40,16 +36,21 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
||||
|
||||
return [
|
||||
{ params: { slug: ['main', 'packages', packageName] } },
|
||||
...getMembers(pkg!).map((member) => {
|
||||
if (member.kind === 'Function' && member.overloadIndex) {
|
||||
return {
|
||||
params: {
|
||||
slug: ['main', 'packages', packageName, `${member.name}:${member.overloadIndex}`],
|
||||
},
|
||||
};
|
||||
}
|
||||
return { params: { slug: ['main', 'packages', packageName, member.name] } };
|
||||
}),
|
||||
...getMembers(pkg!)
|
||||
// Filtering out enum `RESTEvents` because of interface with similar name `RestEvents`
|
||||
// causing next.js export to error
|
||||
.filter((member) => member.name !== 'RESTEvents')
|
||||
.map((member) => {
|
||||
if (member.kind === 'Function' && member.overloadIndex) {
|
||||
return {
|
||||
params: {
|
||||
slug: ['main', 'packages', packageName, `${member.name}:${member.overloadIndex}`],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return { params: { slug: ['main', 'packages', packageName, member.name] } };
|
||||
}),
|
||||
];
|
||||
} catch {
|
||||
return { params: { slug: ['', '', '', ''] } };
|
||||
@@ -77,8 +78,8 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
const model = createApiModel(data);
|
||||
const pkg = findPackage(model, packageName);
|
||||
|
||||
let { containerKey, name } = findMember(model, packageName, memberName)!;
|
||||
if (overloadIndex) {
|
||||
let { containerKey, name } = findMember(model, packageName, memberName) ?? {};
|
||||
if (name && overloadIndex) {
|
||||
containerKey = ApiFunction.getContainerKey(name, parseInt(overloadIndex, 10));
|
||||
}
|
||||
|
||||
@@ -87,7 +88,8 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
packageName,
|
||||
data: {
|
||||
members: pkg ? getMembers(pkg) : [],
|
||||
member: memberName ? findMemberByKey(model, packageName, containerKey)?.toJSON() ?? null : null,
|
||||
member:
|
||||
memberName && containerKey ? findMemberByKey(model, packageName, containerKey)?.toJSON() ?? null : null,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -116,13 +118,11 @@ const member = (props: any) => {
|
||||
case 'Enum':
|
||||
return <Enum data={props as ReturnType<DocEnum['toJSON']>} />;
|
||||
default:
|
||||
return <div>Cannot render that item type</div>;
|
||||
return <Box>Cannot render that item type</Box>;
|
||||
}
|
||||
};
|
||||
|
||||
export default function Slug(
|
||||
props: Partial<ItemListProps & { error?: string; data: { member: ReturnType<typeof findMember> } }>,
|
||||
) {
|
||||
export default function Slug(props: Partial<SidebarLayoutProps & { error?: string }>) {
|
||||
return props.error ? (
|
||||
<div className="flex max-w-full h-full bg-white dark:bg-dark">{props.error}</div>
|
||||
) : (
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export default function DocsLanding() {
|
||||
return <div>Documentation</div>;
|
||||
}
|
||||
@@ -1,73 +1,117 @@
|
||||
import { createStyles, Container, Title, Button, Group, Text, Center } from '@mantine/core';
|
||||
import Image from 'next/future/image';
|
||||
import Link from 'next/link';
|
||||
import { forwardRef, MouseEventHandler, Ref } from 'react';
|
||||
import codeSample from '../assets/code-sample.png';
|
||||
import logo from '../assets/djs_logo_rainbow_400x400.png';
|
||||
import vercelLogo from '../assets/powered-by-vercel.svg';
|
||||
import text from '../text.json';
|
||||
|
||||
interface ButtonProps {
|
||||
label: string;
|
||||
href?: string;
|
||||
ref?: Ref<HTMLAnchorElement>;
|
||||
onClick?: MouseEventHandler<HTMLAnchorElement>;
|
||||
}
|
||||
const useStyles = createStyles((theme) => ({
|
||||
inner: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingTop: theme.spacing.xl * 4,
|
||||
paddingBottom: theme.spacing.xl * 4,
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
const LinkButton = forwardRef<HTMLAnchorElement, ButtonProps>(({ label, onClick, href }: ButtonProps, ref) => (
|
||||
<a
|
||||
href={href}
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
className="no-underline max-h-[70px] bg-blurple px-3 py-4 rounded-lg font-semibold text-white"
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
));
|
||||
[theme.fn.smallerThan('md')]: {
|
||||
flexDirection: 'column',
|
||||
gap: 50,
|
||||
},
|
||||
},
|
||||
|
||||
content: {
|
||||
maxWidth: 480,
|
||||
marginRight: theme.spacing.xl * 3,
|
||||
|
||||
[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',
|
||||
},
|
||||
|
||||
control: {
|
||||
[theme.fn.smallerThan('xs')]: {
|
||||
flex: 1,
|
||||
},
|
||||
},
|
||||
|
||||
image: {
|
||||
flex: 1,
|
||||
height: '100%',
|
||||
maxWidth: 550,
|
||||
|
||||
[theme.fn.smallerThan('xs')]: {
|
||||
maxWidth: 350,
|
||||
},
|
||||
},
|
||||
|
||||
vercel: {
|
||||
height: '100%',
|
||||
maxWidth: 250,
|
||||
paddingBottom: theme.spacing.xl * 4,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function IndexRoute() {
|
||||
const { classes } = useStyles();
|
||||
return (
|
||||
<main className="w-full max-w-full max-h-full h-full flex-col bg-white dark:bg-dark overflow-y-auto">
|
||||
<div className="flex h-[65px] sticky top-0 border-b border-gray justify-center px-10 bg-white dark:bg-dark">
|
||||
<div className="flex items-center w-full max-w-[1100px] justify-between">
|
||||
<div className="h-[50px] w-[50px] rounded-lg overflow-hidden">
|
||||
<Image className="h-[50px] w-[50px]" src={logo} />
|
||||
</div>
|
||||
<div className="flex flex-row space-x-8">
|
||||
<Link href="/docs" passHref>
|
||||
<a className="no-underline text-blurple font-semibold">Docs</a>
|
||||
</Link>
|
||||
<a className="text-blurple font-semibold">Guide</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="xl:flex xl:flex-col xl:justify-center w-full max-w-full box-border p-10">
|
||||
<div className="flex flex-col xl:flex-row grow max-w-[1100px] pb-10 space-y-10 xl:space-x-20 place-items-center place-self-center">
|
||||
<div className="flex flex-col max-w-[800px] lt-xl:items-center">
|
||||
<h1 className="font-bold text-6xl text-blurple my-2">{text.heroTitle}</h1>
|
||||
<p className="text-xl text-dark-100 dark:text-gray-300">{text.heroDescription}</p>
|
||||
<div className="flex flew-row space-x-4">
|
||||
<LinkButton label="Read the guide" />
|
||||
<div>
|
||||
<Container size="lg">
|
||||
<div className={classes.inner}>
|
||||
<div 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">
|
||||
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>
|
||||
<LinkButton label="Check out the docs" />
|
||||
<Button component="a" radius="xl" size="md" className={classes.control}>
|
||||
Docs
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:flex sm:grow sm:shrink h-full xl:items-center hidden">
|
||||
<Image src={codeSample} className="max-w-[600px] h-full rounded-xl shadow-md overflow-hidden" />
|
||||
<Link href="https://discordjs.guide" passHref>
|
||||
<Button component="a" variant="default" radius="xl" size="md" className={classes.control}>
|
||||
Guide
|
||||
</Button>
|
||||
</Link>
|
||||
</Group>
|
||||
</div>
|
||||
<Image src={codeSample} className={classes.image} />
|
||||
</div>
|
||||
<div className="flex place-content-center">
|
||||
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss" title="Vercel">
|
||||
<Image
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
src={vercelLogo}
|
||||
alt="Vercel"
|
||||
className="max-w-[250px] shadow-md overflow-hidden"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Center>
|
||||
<Link href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss" passHref>
|
||||
<a title="Vercel">
|
||||
<Image
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
src={vercelLogo}
|
||||
alt="Vercel"
|
||||
className={classes.vercel}
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
</Center>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user