feat(nx-dev): github star widget

This commit is contained in:
Juri 2023-07-24 23:44:51 +02:00 committed by Juri Strumpflohner
parent 810b584c33
commit bdcec6de55
14 changed files with 203 additions and 43 deletions

View File

@ -4,7 +4,7 @@ import {
ProcessedDocument,
RelatedDocument,
} from '@nx/nx-dev/models-document';
import { Breadcrumbs, Footer } from '@nx/nx-dev/ui-common';
import { Breadcrumbs, Footer, GitHubStarWidget } from '@nx/nx-dev/ui-common';
import { renderMarkdown } from '@nx/nx-dev/ui-markdoc';
import { NextSeo } from 'next-seo';
import { useRouter } from 'next/router';
@ -15,9 +15,11 @@ import { collectHeadings, TableOfContents } from './table-of-contents';
export function DocViewer({
document,
relatedDocuments,
widgetData,
}: {
document: ProcessedDocument;
relatedDocuments: RelatedDocument[];
widgetData: { githubStarsCount: number };
}): JSX.Element {
const router = useRouter();
const hideTableOfContent =
@ -109,7 +111,13 @@ export function DocViewer({
elementRef={ref}
path={router.basePath}
headings={vm.tableOfContent}
/>
>
{widgetData.githubStarsCount > 0 && (
<GitHubStarWidget
starsCount={widgetData.githubStarsCount}
/>
)}
</TableOfContents>
</div>
)}
</div>

View File

@ -39,10 +39,12 @@ export function TableOfContents({
elementRef,
headings,
path,
children,
}: {
elementRef: any;
headings: Heading[];
path: string;
children: React.ReactNode;
}): JSX.Element {
const headingLevelTargets: number[] = [1, 2, 3]; // matching to: H1, H2, H3...
const items = headings.filter(
@ -60,32 +62,35 @@ export function TableOfContents({
);
return (
<nav className="toc">
<span className="pl-4 font-medium">On this page</span>
{!!items.length ? (
<ul className="mt-4 flex-col">
{items.map((item) => {
const href = `${path}#${item.id}`;
return (
<li key={item.title}>
<Link
href={href}
className={cx(
'block w-full border-l-4 border-slate-200 py-1 pl-3 transition hover:border-slate-500 dark:border-slate-700/40 dark:hover:border-slate-700',
{
'border-slate-500 bg-slate-50 dark:border-slate-700 dark:bg-slate-800/60':
activeId === item.id,
'pl-6': item.level === 3,
}
)}
>
{item.level === 1 ? 'Overview' : item.title}
</Link>
</li>
);
})}
</ul>
) : null}
</nav>
<>
<nav className="toc">
<span className="pl-4 font-medium">On this page</span>
{!!items.length ? (
<ul className="flex-col mt-4">
{items.map((item) => {
const href = `${path}#${item.id}`;
return (
<li key={item.title}>
<Link
href={href}
className={cx(
'block w-full border-l-4 border-slate-200 py-1 pl-3 transition hover:border-slate-500 dark:border-slate-700/40 dark:hover:border-slate-700',
{
'border-slate-500 bg-slate-50 dark:border-slate-700 dark:bg-slate-800/60':
activeId === item.id,
'pl-6': item.level === 3,
}
)}
>
{item.level === 1 ? 'Overview' : item.title}
</Link>
</li>
);
})}
</ul>
) : null}
</nav>
<div className="p-4">{children}</div>
</>
);
}

View File

@ -0,0 +1,33 @@
import { Octokit } from 'octokit';
let cachedGithubStarCountPromise: null | Promise<number> = null;
export async function fetchGithubStarCount() {
if (cachedGithubStarCountPromise !== null) {
// If the promise is in the cache, return it directly
return cachedGithubStarCountPromise;
}
cachedGithubStarCountPromise = (async () => {
try {
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const responseData = await octokit.request('GET /repos/{owner}/{repo}', {
owner: 'nrwl',
repo: 'nx',
headers: {
'X-GitHub-Api-Version': '2022-11-28',
},
retry: { enabled: false },
throttle: {
enabled: true,
},
});
return responseData.data.stargazers_count;
} catch (e) {
return 0; // fallback, will hide GitHub star widget
}
})();
return cachedGithubStarCountPromise;
}

View File

@ -1,7 +1,7 @@
import { getBasicNxSection } from '@nx/nx-dev/data-access-menu';
import { DocViewer } from '@nx/nx-dev/feature-doc-viewer';
import { ProcessedDocument, RelatedDocument } from '@nx/nx-dev/models-document';
import { Menu, MenuItem } from '@nx/nx-dev/models-menu';
import { MenuItem } from '@nx/nx-dev/models-menu';
import { DocumentationHeader, SidebarContainer } from '@nx/nx-dev/ui-common';
import { GetStaticPaths, GetStaticProps } from 'next';
import { useRouter } from 'next/router';
@ -10,15 +10,18 @@ import { menusApi } from '../lib/menus.api';
import { useNavToggle } from '../lib/navigation-toggle.effect';
import { nxDocumentationApi } from '../lib/nx.api';
import { tagsApi } from '../lib/tags.api';
import { fetchGithubStarCount } from '../lib/githubStars.api';
export default function NxDocumentation({
document,
menu,
relatedDocuments,
widgetData,
}: {
document: ProcessedDocument;
menu: MenuItem[];
relatedDocuments: RelatedDocument[];
widgetData: { githubStarsCount: number };
}) {
const router = useRouter();
const { toggleNav, navIsOpen } = useNavToggle();
@ -40,16 +43,8 @@ export default function NxDocumentation({
return () => router.events.off('routeChangeComplete', handleRouteChange);
}, [router, wrapperElement]);
const vm: {
document: ProcessedDocument;
menu: Menu;
relatedDocuments: RelatedDocument[];
} = {
document,
menu: {
sections: [getBasicNxSection(menu)],
},
relatedDocuments,
const menuWithSections = {
sections: [getBasicNxSection(menu)],
};
return (
@ -62,7 +57,7 @@ export default function NxDocumentation({
role="main"
className="flex h-full flex-1 overflow-y-hidden"
>
<SidebarContainer menu={vm.menu} navIsOpen={navIsOpen} />
<SidebarContainer menu={menuWithSections} navIsOpen={navIsOpen} />
<div
ref={wrapperElement}
id="wrapper"
@ -70,8 +65,9 @@ export default function NxDocumentation({
className="relative flex flex-grow flex-col items-stretch justify-start overflow-y-scroll"
>
<DocViewer
document={vm.document}
relatedDocuments={vm.relatedDocuments}
document={document}
relatedDocuments={relatedDocuments}
widgetData={widgetData}
/>
</div>
</main>
@ -100,6 +96,9 @@ export const getStaticProps: GetStaticProps = async ({
return {
props: {
document,
widgetData: {
githubStarsCount: await fetchGithubStarCount(),
},
relatedDocuments: tagsApi
.getAssociatedItemsFromTags(document.tags)
.filter((item) => item.path !== '/' + params.segments.join('/')), // Remove currently displayed item

View File

@ -10,15 +10,18 @@ import { menusApi } from '../../lib/menus.api';
import { useNavToggle } from '../../lib/navigation-toggle.effect';
import { nxPluginsApi } from '../../lib/plugins.api';
import { tagsApi } from '../../lib/tags.api';
import { fetchGithubStarCount } from '../../lib/githubStars.api';
export default function Pages({
document,
menu,
relatedDocuments,
widgetData,
}: {
document: ProcessedDocument;
menu: MenuItem[];
relatedDocuments: RelatedDocument[];
widgetData: { githubStarsCount: number };
}): JSX.Element {
const router = useRouter();
const { toggleNav, navIsOpen } = useNavToggle();
@ -72,6 +75,7 @@ export default function Pages({
<DocViewer
document={vm.document}
relatedDocuments={vm.relatedDocuments}
widgetData={widgetData}
/>
</div>
</main>
@ -98,6 +102,9 @@ export const getStaticProps: GetStaticProps = async ({
return {
props: {
document,
widgetData: {
githubStarsCount: await fetchGithubStarCount(),
},
relatedDocuments: tagsApi
.getAssociatedItemsFromTags(document.tags)
.filter((item) => item.path !== '/' + segments.join('/')), // Remove currently displayed item

View File

@ -10,15 +10,18 @@ import { menusApi } from '../../lib/menus.api';
import { useNavToggle } from '../../lib/navigation-toggle.effect';
import { nxPluginsApi } from '../../lib/plugins.api';
import { tagsApi } from '../../lib/tags.api';
import { fetchGithubStarCount } from '../../lib/githubStars.api';
export default function PluginsRoot({
document,
menu,
relatedDocuments,
widgetData,
}: {
document: ProcessedDocument;
menu: MenuItem[];
relatedDocuments: RelatedDocument[];
widgetData: { githubStarsCount: number };
}) {
const router = useRouter();
const { toggleNav, navIsOpen } = useNavToggle();
@ -72,6 +75,7 @@ export default function PluginsRoot({
<DocViewer
document={document}
relatedDocuments={vm.relatedDocuments}
widgetData={widgetData}
/>
</div>
</main>
@ -87,6 +91,9 @@ export const getStaticProps: GetStaticProps = async () => {
return {
props: {
document,
widgetData: {
githubStarsCount: await fetchGithubStarCount(),
},
menu: menusApi.getMenu('extending-nx', ''),
relatedDocuments: document.tags
.map((t) => tagsApi.getAssociatedItems(t))

View File

@ -13,15 +13,18 @@ import { nxCloudApi } from '../../lib/cloud.api';
import { menusApi } from '../../lib/menus.api';
import { useNavToggle } from '../../lib/navigation-toggle.effect';
import { tagsApi } from '../../lib/tags.api';
import { fetchGithubStarCount } from '../../lib/githubStars.api';
export default function Pages({
document,
menu,
relatedDocuments,
widgetData,
}: {
document: ProcessedDocument;
menu: MenuItem[];
relatedDocuments: RelatedDocument[];
widgetData: { githubStarsCount: number };
}) {
const router = useRouter();
const { toggleNav, navIsOpen } = useNavToggle();
@ -75,6 +78,7 @@ export default function Pages({
<DocViewer
document={vm.document}
relatedDocuments={vm.relatedDocuments}
widgetData={widgetData}
/>
</div>
</main>
@ -99,6 +103,9 @@ export const getStaticProps: GetStaticProps = async ({
return {
props: {
document,
widgetData: {
githubStarsCount: await fetchGithubStarCount(),
},
relatedDocuments: tagsApi
.getAssociatedItemsFromTags(document.tags)
.filter((item) => item.path !== '/' + segments.join('/')), // Remove currently displayed item

View File

@ -13,15 +13,18 @@ import { nxCloudApi } from '../../lib/cloud.api';
import { menusApi } from '../../lib/menus.api';
import { useNavToggle } from '../../lib/navigation-toggle.effect';
import { tagsApi } from '../../lib/tags.api';
import { fetchGithubStarCount } from '../../lib/githubStars.api';
export default function CloudRoot({
document,
menu,
relatedDocuments,
widgetData,
}: {
document: ProcessedDocument;
menu: MenuItem[];
relatedDocuments: RelatedDocument[];
widgetData: { githubStarsCount: number };
}) {
const router = useRouter();
const { toggleNav, navIsOpen } = useNavToggle();
@ -75,6 +78,7 @@ export default function CloudRoot({
<DocViewer
document={document}
relatedDocuments={vm.relatedDocuments}
widgetData={widgetData}
/>
</div>
</main>
@ -90,6 +94,9 @@ export const getStaticProps: GetStaticProps = async () => {
return {
props: {
document,
widgetData: {
githubStarsCount: await fetchGithubStarCount(),
},
menu: menusApi.getMenu('cloud', ''),
relatedDocuments: document.tags
.map((t) => tagsApi.getAssociatedItems(t))

View File

@ -13,16 +13,19 @@ import { menusApi } from '../../../../lib/menus.api';
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
import { nxPackagesApi } from '../../../../lib/packages.api';
import { tagsApi } from '../../../../lib/tags.api';
import { fetchGithubStarCount } from '../../../../lib/githubStars.api';
export default function PackageDocument({
document,
menu,
relatedDocuments,
widgetData,
}: {
document: ProcessedDocument;
menu: MenuItem[];
pkg: ProcessedPackageMetadata;
relatedDocuments: RelatedDocument[];
widgetData: { githubStarsCount: number };
}): JSX.Element {
const router = useRouter();
const { toggleNav, navIsOpen } = useNavToggle();
@ -79,6 +82,7 @@ export default function PackageDocument({
<DocViewer
document={vm.document}
relatedDocuments={vm.relatedDocuments}
widgetData={widgetData}
/>
</div>
</main>
@ -120,6 +124,9 @@ export async function getStaticProps({
props: {
pkg: nxPackagesApi.getPackage([params.name]),
document,
widgetData: {
githubStarsCount: await fetchGithubStarCount(),
},
relatedDocuments: tagsApi
.getAssociatedItemsFromTags(document.tags)
.filter((item) => item.path !== '/' + segments.join('/')), // Remove currently displayed item

View File

@ -11,16 +11,19 @@ import { menusApi } from '../../../../lib/menus.api';
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
import { content } from '../../../../lib/rspack/content/overview';
import { pkg } from '../../../../lib/rspack/pkg';
import { fetchGithubStarCount } from '../../../../lib/githubStars.api';
export default function Overview({
document,
menu,
relatedDocuments,
widgetData,
}: {
document: ProcessedDocument;
menu: MenuItem[];
pkg: ProcessedPackageMetadata;
relatedDocuments: RelatedDocument[];
widgetData: { githubStarsCount: number };
}): JSX.Element {
const router = useRouter();
const { toggleNav, navIsOpen } = useNavToggle();
@ -77,6 +80,7 @@ export default function Overview({
<DocViewer
document={vm.document}
relatedDocuments={vm.relatedDocuments}
widgetData={widgetData}
/>
</div>
</main>
@ -99,6 +103,9 @@ export async function getStaticProps() {
props: {
pkg,
document,
widgetData: {
githubStarsCount: await fetchGithubStarCount(),
},
relatedDocuments: [],
menu: menusApi.getMenu('packages', ''),
},

View File

@ -11,16 +11,19 @@ import { menusApi } from '../../../../lib/menus.api';
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
import { content } from '../../../../lib/rspack/content/rspack-config-setup';
import { pkg } from '../../../../lib/rspack/pkg';
import { fetchGithubStarCount } from '../../../../lib/githubStars.api';
export default function RspackConfigSetup({
document,
menu,
relatedDocuments,
widgetData,
}: {
document: ProcessedDocument;
menu: MenuItem[];
pkg: ProcessedPackageMetadata;
relatedDocuments: RelatedDocument[];
widgetData: { githubStarsCount: number };
}): JSX.Element {
const router = useRouter();
const { toggleNav, navIsOpen } = useNavToggle();
@ -77,6 +80,7 @@ export default function RspackConfigSetup({
<DocViewer
document={vm.document}
relatedDocuments={vm.relatedDocuments}
widgetData={widgetData}
/>
</div>
</main>
@ -100,6 +104,9 @@ export async function getStaticProps() {
props: {
pkg,
document,
widgetData: {
githubStarsCount: await fetchGithubStarCount(),
},
relatedDocuments: [],
menu: menusApi.getMenu('packages', ''),
},

View File

@ -11,16 +11,19 @@ import { menusApi } from '../../../../lib/menus.api';
import { useNavToggle } from '../../../../lib/navigation-toggle.effect';
import { content } from '../../../../lib/rspack/content/rspack-plugin';
import { pkg } from '../../../../lib/rspack/pkg';
import { fetchGithubStarCount } from '../../../../lib/githubStars.api';
export default function RspackPlugins({
document,
menu,
relatedDocuments,
widgetData,
}: {
document: ProcessedDocument;
menu: MenuItem[];
pkg: ProcessedPackageMetadata;
relatedDocuments: RelatedDocument[];
widgetData: { githubStarsCount: number };
}): JSX.Element {
const router = useRouter();
const { toggleNav, navIsOpen } = useNavToggle();
@ -77,6 +80,7 @@ export default function RspackPlugins({
<DocViewer
document={vm.document}
relatedDocuments={vm.relatedDocuments}
widgetData={widgetData}
/>
</div>
</main>
@ -99,6 +103,9 @@ export async function getStaticProps() {
props: {
pkg,
document,
widgetData: {
githubStarsCount: await fetchGithubStarCount(),
},
relatedDocuments: [],
menu: menusApi.getMenu('packages', ''),
},

View File

@ -13,3 +13,4 @@ export * from './lib/nx-users-showcase';
export * from './lib/plugin-card';
export * from './lib/testimonial-card';
export * from './lib/typography';
export * from './lib/github-star-widget';

View File

@ -0,0 +1,58 @@
import { sendCustomEvent } from '@nx/nx-dev/feature-analytics';
const GithubIcon = (props: any) => {
return (
<svg
fill="currentColor"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
{...props}
>
{/*<title>GitHub</title>*/}
<path d="M8 0a8 8 0 0 0-2.53 15.59c.4.07.55-.17.55-.38l-.01-1.49c-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82a7.42 7.42 0 0 1 4 0c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48l-.01 2.2c0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8a8 8 0 0 0-8-8z" />
</svg>
);
};
export function GitHubStarWidget({ starsCount }: { starsCount: number }) {
const formatStars = (count: number) => {
if (count >= 1000) {
return (count / 1000).toFixed(1) + 'k';
} else {
return count;
}
};
const handleClick = (eventAction: string) => {
sendCustomEvent(
eventAction,
'githubstars-toc-sidebar',
'githubstarswidget'
);
};
return (
<div className="flex items-center justify-between pl-2 pr-2 space-x-2 border rounded-md border-slate-200 hover:border-slate-400 dark:border-slate-700 print:hidden">
<a
href="https://github.com/nrwl/nx"
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-between space-x-2"
onClick={() => handleClick('githubstars_iconclick')}
>
<GithubIcon className="w-6 h-6" />
<span className="font-semibold text-md">{formatStars(starsCount)}</span>
</a>
<a
href="https://github.com/nrwl/nx"
target="_blank"
rel="noreferrer noopener"
className="px-4 py-2 border-transparent hover:text-slate-900 dark:hover:text-sky-400 whitespace-nowrap font-bold"
onClick={() => handleClick('githubstars_buttonclick')}
>
Give us a Star!
</a>
</div>
);
}