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