@waveso/docs 0.7.1 → 0.9.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 +226 -0
- package/README.md +54 -5
- package/dist/frontmatter.d.ts +1 -0
- package/dist/frontmatter.js +1 -0
- package/dist/meta.d.ts +2 -0
- package/dist/meta.js +5 -2
- package/dist/next.d.ts +14 -0
- package/dist/next.js +3 -2
- package/dist/react/layout.d.ts +16 -1
- package/dist/react/layout.js +2 -1
- package/dist/react/nav.d.ts +4 -1
- package/dist/react/nav.js +3 -2
- package/dist/react/next-nav.d.ts +4 -1
- package/dist/react/next-nav.js +2 -1
- package/dist/react/search-dialog.d.ts +24 -1
- package/dist/react/search-dialog.js +104 -16
- package/dist/react/sidebar.d.ts +51 -3
- package/dist/react/sidebar.js +126 -19
- package/dist/source.js +9 -4
- package/dist/styles.css +297 -13
- package/dist/types.d.ts +27 -0
- package/package.json +1 -1
|
@@ -26,7 +26,7 @@ const FOCUSABLE_SELECTOR = [
|
|
|
26
26
|
* portalled to `document.body`, so a navbar's stacking context cannot trap
|
|
27
27
|
* it behind the page.
|
|
28
28
|
*/
|
|
29
|
-
function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", placeholder = "Search documentation", dialogLabel = "Search documentation", pageSize = 20, minQueryLength = 2, debounceMs = 120, className, miniSearchOptions, hintLabel, shortQueryLabel, loadingLabel, errorLabel, emptyLabel, resultCountLabels, locale }) {
|
|
29
|
+
function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", placeholder = "Search documentation", dialogLabel = "Search documentation", pageSize = 20, minQueryLength = 2, debounceMs = 120, className, miniSearchOptions, hintLabel, shortQueryLabel, loadingLabel, errorLabel, emptyLabel, selectLabel = "Select", openLabel = "Open", closeLabel = "Close", resultCountLabels, locale }) {
|
|
30
30
|
const [isOpen, setIsOpen] = useState(false);
|
|
31
31
|
const [query, setQuery] = useState("");
|
|
32
32
|
const [hits, setHits] = useState([]);
|
|
@@ -53,7 +53,18 @@ function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", place
|
|
|
53
53
|
const movedByKeyboard = useRef(false);
|
|
54
54
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
55
55
|
const [status, setStatus] = useState("idle");
|
|
56
|
-
|
|
56
|
+
/**
|
|
57
|
+
* The trigger's shortcut, split rather than held as one string.
|
|
58
|
+
*
|
|
59
|
+
* ⚠️ THE `⌘` NEEDS ITS OWN `font-size` AND CSS CANNOT SELECT A CHARACTER.
|
|
60
|
+
* Measured in the shipped mono stack at 12px, its ink is 6.39px tall against
|
|
61
|
+
* the `K`'s 8.75px — so as one string the symbol sits visibly short of the
|
|
62
|
+
* letter beside it. Two nodes is the only way to scale one and not the other.
|
|
63
|
+
*
|
|
64
|
+
* `null` until the effect below resolves the platform: reading it during
|
|
65
|
+
* render would disagree with the server's markup and break hydration.
|
|
66
|
+
*/
|
|
67
|
+
const [shortcut, setShortcut] = useState(null);
|
|
57
68
|
const dialogRef = useRef(null);
|
|
58
69
|
const triggerRef = useRef(null);
|
|
59
70
|
const inputRef = useRef(null);
|
|
@@ -126,7 +137,10 @@ function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", place
|
|
|
126
137
|
}, [isOpen, closeDialog]);
|
|
127
138
|
useEffect(() => {
|
|
128
139
|
const isApple = /mac|iphone|ipad|ipod/i.test(navigator.userAgent);
|
|
129
|
-
|
|
140
|
+
setShortcut({
|
|
141
|
+
modifier: isApple ? "⌘" : "Ctrl",
|
|
142
|
+
isSymbol: isApple
|
|
143
|
+
});
|
|
130
144
|
}, []);
|
|
131
145
|
useEffect(() => {
|
|
132
146
|
if (isOpen) {
|
|
@@ -267,13 +281,21 @@ function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", place
|
|
|
267
281
|
onClick: openDialog,
|
|
268
282
|
onPointerEnter: warmIndex,
|
|
269
283
|
onFocus: warmIndex,
|
|
270
|
-
children: [
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
284
|
+
children: [
|
|
285
|
+
/* @__PURE__ */ jsx(SearchGlyph, {}),
|
|
286
|
+
/* @__PURE__ */ jsx("span", {
|
|
287
|
+
className: "wave-docs-search-trigger-label",
|
|
288
|
+
children: triggerLabel
|
|
289
|
+
}),
|
|
290
|
+
shortcut === null ? null : /* @__PURE__ */ jsxs("kbd", {
|
|
291
|
+
className: "wave-docs-search-trigger-kbd",
|
|
292
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
293
|
+
className: "wave-docs-search-trigger-mod",
|
|
294
|
+
"data-symbol": shortcut.isSymbol ? "" : void 0,
|
|
295
|
+
children: shortcut.modifier
|
|
296
|
+
}), shortcut.isSymbol ? "K" : " K"]
|
|
297
|
+
})
|
|
298
|
+
]
|
|
277
299
|
}), isOpen ? createPortal(/* @__PURE__ */ jsx("div", {
|
|
278
300
|
className: "wave-docs-search-backdrop",
|
|
279
301
|
onMouseDown: (event) => {
|
|
@@ -288,7 +310,7 @@ function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", place
|
|
|
288
310
|
children: [
|
|
289
311
|
/* @__PURE__ */ jsxs("div", {
|
|
290
312
|
className: "wave-docs-search-input-row",
|
|
291
|
-
children: [/* @__PURE__ */ jsx("input", {
|
|
313
|
+
children: [/* @__PURE__ */ jsx(SearchGlyph, {}), /* @__PURE__ */ jsx("input", {
|
|
292
314
|
ref: inputRef,
|
|
293
315
|
className: "wave-docs-search-input",
|
|
294
316
|
type: "text",
|
|
@@ -305,11 +327,6 @@ function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", place
|
|
|
305
327
|
autoComplete: "off",
|
|
306
328
|
autoCorrect: "off",
|
|
307
329
|
spellCheck: false
|
|
308
|
-
}), /* @__PURE__ */ jsx("button", {
|
|
309
|
-
type: "button",
|
|
310
|
-
className: "wave-docs-search-close",
|
|
311
|
-
onClick: closeDialog,
|
|
312
|
-
children: "Close"
|
|
313
330
|
})]
|
|
314
331
|
}),
|
|
315
332
|
/* @__PURE__ */ jsx("div", {
|
|
@@ -346,11 +363,82 @@ function SearchDialog({ indexUrl, navigate, Link, triggerLabel = "Search", place
|
|
|
346
363
|
},
|
|
347
364
|
resultCountLabels,
|
|
348
365
|
locale
|
|
366
|
+
}),
|
|
367
|
+
/* @__PURE__ */ jsxs("div", {
|
|
368
|
+
className: "wave-docs-search-footer",
|
|
369
|
+
children: [
|
|
370
|
+
/* @__PURE__ */ jsxs("span", {
|
|
371
|
+
className: "wave-docs-search-hint",
|
|
372
|
+
"aria-hidden": "true",
|
|
373
|
+
children: [
|
|
374
|
+
/* @__PURE__ */ jsx("kbd", {
|
|
375
|
+
className: "wave-docs-search-kbd",
|
|
376
|
+
children: "↑"
|
|
377
|
+
}),
|
|
378
|
+
/* @__PURE__ */ jsx("kbd", {
|
|
379
|
+
className: "wave-docs-search-kbd",
|
|
380
|
+
children: "↓"
|
|
381
|
+
}),
|
|
382
|
+
selectLabel
|
|
383
|
+
]
|
|
384
|
+
}),
|
|
385
|
+
/* @__PURE__ */ jsxs("span", {
|
|
386
|
+
className: "wave-docs-search-hint",
|
|
387
|
+
"aria-hidden": "true",
|
|
388
|
+
children: [/* @__PURE__ */ jsx("kbd", {
|
|
389
|
+
className: "wave-docs-search-kbd",
|
|
390
|
+
children: "↵"
|
|
391
|
+
}), openLabel]
|
|
392
|
+
}),
|
|
393
|
+
/* @__PURE__ */ jsxs("button", {
|
|
394
|
+
type: "button",
|
|
395
|
+
className: "wave-docs-search-close",
|
|
396
|
+
"aria-label": closeLabel,
|
|
397
|
+
"aria-keyshortcuts": "Escape",
|
|
398
|
+
onClick: closeDialog,
|
|
399
|
+
children: [/* @__PURE__ */ jsx("kbd", {
|
|
400
|
+
className: "wave-docs-search-kbd",
|
|
401
|
+
children: "Esc"
|
|
402
|
+
}), /* @__PURE__ */ jsx("span", { children: closeLabel })]
|
|
403
|
+
})
|
|
404
|
+
]
|
|
349
405
|
})
|
|
350
406
|
]
|
|
351
407
|
})
|
|
352
408
|
}), document.body) : null] });
|
|
353
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* The magnifier, on the trigger and in the dialog's input row.
|
|
412
|
+
*
|
|
413
|
+
* Decorative in both places: the trigger carries its name in `aria-label` and
|
|
414
|
+
* the input carries its own, so this glyph would only ever repeat a word that
|
|
415
|
+
* is already there — announced as "search Search" and, on some engines, as the
|
|
416
|
+
* name of the character.
|
|
417
|
+
*
|
|
418
|
+
* Inline SVG rather than a `::before` glyph, matching every other icon in this
|
|
419
|
+
* package: generated content is announced by some screen-reader and browser
|
|
420
|
+
* pairs, which is the one thing `aria-hidden` cannot take back.
|
|
421
|
+
*/
|
|
422
|
+
function SearchGlyph() {
|
|
423
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
424
|
+
className: "wave-docs-search-glyph",
|
|
425
|
+
"aria-hidden": "true",
|
|
426
|
+
focusable: "false",
|
|
427
|
+
viewBox: "0 0 24 24",
|
|
428
|
+
width: "16",
|
|
429
|
+
height: "16",
|
|
430
|
+
fill: "none",
|
|
431
|
+
stroke: "currentColor",
|
|
432
|
+
strokeWidth: "2",
|
|
433
|
+
strokeLinecap: "round",
|
|
434
|
+
strokeLinejoin: "round",
|
|
435
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
436
|
+
cx: "11",
|
|
437
|
+
cy: "11",
|
|
438
|
+
r: "7"
|
|
439
|
+
}), /* @__PURE__ */ jsx("path", { d: "m20 20-3.5-3.5" })]
|
|
440
|
+
});
|
|
441
|
+
}
|
|
354
442
|
/** One result row: a real link, so middle-click and "open in new tab" work. */
|
|
355
443
|
function SearchResultOption({ hit, id, isActive, setSize, posInSet, onActivate, onSelect, Link }) {
|
|
356
444
|
function handleClick(event) {
|
package/dist/react/sidebar.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { DocNavNode } from "../types.js";
|
|
2
2
|
import { DocsLinkComponent } from "./markdown-components.js";
|
|
3
|
-
import { ReactNode } from "react";
|
|
3
|
+
import { ComponentType, ReactNode } from "react";
|
|
4
4
|
//#region src/react/sidebar.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* `icon` names to components, for `DocsSidebar`'s `icons` prop.
|
|
7
|
+
*
|
|
8
|
+
* The component is rendered with no props: an icon that needs configuration is
|
|
9
|
+
* a closure the host writes, not a contract this package invents. It should
|
|
10
|
+
* draw at the size it is given — the column is `1rem` square and the built-in
|
|
11
|
+
* markers use `currentColor`, so anything following those two conventions sits
|
|
12
|
+
* in line with them.
|
|
13
|
+
*/
|
|
14
|
+
type DocsIconMap = Record<string, ComponentType>;
|
|
5
15
|
interface DocsSidebarProps {
|
|
6
16
|
/** The tree from `@waveso/docs/source`. */
|
|
7
17
|
nav: DocNavNode[];
|
|
@@ -32,6 +42,44 @@ interface DocsSidebarProps {
|
|
|
32
42
|
* The separating space is markup, so this is the sentence and nothing else.
|
|
33
43
|
*/
|
|
34
44
|
externalLink?: string | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* The marker column. `true` (default), `false`, or your own icons.
|
|
47
|
+
*
|
|
48
|
+
* Weight and a chevron were the only things separating a category from a
|
|
49
|
+
* page, and in a tree where the two interleave — a `Reference` group sitting
|
|
50
|
+
* directly above an `Internals` page — that is not enough to scan. A leading
|
|
51
|
+
* glyph gives the column a shape you read before you read any words.
|
|
52
|
+
*
|
|
53
|
+
* `false` renders no markers at all, for a host whose own navigation has a
|
|
54
|
+
* different vocabulary and does not want a second one. The external-link mark
|
|
55
|
+
* returns to the trailing edge there — turning off a decorative column is not
|
|
56
|
+
* consent to drop a warning.
|
|
57
|
+
*
|
|
58
|
+
* A **map** replaces the defaults with your components, keyed by the `icon`
|
|
59
|
+
* name authored in frontmatter or `meta.json`:
|
|
60
|
+
*
|
|
61
|
+
* ```tsx
|
|
62
|
+
* import { Book, Rocket } from 'lucide-react';
|
|
63
|
+
*
|
|
64
|
+
* <DocsSidebar nav={nav} pathname={pathname}
|
|
65
|
+
* icons={{ book: Book, rocket: Rocket }} />
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* ```yaml
|
|
69
|
+
* # content/reference/index.md
|
|
70
|
+
* icon: book
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* ⚠️ A NAME THE HOST RESOLVES, NEVER ART THIS PACKAGE SHIPS. Content is
|
|
74
|
+
* authored in YAML and JSON and cannot carry a React element, and a docs
|
|
75
|
+
* package mounted inside someone else's application must not put its
|
|
76
|
+
* iconography beside theirs. Three markers ship; everything else is yours.
|
|
77
|
+
*
|
|
78
|
+
* A name with no entry in the map falls back to the built-in marker for that
|
|
79
|
+
* node's type. A typo in one file leaves a folder where a book should be —
|
|
80
|
+
* not a hole in the column.
|
|
81
|
+
*/
|
|
82
|
+
icons?: boolean | DocsIconMap | undefined;
|
|
35
83
|
className?: string | undefined;
|
|
36
84
|
}
|
|
37
85
|
/**
|
|
@@ -76,6 +124,6 @@ interface DocsSidebarProps {
|
|
|
76
124
|
* nothing prefetches locally whatever this says. Do not "fix" it back because
|
|
77
125
|
* the network tab looks the same.
|
|
78
126
|
*/
|
|
79
|
-
declare function DocsSidebar({ nav, pathname, Link, label, expandGroup, collapseGroup, externalLink, className }: DocsSidebarProps): ReactNode;
|
|
127
|
+
declare function DocsSidebar({ nav, pathname, Link, label, expandGroup, collapseGroup, externalLink, icons, className }: DocsSidebarProps): ReactNode;
|
|
80
128
|
//#endregion
|
|
81
|
-
export { DocsSidebar, DocsSidebarProps };
|
|
129
|
+
export { DocsIconMap, DocsSidebar, DocsSidebarProps };
|
package/dist/react/sidebar.js
CHANGED
|
@@ -15,6 +15,24 @@ function normalizeHref(href) {
|
|
|
15
15
|
function isActiveHref(pathname, href) {
|
|
16
16
|
return normalizeHref(pathname) === normalizeHref(href);
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* `{ [key]: true }` for every group the active page lives inside, keyed the way
|
|
20
|
+
* the tree renders — `${prefix}-${index}`, index counted over *all* siblings
|
|
21
|
+
* because that is what `NavList` does.
|
|
22
|
+
*
|
|
23
|
+
* Shaped as the state itself rather than as a list of keys so both callers can
|
|
24
|
+
* spread it: it seeds the map on mount and merges into it on every navigation,
|
|
25
|
+
* and neither needs a loop of its own.
|
|
26
|
+
*/
|
|
27
|
+
function openAlong(nodes, pathname, prefix, into = {}) {
|
|
28
|
+
nodes.forEach((node, index) => {
|
|
29
|
+
if (node.type !== "group" || !containsActive(node, pathname)) return;
|
|
30
|
+
const key = `${prefix}-${index}`;
|
|
31
|
+
into[key] = true;
|
|
32
|
+
openAlong(node.children, pathname, key, into);
|
|
33
|
+
});
|
|
34
|
+
return into;
|
|
35
|
+
}
|
|
18
36
|
/** Whether the active page lives anywhere under this node. */
|
|
19
37
|
function containsActive(node, pathname) {
|
|
20
38
|
switch (node.type) {
|
|
@@ -66,18 +84,22 @@ function containsActive(node, pathname) {
|
|
|
66
84
|
* nothing prefetches locally whatever this says. Do not "fix" it back because
|
|
67
85
|
* the network tab looks the same.
|
|
68
86
|
*/
|
|
69
|
-
function DocsSidebar({ nav, pathname, Link, label = "Docs", expandGroup, collapseGroup, externalLink, className }) {
|
|
87
|
+
function DocsSidebar({ nav, pathname, Link, label = "Docs", expandGroup, collapseGroup, externalLink, icons = true, className }) {
|
|
88
|
+
const iconMap = icons === false ? false : icons === true ? {} : icons;
|
|
70
89
|
const text = {
|
|
71
90
|
expandGroup: expandGroup ?? DEFAULT_SIDEBAR_LABELS.expandGroup,
|
|
72
91
|
collapseGroup: collapseGroup ?? DEFAULT_SIDEBAR_LABELS.collapseGroup,
|
|
73
92
|
externalLink: externalLink ?? DEFAULT_SIDEBAR_LABELS.externalLink
|
|
74
93
|
};
|
|
75
94
|
const baseId = useId();
|
|
76
|
-
const [toggled, setToggled] = useState(
|
|
95
|
+
const [toggled, setToggled] = useState(() => openAlong(nav, pathname, baseId));
|
|
77
96
|
const lastPathname = useRef(pathname);
|
|
78
97
|
if (lastPathname.current !== pathname) {
|
|
79
98
|
lastPathname.current = pathname;
|
|
80
|
-
setToggled({
|
|
99
|
+
setToggled((previous) => ({
|
|
100
|
+
...previous,
|
|
101
|
+
...openAlong(nav, pathname, baseId)
|
|
102
|
+
}));
|
|
81
103
|
}
|
|
82
104
|
const handleToggle = (key, isOpen) => {
|
|
83
105
|
setToggled((previous) => ({
|
|
@@ -108,6 +130,7 @@ function DocsSidebar({ nav, pathname, Link, label = "Docs", expandGroup, collaps
|
|
|
108
130
|
nodes: nav,
|
|
109
131
|
depth: 0,
|
|
110
132
|
keyPrefix: baseId,
|
|
133
|
+
icons: iconMap,
|
|
111
134
|
pathname,
|
|
112
135
|
Link,
|
|
113
136
|
toggled,
|
|
@@ -157,7 +180,7 @@ function scrollableAncestor(element) {
|
|
|
157
180
|
}
|
|
158
181
|
return null;
|
|
159
182
|
}
|
|
160
|
-
function NavList({ nodes, depth, keyPrefix, pathname, Link, toggled, onToggle, text, id }) {
|
|
183
|
+
function NavList({ nodes, depth, keyPrefix, icons, pathname, Link, toggled, onToggle, text, id }) {
|
|
161
184
|
const holdsActive = nodes.some((node) => (node.type === "page" || node.type === "link" && !node.external) && isActiveHref(pathname, node.href));
|
|
162
185
|
return /* @__PURE__ */ jsx("ul", {
|
|
163
186
|
id,
|
|
@@ -175,31 +198,47 @@ function NavList({ nodes, depth, keyPrefix, pathname, Link, toggled, onToggle, t
|
|
|
175
198
|
}, key);
|
|
176
199
|
case "link": return /* @__PURE__ */ jsx("li", {
|
|
177
200
|
className: "wave-docs-sidebar__item",
|
|
178
|
-
children: /* @__PURE__ */
|
|
201
|
+
children: /* @__PURE__ */ jsxs(NavLink, {
|
|
179
202
|
href: node.href,
|
|
180
203
|
isExternal: node.external,
|
|
181
204
|
isActive: !node.external && isActiveHref(pathname, node.href),
|
|
182
205
|
isNearby: holdsActive,
|
|
183
206
|
Link,
|
|
184
207
|
externalLink: text.externalLink,
|
|
185
|
-
|
|
208
|
+
icons,
|
|
209
|
+
children: [icons === false ? null : /* @__PURE__ */ jsx(NavIcon, {
|
|
210
|
+
type: "external",
|
|
211
|
+
name: node.icon,
|
|
212
|
+
icons
|
|
213
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
214
|
+
className: "wave-docs-sidebar__label",
|
|
215
|
+
children: node.title
|
|
216
|
+
})]
|
|
186
217
|
})
|
|
187
218
|
}, key);
|
|
188
219
|
case "page": return /* @__PURE__ */ jsx("li", {
|
|
189
220
|
className: "wave-docs-sidebar__item",
|
|
190
|
-
children: /* @__PURE__ */
|
|
221
|
+
children: /* @__PURE__ */ jsxs(NavLink, {
|
|
191
222
|
href: node.href,
|
|
192
223
|
isExternal: false,
|
|
193
224
|
isActive: isActiveHref(pathname, node.href),
|
|
194
225
|
isNearby: holdsActive,
|
|
195
226
|
Link,
|
|
196
|
-
children:
|
|
227
|
+
children: [icons === false ? null : /* @__PURE__ */ jsx(NavIcon, {
|
|
228
|
+
type: "file",
|
|
229
|
+
name: node.icon,
|
|
230
|
+
icons
|
|
231
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
232
|
+
className: "wave-docs-sidebar__label",
|
|
233
|
+
children: node.title
|
|
234
|
+
})]
|
|
197
235
|
})
|
|
198
236
|
}, key);
|
|
199
237
|
case "group": return /* @__PURE__ */ jsx(NavGroup, {
|
|
200
238
|
node,
|
|
201
239
|
itemKey: key,
|
|
202
240
|
depth,
|
|
241
|
+
icons,
|
|
203
242
|
pathname,
|
|
204
243
|
Link,
|
|
205
244
|
toggled,
|
|
@@ -211,7 +250,7 @@ function NavList({ nodes, depth, keyPrefix, pathname, Link, toggled, onToggle, t
|
|
|
211
250
|
})
|
|
212
251
|
});
|
|
213
252
|
}
|
|
214
|
-
function NavGroup({ node, itemKey, depth, pathname, Link, toggled, onToggle, text }) {
|
|
253
|
+
function NavGroup({ node, itemKey, depth, icons, pathname, Link, toggled, onToggle, text }) {
|
|
215
254
|
const listId = `${itemKey}-list`;
|
|
216
255
|
const hasActive = containsActive(node, pathname);
|
|
217
256
|
const isOpen = toggled[itemKey] ?? hasActive;
|
|
@@ -227,17 +266,32 @@ function NavGroup({ node, itemKey, depth, pathname, Link, toggled, onToggle, tex
|
|
|
227
266
|
"aria-expanded": isOpen,
|
|
228
267
|
"aria-controls": isOpen ? listId : void 0,
|
|
229
268
|
onClick: () => onToggle(itemKey, !isOpen),
|
|
230
|
-
children: [
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
269
|
+
children: [
|
|
270
|
+
icons === false ? null : /* @__PURE__ */ jsx(NavIcon, {
|
|
271
|
+
type: "folder",
|
|
272
|
+
name: node.icon,
|
|
273
|
+
icons
|
|
274
|
+
}),
|
|
275
|
+
/* @__PURE__ */ jsx("span", {
|
|
276
|
+
className: "wave-docs-sidebar__group-title",
|
|
277
|
+
children: node.title
|
|
278
|
+
}),
|
|
279
|
+
/* @__PURE__ */ jsx(Chevron, { isOpen })
|
|
280
|
+
]
|
|
281
|
+
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(NavLink, {
|
|
235
282
|
href: node.href,
|
|
236
283
|
isExternal: false,
|
|
237
284
|
isActive: isGroupActive,
|
|
238
285
|
isNearby: hasActive,
|
|
239
286
|
Link,
|
|
240
|
-
children:
|
|
287
|
+
children: [icons === false ? null : /* @__PURE__ */ jsx(NavIcon, {
|
|
288
|
+
type: "folder",
|
|
289
|
+
name: node.icon,
|
|
290
|
+
icons
|
|
291
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
292
|
+
className: "wave-docs-sidebar__label",
|
|
293
|
+
children: node.title
|
|
294
|
+
})]
|
|
241
295
|
}), /* @__PURE__ */ jsx("button", {
|
|
242
296
|
type: "button",
|
|
243
297
|
className: "wave-docs-sidebar__group-toggle",
|
|
@@ -252,6 +306,7 @@ function NavGroup({ node, itemKey, depth, pathname, Link, toggled, onToggle, tex
|
|
|
252
306
|
nodes: node.children,
|
|
253
307
|
depth: depth + 1,
|
|
254
308
|
keyPrefix: itemKey,
|
|
309
|
+
icons,
|
|
255
310
|
pathname,
|
|
256
311
|
Link,
|
|
257
312
|
toggled,
|
|
@@ -260,7 +315,7 @@ function NavGroup({ node, itemKey, depth, pathname, Link, toggled, onToggle, tex
|
|
|
260
315
|
}) : null]
|
|
261
316
|
});
|
|
262
317
|
}
|
|
263
|
-
function NavLink({ href, isExternal, isActive, isNearby = false, Link, externalLink = DEFAULT_SIDEBAR_LABELS.externalLink, children }) {
|
|
318
|
+
function NavLink({ href, isExternal, isActive, isNearby = false, Link, externalLink = DEFAULT_SIDEBAR_LABELS.externalLink, icons = {}, children }) {
|
|
264
319
|
const className = "wave-docs-sidebar__link";
|
|
265
320
|
if (isExternal) return /* @__PURE__ */ jsxs("a", {
|
|
266
321
|
className,
|
|
@@ -269,7 +324,7 @@ function NavLink({ href, isExternal, isActive, isNearby = false, Link, externalL
|
|
|
269
324
|
rel: "noopener noreferrer",
|
|
270
325
|
children: [
|
|
271
326
|
children,
|
|
272
|
-
/* @__PURE__ */ jsx("svg", {
|
|
327
|
+
icons === false ? /* @__PURE__ */ jsx("svg", {
|
|
273
328
|
className: "wave-docs-sidebar__external",
|
|
274
329
|
"aria-hidden": "true",
|
|
275
330
|
focusable: "false",
|
|
@@ -281,8 +336,8 @@ function NavLink({ href, isExternal, isActive, isNearby = false, Link, externalL
|
|
|
281
336
|
strokeWidth: "2",
|
|
282
337
|
strokeLinecap: "round",
|
|
283
338
|
strokeLinejoin: "round",
|
|
284
|
-
children: /* @__PURE__ */ jsx("path", { d:
|
|
285
|
-
}),
|
|
339
|
+
children: /* @__PURE__ */ jsx("path", { d: NAV_ICON_PATHS.external[0] })
|
|
340
|
+
}) : null,
|
|
286
341
|
/* @__PURE__ */ jsxs("span", {
|
|
287
342
|
className: "wave-docs-sr-only",
|
|
288
343
|
children: [" ", externalLink]
|
|
@@ -303,6 +358,58 @@ function NavLink({ href, isExternal, isActive, isNearby = false, Link, externalL
|
|
|
303
358
|
children
|
|
304
359
|
});
|
|
305
360
|
}
|
|
361
|
+
/**
|
|
362
|
+
* The glyph at the head of a row: a folder for a group, a page for a page.
|
|
363
|
+
*
|
|
364
|
+
* Weight and a chevron were the only things telling a category from a page, and
|
|
365
|
+
* where the two interleave — a `Reference` group directly above an `Internals`
|
|
366
|
+
* page — that is not enough to scan a column of twenty. A silhouette is read
|
|
367
|
+
* before any word is.
|
|
368
|
+
*
|
|
369
|
+
* An external link takes the third glyph, in the same leading slot. It used to
|
|
370
|
+
* carry that mark at the *far* end of its row, which cost twice: the leading
|
|
371
|
+
* slot then had to be an empty box to keep the column from going ragged, and
|
|
372
|
+
* the trailing edge held two unrelated meanings — "opens elsewhere" on one row,
|
|
373
|
+
* "expands" on the next. Leading is what a row *is*; trailing is what it
|
|
374
|
+
* *does*. With the mark moved, the only thing at the far end of any row is a
|
|
375
|
+
* chevron, which is what makes a group legible from across the column — and
|
|
376
|
+
* leaves that edge free for a status dot or an overflow control later.
|
|
377
|
+
*
|
|
378
|
+
* ⚠️ THE VISUAL MARK MOVED AND THE ANNOUNCED ONE DID NOT. The sr-only "(opens
|
|
379
|
+
* in a new tab)" stays after the link text, so the name is still read as
|
|
380
|
+
* "GitHub, opens in a new tab" rather than the other way round.
|
|
381
|
+
*
|
|
382
|
+
* Inline SVG, matching `Chevron` and the external mark rather than a font or a
|
|
383
|
+
* dependency — the package ships no icon set, and these two are as generic as
|
|
384
|
+
* the chevron beside them.
|
|
385
|
+
*/
|
|
386
|
+
const NAV_ICON_PATHS = {
|
|
387
|
+
folder: ["M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"],
|
|
388
|
+
file: ["M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", "M14 2v4a2 2 0 0 0 2 2h4"],
|
|
389
|
+
external: ["M14 4h6v6M20 4l-8 8M18 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5"]
|
|
390
|
+
};
|
|
391
|
+
function NavIcon({ type, name, icons }) {
|
|
392
|
+
const Custom = name === void 0 ? void 0 : icons[name];
|
|
393
|
+
if (Custom !== void 0) return /* @__PURE__ */ jsx("span", {
|
|
394
|
+
className: "wave-docs-sidebar__icon",
|
|
395
|
+
"aria-hidden": "true",
|
|
396
|
+
children: /* @__PURE__ */ jsx(Custom, {})
|
|
397
|
+
});
|
|
398
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
399
|
+
className: "wave-docs-sidebar__icon",
|
|
400
|
+
"aria-hidden": "true",
|
|
401
|
+
focusable: "false",
|
|
402
|
+
viewBox: "0 0 24 24",
|
|
403
|
+
width: "16",
|
|
404
|
+
height: "16",
|
|
405
|
+
fill: "none",
|
|
406
|
+
stroke: "currentColor",
|
|
407
|
+
strokeWidth: "2",
|
|
408
|
+
strokeLinecap: "round",
|
|
409
|
+
strokeLinejoin: "round",
|
|
410
|
+
children: NAV_ICON_PATHS[type].map((d) => /* @__PURE__ */ jsx("path", { d }, d))
|
|
411
|
+
});
|
|
412
|
+
}
|
|
306
413
|
function Chevron({ isOpen }) {
|
|
307
414
|
return /* @__PURE__ */ jsx("svg", {
|
|
308
415
|
className: "wave-docs-sidebar__chevron",
|
package/dist/source.js
CHANGED
|
@@ -381,17 +381,20 @@ function buildNav(dir, config) {
|
|
|
381
381
|
const visible = index && isVisibleIn(index.doc, config) ? index : void 0;
|
|
382
382
|
const title = groupTitle(child, visible?.doc);
|
|
383
383
|
const href = visible?.doc.href;
|
|
384
|
+
const icon = child.meta?.icon ?? visible?.doc.frontmatter.icon;
|
|
384
385
|
const group = {
|
|
385
386
|
type: "group",
|
|
386
387
|
title,
|
|
387
388
|
children,
|
|
388
|
-
...href !== void 0 ? { href } : {}
|
|
389
|
+
...href !== void 0 ? { href } : {},
|
|
390
|
+
...icon !== void 0 ? { icon } : {}
|
|
389
391
|
};
|
|
390
392
|
const node = children.length === 0 && visible !== void 0 && href !== void 0 ? {
|
|
391
393
|
type: "page",
|
|
392
394
|
title,
|
|
393
395
|
href,
|
|
394
|
-
slug: visible.doc.slug
|
|
396
|
+
slug: visible.doc.slug,
|
|
397
|
+
...icon !== void 0 ? { icon } : {}
|
|
395
398
|
} : group;
|
|
396
399
|
const order = visible?.doc.frontmatter.order;
|
|
397
400
|
entries.push({
|
|
@@ -403,7 +406,8 @@ function buildNav(dir, config) {
|
|
|
403
406
|
type: "page",
|
|
404
407
|
title: navTitle(visible.doc),
|
|
405
408
|
href,
|
|
406
|
-
slug: visible.doc.slug
|
|
409
|
+
slug: visible.doc.slug,
|
|
410
|
+
...visible.doc.frontmatter.icon !== void 0 ? { icon: visible.doc.frontmatter.icon } : {}
|
|
407
411
|
} } : {},
|
|
408
412
|
...order !== void 0 ? { order } : {}
|
|
409
413
|
});
|
|
@@ -420,7 +424,8 @@ function toPageEntry(page, config) {
|
|
|
420
424
|
type: "page",
|
|
421
425
|
title,
|
|
422
426
|
href: page.doc.href,
|
|
423
|
-
slug: page.doc.slug
|
|
427
|
+
slug: page.doc.slug,
|
|
428
|
+
...page.doc.frontmatter.icon !== void 0 ? { icon: page.doc.frontmatter.icon } : {}
|
|
424
429
|
},
|
|
425
430
|
...order !== void 0 ? { order } : {},
|
|
426
431
|
...isVisibleIn(page.doc, config) ? {} : { hidden: true }
|