mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 05:23:40 +00:00
Core: Improve Breadcrumbs API
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-core': patch
|
||||
---
|
||||
|
||||
Breadcrumbs API: Fix root folders being filtered when `includeRoot` is set to `true`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-core': minor
|
||||
---
|
||||
|
||||
Breadcrumbs API: default `includePage` to `false`.
|
||||
@@ -22,8 +22,8 @@ export async function GET(): Promise<Response> {
|
||||
url: page.url,
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
breadcrumbs: items.flatMap<string>((item) =>
|
||||
typeof item.name === 'string' ? item.name : [],
|
||||
breadcrumbs: items.flatMap<string>((item, i) =>
|
||||
i > 0 && typeof item.name === 'string' ? item.name : [],
|
||||
),
|
||||
} satisfies OramaDocument;
|
||||
});
|
||||
|
||||
@@ -20,7 +20,6 @@ import type { z } from 'zod';
|
||||
import { DefaultChatTransport } from 'ai';
|
||||
import { Markdown } from './markdown';
|
||||
import { Presence } from '@radix-ui/react-presence';
|
||||
import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
|
||||
|
||||
const Context = createContext<{
|
||||
open: boolean;
|
||||
@@ -269,7 +268,7 @@ export function AISearchTrigger() {
|
||||
}),
|
||||
});
|
||||
|
||||
const onKeyPress = useEffectEvent((e: KeyboardEvent) => {
|
||||
const onKeyPress = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && open) {
|
||||
setOpen(false);
|
||||
e.preventDefault();
|
||||
@@ -279,12 +278,15 @@ export function AISearchTrigger() {
|
||||
setOpen(true);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onKeyPressRef = useRef(onKeyPress);
|
||||
onKeyPressRef.current = onKeyPress;
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', onKeyPress);
|
||||
return () => window.removeEventListener('keydown', onKeyPress);
|
||||
}, [onKeyPress]);
|
||||
const listener = (e: KeyboardEvent) => onKeyPressRef.current(e);
|
||||
window.addEventListener('keydown', listener);
|
||||
return () => window.removeEventListener('keydown', listener);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Context value={useMemo(() => ({ chat, open, setOpen }), [chat, open])}>
|
||||
|
||||
@@ -10,21 +10,23 @@ export interface BreadcrumbItem {
|
||||
|
||||
export interface BreadcrumbOptions {
|
||||
/**
|
||||
* Include the root itself in the breadcrumb items array.
|
||||
* Specify the url by passing an object instead
|
||||
* Include the root folders in the breadcrumb items array.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
includeRoot?:
|
||||
| boolean
|
||||
| {
|
||||
/**
|
||||
* Specify the url of root
|
||||
*/
|
||||
url: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Include the page itself in the breadcrumb items array
|
||||
*
|
||||
* @defaultValue true
|
||||
* @defaultValue false
|
||||
*/
|
||||
includePage?: boolean;
|
||||
|
||||
@@ -64,38 +66,45 @@ export function getBreadcrumbItemsFromPath(
|
||||
path: PageTree.Node[],
|
||||
options: BreadcrumbOptions,
|
||||
): BreadcrumbItem[] {
|
||||
const { includePage = true, includeSeparator = false, includeRoot } = options;
|
||||
const {
|
||||
includePage = false,
|
||||
includeSeparator = false,
|
||||
includeRoot = false,
|
||||
} = options;
|
||||
let items: BreadcrumbItem[] = [];
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
const item = path[i];
|
||||
|
||||
path.forEach((item, i) => {
|
||||
if (item.type === 'separator' && item.name && includeSeparator) {
|
||||
items.push({
|
||||
name: item.name,
|
||||
});
|
||||
switch (item.type) {
|
||||
case 'page':
|
||||
if (includePage)
|
||||
items.push({
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
});
|
||||
break;
|
||||
case 'folder':
|
||||
if (item.root && !includeRoot) {
|
||||
items = [];
|
||||
break;
|
||||
}
|
||||
|
||||
// only show the index node of folders if possible
|
||||
if (i === path.length - 1 || item.index !== path[i + 1]) {
|
||||
items.push({
|
||||
name: item.name,
|
||||
url: item.index?.url,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'separator':
|
||||
if (item.name && includeSeparator)
|
||||
items.push({
|
||||
name: item.name,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if (item.type === 'folder') {
|
||||
const next = path.at(i + 1);
|
||||
if (next && item.index === next) return;
|
||||
|
||||
if (item.root) {
|
||||
items = [];
|
||||
return;
|
||||
}
|
||||
|
||||
items.push({
|
||||
name: item.name,
|
||||
url: item.index?.url,
|
||||
});
|
||||
}
|
||||
|
||||
if (item.type === 'page' && includePage) {
|
||||
items.push({
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (includeRoot) {
|
||||
items.unshift({
|
||||
@@ -113,7 +122,7 @@ export function getBreadcrumbItemsFromPath(
|
||||
* - When the page doesn't exist, return null
|
||||
*
|
||||
* @returns The path to the target node from root
|
||||
* @internal
|
||||
* @internal Don't use this on your own
|
||||
*/
|
||||
export function searchPath(
|
||||
nodes: PageTree.Node[],
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/**
|
||||
* normalize URL into the Fumadocs standard form (`/slug-1/slug-2`)
|
||||
* normalize URL into the Fumadocs standard form (`/slug-1/slug-2`).
|
||||
*
|
||||
* This includes URLs with trailing slashes.
|
||||
*/
|
||||
export function normalizeUrl(url: string) {
|
||||
if (url.startsWith('http://') || url.startsWith('https://')) return url;
|
||||
|
||||
@@ -154,16 +154,9 @@ test('Breadcrumbs', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(getBreadcrumbItems('/docs/folder', tree)).toStrictEqual([
|
||||
{ name: 'World', url: '/docs/folder' },
|
||||
]);
|
||||
expect(
|
||||
getBreadcrumbItems('/docs/folder', tree, { includePage: true }),
|
||||
).toStrictEqual([{ name: 'World', url: '/docs/folder' }]);
|
||||
|
||||
expect(getBreadcrumbItems('/', tree)).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"name": "Introduction",
|
||||
"url": "/",
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(getBreadcrumbItems('/invalid', tree)).toMatchInlineSnapshot(`[]`);
|
||||
});
|
||||
|
||||
@@ -340,9 +340,9 @@ function FooterItem({ item, index }: { item: Item; index: 0 | 1 }) {
|
||||
export type BreadcrumbProps = BreadcrumbOptions & ComponentProps<'div'>;
|
||||
|
||||
export function PageBreadcrumb({
|
||||
includeRoot = false,
|
||||
includeRoot,
|
||||
includeSeparator,
|
||||
includePage = false,
|
||||
includePage,
|
||||
...props
|
||||
}: BreadcrumbProps) {
|
||||
const path = useTreePath();
|
||||
|
||||
Reference in New Issue
Block a user