@threestarsoftware/tssgrid 0.1.8 → 0.1.10
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 +13 -0
- package/README.md +3 -2
- package/package.json +1 -1
- package/src/tssgrid.d.ts +25 -4
- package/src/tssgrid.js +50 -1
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.10] - 2026-09-16
|
|
7
|
+
|
|
8
|
+
### 追加
|
|
9
|
+
- **`scrollToRow(r, align?)`(指定行までスクロール=HTML の `#` アンカー相当)**(コア)— 任意の行を画面の**先頭へ**持ってくる公開メソッド。`align` は `'start'`(既定=先頭・sticky ヘッダ直下)/ `'center'` / `'end'` / `'nearest'`(可視ならそのまま)。**仮想(均一行高)でも非仮想(実DOM測定・可変行高)でも動く**。範囲外の `r` は自動クランプ。**選択は変えない**(目印が欲しい時は `rowClass` 等を併用)。動く例 `examples/scroll-to-row.html`(1000行・行番号ジャンプ+align 切替+rowClass ハイライト)。
|
|
10
|
+
|
|
11
|
+
## [0.1.9] - 2026-09-15
|
|
12
|
+
|
|
13
|
+
### 追加
|
|
14
|
+
- **`rowClass(r, row, src)`(行ごとに `<tr>` へクラスを付与)**(コア)— 行の状態で**行まるごと色を付ける**のが素直に書けるオプション。`rowClass: (r, row) => row[3] === '欠品' ? 'row-ng' : ''` のように返したクラスが `<tr>` に付く(配列可)。CSS は `.tssgrid tr.row-ng > td { background:#fdecea }` の要領。従来は表全体版 `cellClass` で全 `<td>` に付けて代用していたが、**行単位はこちらが自然**。編集で行の状態が変わっても `_applyRowClass` で即追従。
|
|
15
|
+
|
|
16
|
+
### 修正
|
|
17
|
+
- **`cellClass` / `cellStyle` の型定義(`.d.ts`)が実装と食い違っていたのを修正**(型のみ・実行時は元から正しい)— (1) **列版**(`columns[c].cellClass`)は第1引数がそのセルの値で `(value, { r, c, row, src }) => クラス`。旧型は `(r, c, value, row) => string` で**引数順が逆**だった。(2) **表全体版**(`TssGridOptions.cellClass` = `(r, c, value, row, src)`)は**そもそも型に無かった**ので追加。`cellStyle` も同様に両方を修正。返り値も `string | string[] | false | null` 等を許容する `ClassValue`/`StyleValue` に。**影響は TypeScript / エディタ補完のみ**(生の JS・README・examples は元から正しい)。
|
|
18
|
+
|
|
6
19
|
## [0.1.8] - 2026-09-02
|
|
7
20
|
|
|
8
21
|
### 追加
|
package/README.md
CHANGED
|
@@ -918,7 +918,7 @@ new TssGrid(el, {
|
|
|
918
918
|
});
|
|
919
919
|
```
|
|
920
920
|
|
|
921
|
-
メソッド: `getData()` / `getRows()` / `getRow(r)` / `getColumn(cOrKey)` / `getColumns()` / `setData(rows)` / `appendRows(rows,opts?)`(**背景で"育てる"追記**=data末尾に足し可視窓だけ再描画。buildTable全再描画せずスクロール位置・選択・編集中エディタを保ち履歴にも積まない。「先頭ページを描く→残りをidleでappendRows」でfirst-paintを早める土台。既存経路は不変のopt-in) / `setRowCount(total)` + `fillRows(start,rows)`(**遅延ロード=on-demand**=virtual前提。総件数を確保し未取得ぶんは「読込中」行、`onViewportChange(start,end)` で要る範囲を受け `fillRows` で実データを流し込む。sort/filterは遅延中ローカル計算せず `onSortFilterChange` でサーバ委譲、未取得セルは編集不可、export(getData/toCSV)はロード済みのみ=AG Grid の Infinite Row Model 相当を push で) / `getValue(r,cOrKey)` / `setValue(r,cOrKey,val,force?)` / `setValueRaw(r,cOrKey,val)`(履歴に積まない派生値書き込み・計算列用) / `hideColumn(c)` / `showColumn(c)` / `toggleColumn(c)` / `isColumnHidden(c)` / `getHiddenColumns()` / `setAlignment(align,range?)` / `getAlignment(r,c)` / `setCellReadOnly(flag,range?)` / `autoSizeColumn(c)` / `autoSizeAllColumns()` / `sortBy(cOrKey, dir)` / `sortRows(cmp)` / `addShortcut(s)` / `removeShortcut(name)` / `usePlugin(fn)` / `getPlugin(name)` / `destroy()` / `setActive(r,c)` / `selectRow(r)` / `selectCol(c)` / `selectAll()` / `setColWidth(c,px)` / `setRowHeight(r,px)` / `freezeCols(n)` / `freezeRows(n)` / `moveRow(from,to)` / `moveRows(r0,r1,to)` / `clearSort()` / `toCSV(opts)` / `downloadCSV(name,opts)` / `insertRow(ri,'above'|'below')` / `insertRows(ri,count?,'above'|'below')`(count 行を1回の再描画・1 undo で一括挿入=大量挿入が O(N)) / `deleteRows(r0,r1?)` / `insertCol(ci,'left'|'right')` / `deleteCols(c0,c1?)` / `redraw()`(列定義変更後の再描画, データ・選択は保持)ほか。
|
|
921
|
+
メソッド: `getData()` / `getRows()` / `getRow(r)` / `getColumn(cOrKey)` / `getColumns()` / `setData(rows)` / `appendRows(rows,opts?)`(**背景で"育てる"追記**=data末尾に足し可視窓だけ再描画。buildTable全再描画せずスクロール位置・選択・編集中エディタを保ち履歴にも積まない。「先頭ページを描く→残りをidleでappendRows」でfirst-paintを早める土台。既存経路は不変のopt-in) / `setRowCount(total)` + `fillRows(start,rows)`(**遅延ロード=on-demand**=virtual前提。総件数を確保し未取得ぶんは「読込中」行、`onViewportChange(start,end)` で要る範囲を受け `fillRows` で実データを流し込む。sort/filterは遅延中ローカル計算せず `onSortFilterChange` でサーバ委譲、未取得セルは編集不可、export(getData/toCSV)はロード済みのみ=AG Grid の Infinite Row Model 相当を push で) / `getValue(r,cOrKey)` / `setValue(r,cOrKey,val,force?)` / `setValueRaw(r,cOrKey,val)`(履歴に積まない派生値書き込み・計算列用) / `hideColumn(c)` / `showColumn(c)` / `toggleColumn(c)` / `isColumnHidden(c)` / `getHiddenColumns()` / `setAlignment(align,range?)` / `getAlignment(r,c)` / `setCellReadOnly(flag,range?)` / `autoSizeColumn(c)` / `autoSizeAllColumns()` / `sortBy(cOrKey, dir)` / `sortRows(cmp)` / `addShortcut(s)` / `removeShortcut(name)` / `usePlugin(fn)` / `getPlugin(name)` / `destroy()` / `setActive(r,c)` / `scrollToRow(r, align?)`(**指定行までスクロール=HTML の `#` アンカー相当**。`align` は `'start'`〔既定=先頭・sticky ヘッダ直下〕/`'center'`/`'end'`/`'nearest'`〔可視ならそのまま〕。仮想・非仮想どちらでも動き、範囲外 `r` は自動クランプ。**選択は変えない**ので目印は `rowClass` 等を併用。例 `examples/scroll-to-row.html`) / `selectRow(r)` / `selectCol(c)` / `selectAll()` / `setColWidth(c,px)` / `setRowHeight(r,px)` / `freezeCols(n)` / `freezeRows(n)` / `moveRow(from,to)` / `moveRows(r0,r1,to)` / `clearSort()` / `toCSV(opts)` / `downloadCSV(name,opts)` / `insertRow(ri,'above'|'below')` / `insertRows(ri,count?,'above'|'below')`(count 行を1回の再描画・1 undo で一括挿入=大量挿入が O(N)) / `deleteRows(r0,r1?)` / `insertCol(ci,'left'|'right')` / `deleteCols(c0,c1?)` / `redraw()`(列定義変更後の再描画, データ・選択は保持)ほか。
|
|
922
922
|
|
|
923
923
|
## イベント
|
|
924
924
|
|
|
@@ -1089,7 +1089,8 @@ columns: [
|
|
|
1089
1089
|
cellClass: (r, c, value, row, src) => src.holiday_kbn === '1' ? 'sat' : '', // 全体版(src=元レコード=非表示フィールドも見える)
|
|
1090
1090
|
// CSS は業務側で: .tssgrid td.tg-neg { color:#c00 } .tssgrid td.tg-row-ng { background:#fdecea }
|
|
1091
1091
|
```
|
|
1092
|
-
- 列版 `columns[c].cellClass(value, {r,c,row,src})` / 全体版 `cellClass(r,c,value,row,src)
|
|
1092
|
+
- 列版 `columns[c].cellClass(value, {r,c,row,src})` / 全体版 `cellClass(r,c,value,row,src)`。**引数順が違う**ので注意(列版は第1引数が値・全体版は先頭が r,c)。返り値は文字列/配列/空(クラス無し)。
|
|
1093
|
+
- **行に色を付けるなら `rowClass` が簡単**: `rowClass: (r, row, src) => 'row-ng'` で **`<tr>` に直接クラス**が付きます(全 td に付ける全体版 cellClass の代用より素直)。**その行のセルを編集すれば即追従**。CSS は `.tssgrid tr.row-ng > td { background:#fdecea }` のように行単位で当てます。
|
|
1093
1094
|
- `src` は**元レコード**(オブジェクトデータ時)。**画面に出していない隠しフィールド基準の色付け**(勤怠の `holiday_kbn` で土日祝を色分け等)に使えます → 実例: [`examples/attendance.html`](https://tssgrid.threestarsoftware.co.jp/examples/attendance.html)。
|
|
1094
1095
|
- **他列に依存する行ルール**(状態で行を色分け等)は、依存先の編集時に `grid.redraw()` で行全体を再評価(`onAfterChange: () => grid.redraw()`)。
|
|
1095
1096
|
- 同じセルに行ルールとセルルールが両方付く場合は **CSSの記述順(後勝ち)**で優先を決めます(セル個別を後に書く)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threestarsoftware/tssgrid",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "軽量・ノービルド・日本語IME堅牢な編集グリッド(vanilla JS / 依存なし / MIT)。Excel ライクな入力画面を <script> タグ1本で。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ThreeStarSoftware (https://www.threestarsoftware.co.jp/)",
|
package/src/tssgrid.d.ts
CHANGED
|
@@ -25,6 +25,12 @@ 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
|
+
export interface CellClassCtx { r: number; c: number; row: any[]; src: Record<string, any> | null; }
|
|
30
|
+
/** cellClass/cellStyle の戻り値のクラス。文字列(空白区切り可)/配列/null 等(=クラス無し)。 */
|
|
31
|
+
export type ClassValue = string | string[] | null | undefined | false;
|
|
32
|
+
/** cellStyle の戻り値。CSS プロパティのオブジェクト(`{'background':'#fde','--pct':'42%'}`)か文字列(`'background:#fde;--pct:42%'`)。 */
|
|
33
|
+
export type StyleValue = string | Partial<CSSStyleDeclaration> | Record<string, string> | null | undefined;
|
|
28
34
|
export interface SelectionInfo { range: SelectionRange; active: CellCoord; extent: CellCoord; }
|
|
29
35
|
|
|
30
36
|
/** validator が弾いたセル(onInvalid / getInvalidCells)。 */
|
|
@@ -73,10 +79,11 @@ export interface ColumnDef {
|
|
|
73
79
|
thousands?: boolean;
|
|
74
80
|
/** 数値型: 接頭辞(例 '¥')。 */
|
|
75
81
|
prefix?: string;
|
|
76
|
-
/** 値/行を見てセルに付与する CSS
|
|
77
|
-
cellClass
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
/** 値/行を見てセルに付与する CSS クラス(条件付き書式)。**引数は `(value, { r, c, row, src })`**(第1引数がそのセルの値)。
|
|
83
|
+
* *表全体版は `TssGridOptions.cellClass`(引数順が違う: `(r, c, value, row, src)`)。 */
|
|
84
|
+
cellClass?: (value: any, cell: CellClassCtx) => ClassValue;
|
|
85
|
+
/** セル単位のインラインスタイル(連続色・データバー等)。**引数は `(value, { r, c, row, src })`**。 */
|
|
86
|
+
cellStyle?: (value: any, cell: CellClassCtx) => StyleValue;
|
|
80
87
|
/** true で表示だけ生HTML(値はテキスト保存のまま)。 */
|
|
81
88
|
html?: boolean;
|
|
82
89
|
/** 列単位の折り返し。 */
|
|
@@ -105,6 +112,14 @@ export interface TssGridOptions {
|
|
|
105
112
|
/** 入力フロー制御。Enter/Tab の移動先を上書き(null で既定方向)。入力セルだけ巡回できる。 */
|
|
106
113
|
nextCell?: (a: { r: number; c: number; key: string; shift: boolean }) => { r: number; c: number } | null;
|
|
107
114
|
columns?: ColumnDef[];
|
|
115
|
+
/** 表全体の条件付きクラス(全セルで呼ばれる)。**引数は `(r, c, value, row, src)`**(先頭が行/列番号・列版と順序が違う)。
|
|
116
|
+
* `row`=その行の値配列, `src`=元レコード(object モード時・非表示フィールドも見える)。行に色を付けたいなら `rowClass` の方が簡単。 */
|
|
117
|
+
cellClass?: (r: number, c: number, value: any, row: any[], src: Record<string, any> | null) => ClassValue;
|
|
118
|
+
/** 表全体のセル単位インラインスタイル。**引数は `(r, c, value, row, src)`**。返り値は CSS オブジェクト or 文字列。 */
|
|
119
|
+
cellStyle?: (r: number, c: number, value: any, row: any[], src: Record<string, any> | null) => StyleValue;
|
|
120
|
+
/** 行ごとに `<tr>` へ付与する CSS クラス(**行に色を付けるならこれ**)。`(r, row, src)` で呼ばれ、文字列/配列を返す。
|
|
121
|
+
* 編集でセルが再描画されると即追従。CSS は `.tssgrid tr.クラス > td { … }` のように行単位で当てる。 */
|
|
122
|
+
rowClass?: (r: number, row: any[], src: Record<string, any> | null) => ClassValue;
|
|
108
123
|
/** 検証NG時。'revert'=元に戻す / 'keep'=赤く警告して残す。 */
|
|
109
124
|
invalidMode?: 'revert' | 'keep';
|
|
110
125
|
invalidTitle?: boolean;
|
|
@@ -384,6 +399,12 @@ export declare class TssGrid {
|
|
|
384
399
|
selectRow(r: number): void;
|
|
385
400
|
selectCol(c: number): void;
|
|
386
401
|
selectAll(): void;
|
|
402
|
+
/**
|
|
403
|
+
* 指定行までスクロール(HTML の `#` アンカー相当・選択は変えない)。
|
|
404
|
+
* `align` は `'start'`(既定=その行を先頭=sticky ヘッダ直下へ)/ `'center'` / `'end'` / `'nearest'`(可視ならそのまま)。
|
|
405
|
+
* 仮想(均一行高)でも非仮想(可変行高・実DOM測定)でも動く。範囲外の `r` は自動でクランプ。
|
|
406
|
+
*/
|
|
407
|
+
scrollToRow(r: number, align?: 'start' | 'center' | 'end' | 'nearest'): void;
|
|
387
408
|
/** 選択中の列インデックス(Ctrl+ヘッダクリックの飛び飛び選択に対応・昇順)。列選択モードでなければ `[]`。 */
|
|
388
409
|
getSelectedColumns(): number[];
|
|
389
410
|
/** 選択中の行インデックス(Ctrl+行ヘッダクリックの飛び飛び選択に対応・昇順)。行選択モードでなければ `[]`。 */
|
package/src/tssgrid.js
CHANGED
|
@@ -266,6 +266,9 @@
|
|
|
266
266
|
// セル単位インラインスタイル: cellStyle(r,c,value,row,src)→{CSSプロパティ} or 'k:v;…'。列版 columns[c].cellStyle(value,{r,c,row,src})。
|
|
267
267
|
// クラス(離散)では出せない連続色・データバー幅(CSS変数)向け。例: cellStyle:(r,c,v)=>({background:heat(v)})
|
|
268
268
|
this.cellStyle = opts.cellStyle || null;
|
|
269
|
+
// 行ごとに <tr> へ付けるクラス(行の色付け向け)。rowClass(r, row, src)→クラス文字列/配列。
|
|
270
|
+
// 全体版 cellClass でも代用できるが(全 td に付く)、行単位はこちらが素直。編集で再描画時に追従(_applyRowClass)。
|
|
271
|
+
this.rowClass = opts.rowClass || null;
|
|
269
272
|
// 列の非表示(描画で畳む+移動/幅/コピーは隠し列をスキップ)。実行時は hideColumn/showColumn。
|
|
270
273
|
this.hiddenCols = new Set((opts.hiddenColumns || []).map(Number));
|
|
271
274
|
// セル単位の可変状態(キー "r,c")。整列 / 実行時 readOnly。構造変更で _structCmd がキー追従。
|
|
@@ -1154,6 +1157,21 @@
|
|
|
1154
1157
|
for (const k of next) td.classList.add(k);
|
|
1155
1158
|
if (next.length) td.dataset.ccls = next.join(' '); else delete td.dataset.ccls;
|
|
1156
1159
|
}
|
|
1160
|
+
// 行クラス: rowClass(r, row, src) → 空白区切り文字列(配列は join)。無効/例外は ''。
|
|
1161
|
+
_rowClassStr(r) {
|
|
1162
|
+
if (typeof this.rowClass !== 'function') return '';
|
|
1163
|
+
try { const x = this.rowClass(r, this.data[r], this._src ? this._src[r] : null); return x ? (Array.isArray(x) ? x.filter(Boolean).join(' ') : String(x).trim()) : ''; } catch (_) { return ''; }
|
|
1164
|
+
}
|
|
1165
|
+
// <tr> の行クラスを反映(前回分 data-rcls を外して付け直す=セル再描画で追従)。
|
|
1166
|
+
_applyRowClass(r) {
|
|
1167
|
+
const tr = this.table && this.table.querySelector('tbody tr[data-r="' + r + '"]');
|
|
1168
|
+
if (!tr) return;
|
|
1169
|
+
const prev = tr.dataset.rcls ? tr.dataset.rcls.split(/\s+/) : [];
|
|
1170
|
+
for (const k of prev) if (k) tr.classList.remove(k);
|
|
1171
|
+
const next = this._rowClassStr(r);
|
|
1172
|
+
if (next) { for (const k of next.split(/\s+/)) if (k) tr.classList.add(k); tr.dataset.rcls = next; }
|
|
1173
|
+
else delete tr.dataset.rcls;
|
|
1174
|
+
}
|
|
1157
1175
|
// セル単位のインラインスタイル: 列版 columns[c].cellStyle(value,{r,c,row,src}) + 全体版 cellStyle(r,c,value,row,src)。
|
|
1158
1176
|
// 返り値は CSSプロパティのオブジェクト {'background':'#fde','--pct':'42%'} か文字列 'background:#fde;--pct:42%'。
|
|
1159
1177
|
// cellClass(クラス=離散)では出せない**連続値の背景色・データバー幅(CSS変数)**などに(プロパティ名は kebab/標準名 or --var)。
|
|
@@ -1193,6 +1211,7 @@
|
|
|
1193
1211
|
}
|
|
1194
1212
|
if (this.cellClass || this.colCfg(c).cellClass) this._applyCellClass(td, r, c); // 条件付き書式
|
|
1195
1213
|
if (this.cellStyle || this.colCfg(c).cellStyle) this._applyCellStyle(td, r, c); // セル単位インラインスタイル
|
|
1214
|
+
if (this.rowClass) this._applyRowClass(r); // 行クラス(編集で行の色付けが即追従)
|
|
1196
1215
|
}
|
|
1197
1216
|
|
|
1198
1217
|
_colWidth(c) { return this.colW[c] != null ? this.colW[c] : this.defColW; }
|
|
@@ -1373,7 +1392,8 @@
|
|
|
1373
1392
|
const inner = after ? (lbl + mark) : (mark + lbl); // ⠿ を番号の前/後に(rowReorderMark)。番号なしは前後どちらでも中央
|
|
1374
1393
|
const rhCls = (canMove && this.rowReorderWhole) ? ' tg-rowmove-cell' : ''; // 'header'=行ヘッダーセル全体を掴み手に(grab カーソル)。番号/⠿の表示は独立
|
|
1375
1394
|
const rcls = (r < this.frozenRows) ? (' tg-frozen-row' + (r === this.frozenRows - 1 ? ' tg-frozen-row-edge' : '')) : ''; // 行固定
|
|
1376
|
-
|
|
1395
|
+
const rowc = this.rowClass ? this._rowClassStr(r) : ''; // 行クラス(rowClass)=<tr> に付与
|
|
1396
|
+
let html = '<tr data-r="' + r + '" class="tg-row' + rcls + (rowc ? ' ' + rowc : '') + '"' + (rowc ? ' data-rcls="' + TssGrid.esc(rowc) + '"' : '') + ' style="height:' + this._rowHeight(r) + 'px">' + (rh ? '<th class="rowhead' + rhCls + '" data-r="' + r + '">' + inner + rgrip + '</th>' : '');
|
|
1377
1397
|
for (let c = 0; c < this.COLS; c++) {
|
|
1378
1398
|
if (this._merges && this._isCovered(r, c)) continue; // 結合に覆われた従属セルは td を出さない(アンカーが colspan で覆う)
|
|
1379
1399
|
const cs = this._merges ? this._colspanAt(r, c) : 1; // 結合アンカーは colspan で従属列ぶんを覆う
|
|
@@ -1466,6 +1486,35 @@
|
|
|
1466
1486
|
if (st !== cur) this.wrap.scrollTop = st;
|
|
1467
1487
|
this._renderWindow(this._vWindow(), false); // 窓を即同期(place 前に DOM を確定)
|
|
1468
1488
|
}
|
|
1489
|
+
// 指定行までスクロール(HTML の # アンカー相当)。align: 'start'(既定=先頭へ)/'center'/'end'/'nearest'。
|
|
1490
|
+
// 仮想(均一行高)でも非仮想(実DOM・可変行高)でも動く。sticky ヘッダの下にちゃんと収める。
|
|
1491
|
+
scrollToRow(r, align = 'start') {
|
|
1492
|
+
if (!this.wrap || !this.ROWS) return;
|
|
1493
|
+
r = Math.max(0, Math.min(this.ROWS - 1, Math.trunc(r || 0)));
|
|
1494
|
+
if (align === 'nearest' && this.virtual) { this._vEnsureVisible(r); return; } // 最小移動は既存ロジックに委譲
|
|
1495
|
+
const head = this.colHeaders ? (this._theadH || 0) : 0;
|
|
1496
|
+
const viewH = this.wrap.clientHeight || 400;
|
|
1497
|
+
let top, rowH;
|
|
1498
|
+
if (this.virtual) {
|
|
1499
|
+
rowH = this.defRowH; top = r * rowH; // 均一行高: content-top(sticky thead 直下が基準)
|
|
1500
|
+
} else {
|
|
1501
|
+
const tr = this.table && this.table.querySelector('tbody tr[data-r="' + r + '"]');
|
|
1502
|
+
if (!tr) return;
|
|
1503
|
+
const t = tr.getBoundingClientRect(), w = this.wrap.getBoundingClientRect();
|
|
1504
|
+
rowH = t.height; top = t.top - w.top + this.wrap.scrollTop - head; // 実DOMから測る(可変行高対応)
|
|
1505
|
+
}
|
|
1506
|
+
let st = top; // start: 行を先頭(sticky thead 直下)へ
|
|
1507
|
+
if (align === 'center') st = top - Math.max(0, (viewH - head - rowH) / 2);
|
|
1508
|
+
else if (align === 'end') st = top - Math.max(0, viewH - head - rowH);
|
|
1509
|
+
else if (align === 'nearest') { // 非仮想の nearest: 既に可視なら動かさない
|
|
1510
|
+
const vt = top - this.wrap.scrollTop;
|
|
1511
|
+
if (vt >= 0 && vt + rowH <= viewH - head) return;
|
|
1512
|
+
if (vt > 0) st = top - (viewH - head - rowH); // 下にはみ出し→下端、上は top のまま
|
|
1513
|
+
}
|
|
1514
|
+
const max = this.wrap.scrollHeight - this.wrap.clientHeight;
|
|
1515
|
+
this.wrap.scrollTop = Math.max(0, Math.min(st, max));
|
|
1516
|
+
if (this.virtual) this._renderWindow(this._vWindow(), false); // 窓を即同期
|
|
1517
|
+
}
|
|
1469
1518
|
buildTable() {
|
|
1470
1519
|
if (this._renderPaused) { this._renderDirty = true; return; } // バッチ描画中は最後にまとめて1回(_withBatchedRender)
|
|
1471
1520
|
if (this._customEditor && this._customCancel) this._customCancel(); // 開いているカスタムエディタを閉じる(body直下ポップアップの取り残し防止・_commitActive と同じ扱い)
|