@threestarsoftware/tssgrid 0.1.21 → 0.1.23

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 CHANGED
@@ -3,6 +3,18 @@
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.23] - 2026-10-08
7
+
8
+ ### 追加
9
+ - **`TssGrid.zen2han(s)` / `TssGrid.esc(s)` を公開 static として型定義(`.d.ts`)に追加**(コア)— 全角→半角ユーティリティ(内部名 `_zen2han` を公開名 `zen2han` にリネーム)と HTML エスケープ `esc` を、**TypeScript から型付きで呼べる**ように宣言。`number`/`date`/`time` の組込 parse で使うほか、**カスタム `parse`/`format` から `TssGrid.zen2han(v)` を再利用**できる(例: 和暦・年月日の自前パーサの前処理で全角も吸収)。挙動は不変。
10
+
11
+ ## [0.1.22] - 2026-10-07
12
+
13
+ ### 追加
14
+ - **`renderCell(r, cOrKey)` を公開**(コア)— セル1つだけ表示を描き直す(テキスト/HTML・条件付き書式〔cellClass/cellStyle/rowClass〕・無効表示を最新データで)。全体 `redraw()` より軽い。`format` が外部状態に依存する/セル内ボタンの状態を更新した等で「このセルだけ」描き直したい時に。`c` は列 index か data キー、範囲外 `r`・未解決 key・窓外/非表示は安全に no-op。**TypeScript の型定義(`.d.ts`)にも追加**。
15
+ - **`date` / `time` 列が IME ON の全角入力を受理**(コア)— 従来は半角しか受け付けず弾いていた全角の日付/時刻(`2026/01/05`・`9:30` 等)を、**半角化して受理**するように(`number` 列と同じく `_zen2han` を通す。全角スラッシュ `/`・全角コロン `:` も対応)。全角の日付/時刻は元々不正なので常に正規化=受理が広がるだけ(半角は従来どおり・実在しない日付/時刻は引き続き拒否)。
16
+ - **ページネーション(サーバ側ページング)のサンプルを追加** — `examples/pagination.html`。ページネーションはサーバ API 形に依存するため**本体は「渡された1ページ分を描く」だけ**に徹する方針を、モックサーバ `fetchPage(offset,limit)→{rows,total}` + `grid.setData(rows)` + グリッド外の前/次/ページサイズ/「N件中 M〜」で提示。README に「ページネーション(サーバ側ページング)」節も追加。
17
+
6
18
  ## [0.1.21] - 2026-10-06
7
19
 
8
20
  ### 修正
package/README.md CHANGED
@@ -351,6 +351,22 @@ new TssGrid(el, { virtual: { buffer: 10 }, data }); // 窓の上下バッフ
351
351
  - 制約(v1): **行高一定**/**固定行列・折り返し・セル結合は非対応**(指定時は自動で無効化+警告)。行番号の桁数が増える場合は `rowHeaderWidth` で行ヘッダ幅を広げられます。
352
352
  - フィルタとの関係: `filter()` は「絞り込んだ部分集合だけ描く」軽さ、`virtual` は「絞らず全件をスクロールする」軽さ。用途で使い分け(v1 では併用は未対応)。
353
353
 
354
+ ### ページネーション(サーバ側ページング)
355
+
356
+ ページネーションは**サーバの API 形(offset/limit・`total` 件数の有無)に依存**するので、**TssGrid 本体は「渡された1ページ分を描く」だけ**に徹します(コアに作り込みません)。ロジックはアプリ側で、**ページ変更 → fetch → `grid.setData(pageRows)`**、ページ番号ボタンや「N 件中 M〜」は**グリッドの外**に置きます。動く例: [`examples/pagination.html`](https://tssgrid.threestarsoftware.co.jp/examples/pagination.html)。
357
+
358
+ ```js
359
+ async function load() {
360
+ const { rows, total } = await fetchPage((page - 1) * size, size); // サーバの offset/limit
361
+ grid.setData(rows); // ← TssGrid は1ページ分を描くだけ
362
+ renderCount(total); // 「N 件中 M〜」はグリッドの外で
363
+ }
364
+ // 前/次/ページ番号は <button> で外に置き、go(p) → load() を呼ぶ
365
+ ```
366
+ - **`total` が返るサーバ**: 「全何件・何ページ」まで出せる(上の例)。**`total` が無い(cursor のみ)**: ページ番号は出せないので **`appendRows` で「もっと読む」=無限スクロール型**に。
367
+ - **1本の長いスクロールで必要分だけ取得**したいなら、ページ送りでなく `setRowCount(total)` + `onViewportChange` + `fillRows(start, rows)`(AG Grid の Infinite Row Model 相当・上の「メソッド」参照)。
368
+ - sort/filter をサーバ側でやる場合も同じ=**操作 → 再 fetch → `setData`**(遅延スクロール型なら `onSortFilterChange` で委譲)。
369
+
354
370
  ### 同梱プラグイン: ヘッダ オートフィルタ(filter UI)
355
371
 
356
372
  `plugins/tss-filter.js`+`.css`= 各見出しの**ロート(漏斗)アイコン**をクリックで**値チェックリスト+検索**のポップアップ(Excel風。ソートの▲▼と区別できるアイコン)。複数列の条件は **AND**。中身は上の**コア `filter()` を呼ぶだけ**。動く例: [`examples/filter-ui.html`](https://tssgrid.threestarsoftware.co.jp/examples/filter-ui.html)。
@@ -972,7 +988,7 @@ new TssGrid(el, {
972
988
  });
973
989
  ```
974
990
 
975
- メソッド: `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()`(列定義変更後の再描画, データ・選択は保持)ほか。
991
+ メソッド: `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()`(列定義変更後の再描画, データ・選択は保持) / `renderCell(r, cOrKey)`(**セル1つだけ表示を描き直す**=テキスト/HTML・条件付き書式・無効表示を最新データで。全体 `redraw()` より軽い。`format` が外部状態に依存する/セル内ボタンの状態を更新した等に。窓外/非表示は no-op)ほか。
976
992
 
977
993
  ## イベント
978
994
 
@@ -1046,8 +1062,8 @@ new TssGrid(el, {
1046
1062
  | `'text'`(既定) | テキスト入力 | そのまま |
1047
1063
  | `'dropdown'` | `<select>`(`options` 必須)。セルに ▾ を表示、**1クリックで一覧が開き**、選ぶと即確定 | 選択肢 or 空のみ許可。範囲外は拒否。**クリア用の空オプションは自動で先頭に付く**ので `options` に `''` を入れない(入れると空が二重)。`allowEmpty:false` で**空オプションを出さず空値も弾く**=ラジオ的な必須選択。**`options` は `string[]` または `{value,label}[]`=保存値(value=内部コード)と表示(label)を分離**(`getData`/`getRows` は value で返る・検証も value で照合) |
1048
1064
  | `'checkbox'` | Space / クリックでトグル | `checked`(既定 `'1'`) / `unchecked`(既定 `''`)。貼付値も正規化 |
1049
- | `'date'` | `<input type="date">` | `YYYY-MM-DD` のみ。実在しない日付(例 `2026-02-30`)は拒否 |
1050
- | `'time'` | `<input type="time">` / テキスト | `HH:MM[:SS]`(24時間制)で保存。`hour12:true` で AM/PM 表示・入力 |
1065
+ | `'date'` | `<input type="date">` | `YYYY-MM-DD` のみ。実在しない日付(例 `2026-02-30`)は拒否。**全角→半角を正規化**(IME ON のまま `2026/01/05` と打っても受理。区切りは `-` `/` `.` + 8桁 `20260105` も可) |
1066
+ | `'time'` | `<input type="time">` / テキスト | `HH:MM[:SS]`(24時間制)で保存。`hour12:true` で AM/PM 表示・入力。**全角→半角を正規化**(IME ON のまま `9:30` と打っても受理) |
1051
1067
  | `'number'` | テキスト(編集時は素の数値) | 数値のみ。`decimals`/`thousands`/`prefix`/`suffix` で表示書式(→ [CellFormat](#cellformat宣言的な表示書式数値--日付))。**全角→半角を既定で正規化**(`123`→`123`、`zenkaku:false`で無効化)。右寄せ |
1052
1068
 
1053
1069
  - **`validator(value, { r, c })`**: `true`/`undefined` で許可、`false` で拒否、**文字列を返すとエラーメッセージ付きで拒否**。型と併用可(型 → validator の順で評価)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threestarsoftware/tssgrid",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
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
@@ -462,6 +462,9 @@ export declare class TssGrid {
462
462
  downloadCSV(filename: string, opts?: CsvOptions): void;
463
463
  /** 列定義変更後などの再描画(データ・選択は保持)。 */
464
464
  redraw(): void;
465
+ /** セル1つだけ表示を描き直す(テキスト/HTML・条件付き書式・無効表示を最新データで)。全体 redraw より軽い。
466
+ * format が外部状態に依存する/セル内ボタンの状態を更新した等に。`c` は列 index か data キー。窓外/非表示は no-op。 */
467
+ renderCell(r: number, cOrKey: number | string): void;
465
468
  /** 画像/フォント等の遅延レイアウト後に幾何キャッシュを再構築する保険。 */
466
469
  refreshGeometry(): void;
467
470
  /** グリッドを破棄(リスナ除去・DOM 撤去・idempotent)。フレームワークの teardown で呼ぶ。 */
@@ -486,6 +489,13 @@ export declare class TssGrid {
486
489
  /** 遅延ロード: 未取得行を表す sentinel。setRowCount が未取得ぶんを data[r] に入れ、fillRows で実データへ差し替える。 */
487
490
  static readonly PENDING: unique symbol;
488
491
 
492
+ /** 全角→半角(数字・符号・小数点・カンマ・%・¥・空白+日付/時刻の区切り /:)。IME 誤入力対策のユーティリティ。
493
+ * number/date/time の組込 parse で使うほか、カスタム `parse`/`format` から `TssGrid.zen2han(v)` として呼べる。 */
494
+ static zen2han(s: string): string;
495
+
496
+ /** HTML エスケープ(`&` `<` `>`)。`format` で HTML を組む時などに。 */
497
+ static esc(s: any): string;
498
+
489
499
  [k: string]: any;
490
500
  }
491
501
 
package/src/tssgrid.js CHANGED
@@ -526,6 +526,13 @@
526
526
  }
527
527
  // 列定義(format/hour12 等)を変えた後の再描画。データと選択は保持。
528
528
  redraw() { this.buildTable(); this.setActive(this.active.r, this.active.c); }
529
+ // セル1つだけ表示を描き直す(テキスト/HTML・条件付き書式〔cellClass/cellStyle/rowClass〕・無効表示を最新データで)。
530
+ // 全体 redraw() より軽い。format が外部状態に依存する/セル内ボタンの状態を更新した等で「このセルだけ」を描き直す用。
531
+ // c は列 index か data キー。非表示・仮想で窓外のセルは安全に no-op(描画対象の DOM が無いだけ)。
532
+ renderCell(r, c) {
533
+ const ci = this._resolveCol(c); if (ci < 0 || r < 0 || r >= this.ROWS) return;
534
+ this._renderCell(r, ci);
535
+ }
529
536
 
530
537
  // ---- 並べ替え(A方式: データ実体を並べ替え。Undo には積まない/状態は行に追従) ----
531
538
  // 行を order(=新しい並びの「元index配列」)で再配置。data/rowH/_src と セル状態(整列/cellRO)も行ごと追従。
@@ -954,7 +961,8 @@
954
961
  static _colLabel(n) { let s = ''; n = n | 0; do { s = String.fromCharCode(65 + (n % 26)) + s; n = Math.floor(n / 26) - 1; } while (n >= 0); return s; }
955
962
  // 入力文字列 → 'yyyy-mm-dd'(不正は null)。yyyymmdd / yyyy-mm-dd / yyyy/mm/dd / yyyy.mm.dd を許容。
956
963
  static _parseDate(s) {
957
- s = String(s).trim(); if (s === '') return null;
964
+ s = TssGrid.zen2han(String(s).trim()); // IME ON の全角(2026/01/01 等)も半角化して受理
965
+ if (s === '') return null;
958
966
  let y, m, d;
959
967
  const sep = /^(\d{4})[-/.](\d{1,2})[-/.](\d{1,2})$/.exec(s);
960
968
  if (sep) { y = +sep[1]; m = +sep[2]; d = +sep[3]; }
@@ -965,7 +973,8 @@
965
973
  }
966
974
  // 入力文字列 → 24時間制 'HH:MM' / 'HH:MM:SS'(不正は null)。24h と 12h(AM/PM) の両方を受理。
967
975
  static _parseTime(s) {
968
- const m = /^(\d{1,2}):(\d{2})(?::(\d{2}))?\s*([AaPp][.]?[Mm][.]?)?$/.exec(String(s).trim());
976
+ // IME ON の全角(9:30 等)も半角化して受理
977
+ const m = /^(\d{1,2}):(\d{2})(?::(\d{2}))?\s*([AaPp][.]?[Mm][.]?)?$/.exec(TssGrid.zen2han(String(s).trim()));
969
978
  if (!m) return null;
970
979
  let h = +m[1]; const mi = +m[2], se = m[3] == null ? null : +m[3];
971
980
  const ap = m[4] ? m[4][0].toLowerCase() : null; // 'a' | 'p' | null
@@ -982,17 +991,20 @@
982
991
  return h + ':' + m[2] + (m[3] != null ? ':' + m[3] : '') + ' ' + ap;
983
992
  }
984
993
  // CellFormat: 入力(グルーピング/前後綴り混じり) → 正規化した数値文字列(保存値)。不正は null。
985
- // 全角→半角(数値文脈)。数字・符号・小数点・カンマ・%・¥・空白を半角化。業務フォームの IME 誤入力対策。
986
- static _zen2han(s) {
994
+ // 全角→半角(公開 static ユーティリティ)。数字・符号・小数点・カンマ・%・¥・空白+日付/時刻の区切り(/:)を半角化。
995
+ // 業務フォームの IME 誤入力対策(IME ON のまま「2026/01/01」「9:30」等を打っても弾かず半角で受理)。
996
+ // 内部(number/date/time の parse)で使うほか、カスタム parse/format から TssGrid.zen2han(v) として呼べる。
997
+ static zen2han(s) {
987
998
  return String(s)
988
999
  .replace(/[0-9]/g, ch => String.fromCharCode(ch.charCodeAt(0) - 0xFEE0))
989
1000
  .replace(/+/g, '+').replace(/[-−ー―]/g, '-')
990
1001
  .replace(/./g, '.').replace(/,/g, ',')
1002
+ .replace(///g, '/').replace(/:/g, ':')
991
1003
  .replace(/%/g, '%').replace(/¥/g, '¥').replace(/ /g, ' ');
992
1004
  }
993
1005
  static _parseNumber(v, cfg) {
994
1006
  let s = String(v).trim();
995
- if (cfg.zenkaku !== false) s = TssGrid._zen2han(s); // 既定で全角→半角(cfg.zenkaku:false で無効化)
1007
+ if (cfg.zenkaku !== false) s = TssGrid.zen2han(s); // 既定で全角→半角(cfg.zenkaku:false で無効化)
996
1008
  if (cfg.prefix) s = s.split(cfg.prefix).join('');
997
1009
  if (cfg.suffix) s = s.split(cfg.suffix).join('');
998
1010
  s = s.replace(/,/g, '').replace(/\s/g, '');