Skip to content

Commit b80958f

Browse files
authored
fix: search exiting, markdown rendering (#955)
1 parent 70b7526 commit b80958f

6 files changed

Lines changed: 79 additions & 28 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@node-core/doc-kit': patch
3+
---
4+
5+
Close Orama search when the target link is on the same page
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@node-core/doc-kit': patch
3+
---
4+
5+
Render markdown `code` snippets in the sidebar

‎packages/core/src/generators/web/ui/components/SearchBox/index.jsx‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,45 @@ import styles from './index.module.css';
1111
import useOrama from '../../hooks/useOrama.mjs';
1212
import { relativeOrAbsolute } from '../../utils/relativeOrAbsolute.mjs';
1313

14+
/**
15+
* Dismisses the search modal the clicked hit sits in
16+
*
17+
* @param {MouseEvent} event
18+
*/
19+
const followSearchHit = event => {
20+
if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {
21+
return;
22+
}
23+
24+
const target = new URL(event.currentTarget.href);
25+
26+
event.preventDefault();
27+
28+
// Escape is the only dismissal `SearchModal` exposes to the hits it renders.
29+
event.currentTarget.dispatchEvent(
30+
new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })
31+
);
32+
33+
requestAnimationFrame(() => {
34+
if (target.pathname !== window.location.pathname) {
35+
window.location.href = target.href;
36+
return;
37+
}
38+
39+
requestAnimationFrame(() =>
40+
document
41+
.getElementById(decodeURIComponent(target.hash.slice(1)))
42+
?.scrollIntoView()
43+
);
44+
});
45+
};
46+
47+
/**
48+
* A search hit link that dismisses the modal when followed.
49+
* @param {Object} props - Anchor props, as passed by `SearchHit`.
50+
*/
51+
const SearchHitLink = props => <a {...props} onClick={followSearchHit} />;
52+
1453
const SearchBox = ({ pathname }) => {
1554
const client = useOrama(pathname);
1655

@@ -21,6 +60,7 @@ const SearchBox = ({ pathname }) => {
2160
noResultsTitle="No results found for"
2261
onHit={hit => (
2362
<SearchHit
63+
as={SearchHitLink}
2464
document={{
2565
...hit.document,
2666
href: relativeOrAbsolute(hit.document.href, pathname),

‎packages/core/src/generators/web/ui/components/SideBar/index.jsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import SideBar from '@node-core/ui-components/Containers/Sidebar';
33

44
import styles from './index.module.css';
55
import { relativeOrAbsolute } from '../../utils/relativeOrAbsolute.mjs';
6+
import { renderLabel } from '../../utils/renderLabel.jsx';
67

78
import { project, version, versions, pages } from '#theme/config';
89

@@ -37,7 +38,7 @@ export default ({ metadata }) => {
3738
}));
3839

3940
const items = pages.map(([heading, path]) => ({
40-
label: heading,
41+
label: renderLabel(heading),
4142
link:
4243
metadata.path === path
4344
? `${metadata.basename}.html`
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Renders a raw Markdown heading. We intentionally
3+
* only account for backticks, since running a full
4+
* markdown parse is much slower (and there's never
5+
* a case where'd have extremely complex markdown
6+
* within a sidebar like this)
7+
*
8+
* @param {string} label - Raw heading text.
9+
* @returns {import('preact').ComponentChildren}
10+
*/
11+
export const renderLabel = label => {
12+
const segments = label.split('`');
13+
14+
if (segments.length === 1) {
15+
return label;
16+
}
17+
18+
// Odd-indexed segments sat between a pair of backticks.
19+
return segments.map((segment, index) =>
20+
index % 2 ? (
21+
<code key={`code:${segment}`}>{segment}</code>
22+
) : (
23+
<span key={`text:${segment}`}>{segment}</span>
24+
)
25+
);
26+
};

‎www/theme/SideBar.jsx‎

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import SideBar from '@node-core/ui-components/Containers/Sidebar';
22

33
import { relativeOrAbsolute } from '../../packages/core/src/generators/web/ui/utils/relativeOrAbsolute.mjs';
4+
import { renderLabel } from '../../packages/core/src/generators/web/ui/utils/renderLabel.jsx';
45

56
import { pages } from '#theme/config';
67

@@ -22,33 +23,6 @@ const GUIDE_ORDER = [
2223

2324
const isGenerator = ([, path]) => path.startsWith('/generators/');
2425

25-
/**
26-
* Page labels are raw heading text, so a heading like `` ## `web` Generator ``
27-
* arrives with its backticks intact. The sidebar renders the label verbatim,
28-
* showing the literal backticks. Split on backtick pairs and wrap the enclosed
29-
* spans in `<code>` so they render as inline code; labels without backticks pass
30-
* through as a plain string.
31-
*
32-
* @param {string} label
33-
* @returns {import('react').ReactNode}
34-
*/
35-
const renderLabel = label => {
36-
const segments = label.split('`');
37-
38-
if (segments.length === 1) {
39-
return label;
40-
}
41-
42-
// Odd-indexed segments sat between a pair of backticks.
43-
return segments.map((segment, index) =>
44-
index % 2 ? (
45-
<code key={`code:${segment}`}>{segment}</code>
46-
) : (
47-
<span key={`text:${segment}`}>{segment}</span>
48-
)
49-
);
50-
};
51-
5226
/**
5327
* Orders the narrative pages by `GUIDE_ORDER`, leaving anything unlisted at the
5428
* end in the alphabetical order `jsx-ast` already produced.

0 commit comments

Comments
 (0)