@threestarsoftware/tssgrid 0.0.1
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/LICENSE +21 -0
- package/README.md +1091 -0
- package/package.json +57 -0
- package/plugins/tss-autocomplete.css +16 -0
- package/plugins/tss-autocomplete.js +121 -0
- package/plugins/tss-bar.js +51 -0
- package/plugins/tss-calc.js +103 -0
- package/plugins/tss-calendar.css +40 -0
- package/plugins/tss-calendar.js +133 -0
- package/plugins/tss-filter.css +32 -0
- package/plugins/tss-filter.js +146 -0
- package/plugins/tss-formula.js +87 -0
- package/plugins/tss-furigana.js +84 -0
- package/plugins/tss-heatmap.js +66 -0
- package/plugins/tss-merge.js +144 -0
- package/plugins/tss-messages.js +68 -0
- package/plugins/tss-running.js +82 -0
- package/plugins/tss-shared.js +114 -0
- package/plugins/tss-sparkline.js +57 -0
- package/plugins/tss-syncscroll.js +95 -0
- package/plugins/tss-time.css +23 -0
- package/plugins/tss-time.js +91 -0
- package/plugins/tss-toggle.css +7 -0
- package/plugins/tss-toggle.js +86 -0
- package/plugins/tss-totals.js +92 -0
- package/src/tssgrid.css +201 -0
- package/src/tssgrid.d.ts +329 -0
- package/src/tssgrid.js +2440 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*! TssTotals — 列方向の集計(合計/平均/件数/最小/最大)プラグイン。**計算だけ=描画は利用者側**。
|
|
2
|
+
*
|
|
3
|
+
* 「縦の合計」をコアに埋めると合計行の置き場が minSpareRows/ソート/空行処理と衝突して肥大化する。
|
|
4
|
+
* そこで **集計ロジックだけ** を担い、結果は onTotals コールバック / getTotals() で返す。
|
|
5
|
+
* どこに出すかは自由=プレーンなフッタ、ラベル、あるいは **別の TssGrid(合計表)** に流すのも一行。
|
|
6
|
+
*
|
|
7
|
+
* 使い方:
|
|
8
|
+
* plugins: [ TssTotals({
|
|
9
|
+
* columns: { amount: 'sum', quantity: 'sum', price: 'avg' }, // dataキー → 集計種別
|
|
10
|
+
* onTotals: (t) => { footer.textContent = '合計 ' + t.amount; } // 再計算のたびに呼ばれる
|
|
11
|
+
* }) ]
|
|
12
|
+
* // ↑ formula と併用するときは plugins:[TssFormula, TssTotals](合計を formula 結果の後に出すため)
|
|
13
|
+
*
|
|
14
|
+
* opts:
|
|
15
|
+
* columns: { dataキー: 'sum'|'avg'|'count'|'min'|'max' | ((rawValues, numbers)=>number) }
|
|
16
|
+
* count=非空セル数 / sum・avg・min・max=数値セルのみ。関数で独自集計も可。
|
|
17
|
+
* skipEmpty: 全列が空の行を除外(既定 true。末尾の予備空行などを集計から外す)
|
|
18
|
+
* decimals: 結果の小数桁(avg 等。既定 null=丸めない)
|
|
19
|
+
* onTotals: (totals) => void 再計算のたびに呼ばれる。totals = { dataキー: 数値 }
|
|
20
|
+
*
|
|
21
|
+
* handle(usePlugin の戻り / grid.getPlugin('totals')):
|
|
22
|
+
* getTotals() … 最新の集計オブジェクト / recompute() … 手動再計算 / destroy() … フック解除
|
|
23
|
+
*/
|
|
24
|
+
(function (root) {
|
|
25
|
+
'use strict';
|
|
26
|
+
function TssTotals(opts) {
|
|
27
|
+
opts = opts || {};
|
|
28
|
+
var spec = opts.columns || {};
|
|
29
|
+
var skipEmpty = opts.skipEmpty !== false;
|
|
30
|
+
var decimals = (opts.decimals == null) ? null : opts.decimals;
|
|
31
|
+
var onTotals = (typeof opts.onTotals === 'function') ? opts.onTotals : null;
|
|
32
|
+
|
|
33
|
+
function aggregate(kind, values) {
|
|
34
|
+
var nums = [];
|
|
35
|
+
for (var i = 0; i < values.length; i++) { var s = values[i]; if (s === '' || s == null) continue; var n = Number(s); if (isFinite(n)) nums.push(n); }
|
|
36
|
+
if (typeof kind === 'function') { try { return kind(values, nums); } catch (e) { return 0; } }
|
|
37
|
+
var sum = 0, k;
|
|
38
|
+
switch (kind) {
|
|
39
|
+
case 'sum': for (k = 0; k < nums.length; k++) sum += nums[k]; return sum;
|
|
40
|
+
case 'avg': if (!nums.length) return 0; for (k = 0; k < nums.length; k++) sum += nums[k]; return sum / nums.length;
|
|
41
|
+
case 'count': { var c = 0; for (k = 0; k < values.length; k++) if (values[k] !== '' && values[k] != null) c++; return c; }
|
|
42
|
+
case 'min': return nums.length ? Math.min.apply(null, nums) : 0;
|
|
43
|
+
case 'max': return nums.length ? Math.max.apply(null, nums) : 0;
|
|
44
|
+
default: return 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// この plugin の実体(usePlugin が factory(grid, opts) として呼ぶ)
|
|
49
|
+
return function (grid) {
|
|
50
|
+
var last = {};
|
|
51
|
+
function emptyRow(r) {
|
|
52
|
+
for (var c = 0; c < grid.COLS; c++) { var k = grid.columns[c] && grid.columns[c].data; if (k == null) continue; if (grid.data[r][c] !== '' && grid.data[r][c] != null) return false; }
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
function compute() {
|
|
56
|
+
var totals = {}, keys = Object.keys(spec);
|
|
57
|
+
for (var ki = 0; ki < keys.length; ki++) {
|
|
58
|
+
var key = keys[ki];
|
|
59
|
+
var col = -1; for (var cc = 0; cc < grid.COLS; cc++) if (grid.columns[cc] && grid.columns[cc].data === key) { col = cc; break; }
|
|
60
|
+
if (col < 0) { totals[key] = 0; continue; }
|
|
61
|
+
var vals = [];
|
|
62
|
+
for (var r = 0; r < grid.ROWS; r++) { if (skipEmpty && emptyRow(r)) continue; vals.push(grid.data[r][col]); }
|
|
63
|
+
var v = aggregate(spec[key], vals);
|
|
64
|
+
if (decimals != null && typeof v === 'number') { var p = Math.pow(10, decimals); v = Math.round(v * p) / p; }
|
|
65
|
+
totals[key] = v;
|
|
66
|
+
}
|
|
67
|
+
last = totals;
|
|
68
|
+
if (onTotals) { try { onTotals(totals); } catch (e) {} }
|
|
69
|
+
return totals;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 変更フック: いずれも「先に他のフック(prev)を回してから合計」=formula 等の派生結果を反映した後に集計。
|
|
73
|
+
var prevAfter = grid.onAfterChange;
|
|
74
|
+
grid.onAfterChange = function (ch, src) { if (prevAfter) { try { prevAfter.call(grid, ch, src); } catch (e) {} } compute(); };
|
|
75
|
+
var prevSetData = grid.setData.bind(grid);
|
|
76
|
+
grid.setData = function (rows) { prevSetData(rows); compute(); };
|
|
77
|
+
var prevStruct = grid.onStructureChange;
|
|
78
|
+
grid.onStructureChange = function (info) { if (prevStruct) { try { prevStruct.call(grid, info); } catch (e) {} } compute(); };
|
|
79
|
+
|
|
80
|
+
compute(); // 初期集計
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
name: 'totals',
|
|
84
|
+
getTotals: function () { return last; },
|
|
85
|
+
recompute: compute,
|
|
86
|
+
destroy: function () { grid.onAfterChange = prevAfter; grid.setData = prevSetData; grid.onStructureChange = prevStruct; },
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (typeof module !== 'undefined' && module.exports) module.exports = TssTotals;
|
|
91
|
+
else root.TssTotals = TssTotals;
|
|
92
|
+
})(typeof self !== 'undefined' ? self : this);
|
package/src/tssgrid.css
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/* TssGrid — スタイル(すべて .tssgrid 配下にscope)
|
|
2
|
+
|
|
3
|
+
■ テーマ(業務側で上書きしやすいよう主要な色/サイズを CSS 変数に)
|
|
4
|
+
業務ごとに統一感を出したいときは、これらを上書きするだけでOK(フルのテーマエンジンは持たない軽量方針):
|
|
5
|
+
.tssgrid { --tg-accent:#c0392b; --tg-header-bg:#f3eaea; --tg-radius:8px; } // 全グリッド
|
|
6
|
+
.myapp .tssgrid { --tg-font-size:14px; } // 特定業務だけ
|
|
7
|
+
個別の細かい調整は従来どおり `.tssgrid .tg-xxx { … }` で上書きできます。 */
|
|
8
|
+
.tssgrid {
|
|
9
|
+
--tg-font-size: 13px;
|
|
10
|
+
--tg-border: #d6dde4; /* セル罫線 */
|
|
11
|
+
--tg-wrap-border: #b9c2cc; /* 外枠 */
|
|
12
|
+
--tg-cell-bg: #fff; /* セル背景 */
|
|
13
|
+
--tg-header-bg: #eef2f6; /* ヘッダ/行番号 背景の既定(下の2つの共通元) */
|
|
14
|
+
--tg-colheader-bg: var(--tg-header-bg); /* 列ヘッダ背景(未指定なら --tg-header-bg を継ぐ) */
|
|
15
|
+
--tg-rowhead-bg: var(--tg-header-bg); /* 行番号列 背景(未指定なら --tg-header-bg を継ぐ) */
|
|
16
|
+
--tg-header-fg: #222; /* 列ヘッダ文字 */
|
|
17
|
+
--tg-rowhead-fg: #8889a0; /* 行番号文字 */
|
|
18
|
+
--tg-accent: #1573c0; /* 選択枠・フィルハンドル(会社カラーの青) */
|
|
19
|
+
--tg-sel-bg: #eef4ff; /* 選択セル背景・メニューhover */
|
|
20
|
+
--tg-selhdr-bg: #cdddff; /* 選択中の行/列ヘッダ */
|
|
21
|
+
--tg-edit-accent: #2aa0d8; /* 編集枠・グリップ(青) */
|
|
22
|
+
--tg-edit-bg: #f2fbff; /* 編集中セル背景 */
|
|
23
|
+
--tg-frozen-edge: #9aa7b5; /* 固定列の見切り線 */
|
|
24
|
+
--tg-readonly-bg: #f4f6f8; /* 読み取り専用セル */
|
|
25
|
+
--tg-readonly-fg: #8a94a0;
|
|
26
|
+
--tg-placeholder-fg: #aeb6c0; /* 空セルのヒント */
|
|
27
|
+
--tg-invalid-fg: #d61f2b; /* 検証NG(keep) */
|
|
28
|
+
--tg-invalid-bg: #ffe3e6;
|
|
29
|
+
--tg-muted: #b6bdc6; /* 無効メニュー項目など */
|
|
30
|
+
--tg-radius: 6px; /* メニュー/ダンプの角丸 */
|
|
31
|
+
--tg-box-color: var(--tg-accent); /* セル囲み .tg-box の色 */
|
|
32
|
+
--tg-box-width: 2px; /* セル囲み .tg-box の太さ */
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.tssgrid .tg-wrap {
|
|
36
|
+
position: relative; display: inline-block;
|
|
37
|
+
border: 1px solid var(--tg-wrap-border); overflow: auto; max-height: 60vh; user-select: none;
|
|
38
|
+
isolation: isolate; /* 選択枠等の内部 z-index をこのグリッド内に閉じ込める=複数グリッドが重なっても他表の上に漏れない */
|
|
39
|
+
}
|
|
40
|
+
/* フォーカスが自分に無い時は選択枠を淡く(キーが効かない事を可視化) */
|
|
41
|
+
/* 非フォーカス時: 選択枠/範囲/フィルハンドルをグレー+はっきり薄く。
|
|
42
|
+
=複数グリッドのうちどれがアクティブか一目で分かる(フォーカス中=くっきり青/他=ふわっと薄い)。
|
|
43
|
+
位置の手がかりは残るので initialCell の初期カーソルも薄く見える(消えない)。 */
|
|
44
|
+
.tssgrid .tg-wrap:not(:focus-within) .tg-selrange,
|
|
45
|
+
.tssgrid .tg-wrap:not(:focus-within) .tg-selbox { border-color: #aab4be; }
|
|
46
|
+
.tssgrid .tg-wrap:not(:focus-within) .tg-selrange,
|
|
47
|
+
.tssgrid .tg-wrap:not(:focus-within) .tg-selbox,
|
|
48
|
+
.tssgrid .tg-wrap:not(:focus-within) .tg-fillhandle { opacity: .42; }
|
|
49
|
+
|
|
50
|
+
/* border-collapse: separate(+ border-spacing:0)で各セルが自分の罫線を自分のboxの中に描く。
|
|
51
|
+
collapse だと罫線がテーブル単位の共有1px線になり、sticky セルの背景が境界1pxを覆いきれず
|
|
52
|
+
裏に回ったセルの罫線が透けて見える(sticky+collapse の典型問題)。separate なら sticky セルが
|
|
53
|
+
z-index で裏セルを完全に覆うので透けない。外周の上端/左端は .tg-wrap の border が担う。 */
|
|
54
|
+
.tssgrid table.tg-table { border-collapse: separate; border-spacing: 0; table-layout: fixed; }
|
|
55
|
+
.tssgrid .tg-table th,
|
|
56
|
+
.tssgrid .tg-table td {
|
|
57
|
+
/* 幅は <colgroup> の <col>、行高は <tr> の height で制御
|
|
58
|
+
(CSS で width/height を固定すると fixed レイアウトで縮小が効かない)。
|
|
59
|
+
罫線は右+下のみ(border-spacing:0 で隣接するので二重にならない。外周上/左は wrap が担当) */
|
|
60
|
+
border-right: 1px solid var(--tg-border); border-bottom: 1px solid var(--tg-border); padding: 0 6px;
|
|
61
|
+
font-size: var(--tg-font-size); box-sizing: border-box; white-space: nowrap; overflow: hidden;
|
|
62
|
+
}
|
|
63
|
+
.tssgrid .tg-table th { background: var(--tg-colheader-bg); color: var(--tg-header-fg); font-weight: 600; text-align: center; }
|
|
64
|
+
/* z-index 階層(.tg-wrap 内): 通常セル 0 < 本体sticky(rowhead/frozen) 2 < 選択オーバーレイ 3
|
|
65
|
+
< editor/select 4 < fillhandle 5 < ヘッダ thead 6 < 角/固定列ヘッダ 7 < リサイズ線 8。
|
|
66
|
+
※ 本体sticky を選択オーバーレイより下にするのが肝(border-collapse:separate で本体セルの
|
|
67
|
+
z-index が効くようになり、3 のままだと固定セルが青の選択枠を覆い隠す=枠が見えない)。 */
|
|
68
|
+
/* ヘッダ行=sticky top(行ヘッダ rowhead は sticky left のみ) */
|
|
69
|
+
.tssgrid .tg-table thead th { height: 28px; position: sticky; top: 0; z-index: 6; }
|
|
70
|
+
/* ヘッダはクリックで行/列選択(角は全選択) */
|
|
71
|
+
.tssgrid .tg-table thead th[data-c], .tssgrid .tg-table th.rowhead[data-r], .tssgrid .tg-table th.corner { cursor: pointer; }
|
|
72
|
+
.tssgrid .tg-table td { background: var(--tg-cell-bg); cursor: cell; }
|
|
73
|
+
/* 表示専用(cursor:false): セルのカーソルを既定に(選択枠・ハイライトは JS 側で非表示) */
|
|
74
|
+
.tssgrid.tg-no-cursor .tg-table td { cursor: default; }
|
|
75
|
+
/* 非表示列: col を幅0にし、セルは padding/罫線を消して内容を overflow:hidden で畳む。
|
|
76
|
+
※ display:none は使わない(fixed レイアウトでセルがトラックからずれ、隣の列が消える) */
|
|
77
|
+
.tssgrid .tg-table th.tg-hidden, .tssgrid .tg-table td.tg-hidden { padding: 0; border-right-width: 0; }
|
|
78
|
+
.tssgrid .tg-table td.tg-num { text-align: right; } /* 数値書式の列は右寄せ */
|
|
79
|
+
/* 整列(セル/列)。tg-num の後に置いて明示指定を勝たせる */
|
|
80
|
+
.tssgrid .tg-table td.tg-h-left { text-align: left; }
|
|
81
|
+
.tssgrid .tg-table td.tg-h-center { text-align: center; }
|
|
82
|
+
.tssgrid .tg-table td.tg-h-right { text-align: right; }
|
|
83
|
+
.tssgrid .tg-table td.tg-v-top { vertical-align: top; }
|
|
84
|
+
.tssgrid .tg-table td.tg-v-middle { vertical-align: middle; }
|
|
85
|
+
.tssgrid .tg-table td.tg-v-bottom { vertical-align: bottom; }
|
|
86
|
+
.tssgrid .tg-table td.sel { background: var(--tg-sel-bg); }
|
|
87
|
+
/* 空セルの placeholder(薄いヒント文字。getData には入らない) */
|
|
88
|
+
.tssgrid .tg-table td.tg-placeholder { color: var(--tg-placeholder-fg); }
|
|
89
|
+
/* セル内折り返し: グリッド全体(.tg-wordwrap) or 列単位(td.tg-wrap-cell)。行高は中身に合わせて伸びる */
|
|
90
|
+
.tssgrid.tg-wordwrap .tg-table td, .tssgrid .tg-table td.tg-wrap-cell { white-space: normal; word-break: break-word; }
|
|
91
|
+
/* 読み取り専用セル: 淡色+既定カーソル(編集不可・全変更経路で拒否) */
|
|
92
|
+
.tssgrid .tg-table td.tg-readonly { background: var(--tg-readonly-bg); color: var(--tg-readonly-fg); cursor: default; }
|
|
93
|
+
.tssgrid .tg-table td.tg-readonly.sel { background: #e7edf5; }
|
|
94
|
+
.tssgrid .tg-table td.tg-readonly.tg-dropdown { padding-right: 6px; } /* 開けないので ▾ は出さない */
|
|
95
|
+
.tssgrid .tg-table td.tg-readonly.tg-dropdown::after { display: none; }
|
|
96
|
+
/* invalidMode:'keep' で検証 NG のセル: 値は残して赤く警告(色は変数で調整可) */
|
|
97
|
+
.tssgrid .tg-table td.tg-invalid { color: var(--tg-invalid-fg); background: var(--tg-invalid-bg); }
|
|
98
|
+
.tssgrid .tg-table td.tg-invalid.sel { background: #ffd0d6; }
|
|
99
|
+
.tssgrid .tg-table th.rowhead { background: var(--tg-rowhead-bg); width: 40px; text-align: center; color: var(--tg-rowhead-fg); position: sticky; left: 0; z-index: 2; }
|
|
100
|
+
.tssgrid .tg-table thead th.corner { z-index: 7; } /* 左上の角は最前面(top+left 固定) */
|
|
101
|
+
/* 列固定: 左から frozenCols 列を sticky で固定(left は JS が設定)。z は 選択オーバーレイ(3)より下に */
|
|
102
|
+
.tssgrid .tg-table td.tg-frozen { position: sticky; z-index: 2; }
|
|
103
|
+
.tssgrid .tg-table thead th.tg-frozen { z-index: 7; } /* 固定列のヘッダは最前面(top+left) */
|
|
104
|
+
.tssgrid .tg-table .tg-frozen-edge { border-right: 2px solid var(--tg-frozen-edge); } /* 固定境界の見切り線 */
|
|
105
|
+
/* 行固定: 上から frozenRows 行を sticky top(top は JS が設定)。z は frozen列と同様に選択オーバーレイ(3)より下 */
|
|
106
|
+
.tssgrid .tg-table tr.tg-frozen-row > td { position: sticky; z-index: 2; }
|
|
107
|
+
.tssgrid .tg-table tr.tg-frozen-row > th.rowhead { z-index: 3; } /* 行ヘッダ=top+left の角 */
|
|
108
|
+
.tssgrid .tg-table tr.tg-frozen-row > td.tg-frozen { z-index: 3; } /* 固定行∩固定列の角 */
|
|
109
|
+
.tssgrid .tg-table tr.tg-frozen-row-edge > td, .tssgrid .tg-table tr.tg-frozen-row-edge > th { border-bottom: 2px solid var(--tg-frozen-edge); } /* 固定行の見切り線 */
|
|
110
|
+
/* 選択中の行/列ヘッダ(rowhead より後に置いて背景指定を勝たせる) */
|
|
111
|
+
.tssgrid .tg-table th.selhdr { background: var(--tg-selhdr-bg); color: var(--tg-selhdr-fg, #1a3a6b); }
|
|
112
|
+
/* ネストヘッダ(グループ見出し): colspan で複数列をまとめる結合セル。中央寄せ+区切り線 */
|
|
113
|
+
.tssgrid .tg-table th.tg-grouphdr { text-align: center; font-weight: 600; border-bottom: 1px solid var(--tg-border); cursor: pointer; }
|
|
114
|
+
/* セル結合(TssMerge)アンカー: 既定は縦中央。水平寄せは列/セルの align を尊重。中央寄せ等は業務側で
|
|
115
|
+
.tssgrid td.tg-merged{ text-align:center } のように上書き可。 */
|
|
116
|
+
.tssgrid .tg-table td.tg-merged { vertical-align: middle; }
|
|
117
|
+
/* セル囲み: cellClass で付けて任意のセルを四角く強調。border ではなく inset shadow なので
|
|
118
|
+
A1・1行目・1列目・端セルでも全4辺を囲める+レイアウト不変。色/太さは変数で。
|
|
119
|
+
バリエーションは業務側で: .tssgrid td.tg-box-warn{ --tg-box-color:#e67e22 } 等 */
|
|
120
|
+
.tssgrid .tg-table td.tg-box { box-shadow: inset 0 0 0 var(--tg-box-width) var(--tg-box-color); }
|
|
121
|
+
|
|
122
|
+
/* リサイズ用グリップ(ヘッダ内に配置。境界をドラッグして列幅/行高を変更) */
|
|
123
|
+
.tssgrid .tg-colgrip { position: absolute; top: 0; right: 0; width: 6px; height: 100%; cursor: col-resize; z-index: 3; }
|
|
124
|
+
.tssgrid .tg-rowgrip { position: absolute; left: 0; bottom: 0; width: 100%; height: 6px; cursor: row-resize; z-index: 3; }
|
|
125
|
+
/* 行移動の「つまみ」(行ヘッダ内、行番号の左)。ドラッグで行を並べ替え */
|
|
126
|
+
.tssgrid .tg-rowmove { cursor: grab; color: #b3b6c8; margin-right: 2px; font-size: 11px; line-height: 1; user-select: none; vertical-align: 1px; }
|
|
127
|
+
.tssgrid .tg-rowmove:hover { color: var(--tg-accent); }
|
|
128
|
+
.tssgrid .tg-rowmove:active { cursor: grabbing; }
|
|
129
|
+
.tssgrid .tg-colgrip:hover { background: var(--tg-edit-accent); opacity: .5; }
|
|
130
|
+
.tssgrid .tg-rowgrip:hover { background: var(--tg-edit-accent); opacity: .5; }
|
|
131
|
+
/* リサイズのプレビュー線(ドラッグ中だけ表示、離して確定) */
|
|
132
|
+
.tssgrid .tg-resizeguide { position: absolute; background: #4a90e2; box-shadow: 0 0 0 1px rgba(74,144,226,.3); pointer-events: none; z-index: 8; display: none; }
|
|
133
|
+
|
|
134
|
+
/* 選択オーバーレイは本体sticky(2)より上=固定セル上でも青枠が見える。ヘッダ(6)より下=スクロールで隠れる */
|
|
135
|
+
.tssgrid .tg-selrange { position: absolute; border: 2px solid var(--tg-accent); box-sizing: border-box; pointer-events: none; z-index: 3; display: none; }
|
|
136
|
+
.tssgrid .tg-selbox { position: absolute; border: 1px solid var(--tg-accent); box-sizing: border-box; pointer-events: none; z-index: 3; display: none; }
|
|
137
|
+
.tssgrid .tg-copybox { position: absolute; border: 2px dashed #13a6c8; box-sizing: border-box; pointer-events: none; z-index: 3; display: none; }
|
|
138
|
+
.tssgrid .tg-fillhandle { position: absolute; width: 8px; height: 8px; background: var(--tg-accent); border: 1px solid #fff; box-sizing: border-box; z-index: 5; display: none; cursor: crosshair; }
|
|
139
|
+
.tssgrid .tg-fillpreview{ position: absolute; border: 2px dashed #888; box-sizing: border-box; pointer-events: none; z-index: 3; display: none; }
|
|
140
|
+
|
|
141
|
+
/* 唯一の input。nav=透明sink / edit=可視 / invalid=エラー(将来用フック) */
|
|
142
|
+
.tssgrid .tg-editor { position: absolute; box-sizing: border-box; margin: 0; padding: 0 5px; font-size: var(--tg-font-size); font-family: inherit; z-index: 4; outline: none; }
|
|
143
|
+
.tssgrid .tg-editor.nav { color: transparent; background: transparent; border: none; caret-color: transparent; pointer-events: none; }
|
|
144
|
+
.tssgrid .tg-editor.edit { color: #222; background: var(--tg-edit-bg); border: 2px solid var(--tg-edit-accent); caret-color: auto; pointer-events: auto; }
|
|
145
|
+
.tssgrid .tg-editor.invalid { border-color: #e23; background: #fff3f4; }
|
|
146
|
+
|
|
147
|
+
/* チェックボックス列: セル中央に配置(クリックでトグル) */
|
|
148
|
+
.tssgrid .tg-table td .tg-cb { display: block; margin: 0 auto; cursor: pointer; }
|
|
149
|
+
/* ヘッダの全選択チェックボックス(columns[c].headerCheckbox:false で非表示)。外部CSSで再デザイン可 */
|
|
150
|
+
.tssgrid .tg-table thead th .tg-head-cb { display: inline-block; margin: 0 4px 0 0; vertical-align: middle; cursor: pointer; }
|
|
151
|
+
|
|
152
|
+
/* ドロップダウン列: V(シェブロン)キャレットを常時表示。編集中の <select> と同じ SVG=サイズも一致 */
|
|
153
|
+
.tssgrid .tg-table td.tg-dropdown { position: relative; padding-right: 18px; cursor: pointer; }
|
|
154
|
+
.tssgrid .tg-table td.tg-dropdown::after {
|
|
155
|
+
content: ''; position: absolute; right: 5px; top: 50%; transform: translateY(-50%);
|
|
156
|
+
width: 11px; height: 8px; pointer-events: none;
|
|
157
|
+
background: url("data:image/svg+xml;charset=utf8,<svg xmlns='http://www.w3.org/2000/svg' width='11' height='8'><path d='M1.5 2.2l4 4 4-4' fill='none' stroke='%237d8a99' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>") no-repeat center / 11px 8px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* カスタムエディタのアイコン(editor.icon を申告した列。右端に表示=「ここ開けます」の合図) */
|
|
161
|
+
.tssgrid .tg-table td.tg-haspicker { position: relative; padding-right: 20px; cursor: pointer; }
|
|
162
|
+
.tssgrid .tg-table td.tg-haspicker::after {
|
|
163
|
+
content: attr(data-ico); position: absolute; right: 5px; top: 0; bottom: 0;
|
|
164
|
+
display: flex; align-items: center; font-size: 12px; color: #7d8a99; pointer-events: none;
|
|
165
|
+
}
|
|
166
|
+
.tssgrid .tg-table td.tg-readonly.tg-haspicker { padding-right: 6px; } /* 読取専用は出さない */
|
|
167
|
+
.tssgrid .tg-table td.tg-readonly.tg-haspicker::after { display: none; }
|
|
168
|
+
|
|
169
|
+
/* dropdown 編集用の <select>(editor と同じ位置に重ねる)。
|
|
170
|
+
ネイティブ矢印を消し、非編集セルの ▾ と同じ見た目のキャレットを描いて統一する。 */
|
|
171
|
+
.tssgrid .tg-select {
|
|
172
|
+
position: absolute; box-sizing: border-box; margin: 0; font-size: var(--tg-font-size); font-family: inherit;
|
|
173
|
+
z-index: 4; background-color: var(--tg-edit-bg); outline: none;
|
|
174
|
+
/* セルと同じ罫線(右・下のみ)を transparent で持たせ、青枠は box-shadow(レイアウト非干渉)で描く。
|
|
175
|
+
2px ボーダーだと中身が内側に押されて開いた時に文字・V が右斜め下にズレるため、それを回避。
|
|
176
|
+
padding と V 位置もセル(td)と完全一致=開閉で動かない。 */
|
|
177
|
+
border: 1px solid transparent; border-top-width: 0; border-left-width: 0;
|
|
178
|
+
box-shadow: inset 0 0 0 2px var(--tg-edit-accent);
|
|
179
|
+
/* 開始位置を 1px 上げて上の隙間を消す+ネイティブ select の文字が 1px 下がる分も相殺 */
|
|
180
|
+
margin-top: -1px;
|
|
181
|
+
-webkit-appearance: none; -moz-appearance: none; appearance: none; padding: 0 18px 0 6px;
|
|
182
|
+
background-image: url("data:image/svg+xml;charset=utf8,<svg xmlns='http://www.w3.org/2000/svg' width='11' height='8'><path d='M1.5 2.2l4 4 4-4' fill='none' stroke='%237d8a99' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
|
|
183
|
+
background-repeat: no-repeat; background-position: right 5px center; background-size: 11px 8px;
|
|
184
|
+
}
|
|
185
|
+
.tssgrid .tg-select::-ms-expand { display: none; } /* 旧Edge/IE のネイティブ矢印も消す */
|
|
186
|
+
|
|
187
|
+
.tssgrid .tg-dump { box-sizing: border-box; background: #0b1020; color: #9fe; padding: 8px; border-radius: var(--tg-radius); font-size: 11px; max-height: 120px; overflow: auto; margin-top: 6px; }
|
|
188
|
+
|
|
189
|
+
/* 右クリックメニュー(行/列の挿入・削除)。fixed でカーソル位置に出す(wrap の overflow に切られない) */
|
|
190
|
+
.tssgrid .tg-menu { position: fixed; z-index: 100; background: #fff; border: 1px solid var(--tg-wrap-border); border-radius: var(--tg-radius); box-shadow: 0 4px 16px rgba(0,0,0,.18); padding: 4px 0; min-width: 150px; font-size: var(--tg-font-size); color: #222; user-select: none; }
|
|
191
|
+
.tssgrid .tg-menu .tg-mi { padding: 6px 16px; cursor: pointer; white-space: nowrap; }
|
|
192
|
+
.tssgrid .tg-menu .tg-mi:hover { background: var(--tg-sel-bg); }
|
|
193
|
+
.tssgrid .tg-menu .tg-mi.tg-danger:hover { background: var(--tg-invalid-bg); color: var(--tg-invalid-fg); }
|
|
194
|
+
.tssgrid .tg-menu .tg-mi.tg-disabled { color: var(--tg-muted); cursor: default; }
|
|
195
|
+
.tssgrid .tg-menu .tg-mi.tg-disabled:hover { background: none; }
|
|
196
|
+
.tssgrid .tg-menu .tg-sep { height: 1px; background: #e3e8ee; margin: 4px 0; }
|
|
197
|
+
/* サブメニュー: 親項目に ▶、ホバーで右側へフライアウト */
|
|
198
|
+
.tssgrid .tg-menu .tg-mi.tg-has-sub { position: relative; padding-right: 26px; }
|
|
199
|
+
.tssgrid .tg-menu .tg-mi.tg-has-sub::after { content: '▶'; position: absolute; right: 10px; top: 0; bottom: 0; display: flex; align-items: center; font-size: 9px; color: #7d8a99; }
|
|
200
|
+
.tssgrid .tg-menu .tg-submenu { display: none; position: absolute; left: 100%; top: -5px; background: #fff; border: 1px solid var(--tg-wrap-border); border-radius: var(--tg-radius); box-shadow: 0 4px 16px rgba(0,0,0,.18); padding: 4px 0; min-width: 150px; }
|
|
201
|
+
.tssgrid .tg-menu .tg-mi.tg-has-sub:hover > .tg-submenu { display: block; }
|
package/src/tssgrid.d.ts
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// TssGrid 型定義(公開APIの契約)
|
|
3
|
+
// - これが「外から呼べるもの」の正本。エディタ補完にはここに宣言したものだけ出る
|
|
4
|
+
// =内部メソッド(setCell / pushCmd / place など)は候補に出ず、自然に守られる。
|
|
5
|
+
// - 低レベルだが公開しているメソッドは JSDoc に「通常は○○を推奨」と明記(逃げ道は塞がない)。
|
|
6
|
+
// - v1 ドラフト。実装と差異を見つけたら本ファイルを正として揃える/直す。
|
|
7
|
+
// - 使い方:
|
|
8
|
+
// <script src="tssgrid.js"> 読み込み時 … グローバル TssGrid / HistoryManager に型が付く
|
|
9
|
+
// import { TssGrid } from 'tssgrid' … ESM entry 整備後(§npm化)に有効
|
|
10
|
+
// =============================================================================
|
|
11
|
+
|
|
12
|
+
export type MoveDir = 'down' | 'up' | 'right' | 'left' | 'none';
|
|
13
|
+
export type CellType = 'text' | 'number' | 'date' | 'time' | 'dropdown' | 'checkbox';
|
|
14
|
+
export type ChangeSource = 'edit' | 'paste' | 'fill' | 'delete' | 'undo' | 'redo';
|
|
15
|
+
|
|
16
|
+
/** 1つの値変更(onBeforeChange / onAfterChange の配列要素)。newValue を書き換えると整形確定。 */
|
|
17
|
+
export interface ChangeItem {
|
|
18
|
+
r: number;
|
|
19
|
+
c: number;
|
|
20
|
+
oldValue: any;
|
|
21
|
+
newValue: any;
|
|
22
|
+
source: ChangeSource;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 選択範囲。r0/c0=開始、r1/c1=終了(昇順とは限らない)。 */
|
|
26
|
+
export interface SelectionRange { r0: number; c0: number; r1: number; c1: number; }
|
|
27
|
+
export interface CellCoord { r: number; c: number; }
|
|
28
|
+
export interface SelectionInfo { range: SelectionRange; active: CellCoord; extent: CellCoord; }
|
|
29
|
+
|
|
30
|
+
/** validator が弾いたセル(onInvalid / getInvalidCells)。 */
|
|
31
|
+
export interface Rejection { r: number; c: number; value: any; code?: string; level?: string; message?: string; }
|
|
32
|
+
/** 読み取り専用セルへの変更を弾いた時(onReadOnly)。 */
|
|
33
|
+
export interface ReadOnlyBlock { r: number; c: number; value: any; }
|
|
34
|
+
|
|
35
|
+
/** 行/列の挿入・削除の情報(onStructureChange / onBeforeStructureChange)。 */
|
|
36
|
+
export interface StructureInfo { type: 'insertRow' | 'deleteRows' | 'insertCol' | 'deleteCols'; undo?: boolean; [k: string]: any; }
|
|
37
|
+
|
|
38
|
+
/** validator の戻り値: true=OK / 文字列=エラー文 / オブジェクト=メッセージカタログ解決用。 */
|
|
39
|
+
export type ValidatorResult = true | string | { code: string; params?: Record<string, any>; level?: string };
|
|
40
|
+
|
|
41
|
+
/** 列定義(options.columns[c])。`data` は data オブジェクトのキー(または列 index)。 */
|
|
42
|
+
export interface ColumnDef {
|
|
43
|
+
/** data オブジェクトのキー。配列データなら省略可(位置で対応)。 */
|
|
44
|
+
data?: string | number;
|
|
45
|
+
/** ヘッダ見出し(headers より優先)。 */
|
|
46
|
+
title?: string;
|
|
47
|
+
/** セルタイプ。既定 'text'。 */
|
|
48
|
+
type?: CellType;
|
|
49
|
+
/** dropdown の選択肢。 */
|
|
50
|
+
options?: Array<string | { value: string; label?: string }>;
|
|
51
|
+
/** 列ごとの初期幅(px)。列定義に同居=並べ替え/コピペで幅が追従。 */
|
|
52
|
+
width?: number;
|
|
53
|
+
/** false でこの列だけリサイズ禁止。 */
|
|
54
|
+
resizable?: boolean;
|
|
55
|
+
/** 列既定の水平/垂直整列。 */
|
|
56
|
+
align?: 'left' | 'center' | 'right';
|
|
57
|
+
valign?: 'top' | 'middle' | 'bottom';
|
|
58
|
+
/** 列単位の読み取り専用。関数でセル単位判定も可。 */
|
|
59
|
+
readOnly?: boolean | ((value: any, cell: CellCoord) => boolean);
|
|
60
|
+
/** 入力検証。編集/貼付/フィルの全経路に効く。 */
|
|
61
|
+
validator?: (value: any, cell: CellCoord) => ValidatorResult;
|
|
62
|
+
/** カスタム編集UI(薄いオブジェクト契約 or それを返す関数)。 */
|
|
63
|
+
editor?: object | (() => object);
|
|
64
|
+
/** 入力の正規化(保存値へ変換)。 */
|
|
65
|
+
parse?: (input: string, cell: CellCoord) => any;
|
|
66
|
+
/** 表示の整形(保存値→表示)。文字列 or 宣言的 CellFormat。 */
|
|
67
|
+
format?: string | object | ((value: any, cell: CellCoord) => string);
|
|
68
|
+
/** 数値型: 千区切り表示。 */
|
|
69
|
+
thousands?: boolean;
|
|
70
|
+
/** 数値型: 接頭辞(例 '¥')。 */
|
|
71
|
+
prefix?: string;
|
|
72
|
+
/** 値/行を見てセルに付与する CSS クラス(条件付き書式)。 */
|
|
73
|
+
cellClass?: (r: number, c: number, value: any, row: any) => string;
|
|
74
|
+
/** セル単位のインラインスタイル(連続色・データバー等)。 */
|
|
75
|
+
cellStyle?: (r: number, c: number, value: any, row: any) => Partial<CSSStyleDeclaration> | Record<string, string>;
|
|
76
|
+
/** true で表示だけ生HTML(値はテキスト保存のまま)。 */
|
|
77
|
+
html?: boolean;
|
|
78
|
+
/** 列単位の折り返し。 */
|
|
79
|
+
wordWrap?: boolean;
|
|
80
|
+
/** 空セルの薄いヒント文字。 */
|
|
81
|
+
placeholder?: string;
|
|
82
|
+
[k: string]: any;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** new TssGrid(container, options) のオプション。 */
|
|
86
|
+
export interface TssGridOptions {
|
|
87
|
+
headers?: string[];
|
|
88
|
+
/** 初期データ。2次元配列 or オブジェクト配列。 */
|
|
89
|
+
data?: any[][] | Record<string, any>[];
|
|
90
|
+
/** 渡すと複数グリッドで Undo を共有。 */
|
|
91
|
+
history?: HistoryManager;
|
|
92
|
+
name?: string;
|
|
93
|
+
showDump?: boolean;
|
|
94
|
+
/** Enter 確定後の移動方向(既定 'down')。Shift で逆。実行時に書換可。 */
|
|
95
|
+
enterMoves?: MoveDir;
|
|
96
|
+
/** Tab 確定後の移動方向(既定 'right')。Shift で逆。実行時に書換可。 */
|
|
97
|
+
tabMoves?: MoveDir;
|
|
98
|
+
initialCell?: [number, number] | { r: number; c: number | string };
|
|
99
|
+
/** 入力フロー制御。Enter/Tab の移動先を上書き(null で既定方向)。入力セルだけ巡回できる。 */
|
|
100
|
+
nextCell?: (a: { r: number; c: number; key: string; shift: boolean }) => { r: number; c: number } | null;
|
|
101
|
+
columns?: ColumnDef[];
|
|
102
|
+
/** 検証NG時。'revert'=元に戻す / 'keep'=赤く警告して残す。 */
|
|
103
|
+
invalidMode?: 'revert' | 'keep';
|
|
104
|
+
invalidTitle?: boolean;
|
|
105
|
+
/** エラーの共通メッセージカタログ。 */
|
|
106
|
+
messages?: Record<string, any> | ((info: { code: string; params?: any; level?: string }) => string);
|
|
107
|
+
pasteOverflow?: 'clip' | 'error';
|
|
108
|
+
colWidths?: number[] | Record<string, number>;
|
|
109
|
+
rowHeights?: number[] | Record<string, number>;
|
|
110
|
+
defaultColWidth?: number;
|
|
111
|
+
defaultRowHeight?: number;
|
|
112
|
+
minColWidth?: number;
|
|
113
|
+
minRowHeight?: number;
|
|
114
|
+
resizeMode?: 'preview' | 'live';
|
|
115
|
+
/** 左から固定する列数。freezeCols(n) でも変更可。 */
|
|
116
|
+
frozenCols?: number;
|
|
117
|
+
/** 上から固定する行数。freezeRows(n) でも変更可。frozenCols 併用で四隅固定。 */
|
|
118
|
+
frozenRows?: number;
|
|
119
|
+
/** ヘッダ結合(複数段見出し)。最下段がリーフ=実列。 */
|
|
120
|
+
nestedHeaders?: Array<Array<string | { label: string; colspan?: number }>>;
|
|
121
|
+
width?: number | string;
|
|
122
|
+
height?: number | string;
|
|
123
|
+
resizeCols?: boolean;
|
|
124
|
+
resizeRows?: boolean;
|
|
125
|
+
/** false で選択カーソルを非表示(表示専用グリッド向け・内部選択は保持)。 */
|
|
126
|
+
cursor?: boolean;
|
|
127
|
+
stretchH?: 'none' | 'last' | 'all';
|
|
128
|
+
rowHeaders?: boolean;
|
|
129
|
+
colHeaders?: boolean;
|
|
130
|
+
hiddenColumns?: number[];
|
|
131
|
+
wordWrap?: boolean;
|
|
132
|
+
placeholder?: string;
|
|
133
|
+
className?: string;
|
|
134
|
+
/** false でフィル完全OFF。詳細指定も可。 */
|
|
135
|
+
fillHandle?: boolean | { direction?: 'vertical' | 'horizontal' | 'both'; autoInsertRow?: boolean };
|
|
136
|
+
autoColumnSize?: boolean | number;
|
|
137
|
+
copyPaste?: boolean;
|
|
138
|
+
minSpareRows?: number;
|
|
139
|
+
autoWrapRow?: boolean;
|
|
140
|
+
autoWrapCol?: boolean;
|
|
141
|
+
minRows?: number;
|
|
142
|
+
maxRows?: number;
|
|
143
|
+
minCols?: number;
|
|
144
|
+
maxCols?: number;
|
|
145
|
+
/** グリッド全体を読み取り専用(ビューア用途)。 */
|
|
146
|
+
readOnly?: boolean;
|
|
147
|
+
contextMenu?: boolean | any[] | { items: any[] };
|
|
148
|
+
allowInsertRows?: boolean;
|
|
149
|
+
allowDeleteRows?: boolean;
|
|
150
|
+
allowInsertCols?: boolean;
|
|
151
|
+
allowDeleteCols?: boolean;
|
|
152
|
+
shortcuts?: any[];
|
|
153
|
+
/** プラグイン(関数 or 登録名)。init(grid) で初期化。 */
|
|
154
|
+
plugins?: any[];
|
|
155
|
+
|
|
156
|
+
// ---- イベント / コールバック -------------------------------------------
|
|
157
|
+
/** 確定の直前。検証・自動整形。false で全取消、change.newValue 書換で整形。 */
|
|
158
|
+
onBeforeChange?: (changes: ChangeItem[], source: ChangeSource) => boolean | void;
|
|
159
|
+
/** 確定の直後(undo/redo 含む)。保存・再計算に。 */
|
|
160
|
+
onAfterChange?: (changes: ChangeItem[], source: ChangeSource) => void;
|
|
161
|
+
onEditStart?: (info: { r: number; c: number; value: any }) => void;
|
|
162
|
+
onEditEnd?: (info: { r: number; c: number; oldValue: any; newValue: any; canceled: boolean }) => void;
|
|
163
|
+
onSelectionChange?: (info: SelectionInfo) => void;
|
|
164
|
+
onBeforeSelectionChange?: (info: SelectionInfo) => boolean | void;
|
|
165
|
+
onBeforePaste?: (data: string[][]) => boolean | string[][] | void;
|
|
166
|
+
onAfterPaste?: (data: string[][], info: { range: SelectionRange }) => void;
|
|
167
|
+
onBeforeCopy?: (data: string[][], info: { range: SelectionRange }) => boolean | string[][] | void;
|
|
168
|
+
onBeforeCut?: (data: string[][], info: { range: SelectionRange }) => boolean | string[][] | void;
|
|
169
|
+
onAfterCopy?: (data: string[][], info: { range: SelectionRange; cut: boolean }) => void;
|
|
170
|
+
onBeforeStructureChange?: (info: StructureInfo) => boolean | void;
|
|
171
|
+
onStructureChange?: (info: StructureInfo) => void;
|
|
172
|
+
onBeforeAutofill?: (src: SelectionRange, target: SelectionRange) => boolean | void;
|
|
173
|
+
onAfterAutofill?: (src: SelectionRange, target: SelectionRange) => void;
|
|
174
|
+
onBeforeKeyDown?: (e: KeyboardEvent) => boolean | void;
|
|
175
|
+
onHeaderClick?: (c: number, e: MouseEvent) => boolean | void;
|
|
176
|
+
onInvalid?: (rejections: Rejection[], source: ChangeSource) => void;
|
|
177
|
+
onReadOnly?: (blocked: ReadOnlyBlock[], source: ChangeSource) => void;
|
|
178
|
+
onChange?: (grid: TssGrid) => void;
|
|
179
|
+
onPasteOverflow?: (info: { anchor: CellCoord; height: number; width: number; rows: number; cols: number; overRows: number; overCols: number }) => void;
|
|
180
|
+
|
|
181
|
+
[k: string]: any;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface CsvOptions { formatted?: boolean; delimiter?: string; [k: string]: any; }
|
|
185
|
+
|
|
186
|
+
/** Undo/Redo 履歴。複数グリッドで共有すると Undo タイムラインが繋がる。 */
|
|
187
|
+
export declare class HistoryManager {
|
|
188
|
+
constructor(max?: number);
|
|
189
|
+
onChange: ((h: HistoryManager) => void) | null;
|
|
190
|
+
push(cmd: { apply: () => void; revert: () => void }): void;
|
|
191
|
+
undo(): void;
|
|
192
|
+
redo(): void;
|
|
193
|
+
clear(): void;
|
|
194
|
+
canUndo(): boolean;
|
|
195
|
+
canRedo(): boolean;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 軽量・ノービルド・日本語IME堅牢な編集グリッド。
|
|
200
|
+
* @example
|
|
201
|
+
* const grid = new TssGrid(document.getElementById('app'), {
|
|
202
|
+
* columns: [{ data: 'name', title: '品名' }, { data: 'qty', title: '数量', type: 'number' }],
|
|
203
|
+
* data: [{ name: 'りんご', qty: 12 }],
|
|
204
|
+
* });
|
|
205
|
+
*/
|
|
206
|
+
export declare class TssGrid {
|
|
207
|
+
constructor(container: HTMLElement, options?: TssGridOptions);
|
|
208
|
+
|
|
209
|
+
/** 行数 / 列数(読み取り)。 */
|
|
210
|
+
readonly ROWS: number;
|
|
211
|
+
readonly COLS: number;
|
|
212
|
+
/** 列定義(実行時参照)。 */
|
|
213
|
+
columns: ColumnDef[];
|
|
214
|
+
/** 履歴マネージャ。 */
|
|
215
|
+
history: HistoryManager;
|
|
216
|
+
/** Enter/Tab 確定後の移動方向(代入で即反映)。 */
|
|
217
|
+
enterMoves: MoveDir;
|
|
218
|
+
tabMoves: MoveDir;
|
|
219
|
+
|
|
220
|
+
// ---- データ -------------------------------------------------------------
|
|
221
|
+
/** 全データを2次元配列で取得(コピー)。 */
|
|
222
|
+
getData(): any[][];
|
|
223
|
+
/** 全行をオブジェクト配列で取得(columns[c].data キー基準)。 */
|
|
224
|
+
getRows(): Record<string, any>[];
|
|
225
|
+
/** r 行目をオブジェクトで取得。 */
|
|
226
|
+
getRow(r: number): Record<string, any>;
|
|
227
|
+
/** 列を縦に取得(index か data キー)。 */
|
|
228
|
+
getColumn(cOrKey: number | string): any[] | undefined;
|
|
229
|
+
/** 全列を配列の配列で取得。 */
|
|
230
|
+
getColumns(): any[][];
|
|
231
|
+
/** 1セルの値を取得(c は index か data キー)。 */
|
|
232
|
+
getValue(r: number, cOrKey: number | string): any;
|
|
233
|
+
/** データを差し替え(履歴・フィルタ・整列状態はリセット)。 */
|
|
234
|
+
setData(rows: any[][] | Record<string, any>[]): void;
|
|
235
|
+
/** 1セルに書き込み(履歴あり・検証/onBeforeChange 経由)。force=true で readOnly 貫通。 */
|
|
236
|
+
setValue(r: number, cOrKey: number | string, val: any, force?: boolean): void;
|
|
237
|
+
/** 履歴に積まず・検証も通さず直接書き込み(計算列など派生値用。readOnly 貫通)。 */
|
|
238
|
+
setValueRaw(r: number, cOrKey: number | string, val: any): void;
|
|
239
|
+
|
|
240
|
+
// ---- 並べ替え / フィルタ -------------------------------------------------
|
|
241
|
+
/** 列で並べ替え(c は index か data キー)。 */
|
|
242
|
+
sortBy(cOrKey: number | string, dir?: 'asc' | 'desc'): void;
|
|
243
|
+
/** 比較関数で並べ替え。 */
|
|
244
|
+
sortRows(cmp: (a: any[], b: any[]) => number): void;
|
|
245
|
+
/** 並べ替え解除(入力順に戻す)。 */
|
|
246
|
+
clearSort(): void;
|
|
247
|
+
isSorted(): boolean;
|
|
248
|
+
/** 非破壊フィルタ。pred(row, masterIndex, src)=>boolean、または { col: 値|fn }。 */
|
|
249
|
+
filter(pred: ((row: any[], masterIndex: number, src: any) => boolean) | Record<string, any>): void;
|
|
250
|
+
/** フィルタ解除(フィルタ中に編集/追加した行も含め全部戻る)。 */
|
|
251
|
+
clearFilter(): void;
|
|
252
|
+
isFiltered(): boolean;
|
|
253
|
+
/** フィルタ中でも全行(マスタ)を取得。 */
|
|
254
|
+
getAllRows(): Record<string, any>[];
|
|
255
|
+
|
|
256
|
+
// ---- 選択 / カーソル ----------------------------------------------------
|
|
257
|
+
setActive(r: number, c: number): void;
|
|
258
|
+
setExtent(r: number, c: number): void;
|
|
259
|
+
selectRect(r0: number, c0: number, r1: number, c1: number): void;
|
|
260
|
+
selectRow(r: number): void;
|
|
261
|
+
selectCol(c: number): void;
|
|
262
|
+
selectAll(): void;
|
|
263
|
+
|
|
264
|
+
// ---- 行 / 列 構造 -------------------------------------------------------
|
|
265
|
+
insertRow(ri: number, where?: 'above' | 'below'): void;
|
|
266
|
+
deleteRows(r0: number, r1?: number): void;
|
|
267
|
+
insertCol(ci: number, where?: 'left' | 'right'): void;
|
|
268
|
+
deleteCols(c0: number, c1?: number): void;
|
|
269
|
+
/** 1行を移動(Undo 対応)。 */
|
|
270
|
+
moveRow(from: number, to: number): void;
|
|
271
|
+
/** 複数行をまとめて移動(Undo 対応)。 */
|
|
272
|
+
moveRows(r0: number, r1: number, to: number): void;
|
|
273
|
+
|
|
274
|
+
// ---- 列の表示 / 幅 / 固定 / 行高 ---------------------------------------
|
|
275
|
+
hideColumn(c: number): void;
|
|
276
|
+
showColumn(c: number): void;
|
|
277
|
+
toggleColumn(c: number): void;
|
|
278
|
+
isColumnHidden(c: number): boolean;
|
|
279
|
+
getHiddenColumns(): number[];
|
|
280
|
+
setColWidth(c: number, px: number): void;
|
|
281
|
+
autoSizeColumn(c: number): void;
|
|
282
|
+
autoSizeAllColumns(): void;
|
|
283
|
+
freezeCols(n: number): void;
|
|
284
|
+
freezeRows(n: number): void;
|
|
285
|
+
setRowHeight(r: number, px: number): void;
|
|
286
|
+
|
|
287
|
+
// ---- セル状態 / 検証 ----------------------------------------------------
|
|
288
|
+
setAlignment(align: string, range?: SelectionRange): void;
|
|
289
|
+
getAlignment(r: number, c: number): string;
|
|
290
|
+
setCellReadOnly(flag: boolean, range?: SelectionRange): void;
|
|
291
|
+
/** 今の全セルを検証し、NG を一覧で返す(保存前チェック等)。 */
|
|
292
|
+
getInvalidCells(): Rejection[];
|
|
293
|
+
|
|
294
|
+
// ---- 出力 / 再描画 / 破棄 ----------------------------------------------
|
|
295
|
+
toCSV(opts?: CsvOptions): string;
|
|
296
|
+
downloadCSV(filename: string, opts?: CsvOptions): void;
|
|
297
|
+
/** 列定義変更後などの再描画(データ・選択は保持)。 */
|
|
298
|
+
redraw(): void;
|
|
299
|
+
/** 画像/フォント等の遅延レイアウト後に幾何キャッシュを再構築する保険。 */
|
|
300
|
+
refreshGeometry(): void;
|
|
301
|
+
/** グリッドを破棄(リスナ除去・DOM 撤去・idempotent)。フレームワークの teardown で呼ぶ。 */
|
|
302
|
+
destroy(): void;
|
|
303
|
+
|
|
304
|
+
// ---- 拡張 ---------------------------------------------------------------
|
|
305
|
+
addShortcut(s: { name: string; keys: string; handler: (grid: TssGrid, e: KeyboardEvent) => void }): void;
|
|
306
|
+
removeShortcut(name: string): void;
|
|
307
|
+
usePlugin(plugin: ((grid: TssGrid, opts?: any) => any) | string, opts?: any): any;
|
|
308
|
+
getPlugin(name: string): any;
|
|
309
|
+
|
|
310
|
+
// ---- 低レベル(公開はするが通常は上位APIを推奨) -----------------------
|
|
311
|
+
/** 選択範囲(または range)をクリア。※通常は値消去なら setValue('') を推奨。一括の低レベル手段。 */
|
|
312
|
+
clearRange(): void;
|
|
313
|
+
/** 選択範囲をクリップボードへ。cut=true で切り取り。 */
|
|
314
|
+
copy(cut?: boolean): void;
|
|
315
|
+
/** テーブルDOMを再構築(低レベル)。※通常は redraw() を推奨。主にプラグイン拡張者向け。 */
|
|
316
|
+
buildTable(): void;
|
|
317
|
+
/** セルの DOM 要素を返す(低レベル・内部DOM構造に依存)。結合セルはアンカーに解決。 */
|
|
318
|
+
cellEl(r: number, c: number): HTMLTableCellElement | null;
|
|
319
|
+
|
|
320
|
+
[k: string]: any;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// グローバル(<script> 読み込み時の window.TssGrid / window.HistoryManager)にも型を付ける。
|
|
324
|
+
declare global {
|
|
325
|
+
interface Window {
|
|
326
|
+
TssGrid: typeof TssGrid;
|
|
327
|
+
HistoryManager: typeof HistoryManager;
|
|
328
|
+
}
|
|
329
|
+
}
|