11import * as React from 'react'
2- import { TextAlignStart , ArrowLeft , ArrowRight , Menu } from 'lucide-react'
2+ import { TextAlignStart , ChevronLeft , ChevronRight , Menu } from 'lucide-react'
33import { GithubIcon } from '~/components/icons/GithubIcon'
44import { DiscordIcon } from '~/components/icons/DiscordIcon'
55import { Link , useMatches , useParams } from '@tanstack/react-router'
@@ -17,6 +17,7 @@ import { AdGate } from '~/contexts/AdsContext'
1717import { SearchButton } from './SearchButton'
1818import { FrameworkSelect , useCurrentFramework } from './FrameworkSelect'
1919import { VersionSelect } from './VersionSelect'
20+ import { Card } from './Card'
2021
2122// Component for the collapsed menu strip showing box indicators
2223// Minimap style: boxes with flex height filling available vertical space
@@ -172,6 +173,76 @@ export const useWidthToggle = () => {
172173 return context
173174}
174175
176+ // Create context for doc navigation (prev/next)
177+ type DocNavItem = { label : React . ReactNode ; to : string }
178+ const DocNavigationContext = React . createContext < {
179+ prevItem ?: DocNavItem
180+ nextItem ?: DocNavItem
181+ colorFrom : string
182+ colorTo : string
183+ textColor : string
184+ } | null > ( null )
185+
186+ export const useDocNavigation = ( ) => {
187+ return React . useContext ( DocNavigationContext )
188+ }
189+
190+ export function DocNavigation ( ) {
191+ const context = useDocNavigation ( )
192+ if ( ! context ) return null
193+
194+ const { prevItem, nextItem, colorFrom, colorTo, textColor } = context
195+
196+ if ( ! prevItem && ! nextItem ) return null
197+
198+ return (
199+ < div className = "sticky flex items-stretch bottom-2 z-10 right-0 text-xs md:text-sm print:hidden mt-8" >
200+ < div className = "flex-1 flex justify-start" >
201+ { prevItem ? (
202+ < Card
203+ as = { Link }
204+ from = "/$libraryId/$version/docs"
205+ to = { prevItem . to }
206+ params
207+ className = "py-2 px-3 flex items-center gap-2"
208+ >
209+ < ChevronLeft className = "w-4 h-4" />
210+ < div className = "flex flex-col" >
211+ < span className = "text-[10px] uppercase tracking-wider opacity-60 mb-0.5" >
212+ Previous
213+ </ span >
214+ < span className = "font-bold" > { prevItem . label } </ span >
215+ </ div >
216+ </ Card >
217+ ) : null }
218+ </ div >
219+ < div className = "flex-1 flex justify-end" >
220+ { nextItem ? (
221+ < Card
222+ as = { Link }
223+ from = "/$libraryId/$version/docs"
224+ to = { nextItem . to }
225+ params
226+ className = "py-2 px-3 flex items-center gap-2"
227+ >
228+ < div className = "flex flex-col items-end" >
229+ < span className = "text-[10px] uppercase tracking-wider opacity-60 mb-0.5" >
230+ Next
231+ </ span >
232+ < span
233+ className = { `font-bold bg-linear-to-r ${ colorFrom } ${ colorTo } bg-clip-text text-transparent` }
234+ >
235+ { nextItem . label }
236+ </ span >
237+ </ div >
238+ < ChevronRight className = { twMerge ( 'w-4 h-4' , textColor ) } />
239+ </ Card >
240+ ) : null }
241+ </ div >
242+ </ div >
243+ )
244+ }
245+
175246const useMenuConfig = ( {
176247 config,
177248 repo,
@@ -538,146 +609,114 @@ export function DocsLayout({
538609
539610 return (
540611 < WidthToggleContext . Provider value = { { isFullWidth, setIsFullWidth } } >
541- < div
542- className = { `
612+ < DocNavigationContext . Provider
613+ value = { { prevItem, nextItem, colorFrom, colorTo, textColor } }
614+ >
615+ < div
616+ className = { `
543617 min-h-[calc(100dvh-var(--navbar-height))]
544618 flex flex-col sm:flex-row
545619 w-full transition-all duration-300
546620 [overflow-x:clip]` }
547- >
548- { smallMenu }
549- { largeMenu }
550- < div className = "flex flex-col max-w-full min-w-0 flex-1 min-h-0 relative px-4 sm:px-8" >
551- < div
552- className = { twMerge (
553- `max-w-full min-w-0 flex justify-center w-full min-h-[88dvh] sm:min-h-0` ,
554- ! isExample && ! isFullWidth && 'mx-auto w-[900px]' , // page width
555- ) }
556- >
557- { children }
558- </ div >
559- < AdGate >
560- { /* <div className="flex border-t border-gray-500/20">
621+ >
622+ { smallMenu }
623+ { largeMenu }
624+ < div className = "flex flex-col max-w-full min-w-0 flex-1 min-h-0 relative px-4 sm:px-8" >
625+ < div
626+ className = { twMerge (
627+ `max-w-full min-w-0 flex flex-col justify-center w-full min-h-[88dvh] sm:min-h-0` ,
628+ ! isExample && ! isFullWidth && 'mx-auto w-[900px]' , // page width
629+ ) }
630+ >
631+ { children }
632+ </ div >
633+ < AdGate >
634+ { /* <div className="flex border-t border-gray-500/20">
561635 <div className="py-4 px-2 xl:px-4 mx-auto max-w-full justify-center">
562636 <GamFooter popupPosition="top" />
563637 </div>
564638 </div> */ }
565- < div className = "py-2 pb-4 lg:py-4 lg:pb-6 xl:py-6 xl:pb-8 max-w-full" >
566- < GamHeader />
567- </ div >
568- </ AdGate >
569- < div className = "sticky flex items-center flex-wrap bottom-2 z-10 right-0 text-xs md:text-sm px-1 print:hidden" >
570- < div className = "w-1/2 px-1 flex justify-end flex-wrap" >
571- { prevItem ? (
572- < Link
573- from = "/$libraryId/$version/docs"
574- to = { prevItem . to }
575- params
576- className = "py-1 px-2 bg-white/70 text-black dark:bg-gray-500/40 dark:text-white shadow-md flex items-center justify-center backdrop-blur-sm z-20 rounded-lg overflow-hidden"
577- >
578- < div className = "flex gap-2 items-center font-bold" >
579- < ArrowLeft />
580- { prevItem . label }
581- </ div >
582- </ Link >
583- ) : null }
584- </ div >
585- < div className = "w-1/2 px-1 flex justify-start flex-wrap" >
586- { nextItem ? (
587- < Link
588- from = "/$libraryId/$version/docs"
589- to = { nextItem . to }
590- params
591- className = "py-1 px-2 bg-white/70 text-black dark:bg-gray-500/40 dark:text-white shadow-md flex items-center justify-center backdrop-blur-sm z-20 rounded-lg overflow-hidden"
592- >
593- < div className = "flex gap-2 items-center font-bold" >
594- < span
595- className = { `bg-linear-to-r ${ colorFrom } ${ colorTo } bg-clip-text text-transparent` }
596- >
597- { nextItem . label }
598- </ span > { ' ' }
599- < ArrowRight className = { textColor } />
600- </ div >
601- </ Link >
602- ) : null }
603- </ div >
639+ < div className = "py-2 pb-4 lg:py-4 lg:pb-6 xl:py-6 xl:pb-8 max-w-full" >
640+ < GamHeader />
641+ </ div >
642+ </ AdGate >
604643 </ div >
605- </ div >
606- < div
607- className = "w-full sm:w-[300px] shrink-0 sm:sticky
644+ < div
645+ className = "w-full sm:w-[300px] shrink-0 sm:sticky
608646 sm:top-[var(--navbar-height)]
609647 "
610- >
611- < div className = "sm:sticky sm:top-[var(--navbar-height)] ml-auto flex flex-wrap flex-row justify-center sm:flex-col gap-4 pb-4 max-w-full overflow-hidden" >
612- < div className = "flex flex-wrap items-stretch border-l border-gray-500/20 rounded-bl-lg overflow-hidden w-full" >
613- < div className = "w-full flex gap-2 justify-between border-b border-gray-500/20 px-3 py-2" >
614- < Link
615- className = "font-medium opacity-60 hover:opacity-100 text-xs"
616- to = "/partners"
617- >
618- Partners
619- </ Link >
620- < a
621- href = "https://docs.google.com/document/d/1Hg2MzY2TU6U3hFEZ3MLe2oEOM3JS4-eByti3kdJU3I8"
622- target = "_blank"
623- rel = "noopener noreferrer"
624- className = "font-medium opacity-60 hover:opacity-100 text-xs hover:underline"
625- >
626- Become a Partner
627- </ a >
628- </ div >
629- { activePartners
630- . filter ( ( d ) => d . id !== 'ui-dev' )
631- . map ( ( partner ) => {
632- // flexBasis as percentage based on score, flexGrow to fill remaining row space
633- const widthPercent = Math . round ( partner . score * 100 )
634-
635- return (
636- < a
637- key = { partner . name }
638- href = { partner . href }
639- target = "_blank"
640- rel = "noreferrer"
641- className = "flex items-center justify-center px-3 py-2
648+ >
649+ < div className = "sm:sticky sm:top-[var(--navbar-height)] ml-auto flex flex-wrap flex-row justify-center sm:flex-col gap-4 pb-4 max-w-full overflow-hidden" >
650+ < div className = "flex flex-wrap items-stretch border-l border-gray-500/20 rounded-bl-lg overflow-hidden w-full" >
651+ < div className = "w-full flex gap-2 justify-between border-b border-gray-500/20 px-3 py-2" >
652+ < Link
653+ className = "font-medium opacity-60 hover:opacity-100 text-xs"
654+ to = "/partners"
655+ >
656+ Partners
657+ </ Link >
658+ < a
659+ href = "https://docs.google.com/document/d/1Hg2MzY2TU6U3hFEZ3MLe2oEOM3JS4-eByti3kdJU3I8"
660+ target = "_blank"
661+ rel = "noopener noreferrer"
662+ className = "font-medium opacity-60 hover:opacity-100 text-xs hover:underline"
663+ >
664+ Become a Partner
665+ </ a >
666+ </ div >
667+ { activePartners
668+ . filter ( ( d ) => d . id !== 'ui-dev' )
669+ . map ( ( partner ) => {
670+ // flexBasis as percentage based on score, flexGrow to fill remaining row space
671+ const widthPercent = Math . round ( partner . score * 100 )
672+
673+ return (
674+ < a
675+ key = { partner . name }
676+ href = { partner . href }
677+ target = "_blank"
678+ rel = "noreferrer"
679+ className = "flex items-center justify-center px-3 py-2
642680 border-r border-b border-gray-500/20
643681 hover:bg-gray-500/10 transition-colors duration-150 ease-out"
644- style = { {
645- flexBasis : `${ widthPercent } %` ,
646- flexGrow : 1 ,
647- flexShrink : 0 ,
648- } }
649- >
650- < div
651682 style = { {
652- width : Math . max (
653- 60 + Math . round ( 140 * partner . score ) ,
654- 70 ,
655- ) ,
683+ flexBasis : `${ widthPercent } %` ,
684+ flexGrow : 1 ,
685+ flexShrink : 0 ,
656686 } }
657687 >
658- < PartnerImage
659- config = { partner . image }
660- alt = { partner . name }
661- />
662- </ div >
663- </ a >
664- )
665- } ) }
666- </ div >
667- < AdGate >
668- < GamVrec1
669- popupPosition = "top"
670- borderClassName = "rounded-l-xl rounded-r-none"
671- />
672- </ AdGate >
673- { libraryId === 'query' ? (
674- < div className = "p-4 bg-white/70 dark:bg-black/40 rounded-lg flex flex-col" >
675- < DocsCalloutQueryGG />
688+ < div
689+ style = { {
690+ width : Math . max (
691+ 60 + Math . round ( 140 * partner . score ) ,
692+ 70 ,
693+ ) ,
694+ } }
695+ >
696+ < PartnerImage
697+ config = { partner . image }
698+ alt = { partner . name }
699+ />
700+ </ div >
701+ </ a >
702+ )
703+ } ) }
676704 </ div >
677- ) : null }
705+ < AdGate >
706+ < GamVrec1
707+ popupPosition = "top"
708+ borderClassName = "rounded-l-xl rounded-r-none"
709+ />
710+ </ AdGate >
711+ { libraryId === 'query' ? (
712+ < div className = "p-4 bg-white/70 dark:bg-black/40 rounded-lg flex flex-col" >
713+ < DocsCalloutQueryGG />
714+ </ div >
715+ ) : null }
716+ </ div >
678717 </ div >
679718 </ div >
680- </ div >
719+ </ DocNavigationContext . Provider >
681720 </ WidthToggleContext . Provider >
682721 )
683722}
0 commit comments