Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.
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
17 changes: 12 additions & 5 deletions src/components/GitHubStarCount.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use client'

import { getStarGazers } from '@/lib/stargazers'
import clsx from 'clsx'
import React, { useEffect, useState } from 'react'
import React, { FC, useEffect, useState } from 'react'

const STAR_COUNT_KEY = 'nitric_github_star_count'
const STAR_COUNT_TIMESTAMP_KEY = 'nitric_github_star_count_timestamp'
Expand All @@ -18,10 +19,16 @@ const formatStarCount = (count: number) => {
return count.toString()
}

const DEFAULT_STAR_COUNT = 1150
interface GitHubStarCountProps {
className: string
defaultStarCount: number
}

const GitHubStarCount = ({ className }: { className: string }) => {
const [starCount, setStarCount] = useState(DEFAULT_STAR_COUNT)
const GitHubStarCount: FC<GitHubStarCountProps> = ({
className,
defaultStarCount,
}) => {
const [starCount, setStarCount] = useState(defaultStarCount)

useEffect(() => {
const fetchStarCount = async () => {
Expand All @@ -35,7 +42,7 @@ const GitHubStarCount = ({ className }: { className: string }) => {
cachedTimestamp &&
currentTime - parseInt(cachedTimestamp) < CACHE_DURATION &&
// Ensure cached value is at least the new default value
parseInt(cachedStarCount) >= DEFAULT_STAR_COUNT
parseInt(cachedStarCount) >= defaultStarCount
) {
setStarCount(JSON.parse(cachedStarCount))
} else {
Expand Down
12 changes: 4 additions & 8 deletions src/components/layout/BaseLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import Link from 'next/link'

import { Footer } from '@/components/Footer'
import { Header } from '@/components/layout/Header'
import { Logo } from '@/components/Logo'
import { Navigation } from '@/components/nav/Navigation'
import { getStarGazers } from '@/lib/stargazers'

const experimentalRuntimes = ['go', 'dart']

const v0Runtimes = ['csharp', 'jvm']
export async function BaseLayout({ children }: React.PropsWithChildren) {
const defaultStarCount = await getStarGazers()

export function BaseLayout({ children }: React.PropsWithChildren) {
return (
<div className="w-full lg:ml-72 xl:ml-80">
<header className="contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex">
<div className="contents lg:pointer-events-auto lg:mt-[calc(var(--header-height))] lg:block lg:w-72 lg:overflow-y-auto lg:border-r lg:border-zinc-900/10 lg:px-6 lg:pb-8 lg:pt-4 lg:dark:border-white/10 xl:w-80">
<Header />
<Header defaultStarCount={defaultStarCount} />

<Navigation className="hidden lg:block" />
</div>
Expand Down
151 changes: 80 additions & 71 deletions src/components/layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,83 +43,92 @@ function TopLevelNavItem({
)
}

export const Header = forwardRef<
React.ElementRef<'div'>,
React.ComponentPropsWithoutRef<typeof motion.div>
>(function Header({ className, ...props }, ref) {
let { isOpen: mobileNavIsOpen } = useMobileNavigationStore()
let isInsideMobileNavigation = useIsInsideMobileNavigation()
interface HeaderProps
extends React.ComponentPropsWithoutRef<typeof motion.div> {
defaultStarCount?: number
}

let { scrollY } = useScroll()
let bgOpacityLight = useTransform(scrollY, [0, 72], [0.5, 0.9])
let bgOpacityDark = useTransform(scrollY, [0, 72], [0.2, 0.8])
export const Header = forwardRef<React.ElementRef<'div'>, HeaderProps>(
function Header({ className, defaultStarCount, ...props }, ref) {
let { isOpen: mobileNavIsOpen } = useMobileNavigationStore()
let isInsideMobileNavigation = useIsInsideMobileNavigation()

return (
<motion.div
ref={ref}
className={clsx(
className,
'fixed inset-x-0 top-0 z-50 flex h-14 items-center justify-between gap-12 px-4 transition sm:px-6 lg:z-30 lg:px-8',
!isInsideMobileNavigation && 'backdrop-blur-sm dark:backdrop-blur',
isInsideMobileNavigation
? 'bg-white dark:bg-background'
: 'bg-white/[var(--bg-opacity-light)] dark:bg-background/[var(--bg-opacity-dark)]',
)}
style={
{
'--bg-opacity-light': bgOpacityLight,
'--bg-opacity-dark': bgOpacityDark,
} as any
}
>
<div
let { scrollY } = useScroll()
let bgOpacityLight = useTransform(scrollY, [0, 72], [0.5, 0.9])
let bgOpacityDark = useTransform(scrollY, [0, 72], [0.2, 0.8])

return (
<motion.div
ref={ref}
className={clsx(
'absolute inset-x-0 top-full h-px transition',
(isInsideMobileNavigation || !mobileNavIsOpen) &&
'bg-gray-800/7.5 dark:bg-white/7.5',
className,
'fixed inset-x-0 top-0 z-50 flex h-14 items-center justify-between gap-12 px-4 transition sm:px-6 lg:z-30 lg:px-8',
!isInsideMobileNavigation && 'backdrop-blur-sm dark:backdrop-blur',
isInsideMobileNavigation
? 'bg-white dark:bg-background'
: 'bg-white/[var(--bg-opacity-light)] dark:bg-background/[var(--bg-opacity-dark)]',
)}
/>
<Link href="/" aria-label="Home" className="hidden lg:block">
<Logo className="h-6" />
</Link>

<div className="flex items-center gap-5 lg:hidden">
<MobileNavigation />
<Link href="/" aria-label="Home">
style={
{
'--bg-opacity-light': bgOpacityLight,
'--bg-opacity-dark': bgOpacityDark,
} as any
}
>
<div
className={clsx(
'absolute inset-x-0 top-full h-px transition',
(isInsideMobileNavigation || !mobileNavIsOpen) &&
'bg-gray-800/7.5 dark:bg-white/7.5',
)}
/>
<Link href="/" aria-label="Home" className="hidden lg:block">
<Logo className="h-6" />
</Link>
</div>
<div className="flex items-center gap-5">
<nav className="hidden lg:block">
<ul role="list" className="flex items-center gap-8">
<TopLevelNavItem
parentHref="/get-started/foundations"
href="/get-started/foundations/why-nitric"
>
Foundations
</TopLevelNavItem>
<TopLevelNavItem href="/guides">Guides</TopLevelNavItem>
<li>
<Search />
</li>

<TopLevelNavItem
className="group flex items-center"
rel="noreferrer noopener"
href="https://github.com/nitrictech/nitric"
>
<GitHubIcon className="dark:fill-gray h-5 w-5 fill-current" />
<GitHubStarCount className="ml-2 text-inherit" />
</TopLevelNavItem>
</ul>
</nav>
<div className="flex items-center gap-4">
<div className="lg:hidden">
<Search />
<div className="flex items-center gap-5 lg:hidden">
<MobileNavigation />
<Link href="/" aria-label="Home">
<Logo className="h-6" />
</Link>
</div>
<div className="flex items-center gap-5">
<nav className="hidden lg:block">
<ul role="list" className="flex items-center gap-8">
<TopLevelNavItem
parentHref="/get-started/foundations"
href="/get-started/foundations/why-nitric"
>
Foundations
</TopLevelNavItem>
<TopLevelNavItem href="/guides">Guides</TopLevelNavItem>
<li>
<Search />
</li>

<TopLevelNavItem
className="group flex items-center"
rel="noreferrer noopener"
href="https://github.com/nitrictech/nitric"
>
<GitHubIcon className="dark:fill-gray h-5 w-5 fill-current" />
{typeof defaultStarCount === 'number' && (
<GitHubStarCount
defaultStarCount={defaultStarCount}
className="ml-2 text-inherit"
/>
)}
</TopLevelNavItem>
</ul>
</nav>
<div className="flex items-center gap-4">
<div className="lg:hidden">
<Search />
</div>
<ThemeToggle />
</div>
<ThemeToggle />
</div>
</div>
</motion.div>
)
})
</motion.div>
)
},
)
21 changes: 21 additions & 0 deletions src/lib/stargazers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const DEFAULT_STARGAZERS = 1250

export const getStarGazers = async (): Promise<number> => {
try {
// on next < 15.x fetch will force-cache by default
const response = await fetch(
'https://api.github.com/repos/nitrictech/nitric',
)
if (!response.ok) {
console.error('Error fetching star count:', response.statusText)

return DEFAULT_STARGAZERS
}

const repoData = await response.json()
return repoData.stargazers_count
} catch (e) {
console.error('Error fetching star count:', e)
return DEFAULT_STARGAZERS
}
}
Loading