Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ node_modules
out
*.generated.*
/.cache
/pages/api
/pages/loaders
/pages/plugins
/pages/docs/api
/pages/docs/loaders
/pages/docs/plugins
/generated
/pages/about/governance
4 changes: 3 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ node_modules
out
*.generated.*
/.cache
/pages/api
/pages/docs/api
/pages/docs/loaders
/pages/docs/plugins
versions.json
/generated
4 changes: 3 additions & 1 deletion .stylelintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ node_modules
out
*.generated.*
/.cache
/pages/api
/pages/docs/api
/pages/docs/loaders
/pages/docs/plugins
versions.json
/generated
76 changes: 76 additions & 0 deletions components/MetaBar/index.jsx
Comment thread
avivkeller marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import MetaBar from '@node-core/ui-components/Containers/MetaBar';
import AvatarGroup from '@node-core/ui-components/Common/AvatarGroup';
import BaseButton from '@node-core/ui-components/Common/BaseButton';
import GitHubIcon from '@node-core/ui-components/Icons/Social/GitHub';

import { editURL } from '#theme/config';
import sponsors from '#theme/sponsors' with { type: 'json' };
import SponsorCard from '../Sponsors/Card/index.jsx';

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

const OC_URL = 'https://opencollective.com/webpack';

// Active recurring platinum-tier sponsors, ranked by monthly amount. There are
// only ever a handful, so the MetaBar features them as full expanded cards.
const platinumSponsors = sponsors.sponsors
.filter(sponsor => sponsor.monthly.tier === 'platinum')
.sort((a, b) => b.monthly.value - a.monthly.value);

export default ({ metadata, headings = [], readingTime }) => {
const editThisPage =
metadata.source ?? editURL.replace('{path}', metadata.path);
const authors = metadata.authors?.split(',').map(id => ({
image: `https://avatars.githubusercontent.com/${id.trim()}`,
url: `https://github.com/${id.trim()}`,
nickname: id,
}));

return (
<MetaBar
heading="Table of Contents"
headings={{ items: headings }}
items={{
'Reading Time': readingTime,
Comment thread
avivkeller marked this conversation as resolved.
...(CLIENT && authors?.length
? {
Authors: <AvatarGroup avatars={authors} as="a" limit={5} />,
}
: {}),
Contribute: (
<>
<GitHubIcon className="fill-neutral-700 dark:fill-neutral-100" />
<a href={editThisPage}>Edit this page</a>
</>
),
...(platinumSponsors.length
? {
[platinumSponsors.length > 1
? 'Featured Sponsors'
: 'Featured Sponsor']: (
<div className={styles.sponsors}>
{platinumSponsors.map(sponsor => (
<SponsorCard
key={sponsor.slug}
sponsor={sponsor}
size="sm"
showAmount={false}
/>
))}
<BaseButton
href={OC_URL}
target="_blank"
rel="noreferrer noopener"
kind="primary"
className={styles.becomeSponsor}
>
Become a sponsor
</BaseButton>
</div>
),
}
: {}),
}}
/>
);
};
12 changes: 12 additions & 0 deletions components/MetaBar/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@reference "../../styles/index.css";

.sponsors {
@apply flex
w-full
flex-col
gap-3;
}

.becomeSponsor {
@apply no-underline!;
}
32 changes: 17 additions & 15 deletions components/SideBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,28 @@ const redirect = url => (window.location.href = url);

const PrefetchLink = props => <a {...props} rel="prefetch" />;

const pathnameFor = path => path.replace(/\/index$/, '') || '/';
const pathnameFor = path => {
const clean = path.replace(/\/index$/, '');
if (!clean) return '/';
return clean.startsWith('/') ? clean : `/${clean}`;
};

const groupsFor = path => {
if (Array.isArray(sidebar)) return sidebar;
Comment thread
avivkeller marked this conversation as resolved.

const segment = path.split('/').filter(Boolean)[0];
const matched = sidebar.filter(g => g.groupName.toLowerCase() === segment);
return matched.length > 0 ? matched : sidebar;
return sidebar[segment] ?? [];
};

/**
* Sidebar component for MDX documentation with page navigation.
*/
export default ({ metadata }) => {
const path = pathnameFor(metadata.path);
return (
<SideBar
pathname={path}
groups={groupsFor(path)}
onSelect={redirect}
as={PrefetchLink}
title="Navigation"
/>
);
};
export default ({ metadata }) => (
<SideBar
pathname={pathnameFor(metadata.path)}
groups={groupsFor(metadata.path)}
onSelect={redirect}
as={PrefetchLink}
title="Navigation"
/>
);
10 changes: 8 additions & 2 deletions components/Sponsors/Card/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ const amountLabel = (sponsor, metric) =>
* sponsor: { name: string, slug: string, imageUrl: string|null, url: string, monthly: { value: number, tier: string|null }, allTime: { value: number, tier: string|null }, description?: string },
* size?: 'lg'|'md'|'sm'|'xs',
* metric?: 'monthly'|'allTime',
* showAmount?: boolean,
* }} props
*/
export default function SponsorCard({
sponsor,
size = 'md',
metric = 'monthly',
showAmount = true,
className,
...props
}) {
Expand Down Expand Up @@ -53,7 +55,11 @@ export default function SponsorCard({
<p className={styles.description}>{sponsor.description}</p>
)}
<div className={styles.footer}>
<span className={styles.amount}>{amountLabel(sponsor, metric)}</span>
{showAmount && (
<span className={styles.amount}>
{amountLabel(sponsor, metric)}
</span>
)}
<span className={styles.visit}>Visit &rarr;</span>
</div>
</a>
Expand All @@ -72,7 +78,7 @@ export default function SponsorCard({
</div>
<span className={styles.body}>
<span className={styles.name}>{sponsor.name}</span>
{size !== 'xs' && (
{size !== 'xs' && showAmount && (
<span className={styles.amount}>{amountLabel(sponsor, metric)}</span>
)}
</span>
Expand Down
3 changes: 2 additions & 1 deletion components/Sponsors/Card/index.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
border
border-neutral-200
bg-white
no-underline
no-underline!
transition-colors
duration-150
hover:border-blue-300
Expand Down Expand Up @@ -96,6 +96,7 @@
@apply ml-auto
text-xl
leading-none
font-normal
text-neutral-300
dark:text-neutral-600;
}
11 changes: 10 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ export default [
},
},
{
ignores: ['node_modules/', 'out/', '.cache/', 'webpack/', 'pages/api'],
ignores: [
'node_modules/',
'out/',
'.cache/',
'webpack/',
'pages/api',
'pages/docs/api',
'pages/docs/loaders',
'pages/docs/plugins',
],
},
];
30 changes: 30 additions & 0 deletions layouts/PartialArticle/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import NavBar from '../../components/NavBar.jsx';
import Footer from '../../components/Footer/index.jsx';
import SideBar from '../../components/SideBar.jsx';

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

/**
* Article shell with a sidebar but no metabar. Mirrors the default Layout's
* navigation/sidebar/footer chrome while leaving the main column free for
* custom page content (e.g. the Sponsors page).
*
* @param {{ metadata: object, children: import('preact').ComponentChildren }} props
*/
export default function PartialArticle({ metadata, children }) {
return (
<>
<NavBar metadata={metadata} />

<div className={styles.shell}>
<aside className={styles.sidebar}>
<SideBar metadata={metadata} />
</aside>

<main className={styles.page}>{children}</main>
</div>

<Footer />
</>
);
}
24 changes: 24 additions & 0 deletions layouts/PartialArticle/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@reference "../../styles/index.css";

.shell {
@apply block
w-full
min-[896px]:grid
min-[896px]:grid-cols-[--spacing(56)_1fr];
}

.sidebar {
@apply min-[896px]:sticky
min-[896px]:top-0
min-[896px]:grid
min-[896px]:h-svh
min-[896px]:overflow-y-auto;
}

.page {
@apply min-w-0
bg-white
text-neutral-900
dark:bg-neutral-950
dark:text-white;
}
Loading