Implements a custom sorting for categories

- If the motion-list is sorted by categories, the category is internal sorted by the `category_weight` of the motion
- Adds a slot to pass a custom sorting function to the `sort-filter-bar.component`
This commit is contained in:
GabrielMeyer 2019-07-17 12:26:13 +02:00
parent e28f0f6685
commit ac9bcf8539
3 changed files with 67 additions and 45 deletions

View File

@ -20,6 +20,7 @@ export interface OsSortingDefinition<V> {
export interface OsSortingOption<V> { export interface OsSortingOption<V> {
property: keyof V; property: keyof V;
label?: string; label?: string;
sortFn?: (itemA: V, itemB: V, ascending: boolean, intl?: Intl.Collator) => number;
} }
/** /**
@ -57,7 +58,7 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
/** /**
* The sorting function according to current settings. * The sorting function according to current settings.
*/ */
private sortFn: (a: V, b: V) => number; public sortFn?: (a: V, b: V, ascending: boolean, intl?: Intl.Collator) => number;
/** /**
* Set the current sorting order * Set the current sorting order
@ -201,16 +202,6 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
this.store.set('sorting_' + this.name, this.sortDefinition); this.store.set('sorting_' + this.name, this.sortDefinition);
} }
/**
* Sorts an array of data synchronously, using the currently configured sorting
*
* @param data Array of ViewModels
* @returns the data, sorted with the definitions of this service
*/
public sortSync(data: V[]): V[] {
return data.sort(this.sortFn);
}
/** /**
* Helper function to determine false-like values (if they are not boolean) * Helper function to determine false-like values (if they are not boolean)
* @param property * @param property
@ -246,42 +237,45 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
const firstProperty = this.ascending ? itemA[property] : itemB[property]; const firstProperty = this.ascending ? itemA[property] : itemB[property];
const secondProperty = this.ascending ? itemB[property] : itemA[property]; const secondProperty = this.ascending ? itemB[property] : itemA[property];
switch (typeof firstProperty) { if (this.sortFn) {
case 'boolean': return this.sortFn(itemA, itemB, this.ascending, intl);
if (!firstProperty && secondProperty) { } else {
return -1; switch (typeof firstProperty) {
} else { case 'boolean':
return 1; if (!firstProperty && secondProperty) {
} return -1;
case 'number': } else {
return firstProperty > secondProperty ? 1 : -1; return 1;
case 'string': }
if (!!firstProperty && !secondProperty) { case 'number':
return -1;
} else if (!firstProperty && !!secondProperty) {
return 1;
} else if ((!secondProperty && !secondProperty) || firstProperty === secondProperty) {
return 0;
} else {
return intl.compare(firstProperty, secondProperty);
}
case 'function':
const a = firstProperty();
const b = secondProperty();
return intl.compare(a, b);
case 'object':
if (firstProperty instanceof Date) {
return firstProperty > secondProperty ? 1 : -1; return firstProperty > secondProperty ? 1 : -1;
} else { case 'string':
return intl.compare(firstProperty.toString(), secondProperty.toString()); if (!!firstProperty && !secondProperty) {
} return -1;
case 'undefined': } else if (!firstProperty && !!secondProperty) {
return 1; return 1;
default: } else if ((!secondProperty && !secondProperty) || firstProperty === secondProperty) {
return -1; return 0;
} else {
return intl.compare(firstProperty, secondProperty);
}
case 'function':
const a = firstProperty();
const b = secondProperty();
return intl.compare(a, b);
case 'object':
if (firstProperty instanceof Date) {
return firstProperty > secondProperty ? 1 : -1;
} else {
return intl.compare(firstProperty.toString(), secondProperty.toString());
}
case 'undefined':
return 1;
default:
return -1;
}
} }
}); });
this.outputSubject.next(this.inputData); this.outputSubject.next(this.inputData);
} }
} }

View File

@ -134,6 +134,8 @@ export class SortFilterBarComponent<V extends BaseViewModel> {
} }
public set sortOption(option: OsSortingOption<V>) { public set sortOption(option: OsSortingOption<V>) {
// If the option has a custom sorting function
this.sortService.sortFn = option.sortFn || null;
this.sortService.sortProperty = option.property; this.sortService.sortProperty = option.property;
} }

View File

@ -34,7 +34,7 @@ export class MotionSortListService extends BaseSortListService<ViewMotion> {
{ property: 'identifier' }, { property: 'identifier' },
{ property: 'title' }, { property: 'title' },
{ property: 'submitters' }, { property: 'submitters' },
{ property: 'category' }, { property: 'category', sortFn: this.categorySortFn },
{ property: 'motion_block_id', label: 'Motion block' }, { property: 'motion_block_id', label: 'Motion block' },
{ property: 'state' }, { property: 'state' },
{ property: 'creationDate', label: _('Creation date') }, { property: 'creationDate', label: _('Creation date') },
@ -71,4 +71,30 @@ export class MotionSortListService extends BaseSortListService<ViewMotion> {
sortAscending: true sortAscending: true
}; };
} }
/**
* Custom function to sort the categories internal by the `category_weight` of the motion.
*
* @param itemA The first item to sort
* @param itemB The second item to sort
* @param intl The localizer to compare strings
* @param ascending If the sorting should be in ascended or descended order
*
* @returns {number} The result of comparing.
*/
private categorySortFn(itemA: ViewMotion, itemB: ViewMotion, ascending: boolean, intl: Intl.Collator): number {
const property = 'category';
const subProperty = 'category_weight';
const firstValue = ascending ? itemA[property] : itemB[property];
const secondValue = ascending ? itemB[property] : itemA[property];
const diff = intl.compare(firstValue.toString(), secondValue.toString());
if (diff === 0) {
const firstSubValue = ascending ? itemA[subProperty] : itemB[subProperty];
const secondSubValue = ascending ? itemB[subProperty] : itemA[subProperty];
return firstSubValue > secondSubValue ? 1 : -1;
} else {
return diff;
}
}
} }