@usecross/docs 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +158 -5
- package/dist/index.js +1260 -31
- package/dist/index.js.map +1 -1
- package/dist/ssr.d.ts +1 -1
- package/dist/types-_anC1UJu.d.ts +320 -0
- package/package.json +1 -1
- package/src/components/Sidebar.tsx +171 -28
- package/src/components/TableOfContents.tsx +1 -1
- package/src/components/api/APILayout.tsx +231 -0
- package/src/components/api/APIPage.tsx +216 -0
- package/src/components/api/Breadcrumb.tsx +98 -0
- package/src/components/api/ClassDoc.tsx +257 -0
- package/src/components/api/CodeSpan.tsx +53 -0
- package/src/components/api/Docstring.tsx +269 -0
- package/src/components/api/FunctionDoc.tsx +130 -0
- package/src/components/api/ModuleDoc.tsx +298 -0
- package/src/components/api/ParameterTable.tsx +183 -0
- package/src/components/api/Signature.tsx +317 -0
- package/src/components/api/TableOfContents.tsx +50 -0
- package/src/components/api/index.ts +17 -0
- package/src/components/index.ts +12 -0
- package/src/index.ts +31 -0
- package/src/types.ts +218 -2
- package/dist/types-Qbhh2Ihz.d.ts +0 -136
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { Head, Link, usePage } from '@inertiajs/react'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
import { ThemeToggle } from '../ThemeToggle'
|
|
4
|
+
import { useTheme } from '../ThemeProvider'
|
|
5
|
+
import { MobileMenuButton } from '../DocsLayout'
|
|
6
|
+
import { Sidebar } from '../Sidebar'
|
|
7
|
+
import type { NavSection, SharedProps } from '../../types'
|
|
8
|
+
|
|
9
|
+
interface APILayoutProps {
|
|
10
|
+
children: React.ReactNode
|
|
11
|
+
title: string
|
|
12
|
+
apiNav: NavSection[]
|
|
13
|
+
currentPath: string
|
|
14
|
+
logoUrl?: string
|
|
15
|
+
logoInvertedUrl?: string
|
|
16
|
+
footerLogoUrl?: string
|
|
17
|
+
footerLogoInvertedUrl?: string
|
|
18
|
+
githubUrl?: string
|
|
19
|
+
navLinks?: Array<{ label: string; href: string }>
|
|
20
|
+
/** Right sidebar content (e.g., table of contents) */
|
|
21
|
+
rightSidebar?: React.ReactNode
|
|
22
|
+
/** Custom header component (replaces entire header). Can be a ReactNode or a function that receives mobile menu props. */
|
|
23
|
+
header?: React.ReactNode | ((props: { mobileMenuOpen: boolean; toggleMobileMenu: () => void }) => React.ReactNode)
|
|
24
|
+
/** Header height in pixels. Used to calculate content offset. Defaults to 64 (h-16). */
|
|
25
|
+
headerHeight?: number
|
|
26
|
+
/** Custom footer component */
|
|
27
|
+
footer?: React.ReactNode
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Shared props type for API pages */
|
|
31
|
+
interface APISharedProps extends SharedProps {
|
|
32
|
+
apiNav?: NavSection[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function GitHubIcon() {
|
|
36
|
+
return (
|
|
37
|
+
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
38
|
+
<path
|
|
39
|
+
fillRule="evenodd"
|
|
40
|
+
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
|
41
|
+
clipRule="evenodd"
|
|
42
|
+
/>
|
|
43
|
+
</svg>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Layout component for API documentation pages.
|
|
49
|
+
* Uses the shared Sidebar component with compact styling and collapsible sections.
|
|
50
|
+
*/
|
|
51
|
+
export function APILayout({
|
|
52
|
+
children,
|
|
53
|
+
title,
|
|
54
|
+
apiNav,
|
|
55
|
+
currentPath,
|
|
56
|
+
logoUrl: propLogoUrl,
|
|
57
|
+
logoInvertedUrl: propLogoInvertedUrl,
|
|
58
|
+
footerLogoUrl: propFooterLogoUrl,
|
|
59
|
+
footerLogoInvertedUrl: propFooterLogoInvertedUrl,
|
|
60
|
+
githubUrl: propGithubUrl,
|
|
61
|
+
navLinks: propNavLinks,
|
|
62
|
+
rightSidebar,
|
|
63
|
+
header,
|
|
64
|
+
headerHeight: propHeaderHeight = 64,
|
|
65
|
+
footer,
|
|
66
|
+
}: APILayoutProps) {
|
|
67
|
+
const sharedProps = usePage<{ props: APISharedProps }>().props as unknown as APISharedProps
|
|
68
|
+
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
|
69
|
+
const { resolvedTheme } = useTheme()
|
|
70
|
+
const headerHeight = propHeaderHeight
|
|
71
|
+
|
|
72
|
+
// Merge props - component props take precedence over shared props from Python
|
|
73
|
+
const logoUrl = propLogoUrl ?? sharedProps.logoUrl
|
|
74
|
+
const logoInvertedUrl = propLogoInvertedUrl ?? sharedProps.logoInvertedUrl
|
|
75
|
+
const githubUrl = propGithubUrl ?? sharedProps.githubUrl
|
|
76
|
+
const navLinks = propNavLinks ?? sharedProps.navLinks ?? []
|
|
77
|
+
|
|
78
|
+
const headerLogo = logoInvertedUrl ? (
|
|
79
|
+
<img src={logoInvertedUrl} alt="Logo" className="h-8" />
|
|
80
|
+
) : logoUrl ? (
|
|
81
|
+
<img src={logoUrl} alt="Logo" className="h-8" />
|
|
82
|
+
) : null
|
|
83
|
+
|
|
84
|
+
const footerLogoUrl = propFooterLogoUrl || sharedProps.footerLogoUrl || logoUrl
|
|
85
|
+
const footerLogoInvertedUrl = propFooterLogoInvertedUrl || sharedProps.footerLogoInvertedUrl || logoInvertedUrl
|
|
86
|
+
const currentFooterLogoUrl = resolvedTheme === 'dark' ? (footerLogoInvertedUrl || footerLogoUrl) : footerLogoUrl
|
|
87
|
+
const footerLogo = currentFooterLogoUrl ? (
|
|
88
|
+
<img src={currentFooterLogoUrl} alt="Logo" className="h-6" />
|
|
89
|
+
) : null
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<div className="min-h-screen bg-white dark:bg-[#0f0f0f] flex flex-col transition-colors duration-200">
|
|
93
|
+
<Head title={title} />
|
|
94
|
+
|
|
95
|
+
{/* Fixed navigation */}
|
|
96
|
+
{(typeof header === 'function'
|
|
97
|
+
? header({ mobileMenuOpen, toggleMobileMenu: () => setMobileMenuOpen(!mobileMenuOpen) })
|
|
98
|
+
: header) || (
|
|
99
|
+
<nav className="fixed w-full z-50 bg-white/95 dark:bg-[#0f0f0f]/95 backdrop-blur-sm border-b border-gray-200 dark:border-gray-800 transition-colors">
|
|
100
|
+
<div className="px-4 lg:px-10">
|
|
101
|
+
<div className="flex justify-between h-16 items-center">
|
|
102
|
+
<div className="flex items-center gap-2">
|
|
103
|
+
<MobileMenuButton onClick={() => setMobileMenuOpen(!mobileMenuOpen)} isOpen={mobileMenuOpen} />
|
|
104
|
+
{headerLogo ? (
|
|
105
|
+
<Link href="/" className="flex items-center">
|
|
106
|
+
{headerLogo}
|
|
107
|
+
</Link>
|
|
108
|
+
) : (
|
|
109
|
+
<Link href="/" className="font-bold text-lg text-gray-900 dark:text-white">
|
|
110
|
+
Docs
|
|
111
|
+
</Link>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
<div className="flex items-center gap-6">
|
|
115
|
+
<div className="-mr-2">
|
|
116
|
+
<ThemeToggle size="sm" />
|
|
117
|
+
</div>
|
|
118
|
+
{navLinks.map((link) => (
|
|
119
|
+
<Link
|
|
120
|
+
key={link.href}
|
|
121
|
+
href={link.href}
|
|
122
|
+
className="hidden sm:block text-gray-700 dark:text-gray-300 font-medium hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
|
|
123
|
+
>
|
|
124
|
+
{link.label}
|
|
125
|
+
</Link>
|
|
126
|
+
))}
|
|
127
|
+
{githubUrl && (
|
|
128
|
+
<a
|
|
129
|
+
href={githubUrl}
|
|
130
|
+
target="_blank"
|
|
131
|
+
rel="noopener noreferrer"
|
|
132
|
+
className="text-gray-700 dark:text-gray-300 hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
|
|
133
|
+
>
|
|
134
|
+
<GitHubIcon />
|
|
135
|
+
</a>
|
|
136
|
+
)}
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</nav>
|
|
141
|
+
)}
|
|
142
|
+
|
|
143
|
+
{/* Mobile sidebar */}
|
|
144
|
+
{mobileMenuOpen && (
|
|
145
|
+
<div className="fixed inset-0 z-40 lg:hidden">
|
|
146
|
+
<div className="fixed inset-0 bg-black/50 dark:bg-black/70" onClick={() => setMobileMenuOpen(false)} />
|
|
147
|
+
<div
|
|
148
|
+
className="fixed inset-y-0 left-0 w-72 overflow-y-auto bg-white dark:bg-[#0f0f0f] px-4 py-6 border-r border-gray-200 dark:border-gray-800 transition-colors"
|
|
149
|
+
style={{ paddingTop: headerHeight + 16 }}
|
|
150
|
+
>
|
|
151
|
+
<Sidebar
|
|
152
|
+
nav={apiNav}
|
|
153
|
+
currentPath={currentPath}
|
|
154
|
+
/>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
)}
|
|
158
|
+
|
|
159
|
+
{/* Main content area */}
|
|
160
|
+
<div className="bg-white dark:bg-[#0f0f0f] w-full flex-1 transition-colors" style={{ paddingTop: headerHeight }}>
|
|
161
|
+
<div className="flex">
|
|
162
|
+
{/* Desktop sidebar */}
|
|
163
|
+
<aside
|
|
164
|
+
className="hidden lg:block w-64 shrink-0 border-r border-gray-200 dark:border-gray-800 transition-colors"
|
|
165
|
+
style={{ minHeight: `calc(100vh - ${headerHeight}px)` }}
|
|
166
|
+
>
|
|
167
|
+
<div
|
|
168
|
+
className="sticky px-6 py-6 overflow-y-auto"
|
|
169
|
+
style={{ top: headerHeight, maxHeight: `calc(100vh - ${headerHeight}px)` }}
|
|
170
|
+
>
|
|
171
|
+
<Sidebar
|
|
172
|
+
nav={apiNav}
|
|
173
|
+
currentPath={currentPath}
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
176
|
+
</aside>
|
|
177
|
+
|
|
178
|
+
{/* Right section: content + TOC + footer */}
|
|
179
|
+
<div className="flex-1 min-w-0 flex flex-col">
|
|
180
|
+
<div className="flex-1 p-4 lg:px-10 lg:py-6">
|
|
181
|
+
<div className="flex gap-5">
|
|
182
|
+
{/* Main content */}
|
|
183
|
+
<main className="min-w-0 w-full max-w-4xl">
|
|
184
|
+
{children}
|
|
185
|
+
</main>
|
|
186
|
+
|
|
187
|
+
{/* Table of Contents - desktop only */}
|
|
188
|
+
{rightSidebar && (
|
|
189
|
+
<aside className="hidden xl:block w-56 shrink-0 transition-colors">
|
|
190
|
+
<div
|
|
191
|
+
className="sticky overflow-y-auto"
|
|
192
|
+
style={{ top: headerHeight + 24, maxHeight: `calc(100vh - ${headerHeight + 24}px)` }}
|
|
193
|
+
>
|
|
194
|
+
{rightSidebar}
|
|
195
|
+
</div>
|
|
196
|
+
</aside>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
{/* Footer */}
|
|
202
|
+
{footer || (
|
|
203
|
+
<footer className="border-t border-gray-200 dark:border-gray-800 py-8 px-4 lg:px-10 transition-colors">
|
|
204
|
+
<div className="flex flex-col md:flex-row justify-between items-center gap-6">
|
|
205
|
+
{footerLogo && <Link href="/">{footerLogo}</Link>}
|
|
206
|
+
<div className="flex gap-8 text-sm text-gray-600 dark:text-gray-400">
|
|
207
|
+
{navLinks.map((link) => (
|
|
208
|
+
<Link key={link.href} href={link.href} className="hover:text-black dark:hover:text-white transition-colors">
|
|
209
|
+
{link.label}
|
|
210
|
+
</Link>
|
|
211
|
+
))}
|
|
212
|
+
{githubUrl && (
|
|
213
|
+
<a
|
|
214
|
+
href={githubUrl}
|
|
215
|
+
target="_blank"
|
|
216
|
+
rel="noopener noreferrer"
|
|
217
|
+
className="hover:text-black dark:hover:text-white transition-colors"
|
|
218
|
+
>
|
|
219
|
+
GitHub
|
|
220
|
+
</a>
|
|
221
|
+
)}
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
</footer>
|
|
225
|
+
)}
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { APIPageProps, GriffeModule, GriffeClass, GriffeFunction, GriffeMember, GriffeAlias } from '../../types'
|
|
2
|
+
import { APILayout } from './APILayout'
|
|
3
|
+
import { ModuleDoc } from './ModuleDoc'
|
|
4
|
+
import { ClassDoc } from './ClassDoc'
|
|
5
|
+
import { FunctionDoc } from './FunctionDoc'
|
|
6
|
+
import { TableOfContents, generateClassToc, type TocItem } from './TableOfContents'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolve an alias to its target in the API data
|
|
10
|
+
*/
|
|
11
|
+
function resolveAlias(alias: GriffeAlias, apiData: GriffeModule): GriffeMember | null {
|
|
12
|
+
const targetPath = alias.target_path
|
|
13
|
+
if (!targetPath) return null
|
|
14
|
+
|
|
15
|
+
// Split the target path into parts
|
|
16
|
+
const parts = targetPath.split('.')
|
|
17
|
+
const packageName = apiData.name
|
|
18
|
+
|
|
19
|
+
let current: GriffeModule | GriffeClass = apiData
|
|
20
|
+
for (let i = 0; i < parts.length; i++) {
|
|
21
|
+
const part = parts[i]
|
|
22
|
+
|
|
23
|
+
// Skip the package name if it matches
|
|
24
|
+
if (i === 0 && part === packageName) continue
|
|
25
|
+
|
|
26
|
+
// Look in members
|
|
27
|
+
if (current.members) {
|
|
28
|
+
const member: GriffeMember | undefined = current.members[part]
|
|
29
|
+
if (member) {
|
|
30
|
+
// Only modules and classes have members to recurse into
|
|
31
|
+
if (member.kind === 'module' || member.kind === 'class') {
|
|
32
|
+
current = member as GriffeModule | GriffeClass
|
|
33
|
+
} else {
|
|
34
|
+
// Found a function, attribute, or alias - return it
|
|
35
|
+
return member
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return current
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generate TOC items based on item type
|
|
50
|
+
*/
|
|
51
|
+
function generateTocItems(item: GriffeMember, apiData: GriffeModule): TocItem[] {
|
|
52
|
+
// Resolve aliases first
|
|
53
|
+
if (item.kind === 'alias') {
|
|
54
|
+
const resolved = resolveAlias(item as GriffeAlias, apiData)
|
|
55
|
+
if (resolved) {
|
|
56
|
+
return generateTocItems(resolved, apiData)
|
|
57
|
+
}
|
|
58
|
+
return []
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (item.kind === 'class') {
|
|
62
|
+
return generateClassToc(item as GriffeClass)
|
|
63
|
+
}
|
|
64
|
+
if (item.kind === 'function') {
|
|
65
|
+
const fn = item as GriffeFunction
|
|
66
|
+
const items: TocItem[] = [{ id: fn.name, title: fn.name, level: 1 }]
|
|
67
|
+
if (fn.parameters && fn.parameters.length > 0) {
|
|
68
|
+
items.push({ id: 'parameters', title: 'Parameters', level: 2 })
|
|
69
|
+
}
|
|
70
|
+
return items
|
|
71
|
+
}
|
|
72
|
+
if (item.kind === 'module') {
|
|
73
|
+
const mod = item as GriffeModule
|
|
74
|
+
const items: TocItem[] = [{ id: mod.name, title: mod.name, level: 1 }]
|
|
75
|
+
if (mod.members) {
|
|
76
|
+
const members = Object.values(mod.members)
|
|
77
|
+
const classes = members.filter(m => m.kind === 'class')
|
|
78
|
+
const functions = members.filter(m => m.kind === 'function')
|
|
79
|
+
|
|
80
|
+
if (classes.length > 0) {
|
|
81
|
+
items.push({ id: 'classes', title: 'Classes', level: 2 })
|
|
82
|
+
}
|
|
83
|
+
if (functions.length > 0) {
|
|
84
|
+
items.push({ id: 'functions', title: 'Functions', level: 2 })
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return items
|
|
88
|
+
}
|
|
89
|
+
return []
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Determine what kind of content to render based on the current item
|
|
94
|
+
*/
|
|
95
|
+
function APIContent({
|
|
96
|
+
item,
|
|
97
|
+
prefix,
|
|
98
|
+
currentPath,
|
|
99
|
+
apiData,
|
|
100
|
+
displayPath,
|
|
101
|
+
githubUrl,
|
|
102
|
+
}: {
|
|
103
|
+
item: GriffeMember
|
|
104
|
+
prefix: string
|
|
105
|
+
currentPath: string
|
|
106
|
+
apiData: GriffeModule
|
|
107
|
+
/** Override the display path (used for aliases to show alias name instead of target) */
|
|
108
|
+
displayPath?: string
|
|
109
|
+
/** GitHub repository URL for "Open in GitHub" links */
|
|
110
|
+
githubUrl?: string
|
|
111
|
+
}) {
|
|
112
|
+
// Handle aliases by resolving to target
|
|
113
|
+
if (item.kind === 'alias') {
|
|
114
|
+
const alias = item as GriffeAlias
|
|
115
|
+
const resolved = resolveAlias(alias, apiData)
|
|
116
|
+
if (resolved) {
|
|
117
|
+
// Pass the alias path as displayPath so title shows "strawberry.enum" not "strawberry.types.enum.enum"
|
|
118
|
+
const aliasDisplayPath = alias.path || `${apiData.name}.${alias.name}`
|
|
119
|
+
return <APIContent item={resolved} prefix={prefix} currentPath={currentPath} apiData={apiData} displayPath={aliasDisplayPath} githubUrl={githubUrl} />
|
|
120
|
+
}
|
|
121
|
+
// Could not resolve alias
|
|
122
|
+
return (
|
|
123
|
+
<div className="text-gray-600 dark:text-gray-300">
|
|
124
|
+
<p>Could not resolve alias: {alias.target_path}</p>
|
|
125
|
+
</div>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
switch (item.kind) {
|
|
130
|
+
case 'module':
|
|
131
|
+
return <ModuleDoc module={item as GriffeModule} prefix={prefix} showFull displayPath={displayPath} githubUrl={githubUrl} />
|
|
132
|
+
|
|
133
|
+
case 'class':
|
|
134
|
+
return <ClassDoc cls={item as GriffeClass} prefix={prefix} currentPath={currentPath} displayPath={displayPath} githubUrl={githubUrl} />
|
|
135
|
+
|
|
136
|
+
case 'function':
|
|
137
|
+
return <FunctionDoc fn={item as GriffeFunction} displayPath={displayPath} githubUrl={githubUrl} />
|
|
138
|
+
|
|
139
|
+
default:
|
|
140
|
+
return (
|
|
141
|
+
<div className="text-gray-600 dark:text-gray-300">
|
|
142
|
+
<p>Unknown item type: {item.kind}</p>
|
|
143
|
+
<pre className="mt-4 text-xs bg-gray-100 dark:bg-gray-800 p-4 rounded overflow-auto">
|
|
144
|
+
{JSON.stringify(item, null, 2)}
|
|
145
|
+
</pre>
|
|
146
|
+
</div>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Main API documentation page component.
|
|
153
|
+
*
|
|
154
|
+
* Renders API documentation with a separate sidebar navigation.
|
|
155
|
+
* Automatically handles different item types (modules, classes, functions).
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* // In your pages configuration:
|
|
159
|
+
* createDocsApp({
|
|
160
|
+
* pages: {
|
|
161
|
+
* 'api/APIPage': APIPage,
|
|
162
|
+
* // ...
|
|
163
|
+
* }
|
|
164
|
+
* })
|
|
165
|
+
*/
|
|
166
|
+
export function APIPage({
|
|
167
|
+
apiData,
|
|
168
|
+
currentItem,
|
|
169
|
+
currentPath,
|
|
170
|
+
currentModule,
|
|
171
|
+
apiNav,
|
|
172
|
+
prefix,
|
|
173
|
+
logoUrl,
|
|
174
|
+
logoInvertedUrl,
|
|
175
|
+
footerLogoUrl,
|
|
176
|
+
footerLogoInvertedUrl,
|
|
177
|
+
githubUrl,
|
|
178
|
+
navLinks,
|
|
179
|
+
header,
|
|
180
|
+
headerHeight,
|
|
181
|
+
footer,
|
|
182
|
+
}: APIPageProps) {
|
|
183
|
+
// Determine what to render
|
|
184
|
+
const itemToRender = currentItem || apiData
|
|
185
|
+
|
|
186
|
+
// Determine the title
|
|
187
|
+
let title = 'API Reference'
|
|
188
|
+
if (itemToRender) {
|
|
189
|
+
const name = itemToRender.name || currentModule
|
|
190
|
+
const kind = itemToRender.kind
|
|
191
|
+
title = `${name} (${kind}) - API Reference`
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Generate table of contents
|
|
195
|
+
const tocItems = itemToRender ? generateTocItems(itemToRender, apiData) : []
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<APILayout
|
|
199
|
+
title={title}
|
|
200
|
+
apiNav={apiNav}
|
|
201
|
+
currentPath={currentPath}
|
|
202
|
+
logoUrl={logoUrl}
|
|
203
|
+
logoInvertedUrl={logoInvertedUrl}
|
|
204
|
+
footerLogoUrl={footerLogoUrl}
|
|
205
|
+
footerLogoInvertedUrl={footerLogoInvertedUrl}
|
|
206
|
+
githubUrl={githubUrl}
|
|
207
|
+
navLinks={navLinks}
|
|
208
|
+
rightSidebar={tocItems.length > 0 ? <TableOfContents items={tocItems} /> : undefined}
|
|
209
|
+
header={header}
|
|
210
|
+
headerHeight={headerHeight}
|
|
211
|
+
footer={footer}
|
|
212
|
+
>
|
|
213
|
+
<APIContent item={itemToRender} prefix={prefix} currentPath={currentPath} apiData={apiData} githubUrl={githubUrl} />
|
|
214
|
+
</APILayout>
|
|
215
|
+
)
|
|
216
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Link } from '@inertiajs/react'
|
|
2
|
+
|
|
3
|
+
interface BreadcrumbItem {
|
|
4
|
+
label: string
|
|
5
|
+
href?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface BreadcrumbProps {
|
|
9
|
+
items: BreadcrumbItem[]
|
|
10
|
+
className?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Breadcrumb navigation for API documentation pages.
|
|
15
|
+
* Shows the path: API > Module > Class
|
|
16
|
+
*/
|
|
17
|
+
export function Breadcrumb({ items, className = '' }: BreadcrumbProps) {
|
|
18
|
+
if (items.length === 0) return null
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<nav className={`flex items-center gap-2 text-sm mb-4 ${className}`}>
|
|
22
|
+
{items.map((item, index) => (
|
|
23
|
+
<span key={index} className="flex items-center gap-2">
|
|
24
|
+
{index > 0 && (
|
|
25
|
+
<ChevronIcon className="text-gray-400 dark:text-gray-500" />
|
|
26
|
+
)}
|
|
27
|
+
{item.href ? (
|
|
28
|
+
<Link
|
|
29
|
+
href={item.href}
|
|
30
|
+
className="text-gray-500 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
|
|
31
|
+
>
|
|
32
|
+
{item.label}
|
|
33
|
+
</Link>
|
|
34
|
+
) : (
|
|
35
|
+
<span className="text-gray-900 dark:text-white font-medium">
|
|
36
|
+
{item.label}
|
|
37
|
+
</span>
|
|
38
|
+
)}
|
|
39
|
+
</span>
|
|
40
|
+
))}
|
|
41
|
+
</nav>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function ChevronIcon({ className }: { className?: string }) {
|
|
46
|
+
return (
|
|
47
|
+
<svg
|
|
48
|
+
className={`w-4 h-4 ${className}`}
|
|
49
|
+
fill="none"
|
|
50
|
+
viewBox="0 0 24 24"
|
|
51
|
+
stroke="currentColor"
|
|
52
|
+
>
|
|
53
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
54
|
+
</svg>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Generate breadcrumb items from a path like "/api/strawberry/schema/schema/Schema"
|
|
60
|
+
* Combines module path into dotted notation: API > strawberry.schema.schema > Schema
|
|
61
|
+
*/
|
|
62
|
+
export function generateBreadcrumb(currentPath: string, prefix: string = '/api'): BreadcrumbItem[] {
|
|
63
|
+
const items: BreadcrumbItem[] = [{ label: 'API', href: prefix }]
|
|
64
|
+
|
|
65
|
+
// Remove prefix from path
|
|
66
|
+
const relativePath = currentPath.startsWith(prefix)
|
|
67
|
+
? currentPath.slice(prefix.length)
|
|
68
|
+
: currentPath
|
|
69
|
+
|
|
70
|
+
// Split path into parts
|
|
71
|
+
const parts = relativePath.split('/').filter(Boolean)
|
|
72
|
+
|
|
73
|
+
if (parts.length === 0) return items
|
|
74
|
+
|
|
75
|
+
// If we have parts, combine all but the last into a module path
|
|
76
|
+
if (parts.length === 1) {
|
|
77
|
+
// Just one part - show it as the current item
|
|
78
|
+
items.push({ label: parts[0] })
|
|
79
|
+
} else {
|
|
80
|
+
// Multiple parts: combine all but last into module path
|
|
81
|
+
const moduleParts = parts.slice(0, -1)
|
|
82
|
+
const finalItem = parts[parts.length - 1]
|
|
83
|
+
|
|
84
|
+
// Build href for module path (link to the parent)
|
|
85
|
+
const moduleHref = prefix + '/' + moduleParts.join('/')
|
|
86
|
+
|
|
87
|
+
// Add module path as dotted notation
|
|
88
|
+
items.push({
|
|
89
|
+
label: moduleParts.join('.'),
|
|
90
|
+
href: moduleHref,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// Add final item (class/function name)
|
|
94
|
+
items.push({ label: finalItem })
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return items
|
|
98
|
+
}
|