@threestarsoftware/tssgrid 0.1.14 → 0.1.16
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 +14 -0
- package/README.md +18 -1
- package/package.json +1 -1
- package/src/tssgrid.d.ts +8 -0
- package/src/tssgrid.js +61 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
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.16] - 2026-10-04
|
|
7
|
+
|
|
8
|
+
### 修正
|
|
9
|
+
- **ドロップダウン(`type:'dropdown'`)で「未選択」のまま矢印キーを押すと先頭オプションが選ばれてしまう不具合を修正**(コア)— ネイティブ `<select>` はフォーカス中(リストが閉じていても)`↑↓←→` でオプションが変わる仕様で、特に「未選択(空)を選ぶと値が変わらず `change` が出ない=エディタへフォーカスが戻らない」ため `<select>` にフォーカスが残り、次の矢印が `<select>` に吸われて **空→先頭** に変化+即確定していた。`<select>` の keydown で `←→↑↓` を横取りし、**現在値を確定して隣セルへ移動(Excel 流の nav)**に。リスト展開中は OS ポップアップがキーを奪うのでこのハンドラは発火しない=閉じてフォーカスだけ残った時の誤爆のみを防ぐ。`Enter`/`Tab`/`Esc` は不変。
|
|
10
|
+
|
|
11
|
+
## [0.1.15] - 2026-09-30
|
|
12
|
+
|
|
13
|
+
### 追加
|
|
14
|
+
- **Excel 入力ショートカットを組込**(コア・既定 ON・opt-out 可・すべて未バインドキー=追加的)— 業務入力でよく使うキーを最初から用意。**`Ctrl+;`=今日の日付** / **`Ctrl+:`=現在時刻** / **`Ctrl+D`=上のセルをコピー(fill down)** / **`Ctrl+R`=左のセルをコピー(fill right)**。`Ctrl+;`/`Ctrl+:` は**打鍵結果の文字で判定**するので **JIS の `:` 専用キーも US の `Ctrl+Shift+;` も両対応**。fill は範囲選択で一括(単一セルは上/左をコピー)、`setValue`/検証を通り **Undo 1回**・`readOnly` スキップ・**列の型に合わない値は貼付同様に弾く**。無効化は `dateShortcuts:false` / `fillShortcuts:false`(`addShortcut` で同キー登録すればそちらが優先)。
|
|
15
|
+
- **`todayFormat` / `nowFormat`**(コア)— `Ctrl+;` / `Ctrl+:` が**テキスト等(date/time 型でない)列**に入れる日付/時刻の形式を選べる(既定 `'YYYY-MM-DD'` / `'HH:MM'`)。文字列トークン(`YYYY`/`MM`/`DD`・`HH`/`mm`)か `(d: Date) => string`。例 `todayFormat:'YYYY/MM/DD'`。**`date`/`time` 型の列は従来どおり列の仕組み(ISO 保存+列 `format` 表示)に従う**(この設定に非依存)。
|
|
16
|
+
|
|
17
|
+
### 変更
|
|
18
|
+
- README のカスタムショートカット例を `Ctrl+D`→`Alt+D` に変更(組込 `Ctrl+D`=fill down と紛れないように・機能変更なし)。
|
|
19
|
+
|
|
6
20
|
## [0.1.14] - 2026-09-29
|
|
7
21
|
|
|
8
22
|
### 修正
|
package/README.md
CHANGED
|
@@ -392,7 +392,7 @@ shared.getMaster(); // マスタ全行(getRows 形式
|
|
|
392
392
|
```js
|
|
393
393
|
new TssGrid(el, {
|
|
394
394
|
shortcuts: [
|
|
395
|
-
{ keys: '
|
|
395
|
+
{ keys: 'Alt+D', name: 'dup', context: 'grid', handler: (e, { grid }) => grid.insertRow(grid.active.r, 'below') },
|
|
396
396
|
{ keys: ['Ctrl+S', 'Ctrl+Shift+S'], handler: () => save() }, // 複数キー可
|
|
397
397
|
],
|
|
398
398
|
});
|
|
@@ -402,6 +402,19 @@ grid.removeShortcut('dup');
|
|
|
402
402
|
- マッチすると既定動作を抑止します(`handler` が `true` を返すと既定も続行)。`cmd`/`meta` は `Ctrl` 扱い。
|
|
403
403
|
- 全キーをまとめて捌きたいなら **`onBeforeKeyDown(e)`**(`false` / `preventDefault()` でグリッド既定を止める)。
|
|
404
404
|
|
|
405
|
+
**組込ショートカット(Excel 互換・既定 ON)**: 業務入力でよく使うキーを最初から用意(すべて未バインドキー=追加的)。
|
|
406
|
+
|
|
407
|
+
| キー | 動作 | 備考 |
|
|
408
|
+
|---|---|---|
|
|
409
|
+
| `Ctrl+;` | **今日の日付**(`YYYY-MM-DD`)をセルへ | `date` 列にそのまま入る |
|
|
410
|
+
| `Ctrl+:` | **現在時刻**(`HH:MM`)をセルへ | **JIS の `:` 専用キーも US の `Ctrl+Shift+;` も両対応**(打鍵結果の文字で判定) |
|
|
411
|
+
| `Ctrl+D` | **上のセルをコピー**(fill down)| 範囲選択なら先頭行を下へ一括 |
|
|
412
|
+
| `Ctrl+R` | **左のセルをコピー**(fill right)| 範囲選択なら左端列を右へ一括 |
|
|
413
|
+
|
|
414
|
+
- いずれも `setValue`/検証を通り **Undo 1回**で戻せます(fill は**列の型に合わない値は貼り付けと同様に弾かれます**)。`readOnly` セルはスキップ。
|
|
415
|
+
- 無効化: `dateShortcuts: false`(`Ctrl+;`/`Ctrl+:`)・`fillShortcuts: false`(`Ctrl+D`/`Ctrl+R`)。独自に `addShortcut` で同じキーを登録すれば**そちらが優先**されます。
|
|
416
|
+
- **入れる形式**: `date`/`time` 型の列は**列の仕組みに従う**(ISO 保存=`YYYY-MM-DD`/`HH:MM`、見た目は列の `format` で `/` でも自由)。**それ以外(テキスト等)の列は** `todayFormat` / `nowFormat` で形式を選べます(既定 `'YYYY-MM-DD'` / `'HH:MM'`。トークン `YYYY`/`MM`/`DD`・`HH`/`mm`、または `(d: Date) => string`)。例: `todayFormat: 'YYYY/MM/DD'`(スラッシュ区切り)。
|
|
417
|
+
|
|
405
418
|
**プラグイン**(軽量 base-plugin 相当。`init(grid)→{destroy?}` の薄い契約):
|
|
406
419
|
|
|
407
420
|
```js
|
|
@@ -935,6 +948,10 @@ new TssGrid(el, { hiddenColumns: [1] }); // 初期で隠す
|
|
|
935
948
|
| `onHeaderRender` | `(th, c)=>void` | — | 各列ヘッダ `th` の生成後に呼ぶ(**再描画毎に自動で再実行**)。ソートアイコン等の要素差し込み用 → 動く例: [`examples/multisort.html`](https://tssgrid.threestarsoftware.co.jp/examples/multisort.html) |
|
|
936
949
|
| `disjointSelect` | `boolean` | `true` | Ctrl+ヘッダで**行/列を飛び飛びに複数選択**(`false` で Ctrl+クリックは単一選択に戻る)。`getSelectedColumns()`/`getSelectedRows()`・飛び飛びコピー/カットと連動 |
|
|
937
950
|
| `shortcuts` | `Array` | `[]` | カスタムショートカット(→ [ショートカット / プラグイン](#ショートカット--プラグイン--破棄)) |
|
|
951
|
+
| `dateShortcuts` | `boolean` | `true` | 組込 `Ctrl+;`=今日 / `Ctrl+:`=現在時刻(`false` で無効) |
|
|
952
|
+
| `fillShortcuts` | `boolean` | `true` | 組込 `Ctrl+D`=上をコピー / `Ctrl+R`=左をコピー(`false` で無効) |
|
|
953
|
+
| `todayFormat` | `string \| (d:Date)=>string` | `'YYYY-MM-DD'` | `Ctrl+;` が**テキスト等の列**に入れる日付形式(トークン `YYYY`/`MM`/`DD`)。`date` 列は列準拠 |
|
|
954
|
+
| `nowFormat` | `string \| (d:Date)=>string` | `'HH:MM'` | `Ctrl+:` が**テキスト等の列**に入れる時刻形式(トークン `HH`/`mm`)。`time` 列は列準拠 |
|
|
938
955
|
| `plugins` | `Array` | `[]` | プラグイン(関数 or 登録名)。`init(grid)` で初期化 |
|
|
939
956
|
| `onPasteOverflow` | `(info)=>void` | — | `pasteOverflow:'error'` で超過時。`info = { anchor:{r,c}, height, width, rows, cols, overRows, overCols }` |
|
|
940
957
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threestarsoftware/tssgrid",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
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
|
@@ -206,6 +206,14 @@ export interface TssGridOptions {
|
|
|
206
206
|
allowInsertCols?: boolean;
|
|
207
207
|
allowDeleteCols?: boolean;
|
|
208
208
|
shortcuts?: any[];
|
|
209
|
+
/** 組込ショートカット Ctrl+;(今日)/ Ctrl+:(現在時刻)を有効化(既定 true)。 */
|
|
210
|
+
dateShortcuts?: boolean;
|
|
211
|
+
/** 組込ショートカット Ctrl+D(上をコピー=fill down)/ Ctrl+R(左をコピー=fill right)を有効化(既定 true)。 */
|
|
212
|
+
fillShortcuts?: boolean;
|
|
213
|
+
/** Ctrl+; が「date 型でない列」に入れる日付形式(既定 'YYYY-MM-DD')。トークン YYYY/MM/DD、または (d:Date)=>string。date 列は列の仕組み(ISO 保存+列 format 表示)に従う。 */
|
|
214
|
+
todayFormat?: string | ((d: Date) => string);
|
|
215
|
+
/** Ctrl+: が「time 型でない列」に入れる時刻形式(既定 'HH:MM')。トークン HH/mm、または (d:Date)=>string。time 列は 'HH:MM' 保存。 */
|
|
216
|
+
nowFormat?: string | ((d: Date) => string);
|
|
209
217
|
/** プラグイン(関数 or 登録名)。init(grid) で初期化。 */
|
|
210
218
|
plugins?: any[];
|
|
211
219
|
|
package/src/tssgrid.js
CHANGED
|
@@ -281,6 +281,13 @@
|
|
|
281
281
|
this._merges = null; // セル結合(横): [{r,c,colspan}] or null。null=結合なし=全経路ゼロコスト
|
|
282
282
|
this._listeners = []; // destroy() 用に登録した document/window リスナを記録
|
|
283
283
|
this._destroyed = false;
|
|
284
|
+
// Excel 入力ショートカット(組込・既定 ON・opt-out 可・未バインドキーなので追加的)。
|
|
285
|
+
this.dateShortcuts = opts.dateShortcuts !== false; // Ctrl+;=今日の日付 / Ctrl+:=現在時刻(文字で判定=JIS の Ctrl+: も US の Ctrl+Shift+; も拾う)
|
|
286
|
+
this.fillShortcuts = opts.fillShortcuts !== false; // Ctrl+D=上のセルをコピー(fill down) / Ctrl+R=左のセルをコピー(fill right)
|
|
287
|
+
// Ctrl+; / Ctrl+: が「テキスト等(date/time 型でない)」列に入れる形式(文字列トークン YYYY/MM/DD・HH/mm、または関数)。
|
|
288
|
+
// date/time 型の列はこの設定に関係なく列の仕組み(ISO 保存+列 format 表示)に従う。
|
|
289
|
+
this.todayFormat = opts.todayFormat || 'YYYY-MM-DD';
|
|
290
|
+
this.nowFormat = opts.nowFormat || 'HH:MM';
|
|
284
291
|
(opts.shortcuts || []).forEach(s => this.addShortcut(s));
|
|
285
292
|
|
|
286
293
|
this._padToMin(); // minRows/minCols まで空行・空列を補う
|
|
@@ -2290,6 +2297,45 @@
|
|
|
2290
2297
|
else { const { r0, r1, c0, c1 } = this.rectRange(); for (let r = r0; r <= r1; r++) for (let c = c0; c <= c1; c++) this.setCell(r, c, '', changes); }
|
|
2291
2298
|
this.pushCmd(changes, before, this.snapSel(), 'delete');
|
|
2292
2299
|
}
|
|
2300
|
+
// フォーマット文字列 or 関数で日時を成形。トークン: YYYY/MM/DD(日付)・HH/mm(時刻)。
|
|
2301
|
+
_fmtDateTime(d, spec, isTime) {
|
|
2302
|
+
if (typeof spec === 'function') { try { return String(spec(d)); } catch (_) { return ''; } }
|
|
2303
|
+
const p2 = n => String(n).padStart(2, '0');
|
|
2304
|
+
const map = isTime ? { HH: p2(d.getHours()), mm: p2(d.getMinutes()) }
|
|
2305
|
+
: { YYYY: String(d.getFullYear()), MM: p2(d.getMonth() + 1), DD: p2(d.getDate()) };
|
|
2306
|
+
return String(spec).replace(/YYYY|MM|DD|HH|mm/g, t => (map[t] != null ? map[t] : t));
|
|
2307
|
+
}
|
|
2308
|
+
// Excel の Ctrl+; / Ctrl+: 相当。date/time 列は ISO ストレージ(表示は列 format)に従い、
|
|
2309
|
+
// テキスト等の列は todayFormat / nowFormat(既定 'YYYY-MM-DD' / 'HH:MM')で成形。検証/Undo を通る setValue。
|
|
2310
|
+
_insertDateTime(isTime) {
|
|
2311
|
+
const r = this.active.r, c = this.active.c;
|
|
2312
|
+
if (this._isReadOnly(r, c)) return;
|
|
2313
|
+
const d = new Date(), p2 = n => String(n).padStart(2, '0');
|
|
2314
|
+
let val;
|
|
2315
|
+
if (isTime) {
|
|
2316
|
+
val = this.colType(c) === 'time' ? (p2(d.getHours()) + ':' + p2(d.getMinutes())) // time 列は保存形式 HH:MM
|
|
2317
|
+
: this._fmtDateTime(d, this.nowFormat, true);
|
|
2318
|
+
} else {
|
|
2319
|
+
val = this.colType(c) === 'date' ? (d.getFullYear() + '-' + p2(d.getMonth() + 1) + '-' + p2(d.getDate())) // date 列は ISO 保存(表示は列 format)
|
|
2320
|
+
: this._fmtDateTime(d, this.todayFormat, false);
|
|
2321
|
+
}
|
|
2322
|
+
this.setValue(r, c, val);
|
|
2323
|
+
}
|
|
2324
|
+
// Excel の Ctrl+D / Ctrl+R 相当。選択範囲を先頭行/左端列の値で埋める(単一セルなら上/左のセルをコピー)。readOnly はスキップ・1 undo。
|
|
2325
|
+
_fillFrom(dir) {
|
|
2326
|
+
const { r0, r1, c0, c1 } = this.rectRange();
|
|
2327
|
+
const single = (r0 === r1 && c0 === c1);
|
|
2328
|
+
const before = this.snapSel(), changes = [];
|
|
2329
|
+
const put = (r, c, v) => { if (!this._isReadOnly(r, c)) this.setCell(r, c, v, changes); };
|
|
2330
|
+
if (dir === 'down') {
|
|
2331
|
+
if (single) { if (r0 > 0) put(r0, c0, this.data[r0 - 1][c0]); }
|
|
2332
|
+
else { for (let c = c0; c <= c1; c++) { const v = this.data[r0][c]; for (let r = r0 + 1; r <= r1; r++) put(r, c, v); } }
|
|
2333
|
+
} else {
|
|
2334
|
+
if (single) { if (c0 > 0) put(r0, c0, this.data[r0][c0 - 1]); }
|
|
2335
|
+
else { for (let r = r0; r <= r1; r++) { const v = this.data[r][c0]; for (let c = c0 + 1; c <= c1; c++) put(r, c, v); } }
|
|
2336
|
+
}
|
|
2337
|
+
if (changes.length) this.pushCmd(changes, before, this.snapSel(), 'edit');
|
|
2338
|
+
}
|
|
2293
2339
|
|
|
2294
2340
|
// ---- 行/列の挿入・削除(構造変更。Undo に1コマンドで積む) ----
|
|
2295
2341
|
// 並列配列(columns/colW は疎なことがある)を現在の COLS/ROWS 長に揃えてから splice する。
|
|
@@ -2965,6 +3011,18 @@
|
|
|
2965
3011
|
if (e.key === 'Enter') { e.preventDefault(); this._commitSelect(); this._advance('enter', e.shiftKey); }
|
|
2966
3012
|
else if (e.key === 'Tab') { e.preventDefault(); this._commitSelect(); this._advance('tab', e.shiftKey); }
|
|
2967
3013
|
else if (e.key === 'Escape') { e.preventDefault(); this._cancelSelect(); }
|
|
3014
|
+
else if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
|
|
3015
|
+
// ←→↑↓ はネイティブ <select> の「オプション変更」を止め、現在値を確定して隣セルへ移動(Excel 流の nav)。
|
|
3016
|
+
// これが無いと、未選択(空)のまま矢印を押すと select が選択肢を 空→先頭 に変え change が即確定=「矢印で先頭が選ばれる」不具合になる。
|
|
3017
|
+
// リストが開いている時は OS ポップアップがキーを奪いこのハンドラは発火しない=閉じて focus だけ残った時の誤爆のみ防ぐ。
|
|
3018
|
+
e.preventDefault();
|
|
3019
|
+
this._commitSelect();
|
|
3020
|
+
const r = this.active.r, c = this.active.c;
|
|
3021
|
+
if (e.key === 'ArrowUp') this.setActive(this._stepRowM(r, c, -1), c);
|
|
3022
|
+
else if (e.key === 'ArrowDown') this.setActive(this._stepRowM(r, c, 1), c);
|
|
3023
|
+
else if (e.key === 'ArrowLeft') this.setActive(r, this._stepColM(r, c, -1));
|
|
3024
|
+
else this.setActive(r, this._stepColM(r, c, 1));
|
|
3025
|
+
}
|
|
2968
3026
|
});
|
|
2969
3027
|
this.select.addEventListener('blur', () => { if (this.mode === 'edit' && this.select.style.display !== 'none') this._commitSelect(); });
|
|
2970
3028
|
// 選択肢を選んだら即確定(移動はしない=Excel と同様にそのセルに留まる)。
|
|
@@ -3289,6 +3347,9 @@
|
|
|
3289
3347
|
if (ctrl && (e.key.toLowerCase() === 'y' || (e.shiftKey && e.key.toLowerCase() === 'z'))) { e.preventDefault(); this.history.redo(); return; }
|
|
3290
3348
|
if (ctrl && e.key.toLowerCase() === 'c') { e.preventDefault(); this.copy(false); return; }
|
|
3291
3349
|
if (ctrl && e.key.toLowerCase() === 'x') { e.preventDefault(); this.copy(true); return; }
|
|
3350
|
+
// Excel 入力ショートカット(未バインドキー=追加的)。文字で判定=JIS(Ctrl+:) も US(Ctrl+Shift+;) も拾う。
|
|
3351
|
+
if (this.dateShortcuts && ctrl && (e.key === ';' || e.key === ':')) { e.preventDefault(); this._insertDateTime(e.key === ':'); return; }
|
|
3352
|
+
if (this.fillShortcuts && ctrl && !e.shiftKey && (e.key.toLowerCase() === 'd' || e.key.toLowerCase() === 'r')) { e.preventDefault(); this._fillFrom(e.key.toLowerCase() === 'd' ? 'down' : 'right'); return; }
|
|
3292
3353
|
// Ctrl+V は keydown で拾わず native paste イベントへ委譲(editor の paste リスナが処理)。
|
|
3293
3354
|
// ここで preventDefault すると paste イベント自体を止めかねないため分岐を置かない。
|
|
3294
3355
|
if (e.shiftKey && e.key.startsWith('Arrow')) {
|