Improve item range selection.

This commit is contained in:
pk
2026-09-27 18:57:33 +02:00
parent 2f3c81e4b2
commit 0291fe46f0
2 changed files with 41 additions and 5 deletions
+7
View File
@@ -36,6 +36,13 @@ class TpTableItem extends BaseElement {
padding: 0;
}
/* Rows are click targets in row-click/ctrl-click selection modes —
a shift+click range gesture must not select their text. Checkbox
tables keep text selectable (selection lives in the checkbox). */
:host([data-selection-mode]) {
user-select: none;
}
.wrap {
display: grid;
grid-template-columns: 0.5fr 3fr 0.5fr 0.5fr 0.5fr; /* Overridden by javascript */
+34 -5
View File
@@ -187,6 +187,7 @@ export class TpTable extends DomQuery(LitElement) {
${this.renderTableHeader(columns, showCheckboxColumn)}
<div class="list"
@row-selection-changed=${(e) => this._selectionChanged(e)}
@mousedown=${(e) => this._listMousedown(e)}
@click=${(e) => this._handleRowClick(e)}>
${!Array.isArray(items) || items.length === 0 ? html`
<div class="empty-message">
@@ -311,6 +312,7 @@ export class TpTable extends DomQuery(LitElement) {
super();
this._selItems = new Map();
this._lastSelectedId = null;
this._shiftDownItem = null;
this.selectionMode = 'checkbox';
}
@@ -632,18 +634,42 @@ export class TpTable extends DomQuery(LitElement) {
}
}
// Text selection starts on MOUSEDOWN — preventing it at click is too
// late: the browser has already selected and the click may even retarget
// to a common ancestor instead of the row. Shift/ctrl/meta clicks are
// selection gestures in row-click mode, so the browser must not start a
// text selection from them.
_listMousedown(e) {
if (this.selectionMode !== 'row-click' || !this.selectable) return;
if (e.shiftKey || e.ctrlKey || e.metaKey) {
e.preventDefault();
}
// Remember the row the shift gesture started on: if the click
// retargets (row re-rendered between mousedown and mouseup), its
// composed path no longer contains a row and the range would
// silently collapse to the previously selected rows.
this._shiftDownItem = e.shiftKey
? e.composedPath().find(n => n?.hasAttribute && n.hasAttribute('item')) || null
: null;
}
_handleRowClick(e) {
if (this.selectionMode !== 'row-click' || !this.selectable) return;
// Prevent the browser's native shift+click text selection since we're
// using shift+click for range selection.
e.preventDefault();
// Walk through composedPath to find the <tp-table-item> host, since
// lit-virtualizer puts rows in a shadow tree.
const itemEl = e.composedPath().find(
let itemEl = e.composedPath().find(
n => n?.hasAttribute && n.hasAttribute('item')
);
if (!itemEl && e.shiftKey && this._shiftDownItem) {
itemEl = this._shiftDownItem;
}
this._shiftDownItem = null;
if (!itemEl || !itemEl.item) return;
const id = this.getItemId(itemEl.item);
@@ -661,6 +687,9 @@ export class TpTable extends DomQuery(LitElement) {
const it = items[i];
this._selItems.set(this.getItemId(it), it);
}
// Stray text selection from before the gesture must not survive.
window.getSelection?.()?.removeAllRanges?.();
}
} else if (e.ctrlKey || e.metaKey) {
// Toggle individual row.