Skip to content

Commit

Permalink
Implement hierarchical parameter for Tabulator
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Aug 6, 2021
1 parent b220588 commit ce68889
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 9 deletions.
2 changes: 2 additions & 0 deletions panel/models/tabulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class DataTabulator(HTMLBox):

hidden_columns = List(String)

indexes = List(String)

layout = Enum('fit_data', 'fit_data_fill', 'fit_data_stretch', 'fit_data_table', 'fit_columns', default="fit_data")

source = Instance(ColumnDataSource)
Expand Down
52 changes: 50 additions & 2 deletions panel/models/tabulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,45 @@ import {PanelHTMLBoxView, set_size} from "./layout"

declare const Tabulator: any;

function find_group(key: any, value: string, records: any[]): any {
for (const record of records) {
if (record[key] == value)
return record
}
return null
}

function group_data(records: any[], columns: any[], indexes: string[]): any[] {
const grouped = []
const index_field = columns[0].field
for (const record of records) {
const key = indexes[0]
const value = record[indexes[0]]
let group = find_group(key, value, grouped)
if (group == null) {
group = {_children: []}
group[index_field] = value
grouped.push(group)
}
let subgroup = group
for (const index of indexes.slice(1)) {
for (const column of columns.slice(1))
group[column.field] = ""
subgroup = find_group(index, record[index], subgroup._children)
if (subgroup == null) {
subgroup = {_children: []}
subgroup[index_field] = record[index]
}
group._children.push(subgroup)
group = subgroup
}
for (const column of columns.slice(1))
subgroup[column.field] = record[column.field]
}
return grouped
}


// The view of the Bokeh extension/ HTML element
// Here you can define how to render the model as well as react to model changes or View events.
export class DataTabulatorView extends PanelHTMLBoxView {
Expand Down Expand Up @@ -199,6 +238,8 @@ export class DataTabulatorView extends PanelHTMLBoxView {
data = []
else
data = transform_cds_to_records(cds, true)
if (configuration.dataTree)
data = group_data(data, this.model.columns, this.model.indexes)
return {
...configuration,
"data": data,
Expand Down Expand Up @@ -250,7 +291,10 @@ export class DataTabulatorView extends PanelHTMLBoxView {
tab_column.formatter = "tickCross"
else {
tab_column.formatter = (cell: any) => {
return column.formatter.doFormat(cell.getRow(), cell, cell.getValue(), null, null)
const formatted = column.formatter.doFormat(cell.getRow(), cell, cell.getValue(), null, null)
const node = div()
node.innerHTML = formatted
return node.children[0].innerHTML
}
}
}
Expand Down Expand Up @@ -318,7 +362,9 @@ export class DataTabulatorView extends PanelHTMLBoxView {
// Update table

setData(): void {
const data = transform_cds_to_records(this.model.source, true);
let data = transform_cds_to_records(this.model.source, true);
if (this.model.configuration.dataTree)
data = group_data(data, this.model.columns, this.model.indexes)
if (this.model.pagination != null)
this.tabulator.rowManager.setData(data, true, false)
else
Expand Down Expand Up @@ -559,6 +605,7 @@ export namespace DataTabulator {
frozen_rows: p.Property<number[]>
groupby: p.Property<string[]>
hidden_columns: p.Property<string[]>
indexes: p.Property<string[]>
layout: p.Property<typeof TableLayout["__type__"]>
max_page: p.Property<number>
page: p.Property<number>
Expand Down Expand Up @@ -599,6 +646,7 @@ export class DataTabulator extends HTMLBox {
frozen_rows: [ Array(Number), [] ],
groupby: [ Array(String), [] ],
hidden_columns: [ Array(String), [] ],
indexes: [ Array(String), [] ],
layout: [ TableLayout, "fit_data" ],
max_page: [ Number, 0 ],
pagination: [ Nullable(String), null ],
Expand Down
18 changes: 11 additions & 7 deletions panel/widgets/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class BaseTable(ReactiveData, Widget):
Bokeh CellFormatter to use for a particular column
(overrides the default chosen based on the type).""")

hierarchical = param.Boolean(default=False, constant=True, doc="""
Whether to generate a hierachical index.""")

row_height = param.Integer(default=40, doc="""
The height of each table row.""")

Expand Down Expand Up @@ -91,7 +94,7 @@ def _get_columns(self):

indexes = self.indexes
col_names = list(self.value.columns)
if len(indexes) == 1:
if not self.hierarchical or len(indexes) == 1:
col_names = indexes + col_names
else:
col_names = indexes[-1:] + col_names
Expand Down Expand Up @@ -162,7 +165,7 @@ def _get_column_definitions(self, col_names, df):
col_kwargs['width'] = 0

title = self.titles.get(col, str(col))
if col in indexes and len(indexes) > 1 and self.hierarchical:
if col in indexes and len(indexes) > 1 and getattr(self, 'hierarchical', False):
title = 'Index: %s' % ' | '.join(indexes)
column = TableColumn(field=str(col), title=title,
editor=editor, formatter=formatter,
Expand Down Expand Up @@ -575,9 +578,6 @@ class DataFrame(BaseTable):
``"none"``
Do not automatically compute column widths.""")

hierarchical = param.Boolean(default=False, constant=True, doc="""
Whether to generate a hierachical index.""")

fit_columns = param.Boolean(default=None, doc="""
Whether columns should expand to the available width. This
results in no horizontal scrollbar showing up, but data can
Expand Down Expand Up @@ -765,11 +765,13 @@ class Tabulator(BaseTable):

_data_params = ['value', 'page', 'page_size', 'pagination', 'sorters']

_config_params = ['frozen_columns', 'groups', 'selectable']
_config_params = ['frozen_columns', 'groups', 'selectable', 'hierarchical']

_manual_params = BaseTable._manual_params + _config_params

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

def __init__(self, value=None, **params):
configuration = params.pop('configuration', {})
Expand Down Expand Up @@ -1035,6 +1037,7 @@ def _get_properties(self, source):
if self.pagination:
length = 0 if self._processed is None else len(self._processed)
props['max_page'] = length//self.page_size + bool(length%self.page_size)
props['indexes'] = self.indexes
return props

def _get_model(self, doc, root=None, parent=None, comm=None):
Expand Down Expand Up @@ -1140,6 +1143,7 @@ def _get_configuration(self, columns):
raise ValueError("Groups must be defined either explicitly "
"or via the configuration, not both.")
configuration['columns'] = self._config_columns(columns)
configuration['dataTree'] = self.hierarchical
return configuration

def download(self, filename='table.csv'):
Expand Down

0 comments on commit ce68889

Please sign in to comment.