ギャツビーMDX:紙吹雪効果でコードボタンをコピーする


TLドクター
我々は、ギャツビーMDXブログのクリップボードボタンに汎用性の高いコピーを構築します.コードが退屈であるので、我々は紙吹雪爆発で少しの才能を加えます
我々がこのチュートリアルで構築するものの速いプレビュー.



ジムラッティ💥

どのように私は私の投稿に婚約🎉MDXとプリズム反応レンダラモジュールは、私の人生をより簡単にしました
午後14時17分
開始する前に、チェックすることができますlive example .

インストール


これから新しいギャツビープロジェクトを作成しますstarter template これは、MDXのブログとプリズム反応レンダラモジュールの組み込みサポートしています.
gatsby new gatsby-starter-blog-mdx https://github.com/hagnerd/gatsby-starter-blog-mdx

MDXの基礎を理解する


MDXのエントリポイントはMDXProvider 内部コンポーネントをMDXにマッピングするコンポーネントです.また、非常に重要な支柱を持っています.
The components propは、各HTML要素のデフォルトコンポーネントをオーバーライドできるオブジェクトですhere is a list for them ) またはもショートコードとして自分自身を提供します.
GatsbyテンプレートはMDXProvider インサイドwrapRootElement ブラウザAPI .
WraproOlementブラウザーAPIは、アプリケーションをラップするすべてのプロバイダーコンポーネントを設定するのに便利です.
以下の通りwrap-root-element.js ファイルを設定するMDXProvider そして、pre カスタム要素Code コンポーネント.
import React from "react"
import { MDXProvider } from "@mdx-js/react"
import { Code } from "./src/components/code"
import { preToCodeBlock } from "mdx-utils"

const components = {
  pre: preProps => {
    const props = preToCodeBlock(preProps)
    if (props) {
      return <Code {...props} />
    } else {
      return <pre {...preProps} />
    }
  },
}
export const wrapRootElement = ({ element }) => (
  <MDXProvider components={components}>{element}</MDXProvider>
)
それから、我々のラッパーは両方とも加えられますgatsby-browser and gatsby-ssr.js Gatsbyアプリケーションのルート要素をレンダリングするファイル.
import { wrapRootElement as wrap } from "./wrap-root-element"

export const wrapRootElement = wrap

カスタムコードコンポーネントを調整する


習慣Code コンポーネントの生活src/components/code.js ファイルと利用prism-react-renderer . The prism-react-renderer あなたのPrismJS強調表示コードでいくつかの余分なUIをレンダリングするのに最適な方法です.
ライブラリは、プリズムを使用してコードをトークン化し、すぐに反応にそれをレンダリングする小さなレンダリング小道具駆動コンポーネントを提供します.
デフォルトcode.js は以下の通りです.
import React from "react"
import { render } from "react-dom"
import Highlight, { defaultProps } from "prism-react-renderer"
import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live"

export const Code = ({ codeString, language, ...props }) => {
  if (props["react-live"]) {
    return (
      <LiveProvider code={codeString} noInline={true}>
        <LiveEditor />
        <LiveError />
        <LivePreview />
      </LiveProvider>
    )
  } else {
    return (
      <Highlight {...defaultProps} code={codeString} language={language}>
        {({ className, style, tokens, getLineProps, getTokenProps }) => (
          <pre className={className} style={style}>
            {tokens.map((line, i) => (
              <div {...getLineProps({ line, key: i })}>
                {line.map((token, key) => (
                  <span {...getTokenProps({ token, key })} />
                ))}
              </div>
            ))}
          </pre>
        )}
      </Highlight>
    )
  }
}
魔法は、内部で起こりますHighlight コンポーネント.The pre 要素は、コードラッパーをレンダリングし、レンダリングプロップ関数は、各行とトークン/単語に必要な小道具を提供します.
クリップボードのボタンへのコピーはpre 要素.

コピーボタンを作成する


コピーボタンは、コードセクションの右上隅に配置されます.
そのポジショニングを達成するために、pre 要素relative そして、詰め物の少しを加えてください.
<pre
  className={className}
  style={{
    ...style,
    padding: "2rem",
    position: "relative",
  }}
>
  ...
</pre>
The Button コンポーネントは単純なボタン要素ですposition: absolute :
const Button = props => (
  <button
    style={{
      position: "absolute",
      top: 0,
      right: 0,
      border: "none",
      boxShadow: "none",
      textDecoration: "none",
      margin: "8px",
      padding: "8px 12px",
      background: "#E2E8F022",
      color: "white",
      borderRadius: "8px",
      cursor: "pointer",
      color: "#E2E8F0",
      fontSize: "14px",
      fontFamily: "sans-serif",
      lineHeight: "1",
    }}
    {...props}
  />
)
より良いUXのために、あなたのユーザーは彼らの行動の結果について知らされなければなりません.それで、コードがコピーされるならば、それはボタンのテキストをトグルする素晴らしい余分の機能です.
それから、我々は反応フック状態変数を設定しなければなりませんisCopied .
const [isCopied, setIsCopied] = React.useState(false)
The isCopied 変数は、ユーザーがコピーボタンをクリックしたときにtrueを取得し、特定の時間(後に3秒)後にfalseにリセットします.
<Button
  onClick={() => {
    copyToClipboard(codeString)
    setIsCopied(true)
    setTimeout(() => setIsCopied(false), 3000)
  }}
>
  {isCopied ? "🎉 Copied!" : "Copy"}
</Button>
The copyToClipboard ここに私たちのコア機能です.私はからの機能を再利用しましたthis article .
const copyToClipboard = str => {
  const el = document.createElement("textarea")
  el.value = str
  el.setAttribute("readonly", "")
  el.style.position = "absolute"
  el.style.left = "-9999px"
  document.body.appendChild(el)
  el.select()
  document.execCommand("copy")
  document.body.removeChild(el)
}

最終コードコンポーネント


今までに、我々は習慣を持っているCode コンポーネントcopyToClipboard 関数とButton コンポーネント.次に、最後のコードコンポーネントは次のようになります.
export const Code = ({ codeString, children, language, ...props }) => {
  const [isCopied, setIsCopied] = React.useState(false)

  if (props["react-live"]) {
    return (
      <LiveProvider code={codeString} noInline={true}>
        <LiveEditor />
        <LiveError />
        <LivePreview />
      </LiveProvider>
    )
  } else {
    return (
      <Highlight
        {...defaultProps}
        code={codeString}
        language={language}
        theme={dracula}
      >
        {({ className, style, tokens, getLineProps, getTokenProps }) => (
          <pre
            className={className}
            style={{
              ...style,
              padding: "2rem",
              position: "relative",
            }}
          >
            <Button
              onClick={() => {
                copyToClipboard(codeString)
                setIsCopied(true)
                setTimeout(() => setIsCopied(false), 3000)
              }}
            >
              {isCopied ? "🎉 Copied!" : "Copy"}
            </Button>

            {tokens.map((line, i) => (
              <div {...getLineProps({ line, key: i })} style={style}>
                {line.map((token, key) => (
                  <span {...getTokenProps({ token, key })} />
                ))}
              </div>
            ))}
          </pre>
        )}
      </Highlight>
    )
  }
}

紙吹雪党


コードはうまく動作します.クリップボード機能へのコピーは完璧です.しかし、我々はまだ才能を逃します!
フロントエンド開発コミュニティに既知の秘密があります!

Everything is better with a little confetti


それはとても役に立たないです、しかし、我々は紙吹雪雨で我々の読者を祝うつもりです.
これを生命にもたらすために、我々は反応依存性をインストールしなければならないでしょうreact-dom-confetti .
    yarn add react-dom-confetti
設定はかなり簡単です.それはちょうどJSONオブジェクトですa couple of options :
const config = {
  angle: 90,
  spread: 360,
  startVelocity: 40,
  elementCount: 70,
  dragFriction: 0.12,
  duration: 3000,
  stagger: 3,
  width: "10px",
  height: "10px",
  perspective: "500px",
  colors: ["#a864fd", "#29cdff", "#78ff44", "#ff718d", "#fdff6a"],
}
次のステップはConfetti コンポーネント.この成分は、支柱ごとに紙吹雪に爆発するactive が真.
次に、iscopy変数を渡す必要があります.簡単に?
<Confetti active={isCopied} config={config} />
Confettiコンポーネントを配置するには、以前と同じポジショニングトリックを使用できます.なぜなら、ボタンの前で爆発を起こしたいからです.
我々は、AをセットアップしますWrapper コンポーネントConfetti and Highlight 属性を持つコンポーネントposition: relative . それから、紙吹雪のコンポーネントをConfettiWrapper は、右上隅に絶対配置されます.
<Wrapper>
  <Highlight>...</Highlight>

  <ConfettiWrapper>
    <Confetti active={isCopied} config={config} />
  </ConfettiWrapper>
</Wrapper>
と二つのラッパのコード:
const Wrapper = props => <div style={{ position: "relative" }} {...props} />

const ConfettiWrapper = props => (
  <div style={{ position: "absolute", top: 0, right: 0 }} {...props} />
)

それはすべての人々です


クローンGithub repository そして、私にタグ付けによってあなたの創造を私に示すのを忘れないでください💪
あなたがこのポストが好きであるならば、あなたは私がコード化、デザインとマイクロスタートアップをブートストラップすることについて毎日のヒントを共有するところで、私について続くことができます.