@threestarsoftware/tssgrid 0.1.10 → 0.1.12
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 +10 -0
- package/README.md +23 -0
- package/package.json +1 -1
- package/plugins/tss-combo.css +26 -0
- package/plugins/tss-combo.js +182 -0
- package/src/tssgrid.d.ts +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
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.12] - 2026-09-24
|
|
7
|
+
|
|
8
|
+
### 追加
|
|
9
|
+
- **コード付きプルダウン+「その他」で自由入力 プラグイン `tss-combo`**(プラグイン・無依存・コア無改変)— 「**選択肢から選ぶ。ただし『その他』を選んだら自由入力**」を、業務側で**その他かどうかを分岐せず** **(コード, 値) のペア**で扱えるカスタムエディタ(`columns[c].editor` 契約に乗る)。**コードはそのまま**(選んだ項目の code/その他は固定 code)、**値だけ**が「選択ラベル or 入力文字列」に変わる。値列にエディタを付け `codeField` で**別のコード列へ code を書き戻す**(`setValueRaw`=派生値・`readOnly` 列にも書ける)。`options` は `{code,label}` か文字列配列、`other` 省略で自由入力なし(素のコード選択)。既存の「その他」値(選択肢に無い値)は開くと**入力欄に復元**。undo/redo・貼付で code を追従させたい時用に `TssCombo.codeOf({options,other})` ヘルパ。動く例 `examples/combo-other.html`(tss-combo の各パターン=コード+名称両表示/hidden/その他なし/文字列options + autocomplete・2セル方式の比較も)。
|
|
10
|
+
|
|
11
|
+
## [0.1.11] - 2026-09-16
|
|
12
|
+
|
|
13
|
+
### 修正
|
|
14
|
+
- **表全体版 `cellClass` / `cellStyle` / `rowClass` と `CellClassCtx` の `src`(元レコード)を `any` に**(型定義のみ・実行時は不変)— `src` は関数の**引数(反変位置)**なので `Record<string, any> | null` で固定すると、**自分で行の型を書いている利用者が関数を渡せず** `as unknown as MyRow` の二重キャストを強いられていた(0.1.9〜0.1.10 で該当)。`any` にすると利用側が `src: MyRow` と**そのまま注釈でき、関数内はその型で検査される**(今より安全側・既に `row: any[]` なので揃う)。d.ts 4か所(`CellClassCtx.src` / 表全体 `cellClass`・`cellStyle`・`rowClass`)+ api.html を更新。※将来の `TssGrid<Row>` ジェネリック化は 0.2 系で検討。
|
|
15
|
+
|
|
6
16
|
## [0.1.10] - 2026-09-16
|
|
7
17
|
|
|
8
18
|
### 追加
|
package/README.md
CHANGED
|
@@ -647,6 +647,29 @@ columns: [
|
|
|
647
647
|
- ↑↓で候補移動・Enter確定・Esc取消・クリック選択。`source`(配列 or 関数) / `strict`(候補のみ) / `minChars` / `max` / `match`(`includes`/`startsWith`/関数) / `openOnClick`。
|
|
648
648
|
- カスタムエディタ契約(`editor.open/close`)に乗るだけ。見た目は `tss-ac-*` クラスと `--ac-*` 変数で。
|
|
649
649
|
|
|
650
|
+
### 同梱プラグイン: コード付きプルダウン+その他自由入力(tss-combo)
|
|
651
|
+
|
|
652
|
+
`plugins/tss-combo.js`= 「**選択肢から選ぶ。ただし『その他』を選んだら自由入力**」を、業務側で**その他かどうかを分岐せず** **(コード, 値) のペア**で扱えるようにするエディタ。**コードはそのまま**(選んだ項目の code/その他は固定 code)、**値だけ**が「選択ラベル or 入力文字列」に変わる。動く例: [`examples/combo-other.html`](https://tssgrid.threestarsoftware.co.jp/examples/combo-other.html)(tss-combo の各パターン+ autocomplete / 2セル方式の比較も)。
|
|
653
|
+
|
|
654
|
+
```html
|
|
655
|
+
<link rel="stylesheet" href="plugins/tss-combo.css">
|
|
656
|
+
<script src="plugins/tss-combo.js"></script>
|
|
657
|
+
```
|
|
658
|
+
```js
|
|
659
|
+
columns: [
|
|
660
|
+
{ data: 'payCode', readOnly: true }, // コード列(hidden 運用も可=hiddenColumns:[…])
|
|
661
|
+
{ data: 'payName', editor: TssCombo({
|
|
662
|
+
options: [ { code:'01', label:'現金' }, { code:'02', label:'振込' } ],
|
|
663
|
+
other: { code:'99', label:'その他' }, // 選ぶと入力欄に切替(省略で自由入力なし)
|
|
664
|
+
codeField: 'payCode', // コードの書き戻し先(data キー or 列 index)
|
|
665
|
+
}) },
|
|
666
|
+
];
|
|
667
|
+
// 通常項目 → (payCode, payName) = (その code, label)/その他 → ('99', 入力文字列)
|
|
668
|
+
```
|
|
669
|
+
- 値列にエディタを付け、`codeField` で**別のコード列へ code を書き戻す**(`setValueRaw`=派生値・`readOnly` 列にも書ける)。`options` は `{code,label}` か文字列配列(code=label)。既存の「その他」値(選択肢に無い値)は開くと**入力欄に復元**。
|
|
670
|
+
- undo/redo・貼付で code を値に追従させたい時は `TssCombo.codeOf({options, other})` を `onAfterChange` に噛ませる(任意)。
|
|
671
|
+
- 見た目は `tg-combo*` クラス、色は `--tg-accent` を流用。`openOnClick` / `otherPlaceholder` / `icon` / `className`。
|
|
672
|
+
|
|
650
673
|
### 同梱プラグイン: セル内スパークライン(sparkline・ミニグラフ)
|
|
651
674
|
|
|
652
675
|
`plugins/tss-sparkline.js`(**SVG自前描画**)= セルの数列を**ミニ折れ線/棒**で描く。`columns[c].html:true` + `format`(format が返す SVG がセルに入る)seam に乗るだけ。Chart.js 等は積まない。動く例: [`examples/sparkline.html`](https://tssgrid.threestarsoftware.co.jp/examples/sparkline.html)(月次売上→動向グラフ・編集でライブ更新)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threestarsoftware/tssgrid",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
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,26 @@
|
|
|
1
|
+
/* TssCombo — コード付きプルダウン+その他自由入力(plugins/tss-combo.js)。色は --tg-accent を流用。 */
|
|
2
|
+
.tg-combo {
|
|
3
|
+
position: fixed; z-index: 1000; min-width: 150px; max-height: 60vh; overflow: auto;
|
|
4
|
+
background: #fff; color: #1f2733; border: 1px solid #c4ccd6; border-radius: 8px;
|
|
5
|
+
box-shadow: 0 6px 22px rgba(20, 40, 80, .22);
|
|
6
|
+
font: 13px "Meiryo UI", "Segoe UI", sans-serif; padding: 4px; box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
.tg-combo .tg-combo-item {
|
|
9
|
+
padding: 5px 10px; border-radius: 5px; cursor: pointer; white-space: nowrap; outline: none;
|
|
10
|
+
}
|
|
11
|
+
.tg-combo .tg-combo-item:hover,
|
|
12
|
+
.tg-combo .tg-combo-item.active { background: var(--tg-accent, #1573c0); color: #fff; }
|
|
13
|
+
.tg-combo .tg-combo-other { font-style: italic; opacity: .95; }
|
|
14
|
+
.tg-combo .tg-combo-sep { height: 1px; background: #e2e7ee; margin: 4px 6px; }
|
|
15
|
+
/* その他モードの入力欄 */
|
|
16
|
+
.tg-combo .tg-combo-inputwrap { padding: 4px; min-width: 180px; }
|
|
17
|
+
.tg-combo .tg-combo-input {
|
|
18
|
+
width: 100%; box-sizing: border-box; padding: 5px 8px; font: inherit;
|
|
19
|
+
border: 1px solid #c4ccd6; border-radius: 5px; outline: none;
|
|
20
|
+
}
|
|
21
|
+
.tg-combo .tg-combo-input:focus { border-color: var(--tg-accent, #1573c0); }
|
|
22
|
+
.tg-combo .tg-combo-actions { display: flex; gap: 6px; margin-top: 6px; justify-content: flex-end; }
|
|
23
|
+
.tg-combo .tg-combo-actions button {
|
|
24
|
+
padding: 3px 12px; font: inherit; cursor: pointer; border-radius: 5px; border: 1px solid #c4ccd6; background: #f5f7fa;
|
|
25
|
+
}
|
|
26
|
+
.tg-combo .tg-combo-actions .tg-combo-ok { background: var(--tg-accent, #1573c0); color: #fff; border-color: transparent; }
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/*! TssCombo — コード付きプルダウン+「その他」で自由入力。TssGrid カスタムエディタ(ポップアップ型)。
|
|
2
|
+
*
|
|
3
|
+
* ねらい: 「選択肢から選ぶ。ただし『その他』を選んだら自由入力」を **1操作で (コード, 値) のペア**にする。
|
|
4
|
+
* - 通常項目を選ぶ → 値セル=そのラベル / コードセル=その code
|
|
5
|
+
* - 「その他」を選ぶ → 入力欄に切替 → 値セル=入力文字列 / コードセル=その他の code(固定)
|
|
6
|
+
* 業務側は「その他かどうか」を分岐せず (コード, 値) を読むだけでよい(コードはそのまま・値だけが選択/入力で変わる)。
|
|
7
|
+
*
|
|
8
|
+
* 使い方(値列にエディタを付け、codeField でコードの書き戻し先=別列を指す):
|
|
9
|
+
* columns: [
|
|
10
|
+
* { data:'payCode' }, // コード列(そのまま保存: 01/02/…/99)。hidden:true でも可
|
|
11
|
+
* { data:'payName', editor: TssCombo({
|
|
12
|
+
* options: [
|
|
13
|
+
* { code:'01', label:'現金' },
|
|
14
|
+
* { code:'02', label:'振込' },
|
|
15
|
+
* { code:'03', label:'クレジット' },
|
|
16
|
+
* ],
|
|
17
|
+
* other: { code:'99', label:'その他' }, // これを選ぶと入力欄に切替(省略で「その他」無効=素のコード選択)
|
|
18
|
+
* codeField: 'payCode', // コードの書き戻し先(data キー or 列 index)
|
|
19
|
+
* }) },
|
|
20
|
+
* ]
|
|
21
|
+
*
|
|
22
|
+
* opts:
|
|
23
|
+
* options : [{ code, label }](`value` も code の別名として可/文字列は code=label)。必須。
|
|
24
|
+
* other : { code, label } でその他を有効化。`other:true` なら {code:'99', label:'その他'}。省略で無効。
|
|
25
|
+
* codeField : コードの書き戻し先の列(data キー or index)。省略時はコードを書かず値だけ確定。
|
|
26
|
+
* otherPlaceholder : 入力欄のプレースホルダ(既定 '自由入力')。
|
|
27
|
+
* openOnClick: 既定 true(プルダウン同様シングルクリックで開く)。
|
|
28
|
+
* icon : セル右の目印(既定 '▾')。className: ポップアップに付ける任意クラス。
|
|
29
|
+
*/
|
|
30
|
+
(function (root) {
|
|
31
|
+
'use strict';
|
|
32
|
+
function esc(s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"' }[c]; }); }
|
|
33
|
+
// options を [{code,label}] に正規化(value を code の別名に/文字列は両方同じ)。
|
|
34
|
+
function normOpt(o) {
|
|
35
|
+
if (o && typeof o === 'object') {
|
|
36
|
+
var code = o.code != null ? o.code : (o.value != null ? o.value : o.label);
|
|
37
|
+
return { code: String(code == null ? '' : code), label: String(o.label != null ? o.label : code) };
|
|
38
|
+
}
|
|
39
|
+
return { code: String(o), label: String(o) };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function TssCombo(opts) {
|
|
43
|
+
opts = opts || {};
|
|
44
|
+
var list = (opts.options || []).map(normOpt);
|
|
45
|
+
var other = opts.other === true ? { code: '99', label: 'その他' }
|
|
46
|
+
: (opts.other && typeof opts.other === 'object') ? normOpt(opts.other) : null;
|
|
47
|
+
var codeField = opts.codeField;
|
|
48
|
+
var placeholder = opts.otherPlaceholder != null ? opts.otherPlaceholder : '自由入力';
|
|
49
|
+
|
|
50
|
+
var pop = null, ctxRef = null, done = false, onDoc = null, onKey = null;
|
|
51
|
+
var items = [], hi = -1, composing = false;
|
|
52
|
+
|
|
53
|
+
function teardown() {
|
|
54
|
+
if (onDoc) document.removeEventListener('mousedown', onDoc, true);
|
|
55
|
+
if (onKey) document.removeEventListener('keydown', onKey, true);
|
|
56
|
+
onDoc = onKey = null;
|
|
57
|
+
if (pop && pop.parentNode) pop.parentNode.removeChild(pop);
|
|
58
|
+
pop = null; items = []; hi = -1; composing = false;
|
|
59
|
+
}
|
|
60
|
+
// (コード, 値) を確定=値セルは commit(履歴に積む)、コードセルは別列へ setValueRaw で書き戻す。
|
|
61
|
+
// ★ setValueRaw を使う理由: コードは「値から導く派生値」なのでコード列を readOnly:true にしても貫通して書け、
|
|
62
|
+
// 履歴に独立ステップを作らない(=値のセルだけが 1 undo 単位)。undo/redo でコードも値に追従させたい場合は
|
|
63
|
+
// 下の TssCombo.codeOf を onAfterChange に噛ませる(任意)。
|
|
64
|
+
function commitPair(code, value) {
|
|
65
|
+
if (done) return; done = true;
|
|
66
|
+
var c = ctxRef; teardown();
|
|
67
|
+
c.commit(value); // 値列(このセル)=履歴に積む
|
|
68
|
+
if (codeField != null && c.grid && c.r != null) {
|
|
69
|
+
try { c.grid.setValueRaw(c.r, codeField, code); } catch (e) {} // コード列(別列・派生値・readOnly 貫通)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function doCancel() { if (done) return; done = true; var c = ctxRef; teardown(); if (c.cancel) c.cancel(); }
|
|
73
|
+
|
|
74
|
+
function position(anchor) {
|
|
75
|
+
var r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.offsetHeight;
|
|
76
|
+
var left = Math.max(6, Math.min(r.left, window.innerWidth - pw - 6));
|
|
77
|
+
var top = r.bottom + 2;
|
|
78
|
+
if (top + ph > window.innerHeight - 6) top = Math.max(6, r.top - 2 - ph); // 下にはみ出すなら上へ
|
|
79
|
+
pop.style.left = left + 'px'; pop.style.top = top + 'px'; pop.style.minWidth = r.width + 'px';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---- リスト表示(選択モード)----
|
|
83
|
+
function renderList(highlight) {
|
|
84
|
+
var rows = list.map(function (o, i) {
|
|
85
|
+
return '<div class="tg-combo-item" data-i="' + i + '" role="option">' + esc(o.label) + '</div>';
|
|
86
|
+
});
|
|
87
|
+
if (other) rows.push('<div class="tg-combo-sep"></div>' +
|
|
88
|
+
'<div class="tg-combo-item tg-combo-other" data-other="1" role="option">' + esc(other.label) + '…</div>');
|
|
89
|
+
pop.innerHTML = rows.join('');
|
|
90
|
+
items = pop.querySelectorAll('.tg-combo-item');
|
|
91
|
+
hi = -1;
|
|
92
|
+
Array.prototype.forEach.call(items, function (el, k) {
|
|
93
|
+
el.addEventListener('mouseenter', function () { activate(k); });
|
|
94
|
+
el.addEventListener('mousedown', function (e) { e.preventDefault(); }); // フォーカスを奪わない
|
|
95
|
+
el.addEventListener('click', function (e) { e.stopPropagation(); choose(k); });
|
|
96
|
+
});
|
|
97
|
+
// 現在値に一致する項目をハイライト(無ければ先頭)
|
|
98
|
+
var want = 0;
|
|
99
|
+
if (highlight != null) for (var i = 0; i < list.length; i++) if (list[i].label === highlight) { want = i; break; }
|
|
100
|
+
activate(want);
|
|
101
|
+
}
|
|
102
|
+
function activate(k) {
|
|
103
|
+
if (k < 0 || k >= items.length) return;
|
|
104
|
+
Array.prototype.forEach.call(items, function (el, i) { el.classList.toggle('active', i === k); });
|
|
105
|
+
hi = k; if (items[k]) items[k].scrollIntoView({ block: 'nearest' });
|
|
106
|
+
}
|
|
107
|
+
function choose(k) {
|
|
108
|
+
var el = items[k]; if (!el) return;
|
|
109
|
+
if (el.dataset.other) return showInput(''); // 「その他」→入力モードへ
|
|
110
|
+
var o = list[k]; commitPair(o.code, o.label); // 通常項目→(code, label) 確定
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ---- 入力表示(その他モード)----
|
|
114
|
+
function showInput(initial) {
|
|
115
|
+
pop.innerHTML = '<div class="tg-combo-inputwrap">' +
|
|
116
|
+
'<input class="tg-combo-input" type="text" placeholder="' + esc(placeholder) + '">' +
|
|
117
|
+
'<div class="tg-combo-actions"><button type="button" class="tg-combo-ok">確定</button>' +
|
|
118
|
+
'<button type="button" class="tg-combo-back">戻る</button></div></div>';
|
|
119
|
+
var input = pop.querySelector('.tg-combo-input');
|
|
120
|
+
input.value = initial == null ? '' : initial;
|
|
121
|
+
input.addEventListener('compositionstart', function () { composing = true; });
|
|
122
|
+
input.addEventListener('compositionend', function () { composing = false; });
|
|
123
|
+
input.addEventListener('keydown', function (e) {
|
|
124
|
+
if (e.key === 'Enter') { if (composing || e.isComposing || e.keyCode === 229) return; e.preventDefault(); confirmInput(input.value); }
|
|
125
|
+
else if (e.key === 'Escape') { e.preventDefault(); if (list.length) renderListWithHighlight(); else doCancel(); }
|
|
126
|
+
});
|
|
127
|
+
pop.querySelector('.tg-combo-ok').addEventListener('mousedown', function (e) { e.preventDefault(); confirmInput(input.value); });
|
|
128
|
+
pop.querySelector('.tg-combo-back').addEventListener('mousedown', function (e) { e.preventDefault(); renderListWithHighlight(); });
|
|
129
|
+
setTimeout(function () { input.focus(); input.setSelectionRange(input.value.length, input.value.length); }, 0);
|
|
130
|
+
}
|
|
131
|
+
function confirmInput(v) { commitPair(other ? other.code : '', v); } // (その他code, 入力値) 確定
|
|
132
|
+
function renderListWithHighlight() { renderList(ctxRef ? ctxRef.value : null); }
|
|
133
|
+
|
|
134
|
+
function handleKey(e) {
|
|
135
|
+
if (!pop || pop.querySelector('.tg-combo-input')) return; // 入力モードのキーは input 側で処理
|
|
136
|
+
if (e.key === 'ArrowDown') { e.preventDefault(); activate((hi + 1) % items.length); }
|
|
137
|
+
else if (e.key === 'ArrowUp') { e.preventDefault(); activate((hi - 1 + items.length) % items.length); }
|
|
138
|
+
else if (e.key === 'Enter') { e.preventDefault(); if (hi >= 0) choose(hi); }
|
|
139
|
+
else if (e.key === 'Escape') { e.preventDefault(); doCancel(); }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
openOnClick: opts.openOnClick !== false,
|
|
144
|
+
icon: opts.icon != null ? opts.icon : '▾',
|
|
145
|
+
open: function (ctx) {
|
|
146
|
+
ctxRef = ctx; done = false;
|
|
147
|
+
pop = document.createElement('div');
|
|
148
|
+
pop.className = 'tg-combo' + (opts.className ? ' ' + opts.className : '');
|
|
149
|
+
pop.setAttribute('role', 'listbox');
|
|
150
|
+
document.body.appendChild(pop);
|
|
151
|
+
// 現在値が選択肢に無い=過去に「その他」で入れた値 → いきなり入力モードで復元
|
|
152
|
+
var cur = ctx.value, known = cur != null && cur !== '' && list.some(function (o) { return o.label === String(cur); });
|
|
153
|
+
if (other && cur != null && cur !== '' && !known) showInput(String(cur));
|
|
154
|
+
else renderList(cur);
|
|
155
|
+
position(ctx.td);
|
|
156
|
+
onDoc = function (e) { if (pop && !pop.contains(e.target) && !(ctx.td && ctx.td.contains(e.target))) doCancel(); };
|
|
157
|
+
onKey = handleKey;
|
|
158
|
+
document.addEventListener('mousedown', onDoc, true);
|
|
159
|
+
document.addEventListener('keydown', onKey, true);
|
|
160
|
+
},
|
|
161
|
+
close: function () { teardown(); },
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 値 → コードを引く関数を返す(同じ options/other から)。undo/redo・貼付でコードを値に追従させたい時、
|
|
166
|
+
// onAfterChange: (chs) => chs.forEach(ch => { if (ch.c === 値列) grid.setValueRaw(ch.r, コード列, codeOf(grid.getValue(ch.r, 値列))); })
|
|
167
|
+
// のように噛ませる(任意)。値が選択肢に無ければ other の code、空なら ''。
|
|
168
|
+
TssCombo.codeOf = function (opts) {
|
|
169
|
+
opts = opts || {};
|
|
170
|
+
var list = (opts.options || []).map(normOpt);
|
|
171
|
+
var other = opts.other === true ? { code: '99' }
|
|
172
|
+
: (opts.other && typeof opts.other === 'object') ? normOpt(opts.other) : null;
|
|
173
|
+
return function (value) {
|
|
174
|
+
if (value == null || value === '') return '';
|
|
175
|
+
for (var i = 0; i < list.length; i++) if (list[i].label === String(value)) return list[i].code;
|
|
176
|
+
return other ? other.code : '';
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
if (typeof module !== 'undefined' && module.exports) module.exports = TssCombo;
|
|
181
|
+
else root.TssCombo = TssCombo;
|
|
182
|
+
})(typeof self !== 'undefined' ? self : this);
|
package/src/tssgrid.d.ts
CHANGED
|
@@ -25,8 +25,9 @@ export interface ChangeItem {
|
|
|
25
25
|
/** 選択範囲。r0/c0=開始、r1/c1=終了(昇順とは限らない)。 */
|
|
26
26
|
export interface SelectionRange { r0: number; c0: number; r1: number; c1: number; }
|
|
27
27
|
export interface CellCoord { r: number; c: number; }
|
|
28
|
-
/** cellClass/cellStyle(列版)が受け取る第2引数。row=その行の値配列, src=元レコード(object モード時・非表示フィールドも見える/配列モードは null)。
|
|
29
|
-
|
|
28
|
+
/** cellClass/cellStyle(列版)が受け取る第2引数。row=その行の値配列, src=元レコード(object モード時・非表示フィールドも見える/配列モードは null)。
|
|
29
|
+
* src は `any`=利用側が自分の行型(`src: MyRow`)でそのまま受けられる(`Record<string,any>|null` だと二重キャストを強いるため)。 */
|
|
30
|
+
export interface CellClassCtx { r: number; c: number; row: any[]; src: any; }
|
|
30
31
|
/** cellClass/cellStyle の戻り値のクラス。文字列(空白区切り可)/配列/null 等(=クラス無し)。 */
|
|
31
32
|
export type ClassValue = string | string[] | null | undefined | false;
|
|
32
33
|
/** cellStyle の戻り値。CSS プロパティのオブジェクト(`{'background':'#fde','--pct':'42%'}`)か文字列(`'background:#fde;--pct:42%'`)。 */
|
|
@@ -114,12 +115,12 @@ export interface TssGridOptions {
|
|
|
114
115
|
columns?: ColumnDef[];
|
|
115
116
|
/** 表全体の条件付きクラス(全セルで呼ばれる)。**引数は `(r, c, value, row, src)`**(先頭が行/列番号・列版と順序が違う)。
|
|
116
117
|
* `row`=その行の値配列, `src`=元レコード(object モード時・非表示フィールドも見える)。行に色を付けたいなら `rowClass` の方が簡単。 */
|
|
117
|
-
cellClass?: (r: number, c: number, value: any, row: any[], src:
|
|
118
|
+
cellClass?: (r: number, c: number, value: any, row: any[], src: any) => ClassValue;
|
|
118
119
|
/** 表全体のセル単位インラインスタイル。**引数は `(r, c, value, row, src)`**。返り値は CSS オブジェクト or 文字列。 */
|
|
119
|
-
cellStyle?: (r: number, c: number, value: any, row: any[], src:
|
|
120
|
+
cellStyle?: (r: number, c: number, value: any, row: any[], src: any) => StyleValue;
|
|
120
121
|
/** 行ごとに `<tr>` へ付与する CSS クラス(**行に色を付けるならこれ**)。`(r, row, src)` で呼ばれ、文字列/配列を返す。
|
|
121
122
|
* 編集でセルが再描画されると即追従。CSS は `.tssgrid tr.クラス > td { … }` のように行単位で当てる。 */
|
|
122
|
-
rowClass?: (r: number, row: any[], src:
|
|
123
|
+
rowClass?: (r: number, row: any[], src: any) => ClassValue;
|
|
123
124
|
/** 検証NG時。'revert'=元に戻す / 'keep'=赤く警告して残す。 */
|
|
124
125
|
invalidMode?: 'revert' | 'keep';
|
|
125
126
|
invalidTitle?: boolean;
|