fumadocs-ui 15.2.8 → 16.0.3

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.
Files changed (202) hide show
  1. package/README.md +1 -1
  2. package/css/black.css +4 -2
  3. package/css/catppuccin.css +2 -0
  4. package/css/default.css +33 -0
  5. package/css/dusk.css +2 -0
  6. package/css/neutral.css +11 -8
  7. package/css/ocean.css +5 -3
  8. package/css/preset.css +237 -16
  9. package/css/purple.css +12 -10
  10. package/css/shadcn.css +31 -0
  11. package/css/shiki.css +78 -51
  12. package/css/solar.css +48 -0
  13. package/css/vitepress.css +3 -4
  14. package/dist/components/accordion.d.ts +6 -3
  15. package/dist/components/accordion.d.ts.map +1 -1
  16. package/dist/components/accordion.js +19 -11
  17. package/dist/components/banner.d.ts +8 -2
  18. package/dist/components/banner.d.ts.map +1 -1
  19. package/dist/components/banner.js +25 -30
  20. package/dist/components/callout.d.ts +2 -2
  21. package/dist/components/callout.d.ts.map +1 -1
  22. package/dist/components/callout.js +15 -18
  23. package/dist/components/card.d.ts.map +1 -1
  24. package/dist/components/card.js +2 -2
  25. package/dist/components/codeblock.d.ts +19 -23
  26. package/dist/components/codeblock.d.ts.map +1 -1
  27. package/dist/components/codeblock.js +55 -24
  28. package/dist/components/dialog/search-algolia.d.ts +5 -4
  29. package/dist/components/dialog/search-algolia.d.ts.map +1 -1
  30. package/dist/components/dialog/search-algolia.js +21 -7
  31. package/dist/components/dialog/search-default.d.ts +4 -2
  32. package/dist/components/dialog/search-default.d.ts.map +1 -1
  33. package/dist/components/dialog/search-default.js +22 -6
  34. package/dist/components/dialog/search-orama.d.ts +5 -3
  35. package/dist/components/dialog/search-orama.d.ts.map +1 -1
  36. package/dist/components/dialog/search-orama.js +21 -6
  37. package/dist/components/dialog/search.d.ts +61 -27
  38. package/dist/components/dialog/search.d.ts.map +1 -1
  39. package/dist/components/dialog/search.js +155 -67
  40. package/dist/components/dynamic-codeblock.d.ts +16 -2
  41. package/dist/components/dynamic-codeblock.d.ts.map +1 -1
  42. package/dist/components/dynamic-codeblock.js +22 -10
  43. package/dist/components/files.d.ts.map +1 -1
  44. package/dist/components/files.js +1 -1
  45. package/dist/components/github-info.js +1 -1
  46. package/dist/components/heading.js +1 -1
  47. package/dist/components/image-zoom.css +4 -0
  48. package/dist/components/inline-toc.d.ts +2 -2
  49. package/dist/components/inline-toc.d.ts.map +1 -1
  50. package/dist/components/inline-toc.js +1 -1
  51. package/dist/components/layout/language-toggle.js +1 -1
  52. package/dist/components/layout/root-toggle.d.ts +5 -15
  53. package/dist/components/layout/root-toggle.d.ts.map +1 -1
  54. package/dist/components/layout/root-toggle.js +10 -12
  55. package/dist/components/layout/search-toggle.d.ts +6 -4
  56. package/dist/components/layout/search-toggle.d.ts.map +1 -1
  57. package/dist/components/layout/search-toggle.js +5 -5
  58. package/dist/components/layout/sidebar.d.ts +21 -13
  59. package/dist/components/layout/sidebar.d.ts.map +1 -1
  60. package/dist/components/layout/sidebar.js +67 -65
  61. package/dist/components/layout/theme-toggle.js +1 -1
  62. package/dist/components/layout/toc-clerk.d.ts +2 -4
  63. package/dist/components/layout/toc-clerk.d.ts.map +1 -1
  64. package/dist/components/layout/toc-clerk.js +9 -5
  65. package/dist/components/layout/toc-thumb.js +1 -1
  66. package/dist/components/layout/toc.d.ts +6 -22
  67. package/dist/components/layout/toc.d.ts.map +1 -1
  68. package/dist/components/layout/toc.js +14 -18
  69. package/dist/components/tabs.d.ts +18 -18
  70. package/dist/components/tabs.d.ts.map +1 -1
  71. package/dist/components/tabs.js +44 -105
  72. package/dist/components/tabs.unstyled.d.ts +24 -0
  73. package/dist/components/tabs.unstyled.d.ts.map +1 -0
  74. package/dist/components/tabs.unstyled.js +89 -0
  75. package/dist/components/type-table.d.ts +19 -10
  76. package/dist/components/type-table.d.ts.map +1 -1
  77. package/dist/components/type-table.js +19 -10
  78. package/dist/components/ui/button.d.ts +2 -1
  79. package/dist/components/ui/button.d.ts.map +1 -1
  80. package/dist/components/ui/button.js +12 -8
  81. package/dist/components/ui/navigation-menu.js +2 -2
  82. package/dist/components/ui/popover.js +1 -1
  83. package/dist/components/ui/scroll-area.d.ts.map +1 -1
  84. package/dist/components/ui/scroll-area.js +1 -1
  85. package/dist/contexts/search.d.ts +10 -2
  86. package/dist/contexts/search.d.ts.map +1 -1
  87. package/dist/contexts/search.js +7 -1
  88. package/dist/contexts/sidebar.d.ts.map +1 -1
  89. package/dist/contexts/sidebar.js +2 -3
  90. package/dist/contexts/tree.d.ts +2 -1
  91. package/dist/contexts/tree.d.ts.map +1 -1
  92. package/dist/contexts/tree.js +9 -2
  93. package/dist/i18n.d.ts +13 -11
  94. package/dist/i18n.d.ts.map +1 -1
  95. package/dist/i18n.js +14 -37
  96. package/dist/icons.d.ts +49 -0
  97. package/dist/icons.d.ts.map +1 -0
  98. package/dist/icons.js +282 -0
  99. package/dist/layouts/docs/client.d.ts +9 -0
  100. package/dist/layouts/docs/client.d.ts.map +1 -0
  101. package/dist/layouts/docs/client.js +46 -0
  102. package/dist/layouts/docs/index.d.ts +36 -0
  103. package/dist/layouts/docs/index.d.ts.map +1 -0
  104. package/dist/layouts/docs/index.js +77 -0
  105. package/dist/layouts/docs/page-client.d.ts +25 -0
  106. package/dist/layouts/docs/page-client.d.ts.map +1 -0
  107. package/dist/layouts/docs/page-client.js +157 -0
  108. package/dist/layouts/docs/page.d.ts +17 -0
  109. package/dist/layouts/docs/page.d.ts.map +1 -0
  110. package/dist/layouts/docs/page.js +26 -0
  111. package/dist/layouts/{home.d.ts → home/index.d.ts} +2 -3
  112. package/dist/layouts/home/index.d.ts.map +1 -0
  113. package/dist/layouts/home/index.js +56 -0
  114. package/dist/layouts/home/menu.d.ts +1 -1
  115. package/dist/layouts/home/menu.d.ts.map +1 -1
  116. package/dist/layouts/home/menu.js +3 -6
  117. package/dist/layouts/home/navbar.d.ts +4 -4
  118. package/dist/layouts/home/navbar.d.ts.map +1 -1
  119. package/dist/layouts/home/navbar.js +3 -4
  120. package/dist/layouts/notebook/client.d.ts +11 -0
  121. package/dist/layouts/notebook/client.d.ts.map +1 -0
  122. package/dist/layouts/notebook/client.js +45 -0
  123. package/dist/layouts/notebook/index.d.ts +34 -0
  124. package/dist/layouts/notebook/index.d.ts.map +1 -0
  125. package/dist/layouts/notebook/index.js +112 -0
  126. package/dist/layouts/shared/client.d.ts +6 -0
  127. package/dist/layouts/shared/client.d.ts.map +1 -0
  128. package/dist/layouts/{links.js → shared/client.js} +4 -6
  129. package/dist/layouts/shared/index.d.ts +122 -0
  130. package/dist/layouts/shared/index.d.ts.map +1 -0
  131. package/dist/layouts/{shared.js → shared/index.js} +1 -15
  132. package/dist/mdx.d.ts +7 -2
  133. package/dist/mdx.d.ts.map +1 -1
  134. package/dist/mdx.js +5 -1
  135. package/dist/mdx.server.d.ts.map +1 -1
  136. package/dist/mdx.server.js +10 -3
  137. package/dist/page.d.ts +33 -35
  138. package/dist/page.d.ts.map +1 -1
  139. package/dist/page.js +28 -32
  140. package/dist/provider/base.d.ts +1 -1
  141. package/dist/provider/base.d.ts.map +1 -1
  142. package/dist/provider/base.js +9 -9
  143. package/dist/provider/index.d.ts +5 -3
  144. package/dist/provider/index.d.ts.map +1 -1
  145. package/dist/provider/index.js +6 -6
  146. package/dist/provider/next.d.ts +4 -0
  147. package/dist/provider/next.d.ts.map +1 -0
  148. package/dist/provider/next.js +7 -0
  149. package/dist/provider/react-router.d.ts +4 -0
  150. package/dist/provider/react-router.d.ts.map +1 -0
  151. package/dist/provider/react-router.js +7 -0
  152. package/dist/provider/tanstack.d.ts +4 -0
  153. package/dist/provider/tanstack.d.ts.map +1 -0
  154. package/dist/provider/tanstack.js +7 -0
  155. package/dist/provider/waku.d.ts +4 -0
  156. package/dist/provider/waku.d.ts.map +1 -0
  157. package/dist/provider/waku.js +7 -0
  158. package/dist/style.css +944 -470
  159. package/dist/utils/get-sidebar-tabs.d.ts +18 -4
  160. package/dist/utils/get-sidebar-tabs.d.ts.map +1 -1
  161. package/dist/utils/get-sidebar-tabs.js +11 -8
  162. package/dist/utils/is-active.d.ts +2 -0
  163. package/dist/utils/is-active.d.ts.map +1 -1
  164. package/dist/utils/is-active.js +12 -4
  165. package/dist/utils/merge-refs.d.ts +3 -0
  166. package/dist/utils/merge-refs.d.ts.map +1 -0
  167. package/dist/utils/merge-refs.js +12 -0
  168. package/dist/utils/use-copy-button.d.ts +1 -1
  169. package/dist/utils/use-copy-button.d.ts.map +1 -1
  170. package/dist/utils/use-copy-button.js +9 -7
  171. package/package.json +76 -32
  172. package/css/animations.css +0 -199
  173. package/dist/components/ui/tabs.d.ts +0 -8
  174. package/dist/components/ui/tabs.d.ts.map +0 -1
  175. package/dist/components/ui/tabs.js +0 -16
  176. package/dist/layouts/docs/shared.d.ts +0 -25
  177. package/dist/layouts/docs/shared.d.ts.map +0 -1
  178. package/dist/layouts/docs/shared.js +0 -24
  179. package/dist/layouts/docs-client.d.ts +0 -5
  180. package/dist/layouts/docs-client.d.ts.map +0 -1
  181. package/dist/layouts/docs-client.js +0 -33
  182. package/dist/layouts/docs.d.ts +0 -30
  183. package/dist/layouts/docs.d.ts.map +0 -1
  184. package/dist/layouts/docs.js +0 -50
  185. package/dist/layouts/home.d.ts.map +0 -1
  186. package/dist/layouts/home.js +0 -44
  187. package/dist/layouts/links.d.ts +0 -79
  188. package/dist/layouts/links.d.ts.map +0 -1
  189. package/dist/layouts/notebook-client.d.ts +0 -12
  190. package/dist/layouts/notebook-client.d.ts.map +0 -1
  191. package/dist/layouts/notebook-client.js +0 -47
  192. package/dist/layouts/notebook.d.ts +0 -17
  193. package/dist/layouts/notebook.d.ts.map +0 -1
  194. package/dist/layouts/notebook.js +0 -68
  195. package/dist/layouts/shared.d.ts +0 -65
  196. package/dist/layouts/shared.d.ts.map +0 -1
  197. package/dist/page-client.d.ts +0 -28
  198. package/dist/page-client.d.ts.map +0 -1
  199. package/dist/page-client.js +0 -150
  200. package/dist/page.server.d.ts +0 -13
  201. package/dist/page.server.d.ts.map +0 -1
  202. package/dist/page.server.js +0 -36
@@ -1 +1 @@
1
- {"version":3,"file":"search-default.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/search-default.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAGjD,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,OAAO,EAEb,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,wBAAyB,SAAQ,WAAW;IAC3D;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAE1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IAEjB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,EAC1C,UAAU,EACV,IAAI,EACJ,GAAG,EACH,OAAO,EACP,IAAc,EACd,UAAkB,EAClB,GAAG,KAAK,EACT,EAAE,wBAAwB,GAAG,SAAS,CA8CtC"}
1
+ {"version":3,"file":"search-default.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/search-default.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAC;AAG1D,OAAO,EAUL,KAAK,WAAW,EAGjB,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE7D,MAAM,WAAW,wBAAyB,SAAQ,WAAW;IAC3D,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IAErB;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAE1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IAEjB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,EAC1C,UAAU,EACV,IAAS,EACT,GAAG,EACH,OAAO,EACP,IAAc,EACd,UAAkB,EAClB,KAAU,EACV,MAAM,EACN,GAAG,KAAK,EACT,EAAE,wBAAwB,2CAkE1B"}
@@ -1,24 +1,40 @@
1
1
  'use client';
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useDocsSearch } from 'fumadocs-core/search/client';
4
- import { useState } from 'react';
4
+ import { useMemo, useState } from 'react';
5
5
  import { useOnChange } from 'fumadocs-core/utils/use-on-change';
6
6
  import { useI18n } from '../../contexts/i18n.js';
7
- import { SearchDialog, TagsList, } from './search.js';
8
- export default function DefaultSearchDialog({ defaultTag, tags, api, delayMs, type = 'fetch', allowClear = false, ...props }) {
7
+ import { SearchDialog, SearchDialogClose, SearchDialogContent, SearchDialogFooter, SearchDialogHeader, SearchDialogIcon, SearchDialogInput, SearchDialogList, SearchDialogOverlay, TagsList, TagsListItem, } from './search.js';
8
+ export default function DefaultSearchDialog({ defaultTag, tags = [], api, delayMs, type = 'fetch', allowClear = false, links = [], footer, ...props }) {
9
9
  const { locale } = useI18n();
10
10
  const [tag, setTag] = useState(defaultTag);
11
11
  const { search, setSearch, query } = useDocsSearch(type === 'fetch'
12
12
  ? {
13
13
  type: 'fetch',
14
14
  api,
15
+ locale,
16
+ tag,
17
+ delayMs,
15
18
  }
16
19
  : {
17
20
  type: 'static',
18
21
  from: api,
19
- }, locale, tag, delayMs);
22
+ locale,
23
+ tag,
24
+ delayMs,
25
+ });
26
+ const defaultItems = useMemo(() => {
27
+ if (links.length === 0)
28
+ return null;
29
+ return links.map(([name, link]) => ({
30
+ type: 'page',
31
+ id: name,
32
+ content: name,
33
+ url: link,
34
+ }));
35
+ }, [links]);
20
36
  useOnChange(defaultTag, (v) => {
21
37
  setTag(v);
22
38
  });
23
- return (_jsx(SearchDialog, { search: search, onSearchChange: setSearch, isLoading: query.isLoading, results: query.data ?? [], ...props, footer: tags ? (_jsxs(_Fragment, { children: [_jsx(TagsList, { tag: tag, onTagChange: setTag, items: tags, allowClear: allowClear }), props.footer] })) : (props.footer) }));
39
+ return (_jsxs(SearchDialog, { search: search, onSearchChange: setSearch, isLoading: query.isLoading, ...props, children: [_jsx(SearchDialogOverlay, {}), _jsxs(SearchDialogContent, { children: [_jsxs(SearchDialogHeader, { children: [_jsx(SearchDialogIcon, {}), _jsx(SearchDialogInput, {}), _jsx(SearchDialogClose, {})] }), _jsx(SearchDialogList, { items: query.data !== 'empty' ? query.data : defaultItems })] }), _jsxs(SearchDialogFooter, { children: [tags.length > 0 && (_jsx(TagsList, { tag: tag, onTagChange: setTag, allowClear: allowClear, children: tags.map((tag) => (_jsx(TagsListItem, { value: tag.value, children: tag.name }, tag.value))) })), footer] })] }));
24
40
  }
@@ -1,7 +1,9 @@
1
1
  import { type OramaCloudOptions } from 'fumadocs-core/search/client';
2
2
  import { type ReactNode } from 'react';
3
- import { type SharedProps, type TagItem } from './search.js';
3
+ import { type SharedProps } from './search.js';
4
+ import type { SearchLink, TagItem } from '../../contexts/search.js';
4
5
  export interface OramaSearchDialogProps extends SharedProps {
6
+ links?: SearchLink[];
5
7
  client: OramaCloudOptions['client'];
6
8
  searchOptions?: OramaCloudOptions['params'];
7
9
  index?: OramaCloudOptions['index'];
@@ -11,7 +13,7 @@ export interface OramaSearchDialogProps extends SharedProps {
11
13
  /**
12
14
  * Add the "Powered by Orama" label
13
15
  *
14
- * @defaultValue false
16
+ * @defaultValue true
15
17
  */
16
18
  showOrama?: boolean;
17
19
  /**
@@ -24,5 +26,5 @@ export interface OramaSearchDialogProps extends SharedProps {
24
26
  /**
25
27
  * Orama Cloud integration
26
28
  */
27
- export default function OramaSearchDialog({ client, searchOptions, tags, defaultTag, showOrama, allowClear, index, ...props }: OramaSearchDialogProps): ReactNode;
29
+ export default function OramaSearchDialog({ client, searchOptions, tags, defaultTag, showOrama, allowClear, index, footer, links, ...props }: OramaSearchDialogProps): import("react/jsx-runtime").JSX.Element;
28
30
  //# sourceMappingURL=search-orama.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-orama.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/search-orama.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAEjD,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,OAAO,EAEb,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,sBAAuB,SAAQ,WAAW;IACzD,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACpC,aAAa,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC5C,KAAK,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAEnC,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IAEjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,EACxC,MAAM,EACN,aAAa,EACb,IAAI,EACJ,UAAU,EACV,SAAiB,EACjB,UAAkB,EAClB,KAAK,EACL,GAAG,KAAK,EACT,EAAE,sBAAsB,GAAG,SAAS,CA2CpC"}
1
+ {"version":3,"file":"search-orama.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/search-orama.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAC;AAE1D,OAAO,EAUL,KAAK,WAAW,EAGjB,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAG7D,MAAM,WAAW,sBAAuB,SAAQ,WAAW;IACzD,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACpC,aAAa,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC5C,KAAK,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAEnC,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IAEjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,EACxC,MAAM,EACN,aAAa,EACb,IAAS,EACT,UAAU,EACV,SAAgB,EAChB,UAAkB,EAClB,KAAK,EACL,MAAM,EACN,KAAU,EACV,GAAG,KAAK,EACT,EAAE,sBAAsB,2CAgExB"}
@@ -1,24 +1,39 @@
1
1
  'use client';
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useDocsSearch, } from 'fumadocs-core/search/client';
4
- import { useState } from 'react';
4
+ import { useMemo, useState } from 'react';
5
5
  import { useOnChange } from 'fumadocs-core/utils/use-on-change';
6
- import { SearchDialog, TagsList, } from './search.js';
6
+ import { SearchDialog, SearchDialogClose, SearchDialogContent, SearchDialogFooter, SearchDialogHeader, SearchDialogIcon, SearchDialogInput, SearchDialogList, SearchDialogOverlay, TagsList, TagsListItem, } from './search.js';
7
+ import { useI18n } from '../../contexts/i18n.js';
7
8
  /**
8
9
  * Orama Cloud integration
9
10
  */
10
- export default function OramaSearchDialog({ client, searchOptions, tags, defaultTag, showOrama = false, allowClear = false, index, ...props }) {
11
+ export default function OramaSearchDialog({ client, searchOptions, tags = [], defaultTag, showOrama = true, allowClear = false, index, footer, links = [], ...props }) {
12
+ const { locale } = useI18n();
11
13
  const [tag, setTag] = useState(defaultTag);
12
14
  const { search, setSearch, query } = useDocsSearch({
13
15
  type: 'orama-cloud',
14
16
  client,
15
17
  index,
16
18
  params: searchOptions,
17
- }, undefined, tag);
19
+ locale,
20
+ tag,
21
+ });
22
+ const defaultItems = useMemo(() => {
23
+ if (links.length === 0)
24
+ return null;
25
+ return links.map(([name, link]) => ({
26
+ type: 'page',
27
+ id: name,
28
+ content: name,
29
+ url: link,
30
+ }));
31
+ }, [links]);
18
32
  useOnChange(defaultTag, (v) => {
19
33
  setTag(v);
20
34
  });
21
- return (_jsx(SearchDialog, { search: search, onSearchChange: setSearch, results: query.data ?? [], isLoading: query.isLoading, ...props, footer: tags ? (_jsxs(_Fragment, { children: [_jsx(TagsList, { tag: tag, onTagChange: setTag, items: tags, allowClear: allowClear, children: showOrama ? _jsx(Label, {}) : null }), props.footer] })) : (props.footer) }));
35
+ const label = showOrama && _jsx(Label, {});
36
+ return (_jsxs(SearchDialog, { search: search, onSearchChange: setSearch, isLoading: query.isLoading, ...props, children: [_jsx(SearchDialogOverlay, {}), _jsxs(SearchDialogContent, { children: [_jsxs(SearchDialogHeader, { children: [_jsx(SearchDialogIcon, {}), _jsx(SearchDialogInput, {}), _jsx(SearchDialogClose, {})] }), _jsx(SearchDialogList, { items: query.data !== 'empty' ? query.data : defaultItems }), _jsxs(SearchDialogFooter, { children: [tags.length > 0 ? (_jsxs(TagsList, { tag: tag, onTagChange: setTag, allowClear: allowClear, children: [tags.map((tag) => (_jsx(TagsListItem, { value: tag.value, children: tag.name }, tag.value))), label] })) : (label), footer] })] })] }));
22
37
  }
23
38
  function Label() {
24
39
  return (_jsx("a", { href: "https://orama.com", rel: "noreferrer noopener", className: "ms-auto text-xs text-fd-muted-foreground", children: "Search powered by Orama" }));
@@ -1,38 +1,72 @@
1
- import { type ReactNode, type HTMLAttributes } from 'react';
2
- import type { SortedResult } from 'fumadocs-core/server';
3
- export type SearchLink = [name: string, href: string];
4
- type ReactSortedResult = Omit<SortedResult, 'content'> & {
1
+ import { type ComponentProps, type ReactNode } from 'react';
2
+ import { DialogContent, DialogOverlay } from '@radix-ui/react-dialog';
3
+ import type { HighlightedText, ReactSortedResult as BaseResultType } from 'fumadocs-core/search';
4
+ import type { SharedProps } from '../../contexts/search.js';
5
+ export type SearchItemType = (BaseResultType & {
5
6
  external?: boolean;
6
- content: ReactNode;
7
+ }) | {
8
+ id: string;
9
+ type: 'action';
10
+ node: ReactNode;
11
+ onSelect: () => void;
7
12
  };
8
- export interface SharedProps {
9
- open: boolean;
10
- onOpenChange: (open: boolean) => void;
11
- /**
12
- * Custom links to be displayed if search is empty
13
- */
14
- links?: SearchLink[];
15
- }
16
- interface SearchDialogProps extends SharedProps {
13
+ export type { SharedProps };
14
+ export interface SearchDialogProps extends SharedProps {
17
15
  search: string;
18
16
  onSearchChange: (v: string) => void;
19
17
  isLoading?: boolean;
20
- hideResults?: boolean;
21
- results: ReactSortedResult[] | 'empty';
22
- footer?: ReactNode;
18
+ children: ReactNode;
23
19
  }
24
- export declare function SearchDialog({ open, onOpenChange, footer, links, search, onSearchChange, isLoading, ...props }: SearchDialogProps): import("react/jsx-runtime").JSX.Element;
25
- export interface TagItem {
26
- name: string;
27
- value: string | undefined;
28
- props?: HTMLAttributes<HTMLButtonElement>;
29
- }
30
- export interface TagsListProps extends HTMLAttributes<HTMLDivElement> {
20
+ export declare function SearchDialog({ open, onOpenChange, search, onSearchChange, isLoading, children, }: SearchDialogProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function SearchDialogHeader(props: ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
22
+ export declare function SearchDialogInput(props: ComponentProps<'input'>): import("react/jsx-runtime").JSX.Element;
23
+ export declare function SearchDialogClose({ children, className, ...props }: ComponentProps<'button'>): import("react/jsx-runtime").JSX.Element;
24
+ export declare function SearchDialogFooter(props: ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
25
+ export declare function SearchDialogOverlay(props: ComponentProps<typeof DialogOverlay>): import("react/jsx-runtime").JSX.Element;
26
+ export declare function SearchDialogContent({ children, ...props }: ComponentProps<typeof DialogContent>): import("react/jsx-runtime").JSX.Element;
27
+ export declare function SearchDialogList({ items, Empty, Item, ...props }: Omit<ComponentProps<'div'>, 'children'> & {
28
+ items: SearchItemType[] | null | undefined;
29
+ /**
30
+ * Renderer for empty list UI
31
+ */
32
+ Empty?: () => ReactNode;
33
+ /**
34
+ * Renderer for items
35
+ */
36
+ Item?: (props: {
37
+ item: SearchItemType;
38
+ onClick: () => void;
39
+ }) => ReactNode;
40
+ }): import("react/jsx-runtime").JSX.Element;
41
+ export declare function SearchDialogListItem({ item, className, children, renderHighlights: render, ...props }: ComponentProps<'button'> & {
42
+ renderHighlights?: typeof renderHighlights;
43
+ item: SearchItemType;
44
+ }): import("react/jsx-runtime").JSX.Element;
45
+ export declare function SearchDialogIcon(props: ComponentProps<'svg'>): import("react/jsx-runtime").JSX.Element;
46
+ export interface TagsListProps extends ComponentProps<'div'> {
31
47
  tag?: string;
32
48
  onTagChange: (tag: string | undefined) => void;
33
49
  allowClear?: boolean;
34
- items: TagItem[];
35
50
  }
36
- export declare function TagsList({ tag, onTagChange, items, allowClear, ...props }: TagsListProps): import("react/jsx-runtime").JSX.Element;
37
- export {};
51
+ export declare function TagsList({ tag, onTagChange, allowClear, ...props }: TagsListProps): import("react/jsx-runtime").JSX.Element;
52
+ export declare function TagsListItem({ value, className, ...props }: ComponentProps<'button'> & {
53
+ value: string;
54
+ }): import("react/jsx-runtime").JSX.Element;
55
+ declare function renderHighlights(highlights: HighlightedText<ReactNode>[]): ReactNode;
56
+ export declare function useSearch(): {
57
+ open: boolean;
58
+ onOpenChange: (open: boolean) => void;
59
+ search: string;
60
+ onSearchChange: (v: string) => void;
61
+ isLoading: boolean;
62
+ };
63
+ export declare function useTagsList(): {
64
+ value?: string;
65
+ onValueChange: (value: string | undefined) => void;
66
+ allowClear: boolean;
67
+ };
68
+ export declare function useSearchList(): {
69
+ active: string | null;
70
+ setActive: (v: string | null) => void;
71
+ };
38
72
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/search.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,SAAS,EAKd,KAAK,cAAc,EACpB,MAAM,OAAO,CAAC;AAWf,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAKzD,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAEtD,KAAK,iBAAiB,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,GAAG;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,SAAS,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAEtC;;OAEG;IACH,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,UAAU,iBAAkB,SAAQ,WAAW;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC;IAEvC,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAU,EACV,MAAM,EACN,cAAc,EACd,SAAS,EACT,GAAG,KAAK,EACT,EAAE,iBAAiB,2CAyDnB;AA4JD,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAE1B,KAAK,CAAC,EAAE,cAAc,CAAC,iBAAiB,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc,CAAC,cAAc,CAAC;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAC/C,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAaD,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,WAAW,EACX,KAAK,EACL,UAAU,EACV,GAAG,KAAK,EACT,EAAE,aAAa,2CAkCf"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/search.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,cAAc,EAGnB,KAAK,SAAS,EAOf,MAAM,OAAO,CAAC;AAGf,OAAO,EAEL,aAAa,EACb,aAAa,EAEd,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,IAAI,cAAc,EACpC,MAAM,sBAAsB,CAAC;AAI9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKrD,MAAM,MAAM,cAAc,GACtB,CAAC,cAAc,GAAG;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC,GACF;IACE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAGN,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAsBD,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,cAAc,EACd,SAAiB,EACjB,QAAQ,GACT,EAAE,iBAAiB,2CAuBnB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,2CAO9D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,2CAa/D;AAED,wBAAgB,iBAAiB,CAAC,EAChC,QAAgB,EAChB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,QAAQ,CAAC,2CAoB1B;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,2CAO9D;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,CAAC,OAAO,aAAa,CAAC,2CAW5C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,OAAO,aAAa,CAAC,2CAiBtC;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,KAAY,EACZ,KAIC,EACD,IAAqD,EACrD,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IAC3C,KAAK,EAAE,cAAc,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IAC3C;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,SAAS,CAAC;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAA;KAAE,KAAK,SAAS,CAAC;CAC5E,2CA0GA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,gBAAgB,EAAE,MAAyB,EAC3C,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG;IAC5B,gBAAgB,CAAC,EAAE,OAAO,gBAAgB,CAAC;IAC3C,IAAI,EAAE,cAAc,CAAC;CACtB,2CAuEA;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,2CAa5D;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc,CAAC,KAAK,CAAC;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAC/C,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAaD,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,WAAW,EACX,UAAkB,EAClB,GAAG,KAAK,EACT,EAAE,aAAa,2CAoBf;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf,2CAkBA;AAED,iBAAS,gBAAgB,CAAC,UAAU,EAAE,eAAe,CAAC,SAAS,CAAC,EAAE,GAAG,SAAS,CAY7E;AAED,wBAAgB,SAAS;UAtcjB,OAAO;kBACC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI;YAC7B,MAAM;oBACE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI;eAExB,OAAO;EAqcnB;AAED,wBAAgB,WAAW;YA9bjB,MAAM;mBACC,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI;gBACtC,OAAO;EAgcpB;AAED,wBAAgB,aAAa;YAzcnB,MAAM,GAAG,IAAI;eACV,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI;EA4ctC"}
@@ -1,60 +1,86 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { FileText, Hash, Loader2, SearchIcon, Text } from 'lucide-react';
4
- import { useMemo, useEffect, useState, useCallback, } from 'react';
5
- import { useI18n } from '../../contexts/i18n.js';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { ChevronRight, Hash, Search as SearchIcon } from '../../icons.js';
4
+ import { createContext, Fragment, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
5
+ import { I18nLabel, useI18n } from '../../contexts/i18n.js';
6
6
  import { cn } from '../../utils/cn.js';
7
- import { useSidebar } from '../../contexts/sidebar.js';
8
- import { buttonVariants } from '../../components/ui/button.js';
9
7
  import { Dialog, DialogContent, DialogOverlay, DialogTitle, } from '@radix-ui/react-dialog';
10
8
  import { cva } from 'class-variance-authority';
11
9
  import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
12
10
  import { useRouter } from 'fumadocs-core/framework';
13
- export function SearchDialog({ open, onOpenChange, footer, links = [], search, onSearchChange, isLoading, ...props }) {
11
+ import { useOnChange } from 'fumadocs-core/utils/use-on-change';
12
+ import scrollIntoView from 'scroll-into-view-if-needed';
13
+ import { buttonVariants } from '../../components/ui/button.js';
14
+ const Context = createContext(null);
15
+ const ListContext = createContext(null);
16
+ const TagsListContext = createContext(null);
17
+ export function SearchDialog({ open, onOpenChange, search, onSearchChange, isLoading = false, children, }) {
18
+ const [active, setActive] = useState(null);
19
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsx(Context.Provider, { value: useMemo(() => ({
20
+ open,
21
+ onOpenChange,
22
+ search,
23
+ onSearchChange,
24
+ active,
25
+ setActive,
26
+ isLoading,
27
+ }), [active, isLoading, onOpenChange, onSearchChange, open, search]), children: children }) }));
28
+ }
29
+ export function SearchDialogHeader(props) {
30
+ return (_jsx("div", { ...props, className: cn('flex flex-row items-center gap-2 p-3', props.className) }));
31
+ }
32
+ export function SearchDialogInput(props) {
14
33
  const { text } = useI18n();
15
- const defaultItems = useMemo(() => links.map(([name, link]) => ({
16
- type: 'page',
17
- id: name,
18
- content: name,
19
- url: link,
20
- })), [links]);
21
- return (_jsxs(Dialog, { open: open, onOpenChange: onOpenChange, children: [_jsx(DialogOverlay, { className: "fixed inset-0 z-50 bg-black/30 backdrop-blur-sm data-[state=closed]:animate-fd-fade-out data-[state=open]:animate-fd-fade-in" }), _jsxs(DialogContent, { "aria-describedby": undefined, className: "fixed left-1/2 top-[10vh] z-50 w-[98vw] max-w-screen-sm -translate-x-1/2 rounded-lg border bg-fd-popover text-fd-popover-foreground shadow-lg data-[state=closed]:animate-fd-dialog-out data-[state=open]:animate-fd-dialog-in", children: [_jsx(DialogTitle, { className: "hidden", children: text.search }), _jsxs("div", { className: "flex flex-row items-center gap-2 px-3", children: [_jsx(LoadingIndicator, { isLoading: isLoading ?? false }), _jsx("input", { value: search, onChange: (e) => {
22
- onSearchChange(e.target.value);
23
- }, placeholder: text.search, className: "w-0 flex-1 bg-transparent py-3 text-base placeholder:text-fd-muted-foreground focus-visible:outline-none" }), _jsx("button", { type: "button", "aria-label": "Close Search", onClick: () => onOpenChange(false), className: cn(buttonVariants({
24
- color: 'outline',
25
- className: 'text-xs p-1.5',
26
- })), children: "Esc" })] }), props.results !== 'empty' || defaultItems.length > 0 ? (_jsx(SearchResults, { items: props.results === 'empty' ? defaultItems : props.results, onSelect: () => onOpenChange(false) })) : null, footer ? (_jsx("div", { className: "mt-auto flex flex-col border-t p-3", children: footer })) : null] })] }));
27
- }
28
- const icons = {
29
- text: _jsx(Text, { className: "size-4 text-fd-muted-foreground" }),
30
- heading: _jsx(Hash, { className: "size-4 text-fd-muted-foreground" }),
31
- page: _jsx(FileText, { className: "size-4 text-fd-muted-foreground" }),
32
- };
33
- function SearchResults({ items, onSelect, ...props }) {
34
- const [active, setActive] = useState();
34
+ const { search, onSearchChange } = useSearch();
35
+ return (_jsx("input", { ...props, value: search, onChange: (e) => onSearchChange(e.target.value), placeholder: text.search, className: "w-0 flex-1 bg-transparent text-lg placeholder:text-fd-muted-foreground focus-visible:outline-none" }));
36
+ }
37
+ export function SearchDialogClose({ children = 'ESC', className, ...props }) {
38
+ const { onOpenChange } = useSearch();
39
+ return (_jsx("button", { type: "button", onClick: () => onOpenChange(false), className: cn(buttonVariants({
40
+ color: 'outline',
41
+ size: 'sm',
42
+ className: 'font-mono text-fd-muted-foreground',
43
+ }), className), ...props, children: children }));
44
+ }
45
+ export function SearchDialogFooter(props) {
46
+ return (_jsx("div", { ...props, className: cn('bg-fd-secondary/50 p-3 empty:hidden', props.className) }));
47
+ }
48
+ export function SearchDialogOverlay(props) {
49
+ return (_jsx(DialogOverlay, { ...props, className: cn('fixed inset-0 z-50 backdrop-blur-xs bg-fd-overlay data-[state=open]:animate-fd-fade-in data-[state=closed]:animate-fd-fade-out', props.className) }));
50
+ }
51
+ export function SearchDialogContent({ children, ...props }) {
35
52
  const { text } = useI18n();
53
+ return (_jsxs(DialogContent, { "aria-describedby": undefined, ...props, className: cn('fixed left-1/2 top-4 md:top-[calc(50%-250px)] z-50 w-[calc(100%-1rem)] max-w-screen-sm -translate-x-1/2 rounded-xl border bg-fd-popover text-fd-popover-foreground shadow-2xl shadow-black/50 overflow-hidden data-[state=closed]:animate-fd-dialog-out data-[state=open]:animate-fd-dialog-in', '*:border-b *:has-[+:last-child[data-empty=true]]:border-b-0 *:data-[empty=true]:border-b-0 *:last:border-b-0', props.className), children: [_jsx(DialogTitle, { className: "hidden", children: text.search }), children] }));
54
+ }
55
+ export function SearchDialogList({ items = null, Empty = () => (_jsx("div", { className: "py-12 text-center text-sm text-fd-muted-foreground", children: _jsx(I18nLabel, { label: "searchNoResult" }) })), Item = (props) => _jsx(SearchDialogListItem, { ...props }), ...props }) {
56
+ const ref = useRef(null);
57
+ const [active, setActive] = useState(() => items && items.length > 0 ? items[0].id : null);
58
+ const { onOpenChange } = useSearch();
36
59
  const router = useRouter();
37
- const sidebar = useSidebar();
38
- if (items.length > 0 &&
39
- (!active || items.every((item) => item.id !== active))) {
40
- setActive(items[0].id);
41
- }
42
- const onOpen = ({ external, url }) => {
43
- if (external)
44
- window.open(url, '_blank')?.focus();
45
- else
46
- router.push(url);
47
- onSelect?.(url);
48
- sidebar.setOpen(false);
60
+ const onOpen = (item) => {
61
+ if (item.type === 'action') {
62
+ item.onSelect();
63
+ }
64
+ else if (item.external) {
65
+ window.open(item.url, '_blank')?.focus();
66
+ }
67
+ else {
68
+ router.push(item.url);
69
+ }
70
+ onOpenChange(false);
49
71
  };
50
72
  const onKey = useEffectEvent((e) => {
73
+ if (!items || e.isComposing)
74
+ return;
51
75
  if (e.key === 'ArrowDown' || e.key == 'ArrowUp') {
52
- setActive((cur) => {
53
- const idx = items.findIndex((item) => item.id === cur);
54
- if (idx === -1)
55
- return items.at(0)?.id;
56
- return items.at((e.key === 'ArrowDown' ? idx + 1 : idx - 1) % items.length)?.id;
57
- });
76
+ let idx = items.findIndex((item) => item.id === active);
77
+ if (idx === -1)
78
+ idx = 0;
79
+ else if (e.key === 'ArrowDown')
80
+ idx++;
81
+ else
82
+ idx--;
83
+ setActive(items.at(idx % items.length)?.id ?? null);
58
84
  e.preventDefault();
59
85
  }
60
86
  if (e.key === 'Enter') {
@@ -65,26 +91,58 @@ function SearchResults({ items, onSelect, ...props }) {
65
91
  }
66
92
  });
67
93
  useEffect(() => {
94
+ const element = ref.current;
95
+ if (!element)
96
+ return;
97
+ const observer = new ResizeObserver(() => {
98
+ const viewport = element.firstElementChild;
99
+ element.style.setProperty('--fd-animated-height', `${viewport.clientHeight}px`);
100
+ });
101
+ const viewport = element.firstElementChild;
102
+ if (viewport)
103
+ observer.observe(viewport);
68
104
  window.addEventListener('keydown', onKey);
69
105
  return () => {
106
+ observer.disconnect();
70
107
  window.removeEventListener('keydown', onKey);
71
108
  };
72
- }, [onKey]);
73
- return (_jsxs("div", { ...props, className: cn('flex max-h-[460px] flex-col overflow-y-auto border-t p-2', props.className), children: [items.length === 0 ? (_jsx("div", { className: "py-12 text-center text-sm", children: text.searchNoResult })) : null, items.map((item) => (_jsxs(CommandItem, { value: item.id, active: active, onActiveChange: setActive, onClick: () => {
74
- onOpen(item);
75
- }, children: [item.type !== 'page' ? (_jsx("div", { role: "none", className: "ms-2 h-full min-h-10 w-px bg-fd-border" })) : null, icons[item.type], _jsx("p", { className: "w-0 flex-1 truncate", children: item.content })] }, item.id)))] }));
76
- }
77
- function LoadingIndicator({ isLoading }) {
78
- return (_jsxs("div", { className: "relative size-4", children: [_jsx(Loader2, { className: cn('absolute size-full animate-spin text-fd-primary transition-opacity', !isLoading && 'opacity-0') }), _jsx(SearchIcon, { className: cn('absolute size-full text-fd-muted-foreground transition-opacity', isLoading && 'opacity-0') })] }));
79
- }
80
- function CommandItem({ active, onActiveChange, value, ...props }) {
81
- return (_jsx("button", { ref: useCallback((element) => {
82
- if (active === value && element) {
83
- element.scrollIntoView({
109
+ }, []);
110
+ useOnChange(items, () => {
111
+ if (items && items.length > 0) {
112
+ setActive(items[0].id);
113
+ }
114
+ });
115
+ return (_jsx("div", { ...props, ref: ref, "data-empty": items === null, className: cn('overflow-hidden h-(--fd-animated-height) transition-[height]', props.className), children: _jsx("div", { className: cn('w-full flex flex-col overflow-y-auto max-h-[460px] p-1', !items && 'hidden'), children: _jsxs(ListContext.Provider, { value: useMemo(() => ({
116
+ active,
117
+ setActive,
118
+ }), [active]), children: [items?.length === 0 && Empty(), items?.map((item) => (_jsx(Fragment, { children: Item({ item, onClick: () => onOpen(item) }) }, item.id)))] }) }) }));
119
+ }
120
+ export function SearchDialogListItem({ item, className, children, renderHighlights: render = renderHighlights, ...props }) {
121
+ const { active: activeId, setActive } = useSearchList();
122
+ const active = item.id === activeId;
123
+ if (item.type === 'action') {
124
+ children ?? (children = item.node);
125
+ }
126
+ else {
127
+ children ?? (children = _jsxs(_Fragment, { children: [_jsx("div", { className: "inline-flex items-center text-fd-muted-foreground text-xs empty:hidden", children: item.breadcrumbs?.map((item, i) => (_jsxs(Fragment, { children: [i > 0 && _jsx(ChevronRight, { className: "size-4" }), item] }, i))) }), item.type !== 'page' && (_jsx("div", { role: "none", className: "absolute start-3 inset-y-0 w-px bg-fd-border" })), _jsxs("p", { className: cn('min-w-0 truncate', item.type !== 'page' && 'ps-4', item.type === 'page' || item.type === 'heading'
128
+ ? 'font-medium'
129
+ : 'text-fd-popover-foreground/80'), children: [item.type === 'heading' && (_jsx(Hash, { className: "inline me-1 size-4 text-fd-muted-foreground" })), item.contentWithHighlights
130
+ ? render(item.contentWithHighlights)
131
+ : item.content] })] }));
132
+ }
133
+ return (_jsx("button", { type: "button", ref: useCallback((element) => {
134
+ if (active && element) {
135
+ scrollIntoView(element, {
136
+ scrollMode: 'if-needed',
84
137
  block: 'nearest',
138
+ boundary: element.parentElement,
85
139
  });
86
140
  }
87
- }, [active, value]), type: "button", "aria-selected": active === value, onPointerMove: () => onActiveChange(value), ...props, className: cn('flex min-h-10 select-none flex-row items-center gap-2.5 rounded-lg px-2 text-start text-sm', active === value && 'bg-fd-accent text-fd-accent-foreground', props.className), children: props.children }));
141
+ }, [active]), "aria-selected": active, className: cn('relative select-none px-2.5 py-2 text-start text-sm rounded-lg', active && 'bg-fd-accent text-fd-accent-foreground', className), onPointerMove: () => setActive(item.id), ...props, children: children }));
142
+ }
143
+ export function SearchDialogIcon(props) {
144
+ const { isLoading } = useSearch();
145
+ return (_jsx(SearchIcon, { ...props, className: cn('size-5 text-fd-muted-foreground', isLoading && 'animate-pulse duration-400', props.className) }));
88
146
  }
89
147
  const itemVariants = cva('rounded-md border px-2 py-0.5 text-xs font-medium text-fd-muted-foreground transition-colors', {
90
148
  variants: {
@@ -93,13 +151,43 @@ const itemVariants = cva('rounded-md border px-2 py-0.5 text-xs font-medium text
93
151
  },
94
152
  },
95
153
  });
96
- export function TagsList({ tag, onTagChange, items, allowClear, ...props }) {
97
- return (_jsxs("div", { ...props, className: cn('flex flex-row items-center gap-1 flex-wrap', props.className), children: [items.map((item) => (_jsx("button", { type: "button", "data-active": tag === item.value, className: cn(itemVariants({ active: tag === item.value }), item.props?.className), onClick: () => {
98
- if (tag === item.value && allowClear) {
99
- onTagChange(undefined);
100
- }
101
- else {
102
- onTagChange(item.value);
103
- }
104
- }, tabIndex: -1, ...item.props, children: item.name }, item.value))), props.children] }));
154
+ export function TagsList({ tag, onTagChange, allowClear = false, ...props }) {
155
+ return (_jsx("div", { ...props, className: cn('flex items-center gap-1 flex-wrap', props.className), children: _jsx(TagsListContext.Provider, { value: useMemo(() => ({
156
+ value: tag,
157
+ onValueChange: onTagChange,
158
+ allowClear,
159
+ }), [allowClear, onTagChange, tag]), children: props.children }) }));
160
+ }
161
+ export function TagsListItem({ value, className, ...props }) {
162
+ const { onValueChange, value: selectedValue, allowClear } = useTagsList();
163
+ const selected = value === selectedValue;
164
+ return (_jsx("button", { type: "button", "data-active": selected, className: cn(itemVariants({ active: selected, className })), onClick: () => {
165
+ onValueChange(selected && allowClear ? undefined : value);
166
+ }, tabIndex: -1, ...props, children: props.children }));
167
+ }
168
+ function renderHighlights(highlights) {
169
+ return highlights.map((node, i) => {
170
+ if (node.styles?.highlight) {
171
+ return (_jsx("span", { className: "text-fd-primary underline", children: node.content }, i));
172
+ }
173
+ return _jsx(Fragment, { children: node.content }, i);
174
+ });
175
+ }
176
+ export function useSearch() {
177
+ const ctx = useContext(Context);
178
+ if (!ctx)
179
+ throw new Error('Missing <SearchDialog />');
180
+ return ctx;
181
+ }
182
+ export function useTagsList() {
183
+ const ctx = useContext(TagsListContext);
184
+ if (!ctx)
185
+ throw new Error('Missing <TagsList />');
186
+ return ctx;
187
+ }
188
+ export function useSearchList() {
189
+ const ctx = useContext(ListContext);
190
+ if (!ctx)
191
+ throw new Error('Missing <SearchDialogList />');
192
+ return ctx;
105
193
  }
@@ -1,7 +1,21 @@
1
+ import { type CodeBlockProps } from '../components/codeblock.js';
1
2
  import type { HighlightOptionsCommon, HighlightOptionsThemes } from 'fumadocs-core/highlight';
2
- export declare function DynamicCodeBlock({ lang, code, options, }: {
3
+ export interface DynamicCodeblockProps {
3
4
  lang: string;
4
5
  code: string;
6
+ /**
7
+ * Extra props for the underlying `<CodeBlock />` component.
8
+ *
9
+ * Ignored if you defined your own `pre` component in `options.components`.
10
+ */
11
+ codeblock?: CodeBlockProps;
12
+ /**
13
+ * Wrap in React `<Suspense />` and provide a fallback.
14
+ *
15
+ * @defaultValue true
16
+ */
17
+ wrapInSuspense?: boolean;
5
18
  options?: Omit<HighlightOptionsCommon, 'lang'> & HighlightOptionsThemes;
6
- }): import("react").ReactNode;
19
+ }
20
+ export declare function DynamicCodeBlock({ lang, code, codeblock, options, wrapInSuspense, }: DynamicCodeblockProps): import("react/jsx-runtime").JSX.Element;
7
21
  //# sourceMappingURL=dynamic-codeblock.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-codeblock.d.ts","sourceRoot":"","sources":["../../src/components/dynamic-codeblock.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AAcjC,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,sBAAsB,EAAE,MAAM,CAAC,GAAG,sBAAsB,CAAC;CACzE,6BAUA"}
1
+ {"version":3,"file":"dynamic-codeblock.d.ts","sourceRoot":"","sources":["../../src/components/dynamic-codeblock.tsx"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,cAAc,EAAO,MAAM,wBAAwB,CAAC;AAC7E,OAAO,KAAK,EAEV,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AAajC,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,GAAG,sBAAsB,CAAC;CACzE;AAkBD,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,OAAO,EACP,cAAqB,GACtB,EAAE,qBAAqB,2CAgCvB"}
@@ -3,19 +3,31 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { CodeBlock, Pre } from '../components/codeblock.js';
4
4
  import { useShiki } from 'fumadocs-core/highlight/client';
5
5
  import { cn } from '../utils/cn.js';
6
- const components = {
7
- pre(props) {
8
- return (_jsx(CodeBlock, { ...props, className: cn('my-0', props.className), children: _jsx(Pre, { children: props.children }) }));
9
- },
10
- };
11
- export function DynamicCodeBlock({ lang, code, options, }) {
12
- return useShiki(code, {
6
+ import { createContext, Suspense, use, useDeferredValue, useId, } from 'react';
7
+ const PropsContext = createContext(undefined);
8
+ function DefaultPre(props) {
9
+ const extraProps = use(PropsContext);
10
+ return (_jsx(CodeBlock, { ...props, ...extraProps, className: cn('my-0', props.className, extraProps?.className), children: _jsx(Pre, { children: props.children }) }));
11
+ }
12
+ export function DynamicCodeBlock({ lang, code, codeblock, options, wrapInSuspense = true, }) {
13
+ const id = useId();
14
+ const shikiOptions = {
13
15
  lang,
14
16
  ...options,
15
17
  components: {
16
- ...components,
18
+ pre: DefaultPre,
17
19
  ...options?.components,
18
20
  },
19
- withPrerenderScript: true,
20
- });
21
+ };
22
+ const children = (_jsx(PropsContext, { value: codeblock, children: _jsx(Internal, { id: id, ...useDeferredValue({ code, options: shikiOptions }) }) }));
23
+ if (wrapInSuspense)
24
+ return (_jsx(Suspense, { fallback: _jsx(Placeholder, { code: code, components: shikiOptions.components }), children: children }));
25
+ return children;
26
+ }
27
+ function Placeholder({ code, components = {}, }) {
28
+ const { pre: Pre = 'pre', code: Code = 'code' } = components;
29
+ return (_jsx(Pre, { children: _jsx(Code, { children: code.split('\n').map((line, i) => (_jsx("span", { className: "line", children: line }, i))) }) }));
30
+ }
31
+ function Internal({ id, code, options, }) {
32
+ return useShiki(code, options, [id, options.lang, code]);
21
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/components/files.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAY,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAYtE,wBAAgB,KAAK,CAAC,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,cAAc,CAAC,GAAG,KAAK,CAAC,YAAY,CASrD;AAED,MAAM,WAAW,SAAU,SAAQ,cAAc,CAAC,cAAc,CAAC;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,WAAY,SAAQ,cAAc,CAAC,cAAc,CAAC;IACjE,IAAI,EAAE,MAAM,CAAC;IAEb,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,IAAI,CAAC,EACnB,IAAI,EACJ,IAAmB,EACnB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,SAAS,GAAG,KAAK,CAAC,YAAY,CAOhC;AAED,wBAAgB,MAAM,CAAC,EACrB,IAAI,EACJ,WAAmB,EACnB,GAAG,KAAK,EACT,EAAE,WAAW,GAAG,KAAK,CAAC,YAAY,CAclC"}
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/components/files.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAYtE,wBAAgB,KAAK,CAAC,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,cAAc,CAAC,GAAG,KAAK,CAAC,YAAY,CASrD;AAED,MAAM,WAAW,SAAU,SAAQ,cAAc,CAAC,cAAc,CAAC;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,WAAY,SAAQ,cAAc,CAAC,cAAc,CAAC;IACjE,IAAI,EAAE,MAAM,CAAC;IAEb,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,IAAI,CAAC,EACnB,IAAI,EACJ,IAAmB,EACnB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,SAAS,GAAG,KAAK,CAAC,YAAY,CAOhC;AAED,wBAAgB,MAAM,CAAC,EACrB,IAAI,EACJ,WAAmB,EACnB,GAAG,KAAK,EACT,EAAE,WAAW,GAAG,KAAK,CAAC,YAAY,CAclC"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { cva } from 'class-variance-authority';
4
- import { FileIcon, FolderIcon, FolderOpen } from 'lucide-react';
4
+ import { File as FileIcon, Folder as FolderIcon, FolderOpen, } from '../icons.js';
5
5
  import { useState } from 'react';
6
6
  import { cn } from '../utils/cn.js';
7
7
  import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from './ui/collapsible.js';
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from '../utils/cn.js';
3
- import { Star } from 'lucide-react';
3
+ import { Star } from '../icons.js';
4
4
  async function getRepoStarsAndForks(owner, repo, token) {
5
5
  const endpoint = `https://api.github.com/repos/${owner}/${repo}`;
6
6
  const headers = new Headers({