mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	partial selection model implementation
This commit is contained in:
		| @@ -30,9 +30,15 @@ export class DateDropdownComponent implements OnInit, OnDestroy { | ||||
|   @Input() | ||||
|   dateBefore: string | ||||
|  | ||||
|   @Output() | ||||
|   dateBeforeChange = new EventEmitter<string>() | ||||
|  | ||||
|   @Input() | ||||
|   dateAfter: string | ||||
|  | ||||
|   @Output() | ||||
|   dateAfterChange = new EventEmitter<string>() | ||||
|  | ||||
|   @Input() | ||||
|   title: string | ||||
|  | ||||
| @@ -83,6 +89,8 @@ export class DateDropdownComponent implements OnInit, OnDestroy { | ||||
|   } | ||||
|  | ||||
|   onChange() { | ||||
|     this.dateAfterChange.emit(this.dateAfter) | ||||
|     this.dateBeforeChange.emit(this.dateBefore) | ||||
|     this.datesSet.emit({after: this.dateAfter, before: this.dateBefore}) | ||||
|   } | ||||
|  | ||||
| @@ -91,12 +99,12 @@ export class DateDropdownComponent implements OnInit, OnDestroy { | ||||
|   } | ||||
|  | ||||
|   clearBefore() { | ||||
|     this.dateBefore = null; | ||||
|     this.dateBefore = null | ||||
|     this.onChange() | ||||
|   } | ||||
|  | ||||
|   clearAfter() { | ||||
|     this.dateAfter = null; | ||||
|     this.dateAfter = null | ||||
|     this.onChange() | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| <div class="btn-group" ngbDropdown role="group" (openChange)="dropdownOpenChange($event)" #dropdown="ngbDropdown"> | ||||
|   <button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="type !== types.Editing && itemsSelected?.length > 0 ? 'btn-primary' : 'btn-outline-primary'"> | ||||
|   <button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="type !== types.Editing && selectionModel.selectionSize() > 0 ? 'btn-primary' : 'btn-outline-primary'"> | ||||
|     <div class="d-none d-md-inline">{{title}}</div> | ||||
|     <div class="d-inline-block d-md-none"> | ||||
|       <svg class="toolbaricon" fill="currentColor"> | ||||
|         <use attr.xlink:href="assets/bootstrap-icons.svg#{{icon}}" /> | ||||
|       </svg> | ||||
|     </div> | ||||
|     <ng-container *ngIf="type !== types.Editing && itemsSelected?.length > 0"> | ||||
|     <ng-container *ngIf="type !== types.Editing && selectionModel.selectionSize() > 0"> | ||||
|       <div class="badge bg-secondary text-light rounded-pill badge-corner"> | ||||
|         {{itemsSelected?.length}} | ||||
|         {{selectionModel.selectionSize()}} | ||||
|       </div> | ||||
|     </ng-container> | ||||
|   </button> | ||||
| @@ -19,9 +19,9 @@ | ||||
|           <input class="form-control" type="text" [(ngModel)]="filterText" placeholder="Filter {{title}}" (keyup.enter)="listFilterEnter()" #listFilterTextInput> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div *ngIf="toggleableItems" class="items"> | ||||
|         <ng-container *ngFor="let toggleableItem of toggleableItems | filter: filterText"> | ||||
|           <app-toggleable-dropdown-button [toggleableItem]="toggleableItem" (toggle)="toggleItem($event)"></app-toggleable-dropdown-button> | ||||
|       <div *ngIf="selectionModel.items" class="items"> | ||||
|         <ng-container *ngFor="let toggleableItem of selectionModel.items | filter: filterText"> | ||||
|           <app-toggleable-dropdown-button [toggleableItem]="toggleableItem" (toggle)="selectionModel.toggle(toggleableItem.item)"></app-toggleable-dropdown-button> | ||||
|         </ng-container> | ||||
|       </div> | ||||
|       <button *ngIf="type == types.Editing" class="list-group-item list-group-item-action bg-light" (click)="dropdown.close()" [disabled]="!hasBeenToggled || (toggleableItems | filter: filterText).length == 0"> | ||||
|   | ||||
| @@ -3,12 +3,55 @@ import { FilterPipe } from  'src/app/pipes/filter.pipe'; | ||||
| import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { ToggleableItem, ToggleableItemState } from './toggleable-dropdown-button/toggleable-dropdown-button.component'; | ||||
| import { MatchingModel } from 'src/app/data/matching-model'; | ||||
| import { Subject } from 'rxjs'; | ||||
|  | ||||
| export enum FilterableDropdownType { | ||||
|   Filtering = 'filtering', | ||||
|   Editing = 'editing' | ||||
| } | ||||
|  | ||||
| export class FilterableDropdownSelectionModel { | ||||
|  | ||||
|   changed = new Subject<FilterableDropdownSelectionModel>() | ||||
|  | ||||
|   multiple = false | ||||
|  | ||||
|   items: ToggleableItem[] = [] | ||||
|  | ||||
|   getSelected() { | ||||
|     return this.items.filter(i => i.state == ToggleableItemState.Selected).map(i => i.item) | ||||
|   } | ||||
|  | ||||
|  | ||||
|  | ||||
|   toggle(item: MatchingModel, fireEvent = true) { | ||||
|     console.log("TOGGLE TAG") | ||||
|     let toggleableItem = this.items.find(i => i.item == item) | ||||
|     console.log(toggleableItem) | ||||
|  | ||||
|     if (toggleableItem) { | ||||
|       if (toggleableItem.state == ToggleableItemState.Selected) { | ||||
|         toggleableItem.state = ToggleableItemState.NotSelected | ||||
|       } else { | ||||
|         this.items.forEach(i => { | ||||
|           if (i.item == item) { | ||||
|             i.state = ToggleableItemState.Selected | ||||
|           } else if (!this.multiple) { | ||||
|             i.state = ToggleableItemState.NotSelected | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|       if (fireEvent) { | ||||
|         this.changed.next(this) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   selectionSize() { | ||||
|     return this.getSelected().length | ||||
|   } | ||||
| } | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-filterable-dropdown', | ||||
|   templateUrl: './filterable-dropdown.component.html', | ||||
| @@ -24,33 +67,45 @@ export class FilterableDropdownComponent { | ||||
|   @Input() | ||||
|   set items(items: MatchingModel[]) { | ||||
|     if (items) { | ||||
|       this._toggleableItems = items.map(i => { | ||||
|       this._selectionModel.items = items.map(i => { | ||||
|         return {item: i, state: ToggleableItemState.NotSelected, count: i.document_count} | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   _toggleableItems: ToggleableItem[] = [] | ||||
|  | ||||
|   @Input() | ||||
|   set toggleableItems (toggleableItems: ToggleableItem[]) { | ||||
|     if (this.type == FilterableDropdownType.Editing && this.dropdown?.isOpen()) return | ||||
|     else this._toggleableItems = toggleableItems | ||||
|   get items(): MatchingModel[] { | ||||
|     return this._selectionModel.items.map(i => i.item) | ||||
|   } | ||||
|  | ||||
|   get toggleableItems(): ToggleableItem[] { | ||||
|     return this._toggleableItems | ||||
|   } | ||||
|   _selectionModel = new FilterableDropdownSelectionModel() | ||||
|  | ||||
|   @Input() | ||||
|   set itemsSelected(itemsSelected: MatchingModel[]) { | ||||
|     this.toggleableItems.forEach(i => { | ||||
|       i.state = (itemsSelected.find(is => is.id == i.item.id)) ? ToggleableItemState.Selected : ToggleableItemState.NotSelected | ||||
|   set selectionModel(model: FilterableDropdownSelectionModel) { | ||||
|     if (this.selectionModel) { | ||||
|       this.selectionModel.changed.complete() | ||||
|       model.items = this.selectionModel.items | ||||
|       model.multiple = this.selectionModel.multiple | ||||
|     } | ||||
|     model.changed.subscribe(updatedModel => { | ||||
|       this.selectionModelChange.next(updatedModel) | ||||
|     }) | ||||
|     this._selectionModel = model | ||||
|   } | ||||
|  | ||||
|   get itemsSelected(): MatchingModel[] { | ||||
|     return this.toggleableItems.filter(ti => ti.state == ToggleableItemState.Selected).map(ti => ti.item) | ||||
|   get selectionModel(): FilterableDropdownSelectionModel { | ||||
|     return this._selectionModel | ||||
|   } | ||||
|  | ||||
|   @Output() | ||||
|   selectionModelChange = new EventEmitter<FilterableDropdownSelectionModel>() | ||||
|  | ||||
|   @Input() | ||||
|   set multiple(value: boolean) { | ||||
|     this.selectionModel.multiple = value | ||||
|   } | ||||
|  | ||||
|   get multiple() { | ||||
|     return this.selectionModel.multiple | ||||
|   } | ||||
|  | ||||
|   @Input() | ||||
| @@ -64,50 +119,40 @@ export class FilterableDropdownComponent { | ||||
|  | ||||
|   types = FilterableDropdownType | ||||
|  | ||||
|   @Input() | ||||
|   singular: boolean = false | ||||
|  | ||||
|   @Output() | ||||
|   toggle = new EventEmitter() | ||||
|  | ||||
|   @Output() | ||||
|   open = new EventEmitter() | ||||
|  | ||||
|   @Output() | ||||
|   editingComplete = new EventEmitter() | ||||
|  | ||||
|   hasBeenToggled:boolean = false | ||||
|  | ||||
|   constructor(private filterPipe: FilterPipe) { } | ||||
|   constructor(private filterPipe: FilterPipe) { | ||||
|     this.selectionModel = new FilterableDropdownSelectionModel() | ||||
|   } | ||||
|  | ||||
|   toggleItem(toggleableItem: ToggleableItem): void { | ||||
|     if (this.singular && toggleableItem.state == ToggleableItemState.Selected) { | ||||
|       this._toggleableItems.filter(ti => ti.item.id !== toggleableItem.item.id).forEach(ti => ti.state = ToggleableItemState.NotSelected) | ||||
|     } | ||||
|     this.hasBeenToggled = true | ||||
|     this.toggle.emit(toggleableItem.item) | ||||
|     // if (this.singular && toggleableItem.state == ToggleableItemState.Selected) { | ||||
|     //   this.selectionModel.items.filter(ti => ti.item.id !== toggleableItem.item.id).forEach(ti => ti.state = ToggleableItemState.NotSelected) | ||||
|     // } | ||||
|     // this.hasBeenToggled = true | ||||
|     // this.toggle.emit(toggleableItem.item) | ||||
|   } | ||||
|  | ||||
|   dropdownOpenChange(open: boolean): void { | ||||
|     if (open) { | ||||
|       setTimeout(() => { | ||||
|         this.listFilterTextInput.nativeElement.focus(); | ||||
|       }, 0) | ||||
|       this.hasBeenToggled = false | ||||
|       this.open.next() | ||||
|     } else { | ||||
|       this.filterText = '' | ||||
|       if (this.type == FilterableDropdownType.Editing) this.editingComplete.emit(this.toggleableItems) | ||||
|     } | ||||
|     // if (open) { | ||||
|     //   setTimeout(() => { | ||||
|     //     this.listFilterTextInput.nativeElement.focus(); | ||||
|     //   }, 0) | ||||
|     //   this.hasBeenToggled = false | ||||
|     //   this.open.next() | ||||
|     // } else { | ||||
|     //   this.filterText = '' | ||||
|     //   if (this.type == FilterableDropdownType.Editing) this.editingComplete.emit(this.toggleableItems) | ||||
|     // } | ||||
|   } | ||||
|  | ||||
|   listFilterEnter(): void { | ||||
|     let filtered = this.filterPipe.transform(this.toggleableItems, this.filterText) | ||||
|     if (filtered.length == 1) { | ||||
|       let toggleableItem = this.toggleableItems.find(ti => ti.item.id == filtered[0].item.id) | ||||
|       if (toggleableItem) toggleableItem.state = ToggleableItemState.Selected | ||||
|       this.toggleItem(filtered[0]) | ||||
|       this.dropdown.close() | ||||
|     } | ||||
|     // let filtered = this.filterPipe.transform(this.toggleableItems, this.filterText) | ||||
|     // if (filtered.length == 1) { | ||||
|     //   let toggleableItem = this.toggleableItems.find(ti => ti.item.id == filtered[0].item.id) | ||||
|     //   if (toggleableItem) toggleableItem.state = ToggleableItemState.Selected | ||||
|     //   this.toggleItem(filtered[0]) | ||||
|     //   this.dropdown.close() | ||||
|     // } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -31,8 +31,7 @@ export class ToggleableDropdownButtonComponent { | ||||
|   } | ||||
|  | ||||
|   toggleItem(): void { | ||||
|     this.toggleableItem.state = (this.toggleableItem.state == ToggleableItemState.NotSelected || this.toggleableItem.state == ToggleableItemState.PartiallySelected) ? ToggleableItemState.Selected : ToggleableItemState.NotSelected | ||||
|     this.toggle.emit(this.toggleableItem) | ||||
|     this.toggle.emit() | ||||
|   } | ||||
|  | ||||
|   getSelectedIconName() { | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| <div class="row"> | ||||
| <!-- <div class="row"> | ||||
|   <div class="col-auto mb-2 mb-xl-0" role="group" aria-label="Select"> | ||||
|     <button class="btn btn-sm btn-outline-danger" (click)="documentList.selectNone()"> | ||||
|       <svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor"> | ||||
| @@ -58,4 +58,4 @@ | ||||
|       Delete | ||||
|     </button> | ||||
|   </div> | ||||
| </div> | ||||
| </div> --> | ||||
|   | ||||
| @@ -8,11 +8,11 @@ | ||||
|   <div class="w-100 d-xl-none"></div> | ||||
|    <div class="col col-xl-auto mb-2 mb-xl-0"> | ||||
|      <div class="d-flex"> | ||||
|        <app-filterable-dropdown class="mr-2 mr-md-3" [items]="tags" [itemsSelected]="selectedTags" title="Tags" icon="tag-fill" (toggle)="toggleTag($event.id)"></app-filterable-dropdown> | ||||
|        <app-filterable-dropdown class="mr-2 mr-md-3" [items]="correspondents" [itemsSelected]="selectedCorrespondents" title="Correspondents" icon="person-fill" (toggle)="toggleCorrespondent($event.id)"></app-filterable-dropdown> | ||||
|        <app-filterable-dropdown class="mr-2 mr-md-3" [items]="documentTypes" [itemsSelected]="selectedDocumentTypes" title="Document types" icon="file-earmark-fill" (toggle)="toggleDocumentType($event.id)"></app-filterable-dropdown> | ||||
|        <app-date-dropdown class="mr-2 mr-md-3" [dateBefore]="dateCreatedBefore" [dateAfter]="dateCreatedAfter" title="Created" (datesSet)="onDatesCreatedSet($event)"></app-date-dropdown> | ||||
|        <app-date-dropdown [dateBefore]="dateAddedBefore" [dateAfter]="dateAddedAfter" title="Added"  (datesSet)="onDatesAddedSet($event)"></app-date-dropdown> | ||||
|        <app-filterable-dropdown class="mr-2 mr-md-3" [items]="tags" [(selectionModel)]="tagSelectionModel" (selectionModelChange)="updateRules()" [multiple]="true" title="Tags" icon="tag-fill"></app-filterable-dropdown> | ||||
|        <app-filterable-dropdown class="mr-2 mr-md-3" [items]="correspondents" [(selectionModel)]="correspondentSelectionModel" (selectionModelChange)="updateRules()" title="Correspondents" icon="person-fill"></app-filterable-dropdown> | ||||
|        <app-filterable-dropdown class="mr-2 mr-md-3" [items]="documentTypes" [(selectionModel)]="documentTypeSelectionModel" (selectionModelChange)="updateRules()" title="Document types" icon="file-earmark-fill"></app-filterable-dropdown> | ||||
|        <app-date-dropdown class="mr-2 mr-md-3" [(dateBefore)]="dateCreatedBefore" [(dateAfter)]="dateCreatedAfter" title="Created" (datesSet)="updateRules()"></app-date-dropdown> | ||||
|        <app-date-dropdown [(dateBefore)]="dateAddedBefore" [(dateAfter)]="dateAddedAfter" title="Added"  (datesSet)="updateRules()"></app-date-dropdown> | ||||
|      </div> | ||||
|    </div> | ||||
|    <div class="w-100 d-xl-none"></div> | ||||
|   | ||||
| @@ -3,14 +3,13 @@ import { PaperlessTag } from 'src/app/data/paperless-tag'; | ||||
| import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; | ||||
| import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'; | ||||
| import { Subject, Subscription } from 'rxjs'; | ||||
| import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; | ||||
| import { NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap'; | ||||
| import { debounceTime, distinctUntilChanged, filter, flatMap, mergeMap } from 'rxjs/operators'; | ||||
| import { DocumentTypeService } from 'src/app/services/rest/document-type.service'; | ||||
| import { TagService } from 'src/app/services/rest/tag.service'; | ||||
| import { CorrespondentService } from 'src/app/services/rest/correspondent.service'; | ||||
| import { FilterRule } from 'src/app/data/filter-rule'; | ||||
| import { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_HAS_TAG, FILTER_RULE_TYPES, FILTER_TITLE } from 'src/app/data/filter-rule-type'; | ||||
| import { DateSelection } from 'src/app/components/common/date-dropdown/date-dropdown.component'; | ||||
| import { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component'; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-filter-editor', | ||||
| @@ -46,37 +45,91 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|   ) { } | ||||
|  | ||||
|   tags: PaperlessTag[] = [] | ||||
|   correspondents: PaperlessCorrespondent[] | ||||
|   correspondents: PaperlessCorrespondent[] = [] | ||||
|   documentTypes: PaperlessDocumentType[] = [] | ||||
|  | ||||
|   _titleFilter = "" | ||||
|  | ||||
|   tagSelectionModel = new FilterableDropdownSelectionModel() | ||||
|   correspondentSelectionModel = new FilterableDropdownSelectionModel() | ||||
|   documentTypeSelectionModel = new FilterableDropdownSelectionModel() | ||||
|  | ||||
|   dateCreatedBefore: string | ||||
|   dateCreatedAfter: string | ||||
|   dateAddedBefore: string | ||||
|   dateAddedAfter: string | ||||
|  | ||||
|   @Input() | ||||
|   filterRules: FilterRule[] | ||||
|   set filterRules (value: FilterRule[]) { | ||||
|     console.log("SET FILTER RULES") | ||||
|     value.forEach(rule => { | ||||
|       switch (rule.rule_type) { | ||||
|         case FILTER_TITLE: | ||||
|           this._titleFilter = rule.value | ||||
|           break | ||||
|         case FILTER_CREATED_AFTER: | ||||
|           this.dateCreatedAfter = rule.value | ||||
|           break | ||||
|         case FILTER_CREATED_BEFORE: | ||||
|           this.dateCreatedBefore = rule.value | ||||
|           break | ||||
|         case FILTER_ADDED_AFTER: | ||||
|           this.dateAddedAfter = rule.value | ||||
|           break | ||||
|         case FILTER_ADDED_BEFORE: | ||||
|           this.dateAddedBefore = rule.value | ||||
|           break | ||||
|       } | ||||
|     }) | ||||
|  | ||||
|     this.tagService.getCachedMany(value.filter(v => v.rule_type == FILTER_HAS_TAG).map(rule => +rule.value)).subscribe(tags => { | ||||
|       console.log(tags) | ||||
|       tags.forEach(tag => this.tagSelectionModel.toggle(tag, false)) | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   @Output() | ||||
|   filterRulesChange = new EventEmitter<FilterRule[]>() | ||||
|  | ||||
|   updateRules() { | ||||
|     console.log("UPDATE RULES!!!") | ||||
|     let filterRules: FilterRule[] = [] | ||||
|     if (this._titleFilter) { | ||||
|       filterRules.push({rule_type: FILTER_TITLE, value: this._titleFilter}) | ||||
|     } | ||||
|     this.tagSelectionModel.getSelected().forEach(tag => { | ||||
|       filterRules.push({rule_type: FILTER_HAS_TAG, value: tag.id.toString()}) | ||||
|     }) | ||||
|     this.correspondentSelectionModel.getSelected().forEach(correspondent => { | ||||
|       filterRules.push({rule_type: FILTER_CORRESPONDENT, value: correspondent.id.toString()}) | ||||
|     }) | ||||
|     this.documentTypeSelectionModel.getSelected().forEach(documentType => { | ||||
|       filterRules.push({rule_type: FILTER_DOCUMENT_TYPE, value: documentType.id.toString()}) | ||||
|     }) | ||||
|     if (this.dateCreatedBefore) { | ||||
|       filterRules.push({rule_type: FILTER_CREATED_BEFORE, value: this.dateCreatedBefore}) | ||||
|     } | ||||
|     if (this.dateCreatedAfter) { | ||||
|       filterRules.push({rule_type: FILTER_CREATED_AFTER, value: this.dateCreatedAfter}) | ||||
|     } | ||||
|     if (this.dateAddedBefore) { | ||||
|       filterRules.push({rule_type: FILTER_ADDED_BEFORE, value: this.dateAddedBefore}) | ||||
|     } | ||||
|     if (this.dateAddedAfter) { | ||||
|       filterRules.push({rule_type: FILTER_ADDED_AFTER, value: this.dateAddedAfter}) | ||||
|     } | ||||
|     console.log(filterRules) | ||||
|     this.filterRulesChange.next(filterRules) | ||||
|   } | ||||
|  | ||||
|   hasFilters() { | ||||
|     return this.filterRules.length > 0 | ||||
|   } | ||||
|  | ||||
|   get selectedTags(): PaperlessTag[] { | ||||
|     let tagRules: FilterRule[] = this.filterRules.filter(fr => fr.rule_type == FILTER_HAS_TAG) | ||||
|     return this.tags?.filter(t => tagRules.find(tr => +tr.value == t.id)) | ||||
|   } | ||||
|  | ||||
|   get selectedCorrespondents(): PaperlessCorrespondent[] { | ||||
|     let correspondentRules: FilterRule[] = this.filterRules.filter(fr => fr.rule_type == FILTER_CORRESPONDENT) | ||||
|     return this.correspondents?.filter(c => correspondentRules.find(cr => +cr.value == c.id)) | ||||
|   } | ||||
|  | ||||
|   get selectedDocumentTypes(): PaperlessDocumentType[] { | ||||
|     let documentTypeRules: FilterRule[] = this.filterRules.filter(fr => fr.rule_type == FILTER_DOCUMENT_TYPE) | ||||
|     return this.documentTypes?.filter(dt => documentTypeRules.find(dtr => +dtr.value == dt.id)) | ||||
|     return this._titleFilter ||  | ||||
|       this.dateCreatedAfter || this.dateAddedBefore || this.dateCreatedAfter || this.dateCreatedBefore || | ||||
|       this.tagSelectionModel.selectionSize() || this.correspondentSelectionModel.selectionSize() || this.documentTypeSelectionModel.selectionSize() | ||||
|   } | ||||
|  | ||||
|   get titleFilter() { | ||||
|     let existingRule = this.filterRules.find(rule => rule.rule_type == FILTER_TITLE) | ||||
|     return existingRule ? existingRule.value : '' | ||||
|     return this._titleFilter | ||||
|   } | ||||
|  | ||||
|   set titleFilter(value) { | ||||
| @@ -97,142 +150,27 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|       debounceTime(400), | ||||
|       distinctUntilChanged() | ||||
|     ).subscribe(title => { | ||||
|       this.setTitleRule(title) | ||||
|       this._titleFilter = title | ||||
|       this.updateRules() | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   ngOnDestroy() { | ||||
|     this.titleFilterDebounce.complete() | ||||
|     // TODO: not sure if both is necessary | ||||
|     this.subscription.unsubscribe() | ||||
|   } | ||||
|  | ||||
|   applyFilters() { | ||||
|     this.filterRulesChange.next(this.filterRules) | ||||
|   } | ||||
|  | ||||
|   clearSelected() { | ||||
|     this.filterRules = [] | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   private toggleFilterRule(filterRuleTypeID: number, value: number) { | ||||
|  | ||||
|     let filterRuleType = FILTER_RULE_TYPES.find(t => t.id == filterRuleTypeID) | ||||
|  | ||||
|     let existingRule = this.filterRules.find(rule => rule.rule_type == filterRuleTypeID && rule.value == value?.toString()) | ||||
|     let existingRuleOfSameType = this.filterRules.find(rule => rule.rule_type == filterRuleTypeID) | ||||
|  | ||||
|     if (existingRule) { | ||||
|       // if this exact rule already exists, remove it in all cases. | ||||
|       this.filterRules.splice(this.filterRules.indexOf(existingRule), 1) | ||||
|     } else if (filterRuleType.multi || !existingRuleOfSameType) { | ||||
|       // if we allow multiple rules per type, or no rule of this type already exists, push a new rule. | ||||
|       this.filterRules.push({rule_type: filterRuleTypeID, value: value?.toString()}) | ||||
|     } else { | ||||
|       // otherwise (i.e., no multi support AND there's already a rule of this type), update the rule. | ||||
|       existingRuleOfSameType.value = value?.toString() | ||||
|     } | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   private setTitleRule(title: string) { | ||||
|     let existingRule = this.filterRules.find(rule => rule.rule_type == FILTER_TITLE) | ||||
|  | ||||
|     if (!existingRule && title) { | ||||
|       this.filterRules.push({rule_type: FILTER_TITLE, value: title}) | ||||
|     } else if (existingRule && !title) { | ||||
|       this.filterRules.splice(this.filterRules.findIndex(rule => rule.rule_type == FILTER_TITLE), 1) | ||||
|     } else if (existingRule && title) { | ||||
|       existingRule.value = title | ||||
|     } | ||||
|     this.applyFilters() | ||||
|     this._titleFilter = "" | ||||
|     this.updateRules() | ||||
|   } | ||||
|  | ||||
|   toggleTag(tagId: number) { | ||||
|     this.toggleFilterRule(FILTER_HAS_TAG, tagId) | ||||
|   } | ||||
|  | ||||
|   toggleCorrespondent(correspondentId: number) { | ||||
|     this.toggleFilterRule(FILTER_CORRESPONDENT, correspondentId) | ||||
|   } | ||||
|  | ||||
|   toggleDocumentType(documentTypeId: number) { | ||||
|     this.toggleFilterRule(FILTER_DOCUMENT_TYPE, documentTypeId) | ||||
|   } | ||||
|  | ||||
|  | ||||
|  | ||||
|   // Date handling | ||||
|  | ||||
|  | ||||
|   onDatesCreatedSet(dates: DateSelection) { | ||||
|     this.setDateCreatedBefore(dates.before) | ||||
|     this.setDateCreatedAfter(dates.after) | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   onDatesAddedSet(dates: DateSelection) { | ||||
|     this.setDateAddedBefore(dates.before) | ||||
|     this.setDateAddedAfter(dates.after) | ||||
|     this.applyFilters() | ||||
|   } | ||||
|  | ||||
|   get dateCreatedBefore(): string { | ||||
|     let createdBeforeRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_CREATED_BEFORE) | ||||
|     return createdBeforeRule ? createdBeforeRule.value : null | ||||
|   } | ||||
|  | ||||
|   get dateCreatedAfter(): string { | ||||
|     let createdAfterRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_CREATED_AFTER) | ||||
|     return createdAfterRule ? createdAfterRule.value : null | ||||
|   } | ||||
|  | ||||
|   get dateAddedBefore(): string { | ||||
|     let addedBeforeRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_ADDED_BEFORE) | ||||
|     return addedBeforeRule ? addedBeforeRule.value : null | ||||
|   } | ||||
|  | ||||
|   get dateAddedAfter(): string { | ||||
|     let addedAfterRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_ADDED_AFTER) | ||||
|     return addedAfterRule ? addedAfterRule.value : null | ||||
|   } | ||||
|  | ||||
|   setDateCreatedBefore(date?: string) { | ||||
|     if (date) this.setDateFilter(date, FILTER_CREATED_BEFORE) | ||||
|     else this.clearDateFilter(FILTER_CREATED_BEFORE) | ||||
|   } | ||||
|  | ||||
|   setDateCreatedAfter(date?: string) { | ||||
|     if (date) this.setDateFilter(date, FILTER_CREATED_AFTER) | ||||
|     else this.clearDateFilter(FILTER_CREATED_AFTER) | ||||
|   } | ||||
|  | ||||
|   setDateAddedBefore(date?: string) { | ||||
|     if (date) this.setDateFilter(date, FILTER_ADDED_BEFORE) | ||||
|     else this.clearDateFilter(FILTER_ADDED_BEFORE) | ||||
|   } | ||||
|  | ||||
|   setDateAddedAfter(date?: string) { | ||||
|     if (date) this.setDateFilter(date, FILTER_ADDED_AFTER) | ||||
|     else this.clearDateFilter(FILTER_ADDED_AFTER) | ||||
|   } | ||||
|  | ||||
|   setDateFilter(date: string, dateRuleTypeID: number) { | ||||
|     let existingRule = this.filterRules.find(rule => rule.rule_type == dateRuleTypeID) | ||||
|  | ||||
|     if (existingRule) { | ||||
|       existingRule.value = date | ||||
|     } else { | ||||
|       this.filterRules.push({rule_type: dateRuleTypeID, value: date}) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   clearDateFilter(dateRuleTypeID: number) { | ||||
|     let ruleIndex = this.filterRules.findIndex(rule => rule.rule_type == dateRuleTypeID) | ||||
|     if (ruleIndex != -1) { | ||||
|       this.filterRules.splice(ruleIndex, 1) | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 jonaswinkler
					jonaswinkler