ブログに検索機能がほしい
まだあまり記事数が多くないが、やはりブログに検索機能がほしいと思い、Gatsbyで実装できるものを調査。
Gatsby公式ページにも検索機能の追加についての説明がある。
メジャーなのは、SaaSであるAlgoliaを使うもの。 ただAlgoliaは有料なので個人的にオーバースペックかと思い、ローカルで実装できるものを探す。
検索機能の追加
以下のページを参考に、GatsbyでMarkdownファイルを入稿する際に読み込む記事のタイトルや概要を対象に検索できる機能を追加する。
検索はモーダルウィンドウで実行するようにするため、react-modal
を追加する。
yarn add react-modal
あとは、検索用のコンポーネントsearch.js
と、モーダル表示用のコンポーネントmodal-search.js
を作成。
src/components/search.js
import React, { useState } from "react"
import { Link, useStaticQuery, graphql } from "gatsby"
const SearchResult = () => {
const data = useStaticQuery(graphql`
query {
allMarkdownRemark(sort: { order: DESC, fields: frontmatter___date }) {
edges {
node {
excerpt(pruneLength: 200)
id
frontmatter {
title
description
date(formatString: "YYYY.MM.DD")
tags
}
fields {
slug
}
}
}
}
}
`)
const allPosts = data.allMarkdownRemark.edges
const emptyQuery = ""
const [state, setState] = useState({
filteredData: [],
query: emptyQuery,
})
const handleInputChange = event => {
console.log(event.target.value)
const query = event.target.value
const posts = data.allMarkdownRemark.edges || []
const filteredData = posts.filter(post => {
const { description, title } = post.node.frontmatter
return (
description.toLowerCase().includes(query.toLowerCase()) ||
title.toLowerCase().includes(query.toLowerCase())
)
})
setState({
query,
filteredData,
})
}
const { filteredData, query } = state
const hasSearchResults = filteredData && query !== emptyQuery
const posts = hasSearchResults ? filteredData : allPosts
return (
<div>
<div className="result-inner">
<input
type="text"
aria-label="Search"
placeholder="検索ワードを入力..."
onChange={handleInputChange}
/>
<div className="result-inner__res">
{query !== "" ?
query + " の検索結果: " + posts.length + "件"
: posts.length + "件の記事があります"
}
</div>
<ul className="result-inner__search">
{query !== "" ?
posts.map(({ node }) => {
const { slug } = node.fields
const { title } = node.frontmatter
return (
<li key={slug}>
<Link to={slug}>{title}</Link>
</li>
)})
: ""
}
</ul>
</div>
</div>
)
}
export default SearchResult
src/components/modal-search.js
import React from 'react';
import Modal from 'react-modal';
import Search from "./search";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSearch } from "@fortawesome/free-solid-svg-icons";
Modal.setAppElement('#___gatsby')
class ModalWindow extends React.Component {
constructor() {
super();
this.state = {
modalIsOpen: false
};
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal() {
this.setState({modalIsOpen: true});
}
closeModal() {
this.setState({modalIsOpen: false});
}
render() {
return (
<div>
<FontAwesomeIcon icon={faSearch} onClick={this.openModal} />
<Modal
isOpen={this.state.modalIsOpen}
onRequestClose={this.closeModal}
contentLabel="Seach Modal"
className="modalSearchWindow"
overlayClassName="modalSearchOverlay"
>
<Search />
<button onClick={this.closeModal}>閉じる</button>
</Modal>
</div>
);
}
}
export default ModalWindow;
あとは、検索アイコンを入れたい場所に<ModalSeach />
を入れれば表示される。
検索用のアイコンはfontawesomeを使用。
検索フォームに文字を入力する都度、タイトルと概要を検索し、該当するタイトルを表示する。 検索対象は、本文やタグを追加することもできる。