@fumadocs/ui 16.2.5
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/LICENSE +21 -0
- package/css/base.css +217 -0
- package/css/colors/black.css +39 -0
- package/css/colors/catppuccin.css +49 -0
- package/css/colors/dusk.css +47 -0
- package/css/colors/index.css +51 -0
- package/css/colors/neutral.css +7 -0
- package/css/colors/ocean.css +48 -0
- package/css/colors/purple.css +39 -0
- package/css/colors/shadcn.css +36 -0
- package/css/colors/solar.css +75 -0
- package/css/colors/vitepress.css +77 -0
- package/css/shiki.css +90 -0
- package/dist/cn.d.ts +2 -0
- package/dist/cn.d.ts.map +1 -0
- package/dist/cn.js +1 -0
- package/dist/components/toc/clerk.d.ts +3 -0
- package/dist/components/toc/clerk.d.ts.map +1 -0
- package/dist/components/toc/clerk.js +76 -0
- package/dist/components/toc/default.d.ts +3 -0
- package/dist/components/toc/default.d.ts.map +1 -0
- package/dist/components/toc/default.js +19 -0
- package/dist/components/toc/index.d.ts +11 -0
- package/dist/components/toc/index.d.ts.map +1 -0
- package/dist/components/toc/index.js +64 -0
- package/dist/contexts/i18n.d.ts +50 -0
- package/dist/contexts/i18n.d.ts.map +1 -0
- package/dist/contexts/i18n.js +55 -0
- package/dist/contexts/search.d.ts +61 -0
- package/dist/contexts/search.d.ts.map +1 -0
- package/dist/contexts/search.js +59 -0
- package/dist/contexts/tree.d.ts +15 -0
- package/dist/contexts/tree.d.ts.map +1 -0
- package/dist/contexts/tree.js +32 -0
- package/dist/hooks/use-copy-button.d.ts +3 -0
- package/dist/hooks/use-copy-button.d.ts.map +1 -0
- package/dist/hooks/use-copy-button.js +27 -0
- package/dist/hooks/use-footer-items.d.ts +6 -0
- package/dist/hooks/use-footer-items.d.ts.map +1 -0
- package/dist/hooks/use-footer-items.js +27 -0
- package/dist/hooks/use-is-scroll-top.d.ts +4 -0
- package/dist/hooks/use-is-scroll-top.d.ts.map +1 -0
- package/dist/hooks/use-is-scroll-top.js +17 -0
- package/dist/i18n.d.ts +14 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +16 -0
- package/dist/icons.d.ts +49 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +281 -0
- package/dist/link-item.d.ts +78 -0
- package/dist/link-item.d.ts.map +1 -0
- package/dist/link-item.js +12 -0
- package/dist/merge-refs.d.ts +3 -0
- package/dist/merge-refs.d.ts.map +1 -0
- package/dist/merge-refs.js +12 -0
- package/dist/og/next.d.ts +15 -0
- package/dist/og/next.d.ts.map +1 -0
- package/dist/og/next.js +45 -0
- package/dist/typography/index.d.ts +10 -0
- package/dist/typography/index.d.ts.map +1 -0
- package/dist/typography/index.js +137 -0
- package/dist/typography/styles.d.ts +93 -0
- package/dist/typography/styles.d.ts.map +1 -0
- package/dist/typography/styles.js +433 -0
- package/dist/urls.d.ts +6 -0
- package/dist/urls.d.ts.map +1 -0
- package/dist/urls.js +13 -0
- package/package.json +75 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
@import './index.css';
|
|
2
|
+
|
|
3
|
+
@theme {
|
|
4
|
+
--color-fd-muted: hsl(0, 0%, 96.1%);
|
|
5
|
+
--color-fd-popover: hsl(0, 0%, 100%);
|
|
6
|
+
--color-fd-popover-foreground: hsl(0, 0%, 15.1%);
|
|
7
|
+
--color-fd-card-foreground: hsl(0, 0%, 3.9%);
|
|
8
|
+
--color-fd-border: hsl(0, 0%, 89.8%);
|
|
9
|
+
--color-fd-primary-foreground: hsl(0, 0%, 98%);
|
|
10
|
+
--color-fd-secondary-foreground: hsl(0, 0%, 9%);
|
|
11
|
+
--color-fd-accent: hsl(0, 0%, 94.1%);
|
|
12
|
+
--color-fd-ring: hsl(0, 0%, 63.9%);
|
|
13
|
+
|
|
14
|
+
--color-fd-background: hsl(0, 0%, 100%);
|
|
15
|
+
--color-fd-card: hsl(0, 0%, 100%);
|
|
16
|
+
--color-fd-foreground: hsl(240, 6%, 25%);
|
|
17
|
+
--color-fd-muted-foreground: hsl(240, 6%, 50%);
|
|
18
|
+
--color-fd-secondary: hsl(240, 6%, 97%);
|
|
19
|
+
--color-fd-accent-foreground: hsl(240, 6%, 25%);
|
|
20
|
+
--color-fd-primary: hsl(226, 55%, 45%);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.dark {
|
|
24
|
+
--color-fd-ring: hsl(234, 100%, 83%);
|
|
25
|
+
--color-fd-primary: hsl(234, 100%, 83%);
|
|
26
|
+
--color-fd-secondary-foreground: hsl(60, 100%, 98%);
|
|
27
|
+
--color-fd-card-foreground: hsl(60, 100%, 98%);
|
|
28
|
+
--color-fd-background: hsl(240, 7%, 11%);
|
|
29
|
+
--color-fd-foreground: hsl(60, 100%, 98%);
|
|
30
|
+
--color-fd-popover: hsl(240, 7%, 11%);
|
|
31
|
+
--color-fd-popover-foreground: hsl(60, 100%, 98%);
|
|
32
|
+
--color-fd-primary-foreground: hsl(240, 7%, 11%);
|
|
33
|
+
--color-fd-card: hsl(240, 7%, 11%);
|
|
34
|
+
--color-fd-muted: hsl(0, 0%, 13%);
|
|
35
|
+
--color-fd-border: hsl(240, 4%, 19%);
|
|
36
|
+
--color-fd-accent: hsl(0, 0%, 15%);
|
|
37
|
+
--color-fd-secondary: hsl(240, 4%, 9%);
|
|
38
|
+
--color-fd-accent-foreground: hsl(0, 0%, 100%);
|
|
39
|
+
--color-fd-muted-foreground: hsl(240, 4%, 65%);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.prose {
|
|
43
|
+
--tw-prose-body: color-mix(
|
|
44
|
+
in oklab,
|
|
45
|
+
var(--color-fd-foreground) 85%,
|
|
46
|
+
transparent
|
|
47
|
+
);
|
|
48
|
+
--tw-prose-headings: color-mix(
|
|
49
|
+
in oklab,
|
|
50
|
+
var(--color-fd-foreground) 85%,
|
|
51
|
+
transparent
|
|
52
|
+
);
|
|
53
|
+
--tw-prose-links: var(--color-fd-primary);
|
|
54
|
+
--tw-prose-code: var(--color-fd-primary);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.prose :where(code):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
|
|
58
|
+
border: none;
|
|
59
|
+
background-color: color-mix(
|
|
60
|
+
in oklab,
|
|
61
|
+
var(--color-fd-primary) 10%,
|
|
62
|
+
transparent
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#nd-sidebar {
|
|
67
|
+
background-color: hsl(240, 6%, 97%);
|
|
68
|
+
border-color: transparent;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.dark #nd-sidebar {
|
|
72
|
+
background-color: hsl(240, 4%, 9%);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
button[data-search-full] {
|
|
76
|
+
background-color: var(--color-fd-background);
|
|
77
|
+
}
|
package/css/shiki.css
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
.shiki:not(.not-fumadocs-codeblock *) {
|
|
2
|
+
--padding-left: calc(var(--spacing) * 4);
|
|
3
|
+
--padding-right: calc(var(--spacing) * 4);
|
|
4
|
+
|
|
5
|
+
code span {
|
|
6
|
+
color: var(--shiki-light);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.dark .shiki:not(.not-fumadocs-codeblock *) {
|
|
11
|
+
code span {
|
|
12
|
+
color: var(--shiki-dark);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
:is(pre *):is(.shiki *):not(.not-fumadocs-codeblock *) {
|
|
17
|
+
.line& {
|
|
18
|
+
position: relative;
|
|
19
|
+
min-height: 1lh;
|
|
20
|
+
padding-left: var(--padding-left);
|
|
21
|
+
padding-right: var(--padding-right);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.has-focused .line&:not(.focused) {
|
|
25
|
+
filter: blur(2px);
|
|
26
|
+
transition: filter 200ms;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.has-focused:hover .line&:not(.focused) {
|
|
30
|
+
filter: blur(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
[data-line-numbers] .twoslash-meta-line& {
|
|
34
|
+
padding-left: calc(var(--padding-left) + 7 * var(--spacing));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[data-line-numbers] .line& {
|
|
38
|
+
counter-increment: line;
|
|
39
|
+
padding-left: calc(var(--padding-left) + 7 * var(--spacing));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
[data-line-numbers] .line&::after {
|
|
43
|
+
position: absolute;
|
|
44
|
+
content: counter(line);
|
|
45
|
+
color: color-mix(
|
|
46
|
+
in oklab,
|
|
47
|
+
var(--fd-counter-color, var(--color-fd-muted-foreground)) 60%,
|
|
48
|
+
transparent
|
|
49
|
+
);
|
|
50
|
+
@apply top-0 left-4;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.diff&::before {
|
|
54
|
+
position: absolute;
|
|
55
|
+
left: calc(var(--spacing) * 1.5);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.diff.remove& {
|
|
59
|
+
opacity: 0.7;
|
|
60
|
+
--fd-counter-color: var(--color-fd-diff-remove-symbol);
|
|
61
|
+
@apply bg-fd-diff-remove;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.diff.remove&::before {
|
|
65
|
+
content: '-';
|
|
66
|
+
@apply text-fd-diff-remove-symbol;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.diff.add& {
|
|
70
|
+
--fd-counter-color: var(--color-fd-diff-add-symbol);
|
|
71
|
+
@apply bg-fd-diff-add;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.diff.add&::before {
|
|
75
|
+
content: '+';
|
|
76
|
+
@apply text-fd-diff-add-symbol;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.highlighted& {
|
|
80
|
+
--fd-counter-color: var(--color-fd-primary);
|
|
81
|
+
padding-left: calc(var(--padding-left) - 2px);
|
|
82
|
+
|
|
83
|
+
@apply border-l-2 border-fd-primary/50 bg-fd-primary/10;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.highlighted-word& {
|
|
87
|
+
padding: 1px;
|
|
88
|
+
@apply border -my-px border-fd-primary/30 bg-fd-primary/10 rounded-md font-medium;
|
|
89
|
+
}
|
|
90
|
+
}
|
package/dist/cn.d.ts
ADDED
package/dist/cn.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cn.d.ts","sourceRoot":"","sources":["../src/cn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/cn.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { twMerge as cn } from 'tailwind-merge';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clerk.d.ts","sourceRoot":"","sources":["../../../src/components/toc/clerk.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAA+B,MAAM,OAAO,CAAC;AAMzE,wBAAgB,QAAQ,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,2CAsG3E"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as Primitive from 'fumadocs-core/toc';
|
|
4
|
+
import { useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { cn } from '../../cn.js';
|
|
6
|
+
import { TocThumb, useTOCItems } from './index.js';
|
|
7
|
+
import { mergeRefs } from '../../merge-refs.js';
|
|
8
|
+
import { useI18n } from '../../contexts/i18n.js';
|
|
9
|
+
export function TOCItems({ ref, className, ...props }) {
|
|
10
|
+
const containerRef = useRef(null);
|
|
11
|
+
const items = useTOCItems();
|
|
12
|
+
const { text } = useI18n();
|
|
13
|
+
const [svg, setSvg] = useState();
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!containerRef.current)
|
|
16
|
+
return;
|
|
17
|
+
const container = containerRef.current;
|
|
18
|
+
function onResize() {
|
|
19
|
+
if (container.clientHeight === 0)
|
|
20
|
+
return;
|
|
21
|
+
let w = 0, h = 0;
|
|
22
|
+
const d = [];
|
|
23
|
+
for (let i = 0; i < items.length; i++) {
|
|
24
|
+
const element = container.querySelector(`a[href="#${items[i].url.slice(1)}"]`);
|
|
25
|
+
if (!element)
|
|
26
|
+
continue;
|
|
27
|
+
const styles = getComputedStyle(element);
|
|
28
|
+
const offset = getLineOffset(items[i].depth) + 1, top = element.offsetTop + parseFloat(styles.paddingTop), bottom = element.offsetTop +
|
|
29
|
+
element.clientHeight -
|
|
30
|
+
parseFloat(styles.paddingBottom);
|
|
31
|
+
w = Math.max(offset, w);
|
|
32
|
+
h = Math.max(h, bottom);
|
|
33
|
+
d.push(`${i === 0 ? 'M' : 'L'}${offset} ${top}`);
|
|
34
|
+
d.push(`L${offset} ${bottom}`);
|
|
35
|
+
}
|
|
36
|
+
setSvg({
|
|
37
|
+
path: d.join(' '),
|
|
38
|
+
width: w + 1,
|
|
39
|
+
height: h,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const observer = new ResizeObserver(onResize);
|
|
43
|
+
onResize();
|
|
44
|
+
observer.observe(container);
|
|
45
|
+
return () => {
|
|
46
|
+
observer.disconnect();
|
|
47
|
+
};
|
|
48
|
+
}, [items]);
|
|
49
|
+
if (items.length === 0)
|
|
50
|
+
return (_jsx("div", { className: "rounded-lg border bg-fd-card p-3 text-xs text-fd-muted-foreground", children: text.tocNoHeadings }));
|
|
51
|
+
return (_jsxs(_Fragment, { children: [svg && (_jsx("div", { className: "absolute start-0 top-0 rtl:-scale-x-100", style: {
|
|
52
|
+
width: svg.width,
|
|
53
|
+
height: svg.height,
|
|
54
|
+
maskImage: `url("data:image/svg+xml,${
|
|
55
|
+
// Inline SVG
|
|
56
|
+
encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${svg.width} ${svg.height}"><path d="${svg.path}" stroke="black" stroke-width="1" fill="none" /></svg>`)}")`,
|
|
57
|
+
}, children: _jsx(TocThumb, { containerRef: containerRef, className: "translate-y-(--fd-top) h-(--fd-height) bg-fd-primary transition-[translate,height]" }) })), _jsx("div", { ref: mergeRefs(containerRef, ref), className: cn('flex flex-col', className), ...props, children: items.map((item, i) => (_jsx(TOCItem, { item: item, upper: items[i - 1]?.depth, lower: items[i + 1]?.depth }, item.url))) })] }));
|
|
58
|
+
}
|
|
59
|
+
function getItemOffset(depth) {
|
|
60
|
+
if (depth <= 2)
|
|
61
|
+
return 14;
|
|
62
|
+
if (depth === 3)
|
|
63
|
+
return 26;
|
|
64
|
+
return 36;
|
|
65
|
+
}
|
|
66
|
+
function getLineOffset(depth) {
|
|
67
|
+
return depth >= 3 ? 10 : 0;
|
|
68
|
+
}
|
|
69
|
+
function TOCItem({ item, upper = item.depth, lower = item.depth, }) {
|
|
70
|
+
const offset = getLineOffset(item.depth), upperOffset = getLineOffset(upper), lowerOffset = getLineOffset(lower);
|
|
71
|
+
return (_jsxs(Primitive.TOCItem, { href: item.url, style: {
|
|
72
|
+
paddingInlineStart: getItemOffset(item.depth),
|
|
73
|
+
}, className: "prose relative py-1.5 text-sm text-fd-muted-foreground hover:text-fd-accent-foreground transition-colors wrap-anywhere first:pt-0 last:pb-0 data-[active=true]:text-fd-primary", children: [offset !== upperOffset && (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", className: "absolute -top-1.5 start-0 size-4 rtl:-scale-x-100", children: _jsx("line", { x1: upperOffset, y1: "0", x2: offset, y2: "12", className: "stroke-fd-foreground/10", strokeWidth: "1" }) })), _jsx("div", { className: cn('absolute inset-y-0 w-px bg-fd-foreground/10', offset !== upperOffset && 'top-1.5', offset !== lowerOffset && 'bottom-1.5'), style: {
|
|
74
|
+
insetInlineStart: offset,
|
|
75
|
+
} }), item.title] }));
|
|
76
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../src/components/toc/default.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAU,MAAM,OAAO,CAAC;AAKpD,wBAAgB,QAAQ,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,2CAgC3E"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useI18n } from '../../contexts/i18n.js';
|
|
4
|
+
import { cn } from '../../cn.js';
|
|
5
|
+
import { useRef } from 'react';
|
|
6
|
+
import { mergeRefs } from '../../merge-refs.js';
|
|
7
|
+
import { TocThumb, useTOCItems } from './index.js';
|
|
8
|
+
import * as Primitive from 'fumadocs-core/toc';
|
|
9
|
+
export function TOCItems({ ref, className, ...props }) {
|
|
10
|
+
const containerRef = useRef(null);
|
|
11
|
+
const items = useTOCItems();
|
|
12
|
+
const { text } = useI18n();
|
|
13
|
+
if (items.length === 0)
|
|
14
|
+
return (_jsx("div", { className: "rounded-lg border bg-fd-card p-3 text-xs text-fd-muted-foreground", children: text.tocNoHeadings }));
|
|
15
|
+
return (_jsxs(_Fragment, { children: [_jsx(TocThumb, { containerRef: containerRef, className: "absolute top-0 translate-y-(--fd-top) h-(--fd-height) w-px bg-fd-primary transition-[translate,height]" }), _jsx("div", { ref: mergeRefs(ref, containerRef), className: cn('flex flex-col border-s border-fd-foreground/10', className), ...props, children: items.map((item) => (_jsx(TOCItem, { item: item }, item.url))) })] }));
|
|
16
|
+
}
|
|
17
|
+
function TOCItem({ item }) {
|
|
18
|
+
return (_jsx(Primitive.TOCItem, { href: item.url, className: cn('prose py-1.5 text-sm text-fd-muted-foreground transition-colors wrap-anywhere first:pt-0 last:pb-0 data-[active=true]:text-fd-primary', item.depth <= 2 && 'ps-3', item.depth === 3 && 'ps-6', item.depth >= 4 && 'ps-8'), children: item.title }));
|
|
19
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as Primitive from 'fumadocs-core/toc';
|
|
2
|
+
import { type ComponentProps, type RefObject } from 'react';
|
|
3
|
+
export declare function useTOCItems(): Primitive.TOCItemType[];
|
|
4
|
+
export declare function TOCProvider({ toc, children, ...props }: ComponentProps<typeof Primitive.AnchorProvider>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function TOCScrollArea({ ref, className, ...props }: ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
interface RefProps {
|
|
7
|
+
containerRef: RefObject<HTMLElement | null>;
|
|
8
|
+
}
|
|
9
|
+
export declare function TocThumb({ containerRef, ...props }: ComponentProps<'div'> & RefProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/toc/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AAOf,wBAAgB,WAAW,IAAI,SAAS,CAAC,WAAW,EAAE,CAErD;AAED,wBAAgB,WAAW,CAAC,EAC1B,GAAG,EACH,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,2CAQjD;AAED,wBAAgB,aAAa,CAAC,EAC5B,GAAG,EACH,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,KAAK,CAAC,2CAiBvB;AAID,UAAU,QAAQ;IAChB,YAAY,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC7C;AAED,wBAAgB,QAAQ,CAAC,EACvB,YAAY,EACZ,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,QAAQ,2CA4BlC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as Primitive from 'fumadocs-core/toc';
|
|
4
|
+
import { createContext, use, useEffect, useEffectEvent, useRef, } from 'react';
|
|
5
|
+
import { cn } from '../../cn.js';
|
|
6
|
+
import { mergeRefs } from '../../merge-refs.js';
|
|
7
|
+
import { useOnChange } from 'fumadocs-core/utils/use-on-change';
|
|
8
|
+
const TOCContext = createContext([]);
|
|
9
|
+
export function useTOCItems() {
|
|
10
|
+
return use(TOCContext);
|
|
11
|
+
}
|
|
12
|
+
export function TOCProvider({ toc, children, ...props }) {
|
|
13
|
+
return (_jsx(TOCContext, { value: toc, children: _jsx(Primitive.AnchorProvider, { toc: toc, ...props, children: children }) }));
|
|
14
|
+
}
|
|
15
|
+
export function TOCScrollArea({ ref, className, ...props }) {
|
|
16
|
+
const viewRef = useRef(null);
|
|
17
|
+
return (_jsx("div", { ref: mergeRefs(viewRef, ref), className: cn('relative min-h-0 text-sm ms-px overflow-auto [scrollbar-width:none] mask-[linear-gradient(to_bottom,transparent,white_16px,white_calc(100%-16px),transparent)] py-3', className), ...props, children: _jsx(Primitive.ScrollProvider, { containerRef: viewRef, children: props.children }) }));
|
|
18
|
+
}
|
|
19
|
+
export function TocThumb({ containerRef, ...props }) {
|
|
20
|
+
const thumbRef = useRef(null);
|
|
21
|
+
const active = Primitive.useActiveAnchors();
|
|
22
|
+
const onPrint = useEffectEvent(() => {
|
|
23
|
+
if (!containerRef.current || !thumbRef.current)
|
|
24
|
+
return;
|
|
25
|
+
update(thumbRef.current, calc(containerRef.current, active));
|
|
26
|
+
});
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!containerRef.current)
|
|
29
|
+
return;
|
|
30
|
+
const container = containerRef.current;
|
|
31
|
+
const observer = new ResizeObserver(onPrint);
|
|
32
|
+
observer.observe(container);
|
|
33
|
+
return () => {
|
|
34
|
+
observer.disconnect();
|
|
35
|
+
};
|
|
36
|
+
}, [containerRef]);
|
|
37
|
+
useOnChange(active, () => {
|
|
38
|
+
if (containerRef.current && thumbRef.current) {
|
|
39
|
+
update(thumbRef.current, calc(containerRef.current, active));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return _jsx("div", { ref: thumbRef, role: "none", ...props });
|
|
43
|
+
}
|
|
44
|
+
function calc(container, active) {
|
|
45
|
+
if (active.length === 0 || container.clientHeight === 0) {
|
|
46
|
+
return [0, 0];
|
|
47
|
+
}
|
|
48
|
+
let upper = Number.MAX_VALUE, lower = 0;
|
|
49
|
+
for (const item of active) {
|
|
50
|
+
const element = container.querySelector(`a[href="#${item}"]`);
|
|
51
|
+
if (!element)
|
|
52
|
+
continue;
|
|
53
|
+
const styles = getComputedStyle(element);
|
|
54
|
+
upper = Math.min(upper, element.offsetTop + parseFloat(styles.paddingTop));
|
|
55
|
+
lower = Math.max(lower, element.offsetTop +
|
|
56
|
+
element.clientHeight -
|
|
57
|
+
parseFloat(styles.paddingBottom));
|
|
58
|
+
}
|
|
59
|
+
return [upper, lower - upper];
|
|
60
|
+
}
|
|
61
|
+
function update(element, info) {
|
|
62
|
+
element.style.setProperty('--fd-top', `${info[0]}px`);
|
|
63
|
+
element.style.setProperty('--fd-height', `${info[1]}px`);
|
|
64
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface Translations {
|
|
3
|
+
search: string;
|
|
4
|
+
searchNoResult: string;
|
|
5
|
+
toc: string;
|
|
6
|
+
tocNoHeadings: string;
|
|
7
|
+
lastUpdate: string;
|
|
8
|
+
chooseLanguage: string;
|
|
9
|
+
nextPage: string;
|
|
10
|
+
previousPage: string;
|
|
11
|
+
chooseTheme: string;
|
|
12
|
+
editOnGithub: string;
|
|
13
|
+
}
|
|
14
|
+
export interface LocaleItem {
|
|
15
|
+
name: string;
|
|
16
|
+
locale: string;
|
|
17
|
+
}
|
|
18
|
+
interface I18nContextType {
|
|
19
|
+
locale?: string;
|
|
20
|
+
onChange?: (v: string) => void;
|
|
21
|
+
text: Translations;
|
|
22
|
+
locales?: LocaleItem[];
|
|
23
|
+
}
|
|
24
|
+
export declare const defaultTranslations: Translations;
|
|
25
|
+
export declare function I18nLabel(props: {
|
|
26
|
+
label: keyof Translations;
|
|
27
|
+
}): string;
|
|
28
|
+
export declare function useI18n(): I18nContextType;
|
|
29
|
+
export interface I18nProviderProps {
|
|
30
|
+
/**
|
|
31
|
+
* Current locale
|
|
32
|
+
*/
|
|
33
|
+
locale: string;
|
|
34
|
+
/**
|
|
35
|
+
* Handle changes to the locale, redirect user when not specified.
|
|
36
|
+
*/
|
|
37
|
+
onLocaleChange?: (v: string) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Translations of current locale
|
|
40
|
+
*/
|
|
41
|
+
translations?: Partial<Translations>;
|
|
42
|
+
/**
|
|
43
|
+
* Available languages
|
|
44
|
+
*/
|
|
45
|
+
locales?: LocaleItem[];
|
|
46
|
+
children?: ReactNode;
|
|
47
|
+
}
|
|
48
|
+
export declare function I18nProvider({ locales, locale, onLocaleChange, children, translations, }: I18nProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
49
|
+
export {};
|
|
50
|
+
//# sourceMappingURL=i18n.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/contexts/i18n.tsx"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,SAAS,EAIf,MAAM,OAAO,CAAC;AAGf,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IAEvB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IAEtB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;CACxB;AAED,eAAO,MAAM,mBAAmB,EAAE,YAWjC,CAAC;AAMF,wBAAgB,SAAS,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,YAAY,CAAA;CAAE,GAAG,MAAM,CAItE;AAED,wBAAgB,OAAO,IAAI,eAAe,CAEzC;AAED,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAErC;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAErC;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IAEvB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,EAC3B,OAAY,EACZ,MAAM,EACN,cAAc,EACd,QAAQ,EACR,YAAY,GACb,EAAE,iBAAiB,2CAuCnB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useMemo, useRef, } from 'react';
|
|
4
|
+
import { usePathname, useRouter } from 'fumadocs-core/framework';
|
|
5
|
+
export const defaultTranslations = {
|
|
6
|
+
search: 'Search',
|
|
7
|
+
searchNoResult: 'No results found',
|
|
8
|
+
toc: 'On this page',
|
|
9
|
+
tocNoHeadings: 'No Headings',
|
|
10
|
+
lastUpdate: 'Last updated on',
|
|
11
|
+
chooseLanguage: 'Choose a language',
|
|
12
|
+
nextPage: 'Next Page',
|
|
13
|
+
previousPage: 'Previous Page',
|
|
14
|
+
chooseTheme: 'Theme',
|
|
15
|
+
editOnGithub: 'Edit on GitHub',
|
|
16
|
+
};
|
|
17
|
+
const I18nContext = createContext({
|
|
18
|
+
text: defaultTranslations,
|
|
19
|
+
});
|
|
20
|
+
export function I18nLabel(props) {
|
|
21
|
+
const { text } = useI18n();
|
|
22
|
+
return text[props.label];
|
|
23
|
+
}
|
|
24
|
+
export function useI18n() {
|
|
25
|
+
return useContext(I18nContext);
|
|
26
|
+
}
|
|
27
|
+
export function I18nProvider({ locales = [], locale, onLocaleChange, children, translations, }) {
|
|
28
|
+
const router = useRouter();
|
|
29
|
+
const pathname = usePathname();
|
|
30
|
+
const onChange = (value) => {
|
|
31
|
+
if (onLocaleChange) {
|
|
32
|
+
return onLocaleChange(value);
|
|
33
|
+
}
|
|
34
|
+
const segments = pathname.split('/').filter((v) => v.length > 0);
|
|
35
|
+
// If locale prefix hidden
|
|
36
|
+
if (segments[0] !== locale) {
|
|
37
|
+
segments.unshift(value);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
segments[0] = value;
|
|
41
|
+
}
|
|
42
|
+
router.push(`/${segments.join('/')}`);
|
|
43
|
+
};
|
|
44
|
+
const onChangeRef = useRef(onChange);
|
|
45
|
+
onChangeRef.current = onChange;
|
|
46
|
+
return (_jsx(I18nContext, { value: useMemo(() => ({
|
|
47
|
+
locale,
|
|
48
|
+
locales,
|
|
49
|
+
text: {
|
|
50
|
+
...defaultTranslations,
|
|
51
|
+
...translations,
|
|
52
|
+
},
|
|
53
|
+
onChange: (v) => onChangeRef.current(v),
|
|
54
|
+
}), [locale, locales, translations]), children: children }));
|
|
55
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type ComponentType, type ReactNode } from 'react';
|
|
2
|
+
interface HotKey {
|
|
3
|
+
display: ReactNode;
|
|
4
|
+
/**
|
|
5
|
+
* Key code or a function determining whether the key is pressed.
|
|
6
|
+
*/
|
|
7
|
+
key: string | ((e: KeyboardEvent) => boolean);
|
|
8
|
+
}
|
|
9
|
+
export interface SharedProps {
|
|
10
|
+
open: boolean;
|
|
11
|
+
onOpenChange: (open: boolean) => void;
|
|
12
|
+
}
|
|
13
|
+
export type SearchLink = [name: string, href: string];
|
|
14
|
+
export interface TagItem {
|
|
15
|
+
name: string;
|
|
16
|
+
value: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SearchProviderProps {
|
|
19
|
+
/**
|
|
20
|
+
* Preload search dialog before opening it
|
|
21
|
+
*
|
|
22
|
+
* @defaultValue `true`
|
|
23
|
+
*/
|
|
24
|
+
preload?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Custom links to be displayed if search is empty
|
|
27
|
+
*/
|
|
28
|
+
links?: SearchLink[];
|
|
29
|
+
/**
|
|
30
|
+
* Hotkeys for triggering search dialog
|
|
31
|
+
*
|
|
32
|
+
* @defaultValue Meta/Ctrl + K
|
|
33
|
+
*/
|
|
34
|
+
hotKey?: HotKey[];
|
|
35
|
+
/**
|
|
36
|
+
* Replace default search dialog, allowing you to use other solutions such as Algolia Search
|
|
37
|
+
*
|
|
38
|
+
* It receives the `open` and `onOpenChange` prop, can be lazy loaded with `next/dynamic`
|
|
39
|
+
*/
|
|
40
|
+
SearchDialog: ComponentType<SharedProps>;
|
|
41
|
+
/**
|
|
42
|
+
* Additional props to the dialog
|
|
43
|
+
*/
|
|
44
|
+
options?: Partial<SharedProps & Record<string, unknown>>;
|
|
45
|
+
children?: ReactNode;
|
|
46
|
+
}
|
|
47
|
+
interface SearchContextType {
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
hotKey: HotKey[];
|
|
50
|
+
setOpenSearch: (value: boolean) => void;
|
|
51
|
+
}
|
|
52
|
+
export declare function useSearchContext(): SearchContextType;
|
|
53
|
+
export declare function SearchProvider({ SearchDialog, children, preload, options, hotKey, links, }: SearchProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
54
|
+
/**
|
|
55
|
+
* Show children only when search is enabled via React Context
|
|
56
|
+
*/
|
|
57
|
+
export declare function SearchOnly({ children }: {
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
}): ReactNode;
|
|
60
|
+
export {};
|
|
61
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/contexts/search.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAEf,UAAU,MAAM;IACd,OAAO,EAAE,SAAS,CAAC;IAEnB;;OAEG;IACH,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAEtD,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IAErB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;;;OAIG;IACH,YAAY,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAEzC;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEzD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;AAQD,wBAAgB,gBAAgB,IAAI,iBAAiB,CAEpD;AAcD,wBAAgB,cAAc,CAAC,EAC7B,YAAY,EACZ,QAAQ,EACR,OAAc,EACd,OAAO,EACP,MASC,EACD,KAAK,GACN,EAAE,mBAAmB,2CA2CrB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,aAI/D"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, use, useEffect, useEffectEvent, useMemo, useState, } from 'react';
|
|
4
|
+
const SearchContext = createContext({
|
|
5
|
+
enabled: false,
|
|
6
|
+
hotKey: [],
|
|
7
|
+
setOpenSearch: () => undefined,
|
|
8
|
+
});
|
|
9
|
+
export function useSearchContext() {
|
|
10
|
+
return use(SearchContext);
|
|
11
|
+
}
|
|
12
|
+
function MetaOrControl() {
|
|
13
|
+
const [key, setKey] = useState('⌘');
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const isWindows = window.navigator.userAgent.includes('Windows');
|
|
16
|
+
if (isWindows)
|
|
17
|
+
setKey('Ctrl');
|
|
18
|
+
}, []);
|
|
19
|
+
return key;
|
|
20
|
+
}
|
|
21
|
+
export function SearchProvider({ SearchDialog, children, preload = true, options, hotKey = [
|
|
22
|
+
{
|
|
23
|
+
key: (e) => e.metaKey || e.ctrlKey,
|
|
24
|
+
display: _jsx(MetaOrControl, {}),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: 'k',
|
|
28
|
+
display: 'K',
|
|
29
|
+
},
|
|
30
|
+
], links, }) {
|
|
31
|
+
const [isOpen, setIsOpen] = useState(preload ? false : undefined);
|
|
32
|
+
const onKeyDown = useEffectEvent((e) => {
|
|
33
|
+
if (hotKey.every((v) => typeof v.key === 'string' ? e.key === v.key : v.key(e))) {
|
|
34
|
+
setIsOpen((open) => !open);
|
|
35
|
+
e.preventDefault();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
window.addEventListener('keydown', onKeyDown);
|
|
40
|
+
return () => {
|
|
41
|
+
window.removeEventListener('keydown', onKeyDown);
|
|
42
|
+
};
|
|
43
|
+
}, [hotKey]);
|
|
44
|
+
return (_jsxs(SearchContext, { value: useMemo(() => ({
|
|
45
|
+
enabled: true,
|
|
46
|
+
hotKey,
|
|
47
|
+
setOpenSearch: setIsOpen,
|
|
48
|
+
}), [hotKey]), children: [isOpen !== undefined && (_jsx(SearchDialog, { open: isOpen, onOpenChange: setIsOpen,
|
|
49
|
+
// @ts-expect-error -- insert prop for official UIs
|
|
50
|
+
links: links, ...options })), children] }));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Show children only when search is enabled via React Context
|
|
54
|
+
*/
|
|
55
|
+
export function SearchOnly({ children }) {
|
|
56
|
+
const search = useSearchContext();
|
|
57
|
+
if (search.enabled)
|
|
58
|
+
return children;
|
|
59
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type * as PageTree from 'fumadocs-core/page-tree';
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
type MakeRequired<O, K extends keyof O> = Omit<O, K> & Pick<Required<O>, K>;
|
|
4
|
+
interface TreeContextType {
|
|
5
|
+
root: MakeRequired<PageTree.Root | PageTree.Folder, '$id'>;
|
|
6
|
+
full: PageTree.Root;
|
|
7
|
+
}
|
|
8
|
+
export declare function TreeContextProvider({ tree: rawTree, children, }: {
|
|
9
|
+
tree: PageTree.Root;
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare function useTreePath(): PageTree.Node[];
|
|
13
|
+
export declare function useTreeContext(): TreeContextType;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/contexts/tree.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,KAAK,SAAS,EAAuC,MAAM,OAAO,CAAC;AAG5E,KAAK,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE5E,UAAU,eAAe;IACvB,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC3D,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;CACrB;AAKD,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EAAE,OAAO,EACb,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;CACrB,2CA8BA;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAAC,IAAI,EAAE,CAE7C;AAED,wBAAgB,cAAc,IAAI,eAAe,CAMhD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { usePathname } from 'fumadocs-core/framework';
|
|
4
|
+
import { useMemo, useRef, createContext, use } from 'react';
|
|
5
|
+
import { searchPath } from 'fumadocs-core/breadcrumb';
|
|
6
|
+
const TreeContext = createContext(null);
|
|
7
|
+
const PathContext = createContext([]);
|
|
8
|
+
export function TreeContextProvider({ tree: rawTree, children, }) {
|
|
9
|
+
const nextIdRef = useRef(0);
|
|
10
|
+
const pathname = usePathname();
|
|
11
|
+
// I found that object-typed props passed from a RSC will be re-constructed, hence breaking all hooks' dependencies
|
|
12
|
+
// using the id here to make sure this never happens
|
|
13
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
14
|
+
const tree = useMemo(() => rawTree, [rawTree.$id ?? rawTree]);
|
|
15
|
+
const path = useMemo(() => {
|
|
16
|
+
return (searchPath(tree.children, pathname) ??
|
|
17
|
+
(tree.fallback ? searchPath(tree.fallback.children, pathname) : null) ??
|
|
18
|
+
[]);
|
|
19
|
+
}, [tree, pathname]);
|
|
20
|
+
const root = path.findLast((item) => item.type === 'folder' && item.root) ?? tree;
|
|
21
|
+
root.$id ?? (root.$id = String(nextIdRef.current++));
|
|
22
|
+
return (_jsx(TreeContext, { value: useMemo(() => ({ root, full: tree }), [root, tree]), children: _jsx(PathContext, { value: path, children: children }) }));
|
|
23
|
+
}
|
|
24
|
+
export function useTreePath() {
|
|
25
|
+
return use(PathContext);
|
|
26
|
+
}
|
|
27
|
+
export function useTreeContext() {
|
|
28
|
+
const ctx = use(TreeContext);
|
|
29
|
+
if (!ctx)
|
|
30
|
+
throw new Error('You must wrap this component under <DocsLayout />');
|
|
31
|
+
return ctx;
|
|
32
|
+
}
|