@threestarsoftware/tssgrid 0.0.1 → 0.0.3
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/README.md +24 -0
- package/package.json +1 -1
- package/plugins/tss-sum.js +111 -0
- package/src/tssgrid.js +2 -2
package/README.md
CHANGED
|
@@ -427,6 +427,30 @@ new TssGrid(el, {
|
|
|
427
427
|
- **コアに合計行を埋めない**理由=置き場が `minSpareRows`/ソート/空行処理と衝突して肥大化するため。集計を外出しして「どこに出すかは自由」にした。
|
|
428
428
|
- formula と併用時は **`plugins: [TssFormula, TssTotals(...)]` の順**(合計を派生列の結果の後に計算するため)。`sum`/`avg`/`min`/`max` は数値セルのみ・`count` は非空セル数。
|
|
429
429
|
|
|
430
|
+
### 同梱プラグイン: 条件付き合計(tss-sum / ラベル付き合計行・整列表示)
|
|
431
|
+
|
|
432
|
+
`plugins/tss-sum.js`= **ラベル付きの"合計行"**(小計・消費税10%・消費税8%・合計 など)を、**条件(`when`)+対象(`of`)を宣言**で定義して作る。`tss-totals` が「列ごと1値」なのに対し、こちらは**同じ列を条件別に何本でも集計**でき(消費税10%と8%は両方 amount 由来=per-column では衝突する)、**ソースと同一列構成の readonly 集計グリッドを生成**するので**金額が縦にピッタリ揃う**(画面でも印刷でも)。請求書・見積などの合計欄に。動く例: [`examples/invoice.html`](https://tssgrid.threestarsoftware.co.jp/examples/invoice.html)(インボイス請求書の合計欄)。
|
|
433
|
+
|
|
434
|
+
```html
|
|
435
|
+
<script src="plugins/tss-merge.js"></script> <!-- ラベルの横結合に利用 -->
|
|
436
|
+
<script src="plugins/tss-sum.js"></script>
|
|
437
|
+
```
|
|
438
|
+
```js
|
|
439
|
+
const sum = TssSum.attach(grid, document.getElementById('sumgrid'), {
|
|
440
|
+
format: v => '¥' + Number(v).toLocaleString('ja-JP'),
|
|
441
|
+
rows: [
|
|
442
|
+
{ label: '小計(税抜)', of: r => (+r[2]||0)*(+r[3]||0) }, // 全データ行の合計
|
|
443
|
+
{ label: '消費税(10%)', when: r => r[4]==='10%', of: r => Math.round((+r[2]||0)*(+r[3]||0)*0.10) }, // 条件付き
|
|
444
|
+
{ label: '消費税(8%)', when: r => r[4]==='8%', of: r => Math.round((+r[2]||0)*(+r[3]||0)*0.08) },
|
|
445
|
+
{ label: '合計(税込)', total: v => v[0]+v[1]+v[2] }, // 既出行から派生
|
|
446
|
+
],
|
|
447
|
+
onCompute: vals => { /* 上部の総額表示など */ },
|
|
448
|
+
});
|
|
449
|
+
// sum.refresh() 手動再計算 / sum.detach() フック解除+集計グリッド破棄
|
|
450
|
+
```
|
|
451
|
+
- 行は **`{label, when?, of}`**(`when` 一致行で `of(row)` を合計=SUMIF)または **`{label, total}`**(既出行の値から派生)。`row` は内部配列(列index)で受ける。
|
|
452
|
+
- ソースの **`onAfterChange`/`onStructureChange` を自動で包んで再計算**=編集・行追加削除・Undo すべてに追従。集計グリッドは**表示専用**(同一列幅で `金額` 列が揃う)。
|
|
453
|
+
|
|
430
454
|
### 同梱プラグイン: 残高/累計(running total・縦の連鎖)
|
|
431
455
|
|
|
432
456
|
`plugins/tss-running.js`=「**前の行の結果 + この行の増減**」で縦に積む列。`formula`(行内)でも `totals`(全体合計)でも出せない**縦方向の連鎖**を担う。出納帳/通帳の**残高**、在庫受払の**在庫数**、経費・ポイントの**累計**など。動く例: [`examples/running.html`](https://tssgrid.threestarsoftware.co.jp/examples/running.html)(現金出納帳=入金・出金→残高)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threestarsoftware/tssgrid",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
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,111 @@
|
|
|
1
|
+
/*! TssSum — 条件付き合計グリッドの軽量プラグイン。
|
|
2
|
+
*
|
|
3
|
+
* tss-totals との住み分け: tss-totals は「列ごとの集計(sum/avg/min/max/count)」を onTotals で返す。
|
|
4
|
+
* tss-sum は「同じ列を条件別に複数集計=ラベル付きの"合計行"(小計/消費税10%/消費税8%/合計 など)」+
|
|
5
|
+
* ソースと整列した表示グリッドまで担う(per-column キーでは消費税10%と8%が衝突するため別抽象が要る)。
|
|
6
|
+
*
|
|
7
|
+
* ソースグリッドと「同一の列構成」の readonly 集計グリッドを作るので、金額列の幾何を共有し
|
|
8
|
+
* 画面でも印刷でも値が縦にピッタリ揃う。各合計行は宣言で(条件+対象)または派生で定義する。
|
|
9
|
+
* ソースの onAfterChange / onStructureChange を包んで自動で再計算する。
|
|
10
|
+
*
|
|
11
|
+
* 使い方:
|
|
12
|
+
* const sum = TssSum.attach(grid, document.getElementById('sumgrid'), {
|
|
13
|
+
* rows: [
|
|
14
|
+
* { label: '小計(税抜)', of: r => (+r[2]||0)*(+r[3]||0) }, // 全データ行の合計
|
|
15
|
+
* { label: '消費税(10%)', when: r => r[4]==='10%', of: r => Math.round((+r[2]||0)*(+r[3]||0)*0.10) }, // 条件付き
|
|
16
|
+
* { label: '消費税(8%)', when: r => r[4]==='8%', of: r => Math.round((+r[2]||0)*(+r[3]||0)*0.08) },
|
|
17
|
+
* { label: '合計(税込)', total: v => v[0]+v[1]+v[2] }, // 既出行から派生
|
|
18
|
+
* ],
|
|
19
|
+
* format: v => '¥' + Number(v).toLocaleString('ja-JP'),
|
|
20
|
+
* onCompute: vals => { ... }, // 計算後フック(上部の総額表示など)
|
|
21
|
+
* });
|
|
22
|
+
* sum.refresh(); // 手動再計算 / sum.detach(); // フック解除+集計グリッド破棄
|
|
23
|
+
*
|
|
24
|
+
* 行の定義:
|
|
25
|
+
* { label, when?(row)->bool, of(row)->number } … when に一致するデータ行で of(row) を合計(when 省略=全行)
|
|
26
|
+
* { label, total(priorValues[])->number } … それまでの行の値配列から派生(小計+税 など)
|
|
27
|
+
* opts: rows(必須) / valueCol(既定=最終列) / sumCol(of 省略時に合計する列) / format / rowHeight(既定28)
|
|
28
|
+
* / onCompute(vals) / name / TssGrid(モジュール利用時にコンストラクタを注入)
|
|
29
|
+
* 返り値: { grid, refresh, detach }
|
|
30
|
+
* 注意: ラベルの横結合に window.TssMerge を使う(無ければラベルは先頭列のみ)。集計グリッドは表示専用。
|
|
31
|
+
*/
|
|
32
|
+
(function (root) {
|
|
33
|
+
'use strict';
|
|
34
|
+
|
|
35
|
+
function TssSum() { /* 名前空間。attach を使う */ }
|
|
36
|
+
|
|
37
|
+
TssSum.attach = function (source, mountEl, opts) {
|
|
38
|
+
opts = opts || {};
|
|
39
|
+
var Grid = opts.TssGrid || root.TssGrid;
|
|
40
|
+
if (!Grid) throw new Error('TssSum: TssGrid が見つかりません(opts.TssGrid で渡してください)');
|
|
41
|
+
var rows = opts.rows || [];
|
|
42
|
+
var COLS = source.COLS;
|
|
43
|
+
var valueCol = (opts.valueCol != null) ? opts.valueCol : COLS - 1;
|
|
44
|
+
var fmt = opts.format || function (v) { return v; };
|
|
45
|
+
|
|
46
|
+
// ソースと同じ列幅・行ヘッダ・全体幅でミラー(=金額列が揃う)。全セル readonly。
|
|
47
|
+
var columns = [];
|
|
48
|
+
for (var c = 0; c < COLS; c++) {
|
|
49
|
+
var col = { width: source._colWidth(c), readOnly: true };
|
|
50
|
+
if (c === valueCol) { col.align = 'right'; col.format = fmt; }
|
|
51
|
+
columns.push(col);
|
|
52
|
+
}
|
|
53
|
+
var data = rows.map(function (spec) {
|
|
54
|
+
var r = new Array(COLS).fill('');
|
|
55
|
+
r[0] = (spec.label != null) ? spec.label : ''; // ラベルは先頭列(アンカー)
|
|
56
|
+
return r;
|
|
57
|
+
});
|
|
58
|
+
var plugins = [];
|
|
59
|
+
if (root.TssMerge && valueCol > 1) { // ラベルを valueCol の手前まで横結合
|
|
60
|
+
plugins.push(root.TssMerge.plugin({
|
|
61
|
+
merges: rows.map(function (_, r) { return { r: r, c: 0, colspan: valueCol }; }),
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
var tg = new Grid(mountEl, {
|
|
65
|
+
name: opts.name || (source.name ? source.name + '-sum' : 'sum'),
|
|
66
|
+
width: source.width, rowHeight: opts.rowHeight || 28,
|
|
67
|
+
rowHeaders: source.rowHeaders, colHeaders: false,
|
|
68
|
+
columns: columns, data: data, plugins: plugins,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
function num(v) { return Number(v) || 0; }
|
|
72
|
+
function refresh() {
|
|
73
|
+
var src = source.data, vals = [];
|
|
74
|
+
for (var i = 0; i < rows.length; i++) {
|
|
75
|
+
var spec = rows[i], v;
|
|
76
|
+
if (typeof spec.total === 'function') {
|
|
77
|
+
v = spec.total(vals.slice());
|
|
78
|
+
} else {
|
|
79
|
+
v = 0;
|
|
80
|
+
for (var k = 0; k < src.length; k++) {
|
|
81
|
+
var row = src[k];
|
|
82
|
+
if (spec.when && !spec.when(row)) continue;
|
|
83
|
+
v += spec.of ? num(spec.of(row)) : num(row[(opts.sumCol != null) ? opts.sumCol : valueCol]);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
vals[i] = v;
|
|
87
|
+
tg.data[i][valueCol] = String(v);
|
|
88
|
+
}
|
|
89
|
+
tg.buildTable();
|
|
90
|
+
if (opts.onCompute) { try { opts.onCompute(vals.slice()); } catch (e) {} }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ソースの変更フックを包んで自動再計算(既存ハンドラは温存)
|
|
94
|
+
var origAC = source.onAfterChange, origSC = source.onStructureChange;
|
|
95
|
+
source.onAfterChange = function () { if (origAC) origAC.apply(this, arguments); refresh(); };
|
|
96
|
+
source.onStructureChange = function () { if (origSC) origSC.apply(this, arguments); refresh(); };
|
|
97
|
+
refresh();
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
grid: tg,
|
|
101
|
+
refresh: refresh,
|
|
102
|
+
detach: function () {
|
|
103
|
+
source.onAfterChange = origAC; source.onStructureChange = origSC;
|
|
104
|
+
if (tg.destroy) tg.destroy();
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
if (typeof module === 'object' && module.exports) module.exports = TssSum;
|
|
110
|
+
root.TssSum = TssSum;
|
|
111
|
+
})(typeof window !== 'undefined' ? window : this);
|
package/src/tssgrid.js
CHANGED
|
@@ -1844,8 +1844,8 @@
|
|
|
1844
1844
|
if (td) {
|
|
1845
1845
|
const r = +td.dataset.r, c = +td.dataset.c, s = this.rectRange();
|
|
1846
1846
|
if (!(r >= s.r0 && r <= s.r1 && c >= s.c0 && c <= s.c1)) this.setActive(r, c);
|
|
1847
|
-
} else if (th.dataset.c != null)
|
|
1848
|
-
else if (th.dataset.r != null)
|
|
1847
|
+
} else if (th.dataset.c != null) { const c = +th.dataset.c, s = this.rectRange(); if (!(c >= s.c0 && c <= s.c1)) this.selectCol(c); } // 既存選択の中なら維持(複数列右クリックで解除しない)
|
|
1848
|
+
else if (th.dataset.r != null) { const r = +th.dataset.r, s = this.rectRange(); if (!(r >= s.r0 && r <= s.r1)) this.selectRow(r); } // 同上(複数行)
|
|
1849
1849
|
this._showMenu(e.clientX, e.clientY);
|
|
1850
1850
|
}
|
|
1851
1851
|
// 既定のメニュー項目キー(allow フラグ・copyPaste で出し分け)。
|