mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-10 00:23:30 +01:00
337 lines
9.4 KiB
TypeScript
337 lines
9.4 KiB
TypeScript
import {
|
|
useMantineTheme,
|
|
AppShell,
|
|
Navbar,
|
|
MediaQuery,
|
|
Header,
|
|
Burger,
|
|
Anchor,
|
|
Breadcrumbs,
|
|
ScrollArea,
|
|
Group,
|
|
Text,
|
|
ThemeIcon,
|
|
Box,
|
|
UnstyledButton,
|
|
createStyles,
|
|
Menu,
|
|
ActionIcon,
|
|
useMantineColorScheme,
|
|
Center,
|
|
Stack,
|
|
Skeleton,
|
|
LoadingOverlay,
|
|
} from '@mantine/core';
|
|
import { NextLink } from '@mantine/next';
|
|
import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
|
|
import Image from 'next/future/image';
|
|
import Link from 'next/link';
|
|
import { useRouter } from 'next/router';
|
|
import { type PropsWithChildren, useState, useEffect } from 'react';
|
|
import { VscChevronDown, VscGithubInverted, VscPackage, VscVersions } from 'react-icons/vsc';
|
|
import { WiDaySunny, WiNightClear } from 'react-icons/wi';
|
|
import useSWR from 'swr';
|
|
import { SidebarItems } from './SidebarItems';
|
|
import type { ApiItemJSON } from '~/DocModel/ApiNodeJSONEncoder';
|
|
import type { findMember } from '~/util/model.server';
|
|
import { PACKAGES } from '~/util/packages';
|
|
import type { getMembers } from '~/util/parse.server';
|
|
|
|
const fetcher = (url: string) => fetch(url).then((res) => res.json());
|
|
|
|
export interface SidebarLayoutProps {
|
|
packageName: string;
|
|
branchName: string;
|
|
data: {
|
|
members: ReturnType<typeof getMembers>;
|
|
member: ReturnType<typeof findMember>;
|
|
source: MDXRemoteSerializeResult;
|
|
};
|
|
|
|
selectedMember?: ApiItemJSON | undefined;
|
|
}
|
|
|
|
export type Members = SidebarLayoutProps['data']['members'];
|
|
|
|
export interface GroupedMembers {
|
|
Classes: Members;
|
|
Functions: Members;
|
|
Enums: Members;
|
|
Interfaces: Members;
|
|
Types: Members;
|
|
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)',
|
|
},
|
|
}),
|
|
);
|
|
|
|
const packageMenuItems = PACKAGES.map((pkg) => (
|
|
<Menu.Item key={pkg} component={NextLink} href={`/docs/packages/${pkg}/main`}>
|
|
{pkg}
|
|
</Menu.Item>
|
|
));
|
|
|
|
export function SidebarLayout({
|
|
packageName,
|
|
branchName,
|
|
data,
|
|
children,
|
|
}: 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();
|
|
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
|
|
|
|
const [opened, setOpened] = useState(false);
|
|
const [openedLibPicker, setOpenedLibPicker] = useState(false);
|
|
const [openedVersionPicker, setOpenedVersionPicker] = useState(false);
|
|
|
|
useEffect(() => {
|
|
setOpened(false);
|
|
setOpenedLibPicker(false);
|
|
setOpenedVersionPicker(false);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
setAsPathWithoutQueryAndAnchor(router.asPath.split('?')[0]?.split('#')[0]?.split(':')[0] ?? '');
|
|
}, [router.asPath]);
|
|
|
|
const { classes } = useStyles({ openedLib: openedLibPicker, openedVersion: openedVersionPicker });
|
|
|
|
const versionMenuItems =
|
|
versions?.map((item) => (
|
|
<Menu.Item key={item} component={NextLink} href={`/docs/packages/${packageName ?? 'builders'}/${item}`}>
|
|
{item}
|
|
</Menu.Item>
|
|
)) ?? [];
|
|
|
|
const breadcrumbs = asPathWithoutQueryAndAnchor.split('/').map((path, idx, original) => (
|
|
<Link key={idx} href={original.slice(0, idx + 1).join('/')} passHref>
|
|
<Anchor component="a">{path}</Anchor>
|
|
</Link>
|
|
));
|
|
|
|
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>
|
|
</>
|
|
}
|
|
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((o) => !o))}
|
|
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>
|
|
<ActionIcon
|
|
component="a"
|
|
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"
|
|
>
|
|
{colorScheme === 'dark' ? <WiDaySunny size={30} /> : <WiNightClear size={30} />}
|
|
</ActionIcon>
|
|
</Group>
|
|
</Box>
|
|
</Header>
|
|
}
|
|
>
|
|
<article>
|
|
<Box
|
|
sx={(theme) => ({
|
|
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)',
|
|
})}
|
|
p="lg"
|
|
pb={80}
|
|
>
|
|
{children}
|
|
</Box>
|
|
<Box sx={{ height: 200 }}></Box>
|
|
<Box
|
|
component="footer"
|
|
sx={(theme) => ({
|
|
position: 'fixed',
|
|
bottom: 0,
|
|
left: 0,
|
|
right: 0,
|
|
height: 200,
|
|
background: theme.colorScheme === 'dark' ? theme.colors.dark![7] : theme.colors.gray![0],
|
|
paddingLeft: 324,
|
|
paddingRight: data?.member?.kind !== 'Class' && data?.member?.kind !== 'Interface' ? 24 : 324,
|
|
|
|
[theme.fn.smallerThan('lg')]: {
|
|
paddingRight: 24,
|
|
},
|
|
|
|
[theme.fn.smallerThan('md')]: {
|
|
paddingLeft: 24,
|
|
},
|
|
})}
|
|
pt={70}
|
|
>
|
|
<Center>
|
|
<Link href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss" passHref>
|
|
<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>
|
|
</Box>
|
|
</article>
|
|
</AppShell>
|
|
);
|
|
}
|