1
0
mirror of https://github.com/kremalicious/blog.git synced 2025-01-03 02:15:08 +01:00

make search functional

This commit is contained in:
Matthias Kretschmann 2018-08-28 23:28:42 +02:00
parent e708904f64
commit 366f9c5ea4
Signed by: m
GPG Key ID: 606EEEF3C479A91F
13 changed files with 210 additions and 67 deletions

View File

@ -70,6 +70,39 @@ module.exports = {
includePaths: [`${__dirname}/node_modules`, `${__dirname}/src/styles`]
}
},
{
resolve: 'gatsby-plugin-lunr',
options: {
languages: [
{
// ISO 639-1 language codes. See https://lunrjs.com/guides/language_support.html for details
name: 'en'
}
],
// Fields to index. If store === true value will be stored in index file.
// Attributes for custom indexing logic. See https://lunrjs.com/docs/lunr.Builder.html for details
fields: [
{ name: 'title', store: true, attributes: { boost: 20 } },
{ name: 'content' },
{ name: 'excerpt', attributes: { boost: 10 } },
{ name: 'category', store: true, attributes: { boost: 5 } },
{ name: 'tags', store: true },
{ name: 'url', store: true }
],
// How to resolve each field's value for a supported node type
resolvers: {
// For any node of type MarkdownRemark, list how to resolve the fields' values
MarkdownRemark: {
title: node => node.frontmatter.title,
content: node => node.rawMarkdownBody,
excerpt: node => node.frontmatter.excerpt,
category: node => node.frontmatter.category,
tags: node => node.frontmatter.tags,
url: node => node.fields.slug
}
}
}
},
'gatsby-plugin-react-helmet',
'gatsby-transformer-yaml',
'gatsby-transformer-sharp',

View File

@ -27,6 +27,7 @@
"gatsby": "^2.0.0-rc.0",
"gatsby-image": "^2.0.0-rc.0",
"gatsby-plugin-catch-links": "^2.0.2-rc.0",
"gatsby-plugin-lunr": "^1.1.0",
"gatsby-plugin-matomo": "^0.4.1",
"gatsby-plugin-react-helmet": "^3.0.0-rc.0",
"gatsby-plugin-sass": "^2.0.0-rc.0",

View File

@ -11,7 +11,7 @@ const Layout = ({ children }) => (
<Head />
<Header />
<main className={styles.document}>
<main className={styles.document} id="document">
<div className={styles.content}>
<Container>{children}</Container>
</div>

View File

@ -0,0 +1,11 @@
import React from 'react'
import SearchIcon from '../svg/MagnifyingGlass'
import styles from './SearchButton.module.scss'
const SearchButton = props => (
<button type="button" className={styles.searchButton} {...props}>
<SearchIcon />
</button>
)
export default SearchButton

View File

@ -0,0 +1,33 @@
@import 'variables';
.searchButton {
padding: .65rem .85rem;
text-align: center;
line-height: 1;
vertical-align: middle;
display: inline-block;
margin-right: $spacer / 4;
&:focus {
outline: 0;
}
svg {
fill: $text-color-light;
width: 21px;
height: 21px;
}
&:hover,
&:focus {
svg {
fill: $brand-cyan;
}
}
&:active {
svg {
fill: darken($brand-cyan, 30%);
}
}
}

View File

@ -0,0 +1,14 @@
import React, { Fragment } from 'react'
import Input from './Input'
import styles from './SearchInput.module.scss'
const SearchInput = props => (
<Fragment>
<Input autoFocus type="search" placeholder="Search everything" {...props} />
<button className={styles.searchInputClose} onClick={props.onToggle}>
&times;
</button>
</Fragment>
)
export default SearchInput

View File

@ -0,0 +1,7 @@
@import 'variables';
.searchInputClose {
position: absolute;
right: $spacer / 4;
top: $spacer / 4;
}

View File

@ -0,0 +1,30 @@
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import { Link } from 'gatsby'
import Container from '../atoms/Container'
import styles from './SearchResults.module.scss'
const SearchResults = ({ results, onClose }) =>
ReactDOM.createPortal(
<div className={styles.searchResults}>
<Container>
<ul>
{results.map(page => (
<li key={page.url}>
<Link to={page.url} onClick={onClose}>
{page.title}
</Link>
</li>
))}
</ul>
</Container>
</div>,
document.getElementById('document')
)
SearchResults.propTypes = {
results: PropTypes.array.isRequired
}
export default SearchResults

View File

@ -0,0 +1,28 @@
@import 'variables';
@import 'mixins';
.searchResults {
position: absolute;
left: 0;
right: 0;
z-index: 10;
top: 0;
bottom: 0;
background: $body-background-color;
ul {
@include breakoutviewport;
margin-top: $spacer;
li::before {
top: $spacer / 7;
}
}
a {
padding-top: $spacer / 6;
padding-bottom: $spacer / 6;
display: inline-block;
}
}

View File

@ -1,9 +1,11 @@
import React, { PureComponent, Fragment } from 'react'
// import { Link } from 'gatsby'
import PropTypes from 'prop-types'
import Helmet from 'react-helmet'
import { CSSTransition } from 'react-transition-group'
import Input from '../atoms/Input'
import SearchIcon from '../svg/MagnifyingGlass'
import SearchInput from '../atoms/SearchInput'
import SearchButton from '../atoms/SearchButton'
import SearchResults from '../atoms/SearchResults'
import styles from './Search.module.scss'
class Search extends PureComponent {
@ -11,7 +13,9 @@ class Search extends PureComponent {
super(props)
this.state = {
searchOpen: false
searchOpen: false,
query: '',
results: []
}
}
@ -21,51 +25,70 @@ class Search extends PureComponent {
}))
}
closeSearch = () => {
this.setState({
searchOpen: false,
query: '',
results: []
})
}
isSearchOpen = () => this.state.searchOpen === true
getSearchResults(query) {
if (!query || !window.__LUNR__) return []
const lunrIndex = window.__LUNR__[this.props.lng]
const results = lunrIndex.index.search(query)
return results.map(({ ref }) => lunrIndex.store[ref])
}
search = event => {
const query = event.target.value
const results = this.getSearchResults(query)
this.setState({
results,
query
})
}
render() {
const { searchOpen, query, results } = this.state
return (
<Fragment>
<Helmet>
<body className={this.isSearchOpen() ? 'has-search-open' : null} />
</Helmet>
<button
type="button"
className={styles.searchButton}
onClick={this.toggleSearch}
>
<SearchIcon />
</button>
<SearchButton onClick={this.toggleSearch} />
{this.state.searchOpen && (
{searchOpen && (
<CSSTransition
appear={this.state.searchOpen}
in={this.state.searchOpen}
appear={searchOpen}
in={searchOpen}
timeout={200}
classNames={styles}
>
<section className={styles.search}>
<Input
autoFocus
type="search"
placeholder="Search everything"
onBlur={this.toggleSearch}
// value={this.state.query}
// onChange={this.search}
<SearchInput
value={query}
onChange={this.search}
onToggle={this.closeSearch}
/>
<button
className={styles.searchInputClose}
onClick={this.toggleSearch}
>
&times;
</button>
</section>
</CSSTransition>
)}
{query && (
<SearchResults results={results} onClose={this.closeSearch} />
)}
</Fragment>
)
}
}
Search.propTypes = {
lng: PropTypes.string.isRequired
}
export default Search

View File

@ -1,37 +1,5 @@
@import 'variables';
.searchButton {
padding: .65rem .85rem;
text-align: center;
line-height: 1;
vertical-align: middle;
display: inline-block;
margin-right: $spacer / 4;
&:focus {
outline: 0;
}
svg {
fill: $text-color-light;
width: 21px;
height: 21px;
}
&:hover,
&:focus {
svg {
fill: $brand-cyan;
}
}
&:active {
svg {
fill: darken($brand-cyan, 30%);
}
}
}
.search {
position: absolute;
left: $spacer / 2;
@ -49,12 +17,6 @@
}
}
.searchInputClose {
position: absolute;
right: $spacer / 4;
top: $spacer / 4;
}
.appear,
.enter {
opacity: .01;

View File

@ -19,7 +19,7 @@ class Header extends PureComponent {
</h1>
<nav role="navigation" className={styles.nav}>
<Search />
<Search lng="en" />
<Menu />
</nav>
</div>

View File

@ -76,6 +76,7 @@ export const pageQuery = graphql`
slug
date(formatString: "MMMM DD, YYYY")
}
rawMarkdownBody
}
contentYaml {