AdminLTEにreact-grid-layout機能を追加する（Visual Studio版）


I will apply react-grid-layout to Admin LTE built in last time.
[AdminLTE をVisual Studio でビルドする - Qiita](https://qiita.com/sugasaki/items/35cbfadbc4666f6c095b)

react-grid-layoutを使ってドロップ可能なグリッドシステムをAdminLTEに適用します。


![grid.gif](https://qiita-image-store.s3.amazonaws.com/0/49910/39fcd970-6bc3-b895-6b57-b2db392fa13c.gif)

# react-grid-layoutとは

React-Grid-Layout はPackery や Gridster　に影響を受けて作成された派生プロジェクト。

* [リポジトリ](https://github.com/STRML/react-grid-layout)
* [デモ](https://strml.github.io/react-grid-layout/examples/0-showcase.html)


## react-grid-layout機能を追加

[前回](https://qiita.com/sugasaki/items/35cbfadbc4666f6c095b)作ったAdminLTEに機能を追加していきます


### package.jsonの編集
package.jsonに以下を追記します。バージョン番号はその時の最新とします。

``` 
    "@types/react-grid-layout": "0.16.4",
    "react-grid-layout": "0.16.5"
``` 

### CSS参照の追加

ClientApp/css/site.cssに以下を追加。


```css:react-grid-layout.css
.react-grid-item:not(.react-grid-placeholder) {
    background: #ccc;
    border: 1px solid black;
}
```



<b>boot.tsxに追加</b>
function renderApp()の上に以下を追加します。

```ts:boot.tsx

import 'admin-lte';

// Font Awesome
import '../node_modules/font-awesome/css/font-awesome.min.css';
//// Ionicons
import '../node_modules/ionicons/dist/css/ionicons.css';
 //Morris chart
import '../node_modules/morris.js/morris.css';
// jvectormap
import '../node_modules/jvectormap/jquery-jvectormap.css';
// Date Picker
import '../node_modules/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css';
// Daterange picker
import '../node_modules/bootstrap-daterangepicker/daterangepicker.css';

//react-grid-layout
import '../node_modules/react-grid-layout/css/styles.css';
import '../node_modules/react-resizable/css/styles.css';

```


## コンポーネント作成


## コンポーネント作成

### ReactGridLayoutComponentコンポーネントを作成

ClientApp/components/Counter.tsxを複製してReactGridLayoutComponent.tsxに名前を変更します。（名前は別の名前でもOK。）
もしくは、ClientApp/components/ にReactGridLayoutComponent.tsxを作成します。


#### インターフェース
レイアウトを管理するインターフェースを作成します。
ファイル末尾でもいいし、import文の下でもOK。

```ts:ReactGridLayoutComponent.tsx
interface ReactGridLayoutItem {
    i: string;
    x: number;
    y: number;
    w: number;
    h: number;
    minW?: number;
    maxW?: number;
    static?: boolean;
}

interface ReactGridLayoutState {
    layout: Array<ReactGridLayoutItem>;
    loading: boolean;
}
```


#### クラス名
クラス名をReactGridLayoutComponentに変更します。
Component引数を以下のように変更します。

`RouteComponentProps<{}>, Array<ReactGridLayoutItem>`


変更後は以下になります。

```ts:ReactGridLayoutComponent.tsx
export class ReactGridLayoutComponent extends React.Component<RouteComponentProps<{}>, ReactGridLayoutState> {
```



#### インポート
importは以下になるようにします。

```ts:ReactGridLayoutComponent.tsx
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import * as ReactGridLayout from 'react-grid-layout';
```


#### コンストラクタの追加

コンストラクタを追加し、画面ロード時にReactGridLayoutに3×1のグリッドを作成するためのlayout変数を作成します。

```ts:ReactGridLayoutComponent.tsx
    constructor() {
        super();

        var layout: ReactGridLayoutItem[] = [
            { i: 'a', x: 0, y: 0, w: 1, h: 2 },
            { i: 'b', x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 },
            { i: 'c', x: 4, y: 0, w: 1, h: 2 },
            { i: 'd', x: 4, y: 4, w: 1, h: 2 },
            { i: 'e', x: 4, y: 6, w: 1, h: 2 },
            { i: 'f', x: 4, y: 8, w: 1, h: 2 },
        ];


        this.state = { layout: layout, loading: true };
    }

```

#### レンダー
renderには<ReactGridLayout >コンポーネントを表示するコードを記入します。

```ts:ReactGridLayoutComponent.tsx
    public render() {
        return <div>
            <ReactGridLayout className="layout" layout={this.state.layout} cols={12} rowHeight={30} width={1200}>
                <div key="a">a</div>
                <div key="b">b</div>
                <div key="c">c</div>
                <div key="d">d</div>
                <div key="e">e</div>
                <div key="f">f</div>
            </ReactGridLayout>
        </div>;
    }
```


最終的には以下になります。

```ts:ReactGridLayoutComponent.tsx
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import * as ReactGridLayout from 'react-grid-layout';


interface CounterState {
    currentCount: number;
}

export class ReactGridLayoutComponent extends React.Component<RouteComponentProps<{}>, ReactGridLayoutState> {
    constructor() {
        super();

        var layout: ReactGridLayoutItem[] = [
            { i: 'a', x: 0, y: 0, w: 1, h: 2 },
            { i: 'b', x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 },
            { i: 'c', x: 4, y: 0, w: 1, h: 2 },
            { i: 'd', x: 4, y: 4, w: 1, h: 2 },
            { i: 'e', x: 4, y: 6, w: 1, h: 2 },
            { i: 'f', x: 4, y: 8, w: 1, h: 2 },
        ];

        this.state = { layout: layout, loading: true };
    }

    public render() {
        return <div>
            <ReactGridLayout className="layout" layout={this.state.layout} cols={12} rowHeight={30} width={1200}>
                <div key="a">a</div>
                <div key="b">b</div>
                <div key="c">c</div>
                <div key="d">d</div>
                <div key="e">e</div>
                <div key="f">f</div>
            </ReactGridLayout>
        </div>;
    }

}


interface ReactGridLayoutItem {
    i: string;
    x: number;
    y: number;
    w: number;
    h: number;
    minW?: number;
    maxW?: number;
    static?: boolean;
}

interface ReactGridLayoutState {
    layout: Array<ReactGridLayoutItem>;
    loading: boolean;
}


```

## ルート情報の追加

#### ルート情報の追加

/ClientApp/routes.tsxに以下の2行を追加します。

```ts
import { ReactGridLayoutComponent } from './components/ReactGridLayoutComponent';`
<Route path='/reactgridlayout' component={ReactGridLayoutComponent } />
```

最終的には以下になります。

```ts:routes.tsx

import * as React from 'react';
import { Route } from 'react-router-dom';
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { FetchData } from './components/FetchData';
import { Counter } from './components/Counter';
import { ReactGridLayoutComponent } from './components/ReactGridLayoutComponent';

export const routes = <Layout>
    <Route exact path='/' component={ Home } />
    <Route path='/counter' component={ Counter } />
    <Route path='/fetchdata' component={ FetchData } />
    <Route path='/reactgridlayout' component={ReactGridLayoutComponent } />
</Layout>;

```



#### 画面メニューに参照の追加
ReactGridLayout画面を表示するリンクを追加します。
/ClientApp/components/MainSidebar.tsxに以下を追加します。

```ts
<ul className="treeview-menu">
    <li className="active">
        <NavLink to={'/reactgridlayout'} activeClassName='active'>
            <span className='glyphicon glyphicon-th-list'></span> react-grid-layout
        </NavLink>
    </li>
</ul>


```


### 実行
この状態で実行してみる。エラーなく表示されればOKです。

![grid.gif](https://qiita-image-store.s3.amazonaws.com/0/49910/39fcd970-6bc3-b895-6b57-b2db392fa13c.gif)


### うまく動かない場合

#### NPM系

NPMアイコンが以下のような場合はライブラリがインストールされていない場合があるので、
pacage.jsonを見直してみてください。

![image](https://qiita-image-store.s3.amazonaws.com/0/49910/2edf18d0-7e48-0606-f159-123029f8181d.png)

NPMを開けば何がインストールされていないかを知ることができます。

![image](https://qiita-image-store.s3.amazonaws.com/0/49910/5984940c-c955-a7a0-e70a-87f342b374bd.png)

#### VS系の原因

ただたんにVSが表示不具合を起こしている場合もあります。
再起動したら治る時があります。


## npm-shrinkwrap.jsonの再作成

コマンドプロンプトにて以下を実行する事で、npm-shrinkwrap.jsonを再作成する事ができます。

>npm shrinkwrap


## ソース
今回のソースは以下に置いてます。

[Admin-LTE-VS/2 admin-lte-react-grid-layout at master · sugasaki/Admin-LTE-VS](https://github.com/sugasaki/Admin-LTE-VS/tree/master/2%20admin-lte-react-grid-layout)