Skip to content

Commit 580b67c

Browse files
committed
Move previous/next buttons to content
1 parent 1a05a5b commit 580b67c

3 files changed

Lines changed: 167 additions & 125 deletions

File tree

src/components/Doc.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react'
22
import { FoldHorizontal, UnfoldHorizontal } from 'lucide-react'
33
import { twMerge } from 'tailwind-merge'
4-
import { useWidthToggle } from '~/components/DocsLayout'
4+
import { useWidthToggle, DocNavigation } from '~/components/DocsLayout'
55
import { AdGate } from '~/contexts/AdsContext'
66
import { GamHeader } from './Gam'
77
import { Toc } from './Toc'
@@ -26,6 +26,8 @@ type DocProps = {
2626
pagePath?: string
2727
// Breadcrumb props (optional)
2828
config?: ConfigSchema
29+
// Footer content rendered after markdown
30+
footer?: React.ReactNode
2931
}
3032

3133
export function Doc({
@@ -42,6 +44,7 @@ export function Doc({
4244
libraryVersion,
4345
pagePath,
4446
config,
47+
footer,
4548
}: DocProps) {
4649
// Extract headings synchronously during render to avoid hydration mismatch
4750
const { headings, markup } = React.useMemo(
@@ -121,7 +124,7 @@ export function Doc({
121124
isTocVisible && 'max-w-full',
122125
)}
123126
>
124-
<div className="flex overflow-auto flex-col w-full">
127+
<div className="flex flex-col w-full">
125128
{config && (
126129
<div className="mb-3">
127130
<DocBreadcrumb
@@ -156,6 +159,7 @@ export function Doc({
156159
) : null
157160
}
158161
/>
162+
{footer ?? <DocNavigation />}
159163
</div>
160164

161165
{isTocVisible && (

src/components/DocsLayout.tsx

Lines changed: 161 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react'
2-
import { TextAlignStart, ArrowLeft, ArrowRight, Menu } from 'lucide-react'
2+
import { TextAlignStart, ChevronLeft, ChevronRight, Menu } from 'lucide-react'
33
import { GithubIcon } from '~/components/icons/GithubIcon'
44
import { DiscordIcon } from '~/components/icons/DiscordIcon'
55
import { Link, useMatches, useParams } from '@tanstack/react-router'
@@ -17,6 +17,7 @@ import { AdGate } from '~/contexts/AdsContext'
1717
import { SearchButton } from './SearchButton'
1818
import { FrameworkSelect, useCurrentFramework } from './FrameworkSelect'
1919
import { 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+
175246
const 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
}

src/components/MarkdownContent.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ export function MarkdownContent({
9898
<SquarePen /> Edit on GitHub
9999
</a>
100100
</div>
101-
<div className="h-24" />
102101
</>
103102
)
104103
}

0 commit comments

Comments
 (0)