|
| 1 | +import { type ReactNode } from 'react'; |
| 2 | +import { |
| 3 | + useDocById, |
| 4 | + findFirstSidebarItemLink, |
| 5 | +} from '@docusaurus/plugin-content-docs/client'; |
| 6 | +import { usePluralForm } from '@docusaurus/theme-common'; |
| 7 | +import isInternalUrl from '@docusaurus/isInternalUrl'; |
| 8 | +import { translate } from '@docusaurus/Translate'; |
| 9 | + |
| 10 | +import type { |
| 11 | + PropSidebarItemCategory, |
| 12 | + PropSidebarItemLink, |
| 13 | +} from '@docusaurus/plugin-content-docs'; |
| 14 | + |
| 15 | +import { EuiCard, EuiIcon } from '@elastic/eui'; |
| 16 | + |
| 17 | +function useCategoryItemsPlural() { |
| 18 | + const { selectMessage } = usePluralForm(); |
| 19 | + return (count: number) => |
| 20 | + selectMessage( |
| 21 | + count, |
| 22 | + translate( |
| 23 | + { |
| 24 | + message: '1 item|{count} items', |
| 25 | + id: 'theme.docs.DocCard.categoryDescription.plurals', |
| 26 | + description: |
| 27 | + 'The default description for a category card in the generated index about how many items this category includes', |
| 28 | + }, |
| 29 | + { count } |
| 30 | + ) |
| 31 | + ); |
| 32 | +} |
| 33 | + |
| 34 | +function CardLayout({ |
| 35 | + href, |
| 36 | + icon, |
| 37 | + title, |
| 38 | + description, |
| 39 | +}: { |
| 40 | + href: string; |
| 41 | + icon: string; |
| 42 | + title: string; |
| 43 | + description?: string; |
| 44 | +}): ReactNode { |
| 45 | + return ( |
| 46 | + <EuiCard |
| 47 | + icon={<EuiIcon size="l" type={icon} />} |
| 48 | + title={title} |
| 49 | + description={description || ''} |
| 50 | + titleSize="xs" |
| 51 | + layout="horizontal" |
| 52 | + href={href} |
| 53 | + /> |
| 54 | + ); |
| 55 | +} |
| 56 | + |
| 57 | +function CardCategory({ item }: { item: PropSidebarItemCategory }): ReactNode { |
| 58 | + const href = findFirstSidebarItemLink(item); |
| 59 | + const categoryItemsPlural = useCategoryItemsPlural(); |
| 60 | + |
| 61 | + // Unexpected: categories that don't have a link have been filtered upfront |
| 62 | + if (!href) { |
| 63 | + return null; |
| 64 | + } |
| 65 | + |
| 66 | + return ( |
| 67 | + <CardLayout |
| 68 | + href={href} |
| 69 | + // Coincidentally, `folderOpen` is the same icon in EUI icon library |
| 70 | + icon="folderOpen" |
| 71 | + title={item.label} |
| 72 | + description={item.description ?? categoryItemsPlural(item.items.length)} |
| 73 | + /> |
| 74 | + ); |
| 75 | +} |
| 76 | + |
| 77 | +function CardLink({ item }: { item: PropSidebarItemLink }): ReactNode { |
| 78 | + // We update Docusaurus `link` icon to EUI `popout` icon |
| 79 | + const icon = isInternalUrl(item.href) ? 'document' : 'popout'; |
| 80 | + const doc = useDocById(item.docId ?? undefined); |
| 81 | + return ( |
| 82 | + <CardLayout |
| 83 | + href={item.href} |
| 84 | + icon={icon} |
| 85 | + title={item.label} |
| 86 | + description={item.description ?? doc?.description} |
| 87 | + /> |
| 88 | + ); |
| 89 | +} |
| 90 | + |
| 91 | +export default function DocCard({ |
| 92 | + item, |
| 93 | +}: { |
| 94 | + item: PropSidebarItemCategory | PropSidebarItemLink; |
| 95 | +}): ReactNode { |
| 96 | + switch (item.type) { |
| 97 | + case 'link': |
| 98 | + return <CardLink item={item} />; |
| 99 | + case 'category': |
| 100 | + return <CardCategory item={item} />; |
| 101 | + default: |
| 102 | + throw new Error(`unknown item type ${JSON.stringify(item)}`); |
| 103 | + } |
| 104 | +} |
0 commit comments