Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b2f4c41
commit b8ac571
Showing
12 changed files
with
532 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import React from 'react'; | ||
import { connectSearchBox } from 'react-instantsearch-dom'; | ||
import { Search as SearchIcon } from '@styled-icons/fa-solid/Search'; | ||
|
||
export default connectSearchBox(({ refine, currentRefinement, className, onFocus }) => ( | ||
<form className={className}> | ||
<input | ||
className="SearchInput" | ||
type="text" | ||
placeholder="Search" | ||
aria-label="Search" | ||
onChange={(e) => refine(e.target.value)} | ||
value={currentRefinement} | ||
onFocus={onFocus} | ||
/> | ||
<SearchIcon className="SearchIcon" /> | ||
</form> | ||
)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* eslint-disable react/prop-types */ | ||
import React from 'react'; | ||
import { Link } from 'gatsby'; | ||
import { | ||
connectStateResults, | ||
Highlight, | ||
Hits, | ||
Index, | ||
Snippet, | ||
PoweredBy, | ||
} from 'react-instantsearch-dom'; | ||
|
||
const HitCount = connectStateResults(({ searchResults }) => { | ||
const hitCount = searchResults && searchResults.nbHits; | ||
|
||
return hitCount > 0 ? ( | ||
<div className="HitCount"> | ||
{hitCount} result{hitCount !== 1 ? `s` : ``} | ||
</div> | ||
) : null; | ||
}); | ||
|
||
const PageHit = ({ hit }) => ( | ||
<div> | ||
<Link to={hit.slug}> | ||
<h4> | ||
<Highlight attribute="title" hit={hit} tagName="mark" /> | ||
</h4> | ||
</Link> | ||
<Snippet attribute="excerpt" hit={hit} tagName="mark" /> | ||
</div> | ||
); | ||
|
||
const HitsInIndex = ({ index }) => ( | ||
<Index indexName={index.name}> | ||
<HitCount /> | ||
<Hits className="Hits" hitComponent={PageHit} /> | ||
</Index> | ||
); | ||
|
||
const SearchResult = ({ indices, className }) => ( | ||
<div className={className}> | ||
{indices.map((index) => ( | ||
<HitsInIndex index={index} key={index.name} /> | ||
))} | ||
<PoweredBy /> | ||
</div> | ||
); | ||
|
||
export default SearchResult; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import styled, { css } from 'styled-components'; | ||
import SearchBox from './SearchBox'; | ||
|
||
const open = css` | ||
width: 10em; | ||
background: ${({ theme }) => theme.background}; | ||
cursor: text; | ||
margin-left: -1.6em; | ||
padding-left: 1.6em; | ||
`; | ||
|
||
const closed = css` | ||
width: 0; | ||
background: transparent; | ||
cursor: pointer; | ||
margin-left: -1em; | ||
padding-left: 1em; | ||
`; | ||
|
||
export default styled(SearchBox)` | ||
display: flex; | ||
flex-direction: row-reverse; | ||
align-items: center; | ||
margin-bottom: 0; | ||
.SearchInput { | ||
outline: none; | ||
border: ${({ hasFocus }) => (hasFocus ? 'auto' : 'none')}; | ||
font-size: 1em; | ||
transition: 100ms; | ||
border-radius: 2px; | ||
color: ${({ theme }) => theme.foreground}; | ||
::placeholder { | ||
color: ${({ theme }) => theme.faded}; | ||
} | ||
${({ hasFocus }) => (hasFocus ? open : closed)} | ||
} | ||
.SearchIcon { | ||
width: 1em; | ||
margin: 0.3em; | ||
color: ${({ theme }) => theme.foreground}; | ||
pointer-events: none; | ||
} | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import styled, { css } from 'styled-components'; | ||
import SearchResult from './SearchResult'; | ||
|
||
const Popover = css` | ||
max-height: 80vh; | ||
overflow: scroll; | ||
-webkit-overflow-scrolling: touch; | ||
position: absolute; | ||
z-index: 2; | ||
right: 0; | ||
top: 100%; | ||
margin-top: 0.5em; | ||
width: 80vw; | ||
max-width: 30em; | ||
box-shadow: 0 0 5px 0; | ||
padding: 1em; | ||
border-radius: 2px; | ||
background: ${({ theme }) => theme.background}; | ||
`; | ||
|
||
export default styled(SearchResult)` | ||
display: ${(props) => (props.show ? `block` : `none`)}; | ||
${Popover} | ||
.HitCount { | ||
display: flex; | ||
justify-content: flex-end; | ||
} | ||
.Hits { | ||
ul { | ||
list-style: none; | ||
margin-left: 0; | ||
} | ||
li.ais-Hits-item { | ||
margin-bottom: 1em; | ||
a { | ||
color: ${({ theme }) => theme.foreground}; | ||
h4 { | ||
margin-bottom: 0.2em; | ||
} | ||
} | ||
} | ||
} | ||
.ais-PoweredBy { | ||
display: flex; | ||
justify-content: flex-end; | ||
font-size: 80%; | ||
svg { | ||
width: 70px; | ||
} | ||
} | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import styled from 'styled-components'; | ||
|
||
export default styled.div` | ||
position: relative; | ||
margin-right: 1em; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* eslint-disable react/prop-types */ | ||
import React, { createRef, useState } from 'react'; | ||
import algoliasearch from 'algoliasearch/lite'; | ||
import { InstantSearch } from 'react-instantsearch-dom'; | ||
import { ThemeProvider } from 'styled-components'; | ||
import StyledSearchBox from './StyledSearchBox'; | ||
import StyledSearchResult from './StyledSearchResult'; | ||
import StyledSearchRoot from './StyledSearchRoot'; | ||
import useClickOutside from './useClickOutside'; | ||
|
||
const theme = { | ||
foreground: '#050505', | ||
background: 'white', | ||
faded: '#888', | ||
}; | ||
|
||
export default function Search({ indices }) { | ||
const rootRef = createRef(); | ||
const [query, setQuery] = useState(); | ||
const [hasFocus, setFocus] = useState(false); | ||
const searchClient = algoliasearch( | ||
process.env.GATSBY_ALGOLIA_APP_ID, | ||
process.env.GATSBY_ALGOLIA_SEARCH_KEY, | ||
); | ||
|
||
useClickOutside(rootRef, () => setFocus(false)); | ||
|
||
return ( | ||
<ThemeProvider theme={theme}> | ||
<StyledSearchRoot ref={rootRef}> | ||
<InstantSearch | ||
searchClient={searchClient} | ||
indexName={indices[0].name} | ||
// eslint-disable-next-line no-shadow | ||
onSearchStateChange={({ query }) => setQuery(query)} | ||
> | ||
<StyledSearchBox onFocus={() => setFocus(true)} hasFocus={hasFocus} /> | ||
<StyledSearchResult show={query && query.length > 0 && hasFocus} indices={indices} /> | ||
</InstantSearch> | ||
</StyledSearchRoot> | ||
</ThemeProvider> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* eslint-disable no-restricted-syntax */ | ||
import { useEffect } from 'react'; | ||
|
||
const events = [`mousedown`, `touchstart`]; | ||
|
||
export default (ref, onClickOutside) => { | ||
const isOutside = (element) => !ref.current || !ref.current.contains(element); | ||
|
||
const onClick = (event) => { | ||
if (isOutside(event.target)) { | ||
onClickOutside(); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
for (const event of events) { | ||
document.addEventListener(event, onClick); | ||
} | ||
|
||
return () => { | ||
for (const event of events) document.removeEventListener(event, onClick); | ||
}; | ||
}); | ||
}; |
Oops, something went wrong.