fumadocs-ui 14.5.5 → 14.6.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.
@@ -27,7 +27,7 @@ export const CodeBlock = forwardRef(({ title, allowCopy = true, keepBackground =
27
27
  ? {
28
28
  __html: icon,
29
29
  }
30
- : undefined, children: typeof icon !== 'string' ? icon : null })) : null, _jsx("figcaption", { className: "flex-1 truncate text-fd-muted-foreground", children: title }), allowCopy ? (_jsx(CopyButton, { className: "-me-2", onCopy: onCopy })) : null] })) : (allowCopy && (_jsx(CopyButton, { className: "absolute right-2 top-2 z-[2] backdrop-blur-md", onCopy: onCopy }))), _jsxs(ScrollArea, { ref: areaRef, dir: "ltr", children: [_jsx(ScrollViewport, { ...viewportProps, className: cn('max-h-[400px]', viewportProps?.className), children: props.children }), _jsx(ScrollBar, { orientation: "horizontal" })] })] }));
30
+ : undefined, children: typeof icon !== 'string' ? icon : null })) : null, _jsx("figcaption", { className: "flex-1 truncate text-fd-muted-foreground", children: title }), allowCopy ? (_jsx(CopyButton, { className: "-me-2", onCopy: onCopy })) : null] })) : (allowCopy && (_jsx(CopyButton, { className: "absolute right-2 top-2 z-[2] backdrop-blur-md", onCopy: onCopy }))), _jsxs(ScrollArea, { ref: areaRef, dir: "ltr", children: [_jsx(ScrollViewport, { ...viewportProps, className: cn('max-h-[600px]', viewportProps?.className), children: props.children }), _jsx(ScrollBar, { orientation: "horizontal" })] })] }));
31
31
  });
32
32
  CodeBlock.displayName = 'CodeBlock';
33
33
  function CopyButton({ className, onCopy, ...props }) {
@@ -46,8 +46,7 @@ function SearchList({ items, hideResults = false }) {
46
46
  (!active || items.every((item) => item.id !== active))) {
47
47
  setActive(items[0].id);
48
48
  }
49
- const listenerRef = useRef();
50
- listenerRef.current = (e) => {
49
+ function onKey(e) {
51
50
  if (e.key === 'ArrowDown' || e.key == 'ArrowUp') {
52
51
  setActive((cur) => {
53
52
  const idx = items.findIndex((item) => item.id === cur);
@@ -63,11 +62,11 @@ function SearchList({ items, hideResults = false }) {
63
62
  onOpen(selected.url);
64
63
  e.preventDefault();
65
64
  }
66
- };
65
+ }
66
+ const listenerRef = useRef(onKey);
67
+ listenerRef.current = onKey;
67
68
  useEffect(() => {
68
- const listener = (e) => {
69
- listenerRef.current?.(e);
70
- };
69
+ const listener = (e) => listenerRef.current?.(e);
71
70
  window.addEventListener('keydown', listener);
72
71
  return () => {
73
72
  window.removeEventListener('keydown', listener);
@@ -0,0 +1,7 @@
1
+ import type { HighlightOptions } from 'fumadocs-core/server';
2
+ export declare function DynamicCodeBlock({ lang, code, options, }: {
3
+ lang: string;
4
+ code: string;
5
+ options?: Omit<HighlightOptions, 'lang'>;
6
+ }): import("react/jsx-runtime").JSX.Element;
7
+ //# sourceMappingURL=dynamic-codeblock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-codeblock.d.ts","sourceRoot":"","sources":["../../src/components/dynamic-codeblock.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAc7D,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,IAAI,EACJ,OAAO,GACR,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;CAC1C,2CAuBA"}
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { CodeBlock, Pre } from '../components/codeblock';
4
+ import { PrerenderScript, useShiki } from 'fumadocs-core/utils/use-shiki';
5
+ import { useId } from 'react';
6
+ const components = {
7
+ pre(props) {
8
+ return (_jsx(CodeBlock, { ...props, children: _jsx(Pre, { children: props.children }) }));
9
+ },
10
+ };
11
+ export function DynamicCodeBlock({ lang, code, options, }) {
12
+ const scriptKey = useId();
13
+ const shikiOptions = {
14
+ lang,
15
+ scriptKey,
16
+ ...options,
17
+ components: {
18
+ ...components,
19
+ ...options?.components,
20
+ },
21
+ };
22
+ const children = useShiki(code, shikiOptions);
23
+ return (_jsxs(_Fragment, { children: [_jsx(PrerenderScript, { scriptKey: scriptKey, code: code, options: shikiOptions }), children] }));
24
+ }
@@ -1,5 +1,4 @@
1
1
  import { type HTMLAttributes, type ReactNode } from 'react';
2
- import type { PageTree } from 'fumadocs-core/server';
3
2
  export interface Option {
4
3
  /**
5
4
  * Redirect URL of the folder, usually the index page
@@ -9,12 +8,13 @@ export interface Option {
9
8
  title: ReactNode;
10
9
  description?: ReactNode;
11
10
  /**
12
- * Detect from page tree nodes
11
+ * Detect from a list of urls
13
12
  */
14
- folder?: PageTree.Folder;
13
+ urls?: string[];
15
14
  props?: HTMLAttributes<HTMLElement>;
16
15
  }
17
- export declare function RootToggle({ options, ...props }: {
16
+ export declare function RootToggle({ options, placeholder, ...props }: {
17
+ placeholder?: ReactNode;
18
18
  options: Option[];
19
19
  } & HTMLAttributes<HTMLButtonElement>): import("react/jsx-runtime").JSX.Element;
20
20
  //# sourceMappingURL=root-toggle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"root-toggle.d.ts","sourceRoot":"","sources":["../../../src/components/layout/root-toggle.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAC;AAO/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGrD,MAAM,WAAW,MAAM;IACrB;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IAExB;;OAEG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC;IAEzB,KAAK,CAAC,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC;CACrC;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,GAAG,KAAK,EACT,EAAE;IACD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,cAAc,CAAC,iBAAiB,CAAC,2CAqDpC"}
1
+ {"version":3,"file":"root-toggle.d.ts","sourceRoot":"","sources":["../../../src/components/layout/root-toggle.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAC;AAQ/E,MAAM,WAAW,MAAM;IACrB;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IAExB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB,KAAK,CAAC,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC;CACrC;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,WAAW,EACX,GAAG,KAAK,EACT,EAAE;IACD,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,cAAc,CAAC,iBAAiB,CAAC,2CAuDpC"}
@@ -8,22 +8,21 @@ import { cn } from '../../utils/cn';
8
8
  import { isActive } from '../../utils/is-active';
9
9
  import { useSidebar } from '../../contexts/sidebar';
10
10
  import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
11
- import { useTreePath } from '../../contexts/tree';
12
- export function RootToggle({ options, ...props }) {
11
+ export function RootToggle({ options, placeholder, ...props }) {
13
12
  const [open, setOpen] = useState(false);
14
13
  const { closeOnRedirect } = useSidebar();
15
14
  const pathname = usePathname();
16
- const path = useTreePath();
17
15
  const selected = useMemo(() => {
18
- return options.findLast((item) => item.folder
19
- ? path.includes(item.folder)
16
+ return options.findLast((item) => item.urls
17
+ ? item.urls.includes(pathname)
20
18
  : isActive(item.url, pathname, true));
21
- }, [path, options, pathname]);
19
+ }, [options, pathname]);
22
20
  const onClick = () => {
23
21
  closeOnRedirect.current = false;
24
22
  setOpen(false);
25
23
  };
26
- return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs(PopoverTrigger, { ...props, className: cn('flex flex-row items-center gap-2 rounded-lg px-2 py-1.5 hover:bg-fd-accent/50 hover:text-fd-accent-foreground', props.className), children: [selected ? _jsx(Item, { ...selected }) : null, _jsx(ChevronDown, { className: "me-1.5 size-4 text-fd-muted-foreground" })] }), _jsx(PopoverContent, { className: "w-[var(--radix-popover-trigger-width)] overflow-hidden p-0", children: options.map((item) => (_jsx(Link, { href: item.url, onClick: onClick, ...item.props, className: cn('flex w-full flex-row items-center gap-2 px-2 py-1.5', selected === item
24
+ const item = selected ? _jsx(Item, { ...selected }) : placeholder;
25
+ return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [item ? (_jsxs(PopoverTrigger, { ...props, className: cn('flex flex-row items-center gap-2 rounded-lg px-2 py-1.5 hover:bg-fd-accent/50 hover:text-fd-accent-foreground', props.className), children: [item, _jsx(ChevronDown, { className: "me-1.5 size-4 text-fd-muted-foreground" })] })) : null, _jsx(PopoverContent, { className: "w-[var(--radix-popover-trigger-width)] overflow-hidden p-0", children: options.map((item) => (_jsx(Link, { href: item.url, onClick: onClick, ...item.props, className: cn('flex w-full flex-row items-center gap-2 px-2 py-1.5', selected === item
27
26
  ? 'bg-fd-accent text-fd-accent-foreground'
28
27
  : 'hover:bg-fd-accent/50', item.props?.className), children: _jsx(Item, { ...item }) }, item.url))) })] }));
29
28
  }
@@ -1,6 +1,6 @@
1
1
  import { type HTMLAttributes, type ReactNode, type RefObject } from 'react';
2
2
  export type TOCThumb = [top: number, height: number];
3
3
  export declare function TocThumb({ containerRef, ...props }: HTMLAttributes<HTMLDivElement> & {
4
- containerRef: RefObject<HTMLElement>;
4
+ containerRef: RefObject<HTMLElement | null>;
5
5
  }): ReactNode;
6
6
  //# sourceMappingURL=toc-thumb.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toc-thumb.d.ts","sourceRoot":"","sources":["../../../src/components/layout/toc-thumb.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AAIf,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAgCrD,wBAAgB,QAAQ,CAAC,EACvB,YAAY,EACZ,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,cAAc,CAAC,GAAG;IAClC,YAAY,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;CACtC,GAAG,SAAS,CAgCZ"}
1
+ {"version":3,"file":"toc-thumb.d.ts","sourceRoot":"","sources":["../../../src/components/layout/toc-thumb.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AAIf,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAgCrD,wBAAgB,QAAQ,CAAC,EACvB,YAAY,EACZ,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,cAAc,CAAC,GAAG;IAClC,YAAY,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC7C,GAAG,SAAS,CAgCZ"}
@@ -21,5 +21,11 @@ export interface TabsProps extends BaseProps {
21
21
  updateAnchor?: boolean;
22
22
  }
23
23
  export declare function Tabs({ groupId, items, persist, defaultIndex, updateAnchor, ...props }: TabsProps): import("react/jsx-runtime").JSX.Element;
24
- export declare function Tab({ value, className, ...props }: TabsContentProps): import("react/jsx-runtime").JSX.Element;
24
+ export type TabProps = Omit<TabsContentProps, 'value'> & {
25
+ /**
26
+ * Value of tab, detect from index if unspecified.
27
+ */
28
+ value?: TabsContentProps['value'];
29
+ };
30
+ export declare function Tab({ value, className, ...props }: TabProps): import("react/jsx-runtime").JSX.Element;
25
31
  //# sourceMappingURL=tabs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/components/tabs.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,SAAS,IAAI,SAAS,EACvB,MAAM,sBAAsB,CAAC;AAW9B,OAAO,KAAK,SAAS,MAAM,WAAW,CAAC;AAEvC,OAAO,EAAE,SAAS,EAAE,CAAC;AAmBrB,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAMD,wBAAgB,IAAI,CAAC,EACnB,OAAO,EACP,KAAU,EACV,OAAe,EACf,YAAgB,EAChB,YAAoB,EACpB,GAAG,KAAK,EACT,EAAE,SAAS,2CA+EX;AAMD,wBAAgB,GAAG,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,gBAAgB,2CAkBnE"}
1
+ {"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/components/tabs.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,SAAS,IAAI,SAAS,EACvB,MAAM,sBAAsB,CAAC;AAY9B,OAAO,KAAK,SAAS,MAAM,WAAW,CAAC;AAEvC,OAAO,EAAE,SAAS,EAAE,CAAC;AAmBrB,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAQD,wBAAgB,IAAI,CAAC,EACnB,OAAO,EACP,KAAU,EACV,OAAe,EACf,YAAgB,EAChB,YAAoB,EACpB,GAAG,KAAK,EACT,EAAE,SAAS,2CAqFX;AAMD,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,GAAG;IACvD;;OAEG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;CACnC,CAAC;AAEF,wBAAgB,GAAG,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,QAAQ,2CA6B3D"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useMemo, useState, useCallback, createContext, useContext, useRef, useLayoutEffect, } from 'react';
3
+ import { useMemo, useState, createContext, useContext, useRef, useLayoutEffect, useId, useEffect, } from 'react';
4
4
  import { cn } from '../utils/cn';
5
5
  import * as Primitive from './ui/tabs';
6
6
  export { Primitive };
@@ -14,20 +14,23 @@ function removeChangeListener(id, listener) {
14
14
  const list = listeners.get(id) ?? [];
15
15
  listeners.set(id, list.filter((item) => item !== listener));
16
16
  }
17
- const ValueToMapContext = createContext(undefined);
17
+ const TabsContext = createContext(null);
18
18
  export function Tabs({ groupId, items = [], persist = false, defaultIndex = 0, updateAnchor = false, ...props }) {
19
19
  const values = useMemo(() => items.map((item) => toValue(item)), [items]);
20
20
  const [value, setValue] = useState(values[defaultIndex]);
21
- const valueToIdMapRef = useRef(new Map());
22
- const onChangeRef = useRef();
23
- onChangeRef.current = (v) => {
21
+ const valueToIdMap = useMemo(() => new Map(), []);
22
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- re-reconstruct the collection if items changed
23
+ const collection = useMemo(() => createCollection(), [items]);
24
+ const onChange = (v) => {
24
25
  if (values.includes(v))
25
26
  setValue(v);
26
27
  };
28
+ const onChangeRef = useRef(onChange);
29
+ onChangeRef.current = onChange;
27
30
  useLayoutEffect(() => {
28
31
  if (!groupId)
29
32
  return;
30
- const onUpdate = (v) => onChangeRef.current?.(v);
33
+ const onUpdate = (v) => onChangeRef.current(v);
31
34
  const previous = persist
32
35
  ? localStorage.getItem(groupId)
33
36
  : sessionStorage.getItem(groupId);
@@ -42,40 +45,83 @@ export function Tabs({ groupId, items = [], persist = false, defaultIndex = 0, u
42
45
  const hash = window.location.hash.slice(1);
43
46
  if (!hash)
44
47
  return;
45
- const entry = Array.from(valueToIdMapRef.current.entries()).find(([_, id]) => id === hash);
46
- if (entry)
47
- setValue(entry[0]);
48
- }, []);
49
- const onValueChange = useCallback((v) => {
50
- if (updateAnchor) {
51
- const id = valueToIdMapRef.current.get(v);
52
- if (id) {
53
- window.history.replaceState(null, '', `#${id}`);
48
+ for (const [value, id] of valueToIdMap.entries()) {
49
+ if (id === hash) {
50
+ setValue(value);
51
+ break;
54
52
  }
55
53
  }
56
- if (groupId) {
57
- listeners.get(groupId)?.forEach((item) => {
58
- item(v);
59
- });
60
- if (persist)
61
- localStorage.setItem(groupId, v);
62
- else
63
- sessionStorage.setItem(groupId, v);
64
- }
65
- else {
66
- setValue(v);
67
- }
68
- }, [groupId, persist, updateAnchor]);
69
- return (_jsxs(Primitive.Tabs, { value: value, onValueChange: onValueChange, ...props, className: cn('my-4', props.className), children: [_jsx(Primitive.TabsList, { children: values.map((v, i) => (_jsx(Primitive.TabsTrigger, { value: v, children: items[i] }, v))) }), _jsx(ValueToMapContext.Provider, { value: valueToIdMapRef.current, children: props.children })] }));
54
+ }, [valueToIdMap]);
55
+ return (_jsxs(Primitive.Tabs, { value: value, onValueChange: (v) => {
56
+ if (updateAnchor) {
57
+ const id = valueToIdMap.get(v);
58
+ if (id) {
59
+ window.history.replaceState(null, '', `#${id}`);
60
+ }
61
+ }
62
+ if (groupId) {
63
+ listeners.get(groupId)?.forEach((item) => {
64
+ item(v);
65
+ });
66
+ if (persist)
67
+ localStorage.setItem(groupId, v);
68
+ else
69
+ sessionStorage.setItem(groupId, v);
70
+ }
71
+ else {
72
+ setValue(v);
73
+ }
74
+ }, ...props, className: cn('my-4', props.className), children: [_jsx(Primitive.TabsList, { children: values.map((v, i) => (_jsx(Primitive.TabsTrigger, { value: v, children: items[i] }, v))) }), _jsx(TabsContext.Provider, { value: useMemo(() => ({ items, valueToIdMap, collection }), [valueToIdMap, collection, items]), children: props.children })] }));
70
75
  }
71
76
  function toValue(v) {
72
77
  return v.toLowerCase().replace(/\s/, '-');
73
78
  }
74
79
  export function Tab({ value, className, ...props }) {
75
- const v = toValue(value);
76
- const valueToIdMap = useContext(ValueToMapContext);
77
- if (props.id) {
78
- valueToIdMap?.set(v, props.id);
80
+ const ctx = useContext(TabsContext);
81
+ const resolvedValue = value ??
82
+ // eslint-disable-next-line react-hooks/rules-of-hooks -- `value` is not supposed to change
83
+ ctx?.items.at(useCollectionIndex());
84
+ if (!resolvedValue)
85
+ throw new Error('Failed to resolve tab `value`, please pass a `value` prop to the Tab component.');
86
+ const v = toValue(resolvedValue);
87
+ if (props.id && ctx) {
88
+ ctx.valueToIdMap.set(v, props.id);
89
+ }
90
+ return (_jsx(Primitive.TabsContent, { value: v, className: cn('prose-no-margin [&>figure:only-child]:-m-4 [&>figure:only-child]:rounded-none [&>figure:only-child]:border-none', className), ...props, children: props.children }));
91
+ }
92
+ function createCollection() {
93
+ return [];
94
+ }
95
+ /**
96
+ * Inspired by Headless UI.
97
+ *
98
+ * Return the index of children, this is made possible by registering the order of render from children using React context.
99
+ * This is supposed by work with pre-rendering & pure client-side rendering.
100
+ */
101
+ function useCollectionIndex() {
102
+ const key = useId();
103
+ const ctx = useContext(TabsContext);
104
+ if (!ctx)
105
+ throw new Error('You must wrap your component in <Tabs>');
106
+ const list = ctx.collection;
107
+ function register() {
108
+ if (!list.includes(key))
109
+ list.push(key);
110
+ }
111
+ function unregister() {
112
+ const idx = list.indexOf(key);
113
+ if (idx !== -1)
114
+ list.splice(idx, 1);
79
115
  }
80
- return (_jsx(Primitive.TabsContent, { value: v, className: cn('prose-no-margin [&>figure:only-child]:-m-4 [&>figure:only-child]:rounded-none [&>figure:only-child]:border-none', className), ...props }));
116
+ useMemo(() => {
117
+ // re-order the item to the bottom if registered
118
+ unregister();
119
+ register();
120
+ // eslint-disable-next-line -- register
121
+ }, [list]);
122
+ useEffect(() => {
123
+ return unregister;
124
+ // eslint-disable-next-line -- clean up only
125
+ }, []);
126
+ return list.indexOf(key);
81
127
  }
@@ -36,6 +36,6 @@ export interface SidebarComponents {
36
36
  }
37
37
  export declare function SidebarLinkItem({ item }: {
38
38
  item: LinkItemType;
39
- }): string | number | bigint | boolean | Iterable<ReactNode> | Promise<import("react").AwaitedReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
39
+ }): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
40
40
  export declare function getSidebarTabsFromOptions(options: SidebarOptions['tabs'], tree: PageTree.Root): Option[] | undefined;
41
41
  //# sourceMappingURL=shared.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/layouts/docs/shared.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAML,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAE9D,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,SAAS,CAAC;IAErB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAExC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,KAAK,CAAC;IAErC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,EAAE,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAA;KAAE,CAAC,CAAC;IAClC,MAAM,EAAE,EAAE,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,SAAS,EAAE,EAAE,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAA;KAAE,CAAC,CAAC;CAC7C;AAED,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,qKA+C/D;AAED,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,EAC/B,IAAI,EAAE,QAAQ,CAAC,IAAI,wBASpB"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/layouts/docs/shared.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAML,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAE9D,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,SAAS,CAAC;IAErB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAExC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,KAAK,CAAC;IAErC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,EAAE,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAA;KAAE,CAAC,CAAC;IAClC,MAAM,EAAE,EAAE,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,SAAS,EAAE,EAAE,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAA;KAAE,CAAC,CAAC;CAC7C;AAED,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,2UA+C/D;AAED,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,EAC/B,IAAI,EAAE,QAAQ,CAAC,IAAI,wBASpB"}
package/dist/style.css CHANGED
@@ -105,7 +105,7 @@
105
105
  --tw-contain-paint: ;
106
106
  --tw-contain-style: ;
107
107
  }/*
108
- ! tailwindcss v3.4.15 | MIT License | https://tailwindcss.com
108
+ ! tailwindcss v3.4.16 | MIT License | https://tailwindcss.com
109
109
  *//*
110
110
  1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
111
111
  2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
@@ -1250,6 +1250,9 @@ body {
1250
1250
  .max-h-\[460px\] {
1251
1251
  max-height: 460px;
1252
1252
  }
1253
+ .max-h-\[600px\] {
1254
+ max-height: 600px;
1255
+ }
1253
1256
  .max-h-\[var\(--radix-popover-content-available-height\)\] {
1254
1257
  max-height: var(--radix-popover-content-available-height);
1255
1258
  }
@@ -1 +1 @@
1
- {"version":3,"file":"get-sidebar-tabs.d.ts","sourceRoot":"","sources":["../../src/utils/get-sidebar-tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAE9D,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACtE;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,QAAQ,CAAC,IAAI,EACvB,EAAE,SAAS,EAAE,GAAE,UAAe,GAC7B,MAAM,EAAE,CA0BV"}
1
+ {"version":3,"file":"get-sidebar-tabs.d.ts","sourceRoot":"","sources":["../../src/utils/get-sidebar-tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAE9D,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACtE;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,QAAQ,CAAC,IAAI,EACvB,EAAE,SAAS,EAAE,GAAE,UAAe,GAC7B,MAAM,EAAE,CA8BV"}
@@ -1,24 +1,38 @@
1
1
  export function getSidebarTabs(pageTree, { transform } = {}) {
2
- const options = [];
3
- function traverse(node) {
4
- if (node.type === 'folder' && node.root) {
2
+ function findOptions(node) {
3
+ const results = [];
4
+ if (node.root) {
5
5
  const index = node.index ?? node.children.at(0);
6
6
  if (index?.type === 'page') {
7
7
  const option = {
8
8
  url: index.url,
9
9
  title: node.name,
10
10
  icon: node.icon,
11
- folder: node,
12
11
  description: node.description,
12
+ urls: getFolderUrls(node),
13
13
  };
14
14
  const mapped = transform ? transform(option, node) : option;
15
15
  if (mapped)
16
- options.push(mapped);
16
+ results.push(mapped);
17
17
  }
18
18
  }
19
- if (node.type === 'folder')
20
- node.children.forEach(traverse);
19
+ for (const child of node.children) {
20
+ if (child.type === 'folder')
21
+ results.push(...findOptions(child));
22
+ }
23
+ return results;
24
+ }
25
+ return findOptions(pageTree);
26
+ }
27
+ function getFolderUrls(folder) {
28
+ const results = [];
29
+ if (folder.index)
30
+ results.push(folder.index.url);
31
+ for (const child of folder.children) {
32
+ if (child.type === 'page')
33
+ results.push(child.url);
34
+ if (child.type === 'folder')
35
+ results.push(...getFolderUrls(child));
21
36
  }
22
- pageTree.children.forEach(traverse);
23
- return options;
37
+ return results;
24
38
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-ui",
3
- "version": "14.5.5",
3
+ "version": "14.6.0",
4
4
  "description": "The framework for building a documentation website in Next.js",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -63,23 +63,23 @@
63
63
  "@radix-ui/react-tabs": "^1.1.1",
64
64
  "class-variance-authority": "^0.7.1",
65
65
  "lodash.merge": "^4.6.2",
66
- "lucide-react": "^0.462.0",
67
- "next-themes": "^0.4.3",
66
+ "lucide-react": "^0.468.0",
67
+ "next-themes": "^0.4.4",
68
68
  "postcss-selector-parser": "^7.0.0",
69
- "react-medium-image-zoom": "^5.2.11",
69
+ "react-medium-image-zoom": "^5.2.12",
70
70
  "tailwind-merge": "^2.5.5",
71
- "fumadocs-core": "14.5.5"
71
+ "fumadocs-core": "14.6.0"
72
72
  },
73
73
  "devDependencies": {
74
- "@next/eslint-plugin-next": "^15.0.3",
74
+ "@next/eslint-plugin-next": "^15.0.4",
75
75
  "@types/lodash.merge": "^4.6.9",
76
- "@types/react": "^18.3.12",
77
- "@types/react-dom": "^18.3.1",
76
+ "@types/react": "^19.0.1",
77
+ "@types/react-dom": "^19.0.2",
78
78
  "autoprefixer": "^10.4.20",
79
- "next": "15.0.3",
79
+ "next": "15.0.4",
80
80
  "postcss": "^8.4.49",
81
81
  "postcss-cli": "^11.0.0",
82
- "tailwindcss": "^3.4.15",
82
+ "tailwindcss": "^3.4.16",
83
83
  "tsc-alias": "^1.8.10",
84
84
  "@fumadocs/cli": "0.0.4",
85
85
  "eslint-config-custom": "0.0.0",
@@ -87,8 +87,8 @@
87
87
  },
88
88
  "peerDependencies": {
89
89
  "next": "14.x.x || 15.x.x",
90
- "react": ">= 18",
91
- "react-dom": ">= 18",
90
+ "react": "18.x.x || 19.x.x",
91
+ "react-dom": "18.x.x || 19.x.x",
92
92
  "tailwindcss": "^3.4.14"
93
93
  },
94
94
  "peerDependenciesMeta": {