@waveso/docs 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +252 -1
- package/README.md +188 -90
- package/dist/anchors.d.ts +44 -0
- package/dist/anchors.js +76 -0
- package/dist/errors.d.ts +2 -0
- package/dist/frontmatter.d.ts +8 -0
- package/dist/frontmatter.js +7 -1
- package/dist/link-suggestion.d.ts +31 -0
- package/dist/link-suggestion.js +94 -0
- package/dist/next.d.ts +34 -27
- package/dist/next.js +40 -13
- package/dist/plugins/rehype-fallback-heading-ids.js +1 -1
- package/dist/plugins/remark-doc-links.d.ts +33 -1
- package/dist/plugins/remark-doc-links.js +24 -8
- package/dist/react/hero.d.ts +19 -0
- package/dist/react/hero.js +44 -0
- package/dist/react/layout.d.ts +1 -3
- package/dist/react/layout.js +17 -54
- package/dist/react/link-adapter.d.ts +34 -0
- package/dist/react/link-adapter.js +30 -0
- package/dist/react/nav.d.ts +11 -11
- package/dist/react/nav.js +106 -58
- package/dist/react/next-link.d.ts +6 -28
- package/dist/react/next-link.js +45 -24
- package/dist/react/next-nav.d.ts +5 -1
- package/dist/react/next-nav.js +5 -3
- package/dist/react/next-search.js +1 -1
- package/dist/react/shell-labels.d.ts +8 -2
- package/dist/react/toc.d.ts +13 -3
- package/dist/react/toc.js +17 -10
- package/dist/render.d.ts +1 -1
- package/dist/render.js +71 -11
- package/dist/source.js +7 -3
- package/dist/styles.css +1079 -237
- package/dist/types.d.ts +102 -6
- package/package.json +10 -3
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { DocsLinkComponent } from "./markdown-components.js";
|
|
2
|
+
import { ComponentProps, ComponentType } from "react";
|
|
3
|
+
//#region src/react/link-adapter.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The part of `next/link` this package uses.
|
|
6
|
+
*
|
|
7
|
+
* Declared structurally rather than imported: `next` is an optional peer, and
|
|
8
|
+
* a type-only import of it would still be a hard resolution requirement for
|
|
9
|
+
* anyone type-checking against our `.d.ts`.
|
|
10
|
+
*/
|
|
11
|
+
type NextLinkComponent = ComponentType<Omit<ComponentProps<'a'>, 'href' | 'ref'> & {
|
|
12
|
+
href: string;
|
|
13
|
+
prefetch?: boolean | null;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Adapt `next/link` to {@link DocsLinkProps}.
|
|
17
|
+
*
|
|
18
|
+
* `next/link` widens `href` to `string | UrlObject` and `prefetch` to
|
|
19
|
+
* `boolean | 'auto' | null`; the React layer promises neither, because it must
|
|
20
|
+
* also run with a plain `<a>`. One wrapper keeps that mismatch in a single
|
|
21
|
+
* place instead of at every call site.
|
|
22
|
+
*
|
|
23
|
+
* `prefetch` is omitted rather than passed as `undefined`, which is not
|
|
24
|
+
* pedantry: under `exactOptionalPropertyTypes` — which this package compiles
|
|
25
|
+
* with, and which any consumer may turn on — `undefined` is not assignable to
|
|
26
|
+
* `boolean | 'auto' | null`, and `<SearchDialog Link={Link} />` written by
|
|
27
|
+
* hand fails to compile for a reason that reads as our bug.
|
|
28
|
+
*
|
|
29
|
+
* Call it once at module scope, never during a render: a fresh component
|
|
30
|
+
* identity for `a` remounts every link in the document on every render.
|
|
31
|
+
*/
|
|
32
|
+
declare function wrapNextLink(NextLink: NextLinkComponent): DocsLinkComponent;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { NextLinkComponent, wrapNextLink };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
//#region src/react/link-adapter.ts
|
|
3
|
+
/**
|
|
4
|
+
* Adapt `next/link` to {@link DocsLinkProps}.
|
|
5
|
+
*
|
|
6
|
+
* `next/link` widens `href` to `string | UrlObject` and `prefetch` to
|
|
7
|
+
* `boolean | 'auto' | null`; the React layer promises neither, because it must
|
|
8
|
+
* also run with a plain `<a>`. One wrapper keeps that mismatch in a single
|
|
9
|
+
* place instead of at every call site.
|
|
10
|
+
*
|
|
11
|
+
* `prefetch` is omitted rather than passed as `undefined`, which is not
|
|
12
|
+
* pedantry: under `exactOptionalPropertyTypes` — which this package compiles
|
|
13
|
+
* with, and which any consumer may turn on — `undefined` is not assignable to
|
|
14
|
+
* `boolean | 'auto' | null`, and `<SearchDialog Link={Link} />` written by
|
|
15
|
+
* hand fails to compile for a reason that reads as our bug.
|
|
16
|
+
*
|
|
17
|
+
* Call it once at module scope, never during a render: a fresh component
|
|
18
|
+
* identity for `a` remounts every link in the document on every render.
|
|
19
|
+
*/
|
|
20
|
+
function wrapNextLink(NextLink) {
|
|
21
|
+
return function DocsNextLink({ href, prefetch, children, ...rest }) {
|
|
22
|
+
return createElement(NextLink, {
|
|
23
|
+
...rest,
|
|
24
|
+
href,
|
|
25
|
+
...prefetch === void 0 ? {} : { prefetch }
|
|
26
|
+
}, children);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { wrapNextLink };
|
package/dist/react/nav.d.ts
CHANGED
|
@@ -2,14 +2,7 @@ import { DocNavNode } from "../types.js";
|
|
|
2
2
|
import { DocsLinkComponent } from "./markdown-components.js";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
//#region src/react/nav.d.ts
|
|
5
|
-
/**
|
|
6
|
-
* The drawer's `id`, and the header trigger's `commandfor`.
|
|
7
|
-
*
|
|
8
|
-
* A constant rather than a `useId`, for two reasons that both matter: the
|
|
9
|
-
* trigger is rendered on the server in a different subtree and cannot see a
|
|
10
|
-
* hook's value, and `command`/`commandfor` must agree before React hydrates or
|
|
11
|
-
* the button does nothing on the first tap.
|
|
12
|
-
*/
|
|
5
|
+
/** The navigation's `id`, and the trigger's `aria-controls`. */
|
|
13
6
|
declare const DOCS_NAV_ID = "wave-docs-nav";
|
|
14
7
|
interface DocsNavProps {
|
|
15
8
|
/** The tree from `docs.source.nav()`. */
|
|
@@ -18,15 +11,22 @@ interface DocsNavProps {
|
|
|
18
11
|
pathname: string;
|
|
19
12
|
/** Client-side router link, e.g. `next/link`. Falls back to `<a>`. */
|
|
20
13
|
Link?: DocsLinkComponent | undefined;
|
|
21
|
-
/** Accessible name for the nav landmark
|
|
14
|
+
/** Accessible name for the nav landmark. */
|
|
22
15
|
label?: string | undefined;
|
|
23
|
-
/** Accessible name for the
|
|
16
|
+
/** Accessible name for the trigger while the sidebar is open. */
|
|
24
17
|
closeLabel?: string | undefined;
|
|
18
|
+
/** Accessible name for the trigger while the sidebar is closed. */
|
|
19
|
+
openLabel?: string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Rendered above the tree. The search trigger goes here — it belongs to the
|
|
22
|
+
* navigation, so it moves with it and is never a second thing to place.
|
|
23
|
+
*/
|
|
24
|
+
children?: ReactNode;
|
|
25
25
|
/** Passed through to the tree. See `DocsSidebarProps.expandGroup`. */
|
|
26
26
|
expandGroup?: string | undefined;
|
|
27
27
|
collapseGroup?: string | undefined;
|
|
28
28
|
externalLink?: string | undefined;
|
|
29
29
|
}
|
|
30
|
-
declare function DocsNav({ nav, pathname, Link, label, closeLabel, expandGroup, collapseGroup, externalLink }: DocsNavProps): ReactNode;
|
|
30
|
+
declare function DocsNav({ nav, pathname, Link, label, children, closeLabel, openLabel, expandGroup, collapseGroup, externalLink }: DocsNavProps): ReactNode;
|
|
31
31
|
//#endregion
|
|
32
32
|
export { DOCS_NAV_ID, DocsNav, DocsNavProps };
|
package/dist/react/nav.js
CHANGED
|
@@ -1,73 +1,121 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { DocsSidebar } from "./sidebar.js";
|
|
3
|
-
import { useEffect, useRef } from "react";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/react/nav.tsx
|
|
6
|
-
/**
|
|
7
|
-
* The drawer's `id`, and the header trigger's `commandfor`.
|
|
8
|
-
*
|
|
9
|
-
* A constant rather than a `useId`, for two reasons that both matter: the
|
|
10
|
-
* trigger is rendered on the server in a different subtree and cannot see a
|
|
11
|
-
* hook's value, and `command`/`commandfor` must agree before React hydrates or
|
|
12
|
-
* the button does nothing on the first tap.
|
|
13
|
-
*/
|
|
6
|
+
/** The navigation's `id`, and the trigger's `aria-controls`. */
|
|
14
7
|
const DOCS_NAV_ID = "wave-docs-nav";
|
|
15
|
-
function DocsNav({ nav, pathname, Link, label = "Documentation", closeLabel = "Close navigation", expandGroup, collapseGroup, externalLink }) {
|
|
16
|
-
const
|
|
8
|
+
function DocsNav({ nav, pathname, Link, label = "Documentation", children, closeLabel = "Close navigation", openLabel = "Open navigation", expandGroup, collapseGroup, externalLink }) {
|
|
9
|
+
const shellRef = useRef(null);
|
|
10
|
+
const navRef = useRef(null);
|
|
11
|
+
const returnFocusRef = useRef(null);
|
|
12
|
+
/**
|
|
13
|
+
* ⚠️ THREE STATES, AND THE THIRD ONE IS WHY THERE IS NO FLASH.
|
|
14
|
+
*
|
|
15
|
+
* `null` is "nobody has chosen yet". The server renders it, no `data-state`
|
|
16
|
+
* reaches the DOM, and the stylesheet decides per mode — closed where the
|
|
17
|
+
* navigation would cover the article, open where it would sit beside it. So
|
|
18
|
+
* the first paint is already right at both shapes, with no JavaScript and
|
|
19
|
+
* nothing to correct afterwards.
|
|
20
|
+
*
|
|
21
|
+
* A boolean would have to be picked before the container's width is known,
|
|
22
|
+
* which is either a flash on a phone or a flash on a desktop.
|
|
23
|
+
*/
|
|
24
|
+
const [choice, setChoice] = useState(null);
|
|
25
|
+
const [mode, setMode] = useState(null);
|
|
26
|
+
const [ready, setReady] = useState(false);
|
|
27
|
+
const open = choice ?? mode === "push";
|
|
28
|
+
const covering = mode === "cover" && open;
|
|
17
29
|
useEffect(() => {
|
|
18
|
-
|
|
30
|
+
const shell = shellRef.current;
|
|
31
|
+
const layout = shell?.parentElement;
|
|
32
|
+
if (shell == null || layout == null) return;
|
|
33
|
+
const read = () => {
|
|
34
|
+
const value = getComputedStyle(shell).getPropertyValue("--wave-docs-sidebar-mode").trim();
|
|
35
|
+
setMode(value === "push" ? "push" : "cover");
|
|
36
|
+
};
|
|
37
|
+
read();
|
|
38
|
+
const frame = requestAnimationFrame(() => {
|
|
39
|
+
setReady(true);
|
|
40
|
+
});
|
|
41
|
+
const observer = new ResizeObserver(read);
|
|
42
|
+
observer.observe(layout);
|
|
43
|
+
return () => {
|
|
44
|
+
cancelAnimationFrame(frame);
|
|
45
|
+
observer.disconnect();
|
|
46
|
+
};
|
|
47
|
+
}, []);
|
|
48
|
+
const close = useCallback(() => {
|
|
49
|
+
setChoice(false);
|
|
50
|
+
}, []);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (covering) setChoice(false);
|
|
19
53
|
}, [pathname]);
|
|
20
54
|
useEffect(() => {
|
|
21
|
-
if (
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (button === null) return;
|
|
27
|
-
if (button.getAttribute("commandfor") !== "wave-docs-nav") return;
|
|
28
|
-
const dialog = ref.current;
|
|
29
|
-
if (dialog === null) return;
|
|
30
|
-
if (button.getAttribute("command") === "close") dialog.close();
|
|
31
|
-
else dialog.showModal();
|
|
55
|
+
if (!covering) return;
|
|
56
|
+
const onKeyDown = (event) => {
|
|
57
|
+
if (event.key !== "Escape") return;
|
|
58
|
+
event.stopPropagation();
|
|
59
|
+
close();
|
|
32
60
|
};
|
|
33
|
-
document.addEventListener("
|
|
61
|
+
document.addEventListener("keydown", onKeyDown);
|
|
34
62
|
return () => {
|
|
35
|
-
document.removeEventListener("
|
|
63
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
36
64
|
};
|
|
37
|
-
}, []);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
65
|
+
}, [covering, close]);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const shell = shellRef.current;
|
|
68
|
+
const layout = shell?.parentElement;
|
|
69
|
+
if (shell == null || layout == null) return;
|
|
70
|
+
const outside = [...layout.children].filter((child) => child !== shell && !child.classList.contains(SCRIM_CLASS));
|
|
71
|
+
if (!covering) {
|
|
72
|
+
for (const element of outside) element.removeAttribute("inert");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
returnFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
76
|
+
for (const element of outside) element.setAttribute("inert", "");
|
|
77
|
+
navRef.current?.focus();
|
|
78
|
+
return () => {
|
|
79
|
+
for (const element of outside) element.removeAttribute("inert");
|
|
80
|
+
returnFocusRef.current?.focus({ preventScroll: true });
|
|
81
|
+
};
|
|
82
|
+
}, [covering]);
|
|
83
|
+
const state = choice === null && mode === null ? void 0 : open;
|
|
84
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
85
|
+
ref: shellRef,
|
|
86
|
+
className: "wave-docs-layout__sidebar",
|
|
87
|
+
...state === void 0 ? {} : { "data-state": state ? "open" : "closed" },
|
|
88
|
+
...ready ? { "data-ready": "" } : {},
|
|
89
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
90
|
+
ref: navRef,
|
|
91
|
+
id: DOCS_NAV_ID,
|
|
92
|
+
className: "wave-docs-layout__sidebar-nav",
|
|
93
|
+
tabIndex: -1,
|
|
94
|
+
children: [children, /* @__PURE__ */ jsx(DocsSidebar, {
|
|
95
|
+
nav,
|
|
96
|
+
pathname,
|
|
97
|
+
label,
|
|
98
|
+
Link,
|
|
99
|
+
...expandGroup === void 0 ? {} : { expandGroup },
|
|
100
|
+
...collapseGroup === void 0 ? {} : { collapseGroup },
|
|
101
|
+
...externalLink === void 0 ? {} : { externalLink }
|
|
102
|
+
})]
|
|
103
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
45
104
|
type: "button",
|
|
46
|
-
className: "wave-docs-
|
|
47
|
-
"aria-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
width: "16",
|
|
54
|
-
height: "16",
|
|
55
|
-
fill: "none",
|
|
56
|
-
stroke: "currentColor",
|
|
57
|
-
strokeWidth: "1.5",
|
|
58
|
-
strokeLinecap: "round",
|
|
59
|
-
children: /* @__PURE__ */ jsx("path", { d: "M4 4l8 8M12 4l-8 8" })
|
|
60
|
-
})
|
|
61
|
-
}), /* @__PURE__ */ jsx(DocsSidebar, {
|
|
62
|
-
nav,
|
|
63
|
-
pathname,
|
|
64
|
-
label,
|
|
65
|
-
Link,
|
|
66
|
-
...expandGroup === void 0 ? {} : { expandGroup },
|
|
67
|
-
...collapseGroup === void 0 ? {} : { collapseGroup },
|
|
68
|
-
...externalLink === void 0 ? {} : { externalLink }
|
|
105
|
+
className: "wave-docs-layout__sidebar-trigger",
|
|
106
|
+
"aria-controls": DOCS_NAV_ID,
|
|
107
|
+
"aria-label": open ? closeLabel : openLabel,
|
|
108
|
+
...state === void 0 ? {} : { "aria-expanded": state },
|
|
109
|
+
onClick: () => {
|
|
110
|
+
setChoice(!open);
|
|
111
|
+
}
|
|
69
112
|
})]
|
|
70
|
-
})
|
|
113
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
114
|
+
className: SCRIM_CLASS,
|
|
115
|
+
"aria-hidden": "true",
|
|
116
|
+
onClick: close
|
|
117
|
+
})] });
|
|
71
118
|
}
|
|
119
|
+
const SCRIM_CLASS = "wave-docs-layout__sidebar-scrim";
|
|
72
120
|
//#endregion
|
|
73
121
|
export { DOCS_NAV_ID, DocsNav };
|
|
@@ -1,34 +1,12 @@
|
|
|
1
1
|
import { DocsLinkComponent } from "./markdown-components.js";
|
|
2
|
-
import { ComponentProps, ComponentType } from "react";
|
|
3
2
|
//#region src/react/next-link.d.ts
|
|
4
3
|
/**
|
|
5
|
-
*
|
|
4
|
+
* Module scope, and never inside a render.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* A fresh component identity for a link remounts every link in the document on
|
|
7
|
+
* every render — which is why `next-search.tsx` and `next-nav.tsx` each build
|
|
8
|
+
* theirs at module scope too, and why this is a constant rather than a factory.
|
|
10
9
|
*/
|
|
11
|
-
|
|
12
|
-
href: string;
|
|
13
|
-
prefetch?: boolean | null;
|
|
14
|
-
}>;
|
|
15
|
-
/**
|
|
16
|
-
* Adapt `next/link` to {@link DocsLinkProps}.
|
|
17
|
-
*
|
|
18
|
-
* `next/link` widens `href` to `string | UrlObject` and `prefetch` to
|
|
19
|
-
* `boolean | 'auto' | null`; the React layer promises neither, because it must
|
|
20
|
-
* also run with a plain `<a>`. One wrapper keeps that mismatch in a single
|
|
21
|
-
* place instead of at every call site.
|
|
22
|
-
*
|
|
23
|
-
* `prefetch` is omitted rather than passed as `undefined`, which is not
|
|
24
|
-
* pedantry: under `exactOptionalPropertyTypes` — which this package compiles
|
|
25
|
-
* with, and which any consumer may turn on — `undefined` is not assignable to
|
|
26
|
-
* `boolean | 'auto' | null`, and `<SearchDialog Link={Link} />` written by
|
|
27
|
-
* hand fails to compile for a reason that reads as our bug.
|
|
28
|
-
*
|
|
29
|
-
* Call it once at module scope, never during a render: a fresh component
|
|
30
|
-
* identity for `a` remounts every link in the document on every render.
|
|
31
|
-
*/
|
|
32
|
-
declare function wrapNextLink(NextLink: NextLinkComponent): DocsLinkComponent;
|
|
10
|
+
declare const DocsLink: DocsLinkComponent;
|
|
33
11
|
//#endregion
|
|
34
|
-
export {
|
|
12
|
+
export { DocsLink };
|
package/dist/react/next-link.js
CHANGED
|
@@ -1,30 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
import { wrapNextLink } from "./link-adapter.js";
|
|
3
|
+
import NextLink from "next/link";
|
|
4
|
+
//#region src/react/next-link.tsx
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
6
|
+
* `next/link`, adapted to this package's {@link DocsLinkProps} and ready to pass.
|
|
5
7
|
*
|
|
6
|
-
* `next/link`
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* ⚠️ IT EXISTS BECAUSE PASSING `next/link` DIRECTLY DOES NOT TYPE-CHECK. Under
|
|
9
|
+
* `exactOptionalPropertyTypes` — which this package compiles with, and which any
|
|
10
|
+
* consumer may turn on — Next's `LinkProps` re-declares `onClick?`,
|
|
11
|
+
* `onMouseEnter?` and `onTouchStart?` *without* `| undefined` while React's
|
|
12
|
+
* anchor props include it, so `<DocsSidebar Link={Link} />` fails to compile
|
|
13
|
+
* over three handlers `next/link` accepts perfectly well at run time. It is a
|
|
14
|
+
* disagreement between two dependencies' declaration files, true of every
|
|
15
|
+
* `next/link` call site in a project with that flag on, and nothing the shape of
|
|
16
|
+
* `DocsLinkProps` can fix without breaking the plain-`<a>` fallback that makes
|
|
17
|
+
* these components host-agnostic.
|
|
10
18
|
*
|
|
11
|
-
* `
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* hand fails to compile for a reason that reads as our bug.
|
|
19
|
+
* `docs.Layout` and `DocsSearch` have always absorbed it internally, so it only
|
|
20
|
+
* ever bit someone composing a shell by hand — who was told to write
|
|
21
|
+
* `Link={Link as DocsLinkComponent}` and wait for this module. This is it; the
|
|
22
|
+
* cast is retired.
|
|
16
23
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
24
|
+
* ```tsx
|
|
25
|
+
* 'use client';
|
|
26
|
+
* import { DocsLink } from '@waveso/docs/react/next-link';
|
|
27
|
+
* import { DocsSidebar } from '@waveso/docs/react/sidebar';
|
|
28
|
+
*
|
|
29
|
+
* <DocsSidebar nav={nav} pathname={pathname} Link={DocsLink} />
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* ## Why `'use client'`
|
|
33
|
+
*
|
|
34
|
+
* Not for a hook — there is none. {@link DocsLink} is a *function*, and a
|
|
35
|
+
* function cannot be handed from a Server Component to a Client one: React
|
|
36
|
+
* serialises those props and refuses. Without the directive this module's export
|
|
37
|
+
* would be a server reference, and passing it to `DocsSidebar` — which is itself
|
|
38
|
+
* `'use client'` — would fail `next build` with "Functions cannot be passed
|
|
39
|
+
* directly to Client Components". The directive makes it a client reference,
|
|
40
|
+
* which crosses fine.
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Module scope, and never inside a render.
|
|
44
|
+
*
|
|
45
|
+
* A fresh component identity for a link remounts every link in the document on
|
|
46
|
+
* every render — which is why `next-search.tsx` and `next-nav.tsx` each build
|
|
47
|
+
* theirs at module scope too, and why this is a constant rather than a factory.
|
|
19
48
|
*/
|
|
20
|
-
|
|
21
|
-
return function DocsNextLink({ href, prefetch, children, ...rest }) {
|
|
22
|
-
return createElement(NextLink, {
|
|
23
|
-
...rest,
|
|
24
|
-
href,
|
|
25
|
-
...prefetch === void 0 ? {} : { prefetch }
|
|
26
|
-
}, children);
|
|
27
|
-
};
|
|
28
|
-
}
|
|
49
|
+
const DocsLink = wrapNextLink(NextLink);
|
|
29
50
|
//#endregion
|
|
30
|
-
export {
|
|
51
|
+
export { DocsLink };
|
package/dist/react/next-nav.d.ts
CHANGED
|
@@ -5,11 +5,15 @@ interface DocsNextNavProps {
|
|
|
5
5
|
nav: DocNavNode[];
|
|
6
6
|
label?: string | undefined;
|
|
7
7
|
closeLabel?: string | undefined;
|
|
8
|
+
/** Accessible name for the trigger while the sidebar is closed. */
|
|
9
|
+
openLabel?: string | undefined;
|
|
10
|
+
/** Rendered above the tree. See `DocsNavProps.children`. */
|
|
11
|
+
children?: ReactNode;
|
|
8
12
|
/** Passed through to the tree. See `DocsSidebarProps.expandGroup`. */
|
|
9
13
|
expandGroup?: string | undefined;
|
|
10
14
|
collapseGroup?: string | undefined;
|
|
11
15
|
externalLink?: string | undefined;
|
|
12
16
|
}
|
|
13
|
-
declare function DocsNextNav({ nav, label, closeLabel, expandGroup, collapseGroup, externalLink }: DocsNextNavProps): ReactNode;
|
|
17
|
+
declare function DocsNextNav({ nav, label, closeLabel, openLabel, children, expandGroup, collapseGroup, externalLink }: DocsNextNavProps): ReactNode;
|
|
14
18
|
//#endregion
|
|
15
19
|
export { DocsNextNav, DocsNextNavProps };
|
package/dist/react/next-nav.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { wrapNextLink } from "./
|
|
2
|
+
import { wrapNextLink } from "./link-adapter.js";
|
|
3
3
|
import { DocsNav } from "./nav.js";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
import NextLink from "next/link";
|
|
@@ -19,16 +19,18 @@ import { usePathname } from "next/navigation";
|
|
|
19
19
|
*/
|
|
20
20
|
/** Module scope: a fresh identity here remounts every nav link on every render. */
|
|
21
21
|
const Link = wrapNextLink(NextLink);
|
|
22
|
-
function DocsNextNav({ nav, label, closeLabel, expandGroup, collapseGroup, externalLink }) {
|
|
22
|
+
function DocsNextNav({ nav, label, closeLabel, openLabel, children, expandGroup, collapseGroup, externalLink }) {
|
|
23
23
|
return /* @__PURE__ */ jsx(DocsNav, {
|
|
24
24
|
nav,
|
|
25
25
|
pathname: usePathname(),
|
|
26
26
|
Link,
|
|
27
27
|
...label === void 0 ? {} : { label },
|
|
28
28
|
...closeLabel === void 0 ? {} : { closeLabel },
|
|
29
|
+
...openLabel === void 0 ? {} : { openLabel },
|
|
29
30
|
...expandGroup === void 0 ? {} : { expandGroup },
|
|
30
31
|
...collapseGroup === void 0 ? {} : { collapseGroup },
|
|
31
|
-
...externalLink === void 0 ? {} : { externalLink }
|
|
32
|
+
...externalLink === void 0 ? {} : { externalLink },
|
|
33
|
+
children
|
|
32
34
|
});
|
|
33
35
|
}
|
|
34
36
|
//#endregion
|
|
@@ -44,9 +44,15 @@
|
|
|
44
44
|
interface DocsLabels {
|
|
45
45
|
/** The navigation landmark's accessible name. Default `'Documentation'`. */
|
|
46
46
|
nav?: string | undefined;
|
|
47
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* The sidebar's trigger, named for what pressing it does next. One button
|
|
49
|
+
* carries both strings: `openNav` while the sidebar is closed, `closeNav`
|
|
50
|
+
* while it is open.
|
|
51
|
+
*
|
|
52
|
+
* Default `'Open navigation'`.
|
|
53
|
+
*/
|
|
48
54
|
openNav?: string | undefined;
|
|
49
|
-
/** The button
|
|
55
|
+
/** The same button, while the sidebar is open. Default `'Close navigation'`. */
|
|
50
56
|
closeNav?: string | undefined;
|
|
51
57
|
/** The skip link's visible text. Default `'Skip to content'`. */
|
|
52
58
|
skipToContent?: string | undefined;
|
package/dist/react/toc.d.ts
CHANGED
|
@@ -8,9 +8,19 @@ interface DocsTocProps {
|
|
|
8
8
|
label?: string | undefined;
|
|
9
9
|
/**
|
|
10
10
|
* Region of the viewport that counts as "current", as an
|
|
11
|
-
* `IntersectionObserver` root margin. The default
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* `IntersectionObserver` root margin. The default ignores the bottom 60% of
|
|
12
|
+
* the screen, so the active entry tracks what you are reading rather than
|
|
13
|
+
* what has scrolled into view.
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ IT USED TO RESERVE 80px FOR A STICKY HEADER, AND TWO THINGS WERE WRONG
|
|
16
|
+
* WITH THAT. There is no header — above 80rem, where this component
|
|
17
|
+
* is rendered at all, nothing of ours overlays the content. And 80px never
|
|
18
|
+
* matched the header anyway: the token was `3.5rem`, which is 56px, so the
|
|
19
|
+
* figure was never derived from the thing it claimed to reserve for.
|
|
20
|
+
*
|
|
21
|
+
* This component is exported standalone, so its default cannot assume our
|
|
22
|
+
* shell. It assumes nothing overlays the content; a host whose own chrome
|
|
23
|
+
* does sets this prop, which is what the prop is for.
|
|
14
24
|
*
|
|
15
25
|
* Only `px` and `%` are legal here — `IntersectionObserver` throws on any
|
|
16
26
|
* other unit, `rem` included.
|
package/dist/react/toc.js
CHANGED
|
@@ -3,7 +3,7 @@ import { DOCS_CONTENT_ID } from "../docs-content-id.js";
|
|
|
3
3
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/react/toc.tsx
|
|
6
|
-
const DEFAULT_ROOT_MARGIN = "
|
|
6
|
+
const DEFAULT_ROOT_MARGIN = "0px 0px -60% 0px";
|
|
7
7
|
/**
|
|
8
8
|
* How many frames to keep looking for headings that are not in the document
|
|
9
9
|
* yet. ~1s at 60Hz, which covers a `<Suspense>` boundary resolving or a
|
|
@@ -86,15 +86,22 @@ function DocsToc({ entries, label = "On this page", rootMargin = DEFAULT_ROOT_MA
|
|
|
86
86
|
return /* @__PURE__ */ jsxs("nav", {
|
|
87
87
|
"aria-label": label,
|
|
88
88
|
className: ["wave-docs-toc", className].filter(Boolean).join(" "),
|
|
89
|
-
children: [
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
children: [
|
|
90
|
+
/* @__PURE__ */ jsx("p", {
|
|
91
|
+
className: "wave-docs-toc__title",
|
|
92
|
+
children: label
|
|
93
|
+
}),
|
|
94
|
+
/* @__PURE__ */ jsx(TocList, {
|
|
95
|
+
entries,
|
|
96
|
+
activeId,
|
|
97
|
+
onSelect: setActiveId
|
|
98
|
+
}),
|
|
99
|
+
/* @__PURE__ */ jsx("a", {
|
|
100
|
+
className: "wave-docs-toc__top",
|
|
101
|
+
href: `#${DOCS_CONTENT_ID}`,
|
|
102
|
+
children: topLabel
|
|
103
|
+
})
|
|
104
|
+
]
|
|
98
105
|
});
|
|
99
106
|
}
|
|
100
107
|
function TocList({ entries, activeId, onSelect }) {
|
package/dist/render.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { PluggableList } from "unified";
|
|
|
12
12
|
* same object to the source walk and to the renderer. Nothing here re-applies
|
|
13
13
|
* defaults, so the two cannot drift.
|
|
14
14
|
*/
|
|
15
|
-
type DocsRendererConfig = Pick<ResolvedDocsConfig, 'basePath' | '
|
|
15
|
+
type DocsRendererConfig = Pick<ResolvedDocsConfig, 'basePath' | 'onBrokenLinks' | 'onBrokenAnchors' | 'externalRoutes'>;
|
|
16
16
|
interface DocsRendererOptions {
|
|
17
17
|
config: DocsRendererConfig;
|
|
18
18
|
/**
|