mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
Core & UI: Drop framework-side createContext
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'fumadocs-core': patch
|
||||
'fumadocs-ui': patch
|
||||
---
|
||||
|
||||
Drop framework-side `createContext`
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: 中文
|
||||
description: 您的第一個文檔
|
||||
---
|
||||
|
||||
## Hi 中文
|
||||
|
||||
Fumadocs 對 i18n 有良好的支持
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: Hello World
|
||||
description: Your first document
|
||||
---
|
||||
|
||||
Hey there!
|
||||
|
||||
## Heading
|
||||
|
||||
<Cards>
|
||||
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
|
||||
<Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
|
||||
</Cards>
|
||||
|
||||
### Heading
|
||||
|
||||
#### Heading
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"title": "Git",
|
||||
"root": true,
|
||||
"pages": ["index", "test"],
|
||||
"icon": "git"
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: Hello World
|
||||
description: Your first document
|
||||
---
|
||||
|
||||
Hey there!
|
||||
|
||||
## Heading
|
||||
|
||||
<Cards>
|
||||
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
|
||||
<Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
|
||||
</Cards>
|
||||
|
||||
### Heading
|
||||
|
||||
```js
|
||||
console.log('Hello World');
|
||||
```
|
||||
|
||||
#### Heading
|
||||
@@ -2,7 +2,11 @@
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"target": "ESNext",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@@ -13,10 +17,12 @@
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
@@ -24,6 +30,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
import type { ComponentProps, FC, ReactNode } from 'react';
|
||||
import React from 'react';
|
||||
import { createContext, use, useMemo } from 'react';
|
||||
import type { StaticImport } from 'next/dist/shared/lib/get-img-props';
|
||||
|
||||
export interface ImageProps extends Omit<ComponentProps<'img'>, 'src'> {
|
||||
@@ -46,7 +46,7 @@ const notImplemented = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const FrameworkContext = createContext<Framework>('FrameworkContext', {
|
||||
const FrameworkContext = createContext<Framework>({
|
||||
useParams: notImplemented,
|
||||
useRouter: notImplemented,
|
||||
usePathname: notImplemented,
|
||||
@@ -60,7 +60,7 @@ export function FrameworkProvider({
|
||||
Image,
|
||||
children,
|
||||
}: Framework & { children: ReactNode }) {
|
||||
const framework = React.useMemo(
|
||||
const framework = useMemo(
|
||||
() => ({
|
||||
usePathname,
|
||||
useRouter,
|
||||
@@ -71,27 +71,23 @@ export function FrameworkProvider({
|
||||
[Link, usePathname, useRouter, useParams, Image],
|
||||
);
|
||||
|
||||
return (
|
||||
<FrameworkContext.Provider value={framework}>
|
||||
{children}
|
||||
</FrameworkContext.Provider>
|
||||
);
|
||||
return <FrameworkContext value={framework}>{children}</FrameworkContext>;
|
||||
}
|
||||
|
||||
export function usePathname() {
|
||||
return FrameworkContext.use().usePathname();
|
||||
return use(FrameworkContext).usePathname();
|
||||
}
|
||||
|
||||
export function useRouter() {
|
||||
return FrameworkContext.use().useRouter();
|
||||
return use(FrameworkContext).useRouter();
|
||||
}
|
||||
|
||||
export function useParams() {
|
||||
return FrameworkContext.use().useParams();
|
||||
return use(FrameworkContext).useParams();
|
||||
}
|
||||
|
||||
export function Image(props: ImageProps) {
|
||||
const { Image } = FrameworkContext.use();
|
||||
const { Image } = use(FrameworkContext);
|
||||
if (!Image) {
|
||||
const { src, alt, priority, ...rest } = props;
|
||||
|
||||
@@ -109,7 +105,7 @@ export function Image(props: ImageProps) {
|
||||
}
|
||||
|
||||
export function Link(props: LinkProps) {
|
||||
const { Link } = FrameworkContext.use();
|
||||
const { Link } = use(FrameworkContext);
|
||||
if (!Link) {
|
||||
const { href, prefetch: _, ...rest } = props;
|
||||
return <a href={href} {...rest} />;
|
||||
@@ -117,26 +113,3 @@ export function Link(props: LinkProps) {
|
||||
|
||||
return <Link {...props} />;
|
||||
}
|
||||
|
||||
export function createContext<T>(name: string, v?: T) {
|
||||
const Context = React.createContext(v);
|
||||
|
||||
return {
|
||||
Provider: (props: { value: T; children: ReactNode }) => {
|
||||
return (
|
||||
<Context.Provider value={props.value}>
|
||||
{props.children}
|
||||
</Context.Provider>
|
||||
);
|
||||
},
|
||||
use: (errorMessage?: string): Exclude<T, undefined | null> => {
|
||||
const value = React.useContext(Context);
|
||||
|
||||
if (!value)
|
||||
throw new Error(
|
||||
errorMessage ?? `Provider of ${name} is required but missing.`,
|
||||
);
|
||||
return value as Exclude<T, undefined | null>;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
import type * as PageTree from 'fumadocs-core/page-tree';
|
||||
import { createContext, usePathname } from 'fumadocs-core/framework';
|
||||
import { type ReactNode, useMemo, useRef } from 'react';
|
||||
import { usePathname } from 'fumadocs-core/framework';
|
||||
import { type ReactNode, useMemo, useRef, createContext, use } from 'react';
|
||||
import { searchPath } from 'fumadocs-core/breadcrumb';
|
||||
|
||||
type MakeRequired<O, K extends keyof O> = Omit<O, K> & Pick<Required<O>, K>;
|
||||
@@ -11,8 +11,8 @@ interface TreeContextType {
|
||||
full: PageTree.Root;
|
||||
}
|
||||
|
||||
const TreeContext = createContext<TreeContextType>('TreeContext');
|
||||
const PathContext = createContext<PageTree.Node[]>('PathContext', []);
|
||||
const TreeContext = createContext<TreeContextType | null>(null);
|
||||
const PathContext = createContext<PageTree.Node[]>([]);
|
||||
|
||||
export function TreeContextProvider(props: {
|
||||
tree: PageTree.Root;
|
||||
@@ -26,11 +26,11 @@ export function TreeContextProvider(props: {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const tree = useMemo(() => props.tree, [props.tree.$id ?? props.tree]);
|
||||
const path = useMemo(() => {
|
||||
let result = searchPath(tree.children, pathname);
|
||||
if (result) return result;
|
||||
|
||||
if (tree.fallback) result = searchPath(tree.fallback.children, pathname);
|
||||
return result ?? [];
|
||||
return (
|
||||
searchPath(tree.children, pathname) ??
|
||||
(tree.fallback ? searchPath(tree.fallback.children, pathname) : null) ??
|
||||
[]
|
||||
);
|
||||
}, [tree, pathname]);
|
||||
|
||||
const root =
|
||||
@@ -38,21 +38,25 @@ export function TreeContextProvider(props: {
|
||||
root.$id ??= String(nextIdRef.current++);
|
||||
|
||||
return (
|
||||
<TreeContext.Provider
|
||||
<TreeContext
|
||||
value={useMemo(
|
||||
() => ({ root, full: tree }) as TreeContextType,
|
||||
[root, tree],
|
||||
)}
|
||||
>
|
||||
<PathContext.Provider value={path}>{props.children}</PathContext.Provider>
|
||||
</TreeContext.Provider>
|
||||
<PathContext value={path}>{props.children}</PathContext>
|
||||
</TreeContext>
|
||||
);
|
||||
}
|
||||
|
||||
export function useTreePath(): PageTree.Node[] {
|
||||
return PathContext.use();
|
||||
return use(PathContext);
|
||||
}
|
||||
|
||||
export function useTreeContext(): TreeContextType {
|
||||
return TreeContext.use('You must wrap this component under <DocsLayout />');
|
||||
const ctx = use(TreeContext);
|
||||
|
||||
if (!ctx)
|
||||
throw new Error('You must wrap this component under <DocsLayout />');
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@ import {
|
||||
Fragment,
|
||||
useEffect,
|
||||
useEffectEvent,
|
||||
createContext,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
use,
|
||||
} from 'react';
|
||||
import { ChevronDown, ChevronLeft, ChevronRight } from '@/icons';
|
||||
import Link from 'fumadocs-core/link';
|
||||
@@ -15,7 +17,7 @@ import { cn } from '@/utils/cn';
|
||||
import { useI18n } from '@/contexts/i18n';
|
||||
import { useTreeContext, useTreePath } from '@/contexts/tree';
|
||||
import type * as PageTree from 'fumadocs-core/page-tree';
|
||||
import { createContext, usePathname } from 'fumadocs-core/framework';
|
||||
import { usePathname } from 'fumadocs-core/framework';
|
||||
import {
|
||||
type BreadcrumbOptions,
|
||||
getBreadcrumbItemsFromPath,
|
||||
@@ -34,11 +36,11 @@ import { useActiveAnchor } from 'fumadocs-core/toc';
|
||||
const TocPopoverContext = createContext<{
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
}>('TocPopoverContext');
|
||||
} | null>(null);
|
||||
|
||||
export function PageTOCPopoverTrigger(props: ComponentProps<'button'>) {
|
||||
const { text } = useI18n();
|
||||
const { open } = TocPopoverContext.use();
|
||||
const { open } = use(TocPopoverContext)!;
|
||||
const items = useTOCItems();
|
||||
const active = useActiveAnchor();
|
||||
const selected = useMemo(
|
||||
@@ -182,7 +184,7 @@ export function PageTOCPopover(props: ComponentProps<'div'>) {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<TocPopoverContext.Provider
|
||||
<TocPopoverContext
|
||||
value={useMemo(
|
||||
() => ({
|
||||
open,
|
||||
@@ -214,7 +216,7 @@ export function PageTOCPopover(props: ComponentProps<'div'>) {
|
||||
{props.children}
|
||||
</header>
|
||||
</Collapsible>
|
||||
</TocPopoverContext.Provider>
|
||||
</TocPopoverContext>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user