Skip to content

Commit 33dcf64

Browse files
authored
chore: add partner section on homepage
1 parent 6b96719 commit 33dcf64

File tree

24 files changed

+687
-1
lines changed

24 files changed

+687
-1
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@reference "../../../../styles/index.css";
2+
3+
.partnerIcon {
4+
@apply h-9!
5+
w-auto!
6+
min-w-9!
7+
!p-2;
8+
9+
svg {
10+
@apply !h-4
11+
!w-auto;
12+
}
13+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Skeleton from '@node-core/ui-components/Common/Skeleton';
2+
import type { ComponentProps, FC } from 'react';
3+
import { cloneElement } from 'react';
4+
5+
import type { Partners } from '#site/types';
6+
7+
import style from './index.module.css';
8+
import Button from '../../Button';
9+
10+
type ParnetsIconProps = Partners & ComponentProps<typeof Skeleton>;
11+
12+
const PartnersIcon: FC<ParnetsIconProps> = ({ href, logo, loading }) => {
13+
return (
14+
<Skeleton loading={loading} className="h-9 w-9 p-2">
15+
<Button kind="secondary" href={href} className={style.partnerIcon}>
16+
{cloneElement(logo, {
17+
className: 'h-4 w-auto',
18+
width: 'auto',
19+
height: '16px',
20+
})}
21+
</Button>
22+
</Skeleton>
23+
);
24+
};
25+
26+
export default PartnersIcon;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@reference "../../../../styles/index.css";
2+
3+
.partnersIconList {
4+
@apply flex
5+
flex-row
6+
flex-wrap
7+
items-center
8+
gap-2;
9+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use client';
2+
3+
import { useEffect, useRef, useState, type FC } from 'react';
4+
5+
import PARTNERS from '#site/next.partners.constants';
6+
import type { PartnerCategory, Partners } from '#site/types';
7+
8+
import PartnerIcon from '../PartnerIcon';
9+
import style from './index.module.css';
10+
import { randomPartnerList } from '../utils';
11+
12+
type PartnersIconListProps = {
13+
maxLength?: number;
14+
categories?: PartnerCategory;
15+
};
16+
17+
const PartnersIconList: FC<PartnersIconListProps> = ({
18+
maxLength = 6,
19+
categories,
20+
}) => {
21+
const initialRenderer = useRef(true);
22+
23+
const [seedList, setSeedList] = useState<Array<Partners>>(
24+
PARTNERS.slice(0, maxLength)
25+
);
26+
27+
useEffect(() => {
28+
// We intentionally render the initial default "mock" list of sponsors
29+
// to have the Skeletons loading, and then we render the actual list
30+
// after an enough amount of time has passed to give a proper sense of Animation
31+
// We do this client-side effect, to ensure that a random-amount of sponsors is renderered
32+
// on every page load. Since our page is natively static, we need to ensure that
33+
// on the client-side we have a random amount of sponsors rendered.
34+
// Although whilst we are deployed on Vercel or other environment that supports ISR
35+
// (Incremental Static Generation) whose would invalidate the cache every 5 minutes
36+
// We want to ensure that this feature is compatible on a full-static environment
37+
const renderSponsorsAnimation = setTimeout(() => {
38+
initialRenderer.current = false;
39+
40+
setSeedList(randomPartnerList(PARTNERS, maxLength, 1, categories));
41+
}, 0);
42+
43+
return () => clearTimeout(renderSponsorsAnimation);
44+
// We only want this to run once on initial render
45+
// We don't really care if the props change as realistically they shouldn't ever
46+
// eslint-disable-next-line react-hooks/exhaustive-deps
47+
}, []);
48+
49+
return (
50+
<div className={style.partnersIconList}>
51+
{seedList.map((partner, index) => (
52+
<PartnerIcon
53+
{...partner}
54+
key={index}
55+
loading={initialRenderer.current}
56+
/>
57+
))}
58+
</div>
59+
);
60+
};
61+
62+
export default PartnersIconList;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { PartnerCategory, Partners } from '#site/types/partners.js';
2+
3+
function randomPartnerList(
4+
partners: Array<Partners>,
5+
pick = 4,
6+
dateSeed = 5,
7+
category?: PartnerCategory
8+
) {
9+
const now = new Date();
10+
const minutes = Math.floor(now.getUTCMinutes() / dateSeed) * dateSeed;
11+
12+
const fixedTime = new Date(
13+
Date.UTC(
14+
now.getUTCFullYear(),
15+
now.getUTCMonth(),
16+
now.getUTCDate(),
17+
now.getUTCHours(),
18+
minutes,
19+
0,
20+
0
21+
)
22+
);
23+
24+
// We create a seed from the rounded date (timestamp in ms)
25+
const seed = fixedTime.getTime();
26+
const rng = mulberry32(seed);
27+
28+
// Create a copy of the array to avoid modifying the original
29+
const shuffled = partners
30+
.filter(partner => !category || partner.categories.includes(category))
31+
.slice()
32+
.sort(() => rng() - 0.5);
33+
34+
return shuffled.slice(0, pick);
35+
}
36+
37+
// This function returns a random list of partners based on a fixed time seed
38+
39+
function mulberry32(seed: number) {
40+
return function () {
41+
let t = (seed += 0x6d2b79f5);
42+
t = Math.imul(t ^ (t >>> 15), t | 1);
43+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
44+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
45+
};
46+
}
47+
48+
export { randomPartnerList };

apps/site/navigation.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@
117117
"branding": {
118118
"link": "/about/branding",
119119
"label": "components.navigation.about.links.branding"
120+
},
121+
"partners": {
122+
"link": "/about/partners",
123+
"label": "components.navigation.about.links.partners"
120124
}
121125
}
122126
},

apps/site/next.mdx.use.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import BadgeGroup from '@node-core/ui-components/Common/BadgeGroup';
44

55
import Button from './components/Common/Button';
6+
import PartnersIconList from './components/Common/Partners/PartnersIconList';
67
import DownloadReleasesTable from './components/Downloads/DownloadReleasesTable';
78
import Link from './components/Link';
89
import LinkWithArrow from './components/LinkWithArrow';
@@ -24,6 +25,8 @@ export const mdxComponents = {
2425
WithBanner,
2526
// HOC for providing Badge Data
2627
WithBadgeGroup,
28+
// Shows a list of Node.js Partners
29+
PartnersIconList,
2730
// Standalone Badge Group
2831
BadgeGroup,
2932
// Renders an container for Upcoming Node.js Meetings

apps/site/next.partners.constants.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { Partners } from '#site/types';
2+
import { partnersList } from '#site/util/partners';
3+
import partners from '#site/util/partners/constants.json' with { type: 'json' };
4+
5+
const PARTNERS = partnersList(partners as Array<Omit<Partners, 'logo'>>);
6+
7+
export default PARTNERS as Array<Partners>;

apps/site/pages/en/index.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ layout: home
2626
<br />
2727
<small className="!text-xs">for Node.js 18 and below</small>
2828
</Button>
29+
30+
<div className="flex justify-center xs:mt-3">
31+
<PartnersIconList />
32+
</div>
33+
<span className="text-center text-sm! text-neutral-800 dark:text-neutral-600">Node.js is proudly supported by the partners above <Link href="/about/partners">and more</Link>.</span>
2934
</div>
3035

3136
</div>

apps/site/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export * from './redirects';
1010
export * from './server';
1111
export * from './github';
1212
export * from './calendar';
13+
export * from './partners';
1314
export * from './author';
1415
export * from './download';
1516
export * from './userAgent';

0 commit comments

Comments
 (0)