@farming-labs/theme 0.0.48 → 0.0.50

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.
@@ -68,6 +68,10 @@ declare const ColorfulUIDefaults: {
68
68
  CodeBlock: {
69
69
  showCopyButton: boolean;
70
70
  };
71
+ HoverLink: {
72
+ linkLabel: string;
73
+ showIndicator: boolean;
74
+ };
71
75
  Tabs: {
72
76
  style: string;
73
77
  };
@@ -70,6 +70,10 @@ const ColorfulUIDefaults = {
70
70
  icon: true
71
71
  },
72
72
  CodeBlock: { showCopyButton: true },
73
+ HoverLink: {
74
+ linkLabel: "Open page",
75
+ showIndicator: false
76
+ },
73
77
  Tabs: { style: "default" }
74
78
  }
75
79
  };
@@ -70,6 +70,10 @@ declare const DarkBoldUIDefaults: {
70
70
  CodeBlock: {
71
71
  showCopyButton: boolean;
72
72
  };
73
+ HoverLink: {
74
+ linkLabel: string;
75
+ showIndicator: boolean;
76
+ };
73
77
  Tabs: {
74
78
  style: string;
75
79
  };
@@ -72,6 +72,10 @@ const DarkBoldUIDefaults = {
72
72
  icon: true
73
73
  },
74
74
  CodeBlock: { showCopyButton: true },
75
+ HoverLink: {
76
+ linkLabel: "Open page",
77
+ showIndicator: false
78
+ },
75
79
  Tabs: { style: "default" }
76
80
  }
77
81
  };
@@ -91,6 +91,10 @@ declare const DarksharpUIDefaults: {
91
91
  CodeBlock: {
92
92
  showCopyButton: boolean;
93
93
  };
94
+ HoverLink: {
95
+ linkLabel: string;
96
+ showIndicator: boolean;
97
+ };
94
98
  Tabs: {
95
99
  style: string;
96
100
  };
@@ -87,6 +87,10 @@ const DarksharpUIDefaults = {
87
87
  icon: true
88
88
  },
89
89
  CodeBlock: { showCopyButton: true },
90
+ HoverLink: {
91
+ linkLabel: "Open page",
92
+ showIndicator: false
93
+ },
90
94
  Tabs: { style: "default" }
91
95
  }
92
96
  };
@@ -90,6 +90,10 @@ declare const DefaultUIDefaults: {
90
90
  CodeBlock: {
91
91
  showCopyButton: boolean;
92
92
  };
93
+ HoverLink: {
94
+ linkLabel: string;
95
+ showIndicator: boolean;
96
+ };
93
97
  Tabs: {
94
98
  style: string;
95
99
  };
@@ -86,6 +86,10 @@ const DefaultUIDefaults = {
86
86
  icon: true
87
87
  },
88
88
  CodeBlock: { showCopyButton: true },
89
+ HoverLink: {
90
+ linkLabel: "Open page",
91
+ showIndicator: false
92
+ },
89
93
  Tabs: { style: "default" }
90
94
  }
91
95
  };
@@ -70,6 +70,10 @@ declare const GreenTreeUIDefaults: {
70
70
  CodeBlock: {
71
71
  showCopyButton: boolean;
72
72
  };
73
+ HoverLink: {
74
+ linkLabel: string;
75
+ showIndicator: boolean;
76
+ };
73
77
  Tabs: {
74
78
  style: string;
75
79
  };
@@ -72,6 +72,10 @@ const GreenTreeUIDefaults = {
72
72
  icon: true
73
73
  },
74
74
  CodeBlock: { showCopyButton: true },
75
+ HoverLink: {
76
+ linkLabel: "Open page",
77
+ showIndicator: false
78
+ },
75
79
  Tabs: { style: "default" }
76
80
  }
77
81
  };
@@ -8,8 +8,14 @@ interface HoverLinkProps {
8
8
  description: string;
9
9
  children: ReactNode;
10
10
  linkLabel?: string;
11
+ previewLabel?: string;
11
12
  external?: boolean;
12
13
  prefetch?: boolean;
14
+ showIndicator?: boolean;
15
+ align?: "start" | "center" | "end";
16
+ side?: "top" | "right" | "bottom" | "left";
17
+ sideOffset?: number;
18
+ closeDelay?: number;
13
19
  }
14
20
  declare function HoverLink({
15
21
  href,
@@ -17,8 +23,14 @@ declare function HoverLink({
17
23
  description,
18
24
  children,
19
25
  linkLabel,
26
+ previewLabel,
20
27
  external,
21
- prefetch
28
+ prefetch,
29
+ showIndicator,
30
+ align,
31
+ side,
32
+ sideOffset,
33
+ closeDelay
22
34
  }: HoverLinkProps): react_jsx_runtime0.JSX.Element;
23
35
  //#endregion
24
- export { HoverLink };
36
+ export { HoverLink, HoverLinkProps };
@@ -6,9 +6,12 @@ import Link from "fumadocs-core/link";
6
6
  import { Popover, PopoverContent, PopoverTrigger } from "fumadocs-ui/components/ui/popover";
7
7
 
8
8
  //#region src/hover-link.tsx
9
- function HoverLink({ href, title, description, children, linkLabel = "Open page", external, prefetch }) {
9
+ function HoverLink({ href, title, description, children, linkLabel = "Open page", previewLabel, external, prefetch, showIndicator = false, align = "center", side = "bottom", sideOffset = 12, closeDelay = 90 }) {
10
10
  const [open, setOpen] = useState(false);
11
11
  const closeTimerRef = useRef(null);
12
+ const triggerRef = useRef(null);
13
+ const contentRef = useRef(null);
14
+ const underlineColor = open ? "color-mix(in srgb, var(--color-fd-foreground, currentColor) 68%, transparent)" : "color-mix(in srgb, var(--color-fd-foreground, currentColor) 46%, transparent)";
12
15
  function clearCloseTimer() {
13
16
  if (closeTimerRef.current !== null) {
14
17
  window.clearTimeout(closeTimerRef.current);
@@ -21,7 +24,14 @@ function HoverLink({ href, title, description, children, linkLabel = "Open page"
21
24
  }
22
25
  function closePopover() {
23
26
  clearCloseTimer();
24
- closeTimerRef.current = window.setTimeout(() => setOpen(false), 120);
27
+ closeTimerRef.current = window.setTimeout(() => setOpen(false), closeDelay);
28
+ }
29
+ function isWithinHoverArea(target) {
30
+ if (!(target instanceof Node)) return false;
31
+ return triggerRef.current !== null && triggerRef.current.contains(target) || contentRef.current !== null && contentRef.current.contains(target);
32
+ }
33
+ function shouldOpenFromFocus(element) {
34
+ return typeof element.matches === "function" && element.matches(":focus-visible");
25
35
  }
26
36
  useEffect(() => {
27
37
  return () => {
@@ -30,47 +40,80 @@ function HoverLink({ href, title, description, children, linkLabel = "Open page"
30
40
  }, []);
31
41
  return /* @__PURE__ */ jsxs(Popover, {
32
42
  open,
33
- onOpenChange: setOpen,
43
+ onOpenChange: (nextOpen) => {
44
+ clearCloseTimer();
45
+ setOpen(nextOpen);
46
+ },
34
47
  children: [/* @__PURE__ */ jsx(PopoverTrigger, {
35
48
  asChild: true,
36
49
  children: /* @__PURE__ */ jsxs("button", {
50
+ ref: triggerRef,
37
51
  type: "button",
38
- onMouseEnter: openPopover,
39
- onMouseLeave: closePopover,
40
- onFocus: openPopover,
41
- onBlur: closePopover,
42
- onClick: () => setOpen((value) => !value),
52
+ onPointerEnter: openPopover,
53
+ onPointerLeave: (event) => {
54
+ if (isWithinHoverArea(event.relatedTarget)) return;
55
+ closePopover();
56
+ },
57
+ onFocus: (event) => {
58
+ if (!shouldOpenFromFocus(event.currentTarget)) return;
59
+ openPopover();
60
+ },
61
+ onBlur: (event) => {
62
+ if (isWithinHoverArea(event.relatedTarget)) return;
63
+ closePopover();
64
+ },
65
+ onClick: openPopover,
43
66
  style: {
44
- display: "inline-flex",
45
- alignItems: "center",
46
- gap: "0.35rem",
67
+ display: "inline",
47
68
  border: "none",
48
69
  background: "transparent",
49
70
  padding: 0,
71
+ margin: 0,
50
72
  color: "var(--color-fd-foreground, currentColor)",
51
73
  cursor: "pointer",
52
74
  textDecoration: "underline",
53
75
  textDecorationStyle: "dashed",
76
+ textDecorationThickness: "0.08em",
54
77
  textUnderlineOffset: "0.22em",
55
- textDecorationColor: "color-mix(in srgb, var(--color-fd-border, currentColor) 85%, transparent)",
56
- font: "inherit"
78
+ textDecorationColor: underlineColor,
79
+ font: "inherit",
80
+ lineHeight: "inherit",
81
+ verticalAlign: "baseline",
82
+ appearance: "none",
83
+ transition: "text-decoration-color 180ms ease"
57
84
  },
58
- children: [/* @__PURE__ */ jsx("span", { children }), /* @__PURE__ */ jsx("span", {
85
+ children: [/* @__PURE__ */ jsx("span", { children }), showIndicator ? /* @__PURE__ */ jsx("span", {
59
86
  "aria-hidden": "true",
60
87
  style: {
88
+ marginInlineStart: "0.3em",
61
89
  fontSize: "0.75em",
62
90
  opacity: .8
63
91
  },
64
92
  children: "+"
65
- })]
93
+ }) : null]
66
94
  })
67
95
  }), /* @__PURE__ */ jsx(PopoverContent, {
68
- align: "center",
69
- sideOffset: 12,
70
- onMouseEnter: openPopover,
71
- onMouseLeave: closePopover,
96
+ ref: contentRef,
97
+ align,
98
+ side,
99
+ sideOffset,
100
+ onPointerEnter: openPopover,
101
+ onPointerLeave: (event) => {
102
+ if (isWithinHoverArea(event.relatedTarget)) return;
103
+ closePopover();
104
+ },
72
105
  onFocusCapture: openPopover,
73
- onBlurCapture: closePopover,
106
+ onBlurCapture: (event) => {
107
+ if (isWithinHoverArea(event.relatedTarget)) return;
108
+ closePopover();
109
+ },
110
+ onInteractOutside: () => {
111
+ clearCloseTimer();
112
+ setOpen(false);
113
+ },
114
+ onCloseAutoFocus: (event) => {
115
+ event.preventDefault();
116
+ },
74
117
  style: {
75
118
  width: "min(22rem, calc(100vw - 2rem))",
76
119
  borderRadius: "calc(var(--radius, 0.75rem) + 2px)",
@@ -94,7 +137,7 @@ function HoverLink({ href, title, description, children, linkLabel = "Open page"
94
137
  gap: "0.35rem"
95
138
  },
96
139
  children: [
97
- /* @__PURE__ */ jsx("span", {
140
+ previewLabel ? /* @__PURE__ */ jsx("span", {
98
141
  style: {
99
142
  fontSize: "0.68rem",
100
143
  textTransform: "uppercase",
@@ -102,8 +145,8 @@ function HoverLink({ href, title, description, children, linkLabel = "Open page"
102
145
  fontFamily: "var(--fd-font-mono, var(--font-geist-mono, ui-monospace, monospace))",
103
146
  color: "color-mix(in srgb, var(--color-fd-popover-foreground, currentColor) 55%, transparent)"
104
147
  },
105
- children: "Link Preview"
106
- }),
148
+ children: previewLabel
149
+ }) : null,
107
150
  /* @__PURE__ */ jsx(Link, {
108
151
  href,
109
152
  external,
package/dist/index.d.mts CHANGED
@@ -5,10 +5,10 @@ import { DocsPageClient } from "./docs-page-client.mjs";
5
5
  import { RootProvider } from "./provider.mjs";
6
6
  import { PageActions } from "./page-actions.mjs";
7
7
  import { withLangInUrl } from "./i18n.mjs";
8
- import { HoverLink } from "./hover-link.mjs";
8
+ import { HoverLink, HoverLinkProps } from "./hover-link.mjs";
9
9
  import { DocsLayout } from "fumadocs-ui/layouts/docs";
10
10
  import { AIConfig, BreadcrumbConfig, CopyMarkdownConfig, DocsConfig, DocsMetadata, DocsNav, DocsTheme, FontStyle, OGConfig, OpenDocsConfig, OpenDocsProvider, PageActionsConfig, PageFrontmatter, SidebarConfig, ThemeToggleConfig, TypographyConfig, UIConfig, createTheme, deepMerge, defineDocs, extendTheme } from "@farming-labs/docs";
11
11
  import { DocsBody, DocsPage } from "fumadocs-ui/layouts/docs/page";
12
12
  import { Tab, Tabs } from "fumadocs-ui/components/tabs";
13
13
  import { CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, Pre } from "fumadocs-ui/components/codeblock";
14
- export { type AIConfig, type BreadcrumbConfig, CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, type CopyMarkdownConfig, DocsBody, DocsCommandSearch, type DocsConfig, DocsLayout, type DocsMetadata, type DocsNav, DocsPage, DocsPageClient, type DocsTheme, type FontStyle, DefaultUIDefaults as FumadocsUIDefaults, HoverLink, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, PageActions, type PageActionsConfig, type PageFrontmatter, Pre, RootProvider, type SidebarConfig, Tab, Tabs, type ThemeToggleConfig, type TypographyConfig, type UIConfig, createDocsLayout, createDocsMetadata, createPageMetadata, createTheme, deepMerge, defineDocs, extendTheme, fumadocs, withLangInUrl };
14
+ export { type AIConfig, type BreadcrumbConfig, CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, type CopyMarkdownConfig, DocsBody, DocsCommandSearch, type DocsConfig, DocsLayout, type DocsMetadata, type DocsNav, DocsPage, DocsPageClient, type DocsTheme, type FontStyle, DefaultUIDefaults as FumadocsUIDefaults, HoverLink, type HoverLinkProps, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, PageActions, type PageActionsConfig, type PageFrontmatter, Pre, RootProvider, type SidebarConfig, Tab, Tabs, type ThemeToggleConfig, type TypographyConfig, type UIConfig, createDocsLayout, createDocsMetadata, createPageMetadata, createTheme, deepMerge, defineDocs, extendTheme, fumadocs, withLangInUrl };
package/dist/mdx.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { HoverLink } from "./hover-link.mjs";
2
2
  import { MDXImg } from "./mdx-img.mjs";
3
3
  import React from "react";
4
- import { CodeBlockCopyData } from "@farming-labs/docs";
4
+ import { CodeBlockCopyData, DocsTheme } from "@farming-labs/docs";
5
5
  import * as react_jsx_runtime0 from "react/jsx-runtime";
6
6
  import { Tab, Tabs } from "fumadocs-ui/components/tabs";
7
7
  import * as fumadocs_ui_components_codeblock0 from "fumadocs-ui/components/codeblock";
@@ -38,6 +38,8 @@ declare const extendedMdxComponents: {
38
38
  interface GetMDXComponentsOptions {
39
39
  /** Called when the user clicks the copy button on a code block (in addition to the default copy). */
40
40
  onCopyClick?: (data: CodeBlockCopyData) => void;
41
+ /** Theme config used to apply built-in MDX component defaults from `theme.ui.components`. */
42
+ theme?: DocsTheme;
41
43
  }
42
44
  declare function getMDXComponents<T extends Record<string, unknown> = Record<string, unknown>>(overrides?: T, options?: GetMDXComponentsOptions): typeof extendedMdxComponents & T;
43
45
  //#endregion
package/dist/mdx.mjs CHANGED
@@ -1,10 +1,29 @@
1
1
  import { HoverLink } from "./hover-link.mjs";
2
2
  import { MDXImg } from "./mdx-img.mjs";
3
3
  import { createPreWithCopyCallback } from "./code-block-copy-wrapper.mjs";
4
+ import React from "react";
4
5
  import { Tab, Tabs } from "fumadocs-ui/components/tabs";
5
6
  import defaultMdxComponents from "fumadocs-ui/mdx";
6
7
 
7
8
  //#region src/mdx.ts
9
+ /**
10
+ * Re-export fumadocs-ui MDX components.
11
+ *
12
+ * Includes all default MDX components (headings, code blocks, callouts, cards)
13
+ * plus Tabs/Tab for tabbed content and InstallTabs for package manager tabs.
14
+ * Overrides `img` so that ![alt](url) in markdown works without width/height
15
+ * (uses Next Image with unoptimized + default dimensions for external URLs).
16
+ *
17
+ * When `options.onCopyClick` is provided, the default `pre` component is wrapped
18
+ * so that the callback is fired when the user clicks the code block copy button.
19
+ *
20
+ * Usage in mdx-components.tsx:
21
+ * import { getMDXComponents } from "@farming-labs/theme/mdx";
22
+ * return getMDXComponents(
23
+ * { ...docsConfig.components, ...components },
24
+ * { onCopyClick: docsConfig.onCopyClick, theme: docsConfig.theme }
25
+ * );
26
+ */
8
27
  const extendedMdxComponents = {
9
28
  ...defaultMdxComponents,
10
29
  img: MDXImg,
@@ -12,13 +31,45 @@ const extendedMdxComponents = {
12
31
  Tab,
13
32
  Tabs
14
33
  };
34
+ const mdxComponentDefaults = { HoverLink: {
35
+ linkLabel: "Open page",
36
+ showIndicator: false,
37
+ align: "center",
38
+ side: "bottom",
39
+ sideOffset: 12,
40
+ closeDelay: 90
41
+ } };
42
+ function applyBuiltInComponentDefaults(options) {
43
+ const themeComponents = options?.theme?.ui?.components;
44
+ if (!themeComponents) return extendedMdxComponents;
45
+ const entries = Object.entries(themeComponents);
46
+ if (entries.length === 0) return extendedMdxComponents;
47
+ const components = { ...extendedMdxComponents };
48
+ for (const [name, value] of entries) {
49
+ const Component = components[name];
50
+ if (!Component) continue;
51
+ const builtInDefaults = mdxComponentDefaults[name] ?? {};
52
+ const componentDefaults = typeof value === "function" ? value(builtInDefaults) : {
53
+ ...builtInDefaults,
54
+ ...value ?? {}
55
+ };
56
+ if (!componentDefaults || typeof componentDefaults !== "object") continue;
57
+ components[name] = function ThemedComponent(props) {
58
+ return React.createElement(Component, {
59
+ ...componentDefaults,
60
+ ...props
61
+ });
62
+ };
63
+ }
64
+ return components;
65
+ }
15
66
  function getMDXComponents(overrides, options) {
16
67
  const base = {
17
- ...extendedMdxComponents,
68
+ ...applyBuiltInComponentDefaults(options),
18
69
  ...overrides
19
70
  };
20
71
  if (options?.onCopyClick) {
21
- const DefaultPre = defaultMdxComponents.pre;
72
+ const DefaultPre = base.pre;
22
73
  if (DefaultPre) base.pre = createPreWithCopyCallback(DefaultPre, options.onCopyClick);
23
74
  }
24
75
  return base;
@@ -68,7 +68,12 @@ declare const PixelBorderUIDefaults: {
68
68
  sticky: boolean;
69
69
  };
70
70
  };
71
- components: {};
71
+ components: {
72
+ HoverLink: {
73
+ linkLabel: string;
74
+ showIndicator: boolean;
75
+ };
76
+ };
72
77
  };
73
78
  /**
74
79
  * Pixel-border theme preset factory.
@@ -72,7 +72,10 @@ const PixelBorderUIDefaults = {
72
72
  sticky: true
73
73
  }
74
74
  },
75
- components: {}
75
+ components: { HoverLink: {
76
+ linkLabel: "Open page",
77
+ showIndicator: false
78
+ } }
76
79
  };
77
80
  /**
78
81
  * Pixel-border theme preset factory.
@@ -69,6 +69,10 @@ declare const ShinyUIDefaults: {
69
69
  CodeBlock: {
70
70
  showCopyButton: boolean;
71
71
  };
72
+ HoverLink: {
73
+ linkLabel: string;
74
+ showIndicator: boolean;
75
+ };
72
76
  Tabs: {
73
77
  style: string;
74
78
  };
@@ -71,6 +71,10 @@ const ShinyUIDefaults = {
71
71
  icon: true
72
72
  },
73
73
  CodeBlock: { showCopyButton: true },
74
+ HoverLink: {
75
+ linkLabel: "Open page",
76
+ showIndicator: false
77
+ },
74
78
  Tabs: { style: "default" }
75
79
  }
76
80
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/theme",
3
- "version": "0.0.48",
3
+ "version": "0.0.50",
4
4
  "description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
5
5
  "keywords": [
6
6
  "docs",
@@ -110,7 +110,7 @@
110
110
  "tsdown": "^0.20.3",
111
111
  "typescript": "^5.9.3",
112
112
  "vitest": "^3.2.4",
113
- "@farming-labs/docs": "0.0.48"
113
+ "@farming-labs/docs": "0.0.50"
114
114
  },
115
115
  "peerDependencies": {
116
116
  "@farming-labs/docs": ">=0.0.1",