やったことをかいておく
gatsby-starter-blog
を生成し、カスタムしている
table of contents
exclude: Table of Contents
tight: false
ordered: false
from-heading: 2
to-heading: 6
class-name: "table-of-contents"
slugの生成方法を変える
slugは各postのpath部分に使われる
gatsby-node.js
をみると、markdownの入ったファイルのパスを取得し、設定している
// graphqlのnodeにslugというfieldを作成している
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
.md
ファイルのfrontmatterにslugを定義して、これとdateよりYYYY/MM/DD
をprefixに使いたい
---
title: My Second Post!
date: "2015-05-06T23:46:37.121Z"
slug: "aa"
---
...
について/2015/05/06/aa
をこのpostのpathとしたい
graphqlで値を取得して、onCreateNode時に値を渡してやればよい
...
const slug = node.frontmatter.slug
const dt = moment(node.frontmatter.date).format("YYYY/MM/DD")
const value = dt + "/" + slug
...
参考
どのようにmarkdownからページを作成しているか
draftのコンテンツは生成しない
markdownファイルのfrontmatterにdraft項目を追加する
コンテンツページ生成、post一覧取得時のクエリに条件式を追加してやればよい
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC },
filter: {frontmatter: {draft: {eq: false}}},
limit: 1000
) {
develop(local開発)時には、draftの値関係なく生成するようにしたかったが、一旦これで
↓を使ってどうにかできそう Different GraphQL query in development and production · Issue #12460 · gatsbyjs/gatsby · GitHub
参考
- Different GraphQL query in development and production · Issue #12460 · gatsbyjs/gatsby · GitHub
- GitHub - shooontan/gatsby-plugin-draft: 📝Gatsby plugin for adding draft field 実装した後にplugin見つけた
投稿一覧ページを無限スクロールにする
コード例
// 表示する分だけsliceして渡すようにしている
const Posts: React.FC<Props> = ({ posts, showPosts }) => {
return posts.slice(0, showPosts).map(({ node }) => {
return <Article post={node} />
})
}
const BlogIndex = ({ data, location }) => {
const siteTitle = data.site.siteMetadata.title
const posts = data.allMarkdownRemark.edges
const pageSize = 10
const [hasMore, setHasMore] = React.useState(true)
const [pageNum, setPageNum] = React.useState(1)
let loadMore = () => {
if (pageSize * pageNum >= posts.length) {
setHasMore(false)
return
}
setPageNum(pageNum + 1)
}
const loader = <div className="loader">Loading...</div>
return (
<Layout location={location} title={siteTitle}>
<SEO title="All posts" />
<InfiniteScroll
pageStart={0}
loadMore={loadMore}
hasMore={hasMore}
loader={loader}
>
<Posts posts={posts} showPosts={pageNum * pageSize} />
</InfiniteScroll>
</Layout>
)
}
参考
search consoleのtagを設定する
サイトの所有権を確認する - Search Console ヘルプ のHTML タグを設定する
./src/components/seo.js
でmeta tagの管理をしているので、以下のように追記して設置した
コード例
<Helmet
meta={[
...
{
name: "google-site-verification",
content: "xxxx",
},
...
]}
記事にtex(katex)記法を使用
packageを導入
gatsby-browser.js
にcssを追加
$$x=a$$
なんかの構文を使用できる
import "katex/dist/katex.min.css"
記事中でiframe tagの使用
記事中にtableau publicで作成したdashboardを表示したかったので
iframe tagをmdファイルに入れることにした
gatsby-remark-responsive-iframeを使用
参考
- https://kb.tableau.com/articles/howto/embedding-tableau-public-views-in-iframes?lang=ja-jp
iframeにてtableau dashboardを埋め込む方法
back to topボタンの追加
App Bar React component - Material-UI
back to topを追加した
コード例
import React from "react"
import styled from "styled-components"
import { Tooltip, Zoom, useScrollTrigger } from "@mui/material"
type Props = { children: React.ReactNode }
const StyledDiv = styled.div`
position: fixed;
bottom: 1.25rem;
right: 1.25rem;
`
const ScrollTop: React.FC<Props> = props => {
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: 0,
})
const onClick = (event: React.MouseEvent<HTMLDivElement>) => {
const anchor = (
(event.target as HTMLDivElement).ownerDocument || document
).querySelector("#back-to-top-anchor")
if (anchor) {
anchor.scrollIntoView({ behavior: "smooth", block: "center" })
}
}
return (
<Zoom in={trigger}>
<Tooltip title="back to top">
<StyledDiv onClick={onClick}>{props.children}</StyledDiv>
</Tooltip>
</Zoom>
)
}
export default ScrollTop
import Fab from "@mui/material/Fab"
import ScrollTop from "./scroll-top"
<ScrollTop>
<Fab color="primary" size="small" aria-label="scroll back to top">
<KeyboardArrowUpIcon />
</Fab>
</ScrollTop>
tagページの追加
Creating Tags Pages for Blog Posts | Gatsby を参考に
postのfrontmatterに、tagsを追加しておく
---
title: "xxx"
tags:
- tag1
- tag2
tagごとにページを作成した。こんな感じ
algolia検索を追加
Adding Search with Algolia | Gatsbyを参考に
gatsby-plugin-algoliaを設定すると、yarn run build
時に送信してくれる
送信するデータはgraphqlで、index名はqueries.jsのindexName
に記載しておく
...
[Algolia] 1 queries to index
[Algolia] Running 1 query for index prod_blog...
[Algolia] Query resulted in a total of 90 results
[Algolia] Found 90 new `/` updated records...
[Algolia] Done!
⠋ onPostBuild
success onPostBuild - 36.783s
検索機能は、チュートリアル通りのものをヘッダー部分に追加した
記事の目次を追加
記事中に追加
gatsby-remark-table-of-contentsを用いる
readme通りにすると、markdown内に各見出しに対するlinkが作られる
この記事のはじめの方に、折りたたみ形式にして入れている
目次をcomponentとして追加
記事中にあると目次まで戻るの大変なので
記事横など、好きな場所に表示できるよう、componentを作成した
コード例
import React from "react"
import { Link } from "gatsby"
import TocIcon from "@mui/icons-material/Toc"
import { MarkdownHeading } from "../../../graphql-types"
import "./blog-toc.scss"
type TOCProps = {
links: Array<MarkdownHeading>
}
const TOC: React.FC<TOCProps> = ({ links }) => {
const texts = links.map(link => {
return (
<>
<li>
<Link className={`depth-${link.depth} link`} to={`#${link.id}`}>
{link.value}
</Link>
</li>
</>
)
})
return (
<div className="blog-toc">
<TocIcon className="toc-icon" />
目次
<ol>{texts}</ol>
</div>
)
}
export default TOC
.blog-toc {
font-family: "ヒラギノ丸ゴ Pro W4", "ヒラギノ丸ゴ Pro", "sans-serif";
position: -webkit-sticky;
position: sticky;
top: 0;
padding: 0.5rem;
border-left: solid 0.1rem #d3d3d3;
font-size: 13px;
font-weight: 1000;
.toc-icon {
vertical-align: middle;
}
li {
margin-bottom: 0.5rem;
}
.link {
box-shadow: none;
text-decoration: none;
color: inherit;
}
}
右側に追加される
observer APIを用いる
各indexが見えた時に該当する目次項目にactive
というクラスを追加する
コード例
const TOC: React.FC<TOCProps> = ({ links }) => {
const [activeId, setActiveId] = React.useState(``)
React.useEffect(() => {
const observer = new IntersectionObserver(
entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
setActiveId(entry.target.id)
}
})
},
{
rootMargin: `0% 0% -95% 0%`,
}
)
links.forEach(link => {
observer.observe(document.getElementById(link.id))
})
return () => {
links.forEach(link => {
observer.unobserve(document.getElementById(link.id))
})
}
}, [links])
const texts = links.map(link => {
const active = link.id === activeId ? "active" : ""
return (
<>
<li>
<Link
id={`${link.id}`}
className={`depth-${link.depth} link ${active}`}
to={`#${link.id}`}
>
{link.value}
</Link>
</li>
</>
)
})
return (
<div className="blog-toc">
<TocIcon className="toc-icon" />
目次
<ol>{texts}</ol>
</div>
)
}
太字になるように設定した
参考
- https://nickymeuleman.netlify.app/blog/table-of-contents#example
- gatsby-remark-table-of-contents
google adsense component
コード例
import React from "react"
const GoogleAdsense: React.FC = () => {
React.useEffect(() => {
if (window) {
window.adsbygoogle = window.adsbygoogle || []
window.adsbygoogle.push({})
}
}, [])
return (
<>
<ins
className="adsbygoogle"
style={{ display: "block" }}
data-ad-client="ca-pub-xxxxxxx"
data-Namelot="xxxxxxx"
data-ad-format="auto"
data-full-width-responsive="true"
></ins>
</>
)
}
export default GoogleAdsense
読み込み時にスクリプトを実行しなければならないらしく、useEffectでどうにかした
code snippetにタイトル表示
local pluginを作成した
コード例
const visit = require("unist-util-visit")
const titleSep = ":"
module.exports = ({ markdownAST }, { className }) => {
visit(markdownAST, "code", (node, index) => {
// ```xxx:xxx.txt -> lang, title
const [lang, title] = (node.lang || "").split(titleSep)
if (!title) {
return
}
const titleNode = {
type: "html",
value: `
<div class="${className}">
<span>${title}</span>
</div>
`,
}
markdownAST.children.splice(index, 0, titleNode)
node.lang = lang
})
return markdownAST
}
src/styles/global.css
にてstyleを設定した
gatsby-browser.js
にてimport "./src/styles/global.css"
として読み込ませる
.gatsby-code-title {
position: relative;
background: #f5f2f0;
width: 100%;
top: 0.5rem;
}
.gatsby-code-title span {
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
color: #fcfcfc;
background: #a9a9a9;
padding: 0.25rem;
}
prismjsのpluginに対して要素を追加している
参考
- https://www.gatsbyjs.com/docs/creating-a-local-plugin/
local pluginの作り方公式
buy me a coffee buttonの追加
https://www.buymeacoffee.com
コード例
const PayButton: React.FC = () => {
React.useEffect(() => {
let script = document.createElement("script")
script.setAttribute("data-name", "BMC-Widget")
script.setAttribute("data-id", "xxxx")
script.setAttribute("data-description", "Support me on Buy me a coffee!")
script.setAttribute("data-message","☕")
script.setAttribute("data-color", "#FFDD00")
script.setAttribute("data-position", "right")
script.src = "https://cdnjs.buymeacoffee.com/1.0.0/widget.prod.min.js"
script.async = true
script.onload = function () {
var evt = document.createEvent("Event")
evt.initEvent("DOMContentLoaded", false, false)
window.dispatchEvent(evt)
}
document.head.appendChild(script)
}, [])
return null
}
アカウント、widgetを作成し記事の右下に設置した
fixedのみなので、右下に出しっぱなしでやや見栄えが悪い
参考
- https://stackoverflow.com/questions/62039217/add-buy-me-a-coffee-widget-to-react-application
gitpodで動作
installさえできれば起動させて
Simple Browser
を使って画面をみることができる
.gitpod.yml
は↓の感じ
tasks:
- init: yarn
command: yarn run develop