mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Adding sort options for columns.
Summary: Adding sort options for columns. - Sort menu has a new option "More sort options" that opens up Sort left menu - Each sort entry has an additional menu with 3 options -- Order by choice index (for the Choice column, orders by choice position) -- Empty last (puts empty values last in ascending order, first in descending order) -- Natural sort (for Text column, compares strings with numbers as numbers) Updated also CSV/Excel export and api sorting. Most of the changes in this diff is a sort expression refactoring. Pulling out all the methods that works on sortExpression array into a single namespace. Test Plan: Browser tests Reviewers: alexmojaki Reviewed By: alexmojaki Subscribers: dsagal, alexmojaki Differential Revision: https://phab.getgrist.com/D3077
This commit is contained in:
@@ -1,66 +1,34 @@
|
||||
import {GristDoc} from 'app/client/components/GristDoc';
|
||||
import {ClientColumnGetters} from 'app/client/models/ClientColumnGetters';
|
||||
import {ViewSectionRec} from 'app/client/models/entities/ViewSectionRec';
|
||||
import { GristDoc } from 'app/client/components/GristDoc';
|
||||
import { ClientColumnGetters } from 'app/client/models/ClientColumnGetters';
|
||||
import { ViewSectionRec } from 'app/client/models/entities/ViewSectionRec';
|
||||
import * as rowset from 'app/client/models/rowset';
|
||||
import {MANUALSORT} from 'app/common/gristTypes';
|
||||
import {SortFunc} from 'app/common/SortFunc';
|
||||
import { MANUALSORT } from 'app/common/gristTypes';
|
||||
import { SortFunc } from 'app/common/SortFunc';
|
||||
import { Sort } from 'app/common/SortSpec';
|
||||
import * as ko from 'knockout';
|
||||
import range = require('lodash/range');
|
||||
|
||||
|
||||
/**
|
||||
* Adds a column to the given sort spec, replacing its previous occurrence if
|
||||
* it's already in the sort spec.
|
||||
*/
|
||||
export function addToSort(sortSpecObs: ko.Observable<number[]>, colRef: number) {
|
||||
export function addToSort(sortSpecObs: ko.Observable<Sort.SortSpec>, colRef: number, direction: -1|1) {
|
||||
const spec = sortSpecObs.peek();
|
||||
const index = spec.findIndex((colRefSpec) => Math.abs(colRefSpec) === Math.abs(colRef));
|
||||
const index = Sort.findColIndex(spec, colRef);
|
||||
if (index !== -1) {
|
||||
spec.splice(index, 1, colRef);
|
||||
spec.splice(index, 1, colRef * direction);
|
||||
} else {
|
||||
spec.push(colRef);
|
||||
spec.push(colRef * direction);
|
||||
}
|
||||
sortSpecObs(spec);
|
||||
}
|
||||
|
||||
|
||||
// Takes an activeSortSpec and sortRef to flip (negative sortRefs signify descending order) and returns a new
|
||||
// activeSortSpec with that sortRef flipped (or original spec if sortRef not found).
|
||||
export function flipColDirection(spec: number[], sortRef: number): number[] {
|
||||
const idx = spec.findIndex(c => c === sortRef);
|
||||
if (idx !== -1) {
|
||||
const newSpec = Array.from(spec);
|
||||
newSpec[idx] *= -1;
|
||||
return newSpec;
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
// Parses the sortColRefs string, defaulting to an empty array on invalid input.
|
||||
export function parseSortColRefs(sortColRefs: string): number[] {
|
||||
try {
|
||||
return JSON.parse(sortColRefs);
|
||||
} catch (err) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Given the current sort spec, moves sortRef to be immediately before nextSortRef. Moves sortRef
|
||||
// to the end of the sort spec if nextSortRef is null.
|
||||
// If the given sortRef or nextSortRef cannot be found, return sortSpec unchanged.
|
||||
export function reorderSortRefs(spec: number[], sortRef: number, nextSortRef: number|null): number[] {
|
||||
const updatedSpec = spec.slice();
|
||||
|
||||
// Remove sortRef from sortSpec.
|
||||
const _idx = updatedSpec.findIndex(c => c === sortRef);
|
||||
if (_idx === -1) { return spec; }
|
||||
updatedSpec.splice(_idx, 1);
|
||||
|
||||
// Add sortRef to before nextSortRef
|
||||
const _nextIdx = nextSortRef ? updatedSpec.findIndex(c => c === nextSortRef) : updatedSpec.length;
|
||||
if (_nextIdx === -1) { return spec; }
|
||||
updatedSpec.splice(_nextIdx, 0, sortRef);
|
||||
|
||||
return updatedSpec;
|
||||
export function sortBy(sortSpecObs: ko.Observable<Sort.SortSpec>, colRef: number, direction: -1|1) {
|
||||
let spec = sortSpecObs.peek();
|
||||
const colSpec = Sort.findCol(spec, colRef) ?? colRef;
|
||||
spec = [Sort.setColDirection(colSpec, direction)];
|
||||
sortSpecObs(spec);
|
||||
}
|
||||
|
||||
// Updates the manual sort positions to the positions currently displayed in the view, sets the
|
||||
@@ -72,21 +40,27 @@ export async function updatePositions(gristDoc: GristDoc, section: ViewSectionRe
|
||||
|
||||
// Build a sorted array of rowIds the way a view would, using the active sort spec. We just need
|
||||
// the sorted list, and can dispose the observable array immediately.
|
||||
const sortFunc = new SortFunc(new ClientColumnGetters(tableModel, {unversioned: true}));
|
||||
const sortFunc = new SortFunc(new ClientColumnGetters(tableModel, { unversioned: true }));
|
||||
sortFunc.updateSpec(section.activeDisplaySortSpec.peek());
|
||||
const sortedRows = rowset.SortedRowSet.create(null, (a: rowset.RowId, b: rowset.RowId) =>
|
||||
sortFunc.compare(a as number, b as number), tableModel.tableData);
|
||||
const sortedRows = rowset.SortedRowSet.create(
|
||||
null,
|
||||
(a: rowset.RowId, b: rowset.RowId) => sortFunc.compare(a as number, b as number),
|
||||
tableModel.tableData
|
||||
);
|
||||
sortedRows.subscribeTo(tableModel);
|
||||
const sortedRowIds = sortedRows.getKoArray().peek().slice(0);
|
||||
sortedRows.dispose();
|
||||
|
||||
// The action just assigns consecutive positions to the sorted rows.
|
||||
const colInfo = {[MANUALSORT]: range(0, sortedRowIds.length)};
|
||||
await gristDoc.docData.sendActions([
|
||||
// Update row positions and clear the saved sort spec as a single action bundle.
|
||||
['BulkUpdateRecord', tableId, sortedRowIds, colInfo],
|
||||
['UpdateRecord', '_grist_Views_section', section.getRowId(), {sortColRefs: '[]'}]
|
||||
], `Updated table ${tableId} row positions.`);
|
||||
await gristDoc.docData.sendActions(
|
||||
[
|
||||
// Update row positions and clear the saved sort spec as a single action bundle.
|
||||
['BulkUpdateRecord', tableId, sortedRowIds, colInfo],
|
||||
['UpdateRecord', '_grist_Views_section', section.getRowId(), {sortColRefs: '[]'}],
|
||||
],
|
||||
`Updated table ${tableId} row positions.`
|
||||
);
|
||||
// Finally clear out the local sort spec.
|
||||
section.activeSortJson.revert();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user