Skip to content

Commit

Permalink
Ensure Tabulator range selection applies to current view (#7063)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored Aug 3, 2024
1 parent 3fe6307 commit a93b609
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 40 deletions.
66 changes: 27 additions & 39 deletions panel/models/tabulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ export class DataTabulatorView extends HTMLBoxView {
_updating_page: boolean = false
_updating_sort: boolean = false
_selection_updating: boolean = false
_last_selected_row: any = null
_initializing: boolean
_lastVerticalScrollbarTopPosition: number = 0
_lastHorizontalScrollbarLeftPosition: number = 0
Expand Down Expand Up @@ -785,7 +786,7 @@ export class DataTabulatorView extends HTMLBoxView {
_expand_render(cell: any): string {
const index = cell._cell.row.data._index
const icon = this.model.expanded.indexOf(index) < 0 ? "►" : "▼"
return `<i>${icon}</i>`
return icon
}

_update_expand(cell: any): void {
Expand Down Expand Up @@ -1233,44 +1234,25 @@ export class DataTabulatorView extends HTMLBoxView {
const selected = this.model.source.selected
const index: number = row._row.data._index

if (this.model.pagination === "remote") {
const includes = this.model.source.selected.indices.indexOf(index) == -1
const flush = !(e.ctrlKey || e.metaKey || e.shiftKey)
if (e.shiftKey && selected.indices.length) {
const start = selected.indices[selected.indices.length-1]
if (index>start) {
for (let i = start; i<=index; i++) {
indices.push(i)
}
} else {
for (let i = start; i>=index; i--) {
indices.push(i)
}
}
} else {
indices.push(index)
}
this._selection_updating = true
this.model.trigger_event(new SelectionEvent(indices, includes, flush))
this._selection_updating = false
return
}

if (e.ctrlKey || e.metaKey) {
indices = [...this.model.source.selected.indices]
} else if (e.shiftKey && selected.indices.length) {
const start = selected.indices[selected.indices.length-1]
if (index>start) {
for (let i = start; i<index; i++) {
indices.push(i)
}
} else {
for (let i = start; i>index; i--) {
indices.push(i)
}
indices = [...selected.indices]
} else if (e.shiftKey && this._last_selected_row) {
const rows = row._row.parent.getDisplayRows()
const start_idx = rows.indexOf(this._last_selected_row)
if (start_idx !== -1) {
const end_idx = rows.indexOf(row._row)
const reverse = start_idx > end_idx
const [start, end] = reverse ? [end_idx+1, start_idx+1] : [start_idx, end_idx]
indices = rows.slice(start, end).map((r: any) => r.data._index)
if (reverse) { indices = indices.reverse() }
}
}
if (indices.indexOf(index) < 0) {
const flush = !(e.ctrlKey || e.metaKey || e.shiftKey)
const includes = indices.includes(index)
const remote = this.model.pagination === "remote"

// Toggle the index on or off (if remote we let Python do the toggling)
if (!includes || remote) {
indices.push(index)
} else {
indices.splice(indices.indexOf(index), 1)
Expand All @@ -1282,10 +1264,16 @@ export class DataTabulatorView extends HTMLBoxView {
}
}
const filtered = this._filter_selected(indices)
this.tabulator.deselectRow()
this.tabulator.selectRow(filtered)
if (!remote) {
this.tabulator.deselectRow()
this.tabulator.selectRow(filtered)
}
this._last_selected_row = row._row
this._selection_updating = true
selected.indices = filtered
if (!remote) {
selected.indices = filtered
}
this.model.trigger_event(new SelectionEvent(indices, !includes, flush))
this._selection_updating = false
}

Expand Down
37 changes: 37 additions & 0 deletions panel/tests/ui/widgets/test_tabulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3497,6 +3497,42 @@ def contains_filter(df, pattern=None):
wait_until(lambda: tbl.selection == [7], page)


@pytest.mark.parametrize('pagination', ['remote', 'local', None])
def test_range_selection_on_sorted_data_downward(page, pagination):
df = pd.DataFrame({'a': [1, 3, 2, 4, 5, 6, 7, 8, 9], 'b': [6, 5, 6, 7, 7, 7, 7, 7, 7]})
table = Tabulator(df, disabled=True, pagination=pagination)

serve_component(page, table)

page.locator('.tabulator-col-title-holder').nth(2).click()

page.locator('.tabulator-row').nth(0).click()

page.keyboard.down('Shift')

page.locator('.tabulator-row').nth(1).click()

wait_until(lambda: table.selection == [0, 2], page)


@pytest.mark.parametrize('pagination', ['remote', 'local', None])
def test_range_selection_on_sorted_data_upward(page, pagination):
df = pd.DataFrame({'a': [1, 3, 2, 4, 5, 6, 7, 8, 9], 'b': [6, 5, 6, 7, 7, 7, 7, 7, 7]})
table = Tabulator(df, disabled=True, pagination=pagination, page_size=3)

serve_component(page, table)

page.locator('.tabulator-col-title-holder').nth(2).click()

page.locator('.tabulator-row').nth(1).click()

page.keyboard.down('Shift')

page.locator('.tabulator-row').nth(0).click()

wait_until(lambda: table.selection == [2, 0], page)


class Test_RemotePagination:

@pytest.fixture(autouse=True)
Expand All @@ -3516,6 +3552,7 @@ def check_selected(self, page, expected, ui_count=None):
ui_count = len(expected)

expect(page.locator('.tabulator-selected')).to_have_count(ui_count)

wait_until(lambda: self.widget.selection == expected, page)

@contextmanager
Expand Down
3 changes: 2 additions & 1 deletion panel/widgets/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -1275,7 +1275,8 @@ def _cleanup(self, root: Model | None = None) -> None:

def _process_event(self, event) -> None:
if event.event_name == 'selection-change':
self._update_selection(event)
if self.pagination == 'remote':
self._update_selection(event)
return

event_col = self._renamed_cols.get(event.column, event.column)
Expand Down

0 comments on commit a93b609

Please sign in to comment.