Jspreadsheet 使い方メモ２-2（グリッドのオプション）

# はじめに
これは、[Jspreadsheet Advent Calendar 2024](https://qiita.com/advent-calendar/2024/jspreadsheet)の18日目の記事となります。

「[Handsontable 使い方メモ２（グリッドのオプション）](https://qiita.com/opengl-8080/items/9d25e106ff48b66cb908)」と比較しながら書いていきます。

※全てのオプションは網羅していません。

全てのオプションを確認したい場合は、 [公式ドキュメント](https://bossanova.uk/jspreadsheet/v4/docs/quick-reference) を参照

オプションの記事は長かったため、分割することにしました。
* [Jspreadsheet 使い方メモ２-1（グリッドのオプション）](https://qiita.com/yaju/items/2a348462b95a26e118f4)

## 注意
現時点(2025/03/09)では、Jspreadsheet CE Version 4 を対象としています。
Version 5 のリリースにより見直す予定です。

https://bossanova.uk/jspreadsheet/docs/upgrade-from-v4-to-v5

# オプション
## コンテキストメニュー
### メニューを有効にする
デフォルトでコンテキストメニュー(`contextMenu`)は有効になっている。

```js
let grid = document.getElementById('grid');

let table = new jspreadsheet(grid, {
  minDimensions: [5,5],
  contextMenu: true
});
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/4cfb20de-b9b0-e1d1-0782-122fc291f370.png)

* `contextMenu` に `true` を設定すると、右クリックでメニューが開くようになる
* デフォルトで、いくつかのメニューが表示されるようになっている

### 表示するメニューをカスタマイズする
[コンテキストメニューの設定 Jspreadsheet のメモ - 閑古鳥ブログ](https://kankodori-blog.com/posts/2024-03-03/#%E3%82%B3%E3%83%B3%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%83%A1%E3%83%8B%E3%83%A5%E3%83%BC%E3%81%AE%E8%A8%AD%E5%AE%9A)

```js
let grid = document.getElementById('grid');

let table = new jspreadsheet(grid, {
  minDimensions: [5,5],
  contextMenu: function(obj, x, y, e) {
    let items = [];

    if (obj.options.allowInsertRow == true) {
      items.push({
        title: obj.options.text.insertANewRowBefore,
        onclick: function() {
          obj.insertRow(1, parseInt(y), 1);
        }
      });
    }

    return items;
  }
});
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/18b654c5-7abf-fe0d-793d-765e0fa934dd.png)

* 表示させたい項目だけを配列で指定できる
* 以下が、指定できる文字列(全部ではない)
https://github.com/jspreadsheet/ce/blob/master/resources/lang/en_GB.json

|値|説明|デフォルト|
|:--|:--|:--|
|insertANewRowBefore|選択している行の上に空行を挿入|デフォルト|
|insertANewRowAfter|選択している行の下に空行を挿入|デフォルト|
|deleteSelectedColumns|選択している列を削除|デフォルト|
|Copy...|コピー|デフォルト|
|Paste...|貼り付け|デフォルト|
|Save as...|ファイルに保存|デフォルト|
|About|JspreadSheetの情報|デフォルト|
|insertANewColumnBefore|選択している列の前に空列を挿入|デフォルト|
|insertANewColumnAfter|選択している列の後に空列を挿入|デフォルト|
|renameThisColumn|選択している列名を変更| |
|orderAscending|昇順に並び替え| |
|orderDescending|降順に並び替え| |
|deleteSelectedRows|選択している行を削除| |
|editComments|コメントの編集| |
|addComments|コメントの追加| |
|clearComments|コメントの削除| |

### デフォルトで使用できるメニューのラベルを変更する
`text`属性に該当する日本語を割り当てる。
https://github.com/jspreadsheet/ce/blob/master/resources/lang/en_GB.json

```js
let grid = document.getElementById('grid');

let table = new jspreadsheet(grid, {
  minDimensions: [5,5],
  text: {
    insertANewRowBefore: '上に行を挿入',
    insertANewRowAfter: '下に行を挿入',
    deleteSelectedRows: '選択行の削除',
    copy: 'コピー',
    paste: '貼り付け'
  }
});
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/6d477597-e6bb-7feb-da22-410014cbf262.png)

### メニュー項目を自作する
Jsuitesのコンテキストメニューを使用しているので、下記サイトが参考になる。
[Jsuites - JavaScript contextmenu](https://jsuites.net/docs/v4/contextmenu/submenu)

#### 基本
セパレートは`items.push({ type:'line' });`で追加する。

```js
let grid = document.getElementById('grid');

let table = new jspreadsheet(grid, {
  minDimensions: [5,5],
  contextMenu: function(obj, x, y, e) {
    let items = [];

    items.push({
      title: 'メニュ～',
      onclick:function() {
        console.log(`${x},${y},${e}`);
      }
    });

    return items;
  }
});
```


* `contexMenu` にオブジェクトを渡すようにする
  * `items` というプロパティに上書きする設定内容を持った オブジェクト を渡す
  * `title` が、表示で使用される文字列
  * `shortcut` に、ショートカットキーを指定できる
  * `onclick` に、メニューが選択されたときのコールバック関数を指定できる

### ラジオボタン(ぽいもの)を実装する
Handsontableでは名前`name`に関数を指定できるが、名前`title`には文字列しか指定できないため、同じようには実装できない。

### サブメニュー
```js
let grid = document.getElementById('grid');

let table = new jspreadsheet(grid, {
  minDimensions: [5,5],
  contextMenu: function(obj, x, y, e) {
    let items = [];

    items.push({
      title: 'my menu',
      submenu: [
        {
          title:'sub menu 1',
          onclick:function() {
            alert('SubMenu 1');
          }
        },
        {
          title:'sub menu 2',
          onclick:function() {
             alert('SubMenu 2')
          }
        }
      ]
    })

    return items;
  }
});
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/9c06d747-e3b9-6458-35f6-5ee221dd670e.png)

* submenu で次の階層のメニューを追加できる

### 有効無効
```js
let grid = document.getElementById('grid');

let table = new jspreadsheet(grid, {
  minDimensions: [5,5],
  contextMenu: function(obj, x, y, e) {
    let items = [];

    items.push({
      title: 'item1',
      disabled: true
    })
    items.push({
      title: 'item2',
      disabled: false
    })

    return items;
  }
});
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/82010629-1df9-f199-b32d-37bdee0653cd.png)

* `disabled` に boolean を設定することで、項目の有効・無効を指定できる
* Handsontable のように関数による有効無効の切り替えはできない

## コメント
Handsontableの`comments`の機能に近いものは`allowComments`にあたる。
Handsontableの`contextMenu`はない為、コンテキストメニューをカスタマイズする。

```js
let grid = document.getElementById('grid');

let table = new jspreadsheet(grid, {
  minDimensions: [5,5],
  allowComments: true,
  text: {
    addComments: 'コメントの追加',
    editComments: 'コメントの編集',
    clearComments: 'コメントの削除'
  },
  contextMenu: function(obj, x, y, e) {
    let items = [];

    if (obj.options.allowComments == true) {
      let title = obj.records[y][x].getAttribute('title') || '';

      items.push({
        title: title ? obj.options.text.editComments : obj.options.text.addComments,
        onclick:function() {
          let comment = prompt(obj.options.text.comments, title);
          if (comment) {
            obj.setComments([ x, y ], comment);
          }
        }
      });

      if (title) {
        items.push({
          title:obj.options.text.clearComments,
          onclick:function() {
            obj.setComments([ x, y ], '');
          }
        });
      }
    }

    return items;
  }
});
```
![comment.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/7d7eaed5-4cc8-a99d-db5f-3ad40af76d94.gif)

* `allowComments` を `true` にするとセルへのコメント追加を許可(デフォルト：false)

### 任意のセルにコメントを設定する
Handsontableの`cell`の機能に近いものは`comments`にあたるが、Pro版でのみ使用できる。
https://jspreadsheet.com/docs/comments

```js
let grid = document.getElementById('grid');

let table = new jspreadsheet(grid, {
  minDimensions: [5,5],
  allowComments: true
}

table.setComments([ 1, 1 ], 'Hello Comment');
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/270d80d1-0a41-25cf-3a7d-94315376e682.png)

* `setComments`メソッドで、任意のセルにコメントを記述できる

## コピペの有効・無効
Handsontableの`copyPaste`の機能にあたるものはありません。

## コピーできる列数を制限する
Handsontableの`copyColsLimit`の機能にあたるものはありません。

## 罫線の設定
Handsontableの`customBorders`に近いものは`style`にあたる。
Handsontableの`contextMenu: ['borders']`のようなものはない。
https://jspreadsheet.com/docs/style

### 任意の場所に罫線を引く
```js
let grid = document.getElementById('grid');

let table = new jspreadsheet(grid, {
  data: [
    [1, 2, 3],
    [1, 2, 3],
    [1, 2, 3]
  ],
  allowComments: true,
  style: {
    B2: 'border-top: 3px solid red; border-right: 3px solid #1199ff'
  },
});

table.setStyle('C3', 'background-color', 'orange');
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/5b7ff297-23ad-2e61-e77d-532d40ddf6c1.png)

* 罫線を引く場所に CSS を渡すことで、任意の場所に罫線を引くことができる
* `setStyle`メソッドで指定セルにスタイルを設定できる

### 範囲を指定して罫線を引く
Handsontableの`customBorders.range`にあたるものはない
Pro版ではセル指定で`'A:A'`として範囲指定することはできるようだが、CE版は範囲指定ができない。

# 最後に
空データを指定するのに`data: Array.from({ length: 5 }, () => Array(5).fill(''))`と最初に指定していたが、`minDimensions: [5,5]`を見つけて書き直しました。