uni-3 log

    Search by

    gatsbyカスタム作業メモ

    やったことをかいておく

    gatsby-starter-blogを生成し、カスタムしている

    >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に使いたい

    index.md
    ---
    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

    参考

    投稿一覧ページを無限スクロールにする

    react-infinite-scrollerを使用

    >コード例
    // 表示する分だけ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-remark-katex | Gatsby

    gatsby-browser.jsにcssを追加

    $$x=a$$なんかの構文を使用できる

    import "katex/dist/katex.min.css"

    記事中でiframe tagの使用

    記事中にtableau publicで作成したdashboardを表示したかったので

    iframe tagをmdファイルに入れることにした

    gatsby-remark-responsive-iframeを使用

    参考

    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 "@material-ui/core"
    
    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 "@material-ui/core/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が作られる

    この記事のはじめの方に、↓のように入れて、折りたたみ形式にして入れている

        <details><summary><b>>table of contents</b></summary>
    
        ```toc
        exclude: Table of Contents
        tight: false
        ordered: false
        from-heading: 2
        to-heading: 6
        class-name: "table-of-contents"
        ```
    
        </details>

    目次をcomponentとして追加

    記事中にあると目次まで戻るの大変なので

    記事横など、好きな場所に表示できるよう、componentを作成した

    >コード例
    TOC.tsx
    import React from "react"
    import { Link } from "gatsby"
    import TocIcon from "@material-ui/icons/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.scss
    .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というクラスを追加する

    コード例
    TOC.tsx
    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>
      )
    }

    太字になるように設定した

    参考

    google adsense component

    >コード例
    GoogleAdsense.tsx
    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を作成した

    >コード例
    index.js
    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に対して要素を追加している

    参考

    local pluginの作り方公式

    buy me a coffee buttonの追加

    https://www.buymeacoffee.com

    >コード例
    PayButton.tsx
    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のみなので、右下に出しっぱなしでやや見栄えが悪い

    参考

    2021, Built with Gatsby. This site uses Google Analytics.