mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
1 parent
a3ca009680
commit
084294fe79
11 files changed
+302
-13
No files matched your search
@@ -12,7 +12,7 @@ const GuidesLayout = ({ children }: PropsWithChildren) => {
|
||||
return <Layout menuId={menuId}>{children}</Layout>
|
||||
}
|
||||
|
||||
const getMenuId = (pathname: string | null) => {
|
||||
export const getMenuId = (pathname: string | null) => {
|
||||
pathname = (pathname ??= '').replace(/^\/guides\//, '')
|
||||
|
||||
switch (true) {
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
'use client'
|
||||
|
||||
import React, { Fragment } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import {
|
||||
Breadcrumb_Shadcn_ as Breadcrumb,
|
||||
BreadcrumbList_Shadcn_ as BreadcrumbList,
|
||||
BreadcrumbItem_Shadcn_ as BreadcrumbItem,
|
||||
BreadcrumbLink_Shadcn_ as BreadcrumbLink,
|
||||
BreadcrumbSeparator_Shadcn_ as BreadcrumbSeparator,
|
||||
BreadcrumbPage_Shadcn_ as BreadcrumbPage,
|
||||
BreadcrumbEllipsis_Shadcn_ as BreadcrumbEllipsis,
|
||||
Button,
|
||||
cn,
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerFooter,
|
||||
DrawerTrigger,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from 'ui'
|
||||
import { getMenuId } from '../app/guides/layout'
|
||||
import { useBreakpoint } from 'common'
|
||||
import * as NavItems from './Navigation/NavigationMenu/NavigationMenu.constants'
|
||||
|
||||
const Breadcrumbs = ({ className }: { className?: string }) => {
|
||||
const pathname = usePathname()
|
||||
const menuId = getMenuId(pathname)
|
||||
const menu = NavItems[menuId]
|
||||
const breadcrumbs = findMenuItemByUrl(menu, pathname, [])
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const isMobile = useBreakpoint('md')
|
||||
|
||||
const ITEMS_TO_DISPLAY = isMobile ? 4 : 3
|
||||
|
||||
if (!breadcrumbs?.length || breadcrumbs?.length === 1) return null
|
||||
|
||||
const appendedBreadcrumbs = breadcrumbs?.slice(-ITEMS_TO_DISPLAY + 1, isMobile ? -1 : undefined)
|
||||
|
||||
return (
|
||||
<Breadcrumb className={cn(className)}>
|
||||
<BreadcrumbList className="text-foreground-lighter p-0">
|
||||
{breadcrumbs.length >= ITEMS_TO_DISPLAY && (
|
||||
<>
|
||||
<BreadcrumbItem>
|
||||
{breadcrumbs[0].url ? (
|
||||
<BreadcrumbLink href={`/docs${breadcrumbs[0].url}`}>
|
||||
{breadcrumbs[0].title || breadcrumbs[0].name}
|
||||
</BreadcrumbLink>
|
||||
) : (
|
||||
<BreadcrumbPage>{breadcrumbs[0].title || breadcrumbs[0].name}</BreadcrumbPage>
|
||||
)}
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
</>
|
||||
)}
|
||||
{breadcrumbs.length > ITEMS_TO_DISPLAY && (
|
||||
<>
|
||||
<BreadcrumbItem>
|
||||
{!isMobile ? (
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1" aria-label="Toggle menu">
|
||||
<BreadcrumbEllipsis className="h-4 w-4" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
{breadcrumbs.slice(1, -2).map((crumb, index) => (
|
||||
<DropdownMenuItem
|
||||
key={index}
|
||||
className={cn(!crumb.url && 'pointer-events-none')}
|
||||
>
|
||||
{crumb.url ? (
|
||||
<Link href={`/docs${crumb.url}`}>{crumb.title || crumb.name}</Link>
|
||||
) : (
|
||||
crumb.title || crumb.name
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : (
|
||||
<Drawer open={open} onOpenChange={setOpen}>
|
||||
<DrawerTrigger aria-label="Toggle Menu">
|
||||
<BreadcrumbEllipsis className="h-4 w-4" />
|
||||
</DrawerTrigger>
|
||||
<DrawerContent showHandle={false}>
|
||||
<div className="grid gap-1 px-4">
|
||||
{breadcrumbs
|
||||
.slice(1, -2)
|
||||
.map((crumb) =>
|
||||
crumb.url ? (
|
||||
<Link href={`/docs${crumb.url}`}>{crumb.title || crumb.name}</Link>
|
||||
) : (
|
||||
crumb.title || crumb.name
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<DrawerFooter className="pt-4">
|
||||
<DrawerClose asChild>
|
||||
<Button type="outline">Close</Button>
|
||||
</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
)}
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
</>
|
||||
)}
|
||||
{appendedBreadcrumbs?.map((crumb, i) => (
|
||||
<Fragment key={crumb.url}>
|
||||
<BreadcrumbItem
|
||||
className={cn(
|
||||
'flex items-center overflow-hidden',
|
||||
i === appendedBreadcrumbs.length - 1 && 'md:text-foreground-light'
|
||||
)}
|
||||
>
|
||||
{crumb.url ? (
|
||||
<BreadcrumbLink href={`/docs${crumb.url}`}>
|
||||
{crumb.title || crumb.name}
|
||||
</BreadcrumbLink>
|
||||
) : (
|
||||
<BreadcrumbPage>{crumb.title || crumb.name}</BreadcrumbPage>
|
||||
)}
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator
|
||||
className={cn(i === appendedBreadcrumbs.length - 1 && 'md:hidden')}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
)
|
||||
}
|
||||
|
||||
function findMenuItemByUrl(menu, targetUrl, parents = []) {
|
||||
// If the menu has items, recursively search through them
|
||||
if (menu.items) {
|
||||
for (let item of menu.items) {
|
||||
const result = findMenuItemByUrl(item, targetUrl, [...parents, menu])
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the current menu object itself has the target URL
|
||||
if (menu.url === targetUrl) {
|
||||
return [...parents, menu]
|
||||
}
|
||||
|
||||
// If the URL is not found, return null
|
||||
return null
|
||||
}
|
||||
|
||||
export default Breadcrumbs
|
||||
@@ -245,6 +245,7 @@ export const REFERENCES: References = {
|
||||
export const gettingstarted: NavMenuConstant = {
|
||||
icon: 'getting-started',
|
||||
title: 'Start with Supabase',
|
||||
url: '/guides/getting-started',
|
||||
items: [
|
||||
{ name: 'Features', url: '/guides/getting-started/features' },
|
||||
{ name: 'Architecture', url: '/guides/getting-started/architecture' },
|
||||
@@ -1202,7 +1203,7 @@ export const functions: NavMenuConstant = {
|
||||
},
|
||||
{
|
||||
name: 'Examples',
|
||||
url: '/guides/functions/examples',
|
||||
url: undefined,
|
||||
items: [
|
||||
{
|
||||
name: 'CORS support for invoking from the browser',
|
||||
|
||||
@@ -234,7 +234,7 @@ const menus: Menu[] = [
|
||||
},
|
||||
]
|
||||
|
||||
function getMenuById(id: MenuId) {
|
||||
export function getMenuById(id: MenuId) {
|
||||
return menus.find((menu) => menu.id === id)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import * as Accordion from '@radix-ui/react-accordion'
|
||||
|
||||
import * as NavItems from './NavigationMenu.constants'
|
||||
import { getPathWithoutHash } from './NavigationMenu.utils'
|
||||
import NavigationMenuGuideListItems from './NavigationMenuGuideListItems'
|
||||
import { usePathname } from 'next/navigation'
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { components } from '~/features/docs/mdx.shared'
|
||||
import type { WithRequired } from '~/features/helpers.types'
|
||||
import { type GuideFrontmatter } from '~/lib/docs'
|
||||
import { MDXProviderGuides } from './GuidesMdx.client'
|
||||
import Breadcrumbs from '~/components/Breadcrumbs'
|
||||
|
||||
const codeHikeOptions: CodeHikeConfig = {
|
||||
theme: codeHikeTheme,
|
||||
@@ -120,6 +121,7 @@ const GuideTemplate = ({ meta, content, children, editLink, mdxOptions }: GuideT
|
||||
hideToc ? 'col-span-12' : 'col-span-12 md:col-span-9'
|
||||
)}
|
||||
>
|
||||
<Breadcrumbs className="mb-2" />
|
||||
<article
|
||||
// Used to get headings for the table of contents
|
||||
id="sb-docs-guide-main-article"
|
||||
@@ -163,11 +165,11 @@ const GuideTemplate = ({ meta, content, children, editLink, mdxOptions }: GuideT
|
||||
/**
|
||||
* --header-height: height of nav
|
||||
* 1px: height of nav border
|
||||
* 4rem: content padding
|
||||
* 2rem: content padding
|
||||
*/
|
||||
'top-[calc(var(--header-height)+1px+4rem)]',
|
||||
// 5rem accounts for 4rem of top padding + 1rem of extra breathing room
|
||||
'max-h-[calc(100vh-var(--header-height)-5rem)]'
|
||||
'top-[calc(var(--header-height)+1px+2rem)]',
|
||||
// 3rem accounts for 2rem of top padding + 1rem of extra breathing room
|
||||
'max-h-[calc(100vh-var(--header-height)-3rem)]'
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -3,4 +3,4 @@ import { type FC, type PropsWithChildren } from 'react'
|
||||
export const LayoutMainContent: FC<PropsWithChildren<{ className?: string }>> = ({
|
||||
className,
|
||||
children,
|
||||
}) => <div className={['max-w-7xl px-5 mx-auto py-16', className].join(' ')}>{children}</div>
|
||||
}) => <div className={['max-w-7xl px-5 mx-auto py-8', className].join(' ')}>{children}</div>
|
||||
@@ -122,7 +122,7 @@ const StickyHeader: FC<StickyHeader> = ({ icon, ...props }) => {
|
||||
id={props.slug}
|
||||
data-ref-id={props.id}
|
||||
className={cn(
|
||||
'text-2xl font-medium text-foreground scroll-mt-[calc(33px+2rem)] lg:scroll-mt-[calc(var(--header-height)+1px+4rem)]',
|
||||
'text-2xl font-medium text-foreground scroll-mt-[calc(32px+2rem)] lg:scroll-mt-[calc(var(--header-height)+1px+4rem)]',
|
||||
!icon && 'mb-8',
|
||||
props.monoFont && 'font-mono'
|
||||
)}
|
||||
|
||||
@@ -191,6 +191,16 @@ export { Input as Input_Shadcn_ } from './src/components/shadcn/ui/input'
|
||||
|
||||
export { Button as Button_Shadcn_ } from './src/components/shadcn/ui/button'
|
||||
|
||||
export {
|
||||
Breadcrumb as Breadcrumb_Shadcn_,
|
||||
BreadcrumbItem as BreadcrumbItem_Shadcn_,
|
||||
BreadcrumbLink as BreadcrumbLink_Shadcn_,
|
||||
BreadcrumbList as BreadcrumbList_Shadcn_,
|
||||
BreadcrumbEllipsis as BreadcrumbEllipsis_Shadcn_,
|
||||
BreadcrumbPage as BreadcrumbPage_Shadcn_,
|
||||
BreadcrumbSeparator as BreadcrumbSeparator_Shadcn_,
|
||||
} from './src/components/shadcn/ui/breadcrumb'
|
||||
|
||||
export { TextArea as TextArea_Shadcn_ } from './src/components/shadcn/ui/text-area'
|
||||
|
||||
export { Label as Label_Shadcn_ } from './src/components/shadcn/ui/label'
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import * as React from 'react'
|
||||
import { ChevronRightIcon } from 'lucide-react'
|
||||
import { Slot } from '@radix-ui/react-slot'
|
||||
import { cn } from '../../../lib/utils'
|
||||
|
||||
const Breadcrumb = React.forwardRef<
|
||||
HTMLElement,
|
||||
React.ComponentPropsWithoutRef<'nav'> & {
|
||||
separator?: React.ReactNode
|
||||
}
|
||||
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
|
||||
Breadcrumb.displayName = 'Breadcrumb'
|
||||
|
||||
const BreadcrumbList = React.forwardRef<HTMLOListElement, React.ComponentPropsWithoutRef<'ol'>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<ol
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex flex-wrap items-center gap-0.5 break-words text-sm text-muted-foreground sm:gap-1.5',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
BreadcrumbList.displayName = 'BreadcrumbList'
|
||||
|
||||
const BreadcrumbItem = React.forwardRef<HTMLLIElement, React.ComponentPropsWithoutRef<'li'>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<li
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'inline-flex text-foreground-lighter items-center gap-1.5 leading-5',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
BreadcrumbItem.displayName = 'BreadcrumbItem'
|
||||
|
||||
const BreadcrumbLink = React.forwardRef<
|
||||
HTMLAnchorElement,
|
||||
React.ComponentPropsWithoutRef<'a'> & {
|
||||
asChild?: boolean
|
||||
}
|
||||
>(({ asChild, className, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : 'a'
|
||||
|
||||
return (
|
||||
<Comp
|
||||
ref={ref}
|
||||
className={cn('transition-colors underline lg:no-underline hover:text-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
BreadcrumbLink.displayName = 'BreadcrumbLink'
|
||||
|
||||
const BreadcrumbPage = React.forwardRef<HTMLSpanElement, React.ComponentPropsWithoutRef<'span'>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<span
|
||||
ref={ref}
|
||||
role="link"
|
||||
aria-disabled="true"
|
||||
aria-current="page"
|
||||
className={cn('no-underline', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
BreadcrumbPage.displayName = 'BreadcrumbPage'
|
||||
|
||||
const BreadcrumbSeparator = ({ children, className, ...props }: React.ComponentProps<'li'>) => (
|
||||
<li
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
className={cn('[&>svg]:size-3.5', className)}
|
||||
{...props}
|
||||
>
|
||||
{children ?? <ChevronRightIcon />}
|
||||
</li>
|
||||
)
|
||||
BreadcrumbSeparator.displayName = 'BreadcrumbSeparator'
|
||||
|
||||
const BreadcrumbEllipsis = ({ className, ...props }: React.ComponentProps<'span'>) => (
|
||||
<span className={cn('flex h-4 w-4 items-center justify-center', className)} {...props}>
|
||||
<svg
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 15 15"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3.625 7.5C3.625 8.12132 3.12132 8.625 2.5 8.625C1.87868 8.625 1.375 8.12132 1.375 7.5C1.375 6.87868 1.87868 6.375 2.5 6.375C3.12132 6.375 3.625 6.87868 3.625 7.5ZM8.625 7.5C8.625 8.12132 8.12132 8.625 7.5 8.625C6.87868 8.625 6.375 8.12132 6.375 7.5C6.375 6.87868 6.87868 6.375 7.5 6.375C8.12132 6.375 8.625 6.87868 8.625 7.5ZM12.5 8.625C13.1213 8.625 13.625 8.12132 13.625 7.5C13.625 6.87868 13.1213 6.375 12.5 6.375C11.8787 6.375 11.375 6.87868 11.375 7.5C11.375 8.12132 11.8787 8.625 12.5 8.625Z"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">More</span>
|
||||
</span>
|
||||
)
|
||||
BreadcrumbEllipsis.displayName = 'BreadcrumbEllipsis'
|
||||
|
||||
export {
|
||||
Breadcrumb,
|
||||
BreadcrumbList,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
BreadcrumbEllipsis,
|
||||
}
|
||||
@@ -31,19 +31,20 @@ DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName
|
||||
|
||||
const DrawerContent = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> & { showHandle?: boolean }
|
||||
>(({ className, children, showHandle = true, ...props }, ref) => (
|
||||
<DrawerPortal>
|
||||
<DrawerOverlay />
|
||||
<DrawerPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background',
|
||||
!showHandle && 'pt-4',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
||||
{showHandle && <div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />}
|
||||
{children}
|
||||
</DrawerPrimitive.Content>
|
||||
</DrawerPortal>
|
||||
|
||||
Reference in new issue
Block a user