diff --git a/packages/website/src/components/CodeListing.tsx b/packages/website/src/components/CodeListing.tsx index 64bf0a2fb..359bd8648 100644 --- a/packages/website/src/components/CodeListing.tsx +++ b/packages/website/src/components/CodeListing.tsx @@ -1,10 +1,8 @@ -import { Group, Stack, Title } from '@mantine/core'; +import { Badge, Group, Stack, Title } from '@mantine/core'; import type { ReactNode } from 'react'; import { HyperlinkedText } from './HyperlinkedText'; import { TSDoc } from './tsdoc/TSDoc'; -import type { DocItem } from '~/DocModel/DocItem'; -import type { AnyDocNodeJSON } from '~/DocModel/comment/CommentNode'; -import type { TokenDocumentation } from '~/util/parse.server'; +import type { DocProperty } from '~/DocModel/DocProperty'; export enum CodeListingSeparatorType { Type = ':', @@ -12,35 +10,30 @@ export enum CodeListingSeparatorType { } export function CodeListing({ - name, + prop, separator = CodeListingSeparatorType.Type, - summary, - typeTokens, children, - comment, }: { - name: string; - summary?: ReturnType['summary']; - typeTokens: TokenDocumentation[]; + prop: ReturnType; separator?: CodeListingSeparatorType; children?: ReactNode; - className?: string | undefined; - comment?: AnyDocNodeJSON | null; }) { return ( - + + {prop.readonly ? Readonly : null} + {prop.optional ? Optional : null} - {name} + {prop.name} {separator} - <HyperlinkedText tokens={typeTokens} /> + <HyperlinkedText tokens={prop.propertyTypeTokens} /> - {summary && } - {comment && } + {prop.summary && } + {prop.comment && } {children} diff --git a/packages/website/src/components/MethodItem.tsx b/packages/website/src/components/MethodItem.tsx index 9be37effc..202532617 100644 --- a/packages/website/src/components/MethodItem.tsx +++ b/packages/website/src/components/MethodItem.tsx @@ -1,9 +1,10 @@ -import { Group, Stack, Title } from '@mantine/core'; +import { Badge, Group, Stack, Title } from '@mantine/core'; import { HyperlinkedText } from './HyperlinkedText'; import { ParameterTable } from './ParameterTable'; import { TSDoc } from './tsdoc/TSDoc'; import type { DocMethod } from '~/DocModel/DocMethod'; import type { DocMethodSignature } from '~/DocModel/DocMethodSignature'; +import { Visibility } from '~/DocModel/Visibility'; type MethodResolvable = ReturnType | ReturnType; @@ -18,11 +19,21 @@ function getShorthandName(data: MethodResolvable) { } export function MethodItem({ data }: { data: MethodResolvable }) { + const method = data as ReturnType; + return ( - + 1 ? `:${data.overloadIndex}` : ''}`} + className="scroll-mt-30" + spacing="xs" + > + {data.kind === 'Method' && method.visibility === Visibility.Protected ? ( + Protected + ) : null} + {data.kind === 'Method' && method.static ? Static : null} {`${getShorthandName(data)}`} : diff --git a/packages/website/src/components/MethodList.tsx b/packages/website/src/components/MethodList.tsx index 3358fc3af..bab7d7a63 100644 --- a/packages/website/src/components/MethodList.tsx +++ b/packages/website/src/components/MethodList.tsx @@ -12,7 +12,10 @@ export function MethodList({ <Stack> {data.map((method) => ( <> - <MethodItem key={method.name} data={method} /> + <MethodItem + key={`${method.name}${method.overloadIndex && method.overloadIndex > 1 ? `:${method.overloadIndex}` : ''}`} + data={method} + /> <Divider className="bg-gray-100" size="md" /> </> ))} diff --git a/packages/website/src/components/PropertyList.tsx b/packages/website/src/components/PropertyList.tsx index 673efba75..d99085a81 100644 --- a/packages/website/src/components/PropertyList.tsx +++ b/packages/website/src/components/PropertyList.tsx @@ -6,13 +6,7 @@ export function PropertyList({ data }: { data: ReturnType<DocProperty['toJSON']> return ( <Stack> {data.map((prop) => ( - <CodeListing - key={prop.name} - name={prop.name} - typeTokens={prop.propertyTypeTokens} - summary={prop.summary} - comment={prop.comment} - /> + <CodeListing key={prop.name} prop={prop} /> ))} </Stack> ); diff --git a/packages/website/src/components/Section.tsx b/packages/website/src/components/Section.tsx index 4df3ff211..a3fbd0905 100644 --- a/packages/website/src/components/Section.tsx +++ b/packages/website/src/components/Section.tsx @@ -11,7 +11,7 @@ const useStyles = createStyles((theme, { opened }: { opened: boolean }) => ({ fontSize: theme.fontSizes.sm, '&:hover': { - backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![7] : theme.colors.gray![0], + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0], color: theme.colorScheme === 'dark' ? theme.white : theme.black, }, }, diff --git a/packages/website/src/components/Sections.tsx b/packages/website/src/components/Sections.tsx index 11a1619cd..0ab792c05 100644 --- a/packages/website/src/components/Sections.tsx +++ b/packages/website/src/components/Sections.tsx @@ -4,10 +4,15 @@ import { MethodList } from './MethodList'; import { ParameterTable } from './ParameterTable'; import { PropertyList } from './PropertyList'; import { Section } from './Section'; +import type { DocClass } from '~/DocModel/DocClass'; import type { DocInterface } from '~/DocModel/DocInterface'; import type { ParameterDocumentation } from '~/util/parse.server'; -export function PropertiesSection({ data }: { data: ReturnType<DocInterface['toJSON']>['properties'] }) { +export function PropertiesSection({ + data, +}: { + data: ReturnType<DocClass['toJSON']>['properties'] | ReturnType<DocInterface['toJSON']>['properties']; +}) { const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false }); return data.length ? ( @@ -17,7 +22,11 @@ export function PropertiesSection({ data }: { data: ReturnType<DocInterface['toJ ) : null; } -export function MethodsSection({ data }: { data: ReturnType<DocInterface['toJSON']>['methods'] }) { +export function MethodsSection({ + data, +}: { + data: ReturnType<DocClass['toJSON']>['methods'] | ReturnType<DocInterface['toJSON']>['methods']; +}) { const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false }); return data.length ? ( diff --git a/packages/website/src/components/SidebarItems.tsx b/packages/website/src/components/SidebarItems.tsx index 096912771..7d4022e7c 100644 --- a/packages/website/src/components/SidebarItems.tsx +++ b/packages/website/src/components/SidebarItems.tsx @@ -77,7 +77,7 @@ const useStyles = createStyles((theme) => ({ borderLeft: `1px solid ${theme.colorScheme === 'dark' ? theme.colors.dark![4] : theme.colors.gray![3]}`, '&:hover': { - backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![7] : theme.colors.gray![0], + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0], color: theme.colorScheme === 'dark' ? theme.white : theme.black, }, }, diff --git a/packages/website/src/components/SidebarLayout.tsx b/packages/website/src/components/SidebarLayout.tsx index 5c7cfa447..8e7868464 100644 --- a/packages/website/src/components/SidebarLayout.tsx +++ b/packages/website/src/components/SidebarLayout.tsx @@ -3,7 +3,7 @@ import { AppShell, Navbar, MediaQuery, - // Aside, + Aside, Header, Burger, Anchor, @@ -26,6 +26,8 @@ import { type PropsWithChildren, useState } from 'react'; import { VscChevronDown, VscPackage } from 'react-icons/vsc'; import { WiDaySunny, WiNightClear } from 'react-icons/wi'; import { SidebarItems } from './SidebarItems'; +import { TableOfContentsItems } from './TableOfContentsItems'; +import type { DocClass } from '~/DocModel/DocClass'; import type { DocItem } from '~/DocModel/DocItem'; import type { findMember } from '~/util/model.server'; import type { getMembers } from '~/util/parse.server'; @@ -59,7 +61,7 @@ const useStyles = createStyles((theme, { opened }: { opened: boolean }) => ({ color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black, '&:hover': { - backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![7] : theme.colors.gray![0], + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0], color: theme.colorScheme === 'dark' ? theme.white : theme.black, }, }, @@ -153,19 +155,21 @@ export function SidebarLayout({ packageName, data, children }: PropsWithChildren ) : null} </Navbar> } - // aside={ - // packageName && data?.member ? ( - // <MediaQuery smallerThan="sm" styles={{ display: 'none' }}> - // <Aside hiddenBreakpoint="sm" width={{ sm: 200, lg: 300 }}> - // <ScrollArea p="xs"> - // <SidebarItems members={data.members} /> - // </ScrollArea> - // </Aside> - // </MediaQuery> - // ) : ( - // <></> - // ) - // } + aside={ + packageName && data?.member && data.member.kind === 'Class' ? ( + <MediaQuery smallerThan="sm" styles={{ display: 'none' }}> + <Aside hiddenBreakpoint="sm" width={{ sm: 200, lg: 300 }}> + <ScrollArea p="xs"> + <TableOfContentsItems + members={(data.member as unknown as ReturnType<DocClass['toJSON']>).methods} + ></TableOfContentsItems> + </ScrollArea> + </Aside> + </MediaQuery> + ) : ( + <></> + ) + } // footer={ // <Footer height={60} p="md"> // Application footer @@ -195,7 +199,7 @@ export function SidebarLayout({ packageName, data, children }: PropsWithChildren onClick={() => toggleColorScheme()} title="Toggle color scheme" > - {dark ? <WiDaySunny size={18} /> : <WiNightClear size={18} />} + {dark ? <WiDaySunny size={20} /> : <WiNightClear size={20} />} </ActionIcon> </Box> </Header> diff --git a/packages/website/src/components/TableOfContentsItems.tsx b/packages/website/src/components/TableOfContentsItems.tsx new file mode 100644 index 000000000..c47c512cb --- /dev/null +++ b/packages/website/src/components/TableOfContentsItems.tsx @@ -0,0 +1,61 @@ +import { createStyles, Group, Text, Box } from '@mantine/core'; +import { VscListSelection } from 'react-icons/vsc'; +import type { DocClass } from '~/DocModel/DocClass'; +import type { DocInterface } from '~/DocModel/DocInterface'; + +const useStyles = createStyles((theme) => ({ + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + link: { + ...theme.fn.focusStyles(), + display: 'block', + textDecoration: 'none', + color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black, + lineHeight: 1.2, + fontSize: theme.fontSizes.sm, + padding: theme.spacing.xs, + paddingLeft: theme.spacing.md, + marginLeft: 8, + borderTopRightRadius: theme.radius.sm, + borderBottomRightRadius: theme.radius.sm, + borderLeft: `1px solid ${theme.colorScheme === 'dark' ? theme.colors.dark![4] : theme.colors.gray![3]}`, + + '&:hover': { + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0], + }, + }, +})); + +export function TableOfContentsItems({ + members, +}: { + members: ReturnType<DocClass['toJSON']>['methods'] | ReturnType<DocInterface['toJSON']>['methods']; +}) { + const { classes } = useStyles(); + + const items = members.map((member) => { + const key = `${member.name}${member.overloadIndex && member.overloadIndex > 1 ? `:${member.overloadIndex}` : ''}`; + + return ( + <Box<'a'> key={key} href={`#${key}`} component="a" className={classes.link}> + <Group> + <Text className="line-clamp-1 text-ellipsis overflow-hidden">{member.name}</Text> + {member.overloadIndex && member.overloadIndex > 1 ? ( + <Text size="xs" color="dimmed"> + {member.overloadIndex} + </Text> + ) : null} + </Group> + </Box> + ); + }); + + return ( + <Box> + <Group mb="md"> + <VscListSelection size={20} /> + <Text>Table of contents</Text> + </Group> + {items} + </Box> + ); +} diff --git a/packages/website/src/components/model/Function.tsx b/packages/website/src/components/model/Function.tsx index 849272100..382b2866c 100644 --- a/packages/website/src/components/model/Function.tsx +++ b/packages/website/src/components/model/Function.tsx @@ -5,7 +5,7 @@ import type { DocFunction } from '~/DocModel/DocFunction'; export function Function({ data }: { data: ReturnType<DocFunction['toJSON']> }) { return ( <DocContainer - name={`${data.name}${data.overloadIndex ? ` (${data.overloadIndex})` : ''}`} + name={`${data.name}${data.overloadIndex && data.overloadIndex > 1 ? ` (${data.overloadIndex})` : ''}`} kind={data.kind} excerpt={data.excerpt} summary={data.summary} diff --git a/packages/website/src/pages/docs/[...slug].tsx b/packages/website/src/pages/docs/[...slug].tsx index 21e24a8f6..fb3f06416 100644 --- a/packages/website/src/pages/docs/[...slug].tsx +++ b/packages/website/src/pages/docs/[...slug].tsx @@ -53,7 +53,7 @@ export const getStaticPaths: GetStaticPaths = async () => { // causing next.js export to error .filter((member) => member.name !== 'RESTEvents') .map((member) => { - if (member.kind === 'Function' && member.overloadIndex) { + if (member.kind === 'Function' && member.overloadIndex && member.overloadIndex > 1) { return { params: { slug: ['main', 'packages', packageName, `${member.name}:${member.overloadIndex}`], diff --git a/packages/website/src/util/parse.server.ts b/packages/website/src/util/parse.server.ts index 64534551a..47c06966f 100644 --- a/packages/website/src/util/parse.server.ts +++ b/packages/website/src/util/parse.server.ts @@ -34,7 +34,11 @@ export function generatePath(items: readonly ApiItem[]) { path += `${item.displayName}/`; break; case ApiItemKind.Function: - path += `${item.displayName}:${(item as ApiFunction).overloadIndex}/`; + // eslint-disable-next-line no-case-declarations + const functionItem = item as ApiFunction; + path += `${functionItem.displayName}${ + functionItem.overloadIndex && functionItem.overloadIndex > 1 ? `:${functionItem.overloadIndex}` : '' + }:/`; break; default: path += `${item.displayName}/`;