mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	selection model
This commit is contained in:
		| @@ -20,8 +20,8 @@ | ||||
|         </div> | ||||
|       </div> | ||||
|       <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 *ngFor="let item of selectionModel.items | filter: filterText"> | ||||
|           <app-toggleable-dropdown-button [item]="item" [state]="selectionModel.get(item.id)" (toggle)="selectionModel.toggle(item.id)"></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"> | ||||
|   | ||||
| @@ -4,6 +4,7 @@ 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'; | ||||
| import { ThrowStmt } from '@angular/compiler'; | ||||
|  | ||||
| export enum FilterableDropdownType { | ||||
|   Filtering = 'filtering', | ||||
| @@ -16,39 +17,56 @@ export class FilterableDropdownSelectionModel { | ||||
|  | ||||
|   multiple = false | ||||
|  | ||||
|   items: ToggleableItem[] = [] | ||||
|   items: MatchingModel[] = [] | ||||
|  | ||||
|   getSelected() { | ||||
|     return this.items.filter(i => i.state == ToggleableItemState.Selected).map(i => i.item) | ||||
|   selection = new Map<number, ToggleableItemState>() | ||||
|  | ||||
|   getSelectedItems() { | ||||
|     return this.items.filter(i => this.selection.get(i.id) == ToggleableItemState.Selected) | ||||
|   } | ||||
|  | ||||
|  | ||||
|  | ||||
|   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) | ||||
|       } | ||||
|   set(id: number, state: ToggleableItemState, fireEvent = true) { | ||||
|     this.selection.set(id, state) | ||||
|     if (fireEvent) { | ||||
|       this.changed.next(this) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   toggle(id: number, fireEvent = true) { | ||||
|     let state = this.selection.get(id) | ||||
|     if (state == null || state != ToggleableItemState.Selected) { | ||||
|       this.selection.set(id, ToggleableItemState.Selected) | ||||
|     } else if (state == ToggleableItemState.Selected) { | ||||
|       this.selection.set(id, ToggleableItemState.NotSelected) | ||||
|     } | ||||
|  | ||||
|     if (!this.multiple) { | ||||
|       for (let key of this.selection.keys()) { | ||||
|         if (key != id) { | ||||
|           this.selection.set(key, ToggleableItemState.NotSelected) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (fireEvent) { | ||||
|       this.changed.next(this) | ||||
|     } | ||||
|      | ||||
|   } | ||||
|  | ||||
|   get(id: number) { | ||||
|     return this.selection.get(id) || ToggleableItemState.NotSelected | ||||
|   } | ||||
|  | ||||
|   selectionSize() { | ||||
|     return this.getSelected().length | ||||
|     return this.getSelectedItems().length | ||||
|   } | ||||
|  | ||||
|   clear(fireEvent = true) { | ||||
|     this.selection.clear() | ||||
|     if (fireEvent) { | ||||
|       this.changed.next(this) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -67,14 +85,12 @@ export class FilterableDropdownComponent { | ||||
|   @Input() | ||||
|   set items(items: MatchingModel[]) { | ||||
|     if (items) { | ||||
|       this._selectionModel.items = items.map(i => { | ||||
|         return {item: i, state: ToggleableItemState.NotSelected, count: i.document_count} | ||||
|       }) | ||||
|       this._selectionModel.items = items | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   get items(): MatchingModel[] { | ||||
|     return this._selectionModel.items.map(i => i.item) | ||||
|     return this._selectionModel.items | ||||
|   } | ||||
|  | ||||
|   _selectionModel = new FilterableDropdownSelectionModel() | ||||
|   | ||||
| @@ -5,8 +5,8 @@ | ||||
|     </svg> | ||||
|   </div> | ||||
|   <div class="mr-1"> | ||||
|     <app-tag *ngIf="isTag; else displayName" [tag]="toggleableItem?.item" [clickable]="true" linkTitle="Filter by tag"></app-tag> | ||||
|     <ng-template #displayName><small>{{toggleableItem?.item.name}}</small></ng-template> | ||||
|     <app-tag *ngIf="isTag; else displayName" [tag]="item" [clickable]="true" linkTitle="Filter by tag"></app-tag> | ||||
|     <ng-template #displayName><small>{{item.name}}</small></ng-template> | ||||
|   </div> | ||||
|   <div class="badge badge-light rounded-pill ml-auto mr-1">{{toggleableItem?.count}}</div> | ||||
|   <div class="badge badge-light rounded-pill ml-auto mr-1">{{item.document_count}}</div> | ||||
| </button> | ||||
|   | ||||
| @@ -21,13 +21,19 @@ export enum ToggleableItemState { | ||||
| export class ToggleableDropdownButtonComponent { | ||||
|  | ||||
|   @Input() | ||||
|   toggleableItem: ToggleableItem | ||||
|   item: MatchingModel | ||||
|  | ||||
|   @Input() | ||||
|   state: ToggleableItemState | ||||
|  | ||||
|   @Input() | ||||
|   count: number | ||||
|  | ||||
|   @Output() | ||||
|   toggle = new EventEmitter() | ||||
|  | ||||
|   get isTag(): boolean { | ||||
|     return 'is_inbox_tag' in this.toggleableItem?.item // ~ this.item instanceof PaperlessTag | ||||
|     return 'is_inbox_tag' in this.item | ||||
|   } | ||||
|  | ||||
|   toggleItem(): void { | ||||
| @@ -35,9 +41,9 @@ export class ToggleableDropdownButtonComponent { | ||||
|   } | ||||
|  | ||||
|   getSelectedIconName() { | ||||
|     if (this.toggleableItem?.state == ToggleableItemState.Selected) { | ||||
|     if (this.state == ToggleableItemState.Selected) { | ||||
|       return "check" | ||||
|     } else if (this.toggleableItem?.state == ToggleableItemState.PartiallySelected) { | ||||
|     } else if (this.state == ToggleableItemState.PartiallySelected) { | ||||
|       return "dash" | ||||
|     } else { | ||||
|       return "" | ||||
|   | ||||
| @@ -3,13 +3,14 @@ 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, filter, flatMap, mergeMap } from 'rxjs/operators'; | ||||
| import { debounceTime, distinctUntilChanged } 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 { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_HAS_TAG, FILTER_TITLE } from 'src/app/data/filter-rule-type'; | ||||
| import { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component'; | ||||
| import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-filter-editor', | ||||
| @@ -61,7 +62,6 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|  | ||||
|   @Input() | ||||
|   set filterRules (value: FilterRule[]) { | ||||
|     console.log("SET FILTER RULES") | ||||
|     value.forEach(rule => { | ||||
|       switch (rule.rule_type) { | ||||
|         case FILTER_TITLE: | ||||
| @@ -79,31 +79,34 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|         case FILTER_ADDED_BEFORE: | ||||
|           this.dateAddedBefore = rule.value | ||||
|           break | ||||
|         case FILTER_HAS_TAG: | ||||
|           this.tagSelectionModel.set(+rule.value, ToggleableItemState.Selected, false) | ||||
|           break | ||||
|         case FILTER_CORRESPONDENT: | ||||
|           this.correspondentSelectionModel.set(+rule.value, ToggleableItemState.Selected, false) | ||||
|           break | ||||
|         case FILTER_DOCUMENT_TYPE: | ||||
|           this.documentTypeSelectionModel.set(+rule.value, ToggleableItemState.Selected, false) | ||||
|           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 => { | ||||
|     this.tagSelectionModel.getSelectedItems().forEach(tag => { | ||||
|       filterRules.push({rule_type: FILTER_HAS_TAG, value: tag.id.toString()}) | ||||
|     }) | ||||
|     this.correspondentSelectionModel.getSelected().forEach(correspondent => { | ||||
|     this.correspondentSelectionModel.getSelectedItems().forEach(correspondent => { | ||||
|       filterRules.push({rule_type: FILTER_CORRESPONDENT, value: correspondent.id.toString()}) | ||||
|     }) | ||||
|     this.documentTypeSelectionModel.getSelected().forEach(documentType => { | ||||
|     this.documentTypeSelectionModel.getSelectedItems().forEach(documentType => { | ||||
|       filterRules.push({rule_type: FILTER_DOCUMENT_TYPE, value: documentType.id.toString()}) | ||||
|     }) | ||||
|     if (this.dateCreatedBefore) { | ||||
| @@ -118,13 +121,12 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|     if (this.dateAddedAfter) { | ||||
|       filterRules.push({rule_type: FILTER_ADDED_AFTER, value: this.dateAddedAfter}) | ||||
|     } | ||||
|     console.log(filterRules) | ||||
|     this.filterRulesChange.next(filterRules) | ||||
|   } | ||||
|  | ||||
|   hasFilters() { | ||||
|     return this._titleFilter ||  | ||||
|       this.dateCreatedAfter || this.dateAddedBefore || this.dateCreatedAfter || this.dateCreatedBefore || | ||||
|       this.dateAddedAfter || this.dateAddedBefore || this.dateCreatedAfter || this.dateCreatedBefore || | ||||
|       this.tagSelectionModel.selectionSize() || this.correspondentSelectionModel.selectionSize() || this.documentTypeSelectionModel.selectionSize() | ||||
|   } | ||||
|  | ||||
| @@ -161,16 +163,26 @@ export class FilterEditorComponent implements OnInit, OnDestroy { | ||||
|  | ||||
|   clearSelected() { | ||||
|     this._titleFilter = "" | ||||
|     this.tagSelectionModel.clear(false) | ||||
|     this.documentTypeSelectionModel.clear(false) | ||||
|     this.correspondentSelectionModel.clear(false) | ||||
|     this.dateAddedBefore = null | ||||
|     this.dateAddedAfter = null | ||||
|     this.dateCreatedBefore = null | ||||
|     this.dateCreatedAfter = null | ||||
|     this.updateRules() | ||||
|   } | ||||
|  | ||||
|   toggleTag(tagId: number) { | ||||
|     this.tagSelectionModel.toggle(tagId) | ||||
|   } | ||||
|  | ||||
|   toggleCorrespondent(correspondentId: number) { | ||||
|     this.correspondentSelectionModel.toggle(correspondentId) | ||||
|   } | ||||
|  | ||||
|   toggleDocumentType(documentTypeId: number) { | ||||
|     this.documentTypeSelectionModel.toggle(documentTypeId) | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 jonaswinkler
					jonaswinkler