fumadocs-ui 15.4.2 → 15.5.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.
Files changed (76) hide show
  1. package/css/preset.css +12 -0
  2. package/dist/components/accordion.d.ts +4 -2
  3. package/dist/components/accordion.d.ts.map +1 -1
  4. package/dist/components/accordion.js +18 -10
  5. package/dist/components/dialog/search-algolia.d.ts +3 -2
  6. package/dist/components/dialog/search-algolia.d.ts.map +1 -1
  7. package/dist/components/dialog/search-algolia.js +17 -6
  8. package/dist/components/dialog/search-default.d.ts +3 -2
  9. package/dist/components/dialog/search-default.d.ts.map +1 -1
  10. package/dist/components/dialog/search-default.js +18 -6
  11. package/dist/components/dialog/search-orama.d.ts +3 -2
  12. package/dist/components/dialog/search-orama.d.ts.map +1 -1
  13. package/dist/components/dialog/search-orama.js +17 -6
  14. package/dist/components/dialog/search.d.ts +46 -19
  15. package/dist/components/dialog/search.d.ts.map +1 -1
  16. package/dist/components/dialog/search.js +93 -42
  17. package/dist/components/layout/sidebar.d.ts.map +1 -1
  18. package/dist/components/layout/sidebar.js +7 -5
  19. package/dist/components/layout/toc-clerk.d.ts +2 -4
  20. package/dist/components/layout/toc-clerk.d.ts.map +1 -1
  21. package/dist/components/layout/toc-clerk.js +8 -4
  22. package/dist/components/layout/toc.d.ts +6 -18
  23. package/dist/components/layout/toc.d.ts.map +1 -1
  24. package/dist/components/layout/toc.js +14 -16
  25. package/dist/components/tabs.d.ts +7 -7
  26. package/dist/components/tabs.d.ts.map +1 -1
  27. package/dist/components/tabs.js +7 -4
  28. package/dist/components/ui/button.d.ts +1 -1
  29. package/dist/components/ui/button.d.ts.map +1 -1
  30. package/dist/components/ui/button.js +2 -1
  31. package/dist/contexts/search.d.ts +10 -2
  32. package/dist/contexts/search.d.ts.map +1 -1
  33. package/dist/contexts/search.js +3 -1
  34. package/dist/layouts/docs/page-client.d.ts +30 -0
  35. package/dist/layouts/docs/page-client.d.ts.map +1 -0
  36. package/dist/{page-client.js → layouts/docs/page-client.js} +56 -40
  37. package/dist/layouts/docs/page.d.ts +16 -0
  38. package/dist/layouts/docs/page.d.ts.map +1 -0
  39. package/dist/layouts/docs/page.js +26 -0
  40. package/dist/layouts/docs/shared.d.ts +0 -3
  41. package/dist/layouts/docs/shared.d.ts.map +1 -1
  42. package/dist/layouts/docs/shared.js +0 -3
  43. package/dist/layouts/docs-client.d.ts +1 -0
  44. package/dist/layouts/docs-client.d.ts.map +1 -1
  45. package/dist/layouts/docs-client.js +10 -2
  46. package/dist/layouts/docs.d.ts +2 -11
  47. package/dist/layouts/docs.d.ts.map +1 -1
  48. package/dist/layouts/docs.js +20 -25
  49. package/dist/layouts/home/navbar.d.ts.map +1 -1
  50. package/dist/layouts/home/navbar.js +1 -5
  51. package/dist/layouts/home.d.ts +1 -2
  52. package/dist/layouts/home.d.ts.map +1 -1
  53. package/dist/layouts/home.js +8 -6
  54. package/dist/layouts/notebook-client.d.ts +5 -4
  55. package/dist/layouts/notebook-client.d.ts.map +1 -1
  56. package/dist/layouts/notebook-client.js +12 -6
  57. package/dist/layouts/notebook.d.ts.map +1 -1
  58. package/dist/layouts/notebook.js +18 -22
  59. package/dist/layouts/shared.d.ts +0 -9
  60. package/dist/layouts/shared.d.ts.map +1 -1
  61. package/dist/layouts/shared.js +0 -22
  62. package/dist/page.d.ts +25 -18
  63. package/dist/page.d.ts.map +1 -1
  64. package/dist/page.js +12 -18
  65. package/dist/provider/base.d.ts +1 -1
  66. package/dist/provider/base.d.ts.map +1 -1
  67. package/dist/style.css +47 -95
  68. package/dist/utils/merge-refs.d.ts +3 -0
  69. package/dist/utils/merge-refs.d.ts.map +1 -0
  70. package/dist/utils/merge-refs.js +12 -0
  71. package/package.json +8 -8
  72. package/dist/components/ui/hide-if-empty.d.ts +0 -10
  73. package/dist/components/ui/hide-if-empty.d.ts.map +0 -1
  74. package/dist/components/ui/hide-if-empty.js +0 -66
  75. package/dist/page-client.d.ts +0 -28
  76. package/dist/page-client.d.ts.map +0 -1
package/css/preset.css CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  @theme {
7
7
  --spacing-fd-container: 1400px;
8
+ --fd-page-width: 1200px;
8
9
  --fd-sidebar-width: 0px;
9
10
  --fd-toc-width: 0px;
10
11
  --fd-layout-width: 100vw;
@@ -242,6 +243,17 @@
242
243
  [data-rmiz-modal-overlay='visible'] {
243
244
  background-color: var(--color-fd-background);
244
245
  }
246
+
247
+ :root,
248
+ #nd-docs-layout {
249
+ --fd-layout-offset: max(calc(50vw - var(--fd-layout-width) / 2), 0px);
250
+ }
251
+ }
252
+
253
+ @variant max-xl {
254
+ #nd-docs-layout:has([data-toc-popover]) {
255
+ --fd-tocnav-height: calc(var(--spacing) * 10);
256
+ }
245
257
  }
246
258
 
247
259
  @utility fd-scroll-container {
@@ -1,7 +1,9 @@
1
1
  import type { AccordionMultipleProps, AccordionSingleProps } from '@radix-ui/react-accordion';
2
2
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
3
+ import { type ReactNode } from 'react';
3
4
  export declare const Accordions: import("react").ForwardRefExoticComponent<(Omit<AccordionSingleProps, "value" | "onValueChange"> | Omit<AccordionMultipleProps, "value" | "onValueChange">) & import("react").RefAttributes<HTMLDivElement>>;
4
- export declare const Accordion: import("react").ForwardRefExoticComponent<Omit<Omit<AccordionPrimitive.AccordionItemProps & import("react").RefAttributes<HTMLDivElement>, "ref">, "value"> & {
5
- title: string;
5
+ export declare const Accordion: import("react").ForwardRefExoticComponent<Omit<Omit<AccordionPrimitive.AccordionItemProps & import("react").RefAttributes<HTMLDivElement>, "ref">, "title" | "value"> & {
6
+ title: string | ReactNode;
7
+ value?: string;
6
8
  } & import("react").RefAttributes<HTMLDivElement>>;
7
9
  //# sourceMappingURL=accordion.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"accordion.d.ts","sourceRoot":"","sources":["../../src/components/accordion.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,kBAAkB,MAAM,2BAA2B,CAAC;AAYhE,eAAO,MAAM,UAAU,8MA+BrB,CAAC;AAIH,eAAO,MAAM,SAAS;WAGX,MAAM;kDA0Bf,CAAC"}
1
+ {"version":3,"file":"accordion.d.ts","sourceRoot":"","sources":["../../src/components/accordion.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,kBAAkB,MAAM,2BAA2B,CAAC;AAEhE,OAAO,EAGL,KAAK,SAAS,EAIf,MAAM,OAAO,CAAC;AAMf,eAAO,MAAM,UAAU,8MAuCrB,CAAC;AAIH,eAAO,MAAM,SAAS;WAMX,MAAM,GAAG,SAAS;YACjB,MAAM;kDAiCjB,CAAC"}
@@ -2,32 +2,40 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
4
4
  import { Check, ChevronRight, Link as LinkIcon } from '../icons.js';
5
- import { forwardRef, useEffect, useState, } from 'react';
5
+ import { forwardRef, useEffect, useRef, useState, } from 'react';
6
6
  import { cn } from '../utils/cn.js';
7
7
  import { useCopyButton } from '../utils/use-copy-button.js';
8
8
  import { buttonVariants } from '../components/ui/button.js';
9
+ import { mergeRefs } from '../utils/merge-refs.js';
9
10
  export const Accordions = forwardRef(({ type = 'single', className, defaultValue, ...props }, ref) => {
10
- const [value, setValue] = useState(type === 'single' ? (defaultValue ?? '') : (defaultValue ?? []));
11
+ const rootRef = useRef(null);
12
+ const composedRef = mergeRefs(ref, rootRef);
13
+ const [value, setValue] = useState(() => type === 'single' ? (defaultValue ?? '') : (defaultValue ?? []));
11
14
  useEffect(() => {
12
15
  const id = window.location.hash.substring(1);
13
- if (id.length > 0)
14
- setValue((prev) => (typeof prev === 'string' ? id : [id, ...prev]));
16
+ const element = rootRef.current;
17
+ if (!element || id.length === 0)
18
+ return;
19
+ const selected = document.getElementById(id);
20
+ if (!selected || !element.contains(selected))
21
+ return;
22
+ const value = selected.getAttribute('data-accordion-value');
23
+ if (value)
24
+ setValue((prev) => (typeof prev === 'string' ? value : [value, ...prev]));
15
25
  }, []);
16
26
  return (
17
27
  // @ts-expect-error -- Multiple types
18
- _jsx(AccordionPrimitive.Root, { type: type, ref: ref, value: value, onValueChange: setValue, collapsible: type === 'single' ? true : undefined, className: cn('divide-y divide-fd-border overflow-hidden rounded-lg border bg-fd-card', className), ...props }));
28
+ _jsx(AccordionPrimitive.Root, { type: type, ref: composedRef, value: value, onValueChange: setValue, collapsible: type === 'single' ? true : undefined, className: cn('divide-y divide-fd-border overflow-hidden rounded-lg border bg-fd-card', className), ...props }));
19
29
  });
20
30
  Accordions.displayName = 'Accordions';
21
- export const Accordion = forwardRef(({ title, className, id, children, ...props }, ref) => {
22
- return (_jsxs(AccordionPrimitive.Item, { ref: ref,
23
- // Use `id` instead if presents
24
- value: id ?? title, className: cn('group/accordion relative scroll-m-20', className), ...props, children: [_jsxs(AccordionPrimitive.Header, { id: id, className: "not-prose flex flex-row items-center text-fd-card-foreground font-medium has-focus-visible:bg-fd-accent", children: [_jsxs(AccordionPrimitive.Trigger, { className: "flex flex-1 items-center gap-2 px-3 py-2.5 text-start focus-visible:outline-none", children: [_jsx(ChevronRight, { className: "size-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 group-data-[state=open]/accordion:rotate-90" }), title] }), id ? _jsx(CopyButton, { id: id }) : null] }), _jsx(AccordionPrimitive.Content, { className: "overflow-hidden data-[state=closed]:animate-fd-accordion-up data-[state=open]:animate-fd-accordion-down", children: _jsx("div", { className: "px-4 pb-2 text-[15px] prose-no-margin", children: children }) })] }));
31
+ export const Accordion = forwardRef(({ title, className, id, value = String(title), children, ...props }, ref) => {
32
+ return (_jsxs(AccordionPrimitive.Item, { ref: ref, value: value, className: cn('scroll-m-24', className), ...props, children: [_jsxs(AccordionPrimitive.Header, { id: id, "data-accordion-value": value, className: "not-prose flex flex-row items-center text-fd-card-foreground font-medium has-focus-visible:bg-fd-accent", children: [_jsxs(AccordionPrimitive.Trigger, { className: "group flex flex-1 items-center gap-2 px-3 py-2.5 text-start focus-visible:outline-none", children: [_jsx(ChevronRight, { className: "size-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-90" }), title] }), id ? _jsx(CopyButton, { id: id }) : null] }), _jsx(AccordionPrimitive.Content, { className: "overflow-hidden data-[state=closed]:animate-fd-accordion-up data-[state=open]:animate-fd-accordion-down", children: _jsx("div", { className: "px-4 pb-2 text-[15px] prose-no-margin", children: children }) })] }));
25
33
  });
26
34
  function CopyButton({ id }) {
27
35
  const [checked, onClick] = useCopyButton(() => {
28
36
  const url = new URL(window.location.href);
29
37
  url.hash = id;
30
- void navigator.clipboard.writeText(url.toString());
38
+ return navigator.clipboard.writeText(url.toString());
31
39
  });
32
40
  return (_jsx("button", { type: "button", "aria-label": "Copy Link", className: cn(buttonVariants({
33
41
  color: 'ghost',
@@ -1,8 +1,9 @@
1
1
  import { type AlgoliaOptions } from 'fumadocs-core/search/client';
2
2
  import { type ReactNode } from 'react';
3
- import { type SharedProps, type TagItem } from './search.js';
3
+ import type { SearchLink, SharedProps, TagItem } from '../../contexts/search.js';
4
4
  export interface AlgoliaSearchDialogProps extends SharedProps {
5
5
  searchOptions: AlgoliaOptions;
6
+ links?: SearchLink[];
6
7
  footer?: ReactNode;
7
8
  defaultTag?: string;
8
9
  tags?: TagItem[];
@@ -19,5 +20,5 @@ export interface AlgoliaSearchDialogProps extends SharedProps {
19
20
  */
20
21
  allowClear?: boolean;
21
22
  }
22
- export default function AlgoliaSearchDialog({ searchOptions, tags, defaultTag, showAlgolia, allowClear, ...props }: AlgoliaSearchDialogProps): ReactNode;
23
+ export default function AlgoliaSearchDialog({ searchOptions, tags, defaultTag, showAlgolia, allowClear, links, footer, ...props }: AlgoliaSearchDialogProps): import("react/jsx-runtime").JSX.Element;
23
24
  //# sourceMappingURL=search-algolia.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-algolia.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/search-algolia.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,cAAc,EAEpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAEjD,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,OAAO,EAGb,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,wBAAyB,SAAQ,WAAW;IAC3D,aAAa,EAAE,cAAc,CAAC;IAE9B,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IAEjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,EAC1C,aAAa,EACb,IAAI,EACJ,UAAU,EACV,WAAmB,EACnB,UAAkB,EAClB,GAAG,KAAK,EACT,EAAE,wBAAwB,GAAG,SAAS,CAyCtC"}
1
+ {"version":3,"file":"search-algolia.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/search-algolia.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,cAAc,EAEpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAC;AAgB1D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAG1E,MAAM,WAAW,wBAAyB,SAAQ,WAAW;IAC3D,aAAa,EAAE,cAAc,CAAC;IAC9B,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IAErB,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IAEjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,EAC1C,aAAa,EACb,IAAS,EACT,UAAU,EACV,WAAmB,EACnB,UAAkB,EAClB,KAAU,EACV,MAAM,EACN,GAAG,KAAK,EACT,EAAE,wBAAwB,2CAgE1B"}
@@ -1,19 +1,30 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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, TagsListItem, } from './search.js';
7
- export default function AlgoliaSearchDialog({ searchOptions, tags, defaultTag, showAlgolia = false, allowClear = false, ...props }) {
6
+ import { SearchDialog, SearchDialogClose, SearchDialogContent, SearchDialogFooter, SearchDialogHeader, SearchDialogIcon, SearchDialogInput, SearchDialogList, SearchDialogOverlay, TagsList, TagsListItem, } from './search.js';
7
+ import { useI18n } from '../../contexts/i18n.js';
8
+ export default function AlgoliaSearchDialog({ searchOptions, tags = [], defaultTag, showAlgolia = false, allowClear = false, links = [], footer, ...props }) {
8
9
  const [tag, setTag] = useState(defaultTag);
10
+ const { locale } = useI18n();
9
11
  const { search, setSearch, query } = useDocsSearch({
10
12
  type: 'algolia',
13
+ tag,
14
+ locale,
11
15
  ...searchOptions,
12
- }, undefined, tag);
16
+ });
17
+ const defaultItems = useMemo(() => links.map(([name, link]) => ({
18
+ type: 'page',
19
+ id: name,
20
+ content: name,
21
+ url: link,
22
+ })), [links]);
13
23
  useOnChange(defaultTag, (v) => {
14
24
  setTag(v);
15
25
  });
16
- return (_jsx(SearchDialog, { search: search, onSearchChange: setSearch, results: query.data ?? [], isLoading: query.isLoading, ...props, footer: _jsxs(_Fragment, { children: [tags ? (_jsxs(TagsList, { tag: tag, onTagChange: setTag, allowClear: allowClear, children: [tags.map((tag) => (_jsx(TagsListItem, { value: tag.value, children: tag.name }, tag.value))), showAlgolia && _jsx(AlgoliaTitle, {})] })) : (showAlgolia && _jsx(AlgoliaTitle, {})), props.footer] }) }));
26
+ const label = showAlgolia && _jsx(AlgoliaTitle, {});
27
+ 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, {})] }), query.data !== 'empty' && query.data && (_jsx(SearchDialogList, { items: query.data })), query.data === 'empty' && defaultItems.length > 0 && (_jsx(SearchDialogList, { items: 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] })] }));
17
28
  }
18
29
  function AlgoliaTitle() {
19
30
  return (_jsx("a", { href: "https://algolia.com", rel: "noreferrer noopener", className: "ms-auto text-xs text-fd-muted-foreground", children: "Search powered by Algolia" }));
@@ -1,6 +1,7 @@
1
1
  import { type ReactNode } from 'react';
2
- import { type SharedProps, type TagItem } from './search.js';
2
+ import type { SearchLink, SharedProps, TagItem } from '../../contexts/search.js';
3
3
  export interface DefaultSearchDialogProps extends SharedProps {
4
+ links?: SearchLink[];
4
5
  /**
5
6
  * @defaultValue 'fetch'
6
7
  */
@@ -23,5 +24,5 @@ export interface DefaultSearchDialogProps extends SharedProps {
23
24
  */
24
25
  allowClear?: boolean;
25
26
  }
26
- export default function DefaultSearchDialog({ defaultTag, tags, api, delayMs, type, allowClear, ...props }: DefaultSearchDialogProps): ReactNode;
27
+ export default function DefaultSearchDialog({ defaultTag, tags, api, delayMs, type, allowClear, links, footer, ...props }: DefaultSearchDialogProps): import("react/jsx-runtime").JSX.Element;
27
28
  //# sourceMappingURL=search-default.d.ts.map
@@ -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,EAGb,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,CA6CtC"}
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;AAiB1D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE1E,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,2CAsE1B"}
@@ -1,24 +1,36 @@
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, TagsListItem, } 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(() => links.map(([name, link]) => ({
27
+ type: 'page',
28
+ id: name,
29
+ content: name,
30
+ url: link,
31
+ })), [links]);
20
32
  useOnChange(defaultTag, (v) => {
21
33
  setTag(v);
22
34
  });
23
- return (_jsx(SearchDialog, { search: search, onSearchChange: setSearch, isLoading: query.isLoading, results: query.data ?? [], ...props, footer: _jsxs(_Fragment, { children: [tags && (_jsx(TagsList, { tag: tag, onTagChange: setTag, allowClear: allowClear, children: tags.map((tag) => (_jsx(TagsListItem, { value: tag.value, children: tag.name }, tag.value))) })), props.footer] }) }));
35
+ 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, {})] }), query.data !== 'empty' && query.data && (_jsx(SearchDialogList, { items: query.data })), query.data === 'empty' && defaultItems.length > 0 && (_jsx(SearchDialogList, { items: 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
36
  }
@@ -1,7 +1,8 @@
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 { SearchLink, SharedProps, TagItem } from '../../contexts/search.js';
4
4
  export interface OramaSearchDialogProps extends SharedProps {
5
+ links?: SearchLink[];
5
6
  client: OramaCloudOptions['client'];
6
7
  searchOptions?: OramaCloudOptions['params'];
7
8
  index?: OramaCloudOptions['index'];
@@ -24,5 +25,5 @@ export interface OramaSearchDialogProps extends SharedProps {
24
25
  /**
25
26
  * Orama Cloud integration
26
27
  */
27
- export default function OramaSearchDialog({ client, searchOptions, tags, defaultTag, showOrama, allowClear, index, ...props }: OramaSearchDialogProps): ReactNode;
28
+ export default function OramaSearchDialog({ client, searchOptions, tags, defaultTag, showOrama, allowClear, index, footer, links, ...props }: OramaSearchDialogProps): import("react/jsx-runtime").JSX.Element;
28
29
  //# 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,EACL,KAAK,iBAAiB,EAEvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAEjD,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,OAAO,EAGb,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;AAgB1D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAG1E,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,SAAiB,EACjB,UAAkB,EAClB,KAAK,EACL,MAAM,EACN,KAAU,EACV,GAAG,KAAK,EACT,EAAE,sBAAsB,2CAmExB"}
@@ -1,24 +1,35 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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, TagsListItem, } 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 = false, 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(() => links.map(([name, link]) => ({
23
+ type: 'page',
24
+ id: name,
25
+ content: name,
26
+ url: link,
27
+ })), [links]);
18
28
  useOnChange(defaultTag, (v) => {
19
29
  setTag(v);
20
30
  });
21
- return (_jsx(SearchDialog, { search: search, onSearchChange: setSearch, results: query.data ?? [], isLoading: query.isLoading, ...props, footer: _jsxs(_Fragment, { children: [tags ? (_jsxs(TagsList, { tag: tag, onTagChange: setTag, allowClear: allowClear, children: [tags.map((tag) => (_jsx(TagsListItem, { value: tag.value, children: tag.name }, tag.value))), showOrama && _jsx(Label, {})] })) : (showOrama && _jsx(Label, {})), props.footer] }) }));
31
+ const label = showOrama && _jsx(Label, {});
32
+ 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, {})] }), query.data !== 'empty' && query.data && (_jsx(SearchDialogList, { items: query.data })), query.data === 'empty' && defaultItems.length > 0 && (_jsx(SearchDialogList, { items: 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
33
  }
23
34
  function Label() {
24
35
  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,31 +1,43 @@
1
1
  import { type ComponentProps, type ReactNode } from 'react';
2
+ import { DialogContent, DialogOverlay } from '@radix-ui/react-dialog';
2
3
  import type { SortedResult } from 'fumadocs-core/server';
3
- export type SearchLink = [name: string, href: string];
4
+ import type { SharedProps } from '../../contexts/search.js';
4
5
  type ReactSortedResult = Omit<SortedResult, 'content'> & {
5
6
  external?: boolean;
6
7
  content: ReactNode;
7
8
  };
8
- export interface TagItem {
9
- name: string;
10
- value: string;
11
- }
12
- export interface SharedProps {
13
- open: boolean;
14
- onOpenChange: (open: boolean) => void;
15
- /**
16
- * Custom links to be displayed if search is empty
17
- */
18
- links?: SearchLink[];
19
- }
20
- interface SearchDialogProps extends SharedProps {
9
+ export type { SharedProps };
10
+ export interface SearchDialogProps extends SharedProps {
21
11
  search: string;
22
12
  onSearchChange: (v: string) => void;
23
13
  isLoading?: boolean;
24
- hideResults?: boolean;
25
- results: ReactSortedResult[] | 'empty';
26
- footer?: ReactNode;
14
+ children: ReactNode;
27
15
  }
28
- export declare function SearchDialog({ open, onOpenChange, footer, links, search, onSearchChange, isLoading, ...props }: SearchDialogProps): import("react/jsx-runtime").JSX.Element;
16
+ export declare function SearchDialog({ open, onOpenChange, search, onSearchChange, isLoading, children, }: SearchDialogProps): import("react/jsx-runtime").JSX.Element;
17
+ export declare function SearchDialogHeader(props: ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
18
+ export declare function SearchDialogInput(props: ComponentProps<'input'>): import("react/jsx-runtime").JSX.Element;
19
+ export declare function SearchDialogClose({ children, className, ...props }: ComponentProps<'button'>): import("react/jsx-runtime").JSX.Element;
20
+ export declare function SearchDialogFooter(props: ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
21
+ export declare function SearchDialogOverlay(props: ComponentProps<typeof DialogOverlay>): import("react/jsx-runtime").JSX.Element;
22
+ export declare function SearchDialogContent({ children, ...props }: ComponentProps<typeof DialogContent>): import("react/jsx-runtime").JSX.Element;
23
+ export declare function SearchDialogList({ items, Empty, Item, ...props }: Omit<ComponentProps<'div'>, 'children'> & {
24
+ items: ReactSortedResult[];
25
+ /**
26
+ * Renderer for empty list UI
27
+ */
28
+ Empty?: () => ReactNode;
29
+ /**
30
+ * Renderer for items
31
+ */
32
+ Item?: (props: {
33
+ item: ReactSortedResult;
34
+ onClick: () => void;
35
+ }) => ReactNode;
36
+ }): import("react/jsx-runtime").JSX.Element;
37
+ export declare function SearchDialogListItem({ item, className, children, ...props }: ComponentProps<'button'> & {
38
+ item: ReactSortedResult;
39
+ }): import("react/jsx-runtime").JSX.Element;
40
+ export declare function SearchDialogIcon(props: ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
29
41
  export interface TagsListProps extends ComponentProps<'div'> {
30
42
  tag?: string;
31
43
  onTagChange: (tag: string | undefined) => void;
@@ -35,5 +47,20 @@ export declare function TagsList({ tag, onTagChange, allowClear, ...props }: Tag
35
47
  export declare function TagsListItem({ value, className, ...props }: ComponentProps<'button'> & {
36
48
  value: string;
37
49
  }): import("react/jsx-runtime").JSX.Element;
38
- export {};
50
+ export declare function useSearch(): {
51
+ open: boolean;
52
+ onOpenChange: (open: boolean) => void;
53
+ search: string;
54
+ onSearchChange: (v: string) => void;
55
+ isLoading: boolean;
56
+ };
57
+ export declare function useTagsList(): {
58
+ value?: string;
59
+ onValueChange: (value: string | undefined) => void;
60
+ allowClear: boolean;
61
+ };
62
+ export declare function useSearchList(): {
63
+ active: string | null;
64
+ setActive: (v: string | null) => void;
65
+ };
39
66
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/search.tsx"],"names":[],"mappings":"AASA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AAUf,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,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,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,2CA6DnB;AAkJD,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;AAmBD,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,2CAmBA"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/search.tsx"],"names":[],"mappings":"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"}
@@ -1,56 +1,81 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { FileText, Hash, LoaderCircle, Search as SearchIcon, Text, } from '../../icons.js';
4
- import { useCallback, useEffect, useMemo, useState, } from 'react';
5
- import { useI18n } from '../../contexts/i18n.js';
4
+ import { createContext, Fragment, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
5
+ import { I18nLabel, useI18n } from '../../contexts/i18n.js';
6
6
  import { cn } from '../../utils/cn.js';
7
7
  import { buttonVariants } from '../../components/ui/button.js';
8
8
  import { Dialog, DialogContent, DialogOverlay, DialogTitle, } from '@radix-ui/react-dialog';
9
9
  import { cva } from 'class-variance-authority';
10
10
  import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
11
- import { createContext, useRouter } from 'fumadocs-core/framework';
12
- export function SearchDialog({ open, onOpenChange, footer, links = [], search, onSearchChange, isLoading, ...props }) {
11
+ import { useRouter } from 'fumadocs-core/framework';
12
+ import { useOnChange } from 'fumadocs-core/utils/use-on-change';
13
+ const Context = createContext(null);
14
+ const ListContext = createContext(null);
15
+ const TagsListContext = createContext(null);
16
+ export function SearchDialog({ open, onOpenChange, search, onSearchChange, isLoading = false, children, }) {
17
+ const [active, setActive] = useState(null);
18
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsx(Context.Provider, { value: useMemo(() => ({
19
+ open,
20
+ onOpenChange,
21
+ search,
22
+ onSearchChange,
23
+ active,
24
+ setActive,
25
+ isLoading,
26
+ }), [active, isLoading, onOpenChange, onSearchChange, open, search]), children: children }) }));
27
+ }
28
+ export function SearchDialogHeader(props) {
29
+ return (_jsx("div", { ...props, className: cn('flex flex-row items-center gap-2 px-3', props.className) }));
30
+ }
31
+ export function SearchDialogInput(props) {
32
+ const { text } = useI18n();
33
+ const { search, onSearchChange } = useSearch();
34
+ return (_jsx("input", { ...props, value: search, onChange: (e) => onSearchChange(e.target.value), placeholder: text.search, className: "w-0 flex-1 bg-transparent py-3 text-base placeholder:text-fd-muted-foreground focus-visible:outline-none" }));
35
+ }
36
+ export function SearchDialogClose({ children = 'Esc', className, ...props }) {
37
+ const { onOpenChange } = useSearch();
38
+ return (_jsx("button", { type: "button", onClick: () => onOpenChange(false), className: cn(buttonVariants({
39
+ color: 'outline',
40
+ className: 'text-xs p-1.5',
41
+ }), className), ...props, children: children }));
42
+ }
43
+ export function SearchDialogFooter(props) {
44
+ return (_jsx("div", { ...props, className: cn('mt-auto border-t p-3 empty:hidden', props.className) }));
45
+ }
46
+ export function SearchDialogOverlay(props) {
47
+ return (_jsx(DialogOverlay, { ...props, className: cn('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', props.className) }));
48
+ }
49
+ export function SearchDialogContent({ children, ...props }) {
13
50
  const { text } = useI18n();
14
- const [active, setActive] = useState();
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
- setActive(undefined);
24
- }, 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({
25
- color: 'outline',
26
- className: 'text-xs p-1.5',
27
- })), children: "Esc" })] }), props.results !== 'empty' || defaultItems.length > 0 ? (_jsx(SearchResults, { active: active, onActiveChange: setActive, items: props.results === 'empty' ? defaultItems : props.results, onSelect: () => onOpenChange(false) })) : null, _jsx("div", { className: "mt-auto flex flex-col border-t p-3 empty:hidden", children: footer })] })] }));
51
+ return (_jsxs(DialogContent, { "aria-describedby": undefined, ...props, className: cn('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', props.className), children: [_jsx(DialogTitle, { className: "hidden", children: text.search }), children] }));
28
52
  }
29
53
  const icons = {
30
54
  text: _jsx(Text, { className: "size-4 text-fd-muted-foreground" }),
31
55
  heading: _jsx(Hash, { className: "size-4 text-fd-muted-foreground" }),
32
56
  page: _jsx(FileText, { className: "size-4 text-fd-muted-foreground" }),
33
57
  };
34
- function SearchResults({ items, active = items.at(0)?.id, onActiveChange, onSelect, ...props }) {
35
- const { text } = useI18n();
58
+ export function SearchDialogList({ items, Empty = () => (_jsx("div", { className: "py-12 text-center text-sm", children: _jsx(I18nLabel, { label: "searchNoResult" }) })), Item = (props) => _jsx(SearchDialogListItem, { ...props }), ...props }) {
59
+ const [active, setActive] = useState(items.at(0)?.id ?? null);
60
+ const { onOpenChange } = useSearch();
36
61
  const router = useRouter();
37
62
  const onOpen = ({ external, url }) => {
38
63
  if (external)
39
64
  window.open(url, '_blank')?.focus();
40
65
  else
41
66
  router.push(url);
42
- onSelect?.(url);
67
+ onOpenChange(false);
43
68
  };
44
69
  const onKey = useEffectEvent((e) => {
45
70
  if (e.key === 'ArrowDown' || e.key == 'ArrowUp') {
46
- const idx = items.findIndex((item) => item.id === active);
47
- if (idx === -1) {
48
- onActiveChange(items.at(0)?.id);
49
- }
50
- else {
51
- onActiveChange(items.at((e.key === 'ArrowDown' ? idx + 1 : idx - 1) % items.length)
52
- ?.id);
53
- }
71
+ let idx = items.findIndex((item) => item.id === active);
72
+ if (idx === -1)
73
+ idx = 0;
74
+ else if (e.key === 'ArrowDown')
75
+ idx++;
76
+ else
77
+ idx--;
78
+ setActive(items.at(idx % items.length)?.id ?? null);
54
79
  e.preventDefault();
55
80
  }
56
81
  if (e.key === 'Enter') {
@@ -66,21 +91,29 @@ function SearchResults({ items, active = items.at(0)?.id, onActiveChange, onSele
66
91
  window.removeEventListener('keydown', onKey);
67
92
  };
68
93
  }, [onKey]);
69
- 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, { active: active === item.id, onPointerMove: () => onActiveChange(item.id), onClick: () => {
70
- onOpen(item);
71
- }, 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)))] }));
72
- }
73
- function LoadingIndicator({ isLoading }) {
74
- return (_jsxs("div", { className: "relative size-4", children: [_jsx(LoaderCircle, { 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') })] }));
94
+ useOnChange(items, () => {
95
+ if (items.length > 0)
96
+ setActive(items[0].id);
97
+ });
98
+ return (_jsx("div", { ...props, className: cn('flex max-h-[460px] flex-col overflow-y-auto border-t p-2', props.className), children: _jsxs(ListContext.Provider, { value: useMemo(() => ({
99
+ active,
100
+ setActive,
101
+ }), [active]), children: [items.length === 0 && Empty(), items.map((item) => (_jsx(Fragment, { children: Item({ item, onClick: () => onOpen(item) }) }, item.id)))] }) }));
75
102
  }
76
- function CommandItem({ active = false, ...props }) {
77
- return (_jsx("button", { ref: useCallback((element) => {
103
+ export function SearchDialogListItem({ item, className, children, ...props }) {
104
+ const { active: activeId, setActive } = useSearchList();
105
+ const active = item.id === activeId;
106
+ return (_jsxs("button", { type: "button", ref: useCallback((element) => {
78
107
  if (active && element) {
79
108
  element.scrollIntoView({
80
109
  block: 'nearest',
81
110
  });
82
111
  }
83
- }, [active]), type: "button", "aria-selected": active, ...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 && 'bg-fd-accent text-fd-accent-foreground', props.className), children: props.children }));
112
+ }, [active]), "aria-selected": active, className: cn('flex min-h-10 select-none flex-row items-center gap-2.5 rounded-lg px-2 text-start text-sm', active && 'bg-fd-accent text-fd-accent-foreground', className), onPointerMove: () => setActive(item.id), ...props, children: [item.type !== 'page' && (_jsx("div", { role: "none", className: "ms-2 h-full min-h-10 w-px bg-fd-border" })), icons[item.type], _jsx("p", { className: "w-0 flex-1 truncate", children: children ?? item.content })] }));
113
+ }
114
+ export function SearchDialogIcon(props) {
115
+ const { isLoading } = useSearch();
116
+ return (_jsxs("div", { ...props, className: cn('relative size-4', props.className), children: [_jsx(LoaderCircle, { 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') })] }));
84
117
  }
85
118
  const itemVariants = cva('rounded-md border px-2 py-0.5 text-xs font-medium text-fd-muted-foreground transition-colors', {
86
119
  variants: {
@@ -89,7 +122,6 @@ const itemVariants = cva('rounded-md border px-2 py-0.5 text-xs font-medium text
89
122
  },
90
123
  },
91
124
  });
92
- const TagsListContext = createContext('TagsList');
93
125
  export function TagsList({ tag, onTagChange, allowClear = false, ...props }) {
94
126
  return (_jsx("div", { ...props, className: cn('flex items-center gap-1 flex-wrap', props.className), children: _jsx(TagsListContext.Provider, { value: useMemo(() => ({
95
127
  value: tag,
@@ -98,8 +130,27 @@ export function TagsList({ tag, onTagChange, allowClear = false, ...props }) {
98
130
  }), [allowClear, onTagChange, tag]), children: props.children }) }));
99
131
  }
100
132
  export function TagsListItem({ value, className, ...props }) {
101
- const ctx = TagsListContext.use();
102
- return (_jsx("button", { type: "button", "data-active": value === ctx.value, className: cn(itemVariants({ active: value === ctx.value, className })), onClick: () => {
103
- ctx.onValueChange(ctx.value === value && ctx.allowClear ? undefined : value);
133
+ const { onValueChange, value: selectedValue, allowClear } = useTagsList();
134
+ const selected = value === selectedValue;
135
+ return (_jsx("button", { type: "button", "data-active": selected, className: cn(itemVariants({ active: selected, className })), onClick: () => {
136
+ onValueChange(selected && allowClear ? undefined : value);
104
137
  }, tabIndex: -1, ...props, children: props.children }));
105
138
  }
139
+ export function useSearch() {
140
+ const ctx = useContext(Context);
141
+ if (!ctx)
142
+ throw new Error('Missing <SearchDialog />');
143
+ return ctx;
144
+ }
145
+ export function useTagsList() {
146
+ const ctx = useContext(TagsListContext);
147
+ if (!ctx)
148
+ throw new Error('Missing <TagsList />');
149
+ return ctx;
150
+ }
151
+ export function useSearchList() {
152
+ const ctx = useContext(ListContext);
153
+ if (!ctx)
154
+ throw new Error('Missing <SearchDialogList />');
155
+ return ctx;
156
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"sidebar.d.ts","sourceRoot":"","sources":["../../../src/components/layout/sidebar.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,EAAE,EAEP,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AACf,OAAa,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAU1D,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAMrD,MAAM,WAAW,YAAa,SAAQ,cAAc,CAAC,OAAO,CAAC;IAC3D;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA2BD,wBAAgB,OAAO,CAAC,EACtB,gBAAoB,EACpB,QAAe,EACf,WAAkB,EAClB,GAAG,KAAK,EACT,EAAE,YAAY,2CAgHd;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,2CASzD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,2CASzD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,2CAcrD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,2CAkB1D;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,GAAG,KAAK,EACT,EAAE,SAAS,GAAG;IACb,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB,2CAsBA;AAED,wBAAgB,aAAa,CAAC,EAC5B,WAAmB,EACnB,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,2CAgBA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,GAAG,KAAK,EACT,EAAE,uBAAuB,2CAqBzB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,2CAsCjD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,uBAAuB,2CAqBlE;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,2CAgBrE;AAgBD,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,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;IAC1E,SAAS,EAAE,EAAE,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAA;KAAE,CAAC,CAAC;CAC7C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACzC,2CAuDA"}
1
+ {"version":3,"file":"sidebar.d.ts","sourceRoot":"","sources":["../../../src/components/layout/sidebar.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,EAAE,EAEP,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AACf,OAAa,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAU1D,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAMrD,MAAM,WAAW,YAAa,SAAQ,cAAc,CAAC,OAAO,CAAC;IAC3D;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA2BD,wBAAgB,OAAO,CAAC,EACtB,gBAAoB,EACpB,QAAe,EACf,WAAkB,EAClB,GAAG,KAAK,EACT,EAAE,YAAY,2CAiHd;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,2CASzD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,2CASzD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,2CAcrD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,2CAkB1D;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,GAAG,KAAK,EACT,EAAE,SAAS,GAAG;IACb,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB,2CAsBA;AAED,wBAAgB,aAAa,CAAC,EAC5B,WAAmB,EACnB,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,2CAgBA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,GAAG,KAAK,EACT,EAAE,uBAAuB,2CAqBzB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,2CAsCjD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,uBAAuB,2CAqBlE;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,2CAgBrE;AAgBD,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,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;IAC1E,SAAS,EAAE,EAAE,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAA;KAAE,CAAC,CAAC;CAC7C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACzC,2CAuDA"}