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

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

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

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

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

オプションの記事は長かったため、分割することにしました。

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

https://bossanova.uk/jspreadsheet/docs/upgrade-from-v4-to-v5
# オプション
## 初期表示時のカラム幅を自動調整する
Handsontableにはあるが、Jspreadsheetには`autoColumnSize`のような機能は提供されていません。
入力内容が下記の場合
|A|B|
|:--|:--|
|あいうえお|かきくけこさしすせそ|
|やゆよ|たちつてと|

* `defaultColWidth` の初期値 50 になっているため、狭いです
* Handsontableと違い`wordWrap`の初期値が`false`のため、幅に納まらない場合には折り返しされない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/29d7bfbf-8b35-e01f-e777-6c01937656d0.png)

ChatGPTで生成したプログラムを動くように修正しました。
`colAlignments: 'left'`に設定しているため、ヘッダーとデータが左寄せになります。

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

let table = new jspreadsheet(grid, {
  data: [
    ['あいうえお', 'かきくけこさしすせそ'],
    ['やゆよ', 'たちつてと'],
  ],
  colAlignments: 'left',
  onload:function(event) {
    for (let i = 0; i < this.options.columns.length; i++) {
      adjustColumnWidth(this, i);
    }
  }
});

function adjustColumnWidth(instance, columnIndex) {
  let maxWidth = 50; // 最小幅
  for (let rowIndex = 0; rowIndex < instance.getData().length; rowIndex++) {
    const cell = instance.getCellFromCoords(columnIndex, rowIndex);
    if (cell) {
      const cellContent = cell.innerText || cell.textContent;
      const tempElement = document.createElement('div');
      tempElement.style.position = 'absolute';
      tempElement.style.visibility = 'hidden';
      tempElement.style.whiteSpace = 'nowrap';
      tempElement.textContent = cellContent;
      document.body.appendChild(tempElement);

      const contentWidth = tempElement.offsetWidth;
      maxWidth = Math.max(maxWidth, contentWidth + 10); // パディングを追加
      document.body.removeChild(tempElement);
    }
  }
  instance.setWidth(columnIndex, maxWidth);
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/ec11bf53-37a3-a3f6-0abc-948a52410104.png)

## 列ヘッダーを表示する
Handsontableの`colHeaders`の機能に近いものは `columns`にあたる。
列ヘッダーは常に表示されます。
`colAlignments`を設定しない場合、ヘッダーとデータは中央寄せになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/d13193e8-9091-ca68-1792-bdd9857c6f3b.png)

* A, B と簡易なヘッダーが表示される

列ヘッダーを非表示にするにはCSSを使用します。
```css
.jexcel thead {
  display: none;
}
```
### title でカラムごとに設定する
```js
var grid = document.getElementById('grid');

let table = new jspreadsheet(grid, {
  data: [
    ['あいうえお', 'かきくけこ'],
    ['さしすせそ', 'たちつてと']
  ],
  columns: [
    { title: '列１'},
    { title: '列２'},
  ],
  defaultColWidth: 100
});
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/72d9de5a-cff6-839c-52b7-cbf27d17d7e0.png)

* `columns`のJSON配列内に`title`で列名を指定できる

### 列名を関数で処理して決定
Handsontableの`columns`と違い、関数には対応していない。

`columns`にJSON配列を関数で生成することは出来る。

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

let table = new jspreadsheet(grid, {
  data: [
    ['あいうえお', 'かきくけこ'],
    ['さしすせそ', 'たちつてと']
  ],
  columns:['列A', '列B'].map(title => ({ title })),
  defaultColWidth: 100
});
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/72d9de5a-cff6-839c-52b7-cbf27d17d7e0.png)

* `columns`属性の`title`を関数で生成しただけです

## 行ヘッダーを表示する
Handsontableの`rowsHeaders`に該当するものはありません。
行ヘッダーは常に表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/86f972ff-8b9e-0eb5-8a67-7f06219fb0b9.png)

* デフォルトは、1 始まりの連番が表示される

行ヘッダーを非表示にするにはCSSを使用します。
```css
.jexcel colgroup col:first-child {
    width: 0 !important;
}
```
もしくは、`hideIndex()`を使用します。
```js
table.hideIndex();
```

[Hide row number column #288](https://github.com/jspreadsheet/ce/issues/288)

### 行ヘッダー名変更
行数が確定しているなら、関数によりカスタマイズが可能

```js
['a', 'b', 'c'].map((title, index) => {
    const targetCell = document.querySelector(`td[data-y="${index}"].jexcel_row`);
    if (targetCell) targetCell.textContent = title;
});
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/52caf8e0-b6ab-49ec-5e3a-9216e721b1b2.png)

#### プラグイン
Pro v8以降なら行ヘッダー名変更プラグインがあります。
[jSpreadsheet Plugin : Row Header rename](https://www.npmjs.com/package/@jspreadsheet/rowheaderrename)

## 列幅を指定する
### 全ての列をまとめて設定する
Handsontableの`colWidths`の機能に近いものは `defaultColWidth`にあたる。

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

let table = new jspreadsheet(grid, {
  data: [
    ['あいうえお', 'かきくけこ'],
    ['さしすせそ', 'たちつてと']
  ],
  defaultColWidth: 200
});
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/2a954fa2-fd8b-68ed-90f9-bf04a0ce40fe.png)

### 列ごとに指定する（配列指定）
Handsontableの`colWidths`と同じ

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

let table = new jspreadsheet(grid, {
  data: [
    ['あいうえお', 'かきくけこ'],
    ['さしすせそ', 'たちつてと']
  ],
  colWidths: [200, 50]
});
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/60229173-7d60-c78b-371f-60b338c33d3b.png)

* 配列を渡せば、列ごとの幅を指定できる
* Handsontableと違い`wordWrap`の初期値が`false`のため、幅に納まらない場合には折り返しされない

### 列ごとに指定する（関数指定）
Handsontableの`colWidths`と違い、関数には対応していない。

## ソート
Handsontableの`columnSorting`と同じ

### カラム名のクリックでソートできるようにする

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

let table = new jspreadsheet(grid, {
  data: [
    ['a', 'd'],
    ['b', 'c']
  ],
  defaultColWidth: 80,
  columnSorting: true
});
```

![sort.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/2d3045f4-f173-148f-4f5b-b1d409367949.gif)

* `columnSorting` に `true` を指定すれば、カラム名をダブルクリックすることでソートできるようになる
* Handsontableと違い、デフォルト`true`でクリックではなくダブルクリックとなる、現在のソートの向きがデフォルト表示

### ソートさせてから表示する
Handsontableの`columnSorting.sortColumn`のような制御はないです。

### 現在のソートの向きを表示する
カラム名の横に三角印で現在のソートの向きが表示される。上向きが昇順、下向きが降順
Handsontableの`sortIndicator`のように表示有無の制御はなく、デフォルト表示される。

# 最後に
このオプションの元記事が長くて辛かったので、分割することにしました。