@threestarsoftware/tssgrid 0.1.33 → 0.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
3
3
  TssGrid の各リリースの変更点。日付は JST。形式は [Keep a Changelog](https://keepachangelog.com/ja/1.1.0/) 準拠(ゆるめ)。
4
4
  数値は特記なき限り**自社調べ**(headless Chrome / Windows 11・環境差あり)。
5
5
 
6
+ ## [0.1.35] - 2026-11-06
7
+
8
+ ### 追加(プラグイン)
9
+ - **`tss-sort`(見出しクリックで並べ替えUI)を追加**。`tss-filter` と同じ形のプラグインで、各リーフ見出しに **▲▼** を出し、押すたび **昇順 ▲ → 降順 ▼ → 元の順(`clearSort`)** を1列で循環。中身はコアの `sortBy` / `clearSort` を呼ぶだけ(多段は従来どおり `sortByCols` を直接)。`onHeaderClick` / `onHeaderRender` を**連鎖**(既存指定を保ちつつ上乗せ)。`columns`(対象列限定・既定=全リーフ列)/`initial:{col,dir}`(初期の並び)/`iconOnly`(▲▼ の所だけで並べ替え・本体クリックは純粋に列選択)。**本体クリックの列選択は従来どおり**活き、**空行(`minSpareRows`)はコア仕様どおり常に最下部**。`tss-filter` と併用しても ▲▼(ソート)と漏斗(フィルタ)は並ぶだけで**ぶつからない**。API: `getPlugin('sort').getState()`/`.sortBy(col,dir)`/`.clear()`。例 `examples/sort-plugin.html`。従来の手組み例(`examples/sort.html`・`multisort.html`・`sort-filter.html`)はそのまま有効。
10
+
11
+ ### 修正(プラグイン)
12
+ - **`tss-toggle` が関数の `readOnly` を見ていなかった不具合を修正**。列の `readOnly` が関数(`(val,{r})=>boolean`)だと、読取専用のセルでもセグメントを押せて値が変わってしまっていた(`cfg.readOnly === true` だけを見ていたため)。本体と同じ **`_isReadOnly(r, c)`** で判定するようにし、**関数 `readOnly`・セル単位RO・結合の従属・`grid.readOnly`** をすべて尊重。jimu で「直せないときはプラグインを付けない」でしのいでいた回避が不要に。
13
+
14
+ ## [0.1.34] - 2026-11-05
15
+
16
+ ### 改善(プラグイン)
17
+ - **`tss-toggle` を 2択から N択(3つ以上)へ拡張**。これまで内部で選択肢を2つに切り詰めていた(`slice(0,2)`)のを撤廃し、`columns: { ans: ['未回答','出席','欠席'] }` のように **3つ以上の択一**を「並んだセグメント=ラジオ的に全選択肢が常に見える」形で置けるようにした。クリック確定(`setValue`=検証・Undo を通る)・許可値以外の validator 拒否・アクティブセルで **Space は次の値へ循環**(2択なら従来どおり反対側)は据え置き。値は dropdown 同様 **value を保存**。**短いラベルなら dropdown と同程度の幅**で収まり、タップ数は「開く→選ぶ」の 2 から **1** に。用途: 出欠(未回答/出席/欠席)・優先度(高/中/低)など少数の短い択一。選択肢が多い/ラベルが長い/列幅が狭い場合は従来どおり `type:'dropdown'` が省スペース。例 `examples/toggle.html` に出欠3択パネルを追加。**後方互換**(2択の既存指定・`TssToggle.format()` 直指定は不変)。
18
+
6
19
  ## [0.1.33] - 2026-11-05
7
20
 
8
21
  ### 追加
package/README.md CHANGED
@@ -382,6 +382,24 @@ grid.getPlugin('filter').clearAll(); // 全条件クリア
382
382
  ```
383
383
  - 値候補は**マスタ全行から集計**(絞り込み中でも全値が出る)。フィルタ済み列に印。値は HTML エスケープ。
384
384
 
385
+ ### 同梱プラグイン: 見出しクリックで並べ替え(sort UI)
386
+
387
+ `plugins/tss-sort.js`+`.css`= 各見出しに **▲▼** を出し、押すたび **昇順 ▲ → 降順 ▼ → 元の順** を1列で循環。中身はコアの **`sortBy` / `clearSort` を呼ぶだけ**。**列選択は今までどおり**効き、**空行(`minSpareRows`)は常に最下部**。`tss-filter` と併用しても ▲▼(ソート)と漏斗(フィルタ)は**並ぶだけでぶつからない**。動く例: [`examples/sort-plugin.html`](https://tssgrid.threestarsoftware.co.jp/examples/sort-plugin.html)。
388
+
389
+ ```html
390
+ <link rel="stylesheet" href="plugins/tss-sort.css">
391
+ <script src="plugins/tss-sort.js"></script>
392
+ ```
393
+ ```js
394
+ plugins: [ TssSort.plugin() ] // 全リーフ列に ▲▼
395
+ plugins: [ TssSort.plugin({ columns:['createdAt', 'workDate'] }) ] // 対象列を限定
396
+ plugins: [ TssSort.plugin({ initial:{ col:'createdAt', dir:'desc' } }) ] // 初期の並び
397
+ plugins: [ TssSort.plugin({ iconOnly:true }) ] // ▲▼ の所だけ(本体クリックは純粋に選択)
398
+ grid.getPlugin('sort').getState(); // { col, dir } または null
399
+ grid.getPlugin('sort').clear(); // 入力順へ戻す
400
+ ```
401
+ - 単一列ソート(多段は `grid.sortByCols(specs)` を直接)。`onHeaderClick` / `onHeaderRender` フックを**連鎖**して使う(既存指定を保ちつつ上乗せ)。`sortBy`→再描画→`onHeaderRender` 再実行で ▲▼ の点灯も自動更新。
402
+
385
403
  ### 同梱プラグイン: 共有マスタ(複数グリッドで1データ)
386
404
 
387
405
  `plugins/tss-shared.js`= **1つのマスタ(同じ行データ)を複数グリッドで共有**。左右を**別フィルタ/別ソート**で表示でき、**片方の編集がもう片方の同じ行へ即反映**(行参照を共有)。A系/B系を並べて突き合わせ編集する用途。動く例: [`examples/shared-master.html`](https://tssgrid.threestarsoftware.co.jp/examples/shared-master.html)。
@@ -817,9 +835,9 @@ new TssGrid(el, { columns, data, onAfterChange: () => { chart.data = toChartData
817
835
  ```
818
836
  - **おすすめ(いずれも `<script>`/CDN で no-build)**: **Chart.js**(MIT・定番・バランス◎)/ **ApexCharts**(MIT・モダンで映え)/ **Apache ECharts**(Apache-2.0・多機能)/ **uPlot**(MIT・超軽量)。Highcharts/AG Charts は商用有料なので MIT 方針なら避ける。
819
837
 
820
- ### 同梱プラグイン: 2択トグル(toggle・有効|無効)
838
+ ### 同梱プラグイン: 択一トグル/ラジオ(toggle・2択以上)
821
839
 
822
- `plugins/tss-toggle.js`+`.css`= ラジオの2択を**並んだセグメント(有効|無効)**で表示、クリックで切替。チェックボックスより**2ラベルが見える**。値は dropdown 同様 **value を保存**。動く例: [`examples/toggle.html`](https://tssgrid.threestarsoftware.co.jp/examples/toggle.html)。
840
+ `plugins/tss-toggle.js`+`.css`= 択一(**2つ以上**)を**並んだセグメント(有効|無効/未回答|出席|欠席)**で表示、クリックで切替。ラジオボタンの「**全選択肢が常に見える・1クリックで確定**」をセル内で実現。値は dropdown 同様 **value を保存**。動く例: [`examples/toggle.html`](https://tssgrid.threestarsoftware.co.jp/examples/toggle.html)。
823
841
 
824
842
  ```html
825
843
  <link rel="stylesheet" href="plugins/tss-toggle.css">
@@ -827,12 +845,13 @@ new TssGrid(el, { columns, data, onAfterChange: () => { chart.data = toChartData
827
845
  ```
828
846
  ```js
829
847
  plugins: [ TssToggle.plugin({ columns: {
830
- status: ['有効', '無効'], // 文字列2つ(value=label)
848
+ status: ['有効', '無効'], // 文字列(value=label)。2つ以上でOK
849
+ ans: ['未回答', '出席', '欠席'], // 3択以上も可
831
850
  pub: [{ value:'1', label:'公開' }, { value:'0', label:'非公開' }], // value/label 分離
832
851
  } }) ]
833
852
  ```
834
- - クリックでその側に確定(`setValue`=**検証・Undo を通る**)。アクティブセルで **Space** で反対側へトグル。2値以外の打ち込みは自動 validator で拒否。
835
- - 単純な ON/OFF 1個なら組込 `type:'checkbox'`、3択以上は `type:'dropdown'`。見た目は `--tg-toggle-accent` 等で。
853
+ - クリックでその値に確定(`setValue`=**検証・Undo を通る**)。アクティブセルで **Space** で次の値へ循環(2択なら反対側)。許可値以外の打ち込みは自動 validator で拒否。
854
+ - 単純な ON/OFF 1個なら組込 `type:'checkbox'`。**短いラベルの少数択一**(出欠・高/中/低 等)に向き、短ければ dropdown と同程度の幅で収まる。**選択肢が多い/ラベルが長い/列幅が狭い**なら `type:'dropdown'` が省スペース。見た目は `--tg-toggle-accent` 等で。
836
855
 
837
856
  ### 同梱プラグイン: セル結合(merge・横/縦/矩形)
838
857
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threestarsoftware/tssgrid",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "軽量・ノービルド・日本語IME堅牢な編集グリッド(vanilla JS / 依存なし / MIT)。Excel ライクな入力画面を <script> タグ1本で。",
5
5
  "license": "MIT",
6
6
  "author": "ThreeStarSoftware (https://www.threestarsoftware.co.jp/)",
@@ -0,0 +1,21 @@
1
+ /* TssSort — ヘッダの並べ替えUI(plugins/tss-sort.js)。▲▼ は小さな三角を縦2段。色は --tg-accent を流用。
2
+ 既定は薄いグレー(常時表示=出入りのピコピコ無し)、点灯中の向きだけアクセント色。漏斗(filter)の右に並ぶ。 */
3
+ .tssgrid .tg-table thead th .tg-sort-ind {
4
+ display: inline-flex; flex-direction: column; justify-content: center; gap: 2px;
5
+ margin-left: 5px; vertical-align: middle; line-height: 0; user-select: none; cursor: pointer;
6
+ }
7
+ .tssgrid .tg-table thead th .tg-sort-ind .tg-sort-up,
8
+ .tssgrid .tg-table thead th .tg-sort-ind .tg-sort-dn {
9
+ width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent;
10
+ }
11
+ .tssgrid .tg-table thead th .tg-sort-ind .tg-sort-up { border-bottom: 5px solid var(--tg-sort-dim, #9aa6b4); }
12
+ .tssgrid .tg-table thead th .tg-sort-ind .tg-sort-dn { border-top: 5px solid var(--tg-sort-dim, #9aa6b4); }
13
+ .tssgrid .tg-table thead th .tg-sort-ind:hover .tg-sort-up { border-bottom-color: #cdd6e2; }
14
+ .tssgrid .tg-table thead th .tg-sort-ind:hover .tg-sort-dn { border-top-color: #cdd6e2; }
15
+ /* 点灯:確定した向きだけアクセント色、反対側は薄く落とす */
16
+ .tssgrid .tg-table thead th .tg-sort-ind.asc .tg-sort-up { border-bottom-color: var(--tg-sort-accent, var(--tg-accent, #2f6fed)); }
17
+ .tssgrid .tg-table thead th .tg-sort-ind.desc .tg-sort-dn { border-top-color: var(--tg-sort-accent, var(--tg-accent, #2f6fed)); }
18
+ .tssgrid .tg-table thead th .tg-sort-ind.asc .tg-sort-dn,
19
+ .tssgrid .tg-table thead th .tg-sort-ind.desc .tg-sort-up { opacity: .35; }
20
+ /* 本体クリックでも並べ替える既定モードでは見出し全体にカーソルを出す(iconOnly は ▲▼ だけ) */
21
+ .tssgrid:not(.tg-sort-icononly) .tg-table thead th.tg-sortable { cursor: pointer; }
@@ -0,0 +1,105 @@
1
+ /*! TssSort — ヘッダクリックで並べ替えUI。コアの sortBy / clearSort を呼ぶだけ。
2
+ *
3
+ * 各リーフ見出しに ▲▼ インジケータを出し、クリックで「昇順 → 降順 → 元の順(clearSort)」を1列で循環。
4
+ * 値の並べ替え自体は grid.sortBy(c,'asc'|'desc') / grid.clearSort()(コア機能)。空行(minSpareRows)は
5
+ * コア仕様どおり常に最下部のまま。本体クリックの列選択は従来どおり活きる(選択を奪わない)。
6
+ * tss-filter と同時に付けても、▲▼(ソート)と漏斗(フィルタ)は別アイコンで並ぶ=ぶつからない。
7
+ *
8
+ * 使い方:
9
+ * const grid = new TssGrid(el, { plugins: [ TssSort.plugin() ] }); // 全リーフ列に ▲▼
10
+ * TssSort.plugin({ columns: ['createdAt', 'workDate'] }); // 対象列を限定(data キー or index)
11
+ * TssSort.plugin({ initial: { col: 'createdAt', dir: 'desc' } }); // 初期の並び
12
+ * TssSort.plugin({ iconOnly: true }); // ▲▼ の所だけで並べ替え(本体クリックは純粋に選択)
13
+ * grid.getPlugin('sort').clear(); // 並べ替え解除(入力順へ)
14
+ * grid.getPlugin('sort').getState(); // { col, dir } または null
15
+ *
16
+ * opts: columns(対象列の配列・省略=全リーフ列) / initial({col, dir:'asc'|'desc'}) / iconOnly(既定 false)。
17
+ * メモ: 単一列ソート(多段は grid.sortByCols を直接)。onHeaderClick / onHeaderRender フックを「連鎖」して使う
18
+ * (既存の指定があれば保ちつつ上乗せ)。sortBy→再描画→onHeaderRender 再実行で ▲▼ の点灯も自動更新。
19
+ */
20
+ (function (root) {
21
+ 'use strict';
22
+
23
+ function plugin(opts) {
24
+ opts = opts || {};
25
+ const iconOnly = !!opts.iconOnly;
26
+ return function (grid) {
27
+ const cur = { col: -1, dir: null }; // dir: 'asc' | 'desc' | null(唯一の状態)
28
+
29
+ // 対象列 index の集合(省略時は全リーフ列)
30
+ const targets = (() => {
31
+ if (!opts.columns) return null; // null = 全列
32
+ const s = new Set();
33
+ opts.columns.forEach(k => { const ci = grid._resolveCol ? grid._resolveCol(k) : (typeof k === 'number' ? k : -1); if (ci >= 0) s.add(ci); });
34
+ return s;
35
+ })();
36
+ const isTarget = c => !targets || targets.has(c);
37
+
38
+ // 昇順 → 降順 → 元の順(clearSort)を1列で循環。
39
+ function cycle(c) {
40
+ if (cur.col !== c) { cur.col = c; cur.dir = 'asc'; grid.sortBy(c, 'asc'); }
41
+ else if (cur.dir === 'asc') { cur.dir = 'desc'; grid.sortBy(c, 'desc'); }
42
+ else { cur.col = -1; cur.dir = null; if (!grid.clearSort()) grid.redraw(); }
43
+ }
44
+
45
+ // ---- クリック(onHeaderClick を連鎖)----
46
+ const prevClick = grid.onHeaderClick;
47
+ grid.onHeaderClick = function (c, e) {
48
+ // ソート対象で、漏斗(filter)以外のクリック。iconOnly なら ▲▼ の上だけ。
49
+ const onFunnel = e && e.target && e.target.closest && e.target.closest('.tg-filter-btn');
50
+ const onInd = e && e.target && e.target.closest && e.target.closest('.tg-sort-ind');
51
+ if (isTarget(c) && !onFunnel && (!iconOnly || onInd)) cycle(c);
52
+ // 既存の onHeaderClick を尊重(false なら列選択を抑止)。ソート自身は選択を奪わない。
53
+ if (prevClick) { let r; try { r = prevClick.call(grid, c, e); } catch (_) {} if (r === false) return false; }
54
+ return undefined; // 列選択は今までどおり通す
55
+ };
56
+
57
+ // ---- ▲▼ インジケータ(onHeaderRender を連鎖。再描画毎に自動で付け直し)----
58
+ const prevRender = grid.onHeaderRender;
59
+ grid.onHeaderRender = function (th, c) {
60
+ if (prevRender) { try { prevRender.call(grid, th, c); } catch (_) {} }
61
+ if (!isTarget(c)) return;
62
+ th.classList.add('tg-sortable');
63
+ const dir = (c === cur.col) ? cur.dir : null;
64
+ const ind = document.createElement('span');
65
+ ind.className = 'tg-sort-ind' + (dir ? ' ' + dir : '');
66
+ ind.innerHTML = '<span class="tg-sort-up"></span><span class="tg-sort-dn"></span>';
67
+ th.appendChild(ind);
68
+ };
69
+ grid.root.classList.add('tg-has-sort');
70
+ if (iconOnly) grid.root.classList.add('tg-sort-icononly');
71
+
72
+ // 初期の並び
73
+ if (opts.initial && opts.initial.col != null) {
74
+ const ci = grid._resolveCol ? grid._resolveCol(opts.initial.col) : (typeof opts.initial.col === 'number' ? opts.initial.col : -1);
75
+ if (ci >= 0 && isTarget(ci)) { cur.col = ci; cur.dir = opts.initial.dir === 'desc' ? 'desc' : 'asc'; grid.sortBy(ci, cur.dir); }
76
+ }
77
+ grid.buildTable(); // インジケータを反映(onHeaderRender 実行)
78
+
79
+ return {
80
+ name: 'sort',
81
+ // 現在の並び { col:(dataキー or index), dir } または null
82
+ getState: () => (cur.col >= 0 ? { col: (grid.columns[cur.col] && grid.columns[cur.col].data != null ? grid.columns[cur.col].data : cur.col), dir: cur.dir } : null),
83
+ // プログラムから並べ替え(UIの点灯も同期)
84
+ sortBy: (c, dir) => {
85
+ const ci = grid._resolveCol ? grid._resolveCol(c) : (typeof c === 'number' ? c : -1);
86
+ if (ci < 0 || !isTarget(ci)) return;
87
+ cur.col = ci; cur.dir = dir === 'desc' ? 'desc' : 'asc'; grid.sortBy(ci, cur.dir);
88
+ },
89
+ clear: () => { cur.col = -1; cur.dir = null; if (!grid.clearSort()) grid.redraw(); },
90
+ destroy: function () {
91
+ grid.onHeaderClick = prevClick || null;
92
+ grid.onHeaderRender = prevRender || null;
93
+ grid.root.classList.remove('tg-has-sort', 'tg-sort-icononly');
94
+ if (grid.table) grid.table.querySelectorAll('.tg-sort-ind').forEach(el => el.remove());
95
+ if (grid.table) grid.table.querySelectorAll('th.tg-sortable').forEach(th => th.classList.remove('tg-sortable'));
96
+ grid.buildTable();
97
+ },
98
+ };
99
+ };
100
+ }
101
+
102
+ const TssSort = { plugin: plugin };
103
+ if (typeof module !== 'undefined' && module.exports) module.exports = TssSort;
104
+ else root.TssSort = TssSort;
105
+ })(typeof self !== 'undefined' ? self : this);
@@ -1,4 +1,4 @@
1
- /* TssToggle — 2択セグメント。色は変数で上書き可。 */
1
+ /* TssToggle — 2択以上(N択)のセグメント。色は変数で上書き可。横幅ぶんピルが並ぶ。 */
2
2
  .tg-toggle { display: inline-flex; border: 1px solid var(--tg-toggle-border, #cfd7e0); border-radius: 999px; overflow: hidden; font-size: .92em; line-height: 1.7; vertical-align: middle; }
3
3
  .tg-toggle .tg-seg { padding: 0 11px; cursor: pointer; color: var(--tg-toggle-fg, #64748b); user-select: none; transition: background .12s; }
4
4
  .tg-toggle .tg-seg + .tg-seg { border-left: 1px solid var(--tg-toggle-border, #cfd7e0); }
@@ -1,23 +1,25 @@
1
- /*! TssToggle — 2択セグメント・トグル(有効|無効・公開|非公開・男|女 …)。
1
+ /*! TssToggle — 2択以上(N択)のセグメント・トグル/ラジオ(有効|無効・未回答|出席|欠席 …)。
2
2
  *
3
- * ラジオの2択を「並んだピル(セグメント)」で表示し、クリックでどちらかに切替。値は dropdown と同じく
3
+ * 択一(2つ以上)を「並んだピル(セグメント)」で表示し、クリックで切替。値は dropdown と同じく
4
4
  * 選んだ option の value を保存(getData も value)。html:true+format で描画、クリックは委譲で拾う。
5
+ * ラジオボタンの「全選択肢が常に見える・1クリックで確定」をセル内で実現する用途(出欠・優先度 高/中/低 等)。
5
6
  *
6
7
  * 使い方:
7
8
  * plugins: [ TssToggle.plugin({ columns: {
8
- * status: ['有効', '無効'], // 文字列2つ(value=label)
9
+ * status: ['有効', '無効'], // 文字列(value=label)。2つ以上でOK
10
+ * att: ['未回答', '出席', '欠席'], // 3択以上も可
9
11
  * pub: [{value:'1',label:'公開'},{value:'0',label:'非公開'}] // value/label 分離も可
10
12
  * } }) ]
11
13
  * // 列定義側で直接: { data:'status', html:true, format: TssToggle.format(['有効','無効']) } + plugins:[TssToggle.plugin()]
12
14
  *
13
- * opts(plugin): columns({dataキー: 2要素配列}) / accent(選択側の色・CSS変数 --tg-toggle-accent でも)。
15
+ * opts(plugin): columns({dataキー: 2要素以上の配列}) / accent(選択側の色・CSS変数 --tg-toggle-accent でも)。
14
16
  * 挙動: クリックでその側に確定(setValue=検証/Undo を通る)。アクティブセルで Space は反対側へトグル。
15
17
  * 他値を打ち込めないよう、対象列に「2値のみ許可」の validator を自動付与。
16
18
  */
17
19
  (function (root) {
18
20
  'use strict';
19
21
  function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); }
20
- function normOpts(arr) { return (arr || []).slice(0, 2).map(function (o) { return (o && typeof o === 'object') ? { value: String(o.value), label: String(o.label) } : { value: String(o), label: String(o) }; }); }
22
+ function normOpts(arr) { return (arr || []).map(function (o) { return (o && typeof o === 'object') ? { value: String(o.value), label: String(o.label) } : { value: String(o), label: String(o) }; }); }
21
23
 
22
24
  function makeFormat(options) {
23
25
  var opts = normOpts(options);
@@ -45,7 +47,7 @@
45
47
  cfg.html = true;
46
48
  if (typeof cfg.format !== 'function') cfg.format = makeFormat(o);
47
49
  var vals = o.map(function (x) { return x.value; });
48
- if (typeof cfg.validator !== 'function') cfg.validator = function (v) { return v === '' || vals.indexOf(v) >= 0; }; // 2値以外を弾く
50
+ if (typeof cfg.validator !== 'function') cfg.validator = function (v) { return v === '' || vals.indexOf(v) >= 0; }; // 許可値以外を弾く
49
51
  byCol[ci] = vals;
50
52
  });
51
53
  // それ自体は無効でも format 直指定の列をクリックで拾えるよう、format 由来の列も拾う
@@ -53,7 +55,8 @@
53
55
  if (byCol[c]) return byCol[c];
54
56
  return null; // 設定外の列は対象外
55
57
  }
56
- function editable(r, c) { var cfg = grid.columns[c]; return !grid.readOnly && !(cfg && cfg.readOnly === true); }
58
+ // 本体と同じ判定=grid.readOnly / 関数readOnly / セル単位RO / 結合の従属 すべて尊重(cfg.readOnly===true だけ見ていた不具合を修正)。
59
+ function editable(r, c) { return grid._isReadOnly ? !grid._isReadOnly(r, c) : (!grid.readOnly && !(grid.columns[c] && grid.columns[c].readOnly === true)); }
57
60
 
58
61
  function onClick(e) {
59
62
  var seg = e.target.closest && e.target.closest('.tg-seg[data-tg-v]'); if (!seg) return;
@@ -68,7 +71,7 @@
68
71
  if (!vals || !editable(r, c) || grid.mode === 'edit') return;
69
72
  e.preventDefault();
70
73
  var cur = grid.getValue(r, c), i = vals.indexOf(String(cur));
71
- grid.setValue(r, c, vals[(i + 1) % vals.length]); // 反対側へトグル
74
+ grid.setValue(r, c, vals[(i + 1) % vals.length]); // 次の値へ循環(2択なら反対側)
72
75
  }
73
76
  grid.wrap.addEventListener('click', onClick);
74
77
  grid.editor.addEventListener('keydown', onKey);