Skip to content

Commit

Permalink
Improve and standardize selection behavior of Tabulator (#2230)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Apr 22, 2021
1 parent 6af7bb5 commit fbe2e2e
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 13 deletions.
9 changes: 7 additions & 2 deletions examples/reference/widgets/Tabulator.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"* **``page_size``** (``int``): Number of rows on each page.\n",
"* **``pagination``** (`str`, `default=None`): Set to 'local' or 'remote' to enable pagination; by default pagination is disabled with the value set to `None`.\n",
"* **``selection``** (``list``): The currently selected rows.\n",
"* **``selectable``** (`boolean` or `str`): Whether to allow selection of rows. Can be `True`, `False` or `'checkbox'. \n",
"* **``selectable``** (`boolean` or `str`): Whether to allow selection of rows. Can be `True`, `False`, `'checkbox'` or `'toggle'`. \n",
"* **``show_index``** (``boolean``): Whether to show the index column.\n",
"* **`theme`** (``str``): The CSS theme to apply (note that changing the theme will restyle all tables on the page).\n",
"* **`titles`** (``dict``): A mapping from column name to a title to override the name with.\n",
Expand Down Expand Up @@ -434,7 +434,12 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The `selectable` parameter can also be used to control how the selection works. Setting it to `False` will disable selecting entirely, while setting `selectable='checkbox'` will add checkboxes to perform the selection:"
"The `selectable` parameter declares how the selections work. \n",
"\n",
"- `True`: Selects rows on click. To select multiple use Ctrl-select, to select a range use Shift-select\n",
"- `False`: Disables selection\n",
"- `'checkbox'`: Adds a column of checkboxes to toggle selections\n",
"- `'toggle'`: Selection toggles when clicked"
]
},
{
Expand Down
2 changes: 2 additions & 0 deletions panel/models/tabulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class DataTabulator(HTMLBox):

sorters = List(Dict(String, String))

select_mode = Any(default=True)

theme = Enum(*TABULATOR_THEMES, default="simple")

theme_url = String(default=THEME_URL)
Expand Down
44 changes: 38 additions & 6 deletions panel/models/tabulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,14 @@ export class DataTabulatorView extends PanelHTMLBoxView {

getConfiguration(): any {
const pagination = this.model.pagination == 'remote' ? 'local': (this.model.pagination || false)
let selectable = !typeof this.model.select_mode === 'boolean'
let configuration = {
...this.model.configuration,
index: "_index",
selectable: selectable,
renderComplete: () => this.renderComplete(),
rowSelectionChanged: (data: any, rows: any) => this.rowSelectionChanged(data, rows),
rowClick: (e: any, row: any) => this.rowClicked(e, row),
cellEdited: (cell: any) => this.cellEdited(cell),
columns: this.getColumns(),
layout: this.getLayout(),
Expand Down Expand Up @@ -197,9 +200,8 @@ export class DataTabulatorView extends PanelHTMLBoxView {
for (const col of column.columns)
group_columns.push({...col})
columns.push({...column, columns: group_columns})
} else {
} else
columns.push({...column})
}
}
for (const column of this.model.columns) {
let tab_column: any = null
Expand Down Expand Up @@ -238,8 +240,6 @@ export class DataTabulatorView extends PanelHTMLBoxView {
}
}

tab_column.editable = () => this.model.editable

const editor: any = column.editor
const ctype = editor.type
if (tab_column.editor != null) {
Expand All @@ -259,9 +259,10 @@ export class DataTabulatorView extends PanelHTMLBoxView {
} else if (ctype === "SelectEditor") {
tab_column.editor = "select"
tab_column.editorParams = {values: editor.options}
} else {
} else if (editor != null && editor.default_view != null) {
tab_column.editor = (cell: any, onRendered: any, success: any, cancel: any) => this.renderEditor(column, cell, onRendered, success, cancel)
}
tab_column.editable = () => (this.model.editable && (editor.default_view != null))
if (config_columns == null)
columns.push(tab_column)
}
Expand Down Expand Up @@ -459,8 +460,37 @@ export class DataTabulatorView extends PanelHTMLBoxView {

// Update model

rowClicked(e: any, row: any) {
if (this._selection_updating || this._initializing || this.model.select_mode !== true)
return
let indices: number[] = []
const selected = this.model.source.selected
const index: number = row._row.data._index
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)
}
}
if (indices.indexOf(index) < 0)
indices.push(index)
else
indices.splice(indices.indexOf(index), 1)
this.tabulator.deselectRow()
this.tabulator.selectRow(indices)
this._selection_updating = true
selected.indices = indices
this._selection_updating = false
}

rowSelectionChanged(data: any, _: any): void {
if (this._selection_updating || this._initializing)
if (this._selection_updating || this._initializing || (typeof this.model.select_mode) === 'boolean')
return
this._selection_updating = true
const indices: any = data.map((row: any) => row._index)
Expand Down Expand Up @@ -497,6 +527,7 @@ export namespace DataTabulator {
page: p.Property<number>
page_size: p.Property<number>
pagination: p.Property<string | null>
select_mode: p.Property<any>
source: p.Property<ColumnDataSource>
sorters: p.Property<any[]>
styles: p.Property<any>
Expand Down Expand Up @@ -535,6 +566,7 @@ export class DataTabulator extends HTMLBox {
pagination: [ Nullable(String), null ],
page: [ Number, 0 ],
page_size: [ Number, 0 ],
select_mode: [ Any, true ],
source: [ Ref(ColumnDataSource) ],
sorters: [ Array(Any), [] ],
styles: [ Any, {} ],
Expand Down
17 changes: 12 additions & 5 deletions panel/widgets/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,11 +714,15 @@ class Tabulator(BaseTable):
The height of each table row.""")

selectable = param.ObjectSelector(
default=True, objects=[True, False, 'checkbox'], doc="""
Whether a table's rows can be selected or not. Multiple
selection is allowed and can be achieved by either clicking
multiple checkboxes (if enabled) or using Shift + click on
rows.""")
default=True, objects=[True, False, 'checkbox', 'toggle'], doc="""
Defines the selection mode of the Tabulator.
- True: Selects rows on click. To select multiple use
Ctrl-select, to select a range use Shift-select
- checkbox: Adds a column of checkboxes to toggle selections
- toggle: Selection toggles when clicked
- False: Disables selection
""")

sorters = param.List(default=[], doc="""
A list of sorters to apply during pagination.""")
Expand All @@ -735,6 +739,8 @@ class Tabulator(BaseTable):

_manual_params = BaseTable._manual_params + _config_params

_rename = {'disabled': 'editable', 'selection': None, 'selectable': 'select_mode'}

def __init__(self, value=None, **params):
configuration = params.pop('configuration', {})
self.style = None
Expand Down Expand Up @@ -954,6 +960,7 @@ def _get_properties(self, source):
props['groupby'] = self.groupby
props['hidden_columns'] = self.hidden_columns
props['editable'] = not self.disabled
props['select_mode'] = self.selectable
process = {'theme': self.theme, 'frozen_rows': self.frozen_rows}
props.update(self._process_param_change(process))
if self.pagination:
Expand Down

0 comments on commit fbe2e2e

Please sign in to comment.