mirror of
https://github.com/supabase/supabase.git
synced 2026-05-06 08:56:46 -04:00
feat: Support subcategories. Update definitions and partials.
This commit is contained in:
@@ -1,8 +1,16 @@
|
||||
import { type PropsWithChildren } from 'react'
|
||||
'use client'
|
||||
|
||||
import MenuIconPicker from '~/components/Navigation/NavigationMenu/MenuIconPicker'
|
||||
import { type AbbrevApiReferenceSection } from '~/features/docs/Reference.utils'
|
||||
import { ChevronDown } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useState, type PropsWithChildren } from 'react'
|
||||
import {
|
||||
Badge,
|
||||
cn,
|
||||
Collapsible_Shadcn_ as Collapsible,
|
||||
CollapsibleContent_Shadcn_ as CollapsibleContent,
|
||||
CollapsibleTrigger_Shadcn_ as CollapsibleTrigger,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
@@ -10,10 +18,6 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from 'ui'
|
||||
|
||||
import MenuIconPicker from '~/components/Navigation/NavigationMenu/MenuIconPicker'
|
||||
import { type AbbrevApiReferenceSection } from '~/features/docs/Reference.utils'
|
||||
import { ChevronDown } from 'lucide-react'
|
||||
|
||||
interface ReferenceNavigationProps {
|
||||
name: string
|
||||
icon: string
|
||||
@@ -24,7 +28,7 @@ interface ReferenceNavigationProps {
|
||||
sections: AbbrevApiReferenceSection[]
|
||||
}
|
||||
|
||||
export async function ReferenceNavigation({
|
||||
export function ReferenceNavigation({
|
||||
name,
|
||||
icon,
|
||||
library,
|
||||
@@ -44,7 +48,7 @@ export async function ReferenceNavigation({
|
||||
</div>
|
||||
<ul className="flex flex-col gap-2">
|
||||
{sections?.map((section, index) =>
|
||||
section.type === 'category' ? (
|
||||
section.items ? (
|
||||
<li key={`${section.slug}-${String(index)}`}>
|
||||
<RefCategory basePath={basePath} section={section} />
|
||||
</li>
|
||||
@@ -84,23 +88,56 @@ function RefCategory({
|
||||
<>
|
||||
<Divider />
|
||||
{'title' in section && section.slug ? (
|
||||
<a href={`${basePath}#${section.slug}`}>
|
||||
<a href={`#${section.slug}`}>
|
||||
<SideMenuTitle className="py-2">{section.title}</SideMenuTitle>
|
||||
</a>
|
||||
) : (
|
||||
'title' in section && <SideMenuTitle className="py-2">{section.title}</SideMenuTitle>
|
||||
)}
|
||||
<ul className="space-y-2">
|
||||
{section.items?.map((item, index) => (
|
||||
<li key={`${item.slug}-${String(index)}`} className="leading-5">
|
||||
<ReferenceLink section={item} />
|
||||
</li>
|
||||
))}
|
||||
{section.items?.map((item, index) =>
|
||||
item.items?.length ? (
|
||||
<li key={`${item.slug}-${String(index)}`}>
|
||||
<NavSubcategory section={item} />
|
||||
</li>
|
||||
) : (
|
||||
<li key={`${item.slug}-${String(index)}`} className="leading-5">
|
||||
<ReferenceLink section={item} />
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function NavSubcategory({ section }: { section: AbbrevApiReferenceSection }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<Collapsible open={open} onOpenChange={setOpen}>
|
||||
<CollapsibleTrigger className="flex items-center justify-between gap-1 w-full text-left group py-0.5">
|
||||
<span className="text-sm text-foreground-light group-hover:text-foreground transition-colors">
|
||||
{section.title}
|
||||
</span>
|
||||
<ChevronDown
|
||||
size={13}
|
||||
className={cn('transition-transform shrink-0', open ? 'rotate-0' : '-rotate-90')}
|
||||
/>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<ul className="pl-4 mt-1 space-y-1 border-l border-control">
|
||||
{section.items?.map((item, index) => (
|
||||
<li key={`${item.slug}-${String(index)}`} className="leading-5">
|
||||
<ReferenceLink section={item} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
)
|
||||
}
|
||||
|
||||
function Divider() {
|
||||
return <hr className="w-full h-px my-3 bg-control" />
|
||||
}
|
||||
|
||||
@@ -105,20 +105,56 @@ function buildSections(categories: SpecCategory[], specDir: string): object[] {
|
||||
}))
|
||||
: []
|
||||
|
||||
// Group definitions by subcategory, preserving insertion order.
|
||||
// Deduplicate by name within each group (same method can appear on multiple classes).
|
||||
// Definitions without a subcategory are emitted flat; those with one are
|
||||
// collected into a subcategory node the first time the subcategory is seen.
|
||||
const defItems: object[] = []
|
||||
const seenFlat = new Set<string>()
|
||||
const subcatIndex = new Map<string, { items: object[]; seen: Set<string> }>()
|
||||
|
||||
for (const def of definitions) {
|
||||
const fnItem = {
|
||||
id: `${catSlug}-${toSlug(def.name)}`,
|
||||
slug: `${catSlug}-${toSlug(def.name)}`,
|
||||
type: 'function',
|
||||
title: def.name,
|
||||
}
|
||||
if (def.subcategory) {
|
||||
if (!subcatIndex.has(def.subcategory)) {
|
||||
const subcatItems: object[] = []
|
||||
subcatIndex.set(def.subcategory, { items: subcatItems, seen: new Set() })
|
||||
defItems.push({
|
||||
id: `${catSlug}-${toSlug(def.subcategory)}`,
|
||||
slug: `${catSlug}-${toSlug(def.subcategory)}`,
|
||||
type: 'category',
|
||||
title: def.subcategory,
|
||||
items: subcatItems,
|
||||
})
|
||||
}
|
||||
const group = subcatIndex.get(def.subcategory)!
|
||||
if (!group.seen.has(def.name)) {
|
||||
group.seen.add(def.name)
|
||||
group.items.push(fnItem)
|
||||
}
|
||||
} else {
|
||||
if (!seenFlat.has(def.name)) {
|
||||
seenFlat.add(def.name)
|
||||
defItems.push(fnItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Subcategory nodes go last so flat function links appear first in navigation
|
||||
const flatItems = defItems.filter((item: any) => !item.items)
|
||||
const subcatItems = defItems.filter((item: any) => item.items)
|
||||
|
||||
return {
|
||||
id: catSlug,
|
||||
slug: catSlug,
|
||||
type: 'category',
|
||||
title: category,
|
||||
items: [
|
||||
...partialH2Items,
|
||||
...definitions.map((def) => ({
|
||||
id: `${catSlug}-${toSlug(def.name)}`,
|
||||
slug: `${catSlug}-${toSlug(def.name)}`,
|
||||
type: 'function',
|
||||
title: def.name,
|
||||
})),
|
||||
],
|
||||
items: [...partialH2Items, ...flatItems, ...subcatItems],
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
* TypeDoc declarations, and returns structured categories with definitions.
|
||||
*/
|
||||
|
||||
import { readFileSync, readdirSync } from 'fs'
|
||||
import { readdirSync, readFileSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
|
||||
import type { IgnoreDefinition, OverrideDefinition, SpecCategory, SpecConfig } from '../types.js'
|
||||
|
||||
type ContentItem = { kind: string; text: string }
|
||||
@@ -304,9 +305,16 @@ export function processSpec(specDir: string): { categories: SpecCategory[]; conf
|
||||
const sig = decl.signatures[0]
|
||||
const remarkTags = blockTags.filter((t) => t.tag === '@remarks')
|
||||
const examples = parseExamples(blockTags)
|
||||
const subcategoryTag = blockTags.find((t) => t.tag === '@subcategory')
|
||||
const subcategory =
|
||||
subcategoryTag?.content
|
||||
?.map((c) => c.text)
|
||||
.join('')
|
||||
.trim() || undefined
|
||||
|
||||
const definition: any = {
|
||||
name: decl.name,
|
||||
...(subcategory ? { subcategory } : {}),
|
||||
description: contentToMd(decl.comment?.summary ?? []),
|
||||
...(remarkTags.length
|
||||
? { remarks: remarkTags.map((t) => contentToMd(t.content ?? [])) }
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
"version": "v2",
|
||||
"name": "JavaScript",
|
||||
"ignoreDefinitions": ["constructor"],
|
||||
"categoryOrder": ["Database", "Auth", "Functions", "Realtime", "Storage"]
|
||||
"categoryOrder": ["Database", "Auth", "Edge Functions", "Realtime", "Storage"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
## CORS headers for Edge Functions
|
||||
|
||||
Default CORS headers for Supabase Edge Functions.
|
||||
|
||||
Includes all headers sent by Supabase client libraries and allows all standard HTTP methods. Use this for simple CORS configurations with wildcard origin.
|
||||
|
||||
```ts
|
||||
import { corsHeaders } from '@supabase/supabase-js/cors'
|
||||
|
||||
Deno.serve(async (req) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
return new Response('ok', { headers: corsHeaders })
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify({ data: 'Hello' }), {
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
})
|
||||
})
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
+14072
-9039
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+50091
-14066
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user