diff --git a/examples/reference/widgets/Tabulator.ipynb b/examples/reference/widgets/Tabulator.ipynb index 5f6174ad78..0ea054b0c5 100644 --- a/examples/reference/widgets/Tabulator.ipynb +++ b/examples/reference/widgets/Tabulator.ipynb @@ -35,19 +35,32 @@ "* **``groupby``** (`list`): Groups rows in the table by one or more columns.\n", "* **``hierarchical``** (boolean, default=False): Whether to render multi-indexes as hierarchical index (note hierarchical must be enabled during instantiation and cannot be modified later)\n", "* **``hidden_columns``** (`list`): List of columns to hide.\n", - "* **``layout``** (str): Describes the column layout mode with one of the following options `'fit_columns'`, `'fit_data'`, `'fit_data_stretch'`, `'fit_data_fill'`, `'fit_data_table'`. \n", + "* **``layout``** (``str``, `default='fit_data_table'`): Describes the column layout mode with one of the following options `'fit_columns'`, `'fit_data'`, `'fit_data_stretch'`, `'fit_data_fill'`, `'fit_data_table'`. \n", "* **``frozen_columns``** (`list`): List of columns to freeze, preventing them from scrolling out of frame. Column can be specified by name or index.\n", "* **``frozen_rows``**: (`list`): List of rows to freeze, preventing them from scrolling out of frame. Rows can be specified by positive or negative index.\n", - "* **``page``** (``int``): Current page if pagination is enabled.\n", - "* **``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", + "* **``page``** (``int``, `default=1`): Current page, if pagination is enabled.\n", + "* **``page_size``** (``int``, `default=20`): Number of rows on each page, if pagination is enabled.\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", + "* **``row_height``** (``int``, `default=30`): The height of each table row.\n", "* **``selection``** (``list``): The currently selected rows.\n", - "* **``selectable``** (`boolean` or `str`): Whether to allow selection of rows. Can be `True`, `False`, `'checkbox'`, `'checkbox-single'` or `'toggle'`. \n", + "* **``selectable``** (`boolean` or `str` or `int`, `default=True`): Defines the selection mode:\n", + " * `True`\n", + " Selects rows on click. To select multiple use Ctrl-select, to select a range use Shift-select\n", + " * `False`\n", + " Disables selection\n", + " * `'checkbox'`\n", + " Adds a column of checkboxes to toggle selections\n", + " * `'checkbox-single'`\n", + " Same as 'checkbox' but header does not alllow select/deselect all\n", + " * `'toggle'`\n", + " Selection toggles when clicked\n", + " * `int`\n", + " The maximum number of selectable rows.\n", "* **``selectable_rows``** (`callable`): A function that should return a list of integer indexes given a DataFrame indicating which rows may be selected.\n", - "* **``show_index``** (``boolean``): Whether to show the index column.\n", - "* **``text_align``** (``dict`` or ``str``): A mapping from column name to alignment or a fixed column alignment, which should be one of 'left', 'center', 'right'.\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", + "* **``show_index``** (``boolean``, `default=True`): Whether to show the index column.\n", + "* **``text_align``** (``dict`` or ``str``): A mapping from column name to alignment or a fixed column alignment, which should be one of `'left'`, `'center'`, `'right'`.\n", + "* **`theme`** (``str``, `default='simple'`): The CSS theme to apply (note that changing the theme will restyle all tables on the page), which should be one of `'default'`, `'site'`, `'simple'`, `'midnight'`, `'modern'`, `'bootstrap'`, `'bootstrap4'`, `'materialize'`, `'bulma'`, `'semantic-ui'`, or `'fast'`.\n", + "* **``titles``** (``dict``): A mapping from column name to a title to override the name with.\n", "* **``value``** (``pd.DataFrame``): The pandas DataFrame to display and edit\n", "* **``widths``** (``dict``): A dictionary mapping from column name to column width in the rendered table.\n", "\n", @@ -333,7 +346,7 @@ "outputs": [], "source": [ "style_df = pd.DataFrame(np.random.randn(10, 5), columns=list('ABCDE'))\n", - "styled = pn.widgets.Tabulator(style_df, page_size=5)" + "styled = pn.widgets.Tabulator(style_df)" ] }, { @@ -445,7 +458,8 @@ "- `False`: Disables selection\n", "- `'checkbox'`: Adds a column of checkboxes to toggle selections\n", "- `'checkbox-single'`: Same as `'checkbox'` but disables (de)select-all in the header\n", - "- `'toggle'`: Selection toggles when clicked" + "- `'toggle'`: Selection toggles when clicked\n", + "- Any positive `int`: A number that sets the maximum number of selectable rows" ] }, { @@ -596,12 +610,38 @@ "metadata": {}, "outputs": [], "source": [ - "large_df = pd._testing.makeCustomDataframe(100000, 5) \n", - "\n", + "large_df = pd._testing.makeCustomDataframe(100000, 5) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", "paginated_table = pn.widgets.Tabulator(large_df, pagination='remote', page_size=10)\n", "paginated_table" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Contrary to the `'remote'` option, `'local'` pagination entirely loads the data but still allows to display it on multiple pages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "paginated_table = pn.widgets.Tabulator(large_df, pagination='local', page_size=10)\n", + "paginated_table" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -943,22 +983,9 @@ } ], "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.10" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/panel/models/tabulator.ts b/panel/models/tabulator.ts index 77af2cecdb..3a2f170a6e 100644 --- a/panel/models/tabulator.ts +++ b/panel/models/tabulator.ts @@ -251,7 +251,9 @@ 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') + // selectable is true if select_mode is a string (one of 'checkbox', 'checkbox-single' and + // 'toggle'). False if it's a boolean or a number (integer on the python side). + let selectable = (typeof this.model.select_mode) === 'string' const that = this let configuration = { ...this.model.configuration, @@ -587,7 +589,7 @@ export class DataTabulatorView extends PanelHTMLBoxView { // Update model rowClicked(e: any, row: any) { - if (this._selection_updating || this._initializing || this.model.select_mode !== true) + if (this._selection_updating || this._initializing || (typeof this.model.select_mode) === 'string' || this.model.select_mode === false) return let indices: number[] = [] const selected = this.model.source.selected @@ -608,6 +610,12 @@ export class DataTabulatorView extends PanelHTMLBoxView { indices.push(index) else indices.splice(indices.indexOf(index), 1) + // Remove the first selected indices when selectable is an int. + if (typeof this.model.select_mode === 'number') { + while (indices.length > this.model.select_mode) { + indices.shift() + } + } const filtered = this._filter_selected(indices) this.tabulator.deselectRow() this.tabulator.selectRow(filtered) diff --git a/panel/widgets/tables.py b/panel/widgets/tables.py index bb298e9b14..d8d524a888 100644 --- a/panel/widgets/tables.py +++ b/panel/widgets/tables.py @@ -724,16 +724,16 @@ class Tabulator(BaseTable): objects=['local', 'remote']) page = param.Integer(default=1, doc=""" - Currently selected page (indexed starting at 1).""") + Currently selected page (indexed starting at 1), if pagination is enabled.""") page_size = param.Integer(default=20, bounds=(1, None), doc=""" - Number of rows to render per page.""") + Number of rows to render per page, if pagination is enabled.""") row_height = param.Integer(default=30, doc=""" The height of each table row.""") - selectable = param.ObjectSelector( - default=True, objects=[True, False, 'checkbox', 'checkbox-single', 'toggle'], doc=""" + selectable = param.ClassSelector( + default=True, class_=(bool, str, int), doc=""" Defines the selection mode of the Tabulator. - True @@ -747,6 +747,8 @@ class Tabulator(BaseTable): Same as 'checkbox' but header does not alllow select/deselect all - 'toggle' Selection toggles when clicked + - int + The maximum number of selectable rows. """) selectable_rows = param.Callable(default=None, doc="""