mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-17 20:13:30 +01:00
feat: reintroduce outline navigation
This commit is contained in:
@@ -23,7 +23,9 @@ export function Nav() {
|
|||||||
)}
|
)}
|
||||||
universal
|
universal
|
||||||
>
|
>
|
||||||
<Sidebar />
|
<div className="flex flex-col gap-4 p-3">
|
||||||
|
<Sidebar />
|
||||||
|
</div>
|
||||||
</Scrollbars>
|
</Scrollbars>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export function Sidebar() {
|
|||||||
const { setOpened } = useNav();
|
const { setOpened } = useNav();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3 p-3">
|
<div className="flex flex-col gap-4">
|
||||||
{Object.keys(itemsByCategory).map((category, idx) => (
|
{Object.keys(itemsByCategory).map((category, idx) => (
|
||||||
<Section
|
<Section
|
||||||
buttonClassName="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-400 dark:hover:bg-dark-300 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-none focus:ring z-10"
|
buttonClassName="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-400 dark:hover:bg-dark-300 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-none focus:ring z-10"
|
||||||
@@ -41,7 +41,7 @@ export function Sidebar() {
|
|||||||
>
|
>
|
||||||
{itemsByCategory[category]?.map((member, index) => (
|
{itemsByCategory[category]?.map((member, index) => (
|
||||||
<Link
|
<Link
|
||||||
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l p-[5px] pl-6 outline-none focus:rounded focus:border-0 focus:ring ${
|
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l first:mt-1 p-[5px] pl-6 outline-none focus:rounded focus:border-0 focus:ring ${
|
||||||
decodeURIComponent(pathname ?? '') === member.href
|
decodeURIComponent(pathname ?? '') === member.href
|
||||||
? 'bg-blurple text-white'
|
? 'bg-blurple text-white'
|
||||||
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { connect } from '@planetscale/database';
|
import { connect } from '@planetscale/database';
|
||||||
import { cache } from 'react';
|
|
||||||
|
|
||||||
const sql = connect({
|
const sql = connect({
|
||||||
url: process.env.DATABASE_URL!,
|
url: process.env.DATABASE_URL!,
|
||||||
@@ -11,7 +10,7 @@ const sql = connect({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const fetchVersions = cache(async (packageName: string): Promise<string[]> => {
|
export const fetchVersions = async (packageName: string): Promise<string[]> => {
|
||||||
if (process.env.NEXT_PUBLIC_LOCAL_DEV || process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview') {
|
if (process.env.NEXT_PUBLIC_LOCAL_DEV || process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview') {
|
||||||
return ['main'];
|
return ['main'];
|
||||||
}
|
}
|
||||||
@@ -26,9 +25,9 @@ export const fetchVersions = cache(async (packageName: string): Promise<string[]
|
|||||||
} catch {
|
} catch {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
export const fetchModelJSON = cache(async (packageName: string, version: string) => {
|
export const fetchModelJSON = async (packageName: string, version: string) => {
|
||||||
if (process.env.NEXT_PUBLIC_LOCAL_DEV) {
|
if (process.env.NEXT_PUBLIC_LOCAL_DEV) {
|
||||||
try {
|
try {
|
||||||
const res = await readFile(
|
const res = await readFile(
|
||||||
@@ -67,4 +66,4 @@ export const fetchModelJSON = cache(async (packageName: string, version: string)
|
|||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ export default async function Page({ params }: { params: ItemRouteParams }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative top-6">
|
<div className="relative">
|
||||||
<Member member={member} />
|
<Member member={member} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import type { ApiFunction, ApiItem } from '@discordjs/api-extractor-model';
|
|||||||
import { ApiModel } from '@discordjs/api-extractor-model';
|
import { ApiModel } from '@discordjs/api-extractor-model';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { cache, type PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import { fetchModelJSON, fetchVersions } from '~/app/docAPI';
|
import { fetchModelJSON, fetchVersions } from '~/app/docAPI';
|
||||||
import { CmdKDialog } from '~/components/CmdK';
|
import { CmdKDialog } from '~/components/CmdK';
|
||||||
import { Nav } from '~/components/Nav';
|
import { Nav } from '~/components/Nav';
|
||||||
|
import { Outline } from '~/components/Outline';
|
||||||
import type { SidebarSectionItemData } from '~/components/Sidebar';
|
import type { SidebarSectionItemData } from '~/components/Sidebar';
|
||||||
import { resolveItemURI } from '~/components/documentation/util';
|
import { resolveItemURI } from '~/components/documentation/util';
|
||||||
import { addPackageToModel } from '~/util/addPackageToModel';
|
import { addPackageToModel } from '~/util/addPackageToModel';
|
||||||
@@ -36,14 +37,14 @@ export const generateStaticParams = async () => {
|
|||||||
return params;
|
return params;
|
||||||
};
|
};
|
||||||
|
|
||||||
const serializeIntoSidebarItemData = cache((item: ApiItem) => {
|
const serializeIntoSidebarItemData = (item: ApiItem) => {
|
||||||
return {
|
return {
|
||||||
kind: item.kind,
|
kind: item.kind,
|
||||||
name: item.displayName,
|
name: item.displayName,
|
||||||
href: resolveItemURI(item),
|
href: resolveItemURI(item),
|
||||||
overloadIndex: 'overloadIndex' in item ? (item.overloadIndex as number) : undefined,
|
overloadIndex: 'overloadIndex' in item ? (item.overloadIndex as number) : undefined,
|
||||||
} as SidebarSectionItemData;
|
} as SidebarSectionItemData;
|
||||||
});
|
};
|
||||||
|
|
||||||
export default async function PackageLayout({ children, params }: PropsWithChildren<{ params: VersionRouteParams }>) {
|
export default async function PackageLayout({ children, params }: PropsWithChildren<{ params: VersionRouteParams }>) {
|
||||||
const modelJSON = await fetchModelJSON(params.package, params.version);
|
const modelJSON = await fetchModelJSON(params.package, params.version);
|
||||||
@@ -80,15 +81,17 @@ export default async function PackageLayout({ children, params }: PropsWithChild
|
|||||||
<Providers>
|
<Providers>
|
||||||
<main className="mx-auto max-w-7xl px-4 lg:max-w-full">
|
<main className="mx-auto max-w-7xl px-4 lg:max-w-full">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="relative top-2.5 mx-auto max-w-7xl gap-6 lg:max-w-full lg:flex">
|
<div className="relative top-6.5 mx-auto max-w-7xl gap-6 lg:max-w-full lg:flex">
|
||||||
<div className="lg:sticky lg:top-23 lg:h-[calc(100vh_-_105px)]">
|
<div className="lg:sticky lg:top-23 lg:h-[calc(100vh_-_105px)]">
|
||||||
<Nav members={members.map((member) => serializeIntoSidebarItemData(member))} versions={versions} />
|
<Nav members={members.map((member) => serializeIntoSidebarItemData(member))} versions={versions} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx-auto max-w-5xl min-w-xs w-full pb-10">
|
<div className="relative top-4.5 mx-auto max-w-5xl min-w-xs w-full pb-10">
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Outline />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<CmdKDialog />
|
<CmdKDialog />
|
||||||
|
|||||||
@@ -4,13 +4,16 @@ import type { PropsWithChildren } from 'react';
|
|||||||
import { CmdKProvider } from '~/contexts/cmdK';
|
import { CmdKProvider } from '~/contexts/cmdK';
|
||||||
import { MemberProvider } from '~/contexts/member';
|
import { MemberProvider } from '~/contexts/member';
|
||||||
import { NavProvider } from '~/contexts/nav';
|
import { NavProvider } from '~/contexts/nav';
|
||||||
|
import { OutlineProvider } from '~/contexts/outline';
|
||||||
|
|
||||||
export function Providers({ children }: PropsWithChildren) {
|
export function Providers({ children }: PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<NavProvider>
|
<NavProvider>
|
||||||
<MemberProvider>
|
<OutlineProvider>
|
||||||
<CmdKProvider>{children}</CmdKProvider>
|
<MemberProvider>
|
||||||
</MemberProvider>
|
<CmdKProvider>{children}</CmdKProvider>
|
||||||
|
</MemberProvider>
|
||||||
|
</OutlineProvider>
|
||||||
</NavProvider>
|
</NavProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ export function Badges({ item }: { readonly item: ApiDocumentedItem }) {
|
|||||||
const isAbstract = ApiAbstractMixin.isBaseClassOf(item) && item.isAbstract;
|
const isAbstract = ApiAbstractMixin.isBaseClassOf(item) && item.isAbstract;
|
||||||
const isDeprecated = Boolean(item.tsdocComment?.deprecatedBlock);
|
const isDeprecated = Boolean(item.tsdocComment?.deprecatedBlock);
|
||||||
|
|
||||||
return (
|
const isAny = isStatic || isProtected || isReadonly || isAbstract || isDeprecated;
|
||||||
|
|
||||||
|
return isAny ? (
|
||||||
<div className="flex flex-row gap-1 md:ml-7">
|
<div className="flex flex-row gap-1 md:ml-7">
|
||||||
{isDeprecated ? <Badge color={BadgeColor.Danger}>Deprecated</Badge> : null}
|
{isDeprecated ? <Badge color={BadgeColor.Danger}>Deprecated</Badge> : null}
|
||||||
{isProtected ? <Badge>Protected</Badge> : null}
|
{isProtected ? <Badge>Protected</Badge> : null}
|
||||||
@@ -36,5 +38,5 @@ export function Badges({ item }: { readonly item: ApiDocumentedItem }) {
|
|||||||
{isAbstract ? <Badge>Abstract</Badge> : null}
|
{isAbstract ? <Badge>Abstract</Badge> : null}
|
||||||
{isReadonly ? <Badge>Readonly</Badge> : null}
|
{isReadonly ? <Badge>Readonly</Badge> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
) : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,10 +35,12 @@ export function Nav({
|
|||||||
universal
|
universal
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-4 p-3">
|
<div className="flex flex-col gap-4 p-3">
|
||||||
<PackageSelect />
|
<div className="flex flex-col gap-4">
|
||||||
<VersionSelect versions={versions} />
|
<PackageSelect />
|
||||||
|
<VersionSelect versions={versions} />
|
||||||
|
</div>
|
||||||
|
<Sidebar members={members} />
|
||||||
</div>
|
</div>
|
||||||
<Sidebar members={members} />
|
|
||||||
</Scrollbars>
|
</Scrollbars>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,23 +1,32 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { useOutline } from '~/contexts/outline';
|
||||||
import { Scrollbars } from './Scrollbars';
|
import { Scrollbars } from './Scrollbars';
|
||||||
import type { TableOfContentsSerialized } from './TableOfContentItems';
|
|
||||||
import { TableOfContentItems } from './TableOfContentItems';
|
import { TableOfContentItems } from './TableOfContentItems';
|
||||||
|
|
||||||
export function Outline({ members }: { readonly members: TableOfContentsSerialized[] }) {
|
export function Outline() {
|
||||||
|
const { members } = useOutline();
|
||||||
|
|
||||||
|
if (!members) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="fixed bottom-0 right-0 top-[50px] z-20 hidden h-[calc(100vh_-_65px)] w-64 border-l border-light-800 bg-white pr-2 xl:block dark:border-dark-100 dark:bg-dark-600">
|
<div className="lg:sticky lg:top-23 lg:h-[calc(100vh_-_105px)]">
|
||||||
<Scrollbars
|
<aside className="fixed bottom-4 left-4 right-4 top-22 z-20 mx-auto hidden max-w-5xl border border-light-900 rounded-md bg-white/75 shadow backdrop-blur-md lg:sticky lg:block lg:h-full lg:max-w-xs lg:min-w-xs lg:w-full dark:border-dark-100 dark:bg-dark-600/75">
|
||||||
autoHide
|
<Scrollbars
|
||||||
hideTracksWhenNotNeeded
|
autoHide
|
||||||
renderThumbVertical={(props) => <div {...props} className="z-30 rounded bg-light-900 dark:bg-dark-100" />}
|
className="[&>div]:overscroll-none"
|
||||||
renderTrackVertical={(props) => (
|
hideTracksWhenNotNeeded
|
||||||
<div {...props} className="absolute bottom-0.5 right-0.5 top-0.5 z-30 w-1.5 rounded" />
|
renderThumbVertical={(props) => <div {...props} className="z-30 rounded bg-light-900 dark:bg-dark-100" />}
|
||||||
)}
|
renderTrackVertical={(props) => (
|
||||||
universal
|
<div {...props} className="absolute bottom-0.5 right-0.5 top-0.5 z-30 w-1.5 rounded" />
|
||||||
>
|
)}
|
||||||
<TableOfContentItems serializedMembers={members} />
|
universal
|
||||||
</Scrollbars>
|
>
|
||||||
</aside>
|
<TableOfContentItems serializedMembers={members} />
|
||||||
|
</Scrollbars>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export function Sidebar({ members }: { readonly members: SidebarSectionItemData[
|
|||||||
const groupItems = useMemo(() => groupMembers(members), [members]);
|
const groupItems = useMemo(() => groupMembers(members), [members]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3 p-3">
|
<div className="flex flex-col gap-4">
|
||||||
{(Object.keys(groupItems) as (keyof GroupedMembers)[])
|
{(Object.keys(groupItems) as (keyof GroupedMembers)[])
|
||||||
.filter((group) => groupItems[group].length)
|
.filter((group) => groupItems[group].length)
|
||||||
.map((group, idx) => (
|
.map((group, idx) => (
|
||||||
@@ -99,7 +99,7 @@ export function Sidebar({ members }: { readonly members: SidebarSectionItemData[
|
|||||||
>
|
>
|
||||||
{groupItems[group].map((member, index) => (
|
{groupItems[group].map((member, index) => (
|
||||||
<ItemLink
|
<ItemLink
|
||||||
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l p-[5px] pl-6 outline-none focus:rounded focus:border-0 focus:ring ${
|
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l first:mt-1 p-[5px] pl-6 outline-none focus:rounded focus:border-0 focus:ring ${
|
||||||
decodeURIComponent(segment ?? '') === member.href
|
decodeURIComponent(segment ?? '') === member.href
|
||||||
? 'bg-blurple text-white'
|
? 'bg-blurple text-white'
|
||||||
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ import type { PropsWithChildren } from 'react';
|
|||||||
* Layout parent of documentation pages.
|
* Layout parent of documentation pages.
|
||||||
*/
|
*/
|
||||||
export function Documentation({ children }: PropsWithChildren) {
|
export function Documentation({ children }: PropsWithChildren) {
|
||||||
return <div className="w-full flex-col space-y-4">{children}</div>;
|
return <div className="w-full flex flex-col gap-4">{children}</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export function TSDoc({ item, tsdoc }: { readonly item: ApiItem; readonly tsdoc:
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col space-y-2">
|
<div className="flex flex-col gap-2">
|
||||||
{comment.deprecatedBlock ? (
|
{comment.deprecatedBlock ? (
|
||||||
<DeprecatedBlock>{createNode(comment.deprecatedBlock.content)}</DeprecatedBlock>
|
<DeprecatedBlock>{createNode(comment.deprecatedBlock.content)}</DeprecatedBlock>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { ApiClass, ApiConstructor } from '@discordjs/api-extractor-model';
|
import type { ApiClass, ApiConstructor } from '@discordjs/api-extractor-model';
|
||||||
import { ApiItemKind } from '@discordjs/api-extractor-model';
|
import { ApiItemKind } from '@discordjs/api-extractor-model';
|
||||||
// import { Outline } from '../Outline';
|
|
||||||
import { Badges } from '../Badges';
|
import { Badges } from '../Badges';
|
||||||
import { Documentation } from '../documentation/Documentation';
|
import { Documentation } from '../documentation/Documentation';
|
||||||
import { HierarchyText } from '../documentation/HierarchyText';
|
import { HierarchyText } from '../documentation/HierarchyText';
|
||||||
@@ -8,13 +7,16 @@ import { Members } from '../documentation/Members';
|
|||||||
import { ObjectHeader } from '../documentation/ObjectHeader';
|
import { ObjectHeader } from '../documentation/ObjectHeader';
|
||||||
import { ConstructorSection } from '../documentation/section/ConstructorSection';
|
import { ConstructorSection } from '../documentation/section/ConstructorSection';
|
||||||
import { TypeParameterSection } from '../documentation/section/TypeParametersSection';
|
import { TypeParameterSection } from '../documentation/section/TypeParametersSection';
|
||||||
// import { serializeMembers } from '../documentation/util';
|
import { serializeMembers } from '../documentation/util';
|
||||||
|
import { OutlineSetter } from './OutlineSetter';
|
||||||
|
|
||||||
export function Class({ clazz }: { readonly clazz: ApiClass }) {
|
export function Class({ clazz }: { readonly clazz: ApiClass }) {
|
||||||
const constructor = clazz.members.find((member) => member.kind === ApiItemKind.Constructor) as
|
const constructor = clazz.members.find((member) => member.kind === ApiItemKind.Constructor) as
|
||||||
| ApiConstructor
|
| ApiConstructor
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
|
const outlineMembers = serializeMembers(clazz);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Documentation>
|
<Documentation>
|
||||||
<Badges item={clazz} />
|
<Badges item={clazz} />
|
||||||
@@ -24,7 +26,7 @@ export function Class({ clazz }: { readonly clazz: ApiClass }) {
|
|||||||
{clazz.typeParameters.length ? <TypeParameterSection item={clazz} /> : null}
|
{clazz.typeParameters.length ? <TypeParameterSection item={clazz} /> : null}
|
||||||
{constructor ? <ConstructorSection item={constructor} /> : null}
|
{constructor ? <ConstructorSection item={constructor} /> : null}
|
||||||
<Members item={clazz} />
|
<Members item={clazz} />
|
||||||
{/* <Outline members={serializeMembers(clazz)} /> */}
|
<OutlineSetter members={outlineMembers} />
|
||||||
</Documentation>
|
</Documentation>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
import type { ApiInterface } from '@discordjs/api-extractor-model';
|
import type { ApiInterface } from '@discordjs/api-extractor-model';
|
||||||
// import { Outline } from '../Outline';
|
|
||||||
import { Documentation } from '../documentation/Documentation';
|
import { Documentation } from '../documentation/Documentation';
|
||||||
import { HierarchyText } from '../documentation/HierarchyText';
|
import { HierarchyText } from '../documentation/HierarchyText';
|
||||||
import { Members } from '../documentation/Members';
|
import { Members } from '../documentation/Members';
|
||||||
import { ObjectHeader } from '../documentation/ObjectHeader';
|
import { ObjectHeader } from '../documentation/ObjectHeader';
|
||||||
import { TypeParameterSection } from '../documentation/section/TypeParametersSection';
|
import { TypeParameterSection } from '../documentation/section/TypeParametersSection';
|
||||||
// import { serializeMembers } from '../documentation/util';
|
import { serializeMembers } from '../documentation/util';
|
||||||
|
import { OutlineSetter } from './OutlineSetter';
|
||||||
|
|
||||||
export function Interface({ item }: { readonly item: ApiInterface }) {
|
export function Interface({ item }: { readonly item: ApiInterface }) {
|
||||||
|
const outlineMembers = serializeMembers(item);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Documentation>
|
<Documentation>
|
||||||
<ObjectHeader item={item} />
|
<ObjectHeader item={item} />
|
||||||
<HierarchyText item={item} type="Extends" />
|
<HierarchyText item={item} type="Extends" />
|
||||||
{item.typeParameters.length ? <TypeParameterSection item={item} /> : null}
|
{item.typeParameters.length ? <TypeParameterSection item={item} /> : null}
|
||||||
<Members item={item} />
|
<Members item={item} />
|
||||||
{/* <Outline members={serializeMembers(item)} /> */}
|
<OutlineSetter members={outlineMembers} />
|
||||||
</Documentation>
|
</Documentation>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
19
apps/website/src/components/model/OutlineSetter.tsx
Normal file
19
apps/website/src/components/model/OutlineSetter.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, type PropsWithChildren } from 'react';
|
||||||
|
import { useOutline } from '~/contexts/outline';
|
||||||
|
import type { TableOfContentsSerialized } from '../TableOfContentItems';
|
||||||
|
|
||||||
|
export function OutlineSetter({ members }: PropsWithChildren<{ readonly members: TableOfContentsSerialized[] }>) {
|
||||||
|
const { setMembers } = useOutline();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMembers(members);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
setMembers(null);
|
||||||
|
};
|
||||||
|
}, [members, setMembers]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,37 +1,29 @@
|
|||||||
import type { ApiFunction } from '@discordjs/api-extractor-model';
|
import type { ApiFunction } from '@discordjs/api-extractor-model';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { Header } from '../../documentation/Header';
|
import { Documentation } from '~/components/documentation/Documentation';
|
||||||
|
import { ObjectHeader } from '~/components/documentation/ObjectHeader';
|
||||||
import { FunctionBody } from './FunctionBody';
|
import { FunctionBody } from './FunctionBody';
|
||||||
|
|
||||||
const OverloadSwitcher = dynamic(async () => import('../../OverloadSwitcher'));
|
const OverloadSwitcher = dynamic(async () => import('../../OverloadSwitcher'));
|
||||||
|
|
||||||
export function Function({ item }: { readonly item: ApiFunction }) {
|
export function Function({ item }: { readonly item: ApiFunction }) {
|
||||||
const header = (
|
|
||||||
<Header
|
|
||||||
kind={item.kind}
|
|
||||||
name={item.name}
|
|
||||||
sourceURL={item.sourceLocation.fileUrl}
|
|
||||||
sourceLine={item.sourceLocation.fileLine}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (item.getMergedSiblings().length > 1) {
|
if (item.getMergedSiblings().length > 1) {
|
||||||
const overloads = item
|
const overloads = item
|
||||||
.getMergedSiblings()
|
.getMergedSiblings()
|
||||||
.map((sibling, idx) => <FunctionBody item={sibling as ApiFunction} key={`${sibling.displayName}-${idx}`} />);
|
.map((sibling, idx) => <FunctionBody item={sibling as ApiFunction} key={`${sibling.displayName}-${idx}`} />);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Documentation>
|
||||||
{header}
|
<ObjectHeader item={item} />
|
||||||
<OverloadSwitcher methodName={item.displayName} overloads={overloads} />
|
<OverloadSwitcher methodName={item.displayName} overloads={overloads} />
|
||||||
</div>
|
</Documentation>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Documentation>
|
||||||
{header}
|
<ObjectHeader item={item} />
|
||||||
<FunctionBody item={item} />
|
<FunctionBody item={item} />
|
||||||
</div>
|
</Documentation>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import type { ApiFunction } from '@discordjs/api-extractor-model';
|
import type { ApiFunction } from '@discordjs/api-extractor-model';
|
||||||
import { SyntaxHighlighter } from '../../SyntaxHighlighter';
|
|
||||||
import { Documentation } from '../../documentation/Documentation';
|
|
||||||
import { ParameterSection } from '../../documentation/section/ParametersSection';
|
import { ParameterSection } from '../../documentation/section/ParametersSection';
|
||||||
import { SummarySection } from '../../documentation/section/SummarySection';
|
|
||||||
import { TypeParameterSection } from '../../documentation/section/TypeParametersSection';
|
import { TypeParameterSection } from '../../documentation/section/TypeParametersSection';
|
||||||
|
|
||||||
export interface FunctionBodyProps {
|
export interface FunctionBodyProps {
|
||||||
@@ -12,12 +9,9 @@ export interface FunctionBodyProps {
|
|||||||
|
|
||||||
export function FunctionBody({ item }: { readonly item: ApiFunction }) {
|
export function FunctionBody({ item }: { readonly item: ApiFunction }) {
|
||||||
return (
|
return (
|
||||||
<Documentation>
|
<>
|
||||||
{/* @ts-expect-error async component */}
|
|
||||||
<SyntaxHighlighter code={item.excerpt.text} />
|
|
||||||
<SummarySection item={item} />
|
|
||||||
{item.typeParameters.length ? <TypeParameterSection item={item} /> : null}
|
{item.typeParameters.length ? <TypeParameterSection item={item} /> : null}
|
||||||
{item.parameters.length ? <ParameterSection item={item} /> : null}
|
{item.parameters.length ? <ParameterSection item={item} /> : null}
|
||||||
</Documentation>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
22
apps/website/src/contexts/outline.tsx
Normal file
22
apps/website/src/contexts/outline.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { createContext, useContext, useMemo, useState } from 'react';
|
||||||
|
import type { PropsWithChildren, Dispatch, SetStateAction } from 'react';
|
||||||
|
import type { TableOfContentsSerialized } from '~/components/TableOfContentItems';
|
||||||
|
|
||||||
|
export const OutlineContext = createContext<{
|
||||||
|
members: TableOfContentsSerialized[] | null | undefined;
|
||||||
|
setMembers: Dispatch<SetStateAction<TableOfContentsSerialized[] | null | undefined>>;
|
||||||
|
}>({ members: undefined, setMembers: (_) => {} });
|
||||||
|
|
||||||
|
export const OutlineProvider = ({ children }: PropsWithChildren) => {
|
||||||
|
const [members, setMembers] = useState<TableOfContentsSerialized[] | null | undefined>(undefined);
|
||||||
|
|
||||||
|
const value = useMemo(() => ({ members, setMembers }), [members]);
|
||||||
|
|
||||||
|
return <OutlineContext.Provider value={value}>{children}</OutlineContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useOutline() {
|
||||||
|
return useContext(OutlineContext);
|
||||||
|
}
|
||||||
@@ -2,9 +2,8 @@ import type { ApiModel, ApiPackage } from '@discordjs/api-extractor-model';
|
|||||||
import { ApiItem } from '@discordjs/api-extractor-model';
|
import { ApiItem } from '@discordjs/api-extractor-model';
|
||||||
import { TSDocConfiguration } from '@microsoft/tsdoc';
|
import { TSDocConfiguration } from '@microsoft/tsdoc';
|
||||||
import { TSDocConfigFile } from '@microsoft/tsdoc-config';
|
import { TSDocConfigFile } from '@microsoft/tsdoc-config';
|
||||||
import { cache } from 'react';
|
|
||||||
|
|
||||||
export const addPackageToModel = cache((model: ApiModel, data: any) => {
|
export const addPackageToModel = (model: ApiModel, data: any) => {
|
||||||
let apiPackage: ApiPackage;
|
let apiPackage: ApiPackage;
|
||||||
if (data.metadata) {
|
if (data.metadata) {
|
||||||
const tsdocConfiguration = new TSDocConfiguration();
|
const tsdocConfiguration = new TSDocConfiguration();
|
||||||
@@ -24,4 +23,4 @@ export const addPackageToModel = cache((model: ApiModel, data: any) => {
|
|||||||
|
|
||||||
model.addMember(apiPackage);
|
model.addMember(apiPackage);
|
||||||
return model;
|
return model;
|
||||||
});
|
};
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { ApiModel, ApiFunction } from '@discordjs/api-extractor-model';
|
import { ApiModel, ApiFunction } from '@discordjs/api-extractor-model';
|
||||||
import { cache } from 'react';
|
|
||||||
import { fetchModelJSON } from '~/app/docAPI';
|
import { fetchModelJSON } from '~/app/docAPI';
|
||||||
import { addPackageToModel } from './addPackageToModel';
|
import { addPackageToModel } from './addPackageToModel';
|
||||||
import { OVERLOAD_SEPARATOR, PACKAGES } from './constants';
|
import { OVERLOAD_SEPARATOR, PACKAGES } from './constants';
|
||||||
import { findMember, findMemberByKey } from './model';
|
import { findMember, findMemberByKey } from './model';
|
||||||
|
|
||||||
export const fetchMember = cache(async (packageName: string, branchName: string, item?: string) => {
|
export const fetchMember = async (packageName: string, branchName: string, item?: string) => {
|
||||||
if (!PACKAGES.includes(packageName)) {
|
if (!PACKAGES.includes(packageName)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -33,4 +32,4 @@ export const fetchMember = cache(async (packageName: string, branchName: string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return memberName && containerKey ? findMemberByKey(model, packageName, containerKey) ?? null : null;
|
return memberName && containerKey ? findMemberByKey(model, packageName, containerKey) ?? null : null;
|
||||||
});
|
};
|
||||||
|
|||||||
@@ -6,21 +6,20 @@ import type {
|
|||||||
Excerpt,
|
Excerpt,
|
||||||
} from '@discordjs/api-extractor-model';
|
} from '@discordjs/api-extractor-model';
|
||||||
import type { DocSection } from '@microsoft/tsdoc';
|
import type { DocSection } from '@microsoft/tsdoc';
|
||||||
import { cache } from 'react';
|
|
||||||
|
|
||||||
export const findMemberByKey = cache((model: ApiModel, packageName: string, containerKey: string) => {
|
export const findMemberByKey = (model: ApiModel, packageName: string, containerKey: string) => {
|
||||||
const pkg = model.tryGetPackageByName(packageName === 'discord.js' ? packageName : `@discordjs/${packageName}`)!;
|
const pkg = model.tryGetPackageByName(packageName === 'discord.js' ? packageName : `@discordjs/${packageName}`)!;
|
||||||
return (pkg.members[0] as ApiEntryPoint).tryGetMemberByKey(containerKey);
|
return (pkg.members[0] as ApiEntryPoint).tryGetMemberByKey(containerKey);
|
||||||
});
|
};
|
||||||
|
|
||||||
export const findMember = cache((model: ApiModel, packageName: string, memberName: string | undefined) => {
|
export const findMember = (model: ApiModel, packageName: string, memberName: string | undefined) => {
|
||||||
if (!memberName) {
|
if (!memberName) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pkg = model.tryGetPackageByName(packageName === 'discord.js' ? packageName : `@discordjs/${packageName}`)!;
|
const pkg = model.tryGetPackageByName(packageName === 'discord.js' ? packageName : `@discordjs/${packageName}`)!;
|
||||||
return pkg.entryPoints[0]?.findMembersByName(memberName)[0];
|
return pkg.entryPoints[0]?.findMembersByName(memberName)[0];
|
||||||
});
|
};
|
||||||
|
|
||||||
interface ResolvedParameter {
|
interface ResolvedParameter {
|
||||||
description?: DocSection | undefined;
|
description?: DocSection | undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user