mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat(docs): add visual contributing guide (#28214)
Add a contributing guide to docs, which links to resources for contributors and includes examples of usable components when writing docs content.
This commit is contained in:
1 parent
9d5d4626ef
commit
837e314088
25 files changed
+766
-214
No files matched your search
@@ -0,0 +1,82 @@
|
||||
'use client'
|
||||
|
||||
import { Menu } from 'lucide-react'
|
||||
import type { HTMLAttributes } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { useBreakpoint } from 'common'
|
||||
import { cn, Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_ } from 'ui'
|
||||
|
||||
interface TocItem extends HTMLAttributes<HTMLElement> {
|
||||
label: string
|
||||
anchor: string
|
||||
}
|
||||
|
||||
export function ContributingToc({ className }: { className?: string }) {
|
||||
const mobileToc = useBreakpoint('xl')
|
||||
const [tocItems, setTocItems] = useState<Array<TocItem>>([])
|
||||
|
||||
useEffect(() => {
|
||||
const headings = [
|
||||
...document.querySelectorAll('article.prose > h2,h3'),
|
||||
] as Array<HTMLHeadingElement>
|
||||
const tocItems = headings
|
||||
.filter((heading) => !!heading.id)
|
||||
.map((heading) => ({
|
||||
label: heading.textContent.substring(0, heading.textContent.length - 1), // Remove ending `#`
|
||||
anchor: heading.id,
|
||||
}))
|
||||
setTocItems(tocItems)
|
||||
}, [])
|
||||
|
||||
return mobileToc ? (
|
||||
<MobileToc
|
||||
items={tocItems}
|
||||
className={cn(
|
||||
'[--local-top-spacing:2rem]',
|
||||
'fixed top-[calc(var(--header-height)+var(--local-top-spacing))] right-8'
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<TocBase
|
||||
items={tocItems}
|
||||
className={cn(
|
||||
'[--local-top-spacing:5rem]',
|
||||
'fixed top-[calc(var(--header-height)+var(--local-top-spacing))] right-8 w-36',
|
||||
'xl:border-l border-foreground-muted'
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function MobileToc({ items, className }: { items: Array<TocItem>; className?: string }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<Popover_Shadcn_ open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger_Shadcn_ className={cn('border rounded p-2', className)}>
|
||||
<Menu />
|
||||
<span className="sr-only">
|
||||
{open ? 'Close table of contents' : 'Open table of contents'}
|
||||
</span>
|
||||
</PopoverTrigger_Shadcn_>
|
||||
<PopoverContent_Shadcn_ align="end" className="w-48">
|
||||
<TocBase items={items} />
|
||||
</PopoverContent_Shadcn_>
|
||||
</Popover_Shadcn_>
|
||||
)
|
||||
}
|
||||
|
||||
function TocBase({ items, className }: { items: Array<TocItem>; className?: string }) {
|
||||
return (
|
||||
<nav aria-label="Table of contents" className={cn('px-4 text-foreground-light', className)}>
|
||||
<ul className="flex flex-col gap-1">
|
||||
{items.map((item) => (
|
||||
<li key={item.anchor} className="overflow-hidden truncate">
|
||||
<a href={`#${item.anchor}`}>{item.label}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
# Contributing to Supabase Docs
|
||||
|
||||
Thanks for contributing to Supabase Docs! Here are a few resources to help you get started.
|
||||
|
||||
The code and content for our docs site are located in the main [Supabase GitHub repo](https://github.com/supabase/supabase), under the `apps/docs` directory.
|
||||
|
||||
In the repo, you'll also find:
|
||||
|
||||
- The [developers guide](https://github.com/supabase/supabase/blob/master/apps/docs/DEVELOPERS.md), which will help you set up your local machine to develop the docs site
|
||||
- The [contributing guide](https://github.com/supabase/supabase/blob/master/apps/docs/CONTRIBUTING.md), which goes over the content organization and some general guidelines for writing docs content
|
||||
|
||||
## Components
|
||||
|
||||
Our docs content is mainly written in MDX. Aside from standard GitHub-flavored Markdown, you can use the following helper components to help you organize and display your content:
|
||||
|
||||
### Accordion
|
||||
|
||||
For content that requires progressive disclosure:
|
||||
|
||||
```mdx
|
||||
<Accordion
|
||||
type="default"
|
||||
openBehaviour="multiple"
|
||||
chevronAlign="right"
|
||||
justified
|
||||
size="medium"
|
||||
className="text-foreground-light mt-8 mb-6"
|
||||
>
|
||||
<div className="border-b mt-3 pb-3">
|
||||
<AccordionItem
|
||||
header="Accordion item 1"
|
||||
id="item-1"
|
||||
>
|
||||
|
||||
Your content here.
|
||||
|
||||
</AccordionItem>
|
||||
|
||||
</div>
|
||||
<div className="border-b mt-3 pb-3">
|
||||
<AccordionItem
|
||||
header="Accordion item 2"
|
||||
id="item-2"
|
||||
>
|
||||
|
||||
More content here.
|
||||
|
||||
</AccordionItem>
|
||||
|
||||
</div>
|
||||
</Accordion>
|
||||
```
|
||||
|
||||
<Accordion
|
||||
type="default"
|
||||
openBehaviour="multiple"
|
||||
chevronAlign="right"
|
||||
justified
|
||||
size="medium"
|
||||
className="text-foreground-light mt-8 mb-6"
|
||||
>
|
||||
<div className="border-b mt-3 pb-3">
|
||||
<AccordionItem
|
||||
header="Accordion item 1"
|
||||
id="item-1"
|
||||
>
|
||||
|
||||
Your content here.
|
||||
|
||||
</AccordionItem>
|
||||
|
||||
</div>
|
||||
<div className="border-b mt-3 pb-3">
|
||||
<AccordionItem
|
||||
header="Accordion item 2"
|
||||
id="item-2"
|
||||
>
|
||||
|
||||
More content here.
|
||||
|
||||
</AccordionItem>
|
||||
|
||||
</div>
|
||||
</Accordion>
|
||||
|
||||
### Admonition
|
||||
|
||||
For extra information that doesn't fit into the main flow. There are 5 supported types of admonitions:
|
||||
|
||||
- `danger` to warn the user about any missteps that could cause data loss or data leaks
|
||||
- `deprecation` to notify the user about features that are (or will soon be) deprecated
|
||||
- `caution` to warn about anything that could cause a bug or serious user inconvenience
|
||||
- `tip` to point out helpful but optional actions
|
||||
- `note` for anything else
|
||||
|
||||
Leave a blank line between the admonition tag and the contained content. This will prevent Prettier from trying to break the lines within the content.
|
||||
|
||||
```mdx
|
||||
<Admonition type="danger">
|
||||
|
||||
This could lead to data loss!
|
||||
|
||||
</Admonition>
|
||||
|
||||
<Admonition type="deprecation">
|
||||
|
||||
This feature is deprecated.
|
||||
|
||||
</Admonition>
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
You should make sure you don't set this up wrong.
|
||||
|
||||
</Admonition>
|
||||
|
||||
<Admonition type="tip">
|
||||
|
||||
In certain cases, you may want to do this.
|
||||
|
||||
</Admonition>
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
Additional helpful information.
|
||||
|
||||
</Admonition>
|
||||
```
|
||||
|
||||
<Admonition type="danger">
|
||||
|
||||
This could lead to data loss!
|
||||
|
||||
</Admonition>
|
||||
|
||||
<Admonition type="deprecation">
|
||||
|
||||
This feature is deprecated.
|
||||
|
||||
</Admonition>
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
You should make sure you don't set this up wrong.
|
||||
|
||||
</Admonition>
|
||||
|
||||
<Admonition type="tip">
|
||||
|
||||
In certain cases, you may want to do this.
|
||||
|
||||
</Admonition>
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
Additional helpful information.
|
||||
|
||||
</Admonition>
|
||||
|
||||
### Icons
|
||||
|
||||
The following icons are available. They can be styled with [Tailwind](https://tailwindcss.com/) classes:
|
||||
|
||||
```mdx
|
||||
<IconArrowDown />
|
||||
<IconCheck />
|
||||
<IconX />
|
||||
```
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<IconArrowDown />
|
||||
<IconCheck />
|
||||
<IconX />
|
||||
</div>
|
||||
|
||||
### Image
|
||||
|
||||
You can include images with regular Markdown syntax:
|
||||
|
||||
```mdx
|
||||

|
||||
```
|
||||
|
||||

|
||||
|
||||
If your image has alternate light and dark versions, or you want to make it zoomable, you can also use the image component:
|
||||
|
||||
```mdx
|
||||
<Image
|
||||
alt="Supabase architectural diagram"
|
||||
src={{
|
||||
dark: '/docs/img/supabase-architecture.svg',
|
||||
light: '/docs/img/supabase-architecture--light.svg',
|
||||
}}
|
||||
zoomable
|
||||
/>
|
||||
```
|
||||
|
||||
<Image
|
||||
alt="Supabase architectural diagram"
|
||||
src={{
|
||||
dark: '/docs/img/supabase-architecture.svg',
|
||||
light: '/docs/img/supabase-architecture--light.svg',
|
||||
}}
|
||||
zoomable
|
||||
/>
|
||||
|
||||
### Project Variables
|
||||
|
||||
Some guides and tutorials will require that users copy their Supabase project URL and anon key. You can provide those inline if the user is signed in:
|
||||
|
||||
```mdx
|
||||
<ProjectConfigVariables variable="url" />
|
||||
<ProjectConfigVariables variable="anonKey" />
|
||||
```
|
||||
|
||||
<ProjectConfigVariables variable="url" />
|
||||
<ProjectConfigVariables variable="anonKey" />
|
||||
|
||||
### Step Hike
|
||||
|
||||
For tutorials, which feature step-by-step instructions, often with accompanying code, we use the `StepHike` pattern:
|
||||
|
||||
````mdx
|
||||
<StepHikeCompact>
|
||||
|
||||
<StepHikeCompact.Step step={1}>
|
||||
|
||||
<StepHikeCompact.Details title="The first step">
|
||||
|
||||
Explanation of what to do first.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```sql
|
||||
select ...
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={2}>
|
||||
|
||||
<StepHikeCompact.Details title="No code in this step" fullWidth>
|
||||
|
||||
Explanation of what to do next. This stretches the full width of the section: Sweet tiramisu apple biscuit candy cake. Orange ipsum muffin cookie cake biscuit. Orange muffin vanilla sweet sugar candy. Sprinkles jelly sweet orange candy cream.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
</StepHikeCompact>
|
||||
````
|
||||
|
||||
<StepHikeCompact>
|
||||
|
||||
<StepHikeCompact.Step step={1}>
|
||||
|
||||
<StepHikeCompact.Details title="The first step">
|
||||
|
||||
Explanation of what to do first.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```sql
|
||||
select ...
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={2}>
|
||||
|
||||
<StepHikeCompact.Details title="No code in this step" fullWidth>
|
||||
|
||||
Explanation of what to do next. This stretches the full width of the section: Sweet tiramisu apple biscuit candy cake. Orange ipsum muffin cookie cake biscuit. Orange muffin vanilla sweet sugar candy. Sprinkles jelly sweet orange candy cream.
|
||||
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
</StepHikeCompact>
|
||||
|
||||
### Tabs
|
||||
|
||||
Use tabs when users can select between multiple versions of the content. For example, the content might differ based on language or package manager.
|
||||
|
||||
If you include the `queryGroup` prop, the user's selection will sync with other tab groups. Leave out this prop to omit this behavior.
|
||||
|
||||
````mdx
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```js
|
||||
const supabase = createSupabaseClient()
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
```dart
|
||||
void main() async {
|
||||
Supabase.initialize();
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
````
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```js
|
||||
const supabase = createSupabaseClient()
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
```dart
|
||||
void main() async {
|
||||
Supabase.initialize();
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Partials
|
||||
|
||||
We incorporate content reuse in the docs to avoid duplication. If you find yourself writing the same content over and over, you can put it in a partial instead. Here are some examples of commonly used partials:
|
||||
|
||||
<Accordion
|
||||
type="default"
|
||||
openBehaviour="multiple"
|
||||
chevronAlign="right"
|
||||
justified
|
||||
size="medium"
|
||||
className="text-foreground-light mt-8 mb-6"
|
||||
>
|
||||
<div className="border-b mt-3 pb-3">
|
||||
<AccordionItem
|
||||
header="Database setup"
|
||||
id="database-setup"
|
||||
>
|
||||
|
||||
```mdx
|
||||
<DatabaseSetup />
|
||||
```
|
||||
|
||||
<DatabaseSetup />
|
||||
|
||||
</AccordionItem>
|
||||
|
||||
</div>
|
||||
<div className="border-b mt-3 pb-3">
|
||||
<AccordionItem
|
||||
header="Create client for Auth"
|
||||
id="create-client-auth"
|
||||
>
|
||||
|
||||
```mdx
|
||||
<CreateClientSnippet />
|
||||
```
|
||||
|
||||
<CreateClientSnippet />
|
||||
|
||||
</AccordionItem>
|
||||
|
||||
</div>
|
||||
</Accordion>
|
||||
|
||||
To make a new partial:
|
||||
|
||||
1. Make a new MDX file in `apps/docs/components/MDX`.
|
||||
1. Write your reusable content.
|
||||
1. Inside `apps/docs/components/MDX/partials.tsx`, import and re-export your partial.
|
||||
1. Inside `apps/docs/features/docs/mdx.shared.tsx`, import your partial and include it in the `components` object.
|
||||
1. You can now use your partial inside any other MDX file by using: `<YourPartialName />`.
|
||||
@@ -0,0 +1,26 @@
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import { ContributingToc } from '~/app/contributing/ContributingToC'
|
||||
import { MDXProviderGuides } from '~/features/docs/GuidesMdx.client'
|
||||
import { MDXRemoteBase } from '~/features/docs/MdxBase'
|
||||
import { SidebarSkeleton } from '~/layouts/MainSkeleton'
|
||||
|
||||
export default async function ContributingPage() {
|
||||
const contentFile = join(dirname(fileURLToPath(import.meta.url)), 'content.mdx')
|
||||
const content = await readFile(contentFile, 'utf-8')
|
||||
|
||||
return (
|
||||
<SidebarSkeleton>
|
||||
<div className="px-8 py-16">
|
||||
<article className="prose mx-auto">
|
||||
<MDXProviderGuides>
|
||||
<MDXRemoteBase source={content} />
|
||||
</MDXProviderGuides>
|
||||
</article>
|
||||
<ContributingToc />
|
||||
</div>
|
||||
</SidebarSkeleton>
|
||||
)
|
||||
}
|
||||
@@ -4,13 +4,18 @@ import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
|
||||
const ErrorPage = () => (
|
||||
<div className="h-full w-full flex flex-col gap-8 p-8 items-center justify-center">
|
||||
<div className="h-[calc(100vh-var(--header-height))] w-full flex flex-col gap-8 p-8 items-center justify-center">
|
||||
<span className="text-center text-5xl text-foreground-lighter">
|
||||
Sorry, something went wrong
|
||||
</span>
|
||||
<Button asChild type="secondary" className="w-fit p-4 text-lg">
|
||||
<Link href="/">Return to homepage</Link>
|
||||
</Button>
|
||||
<div className="flex flex-row items-center gap-4">
|
||||
<Button asChild type="secondary" className="w-fit p-4 text-lg">
|
||||
<Link href="/">Return to homepage</Link>
|
||||
</Button>
|
||||
<Button type="secondary" className="w-fit p-4 text-lg" onClick={() => location.reload()}>
|
||||
Refresh page
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import rehypeSlug from 'rehype-slug'
|
||||
import { Heading } from 'ui'
|
||||
|
||||
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template'
|
||||
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
|
||||
import { MDXRemoteBase } from '~/features/docs/MdxBase'
|
||||
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
|
||||
import { Tabs, TabPanel } from '~/features/ui/Tabs'
|
||||
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
|
||||
@@ -54,7 +55,7 @@ const DatabaseAdvisorDocs = async () => {
|
||||
|
||||
return (
|
||||
<GuideTemplate meta={meta} editLink={editLink}>
|
||||
<MDXRemoteGuides source={markdownIntro} />
|
||||
<MDXRemoteBase source={markdownIntro} />
|
||||
<Heading tag="h2">Available checks</Heading>
|
||||
<Tabs listClassNames="flex flex-wrap gap-2 [&>button]:!m-0" queryGroup="lint">
|
||||
{lints.map((lint) => (
|
||||
@@ -64,7 +65,7 @@ const DatabaseAdvisorDocs = async () => {
|
||||
label={capitalize(getBasename(lint.path).replace(/_/g, ' '))}
|
||||
>
|
||||
<section id={getBasename(lint.path)}>
|
||||
<MDXRemoteGuides source={lint.content} options={options} />
|
||||
<MDXRemoteBase source={lint.content} options={options} />
|
||||
</section>
|
||||
</TabPanel>
|
||||
))}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { type PropsWithChildren } from 'react'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
import { getMenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu.utils'
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
const GuidesLayout = ({ children }: PropsWithChildren) => {
|
||||
@@ -12,39 +13,4 @@ const GuidesLayout = ({ children }: PropsWithChildren) => {
|
||||
return <Layout menuId={menuId}>{children}</Layout>
|
||||
}
|
||||
|
||||
export const getMenuId = (pathname: string | null) => {
|
||||
pathname = (pathname ??= '').replace(/^\/guides\//, '')
|
||||
|
||||
switch (true) {
|
||||
case pathname.startsWith('ai'):
|
||||
return MenuId.Ai
|
||||
case pathname.startsWith('api'):
|
||||
return MenuId.Api
|
||||
case pathname.startsWith('auth'):
|
||||
return MenuId.Auth
|
||||
case pathname.startsWith('cli'):
|
||||
return MenuId.Cli
|
||||
case pathname.startsWith('database'):
|
||||
return MenuId.Database
|
||||
case pathname.startsWith('functions'):
|
||||
return MenuId.Functions
|
||||
case pathname.startsWith('getting-started'):
|
||||
return MenuId.GettingStarted
|
||||
case pathname.startsWith('graphql'):
|
||||
return MenuId.Graphql
|
||||
case pathname.startsWith('platform'):
|
||||
return MenuId.Platform
|
||||
case pathname.startsWith('realtime'):
|
||||
return MenuId.Realtime
|
||||
case pathname.startsWith('resources'):
|
||||
return MenuId.Resources
|
||||
case pathname.startsWith('self-hosting'):
|
||||
return MenuId.SelfHosting
|
||||
case pathname.startsWith('storage'):
|
||||
return MenuId.Storage
|
||||
default:
|
||||
return MenuId.GettingStarted
|
||||
}
|
||||
}
|
||||
|
||||
export default GuidesLayout
|
||||
@@ -1,6 +1,7 @@
|
||||
import Param from '~/components/Params'
|
||||
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template'
|
||||
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
|
||||
import { MDXRemoteBase } from '~/features/docs/MdxBase'
|
||||
import specAnalyticsV0 from '~/spec/analytics_v0_config.yaml' assert { type: 'yml' }
|
||||
|
||||
const meta = {
|
||||
@@ -23,7 +24,7 @@ const AnalyticsConfigPage = async () => {
|
||||
'supabase/supabase/blob/master/apps/docs/pages/guides/self-hosting/analytics/config.tsx'
|
||||
)}
|
||||
>
|
||||
<MDXRemoteGuides source={descriptionMdx} />
|
||||
<MDXRemoteBase source={descriptionMdx} />
|
||||
|
||||
<div>
|
||||
{specAnalyticsV0.info.tags.map(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Param from '~/components/Params'
|
||||
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template'
|
||||
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
|
||||
import { MDXRemoteBase } from '~/features/docs/MdxBase'
|
||||
import specAuthV1 from '~/spec/gotrue_v1_config.yaml' assert { type: 'yml' }
|
||||
|
||||
const meta = {
|
||||
@@ -23,7 +24,7 @@ const AuthConfigPage = async () => {
|
||||
'supabase/supabase/blob/master/apps/docs/pages/guides/self-hosting/auth/config.tsx'
|
||||
)}
|
||||
>
|
||||
<MDXRemoteGuides source={descriptionMdx} />
|
||||
<MDXRemoteBase source={descriptionMdx} />
|
||||
|
||||
<div>
|
||||
{specAuthV1.info.tags.map((tag: ReturnType<typeof specAuthV1>['info']['tags']) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Param from '~/components/Params'
|
||||
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template'
|
||||
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
|
||||
import { MDXRemoteBase } from '~/features/docs/MdxBase'
|
||||
import specRealtimeV0 from '~/spec/realtime_v0_config.yaml' assert { type: 'yml' }
|
||||
|
||||
const meta = {
|
||||
@@ -23,7 +24,7 @@ const RealtimeConfigPage = async () => {
|
||||
'supabase/supabase/blob/master/apps/docs/pages/guides/self-hosting/realtime/config.tsx'
|
||||
)}
|
||||
>
|
||||
<MDXRemoteGuides source={descriptionMdx} />
|
||||
<MDXRemoteBase source={descriptionMdx} />
|
||||
|
||||
<div>
|
||||
{specRealtimeV0.info.tags.map((tag: ReturnType<typeof specRealtimeV0>['info']['tags']) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Param from '~/components/Params'
|
||||
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template'
|
||||
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
|
||||
import { MDXRemoteBase } from '~/features/docs/MdxBase'
|
||||
import specStorageV0 from '~/spec/storage_v0_config.yaml' assert { type: 'yml' }
|
||||
|
||||
const meta = {
|
||||
@@ -23,7 +24,7 @@ const StorageConfigPage = async () => {
|
||||
'supabase/supabase/blob/master/apps/docs/pages/guides/self-hosting/storage/config.tsx'
|
||||
)}
|
||||
>
|
||||
<MDXRemoteGuides source={descriptionMdx} />
|
||||
<MDXRemoteBase source={descriptionMdx} />
|
||||
|
||||
<div>
|
||||
{specStorageV0.info.tags.map((tag: ReturnType<typeof specStorageV0>['info']['tags']) => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { type Metadata, type Viewport } from 'next'
|
||||
|
||||
import { BASE_PATH } from '~/lib/constants'
|
||||
import { GlobalProviders } from '~/features/app.providers'
|
||||
import { TopNavSkeleton } from '~/layouts/MainSkeleton'
|
||||
|
||||
const metadata: Metadata = {
|
||||
applicationName: 'Supabase Docs',
|
||||
@@ -45,7 +46,9 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<GlobalProviders>{children}</GlobalProviders>
|
||||
<GlobalProviders>
|
||||
<TopNavSkeleton>{children}</TopNavSkeleton>
|
||||
</GlobalProviders>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type Metadata } from 'next'
|
||||
import { type PropsWithChildren } from 'react'
|
||||
import { LayoutMainContent } from '~/layouts/DefaultLayout'
|
||||
import { MainSkeleton } from '~/layouts/MainSkeleton'
|
||||
import { SidebarSkeleton } from '~/layouts/MainSkeleton'
|
||||
|
||||
const metadata: Metadata = {
|
||||
title: 'Not found',
|
||||
@@ -11,9 +11,9 @@ const metadata: Metadata = {
|
||||
}
|
||||
|
||||
const NotFoundLayout = ({ children }: PropsWithChildren) => (
|
||||
<MainSkeleton>
|
||||
<SidebarSkeleton>
|
||||
<LayoutMainContent>{children}</LayoutMainContent>
|
||||
</MainSkeleton>
|
||||
</SidebarSkeleton>
|
||||
)
|
||||
|
||||
export default NotFoundLayout
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from 'ui'
|
||||
import { getMenuId } from '../app/guides/layout'
|
||||
import { getMenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu.utils'
|
||||
import { useBreakpoint } from 'common'
|
||||
import * as NavItems from './Navigation/NavigationMenu/NavigationMenu.constants'
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import {
|
||||
IconMenuDevCli,
|
||||
IconGitHub,
|
||||
IconSupport,
|
||||
IconTerraform,
|
||||
IconTroubleshooting,
|
||||
IconBranching,
|
||||
} from './MenuIcons'
|
||||
@@ -89,6 +88,8 @@ function getMenuIcon(menuKey: string, width: number = 16, height: number = 16, c
|
||||
return <IconGitHub width={width} height={height} className={className} />
|
||||
case 'support':
|
||||
return <IconSupport width={width} height={height} className={className} />
|
||||
case 'contributing':
|
||||
return <IconTroubleshooting width={width} height={height} className={className} />
|
||||
default:
|
||||
return <IconMenuPlatform width={width} height={height} className={className} />
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { IS_DEV } from '~/lib/constants'
|
||||
import type { GlobalMenuItems, NavMenuConstant, References } from '../Navigation.types'
|
||||
|
||||
export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [
|
||||
@@ -191,6 +192,11 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [
|
||||
icon: 'status',
|
||||
href: 'https://status.supabase.com/',
|
||||
},
|
||||
{
|
||||
label: 'Contributing',
|
||||
icon: 'contributing',
|
||||
href: '/contributing' as `/${string}`,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import type { ICommonItem } from '~/components/reference/Reference.types'
|
||||
import type { Json } from '~/types'
|
||||
import { menuState } from '../../../hooks/useMenuState'
|
||||
@@ -105,3 +106,38 @@ export const useCloseMenuOnRouteChange = () => {
|
||||
menuState.setMenuMobileOpen(false)
|
||||
}, [pathname])
|
||||
}
|
||||
|
||||
export const getMenuId = (pathname: string | null) => {
|
||||
pathname = (pathname ??= '').replace(/^\/guides\//, '')
|
||||
|
||||
switch (true) {
|
||||
case pathname.startsWith('ai'):
|
||||
return MenuId.Ai
|
||||
case pathname.startsWith('api'):
|
||||
return MenuId.Api
|
||||
case pathname.startsWith('auth'):
|
||||
return MenuId.Auth
|
||||
case pathname.startsWith('cli'):
|
||||
return MenuId.Cli
|
||||
case pathname.startsWith('database'):
|
||||
return MenuId.Database
|
||||
case pathname.startsWith('functions'):
|
||||
return MenuId.Functions
|
||||
case pathname.startsWith('getting-started'):
|
||||
return MenuId.GettingStarted
|
||||
case pathname.startsWith('graphql'):
|
||||
return MenuId.Graphql
|
||||
case pathname.startsWith('platform'):
|
||||
return MenuId.Platform
|
||||
case pathname.startsWith('realtime'):
|
||||
return MenuId.Realtime
|
||||
case pathname.startsWith('resources'):
|
||||
return MenuId.Resources
|
||||
case pathname.startsWith('self-hosting'):
|
||||
return MenuId.SelfHosting
|
||||
case pathname.startsWith('storage'):
|
||||
return MenuId.Storage
|
||||
default:
|
||||
return MenuId.GettingStarted
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import React, { Fragment } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { Menu } from 'lucide-react'
|
||||
@@ -72,7 +72,7 @@ const TopNavDropdown = () => {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent side="bottom" align="end" className="w-64">
|
||||
{menu.map((menuSection, sectionIdx) => (
|
||||
<>
|
||||
<Fragment key={`topnav--${sectionIdx}`}>
|
||||
{sectionIdx !== 0 && <DropdownMenuSeparator key={`topnav--${sectionIdx}`} />}
|
||||
{menuSection.map((sectionItem, itemIdx) => (
|
||||
<Link
|
||||
@@ -88,7 +88,7 @@ const TopNavDropdown = () => {
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
))}
|
||||
</>
|
||||
</Fragment>
|
||||
))}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
|
||||
@@ -11,7 +11,7 @@ import ApiOperationSection from './ApiOperationSection'
|
||||
import CliCommandSection from './CLICommandSection'
|
||||
import OldVersionAlert from './OldVersionAlert'
|
||||
import type { IAPISpec, ICommonSection, IRefStaticDoc, ISpec, TypeSpec } from './Reference.types'
|
||||
import { MainSkeleton } from '~/layouts/MainSkeleton'
|
||||
import { SidebarSkeleton, TopNavSkeleton } from '~/layouts/MainSkeleton'
|
||||
import MgmtApiOperationSection from '~/components/reference/MgmtApiOperationSection'
|
||||
|
||||
interface RefSectionHandlerProps {
|
||||
@@ -80,66 +80,68 @@ const RefSectionHandler = (props: RefSectionHandlerProps) => {
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="canonical" href={`https://supabase.com${router.basePath}${path}`} />
|
||||
</Head>
|
||||
<MainSkeleton menuId={props.menuId}>
|
||||
{props.isOldVersion && <OldVersionAlert sections={props.sections} />}
|
||||
<RefSubLayout>
|
||||
{props.sections.map((section, i) => {
|
||||
const sectionType = section.type
|
||||
switch (sectionType) {
|
||||
case 'markdown':
|
||||
const markdownData = props.pageProps.docs.find((doc) => doc.id === section.id)
|
||||
<TopNavSkeleton>
|
||||
<SidebarSkeleton menuId={props.menuId}>
|
||||
{props.isOldVersion && <OldVersionAlert sections={props.sections} />}
|
||||
<RefSubLayout>
|
||||
{props.sections.map((section, i) => {
|
||||
const sectionType = section.type
|
||||
switch (sectionType) {
|
||||
case 'markdown':
|
||||
const markdownData = props.pageProps.docs.find((doc) => doc.id === section.id)
|
||||
|
||||
return (
|
||||
<RefEducationSection
|
||||
key={section.id + i}
|
||||
item={section}
|
||||
markdownContent={markdownData}
|
||||
/>
|
||||
)
|
||||
case 'function':
|
||||
return (
|
||||
<RefFunctionSection
|
||||
key={section.id + i}
|
||||
funcData={section}
|
||||
commonFuncData={section}
|
||||
spec={props.spec}
|
||||
typeSpec={props.typeSpec}
|
||||
/>
|
||||
)
|
||||
case 'cli-command':
|
||||
return (
|
||||
<CliCommandSection
|
||||
key={section.id + i}
|
||||
funcData={section}
|
||||
commonFuncData={section}
|
||||
/>
|
||||
)
|
||||
case 'operation':
|
||||
if (props.type === 'mgmt-api') {
|
||||
return (
|
||||
<MgmtApiOperationSection
|
||||
<RefEducationSection
|
||||
key={section.id + i}
|
||||
item={section}
|
||||
markdownContent={markdownData}
|
||||
/>
|
||||
)
|
||||
case 'function':
|
||||
return (
|
||||
<RefFunctionSection
|
||||
key={section.id + i}
|
||||
funcData={section}
|
||||
commonFuncData={section}
|
||||
spec={props.spec}
|
||||
typeSpec={props.typeSpec}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
case 'cli-command':
|
||||
return (
|
||||
<ApiOperationSection
|
||||
<CliCommandSection
|
||||
key={section.id + i}
|
||||
funcData={section}
|
||||
commonFuncData={section}
|
||||
spec={props.spec}
|
||||
/>
|
||||
)
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unknown common section type '${sectionType}'`)
|
||||
}
|
||||
})}
|
||||
</RefSubLayout>
|
||||
</MainSkeleton>
|
||||
case 'operation':
|
||||
if (props.type === 'mgmt-api') {
|
||||
return (
|
||||
<MgmtApiOperationSection
|
||||
key={section.id + i}
|
||||
funcData={section}
|
||||
commonFuncData={section}
|
||||
spec={props.spec}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<ApiOperationSection
|
||||
key={section.id + i}
|
||||
funcData={section}
|
||||
commonFuncData={section}
|
||||
spec={props.spec}
|
||||
/>
|
||||
)
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unknown common section type '${sectionType}'`)
|
||||
}
|
||||
})}
|
||||
</RefSubLayout>
|
||||
</SidebarSkeleton>
|
||||
</TopNavSkeleton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { MDXProvider } from '@mdx-js/react'
|
||||
import { type PropsWithChildren } from 'react'
|
||||
import { components } from '~/features/docs/mdx.shared'
|
||||
import { components } from '~/features/docs/MdxBase.shared'
|
||||
|
||||
const MDXProviderGuides = ({ children }: PropsWithChildren) => (
|
||||
<MDXProvider components={components}>{children}</MDXProvider>
|
||||
|
||||
@@ -1,64 +1,15 @@
|
||||
import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' }
|
||||
import { remarkCodeHike, type CodeHikeConfig } from '@code-hike/mdx'
|
||||
import { ExternalLink } from 'lucide-react'
|
||||
import { type SerializeOptions } from 'next-mdx-remote/dist/types'
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc'
|
||||
import { type ComponentProps, type ReactNode } from 'react'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import rehypeKatex from 'rehype-katex'
|
||||
import remarkMath from 'remark-math'
|
||||
import { type ReactNode } from 'react'
|
||||
|
||||
import { cn } from 'ui'
|
||||
|
||||
import Breadcrumbs from '~/components/Breadcrumbs'
|
||||
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
|
||||
import { components } from '~/features/docs/mdx.shared'
|
||||
import { MDXProviderGuides } from '~/features/docs/GuidesMdx.client'
|
||||
import { MDXRemoteBase } from '~/features/docs/MdxBase'
|
||||
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,
|
||||
lineNumbers: true,
|
||||
showCopyButton: true,
|
||||
skipLanguages: [],
|
||||
autoImport: false,
|
||||
}
|
||||
|
||||
const mdxOptions: SerializeOptions = {
|
||||
mdxOptions: {
|
||||
useDynamicImport: true,
|
||||
remarkPlugins: [
|
||||
[remarkMath, { singleDollarTextMath: false }],
|
||||
remarkGfm,
|
||||
[remarkCodeHike, codeHikeOptions],
|
||||
],
|
||||
rehypePlugins: [rehypeKatex as any],
|
||||
},
|
||||
}
|
||||
|
||||
const MDXRemoteGuides = ({ options = {}, ...props }: ComponentProps<typeof MDXRemote>) => {
|
||||
const { mdxOptions: { remarkPlugins, rehypePlugins, ...otherMdxOptions } = {}, ...otherOptions } =
|
||||
options
|
||||
const {
|
||||
mdxOptions: {
|
||||
remarkPlugins: originalRemarkPlugins,
|
||||
rehypePlugins: originalRehypePlugins,
|
||||
...originalMdxOptions
|
||||
} = {},
|
||||
} = mdxOptions
|
||||
|
||||
const finalOptions = {
|
||||
...mdxOptions,
|
||||
...otherOptions,
|
||||
mdxOptions: {
|
||||
...originalMdxOptions,
|
||||
...otherMdxOptions,
|
||||
remarkPlugins: [...(originalRemarkPlugins ?? []), ...(remarkPlugins ?? [])],
|
||||
rehypePlugins: [...(originalRehypePlugins ?? []), ...(rehypePlugins ?? [])],
|
||||
},
|
||||
} as SerializeOptions
|
||||
|
||||
return <MDXRemote components={components} options={finalOptions} {...props} />
|
||||
}
|
||||
|
||||
const EDIT_LINK_SYMBOL = Symbol('edit link')
|
||||
interface EditLink {
|
||||
@@ -133,7 +84,7 @@ const GuideTemplate = ({ meta, content, children, editLink, mdxOptions }: GuideT
|
||||
)}
|
||||
<hr className="not-prose border-t-0 border-b my-8" />
|
||||
<MDXProviderGuides>
|
||||
{content && <MDXRemoteGuides source={content} options={mdxOptions} />}
|
||||
{content && <MDXRemoteBase source={content} options={mdxOptions} />}
|
||||
</MDXProviderGuides>
|
||||
{children}
|
||||
<footer className="mt-16 not-prose">
|
||||
@@ -177,4 +128,4 @@ const GuideTemplate = ({ meta, content, children, editLink, mdxOptions }: GuideT
|
||||
)
|
||||
}
|
||||
|
||||
export { GuideTemplate, MDXRemoteGuides, newEditLink }
|
||||
export { GuideTemplate, newEditLink }
|
||||
File renamed without changes.
@@ -0,0 +1,57 @@
|
||||
import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' }
|
||||
import { remarkCodeHike, type CodeHikeConfig } from '@code-hike/mdx'
|
||||
import { type SerializeOptions } from 'next-mdx-remote/dist/types'
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc'
|
||||
import { type ComponentProps } from 'react'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import rehypeKatex from 'rehype-katex'
|
||||
import remarkMath from 'remark-math'
|
||||
|
||||
import { components } from '~/features/docs/MdxBase.shared'
|
||||
|
||||
const codeHikeOptions: CodeHikeConfig = {
|
||||
theme: codeHikeTheme,
|
||||
lineNumbers: true,
|
||||
showCopyButton: true,
|
||||
skipLanguages: [],
|
||||
autoImport: false,
|
||||
}
|
||||
|
||||
const mdxOptions: SerializeOptions = {
|
||||
mdxOptions: {
|
||||
useDynamicImport: true,
|
||||
remarkPlugins: [
|
||||
[remarkMath, { singleDollarTextMath: false }],
|
||||
remarkGfm,
|
||||
[remarkCodeHike, codeHikeOptions],
|
||||
],
|
||||
rehypePlugins: [rehypeKatex as any],
|
||||
},
|
||||
}
|
||||
|
||||
const MDXRemoteBase = ({ options = {}, ...props }: ComponentProps<typeof MDXRemote>) => {
|
||||
const { mdxOptions: { remarkPlugins, rehypePlugins, ...otherMdxOptions } = {}, ...otherOptions } =
|
||||
options
|
||||
const {
|
||||
mdxOptions: {
|
||||
remarkPlugins: originalRemarkPlugins,
|
||||
rehypePlugins: originalRehypePlugins,
|
||||
...originalMdxOptions
|
||||
} = {},
|
||||
} = mdxOptions
|
||||
|
||||
const finalOptions = {
|
||||
...mdxOptions,
|
||||
...otherOptions,
|
||||
mdxOptions: {
|
||||
...originalMdxOptions,
|
||||
...otherMdxOptions,
|
||||
remarkPlugins: [...(originalRemarkPlugins ?? []), ...(remarkPlugins ?? [])],
|
||||
rehypePlugins: [...(originalRehypePlugins ?? []), ...(rehypePlugins ?? [])],
|
||||
},
|
||||
} as SerializeOptions
|
||||
|
||||
return <MDXRemote components={components} options={finalOptions} {...props} />
|
||||
}
|
||||
|
||||
export { MDXRemoteBase }
|
||||
@@ -2,11 +2,11 @@ import { type PropsWithChildren } from 'react'
|
||||
|
||||
import HomePageCover from '~/components/HomePageCover'
|
||||
import { LayoutMainContent } from './DefaultLayout'
|
||||
import { MainSkeleton } from './MainSkeleton'
|
||||
import { SidebarSkeleton } from './MainSkeleton'
|
||||
|
||||
const HomeLayout = ({ children }: PropsWithChildren) => {
|
||||
return (
|
||||
<MainSkeleton>
|
||||
<SidebarSkeleton>
|
||||
<article>
|
||||
<HomePageCover title="Supabase Documentation" />
|
||||
<LayoutMainContent>
|
||||
@@ -15,7 +15,7 @@ const HomeLayout = ({ children }: PropsWithChildren) => {
|
||||
</div>
|
||||
</LayoutMainContent>
|
||||
</article>
|
||||
</MainSkeleton>
|
||||
</SidebarSkeleton>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
'use client'
|
||||
|
||||
import { type PropsWithChildren, memo, useEffect, useRef } from 'react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { memo, useEffect, type PropsWithChildren, type ReactNode } from 'react'
|
||||
|
||||
import { cn } from 'ui'
|
||||
|
||||
import DefaultNavigationMenu, {
|
||||
MenuId,
|
||||
} from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import TopNavBar from '~/components/Navigation/NavigationMenu/TopNavBar'
|
||||
import { DOCS_CONTENT_CONTAINER_ID } from '~/features/ui/helpers.constants'
|
||||
import { menuState, useMenuMobileOpen } from '~/hooks/useMenuState'
|
||||
|
||||
import { type MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import TopNavBar from '~/components/Navigation/NavigationMenu/TopNavBar'
|
||||
|
||||
const Footer = dynamic(() => import('~/components/Navigation/Footer'))
|
||||
const NavigationMenu = dynamic(
|
||||
() => import('~/components/Navigation/NavigationMenu/NavigationMenu')
|
||||
@@ -264,7 +267,7 @@ const Container = memo(function Container({
|
||||
)
|
||||
})
|
||||
|
||||
const NavContainer = memo(function NavContainer({ menuId }: { menuId: MenuId }) {
|
||||
const NavContainer = memo(function NavContainer({ children }: PropsWithChildren) {
|
||||
const mobileMenuOpen = useMenuMobileOpen()
|
||||
|
||||
return (
|
||||
@@ -313,53 +316,65 @@ const NavContainer = memo(function NavContainer({ menuId }: { menuId: MenuId })
|
||||
'lg:opacity-100 lg:visible'
|
||||
)}
|
||||
>
|
||||
<NavigationMenu menuId={menuId} />
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
})
|
||||
|
||||
function MainSkeleton({ children, menuId }: PropsWithChildren<{ menuId?: MenuId }>) {
|
||||
const ref = useRef(null)
|
||||
const mobileMenuOpen = useMenuMobileOpen()
|
||||
const hideSideNav = !menuId
|
||||
interface SkeletonProps extends PropsWithChildren {
|
||||
menuId?: MenuId
|
||||
NavigationMenu?: ReactNode
|
||||
}
|
||||
|
||||
function TopNavSkeleton({ children }) {
|
||||
return (
|
||||
<div className="flex flex-col h-full w-full">
|
||||
<div ref={ref} className="hidden lg:sticky w-full lg:flex top-0 left-0 right-0 z-50">
|
||||
<div className="hidden lg:sticky w-full lg:flex top-0 left-0 right-0 z-50">
|
||||
<TopNavBar />
|
||||
</div>
|
||||
<div className="flex flex-row h-full relative">
|
||||
{!hideSideNav && <NavContainer menuId={menuId} />}
|
||||
<Container>
|
||||
<div
|
||||
className={cn(
|
||||
'flex lg:hidden w-full top-0 left-0 right-0 z-50',
|
||||
hideSideNav && 'sticky',
|
||||
mobileMenuOpen && 'z-10'
|
||||
)}
|
||||
>
|
||||
<TopNavBar />
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'sticky',
|
||||
'transition-all top-0 z-10',
|
||||
'backdrop-blur backdrop-filter bg-background'
|
||||
)}
|
||||
>
|
||||
{!hideSideNav && <MobileHeader menuId={menuId} />}
|
||||
</div>
|
||||
<div className="grow">
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
<MobileMenuBackdrop />
|
||||
</Container>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { MainSkeleton }
|
||||
function SidebarSkeleton({ children, menuId, NavigationMenu }: SkeletonProps) {
|
||||
const mobileMenuOpen = useMenuMobileOpen()
|
||||
const hideSideNav = !menuId
|
||||
|
||||
return (
|
||||
<div className="flex flex-row h-full relative">
|
||||
{!hideSideNav && (
|
||||
<NavContainer>{NavigationMenu ?? <DefaultNavigationMenu menuId={menuId} />}</NavContainer>
|
||||
)}
|
||||
<Container>
|
||||
<div
|
||||
className={cn(
|
||||
'flex lg:hidden w-full top-0 left-0 right-0 z-50',
|
||||
hideSideNav && 'sticky',
|
||||
mobileMenuOpen && 'z-10'
|
||||
)}
|
||||
>
|
||||
<TopNavBar />
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'sticky',
|
||||
'transition-all top-0 z-10',
|
||||
'backdrop-blur backdrop-filter bg-background'
|
||||
)}
|
||||
>
|
||||
{!hideSideNav && <MobileHeader menuId={menuId} />}
|
||||
</div>
|
||||
<div className="grow">
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
<MobileMenuBackdrop />
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { TopNavSkeleton, SidebarSkeleton }
|
||||
@@ -7,7 +7,7 @@ import { type FC } from 'react'
|
||||
import { FooterHelpCalloutType } from '~/components/FooterHelpCallout'
|
||||
import { type MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import { LayoutMainContent } from '~/layouts/DefaultLayout'
|
||||
import { MainSkeleton } from '~/layouts/MainSkeleton'
|
||||
import { SidebarSkeleton } from '~/layouts/MainSkeleton'
|
||||
|
||||
interface Props {
|
||||
meta?: {
|
||||
@@ -33,11 +33,9 @@ const Layout: FC<Props> = (props) => {
|
||||
const menuId = props.menuId
|
||||
|
||||
return (
|
||||
<>
|
||||
<MainSkeleton menuId={menuId}>
|
||||
<LayoutMainContent className="pb-0">{props.children}</LayoutMainContent>
|
||||
</MainSkeleton>
|
||||
</>
|
||||
<SidebarSkeleton menuId={menuId}>
|
||||
<LayoutMainContent className="pb-0">{props.children}</LayoutMainContent>
|
||||
</SidebarSkeleton>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user