import { CRITICALITY_CONFIG, isCriticalityBreaking, isCriticalityDangerous, isCriticalitySafe, } from '#lib/graphql-change/criticality' import type { GraphqlChangeset } from '#lib/graphql-changeset/index' import { Box, Flex, Text } from '@radix-ui/themes' import type React from 'react' import { useEffect, useState } from 'react' import { renderDate } from './Changelog.js' interface ChangelogLayoutProps { children: React.ReactNode versions: GraphqlChangeset.ChangeSet[] } interface VersionCounts { breaking: number dangerous: number safe: number } const calculateCounts = (version: GraphqlChangeset.ChangeSet): VersionCounts => { return { breaking: version.changes.filter(isCriticalityBreaking).length, dangerous: version.changes.filter(isCriticalityDangerous).length, safe: version.changes.filter(isCriticalitySafe).length, } } const SidebarEntry: React.FC<{ version: GraphqlChangeset.ChangeSet counts: VersionCounts isActive: boolean }> = ({ version, counts, isActive }) => { const dateId = version.date.toISOString() return ( { e.preventDefault() document.getElementById(dateId)?.scrollIntoView({ behavior: 'smooth' }) }} > {renderDate(version.date)} {counts.breaking > 0 && ( {counts.breaking} )} {counts.dangerous > 0 && ( {counts.dangerous} )} {counts.safe > 0 && ( {counts.safe} )} ) } export const ChangelogLayout: React.FC = ({ children, versions }) => { const [activeVersion, setActiveVersion] = useState(null) // Calculate counts for all versions (SSR-safe) const versionsWithCounts = versions.map(version => ({ version, counts: calculateCounts(version), })) // Set up scroll spy after hydration useEffect(() => { const handleScroll = () => { const scrollPosition = window.scrollY + 100 // Offset for header // Find the current version based on scroll position let currentVersion: string | null = null for (const { version } of versionsWithCounts) { const element = document.getElementById(version.date.toISOString()) if (element) { const { top } = element.getBoundingClientRect() if (top <= 100) { currentVersion = version.date.toISOString() } } } setActiveVersion(currentVersion) } // Initial check handleScroll() // Add scroll listener window.addEventListener('scroll', handleScroll, { passive: true }) return () => { window.removeEventListener('scroll', handleScroll) } }, [versionsWithCounts]) return ( {/* Sidebar */} Releases {versionsWithCounts.map(({ version, counts }) => ( ))} {/* Main content */} {children} ) }