diff --git a/packages/api-extractor-utils/src/ApiNodeJSONEncoder.ts b/packages/api-extractor-utils/src/ApiNodeJSONEncoder.ts index 4f485fded9b3b..2a100b70d5f5a 100644 --- a/packages/api-extractor-utils/src/ApiNodeJSONEncoder.ts +++ b/packages/api-extractor-utils/src/ApiNodeJSONEncoder.ts @@ -82,6 +82,7 @@ export interface ApiMethodSignatureJSON ApiTypeParameterListJSON, ApiParameterListJSON, ApiInheritableJSON { + mergedSiblings: ApiFunctionJSON[]; optional: boolean; overloadIndex: number; returnTypeTokens: TokenDocumentation[]; @@ -133,6 +134,7 @@ export interface ApiVariableJSON extends ApiItemJSON { } export interface ApiFunctionJSON extends ApiItemJSON, ApiTypeParameterListJSON, ApiParameterListJSON { + mergedSiblings: ApiFunctionJSON[]; overloadIndex: number; returnTypeTokens: TokenDocumentation[]; } @@ -255,12 +257,15 @@ export class ApiNodeJSONEncoder { }; } - public static encodeFunction(model: ApiModel, item: FunctionLike, version: string): ApiFunctionJSON { + public static encodeFunction(model: ApiModel, item: FunctionLike, version: string, nested = false): ApiFunctionJSON { return { ...this.encodeItem(model, item, version), ...this.encodeParameterList(model, item, version), ...this.encodeTypeParameterList(model, item, version), returnTypeTokens: item.returnTypeExcerpt.spannedTokens.map((token) => genToken(model, token, version)), + mergedSiblings: nested + ? [] + : item.getMergedSiblings().map((item) => this.encodeFunction(model, item as ApiFunction, version, true)), overloadIndex: item.overloadIndex, }; } diff --git a/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts b/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts index 863dc61b1015a..39f3a4546f75c 100644 --- a/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts +++ b/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts @@ -371,6 +371,18 @@ describe('Slash Commands', () => { ).not.toThrowError(); }); + test('GIVEN builder with subcommand THEN has regular slash command fields', () => { + expect(() => + getBuilder() + .setName('name') + .setDescription('description') + .addSubcommand((option) => option.setName('ye').setDescription('ye')) + .addSubcommand((option) => option.setName('no').setDescription('no')) + .setDMPermission(false) + .setDefaultMemberPermissions(1n), + ).not.toThrowError(); + }); + test('GIVEN builder with already built subcommand group THEN does not throw error', () => { expect(() => getNamedBuilder().addSubcommandGroup(getSubcommandGroup())).not.toThrowError(); }); diff --git a/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts b/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts index c7b5a76c3992b..8bdb6c49e1bf6 100644 --- a/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts +++ b/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts @@ -190,8 +190,7 @@ export class SlashCommandBuilder { export interface SlashCommandBuilder extends SharedNameAndDescription, SharedSlashCommandOptions {} export interface SlashCommandSubcommandsOnlyBuilder - extends SharedNameAndDescription, - Pick {} + extends Omit> {} export interface SlashCommandOptionsOnlyBuilder extends SharedNameAndDescription, diff --git a/packages/website/src/components/DocContainer.tsx b/packages/website/src/components/DocContainer.tsx index 4f7ddf778cdbf..dec6f9c89c23e 100644 --- a/packages/website/src/components/DocContainer.tsx +++ b/packages/website/src/components/DocContainer.tsx @@ -5,6 +5,7 @@ import type { ApiClassJSON, ApiInterfaceJSON, } from '@discordjs/api-extractor-utils'; +import type { ReactNode } from 'react'; import { Fragment, type PropsWithChildren } from 'react'; import { Scrollbars } from 'react-custom-scrollbars-2'; import { @@ -32,6 +33,7 @@ type DocContainerProps = PropsWithChildren<{ methods?: ApiClassJSON['methods'] | ApiInterfaceJSON['methods'] | null; name: string; properties?: ApiClassJSON['properties'] | ApiInterfaceJSON['properties'] | null; + subHeading?: ReactNode; summary?: ApiItemJSON['summary']; typeParams?: TypeParameterData[]; }>; @@ -60,6 +62,7 @@ export function DocContainer({ implementsTokens, methods, properties, + subHeading, }: DocContainerProps) { const matches = useMedia('(max-width: 768px)', true); @@ -71,6 +74,8 @@ export function DocContainer({ {name} + {subHeading} +
} padded dense={matches}> {summary ? : No summary provided.}
diff --git a/packages/website/src/components/MethodItem.tsx b/packages/website/src/components/MethodItem.tsx index 6cdb6f1c04d8b..97224e5add292 100644 --- a/packages/website/src/components/MethodItem.tsx +++ b/packages/website/src/components/MethodItem.tsx @@ -1,6 +1,8 @@ import type { ApiMethodJSON, ApiMethodSignatureJSON } from '@discordjs/api-extractor-utils'; -import { useCallback, useMemo } from 'react'; +import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit'; +import { useCallback, useMemo, useState } from 'react'; import { FiLink } from 'react-icons/fi'; +import { VscChevronDown } from 'react-icons/vsc'; import { HyperlinkedText } from './HyperlinkedText'; import { InheritanceText } from './InheritanceText'; import { ParameterTable } from './ParameterTable'; @@ -8,6 +10,11 @@ import { TSDoc } from './tsdoc/TSDoc'; export function MethodItem({ data }: { data: ApiMethodJSON | ApiMethodSignatureJSON }) { const method = data as ApiMethodJSON; + + const [overloadIndex, setOverloadIndex] = useState(1); + const overloadedData = method.mergedSiblings[overloadIndex - 1]!; + const menuState = useMenuState({ gutter: 8 }); + const key = useMemo( () => `${data.name}${data.overloadIndex && data.overloadIndex > 1 ? `:${data.overloadIndex}` : ''}`, [data.name, data.overloadIndex], @@ -62,13 +69,35 @@ export function MethodItem({ data }: { data: ApiMethodJSON | ApiMethodSignatureJ
+ {data.mergedSiblings.length > 1 ? ( +
+ +
+ {`Overload ${overloadIndex} of ${data.mergedSiblings.length}`} + +
+
+ + {data.mergedSiblings.map((_, idx) => ( + setOverloadIndex(idx + 1)} + >{`Overload ${idx + 1}`} + ))} + +
+ ) : null} {data.summary || data.parameters.length ? (
- {data.deprecated ? : null} - {data.summary ? : null} - {data.remarks ? : null} - {data.comment ? : null} - {data.parameters.length ? : null} + {overloadedData.deprecated ? : null} + {overloadedData.summary ?? data.summary ? : null} + {overloadedData.remarks ? : null} + {overloadedData.comment ? : null} + {overloadedData.parameters.length ? : null} {data.inheritanceData ? : null}
) : null} diff --git a/packages/website/src/components/MethodList.tsx b/packages/website/src/components/MethodList.tsx index 431b3e2aa1674..5b84c7e98ab18 100644 --- a/packages/website/src/components/MethodList.tsx +++ b/packages/website/src/components/MethodList.tsx @@ -5,14 +5,20 @@ import { MethodItem } from './MethodItem'; export function MethodList({ data }: { data: (ApiMethodJSON | ApiMethodSignatureJSON)[] }) { const methodItems = useMemo( () => - data.map((method) => ( - 1 ? `:${method.overloadIndex}` : ''}`} - > - -
- - )), + data.map((method) => { + if (method.overloadIndex > 1) { + return null; + } + + return ( + 1 ? `:${method.overloadIndex}` : ''}`} + > + +
+ + ); + }), [data], ); diff --git a/packages/website/src/components/TableOfContentItems.tsx b/packages/website/src/components/TableOfContentItems.tsx index 7726fdf443ad1..e5bb53b11563b 100644 --- a/packages/website/src/components/TableOfContentItems.tsx +++ b/packages/website/src/components/TableOfContentItems.tsx @@ -27,6 +27,10 @@ export function TableOfContentItems({ const methodItems = useMemo( () => methods.map((member) => { + if (member.overloadIndex && member.overloadIndex > 1) { + return null; + } + const key = `${member.name}${ member.overloadIndex && member.overloadIndex > 1 ? `:${member.overloadIndex}` : '' }`; diff --git a/packages/website/src/components/model/Function.tsx b/packages/website/src/components/model/Function.tsx index 86ea8e5e52948..469a3b33c0ba6 100644 --- a/packages/website/src/components/model/Function.tsx +++ b/packages/website/src/components/model/Function.tsx @@ -1,17 +1,50 @@ import type { ApiFunctionJSON } from '@discordjs/api-extractor-utils'; +import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit'; +import { useState } from 'react'; +import { VscChevronDown } from 'react-icons/vsc'; import { DocContainer } from '../DocContainer'; import { ParametersSection } from '../Sections'; export function Function({ data }: { data: ApiFunctionJSON }) { + const [overloadIndex, setOverloadIndex] = useState(1); + const overloadedData = data.mergedSiblings[overloadIndex - 1]!; + const menuState = useMenuState({ gutter: 8 }); + return ( 1 ? ` (${data.overloadIndex})` : ''}`} - kind={data.kind} - excerpt={data.excerpt} - summary={data.summary} - typeParams={data.typeParameters} + name={`${overloadedData.name}${ + overloadedData.overloadIndex && overloadedData.overloadIndex > 1 ? ` (${overloadedData.overloadIndex})` : '' + }`} + kind={overloadedData.kind} + excerpt={overloadedData.excerpt} + summary={overloadedData.summary} + typeParams={overloadedData.typeParameters} + subHeading={ + data.mergedSiblings.length > 1 ? ( +
+ +
+

{`Overload ${overloadIndex} of ${data.mergedSiblings.length}`}

+ +
+
+ + {data.mergedSiblings.map((_, idx) => ( + setOverloadIndex(idx + 1)} + >{`Overload ${idx + 1}`} + ))} + +
+ ) : null + } > - +
); } diff --git a/packages/website/src/pages/docs/[...slug].tsx b/packages/website/src/pages/docs/[...slug].tsx index c01423dea062d..7eb883bb808ca 100644 --- a/packages/website/src/pages/docs/[...slug].tsx +++ b/packages/website/src/pages/docs/[...slug].tsx @@ -193,7 +193,9 @@ export const getStaticProps: GetStaticProps = async ({ params }) => { packageName, branchName, data: { - members: pkg ? getMembers(pkg, branchName) : [], + members: pkg + ? getMembers(pkg, branchName).filter((item) => item.overloadIndex === null || item.overloadIndex <= 1) + : [], member: memberName && containerKey ? findMemberByKey(model, packageName, containerKey, branchName) ?? null : null, source: mdxSource, @@ -219,7 +221,7 @@ const member = (props?: ApiItemJSON | undefined) => { case 'Class': return ; case 'Function': - return ; + return ; case 'Interface': return ; case 'TypeAlias':