Skip to content

Commit 8b4e140

Browse files
authored
chore: add partner section on homepage
1 parent 77f675c commit 8b4e140

22 files changed

Lines changed: 688 additions & 0 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@reference "../../../../styles/index.css";
2+
3+
.partnerIcon {
4+
@apply h-12
5+
w-auto
6+
p-2;
7+
8+
svg {
9+
@apply h-7
10+
w-auto;
11+
}
12+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 Link from '#site/components/Link';
8+
import style from './index.module.css';
9+
import Button from '../../Button';
10+
11+
type ParnetsIconProps = Partners & ComponentProps<typeof Skeleton>;
12+
13+
const PartnersIcon: FC<ParnetsIconProps> = ({ href, logo, loading }) => {
14+
return (
15+
<Skeleton loading={loading} className="h-12 w-12 p-1">
16+
<Button kind="secondary" href={href} className={style.partnerIcon}>
17+
{cloneElement(logo, {
18+
className: 'h-7 w-auto',
19+
width: 'auto',
20+
height: '28px',
21+
})}
22+
</Button>
23+
</Skeleton>
24+
);
25+
};
26+
27+
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-4;
9+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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+
// Depediendo del ancho que tiene el componente tiene que renderizar un número diferente de sponsors, tiene que se dinamico cada sponsor tiene un ancho de 24x24 pixeles
28+
// crear una función que reciba el ancho del componente y retorne un número de sponsors a renderizar
29+
// const getSponsorsToRender = (width: number) => {
30+
31+
useEffect(() => {
32+
// We intentionally render the initial default "mock" list of sponsors
33+
// to have the Skeletons loading, and then we render the actual list
34+
// after an enough amount of time has passed to give a proper sense of Animation
35+
// We do this client-side effect, to ensure that a random-amount of sponsors is renderered
36+
// on every page load. Since our page is natively static, we need to ensure that
37+
// on the client-side we have a random amount of sponsors rendered.
38+
// Although whilst we are deployed on Vercel or other environment that supports ISR
39+
// (Incremental Static Generation) whose would invalidate the cache every 5 minutes
40+
// We want to ensure that this feature is compatible on a full-static environment
41+
const renderSponsorsAnimation = setTimeout(() => {
42+
initialRenderer.current = false;
43+
44+
setSeedList(randomPartnerList(PARTNERS, maxLength, 1, categories));
45+
}, 0);
46+
47+
return () => clearTimeout(renderSponsorsAnimation);
48+
// We only want this to run once on initial render
49+
// We don't really care if the props change as realistically they shouldn't ever
50+
// eslint-disable-next-line react-hooks/exhaustive-deps
51+
}, []);
52+
53+
return (
54+
<div className={style.partnersIconList}>
55+
{seedList.map((partner, index) => (
56+
<PartnerIcon
57+
{...partner}
58+
key={index}
59+
loading={initialRenderer.current}
60+
/>
61+
))}
62+
</div>
63+
);
64+
};
65+
66+
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/next.mdx.use.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import BadgeGroup from '@node-core/ui-components/Common/BadgeGroup';
44

5+
import PartnersIconList from './components/Common/Partners/PartnersIconList';
56
import DownloadReleasesTable from './components/Downloads/DownloadReleasesTable';
67
import UpcomingMeetings from './components/MDX/Calendar/UpcomingMeetings';
78
import WithBadgeGroup from './components/withBadgeGroup';
@@ -21,6 +22,7 @@ export const mdxComponents = {
2122
WithBanner,
2223
// HOC for providing Badge Data
2324
WithBadgeGroup,
25+
PartnersIconList,
2426
// Standalone Badge Group
2527
BadgeGroup,
2628
// Renders an container for Upcoming Node.js Meetings
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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,13 @@ layout: home
137137
138138
</div>
139139
Learn more what Node.js is able to offer with our [Learning materials](/learn).
140+
<div className='w-full flex flex-col gap-2 xs:mt-3'>
141+
<div className="flex flex-row items-center justify-center gap-4">
142+
<PartnersIconList />
143+
<Link href="/about/partners" className="min-w-max">and more...</Link>
144+
</div>
145+
<span className="xs:mt-1 text-center">
146+
Node.js is proudly supported by our partners above
147+
</span>
148+
</div>
140149
</section>

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';

apps/site/types/partners.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// import type { ReactElement, SVGProps } from 'react';
2+
3+
import type { ReactElement, SVGProps } from 'react';
4+
5+
export interface Partners {
6+
id: string;
7+
// The name of the partner
8+
name: string;
9+
// A logo to render on the partners page
10+
logo: ReactElement<SVGProps<SVGSVGElement>>;
11+
// The promoted link to their website or social media
12+
href: string;
13+
// The categories this partner belongs to
14+
categories: Array<PartnerCategory>;
15+
// An optional description of the partner
16+
description?: string;
17+
threshold: number;
18+
}
19+
20+
export type PartnerCategory = 'infrastructure' | 'security' | 'esp partner';

0 commit comments

Comments
 (0)