feat: Support subcategories. Update definitions and partials.

This commit is contained in:
Jeremias Menichelli
2026-05-05 21:06:58 +02:00
parent 4207448b55
commit fdbddbabce
11 changed files with 81417 additions and 35479 deletions
@@ -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
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
File diff suppressed because it is too large Load Diff