Implements the possibility to sort trees by a given property
- Separates the base sorting function to a lower service, called `base-sort.service`. - Adds the `tree-sort.service`.
This commit is contained in:
parent
2c58d0f0fe
commit
1ec5dfbb88
@ -1,32 +1,15 @@
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
import { BaseSortService, OsSortingDefinition, OsSortingOption } from './base-sort.service';
|
||||||
import { BaseViewModel } from '../../site/base/base-view-model';
|
import { BaseViewModel } from '../../site/base/base-view-model';
|
||||||
import { OpenSlidesStatusService } from '../core-services/openslides-status.service';
|
import { OpenSlidesStatusService } from '../core-services/openslides-status.service';
|
||||||
import { StorageService } from '../core-services/storage.service';
|
import { StorageService } from '../core-services/storage.service';
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes the sorting columns of an associated ListView, and their state.
|
|
||||||
*/
|
|
||||||
export interface OsSortingDefinition<V> {
|
|
||||||
sortProperty: keyof V;
|
|
||||||
sortAscending: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A sorting property (data may be a string, a number, a function, or an object
|
|
||||||
* with a toString method) to sort after. Sorting will be done in {@link filterData}
|
|
||||||
*/
|
|
||||||
export interface OsSortingOption<V> {
|
|
||||||
property: keyof V;
|
|
||||||
label?: string;
|
|
||||||
sortFn?: (itemA: V, itemB: V, ascending: boolean, intl?: Intl.Collator) => number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for generic sorting purposes
|
* Base class for generic sorting purposes
|
||||||
*/
|
*/
|
||||||
export abstract class BaseSortListService<V extends BaseViewModel> {
|
export abstract class BaseSortListService<V extends BaseViewModel> extends BaseSortService<V> {
|
||||||
/**
|
/**
|
||||||
* The data to be sorted. See also the setter for {@link data}
|
* The data to be sorted. See also the setter for {@link data}
|
||||||
*/
|
*/
|
||||||
@ -60,11 +43,6 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
|
|||||||
*/
|
*/
|
||||||
protected abstract readonly storageKey: string;
|
protected abstract readonly storageKey: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* The sorting function according to current settings.
|
|
||||||
*/
|
|
||||||
public sortFn?: (a: V, b: V, ascending: boolean, intl?: Intl.Collator) => number;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current sorting order
|
* Set the current sorting order
|
||||||
*
|
*
|
||||||
@ -131,7 +109,9 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
|
|||||||
protected translate: TranslateService,
|
protected translate: TranslateService,
|
||||||
private store: StorageService,
|
private store: StorageService,
|
||||||
private OSStatus: OpenSlidesStatusService
|
private OSStatus: OpenSlidesStatusService
|
||||||
) {}
|
) {
|
||||||
|
super(translate);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enforce children to implement a function that returns their sorting options
|
* Enforce children to implement a function that returns their sorting options
|
||||||
@ -229,79 +209,14 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to determine false-like values (if they are not boolean)
|
|
||||||
* @param property
|
|
||||||
*/
|
|
||||||
private isFalsy(property: any): boolean {
|
|
||||||
return property === null || property === undefined || property === 0 || property === '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recreates the sorting function. Is supposed to be called on init and
|
* Recreates the sorting function. Is supposed to be called on init and
|
||||||
* every time the sorting (property, ascending/descending) or the language changes
|
* every time the sorting (property, ascending/descending) or the language changes
|
||||||
*/
|
*/
|
||||||
protected updateSortedData(): void {
|
protected updateSortedData(): void {
|
||||||
if (this.inputData && this.sortDefinition) {
|
if (this.inputData) {
|
||||||
const property = this.sortProperty as string;
|
|
||||||
|
|
||||||
const intl = new Intl.Collator(this.translate.currentLang, {
|
|
||||||
numeric: true,
|
|
||||||
ignorePunctuation: true,
|
|
||||||
sensitivity: 'base'
|
|
||||||
});
|
|
||||||
|
|
||||||
this.inputData.sort((itemA, itemB) => {
|
this.inputData.sort((itemA, itemB) => {
|
||||||
// always sort falsy values to the bottom
|
return this.sortItems(itemA, itemB, this.sortProperty, this.ascending);
|
||||||
if (this.isFalsy(itemA[property]) && this.isFalsy(itemB[property])) {
|
|
||||||
return 0;
|
|
||||||
} else if (this.isFalsy(itemA[property])) {
|
|
||||||
return 1;
|
|
||||||
} else if (this.isFalsy(itemB[property])) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const firstProperty = this.ascending ? itemA[property] : itemB[property];
|
|
||||||
const secondProperty = this.ascending ? itemB[property] : itemA[property];
|
|
||||||
|
|
||||||
if (this.sortFn) {
|
|
||||||
return this.sortFn(itemA, itemB, this.ascending, intl);
|
|
||||||
} else {
|
|
||||||
switch (typeof firstProperty) {
|
|
||||||
case 'boolean':
|
|
||||||
if (!firstProperty && secondProperty) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
case 'number':
|
|
||||||
return firstProperty > secondProperty ? 1 : -1;
|
|
||||||
case 'string':
|
|
||||||
if (!!firstProperty && !secondProperty) {
|
|
||||||
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;
|
|
||||||
} 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);
|
||||||
}
|
}
|
||||||
|
19
client/src/app/core/ui-services/base-sort.service.spec.ts
Normal file
19
client/src/app/core/ui-services/base-sort.service.spec.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { BaseSortService } from './base-sort.service';
|
||||||
|
import { E2EImportsModule } from '../../../e2e-imports.module';
|
||||||
|
|
||||||
|
describe('BaseSortService', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule],
|
||||||
|
providers: [BaseSortService]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO testing (does not work without injecting a BaseViewComponent)
|
||||||
|
// it('should be created', () => {
|
||||||
|
// const service: BaseSortService = TestBed.get(BaseSortService);
|
||||||
|
// expect(service).toBeTruthy();
|
||||||
|
// });
|
||||||
|
});
|
124
client/src/app/core/ui-services/base-sort.service.ts
Normal file
124
client/src/app/core/ui-services/base-sort.service.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
|
import { Displayable } from 'app/site/base/displayable';
|
||||||
|
|
||||||
|
export type SortDefinition<T> = keyof T | OsSortingDefinition<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the sorting columns of an associated ListView, and their state.
|
||||||
|
*/
|
||||||
|
export interface OsSortingDefinition<T> {
|
||||||
|
sortProperty: keyof T;
|
||||||
|
sortAscending: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sorting property (data may be a string, a number, a function, or an object
|
||||||
|
* with a toString method) to sort after. Sorting will be done in {@link filterData}
|
||||||
|
*/
|
||||||
|
export interface OsSortingOption<T> {
|
||||||
|
property: keyof T;
|
||||||
|
label?: string;
|
||||||
|
sortFn?: (itemA: T, itemB: T, ascending: boolean, intl?: Intl.Collator) => number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base sorting service with main functionality for sorting.
|
||||||
|
*
|
||||||
|
* Extends sorting services to sort with a consistent function.
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export abstract class BaseSortService<T extends Identifiable & Displayable> {
|
||||||
|
/**
|
||||||
|
* The sorting function according to current settings.
|
||||||
|
*/
|
||||||
|
public sortFn?: (a: T, b: T, ascending: boolean, intl?: Intl.Collator) => number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The international localisation.
|
||||||
|
*/
|
||||||
|
protected intl: Intl.Collator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* Pass the `TranslatorService`.
|
||||||
|
*/
|
||||||
|
public constructor(protected translate: TranslateService) {
|
||||||
|
this.intl = new Intl.Collator(translate.currentLang, {
|
||||||
|
numeric: true,
|
||||||
|
ignorePunctuation: true,
|
||||||
|
sensitivity: 'base'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to determine false-like values (if they are not boolean)
|
||||||
|
* @param property
|
||||||
|
*/
|
||||||
|
private isFalsy(property: any): boolean {
|
||||||
|
return property === null || property === undefined || property === 0 || property === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recreates the sorting function. Is supposed to be called on init and
|
||||||
|
* every time the sorting (property, ascending/descending) or the language changes
|
||||||
|
*/
|
||||||
|
protected sortItems(itemA: T, itemB: T, sortProperty: keyof T, ascending: boolean = true): number {
|
||||||
|
// always sort falsy values to the bottom
|
||||||
|
const property = sortProperty as string;
|
||||||
|
if (this.isFalsy(itemA[property]) && this.isFalsy(itemB[property])) {
|
||||||
|
return 0;
|
||||||
|
} else if (this.isFalsy(itemA[property])) {
|
||||||
|
return 1;
|
||||||
|
} else if (this.isFalsy(itemB[property])) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstProperty = ascending ? itemA[property] : itemB[property];
|
||||||
|
const secondProperty = ascending ? itemB[property] : itemA[property];
|
||||||
|
|
||||||
|
if (this.sortFn) {
|
||||||
|
return this.sortFn(itemA, itemB, ascending, this.intl);
|
||||||
|
} else {
|
||||||
|
switch (typeof firstProperty) {
|
||||||
|
case 'boolean':
|
||||||
|
if (!firstProperty && secondProperty) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case 'number':
|
||||||
|
return firstProperty > secondProperty ? 1 : -1;
|
||||||
|
case 'string':
|
||||||
|
if (!!firstProperty && !secondProperty) {
|
||||||
|
return -1;
|
||||||
|
} else if (!firstProperty && !!secondProperty) {
|
||||||
|
return 1;
|
||||||
|
} else if ((!secondProperty && !secondProperty) || firstProperty === secondProperty) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return this.intl.compare(firstProperty, secondProperty);
|
||||||
|
}
|
||||||
|
case 'function':
|
||||||
|
const a = firstProperty();
|
||||||
|
const b = secondProperty();
|
||||||
|
return this.intl.compare(a, b);
|
||||||
|
case 'object':
|
||||||
|
if (firstProperty instanceof Date) {
|
||||||
|
return firstProperty > secondProperty ? 1 : -1;
|
||||||
|
} else {
|
||||||
|
return this.intl.compare(firstProperty.toString(), secondProperty.toString());
|
||||||
|
}
|
||||||
|
case 'undefined':
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
client/src/app/core/ui-services/tree-sort.service.spec.ts
Normal file
19
client/src/app/core/ui-services/tree-sort.service.spec.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { E2EImportsModule } from '../../../e2e-imports.module';
|
||||||
|
import { TreeSortService } from './tree-sort.service';
|
||||||
|
|
||||||
|
describe('TreeSortService', () => {
|
||||||
|
beforeEach(() =>
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule],
|
||||||
|
providers: [TreeSortService]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO testing (does not work without injecting a BaseViewComponent)
|
||||||
|
// it('should be created', () => {
|
||||||
|
// const service: TreeSortService = TestBed.get(TreeSortService);
|
||||||
|
// expect(service).toBeTruthy();
|
||||||
|
// });
|
||||||
|
});
|
53
client/src/app/core/ui-services/tree-sort.service.ts
Normal file
53
client/src/app/core/ui-services/tree-sort.service.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
|
import { Displayable } from 'app/site/base/displayable';
|
||||||
|
import { BaseSortService } from './base-sort.service';
|
||||||
|
import { FlatNode } from './tree.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorting service for trees.
|
||||||
|
*
|
||||||
|
* Contains base functions to sort a tree by different properties.
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class TreeSortService<T extends Identifiable & Displayable> extends BaseSortService<T> {
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* Calls the `super()`-method.
|
||||||
|
*
|
||||||
|
* @param translate The reference to the `TranslateService`
|
||||||
|
*/
|
||||||
|
public constructor(protected translate: TranslateService) {
|
||||||
|
super(translate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to sort the passed source of a tree
|
||||||
|
* and resets some properties like `level`, `expandable`, `position`.
|
||||||
|
*
|
||||||
|
* @param sourceData The source array of `FlatNode`s.
|
||||||
|
* @param property The property, the array will be sorted by.
|
||||||
|
* @param ascending Boolean, if the array should be sorted in ascending order.
|
||||||
|
*
|
||||||
|
* @returns {FlatNode<T>[]} The sorted array.
|
||||||
|
*/
|
||||||
|
public sortTree(sourceData: FlatNode<T>[], property: keyof T, ascending: boolean = true): FlatNode<T>[] {
|
||||||
|
return sourceData
|
||||||
|
.sort((nodeA, nodeB) => {
|
||||||
|
const itemA = nodeA.item;
|
||||||
|
const itemB = nodeB.item;
|
||||||
|
return this.sortItems(itemA, itemB, property, ascending);
|
||||||
|
})
|
||||||
|
.map((node, index) => {
|
||||||
|
node.level = 0;
|
||||||
|
node.position = index;
|
||||||
|
node.expandable = false;
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,8 @@ import { MatBottomSheet } from '@angular/material/bottom-sheet';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { BaseFilterListService, OsFilterIndicator } from 'app/core/ui-services/base-filter-list.service';
|
import { BaseFilterListService, OsFilterIndicator } from 'app/core/ui-services/base-filter-list.service';
|
||||||
import { OsSortingOption } from 'app/core/ui-services/base-sort-list.service';
|
|
||||||
import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service';
|
import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service';
|
||||||
|
import { OsSortingOption } from 'app/core/ui-services/base-sort.service';
|
||||||
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { FilterMenuComponent } from './filter-menu/filter-menu.component';
|
import { FilterMenuComponent } from './filter-menu/filter-menu.component';
|
||||||
|
@ -6,6 +6,8 @@ import { Component, ContentChild, EventEmitter, Input, OnDestroy, OnInit, Output
|
|||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
import { auditTime } from 'rxjs/operators';
|
import { auditTime } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { SortDefinition } from 'app/core/ui-services/base-sort.service';
|
||||||
|
import { TreeSortService } from 'app/core/ui-services/tree-sort.service';
|
||||||
import { FlatNode, TreeIdNode, TreeService } from 'app/core/ui-services/tree.service';
|
import { FlatNode, TreeIdNode, TreeService } from 'app/core/ui-services/tree.service';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { Displayable } from 'app/site/base/displayable';
|
import { Displayable } from 'app/site/base/displayable';
|
||||||
@ -175,6 +177,19 @@ export class SortingTreeComponent<T extends Identifiable & Displayable> implemen
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter for sorting functions.
|
||||||
|
*
|
||||||
|
* @param predicate An `EventEmitter` to push to sort the tree by the passed property.
|
||||||
|
* It should pass a `SortDefinition`.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
public set sortingDefinition(predicate: EventEmitter<SortDefinition<T>>) {
|
||||||
|
predicate.subscribe((event: SortDefinition<T>) => {
|
||||||
|
this.resolveSortingPredicate(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EventEmitter to send info if changes has been made.
|
* EventEmitter to send info if changes has been made.
|
||||||
*/
|
*/
|
||||||
@ -197,8 +212,9 @@ export class SortingTreeComponent<T extends Identifiable & Displayable> implemen
|
|||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param treeService Service to get data from store and build the tree nodes.
|
* @param treeService Service to get data from store and build the tree nodes.
|
||||||
|
* @param sortService Service to sort tree nodes by their given items.
|
||||||
*/
|
*/
|
||||||
public constructor(private treeService: TreeService) {}
|
public constructor(private treeService: TreeService, private sortService: TreeSortService<T>) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On init method
|
* On init method
|
||||||
@ -947,6 +963,27 @@ export class SortingTreeComponent<T extends Identifiable & Displayable> implemen
|
|||||||
return willFiltered;
|
return willFiltered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to sort the given source-array by the passed property of the underlying items `<T>`.
|
||||||
|
*
|
||||||
|
* @param event `SortDefinition<T>` - can be only a `keyof T` or
|
||||||
|
* an object defining the property and whether ascending order or not:
|
||||||
|
* `{ property: keyof T, ascending: boolean }`.
|
||||||
|
*/
|
||||||
|
private resolveSortingPredicate(event: SortDefinition<T>): void {
|
||||||
|
this.removeSubscription();
|
||||||
|
const sortProperty = typeof event === 'object' ? event.sortProperty : event;
|
||||||
|
const sortAscending = typeof event === 'object' ? event.sortAscending : true;
|
||||||
|
|
||||||
|
this.osTreeData = this.sortService.sortTree(this.osTreeData, sortProperty, sortAscending);
|
||||||
|
this.checkActiveFilters();
|
||||||
|
|
||||||
|
this.dataSource = null;
|
||||||
|
this.dataSource = new ArrayDataSource(this.osTreeData);
|
||||||
|
|
||||||
|
this.madeChanges(true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to check if a node has children.
|
* Function to check if a node has children.
|
||||||
*/
|
*/
|
||||||
|
@ -4,7 +4,8 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
|
|
||||||
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
||||||
import { StorageService } from 'app/core/core-services/storage.service';
|
import { StorageService } from 'app/core/core-services/storage.service';
|
||||||
import { BaseSortListService, OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort-list.service';
|
import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service';
|
||||||
|
import { OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort.service';
|
||||||
import { ViewAssignment } from '../models/view-assignment';
|
import { ViewAssignment } from '../models/view-assignment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,6 +4,7 @@ import { Title } from '@angular/platform-browser';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { SortDefinition } from 'app/core/ui-services/base-sort.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { SortingTreeComponent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
import { SortingTreeComponent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
@ -40,6 +41,11 @@ export abstract class SortTreeViewComponent<V extends BaseViewModel> extends Bas
|
|||||||
*/
|
*/
|
||||||
public readonly changeFilter: EventEmitter<(item: V) => boolean> = new EventEmitter<(item: V) => boolean>();
|
public readonly changeFilter: EventEmitter<(item: V) => boolean> = new EventEmitter<(item: V) => boolean>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emitter to notice the `tree-sorting.service` for sorting the data-source.
|
||||||
|
*/
|
||||||
|
public readonly forceSort = new EventEmitter<SortDefinition<V>>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boolean to check if changes has been made.
|
* Boolean to check if changes has been made.
|
||||||
*/
|
*/
|
||||||
|
@ -4,7 +4,8 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
|
|
||||||
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
||||||
import { StorageService } from 'app/core/core-services/storage.service';
|
import { StorageService } from 'app/core/core-services/storage.service';
|
||||||
import { BaseSortListService, OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort-list.service';
|
import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service';
|
||||||
|
import { OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort.service';
|
||||||
import { ViewMediafile } from '../models/view-mediafile';
|
import { ViewMediafile } from '../models/view-mediafile';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,6 +82,7 @@
|
|||||||
(hasChanged)="receiveChanges($event)"
|
(hasChanged)="receiveChanges($event)"
|
||||||
[model]="motionsObservable"
|
[model]="motionsObservable"
|
||||||
[filterChange]="changeFilter"
|
[filterChange]="changeFilter"
|
||||||
|
[sortingDefinition]="forceSort"
|
||||||
>
|
>
|
||||||
<ng-template #innerNode let-item="item">
|
<ng-template #innerNode let-item="item">
|
||||||
<div class="line">
|
<div class="line">
|
||||||
@ -100,6 +101,13 @@
|
|||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<mat-menu #mainMenu="matMenu">
|
<mat-menu #mainMenu="matMenu">
|
||||||
|
<button mat-menu-item (click)="sortMotionsByIdentifier()">
|
||||||
|
<mat-icon>sort</mat-icon>
|
||||||
|
<span translate>Sort by identifier</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
<button mat-menu-item (click)="pdfExportCallList()">
|
<button mat-menu-item (click)="pdfExportCallList()">
|
||||||
<mat-icon>picture_as_pdf</mat-icon>
|
<mat-icon>picture_as_pdf</mat-icon>
|
||||||
<span translate>Export as PDF</span>
|
<span translate>Export as PDF</span>
|
||||||
|
@ -256,6 +256,19 @@ export class CallListComponent extends SortTreeViewComponent<ViewMotion> impleme
|
|||||||
this.activeCatFilters.next([]);
|
this.activeCatFilters.next([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method requires a confirmation from the user
|
||||||
|
* and starts the sorting by the property `identifier` of the motions
|
||||||
|
* in case of `true`.
|
||||||
|
*/
|
||||||
|
public async sortMotionsByIdentifier(): Promise<void> {
|
||||||
|
const title = this.translate.instant('Do you really want to go ahead?');
|
||||||
|
const text = this.translate.instant('This will reset all made changes and sort the tree...');
|
||||||
|
if (await this.promptService.open(title, text)) {
|
||||||
|
this.forceSort.emit('identifier');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to trigger an update of the filter itself and the information about
|
* Helper to trigger an update of the filter itself and the information about
|
||||||
* the state of filters
|
* the state of filters
|
||||||
|
@ -4,7 +4,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
|
|
||||||
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
||||||
import { StorageService } from 'app/core/core-services/storage.service';
|
import { StorageService } from 'app/core/core-services/storage.service';
|
||||||
import { OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort-list.service';
|
import { OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { MotionSortListService } from './motion-sort-list.service';
|
import { MotionSortListService } from './motion-sort-list.service';
|
||||||
import { ViewMotion } from '../models/view-motion';
|
import { ViewMotion } from '../models/view-motion';
|
||||||
|
@ -4,7 +4,8 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
|
|
||||||
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
||||||
import { StorageService } from 'app/core/core-services/storage.service';
|
import { StorageService } from 'app/core/core-services/storage.service';
|
||||||
import { BaseSortListService, OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort-list.service';
|
import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service';
|
||||||
|
import { OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort.service';
|
||||||
import { ViewMotionBlock } from '../models/view-motion-block';
|
import { ViewMotionBlock } from '../models/view-motion-block';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
@ -6,7 +6,8 @@ import { OpenSlidesStatusService } from 'app/core/core-services/openslides-statu
|
|||||||
import { StorageService } from 'app/core/core-services/storage.service';
|
import { StorageService } from 'app/core/core-services/storage.service';
|
||||||
import { Deferred } from 'app/core/promises/deferred';
|
import { Deferred } from 'app/core/promises/deferred';
|
||||||
import { _ } from 'app/core/translate/translation-marker';
|
import { _ } from 'app/core/translate/translation-marker';
|
||||||
import { BaseSortListService, OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort-list.service';
|
import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service';
|
||||||
|
import { OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { ViewMotion } from '../models/view-motion';
|
import { ViewMotion } from '../models/view-motion';
|
||||||
|
|
||||||
|
@ -4,7 +4,8 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
|
|
||||||
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
|
||||||
import { StorageService } from 'app/core/core-services/storage.service';
|
import { StorageService } from 'app/core/core-services/storage.service';
|
||||||
import { BaseSortListService, OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort-list.service';
|
import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service';
|
||||||
|
import { OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort.service';
|
||||||
import { ViewUser } from '../models/view-user';
|
import { ViewUser } from '../models/view-user';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user