mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-15 02:53:31 +01:00
fix: sidebar behaviour when switching package/version
This commit is contained in:
16
apps/website/src/app/api/docs/entrypoints/route.ts
Normal file
16
apps/website/src/app/api/docs/entrypoints/route.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { NextResponse, type NextRequest } from 'next/server';
|
||||
import { fetchEntryPoints } from '@/util/fetchEntryPoints';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = request.nextUrl;
|
||||
const packageName = searchParams.get('packageName');
|
||||
const version = searchParams.get('version');
|
||||
|
||||
if (!packageName || !version) {
|
||||
return NextResponse.json({ error: 'Missing required parameters' }, { status: 400 });
|
||||
}
|
||||
|
||||
const response = await fetchEntryPoints(packageName, version);
|
||||
|
||||
return NextResponse.json(response);
|
||||
}
|
||||
21
apps/website/src/app/api/docs/sitemap/route.ts
Normal file
21
apps/website/src/app/api/docs/sitemap/route.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { NextResponse, type NextRequest } from 'next/server';
|
||||
import { fetchSitemap } from '@/util/fetchSitemap';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = request.nextUrl;
|
||||
const packageName = searchParams.get('packageName');
|
||||
const version = searchParams.get('version');
|
||||
const entryPoint = searchParams.get('entryPoint');
|
||||
|
||||
if (!packageName || !version) {
|
||||
return NextResponse.json({ error: 'Missing required parameters' }, { status: 400 });
|
||||
}
|
||||
|
||||
const response = await fetchSitemap({
|
||||
entryPoint,
|
||||
packageName,
|
||||
version,
|
||||
});
|
||||
|
||||
return NextResponse.json(response);
|
||||
}
|
||||
15
apps/website/src/app/api/docs/versions/route.ts
Normal file
15
apps/website/src/app/api/docs/versions/route.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { NextResponse, type NextRequest } from 'next/server';
|
||||
import { fetchVersions } from '@/util/fetchVersions';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = request.nextUrl;
|
||||
const packageName = searchParams.get('packageName');
|
||||
|
||||
if (!packageName) {
|
||||
return NextResponse.json({ error: 'Missing required parameters' }, { status: 400 });
|
||||
}
|
||||
|
||||
const response = await fetchVersions(packageName);
|
||||
|
||||
return NextResponse.json(response);
|
||||
}
|
||||
@@ -1,24 +1,5 @@
|
||||
'use cache';
|
||||
|
||||
import { VscGithubInverted } from '@react-icons/all-files/vsc/VscGithubInverted';
|
||||
import type { Metadata } from 'next';
|
||||
import Link from 'next/link';
|
||||
import { Suspense, type PropsWithChildren } from 'react';
|
||||
import { EntryPointSelect } from '@/components/EntrypointSelect';
|
||||
import { Footer } from '@/components/Footer';
|
||||
import { Navigation } from '@/components/Navigation';
|
||||
import { Scrollbars } from '@/components/OverlayScrollbars';
|
||||
import { PackageSelect } from '@/components/PackageSelect';
|
||||
import { SearchButton } from '@/components/SearchButton';
|
||||
import { ThemeSwitchNoSRR } from '@/components/ThemeSwitch';
|
||||
import { VersionSelect } from '@/components/VersionSelect';
|
||||
import { Sidebar, SidebarContent, SidebarHeader, SidebarInset, SidebarTrigger } from '@/components/ui/Sidebar';
|
||||
import { buttonStyles } from '@/styles/ui/button';
|
||||
import { PACKAGES_WITH_ENTRY_POINTS } from '@/util/constants';
|
||||
import { ENV } from '@/util/env';
|
||||
import { fetchEntryPoints } from '@/util/fetchEntryPoints';
|
||||
import { fetchVersions } from '@/util/fetchVersions';
|
||||
import { parseDocsPathParams } from '@/util/parseDocsPathParams';
|
||||
import { CmdK } from './CmdK';
|
||||
|
||||
export async function generateMetadata({
|
||||
@@ -44,82 +25,11 @@ export default async function Layout({
|
||||
params,
|
||||
children,
|
||||
}: PropsWithChildren<{
|
||||
readonly params: Promise<{
|
||||
readonly item?: string[] | undefined;
|
||||
readonly packageName: string;
|
||||
readonly version: string;
|
||||
}>;
|
||||
readonly params: Promise<{ readonly packageName: string; readonly version: string }>;
|
||||
}>) {
|
||||
const { packageName, version, item } = await params;
|
||||
|
||||
const versions = fetchVersions(packageName);
|
||||
|
||||
const hasEntryPoints = PACKAGES_WITH_ENTRY_POINTS.includes(packageName);
|
||||
|
||||
const entryPoints = hasEntryPoints ? fetchEntryPoints(packageName, version) : Promise.resolve([]);
|
||||
const { entryPoints: parsedEntrypoints } = parseDocsPathParams(item);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Sidebar closeButton={false} intent="inset">
|
||||
<SidebarHeader className="bg-[#f3f3f4] p-4 dark:bg-[#121214]">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex place-content-between place-items-center p-1">
|
||||
<Link className="text-xl font-bold" href={`/docs/packages/${packageName}/${version}`}>
|
||||
{packageName}
|
||||
</Link>
|
||||
<div className="flex place-items-center gap-2">
|
||||
<Link
|
||||
aria-label="GitHub"
|
||||
className={buttonStyles({ variant: 'filled', size: 'icon-sm' })}
|
||||
href="https://github.com/discordjs/discord.js"
|
||||
rel="external noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<VscGithubInverted aria-hidden data-slot="icon" size={18} />
|
||||
</Link>
|
||||
<ThemeSwitchNoSRR />
|
||||
</div>
|
||||
</div>
|
||||
<PackageSelect />
|
||||
{/* <h3 className="p-1 text-lg font-semibold">{version}</h3> */}
|
||||
<VersionSelect versionsPromise={versions} />
|
||||
{hasEntryPoints ? <EntryPointSelect entryPointsPromise={entryPoints} /> : null}
|
||||
<SearchButton />
|
||||
</div>
|
||||
</SidebarHeader>
|
||||
<SidebarContent className="bg-[#f3f3f4] p-0 py-4 pl-4 dark:bg-[#121214]">
|
||||
<Scrollbars>
|
||||
<Navigation entryPoint={parsedEntrypoints.join('.')} packageName={packageName} version={version} />
|
||||
</Scrollbars>
|
||||
</SidebarContent>
|
||||
</Sidebar>
|
||||
<SidebarInset>
|
||||
{ENV.IS_LOCAL_DEV ? (
|
||||
<div className="sticky top-0 z-10 flex place-content-center place-items-center border border-red-400/35 bg-red-500/65 p-2 px-4 text-center text-base text-white shadow-md backdrop-blur">
|
||||
Local test environment
|
||||
</div>
|
||||
) : null}
|
||||
{ENV.IS_PREVIEW ? (
|
||||
<div className="sticky top-0 z-10 flex place-content-center place-items-center border border-red-400/35 bg-red-500/65 p-2 px-4 text-center text-base text-white shadow-md backdrop-blur">
|
||||
Preview environment
|
||||
</div>
|
||||
) : null}
|
||||
<div className="bg-[#fbfbfb] pb-12 dark:bg-[#1a1a1e]">
|
||||
<div className="relative px-6 pt-6 md:hidden">
|
||||
<div className="fixed top-5 left-6 z-20 md:hidden">
|
||||
<SidebarTrigger aria-label="Navigation" size="icon" variant="filled" />
|
||||
</div>
|
||||
<div className="flex place-content-end">
|
||||
<Link className="text-xl font-bold" href={`/docs/packages/${packageName}/${version}`}>
|
||||
{packageName}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
</SidebarInset>
|
||||
{children}
|
||||
<Suspense>
|
||||
<CmdK params={params} />
|
||||
</Suspense>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
'use cache';
|
||||
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import rehypeShikiFromHighlighter from '@shikijs/rehype/core';
|
||||
|
||||
49
apps/website/src/app/docs/packages/layout.tsx
Normal file
49
apps/website/src/app/docs/packages/layout.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
// import Link from 'next/link';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { Footer } from '@/components/Footer';
|
||||
import { Navigation } from '@/components/Navigation';
|
||||
import { Scrollbars } from '@/components/OverlayScrollbars';
|
||||
import { SidebarHeader } from '@/components/Sidebar';
|
||||
import { Sidebar, SidebarContent, SidebarInset, SidebarTrigger } from '@/components/ui/Sidebar';
|
||||
import { ENV } from '@/util/env';
|
||||
|
||||
export default async function Layout({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<>
|
||||
<Sidebar closeButton={false} intent="inset">
|
||||
<SidebarHeader />
|
||||
<SidebarContent className="bg-[#f3f3f4] p-0 py-4 pl-4 dark:bg-[#121214]">
|
||||
<Scrollbars>
|
||||
<Navigation />
|
||||
</Scrollbars>
|
||||
</SidebarContent>
|
||||
</Sidebar>
|
||||
<SidebarInset>
|
||||
{ENV.IS_LOCAL_DEV ? (
|
||||
<div className="sticky top-0 z-10 flex place-content-center place-items-center border border-red-400/35 bg-red-500/65 p-2 px-4 text-center text-base text-white shadow-md backdrop-blur">
|
||||
Local test environment
|
||||
</div>
|
||||
) : null}
|
||||
{ENV.IS_PREVIEW ? (
|
||||
<div className="sticky top-0 z-10 flex place-content-center place-items-center border border-red-400/35 bg-red-500/65 p-2 px-4 text-center text-base text-white shadow-md backdrop-blur">
|
||||
Preview environment
|
||||
</div>
|
||||
) : null}
|
||||
<div className="bg-[#fbfbfb] pb-12 dark:bg-[#1a1a1e]">
|
||||
<div className="relative px-6 pt-6 md:hidden">
|
||||
<div className="fixed top-5 left-6 z-20 md:hidden">
|
||||
<SidebarTrigger aria-label="Navigation" size="icon" variant="filled" />
|
||||
</div>
|
||||
{/* <div className="flex place-content-end">
|
||||
<Link className="text-xl font-bold" href={`/docs/packages/${packageName}/${version}`}>
|
||||
{packageName}
|
||||
</Link>
|
||||
</div> */}
|
||||
</div>
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
</SidebarInset>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { isServer, QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
@@ -10,7 +11,30 @@ import { SidebarProvider } from '@/components/ui/Sidebar';
|
||||
import { useSystemThemeFallback } from '@/hooks/useSystemThemeFallback';
|
||||
import { useUnregisterServiceWorker } from '@/hooks/useUnregisterServiceWorker';
|
||||
|
||||
function makeQueryClient() {
|
||||
return new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 60 * 1_000,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let browserQueryClient: QueryClient | undefined;
|
||||
|
||||
function getQueryClient() {
|
||||
if (isServer) {
|
||||
// Server: always make a new query client
|
||||
return makeQueryClient();
|
||||
} else {
|
||||
browserQueryClient ??= makeQueryClient();
|
||||
return browserQueryClient;
|
||||
}
|
||||
}
|
||||
|
||||
export function Providers({ children }: PropsWithChildren) {
|
||||
const queryClient = getQueryClient();
|
||||
const router = useRouter();
|
||||
useUnregisterServiceWorker();
|
||||
useSystemThemeFallback();
|
||||
@@ -20,7 +44,9 @@ export function Providers({ children }: PropsWithChildren) {
|
||||
<ThemeProvider attribute="class">
|
||||
<RouterProvider navigate={router.push}>
|
||||
<JotaiProvider>
|
||||
<SidebarProvider defaultOpen>{children}</SidebarProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<SidebarProvider defaultOpen>{children}</SidebarProvider>
|
||||
</QueryClientProvider>
|
||||
</JotaiProvider>
|
||||
</RouterProvider>
|
||||
</ThemeProvider>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
'use cache';
|
||||
|
||||
import { VscSymbolParameter } from '@react-icons/all-files/vsc/VscSymbolParameter';
|
||||
import { ConstructorNode } from './ConstructorNode';
|
||||
import { DeprecatedNode } from './DeprecatedNode';
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
'use client';
|
||||
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { use } from 'react';
|
||||
import { parseDocsPathParams } from '@/util/parseDocsPathParams';
|
||||
import { Select, SelectList, SelectOption, SelectTrigger } from './ui/Select';
|
||||
|
||||
export function EntryPointSelect({
|
||||
entryPointsPromise,
|
||||
}: {
|
||||
readonly entryPointsPromise: Promise<{ readonly entryPoint: string }[]>;
|
||||
}) {
|
||||
export function EntryPointSelect({ entryPoints }: { readonly entryPoints: { readonly entryPoint: string }[] }) {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const entryPoints = use(entryPointsPromise);
|
||||
const params = useParams<{
|
||||
item?: string[] | undefined;
|
||||
packageName: string;
|
||||
version: string;
|
||||
}>();
|
||||
|
||||
const { entryPoints: parsedEntrypoints } = parseDocsPathParams(params.item as string[] | undefined);
|
||||
const { entryPoints: parsedEntrypoints } = parseDocsPathParams(params.item);
|
||||
|
||||
return (
|
||||
<Select aria-label="Select an entrypoint" defaultSelectedKey={parsedEntrypoints.join('/')}>
|
||||
<Select
|
||||
aria-label="Select an entrypoint"
|
||||
defaultSelectedKey={parsedEntrypoints.join('/')}
|
||||
key={parsedEntrypoints.join('/')}
|
||||
>
|
||||
<SelectTrigger className="bg-[#f3f3f4] dark:bg-[#121214]" />
|
||||
<SelectList classNames={{ popover: 'bg-[#f3f3f4] dark:bg-[#28282d]' }} items={entryPoints}>
|
||||
{(item) => (
|
||||
|
||||
@@ -1,26 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { fetchSitemap } from '@/util/fetchSitemap';
|
||||
import { notFound, useParams } from 'next/navigation';
|
||||
import { parseDocsPathParams } from '@/util/parseDocsPathParams';
|
||||
import { resolveNodeKind } from './DocKind';
|
||||
import { NavigationItem } from './NavigationItem';
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './ui/Collapsible';
|
||||
|
||||
export async function Navigation({
|
||||
entryPoint,
|
||||
packageName,
|
||||
version,
|
||||
}: {
|
||||
readonly entryPoint?: string | undefined;
|
||||
readonly packageName: string;
|
||||
readonly version: string;
|
||||
}) {
|
||||
const node = await fetchSitemap({ entryPoint, packageName, version });
|
||||
export function Navigation() {
|
||||
const params = useParams<{
|
||||
item?: string[] | undefined;
|
||||
packageName: string;
|
||||
version: string;
|
||||
}>();
|
||||
|
||||
if (!node) {
|
||||
const { entryPoints: parsedEntrypoints } = parseDocsPathParams(params.item);
|
||||
|
||||
const { data: node, status } = useQuery({
|
||||
queryKey: ['sitemap', params.packageName, params.version, parsedEntrypoints.join('.')],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(
|
||||
`/api/docs/sitemap?packageName=${params.packageName}&version=${params.version}&entryPoint=${parsedEntrypoints.join('.')}`,
|
||||
);
|
||||
|
||||
return response.json();
|
||||
},
|
||||
});
|
||||
|
||||
if ((status === 'success' && !node) || status === 'error') {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const groupedNodes = node.reduce((acc: any, node: any) => {
|
||||
const groupedNodes = node?.reduce((acc: any, node: any) => {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
(acc[node.kind.toLowerCase()] ||= []).push(node);
|
||||
return acc;
|
||||
@@ -28,7 +40,7 @@ export async function Navigation({
|
||||
|
||||
return (
|
||||
<nav className="flex flex-col gap-2 pr-3">
|
||||
{groupedNodes.class?.length ? (
|
||||
{groupedNodes?.class?.length ? (
|
||||
<Collapsible className="flex flex-col gap-2" defaultOpen>
|
||||
<CollapsibleTrigger className="group flex place-content-between place-items-center rounded-md p-2 hover:bg-[#e7e7e9] dark:hover:bg-[#242428]">
|
||||
<h4 className="font-semibold">Classes</h4>
|
||||
@@ -40,7 +52,12 @@ export async function Navigation({
|
||||
{groupedNodes.class.map((node: any, idx: number) => {
|
||||
const kind = resolveNodeKind(node.kind);
|
||||
return (
|
||||
<NavigationItem key={`${node.name}-${idx}`} node={node} packageName={packageName} version={version}>
|
||||
<NavigationItem
|
||||
key={`${node.name}-${idx}`}
|
||||
node={node}
|
||||
packageName={params.packageName}
|
||||
version={params.version}
|
||||
>
|
||||
<div className={`inline-block h-6 w-6 rounded-full text-center ${kind.background} ${kind.text}`}>
|
||||
{node.kind[0]}
|
||||
</div>{' '}
|
||||
@@ -53,7 +70,7 @@ export async function Navigation({
|
||||
</Collapsible>
|
||||
) : null}
|
||||
|
||||
{groupedNodes.function?.length ? (
|
||||
{groupedNodes?.function?.length ? (
|
||||
<Collapsible className="flex flex-col gap-2" defaultOpen>
|
||||
<CollapsibleTrigger className="group flex place-content-between place-items-center rounded-md p-2 hover:bg-[#e7e7e9] dark:hover:bg-[#242428]">
|
||||
<h4 className="font-semibold">Functions</h4>
|
||||
@@ -65,7 +82,12 @@ export async function Navigation({
|
||||
{groupedNodes.function.map((node: any, idx: number) => {
|
||||
const kind = resolveNodeKind(node.kind);
|
||||
return (
|
||||
<NavigationItem key={`${node.name}-${idx}`} node={node} packageName={packageName} version={version}>
|
||||
<NavigationItem
|
||||
key={`${node.name}-${idx}`}
|
||||
node={node}
|
||||
packageName={params.packageName}
|
||||
version={params.version}
|
||||
>
|
||||
<div className={`inline-block h-6 w-6 rounded-full text-center ${kind.background} ${kind.text}`}>
|
||||
{node.kind[0]}
|
||||
</div>{' '}
|
||||
@@ -78,7 +100,7 @@ export async function Navigation({
|
||||
</Collapsible>
|
||||
) : null}
|
||||
|
||||
{groupedNodes.enum?.length ? (
|
||||
{groupedNodes?.enum?.length ? (
|
||||
<Collapsible className="flex flex-col gap-2" defaultOpen>
|
||||
<CollapsibleTrigger className="group flex place-content-between place-items-center rounded-md p-2 hover:bg-[#e7e7e9] dark:hover:bg-[#242428]">
|
||||
<h4 className="font-semibold">Enums</h4>
|
||||
@@ -90,7 +112,12 @@ export async function Navigation({
|
||||
{groupedNodes.enum.map((node: any, idx: number) => {
|
||||
const kind = resolveNodeKind(node.kind);
|
||||
return (
|
||||
<NavigationItem key={`${node.name}-${idx}`} node={node} packageName={packageName} version={version}>
|
||||
<NavigationItem
|
||||
key={`${node.name}-${idx}`}
|
||||
node={node}
|
||||
packageName={params.packageName}
|
||||
version={params.version}
|
||||
>
|
||||
<div className={`inline-block h-6 w-6 rounded-full text-center ${kind.background} ${kind.text}`}>
|
||||
{node.kind[0]}
|
||||
</div>{' '}
|
||||
@@ -103,7 +130,7 @@ export async function Navigation({
|
||||
</Collapsible>
|
||||
) : null}
|
||||
|
||||
{groupedNodes.interface?.length ? (
|
||||
{groupedNodes?.interface?.length ? (
|
||||
<Collapsible className="flex flex-col gap-2" defaultOpen>
|
||||
<CollapsibleTrigger className="group flex place-content-between place-items-center rounded-md p-2 hover:bg-[#e7e7e9] dark:hover:bg-[#242428]">
|
||||
<h4 className="font-semibold">Interfaces</h4>
|
||||
@@ -115,7 +142,12 @@ export async function Navigation({
|
||||
{groupedNodes.interface.map((node: any, idx: number) => {
|
||||
const kind = resolveNodeKind(node.kind);
|
||||
return (
|
||||
<NavigationItem key={`${node.name}-${idx}`} node={node} packageName={packageName} version={version}>
|
||||
<NavigationItem
|
||||
key={`${node.name}-${idx}`}
|
||||
node={node}
|
||||
packageName={params.packageName}
|
||||
version={params.version}
|
||||
>
|
||||
<div className={`inline-block h-6 w-6 rounded-full text-center ${kind.background} ${kind.text}`}>
|
||||
{node.kind[0]}
|
||||
</div>{' '}
|
||||
@@ -128,7 +160,7 @@ export async function Navigation({
|
||||
</Collapsible>
|
||||
) : null}
|
||||
|
||||
{groupedNodes.typealias?.length ? (
|
||||
{groupedNodes?.typealias?.length ? (
|
||||
<Collapsible className="flex flex-col gap-2" defaultOpen>
|
||||
<CollapsibleTrigger className="group flex place-content-between place-items-center rounded-md p-2 hover:bg-[#e7e7e9] dark:hover:bg-[#242428]">
|
||||
<h4 className="font-semibold">Types</h4>
|
||||
@@ -140,7 +172,12 @@ export async function Navigation({
|
||||
{groupedNodes.typealias.map((node: any, idx: number) => {
|
||||
const kind = resolveNodeKind(node.kind);
|
||||
return (
|
||||
<NavigationItem key={`${node.name}-${idx}`} node={node} packageName={packageName} version={version}>
|
||||
<NavigationItem
|
||||
key={`${node.name}-${idx}`}
|
||||
node={node}
|
||||
packageName={params.packageName}
|
||||
version={params.version}
|
||||
>
|
||||
<div className={`inline-block h-6 w-6 rounded-full text-center ${kind.background} ${kind.text}`}>
|
||||
{node.kind[0]}
|
||||
</div>{' '}
|
||||
@@ -153,7 +190,7 @@ export async function Navigation({
|
||||
</Collapsible>
|
||||
) : null}
|
||||
|
||||
{groupedNodes.variable?.length ? (
|
||||
{groupedNodes?.variable?.length ? (
|
||||
<Collapsible className="flex flex-col gap-2" defaultOpen>
|
||||
<CollapsibleTrigger className="group flex place-content-between place-items-center rounded-md p-2 hover:bg-[#e7e7e9] dark:hover:bg-[#242428]">
|
||||
<h4 className="font-semibold">Variables</h4>
|
||||
@@ -165,7 +202,12 @@ export async function Navigation({
|
||||
{groupedNodes.variable.map((node: any, idx: number) => {
|
||||
const kind = resolveNodeKind(node.kind);
|
||||
return (
|
||||
<NavigationItem key={`${node.name}-${idx}`} node={node} packageName={packageName} version={version}>
|
||||
<NavigationItem
|
||||
key={`${node.name}-${idx}`}
|
||||
node={node}
|
||||
packageName={params.packageName}
|
||||
version={params.version}
|
||||
>
|
||||
<div className={`inline-block h-6 w-6 rounded-full text-center ${kind.background} ${kind.text}`}>
|
||||
{node.kind[0]}
|
||||
</div>{' '}
|
||||
|
||||
@@ -6,10 +6,12 @@ import { PACKAGES } from '@/util/constants';
|
||||
|
||||
export function PackageSelect() {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const params = useParams<{
|
||||
packageName: string;
|
||||
}>();
|
||||
|
||||
return (
|
||||
<Select aria-label="Select a package" defaultSelectedKey={params.packageName as string}>
|
||||
<Select aria-label="Select a package" defaultSelectedKey={params.packageName} key={params.packageName}>
|
||||
<SelectTrigger className="bg-[#f3f3f4] dark:bg-[#121214]" />
|
||||
<SelectList classNames={{ popover: 'bg-[#f3f3f4] dark:bg-[#28282d]' }} items={PACKAGES}>
|
||||
{(item) => (
|
||||
|
||||
70
apps/website/src/components/Sidebar.tsx
Normal file
70
apps/website/src/components/Sidebar.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
'use client';
|
||||
|
||||
import { VscGithubInverted } from '@react-icons/all-files/vsc/VscGithubInverted';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import Link from 'next/link';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { EntryPointSelect } from '@/components/EntrypointSelect';
|
||||
import { PackageSelect } from '@/components/PackageSelect';
|
||||
import { SearchButton } from '@/components/SearchButton';
|
||||
import { ThemeSwitchNoSRR } from '@/components/ThemeSwitch';
|
||||
import { VersionSelect } from '@/components/VersionSelect';
|
||||
import { SidebarHeader as BasSidebarHeader } from '@/components/ui/Sidebar';
|
||||
import { buttonStyles } from '@/styles/ui/button';
|
||||
import { PACKAGES_WITH_ENTRY_POINTS } from '@/util/constants';
|
||||
|
||||
export function SidebarHeader() {
|
||||
const params = useParams<{
|
||||
packageName: string;
|
||||
version: string;
|
||||
}>();
|
||||
|
||||
const hasEntryPoints = PACKAGES_WITH_ENTRY_POINTS.includes(params.packageName);
|
||||
|
||||
const { data: entryPoints } = useQuery({
|
||||
queryKey: ['entryPoints', params.packageName, params.version],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(`/api/docs/entrypoints?packageName=${params.packageName}&version=${params.version}`);
|
||||
|
||||
return response.json();
|
||||
},
|
||||
});
|
||||
|
||||
const { data: versions } = useQuery({
|
||||
queryKey: ['versions', params.packageName],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(`/api/docs/versions?packageName=${params.packageName}`);
|
||||
|
||||
return response.json();
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<BasSidebarHeader className="bg-[#f3f3f4] p-4 dark:bg-[#121214]">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex place-content-between place-items-center p-1">
|
||||
<Link className="text-xl font-bold" href={`/docs/packages/${params.packageName}/${params.version}`}>
|
||||
{params.packageName}
|
||||
</Link>
|
||||
<div className="flex place-items-center gap-2">
|
||||
<Link
|
||||
aria-label="GitHub"
|
||||
className={buttonStyles({ variant: 'filled', size: 'icon-sm' })}
|
||||
href="https://github.com/discordjs/discord.js"
|
||||
rel="external noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<VscGithubInverted aria-hidden data-slot="icon" size={18} />
|
||||
</Link>
|
||||
<ThemeSwitchNoSRR />
|
||||
</div>
|
||||
</div>
|
||||
<PackageSelect />
|
||||
{/* <h3 className="p-1 text-lg font-semibold">{version}</h3> */}
|
||||
<VersionSelect versions={versions ?? []} />
|
||||
{hasEntryPoints ? <EntryPointSelect entryPoints={entryPoints ?? []} /> : null}
|
||||
<SearchButton />
|
||||
</div>
|
||||
</BasSidebarHeader>
|
||||
);
|
||||
}
|
||||
@@ -1,26 +1,27 @@
|
||||
'use client';
|
||||
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { use } from 'react';
|
||||
import { Select, SelectList, SelectOption, SelectTrigger } from './ui/Select';
|
||||
import { Select, SelectList, SelectOption, SelectTrigger } from '@/components/ui/Select';
|
||||
import { DEFAULT_ENTRY_POINT, PACKAGES_WITH_ENTRY_POINTS } from '@/util/constants';
|
||||
|
||||
export function VersionSelect({
|
||||
versionsPromise,
|
||||
}: {
|
||||
readonly versionsPromise: Promise<{ readonly version: string }[]>;
|
||||
}) {
|
||||
export function VersionSelect({ versions }: { readonly versions: { readonly version: string }[] }) {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const versions = use(versionsPromise);
|
||||
const params = useParams<{ packageName: string; version: string }>();
|
||||
|
||||
const hasEntryPoints = PACKAGES_WITH_ENTRY_POINTS.includes(params.packageName);
|
||||
|
||||
return (
|
||||
<Select aria-label="Select a version" defaultSelectedKey={params.version as string}>
|
||||
<Select
|
||||
aria-label="Select a version"
|
||||
defaultSelectedKey={params.version}
|
||||
key={`${params.packageName}-${params.version}`}
|
||||
>
|
||||
<SelectTrigger className="bg-[#f3f3f4] dark:bg-[#121214]" />
|
||||
<SelectList classNames={{ popover: 'bg-[#f3f3f4] dark:bg-[#28282d]' }} items={versions}>
|
||||
{(item) => (
|
||||
<SelectOption
|
||||
className="dark:pressed:bg-[#313135] bg-[#f3f3f4] dark:bg-[#28282d] dark:hover:bg-[#313135]"
|
||||
href={`/docs/packages/${params.packageName}/${item.version}`}
|
||||
href={`/docs/packages/${params.packageName}/${item.version}${hasEntryPoints ? ['', ...DEFAULT_ENTRY_POINT].join('/') : ''}`}
|
||||
id={item.version}
|
||||
key={item.version}
|
||||
onHoverStart={() => router.prefetch(`/docs/packages/${params.packageName}/${item.version}`)}
|
||||
|
||||
@@ -9,6 +9,7 @@ const client = new Cloudflare({
|
||||
|
||||
async function fetchLatestVersion(packageName: string): Promise<string> {
|
||||
const hasEntryPoints = PACKAGES_WITH_ENTRY_POINTS.includes(packageName);
|
||||
|
||||
if (ENV.IS_LOCAL_DEV) {
|
||||
if (hasEntryPoints) {
|
||||
return ['main', ...DEFAULT_ENTRY_POINT].join('/');
|
||||
|
||||
@@ -8,7 +8,7 @@ export async function fetchSitemap({
|
||||
packageName,
|
||||
version,
|
||||
}: {
|
||||
readonly entryPoint?: string | undefined;
|
||||
readonly entryPoint?: string | null | undefined;
|
||||
readonly packageName: string;
|
||||
readonly version: string;
|
||||
}) {
|
||||
|
||||
Reference in New Issue
Block a user