【React + Typescript】ボタン一つでコンポーネントのscssをコピーできるサイトを作った


こんにちは、Yuiです。

先週から引き続き週イチ発信をしています。
今週はボタン一つでコンポーネントのscssをコピーできるサイトを作ったので、その紹介をします。

サイトはこちら→https://ui-components.com/
github→https://github.com/YuikoIto/ui-components

機能紹介

今回、入れている機能は以下です。

  • 表示されているコンポーネントをクリックすると内部のコードが見える
  • コードをボタン一つでコピーできる

めちゃくちゃシンプルです。

そして今はローディングアニメーションボタンインプットの3パターンしか用意できてないですが、徐々に増やしていきます!
正直このままだとコンポーネント集というよりもアトム集になりかねないので、今後はフォーム一式とかも作ってきたいなと思っています。

ローディングアニメーション

ボタン

インプット

ちなみに今回要素をクリックしたらモーダルが開くようになっているので、インプットに関しては子要素のクリックで親要素が開かないように、stopPropagationを使って調整しました。

後半につれて要素の数が少なくなっていっているのは、私が力尽きたからです。
徐々に増やしていきます。

なぜ作ったか

作った動機ですが、私がずっと欲しかったからです。笑
私は仕事柄よくアニメーションを書くことがあるのですが、何回書いてもアニメーションは覚えることができないし、アニメーション以外でもshadowの感じとかは毎回ググってるしでいい加減効率が悪くない?と思うようになりました。

cssフレームワークを使えたらそれが一番ラクだとは思うんですけど、案件によっては入れることができなかったり、ちょっとしたアニメーションはやはり直接書かないといけないというときが出てきます。

そういうときに少しでも時間を短縮できたら良いなと思って、作りました。
デザインは100%私の好みです!シンプルなのが好きなので2色+白で統一してます。

雛形作成

今回も、React+TSで作りました。

npx create-react-app ui-components --template typescript
yarn add react-router-dom @types/react-router-dom
yarn add node-sass@5

node-sass@5に指定しているのは、create-react-appで作ったアプリがnode-sass@6に対応してないためNode Sass version 6.0.0 is incompatible with ^4.0.0 || ^5.0.0.のエラーが出るのを防ぐためです。

個人的にimport時のパス先が長くなるのは嫌なので、scssたちはsrc/stylesフォルダを作って、デフォルトでbaseとなるパスをsrcにしておきます。

tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "src"
  },
}

これでimport ./styles/App.scssと書かないといけないところからimport styles/App.scssとかけるようになりました。

こういう設定はいつも業務ではやるものの個人開発ではサボりがちなので、ちゃんとやっていきたいですね。

というわけでこれでひな形が完成。

全体の構成

全部書くと長いので、ざっくりとした構成だけ書きます。

src/
 ├ components/
 │ └ button/
 │ └ common/
 │ └ input/
 │ └ loader/
 ├ hooks/
 ├ models/
 ├ pages/
 ├ styles/
 │ └ styles.scss
 │ └ components/
 │     └ button/
 │     └ common/
 │     └ input/
 │     └ loader/
 └ App.tsx

まず、今回の肝となるコード表示部分に関してはsrc/components配下にまとめています。
下の画像で表示しているコードたちです。

これは例えばbutton1クラスだと、以下のようにしてbutton1.tsを作って格納しています。

button1.ts
import { styleModel } from "models/styleModel";

export const button1: styleModel = {
  title: "button1",
  scss: `.button1 {
  position: relative;
  border-radius: 20px;
  margin: 65px auto;
  width: 150px;
  padding: 1rem 0;
  font-size: 1.5rem;
  background: #333c5f;
  color: #fff;
  font-weight: bold;
  -webkit-box-shadow: 0 5px 0 #576199;
  box-shadow: 0 5px 0 #576199;
  &:hover {
    -webkit-transform: translate(0, 3px);
    transform: translate(0, 3px);
    -webkit-box-shadow: 0 2px 0 #576199;
    box-shadow: 0 2px 0 #576199;
  }
}
`,
} as const;

そして、buttonフォルダの中にindex.tsを作って、button1〜button4を以下のように格納します。

index.ts
import { button1 } from "components/button/button1";
import { button2 } from "components/button/button2";
import { button3 } from "components/button/button3";
import { button4 } from "components/button/button4";
import { styleModel } from "models/styleModel";

export const buttons: styleModel[] = [button1, button2, button3, button4];

後はbuttonsをmapでまわして表示をするだけです。この辺もう少し効率良いやり方があれば知りたいです。

hooksフォルダに関してはモーダルの開閉部分をカスタムフックにして入れてますが、正直これぐらいの量なら直接ファイル内に書いても良かったかなとも思いました。

modelsに独自で作成した型を格納しています。
そして実際にレイアウトを整えるのに使うscssに関してはstyles配下にまとめています。

シンタックスハイライトの設定をする

今回、コードの表示をするということで、絶対にシンタックスハイライトを導入したかったので、良さそうなライブラリを探しました。

highlight.jsが軽量で良さそうなので、それを使います。

yarn add highlight.js @types/highlight.js
import hljs from "highlight.js";
import "highlight.js/styles/atom-one-dark.css";
// 今回はscssに関するハイライトなので、以下をインポート
import scss from "highlight.js/lib/languages/scss";
// これでpreタグ内のcodeタグにクラス名としてscssを付与すれば良い
// つけなくてもハイライトはされるが、つけると確実にその言語に沿ったハイライトをしてくれるらしい。
hljs.registerLanguage("scss", scss);

const CodeBlock = ({ style }: Props) => {
// このコンポーネントが描画されるタイミングでハイライトをかける
  useEffect(() => {
    hljs.highlightAll();
  });

  return (
    <div className="code-block-container">
      <div className={style.title}></div>
      {isOpen && (
        <Modal closeModal={clickHandler} copyText={copyText}>
          <code>&lt;div class="{style.title}"&gt;&lt;/div&gt;</code>
          <pre>
            <code className="scss">{style.scss}</code>
          </pre>
        </Modal>
      )}
    </div>
  );
};

シンタックスハイライトを付けたい部分に関しては<pre><code></code></pre>で囲います。
私はdivタグの要素に関しては、シンタックスハイライトをつけたくなかったので、<code>タグだけにしました。

ちなみに、シンタックスハイライトにはダークモードライトモードがありますが、私は今回ダークモードで表示をしたかったので、以下のリンクから今回適用しているatom-one-darkモードを見つけました。

本当はdivタグ要素にはライトモードを適用して、scss部分にはダークモードを適用して、よりわかりやすくしたかったんですが、別ファイルでライトモードを適用してインポートしても全部ダークモードに上書きされてしまったので諦めました。
そのうちなんとかすると思います。

デプロイ

今回もデプロイはVercelを使いました。
めちゃくちゃ簡単にデプロイできるので好きです。

制作時間

大体20時間ぐらいです。
そのうち15時間ぐらいは素材をせっせと書いてました。
15時間かかってこんだけしかかけないのかと絶望してますが、このサイトに追加していったら今後はコピペで大丈夫なので結果的に時間短縮になるだろうと信じてます。笑

あとがき

完全に私得なコンポーネント集ができました。
後は要素を増やしていくだけなので、どんどん増やしていきます。

もしこんなのほしい、というのがあればリクエストいただければ嬉しいです。