TssGrid 例: 多段(複数列)ソート onHeaderRender + sortByCols
ヘッダ右端の ▲▼ がソート(▲=昇順・▼=降順、点灯中をもう一度押すと解除)。普通のクリックは単独ソート=別の列を押せば置き換わるので全消しも一発、Ctrl+クリックで多段(押した順=優先度。マーク左に小さく番号)。
ヘッダ本体クリックは列選択(Shift+クリックで列の範囲選択)=ソートと選択が別の場所で両立します。▲▼ は onHeaderRender フックで差し込み(再描画でも自動)、並べ替えの中身は grid.sortByCols(specs)。
デモ
ソートなし(入力順)
要点
let specs = []; // [{col, dir}] 位置=優先度
const find = c => specs.findIndex(s => s.col === c);
const grid = new TssGrid(host, {
headers, columns, data,
// ★ 各ヘッダthの生成後に呼ばれる(再描画毎に自動)=ここで ▲▼ を差し込む
onHeaderRender(th, c) {
const i = find(c), active = i >= 0 ? specs[i].dir : '';
const num = (i >= 0 && specs.length >= 2) ? (i + 1) : '';
const g = document.createElement('span');
g.className = 'ms-grp';
g.innerHTML = '<span class="ms-num">' + num + '</span>' +
'<span class="ms-sort ' + active + '">' +
'<span class="ms-seg ms-up" data-dir="asc"></span>' +
'<span class="ms-seg ms-dn" data-dir="desc"></span>' +
'</span>';
th.appendChild(g);
},
// ★ マーク上ならソート(return false で列選択を抑止)/本体なら列選択(そのまま通す)
onHeaderClick(c, e) {
const seg = e.target.closest('.ms-seg');
if (!seg) return; // 本体クリック=コアの列選択に任せる
const dir = seg.dataset.dir, i = find(c);
if (e.ctrlKey || e.metaKey) { // Ctrl=多段(押した順=優先度・点灯向き再クリックで解除)
if (i >= 0 && specs[i].dir === dir) specs.splice(i, 1);
else if (i >= 0) specs[i].dir = dir; else specs.push({ col: c, dir });
} else { // 単独(置換)=点灯中を再クリックで全解除
if (specs.length === 1 && specs[0].col === c && specs[0].dir === dir) specs = [];
else specs = [{ col: c, dir }];
}
apply();
return false;
},
});
function apply() { specs.length ? grid.sortByCols(specs) : (grid.clearSort() || grid.redraw()); }