Make search result items links
Luke Karrys committed
Oct 21, 2023 at 18:48 UTC
586dc1084c8f9aa5d6077bde4ebd92701e318044
7 files changed
+73
-65
src/components/link.js
+15
@@ -38,4 +38,19 @@ const Link = React.forwardRef(function Link({to, href, ...props}, ref) {
38
return <PrimerLink ref={ref} href={href} {...props} />
39
})
40
41
+export const LinkNoUnderline = React.forwardRef(function LinkNoUnderline({sx, ...props}, ref) {
42
+ return (
43
+ <Link
44
+ ref={ref}
45
+ sx={{
46
+ ...sx,
47
+ '&:hover, &:focus': {
48
+ textDecoration: 'none',
49
+ },
50
+ }}
51
+ {...props}
52
+ />
53
+ )
54
+})
55
+
56
export default Link
src/components/search-results.js
+3
@@ -1,5 +1,6 @@
1
import React from 'react'
2
import {Box, Text} from '@primer/react'
3
+import {LinkNoUnderline} from './link'
4
import useSiteMetadata from '../hooks/use-site-metadata'
5
import * as getNav from '../util/get-nav'
6
@@ -31,6 +32,8 @@ function SearchResults({results, getItemProps, highlightedIndex}) {
32
33
return results.map((item, index) => (
34
<Box
35
+ as={LinkNoUnderline}
36
+ to={item.path}
37
key={item.path}
38
style={{cursor: 'pointer'}}
39
sx={{
src/components/table-of-contents.js
+1
-1
@@ -37,7 +37,7 @@ export const Mobile = withTableOfContents(({items}) => {
37
return (
38
<Box sx={{display: ['block', null, 'none'], mb: 3}}>
39
<Details {...getDetailsProps()}>
40
- <Button variant="invisible" as="summary" leadingIcon={open ? ChevronDownIcon : ChevronRightIcon}>
40
+ <Button as="summary" sx={{display: 'inline-flex'}} leadingIcon={open ? ChevronDownIcon : ChevronRightIcon}>
41
Table of contents
42
</Button>
43
<TableOfContents items={items} />
src/components/variant-select.js
+2
-10
@@ -2,18 +2,10 @@ import React from 'react'
2
import {ActionList, ActionMenu, Box} from '@primer/react'
3
import * as getNav from '../util/get-nav'
4
import usePage from '../hooks/use-page'
5
-import Link from './link'
5
+import {LinkNoUnderline} from './link'
6
7
const VariantItem = ({title, shortName, url, active}) => (
8
- <ActionList.Item
9
- as={Link}
10
- to={url}
11
- id={shortName}
12
- active={active}
13
- sx={{
14
- ':hover': {textDecoration: 'none'},
15
- }}
16
- >
8
+ <ActionList.Item as={LinkNoUnderline} to={url} id={shortName} active={active}>
9
{title}
10
</ActionList.Item>
11
)
src/constants.js
+2
@@ -5,3 +5,5 @@ export const HEADER_BAR = 10
5
export const FULL_HEADER_HEIGHT = HEADER_HEIGHT + HEADER_BAR
6
7
export const SCROLL_MARGIN_TOP = FULL_HEADER_HEIGHT + 24
8
+
9
+export const CLI_PATH = '/cli'
src/hooks/use-search.js
+47
-43
@@ -4,10 +4,7 @@ import {navigate, graphql, useStaticQuery} from 'gatsby'
4
import {useIsMobile} from './use-breakpoint'
5
import usePage from './use-page'
6
import * as getNav from '../util/get-nav'
7
-
8
-// This worker can live for the entire duraction of the site
9
-const WORKER = new Worker(new URL('../util/search.worker.js', import.meta.url))
10
-const CLI_ROOT = '/cli'
7
+import {CLI_PATH} from '../constants'
8
9
const useSearchData = () => {
10
const data = useStaticQuery(graphql`
@@ -51,50 +48,13 @@ const useSearchData = () => {
48
49
const useCliVersion = () => {
50
return getNav.getCurrentOrDefaultVariant(
54
- getNav.getItem(getNav.getVariantRoot(`${CLI_ROOT}/`, {stripTrailing: false})),
51
+ getNav.getItem(getNav.getVariantRoot(`${CLI_PATH}/`, {stripTrailing: false})),
52
usePage().location.pathname,
53
)
54
}
55
59
-function useSearch() {
60
- const [query, setQuery] = React.useState()
61
- const [results, setResults] = React.useState(null)
62
- const queryRef = React.useRef()
63
- const items = useSearchData()
56
+const useSearchCombobox = (results, setQuery) => {
57
const isMobile = useIsMobile()
65
- const {url: cliUrl} = useCliVersion()
66
-
67
- const handleSearchResults = React.useCallback(({data}) => {
68
- if (data.debug) {
69
- console.log(data.debug)
70
- }
71
- if (data.query && data.results && data.query === queryRef.current) {
72
- setResults(data.results)
73
- }
74
- }, [])
75
-
76
- React.useEffect(() => {
77
- WORKER.addEventListener('message', handleSearchResults)
78
- }, [handleSearchResults])
79
-
80
- React.useEffect(() => {
81
- WORKER.postMessage({items})
82
- }, [items])
83
-
84
- React.useEffect(() => {
85
- WORKER.postMessage({cli: {root: CLI_ROOT, current: cliUrl}})
86
- }, [cliUrl])
87
-
88
- React.useEffect(() => {
89
- queryRef.current = query
90
-
91
- if (query) {
92
- WORKER.postMessage({query})
93
- } else {
94
- setResults(null)
95
- }
96
- }, [query])
97
-
58
const combobox = useCombobox({
59
id: 'search-box',
60
items: results || [],
@@ -130,6 +90,50 @@ function useSearch() {
90
return changes
91
},
92
})
93
+ return combobox
94
+}
95
+
96
+function useSearch() {
97
+ const [query, setQuery] = React.useState()
98
+ const [results, setResults] = React.useState(null)
99
+ const queryRef = React.useRef()
100
+ const items = useSearchData()
101
+ const {url: cliUrl} = useCliVersion()
102
+ const worker = React.useRef()
103
+
104
+ const handleSearchResults = React.useCallback(({data}) => {
105
+ if (data.query && data.results && data.query === queryRef.current) {
106
+ setResults(data.results)
107
+ }
108
+ }, [])
109
+
110
+ React.useEffect(() => {
111
+ worker.current = new Worker(new URL('../util/search.worker.js', import.meta.url))
112
+ }, [])
113
+
114
+ React.useEffect(() => {
115
+ worker.current.addEventListener('message', handleSearchResults)
116
+ }, [worker, handleSearchResults])
117
+
118
+ React.useEffect(() => {
119
+ worker.current.postMessage({items})
120
+ }, [worker, items])
121
+
122
+ React.useEffect(() => {
123
+ worker.current.postMessage({cli: {root: CLI_PATH, current: cliUrl}})
124
+ }, [worker, cliUrl])
125
+
126
+ React.useEffect(() => {
127
+ queryRef.current = query
128
+
129
+ if (query) {
130
+ worker.current.postMessage({query})
131
+ } else {
132
+ setResults(null)
133
+ }
134
+ }, [worker, query])
135
+
136
+ const combobox = useSearchCombobox(results, setQuery)
137
138
return {
139
...combobox,
src/mdx/index.js
+3
-11
@@ -6,7 +6,7 @@ import {LinkIcon} from '@primer/octicons-react'
6
import textContent from 'react-addons-text-content'
7
import {SCROLL_MARGIN_TOP} from '../constants'
8
import usePage from '../hooks/use-page'
9
-import SiteLink from '../components/link'
9
+import SiteLink, {LinkNoUnderline} from '../components/link'
10
11
export {default as Code} from './code'
12
export {default as Index} from './nav-hierarchy'
@@ -40,15 +40,7 @@ const StyledHeading = styled(Heading)`
40
41
const HeaderLink = ({autolink, children, ...props}) =>
42
autolink ? (
43
- <SiteLink
44
- {...props}
45
- sx={{
46
- color: 'inherit',
47
- '&:hover, &:focus': {
48
- textDecoration: 'none',
49
- },
50
- }}
51
- >
43
+ <LinkNoUnderline {...props} sx={{color: 'inherit'}}>
44
{children}
45
<Octicon
46
icon={LinkIcon}
@@ -60,7 +52,7 @@ const HeaderLink = ({autolink, children, ...props}) =>
52
verticalAlign: 'middle !important',
53
}}
54
/>
63
- </SiteLink>
55
+ </LinkNoUnderline>
56
) : (
57
children
58
)