Core: Support ReactNode for icons in page tree

This commit is contained in:
Fuma Nama
2025-08-21 17:58:07 +08:00
parent 17b708fc0d
commit ec75601251
7 changed files with 34 additions and 28 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-core': patch
---
Support `ReactNode` for icons in page tree
@@ -1,4 +1,4 @@
import type { ReactElement } from 'react';
import type { ReactNode } from 'react';
import type * as PageTree from './definitions';
import { joinPath } from '@/utils/path';
import type { MetaData, PageData, UrlFn } from '../types';
@@ -69,7 +69,7 @@ export interface BaseOptions<
*/
noRef?: boolean;
transformers?: PageTreeTransformer<Page, Meta>[];
resolveIcon?: (icon: string | undefined) => ReactElement | undefined;
resolveIcon?: (icon: string | undefined) => ReactNode | undefined;
}
export interface PageTreeBuilder<
@@ -1,4 +1,4 @@
import type { ReactElement, ReactNode } from 'react';
import type { ReactNode } from 'react';
export interface Root {
$id?: string;
@@ -28,7 +28,7 @@ export interface Item {
external?: boolean;
description?: ReactNode;
icon?: ReactElement;
icon?: ReactNode;
}
export interface Separator {
@@ -36,7 +36,7 @@ export interface Separator {
type: 'separator';
name?: ReactNode;
icon?: ReactElement;
icon?: ReactNode;
}
export interface Folder {
@@ -54,6 +54,6 @@ export interface Folder {
root?: boolean;
defaultOpen?: boolean;
index?: Item;
icon?: ReactElement;
icon?: ReactNode;
children: Node[];
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { parseFilePath, parseFolderPath } from '@/source/path';
import { describe, expect, test } from 'vitest';
import type { Root } from '@/server/page-tree';
import type { Root } from '@/source/page-tree/definitions';
import { findNeighbour } from '@/utils/page-tree';
import { PageTree } from '../dist/server';
import { getBreadcrumbItems } from '@/breadcrumb';
+4 -5
View File
@@ -101,8 +101,8 @@ export function CodeBlock({
dir="ltr"
{...props}
className={cn(
isTab ? [bg, 'rounded-lg shadow-sm'] : 'my-4 rounded-xl bg-fd-card p-1',
'shiki relative border outline-none not-prose overflow-hidden text-sm',
isTab ? [bg, 'rounded-lg'] : 'my-4 rounded-xl bg-fd-card p-1',
'shiki relative border shadow-sm outline-none not-prose overflow-hidden text-sm',
props.className,
)}
>
@@ -184,8 +184,7 @@ function CopyButton({
type="button"
className={cn(
buttonVariants({
color: 'ghost',
className: '[&_svg]:size-3.5',
className: '[&_svg]:size-3.5 hover:text-fd-accent-foreground',
}),
className,
)}
@@ -257,7 +256,7 @@ export function CodeBlockTabsTrigger({
<TabsTrigger
{...props}
className={cn(
'relative group inline-flex text-sm font-medium text-nowrap items-center gap-2 px-2 first:ms-1 py-1.5 hover:text-fd-accent-foreground data-[state=active]:text-fd-primary [&_svg]:size-3.5',
'relative group inline-flex text-sm font-medium text-nowrap items-center transition-colors gap-2 px-2 first:ms-1 py-1.5 hover:text-fd-accent-foreground data-[state=active]:text-fd-primary [&_svg]:size-3.5',
props.className,
)}
>
+11 -8
View File
@@ -1,17 +1,20 @@
import { cva, type VariantProps } from 'class-variance-authority';
const variants = {
primary: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80',
outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground',
ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground',
secondary:
'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground',
} as const;
export const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none',
{
variants: {
color: {
primary:
'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80',
outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground',
ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground',
secondary:
'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground',
},
variant: variants,
// fumadocs use `color` instead of `variant`
color: variants,
size: {
sm: 'gap-1 px-2 py-1.5 text-xs',
icon: 'p-1.5 [&_svg]:size-5',
+7 -8
View File
@@ -203,28 +203,27 @@ export function DocsLayout({
</SidebarHeader>
{viewport}
<HideIfEmpty as={SidebarFooter}>
<div className="flex text-fd-muted-foreground items-center justify-end empty:hidden">
<div className="flex text-fd-muted-foreground items-center empty:hidden">
{i18n ? (
<LanguageToggle>
<Languages className="size-4.5" />
</LanguageToggle>
) : null}
{iconLinks.map((item, i) => (
<BaseLinkItem
key={i}
item={item}
className={cn(
buttonVariants({ size: 'icon-sm', color: 'ghost' }),
i === iconLinks.length - 1 && 'me-auto',
)}
aria-label={item.label}
>
{item.icon}
</BaseLinkItem>
))}
{i18n ? (
<LanguageToggle>
<Languages className="size-4.5" />
</LanguageToggle>
) : null}
{themeSwitch.enabled !== false &&
(themeSwitch.component ?? (
<ThemeToggle className="p-0" mode={themeSwitch.mode} />
<ThemeToggle className="ms-auto p-0" mode={themeSwitch.mode} />
))}
</div>
{footer}