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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-icons": "^5.5.0",
"react-slot-counter": "^3.3.1",
"rehype-katex": "^7.0.1",
"remark-math": "^6.0.0",
"styled-components": "^6.1.18",
Expand Down
95 changes: 92 additions & 3 deletions src/components/Community/LandingCommunity.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
max-width: 100%;
margin-bottom: 1rem;
padding: auto 1rem;
flex-wrap: wrap;
}

.landing-community .landing-community__header .landing-community__title {
Expand All @@ -26,6 +27,12 @@
text-shadow: 0 0 1px var(--ifm-color-primary);
}

.landing-community .landing-community__header .landing-community__error {
color: var(--ifm-color-warning);
font-size: 0.9rem;
margin-top: 0.5rem;
}

.landing-community .landing-community__content {
display: grid;
grid-template-columns: 1fr 1fr;
Expand All @@ -48,6 +55,23 @@
border-radius: 1rem;
background-color: var(--ifm-color-background);
box-shadow: 0 0 1px var(--ifm-color-primary);
transition: all 0.3s ease;
position: relative;
}

.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item.clickable {
cursor: pointer;
}

.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item.clickable:hover,
.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item.clickable:focus {
transform: scale(1.02);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
outline: none;
}

.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item.loading {
opacity: 0.7;
}

.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item:hover {
Expand All @@ -62,6 +86,49 @@
color: var(--ifm-color-primary);
text-shadow: 0 0 1px var(--ifm-color-primary);
padding-bottom: 0.5rem;
position: relative;
}

/* SlotCounter styling */
.landing-community .slot-counter-number {
font-size: inherit;
font-weight: inherit;
color: inherit;
text-shadow: inherit;
}

.landing-community .slot-counter-separator {
font-size: inherit;
font-weight: inherit;
color: inherit;
text-shadow: inherit;
}

.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item .landing-community__loading {
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
}

.landing-community .loading-spinner {
animation: spin 2s linear infinite;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

.landing-community .external-link-icon {
font-size: 0.8em;
margin-left: 0.3rem;
opacity: 0.7;
transition: opacity 0.2s ease;
}

.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item.clickable:hover .external-link-icon {
opacity: 1;
}

.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item .landing-community__stat-description {
Expand All @@ -75,12 +142,19 @@
border-radius: 1rem;
background-color: var(--ifm-color-background);
box-shadow: 0 0 1px var(--ifm-color-primary);
transition: all 0.3s ease;
position: relative;
}

.landing-community .landing-community__content .landing-community__info:hover {
.landing-community .landing-community__content .landing-community__info.clickable {
cursor: pointer;
transform: scale(1.01);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.4);
}

.landing-community .landing-community__content .landing-community__info.clickable:hover,
.landing-community .landing-community__content .landing-community__info.clickable:focus {
transform: scale(1.01);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
outline: none;
}

.landing-community .landing-community__content .landing-community__info .landing-community__image {
Expand All @@ -100,12 +174,27 @@
color: var(--ifm-color-primary);
text-shadow: 0 0 1px var(--ifm-color-primary);
text-decoration: none;
font-weight: 600;
}

.landing-community .landing-community__content .landing-community__info .landing-community__info-text .landing-community__link:hover {
text-decoration: underline;
}

.landing-community .external-link-indicator {
display: flex;
align-items: center;
justify-content: center;
margin-top: 0.5rem;
gap: 0.5rem;
opacity: 0.7;
transition: opacity 0.2s ease;
}

.landing-community .landing-community__content .landing-community__info.clickable:hover .external-link-indicator {
opacity: 1;
}

@media screen and (max-width: 768px) {
.landing-community .landing-community__content {
grid-template-columns: 1fr;
Expand Down
172 changes: 101 additions & 71 deletions src/components/Community/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { type FC, useEffect, useState, useMemo } from "react";
import SlotCounter from 'react-slot-counter';
import "./LandingCommunity.css";
import { useCommunityStatsContext } from "@site/src/lib/statsProvider";

Expand All @@ -7,121 +8,150 @@ type Props = {
};

export const LandingCommunity: FC<Props> = ({ className }) => {
const { githubStarCountText, githubContributorsCount, githubForksCount } = useCommunityStatsContext();
const [state, setState] = useState({
stat0: 0,
stat1: 0,
stat2: 0,
stat3: 0,
});

const {
githubStarCountText,
githubContributorsCountText,
githubForksCountText,
githubReposCountText,
githubStarCount,
githubContributorsCount,
githubForksCount,
githubReposCount,
loading,
error
} = useCommunityStatsContext();


const generateList = useMemo(() => [
{
stat: githubStarCountText,
description: "Stars on our GitHub repository, showcase the support and contribution, we recieved from the community.",
href: "https://github.com/recodehive",
// https://github.com/CodeHarborHub/codeharborhub.github.io/stargazers
stat: githubStarCount,
statText: githubStarCountText,
description: "Stars across all our GitHub repositories, showcasing the support and appreciation from the community.",
href: "https://github.com/recodehive/Support",
label: "GitHub Stars"
},
{
stat: 20,
description: "Live projects on recodehive, demonstrating the power of open-source collaboration.",
stat: githubReposCount,
statText: githubReposCountText,
description: "Live public projects on RecodHive, demonstrating the power of open-source collaboration.",
href: "https://github.com/orgs/recodehive/repositories?q=visibility%3Apublic+archived%3Afalse",
label: "Public Repositories"
},
{
stat: githubContributorsCount,
description: "List of Contributors who have made our repository better.",
href: "https://github.com/recodehive",
// https://github.com/CodeHarborHub/codeharborhub.github.io/graphs/contributors
statText: githubContributorsCountText,
description: "Amazing contributors who have made our repositories better and helped build our community.",
href: "https://github.com/orgs/recodehive/people",
label: "Contributors"
},
{
stat: githubForksCount,
description: "Forks of our repository, showing how our community extends our work.",
href: "https://github.com/recodehive",

//https://github.com/CodeHarborHub/codeharborhub.github.io/network/members
statText: githubForksCountText,
description: "Forks of our repositories, showing how our community extends and builds upon our work.",
href: "https://github.com/orgs/recodehive/discussions",
label: "Community Forks"
},
], [githubStarCountText, githubContributorsCount, githubForksCount]);
], [githubStarCount, githubStarCountText, githubReposCount, githubReposCountText, githubContributorsCount, githubContributorsCountText, githubForksCount, githubForksCountText]);

const handleDynamicChange = (target: number, index: number) => {
let count = 0;
const increment = target / 100;
const interval = setInterval(() => {
count += increment;
setState(prev => ({ ...prev, [`stat${index}`]: Math.round(count) }));
if (count >= target) {
setState(prev => ({ ...prev, [`stat${index}`]: target }));
clearInterval(interval);
}
}, 20);
const handleCardClick = (href: string) => {
if (href) {
window.open(href, '_blank', 'noopener,noreferrer');
}
};

useEffect(() => {
generateList.forEach((item, index) => {
handleDynamicChange(Number(item.stat), index);
});
}, [generateList]);

return (
<div className={`landing-community ${className || ""}`}>
<div className="landing-community__header">
<h2 className="landing-community__title">
Discover the strength of our{" "}
<span className="landing-community__highlight">amazing community</span>.
</h2>
{error && (
<div className="landing-community__error">
<small>⚠️ Stats may be cached or incomplete</small>
</div>
)}
</div>

<div className="landing-community__content">
<div className="landing-community__stats">
{generateList.map((item, index) => (
<span key={index} className="landing-community__stat-item">
<div
key={index}
className={`landing-community__stat-item ${item.href ? 'clickable' : ''} ${loading ? 'loading' : ''}`}
onClick={() => handleCardClick(item.href)}
role={item.href ? "button" : "presentation"}
tabIndex={item.href ? 0 : -1}
onKeyDown={(e) => {
if (item.href && (e.key === 'Enter' || e.key === ' ')) {
e.preventDefault();
handleCardClick(item.href);
}
}}
title={item.href ? `Click to visit ${item.label}` : item.label}
>
<div className="landing-community__stat-value">
{item.href ? (
<a
href={item.href}
target="_blank"
rel="noopener noreferrer"
>
{`${state[`stat${index}`]}${index !== 1 ? "" : ""}`}
</a>
{loading ? (
<div className="landing-community__loading">
<span className="loading-spinner">⏳</span>
</div>
) : (
`${state[`stat${index}`]}`
<span>
<SlotCounter
value={item.stat}
duration={2}
animateOnVisible={{
triggerOnce: true,
rootMargin: '0px 0px -100px 0px'
}}
numberSlotClassName="slot-counter-number"
separatorClassName="slot-counter-separator"
/>
{item.href && <span className="external-link-icon">↗</span>}
</span>
)}
</div>
<div className="landing-community__stat-description">
{item.description}
</div>
</span>
</div>
))}
</div>

<div className="landing-community__info">
<div
className="landing-community__info clickable"
onClick={() => handleCardClick("https://github.com/recodehive")}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleCardClick("https://github.com/recodehive");
}
}}
title="Click to visit RecodHive GitHub Organization"
>
<img
className="landing-community__image"
src="/community.png"
alt="team collaboration"
loading="lazy"
/>
<div className="landing-community__info-text">
Our developers are the core of Hive community. We take pride in
Our developers are the core of RecodHive community. We take pride in
our{" "}
<a
href="https://github.com/recodehive"
//https://github.com/CodeHarborHub/codeharborhub.github.io/graphs/contributors
target="_blank"
rel="noopener noreferrer"
className="landing-community__link"
>
GitHub community
</a>{" "}
with over{" "}
<a
href="https://github.com/recodehive"
target="_blank"
rel="noopener noreferrer"
className="landing-community__link"
>
500+ contributors
</a>{" "}
powering recodehive.
<span className="landing-community__link">
GitHub organization
</span>{" "}
with amazing{" "}
<span className="landing-community__link">
contributors and maintainers
</span>{" "}
powering RecodHive's growth.
<div className="external-link-indicator">
<span className="external-link-icon">↗</span>
<small>Click to explore our GitHub</small>
</div>
</div>
</div>
</div>
Expand Down
Loading