The Node.js® Website
1import type { Heading } from '@vcarl/remark-headings';
2import { useTranslations } from 'next-intl';
3import { Fragment, useMemo } from 'react';
4import type { FC } from 'react';
5
6import Link from '@/components/Link';
7
8import styles from './index.module.css';
9
10type MetaBarProps = {
11 items: Record<string, React.ReactNode>;
12 headings?: {
13 items: Array<Heading>;
14 minDepth?: number;
15 };
16};
17
18const MetaBar: FC<MetaBarProps> = ({ items, headings }) => {
19 const t = useTranslations();
20
21 // The default depth of headings to display in the table of contents.
22 const { minDepth = 2, items: headingItems = [] } = headings || {};
23
24 const heading = useMemo(
25 () => headingItems.filter(({ depth }) => depth >= minDepth && depth <= 4),
26 [minDepth, headingItems]
27 );
28
29 return (
30 <div className={styles.wrapper}>
31 <dl>
32 {Object.entries(items)
33 .filter(([, value]) => !!value)
34 .map(([key, value]) => (
35 <Fragment key={key}>
36 <dt>{t(key)}</dt>
37 <dd>{value}</dd>
38 </Fragment>
39 ))}
40
41 {heading.length > 0 && (
42 <>
43 <dt>{t('components.metabar.tableOfContents')}</dt>
44 <dd>
45 <ol>
46 {heading.map(head => (
47 <li key={head.value}>
48 <Link href={`#${head?.data?.id}`}>{head.value}</Link>
49 </li>
50 ))}
51 </ol>
52 </dd>
53 </>
54 )}
55 </dl>
56 </div>
57 );
58};
59
60export default MetaBar;