Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: MetaBar Component #6010

Merged
merged 11 commits into from
Oct 17, 2023
1 change: 1 addition & 0 deletions components/Common/AvatarGroup/index.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.avatarGroup {
@apply flex
h-8
items-center;
}
67 changes: 67 additions & 0 deletions components/Common/MetaBar/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
.wrapper {
@apply flex
w-80
flex-col
items-start
gap-8
border-l
border-neutral-200
p-6
dark:border-neutral-900;

dt {
@apply mb-2
text-sm
font-medium
text-neutral-800
dark:text-neutral-200;
}

dd {
@apply mb-8
flex
items-center
gap-2
text-sm
text-neutral-900
dark:text-white;

a {
@apply font-semibold
text-neutral-900
underline
hover:text-neutral-800
dark:text-white
dark:hover:text-neutral-200;
}

ol {
@apply flex
list-none
flex-col
gap-1.5
p-0;
}

svg {
@apply h-4
w-4
text-neutral-600
dark:text-neutral-400;
}

&:last-child {
@apply mb-0;
}
}

[data-on-dark] {
@apply hidden
dark:block;
}

[data-on-light] {
@apply block
dark:hidden;
}
}
99 changes: 99 additions & 0 deletions components/Common/MetaBar/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { CodeBracketIcon } from '@heroicons/react/24/outline';
import type { Meta as MetaObj, StoryObj } from '@storybook/react';
import Image from 'next/image';
import { FormattedMessage } from 'react-intl';

import AvatarGroup from '@/components/Common/AvatarGroup';
import LocalizedLink from '@/components/LocalizedLink';

import MetaBar from './index';

type Story = StoryObj<typeof MetaBar>;
type Meta = MetaObj<typeof MetaBar>;

export const Default: Story = {
args: {
items: {
ovflowd marked this conversation as resolved.
Show resolved Hide resolved
'components.metabar.lastUpdated': new Date().toLocaleDateString(),
'components.metabar.readingTime': '15 minutes',
'components.metabar.addedIn': 'v1.0.0',
'components.metabar.author': 'The Node.js Project',
'components.metabar.authors': (
<AvatarGroup
avatars={[
{
src: 'https://avatars.githubusercontent.com/canerakdas',
alt: 'Caner Akdas',
},
{
src: 'https://avatars.githubusercontent.com/bmuenzenmeyer',
alt: 'Brian Muenzenmeyer',
},
{
src: 'https://avatars.githubusercontent.com/ovflowd',
alt: 'Claudio W',
},
]}
/>
),
'components.metabar.contribute': (
<>
<Image
src="/static/images/logos/social-github-dark.svg"
alt="GitHub Logo"
width={16}
height={16}
data-on-light
/>
<Image
src="/static/images/logos/social-github.svg"
alt="GitHub Logo"
width={16}
height={16}
data-on-dark
/>
<LocalizedLink href="/contribute">
<FormattedMessage id="components.metabar.contributeText" />
</LocalizedLink>
</>
),
'components.metabar.viewAs': (
<>
<CodeBracketIcon />
<LocalizedLink href="/json">JSON</LocalizedLink>
</>
),
},
headings: {
items: [
{
value: 'OpenSSL update assessment, and Node.js project plans',
depth: 1,
data: { id: 'heading-1' },
},
{
value: 'Summary',
depth: 2,
data: { id: 'summary' },
},
{
value: 'Analysis',
depth: 2,
data: { id: 'analysis' },
},
{
value: 'The c_rehash script allows command injection (CVE-2022-2068)',
depth: 3,
data: { id: 'the_c_rehash' },
},
{
value: 'Contact and future updates',
depth: 3,
data: { id: 'contact_and_future_updates' },
},
],
},
},
};

export default { component: MetaBar } as Meta;
69 changes: 69 additions & 0 deletions components/Common/MetaBar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { Heading } from '@vcarl/remark-headings';
import { Fragment, useMemo } from 'react';
import type { FC } from 'react';
import { FormattedMessage } from 'react-intl';

import LocalizedLink from '@/components/LocalizedLink';

import styles from './index.module.css';

type MetaBarProps = {
items: Record<string, React.ReactNode>;
headings?: {
items: Heading[];
depth?: number;
};
};

// The default depth of headings to display in the table of contents.
const DEFAULT_HEADING_DEPTH: number = 2;

const MetaBar: FC<MetaBarProps> = ({
items,
headings = {
items: [],
ovflowd marked this conversation as resolved.
Show resolved Hide resolved
},
}) => {
const heading = useMemo(
() =>
headings.items.filter(
({ depth }) => depth === (headings.depth ?? DEFAULT_HEADING_DEPTH)
ovflowd marked this conversation as resolved.
Show resolved Hide resolved
),
[headings.depth, headings.items]
);

return (
<div className={styles.wrapper}>
<dl>
{Object.entries(items).map(([key, value]) => (
<Fragment key={key}>
<dt>
<FormattedMessage id={key} />
</dt>
<dd>{value}</dd>
</Fragment>
))}
{heading.length > 0 && (
<Fragment key="tableOfContents">
<dt>
<FormattedMessage id="components.metabar.tableOfContents" />
</dt>
<dd>
<ol>
{heading.map(head => (
<li key={head.value}>
<LocalizedLink href={`#${head?.data?.id || head.value}`}>
{head.value}
</LocalizedLink>
</li>
))}
</ol>
</dd>
</Fragment>
)}
</dl>
</div>
);
};

export default MetaBar;
9 changes: 9 additions & 0 deletions i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@
"components.common.crossLink.previous": "Prev",
"components.common.crossLink.next": "Next",
"components.common.codebox.copied": "Copied to clipboard!",
"components.metabar.lastUpdated": "Last Updated",
"components.metabar.readingTime": "Reading Time",
"components.metabar.addedIn": "Added In",
"components.metabar.author": "Author",
"components.metabar.authors": "Authors",
"components.metabar.contribute": "Contribute",
"components.metabar.contributeText": "Edit this page",
"components.metabar.viewAs": "View as",
"components.metabar.tableOfContents": "Table of Contents",
"layouts.blogPost.author.byLine": "{author, select, null {} other {By {author}, }}",
"layouts.blogIndex.currentYear": "News from {year}",
"components.api.jsonLink.title": "View as JSON",
Expand Down
Loading