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 )