@threestarsoftware/tssgrid 0.1.32 → 0.1.33
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 +11 -0
- package/README.md +2 -0
- package/package.json +1 -1
- package/plugins/tss-hyperlink.d.ts +18 -5
- package/plugins/tss-hyperlink.js +64 -17
- package/src/tssgrid.css +2 -0
- package/src/tssgrid.d.ts +4 -0
- package/src/tssgrid.js +15 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
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.33] - 2026-11-05
|
|
7
|
+
|
|
8
|
+
### 追加
|
|
9
|
+
- **`ellipsis`(溢れた文字を … で省略+ホバーで全文)を追加**(コア・`wordWrap` の兄弟)。既定は従来どおり `overflow:hidden` で切るだけ(… は出ない)。`ellipsis:true`(グリッド全体)/`columns[c].ellipsis:true`(列単位)で**末尾を省略記号**にし、**溢れているセルだけホバーで全文を `title`**(ネイティブツールチップ)表示。溢れているセルだけ初回ホバーで1回測るので軽い(未使用なら即 return=ゼロコスト)。**見え方だけ**=値・コピー・編集は全文のまま(コピーは表示文字でなく元の値)。折り返し(`wordWrap`)と同時なら折り返しが優先。例 `examples/ellipsis.html`。
|
|
10
|
+
|
|
11
|
+
### 修正
|
|
12
|
+
- **`html:true` 列の編集がフォーマット後の生HTMLになる不具合を修正**(コア)— 編集開始値を作る `_editText` が、date/number 以外では `_displayValue`(format 適用後)を返しており、`html:true`+`format` 列だと `<a>`/`<span>` の**生HTML**が編集欄に入って崩れていた(その後タグのままリンク化して二重に崩れる)。**`html:true` 列は「生の値」を編集する**ように修正(表示の装飾=format と、編集対象=値、を分離)。html でない format 列は従来どおり表示形を編集(回帰なし)。
|
|
13
|
+
|
|
14
|
+
### 改善(プラグイン)
|
|
15
|
+
- **`tss-hyperlink` に「モードB(値=文字だけ・URLはシステムが付与)」を追加** — 従来は値 `"表示\|URL"` に URL を埋める必要があった(モードA)。`url` オプション(文字列 or `(value,ctx,row)=>string`)を渡すと、**値は表示テキストだけ**になり URL・装飾をシステム側で付与できる(入力・コピー・編集は文字だけ/Tabulator の `link` フォーマッタ同様)。安全な URL を返せば `<a>`、`null`/`''`/不正なら `<span>`(href 無し=**SPA クリックをホストが拾う**用)、`false` で素テキスト。`className` は関数化(行ごとに出し分け)、`attrs`(`data-*`/`aria-*`/`title`/`id`/`role` のみ・値はエスケープ・`null` は出さない)で `data-id` 等を安全に付与。本文は常に自動エスケープ=手書き format の XSS 罠を回避。**`safeUrl` がサイト内相対パス(`/path`・`./`・`../`・`#`・`?`)をそのまま許可**(SPA ルーティング用)、**プロトコル相対 `//host` は拒否**(不意の外部遷移回避)。`TssHyperlink()`(値="text\|url")は**完全後方互換**。
|
|
16
|
+
|
|
6
17
|
## [0.1.32] - 2026-11-03
|
|
7
18
|
|
|
8
19
|
### 改善(プラグイン)
|
package/README.md
CHANGED
|
@@ -771,6 +771,7 @@ columns: [
|
|
|
771
771
|
```
|
|
772
772
|
- **★安全URL**: `http`/`https`/`mailto`/`tel` のみリンク化。**`javascript:`・`data:` 等は素のテキストに落とす**(XSS 無効化)。スキーム無しは `https://` 補完、表示テキスト・href は HTML エスケープ、`target=_blank` に `rel="noopener noreferrer"` を付与。
|
|
773
773
|
- `separator`(既定 `'|'`) / `target`(既定 `'_blank'`) / `className`(既定 `'tss-link'`)。`TssHyperlink.safeUrl(u)` で URL 安全化だけ単体利用も可(許可外は `null`)。
|
|
774
|
+
- **モードB(値=文字だけ・URL はシステムが付与)**: `url` オプション(文字列 or `(value,ctx,row)=>string`)を渡すと値は表示テキストだけになり、**入力・コピー・編集も文字だけ**(Tabulator の `link` フォーマッタ同様)。安全な URL を返せば `<a>`、`null`/`''`/不正なら `<span>`(href 無し=**SPA クリックをホストが拾う**用)、`false` で素テキスト。`className` は関数化(行ごとに出し分け)、`attrs`(`data-*`/`aria-*`/`title`/`id`/`role` のみ・値はエスケープ・`null` は出さない)で `data-id` 等を安全付与。`label` で表示差し替え。サイト内相対パス(`/x`・`./`・`../`・`#`・`?`)は許可、`//host` は拒否。例: `format: TssHyperlink({ url:(v,{r},row)=>'/members/'+row.id })`。
|
|
774
775
|
- 数値しか SVG に入れない=安全。`formula`/`html-cells` と同じ仕組みで、**有料グリッドの "セル内グラフ" を無料で**。
|
|
775
776
|
|
|
776
777
|
### 同梱プラグイン: セル内 横棒グラフ(bar・データバー)
|
|
@@ -976,6 +977,7 @@ new TssGrid(el, { hiddenColumns: [1] }); // 初期で隠す
|
|
|
976
977
|
| `rowHeaders` / `colHeaders` | `boolean` | `true` | 行番号列 / ヘッダ行の表示。`false` で隠す |
|
|
977
978
|
| `hiddenColumns` | `number[]` | `[]` | 初期で隠す列インデックス。実行時は `hideColumn`/`showColumn`/`toggleColumn`(→ [列の表示/非表示](#列の表示非表示)) |
|
|
978
979
|
| `wordWrap` | `boolean` | `false` | セル内で折り返し(行高は内容に合わせ伸びる)。`columns[c].wordWrap` で列単位も可 |
|
|
980
|
+
| `ellipsis` | `boolean` | `false` | 溢れた文字を **… で省略**し、溢れたセルは**ホバーで全文(`title`)**。`columns[c].ellipsis` で列単位も可。見え方だけ=値・コピー・編集は全文のまま。`wordWrap` と同時なら折り返しが優先 |
|
|
979
981
|
| `placeholder` | `string` | — | 空セルに薄いヒント文字(`getData` には入らない)。`columns[c].placeholder` で列単位も可 |
|
|
980
982
|
| `className` | `string` | — | コンテナ(`.tssgrid`)に付ける任意クラス(テーマ付け) |
|
|
981
983
|
| `fillHandle` | `boolean \| {direction,autoInsertRow}` | `true` | **`false`でフィル完全OFF**(ハンドルを出さない=ドラッグでの消去/上書きなし。フィルの起点はハンドルのみなので fill 自体が無効に)。`{direction:'vertical'\|'horizontal'\|'both', autoInsertRow:true}`で詳細指定(下へドラッグで行追加)。設定/表示専用グリッドで誤フィルを防ぐのに有効 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threestarsoftware/tssgrid",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
4
4
|
"description": "軽量・ノービルド・日本語IME堅牢な編集グリッド(vanilla JS / 依存なし / MIT)。Excel ライクな入力画面を <script> タグ1本で。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ThreeStarSoftware (https://www.threestarsoftware.co.jp/)",
|
|
@@ -2,17 +2,30 @@
|
|
|
2
2
|
export = TssHyperlink;
|
|
3
3
|
export as namespace TssHyperlink;
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
|
|
5
|
+
/** セルの値をリンク HTML に変換する format 関数を返す。
|
|
6
|
+
* モードA(既定): 値 "表示テキスト|URL"(または URL 単体)。値に URL が入る。
|
|
7
|
+
* モードB(`url` 指定): 値=表示テキストだけ・URL/装飾はオプションで付与(入力/コピー/編集は文字だけ)。 */
|
|
8
|
+
declare function TssHyperlink(opts?: TssHyperlink.Options): (value: any, ctx?: TssHyperlink.Ctx, row?: any) => string;
|
|
7
9
|
|
|
8
10
|
declare namespace TssHyperlink {
|
|
11
|
+
interface Ctx { r: number; c: number; }
|
|
12
|
+
/** (value, ctx, row) を受けて値を返す関数(className/url/label/attrs 用)。row はその行の値配列。 */
|
|
13
|
+
type Fn<T> = (value: any, ctx: Ctx, row: any) => T;
|
|
14
|
+
|
|
9
15
|
interface Options {
|
|
10
|
-
/**
|
|
16
|
+
/** (モードA)表示テキストと URL の区切り(既定 '|')。 */
|
|
11
17
|
separator?: string;
|
|
12
18
|
/** リンクの target(既定 '_blank')。 */
|
|
13
19
|
target?: string;
|
|
14
|
-
/** <a>
|
|
15
|
-
className?: string
|
|
20
|
+
/** <a>/<span> に付けるクラス。文字列 or 関数(行ごとに出し分け可。既定 'tss-link')。 */
|
|
21
|
+
className?: string | Fn<string | null | undefined>;
|
|
22
|
+
/** 指定するとモードB(値=表示テキストだけ)。URL を文字列 or 関数で付与。
|
|
23
|
+
* 安全な URL を返せば <a>、null/''/不正なら <span>(href 無し=SPA クリック用)、false で素テキスト。 */
|
|
24
|
+
url?: string | Fn<string | null | false | undefined>;
|
|
25
|
+
/** (モードB・任意)表示テキストの差し替え(既定は値そのもの)。 */
|
|
26
|
+
label?: Fn<any>;
|
|
27
|
+
/** 追加属性 (value, ctx, row)=>({key:val})。data- / aria- / title / id / role のみ・値はエスケープ・null は出さない。 */
|
|
28
|
+
attrs?: Fn<Record<string, any>>;
|
|
16
29
|
}
|
|
17
30
|
/** URL を安全化(http/https/mailto/tel のみ許可・スキーム無しは https 補完・許可外は null)。 */
|
|
18
31
|
function safeUrl(u: any): string | null;
|
package/plugins/tss-hyperlink.js
CHANGED
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
/*! TssHyperlink — セル内ハイパーリンク(format ヘルパ)。SVG/HTML 自前描画・無依存・コア無改変。
|
|
2
2
|
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* 2つのモード(Tabulator の link フォーマッタ同様、URL を値に埋めても/システムが付けてもよい):
|
|
4
|
+
* ◆モードA(既定・後方互換): 値 = "表示テキスト|URL" または URL 単体。値に URL が入っている。
|
|
5
|
+
* format: TssHyperlink()
|
|
6
|
+
* ◆モードB(`url` オプション指定時): 値 = 表示テキストだけ。URL・装飾はシステム側(関数/文字列)で付与。
|
|
7
|
+
* 入力・コピー・編集は「文字だけ」。本文は常に自動エスケープ=手書き format の XSS 罠を回避。
|
|
8
|
+
* format: TssHyperlink({ url:(val,{r},row)=>'/x/'+row.id }) // 本物リンク(URLを生成)
|
|
9
|
+
* format: TssHyperlink({ url:()=>null, className:'my-link', // href 無し=<span>(SPA クリックをホストが拾う)
|
|
10
|
+
* attrs:(val,{r})=>({ 'data-id': ids[r] }) })
|
|
5
11
|
*
|
|
6
|
-
* ★セキュリティ: URL
|
|
7
|
-
* -
|
|
8
|
-
* - スキーム無し(example.com/path)は https:// を補完。表示テキスト・href
|
|
12
|
+
* ★セキュリティ: URL も attrs も利用者データになり得る=XSS を最初から潰す。
|
|
13
|
+
* - URL のプロトコルは http/https/mailto/tel のみ許可(javascript: data: vbscript: 等は拒否=ただのテキスト/素の span に落とす)。
|
|
14
|
+
* - スキーム無し(example.com/path)は https:// を補完。表示テキスト・href・属性値は必ず HTML エスケープ(" ' も)。
|
|
15
|
+
* - attrs は data-* / aria-* / title / id / role のキーだけ許可(on* や style, href, class は弾く)。値が null のキーは出さない。
|
|
9
16
|
* - target=_blank には rel="noopener noreferrer" を付与。
|
|
10
17
|
*
|
|
11
|
-
*
|
|
18
|
+
* 使い方(列は html:true が要る=format が HTML を返すため。編集はコアが「生の値」を出すので文字だけ編集になる):
|
|
12
19
|
* columns: [
|
|
13
|
-
* { data:'site', title:'リンク', html:true, readOnly:true, format: TssHyperlink() },
|
|
20
|
+
* { data:'site', title:'リンク', html:true, readOnly:true, format: TssHyperlink() }, // モードA
|
|
21
|
+
* { data:'name', title:'名前', html:true, format: TssHyperlink({ url:(v,{r})=>'/u/'+ids[r] }) }, // モードB
|
|
14
22
|
* ]
|
|
15
|
-
* // 値: "GitHub|https://github.com" / "https://example.com" / "example.com" / "問合せ|mailto:a@b.jp"
|
|
16
23
|
*
|
|
17
24
|
* opts:
|
|
18
|
-
* separator :
|
|
25
|
+
* separator : (モードA)表示テキストと URL の区切り(既定 '|')
|
|
26
|
+
* url : (指定でモードB)URL。文字列 or (value, ctx, row)=>string。安全な URL を返せば <a>、
|
|
27
|
+
* null/''/不正なら <span>(href 無し=SPA クリック用)、false を返すと素テキスト(装飾なし)。
|
|
28
|
+
* label : (モードB・任意)表示テキストを差し替える (value, ctx, row)=>string(既定は値そのもの)
|
|
29
|
+
* attrs : (任意)追加属性 (value, ctx, row)=>({key:val})。data- / aria- / title / id / role のみ・値はエスケープ。
|
|
19
30
|
* target : リンクの target(既定 '_blank'。'_self' 等も可)
|
|
20
|
-
* className
|
|
31
|
+
* className : <a>/<span> に付けるクラス。文字列 or (value, ctx, row)=>string(行ごとに出し分け可。既定 'tss-link')
|
|
21
32
|
*/
|
|
22
33
|
(function (root) {
|
|
23
34
|
'use strict';
|
|
@@ -31,27 +42,63 @@
|
|
|
31
42
|
u = String(u == null ? '' : u).trim();
|
|
32
43
|
if (u === '') return null;
|
|
33
44
|
if (/^(https?:|mailto:|tel:)/i.test(u)) return u; // whitelist したスキーム
|
|
45
|
+
if (/^\/\//.test(u)) return null; // プロトコル相対 //host は拒否(不意の外部遷移/phishing 回避)
|
|
46
|
+
if (/^(\/|\.\/|\.\.\/|[#?])/.test(u)) return u; // サイト内相対(/path ./x ../x #frag ?q)はそのまま(SPA ルーティング用)
|
|
34
47
|
if (!/[:]/.test(u) && /^[\w./?#=&%+\-~@!$*,;()]+$/.test(u)) return 'https://' + u; // スキーム無し=ドメイン/パス→https 補完
|
|
35
48
|
return null; // javascript: data: vbscript: 等=拒否
|
|
36
49
|
}
|
|
50
|
+
// attrs を安全な属性文字列に。data-*/aria-*/title/id/role のみ・値はエスケープ・null は出さない。
|
|
51
|
+
function attrStr(fn, v, ctx, row) {
|
|
52
|
+
if (typeof fn !== 'function') return '';
|
|
53
|
+
var o; try { o = fn(v, ctx, row) || {}; } catch (_) { return ''; }
|
|
54
|
+
var s = '';
|
|
55
|
+
for (var k in o) {
|
|
56
|
+
if (!Object.prototype.hasOwnProperty.call(o, k)) continue;
|
|
57
|
+
var val = o[k];
|
|
58
|
+
if (val == null) continue;
|
|
59
|
+
if (!/^(data-[a-z0-9-]+|aria-[a-z0-9-]+|title|id|role)$/i.test(k)) continue; // 安全キーのみ
|
|
60
|
+
s += ' ' + k.toLowerCase() + '="' + esc(val) + '"';
|
|
61
|
+
}
|
|
62
|
+
return s;
|
|
63
|
+
}
|
|
37
64
|
|
|
38
65
|
function TssHyperlink(opts) {
|
|
39
66
|
opts = opts || {};
|
|
40
67
|
var sep = opts.separator != null ? opts.separator : '|';
|
|
41
68
|
var target = opts.target || '_blank';
|
|
42
|
-
var
|
|
43
|
-
|
|
69
|
+
var defCls = opts.className != null ? opts.className : 'tss-link';
|
|
70
|
+
var modeB = ('url' in opts); // url 指定=モードB(値は文字だけ)
|
|
71
|
+
function clsAttrFor(v, ctx, row) {
|
|
72
|
+
var c = (typeof defCls === 'function') ? defCls(v, ctx, row) : defCls;
|
|
73
|
+
c = (c == null ? '' : String(c));
|
|
74
|
+
return c ? ' class="' + esc(c) + '"' : '';
|
|
75
|
+
}
|
|
76
|
+
return function (value, ctx, row) {
|
|
44
77
|
var v = String(value == null ? '' : value);
|
|
45
78
|
if (v === '') return '';
|
|
79
|
+
var clsAttr = clsAttrFor(v, ctx, row);
|
|
80
|
+
var extra = attrStr(opts.attrs, v, ctx, row);
|
|
81
|
+
var rel = (target === '_blank') ? ' rel="noopener noreferrer"' : '';
|
|
82
|
+
|
|
83
|
+
if (modeB) {
|
|
84
|
+
// 値=表示テキスト。URL は opts.url(文字列/関数)で付与。本文は常にエスケープ。
|
|
85
|
+
var label0 = (typeof opts.label === 'function') ? opts.label(v, ctx, row) : v;
|
|
86
|
+
var label = esc(String(label0 == null ? '' : label0));
|
|
87
|
+
var u = (typeof opts.url === 'function') ? opts.url(v, ctx, row) : opts.url;
|
|
88
|
+
if (u === false) return label; // 明示的にリンクにしない=素テキスト
|
|
89
|
+
var safeB = safeUrl(u);
|
|
90
|
+
if (safeB) return '<a href="' + esc(safeB) + '" target="' + esc(target) + '"' + rel + clsAttr + extra + '>' + label + '</a>';
|
|
91
|
+
return '<span' + clsAttr + extra + '>' + label + '</span>'; // href 無し=SPA クリック用
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// モードA(後方互換): 値 "表示|URL" / "URL"
|
|
46
95
|
var i = v.indexOf(sep);
|
|
47
96
|
var text = i >= 0 ? v.slice(0, i) : v;
|
|
48
97
|
var url = i >= 0 ? v.slice(i + 1) : v;
|
|
49
98
|
var safe = safeUrl(url);
|
|
50
|
-
var
|
|
51
|
-
if (!safe) return
|
|
52
|
-
|
|
53
|
-
return '<a href="' + esc(safe) + '" target="' + esc(target) + '"' + rel +
|
|
54
|
-
(cls ? ' class="' + esc(cls) + '"' : '') + '>' + label + '</a>';
|
|
99
|
+
var labelA = esc((text.trim() || url.trim()));
|
|
100
|
+
if (!safe) return labelA; // 不正/未許可 URL=リンクにせずテキスト表示(=安全)
|
|
101
|
+
return '<a href="' + esc(safe) + '" target="' + esc(target) + '"' + rel + clsAttr + extra + '>' + labelA + '</a>';
|
|
55
102
|
};
|
|
56
103
|
}
|
|
57
104
|
// URL の安全化だけ外から使いたい時用(カスタム format 内など)。許可外は null。
|
package/src/tssgrid.css
CHANGED
|
@@ -97,6 +97,8 @@
|
|
|
97
97
|
.tssgrid .tg-table td.tg-placeholder { color: var(--tg-placeholder-fg); }
|
|
98
98
|
/* セル内折り返し: グリッド全体(.tg-wordwrap) or 列単位(td.tg-wrap-cell)。行高は中身に合わせて伸びる */
|
|
99
99
|
.tssgrid.tg-wordwrap .tg-table td, .tssgrid .tg-table td.tg-wrap-cell { white-space: normal; word-break: break-word; }
|
|
100
|
+
/* 溢れた文字を … で省略: グリッド全体(.tg-ellipsis) or 列単位(td.tg-ellip-cell)。既定は overflow:hidden で切るだけ=…は出ない。ホバーで全文を title 表示(JS)。折り返し(wordWrap)が同時指定なら折り返しが優先(white-space:normal で text-overflow 無効) */
|
|
101
|
+
.tssgrid.tg-ellipsis .tg-table td, .tssgrid .tg-table td.tg-ellip-cell { text-overflow: ellipsis; }
|
|
100
102
|
/* 読み取り専用セル: 淡色+既定カーソル(編集不可・全変更経路で拒否) */
|
|
101
103
|
.tssgrid .tg-table td.tg-readonly { background: var(--tg-readonly-bg); color: var(--tg-readonly-fg); cursor: default; }
|
|
102
104
|
.tssgrid .tg-table td.tg-readonly.sel { background: #e7edf5; }
|
package/src/tssgrid.d.ts
CHANGED
|
@@ -89,6 +89,8 @@ export interface ColumnDef {
|
|
|
89
89
|
html?: boolean;
|
|
90
90
|
/** 列単位の折り返し。 */
|
|
91
91
|
wordWrap?: boolean;
|
|
92
|
+
/** 列単位の … 省略(溢れた文字を省略記号で切る/ホバーで全文を title 表示)。値・コピー・編集は不変=見え方だけ。折り返し(wordWrap)と同時なら折り返しが優先。 */
|
|
93
|
+
ellipsis?: boolean;
|
|
92
94
|
/** 複数行(改行)入力を有効化。編集は隠し textarea(IME 直打ち対応)、表示は改行保持(pre-wrap)。行高は自動で伸びないので rowHeights 等で確保する。 */
|
|
93
95
|
multiline?: boolean;
|
|
94
96
|
/** 空セルの薄いヒント文字。 */
|
|
@@ -171,6 +173,8 @@ export interface TssGridOptions {
|
|
|
171
173
|
colHeaders?: boolean;
|
|
172
174
|
hiddenColumns?: number[];
|
|
173
175
|
wordWrap?: boolean;
|
|
176
|
+
/** グリッド全体の … 省略(溢れた文字を省略記号で切る/ホバーで全文を title 表示)。列単位は columns[c].ellipsis。見え方だけ=値・コピー・編集は不変。 */
|
|
177
|
+
ellipsis?: boolean;
|
|
174
178
|
/** multiline 列の Enter 挙動。'commit'(既定・Excel風): Enter=確定 / Alt+Enter=改行。'newline'(メモ帳風): Enter=改行 / Ctrl+Enter=確定。 */
|
|
175
179
|
multilineEnter?: 'commit' | 'newline';
|
|
176
180
|
/** multiline セル右上の「複数行」ヒント表示(既定 true)。色は CSS 変数 --tg-ml-mark。false で非表示。 */
|
package/src/tssgrid.js
CHANGED
|
@@ -214,6 +214,8 @@
|
|
|
214
214
|
this.stretchH = opts.stretchH || 'none';
|
|
215
215
|
// セル内折り返し(既定 nowrap)。grid 全体 or columns[c].wordWrap。
|
|
216
216
|
this.wordWrap = !!opts.wordWrap;
|
|
217
|
+
// 溢れた文字を … で省略(既定は切るだけ)。grid 全体 or columns[c].ellipsis。ホバーで全文を title 表示(見え方だけ・値/コピー/編集は不変)。
|
|
218
|
+
this.ellipsis = !!opts.ellipsis;
|
|
217
219
|
// 複数行(改行)編集の Enter 挙動: 'commit'(既定・Excel風: Enter=確定 / Alt+Enter=改行) | 'newline'(メモ帳風: Enter=改行 / Ctrl+Enter=確定)。列は columns[c].multiline:true で有効化。
|
|
218
220
|
this.multilineEnter = opts.multilineEnter === 'newline' ? 'newline' : 'commit';
|
|
219
221
|
// multiline セル右上の「複数行」ヒント(既定 ON)。色は CSS 変数 --tg-ml-mark で。false で消す。
|
|
@@ -858,6 +860,7 @@
|
|
|
858
860
|
this.root = q('.tssgrid');
|
|
859
861
|
if (this.className) this.root.className += ' ' + this.className; // 任意クラス(テーマ付け)
|
|
860
862
|
if (this.wordWrap) this.root.classList.add('tg-wordwrap'); // グリッド全体の折り返し
|
|
863
|
+
if (this.ellipsis) this.root.classList.add('tg-ellipsis'); // グリッド全体の … 省略
|
|
861
864
|
if (!this.multilineMark) this.root.classList.add('tg-no-ml-mark'); // multiline の右上ヒントを消す
|
|
862
865
|
if (!this.cursor) this.root.classList.add('tg-no-cursor'); // 表示専用(選択枠なし+セルカーソル既定)
|
|
863
866
|
if (this.width) {
|
|
@@ -1195,6 +1198,8 @@
|
|
|
1195
1198
|
_editText(r, c) {
|
|
1196
1199
|
if (this._nativeInputType(c)) return this.data[r][c];
|
|
1197
1200
|
if (this.colType(c) === 'number') return this.data[r][c];
|
|
1201
|
+
// html:true は format の出力が markup(<a>/<span> 等)=編集対象は生の値。整形後を入れるとタグを編集して崩れる。
|
|
1202
|
+
if (this.colCfg(c).html) return this.data[r][c];
|
|
1198
1203
|
return this._displayValue(r, c);
|
|
1199
1204
|
}
|
|
1200
1205
|
// td の中身を型に応じて描画。data は確定済み前提。空セルは placeholder(あれば)を表示。
|
|
@@ -1485,6 +1490,7 @@
|
|
|
1485
1490
|
if (this.colType(c) === 'number') cls.push('tg-num'); // 数値は右寄せ
|
|
1486
1491
|
if (this._isReadOnly(r, c)) cls.push('tg-readonly'); // 読み取り専用(淡色+カーソル既定)
|
|
1487
1492
|
if (this.colCfg(c).wordWrap) cls.push('tg-wrap-cell'); // 列単位の折り返し
|
|
1493
|
+
if (this.colCfg(c).ellipsis) cls.push('tg-ellip-cell'); // 列単位の … 省略
|
|
1488
1494
|
if (this.colCfg(c).multiline) cls.push('tg-multiline-cell'); // 複数行(改行保持=pre-wrap)
|
|
1489
1495
|
if (this.data[r][c] === '' && this._placeholder(c) != null) cls.push('tg-placeholder'); // 空セルのヒント
|
|
1490
1496
|
const al = this._cellAlign(r, c); // 整列(列既定+セル上書き)
|
|
@@ -3164,6 +3170,15 @@
|
|
|
3164
3170
|
this.setActive(+td.dataset.r, +td.dataset.c); this._beginEdit(this.active.r, this.active.c);
|
|
3165
3171
|
});
|
|
3166
3172
|
this.fillhandle.addEventListener('mousedown', (e) => { e.preventDefault(); e.stopPropagation(); this.filling = { src: this.rectRange() }; this._fillRow = null; });
|
|
3173
|
+
// 溢れて … で切れたセルは、ホバー時に全文を title(ネイティブツールチップ)で出す。
|
|
3174
|
+
// 対象セルだけを初回ホバーで1回測るso軽い(redraw で td が作り直され _ellChk も消える)。ellipsis 未使用なら即 return=ゼロコスト。
|
|
3175
|
+
this.table.addEventListener('mouseover', (e) => {
|
|
3176
|
+
const td = e.target.closest && e.target.closest('td');
|
|
3177
|
+
if (!td || td._ellChk) return;
|
|
3178
|
+
if (!this.ellipsis && !td.classList.contains('tg-ellip-cell')) return;
|
|
3179
|
+
td._ellChk = 1;
|
|
3180
|
+
if (td.scrollWidth > td.clientWidth + 1 && !td.hasAttribute('title')) td.title = td.textContent;
|
|
3181
|
+
});
|
|
3167
3182
|
// 右クリックメニュー(行/列の挿入・削除)
|
|
3168
3183
|
this.table.addEventListener('contextmenu', (e) => this._onContextMenu(e));
|
|
3169
3184
|
this.menuEl.addEventListener('mousedown', (e) => {
|