fumadocs-ui 11.2.2 → 11.3.1

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.
@@ -3,6 +3,7 @@ import { createContext, useContext, useEffect, useMemo, useState } from "react";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
4
  var SearchContext = createContext({
5
5
  enabled: false,
6
+ hotKey: [],
6
7
  setOpenSearch: () => void 0
7
8
  });
8
9
  function useSearchContext() {
@@ -13,12 +14,24 @@ function SearchProvider({
13
14
  children,
14
15
  preload = true,
15
16
  options,
17
+ hotKey = [
18
+ {
19
+ key: "k",
20
+ display: "K"
21
+ },
22
+ {
23
+ key: (e) => e.metaKey || e.ctrlKey,
24
+ display: "\u2318"
25
+ }
26
+ ],
16
27
  links
17
28
  }) {
18
29
  const [isOpen, setIsOpen] = useState(preload ? false : void 0);
19
30
  useEffect(() => {
20
31
  const handler = (e) => {
21
- if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
32
+ if (hotKey.every(
33
+ (v) => typeof v.key === "string" ? e.key === v.key : v.key(e)
34
+ )) {
22
35
  setIsOpen(true);
23
36
  e.preventDefault();
24
37
  }
@@ -27,23 +40,28 @@ function SearchProvider({
27
40
  return () => {
28
41
  window.removeEventListener("keydown", handler);
29
42
  };
30
- }, []);
31
- const ctx = useMemo(
32
- () => ({ enabled: true, setOpenSearch: setIsOpen }),
33
- []
43
+ }, [hotKey]);
44
+ return /* @__PURE__ */ jsxs(
45
+ SearchContext.Provider,
46
+ {
47
+ value: useMemo(
48
+ () => ({ enabled: true, hotKey, setOpenSearch: setIsOpen }),
49
+ [hotKey]
50
+ ),
51
+ children: [
52
+ isOpen !== void 0 && /* @__PURE__ */ jsx(
53
+ SearchDialog,
54
+ {
55
+ open: isOpen,
56
+ onOpenChange: setIsOpen,
57
+ links,
58
+ ...options
59
+ }
60
+ ),
61
+ children
62
+ ]
63
+ }
34
64
  );
35
- return /* @__PURE__ */ jsxs(SearchContext.Provider, { value: ctx, children: [
36
- isOpen !== void 0 && /* @__PURE__ */ jsx(
37
- SearchDialog,
38
- {
39
- open: isOpen,
40
- onOpenChange: setIsOpen,
41
- links,
42
- ...options
43
- }
44
- ),
45
- children
46
- ] });
47
65
  }
48
66
 
49
67
  export {
@@ -0,0 +1,39 @@
1
+ // src/contexts/sidebar.tsx
2
+ import { createContext, useContext, useState, useMemo } from "react";
3
+ import { SidebarProvider as BaseProvider } from "fumadocs-core/sidebar";
4
+ import { jsx } from "react/jsx-runtime";
5
+ var SidebarContext = createContext(
6
+ void 0
7
+ );
8
+ function useSidebar() {
9
+ const ctx = useContext(SidebarContext);
10
+ if (!ctx)
11
+ throw new Error("Missing root provider");
12
+ return ctx;
13
+ }
14
+ function SidebarProvider({
15
+ children
16
+ }) {
17
+ const [open, setOpen] = useState(false);
18
+ const [collapsed, setCollapsed] = useState(false);
19
+ return /* @__PURE__ */ jsx(
20
+ SidebarContext.Provider,
21
+ {
22
+ value: useMemo(
23
+ () => ({
24
+ open,
25
+ setOpen,
26
+ collapsed,
27
+ setCollapsed
28
+ }),
29
+ [open, collapsed]
30
+ ),
31
+ children: /* @__PURE__ */ jsx(BaseProvider, { open, onOpenChange: setOpen, children })
32
+ }
33
+ );
34
+ }
35
+
36
+ export {
37
+ useSidebar,
38
+ SidebarProvider
39
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useSearchContext
3
- } from "./chunk-2KA2UOM3.js";
3
+ } from "./chunk-FSPYEOFC.js";
4
4
  import {
5
5
  useI18n
6
6
  } from "./chunk-PW7TBOIJ.js";
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { HTMLAttributes, ReactNode } from 'react';
3
3
 
4
- declare const Callout: React.ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "title" | "icon" | "type"> & {
4
+ declare const Callout: React.ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "title" | "type" | "icon"> & {
5
5
  title?: ReactNode;
6
6
  /**
7
7
  * @defaultValue info
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import {
3
3
  SearchDialog
4
- } from "../../chunk-HUXFUKD2.js";
5
- import "../../chunk-2KA2UOM3.js";
4
+ } from "../../chunk-PXDQVGII.js";
5
+ import "../../chunk-FSPYEOFC.js";
6
6
  import "../../chunk-PW7TBOIJ.js";
7
7
  import "../../chunk-7GZKFBAP.js";
8
8
  import "../../chunk-TK3TM3MR.js";
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import {
3
3
  SearchDialog
4
- } from "../../chunk-HUXFUKD2.js";
5
- import "../../chunk-2KA2UOM3.js";
4
+ } from "../../chunk-PXDQVGII.js";
5
+ import "../../chunk-FSPYEOFC.js";
6
6
  import {
7
7
  useI18n
8
8
  } from "../../chunk-PW7TBOIJ.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SearchDialog
3
- } from "../../chunk-HUXFUKD2.js";
4
- import "../../chunk-2KA2UOM3.js";
3
+ } from "../../chunk-PXDQVGII.js";
4
+ import "../../chunk-FSPYEOFC.js";
5
5
  import "../../chunk-PW7TBOIJ.js";
6
6
  import "../../chunk-7GZKFBAP.js";
7
7
  import "../../chunk-TK3TM3MR.js";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
- useSidebarCollapse
4
- } from "./chunk-PLXR6QNH.js";
3
+ useSidebar
4
+ } from "./chunk-GDRBCN6Q.js";
5
5
  import {
6
6
  TreeContextProvider,
7
7
  useTreeContext
@@ -21,7 +21,7 @@ import {
21
21
  } from "./chunk-7F2LGCS6.js";
22
22
  import {
23
23
  useSearchContext
24
- } from "./chunk-2KA2UOM3.js";
24
+ } from "./chunk-FSPYEOFC.js";
25
25
  import {
26
26
  useI18n
27
27
  } from "./chunk-PW7TBOIJ.js";
@@ -42,7 +42,7 @@ import "./chunk-6C3VEZWH.js";
42
42
  import { MenuIcon, MoreVertical, SearchIcon, X } from "lucide-react";
43
43
  import Link2 from "fumadocs-core/link";
44
44
  import { SidebarTrigger } from "fumadocs-core/sidebar";
45
- import { useEffect, useState } from "react";
45
+ import { useCallback as useCallback2, useEffect, useState } from "react";
46
46
  import { usePathname as usePathname2 } from "next/navigation";
47
47
 
48
48
  // src/components/theme-toggle.tsx
@@ -296,7 +296,7 @@ function LinksMenu({ items, ...props }) {
296
296
  ),
297
297
  /* @__PURE__ */ jsxs3(PopoverContent, { className: "flex flex-col", children: [
298
298
  items.map((item, i) => /* @__PURE__ */ jsx3(LinkItem, { item, on: "menu" }, i)),
299
- /* @__PURE__ */ jsxs3("div", { className: "flex flex-row px-2 py-1 items-center justify-between", children: [
299
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-row items-center justify-between px-2 py-1", children: [
300
300
  /* @__PURE__ */ jsx3("p", { className: "font-medium", children: "Theme" }),
301
301
  /* @__PURE__ */ jsx3(ThemeToggle, {})
302
302
  ] })
@@ -304,11 +304,11 @@ function LinksMenu({ items, ...props }) {
304
304
  ] });
305
305
  }
306
306
  function SearchToggle() {
307
- const { setOpenSearch } = useSearchContext();
307
+ const { hotKey, setOpenSearch } = useSearchContext();
308
308
  const { text } = useI18n();
309
- const onClick = () => {
309
+ const onClick = useCallback2(() => {
310
310
  setOpenSearch(true);
311
- };
311
+ }, [setOpenSearch]);
312
312
  return /* @__PURE__ */ jsxs3(Fragment, { children: [
313
313
  /* @__PURE__ */ jsx3(
314
314
  "button",
@@ -335,7 +335,7 @@ function SearchToggle() {
335
335
  children: [
336
336
  /* @__PURE__ */ jsx3(SearchIcon, { className: "ms-1 size-4" }),
337
337
  text.search,
338
- /* @__PURE__ */ jsx3("div", { className: "ms-auto inline-flex gap-0.5 text-xs", children: ["\u2318", "K"].map((k) => /* @__PURE__ */ jsx3("kbd", { className: "rounded-md border bg-background px-1.5", children: k }, k)) })
338
+ /* @__PURE__ */ jsx3("div", { className: "ms-auto inline-flex gap-0.5 text-xs", children: hotKey.map((k, i) => /* @__PURE__ */ jsx3("kbd", { className: "rounded-md border bg-background px-1.5", children: k.display }, i)) })
339
339
  ]
340
340
  }
341
341
  )
@@ -349,7 +349,7 @@ import * as Base from "fumadocs-core/sidebar";
349
349
  import { usePathname as usePathname3 } from "next/navigation";
350
350
  import {
351
351
  createContext,
352
- useCallback as useCallback2,
352
+ useCallback as useCallback3,
353
353
  useContext,
354
354
  useEffect as useEffect2,
355
355
  useMemo,
@@ -396,7 +396,7 @@ function Sidebar({
396
396
  return /* @__PURE__ */ jsx4(SidebarContext.Provider, { value: context, children: /* @__PURE__ */ jsxs4(
397
397
  Base.SidebarList,
398
398
  {
399
- minWidth: 768,
399
+ blockScrollingWidth: 768,
400
400
  className: twMerge(
401
401
  "flex w-full flex-col text-[15px] md:sticky md:top-16 md:h-body md:w-[240px] md:text-sm xl:w-[260px]",
402
402
  "max-md:fixed max-md:inset-0 max-md:z-40 max-md:pt-16 max-md:data-[open=false]:hidden",
@@ -494,7 +494,7 @@ function FolderNode({
494
494
  if (shouldExtend)
495
495
  setExtend(true);
496
496
  }, [shouldExtend]);
497
- const onClick = useCallback2(
497
+ const onClick = useCallback3(
498
498
  (e) => {
499
499
  if (e.target !== e.currentTarget || active) {
500
500
  setExtend((prev) => !prev);
@@ -554,23 +554,23 @@ function SeparatorNode({
554
554
  }
555
555
 
556
556
  // src/components/dynamic-sidebar.tsx
557
- import { useCallback as useCallback3, useRef, useState as useState3 } from "react";
557
+ import { useCallback as useCallback4, useRef, useState as useState3 } from "react";
558
558
  import { SidebarIcon } from "lucide-react";
559
559
  import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
560
560
  function DynamicSidebar(props) {
561
- const [open, setOpen] = useSidebarCollapse();
561
+ const { collapsed, setCollapsed } = useSidebar();
562
562
  const [hover, setHover] = useState3(false);
563
563
  const timerRef = useRef(0);
564
- const onCollapse = useCallback3(() => {
565
- setOpen(!open);
566
- }, [open, setOpen]);
567
- const onHover = useCallback3((e) => {
564
+ const onCollapse = useCallback4(() => {
565
+ setCollapsed(!collapsed);
566
+ }, [collapsed, setCollapsed]);
567
+ const onHover = useCallback4((e) => {
568
568
  if (e.pointerType === "touch")
569
569
  return;
570
570
  window.clearTimeout(timerRef.current);
571
571
  setHover(true);
572
572
  }, []);
573
- const onLeave = useCallback3((e) => {
573
+ const onLeave = useCallback4((e) => {
574
574
  if (e.pointerType === "touch")
575
575
  return;
576
576
  window.clearTimeout(timerRef.current);
@@ -579,7 +579,7 @@ function DynamicSidebar(props) {
579
579
  }, 300);
580
580
  }, []);
581
581
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
582
- !open ? /* @__PURE__ */ jsx5(
582
+ collapsed ? /* @__PURE__ */ jsx5(
583
583
  "div",
584
584
  {
585
585
  className: "fixed bottom-0 start-0 top-16 max-md:hidden",
@@ -592,11 +592,11 @@ function DynamicSidebar(props) {
592
592
  }
593
593
  }
594
594
  ) : null,
595
- !open ? /* @__PURE__ */ jsx5(
595
+ collapsed ? /* @__PURE__ */ jsx5(
596
596
  "button",
597
597
  {
598
598
  type: "button",
599
- "aria-label": "Trigger Sidebar",
599
+ "aria-label": "Collapse Sidebar",
600
600
  className: twMerge(
601
601
  buttonVariants({
602
602
  color: "secondary",
@@ -612,27 +612,27 @@ function DynamicSidebar(props) {
612
612
  "div",
613
613
  {
614
614
  id: "dynamic-sidebar",
615
- "data-open": open,
615
+ "data-open": !collapsed,
616
616
  "data-hover": hover,
617
617
  onPointerEnter: onHover,
618
618
  onPointerLeave: onLeave,
619
- "aria-hidden": !open && !hover,
619
+ "aria-hidden": Boolean(collapsed && !hover),
620
620
  className: twMerge(
621
621
  "z-40 transition-transform max-md:absolute",
622
- !open && "md:fixed md:bottom-2 md:start-2 md:top-16 md:overflow-hidden md:rounded-xl md:border md:bg-background md:shadow-md"
622
+ collapsed && "md:fixed md:bottom-2 md:start-2 md:top-16 md:overflow-hidden md:rounded-xl md:border md:bg-background md:shadow-md"
623
623
  ),
624
624
  children: /* @__PURE__ */ jsx5(
625
625
  Sidebar,
626
626
  {
627
627
  ...props,
628
- className: twMerge(!open && "md:h-full"),
628
+ className: twMerge(collapsed && "md:h-full"),
629
629
  footer: /* @__PURE__ */ jsxs5(Fragment2, { children: [
630
630
  props.footer,
631
631
  /* @__PURE__ */ jsx5(
632
632
  "button",
633
633
  {
634
634
  type: "button",
635
- "aria-label": "Trigger Sidebar",
635
+ "aria-label": "Collapse Sidebar",
636
636
  className: twMerge(
637
637
  buttonVariants({
638
638
  color: "ghost",
package/dist/page.d.ts CHANGED
@@ -45,5 +45,11 @@ interface TOCProps {
45
45
  * Add typography styles
46
46
  */
47
47
  declare const DocsBody: React$1.ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & React$1.RefAttributes<HTMLDivElement>>;
48
+ /**
49
+ * For separate MDX page
50
+ */
51
+ declare function withArticle({ children, }: {
52
+ children: React.ReactNode;
53
+ }): React.ReactElement;
48
54
 
49
- export { DocsBody, DocsPage, type DocsPageProps };
55
+ export { DocsBody, DocsPage, type DocsPageProps, withArticle };
package/dist/page.js CHANGED
@@ -50,7 +50,13 @@ function Toc(props) {
50
50
  }
51
51
  var DocsBody = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: twMerge("prose", className), ...props }));
52
52
  DocsBody.displayName = "DocsBody";
53
+ function withArticle({
54
+ children
55
+ }) {
56
+ return /* @__PURE__ */ jsx("main", { className: "container py-12", children: /* @__PURE__ */ jsx("article", { className: "prose", children }) });
57
+ }
53
58
  export {
54
59
  DocsBody,
55
- DocsPage
60
+ DocsPage,
61
+ withArticle
56
62
  };
@@ -1,13 +1,19 @@
1
- import * as React$1 from 'react';
2
1
  import { ReactNode } from 'react';
2
+ import { ThemeProviderProps } from 'next-themes/dist/types';
3
3
  import { DefaultSearchDialogProps } from './components/dialog/search-default.js';
4
4
  import { SearchLink, SharedProps } from './components/dialog/search.js';
5
- import { ThemeProviderProps } from 'next-themes/dist/types';
6
5
  export { u as useI18n } from './i18n-p5QWhj_3.js';
7
6
  export { u as useTreeContext } from './tree-cqNEopxM.js';
8
7
  import 'fumadocs-core/search/shared';
9
8
  import 'fumadocs-core/server';
10
9
 
10
+ interface HotKey {
11
+ display: React.ReactNode;
12
+ /**
13
+ * Key code or a function determining whether the key is pressed.
14
+ */
15
+ key: string | ((e: KeyboardEvent) => boolean);
16
+ }
11
17
  interface SearchProviderProps {
12
18
  /**
13
19
  * Preload search dialog before opening it
@@ -19,27 +25,38 @@ interface SearchProviderProps {
19
25
  * Custom links to be displayed if search is empty
20
26
  */
21
27
  links?: SearchLink[];
28
+ /**
29
+ * Hotkeys for triggering search dialog
30
+ *
31
+ * @defaultValue K + Meta/Ctrl
32
+ */
33
+ hotKey?: HotKey[];
22
34
  /**
23
35
  * Replace default search dialog, allowing you to use other solutions such as Algolia Search
24
36
  *
25
37
  * It receives the `open` and `onOpenChange` prop, can be lazy loaded with `next/dynamic`
26
38
  */
27
- SearchDialog: React$1.ComponentType<SharedProps>;
39
+ SearchDialog: React.ComponentType<SharedProps>;
28
40
  /**
29
41
  * Additional props to the dialog
30
42
  */
31
43
  options?: Partial<SharedProps>;
32
- children?: React$1.ReactNode;
44
+ children?: React.ReactNode;
33
45
  }
34
46
  interface SearchContextType {
35
47
  enabled: boolean;
48
+ hotKey: HotKey[];
36
49
  setOpenSearch: (value: boolean) => void;
37
50
  }
38
51
  declare function useSearchContext(): SearchContextType;
39
52
 
40
- type SidebarCollapseContext = [open: boolean, setOpen: (v: boolean) => void];
41
- declare const SidebarCollapseContext: React$1.Context<SidebarCollapseContext | undefined>;
42
- declare function useSidebarCollapse(): SidebarCollapseContext;
53
+ interface SidebarCollapseContext {
54
+ open: boolean;
55
+ setOpen: (v: boolean) => void;
56
+ collapsed: boolean;
57
+ setCollapsed: (v: boolean) => void;
58
+ }
59
+ declare function useSidebar(): SidebarCollapseContext;
43
60
 
44
61
  interface SearchOptions extends Omit<SearchProviderProps, 'options' | 'children'> {
45
62
  options?: Partial<DefaultSearchDialogProps> | SearchProviderProps['options'];
@@ -81,4 +98,4 @@ interface RootProviderProps {
81
98
  }
82
99
  declare function RootProvider({ children, dir, enableThemeProvider, theme: { enabled, ...theme }, search, }: RootProviderProps): React.ReactElement;
83
100
 
84
- export { RootProvider, type RootProviderProps, useSearchContext, useSidebarCollapse };
101
+ export { RootProvider, type RootProviderProps, useSearchContext, useSidebar };
package/dist/provider.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import {
3
- SidebarCollapseProvider,
4
- useSidebarCollapse
5
- } from "./chunk-PLXR6QNH.js";
3
+ SidebarProvider,
4
+ useSidebar
5
+ } from "./chunk-GDRBCN6Q.js";
6
6
  import {
7
7
  useTreeContext
8
8
  } from "./chunk-C4PI62MH.js";
@@ -10,14 +10,13 @@ import "./chunk-7F2LGCS6.js";
10
10
  import {
11
11
  SearchProvider,
12
12
  useSearchContext
13
- } from "./chunk-2KA2UOM3.js";
13
+ } from "./chunk-FSPYEOFC.js";
14
14
  import {
15
15
  useI18n
16
16
  } from "./chunk-PW7TBOIJ.js";
17
17
  import "./chunk-6C3VEZWH.js";
18
18
 
19
19
  // src/provider.tsx
20
- import { SidebarProvider } from "fumadocs-core/sidebar";
21
20
  import { ThemeProvider } from "next-themes";
22
21
  import dynamic from "next/dynamic";
23
22
  import { DirectionProvider } from "@radix-ui/react-direction";
@@ -33,7 +32,7 @@ function RootProvider({
33
32
  theme: { enabled = true, ...theme } = {},
34
33
  search
35
34
  }) {
36
- let body = /* @__PURE__ */ jsx(SidebarProvider, { children: /* @__PURE__ */ jsx(SidebarCollapseProvider, { children }) });
35
+ let body = /* @__PURE__ */ jsx(DirectionProvider, { dir: dir ?? "ltr", children: /* @__PURE__ */ jsx(SidebarProvider, { children }) });
37
36
  if (search?.enabled !== false)
38
37
  body = /* @__PURE__ */ jsx(SearchProvider, { SearchDialog: DefaultSearchDialog, ...search, children: body });
39
38
  if (enabled && enableThemeProvider)
@@ -48,14 +47,12 @@ function RootProvider({
48
47
  children: body
49
48
  }
50
49
  );
51
- if (dir)
52
- body = /* @__PURE__ */ jsx(DirectionProvider, { dir, children: body });
53
50
  return body;
54
51
  }
55
52
  export {
56
53
  RootProvider,
57
54
  useI18n,
58
55
  useSearchContext,
59
- useSidebarCollapse,
56
+ useSidebar,
60
57
  useTreeContext
61
58
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-ui",
3
- "version": "11.2.2",
3
+ "version": "11.3.1",
4
4
  "description": "The framework for building a documentation website in Next.js",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -96,7 +96,7 @@
96
96
  "next-themes": "^0.3.0",
97
97
  "react-medium-image-zoom": "^5.2.4",
98
98
  "tailwind-merge": "^2.3.0",
99
- "fumadocs-core": "11.2.2"
99
+ "fumadocs-core": "11.3.1"
100
100
  },
101
101
  "devDependencies": {
102
102
  "@algolia/client-search": "^4.23.3",
@@ -1,21 +0,0 @@
1
- // src/contexts/sidebar.tsx
2
- import { createContext, useContext, useState } from "react";
3
- import { jsx } from "react/jsx-runtime";
4
- var SidebarCollapseContext = createContext(void 0);
5
- function useSidebarCollapse() {
6
- const ctx = useContext(SidebarCollapseContext);
7
- if (!ctx)
8
- throw new Error("Missing root provider");
9
- return ctx;
10
- }
11
- function SidebarCollapseProvider({
12
- children
13
- }) {
14
- const [open, setOpen] = useState(true);
15
- return /* @__PURE__ */ jsx(SidebarCollapseContext.Provider, { value: [open, setOpen], children });
16
- }
17
-
18
- export {
19
- useSidebarCollapse,
20
- SidebarCollapseProvider
21
- };