Add front-end support for multi-column grid sorting
user must ctrl-click column header to engage multi-sort
This commit is contained in:
parent
6d7754cf2a
commit
edb5393cdc
|
@ -830,10 +830,10 @@ class Grid(object):
|
|||
if self.sortable:
|
||||
self.active_sorters = []
|
||||
for i in range(1, settings['sorters.length'] + 1):
|
||||
self.active_sorters.append((
|
||||
settings[f'sorters.{i}.key'],
|
||||
settings[f'sorters.{i}.dir'],
|
||||
))
|
||||
self.active_sorters.append({
|
||||
'field': settings[f'sorters.{i}.key'],
|
||||
'order': settings[f'sorters.{i}.dir'],
|
||||
})
|
||||
if self.pageable:
|
||||
self.pagesize = settings['pagesize']
|
||||
self.page = settings['page']
|
||||
|
@ -1229,28 +1229,52 @@ class Grid(object):
|
|||
if not self.active_sorters:
|
||||
return data
|
||||
|
||||
# convert sort settings into a 'sortspec' for use with sa-filters
|
||||
full_spec = []
|
||||
for sortkey, sortdir in self.active_sorters:
|
||||
sortfunc = self.sorters.get(sortkey)
|
||||
if sortfunc:
|
||||
spec = {
|
||||
'sortkey': sortkey,
|
||||
'model': sortfunc._class.__name__,
|
||||
'field': sortfunc._column.name,
|
||||
'direction': sortdir or 'asc',
|
||||
}
|
||||
# spec.sortkey = sortkey
|
||||
full_spec.append(spec)
|
||||
# TODO: is there a better way to check for SA sorting?
|
||||
if self.model_class:
|
||||
|
||||
# apply joins needed for this sort spec
|
||||
for spec in full_spec:
|
||||
sortkey = spec['sortkey']
|
||||
# convert sort settings into a 'sortspec' for use with sa-filters
|
||||
full_spec = []
|
||||
for sorter in self.active_sorters:
|
||||
sortkey = sorter['field']
|
||||
sortdir = sorter['order']
|
||||
sortfunc = self.sorters.get(sortkey)
|
||||
if sortfunc:
|
||||
spec = {
|
||||
'sortkey': sortkey,
|
||||
'model': sortfunc._class.__name__,
|
||||
'field': sortfunc._column.name,
|
||||
'direction': sortdir or 'asc',
|
||||
}
|
||||
full_spec.append(spec)
|
||||
|
||||
# apply joins needed for this sort spec
|
||||
for spec in full_spec:
|
||||
sortkey = spec['sortkey']
|
||||
if sortkey in self.joiners and sortkey not in self.joined:
|
||||
data = self.joiners[sortkey](data)
|
||||
self.joined.add(sortkey)
|
||||
|
||||
return apply_sort(data, full_spec)
|
||||
|
||||
else:
|
||||
# not a SQLAlchemy grid, custom sorter
|
||||
|
||||
assert len(self.active_sorters) < 2
|
||||
|
||||
sortkey = self.active_sorters[0]['field']
|
||||
sortdir = self.active_sorters[0]['order'] or 'asc'
|
||||
|
||||
# Cannot sort unless we have a sort function.
|
||||
sortfunc = self.sorters.get(sortkey)
|
||||
if not sortfunc:
|
||||
return data
|
||||
|
||||
# apply joins needed for this sorter
|
||||
if sortkey in self.joiners and sortkey not in self.joined:
|
||||
data = self.joiners[sortkey](data)
|
||||
self.joined.add(sortkey)
|
||||
|
||||
return apply_sort(data, full_spec)
|
||||
return sortfunc(data, sortdir)
|
||||
|
||||
def paginate_data(self, data):
|
||||
"""
|
||||
|
|
|
@ -202,9 +202,25 @@
|
|||
% endif
|
||||
|
||||
% if grid.sortable:
|
||||
:default-sort="sortingPriority[0]"
|
||||
backend-sorting
|
||||
@sort="onSort"
|
||||
backend-sorting
|
||||
@sort="onSort"
|
||||
@sorting-priority-removed="sortingPriorityRemoved"
|
||||
|
||||
## TODO: there is a bug (?) which prevents the arrow from
|
||||
## displaying for simple default single-column sort. so to
|
||||
## work around that, we *disable* multi-sort until the
|
||||
## component is mounted. seems to work for now..see also
|
||||
## https://github.com/buefy/buefy/issues/2584
|
||||
:sort-multiple="allowMultiSort"
|
||||
|
||||
## nb. specify default sort only if single-column
|
||||
:default-sort="backendSorters.length == 1 ? [backendSorters[0].field, backendSorters[0].order] : null"
|
||||
|
||||
## nb. otherwise there may be default multi-column sort
|
||||
:sort-multiple-data="sortingPriority"
|
||||
|
||||
## user must ctrl-click column header to do multi-sort
|
||||
sort-multiple-key="ctrlKey"
|
||||
% endif
|
||||
|
||||
% if grid.click_handlers:
|
||||
|
@ -353,7 +369,25 @@
|
|||
lastItem: ${json.dumps(grid_data['last_item'] if grid.pageable else None)|n},
|
||||
|
||||
% if grid.sortable:
|
||||
sortingPriority: ${json.dumps(grid.active_sorters)|n},
|
||||
|
||||
## TODO: there is a bug (?) which prevents the arrow from
|
||||
## displaying for simple default single-column sort. so to
|
||||
## work around that, we *disable* multi-sort until the
|
||||
## component is mounted. seems to work for now..see also
|
||||
## https://github.com/buefy/buefy/issues/2584
|
||||
allowMultiSort: false,
|
||||
|
||||
## nb. this contains all truly active sorters
|
||||
backendSorters: ${json.dumps(grid.active_sorters)|n},
|
||||
|
||||
## nb. whereas this will only contain multi-column sorters,
|
||||
## but will be *empty* for single-column sorting
|
||||
% if len(grid.active_sorters) > 1:
|
||||
sortingPriority: ${json.dumps(grid.active_sorters)|n},
|
||||
% else:
|
||||
sortingPriority: [],
|
||||
% endif
|
||||
|
||||
% endif
|
||||
|
||||
## filterable: ${json.dumps(grid.filterable)|n},
|
||||
|
@ -395,6 +429,15 @@
|
|||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
## TODO: there is a bug (?) which prevents the arrow from
|
||||
## displaying for simple default single-column sort. so to
|
||||
## work around that, we *disable* multi-sort until the
|
||||
## component is mounted. seems to work for now..see also
|
||||
## https://github.com/buefy/buefy/issues/2584
|
||||
this.allowMultiSort = true
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
% if grid.click_handlers:
|
||||
|
@ -455,9 +498,9 @@
|
|||
getBasicParams() {
|
||||
let params = {}
|
||||
% if grid.sortable:
|
||||
for (let i = 1; i <= this.sortingPriority.length; i++) {
|
||||
params['sort'+i+'key'] = this.sortingPriority[i-1][0]
|
||||
params['sort'+i+'dir'] = this.sortingPriority[i-1][1]
|
||||
for (let i = 1; i <= this.backendSorters.length; i++) {
|
||||
params['sort'+i+'key'] = this.backendSorters[i-1].field
|
||||
params['sort'+i+'dir'] = this.backendSorters[i-1].order
|
||||
}
|
||||
% endif
|
||||
% if grid.pageable:
|
||||
|
@ -537,14 +580,45 @@
|
|||
this.loadAsyncData()
|
||||
},
|
||||
|
||||
onSort(field, order) {
|
||||
this.sortingPriority = [[field, order]]
|
||||
onSort(field, order, event) {
|
||||
|
||||
if (event.ctrlKey) {
|
||||
|
||||
// engage or enhance multi-column sorting
|
||||
let sorter = this.backendSorters.filter(i => i.field === field)[0]
|
||||
if (sorter) {
|
||||
sorter.order = sorter.order === 'desc' ? 'asc' : 'desc'
|
||||
} else {
|
||||
this.backendSorters.push({field, order})
|
||||
}
|
||||
this.sortingPriority = this.backendSorters
|
||||
|
||||
} else {
|
||||
|
||||
// sort by single column only
|
||||
this.backendSorters = [{field, order}]
|
||||
this.sortingPriority = []
|
||||
}
|
||||
|
||||
// always reset to first page when changing sort options
|
||||
// TODO: i mean..right? would we ever not want that?
|
||||
this.currentPage = 1
|
||||
this.loadAsyncData()
|
||||
},
|
||||
|
||||
sortingPriorityRemoved(field) {
|
||||
|
||||
// prune field from active sorters
|
||||
this.backendSorters = this.backendSorters.filter(
|
||||
(sorter) => sorter.field !== field)
|
||||
|
||||
// nb. must keep active sorter list "as-is" even if
|
||||
// there is only one sorter; buefy seems to expect it
|
||||
this.sortingPriority = this.backendSorters
|
||||
|
||||
this.loadAsyncData()
|
||||
},
|
||||
|
||||
resetView() {
|
||||
this.loading = true
|
||||
|
||||
|
|
Loading…
Reference in a new issue