LeaderLineというHTML要素同士に線を引けるライブラリを使ってみる。(React × TypeScript) 

# 概要
Webページ内の情報を線で結ぶことがしたいので該当技術を色々探していた。
Webページ内の情報全てをSVGにしてしまう方法などが考えられたが、設定すべきパラメータが多くなることが考えられたので、こちらも辞めた。

SVGを埋め込むようなことが出来るらしいが、調査していくと新しく理解することが多くなったので、既存のライブラリ（[LeaderLine](https://anseki.github.io/leader-line/)）で簡単に実現することにした。

**以下では、自分が実装した範囲での実用例を中心に示していきます。**

**LeaderLineのサンプル**
![leader_line_sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280987/8bc58170-56ef-3094-3f84-20ecc9e32671.png)

# 前置き
普段は自然言語処理を少しやる大学院生で、必要に応じてTypeScript, JavaScript, Reactを使用しているため誤用や実装上の不備が多く見られるかもしれません。この記事に有用性を感じて、それらについてご指摘を頂いた場合には出来る限り更新するよう考えています。

本記事は初学者の立場として、参考に出来るものが少なかったのでメモとして残しています。

# LeaderLineについて
TypeScriptで利用するためにTypeが必要になるのですが、[leader-line](https://www.npmjs.com/package/leader-line)では対応していなく、[leader-line-new](https://www.npmjs.com/package/leader-line-new)という方を用いる必要があります。

要素を線で結べるらしい方法はいくつかまとめられていたので、こちらの記事で問題が解決できなかった場合には参照してみて下さい。

参照：[JSで要素間を線で繋いだり動かしたりライブラリ調査@202002 - Qiita](https://qiita.com/mmt/items/0dcd7a24a1dd4baca821)

# とりあえず使ってみる（JavaScript）
JavaScriptでとりあえず使ってみる。

```html:sample1.html
<!DOCTYPE html>
<head>
    <script src="anseki-leader-line-fc2bfda\leader-line.min.js"></script>
    <style type="text/css">
        html{
            margin: auto;
        }
        .main_comment{
            display: inline-block;
        }
        .comments{
            display: inline-block;
        }
        .main_comment{
            width: auto;
            margin: 5rem;
            border: 5px solid tomato;
        }
        .comment{
            width: auto;
            margin: 1rem;
            border: 5px solid turquoise
        }
        </style>
</head>
<body>
    <div class="map">
        <div class="main_comment" id="mc1">メインコメント</div>
        <div class="comments">
            <div class="comment" id="c1">コメント１</div>
            <div class="comment" id="c2">コメント２</div>
        </div>
    </div>
    <script>
        new LeaderLine(
            document.getElementById("mc1"),
            document.getElementById("c1")
            );
        new LeaderLine(
            document.getElementById("mc1"),
            document.getElementById("c2")
        )
    </script>
</body>
```
表示すると下記画像のようなものができます。
htmlファイルを表示したものをデベロッパーツールなどで見ていただくとsvgで色々やってくれているのが分かるかと思います。

**sample1.htmlの結果**
![sample1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280987/830a1b8d-d7fc-2605-8e30-0e30226899ba.png)

# React × TypeScriptでやってみる
まず、Reactのプロジェクトを雛形を利用して作成する。
参照：[create-react-appで React + Typescript な環境を構築する - Qiita](https://qiita.com/sunnyG/items/05c2e9381d6ba2d9fccf)


**実行環境**
```node```: v14.5.0
```create-react-app```: 3.4.1

```
create-react-app {プロジェクト名} --typescript
```

プロジェクト名を```ll_sample```として、

```:例）
create-react-app ll_sample --typescript
```

※もしかしたら```@types/react```に欠損があるかもしれないので、```import React from "react"```の部分で怒られていたら下記を実行してみてください。

```
npm install @types/react
```


上記で述べたように```leader-line-new```をライブラリとして用います。```cd ll_sample```で移動してから、

```
npm install leader-line-new
```

混乱が生じないように、作成される```App.tsx```について変更を加えて、機能を把握することを行っていきます。

```typescript:App.tsx
import React, {useEffect, useState} from 'react';
import './App.css';
import LeaderLine from "leader-line-new";

const App: React.FC = () => {
  // initialLinksで初期化
  const initialLinks: LeaderLine[] = [];
  const [links, setLinks] = useState(initialLinks);
  
  const [link_number, setLinknumber] = useState(0);
  const [num, setNum] = useState(3);

  useEffect(() =>{
    // removeしなければリンク表示が残ったままである
    if (links.length > 0){
      links.filter(l => {
        l.remove();
      })
    }
    let link_id: number = Math.floor(link_number % num);
    let start = document.getElementById("mc_0");
    let end = document.getElementById(String(link_id));

    if (start && end){
      let link = [];
      link.push(new LeaderLine(start, end, {startSocket:"right", endSocket:"left"}));
      setLinks(link);
    }

  },[link_number]);

  // クリックされたら動くfunction
  // setLinknumberでリンク先を変更する
  const handleOnclick = () =>{
    setLinknumber(link_number+1);
  }

  return(
    <div>

      <div className="area">

        <div className="main_comment_area">
          <div className="main_comment" id="mc_0">
            メインコメント
          </div>
          <button onClick={handleOnclick}>リンク先変更</button>
        </div>

        <div className="comments">
          {
            (()=>{
              let comments = [];
              for (let i=0; i<num; i++){
                comments.push(
                  <div className="comment" id={String(i)}>
                    コメント{i}
                  </div>
                )
              }
              return comments;
            })()
          }
        </div>

      </div>

    </div>
  );
}

export default App;

```

これに伴って、LeaderLineの矢印の表示が見やすくなるように簡単に```App.css```を整えます。

```css:App.css
.area{
  display: flex;
}

.main_comment_area{
  margin: 0 20rem 0 0;
}

.main_comment{
  border: 5px solid tomato;
  margin: 5rem;
}

.comment{
  margin: 1rem;
  border: 5px solid turquoise;
}

```

この２つを変更することに加えて、```index.tsx```において```import "./index.css"```をコメントアウト等で無効にしておいてください。
**ここまで終えたら```npm start```等で実行して表示を確認してみて下さい。**

初期状態では下記画像のようになっているかと思います。以降ボタン押すことによって```handleOnclick```が作動し、その中で```link_number```が変更されることで```useEffect```が動き```LeaderLine```による描画の変更が行われます。


**①初期状態**
![初期状態.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280987/7baf7a0e-7d6e-2eee-7605-8dc127a1bdbd.png)

**②「リンク先変更」ボタンを1回押すと・・・**
![step2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280987/38f08c72-b885-65f4-0aa4-2915a2556242.png)

**③「リンク先変更」ボタンをもう1回押すと・・・**
![step3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280987/8df85afe-07cf-69d1-3c78-cf200c7d8157.png)

**④「リンク先変更」ボタンをさらに1回押すと・・・**
![初期状態.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280987/1c4aae2a-de04-2bee-6b4f-a2e2418f3de9.png)


## LeaderLineが関係する部分のプログラムの説明
* **```l.remove()```について** 
```remove()```をしないと、```new LeaderLine()```で生成された描画が残った状態で引き継がれてしまうので注意が必要。

* **```LeaderLine(start, end, {startSocket:"right", endSocket:"left"})```について**   
```startSocket```,```endSocket```はそれぞれ要素のどの位置（```top```,```bottom```,```right```,```left```）にリンクは接続するのかということをオプションとして設定できるようになっています。  
詳しくは[公式ドキュメント](https://github.com/anseki/leader-line)を参照してください。

**これで、```LeaderLine```のReact×TypeScriptでの機能確認は終了となります。**

# 最後に
本記事内で「コメント」という単語を使っている通り、レビューコメントに対して処理を施すということを実験的にやりたくて```LeaderLine```を使用しました。下記リンク先でも```LeaderLine```をとりあえず使いましたが、表示は今後いろいろ弄る予定です。
[レビューコメントを収集してマップ化する機能（いつか公開したい）- GitHub](https://github.com/uchida0/CommentMap_ReactFlask)

**リンク先のマップ化のやつ**
![whisky_sample_leader_line_added.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280987/78016543-489b-8ecf-e60d-fd45ada19ede.png)


~~**（早く就活終わらしていろいろやりたい・・・）**~~