microCMS で作成した記事に Gatsby で目次を付ける
— Gatsby — 5 min read
microCMS + Gatsby なブログに目次を付けました。
前にシンタックスハイライトを行なったように、HTML パーサーである cheerio を使用しました。
また、スタイルの適用にあたっては、 styled components を用いました。
準備:styled components のインストール
- これまで、スタイルの適用には、外部のCSS module を使って、 TypeScript のファイルと CSS ファイルを分けて管理して適用していましたが、目次にスタイルを適用するにあたって上手く行かなかったので、styled components を使用しました。
styled components の特徴
- JavaScript でスタイルを記述する CSS in JS のライブラリ
- コンポーネントとstyleのマッピングが無くなる
- ローカルスコープができる
- 書き方の例としては以下のようになります:
const Button = styled.a` /* This renders the buttons above... Edit me! */ display: inline-block; border-radius: 3px; padding: 0.5rem 0; margin: 0.5rem 1rem; width: 11rem; background: transparent; color: white; border: 2px solid white;
/* The GitHub button is a primary button * edit this to target it specifically! */ ${props => props.primary && css` background: white; color: black; `}`
render( <div> <Button href="https://github.com/styled-components/styled-components" target="_blank" rel="noopener" primary > GitHub </Button>
<Button as={Link} href="/docs"> Documentation </Button> </div>)
Gatsby での styled components の利用
- Gatsby で styled components を使用するために、以下のようにプラグインをインストールします:
$ yarn add gatsby-plugin-styled-components styled-components babel-plugin-styled-components
gatsby-config.js
に以下のように記述します:
module.exports = { plugins: [`gatsby-plugin-styled-components`],}
- 再ビルドすると使用できるようになります。
目次の作成方法
- microCMS のブログ記事を参考に HTML パーサーである cheerio を使用します。
- 具体的には…
- microCMS の API 経由で取得した記事(リッチエディタで作成)の HTML から
h1
,h2
,h3
の要素を抜き出します。 - スタイルを適用するために、抜き出した要素にクラス名を付与します。
- インデントをするために Styled Components を用います。
- microCMS の API 経由で取得した記事(リッチエディタで作成)の HTML から
要素の抜き出し
- 以下のようにして要素を抜き出します
htmlString
には、 microCMS の API 経由で取得した記事の内容(HTML)をそのまま入れます- 配列形式で取得できるので、整形してテキスト、ID、タグ名の配列に変換します。
const $ = cheerio.load(htmlString) const toc = $("h1, h2, h3") .toArray() .map(data => ({ text: data.children[0].data, id: data.attribs.id, name: data.name, }))
- 以下のような形で取得できます:
[ { text: '目次の作り方', id: 'hkY7o3DlYB2', name: 'h1' }, { text: '目次機能のオンオフ', id: 'h4AL9B4qAV6', name: 'h2' }, { text: 'おわりに', id: 'hBwwL6rm8B7', name: 'h1' }]
表示方法
- 以下のように要素を返して目次を表示します。
- スタイルの適用を見越して、リストの項目一つ一つに
list h1
,list h2
,list h3
といったクラス名を付与しています。
- スタイルの適用を見越して、リストの項目一つ一つに
return ( <> {toc.length ? ( <div id="create-table-of-contents"> <h4>目次</h4> <ul id="lists"> {toc.map((toc, index) => { return ( <li className={"list " + toc.name} key={toc.id}> <a href={"#" + toc.id}>{toc.text}</a> </li> ) })} </ul> </div> ) : ( "" )} </> )
字下げを行う
- h2, h3 の要素に対しては字下げを行うよう、スタイルを適用します。
import styled from "styled-components"
const StyledToc = styled.div` & .list.h2 { margin-left: 1em; }
& .list.h3 { margin-left: 2em; }`
- 最終的に、目次のコンポーネントは以下のように作成できました:
import React from "react"import cheerio from "cheerio"import styled from "styled-components"
export type TocTypes = { text: string id: string name: string}
interface Props { htmlString: string}
const StyledToc = styled.div` & .list.h2 { margin-left: 1em; }
& .list.h3 { margin-left: 2em; }`
const Toc: React.FC<Props> = props => { const { htmlString } = props const $ = cheerio.load(htmlString) const toc = $("h1, h2, h3") .toArray() .map(data => ({ text: data.children[0].data, id: data.attribs.id, name: data.name, }))
return ( <> {toc.length ? ( <div id="create-table-of-contents"> <h4>目次</h4> <StyledToc> <ul id="lists"> {toc.map((toc, index) => { return ( <li className={"list " + toc.name} key={toc.id}> <a href={"#" + toc.id}>{toc.text}</a> </li> ) })} </ul> </StyledToc> </div> ) : ( "" )} </> )}
export default Toc